|
|
<template> <div class="mark-handle"> <div v-if="isBookShelf && currentMarkData && currentMarkData.shelfId !== undefined" class="mark-img"> <canvas :id="'canvas' + currentMarkData.shelfId" :width="width" :height="height" /> </div> <div v-else-if="!isBookShelf && currentMarkData && currentMarkData.id !== undefined" class="mark-img"> <canvas :id="'canvas' + currentMarkData.id" :width="width" :height="height" /> </div> <div class="mark-right"> <ul v-if="!isBookShelf" class="mark-info"> <li> <p>所属机构</p> <span>{{ user.fonds.fondsName }}</span> </li> <li> <p>所属楼层</p> <span>{{ currentMarkData && currentMarkData.floorName }}</span> </li> <li> <p>书架</p> <span>{{ currentMarkData && currentMarkData.booksheflCount }}</span> </li> <li><span :class="['row-state', currentMarkData && currentMarkData.signPoint ? 'end-state' : 'cancel-state' ]">{{ currentMarkData && currentMarkData.signPoint ? '已标注': '未标注' }}</span></li> </ul> <ul v-else class="mark-info"> <li> <p>所属机构</p> <span>{{ user.fonds.fondsName }}</span> </li> <li> <p>所属楼层</p> <span>{{ currentMarkData && currentMarkData.floorName }}</span> </li> <li> <p>所属区域</p> <span>{{ currentMarkData && currentMarkData.regionName }}</span> </li> <li> <p>单/双面</p> <span>{{ currentMarkData && currentMarkData.rowType === 1 ? '单面' :'双面' }}</span> </li> <li> <p>书架规格</p> <span>{{ currentMarkData && currentMarkData.shelfFloor+ ' X ' + currentMarkData.shelfShelf }}</span> </li> <li><span :class="['row-state', true ? 'end-state' : 'cancel-state' ]">{{ true ? '已标注': '未标注' }}</span></li> </ul> <div class="mark-button"> <el-button type="primary" :disabled="!isDrawing" @click="clean"><i class="iconfont icon-shanchu" />清空</el-button> <el-button type="primary" :disabled="isDrawing" @click="drawPolygon"><i class="el-icon-edit" style="font-weight: bold; padding-right: 4px; font-size: 16px;" />标注</el-button> <el-button type="primary" :disabled="!isDrawing" @click="submitDraw"><i class="el-icon-folder-checked" style="font-weight: bold; padding-right: 4px; font-size: 16px;" />保存</el-button> </div> </div> <img id="expImg" :src="imageUrl"> </div> </template>
<script> import { saveLibraryRegionSignPoint } from '@/api/area/index' import { saveBookShelfSignPoint } from '@/api/shelf/index' import { fabric } from 'fabric' import { mapGetters } from 'vuex' export default { name: 'Mark', props: { currentMarkData: { type: Object, require: true, default: function() { return {} } }, imageUrl: { type: String, default: '' }, isBookShelf: { type: Boolean, default: false } }, data() { return { bgImgFlag: true, bgImgSrc: this.imageUrl, width: 900, height: 600, canvas: {}, mouseFrom: {}, mouseTo: {}, drawWidth: 2, // 笔触宽度
drawingObject: null, // 当前绘制对象
moveCount: 1, // 绘制移动计数器
doDrawing: false, // 绘制状态
// polygon 相关参数
polygonMode: false, pointArray: [], lineArray: [], savePointsGroup: [], activeShape: false, activeLine: '', line: {}, deleteIconURL: require('@/assets/images/closed.png'), drawinfo: null, isDrawing: false } }, computed: { ...mapGetters([ 'user', 'baseApi' ]) }, watch: { width() { this.canvas.setWidth(this.width) }, height() { this.canvas.setHeight(this.height) }, currentMarkData: { handler(newVal, oldVal) { // 检查 newVal 是否为 null 或 undefined
if (!newVal) { console.log('newVal is null or undefined') return }
// 提取公共逻辑
const handleIdChange = (newId, oldId, idKey) => { if (newId !== oldId) { console.log('id has changed') console.log('handler') this.canvas.clear() this.canvas.dispose() this.drawinfo = newVal.signPoint ? JSON.parse(newVal.signPoint) : null this.$nextTick(() => { this.$refs.markRefs.initCanvas() }) } }
if (this.isBookShelf && oldVal && oldVal.shelfId !== undefined) { handleIdChange(newVal.shelfId, oldVal.shelfId, 'shelfId') } else if (!this.isBookShelf && oldVal && oldVal.id !== undefined) { handleIdChange(newVal.id, oldVal.id, 'id') } }, deep: true },
imageUrl(newVal, oldVal) { if (newVal !== oldVal) { console.log('imageUrl') } } }, mounted() { // this.$nextTick(() => {
// console.log('mounted')
// this.drawinfo = this.currentMarkData && this.currentMarkData.signPoint ? JSON.parse(this.currentMarkData.signPoint) : null
// this.initCanvas()
// })
}, beforeDestroy() { if (this.canvas) { this.canvas.clear() this.canvas.dispose() } }, methods: { initCanvas() { // 检查 currentMarkData 是否存在
if (!this.currentMarkData) { console.error('currentMarkData is null or undefined') return }
const canvasId = `canvas${this.isBookShelf ? this.currentMarkData.shelfId : this.currentMarkData.id}`
// 初始化 fabric.js 画布
this.canvas = new fabric.Canvas(canvasId, { skipTargetFind: false, selectable: false, selection: false }) this.canvas.selectionColor = 'rgba(0,0,0,0.05)' this.canvas.on('mouse:down', this.mousedown) this.canvas.on('mouse:move', this.mousemove) this.canvas.on('object:moving', this.objectMoving) console.log('this.drawinfo', this.drawinfo) console.log('this.canvas', this.canvas) if (this.drawinfo) { console.log('444') this.loadDraw() } else { console.log('555') this.isDrawing = false this.bgImgFlag = true const imgElement = document.getElementById('expImg') imgElement.src = this.imageUrl this.loadExpImg() } }, // 从已渲染的DOM元素加载图片至canvas
loadExpImg() { console.log('loadExpImg') const imgElement = document.getElementById('expImg') const newWidth = this.canvas.width console.log('newWidth', newWidth) imgElement.onload = () => { // console.log('imgElement.src', imgElement.src)
const newHeight = newWidth / (imgElement.width / imgElement.height) new fabric.Image.fromURL( imgElement.src, (img) => { img.set( { originX: 'center', originY: 'center', scaleX: newWidth / imgElement.width, scaleY: newHeight / imgElement.height }, { crossOrigin: 'anonymous' } ) img.on('scaling', (e) => { // 拉伸事件
const h = img.scaleY const w = img.scaleX if (h !== w || w === h) { // 判断缩放值相等或不相等后执行图片等比缩放
if (e.e.movementY === -1 || e.e.movementY === 1) { img.scale(h) // 缩放
} else { img.scale(w) } } }) img.setCoords() img.centeredScaling = true img.centerTransform = true this.canvas.add(img) this.canvas.centerObject(img) this.canvas.renderAll() }, { selectable: true, hasControls: true, lockRotation: true, // 禁止旋转
centeredScaling: true, zIndex: -99, isBgImg: true } ) } }, // 对象移动时的事件处理
objectMoving(e) { const target = e.target if (target.type === 'polygon') { target.setCoords() // 更新params.pointInfo以确保点信息是最新的
// const points = target.points.map(p => ({
// x: p.x + target.left,
// y: p.y + target.top
// }));
// const index = params.pointInfo.findIndex(info => info.pointInfo[0].x === target.points[0].x && info.pointInfo[0].y === target.points[0].y);
// if (index !== -1) {
// params.pointInfo[index] = { pointInfo: points };
// } else {
// params.pointInfo.push({ pointInfo: points });
// }
} }, submitDraw() { const saveCanvasData = { pointInfo: [], imgInfo: '' // img: ''
} this.canvas .toJSON(['isBgImg']) .objects.forEach((item) => { const element = { pointInfo: '' } if (item?.points) { element.pointInfo = item.points saveCanvasData.pointInfo.push(element) } if (item.isBgImg) { saveCanvasData.imgInfo = item // params.img = item.src
delete saveCanvasData.imgInfo.src } }) console.log('saveCanvasData', saveCanvasData)
if (this.isBookShelf) { const params = { 'id': this.currentMarkData.shelfId, 'signPoint': JSON.stringify(saveCanvasData) } saveBookShelfSignPoint(params).then(res => { console.log(res) if (res) { this.$message({ message: '当前书架标注成功', type: 'success', offset: 8 }) this.$emit('handleCloseDialog') } }).catch(err => { console.log(err) }) } else { const params = { 'id': this.currentMarkData.id, 'signPoint': JSON.stringify(saveCanvasData) } saveLibraryRegionSignPoint(params).then(res => { if (res) { this.$message({ message: '当前区域标注成功', type: 'success', offset: 8 }) this.$emit('handleCloseDialog') } }).catch(err => { console.log(err) }) } }, // 鼠标按下时触发
mousedown(e) { if (e === undefined) return // 记录鼠标按下时的坐标
const xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY) this.mouseFrom.x = xy.x this.mouseFrom.y = xy.y this.doDrawing = true
this.canvas.skipTargetFind = false try { console.log('this.pointArray', this.pointArray) // 判断是否闭合多边形,点击红点时闭合多边形
if (this.pointArray.length > 1) { // e.target.id == this.pointArray[0].id 表示点击了初始红点
if (e.target && e.target.id === this.pointArray[0].id) { this.generatePolygon() console.log('画完') this.isDrawing = true return } else { console.log('未画完') this.isDrawing = false } } // 未点击红点即闭合点则继续作画
if (this.polygonMode) { console.log('作画') this.addPoint(e) } } catch (error) { console.log(error) } }, // 鼠标松开执行
mouseup(e) { if (e === undefined) return const xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY) this.mouseTo.x = xy.x this.mouseTo.y = xy.y this.drawingObject = null this.moveCount = 1
// 如果当前操作的是多边形,更新坐标
if (this.activeShape && this.activeShape.type === 'polygon') { this.activeShape.setCoords() } }, // 鼠标滚轮放大缩小
mouse(e) { if (undefined === e) return let zoom = (e.e.deltaY > 0 ? -0.1 : 0.1) + this.canvas.getZoom() zoom = Math.max(0.8, zoom) // 最小为原来的1/10
zoom = Math.min(3, zoom) // 最大是原来的3倍
const zoomPoint = new fabric.Point(e.e.pageX, e.e.pageY) this.canvas.zoomToPoint(zoomPoint, zoom) }, // 鼠标移动过程中已经完成了绘制
mousemove(e) { if (e === undefined) return if (this.moveCount % 2 === 0 && !this.doDrawing) { // 减少绘制频率
return } this.moveCount++ const xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY) this.mouseTo.x = xy.x this.mouseTo.y = xy.y if (this.activeLine && this.activeLine.class === 'line') { const pointer = this.canvas.getPointer(e.e) this.activeLine.set({ x2: pointer.x, y2: pointer.y }) const points = this.activeShape.get('points') points.push({ x: pointer.x, y: pointer.y, zIndex: 1 }) this.activeShape.set({ points: points }) this.canvas.renderAll() } }, // 从画布中删除当前选中的对象
deleteObj() { const activeObjects = this.canvas.getActiveObjects() if (activeObjects.length > 0) { activeObjects.forEach((item) => { this.canvas.remove(item) }) this.canvas.renderAll() } }, // 转换鼠标坐标
transformMouse(mouseX, mouseY) { return { x: mouseX, y: mouseY } }, // 绘制多边形开始
drawPolygon() { console.log(1111) if (this.bgImgFlag) { this.canvas.getObjects().forEach((obj) => { if (obj.isBgImg) { obj.hasControls = false obj.selectable = false obj.evented = false } }) this.bgImgFlag = false this.canvas.renderAll() } this.polygonMode = true this.pointArray = [] // 顶点集合
this.lineArray = [] // 线集合
this.canvas.isDrawingMode = false }, addPoint(e) { const random = Math.floor(Math.random() * 10000) const id = new Date().getTime() + random const circle = new fabric.Circle({ radius: 5, fill: '#ffffff', stroke: '#333333', strokeWidth: 0.5, left: (e.pointer?.x || e.e.layerX) / this.canvas.getZoom(), top: (e.pointer?.y || e.e.layerY) / this.canvas.getZoom(), selectable: false, hasBorders: false, hasControls: false, originX: 'center', originY: 'center', id, objectCaching: false }) if (this.pointArray.length === 0) { circle.set({ fill: 'rgba(196,43, 1, 1)' }) } const points = [ (e.pointer?.x || e.e.layerX) / this.canvas.getZoom(), (e.pointer?.y || e.e.layerY) / this.canvas.getZoom(), (e.pointer?.x || e.e.layerX) / this.canvas.getZoom(), (e.pointer?.y || e.e.layerY) / this.canvas.getZoom() ] const line = new fabric.Line(points, { strokeWidth: 2, fill: 'rgba(196,43, 1, 1)', stroke: 'rgba(196,43, 1, 1)', class: 'line', originX: 'center', originY: 'center', selectable: false, hasBorders: false, hasControls: false, evented: false, objectCaching: false }) if (this.activeShape) { const pos = this.canvas.getPointer(e.e) const points = this.activeShape.get('points') points.push({ x: pos.x, y: pos.y }) const polygon = new fabric.Polygon(points, { stroke: '#333333', strokeWidth: 1, fill: 'rgba(196,43, 1, 0.3)', opacity: 0.3, selectable: false, hasBorders: false, hasControls: false, evented: false, objectCaching: false }) this.canvas.remove(this.activeShape) this.canvas.add(polygon) this.activeShape = polygon this.canvas.renderAll() } else { const polyPoint = [ { x: (e.pointer?.x || e.e.layerX) / this.canvas.getZoom(), y: (e.pointer?.y || e.e.layerY) / this.canvas.getZoom() } ] const polygon = new fabric.Polygon(polyPoint, { stroke: '#333333', strokeWidth: 1, fill: '#cccccc', opacity: 0.3, selectable: false, hasBorders: false, hasControls: false, evented: false, objectCaching: false }) this.activeShape = polygon this.canvas.add(polygon) } this.activeLine = line this.pointArray.push(circle) this.lineArray.push(line) this.canvas.add(line) this.canvas.add(circle) }, generatePolygon() { const points = [] this.pointArray.forEach((point) => { points.push({ x: point.left, y: point.top }) this.canvas.remove(point) }) this.lineArray.forEach((line) => { this.canvas.remove(line) }) if (this.activeShape) { this.canvas.remove(this.activeShape) } if (this.activeLine) { this.canvas.remove(this.activeLine) }
const polygon = new fabric.Polygon(points, { stroke: 'rgba(196,43, 1, 1)', strokeWidth: this.drawWidth, fill: 'rgba(196,43, 1, 0.3)', opacity: 1, selectable: false, hasBorders: false, hasControls: false, name: '测试架号name1' })
let maxIndex = 0 this.canvas._objects.forEach((obj) => { if (obj.index > maxIndex) maxIndex = obj.index }) polygon.index = maxIndex + 1 this.canvas.add(polygon)
this.activeLine = null this.activeShape = null this.polygonMode = false this.doDrawing = false fabric.Image.fromURL(this.deleteIconURL, this.deletecallback) }, // 从画布中删除当前选中的对象
deleteObject() { const activeObject = this.canvas.getActiveObject() if (activeObject) { this.canvas._objects.forEach(item => { if (item.index === activeObject.index) { this.canvas.remove(item) } })
const onlyBgImgItems = this.canvas._objects.filter(item => item.isBgImg) const allItemsAreBgImg = this.canvas._objects.length / 2 === onlyBgImgItems.length console.log('allItemsAreBgImg', allItemsAreBgImg)
if (allItemsAreBgImg) { this.canvas.clear() this.bgImgFlag = true const imgElement = document.getElementById('expImg') imgElement.src = this.imageUrl this.isDrawing = false this.bgImgFlag = true this.loadExpImg() }
this.canvas.remove(activeObject) this.canvas.renderAll() } else { console.log('this.canvas._objects.length', this.canvas._objects.length) } }, // 渲染删除按钮
async deletecallback(img) { const self = this let max = 0 for (let i = 1; i < this.canvas._objects.length; i++) { if (this.canvas._objects[i].index > max) max = this.canvas._objects[i].index } img.index = max const oImg = await img.set({ name: 'delete-btn', left: this.pointArray[0].left - 20, top: this.pointArray[0].top - 20, width: 40, height: 40, angle: 0 }).scale(0.8) this.canvas.add(oImg).renderAll() this.canvas.setActiveObject(oImg) oImg.on('mousedown', function() { self.deleteObject() }) }, // 回显详情信息
loadDraw() { const self = this if (this.currentMarkData.id === '') return const pointGroup = self.drawinfo.pointInfo const imgInfo = self.drawinfo.imgInfo imgInfo.src = self.imageUrl this.isDrawing = true
// 加载底图
fabric.util.enlivenObjects([imgInfo], objects => { objects.forEach(o => { o.selectable = false o.hasControls = false o.centeredScaling = false this.canvas.add(o) }) // 处理多边形绘制回显操作
pointGroup.forEach(async(item, index) => { if (item.pointInfo !== '') { const polygon = new fabric.Polygon(item.pointInfo, { name: item.name, stroke: 'rgba(196,43, 1, 1)', strokeWidth: self.drawWidth, fill: 'rgba(196,43, 1, 0.3)', opacity: 1, selectable: false, hasBorders: false, hasControls: false, originX: 'left', // 设置原点为左上角
originY: 'top' // 设置原点为左上角
}) polygon.index = index
self.canvas.add(polygon) self.activeLine = null self.activeShape = null self.polygonMode = false self.doDrawing = false
polygon.on('mousedown', function(e) { console.log('Rect222' + (index + 1) + ' clicked', e) console.log('e.target.name222', e.target.name) })
polygon.on('mouseover', function(e) { console.log('e22', e) console.log('e.target22', e.target) console.log('e.target.name22', e.target.name) // var delta = new fabric.Point(e.e.movementX, e.e.movementY)
// if(e.target&&e.target.name){
// // that.showDialog(e.target.name)
// }
this.set({ opacity: 0.3, hoverCursor: 'pointer' }) this.canvas.renderAll() })
// 监听鼠标移出事件
polygon.on('mouseout', function() { this.set({ opacity: 1 }) this.canvas.renderAll() })
fabric.Image.fromURL(self.deleteIconURL, async img => { const _self = this img.index = index const oImg = await img.set({ name: 'delete-btn', left: item.pointInfo[0].x - 20, top: item.pointInfo[0].y - 20, width: 40, height: 40, angle: 0 }).scale(0.8) this.canvas.add(oImg) oImg.on('mousedown', function() { _self.deleteObject() }) }) } }) }) this.canvas.renderAll() }, // 清除画布
clean() { this.$confirm('确认清空标注吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { this.canvas.clear() this.isDrawing = false this.bgImgFlag = true const imgElement = document.getElementById('expImg') imgElement.src = this.imageUrl this.loadExpImg() }).catch(() => { console.log('取消清空标注') }) } } } </script>
<style lang="scss" scoped> #expImg{ display: none; } </style>
|