效果图
下载包
pnpm i tui- image- editor
pnpm i tui- color- picker
调用组件
< el- dialog v- model= "imgshow" destroy- on- close width= "40%" draggable align- center : show- close= "true" : close- on- click- modal= "false" > < template #header> < div style= "display: flex; justify-content: space-between" > < div style= "display: flex" > < h3> 图片< / h3> < / div> < / div> < / template> < div class = "newBox" > < ! -- 图片处理框 -- > < SignImage v- if = "cropperObj.cVisible" : dialogVisible= "cropperObj.cVisible" : title= "cropperObj.ctitle" : imgUrl= "cropperObj.previewsImgUrl" @getNewImg= "cropperObj.getNewImg" @closeCropperDialog= "cropperObj.closeCropperView" > < / SignImage> < / div> < / el- dialog>
import { SignImage } from './index' ;
let imgshow = ref< boolean> ( false ) ;
const cropperObj = reactive ( { cVisible : true , ctitle : "" , previewsImgUrl : "https://img0.baidu.com/it/u=2759734465,3558448225&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1708621200&t=acc59373dca9b8658e33e14a974f6c47" , openCropperView : ( ) => { cropperObj. ctitle = "图片处理" cropperObj. cVisible = true } , closeCropperView : ( data ) => { cropperObj. cVisible = false } , getNewImg : ( val : any) => { console. log ( "val" , val) ; pictureAnnotationFun ( val) }
} )
async function pictureAnnotationFun ( file : any) { const formData = new FormData ( ) formData. append ( "file" , file) ; formData. append ( 'id' , xx) ; formData. append ( 'bid' , xxx) ; try { const { code, msg, data } = await uploadimg ( formData) ; if ( code > 0 ) { cropperObj. cVisible = false ; ElMessage ( { showClose : true , message : '上传成功' , type : 'success' , } ) ; } else { ElMessage ( { showClose : true , message : '上传失败' + msg, type : 'error' , duration : 0 , } ) ; } } catch ( e) { ElMessage ( { showClose : true , message : '错误: ' + e. message, type : 'error' , duration : 0 , } ) ; }
}
封装组件代码
< template> < div class = "drawing-container" > < ! -- 绘图组件容器DOM -- > < div id= "tui-image-editor" > < / div> < el- button class = "save" type= "primary" @click= "save" > 保存< / el- button> < / div>
< / template>
< script setup lang= "ts" >
import 'tui-image-editor/dist/tui-image-editor.css' ;
import 'tui-color-picker/dist/tui-color-picker.css' ;
import ImageEditor from 'tui-image-editor' ;
import { nextTick, onMounted, ref } from 'vue' ;
const locale_zh = { ZoomIn : '放大' , ZoomOut : '缩小' , Hand : '手掌' , History : '历史' , Resize : '调整宽高' , Crop : '裁剪' , DeleteAll : '全部删除' , Delete : '删除' , Undo : '撤销' , Redo : '反撤销' , Reset : '重置' , Flip : '镜像' , Rotate : '旋转' , Draw : '画' , Shape : '形状标注' , Icon : '图标标注' , Text : '文字标注' , Mask : '遮罩' , Filter : '滤镜' , Bold : '加粗' , Italic : '斜体' , Underline : '下划线' , Left : '左对齐' , Center : '居中' , Right : '右对齐' , Color : '颜色' , 'Text size' : '字体大小' , Custom : '自定义' , Square : '正方形' , Apply : '应用' , Cancel : '取消' , 'Flip X' : 'X 轴' , 'Flip Y' : 'Y 轴' , Range : '区间' , Stroke : '描边' , Fill : '填充' , Circle : '圆' , Triangle : '三角' , Rectangle : '矩形' , Free : '曲线' , Straight : '直线' , Arrow : '箭头' , 'Arrow-2' : '箭头2' , 'Arrow-3' : '箭头3' , 'Star-1' : '星星1' , 'Star-2' : '星星2' , Polygon : '多边形' , Location : '定位' , Heart : '心形' , Bubble : '气泡' , 'Custom icon' : '自定义图标' , 'Load Mask Image' : '加载蒙层图片' , Grayscale : '灰度' , Blur : '模糊' , Sharpen : '锐化' , Emboss : '浮雕' , 'Remove White' : '除去白色' , Distance : '距离' , Brightness : '亮度' , Noise : '噪音' , 'Color Filter' : '彩色滤镜' , Sepia : '棕色' , Sepia2 : '棕色2' , Invert : '负片' , Pixelate : '像素化' , Threshold : '阈值' , Tint : '色调' , Multiply : '正片叠底' , Blend : '混合色' , Width : '宽度' , Height : '高度' , 'Lock Aspect Ratio' : '锁定宽高比例' ,
} ;
const customTheme = { 'common.bi.image' : '' , 'common.bisize.width' : '0px' , 'common.bisize.height' : '0px' , 'common.backgroundImage' : 'none' , 'common.backgroundColor' : '#f3f4f6' , 'common.border' : '1px solid #333' , 'header.backgroundImage' : 'none' , 'header.backgroundColor' : '#f3f4f6' , 'header.border' : '0px' , 'loadButton.backgroundColor' : '#fff' , 'loadButton.border' : '1px solid #ddd' , 'loadButton.color' : '#222' , 'loadButton.fontFamily' : 'NotoSans, sans-serif' , 'loadButton.fontSize' : '12px' , 'loadButton.display' : 'none' , 'downloadButton.backgroundColor' : '#fdba3b' , 'downloadButton.border' : '1px solid #fdba3b' , 'downloadButton.color' : '#fff' , 'downloadButton.fontFamily' : 'NotoSans, sans-serif' , 'downloadButton.fontSize' : '12px' , 'downloadButton.display' : 'none' , 'menu.normalIcon.color' : '#8a8a8a' , 'menu.activeIcon.color' : '#555555' , 'menu.disabledIcon.color' : '#ccc' , '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.backgroundColor' : '#1e1e1e' , 'submenu.partition.color' : '#858585' , 'submenu.normalLabel.color' : '#858585' , 'submenu.normalLabel.fontWeight' : 'lighter' , 'submenu.activeLabel.color' : '#fff' , 'submenu.activeLabel.fontWeight' : 'lighter' , 'checkbox.border' : '1px solid #ccc' , 'checkbox.backgroundColor' : '#fff' , '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.button.border' : '1px solid #1e1e1e' , 'colorpicker.title.color' : '#fff' ,
} ;
const props = defineProps ( { dialogVisible : { type : Boolean, default : ( ) => { return false ; } , } , title : { type : String, default : '' , } , imgUrl : { type : String, default : '' , } ,
} ) ;
const emit = defineEmits ( [ 'closeCropperDialog' , 'getNewImg' ] ) ;
const instance = ref< any> ( null ) ; onMounted ( ( ) => { nextTick ( ( ) => { init ( ) ; } ) ;
} ) ;
const closeDialog = ( ) => { emit ( 'closeCropperDialog' ) ;
} ; const init = ( ) => { instance. value = new ImageEditor ( document. querySelector ( '#tui-image-editor' ) , { includeUI : { loadImage : { path : props. imgUrl, name : 'image' , } , menu : [ 'resize' , 'crop' , 'rotate' , 'draw' , 'shape' , 'icon' , 'text' , 'filter' ] , initMenu : 'draw' , menuBarPosition : 'bottom' , locale : locale_zh, theme : customTheme, } , cssMaxWidth : 400 , cssMaxHeight : 500 , } ) ; document. getElementsByClassName ( 'tui-image-editor-main' ) [ 0 ] . style. top = '45px' ; document. getElementsByClassName ( 'tie-btn-reset tui-image-editor-item help' ) [ 0 ] . style. display = 'none' ;
} ;
const save = async ( ) => { const base64String = instance. value. toDataURL ( ) ; const data = window. atob ( base64String. split ( ',' ) [ 1 ] ) ; const ia = new Uint8Array ( data. length) ; for ( let i = 0 ; i < data. length; i++ ) { ia[ i] = data. charCodeAt ( i) ; } const file = new File ( [ ia] , "打标.png" , { type : "image/png" } ) ; emit ( 'getNewImg' , file) ; } ;
< / script> < style lang= "scss" scoped>
. drawing- container { width : 100 % ; height : 80vh; position : relative; . save { position : absolute; right : 50px; top : 15px; }
}
< / style>