Kaynağa Gözat

feat: 数据标注功能,包含表格操作、标签txt上传至img相对位置和标注页

TODO: 标签txt内容格式需要确认,点击表格图片响应还未修改(数据库有脏东西,页面数据加载不出来)
Suuuuuukang 10 ay önce
ebeveyn
işleme
f6fb42918c

+ 6 - 0
src/api/modules/upload.ts

@@ -11,6 +11,12 @@ export const uploadImg = (params: FormData) => {
   return http.post<any>('/common/upload', params, { cancel: false })
 }
 
+// 图片上传
+export const uploadPure = (params: FormData) => {
+  // return http.post<Upload.ResFileUrl>(PORT1 + `/file/upload/img`, params, { cancel: false })
+  return http.post<any>('/common/uploadPure', params, { cancel: false })
+}
+
 // 视频上传
 export const uploadVideo = (params: FormData) => {
   // return http.post<any>(PORT1 + `/file/upload/video`, params, { cancel: false })

+ 87 - 23
src/views/demo/components/img-detect.vue

@@ -1,6 +1,10 @@
 <template>
   <el-dialog class="modal-canvas" v-model="visible" title="标注" width="980px" height="700px">
-    <ImgMaker ref="imgMaker" v-if="visible" :src="cover" :area="area" :width="width" :height="height"></ImgMaker>
+    <ImgMaker ref="imgMaker" v-if="visible" :src="cover" :area="area" :width="width" :height="height" :class-def="classDef" :json-data="jsonData"></ImgMaker>
+    <div style="margin-top: 10px; text-align: center">
+      标签
+      <el-button v-for="(item, index) in classes" :key="index" type="primary" @click="onClassChange(item)" :disabled="classDef.label===item.label">{{ item.name }}</el-button>
+    </div>
     <div style=" margin-top: 10px;text-align: center">
       <!-- <el-space> -->
       <el-button type="primary" @click="onClearLast">撤销最后一次的操作</el-button>
@@ -12,7 +16,7 @@
   </el-dialog>
 </template>
 <script lang="ts" setup>
-import { reactive, ref, toRefs, defineProps, watchEffect } from 'vue'
+import {reactive, ref, toRefs, defineProps, watchEffect, watch} from 'vue'
 import ImgMaker from './img-maker.vue'
 const props = defineProps({
   area: {
@@ -30,19 +34,41 @@ const props = defineProps({
   height: {
     type: Number,
     default: 1080
+  },
+  classes: {
+    type: Array
+  },
+  jsonData: {
+    type: String,
+    default: ''
   }
 })
 const emit = defineEmits(['success'])
 const imgMaker = ref()
 const state = reactive({
   visible: false,
-  cover: ''
+  cover: '',
+  classDef: props.classes && props.classes.length > 0 ? props.classes[0] : { name: 'default', color: '#FF00FF', classValue: 0 }
 })
-const { visible, cover } = toRefs(state)
+
+watch(
+  () => props.classes,
+  value => {
+    state.classDef = (value && value.length > 0) ? value[0] : { name: 'default', color: '#FF00FF', classValue: 0 }
+    // console.log(state.classDef)
+  }
+)
+
+const { visible, cover, classDef } = toRefs(state)
 watchEffect(() => {
   state.cover = props.img
 })
 
+const onClassChange = item => {
+  // console.log('change class choice', item)
+  state.classDef = item
+}
+
 const onClearAll = () => {
   imgMaker.value.clearAll()
 }
@@ -58,31 +84,69 @@ const onSubmit = () => {
   let points = imgMaker.value.getData()
   // console.log('points', points)
 
-  let datas = []
-  if (points.length > 0) {
-    datas = points.map(point => {
-      if (point.br) {
-        let w = point.tr.x - point.tl.x
-        let h = point.bl.y - point.tl.y
+  let emitData = {
+    data: '',
+    jsonData: points[0],
+    url: points[1]
+  }
 
-        const algoData = `${point.tl.x / 1920};${point.tl.y / 1080};${w / 1920};${h / 1080}`
-        // return `${point.tl.x};${point.tl.y};${point.tr.x};${point.tr.y};${point.br.x};${point.br.y};${point.bl.x};${point.bl.y}`
-        return algoData
-      } else {
-        let coors = ''
-        Object.keys(point).forEach(item => {
-          coors += `${point[item].x};${point[item].y};`
-        })
-        coors = coors.slice(0, -1)
-        return coors
+  for (let i = 2; i < points.length; i++) {
+    let label = points[i]['label']
+    // console.log(label)
+    emitData.data += label + ', '
+    let index = -1, yMin = 10000000, xMax = -1
+    for (let j = 0; j < points[i]['nodes'].length; j++) {
+      if (points[i]['nodes'][j].y < yMin) {
+        index = j
+        yMin = points[i]['nodes'][j].y
+        xMax = points[i]['nodes'][j].x
+      } else if (points[i]['nodes'][j].y === yMin && points[i]['nodes'][j].x > xMax) {
+        index = j
+        xMax = points[i]['nodes'][j].x
       }
-      // return `${point.tl.x};${point.tl.y};${point.tr.x};${point.tr.y}`
-    })
+    }
+
+    for (let j = 0; j < points[i]['nodes'].length; j++) {
+      let ptr = (j + index) % points[i]['nodes'].length
+      emitData.data += points[i]['nodes'][ptr].x + ', ' + points[i]['nodes'][ptr].y
+      if (j + 1 !== points[i]['nodes'].length) {
+        emitData.data += ', '
+      }
+    }
+
+    if (i + 1 !== points.length) {
+      emitData.data += '; '
+    }
+    // if ()
+    // datas += points[i][]
   }
+
+  // if (points.length > 0) {
+  //   datas = points.map(point => {
+  //     if (point.br) {
+  //       let w = point.tr.x - point.tl.x
+  //       let h = point.bl.y - point.tl.y
+  //
+  //       const algoData = `${point.tl.x / 1920};${point.tl.y / 1080};${w / 1920};${h / 1080}`
+  //       // return `${point.tl.x};${point.tl.y};${point.tr.x};${point.tr.y};${point.br.x};${point.br.y};${point.bl.x};${point.bl.y}`
+  //       return algoData
+  //     } else {
+  //       console.log('all else', point)
+  //       let coors = ''
+  //       Object.keys(point).forEach(item => {
+  //         coors += `${point[item].x};${point[item].y};`
+  //       })
+  //       coors = coors.slice(0, -1)
+  //       return coors
+  //     }
+  //     // return `${point.tl.x};${point.tl.y};${point.tr.x};${point.tr.y}`
+  //   })
+  // }
   // console.log('datas', datas)
   // 归一化
+  // datas.push(points[0])
 
-  emit('success', datas)
+  emit('success', emitData)
   onClearAll()
   state.visible = false
 }

+ 263 - 32
src/views/demo/components/img-maker.vue

@@ -1,7 +1,22 @@
 <template>
   <div>
     <div style="margin-bottom: 10px">
-      <el-button plain type="primary" class="shape-border" @click="drawTypeChange('rectangle')">矩形</el-button>
+      <el-button plain type="primary" class="shape-border" @click="drawTypeChange('rectangle')" :disabled="!state.dragMode">标注模式</el-button>
+      <el-button plain type="primary" class="shape-border" @click="enableDragMode" :disabled="state.dragMode">移动图片</el-button>
+
+      <el-button plain type="primary" style="margin-left: 30px" class="shape-border" @click="selectLastObject" :disabled="!!state.activeTarget">选择最后一个标注(w)</el-button>
+      <el-button
+        plain
+        type="primary"
+        class="shape-border"
+        @click="rotate(8)"
+        :disabled="!state.activeTarget"
+      >
+        顺时针旋转(r)
+      </el-button>
+      <el-button plain type="primary" class="shape-border" @click="rotate(-8)" :disabled="!state.activeTarget">逆时针旋转(e)</el-button>
+      <el-button plain type="primary" style="margin-left: 30px" class="shape-border" @click="changeZoom(1.1)">放大图片</el-button>
+      <el-button plain type="primary" class="shape-border" @click="changeZoom(0.9)">缩小图片</el-button>
       <!-- <el-button plain type="primary" class="shape-border" @click="drawPolygon('polygon')">多边形</el-button> -->
     </div>
     <canvas id="canvas" :width="cWidth" :height="cHeight"></canvas>
@@ -10,6 +25,7 @@
 <script lang="ts" setup>
 import { fabric } from 'fabric'
 import { reactive, watch, onMounted } from 'vue'
+import { ElMessage } from 'element-plus'
 // import { sortPoints } from '@/utils/fabric'
 
 const props = defineProps({
@@ -36,11 +52,18 @@ const props = defineProps({
   cHeight: {
     type: Number,
     default: 540
+  },
+  classDef: {
+    type: Object
+  },
+  jsonData: {
+    type: String,
+    default: ''
   }
 })
 const state = reactive({
   loading: true,
-  radio: 0.5,
+  radio: 1,
   realRadioX: 0.5,
   realRadioY: 0.5,
   imgPoint: { x: 0, y: 0 },
@@ -48,11 +71,16 @@ const state = reactive({
   canvas: {} as any,
   mouseFrom: { x: 0, y: 0 } as canvasPoint,
   mouseTo: { x: 0, y: 0 } as canvasPoint,
+  dragMode: false,
+  isDragging: false,
+  lastPosX: 0,
+  lastPosY: 0,
   // drawType: 'rectangle' as string, //当前绘制图像的种类
   drawType: null as any, //当前绘制图像的种类
-  drawWidth: 1, //笔触宽度
+  drawWidth: 5, //笔触宽度
   color: '#E34F51', //画笔颜色
   drawingObject: null as any, //当前绘制对象
+  activeTarget: null as any, // 当前点击活跃对象
   moveCount: 1, //绘制移动计数器
   doDrawing: false as boolean, // 绘制状态
   rectPath: '' as string, //矩形绘制路径
@@ -64,9 +92,17 @@ const state = reactive({
   activeLine: '' as any,
   line: {} as canvasPoint
 })
+watch(
+  () => props.classDef,
+  value => {
+    // console.log(value)
+    state.color = value && value.color ? value.color : '#E34F51'
+  }
+)
 watch(
   () => state.drawType,
   value => {
+    // console.log(value)
     state.canvas.selection = !value
   }
 )
@@ -83,7 +119,39 @@ watch(
   }
 )
 
+const enableDragMode = () => {
+  state.dragMode = true
+  state.activeTarget = null
+}
+
+const changeZoom = multi => {
+  // 放大
+  state.radio = state.canvas.getZoom() * multi
+  state.canvas.setZoom(state.radio)
+  // state.canvas.setViewport(state.canvas.getCenter()); // 更新视口
+  state.canvas.renderAll()
+}
+
 const loadInit = () => {
+  drawTypeChange('rectangle')
+  state.color = props.classDef && props.classDef.color ? props.classDef.color : '#E34F51'
+  document.addEventListener('keydown', function (e) {
+    if (e.key === 'w' || e.key === 'W') {
+      selectLastObject()
+      return
+    }
+    if (state.activeTarget) {
+      if (e.key === 'r' || e.key === 'R') {
+        // 旋转矩形对象
+        state.activeTarget.rotate(state.activeTarget.angle + 5)
+        state.canvas.renderAll()
+      } else if (e.key === 'e' || e.key === 'E') {
+        // 旋转矩形对象
+        state.activeTarget.rotate(state.activeTarget.angle - 5)
+        state.canvas.renderAll()
+      }
+    }
+  })
   if (props.src == '') {
     return
   }
@@ -93,6 +161,44 @@ const loadInit = () => {
   state.canvas.on('mouse:down', mousedown)
   state.canvas.on('mouse:move', mousemove)
   state.canvas.on('mouse:up', mouseup)
+  // // 添加鼠标滚轮缩放功能
+  // state.canvas.on('mouse:wheel', event => {
+  //   const delta = event.delta
+  //   const zoom = state.canvas.getZoom()
+  //   const point = new fabric.Point(event.x, event.y)
+  //
+  //   if (delta > 0) {
+  //     // 放大
+  //     state.canvas.zoomToPoint(point, zoom * 1.1)
+  //     // state.radio *= 1.1
+  //   } else {
+  //     // 缩小
+  //     state.canvas.zoomToPoint(point, zoom * 0.9)
+  //     // state.radio *= 0.9
+  //   }
+  //   state.canvas.renderAll()
+  //   event.e.preventDefault()
+  //   event.e.stopPropagation()
+  // })
+
+  // nice try!
+  // console.log(props.jsonData)
+  if (props.jsonData && props.jsonData.length > 0) {
+    // console.log('load')
+    state.canvas.loadFromJSON(props.jsonData, () => {
+      let objects = state.canvas.getObjects()
+      // console.log(objects)
+      objects[0].selectable = false
+      for (let i = 1; i < objects.length; i++) {
+        objects[i].selectable = true
+      }
+      state.canvas.renderAll()
+    })
+    // console.log(state.canvas)
+    state.loading = false
+    return
+  }
+
   let imgElement = new Image()
   imgElement.src = props.src
   imgElement.onload = () => {
@@ -110,16 +216,20 @@ const loadInit = () => {
 
     state.realPoint.x = Math.floor(props.width / 2)
     state.realPoint.y = Math.floor(props.height / 2)
-    let imgInstance = new fabric.Image(imgElement, {
-      selectable: false,
-      width: imgElement.width,
-      height: imgElement.height,
-      scaleX: state.radio,
-      scaleY: state.radio
-    })
-    state.canvas.add(imgInstance)
-    drawImage()
-    state.canvas.renderAll()
+
+    if (!(props.jsonData && props.jsonData.length > 0)) {
+      let imgInstance = new fabric.Image(imgElement, {
+        selectable: false,
+        width: imgElement.width,
+        height: imgElement.height,
+        scaleX: state.radio,
+        scaleY: state.radio
+      })
+      state.canvas.add(imgInstance)
+      drawImage()
+      state.canvas.renderAll()
+    }
+    state.radio = state.canvas.getZoom()
     state.loading = false
   }
 }
@@ -128,6 +238,7 @@ const drawImage = () => {
     clearAll()
     return
   }
+  // console.log(props.area)
   let points = props.area.split(',').map(item => {
     let areas = item.split(';')
     let data = areas.map((ars, index) => {
@@ -186,7 +297,27 @@ const drawImageObj = data => {
   canvasObject['points'] = points
   state.canvas.add(canvasObject)
 }
+
+const selectLastObject = () => {
+  let objects = state.canvas.getObjects()
+  state.activeTarget = objects[objects.length - 1]
+  state.activeTarget.set('hasRotatingPoint', true)
+  state.canvas.setActiveObject(state.activeTarget)
+  state.canvas.renderAll()
+}
+
+const rotate = val => {
+  if (!state.activeTarget) {
+    ElMessage.error('请先选择旋转对象!')
+  } else {
+    state.activeTarget.rotate(state.activeTarget.angle + val)
+    state.canvas.renderAll()
+  }
+}
+
 const drawTypeChange = e => {
+  state.dragMode = false
+  state.activeTarget = null
   state.drawType = e
   state.canvas.skipTargetFind = !!e
   if (e == 'pen') {
@@ -198,12 +329,36 @@ const drawTypeChange = e => {
 }
 // 鼠标按下时触发
 const mousedown = e => {
+  if (state.dragMode) {
+    state.isDragging = true
+    state.lastPosX = e.e.clientX - state.canvas.getElement().offsetLeft
+    state.lastPosY = e.e.clientY - state.canvas.getElement().offsetTop
+    return
+  }
+  // console.log(state.canvas.getObjects()[1])
+  // console.log(e)
+  const target = e.target
+  // console.log(target)
+  if (target) {
+    if (target.type === 'rect') {
+      state.activeTarget = target
+      state.activeTarget.set('hasRotatingPoint', true)
+      state.canvas.setActiveObject(state.activeTarget)
+      return
+    } else {
+      state.activeTarget = null
+      // state.canvas.discardActiveObject()
+    }
+  }
+  // console.log(state.lastPosX, state.lastPosY)
+  // console.log(state.canvas.viewportTransform)
   // 记录鼠标按下时的坐标
   let xy = e.pointer || transformMouse(e.e.offsetX, e.e.offsetY)
-
-  state.mouseFrom.x = xy.x
-  state.mouseFrom.y = xy.y
+  // console.log(xy)
+  state.mouseFrom.x = (xy.x - state.canvas.viewportTransform[4]) / state.radio
+  state.mouseFrom.y = (xy.y - state.canvas.viewportTransform[5]) / state.radio
   state.doDrawing = true
+  // console.log(state.drawType)
   // 绘制多边形
   if (state.drawType == 'polygon') {
     state.canvas.skipTargetFind = false
@@ -226,9 +381,19 @@ const mousedown = e => {
 }
 // 鼠标松开执行
 const mouseup = e => {
+  if (state.dragMode) {
+    state.isDragging = false
+    return
+  }
+
+  if (state.activeTarget) {
+    state.activeTarget.setCoords()
+    state.canvas.requestRenderAll()
+    return
+  }
   let xy = e.pointer || transformMouse(e.e.offsetX, e.e.offsetY)
-  state.mouseTo.x = xy.x
-  state.mouseTo.y = xy.y
+  state.mouseTo.x = (xy.x - state.canvas.viewportTransform[4]) / state.radio
+  state.mouseTo.y = (xy.y - state.canvas.viewportTransform[5]) / state.radio
   state.drawingObject = null
   state.moveCount = 1
   if (state.drawType != 'polygon' && state.drawType != 'line') {
@@ -242,6 +407,43 @@ const mouseup = e => {
 }
 //鼠标移动过程中已经完成了绘制
 const mousemove = e => {
+  if (state.dragMode) {
+    if (state.isDragging) {
+      const deltaMove = {
+        x: e.e.clientX - state.canvas.getElement().offsetLeft - state.lastPosX,
+        y: e.e.clientY - state.canvas.getElement().offsetTop - state.lastPosY
+      }
+      state.lastPosX = e.e.clientX - state.canvas.getElement().offsetLeft
+      state.lastPosY = e.e.clientY - state.canvas.getElement().offsetTop
+      state.canvas.relativePan(new fabric.Point(deltaMove.x, deltaMove.y))
+      // state.canvas.viewportTransform[4] += deltaMove.x
+      // state.canvas.viewportTransform[5] += deltaMove.y
+      // state.canvas.getObjects().forEach(obj => {
+      //   if (obj.type !== 'rect') {
+      //     return
+      //   }
+      //   obj.left = (obj.left - state.canvas.viewportTransform[4]) / state.radio;
+      //   obj.top = (obj.top - state.canvas.viewportTransform[5]) / state.radio;
+      //   obj.setCoords();
+      //   obj.setOptions({
+      //     selectable: true,
+      //     evented: true
+      //   });
+      // });
+      state.canvas.requestRenderAll();
+    }
+    return
+  }
+
+  if (state.activeTarget) {
+    // const pointer = state.canvas.getPointer(e.e)
+    // state.activeTarget.set({
+    //   left: pointer.x,
+    //   top: pointer.y
+    // });
+    state.canvas.requestRenderAll()
+    return
+  }
   if (state.moveCount % 2 && !state.doDrawing) {
     //减少绘制频率
     return
@@ -249,8 +451,8 @@ const mousemove = e => {
   state.moveCount++
   let xy = e.pointer || transformMouse(e.e.offsetX, e.e.offsetY)
   if (xy.y >= 0 && xy.x <= props.cWidth && xy.y >= 0 && xy.y <= props.cHeight) {
-    state.mouseTo.x = xy.x
-    state.mouseTo.y = xy.y
+    state.mouseTo.x = (xy.x - state.canvas.viewportTransform[4]) / state.radio
+    state.mouseTo.y = (xy.y - state.canvas.viewportTransform[5]) / state.radio
     // 矩形
     if (state.drawType == 'rectangle') {
       if (state.mouseFrom.x < state.mouseTo.x && state.mouseFrom.y < state.mouseTo.y) {
@@ -314,14 +516,31 @@ const drawing = () => {
     mouseFrom.y +
     ' z'
   state.rectPath = path
-  canvasObject = new fabric.Path(path, {
+  canvasObject = new fabric.Rect({
     left: left,
     top: top,
+    width: Math.abs(mouseTo.x - left),
+    height: Math.abs(mouseTo.y - top),
     stroke: state.color,
-    selectable: false,
     strokeWidth: state.drawWidth,
     fill: 'rgba(255, 255, 255, 0)',
-    hasControls: false
+    centeredRotation: true,
+    angle: 0,
+    label: props.classDef.label
+  })
+  // canvasObject = new fabric.Path(path, {
+  //   left: left,
+  //   top: top,
+  //   stroke: state.color,
+  //   selectable: false,
+  //   strokeWidth: state.drawWidth,
+  //   fill: 'rgba(255, 255, 255, 0)',
+  //   hasControls: false
+  // })
+  // console.log(canvasObject)
+  canvasObject.on('selected', event => {
+    // console.log("selected")
+    state.activeTarget = event.target
   })
   if (canvasObject) {
     state.canvas.add(canvasObject)
@@ -445,8 +664,11 @@ const generatePolygon = () => {
   // })
   // state.canvas.add(polygon)
   let points = [{}]
-  console.log('state.pointArray', state.pointArray)
-
+  // console.log('state.pointArray', state.pointArray)
+  for (let point in state.pointArray) {
+    point = state.pointArray[point]
+    // console.log(point.left, point.top)
+  }
   state.pointArray.map(point => {
     points.push({
       x: point.left,
@@ -535,10 +757,18 @@ const getData = () => {
   // if (state.lineArray.length > 0 && state.lineArray.length < 4) {
   //   clearPolygonLines()
   // }
+  datas.push(JSON.stringify(state.canvas.toJSON(['label'])))
+  datas.push(state.canvas.toDataURL('image/png'))
+
   state.canvas.getObjects().forEach((item, index) => {
+    // console.log(item, index)
     if (index > 0) {
       let aCoords = item.aCoords
-      let point = {}
+      let point = {
+        nodes: [],
+        angle: item.angle,
+        label: item.label
+      }
       if (item.points) {
         item.points.forEach((item, idx) => {
           // if (aCoords[marks[idx]]) {
@@ -558,17 +788,18 @@ const getData = () => {
             x: getPoint(aCoords[mark].x),
             y: getPoint(aCoords[mark].y)
           }
-          point[mark] = getRealPoint(poi)
+          point['nodes'].push(getRealPoint(poi))
         })
       }
-
-      if (item.points && item.points.length === 2) {
-        delete point['br']
-        delete point['bl']
-      }
+      // if (item.points && item.points.length === 2) {
+      //   delete point['br']
+      //   delete point['bl']
+      // }
       datas.push(point)
+      // console.log(point)
     }
   })
+  // console.log()
   return datas
 }
 // 获取归一化数据比例

+ 182 - 37
src/views/demo/data/index.vue

@@ -23,6 +23,7 @@
       <!-- 表格操作 -->
       <template #operation="scope">
         <!-- <el-button type="primary" link :icon="EditPen" v-auth="['demo:data:edit']" @click="openDialog(2, '数据标注', scope.row)"> 标注 </el-button> -->
+        <el-button type="primary" link :icon="EditPen" v-auth="['demo:data:edit']" @click="markImg(scope.row)"> 标注 </el-button>
         <el-button type="primary" link :icon="EditPen" v-auth="['demo:data:edit']" @click="openDialog(2, '数据编辑', scope.row)"> 编辑 </el-button>
         <el-button type="primary" link :icon="View" v-auth="['demo:data:query']" @click="openDialog(3, '数据查看', scope.row)"> 查看 </el-button>
         <el-button type="primary" link :icon="Delete" v-auth="['demo:data:remove']" @click="deleteData(scope.row)"> 删除 </el-button>
@@ -31,12 +32,22 @@
 
     <FormDialog ref="formDialogRef" />
     <ImportPicDataset ref="dialogRef" />
-    <ImgDetect ref="imgDetect" :img="cover" :area="area" :width="width" :height="height" @success="handleImgSuccess"></ImgDetect>
+    <ImgDetect
+      ref="imgDetect"
+      :img="cover"
+      :area="area"
+      :width="width"
+      :height="height"
+      @success="handleImgSuccess"
+      :classes="classes"
+      :json-data="jsonData"
+    >
+    </ImgDetect>
   </div>
 </template>
 
 <script setup lang="tsx" name="Data">
-import { ref, reactive, toRefs } from 'vue'
+import { ref, reactive, toRefs, onMounted } from 'vue'
 import { useHandleData } from '@/hooks/useHandleData'
 import { useDownload } from '@/hooks/useDownload'
 import { ElMessage, ElMessageBox } from 'element-plus'
@@ -46,7 +57,7 @@ import ImportPicDataset from '@/components/ImportPicDataset/index.vue'
 import { ProTableInstance, ColumnProps } from '@/components/ProTable/interface'
 import { Delete, EditPen, Download, Upload, View, CirclePlus } from '@element-plus/icons-vue'
 // import { fabric } from 'fabric'
-import { useDrawArea } from '@/utils/fabric'
+// import { useDrawArea } from '@/utils/fabric'
 import ImgDetect from '../components/img-detect.vue'
 import {
   listDataApi,
@@ -58,14 +69,43 @@ import {
   exportDataApi,
   getDataApi
 } from '@/api/modules/demo/data'
+import { listDataApi as listDictDataApi } from '@/api/modules/system/dictData'
+import { uploadPure } from '@/api/modules/upload'
+import http from '@/api'
+
+onMounted (() => {
+  state.cacheData.url = 'http://localhost:9090/profile/upload/2024/08/08/144745610/test.png'
+  handleImgSuccess({
+    data: 'test change data',
+    jsonData: 'jsonData12345 test change',
+    url: 'testurl'
+  })
+})
+
 const imgDetect = ref()
 const state = reactive({
   area: '' as string,
   width: 1920 as number,
   height: 1080 as number,
-  cover: ''
+  cover: '',
+  classes: [],
+  //   [
+  //   {
+  //     name: '飞机',
+  //     color: '#ea5413',
+  //     label: 'plane'
+  //   },
+  //   {
+  //     name: '汽车',
+  //     color: '#ff00ff',
+  //     label: 'car'
+  //   }
+  // ],
+  jsonData: '',
+  cacheData: {}
+    // '{"version":"5.3.0","objects":[{"type":"image","version":"5.3.0","originX":"left","originY":"top","left":0,"top":0,"width":918,"height":789,"fill":"rgb(0,0,0)","stroke":null,"strokeWidth":0,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":0.68,"scaleY":0.68,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"cropX":0,"cropY":0,"src":"http://localhost:8848/api/profile/upload/2024/08/08/144745610/3-3.jpg","crossOrigin":null,"filters":[]},{"type":"rect","version":"5.3.0","originX":"left","originY":"top","left":181.38,"top":251.38,"width":244,"height":162,"fill":"rgba(255, 255, 255, 0)","stroke":"#E34F51","strokeWidth":5,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":1,"scaleY":1,"angle":-25,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"rx":0,"ry":0}]}'
 })
-const { area, width, height, cover } = toRefs(state)
+const { area, width, height, cover, classes, jsonData } = toRefs(state)
 
 // ProTable 实例
 const proTable = ref<ProTableInstance>()
@@ -82,45 +122,150 @@ const deleteData = async (params: any) => {
 
 // 标注图片
 const markImg = data => {
-  state.cover = '/api' + data.url
-  // area 代表后端的传来的标注数据
-  const area = []
-  if (state.cover != '') {
-    if (area.length != 0) {
-      handleImgSuccess(area)
-    }
-    imgDetect.value.visible = true
-  } else {
+  // console.log(data)
+  state.cacheData = data
+    if (!data.url || data.url.length === 0) {
     ElMessage.warning('缺失图像,暂时不能进行区域添加!')
+    return
   }
-}
-const getList = () => {
-  useDrawArea({
-    src: state.cover,
-    width: state.width,
-    height: state.height,
-    area: state.area
+
+  listDictDataApi({
+    pageNum: 1,
+    pageSize: 10,
+    dictType: 'class_definition'
   })
-    .then(url => {
-      state.cover = url as string
+    .then(res => {
+      // console.log(res)
+      state.classes = []
+      for (let i = 0; i < res.data.list.length; i++) {
+        state.classes.push({
+          name: res.data.list[i].dictLabel,
+          color: res.data.list[i].cssClass,
+          label: res.data.list[i].dictValue
+        })
+      }
     })
-    .catch(error => {
-      console.log(error)
+    .catch(err => {
+      console.log(err)
     })
+
+  state.cover = '/api' + data.url
+  if (state.cacheData.labelurl && state.cacheData.labelurl.length > 0) {
+    // console.log('get label jsonData')
+    http.get<any>(state.cacheData.labelurl.split(';')[1])
+      .then(res => {
+        // console.log(res)
+        state.jsonData = JSON.stringify(res)
+        imgDetect.value.visible = true
+      })
+      .catch(err => {
+        console.log(err)
+      })
+  } else {
+    imgDetect.value.visible = true
+  }
+
+  // area 代表后端的传来的标注数据
+  // console.log(state.cover)
+
+  // const area = []
+  // if (state.cover != '') {
+  //   if (area.length != 0) {
+  //     handleImgSuccess(area)
+  //   }
+  //
+  //   console.log("true???")
+  // } else {
+  //   ElMessage.warning('缺失图像,暂时不能进行区域添加!')
+  // }
 }
+// const getList = () => {
+//   useDrawArea({
+//     src: state.cover,
+//     width: state.width,
+//     height: state.height,
+//     area: state.area
+//   })
+//     .then(url => {
+//       state.cover = url as string
+//     })
+//     .catch(error => {
+//       console.log(error)
+//     })
+// }
 const handleImgSuccess = data => {
-  data.forEach(item => {
-    const area = item.split(';')
-    // try=tly blx=tlx brx=trx bry=bly
-    const tlx = Math.round(Number(area[0]) * 1920)
-    const tly = Math.round(Number(area[1]) * 1080)
-    const trx = tlx + Math.round(Number(area[2]) * 1920)
-    const bly = tly + Math.round(Number(area[3]) * 1080)
-    state.area += `${tlx};${tly};${trx};${tly};${trx};${bly};${tlx};${bly},`
-  })
-  // console.log('state.area', state.area)
-  state.area.slice(0, -1)
-  getList()
+  // console.log(data)
+  state.jsonData = data['jsonData']
+  state.cover = data['url']
+
+  state.cacheData.labelurl = data['data']
+
+  state.cacheData.increment = 'NONE'
+
+  let arr = state.cacheData.url.split('upload/')
+  let filenames = (arr[arr.length - 1]).split('.')
+  filenames[filenames.length - 1] = 'txt'
+  // console.log(filenames.join('.'))
+  let filename = filenames.join('.')
+
+  labelFile(data['data'], filename)
+    .then(res => {
+      // console.log(res)
+      if (res.code === 200) {
+        state.cacheData.labelurl = res.data.url
+
+        filenames[filenames.length - 2] += '_json'
+        filename = filenames.join('.')
+        setTimeout(() => {
+          labelFile(data['jsonData'], filename)
+            .then(res => {
+              if (res.code === 200) {
+                state.cacheData.labelurl += ';' + res.data.url
+                updateDataApi(state.cacheData)
+                  .then(res => {
+                    // console.log(res)
+                    if (res.data) {
+                      ElMessage.success('操作成功')
+                    }
+                  })
+                  .catch(err => {
+                    console.log(err)
+                  })
+              }
+            })
+        }, 1200)
+      }
+    })
+
+  // data.forEach(item => {
+  //   if (item.startsWith('{')) {
+  //     return
+  //   }
+  //   const area = item.split(';')
+  //   // try=tly blx=tlx brx=trx bry=bly
+  //   // mark: 当前用的应该是左上角定点位置和长宽,应该替换为存储点
+  //   const tlx = Math.round(Number(area[0]) * 1920)
+  //   const tly = Math.round(Number(area[1]) * 1080)
+  //   const trx = tlx + Math.round(Number(area[2]) * 1920)
+  //   const bly = tly + Math.round(Number(area[3]) * 1080)
+  //   state.area += `${tlx};${tly};${trx};${tly};${trx};${bly};${tlx};${bly},`
+  // })
+  // // console.log('state.area', state.area)
+  // state.area.slice(0, -1)
+  // getList()
+}
+
+// 创建并提交标注txt文件
+const labelFile = (data, filename) => {
+  // 创建Blob对象
+  const blob = new Blob([data], { type: 'text/plain' })
+
+  // 创建表单数据并添加文件
+  const formData = new FormData()
+
+  formData.append('file', blob, filename)
+
+  return uploadPure(formData)
 }
 
 // 批量删除数据管理信息