tui-image-editor中跨域问题

如何安装 tui-image-editor等就不再赘述,参考这篇博客即可,

https://blog.csdn.net/weixin_44867717/article/details/128212251

简单版参考: https://blog.csdn.net/Bonsoir777/article/details/134153807

官网: https://ui.toast.com/tui-image-editor

 

下面说说我碰到的问题,

跨域

如上图所示,我用的图片地址是上传到阿里云的,图片地址是跨域的,这个需要在下图数据安全中跨域设置里面配置,这样就可以访问。

  如果开启了CDN,则还需要在CDN中配置,

 

  关于CDN缓存问题

    cdn是存在缓存的,如果编辑了图片,保存到数据库,图片名不改变,直接上传到阿里云,由于启用了cdn,这里面存在缓存,如果页面用的是缓存的url,还是会显示以前的图片,所以要改变图片名然后上传,这样保证路径改变了。

 

 

因为我是本地调试,所以用其他一个图片代替,

https://img1.baidu.com/it/u=4131209051,1689521986&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500
 
如下图所示,这样一个弹框就能弹出图片,

 

元素隐藏

  如下代码所示,我选取了一些界面元素,隐藏掉。

document.querySelector('[tooltip-content="Delete"]').style.display = 'none'
document.querySelector('[tooltip-content="DeleteAll"]').style.display = 'none'
document.querySelector('[tooltip-content="添加图标"]').style.display = 'none'
document.querySelector('[tooltip-content="Mask"]').style.display = 'none'
document.querySelector('[tooltip-content="Filter"]').style.display = 'none'


document.querySelector('[tooltip-content="放大"]').style.marginTop = '40px'

  在上述参考博客中,元素隐藏使用的是英文,结果无效,后来才发现,通过汉化后,选取的元素应该是写中文,

  如下是我汉化的配置,就需要通过汉化来选取元素,

// 中文菜单
const localeCN = {
    Crop: '裁剪',
    Draw: '涂鸦',
    Text: '添加文本',
    Free: '任意线条',
    Straight: '直线',
    Icon: '添加图标',
    Color: '颜色',
    Range: '范围',
    ZoomIn: '放大',
    ZoomOut: '缩小',
    Hand: '移动',
    History: '历史记录',
    Undo: '撤销',
    Redo: '前进',
    Reset: '重置',
    Delete: 'Delete',
    DeleteAll: 'DeleteAll',
    Resize: '调整大小',
    Flip: '镜像翻转',
    Rotate: '旋转',
    Shape: '图形',

    Width: '宽',
    Height: '高',
    'Lock Aspect Ratio': '锁定宽高比',
    Apply: '应用',
    Cancel: '取消',
    Custom: '自定义',
    Square: '正方形',
    'Flip X': 'X轴翻转',
    'Flip Y': 'Y轴翻转',
    Rectangle: '正方形',
    Circle: '圆',
    Triangle: '三角形',
    Fill: '填充',
    Stroke: '边框',
    Bold: '粗体',
    Italic: '斜体',
    Underline: '下划线',
    Left: '左',
    Center: '中',
    Right: '右',
    'Text size': '字号',

}

 

CSS样式调整问题

  如下所示   <style lang="scss"> 使用可以生效,但 <style lang="scss" scoped> 就不会生效。

<style lang="scss">
.tui-image-editor-container .tui-image-editor-main {
    top: 0em !important;
}

/* 强制压缩菜单的高度 ,减少占用屏幕的空间*/
.tui-image-editor-container .tui-image-editor-submenu {
    height: auto !important;
}

.tui-image-editor-container.bottom .tui-image-editor-submenu>div {
    padding: 0 !important;
}

.tui-image-editor-container .tui-image-editor-help-menu.top {
    background-color: white;
}

/* 顶部工具栏定位 */
.tui-image-editor-container .tui-image-editor-header {
    top: -55px;
}

.tui-image-editor-container .tui-image-editor-help-menu.top {
    top: -50px;
}

/* 取消超出部分隐藏,否则因为顶部工具栏已经超出去了,会显示不出来
.tui-image-editor-container {
  overflow: auto;
} */
/* 顶部工具栏定位 */
.tui-image-editor-container {
    overflow: visible;
}
</style>

 

完整组件代码

<template>
    <div class="boardBox">
        <!-- 用户可以通过点击大按钮来关闭弹窗 -->
        <div class="closeBigBtn">
            <img class="img" title="保存图片" :src="require('@/assets/monitor/save.png')"
                @click="saveCanvas2Img" />
            <img class="img" title="下载图片" :src="require('@/assets/monitor/download.png')"
                @click="downloadCanvas2Img" />
            <img class="img" title="分享图片" :src="require('@/assets/monitor/share.png')"
                @click="shareCanvas2Img" />
        </div>
        <!-- 绘图组件容器DOM -->
        <div id="tui-image-editor"></div>
    </div>
</template>
<script>
import "tui-image-editor/dist/tui-image-editor.css";
import "tui-color-picker/dist/tui-color-picker.css";
import ImageEditor from "tui-image-editor";


// 中文菜单
const localeCN = {
    Crop: '裁剪',
    Draw: '涂鸦',
    Text: '添加文本',
    Free: '任意线条',
    Straight: '直线',
    Icon: '添加图标',
    Color: '颜色',
    Range: '范围',
    ZoomIn: '放大',
    ZoomOut: '缩小',
    Hand: '移动',
    History: '历史记录',
    Undo: '撤销',
    Redo: '前进',
    Reset: '重置',
    Delete: 'Delete',
    DeleteAll: 'DeleteAll',
    Resize: '调整大小',
    Flip: '镜像翻转',
    Rotate: '旋转',
    Shape: '图形',

    Width: '宽',
    Height: '高',
    'Lock Aspect Ratio': '锁定宽高比',
    Apply: '应用',
    Cancel: '取消',
    Custom: '自定义',
    Square: '正方形',
    'Flip X': 'X轴翻转',
    'Flip Y': 'Y轴翻转',
    Rectangle: '正方形',
    Circle: '圆',
    Triangle: '三角形',
    Fill: '填充',
    Stroke: '边框',
    Bold: '粗体',
    Italic: '斜体',
    Underline: '下划线',
    Left: '左',
    Center: '中',
    Right: '右',
    'Text size': '字号',

}
// 画布组件自定义样式
const customTheme = {
    // image 坐上角度图片
    'common.bi.image': '', // 在这里换上你喜欢的logo图片
    'common.bisize.width': '0px',
    'common.bisize.height': '0px',
    'common.backgroundImage': 'none',
    'common.backgroundColor': '#f3f4f6',
    'common.border': '0px solid #444',

    // header
    'header.backgroundImage': 'none',
    'header.backgroundColor': '#f3f4f6',
    'header.border': '0px',
    'header.display': 'none',

    // load button
    'loadButton.backgroundColor': '#fff',
    'loadButton.border': '1px solid #ddd',
    'loadButton.color': '#222',
    'loadButton.fontFamily': 'NotoSans, sans-serif',
    'loadButton.fontSize': '12px',
    'loadButton.display': 'none', // 可以直接隐藏掉

    // download button
    'downloadButton.backgroundColor': '#fdba3b',
    'downloadButton.border': '1px solid #fdba3b',
    'downloadButton.color': '#fff',
    'downloadButton.fontFamily': 'NotoSans, sans-serif',
    'downloadButton.fontSize': '12px',
    'downloadButton.display': 'none', // 可以直接隐藏掉

    // icons default
    'menu.normalIcon.color': '#8a8a8a',
    'menu.activeIcon.color': '#555555',
    'menu.disabledIcon.color': '#434343',
    'menu.hoverIcon.color': '#e9e9e9',
    'submenu.normalIcon.color': '#8a8a8a',
    'submenu.activeIcon.color': '#e9e9e9',

    'menu.iconSize.width': '24px',
    'menu.iconSize.height': '24px',
    'submenu.iconSize.width': '32px',
    'submenu.iconSize.height': '32px',

    // submenu primary color
    'submenu.backgroundColor': '#1e1e1e',
    'submenu.partition.color': '#858585',

    // submenu labels
    'submenu.normalLabel.color': '#858585',
    'submenu.normalLabel.fontWeight': 'lighter',
    'submenu.activeLabel.color': '#fff',
    'submenu.activeLabel.fontWeight': 'lighter',

    // checkbox style
    'checkbox.border': '1px solid #ccc',
    'checkbox.backgroundColor': '#fff',

    // rango style
    'range.pointer.color': '#fff',
    'range.bar.color': '#666',
    'range.subbar.color': '#d1d1d1',

    'range.disabledPointer.color': '#414141',
    'range.disabledBar.color': '#282828',
    'range.disabledSubbar.color': '#414141',

    'range.value.color': '#fff',
    'range.value.fontWeight': 'lighter',
    'range.value.fontSize': '11px',
    'range.value.border': '1px solid #353535',
    'range.value.backgroundColor': '#151515',
    'range.title.color': '#fff',
    'range.title.fontWeight': 'lighter',

    // colorpicker style
    'colorpicker.button.border': '1px solid #1e1e1e',
    'colorpicker.title.color': '#fff'
}


export default {
    data() {
        return {
            instance: null,
            loading: false,
        };
    },
    mounted() {
        this.init();
    },
    methods: {
        init() {
            this.loading = true;
            let image = "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c1d7a1feb60346449c1a64893888989a~tplv-k3u1fbpfcp-watermark.image";

            image = "https://img1.baidu.com/it/u=4131209051,1689521986&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500";

            // image = "https://cdn.esg-cloud.com/news/08a44f8437004910874d9286f239023d.jpg?x-oss-process=style/w750"

            this.instance = new ImageEditor(
                document.querySelector("#tui-image-editor"),
                {
                    includeUI: {
                        loadImage: {
                            path: image,
                            name: "image",
                        },
                        initMenu: "", // 默认打开的菜单项
                        menuBarPosition: "right", // 菜单所在的位置
                        // 汉化
                        locale: localeCN,
                        // 自定义样式(隐藏默认顶部栏目、按钮颜色。。。)
                        theme: customTheme
                    },
                    cssMaxWidth: 1000, // canvas 最大宽度
                    cssMaxHeight: 1000, // canvas 最大高度
                }
            );

            document.querySelector('[tooltip-content="Delete"]').style.display = 'none'
            document.querySelector('[tooltip-content="DeleteAll"]').style.display = 'none'
            document.querySelector('[tooltip-content="添加图标"]').style.display = 'none'
            document.querySelector('[tooltip-content="Mask"]').style.display = 'none'
            document.querySelector('[tooltip-content="Filter"]').style.display = 'none'


            document.querySelector('[tooltip-content="放大"]').style.marginTop = '40px'
        },
        /** 保存编辑后图片 */
        saveCanvas2Img() {
            // 调用组件官方方法,获取整个编辑后图片的base64数据
            const base64String = this.instance.toDataURL()

            this.$emit('updateImage',base64String)

            // console.log(base64String)

            //todo 传参数给后台接口
        },
        downloadCanvas2Img(){
            const base64String = this.instance.toDataURL()
            this.$emit('downloadImage',base64String)
        },
        shareCanvas2Img(){

        }
    },
};
</script>

<style lang="scss">
.tui-image-editor-container .tui-image-editor-main {
    top: 0em !important;
}

/* 强制压缩菜单的高度 ,减少占用屏幕的空间*/
.tui-image-editor-container .tui-image-editor-submenu {
    height: auto !important;
}

.tui-image-editor-container.bottom .tui-image-editor-submenu>div {
    padding: 0 !important;
}

.tui-image-editor-container .tui-image-editor-help-menu.top {
    background-color: white;
}

/* 顶部工具栏定位 */
.tui-image-editor-container .tui-image-editor-header {
    top: -55px;
}

.tui-image-editor-container .tui-image-editor-help-menu.top {
    top: -50px;
}

/* 取消超出部分隐藏,否则因为顶部工具栏已经超出去了,会显示不出来
.tui-image-editor-container {
  overflow: auto;
} */
/* 顶部工具栏定位 */
.tui-image-editor-container {
    overflow: visible;
}
</style>

<style lang="scss" scoped>
.boardBox {
    //   width: 100%;
    //   height: 80vh;
    // margin-top: 0px;
    height: 100%;
    width: 100%;
    background: #f9f9f9;

    canvas {
        border: 1px solid #b3b3b3;
    }
}

.img{
    width:30px;
    cursor: pointer;
    margin: 5px 10px;
}
</style>

  使用如下,

<CustomModal :visible.sync="showModal">
    <EditImage :imagePath="imagePath" @downloadImage="downloadImageM" @updateImage="updateImageM"></EditImage>
</CustomModal>

  下载图片

   有个下载图片的功能,用的这个博客的代码,不需要后端直接前端下载。

https://www.cnblogs.com/venje/p/12109626.html

downloadImageM(param) {
      let base64String = param;

      let needData = {
        img_id: this.nowHandleImage.img_id,
        base64: base64String
      }

      let h = JSON.stringify(needData)
      //base64太大,413
      // downloadImage(h);

      this.download(base64String);

    },


    download(base64) {
      let imgData = base64;
      this.downloadFile(this.nowHandleImage.img_name, imgData);
    },
    //下载
    downloadFile(fileName, content) {
      let aLink = document.createElement('a');
      let blob = this.base64ToBlob(content); //new Blob([content]);

      let evt = document.createEvent("HTMLEvents");
      evt.initEvent("click", true, true);//initEvent 不加后两个参数在FF下会报错  事件类型,是否冒泡,是否阻止浏览器的默认行为
      aLink.download = fileName;
      aLink.href = URL.createObjectURL(blob);

      // aLink.dispatchEvent(evt);
      aLink.click()
    },
    //base64转blob
    base64ToBlob(code) {
      let parts = code.split(';base64,');
      let contentType = parts[0].split(':')[1];
      let raw = window.atob(parts[1]);
      let rawLength = raw.length;

      let uInt8Array = new Uint8Array(rawLength);

      for (let i = 0; i < rawLength; ++i) {
        uInt8Array[i] = raw.charCodeAt(i);
      }
      return new Blob([uInt8Array], { type: contentType });
    },

  更新图片

base64ToFile(base64String) {
      let parts = base64String.split(';base64,');
      let contentType = parts[0].split(':')[1];
      let raw = window.atob(parts[1]);
      let rawLength = raw.length;

      let uInt8Array = new Uint8Array(rawLength);

      for (let i = 0; i < rawLength; ++i) {
        uInt8Array[i] = raw.charCodeAt(i);
      }
      let options = {
        type: contentType,
        endings: "native"
      }

      let fileName = this.nowHandleImage.img_name;
      return new File([uInt8Array], fileName, options)
    },


    updateImageM(param) {
      if(this.updateImageFlag) return;//防止按钮按多次

      this.updateImageFlag = true;

      let base64String = param;
      // let needData = {
      //   img_id: this.nowHandleImage.img_id,
      //   base64: base64String.substr("data:image/png;base64,".length)
      // }

      //base64字符串太长,转成文件,否则丢失参数信息
      let file = this.base64ToFile(base64String);
      let formData = new FormData();

      formData.append("img_id", this.nowHandleImage.img_id)
      formData.append("file", file);

      updateImage(formData).then(res => {
        this.updateImageFlag = false;
        if (res.result_code == "0") {
          this.$Message.success({
            content: '更新成功',
            duration: 3
          })
          this.initData();
          this.loadMore();
        }
      });
    },

  后端直接接受Multipartfile文件即可,本来想的是POST直接传base64字符串,但当使用很大的图片时,发现参数全部丢失了,只知道GET有长度限制,看来POST也有长度限制,只不过正常情况下应该不会传这么长参数。

@RequestMapping(value = "/updateImage.action")
    public PageData updateImage(MultipartFile file, Integer img_id){
        try {
            return monitorService.updateImage(file,img_id);
        } catch (Exception e) {
            logger.error("获取产线列表异常", e);
            return RespUtil.getErrorResp("-1", "获取产线列表异常");
        }
    }

 

posted @ 2024-05-15 10:24  伟衙内  阅读(29)  评论(0编辑  收藏  举报