瀏覽代碼

Merge branch 'master' of http://192.168.0.115:3000/bill/traffic-laser

xushiting 2 年之前
父節點
當前提交
32874f99a7

文件差異過大導致無法顯示
+ 5 - 1
server/test/a0k4xu045_202305311600080410/attach/sceneStore


+ 0 - 1
src/graphic/CanvasStyle/focus.js

@@ -93,7 +93,6 @@ const DoubleArrowLine = {
   strokeStyle: "red",
 };
 
-
 const TestPoint = {
   strokeStyle: "rgba(0,0,0,0)",
   fillStyle: "#fff",

+ 81 - 48
src/graphic/Renderer/Draw.js

@@ -149,8 +149,7 @@ export const help = {
     return (
       Math.round(
         (mathUtil.getDistance(p1, p2) * coordinate.res * 100) / coordinate.ratio
-      )
-      / 100
+      ) / 100
     );
   },
   getPerpendicularPoint(p1, p2, p3, d) {
@@ -232,12 +231,11 @@ export const help = {
       VectorStyle.PointDrawLine,
       VectorStyle.SingleDashedLine,
       VectorStyle.SingleSolidLine,
-    ]
+    ];
     if (typeof line === "function" && !funStyle.includes(style)) {
-      style = VectorStyle.SingleSolidLine
+      style = VectorStyle.SingleSolidLine;
     }
 
-
     switch (style) {
       case VectorStyle.PointDrawLine:
       case VectorStyle.SingleDashedLine:
@@ -253,8 +251,8 @@ export const help = {
           ]);
         }
 
-        if (typeof line === 'function') {
-          line()
+        if (typeof line === "function") {
+          line();
         } else {
           ctx.moveTo(line[0].x, line[0].y);
           ctx.lineTo(line[1].x, line[1].y);
@@ -426,9 +424,9 @@ export default class Draw {
     const center = coordinate.getScreenXY(vector.center);
     this.context.save();
     if (vector.scale) {
-      this.context.translate(center.x, center.y)
-      this.context.scale(vector.scale, vector.scale)
-      this.context.translate(-center.x, -center.y)
+      this.context.translate(center.x, center.y);
+      this.context.scale(vector.scale, vector.scale);
+      this.context.translate(-center.x, -center.y);
     }
     this.context.drawImage(
       img,
@@ -489,13 +487,13 @@ export default class Draw {
       };
 
       ctx.save();
-      help.setStyle(ctx, styles)
+      help.setStyle(ctx, styles);
       vector.midDivide.leftMidDivide && draw(vector.midDivide.leftMidDivide);
       vector.midDivide.rightMidDivide && draw(vector.midDivide.rightMidDivide);
 
       ctx.restore();
     }
-    console.log(styles, label, vector)
+    console.log(styles, label, vector);
 
     if (import.meta.env.DEV && !isTemp) {
       const startReal = isTemp
@@ -511,9 +509,12 @@ export default class Draw {
     }
 
     this.drawRoadEdge(vector, isTemp);
-    vector.leftLanes && vector.leftLanes.forEach((g) => this.drawLan(g, !!label));
-    vector.rightLanes && vector.rightLanes.forEach((g) => this.drawLan(g, !!label));
-    vector.singleLanes && vector.singleLanes.forEach((g) => this.drawLan(g, !!label));
+    vector.leftLanes &&
+      vector.leftLanes.forEach((g) => this.drawLan(g, !!label));
+    vector.rightLanes &&
+      vector.rightLanes.forEach((g) => this.drawLan(g, !!label));
+    vector.singleLanes &&
+      vector.singleLanes.forEach((g) => this.drawLan(g, !!label));
   }
 
   drawLan(lan, focus) {
@@ -523,9 +524,9 @@ export default class Draw {
     ctx.save();
     ctx.beginPath();
     help.setVectorStyle(ctx, null, "Lane");
-    console.log(focus)
+    console.log(focus);
     if (focus) {
-      ctx.strokeStyle = 'rgba(255, 143, 40, 1)'
+      ctx.strokeStyle = "rgba(255, 143, 40, 1)";
     }
     ctx.lineWidth *= Settings.lineWidth;
     ctx.setLineDash(Style.Lane.dash);
@@ -656,14 +657,16 @@ export default class Draw {
     ctx.save();
     help.setVectorStyle(ctx, vector);
     ctx.beginPath();
-    ctx.arc(
-      extremePoint.x,
-      extremePoint.y,
-      Style.CrossPoint.radius * coordinate.ratio,
-      0,
-      Math.PI * 2,
-      true
-    );
+    if (!Settings.screenMode) {
+      ctx.arc(
+        extremePoint.x,
+        extremePoint.y,
+        Style.CrossPoint.radius * coordinate.ratio,
+        0,
+        Math.PI * 2,
+        true
+      );
+    }
     ctx.stroke();
     ctx.fill();
     ctx.restore();
@@ -690,13 +693,12 @@ export default class Draw {
         vector.midDivide.leftMidDivideCurves,
         // vector.midDivide.rightMidDivideCurves,
       ]);
-      ctx.setLineDash([8 * coordinate.ratio, 8 * coordinate.ratio])
-      ctx.lineWidth *= Settings.lineWidth
+      ctx.setLineDash([8 * coordinate.ratio, 8 * coordinate.ratio]);
+      ctx.lineWidth *= Settings.lineWidth;
       for (let coves of midCovesArray) {
         help.drawCoves(ctx, coves);
       }
 
-
       // midCovesArray = help.transformCoves([
       //   vector.curves,
       // ]);
@@ -704,7 +706,6 @@ export default class Draw {
       // for (let coves of midCovesArray) {
       //   help.drawCoves(ctx, coves);
       // }
-
     }
     ctx.restore();
 
@@ -717,9 +718,9 @@ export default class Draw {
       vector
     );
     vector.leftLanesCurves &&
-      vector.leftLanesCurves.forEach(this.drawCurveLan.bind(this));
+      vector.leftLanesCurves.forEach((data) => this.drawCurveLan(data, foo));
     vector.rightLanesCurves &&
-      vector.rightLanesCurves.forEach(this.drawCurveLan.bind(this));
+      vector.rightLanesCurves.forEach((data) => this.drawCurveLan(data, foo));
 
     if (foo) {
       const leftEdge = dataService.getCurveRoadEdge(vector.leftEdgeId);
@@ -780,9 +781,14 @@ export default class Draw {
     select && help.setStyle(ctx, style);
     // ctx.lineWidth *= Settings.lineWidth;
 
-    help.drawStyleLine(this.context, () => {
-      help.drawCoves(ctx, coves);
-    }, vector.style, vector.weight);
+    help.drawStyleLine(
+      this.context,
+      () => {
+        help.drawCoves(ctx, coves);
+      },
+      vector.style,
+      vector.weight
+    );
 
     // help.drawCoves(ctx, coves);
     ctx.restore();
@@ -792,13 +798,17 @@ export default class Draw {
     }
   }
 
-  drawCurveLan(lines) {
+  drawCurveLan(lines, focus) {
     const [coves] = help.transformCoves([lines]);
     const ctx = this.context;
     ctx.save();
 
     help.setVectorStyle(ctx, null, "CurveLan");
     ctx.lineWidth *= Settings.lineWidth;
+    if (focus) {
+      ctx.strokeStyle = "rgba(255, 153, 0, 1)";
+      ctx.lineWidth *= 2;
+    }
     ctx.setLineDash(Style.Lane.dash);
     help.drawCoves(ctx, coves);
     ctx.restore();
@@ -1015,6 +1025,15 @@ export default class Draw {
         ...style,
         ...stylea,
       };
+    } else if (vector.category === VectorCategory.Point.FixPoint) {
+      const text = dataService.getText(vector?.linkedTextId);
+      if (text) {
+        style = {
+          ...style,
+          fillStyle: text.color,
+          strokeStyle: text.color,
+        };
+      }
     }
 
     if (vector.color) {
@@ -1040,6 +1059,7 @@ export default class Draw {
       ctx.fill();
       ctx.restore();
     };
+
     if (Settings.selectBasePointId === vector.vectorId) {
       style = {
         ...style,
@@ -1130,7 +1150,6 @@ export default class Draw {
     vector.displayPoint &&
       this.drawPoint({ ...vector.center, color: vector.color }, true);
 
-
     // vector.getBound(this.context).forEach(this.drawPoint.bind(this))
   }
 
@@ -1149,9 +1168,13 @@ export default class Draw {
     this.context.scale(width / svgWidth, height / svgHidth);
     const [style, label] = help.getVectorStyle(vector);
     this.context.lineWidth = style.lineWidth / (width / svgWidth);
-    this.context.fillStyle = 'rgba(0,0,0,0)'
-    this.context.strokeStyle = 'rgba(0,0,0,0)'
-    SVGIcons[vector.type].draw(this.context, style.fillStyle || 'rgba(0,0,0,0)', style.strokeStyle || 'rgba(0,0,0,0)');
+    this.context.fillStyle = "rgba(0,0,0,0)";
+    this.context.strokeStyle = "rgba(0,0,0,0)";
+    SVGIcons[vector.type].draw(
+      this.context,
+      style.fillStyle || "rgba(0,0,0,0)",
+      style.strokeStyle || "rgba(0,0,0,0)"
+    );
     this.context.restore();
 
     if (label) {
@@ -1162,15 +1185,20 @@ export default class Draw {
       this.context.lineTo(points[2].x, points[2].y);
       this.context.lineTo(points[3].x, points[3].y);
       this.context.strokeStyle = style.strokeStyle;
-      this.context.fillStyle = 'rgba(255, 153, 0, 0.30)';
+      this.context.fillStyle = "rgba(255, 153, 0, 0.30)";
       this.context.lineWidth = 2 * coordinate.ratio;
       this.context.setLineDash([6 * coordinate.ratio, 2 * coordinate.ratio]);
       this.context.closePath();
       this.context.stroke();
-      this.context.fill()
+      this.context.fill();
       this.context.restore();
       vector.points.forEach((point) =>
-        this.drawPoint({ ...point, fillColor: '#fff', color: style.strokeStyle, radius: 5 })
+        this.drawPoint({
+          ...point,
+          fillColor: "#fff",
+          color: style.strokeStyle,
+          radius: 5,
+        })
       );
     }
   }
@@ -1245,15 +1273,20 @@ export default class Draw {
     ctx.save();
     help.setVectorStyle(ctx, vector);
 
-    help.drawStyleLine(this.context, () => {
-      help.transformCoves([vector.curves]).forEach((coves) => {
-        help.drawCoves(ctx, coves);
-      });
-    }, vector.style, vector.weight);
+    help.drawStyleLine(
+      this.context,
+      () => {
+        help.transformCoves([vector.curves]).forEach((coves) => {
+          help.drawCoves(ctx, coves);
+        });
+      },
+      vector.style,
+      vector.weight
+    );
     ctx.restore();
 
     // if (import.meta.env.DEV) {
-      vector.points.forEach(this.drawPoint.bind(this));
+    vector.points.forEach(this.drawPoint.bind(this));
     // }
   }
 

+ 59 - 57
src/views/graphic/geos/arrow.vue

@@ -3,50 +3,54 @@
     <template v-slot="{ data }">
       <template v-if="data.key === 'color'">
         <Color v-model:color="color">
-          <span class="color" :style="{backgroundColor: color}"></span>
+          <span class="color" :style="{ backgroundColor: color }"></span>
         </Color>
       </template>
     </template>
   </GeoTeleport>
-  <GeoTeleport :menus="typeMenus" v-if="typeMenus" class="type-geo" :active="typeMenus.find(menu => menu.key === type)" />
+  <GeoTeleport
+    :menus="typeMenus"
+    v-if="typeMenus"
+    class="type-geo"
+    :active="typeMenus.find((menu) => menu.key === type)"
+  />
 </template>
 
 <script setup lang="ts">
 import GeoTeleport from "@/views/graphic/geos/geo-teleport.vue";
-import {drawRef, FocusVector, uiType, UIType, useChange} from '@/hook/useGraphic'
-import {computed, ref, UnwrapRef, watch, watchEffect} from "vue";
-import {dataService} from "@/graphic/Service/DataService";
-import GeoActions from "@/graphic/enum/GeoActions"
-import {debounce} from "@/utils";
-import Color from '@/components/color/index.vue'
+
+import { drawRef, FocusVector, uiType, UIType, useChange } from "@/hook/useGraphic";
+import { computed, ref, UnwrapRef, watch, watchEffect } from "vue";
+import { dataService } from "@/graphic/Service/DataService";
+import GeoActions from "@/graphic/enum/GeoActions";
+import { debounce } from "@/utils";
+import Color from "@/components/color/index.vue";
 import VectorCategory from "@/graphic/enum/VectorCategory";
 
-const props = defineProps<{geo: FocusVector}>()
-const vector = computed(() => dataService.getLine(props.geo.vectorId))
-const color = ref("#000000")
-const type = ref(props.geo.category || "SingleArrowLine")
+const props = defineProps<{ geo: FocusVector }>();
+const vector = computed(() => dataService.getLine(props.geo.vectorId));
+const color = ref("#000000");
+const type = ref(props.geo.category || "SingleArrowLine");
 useChange(() => {
-  color.value = vector.value.color
-  type.value = vector.value.category
-})
-
+  color.value = vector.value.color;
+  type.value = vector.value.category;
+});
 
 const syncVector = ([color, type]) => {
-  vector.value.setColor(color)
-  vector.value.setCategory(type)
-  drawRef.value.renderer.autoRedraw()
-  drawRef.value.history.save()
-}
+  vector.value.setColor(color);
+  vector.value.setCategory(type);
+  drawRef.value.renderer.autoRedraw();
+  drawRef.value.history.save();
+};
 
-watch(() => [color.value, type.value], debounce(syncVector, 500))
+watch(() => [color.value, type.value], debounce(syncVector, 500));
 
-
-const typeMenus = ref<UnwrapRef<typeof menus>>()
+const typeMenus = ref<UnwrapRef<typeof menus>>();
 const menus = ref([
   {
-    key: 'color',
-    icon: 'del',
-    text: "颜色"
+    key: "color",
+    icon: "del",
+    text: "颜色",
   },
   {
     key: "type",
@@ -55,47 +59,46 @@ const menus = ref([
     onClick: () => {
       typeMenus.value = !typeMenus.value
         ? [
-          {
-            key: 'SingleArrowLine',
-            icon: 'arrows_s',
-            text: "单向",
-            onClick: () => type.value = "SingleArrowLine"
-          },
-          {
-            key: 'DoubleArrowLine',
-            icon: 'arrows_d',
-            text: "双向",
-            onClick: () => type.value = "DoubleArrowLine"
-          },
-        ]
-      : null
-    }
+            {
+              key: "SingleArrowLine",
+              icon: "arrows_s",
+              text: "单向",
+              onClick: () => (type.value = "SingleArrowLine"),
+            },
+            {
+              key: "DoubleArrowLine",
+              icon: "arrows_d",
+              text: "双向",
+              onClick: () => (type.value = "DoubleArrowLine"),
+            },
+          ]
+        : null;
+    },
   },
   {
-    key: 'copy',
-    icon: 'copy',
+    key: "copy",
+    icon: "copy",
     text: "复制",
     onClick: () => {
-      drawRef.value.uiControl.handleGeo(GeoActions.CopyAction)
-    }
+      drawRef.value.uiControl.handleGeo(GeoActions.CopyAction);
+    },
   },
   {
-    key: 'del',
-    icon: 'del',
+    key: "del",
+    icon: "del",
     text: "删除",
     onClick: () => {
-      drawRef.value.uiControl.handleGeo(GeoActions.DeleteAction)
-    }
-  }
-])
+      drawRef.value.uiControl.handleGeo(GeoActions.DeleteAction);
+    },
+  },
+]);
 
 watchEffect(() => {
-  menus.value[1].icon = type.value === "SingleArrowLine" ? "arrows_s" : "arrows_d"
-})
+  menus.value[1].icon = type.value === "SingleArrowLine" ? "arrows_s" : "arrows_d";
+});
 if (props.geo.category === VectorCategory.Line.NormalLine) {
-  menus.value.shift()
+  menus.value.shift();
 }
-
 </script>
 
 <style scoped lang="scss">
@@ -124,7 +127,6 @@ if (props.geo.category === VectorCategory.Line.NormalLine) {
 .type-geo {
   margin-bottom: 74px;
 }
-
 </style>
 
 <style lang="scss">

+ 13 - 13
src/views/graphic/geos/index.ts

@@ -1,17 +1,16 @@
-import { VectorType } from '@/hook/useGraphic'
-import Arrow from './arrow.vue'
-import Text from './text.vue'
-import Circle from './circle.vue'
-import magnifier from './magnifier.vue'
-import Del from './del.vue'
-import RoadEdge from './roadEdge.vue'
-import Road from './road.vue'
-import NormalLine from './normalLine.vue'
+import { VectorType } from "@/hook/useGraphic";
+import Arrow from "./arrow.vue";
+import Text from "./text.vue";
+import Circle from "./circle.vue";
+import magnifier from "./magnifier.vue";
+import Del from "./del.vue";
+import RoadEdge from "./roadEdge.vue";
+import Road from "./road.vue";
+import NormalLine from "./normalLine.vue";
 import VectorCategory from "@/graphic/enum/VectorCategory";
 import DelAndCopu from "@/views/graphic/geos/delAndCopu.vue";
 
-
-export const GlobalComp = Del
+export const GlobalComp = Del;
 
 export default {
   [VectorType.SVG]: DelAndCopu,
@@ -25,6 +24,7 @@ export default {
   [VectorCategory.Line.NormalLine]: NormalLine,
   [VectorType.CurveLine]: NormalLine,
   [VectorType.Text]: Text,
+  [VectorCategory.Point.FixPoint]: Text,
   [VectorType.Circle]: Circle,
-  [VectorType.Magnifier]: magnifier
-}
+  [VectorType.Magnifier]: magnifier,
+};

+ 49 - 46
src/views/graphic/geos/road.vue

@@ -1,87 +1,90 @@
 <template>
   <div class="layout">
     <GraphicAction class="full-action">
-      <ui-icon type="lock" ctrl @click="clickHandlerFactory(VectorEvents.UnLock)()"/>
+      <ui-icon type="lock" ctrl @click="clickHandlerFactory(VectorEvents.UnLock)()" />
     </GraphicAction>
-    <GeoTeleport :menus="menus" class="geo-teleport-use"  />
+    <GeoTeleport :menus="menus" class="geo-teleport-use" />
   </div>
 </template>
 
 <script setup lang="ts">
 import GeoTeleport from "@/views/graphic/geos/geo-teleport.vue";
 import UiIcon from "@/components/base/components/icon/index.vue";
-import {drawRef, FocusVector, VectorType} from '@/hook/useGraphic'
-import {computed, ref, watchEffect} from "vue";
-import {dataService} from "@/graphic/Service/DataService";
-import GeoActions from "@/graphic/enum/GeoActions"
+import { drawRef, FocusVector, VectorType } from "@/hook/useGraphic";
+import { computed, ref, watchEffect } from "vue";
+import { dataService } from "@/graphic/Service/DataService";
+import GeoActions from "@/graphic/enum/GeoActions";
 import GraphicAction from "@/components/button-pane/index.vue";
 import VectorEvents from "@/graphic/enum/VectorEvents";
 
-const props = defineProps<{geo: FocusVector}>()
-const vector = computed(() => dataService.getRoad(props.geo.vectorId))
+const props = defineProps<{ geo: FocusVector }>();
+const vector = computed(() => dataService.getRoad(props.geo.vectorId));
 const clickHandlerFactory = (key) => {
   return () => {
-    drawRef.value.uiControl.updateVectorForSelectUI(key)
-    console.log(vector)
-  }
-}
-
+    drawRef.value.uiControl.updateVectorForSelectUI(key);
+    console.log(vector);
+  };
+};
 
-const appendMenus = props.geo.type === VectorType.CurveRoad
-  ? [
-    {
-      key: VectorEvents.AddCrossPoint,
-      icon: "control_a",
-      text: "加控制点",
-      onClick: clickHandlerFactory(VectorEvents.AddCrossPoint)
-    },
-    {
-      key: VectorEvents.MinusCrossPoint,
-      icon: "control_d",
-      text: "减控制点" ,
-      onClick: clickHandlerFactory(VectorEvents.MinusCrossPoint)
-    },
-  ]
-  : []
+const appendMenus =
+  props.geo.type === VectorType.CurveRoad
+    ? [
+        {
+          key: VectorEvents.AddCrossPoint,
+          icon: "control_a",
+          text: "加控制点",
+          onClick: clickHandlerFactory(VectorEvents.AddCrossPoint),
+        },
+        {
+          key: VectorEvents.MinusCrossPoint,
+          icon: "control_d",
+          text: "减控制点",
+          onClick: clickHandlerFactory(VectorEvents.MinusCrossPoint),
+        },
+      ]
+    : [];
 const menus = ref([
   {
     key: VectorEvents.AddLane,
     icon: "lane_a",
-    text: "加车道" ,
-    onClick: clickHandlerFactory(VectorEvents.AddLane)
+    text: "加车道",
+    onClick: clickHandlerFactory(VectorEvents.AddLane),
   },
   {
     key: VectorEvents.DelLane,
     icon: "lane_d",
     text: "减车道",
-    onClick: clickHandlerFactory(VectorEvents.DelLane)
+    onClick: clickHandlerFactory(VectorEvents.DelLane),
   },
   ...appendMenus,
   {
-    key: 'copy',
-    icon: 'copy',
+    key: "copy",
+    icon: "copy",
     text: "复制",
     onClick: () => {
-      drawRef.value.uiControl.handleGeo(GeoActions.CopyAction)
-    }
+      drawRef.value.uiControl.handleGeo(GeoActions.CopyAction);
+    },
   },
   {
-    key: 'del',
-    icon: 'del',
+    key: "del",
+    icon: "del",
     text: "删除",
     onClick: () => {
-      drawRef.value.uiControl.handleGeo(GeoActions.DeleteAction)
-    }
-  }
-])
+      drawRef.value.uiControl.handleGeo(GeoActions.DeleteAction);
+    },
+  },
+]);
 
-console.log(vector.value)
+console.log(vector.value);
 watchEffect(() => {
   if (vector.value) {
-    menus.value[1].disabled = vector.value?.leftLanes.length + vector.value?.rightLanes.length + vector.value?.singleLanes.length === 0
+    menus.value[1].disabled =
+      vector.value?.leftLanes.length +
+        vector.value?.rightLanes.length +
+        vector.value?.singleLanes.length ===
+      0;
   }
-})
-
+});
 </script>
 
 <style scoped lang="scss">

+ 56 - 54
src/views/graphic/geos/roadEdge.vue

@@ -1,109 +1,109 @@
 <template>
-  <GeoTeleport :menus="menus" class="geo-teleport-use" :active="active"/>
-  <GeoTeleport :menus="childMenus" v-if="childMenus" class="type-geo"/>
+  <GeoTeleport :menus="menus" class="geo-teleport-use" :active="active" />
+  <GeoTeleport :menus="childMenus" v-if="childMenus" class="type-geo" />
 </template>
 
 <script setup lang="ts">
 import GeoTeleport from "@/views/graphic/geos/geo-teleport.vue";
-import {drawRef, FocusVector, VectorType} from '@/hook/useGraphic'
-import {computed, ref, toRaw, UnwrapRef} from "vue";
-import {dataService} from "@/graphic/Service/DataService";
-import GeoActions from "@/graphic/enum/GeoActions"
-import {UITypeExtend} from "@/views/graphic/menus";
+import { drawRef, FocusVector, VectorType } from "@/hook/useGraphic";
+import { computed, ref, toRaw, UnwrapRef } from "vue";
+import { dataService } from "@/graphic/Service/DataService";
+import GeoActions from "@/graphic/enum/GeoActions";
+import { UITypeExtend } from "@/views/graphic/menus";
 import VectorEvents from "@/graphic/enum/VectorEvents";
 import VectorStyle from "@/graphic/enum/VectorStyle";
 import VectorWeight from "@/graphic/enum/VectorWeight";
 
-
-const props = defineProps<{ geo: FocusVector }>()
-const vector = computed(() => dataService.getRoadEdge(props.geo.vectorId))
-console.error(vector.value)
+const props = defineProps<{ geo: FocusVector }>();
+const vector = computed(() => dataService.getRoadEdge(props.geo.vectorId));
+console.error(vector.value);
 
 const clickHandlerFactory = (key) => {
-  return () => drawRef.value.uiControl.updateVectorForSelectUI(key)
-}
+  return () => drawRef.value.uiControl.updateVectorForSelectUI(key);
+};
 
 const lineTypeMenu = [
   {
     key: VectorStyle.SingleSolidLine,
     icon: "line_sf",
     text: "单实线",
-    onClick: clickHandlerFactory(VectorStyle.SingleSolidLine)
+    onClick: clickHandlerFactory(VectorStyle.SingleSolidLine),
   },
   {
     key: VectorStyle.SingleDashedLine,
     icon: "line_sd",
     text: "单虚线",
-    onClick: clickHandlerFactory(VectorStyle.SingleDashedLine)
+    onClick: clickHandlerFactory(VectorStyle.SingleDashedLine),
   },
   {
     hide: props.geo.type === VectorType.CurveRoadEdge,
     key: VectorStyle.DoubleSolidLine,
     icon: "line_df",
     text: "双实线",
-    onClick: clickHandlerFactory(VectorStyle.DoubleSolidLine)
+    onClick: clickHandlerFactory(VectorStyle.DoubleSolidLine),
   },
   {
     hide: props.geo.type === VectorType.CurveRoadEdge,
     key: VectorStyle.DoubleDashedLine,
     icon: "line_dd",
     text: "双虚线",
-    onClick: clickHandlerFactory(VectorStyle.DoubleDashedLine)
+    onClick: clickHandlerFactory(VectorStyle.DoubleDashedLine),
   },
   {
     hide: props.geo.type === VectorType.CurveRoadEdge,
     key: VectorStyle.BrokenLine,
     icon: "line_broken",
     text: "折线",
-    onClick: clickHandlerFactory(VectorStyle.BrokenLine)
+    onClick: clickHandlerFactory(VectorStyle.BrokenLine),
   },
   {
     key: VectorStyle.PointDrawLine,
     icon: "line_dot",
     text: "点画线",
-    onClick: clickHandlerFactory(VectorStyle.PointDrawLine)
+    onClick: clickHandlerFactory(VectorStyle.PointDrawLine),
   },
   // {key: VectorStyle.Greenbelt, icon: "treelawn", text: "绿化带 ", onClick: clickHandlerFactory(VectorStyle.Greenbelt)},
-]
+];
 
 const lineWidthMenu = [
   {
     key: VectorWeight.Bold,
-    icon: 'l_thick',
+    icon: "l_thick",
     text: "宽度",
     onClick: () => {
-      clickHandlerFactory(VectorWeight.Thinning)()
-      menus.value[1] = lineWidthMenu[1]
-    }
+      clickHandlerFactory(VectorWeight.Thinning)();
+      menus.value[1] = lineWidthMenu[1];
+    },
   },
   {
     key: VectorWeight.Thinning,
-    icon: 'l_thin',
+    icon: "l_thin",
     text: "宽度",
     onClick: () => {
-      clickHandlerFactory(VectorWeight.Bold)()
-      menus.value[1] = lineWidthMenu[0]
-    }
+      clickHandlerFactory(VectorWeight.Bold)();
+      menus.value[1] = lineWidthMenu[0];
+    },
   },
-]
+];
 
-const appendMenus = props.geo.type === VectorType.CurveRoadEdge
-  ? [
-      {
-        key: VectorEvents.AddCrossPoint,
-        icon: "control_a",
-        text: "加控制点",
-        onClick: clickHandlerFactory(VectorEvents.AddCrossPoint)
-      },
-      {
-        key: VectorEvents.MinusCrossPoint,
-        icon: "control_d",
-        text: "减控制点",
-        onClick: clickHandlerFactory(VectorEvents.MinusCrossPoint)
-      },
-    ]
-  : []
-const childMenus = ref<UnwrapRef<typeof menus>>()
+const appendMenus =
+  props.geo.type === VectorType.CurveRoadEdge
+    ? [
+        // {
+        //   key: VectorEvents.AddCrossPoint,
+        //   icon: "control_a",
+        //   text: "加控制点",
+        //   onClick: clickHandlerFactory(VectorEvents.AddCrossPoint)
+        // },
+        // {
+        //   key: VectorEvents.MinusCrossPoint,
+        //   icon: "control_d",
+        //   text: "减控制点",
+        //   onClick: clickHandlerFactory(VectorEvents.MinusCrossPoint)
+        // },
+      ]
+    : [];
+const childMenus = ref<UnwrapRef<typeof menus>>();
 // console.log(vector.value)
 const menus = ref([
   {
@@ -111,18 +111,20 @@ const menus = ref([
     text: "单实线",
     icon: "line",
     onClick() {
-      childMenus.value = toRaw(childMenus.value) === lineTypeMenu ? null : lineTypeMenu
-    }
+      childMenus.value = toRaw(childMenus.value) === lineTypeMenu ? null : lineTypeMenu;
+    },
   },
   vector.value?.weight === VectorWeight.Bold ? lineWidthMenu[0] : lineWidthMenu[1],
-  ...appendMenus
-])
+  ...appendMenus,
+]);
 
 const active = computed(() =>
-  toRaw(childMenus.value) === lineTypeMenu ? menus.value[0]
-    : toRaw(childMenus.value) === lineWidthMenu ? menus.value[1] : null
-)
-
+  toRaw(childMenus.value) === lineTypeMenu
+    ? menus.value[0]
+    : toRaw(childMenus.value) === lineWidthMenu
+    ? menus.value[1]
+    : null
+);
 </script>
 
 <style scoped lang="scss">

+ 64 - 60
src/views/graphic/geos/text.vue

@@ -3,17 +3,17 @@
     <template v-slot="{ data }">
       <template v-if="data.key === 'color'">
         <Color v-model:color="color">
-          <span class="color" :style="{backgroundColor: color}"></span>
+          <span class="color" :style="{ backgroundColor: color }"></span>
         </Color>
       </template>
       <template v-if="data.key === 'fontSize'">
         <ui-input
-            type="select"
-            :options="sizeOption"
-            class="geo-input"
-            dire="top"
-            floatingClass="select-floating"
-            v-model="size"
+          type="select"
+          :options="sizeOption"
+          class="geo-input"
+          dire="top"
+          floatingClass="select-floating"
+          v-model="size"
         />
         <span class="font-size">{{ size }}</span>
       </template>
@@ -23,12 +23,12 @@
   <div class="text-model" v-if="updateText">
     <div class="text-input">
       <ui-input
-          ref="inputTextRef"
-          v-model="text"
-          width="100%"
-          :maxlength="20"
-          height="64px"
-          @blur="updateText = false"
+        ref="inputTextRef"
+        v-model="text"
+        width="100%"
+        :maxlength="20"
+        height="64px"
+        @blur="updateText = false"
       />
     </div>
   </div>
@@ -38,76 +38,80 @@
 import GeoTeleport from "@/views/graphic/geos/geo-teleport.vue";
 import UiInput from "@/components/base/components/input/index.vue";
 import UiIcon from "@/components/base/components/icon/index.vue";
-import {FocusVector, drawRef, useChange} from '@/hook/useGraphic'
-import {computed, ref, watch, watchEffect} from "vue";
-import {dataService} from "@/graphic/Service/DataService";
-import {debounce} from '@/utils'
+import { FocusVector, drawRef, useChange } from "@/hook/useGraphic";
+import { computed, ref, watch, watchEffect } from "vue";
+import { dataService } from "@/graphic/Service/DataService";
+import { debounce } from "@/utils";
 import GeoActions from "@/graphic/enum/GeoActions";
 import Color from "@/components/color/index.vue";
-
-const props = defineProps<{geo: FocusVector}>()
-const inputTextRef = ref()
-const updateText = ref(false)
-const vector = computed(() => dataService.getText(props.geo.vectorId))
-const text = ref("")
-const color = ref("#000000")
-const size = ref(18)
+import VectorCategory from "@/graphic/enum/VectorCategory";
+
+const props = defineProps<{ geo: FocusVector }>();
+const inputTextRef = ref();
+const updateText = ref(false);
+const vector = computed(() => {
+  let vectorId = props.geo.vectorId;
+  if (props.geo.category === VectorCategory.Point.FixPoint) {
+    vectorId = dataService.getPoint(props.geo.vectorId)?.linkedTextId;
+  }
+  return dataService.getText(vectorId);
+});
+const text = ref("");
+const color = ref("#000000");
+const size = ref(18);
 
 const syncVector = ([text, size, color]) => {
-  console.log(text, size, color)
-  vector.value.setValue(text)
-  vector.value.setColor(color)
-  vector.value.setFontSize(size)
-  drawRef.value.renderer.autoRedraw()
-  drawRef.value.history.save()
-}
+  console.log(text, size, color);
+  vector.value.setValue(text);
+  vector.value.setColor(color);
+  vector.value.setFontSize(size);
+  drawRef.value.renderer.autoRedraw();
+  drawRef.value.history.save();
+};
 
 watchEffect(() => {
   if (inputTextRef.value) {
-    inputTextRef.value.vmRef.input.focus()
+    inputTextRef.value.vmRef.input.focus();
   }
-})
+});
 
 useChange(() => {
-  color.value = vector.value.color
-  size.value = vector.value.fontSize
-  text.value = vector.value.value
-})
-watch(
-  () => [text.value, size.value, color.value],
-  debounce(syncVector, 500)
-)
+  console.log(vector.value);
+  color.value = vector.value.color;
+  size.value = vector.value.fontSize;
+  text.value = vector.value.value;
+});
+watch(() => [text.value, size.value, color.value], debounce(syncVector, 500));
 
 const sizeOption = [];
 for (let i = 10; i < 30; i++) {
-  sizeOption.push({label: i, value: i});
+  sizeOption.push({ label: i, value: i });
 }
 const menus = [
   {
-    key: 'color',
-    icon: 'del',
-    text: "颜色"
+    key: "color",
+    icon: "del",
+    text: "颜色",
   },
   {
-    key: 'fontSize',
-    text: "文字大小"
+    key: "fontSize",
+    text: "文字大小",
   },
   {
-    key: 'text',
-    icon: 'edit',
+    key: "text",
+    icon: "edit",
     text: "修改文字",
-    onClick: () => updateText.value = true
+    onClick: () => (updateText.value = true),
   },
   {
-    key: 'del',
-    icon: 'del',
+    key: "del",
+    icon: "del",
     text: "删除",
     onClick: () => {
-      drawRef.value.uiControl.handleGeo(GeoActions.DeleteAction)
-    }
-  }
-]
-
+      drawRef.value.uiControl.handleGeo(GeoActions.DeleteAction);
+    },
+  },
+];
 </script>
 
 <style scoped lang="scss">
@@ -145,8 +149,8 @@ const menus = [
   top: 0;
   right: 0;
   bottom: 0;
-  background-color: rgba(0,0,0,.8);
-  z-index: 4
+  background-color: rgba(0, 0, 0, 0.8);
+  z-index: 4;
 }
 
 .text-input {

+ 59 - 44
src/views/graphic/index.vue

@@ -1,19 +1,17 @@
 <template>
-  <MainPanel
-      :menus="store.menus as any"
-      :active-menu-key="store.activeMenuKey.value">
+  <MainPanel :menus="store.menus as any" :active-menu-key="store.activeMenuKey.value">
     <template v-slot:header>
-      <Header  />
+      <Header />
     </template>
     <Container />
     <ChildMenus
-        v-if="store.child.value && store.activeMenuKey.value === UITypeExtend.template"
-        :menus="store.child.value as any"
-        @quit="store.child.value = null"
+      v-if="store.child.value && store.activeMenuKey.value === UITypeExtend.template"
+      :menus="store.child.value as any"
+      @quit="store.child.value = null"
     />
     <ImageLabel
-        v-if="store.activeMenuKey.value === UITypeExtend.image"
-        @quit="uiType.change(null)"
+      v-if="store.activeMenuKey.value === UITypeExtend.image"
+      @quit="uiType.change(null)"
     />
 
     <GraphicAction class="full-action" v-if="!graphicState.continuedMode">
@@ -24,59 +22,76 @@
       />
     </GraphicAction>
     <Confirm v-if="graphicState.continuedMode" />
-    <Component :is="geoComponent as any" v-else-if="geoComponent" :geo="currentVector"/>
+    <Component
+      :is="geoComponent as any"
+      v-else-if="geoComponent"
+      :geo="currentVector"
+      :key="currentVector.vectorId"
+    />
   </MainPanel>
 </template>
 
 <script lang="ts" setup>
-import MainPanel from '@/components/main-panel/index.vue'
+import MainPanel from "@/components/main-panel/index.vue";
 import ChildMenus from "@/views/graphic/childMenus.vue";
-import Header from './header.vue'
-import Container from './container.vue'
-import GraphicAction from '@/components/button-pane/index.vue'
+import Header from "./header.vue";
+import Container from "./container.vue";
+import GraphicAction from "@/components/button-pane/index.vue";
 import UiIcon from "@/components/base/components/icon/index.vue";
-import Confirm from './confirm.vue'
+import Confirm from "./confirm.vue";
 import ImageLabel from "./imageLabel.vue";
-import {router} from '@/router'
-import {computed, watchEffect} from "vue";
-import {customMap} from '@/hook'
-import {focusMenuRaw, generateMixMenus, mainMenusRaw, photoMenusRaw, Mode, UITypeExtend} from './menus'
-import {currentVector, drawRef, graphicState, uiType} from "@/hook/useGraphic";
-import geos, {GlobalComp} from "./geos/index";
+import { router } from "@/router";
+
+import { computed, watchEffect } from "vue";
+import { customMap } from "@/hook";
+import {
+  focusMenuRaw,
+  generateMixMenus,
+  mainMenusRaw,
+  photoMenusRaw,
+  Mode,
+  UITypeExtend,
+} from "./menus";
+import { currentVector, drawRef, graphicState, uiType } from "@/hook/useGraphic";
+import geos, { GlobalComp } from "./geos/index";
 
 const menusRaws = computed(() => {
-  const mode = Number(router.currentRoute.value.params.mode) as Mode
-  return mode === Mode.Photo ? photoMenusRaw : mainMenusRaw
-})
-const store = computed(() => generateMixMenus(
-  "extend",
-  (mainMenuRaw) => ({
-    ...mainMenuRaw,
-    title: mainMenuRaw.text,
-    name: mainMenuRaw.key,
-    isRoute: false,
-    // icon: 'menu',
-    bottom:  mainMenuRaw.key === UITypeExtend.setup
-  }),
-  menusRaws.value
-))
+  const mode = Number(router.currentRoute.value.params.mode) as Mode;
+  return mode === Mode.Photo ? photoMenusRaw : mainMenusRaw;
+});
+const store = computed(() =>
+  generateMixMenus(
+    "extend",
+    (mainMenuRaw) => ({
+      ...mainMenuRaw,
+      title: mainMenuRaw.text,
+      name: mainMenuRaw.key,
+      isRoute: false,
+      // icon: 'menu',
+      bottom: mainMenuRaw.key === UITypeExtend.setup,
+    }),
+    menusRaws.value
+  )
+);
 
 watchEffect(() => {
   if (graphicState.value.continuedMode) {
-    customMap.sysView = 'full'
+    customMap.sysView = "full";
   } else {
-    customMap.sysView = 'auto'
+    customMap.sysView = "auto";
   }
-})
+});
 
-const focusMenus = computed(() => focusMenuRaw[currentVector.value?.type])
+const focusMenus = computed(() => focusMenuRaw[currentVector.value?.type]);
 const geoComponent = computed(() => {
   if (currentVector.value) {
-    console.log(currentVector.value)
-    return geos[currentVector.value?.type] || geos[currentVector.value?.category] || GlobalComp
+    console.log(currentVector.value);
+    return (
+      geos[currentVector.value?.category] || geos[currentVector.value?.type] || GlobalComp
+    );
   }
-})
-const isFull = computed(() => customMap.sysView === 'full' )
+});
+const isFull = computed(() => customMap.sysView === "full");
 </script>
 
 <style lang="scss" scoped>

+ 96 - 82
src/views/photos/index.vue

@@ -1,42 +1,57 @@
 <template>
   <MainPanel>
     <template v-slot:header>
-      <Header :count="selects.length" :title="`全部照片(${photos.length})`" type="return" :on-back="() => router.back()">
-        <ui-button type="primary" @click="selectMode = !selectMode" width="96px" v-if="sortPhotos.length">
-          {{ selectMode ? '取消' : '选择' }}
+      <Header
+        :count="selects.length"
+        :title="`全部照片(${photos.length})`"
+        type="return"
+        :on-back="() => router.back()"
+      >
+        <ui-button
+          type="primary"
+          @click="selectMode = !selectMode"
+          width="96px"
+          v-if="sortPhotos.length"
+        >
+          {{ selectMode ? "取消" : "选择" }}
         </ui-button>
       </Header>
     </template>
 
     <Photos
       undata-msg="无照片。请返回场景拍照以获取照片"
-      :getURL="data => data.urlRaw || data.url"
+      :getURL="(data) => data.urlRaw || data.url"
       v-model:active="active"
       v-model:selects="selects"
       :select-mode="selectMode"
       :data="sortPhotos"
     />
-<!--    @click="router.push(writeRouteName.scene)"-->
+    <!--    @click="router.push(writeRouteName.scene)"-->
     <ButtonPane class="back fun-ctrl" v-if="!selectMode">
       <ui-icon type="photo" class="icon" />
       <ui-input
-          type="select"
-          :options="photoOptions"
-          dire="top"
-          class="select"
-          v-model="photoType"
+        type="select"
+        :options="photoOptions"
+        dire="top"
+        class="select"
+        v-model="photoType"
       />
     </ButtonPane>
 
-    <ActionMenus class="select-menus" :menus="selectMenus" dire="row" v-if="selects.length" />
+    <ActionMenus
+      class="select-menus"
+      :menus="selectMenus"
+      dire="row"
+      v-if="selects.length"
+    />
   </MainPanel>
 
   <FillSlide
-      :data="sortPhotos"
-      v-model:active="active"
-      @quit="active = null"
-      v-if="active"
-      :getURL="data => data.urlRaw || data.url"
+    :data="sortPhotos"
+    v-model:active="active"
+    @quit="active = null"
+    v-if="active"
+    :getURL="(data) => data.urlRaw || data.url"
   >
     <template v-slot:foot>
       <ActionMenus class="menus" :menus="menus" dire="row" />
@@ -45,146 +60,146 @@
 </template>
 
 <script setup lang="ts">
-import MainPanel from '@/components/main-panel/index.vue'
-import FillSlide from '@/components/fill-slide/index.vue'
-import Header from '@/components/photos/header.vue'
-import {PhotoRaw, photos} from '@/store/photos'
+import MainPanel from "@/components/main-panel/index.vue";
+import FillSlide from "@/components/fill-slide/index.vue";
+import Header from "@/components/photos/header.vue";
+import { PhotoRaw, photos } from "@/store/photos";
 import UiIcon from "@/components/base/components/icon/index.vue";
-import {router, writeRouteName} from '@/router'
+import { router, writeRouteName } from "@/router";
 import ButtonPane from "@/components/button-pane/index.vue";
-import {computed, onDeactivated, ref, watchEffect} from "vue";
-import {Mode} from '@/views/graphic/menus'
+import { computed, onDeactivated, ref, watchEffect } from "vue";
+import { Mode } from "@/views/graphic/menus";
 import UiButton from "@/components/base/components/button/index.vue";
-import Photos from '@/components/photos'
+import Photos from "@/components/photos/index.vue";
 import ActionMenus from "@/components/group-button/index.vue";
-import {useConfirm} from "@/hook";
+import { useConfirm } from "@/hook";
 import UiInput from "@/components/base/components/input/index.vue";
-import {api} from "@/store/sync";
-import {getId} from "@/utils";
+import { api } from "@/store/sync";
+import { getId } from "@/utils";
 
-const sortPhotos = computed(() => [...photos.value].reverse())
-const active = ref<PhotoRaw>()
-const selectMode = ref(false)
-const selects = ref<PhotoRaw[]>([])
+const sortPhotos = computed(() => [...photos.value].reverse());
+const active = ref<PhotoRaw>();
+const selectMode = ref(false);
+const selects = ref<PhotoRaw[]>([]);
 const menus = [
   {
     key: "road",
     text: "现场绘图",
     icon: "draw_s",
-    onClick: () => gotoDraw(Mode.Road)
+    onClick: () => gotoDraw(Mode.Road),
   },
   {
     key: "accident",
     icon: "label",
     text: "照片标注",
-    onClick: () => gotoDraw(Mode.Photo)
+    onClick: () => gotoDraw(Mode.Photo),
   },
   {
     key: "del",
     icon: "del",
     text: "删除",
-    onClick: () => delPhoto()
-  }
-]
+    onClick: () => delPhoto(),
+  },
+];
 
 const selectMenus = [
   {
     key: "del",
     icon: "del",
     text: "删除",
-    onClick: () => delSelects()
+    onClick: () => delSelects(),
   },
-]
+];
 
-const photoType = ref<string>()
+const photoType = ref<string>();
 const photoOptions = [
-  {value: "photograph", label: "相机拍照"},
-  {value: "selectPhotoAlbum", label: "相册选择"},
-  {value: "scene", label: "场景截图"},
-]
+  { value: "photograph", label: "相机拍照" },
+  { value: "selectPhotoAlbum", label: "相册选择" },
+  { value: "scene", label: "场景截图" },
+];
 
 watchEffect(() => {
   if (photoType.value) {
     if (photoType.value === "scene") {
-      router.push(writeRouteName.scene)
+      router.push(writeRouteName.scene);
     } else {
-      api[photoType.value]()
-        .then(url => {
-          photos.value.push({
-            id: getId(),
-            url,
-            urlRaw: url,
-            time: new Date().getTime(),
-            meterPerPixel: null,
-            measures: [],
-            baseLines: [],
-            fixPoints: [],
-            basePoints: []
-          })
-        })
+      api[photoType.value]().then((url) => {
+        photos.value.push({
+          id: getId(),
+          url,
+          urlRaw: url,
+          time: new Date().getTime(),
+          meterPerPixel: null,
+          measures: [],
+          baseLines: [],
+          fixPoints: [],
+          basePoints: [],
+        });
+      });
     }
-    photoType.value = null
+    photoType.value = null;
   }
-})
+});
 
 watchEffect(() => {
   if (!selectMode.value) {
-    selects.value = []
+    selects.value = [];
   }
-})
+});
 
 const delPhotoRaw = (photo = active.value) => {
-  const index = photos.value.indexOf(photo)
-  const reset = active.value ? photos.value.indexOf(active.value) : -1
+  const index = photos.value.indexOf(photo);
+  const reset = active.value ? photos.value.indexOf(active.value) : -1;
   if (~index) {
-    photos.value.splice(index, 1)
+    photos.value.splice(index, 1);
   }
   if (~reset) {
     if (reset >= photos.value.length) {
       if (photos.value.length) {
-        active.value = photos.value[photos.value.length - 1]
+        active.value = photos.value[photos.value.length - 1];
       } else {
-        active.value = null
+        active.value = null;
       }
     } else {
-      active.value = photos.value[reset]
+      active.value = photos.value[reset];
     }
   }
-}
+};
 const delPhoto = async (photo = active.value) => {
   if (await useConfirm(`确定要删除此数据?`)) {
-    delPhotoRaw(photo)
+    delPhotoRaw(photo);
   }
-}
+};
 const delSelects = async () => {
   if (await useConfirm(`确定要删除这${selects.value.length}项数据?`)) {
     while (selects.value.length) {
-      delPhotoRaw(selects.value[0])
-      selects.value.shift()
+      delPhotoRaw(selects.value[0]);
+      selects.value.shift();
     }
     if (!sortPhotos.value.length) {
-      selectMode.value = false
+      selectMode.value = false;
     }
   }
-}
+};
 
 const gotoDraw = (mode: Mode) => {
   router.push({
     name: writeRouteName.graphic,
-    params: {mode, id: active.value.id, action: 'add'}
-  })
-}
+    params: { mode, id: active.value.id, action: "add" },
+  });
+};
 
 onDeactivated(() => {
-  active.value = null
-})
+  active.value = null;
+  selectMode.value = false;
+});
 </script>
 
 <style scoped lang="scss">
 .fun-ctrl {
   color: #fff;
   font-size: 20px;
-  transition: color .3s ease;
+  transition: color 0.3s ease;
 
   .icon {
     position: absolute;
@@ -217,5 +232,4 @@ onDeactivated(() => {
   transform: translateX(-50%);
   bottom: var(--boundMargin);
 }
-
 </style>

+ 76 - 73
src/views/roads/index.vue

@@ -1,21 +1,26 @@
 <template>
   <MainPanel>
     <template v-slot:header>
-      <Header :count="selects.length" :title="`现场图管理(${sortPhotos.length})`" type="return_l" :on-back="() => api.closePage()">
+      <Header
+        :count="selects.length"
+        :title="`现场图管理(${sortPhotos.length})`"
+        type="return_l"
+        :on-back="() => api.closePage()"
+      >
         <ui-button
-            :type="selectMode ? 'primary' : 'normal'"
-            @click="selectMode = !selectMode"
-            width="96px"
-            style="margin-right: 16px"
-            v-if="sortPhotos.length"
+          :type="selectMode ? 'primary' : 'normal'"
+          @click="selectMode = !selectMode"
+          width="96px"
+          style="margin-right: 16px"
+          v-if="sortPhotos.length"
         >
-          {{ selectMode ? '取消' : '选择' }}
+          {{ selectMode ? "取消" : "选择" }}
         </ui-button>
         <ui-button
-            v-if="!selectMode"
-            type="primary"
-            @click="router.push({name: writeRouteName.photos})"
-            width="96px"
+          v-if="!selectMode"
+          type="primary"
+          @click="router.push({ name: writeRouteName.photos })"
+          width="96px"
         >
           新增
         </ui-button>
@@ -23,28 +28,32 @@
     </template>
 
     <Photos
-        undata-msg="无现场图。请点击右上角按钮绘制现场图。"
-        v-model:active="active"
-        v-model:selects="selects"
-        :select-mode="selectMode"
-        :data="sortPhotos"
-        :getURL="data => data?.table?.url || data.url"
+      undata-msg="无现场图。请点击右上角按钮绘制现场图。"
+      v-model:active="active"
+      v-model:selects="selects"
+      :select-mode="selectMode"
+      :data="sortPhotos"
+      :getURL="(data) => data?.table?.url || data.url"
     >
-      <template v-slot="{data}">
-        <p>{{ data.title || '默认标题' }}</p>
+      <template v-slot="{ data }">
+        <p>{{ data.title || "默认标题" }}</p>
       </template>
     </Photos>
 
-
-    <ActionMenus class="select-menus" :menus="selectMenus" dire="row" v-if="selects.length" />
+    <ActionMenus
+      class="select-menus"
+      :menus="selectMenus"
+      dire="row"
+      v-if="selects.length"
+    />
   </MainPanel>
 
   <FillSlide
-      :getURL="data => data?.table?.url || data.url"
-      :data="sortPhotos"
-      v-model:active="active"
-      @quit="active = null"
-      v-if="active"
+    :getURL="(data) => data?.table?.url || data.url"
+    :data="sortPhotos"
+    v-model:active="active"
+    @quit="active = null"
+    v-if="active"
   >
     <template v-slot:foot>
       <ActionMenus class="menus" :menus="menus" dire="row" />
@@ -53,106 +62,102 @@
 </template>
 
 <script setup lang="ts">
-import MainPanel from '@/components/main-panel/index.vue'
-import FillSlide from '@/components/fill-slide/index.vue'
-import {roadPhotos, RoadPhoto} from '@/store/roadPhotos'
+import MainPanel from "@/components/main-panel/index.vue";
+import FillSlide from "@/components/fill-slide/index.vue";
+import { roadPhotos, RoadPhoto } from "@/store/roadPhotos";
 import ActionMenus from "@/components/group-button/index.vue";
-import {router, writeRouteName} from '@/router'
-import {computed, onDeactivated, ref, watchEffect} from "vue";
-import {Mode} from '@/views/graphic/menus'
+import { router, writeRouteName } from "@/router";
+import { computed, onDeactivated, ref, watchEffect } from "vue";
+import { Mode } from "@/views/graphic/menus";
 import UiButton from "@/components/base/components/button/index.vue";
 import Header from "@/components/photos/header.vue";
 import Photos from "@/components/photos/index.vue";
 import ButtonPane from "@/components/button-pane/index.vue";
 import UiIcon from "@/components/base/components/icon/index.vue";
-import {useConfirm} from "@/hook";
-import {api} from "@/store/sync";
-import {photos} from "@/store/photos";
-
-
-const sortPhotos = computed(() => [...roadPhotos.value].reverse())
-const active = ref<RoadPhoto>()
-const selectMode = ref(false)
-const selects = ref<RoadPhoto[]>([])
+import { useConfirm } from "@/hook";
+import { api } from "@/store/sync";
+import { photos } from "@/store/photos";
+
+const sortPhotos = computed(() => [...roadPhotos.value].reverse());
+const active = ref<RoadPhoto>();
+const selectMode = ref(false);
+const selects = ref<RoadPhoto[]>([]);
 const menus = [
   {
     key: "road",
     icon: "edit",
     text: "修改",
-    onClick: () => gotoDraw()
+    onClick: () => gotoDraw(),
   },
   {
     key: "del",
     icon: "del",
     text: "删除",
-    onClick: () => delPhoto()
-  }
-]
+    onClick: () => delPhoto(),
+  },
+];
 
 const selectMenus = [
   {
     key: "del",
     icon: "del",
     text: "删除",
-    onClick: () => delSelects()
+    onClick: () => delSelects(),
   },
-]
+];
 
 watchEffect(() => {
   if (!selectMode.value) {
-    selects.value = []
+    selects.value = [];
   }
-})
+});
 
 const delPhotoRaw = (roadPhoto = active.value) => {
-  const index = roadPhotos.value.indexOf(roadPhoto)
-  const reset = active.value ? roadPhotos.value.indexOf(active.value) : -1
+  const index = roadPhotos.value.indexOf(roadPhoto);
+  const reset = active.value ? roadPhotos.value.indexOf(active.value) : -1;
   if (~index) {
-    roadPhotos.value.splice(index, 1)
+    roadPhotos.value.splice(index, 1);
   }
   if (~reset) {
     if (reset >= roadPhotos.value.length) {
       if (roadPhotos.value.length) {
-        active.value = roadPhotos.value[roadPhotos.value.length - 1]
+        active.value = roadPhotos.value[roadPhotos.value.length - 1];
       } else {
-        active.value = null
+        active.value = null;
       }
     } else {
-      active.value = roadPhotos.value[reset]
+      active.value = roadPhotos.value[reset];
     }
   }
-}
+};
 
 const delPhoto = async (photo = active.value) => {
   if (await useConfirm(`确定要删除此数据?`)) {
-    delPhotoRaw(photo)
+    delPhotoRaw(photo);
   }
-
-}
+};
 const delSelects = async () => {
   if (await useConfirm(`确定要删除这${selects.value.length}项数据?`)) {
     while (selects.value.length) {
-      delPhotoRaw(selects.value[0])
-      selects.value.shift()
+      delPhotoRaw(selects.value[0]);
+      selects.value.shift();
     }
     if (!sortPhotos.value.length) {
-      selectMode.value = false
+      selectMode.value = false;
     }
   }
-}
-
+};
 
 const gotoDraw = () => {
   router.push({
     name: writeRouteName.graphic,
-    params: {mode: Mode.Road, id: active.value.id, action: 'update'}
-  })
-}
+    params: { mode: Mode.Road, id: active.value.id, action: "update" },
+  });
+};
 
 onDeactivated(() => {
-  active.value = null
-})
-
+  active.value = null;
+});
 </script>
 
 <style scoped lang="scss">
@@ -170,7 +175,7 @@ onDeactivated(() => {
 .fun-ctrl {
   color: #fff;
   font-size: 20px;
-  transition: color .3s ease;
+  transition: color 0.3s ease;
 
   .icon {
     position: absolute;
@@ -184,11 +189,9 @@ onDeactivated(() => {
   bottom: var(--boundMargin);
 }
 
-
 .select-menus {
   left: 50%;
   transform: translateX(-50%);
   bottom: var(--boundMargin);
 }
-
 </style>

+ 109 - 112
src/views/roads/tabulation.vue

@@ -2,76 +2,70 @@
   <MainPanel>
     <template v-slot:header>
       <Header title="现场绘图 | 制表" :on-back="onBack" type="return">
-        <ui-button
-            type="primary"
-            @click="saveHandler"
-            width="96px"
-        >
-          保存
-        </ui-button>
+        <ui-button type="primary" @click="saveHandler" width="96px"> 保存 </ui-button>
       </Header>
     </template>
 
-    <div class="tab-layout" v-if="roadPhoto" :class="{downMode}">
+    <div class="tab-layout" v-if="roadPhoto" :class="{ downMode }">
       <div class="content" ref="layoutRef">
         <table>
           <tr>
             <td class="value title" colspan="6" height="64">
-              <span v-if="downMode">{{roadPhoto.title}}</span>
+              <span v-if="downMode">{{ roadPhoto.title }}</span>
               <ui-input
-                  v-else
-                  type="text"
-                  @input="input"
-                  v-model="roadPhoto.title"
-                  @blur="history.push"
+                v-else
+                type="text"
+                @input="input"
+                v-model="roadPhoto.title"
+                @blur="history.push"
               />
             </td>
           </tr>
           <tr>
             <td class="label" width="150" height="64">到达事故现场时间</td>
             <td class="value">
-              <span v-if="downMode">{{history.value.arrivalTime}}</span>
+              <span v-if="downMode">{{ history.value.arrivalTime }}</span>
               <ui-input
-                  v-else
-                  type="text"
-                  @input="input"
-                  v-model="history.value.arrivalTime"
-                  @blur="history.push"
+                v-else
+                type="text"
+                @input="input"
+                v-model="history.value.arrivalTime"
+                @blur="history.push"
               />
             </td>
             <td class="label" width="100">天气</td>
             <td class="value" width="80">
-              <span v-if="downMode">{{history.value.weather}}</span>
+              <span v-if="downMode">{{ history.value.weather }}</span>
               <ui-input
-                  v-else
-                  type="text"
-                  @input="input"
-                  v-model="history.value.weather"
-                  @blur="history.push"
+                v-else
+                type="text"
+                @input="input"
+                v-model="history.value.weather"
+                @blur="history.push"
               />
             </td>
             <td class="label" width="100">路面性质</td>
             <td class="value" width="150">
-              <span v-if="downMode">{{history.value.conditions}}</span>
+              <span v-if="downMode">{{ history.value.conditions }}</span>
               <ui-input
-                  v-else
-                  type="text"
-                  @input="input"
-                  v-model="history.value.conditions"
-                  @blur="history.push"
+                v-else
+                type="text"
+                @input="input"
+                v-model="history.value.conditions"
+                @blur="history.push"
               />
             </td>
           </tr>
           <tr>
-            <td class="label"  height="64">事故发生地点</td>
+            <td class="label" height="64">事故发生地点</td>
             <td class="value" colspan="5">
-              <span v-if="downMode">{{history.value.location}}</span>
+              <span v-if="downMode">{{ history.value.location }}</span>
               <ui-input
-                  v-else
-                  type="text"
-                  @input="input"
-                  v-model="history.value.location"
-                  @blur="history.push"
+                v-else
+                type="text"
+                @input="input"
+                v-model="history.value.location"
+                @blur="history.push"
               />
             </td>
           </tr>
@@ -79,17 +73,17 @@
             <td class="image" colspan="6" height="360">
               <div class="photo-layout">
                 <img
-                    :src="useStaticUrl(roadPhoto.url).value"
-                    @blur="history.push"
-                    class="photo"
-                    :style="{transform: photoCSSMatrix}"
-                    ref="photoRef"
+                  :src="useStaticUrl(roadPhoto.url).value"
+                  @blur="history.push"
+                  class="photo"
+                  :style="{ transform: photoCSSMatrix }"
+                  ref="photoRef"
                 />
                 <img
-                    src="/static/compass.png"
-                    class="compass"
-                    :style="{transform: compassCSSMatrix}"
-                    ref="compassRef"
+                  src="/static/compass.png"
+                  class="compass"
+                  :style="{ transform: compassCSSMatrix }"
+                  ref="compassRef"
                 />
                 <p class="compass-info">比例1 : {{ proportion }}</p>
               </div>
@@ -97,17 +91,17 @@
           </tr>
           <tr>
             <td class="value" colspan="6" height="64">
-              <span v-if="downMode">{{history.value.illustrate}}</span>
+              <span v-if="downMode">{{ history.value.illustrate }}</span>
               <ui-input
-                  v-else
-                  type="text"
-                  @input="input"
-                  v-model="history.value.illustrate"
-                  @blur="history.push"
+                v-else
+                type="text"
+                @input="input"
+                v-model="history.value.illustrate"
+                @blur="history.push"
               />
             </td>
           </tr>
-          <tr >
+          <tr>
             <td class="value date" colspan="6" height="48">
               {{ formatDate(new Date(), "yyyy年MM月dd日hh时mm分") }}
             </td>
@@ -125,103 +119,107 @@
 </template>
 
 <script setup lang="ts">
-import { router, writeRouteName } from '@/router'
+import { router, writeRouteName } from "@/router";
 import { formatDate } from "@/utils";
-import {computed, nextTick, ref, watchEffect} from "vue";
-import { useHistory } from '@/hook/useHistory'
-import {roadPhotos, RoadPhoto, getDefaultTable} from "@/store/roadPhotos";
-import {useStaticUrl} from "@/hook/useStaticUrl";
-import html2canvas from 'html2canvas'
+import { computed, nextTick, onDeactivated, ref, watchEffect } from "vue";
+import { useHistory } from "@/hook/useHistory";
+import { roadPhotos, RoadPhoto, getDefaultTable } from "@/store/roadPhotos";
+import { useStaticUrl } from "@/hook/useStaticUrl";
+import html2canvas from "html2canvas";
 import UiButton from "@/components/base/components/button/index.vue";
 import UiInput from "@/components/base/components/input/index.vue";
-import {HandMode, useHand} from '@/hook/useHand'
+import { HandMode, useHand } from "@/hook/useHand";
 import Header from "@/components/photos/header.vue";
 import MainPanel from "@/components/main-panel/index.vue";
-import {downloadImage, uploadImage} from "@/store/sync";
-import {Mode} from "@/views/graphic/menus";
+import { downloadImage, uploadImage } from "@/store/sync";
+import { Mode } from "@/views/graphic/menus";
 import Message from "@/components/base/components/message/message.vue";
 
 const roadPhoto = computed<RoadPhoto>(() => {
-  let route, params, data
-  if (((route = router.currentRoute.value).name === writeRouteName.tabulation)
-    && (params = route.params).id
-    && (data = roadPhotos.value.find(data => data.id === params.id))) {
-    return data
+  let route, params, data;
+  if (
+    (route = router.currentRoute.value).name === writeRouteName.tabulation &&
+    (params = route.params).id &&
+    (data = roadPhotos.value.find((data) => data.id === params.id))
+  ) {
+    return data;
   } else {
     // router.back();
   }
-})
+});
 const history = computed(
   () => roadPhoto.value && useHistory(getDefaultTable(roadPhoto.value))
-)
+);
 
-const input = () => history.value.state.hasRedo = false
+const input = () => (history.value.state.hasRedo = false);
 
-const compassRef = ref<HTMLImageElement>()
+const compassRef = ref<HTMLImageElement>();
 const { cssMatrix: compassCSSMatrix, matrix: compassMatrix } = useHand(
   compassRef,
   HandMode.Angle,
   () => {
-    history.value.value.compassAngle = compassMatrix.value
-    history.value.push()
+    history.value.value.compassAngle = compassMatrix.value;
+    history.value.push();
   },
   history.value.value.compassAngle
-)
-const photoRef = ref<HTMLImageElement>()
+);
+const photoRef = ref<HTMLImageElement>();
 const { cssMatrix: photoCSSMatrix, matrix: photoMatrix } = useHand(
   photoRef,
   HandMode.MoveAndScale,
   () => {
-    history.value.value.imageTransform = photoMatrix.value
-    history.value.push()
+    history.value.value.imageTransform = photoMatrix.value;
+    history.value.push();
   },
   history.value.value.imageTransform
-)
-
-const proportion = ref(1)
-const photoLoaded = ref(false)
+);
+onDeactivated(() => (photoLoaded.value = false));
+const proportion = ref(1);
+const photoLoaded = ref(false);
 watchEffect(() => {
   if (!roadPhoto.value || !photoRef.value) {
     return;
   }
   if (!photoLoaded.value) {
-    photoRef.value.onload = () => photoLoaded.value = true
+    photoRef.value.onload = () => (photoLoaded.value = true);
+    return;
   }
-  const scale = roadPhoto.value.data.scale || 1
-  const martrixScale = photoMatrix.value[0]
-  const photoWidth = photoRef.value.naturalWidth
-  const prop = ((photoWidth / photoRef.value.offsetWidth) * scale) / martrixScale
-  proportion.value = Math.ceil(prop * 100) / 100
-})
-
+  const scale = roadPhoto.value.data.scale || 1;
+  const martrixScale = photoMatrix.value[0];
+  const photoWidth = photoRef.value.naturalWidth;
+  const prop = ((photoWidth / photoRef.value.offsetWidth) * scale) / martrixScale;
+  console.log(scale, martrixScale, photoWidth, prop);
+  proportion.value = Math.ceil(prop * 100) / 100;
+});
 
 const onBack = () => {
   router.replace({
     name: writeRouteName.graphic,
-    params: {mode: Mode.Road, id: roadPhoto.value.id, action: 'update'}
-  })
-}
+    params: { mode: Mode.Road, id: roadPhoto.value.id, action: "update" },
+  });
+};
 
-const downMode = ref(false)
-const layoutRef = ref<HTMLDivElement>()
+const downMode = ref(false);
+const layoutRef = ref<HTMLDivElement>();
 const getLayoutImage = async () => {
-  downMode.value = true
-  await nextTick()
-  const canvas = await html2canvas(layoutRef.value)
-  Message.success({ msg: "已保存至相册", time: 2000 } )
-  downMode.value = false
-  const blob = await new Promise<Blob>(resolve => canvas.toBlob(resolve, "image/jpeg", 0.95))
-  await downloadImage(blob)
-  return await uploadImage(blob)
-}
+  downMode.value = true;
+  await nextTick();
+  const canvas = await html2canvas(layoutRef.value);
+  Message.success({ msg: "已保存至相册", time: 2000 });
+  downMode.value = false;
+  const blob = await new Promise<Blob>((resolve) =>
+    canvas.toBlob(resolve, "image/jpeg", 0.95)
+  );
+  await downloadImage(blob);
+  return await uploadImage(blob);
+};
 const saveHandler = async () => {
   roadPhoto.value.table = {
     ...history.value.value,
-    url: await getLayoutImage()
-  }
-  router.replace({name: writeRouteName.roads})
-}
-
+    url: await getLayoutImage(),
+  };
+  router.replace({ name: writeRouteName.roads });
+};
 </script>
 
 <style lang="scss" scoped>
@@ -286,7 +284,6 @@ const saveHandler = async () => {
   height: 800px;
   border-collapse: collapse;
 
-
   tr:not(:first-child) {
     &:nth-child(2) td {
       border-top: 2px solid #000;
@@ -307,7 +304,7 @@ const saveHandler = async () => {
 
   .value {
     height: 43px;
-    background-color: #D4E8FF;
+    background-color: #d4e8ff;
   }
 
   .title {

+ 26 - 28
src/views/scene/TrackMeasure.vue

@@ -1,46 +1,45 @@
 <template>
   <div class="photo-btn">
-    <ButtonPane
-        class="item fun-ctrl"
-        :size="80"
-        @click="callback"
-    >
+    <ButtonPane class="item fun-ctrl" :size="80" @click="callback">
       <ui-icon type="affirm" class="icon" />
     </ButtonPane>
     <ButtonPane class="item fun-ctrl" :size="80" @click="active = !active">
-      <ui-icon type="line_h" class="icon" :class="{active}" />
+      <ui-icon type="line_h" class="icon" :class="{ active }" />
     </ButtonPane>
   </div>
 </template>
 <script setup lang="ts">
 import UiIcon from "@/components/base/components/icon/index.vue";
 import ButtonPane from "@/components/button-pane/index.vue";
-import {startMeasure, SuccessMeasureAtom} from "@/views/scene/linkage/measure";
-import {onActivated, onMounted, ref, watchEffect} from "vue";
-import {tempMeasures} from "@/store/measure";
-import {measureDisabledStack} from '@/hook/custom'
+import { startMeasure, SuccessMeasureAtom } from "@/views/scene/linkage/measure";
+import { onActivated, onMounted, ref, watchEffect, nextTick } from "vue";
+import { tempMeasures } from "@/store/measure";
+import { measureDisabledStack } from "@/hook/custom";
+import { useSDK } from "@/hook";
 
-const props = defineProps<{ onConfirm: (data: SuccessMeasureAtom) => void }>()
-const active = ref(true)
+const props = defineProps<{ onConfirm: (data: SuccessMeasureAtom) => void }>();
+const active = ref(true);
 const callback = () => {
-  props.onConfirm(tempMeasures.value[0] as any)
-  tempMeasures.value = []
-}
+  props.onConfirm(tempMeasures.value[0] as any);
+  tempMeasures.value = [];
+};
 watchEffect(() => {
   if (active.value) {
-    tempMeasures.value = []
-    startMeasure('L_LINE', 'red')
-      .then((measure) => {
-        if (measure) {
-          active.value = null
-          tempMeasures.value = [measure]
-
-          console.log(tempMeasures.value)
-        }
-      })
+    tempMeasures.value = [];
+    startMeasure("L_LINE", "red").then((measure) => {
+      if (measure) {
+        active.value = null;
+        tempMeasures.value = [measure];
+        nextTick(() => {
+          const canvas = useSDK().carry.measureMap.get(measure);
+          canvas.bus.on("update", (pos) => {
+            measure.length = canvas.getDistance().value;
+          });
+        });
+      }
+    });
   }
-})
-
+});
 </script>
 
 <style lang="scss" scoped>
@@ -66,5 +65,4 @@ watchEffect(() => {
     font-size: 28px;
   }
 }
-
 </style>

+ 9 - 10
src/views/scene/mode.vue

@@ -8,10 +8,10 @@
 </template>
 
 <script lang="ts" setup>
-import GroupButton from '@/components/group-button/index.vue'
-import {Mode} from "@/sdk";
-import {computed, ref, watch, watchEffect} from "vue";
-import {customMap, disabledMap} from "@/hook/custom/index";
+import GroupButton from "@/components/group-button/index.vue";
+import { Mode } from "@/sdk";
+import { computed, ref, watch, watchEffect } from "vue";
+import { customMap, disabledMap } from "@/hook/custom/index";
 
 const tabs = [
   {
@@ -28,14 +28,14 @@ const tabs = [
 
 const activeKey = ref(tabs[0].mode);
 const menus = computed(() =>
-  tabs.map(tab => ({
+  tabs.map((tab) => ({
     icon: tab.mode === activeKey.value ? tab.activeIcon : tab.icon,
     key: tab.mode,
-    onClick: () => activeKey.value = tab.mode
+    onClick: () => (activeKey.value = tab.mode),
   }))
-)
+);
 
-watch(activeKey, () => customMap.mode = activeKey.value)
+watch(activeKey, () => (customMap.mode = activeKey.value));
 </script>
 
 <style lang="scss" scoped>
@@ -55,5 +55,4 @@ watch(activeKey, () => customMap.mode = activeKey.value)
   }
 }
 </style>
-<script setup lang="ts">
-</script>
+<script setup lang="ts"></script>