效果图
下载包
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>