图书馆综合管理系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

742 lines
24 KiB

<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>