소스 검색

camera init

louhangfei 2 년 전
부모
커밋
836717b293

+ 2 - 1
package.json

@@ -48,6 +48,7 @@
     "element-plus": "2.3.6",
     "element-resize-detector": "1.2.4",
     "fabric": "5.3.0",
+    "konva": "9.3.0",
     "lodash-es": "4.17.21",
     "mockjs": "1.1.0",
     "nprogress": "0.2.0",
@@ -146,4 +147,4 @@
       ]
     }
   }
-}
+}

+ 7 - 0
pnpm-lock.yaml

@@ -56,6 +56,9 @@ dependencies:
   fabric:
     specifier: 5.3.0
     version: 5.3.0
+  konva:
+    specifier: 9.3.0
+    version: 9.3.0
   lodash-es:
     specifier: 4.17.21
     version: 4.17.21
@@ -4785,6 +4788,10 @@ packages:
     resolution: {integrity: sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==}
     dev: true
 
+  /konva@9.3.0:
+    resolution: {integrity: sha512-qLTW06GRwb+WMMUXJcGIb0qP4uO0mZLAwgRI82zuCkRmCH1lFsVPmrPzqqHnjKCMu4Jzw6d/R8JxkPw7gkVnuw==}
+    dev: false
+
   /levn@0.4.1:
     resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
     engines: {node: '>= 0.8.0'}

+ 3 - 0
src/views/result/components/AlgorithmsSetting/AlgorithmsSetting.vue

@@ -0,0 +1,3 @@
+<template> 算法配置 </template>
+<script lang="ts" setup></script>
+<style scoped></style>

+ 11 - 0
src/views/result/components/CameraViewSetting/CameraViewSetting.vue

@@ -0,0 +1,11 @@
+<template>
+  <div>
+    <FenceToolbar />
+    <!-- <FenceEditor /> -->
+  </div>
+</template>
+<script lang="ts" setup>
+  import FenceToolbar from '../FenceToolbar/FenceToolbar.vue';
+  import FenceEditor from '../FenceEditor/FenceEditor.vue';
+</script>
+<style scoped></style>

+ 649 - 0
src/views/result/components/FenceEditor/FenceEditor.vue

@@ -0,0 +1,649 @@
+<template>
+  <div id="editorMap" ref="mapRef"></div>
+</template>
+
+<script lang="ts" setup>
+  import Konva from 'konva';
+  import { ref, onMounted, onUnmounted } from 'vue';
+  import {
+    GROUP_NAME,
+    POLYGON_NAME,
+    Points,
+    ServerLine,
+    ToolObjectItem,
+    toolObject,
+  } from './constants';
+  import { ElMessage } from 'element-plus';
+  import { getDefaultScale } from './utils';
+  import { Group } from 'konva/lib/Group';
+
+  const mapRef = ref<HTMLCanvasElement | null>(null);
+  let currentTool: ToolObjectItem = toolObject[1];
+
+  let stage: Konva.Stage | null = null;
+  let layer: Konva.Layer | null = null;
+  let currentDrawingShape: Konva.Group | null = null; //现在绘画的图形
+  let polygonPoints: number[] = []; //存储绘画多边形各个顶点的数组
+  let stageWidth = 0; //舞台宽
+  let stageHeight = 0; //舞台高
+  let scale = 1; //窗口变化的缩放比例
+  let drawing = false; //一开始不能绘画
+  let currentDel: Konva.Node | null = null; //删除对象
+
+  onMounted(() => {
+    initKonvaStage();
+    //禁止浏览器右击菜单
+    document.oncontextmenu = function () {
+      return false;
+    };
+  });
+
+  onUnmounted(() => {
+    stage?.destroy();
+  });
+
+  /**
+   *初始化konva舞台
+   */
+  function initKonvaStage() {
+    //1实例化stage层
+    stageWidth = mapRef.value?.clientWidth || 0;
+    stageHeight = mapRef.value?.clientHeight || 0;
+    console.log('stageWidth', stageWidth);
+    stage = new Konva.Stage({
+      container: 'editorMap',
+      width: stageWidth,
+      height: stageHeight,
+      ignoreStroke: true,
+      background: '#00ff00',
+    });
+    window.stage = stage;
+    setStageCursor('pointer');
+
+    //2实例化layer层
+    layer = new Konva.Layer();
+    //3添加layer层
+    stage?.add(layer);
+
+    //给***舞台***绑定事件
+    stageBindEvent();
+  }
+  /**
+   * 舞台绑定的事件
+   * @param vc_this
+   */
+  function stageBindEvent() {
+    //鼠标按下
+    stage?.on('mousedown', (e) => {
+      //鼠标左键开始
+      console.log('stage mousedown', e);
+      if (e.evt.button == 0) {
+        if (e.target === stage) {
+          stageMousedown(currentTool!);
+          return;
+        }
+        //图形起始点只能在图片层上
+        // if (e.target === shape) {
+        //   //开始初始绘画
+        //   stageMousedown(currentTool!);
+        //   return;
+        // }
+        //允许后续点绘画在其他图形上
+        if (drawing) {
+          stageMousedown(currentTool!);
+          return;
+        }
+      } else if (e.evt.button == 2) {
+        // 如果polygonPoints为空,那么右击是没有反应的。
+        console.log('mousedown 2');
+      }
+    });
+    //鼠标移动
+    stage?.on('mousemove', () => {
+      if (currentTool && drawing) {
+        //绘画中
+        stageMousemove();
+      }
+    });
+    //鼠标放开
+    stage?.on('mouseup', (e: Konva.KonvaEventObject<any>) => {
+      if (e.evt.button == 0) {
+        if (drawing) {
+          stageMouseup(e);
+        }
+      } else if (e.evt.button == 2) {
+        if (polygonPoints.length != 0) {
+          stageMouseup(e);
+        }
+      }
+    });
+    //   //鼠标滚轮事件取消
+
+    //舞台快捷键
+    var container = stage?.container();
+    if (!container) return;
+    container.tabIndex = 1;
+    container?.focus();
+    container?.addEventListener('keydown', (e) => {
+      //删除的快捷键
+      if (e.keyCode === 46) {
+        removeCurrent();
+      }
+      if (container) {
+        container.style.cursor = 'crosshair';
+      }
+      e.preventDefault();
+      layer?.draw();
+    });
+  }
+
+  const getPolygonInGroup = (g: Konva.Group | null) => {
+    if (!g) return;
+    return g.findOne(POLYGON_NAME) as Konva.Line;
+  };
+
+  /** 设置当前选中的group */
+  function setCurrentGroup(group: Konva.Group) {
+    currentDrawingShape = group;
+    setGroupActive(group);
+    currentDel = group;
+  }
+
+  /**
+   * 在舞台上鼠标点下发生的事件
+   * @param currentTool 当前选择的工具
+   * @param e 传入的event对象
+   */
+  function stageMousedown(currentTool: ToolObjectItem) {
+    console.log('stagemousedown');
+
+    //如果数组长度小于2,初始化多边形和顶点,使它们成为一组,否则什么都不做
+    // 小于2说明一个点也没有,一个点的长度是2,所以要先创建group
+    if (polygonPoints.length < 2) {
+      //最好使用konva提供的鼠标xy点坐标
+      var mousePos = stage?.getPointerPosition();
+      if (!mousePos) return;
+      //考虑鼠标缩放
+      var x = (mousePos.x / scale - layer?.getAttr('x')) / getDefaultScale(layer?.scaleX()),
+        y = (mousePos.y / scale - layer?.getAttr('y')) / getDefaultScale(layer?.scaleY());
+      //拖拽组
+      var group = new Konva.Group({
+        name: currentTool.name + 'group',
+        draggable: false,
+      });
+      polygonPoints = [x, y];
+      //添加多边形的点
+      drawCircle(x, y, group, polygonPoints);
+
+      //绘画多边形
+      drawPolygon(currentTool, polygonPoints, group);
+      //添加多边形的边
+      // drawLine(currentTool, polygonPoints, group);
+      layer?.add(group);
+      setCurrentGroup(group);
+      currentDel = group;
+
+      //使所有顶点在顶层显示
+      stage?.find('Circle').forEach((element) => {
+        element.moveToTop();
+      });
+      layer?.draw();
+    } //多边形增加顶点
+    else {
+      //最好使用konva提供的鼠标xy点坐标
+      var mousePos = stage?.getPointerPosition();
+      if (!mousePos) return;
+      //考虑鼠标缩放
+      var x = (mousePos.x / scale - layer?.getAttr('x')) / getDefaultScale(layer?.scaleX()),
+        y = (mousePos.y / scale - layer?.getAttr('y')) / getDefaultScale(layer?.scaleY());
+      //group继续添加多边形的点
+      drawCircle(x, y, currentDrawingShape!, polygonPoints);
+      polygonPoints.push(x);
+      polygonPoints.push(y);
+
+      const polygon = getPolygonInGroup(currentDrawingShape);
+      currentDel = currentDrawingShape;
+      //绘画多边形
+      polygon?.setAttr('points', polygonPoints);
+      //group继续添加多边形的边
+      //使所有顶点在顶层显示
+      stage?.find('Circle').forEach((element) => {
+        element.moveToTop();
+      });
+      layer?.draw();
+    }
+
+    drawing = true;
+  }
+  /**
+   * 鼠标在舞台上移动事件
+   * @param currentTool 当前选择的工具
+   * @param e 传入的event对象
+   */
+  function stageMousemove() {
+    const container = stage?.container();
+    if (!container) return;
+
+    container.style.cursor = 'crosshair';
+    //多边形初始化后,如果数组长度大于2,鼠标移动时,实时更新下一个点
+    if (polygonPoints.length >= 2) {
+      var mousePos = stage?.getPointerPosition();
+      if (!mousePos) return;
+      var x = (mousePos.x / scale - layer?.getAttr('x')) / getDefaultScale(layer?.scaleX()),
+        y = (mousePos.y / scale - layer?.getAttr('y')) / getDefaultScale(layer?.scaleY());
+      var tempPoints = polygonPoints.concat([]);
+      tempPoints.push(x);
+      tempPoints.push(y);
+
+      const polygon = getPolygonInGroup(currentDrawingShape);
+      //更新多边形
+      polygon?.setAttr('points', tempPoints);
+      //使所有顶点在顶层显示
+      stage?.find('Circle').forEach((element) => {
+        element.moveToTop();
+      });
+    }
+
+    layer?.draw();
+  }
+  /**
+   * 鼠标在舞台弹起
+   * @param currentTool 当前选择的工具
+   * @param e 传入的event对象
+   */
+  function stageMouseup(e: Konva.KonvaEventObject<any>) {
+    if (e.evt.button == 2) {
+      if (polygonPoints.length != 0) {
+        //最好使用konva提供的鼠标xy点坐标
+        var mousePos = stage?.getPointerPosition();
+        if (!mousePos) return;
+        //考虑鼠标缩放
+        var x = (mousePos.x / scale - layer?.getAttr('x')) / getDefaultScale(layer?.scaleX()),
+          y = (mousePos.y / scale - layer?.getAttr('y')) / getDefaultScale(layer?.scaleY());
+        //group继续添加多边形的点
+        // 右击和左击都要添加一个点
+        drawCircle(x, y, currentDrawingShape!, polygonPoints);
+        polygonPoints.push(x);
+        polygonPoints.push(y);
+        const polygon = getPolygonInGroup(currentDrawingShape);
+        //绘画多边形
+        polygon?.setAttr('points', polygonPoints);
+
+        //判断是否是只有两个点的多边形,如果起点和终点相同,不允许绘画
+        if (polygon?.points().length == 2 || polygon?.points().length == 4) {
+          drawing = false;
+          currentDrawingShape?.destroy();
+          polygonPoints = [];
+          ElMessage({
+            message: '顶点数必须大于2个!',
+            type: 'warning',
+            center: true,
+            duration: 1000,
+          });
+          return;
+        }
+        //右键弹起
+        polygonPoints = [];
+        // 停止事件冒泡
+        e.cancelBubble = true;
+        // 停止画多边形
+        drawing = false;
+      }
+    }
+
+    //使所有顶点在顶层显示
+    stage?.find('Circle').forEach((element) => {
+      element.moveToTop();
+    });
+
+    layer?.draw();
+  }
+  /**
+   * 多边形圆形
+   * @param //x x坐标
+   * @param //y y坐标
+   */
+  function drawCircle(x: number, y: number, group: Konva.Group, shapePoints: number[]) {
+    const circle = new Konva.Circle({
+      name: currentTool.name + 'circle',
+      x: x,
+      y: y,
+      radius: 5 / scale / getDefaultScale(layer?.scaleX()),
+      visible: true, //是否显示
+      fill: currentTool.anchorColor,
+      stroke: currentTool.anchorColor,
+      draggable: false,
+      strokeWidth: 0.5,
+      strokeScaleEnabled: false,
+      //增加点击区域
+      hitStrokeWidth: 8 / scale / getDefaultScale(layer?.scaleX()),
+      //设置拖动区域,不能超过舞台大小
+      dragBoundFunc: function (pos) {
+        //左上角
+        if (pos.x < 0 && pos.y < 0) {
+          return {
+            x: 0,
+            y: 0,
+          };
+        } //左侧
+        else if (pos.x <= 0 && 0 <= pos.y && pos.y <= (stage?.height() ?? 0)) {
+          return {
+            x: 0,
+            y: pos.y,
+          };
+        }
+        //左下角
+        else if (pos.x < 0 && pos.y > (stage?.height() ?? 0)) {
+          return {
+            x: 0,
+            y: stage?.height(),
+          };
+        } //下侧
+        else if (0 <= pos.x && pos.x <= (stage?.width() ?? 0) && pos.y > (stage?.height() ?? 0)) {
+          return {
+            x: pos.x,
+            y: stage?.height(),
+          };
+        } //右下角
+        else if (pos.x > (stage?.width() ?? 0) && pos.y > (stage?.height() ?? 0)) {
+          return {
+            x: stage?.width(),
+            y: stage?.height(),
+          };
+        }
+        //右侧
+        else if (pos.x > (stage?.width() ?? 0) && 0 <= pos.y && pos.y <= (stage?.height() ?? 0)) {
+          return {
+            x: stage?.width(),
+            y: pos.y,
+          };
+        }
+        //右上角
+        else if (pos.x > (stage?.width() ?? 0) && pos.y < 0) {
+          return {
+            x: stage?.width(),
+            y: 0,
+          };
+        } //上侧
+        else if (0 <= pos.x && pos.x <= (stage?.width() ?? 0) && pos.y < 0) {
+          return {
+            x: pos.x,
+            y: 0,
+          };
+        }
+      },
+    });
+    group.add(circle);
+    let xChange: number, yChange: number;
+    circle.on('mouseover', () => {
+      const c = stage?.container();
+      if (!c) return;
+      c.style.cursor = 'pointer';
+    });
+    circle.on('mousedown', (e) => {
+      console.log('circle,');
+      if (!drawing) {
+        circle.draggable(true);
+        //将现在绘画的对象改为group
+        setCurrentGroup(circle.getParent() as Konva.Group);
+      } else {
+        circle.draggable(false);
+      }
+      e.cancelBubble = true;
+    });
+    circle.on('mouseleave', () => {
+      const c = stage?.container();
+      if (!c) return;
+      c.style.cursor = 'crosshair';
+    });
+    circle.on('dragstart', () => {
+      switch (currentTool?.type) {
+        case 'poly':
+          //查找拖拽了多边形的哪个点
+          for (var i = 0; i < shapePoints.length; i += 2) {
+            if (
+              circle.getAttr('x') == shapePoints[i] &&
+              circle.getAttr('y') == shapePoints[i + 1]
+            ) {
+              xChange = i;
+              yChange = i + 1;
+              break;
+            }
+          }
+          break;
+        default:
+          break;
+      }
+    });
+    circle.on('dragmove', () => {
+      switch (currentTool?.type) {
+        case 'poly':
+          var x = circle.x();
+          var y = circle.y();
+          //更改拖拽点的位置
+          shapePoints[xChange] = x;
+          shapePoints[yChange] = y;
+          break;
+        default:
+          break;
+      }
+    });
+    circle.on('dragend', (e) => {
+      switch (currentTool?.type) {
+        case 'poly':
+          //使所有顶点在顶层显示
+          stage?.find('Circle').forEach((element) => {
+            element.moveToTop();
+          });
+          circle.draggable(false);
+          break;
+        default:
+          break;
+      }
+      ElMessage({
+        message: '修改成功!',
+        type: 'success',
+        center: true,
+        duration: 1000,
+      });
+      e.cancelBubble = true;
+    });
+    return circle;
+  }
+
+  function setStageCursor(cursor: string) {
+    const c = stage?.container();
+    if (c) {
+      c.style.cursor = cursor;
+    }
+  }
+
+  /** 设置当前的group为active */
+  function setGroupActive(activeGroup: Group) {
+    const groups = stage?.find(GROUP_NAME) as Konva.Group[];
+    if (!groups) return;
+    /** 将其他组的线条设为非高亮 */
+    groups.forEach((g: Konva.Group) => {
+      if (g === activeGroup) return;
+      g.find(POLYGON_NAME).forEach((line) => {
+        (line as Konva.Line).stroke(currentTool?.color!);
+      });
+
+      g.find('Circle').forEach((circle) => {
+        (circle as Konva.Circle).hide();
+      });
+    });
+
+    const thisLine = activeGroup.findOne(POLYGON_NAME) as Konva.Line;
+    if (thisLine) {
+      thisLine.stroke(currentTool.activeColor);
+    }
+    const thisCircles = activeGroup.find('Circle') as Konva.Circle[];
+    if (thisCircles) {
+      thisCircles.forEach((circle) => {
+        (circle as Konva.Circle).show();
+      });
+    }
+    layer?.draw();
+  }
+  /**
+ *多边形
+  @param currentTool
+  * @param points 多边形绘画的各个顶点,类型数组
+  */
+  function drawPolygon(currentTool: ToolObjectItem, points: number[], group: Konva.Group) {
+    let poly = new Konva.Line({
+      name: currentTool.name + 'poly',
+      points: points,
+      /*  fill: currentTool.color, */
+      stroke: currentTool.color,
+      strokeWidth: 1,
+      draggable: false,
+      opacity: 0.5,
+      lineCap: 'round',
+      lineJoin: 'round',
+      closed: true,
+      strokeScaleEnabled: false,
+    });
+    group.add(poly);
+    setCurrentGroup(group);
+    const pParent = group;
+    pParent?.on('mouseleave', () => {
+      const c = stage?.container();
+      if (c) {
+        c.style.cursor = 'crosshair';
+      }
+    });
+    pParent?.on('mousedown', (e) => {
+      console.log('group mouse down');
+      if (e.evt.button == 0) {
+        //绘画结束
+        if (!drawing) {
+          setStageCursor('move');
+          //设置现在绘画节点的对象为该多边形和顶点的组
+          // 如果要让顶点和多边形一起拖拽,必须设置,多边形不能被拖拽
+          poly.setAttr('draggable', false);
+          currentDrawingShape?.setAttr('draggable', true);
+          //使所有顶点在顶层显示
+          stage?.find('Circle').forEach((element) => {
+            element.moveToTop();
+          });
+          //添加删除撤销对象
+          currentDel = currentDrawingShape;
+          setCurrentGroup(poly.getParent() as Konva.Group);
+
+          layer?.draw();
+        } else {
+          setStageCursor('crosshair');
+          poly.getParent()?.setAttr('draggable', false);
+        }
+      }
+    });
+
+    pParent?.on('dragend', () => {
+      console.log('dragend');
+      /** 这里可以把工具的类型用枚举值定义 */
+
+      //使所有顶点在顶层显示
+      stage?.find('Circle').forEach((element) => {
+        element.moveToTop();
+      });
+      //添加删除撤销对象
+      currentDel = currentDrawingShape;
+      layer?.draw();
+      /*   vc_setMaskData(); */
+
+      ElMessage({
+        message: '修改成功!',
+        type: 'success',
+        center: true,
+        duration: 1000,
+      });
+      setStageCursor('crosshair');
+      //设置组不能拖动
+      currentDrawingShape?.setAttr('draggable', false);
+    });
+    return poly;
+  }
+
+  /** 根据json数据创建group */
+  function createGroupByPoints(points: Points) {
+    var group = new Konva.Group({
+      name: currentTool.name + 'group',
+      draggable: false,
+    });
+    //添加多边形的点
+
+    //绘画多边形
+    drawPolygon(currentTool, points, group);
+    for (let i = 0; i < points.length; i += 2) {
+      const x = points[i];
+      const y = points[i + 1];
+      drawCircle(x, y, group, points);
+    }
+    // group.setAttrs({ x: groupData.attrs.x, y: groupData.attrs.y })
+
+    layer?.add(group);
+    layer?.draw();
+  }
+
+  const removeCurrent = () => {
+    if (currentDel) {
+      currentDel.destroy();
+      currentDel = null;
+      currentDrawingShape = null;
+      ElMessage({
+        message: '删除成功!',
+        type: 'success',
+        center: true,
+        duration: 1000,
+      });
+    }
+    layer?.draw();
+  };
+
+  const toObject = () => {
+    const polyGroups = stage?.find('.polygroup');
+    const gropuPoints = polyGroups?.map((item) => {
+      const groupX = item.x();
+      const groupY = item.y();
+
+      const line = (item as Konva.Group).findOne((x: any) => x.className === 'Line') as Konva.Line;
+      const points = line?.points();
+      const newPoints = [];
+      /** 存到后端的时候,只给点的坐标信息,不会给group的位置信息,所以要将点的坐标加上group的位移,才是之后点的最终坐标 */
+      for (let i = 0; i < points.length; i += 2) {
+        newPoints.push([points[i] + groupX, points[i + 1] + groupY]);
+      }
+      return newPoints;
+    });
+    return gropuPoints;
+  };
+
+  const initStageByJSON = (param: { width: number; height: number }) => {
+    stage?.setAttrs({ width: param.width, height: param.height });
+  };
+
+  const toRawObject = () => {
+    return stage?.toObject();
+  };
+
+  defineExpose({
+    remove: removeCurrent,
+    toObject,
+    toRawObject,
+    createGroupByPoints,
+    initStageByJSON,
+  });
+</script>
+
+<style scoped>
+  #editorMap {
+    position: absolute;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    border: 2px solid #0f0;
+  }
+</style>

+ 45 - 0
src/views/result/components/FenceEditor/constants.ts

@@ -0,0 +1,45 @@
+export const toolObject: ToolObjectItem[] = [
+  {
+    name: 'rect',
+    type: 'rect',
+    /* 矩形颜色 */ color: '#75fb4c',
+    /* 边框颜色 */ lineColor: '#75fb4c',
+    /* 顶点颜色 */ anchorColor: 'green',
+    activeColor: '#0f0',
+  },
+  {
+    name: 'poly',
+    type: 'poly',
+    /* 多边形颜色 */ color: '#E63F00',
+    /* 边框颜色 */ lineColor: '#E63F00 ',
+    /* 顶点颜色 */ anchorColor: 'red',
+    /** 选中模式状态下的边框颜色 */
+    activeColor: '#0f0',
+  },
+];
+
+export interface ToolObjectItem {
+  name: string;
+  type: string;
+  /* 矩形颜色 */
+  color: string;
+  /* 边框颜色 */
+  lineColor: string;
+  /* 顶点颜色 */
+  anchorColor: string;
+
+  activeColor: string;
+}
+
+export const GROUP_NAME = '.polygroup';
+export const POLYGON_NAME = '.polypoly';
+
+export type Points = number[];
+
+/** 导出给后端的单个点坐标格式 */
+export type ServerLinePoint = [number, number];
+/** 一个多边形的所有点坐标 */
+export type ServerLine = ServerLinePoint[];
+
+/** 图上所有的多边形 */
+export type ServerLines = ServerLine[];

+ 3 - 0
src/views/result/components/FenceEditor/utils.ts

@@ -0,0 +1,3 @@
+export function getDefaultScale(scale: number | undefined | null) {
+  return scale ?? 1;
+}

+ 101 - 0
src/views/result/components/FenceToolbar/FenceToolbar.vue

@@ -0,0 +1,101 @@
+<template>
+  <div class="camera-wrapper">
+    <div style="text-align: center"> camera 画面 </div>
+    <div class="editor-wrapper" v-if="isFenceOn">
+      <div class="toolbar">
+        <div>
+          <ElButton>编辑</ElButton>
+          <ElButton @click="remove">删除</ElButton>
+          <ElButton @click="toObject">保存到localStorage</ElButton>
+          <ElButton @click="loadGroup">从local加载group</ElButton>
+          <ElButton @click="toRawObject">保存Raw</ElButton>
+        </div>
+      </div>
+      <PolygonEditor class="polygonEditor" ref="polygonEditorRef" />
+    </div>
+  </div>
+  <div>
+    <div
+      >打开电子围栏
+      <ElSwitch v-model="isFenceOn" />
+    </div>
+  </div>
+</template>
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import { ElButton, ElSwitch } from 'element-plus';
+  import PolygonEditor from '../FenceEditor/FenceEditor.vue';
+  import { ServerLines } from '../FenceEditor/constants';
+
+  const isFenceOn = ref(true);
+
+  const polygonEditorRef = ref<typeof PolygonEditor | null>(null);
+
+  const remove = () => {
+    polygonEditorRef.value?.remove();
+  };
+
+  const toObject = () => {
+    const json = polygonEditorRef.value?.toObject();
+    console.log('toObject json', json);
+    localStorage.setItem('mapDataV2', JSON.stringify(json));
+  };
+
+  const toRawObject = () => {
+    const objects = polygonEditorRef.value?.toRawObject();
+    console.log('objects', objects);
+    localStorage.setItem('mapData', JSON.stringify(objects));
+  };
+
+  const loadGroup = () => {
+    const data = localStorage.getItem('mapDataV2');
+    console.log('loadGroup data', data);
+    if (!data) return;
+    const dataJSON = JSON.parse(data) as ServerLines;
+    const groups = dataJSON;
+    const rawLinePoints = groups[0];
+    const points: number[] = [];
+    rawLinePoints.forEach((line) => {
+      points.push(line[0], line[1]);
+    });
+    console.log('points', points);
+
+    polygonEditorRef.value?.createGroupByPoints(points);
+  };
+
+  const preview = () => {
+    polygonEditorRef.value?.preview();
+  };
+</script>
+
+<style scoped>
+  .camera-wrapper {
+    position: relative;
+    border: 1px solid #f00;
+    background: #fff;
+    height: 600px;
+  }
+
+  .toolbar {
+    width: 100px;
+    height: 400px;
+    position: absolute;
+    left: 0;
+    top: 50px;
+    border: 1px solid #f00;
+    z-index: 10;
+    color: #000;
+  }
+
+  .editor-wrapper {
+    position: absolute;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+  }
+
+  .polygonEditor {
+    flex-grow: 1;
+  }
+</style>

+ 11 - 0
src/views/result/store/useCameraDetail.ts

@@ -0,0 +1,11 @@
+/** 相机详情的store */
+
+import { defineStore } from 'pinia';
+import { ref } from 'vue';
+
+const useCameraDetailStore = defineStore('cameraDetail', () => {
+  const detail = ref(null);
+  return { detail };
+});
+
+export default useCameraDetailStore;

+ 8 - 3
src/views/result/success.vue

@@ -4,16 +4,21 @@
     <div class="cameraMain">
       <div class="cameraTree">场景树</div>
       <div class="cameraSettingWrapper">
-        <div class="cameraView">这是相机预览画面</div>
+        <div class="cameraView">
+          <CameraViewSetting />
+        </div>
         <div class="cameraParamsSettingWrapper">
           <div class="cameraParamsSetting"> 相机的参数设置 </div>
-          <div class="algorithmsSetting"> 算法配置 </div>
+          <div class="algorithmsSetting"> <AlgorithmsSetting /> </div>
         </div>
       </div>
     </div>
   </div>
 </template>
-<script lang="ts" setup></script>
+
+<script lang="ts" setup>
+  import CameraViewSetting from './components/CameraViewSetting/CameraViewSetting.vue';
+</script>
 <style lang="scss" scoped>
   .cameraView {
     width: 800px;