Jelajahi Sumber

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

# Conflicts:
#	server/test/SS-t-P1d6CwREny2/attach/sceneStore
xzw 2 tahun lalu
induk
melakukan
37a5c54932
100 mengubah file dengan 2493 tambahan dan 469 penghapusan
  1. 3 0
      package.json
  2. TEMPAT SAMPAH
      public/static/compass.png
  3. 20 3
      server/mock.ts
  4. 0 1
      server/test/SS-t-P1d6CwREny2/attach/SS-t-P1d6CwREny2
  5. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/168422080454248.jpg
  6. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684220972440371.jpg
  7. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684221010192772.jpg
  8. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684221264415308.jpg
  9. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684221284533813.jpg
  10. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684221290143241.jpg
  11. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684221299217667.jpg
  12. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684221417966298.jpg
  13. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684221452347626.jpg
  14. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684221456504124.jpg
  15. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684221515521754.jpg
  16. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684221633262198.jpg
  17. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684222348855226.jpg
  18. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684222350910397.jpg
  19. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684222352856639.jpg
  20. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/168422235480882.jpg
  21. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684224534547269.jpg
  22. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684224536739698.jpg
  23. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684224538498159.jpg
  24. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/168422454025094.jpg
  25. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684224541927325.jpg
  26. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684224916246223.jpg
  27. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684224925315923.jpg
  28. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684288228623376.jpg
  29. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684288784278541.jpg
  30. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684288823324883.jpg
  31. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684288932224272.jpg
  32. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684288964891597.jpg
  33. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684288970767239.jpg
  34. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684288986034365.jpg
  35. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684289002495714.jpg
  36. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684289054985723.jpg
  37. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684289092276308.jpg
  38. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684289107780511.jpg
  39. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684290278024982.jpg
  40. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684383209173120.jpg
  41. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684383211148763.jpg
  42. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684383214188797.jpg
  43. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684398903529291.jpg
  44. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/168439895799432.jpg
  45. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684398968133995.jpg
  46. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684400336977483.jpg
  47. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/168440112662690.jpg
  48. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684404793474961.jpg
  49. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684458265513851.jpg
  50. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684458985974396.jpg
  51. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684459042310272.jpg
  52. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684459138621943.jpg
  53. TEMPAT SAMPAH
      server/test/SS-t-P1d6CwREny2/attach/upload/1684461416640713.jpg
  54. 1 2
      src/components/base/assets/scss/components/_input.scss
  55. 1 1
      src/components/base/assets/scss/editor/_head.scss
  56. 1 1
      src/components/base/assets/scss/editor/_menu.scss
  57. 4 1
      src/components/base/components/gate/layer.vue
  58. 54 3
      src/components/base/components/slide/index.vue
  59. 100 0
      src/components/fill-slide/index.vue
  60. 4 1
      src/components/group-button/index.vue
  61. 12 17
      src/components/main-panel/index.vue
  62. 46 0
      src/components/photos/header.vue
  63. 109 0
      src/components/photos/index.vue
  64. 1 1
      src/dbo/main.ts
  65. 25 4
      src/graphic/CanvasStyle/default.js
  66. 20 4
      src/graphic/CanvasStyle/focus.js
  67. 14 3
      src/graphic/CanvasStyle/select.js
  68. 2 0
      src/graphic/Constant.js
  69. 18 8
      src/graphic/Controls/AddCircle.js
  70. 101 0
      src/graphic/Controls/AddCrossRoad.js
  71. 58 45
      src/graphic/Controls/AddLine.js
  72. 4 2
      src/graphic/Controls/AddMagnifier.js
  73. 3 3
      src/graphic/Controls/AddPoint.js
  74. 1 2
      src/graphic/Controls/AddSVG.js
  75. 1 1
      src/graphic/Controls/AddText.js
  76. 60 0
      src/graphic/Controls/MoveCircle.js
  77. 3 3
      src/graphic/Controls/MoveRoad.js
  78. 1 0
      src/graphic/Controls/MoveText.js
  79. 148 76
      src/graphic/Controls/UIControl.js
  80. 5 4
      src/graphic/Coordinate.js
  81. 31 8
      src/graphic/Geometry/Circle.js
  82. 23 17
      src/graphic/Geometry/ControlPoint.js
  83. 8 8
      src/graphic/Geometry/CurveRoad.js
  84. 24 0
      src/graphic/Geometry/Geometry.js
  85. 21 0
      src/graphic/Geometry/Img.js
  86. 16 1
      src/graphic/Geometry/Line.js
  87. 37 4
      src/graphic/Geometry/Magnifier.js
  88. 11 2
      src/graphic/Geometry/Point.js
  89. 13 9
      src/graphic/Geometry/Road.js
  90. 0 7
      src/graphic/Geometry/SVG.js
  91. 3 13
      src/graphic/Geometry/Text.js
  92. 238 3
      src/graphic/History/Change.js
  93. 257 15
      src/graphic/History/History.js
  94. 305 18
      src/graphic/History/HistoryUtil.js
  95. 154 59
      src/graphic/Layer.js
  96. 134 64
      src/graphic/ListenLayer.js
  97. 149 2
      src/graphic/Load.js
  98. 224 42
      src/graphic/Renderer/Draw.js
  99. 25 11
      src/graphic/Renderer/Render.js
  100. 0 0
      src/graphic/Service/CircleService.js

+ 3 - 0
package.json

@@ -14,12 +14,15 @@
     "body-parser": "^1.20.2",
     "coordtransform": "^2.1.2",
     "driver.js": "^0.9.8",
+    "express-fileupload": "^1.4.0",
     "html2canvas": "^1.4.1",
     "js-base64": "^3.7.5",
     "jspdf": "^2.5.1",
     "mitt": "^3.0.0",
     "sass": "^1.62.0",
     "sass-loader": "^13.2.2",
+    "stateshot": "^1.3.5",
+    "vconsole": "^3.15.0",
     "vue": "^3.2.47",
     "vue-cropper": "1.0.2",
     "vue-i18n": "^9.2.2",

TEMPAT SAMPAH
public/static/compass.png


+ 20 - 3
server/mock.ts

@@ -2,6 +2,7 @@ import express from "express";
 import path from "path";
 import bodyParser from 'body-parser'
 import * as fs from "fs";
+import fileUpload from 'express-fileupload'
 
 const staticDir = path.resolve(__dirname, "test");
 
@@ -14,7 +15,8 @@ export async function createServer(port: number) {
 
   const app = express();
   app.use(express.static(staticDir));
-  app.use(bodyParser())
+  app.use(bodyParser({ limit: '200mb' }))
+  app.use(fileUpload({createParentPath: true}))
   app.listen(port);
   startup = true;
 
@@ -25,11 +27,26 @@ export async function createServer(port: number) {
     const p = path.resolve(staticDir, scene, "./attach", filename)
 
 
-    console.log(p, req.body)
-    fs.writeFileSync(p, JSON.stringify(req.body))
+    if (req.url.includes("/upload")) {
+      return next()
+    } else {
+      fs.writeFileSync(p, JSON.stringify(req.body))
+    }
     res.json({code: 0, msg: 'ok'})
   })
 
+  app.post("/:sceneCode/upload", (req, res) => {
+    const file = (req as any).files.file
+    const relUrl = `/attach/upload/${file.name}`
+    const absUrl = path.resolve(staticDir, `./${req.params.sceneCode}/${relUrl}`)
+    file.mv(absUrl, err => {
+      if (err) {
+        res.json({code: 1, msg: 'ok'})
+      } else {
+        res.json({code: 0, msg: 'ok', data: relUrl})
+      }
+    })
+  })
 
   console.log("模拟环境已开启");
 }

+ 0 - 1
server/test/SS-t-P1d6CwREny2/attach/SS-t-P1d6CwREny2

@@ -1 +0,0 @@
-{}

TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/168422080454248.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684220972440371.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684221010192772.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684221264415308.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684221284533813.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684221290143241.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684221299217667.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684221417966298.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684221452347626.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684221456504124.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684221515521754.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684221633262198.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684222348855226.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684222350910397.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684222352856639.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/168422235480882.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684224534547269.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684224536739698.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684224538498159.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/168422454025094.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684224541927325.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684224916246223.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684224925315923.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684288228623376.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684288784278541.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684288823324883.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684288932224272.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684288964891597.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684288970767239.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684288986034365.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684289002495714.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684289054985723.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684289092276308.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684289107780511.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684290278024982.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684383209173120.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684383211148763.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684383214188797.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684398903529291.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/168439895799432.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684398968133995.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684400336977483.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/168440112662690.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684404793474961.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684458265513851.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684458985974396.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684459042310272.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684459138621943.jpg


TEMPAT SAMPAH
server/test/SS-t-P1d6CwREny2/attach/upload/1684461416640713.jpg


+ 1 - 2
src/components/base/assets/scss/components/_input.scss

@@ -653,10 +653,9 @@
   
   list-style: none;
   max-height: 288px;
-  background: rgba(26, 26, 26, 0.8);
+  background: #161A1A;
   box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.3),
     inset 0 0 1px rgb(255 255 255 / 90%);
-  backdrop-filter: blur(4px);
   border-radius: 4px;
   overflow-y: auto;
   color: var(--colors-content-color);

+ 1 - 1
src/components/base/assets/scss/editor/_head.scss

@@ -8,5 +8,5 @@
     left: 0;
     top: 0;
     width: 100%;
-    z-index: 1000;
+    z-index: 2;
 }

+ 1 - 1
src/components/base/assets/scss/editor/_menu.scss

@@ -9,7 +9,7 @@
     left: var(--editor-menu-left);
     top: var(--editor-head-height);
     bottom: 0;
-    z-index: 2000;
+    z-index: 2;
     overflow: hidden;
 
     backdrop-filter: blur(4px);

+ 4 - 1
src/components/base/components/gate/layer.vue

@@ -1,5 +1,6 @@
 <template>
   <div
+    ref="layerRef"
     class="ui-gate-layer"
     :style="{
       height: normalizeUnitToStyle(height),
@@ -14,7 +15,7 @@
 </template>
 
 <script setup>
-import { ref, computed, provide, watch } from "vue";
+import {ref, computed, provide, watch, reactive} from "vue";
 import { normalizeUnitToStyle } from "../../utils";
 import { Relation } from "./constant";
 
@@ -49,6 +50,8 @@ watch([contentInstances, slideIndex], () => {
 });
 
 provide(Relation, contentInstances);
+const layerRef = ref()
+defineExpose(reactive({dom: layerRef}))
 </script>
 
 <script>

+ 54 - 3
src/components/base/components/slide/index.vue

@@ -1,7 +1,11 @@
 <template>
     <div class="ui-slide" :class="{'stop-animation': stopAmimation}" v-if="items.length">
-        <Gate :index="extendIndex">
-            <GateContent v-for="(item, i) in extendItems">
+        <Gate :index="extendIndex" ref="gate">
+            <GateContent
+                v-for="(item, i) in extendItems"
+                @mousedown.stop.prevent="mousedownHandler"
+                @touchstart.stop.prevent="mousedownHandler"
+            >
                 <slot :raw="item" :active="items[index]" :index="getIndex(i)" />
             </GateContent>
         </Gate>
@@ -68,11 +72,58 @@ const extendItems = computed(() => {
 const index = computed(() => getIndex(extendIndex.value))
 
 watchEffect(() => {
-    console.log(props.currentIndex, extendLength.value)
     extendIndex.value = props.currentIndex + extendLength.value
 })
 
 const stopAmimation = ref(false)
+
+const gate = ref()
+const mousedownHandler = ev => {
+    stopAmimation.value = true
+    const width = gate.value.dom.offsetWidth
+    const dom = gate.value.dom.querySelector(".ui-gate-slides")
+    const startTime = new Date().getTime();
+    const style = window.getComputedStyle(dom, null);
+    const matrixStr = style.transform
+    const matrix = matrixStr.substring(matrixStr.indexOf('(') + 1, matrixStr.indexOf(')'))
+        .split(",")
+    const startX = Number(matrix[4]);
+    const start = {
+        x: ev.pageX || ev.touches[0].pageX,
+        y: ev.pageY || ev.touches[0].pageY
+    }
+    let move
+    const moveHandler = (ev) => {
+        move = {
+            x: (ev.pageX || ev.touches[0].pageX) - start.x,
+            y: (ev.pageY || ev.touches[0].pageY) - start.y
+        }
+        matrix[4] = startX + move.x
+        dom.style.transform = `matrix(${matrix.join(",")})`
+    }
+    const upHandler = ev => {
+        document.documentElement.removeEventListener("mousemove", moveHandler)
+        document.documentElement.removeEventListener("mouseup", upHandler)
+        document.documentElement.removeEventListener("touchmove", moveHandler)
+        document.documentElement.removeEventListener("touchend", upHandler)
+        stopAmimation.value = false
+        const endTime = new Date().getTime()
+        const isFast = endTime - startTime < 200
+        const limen = isFast ? 50 : width / 3
+
+        if (move.x < -limen) {
+            nextHandler()
+        } else if (move.x > limen) {
+            prevHandler()
+        }
+        dom.style.removeProperty("transform")
+    }
+    document.documentElement.addEventListener("mousemove", moveHandler)
+    document.documentElement.addEventListener("mouseup", upHandler)
+    document.documentElement.addEventListener("touchmove", moveHandler)
+    document.documentElement.addEventListener("touchend", upHandler)
+}
+
 let prevent = false
 const openPrevent = (fn) => {
     prevent = true

+ 100 - 0
src/components/fill-slide/index.vue

@@ -0,0 +1,100 @@
+<template>
+  <div class="fill-slide">
+    <div class="header">
+      <slot name="header" />
+      <ui-icon class="close" type="close" @click="$emit('quit')" ctrl />
+    </div>
+    <div class="slide-layout">
+      <ui-slide
+          :items="data"
+          :current-index="data.indexOf(active)"
+          @change="index => $emit('update:active', data[index])"
+      >
+        <template v-slot="{raw}">
+          <template v-if="$slots.default">
+            <slot :data="raw" />
+          </template>
+          <img :src="getStaticFile(getURL ? getURL(raw) : raw.url)" class="image" v-else />
+        </template>
+      </ui-slide>
+    </div>
+    <div class="foot">
+      <slot name="foot" />
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import UiSlide from "@/components/base/components/slide/index.vue";
+import {getStaticFile} from "@/dbo/main";
+import UiIcon from "@/components/base/components/icon/index.vue";
+
+type Item = {url: string}
+
+defineProps<{ data: Item[], active: Item, getURL?: (data: any) => string }>()
+defineEmits<{
+  (e: 'update:active', d: Item): void,
+  (e: 'quit'): void
+}>()
+</script>
+
+<style scoped lang="scss">
+.fill-slide {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: #000;
+  z-index: 3;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  flex-direction: column;
+}
+
+.slide-layout {
+  width: 840px;
+  height: 540px;
+}
+
+.image {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+  border-radius: 4px;
+}
+
+.header {
+  width: 100%;
+  position: relative;
+}
+
+.close {
+  position: absolute;
+  right: 32px;
+  top: 32px;
+  font-size: 20px;
+  color: #fff;
+}
+.foot {
+  width: 100%;
+  padding-bottom: 24px;
+}
+</style>
+
+<style lang="scss">
+.fill-slide .ui-slide .ui-gate-layer {
+  overflow: initial !important;
+  .ui-gate-slides .ui-gate-content {
+    transition: all .3s ease;
+    transform: scale(0.9);
+    opacity: 0.5 !important;
+
+    &.active {
+      transform: scale(1);
+      opacity: 1 !important;
+    }
+  }
+}
+</style>

+ 4 - 1
src/components/group-button/index.vue

@@ -8,7 +8,10 @@
       :class="{ active: activeKey === menu.key, dire }"
       @click="menu.onClick && menu.onClick(menu)"
     >
-      <ui-icon :type="menu.icon || 'close'" class="icon"/>
+      <template v-if="$slots.default">
+        <slot :data="menu" />
+      </template>
+      <ui-icon :type="menu.icon || 'close'" class="icon" v-else/>
       <p v-if="menu.text">{{ menu.text }}</p>
     </div>
   </ButtonPane>

+ 12 - 17
src/components/main-panel/index.vue

@@ -1,19 +1,14 @@
 <template>
   <UiEditorLayout class="layout" :class="layoutClass">
     <UiEditorHead class="header">
-<!--      <div-->
-<!--        class="menu"-->
-<!--        :class="{ abs: isFull }"-->
-<!--        @click="customMap.sysView = isFull ? 'auto' : 'full'"-->
-<!--      >-->
-<!--        <ui-icon :type="isFull ? 'menu' : 'close'" ctrl />-->
-<!--      </div>-->
       <slot name="header" />
     </UiEditorHead>
     <slot/>
 
     <Menu
+      v-if="menus"
       :menu="menus"
+      :class="{fill: isFull}"
       :active-key="activeMenuKey"
       @update:active-key="val => emit('update:activeMenuKey', val)"
     />
@@ -23,17 +18,14 @@
 <script setup lang="ts">
 import UiEditorLayout from "@/components/base/editor/layout/index.vue";
 import UiEditorHead from "@/components/base/editor/layout/Head.vue";
-import UiIcon from "@/components/base/components/icon/index.vue";
 import Menu from '@/views/sys/menu'
 import {MenuAtom, MenuRaw} from "@/views/sys/menu/menu.js";
 import { customMap } from "@/hook/custom";
-import { computed, ref } from "vue";
-// import "@/preset/pc.scss"
-// import "@/preset/style.scss"
+import { computed } from "vue";
 
 const props = defineProps<{
-  menus: MenuRaw,
-  activeMenuKey: string,
+  menus?: MenuRaw,
+  activeMenuKey?: string,
 }>();
 const emit = defineEmits<{
   (e: 'update:activeMenuKey', t: MenuAtom['name']): void
@@ -56,12 +48,11 @@ const layoutClass = computed(() => ({
 
 .header {
   color: rgba(var(--colors-primary-fill), 0.8);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 0 20px;
+  padding: 8px 20px;
   transition: top 0.3s ease;
   top: var(--header-top);
+  display: flex;
+  align-items: center;
 }
 
 .header .menu {
@@ -111,4 +102,8 @@ const layoutClass = computed(() => ({
     }
   }
 }
+
+.fill {
+  left: calc(-1 * var(--editor-menu-width))
+}
 </style>

+ 46 - 0
src/components/photos/header.vue

@@ -0,0 +1,46 @@
+<template>
+  <div class="photos-header">
+    <div>
+      <ui-icon type="close" ctrl style="margin-right: 10px" @click="router.back" />
+      <span>{{ title }}</span>
+    </div>
+    <span class="center" v-if="count">
+      已选择 {{ count }} 张
+    </span>
+    <div class="right">
+      <slot />
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import UiIcon from "@/components/base/components/icon/index.vue";
+import {router} from '@/router'
+
+defineProps<{ count?: number, title: string }>()
+</script>
+
+<style lang="scss" scoped>
+.photos-header {
+  width: 100%;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  position: relative;
+
+  .center {
+    position: absolute;
+    left: 0;
+    right: 0;
+    white-space: nowrap;
+    pointer-events: none;
+    height: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    text-align: center;
+  }
+}
+
+
+</style>

+ 109 - 0
src/components/photos/index.vue

@@ -0,0 +1,109 @@
+<template>
+  <div class="photos-layout">
+    <div class="photos">
+      <div
+          v-for="photo in data"
+          :key="photo.id"
+          class="photo"
+          @click="selectMode ? changeSelects(photo, !selects.includes(photo)) : $emit('update:active', photo)"
+      >
+        <div class="img-layout">
+          <img :src="getStaticFile(getURL ? getURL(photo) : photo.url)" />
+          <ui-input
+              width="24px"
+              height="24px"
+              v-if="selectMode"
+              type="checkbox"
+              :modelValue="selects.includes(photo)"
+              @update:modelValue="selected => changeSelects(photo, selected)"
+              @click.stop
+              class="photo-select"
+          />
+        </div>
+        <slot :data="photo" />
+      </div>
+    </div>
+  </div>
+</template>
+<script setup lang="ts">
+import {getStaticFile} from "@/dbo/main";
+import UiInput from "@/components/base/components/input/index.vue";
+
+type Item = { url: string, id: string }
+
+const props = defineProps<{
+  data: Item[],
+  getURL?: (data: any) => string
+  active?: Item,
+  selects?: Item[],
+  selectMode?: boolean
+}>()
+
+const emit = defineEmits<{
+  (e: "update:active", a: Item): void,
+  (e: "update:selects", a: Item[]): void,
+}>()
+
+const changeSelects = (item: Item, selected: boolean) => {
+  const olSelected = props.selects.includes(item)
+  if (selected !== olSelected) {
+    if (selected) {
+      emit('update:selects', [...props.selects, item])
+    } else {
+      emit('update:selects', props.selects.filter(oItem => oItem !== item))
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.photos-layout {
+  position: absolute;
+  top: calc(var(--header-top) + var(--editor-head-height));
+  left: 0;
+  right: 0;
+  bottom: 0;
+  overflow-y: auto;
+  background: #2E2E2E;
+}
+.photos {
+  display: grid;
+  grid-gap: 24px;
+  padding: 24px;
+  grid-template-columns: repeat(4, 1fr);
+}
+.photo {
+  position: relative;
+}
+.photo-select {
+  position: absolute;
+  right: 16px;
+  bottom: 16px;
+  z-index: 1;
+}
+
+.img-layout {
+  position: relative;
+}
+.photo img {
+  width: 100%;
+  border-radius: 4px;
+}
+</style>
+
+<style lang="scss">
+.photo-select.ui-input .checkbox input + .replace {
+  background-color: #ccc;
+  border-color: #fff;
+  border-radius: 50%;
+}
+.photo-select.ui-input .checkbox input + .replace.checked {
+  background-color: var(--colors-primary-base);
+  border-color: var(--colors-primary-base);
+  color: #fff;
+
+  .icon {
+    font-size: 18px !important;
+  }
+}
+</style>

+ 1 - 1
src/dbo/main.ts

@@ -9,6 +9,6 @@ const baseURL =
 
 instance.defaults.baseURL = baseURL
 
-console.log(baseURL)
+export const getStaticFile = (url: string) => baseURL + url
 
 export default instance

+ 25 - 4
src/graphic/CanvasStyle/default.js

@@ -16,6 +16,24 @@ const Lane = {
   dash: [8, 8],
 };
 
+const Arrow = {
+  lineWidth: 2,
+  strokeStyle: "red",
+};
+
+const Magnifier = {
+  strokeStyle: "#2F8FFF",
+  lineWidth: 1,
+  fillStyle: "rgba(0,0,0,0)",
+  radius: 10,
+  target: {
+    radius: 100,
+    realRadius: 50,
+    strokeStyle: "#2F8FFF",
+    lineWidth: 3,
+  },
+};
+
 const CurveRoad = {
   ...Road,
   lineWidth: 1,
@@ -34,6 +52,7 @@ const CurveLan = {
 const Text = {
   strokeStyle: "rgb(0,0,0,1)",
   fillStyle: "rgb(0,0,0,1)",
+  fontSize: 14,
   strokeStyle_adding: "rgba(243, 255, 0, 0.8)",
   fillStyle_adding: "rgba(243, 255, 0, 0.8)",
   lineWidth: 1,
@@ -64,7 +83,7 @@ const CurveRoadPoint = {
   ...Point,
 };
 
-const ControlPoint = {
+const CrossPoint = {
   ...Point,
   strokeStyle: "#3290FF",
   radius: 8,
@@ -74,6 +93,7 @@ const Circle = {
   strokeStyle: "red",
   fillStyle: "rgba(0,0,0,0)",
   lineWidth: 2,
+  radius: 30,
 };
 
 const Measure = {
@@ -83,9 +103,8 @@ const Measure = {
 };
 
 const NormalLine = {
-  strokeStyle: "#CED806",
+  strokeStyle: "#000",
   lineWidth: 2,
-  dash: [3, 2, 2],
 };
 const GuideLine = {
   strokeStyle: "#CED806",
@@ -144,13 +163,15 @@ export default {
   BaseLine,
   Circle,
   Text,
-  ControlPoint,
+  CrossPoint,
   CurveRoadPoint,
   GuideLine,
+  Magnifier,
   Font: CanvasFont,
   MeasureLine,
   Measure,
   Element,
   RoadPoint,
+  Arrow,
   bgColor: "#fff",
 };

+ 20 - 4
src/graphic/CanvasStyle/focus.js

@@ -6,6 +6,16 @@ const Road = {
   strokeStyle: "#3290FF",
 };
 
+const NormalLine = {
+  ...def.NormalLine,
+  lineWidth: 2,
+  strokeStyle: "#3290FF",
+};
+
+const Magnifier = {
+  ...def.Magnifier,
+};
+
 const CurveRoad = {
   ...def.CurveRoad,
   ...Road,
@@ -32,8 +42,8 @@ const CurveRoadPoint = {
   ...Point,
 };
 
-const ControlPoint = {
-  ...def.ControlPoint,
+const CrossPoint = {
+  ...def.CrossPoint,
   fillStyle: "#3290FF",
 };
 
@@ -48,15 +58,21 @@ const CurveRoadEdge = {
   lineWidth: 2,
   strokeStyle: "#3290FF",
 };
-
+const Arrow = {
+  lineWidth: 2,
+  strokeStyle: "red",
+};
 export default {
   Road,
   Text,
   Point,
+  Arrow,
   RoadPoint,
   CurveRoadPoint,
-  ControlPoint,
+  CrossPoint,
   CurveRoad,
   RoadEdge,
+  Magnifier,
+  NormalLine,
   CurveRoadEdge,
 };

+ 14 - 3
src/graphic/CanvasStyle/select.js

@@ -6,6 +6,15 @@ const Road = {
   strokeStyle: "#3290FF",
 };
 
+const NormalLine = {
+  ...def.NormalLine,
+  lineWidth: 2,
+  strokeStyle: "#3290FF",
+};
+const Arrow = {
+  lineWidth: 2,
+  strokeStyle: "red",
+};
 const CurveRoad = {
   ...def.CurveRoad,
   ...Road,
@@ -44,8 +53,8 @@ const CurveRoadPoint = {
   ...Point,
 };
 
-const ControlPoint = {
-  ...def.ControlPoint,
+const CrossPoint = {
+  ...def.CrossPoint,
   fillStyle: "#3290FF",
 };
 
@@ -55,8 +64,10 @@ export default {
   Point,
   RoadPoint,
   CurveRoadPoint,
-  ControlPoint,
+  CrossPoint,
   CurveRoad,
   RoadEdge,
+  NormalLine,
   CurveRoadEdge,
+  Arrow,
 };

+ 2 - 0
src/graphic/Constant.js

@@ -34,5 +34,7 @@ const Constant = {
   maxRoadSideWidth: 800,
   oneWay: "oneWay", //one表示单向,two表示双向
   twoWay: "twoWay", //one表示单向,two表示双向
+  defaultSingleLaneWidth: 30, //单个车道的宽度
+  defaultMidDivideWidth: 5, //隔离带的宽度
 };
 export default Constant;

+ 18 - 8
src/graphic/Controls/AddCircle.js

@@ -1,10 +1,11 @@
 import { mathUtil } from "../Util/MathUtil";
 import { circleService } from "../Service/CircleService";
+import { listenLayer } from "../ListenLayer";
 
 export default class AddCircle {
   constructor() {
+    this.newCircle = null;
     this.center = null;
-    this.radius = null;
   }
 
   setCenter(value) {
@@ -12,19 +13,28 @@ export default class AddCircle {
     mathUtil.clonePoint(this.center, value);
   }
 
-  setRadius(value) {
-    this.radius = value;
+  buildCircle(position) {
+    if (this.newCircle == null && !mathUtil.equalPoint(this.center, position)) {
+      const radius = mathUtil.getDistance(this.center, position);
+      this.newCircle = circleService.create(this.center, radius);
+    }
   }
 
-  buildCircle() {
-    circleService.create(this.center, this.radius);
-    listenLayer.clear();
-    this.clear();
+  updateCircle(position) {
+    if (this.newCircle != null && !mathUtil.equalPoint(this.center, position)) {
+      this.newCircle.setRadius(mathUtil.getDistance(this.center, position));
+    }
+  }
+
+  finish(position) {
+    if (this.newCircle != null && mathUtil.equalPoint(this.center, position)) {
+      dataService.deleteLine(this.newLine.vectorId);
+    }
   }
 
   clear() {
+    this.newCircle = null;
     this.center = null;
-    this.radius = null;
   }
 }
 

+ 101 - 0
src/graphic/Controls/AddCrossRoad.js

@@ -0,0 +1,101 @@
+import { mathUtil } from "../Util/MathUtil";
+import { circleService } from "../Service/CircleService";
+import { listenLayer } from "../ListenLayer";
+import { roadPointService } from "../Service/RoadPointService";
+import { roadService } from "../Service/RoadService";
+import { edgeService } from "../Service/EdgeService";
+
+export default class AddCrossRoad {
+  constructor() {}
+
+  build(position, count) {}
+
+  //三岔口
+  buildThree(position) {
+    const len = 300;
+    let end = roadPointService.create(position);
+    let start1 = roadPointService.create({
+      x: end.x,
+      y: end.y + len,
+    });
+
+    let start2 = roadPointService.create({
+      x: end.x + len,
+      y: end.y,
+    });
+
+    let start3 = roadPointService.create({
+      x: end.x - len,
+      y: end.y,
+    });
+
+    //需要设置公路的车道数,是否双向等等
+    // this.leftDrivewayCount = Settings.roadLeftDrivewayCount;
+    // this.rightDrivewayCount = Settings.roadRightDrivewayCount;
+    // this.singleRoadDrivewayCount = Settings.singleRoadDrivewayCount;
+    // this.way = Settings.wayType;
+
+    roadService.create(start1.vectorId, end.vectorId);
+    roadService.create(start2.vectorId, end.vectorId);
+    roadService.create(start3.vectorId, end.vectorId);
+    edgeService.updateEdgeForMulRoad(end.vectorId);
+  }
+
+  //四岔口
+  buildFour(position) {
+    const len = 300;
+    let end = roadPointService.create(position);
+    let start1 = roadPointService.create({
+      x: end.x,
+      y: end.y + len,
+    });
+
+    let start2 = roadPointService.create({
+      x: end.x,
+      y: end.y - len,
+    });
+
+    let start3 = roadPointService.create({
+      x: end.x + len,
+      y: end.y,
+    });
+
+    let start4 = roadPointService.create({
+      x: end.x - len,
+      y: end.y,
+    });
+
+    //需要设置公路的车道数,是否双向等等
+    // this.leftDrivewayCount = Settings.roadLeftDrivewayCount;
+    // this.rightDrivewayCount = Settings.roadRightDrivewayCount;
+    // this.singleRoadDrivewayCount = Settings.singleRoadDrivewayCount;
+    // this.way = Settings.wayType;
+
+    roadService.create(start1.vectorId, end.vectorId);
+    roadService.create(start2.vectorId, end.vectorId);
+    roadService.create(start3.vectorId, end.vectorId);
+    roadService.create(start4.vectorId, end.vectorId);
+    edgeService.updateEdgeForMulRoad(end.vectorId);
+  }
+
+  //五岔口
+  buildFive(position) {
+    const len = 300;
+    const points = mathUtil.createFivePointedStar(position, len);
+    let end = roadPointService.create(position);
+    let start1 = roadPointService.create(points[0]);
+    let start2 = roadPointService.create(points[1]);
+    let start3 = roadPointService.create(points[2]);
+    let start4 = roadPointService.create(points[3]);
+    let start5 = roadPointService.create(points[4]);
+    roadService.create(start1.vectorId, end.vectorId);
+    roadService.create(start2.vectorId, end.vectorId);
+    roadService.create(start3.vectorId, end.vectorId);
+    roadService.create(start4.vectorId, end.vectorId);
+    roadService.create(start5.vectorId, end.vectorId);
+    edgeService.updateEdgeForMulRoad(end.vectorId);
+  }
+}
+
+const addCrossRoad = new AddCrossRoad();
+export { addCrossRoad };

+ 58 - 45
src/graphic/Controls/AddLine.js

@@ -7,61 +7,66 @@ import { mathUtil } from "../Util/MathUtil";
 
 export default class AddLine {
   constructor() {
+    this.newLine = null;
     this.startInfo = {};
-    this.endInfo = {};
     this.baseLineId = null;
     this.category = VectorCategory.Line.NormalLine;
   }
 
-  setPointInfo(dir, pointInfo) {
-    if (dir == "start") {
-      this.startInfo = {
-        position: {
-          x: pointInfo.x,
-          y: pointInfo.y,
-        },
-        linkedPointId: pointInfo.linkedPointId,
-        lineId: pointInfo.lineId,
-      };
-    } else if (dir == "end") {
-      this.endInfo = {
-        position: {
-          x: pointInfo.x,
-          y: pointInfo.y,
-        },
-        linkedPointId: pointInfo.linkedPointId,
-        lineId: pointInfo.lineId,
-      };
+  setPointInfo(pointInfo) {
+    this.startInfo = {
+      position: {
+        x: pointInfo.x,
+        y: pointInfo.y,
+      },
+      linkedPointId: pointInfo.linkedPointId,
+      lineId: pointInfo.lineId,
+    };
+  }
+
+  setNewLinePoint(position) {
+    if (listenLayer.modifyPoint) {
+      this.setPointInfo(listenLayer.modifyPoint);
+    } else {
+      this.setPointInfo(position);
+    }
+    return true;
+  }
+
+  buildLine(position) {
+    if (
+      this.newLine == null &&
+      !mathUtil.equalPoint(this.startInfo.position, position)
+    ) {
+      this.newLine = lineService.create(
+        this.startInfo.position,
+        position,
+        this.category
+      );
+    }
+  }
+
+  updateLine(position) {
+    if (
+      this.newLine != null &&
+      !mathUtil.equalPoint(this.startInfo.position, position)
+    ) {
+      let point = dataService.getPoint(this.newLine.endId);
+      point.setPosition(position);
     }
   }
 
-  setNewLinePoint(dir, position) {
-    if (dir == "start") {
-      if (listenLayer.modifyPoint) {
-        this.setPointInfo(dir, listenLayer.modifyPoint);
-      } else {
-        this.setPointInfo(dir, position);
-      }
-      return true;
-    } else if (dir == "end") {
-      if (listenLayer.modifyPoint) {
-        this.setPointInfo(dir, listenLayer.modifyPoint);
-      } else {
-        this.setPointInfo(dir, position);
-      }
-      return true;
+  finish(position) {
+    if (
+      this.newLine != null &&
+      mathUtil.equalPoint(this.startInfo.position, position)
+    ) {
+      dataService.deleteLine(this.newLine.vectorId);
     }
-    return false;
   }
 
-  buildLine() {
-    lineService.create(
-      this.startInfo.position,
-      this.endInfo.position,
-      this.category
-    );
-    listenLayer.clear();
-    this.clear();
+  setCategory(value) {
+    this.category = value;
   }
 
   setBaseLineId(baseLineId) {
@@ -72,9 +77,17 @@ export default class AddLine {
     return this.baseLineId;
   }
 
+  clearVectorData() {
+    this.newLine = null;
+    this.startInfo = {};
+    this.baseLineId = null;
+  }
+
   clear() {
+    this.newLine = null;
     this.startInfo = {};
-    this.endInfo = {};
+    this.baseLineId = null;
+    this.category = VectorCategory.Line.NormalLine;
   }
 }
 

+ 4 - 2
src/graphic/Controls/AddMagnifier.js

@@ -1,5 +1,6 @@
 import { mathUtil } from "../Util/MathUtil";
 import { magnifierService } from "../Service/MagnifierService";
+import { listenLayer } from "../ListenLayer";
 
 export default class AddMagnifier {
   constructor() {
@@ -9,10 +10,11 @@ export default class AddMagnifier {
   buildMagnifier(position) {
     this.newMagnifier = magnifierService.create(position);
     listenLayer.clear();
-    this.clear();
   }
 
-  clear() {}
+  clear() {
+    this.newMagnifier = null;
+  }
 }
 
 const addMagnifier = new AddMagnifier();

+ 3 - 3
src/graphic/Controls/AddPoint.js

@@ -22,7 +22,7 @@ export default class AddPoint {
     let line = mathUtil.createLine1(startPoint, endPoint);
     let vLine1 = mathUtil.getVerticalLine(line, testPoint);
     let join = mathUtil.getJoinLinePoint(basePoint, vLine1);
-    join = pointService.addPoint(join, VectorCategory.Point.TestBasePoint);
+    join = pointService.create(join, VectorCategory.Point.TestBasePoint);
 
     lineService.createByPointId(
       testPointId,
@@ -49,7 +49,7 @@ export default class AddPoint {
       let line = mathUtil.createLine1(startPoint, endPoint);
       if (testPointId2 == null) {
         let join = mathUtil.getJoinLinePoint(testPoint1, line);
-        join = pointService.addPoint(join, VectorCategory.Point.TestBasePoint);
+        join = pointService.create(join, VectorCategory.Point.TestBasePoint);
         lineService.createByPointId(
           testPointId1,
           join.vectorId,
@@ -64,7 +64,7 @@ export default class AddPoint {
       } else {
         let testPoint2 = dataService.getPoint(testPointId2);
         let join = mathUtil.getJoinLinePoint(testPoint2, line);
-        join = pointService.addPoint(join, VectorCategory.Point.TestBasePoint);
+        join = pointService.create(join, VectorCategory.Point.TestBasePoint);
         lineService.createByPointId(
           testPointId2,
           join.vectorId,

+ 1 - 2
src/graphic/Controls/AddSVG.js

@@ -1,5 +1,6 @@
 import { mathUtil } from "../Util/MathUtil";
 import { svgService } from "../Service/SVGService";
+import { listenLayer } from "../ListenLayer";
 
 export default class AddSVG {
   constructor() {
@@ -14,8 +15,6 @@ export default class AddSVG {
 
   buildSVG(center) {
     this.newSVG = svgService.create(center);
-    listenLayer.clear();
-    this.clear();
   }
 
   clear() {

+ 1 - 1
src/graphic/Controls/AddText.js

@@ -1,5 +1,6 @@
 import { mathUtil } from "../Util/MathUtil";
 import { textService } from "../Service/TextService";
+import { listenLayer } from "../ListenLayer";
 
 export default class AddText {
   constructor() {
@@ -20,7 +21,6 @@ export default class AddText {
   buildText(center) {
     this.newText = textService.create(center);
     listenLayer.clear();
-    this.clear();
   }
 
   clear() {

+ 60 - 0
src/graphic/Controls/MoveCircle.js

@@ -0,0 +1,60 @@
+import { dataService } from "../Service/DataService";
+import { mathUtil } from "../Util/MathUtil";
+import Constant from "../Constant.js";
+
+export default class MoveCircle {
+  constructor() {}
+
+  moveFull(circleId, dx, dy) {
+    dx = dx;
+    dy = -dy;
+    let circle = dataService.getCircle(circleId);
+    circle.center.x += dx;
+    circle.center.y += dy;
+    circle.createPoints();
+  }
+
+  movePoint(position, circleId, pointIndex) {
+    let circle = dataService.getCircle(circleId);
+    let flag = this.check(position, circle.center, pointIndex);
+
+    let line = mathUtil.createLine1(circle.center, circle.points[pointIndex]);
+    let join = mathUtil.getJoinLinePoint(position, line);
+
+    let radius = mathUtil.getDistance(circle.center, join) / Math.sqrt(2);
+    //没有超出界
+    if (flag && radius > Constant.minAdsorbPix) {
+      circle.setRadius(radius);
+      circle.createPoints();
+    }
+  }
+
+  check(position, center, pointIndex) {
+    if (pointIndex == 0) {
+      if (position.x < center.x && position.y > center.y) {
+        return true;
+      }
+    } else if (pointIndex == 1) {
+      if (position.x > center.x && position.y > center.y) {
+        return true;
+      }
+    } else if (pointIndex == 2) {
+      if (position.x > center.x && position.y < center.y) {
+        return true;
+      }
+    } else if (pointIndex == 3) {
+      if (position.x < center.x && position.y < center.y) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  // moveRing(position, circleId) {
+  //   let circle = dataService.getCircle(circleId);
+  //   circle.setRadius(mathUtil.getDistance(circle.center, position));
+  // }
+}
+
+const moveCircle = new MoveCircle();
+export { moveCircle };

+ 3 - 3
src/graphic/Controls/MoveRoad.js

@@ -5,7 +5,7 @@ import { mathUtil } from "../Util/MathUtil";
 import { curveRoadService } from "../Service/CurveRoadService";
 import { coordinate } from "../Coordinate";
 import { edgeService } from "../Service/EdgeService";
-import { controlPointService } from "../Service/ControlPointService";
+import { crossPointService } from "../Service/CrossPointService";
 
 export default class MoveRoad {
   constructor() {
@@ -1145,8 +1145,8 @@ export default class MoveRoad {
     curveRoadService.updateForMovePoint(pointId, position);
   }
 
-  moveControlPoint(controlPointId, position) {
-    controlPointService.updateForMovePoint(controlPointId, position);
+  moveCrossPoint(crossPointId, position) {
+    crossPointService.updateForMovePoint(crossPointId, position);
   }
 
   moveEdge(edgeId, position) {

+ 1 - 0
src/graphic/Controls/MoveText.js

@@ -1,4 +1,5 @@
 import { dataService } from "../Service/DataService";
+import { mathUtil } from "../Util/MathUtil";
 
 export default class MoveText {
   constructor() {}

+ 148 - 76
src/graphic/Controls/UIControl.js

@@ -2,6 +2,7 @@ import { coordinate } from "../Coordinate.js";
 import LayerEvents from "../enum/LayerEvents.js";
 import UIEvents from "../enum/UIEvents.js";
 import VectorType from "../enum/VectorType.js";
+import GeoActions from "../enum/GeoActions.js";
 import { stateService } from "../Service/StateService.js";
 import { uiService } from "../Service/UIService.js";
 import { dataService } from "../Service/DataService.js";
@@ -12,12 +13,15 @@ import { textService } from "../Service/TextService.js/";
 import Constant from "../Constant";
 // import { roomsUtil } from "../Room/RoomsUtil.js";
 import { addRoad } from "../Controls/AddRoad";
+import { addLine } from "./AddLine.js";
+import VectorCategory from "../enum/VectorCategory.js";
 // import { floorplanData } from "../VectorData.js";
 
 export default class UIControl {
-  constructor(layer, newsletter) {
+  constructor(layer, newsletter, graphicStateUI) {
     this.layer = layer;
     this.newsletter = newsletter;
+    this.graphicStateUI = graphicStateUI;
   }
 
   get selectUI() {
@@ -70,13 +74,19 @@ export default class UIControl {
           stateService.setEventName(LayerEvents.AddRoad);
         } else if (selectUI == UIEvents.CurveRoad) {
           stateService.setEventName(LayerEvents.AddCurveRoad);
+        } else if (selectUI == UIEvents.Arrow) {
+          stateService.setEventName(LayerEvents.AddLine);
+          addLine.setCategory(VectorCategory.Line.ArrowLine);
+        } else if (selectUI == UIEvents.MeasureLine) {
+          stateService.setEventName(LayerEvents.AddLine);
+          addLine.setCategory(VectorCategory.Line.MeasureLine);
         } else if (selectUI == UIEvents.Line) {
           stateService.setEventName(LayerEvents.AddLine);
         } else if (selectUI == UIEvents.Circle) {
           stateService.setEventName(LayerEvents.AddCircle);
         } else if (selectUI == UIEvents.Text) {
           stateService.setEventName(LayerEvents.AddText);
-        } else if (selectUI == UIEvents.magnifier) {
+        } else if (selectUI == UIEvents.Magnifier) {
           stateService.setEventName(LayerEvents.AddMagnifier);
         } else if (selectUI == UIEvents.SVG) {
           stateService.setEventName(LayerEvents.AddSVG);
@@ -87,82 +97,144 @@ export default class UIControl {
     }
   }
 
+  async handleGeo(action) {
+    let needAutoRedraw = false;
+    const item = stateService.getFocusItem();
+    if (item && item.vectorId) {
+      switch (action) {
+        case GeoActions.CopyAction:
+          await dataService.copyVector(item.vectorId, item.type);
+          needAutoRedraw = true;
+          break;
+        case GeoActions.DeleteAction:
+          dataService.deleteVector(item.vectorId, item.type);
+          needAutoRedraw = true;
+          break;
+      }
+    }
+
+    if (needAutoRedraw) {
+      this.layer.history.save();
+      this.layer.renderer.autoRedraw();
+    }
+  }
+
+
+  //截图
+  async screenShot() {
+    let canvas = this.layer.canvas;
+    //隐藏grid
+    dataService.setGridDisplay(false);
+    this.layer.renderer.autoRedraw();
+    // this.downloadCadImg(canvas, "test.jpg");
+    const blob = await this.getCadBlob(canvas)
+    //显示grid
+    dataService.setGridDisplay(true);
+    this.layer.renderer.autoRedraw();
+
+    return blob
+  }
+
+  getCadBlob(canvas) {
+    var type = "jpg";
+    return new Promise(resolve => canvas.toBlob(resolve, `${type}/image`))
+  }
+
+  downloadCadImg(canvas, filename) {
+    // 图片导出为 jpg 格式
+    var type = "jpg";
+    var imgData = canvas.toDataURL(type, 3);
+    canvas.toBlob(`${type}/image`)
+
+    // 加工image data,替换mime type
+    imgData = imgData.replace(this._fixType(type), "image/octet-stream");
+    // 下载后的图片名
+    //var filename = 'cad_' + new Date().getTime() + '.' + type
+    // download
+    debugger
+    this.saveFile(imgData, filename);
+  }
+
+  saveFile(data, filename) {
+    var save_link = document.createElementNS(
+      "http://www.w3.org/1999/xhtml",
+      "a"
+    );
+    save_link.href = data;
+    save_link.download = filename;
+
+    var event = document.createEvent("MouseEvents");
+    event.initMouseEvent(
+      "click",
+      true,
+      false,
+      window,
+      0,
+      0,
+      0,
+      0,
+      0,
+      false,
+      false,
+      false,
+      false,
+      0,
+      null
+    );
+    save_link.dispatchEvent(event);
+  }
+
+  _fixType(type) {
+    type = type.toLowerCase().replace(/jpg/i, "jpeg");
+    var r = type.match(/png|jpeg|bmp|gif/)[0];
+    return "image/" + r;
+  }
   /****************************************************************************针对菜单*******************************************************************************/
 
-  // execute(name, value) {
-  //   stateService.clearFocusItem();
-  //   stateService.clearSelectItem();
-  //   //this.layer.$xui.hideProps()
-  //   this.layer.uiControl.currentUI = null;
-  //   switch (name) {
-  //     case "recall": //撤销
-  //       this.menu_revoke();
-  //       break;
-  //     case "recover": //恢复
-  //       this.menu_recovery();
-  //       break;
-  //     case "default": //恢复默认
-  //       this.menu_default();
-  //       break;
-  //     case "download": //下载
-  //       this.layer.div.style.visibility = "hidden";
-  //       this.menu_screenShot(value).then(() => {
-  //         this.layer.div.style.visibility = "visible";
-  //       });
-  //       break;
-  //     case "texture": //底图
-  //       this.showTexture = value;
-  //       this.layer.app.dom.querySelector(
-  //         '.player[name="main"]'
-  //       ).style.visibility = this.showTexture ? "visible" : "hidden";
-  //       break;
-  //     case "clear": //清空
-  //       this.menu_clear();
-  //       break;
-  //     case "panos": //漫游点
-  //       this.menu_panos(value);
-  //       break;
-  //     case "rotate": //旋转
-  //       this.menu_rotate();
-  //       break;
-  //     case "flex": //适应视图
-  //       this.menu_flex();
-  //       break;
-  //   }
-  // }
-
-  // //撤销
-  // menu_revoke() {
-  //   // this.layer.history.goPreState()
-  //   // this.layer.renderer.autoRedraw()
-
-  //   // const historyState = historyService.getHistoryState()
-  //   // if (historyState.pre) {
-  //   //     this.layer.$xui.toolbar.recall = true
-  //   // } else {
-  //   //     this.layer.$xui.toolbar.recall = false
-  //   // }
-  //   // this.layer.$xui.toolbar.recover = true
-  //   this.layer.stopAddVector();
-  //   this.layer.revokeHistory();
-  // }
-
-  // //恢复
-  // menu_recovery() {
-  //   // this.layer.history.goNextState()
-  //   // this.layer.renderer.autoRedraw()
-
-  //   // const historyState = historyService.getHistoryState()
-  //   // if (historyState.next) {
-  //   //     this.layer.$xui.toolbar.recover = true
-  //   // } else {
-  //   //     this.layer.$xui.toolbar.recover = false
-  //   // }
-  //   // this.layer.$xui.toolbar.recall = true
-  //   this.layer.recoveryHistory();
-  // }
-
-  // menu_default() {}
+  //撤销
+  menu_revoke() {
+    this.layer.history.goPreState();
+
+    const historyState = historyService.getHistoryState();
+    this.graphicStateUI.canRevoke = historyState.pre;
+    this.graphicStateUI.canRecovery = true;
+
+    this.layer.stopAddVector();
+    this.layer.renderer.autoRedraw();
+  }
+
+  //恢复
+  menu_recovery() {
+    this.layer.history.goNextState();
+
+    const historyState = historyService.getHistoryState();
+    this.graphicStateUI.canRecovery = historyState.next;
+    this.graphicStateUI.canRevoke = true;
+
+    this.layer.stopAddVector();
+    this.layer.renderer.autoRedraw();
+  }
+
+  menu_view_reset() {
+    coordinate.reSet(this.layer.canvas);
+    this.layer.renderer.autoRedraw();
+  }
+
+  // value 为true则开 false则关
+  menu_backgroundImg(value) {
+    console.log(value)
+    //
+    const backgroundImg = dataService.getBackgroundImg();
+    backgroundImg.setDisplay(value);
+    this.layer.renderer.autoRedraw();
+  }
+
+  menu_clear() {
+    dataService.clear();
+    elementService.hideAll();
+    this.layer.history.save();
+    this.layer.renderer.autoRedraw();
+  }
 
   /******************************************************************************************************************************************************************/
 }

+ 5 - 4
src/graphic/Coordinate.js

@@ -1,11 +1,12 @@
 const defaultZoom = 100;
+const defaultRes = 1;
 
 export default class Coordinate {
   constructor() {
     this.center = null; // 世界坐标,中心点。默认是[0,0]
     this.defaultZoom = defaultZoom;
     this.zoom = defaultZoom; // 当前缩放比例,不会改变世界坐标系的坐标,只是改变渲染时转换的屏幕坐标
-    this.res = 80; //比例尺。实际尺寸与屏幕像素的比例,用于测量。与之前的绘制不同,目前存储的数据与像素的比例是1:1,只是最后测量值再除以这个比例
+    this.res = defaultRes; //比例尺。实际尺寸与屏幕像素的比例,用于测量。与之前的绘制不同,目前存储的数据与像素的比例是1:1,只是最后测量值再除以这个比例
     this.ratio = 1; //不同硬件设备,像素率不同
 
     this.width == null;
@@ -116,9 +117,9 @@ export default class Coordinate {
 
   clear() {
     this.center = null;
-    this.zoom = 100;
-    this.res = 80;
-    this.ratio = 1;
+    this.zoom = defaultZoom;
+    this.res = defaultRes;
+    this.ratio = defaultRes;
   }
 }
 

+ 31 - 8
src/graphic/Geometry/Circle.js

@@ -2,29 +2,52 @@ import VectorType from "../enum/VectorType.js";
 import SelectState from "../enum/SelectState.js";
 import Geometry from "./Geometry";
 import Constant from "../Constant.js";
+import Style from "../CanvasStyle";
 
 //不靠墙
 export default class Circle extends Geometry {
   constructor(center, radius, vectorId) {
     super();
-    this.radius = 5;
+    this.radius = Style.Circle.radius;
     this.center = null;
+    this.color = Style.Circle.strokeStyle;
+    this.points = []; //包含圆的4个顶点,按照顺时针的方式存入数组中,第一个元素是左上角顶点
     this.geoType = VectorType.Circle;
     this.setId(vectorId);
 
     this.setRadius(radius);
     this.setCenter(center);
+    this.createPoints();
   }
 
-  setRadius(radius) {
-    this.radius = radius;
+  createPoints() {
+    this.points[0] = {
+      x: this.center.x - this.radius,
+      y: this.center.y + this.radius,
+    };
+    this.points[1] = {
+      x: this.center.x + this.radius,
+      y: this.center.y + this.radius,
+    };
+    this.points[2] = {
+      x: this.center.x + this.radius,
+      y: this.center.y - this.radius,
+    };
+    this.points[3] = {
+      x: this.center.x - this.radius,
+      y: this.center.y - this.radius,
+    };
   }
 
-  setCenter(center) {
-    if (center) {
-      this.center = {};
-      this.center.x = center.x;
-      this.center.y = center.y;
+  setPoints(points) {
+    if (points && points.length == 4) {
+      this.points = JSON.parse(JSON.stringify(points));
+    } else {
+      this.createPoints();
     }
   }
+
+  setColor(value) {
+    this.color = value;
+  }
 }

+ 23 - 17
src/graphic/Geometry/ControlPoint.js

@@ -2,7 +2,7 @@ import Constant from "../Constant.js";
 import { dataService } from "../Service/DataService.js";
 import { mathUtil } from "../Util/MathUtil.js";
 import VectorType from "../enum/VectorType.js";
-import Geometry from "./Geometry";
+import Geometry from "./Geometry.js";
 
 // 二次贝塞尔曲线
 // var ctx=c.getContext("2d");
@@ -11,7 +11,7 @@ import Geometry from "./Geometry";
 // ctx.quadraticCurveTo(20,100,20,20);
 // ctx.stroke();
 
-export default class ControlPoint extends Geometry {
+export default class CrossPoint extends Geometry {
   constructor(position, vectorId) {
     super();
     //控制点坐标
@@ -30,7 +30,7 @@ export default class ControlPoint extends Geometry {
 
     this.extremePoint = null; //极值
     this.curves = [];
-    this.geoType = VectorType.ControlPoint;
+    this.geoType = VectorType.CrossPoint;
     this.setId(vectorId);
 
     this.setPosition(position);
@@ -44,33 +44,39 @@ export default class ControlPoint extends Geometry {
   setExtremePoint() {
     let points = [];
     let edge1 = dataService.getRoadEdge(this.edgeInfo1.id);
+    let curve = {};
     if (this.edgeInfo1.dir == "start") {
-      points[0] = edge1.start;
+      curve.start = {};
+      mathUtil.clonePoint(curve.start, edge1.start);
     } else if (this.edgeInfo1.dir == "end") {
-      points[0] = edge1.end;
+      curve.start = {};
+      mathUtil.clonePoint(curve.start, edge1.end);
     }
-    points[1] = {
+    curve.controls = [];
+    curve.controls.push({
       x: this.x,
       y: this.y,
-    };
+    });
     let edge2 = dataService.getRoadEdge(this.edgeInfo2.id);
     if (this.edgeInfo2.dir == "start") {
-      points[2] = edge2.start;
+      curve.end = {};
+      mathUtil.clonePoint(curve.end, edge2.start);
     } else if (this.edgeInfo2.dir == "end") {
-      points[2] = edge2.end;
+      curve.end = {};
+      mathUtil.clonePoint(curve.end, edge2.end);
     }
-    let curves = mathUtil.getCurvesByPoints(points);
-    let joinInfo = mathUtil.getHitInfoForCurves(
-      points[1],
-      curves,
+
+    let joinInfo = mathUtil.getHitInfoForCurve(
+      curve.controls[0],
+      curve,
       Constant.defaultRoadWidth
     );
     this.extremePoint = {};
     mathUtil.clonePoint(this.extremePoint, joinInfo.position);
     this.curves = mathUtil.getCurvesByPoints([
-      points[0],
+      curve.start,
       this.extremePoint,
-      points[2],
+      curve.end,
     ]);
   }
 
@@ -83,8 +89,8 @@ export default class ControlPoint extends Geometry {
       points[0] = edge1.end;
     }
     points[1] = {
-      x: this.x,
-      y: this.y,
+      x: this.extremePoint.x,
+      y: this.extremePoint.y,
     };
     let edge2 = dataService.getRoadEdge(this.edgeInfo2.id);
     if (this.edgeInfo2.dir == "start") {

+ 8 - 8
src/graphic/Geometry/CurveRoad.js

@@ -1,6 +1,6 @@
 import VectorType from "../enum/VectorType.js";
 import Road from "./Road.js";
-import Setting from "../Setting";
+import Settings from "../Settings";
 
 export default class CurveRoad extends Road {
   constructor(startId, endId, vectorId) {
@@ -9,20 +9,20 @@ export default class CurveRoad extends Road {
     this.leftLanesCurves = []; //左车道曲线
     this.rightLanesCurves = []; //左车道曲线
     this.singleLanesCurves = []; //单向车道
-    this.leftDrivewayCount = Setting.curveRoadLeftDrivewayCount; //左边的车道个数
-    this.rightDrivewayCount = Setting.curveRoadRightDrivewayCount; //右边的车道个数
-    this.leftWidth = Setting.leftCurveRoadWidth;
-    this.rightWidth = Setting.rightCurveRoadWidth;
+    this.leftDrivewayCount = Settings.curveRoadLeftDrivewayCount; //左边的车道个数
+    this.rightDrivewayCount = Settings.curveRoadRightDrivewayCount; //右边的车道个数
+    this.leftWidth = Settings.leftCurveRoadWidth;
+    this.rightWidth = Settings.rightCurveRoadWidth;
     this.midDivide = {
       leftMidDivide: [],
       leftMidDivideCurves: [],
       rightMidDivide: [],
       rightMidDivideCurves: [],
-      midDivideWidth: Setting.curveRoadMidDivideWidth,
+      midDivideWidth: Settings.curveRoadMidDivideWidth,
     };
     this.curves = [];
-    this.singleCurveRoadWidth = Setting.singleCurveRoadWidth;
-    this.singleCurveRoadDrivewayCount = Setting.singleCurveRoadDrivewayCount;
+    this.singleCurveRoadWidth = Settings.singleCurveRoadWidth;
+    this.singleCurveRoadDrivewayCount = Settings.singleCurveRoadDrivewayCount;
     this.geoType = VectorType.CurveRoad;
     this.setId(vectorId);
   }

+ 24 - 0
src/graphic/Geometry/Geometry.js

@@ -40,10 +40,34 @@ export default class Geometry {
     this.rightEdgeId = edgeId;
   }
 
+  setDisplay(value) {
+    this.display = value;
+  }
+
+  setParent(value) {
+    this.parent = value;
+  }
+
   getParent() {
     return this.parent;
   }
 
+  setCenter(center) {
+    if (center) {
+      this.center = {};
+      this.center.x = center.x;
+      this.center.y = center.y;
+    }
+  }
+
+  setRadius(radius) {
+    this.radius = radius;
+  }
+
+  setColor(value) {
+    this.color = value;
+  }
+
   // ptSrc: 圆上某点(初始点);
   // ptRotationCenter: 圆心点;
   // angle: 旋转角度°  -- [angle * M_PI / 180]:将角度换算为弧度

+ 21 - 0
src/graphic/Geometry/Img.js

@@ -1,3 +1,4 @@
+import { mathUtil } from "../Util/MathUtil.js";
 import VectorType from "../enum/VectorType.js";
 import Geometry from "./Geometry";
 
@@ -6,12 +7,32 @@ export default class Img extends Geometry {
     super();
     this.src = null;
     this.angle = 0;
+    this.display = true;
+    this.center = null;
+    this.imageData = null;
     this.geoType = VectorType.Img;
     this.setId(vectorId);
     this.setSrc(src);
   }
 
+  setAngle(value) {
+    this.angle = value;
+  }
+
   setSrc(src) {
     this.src = src;
   }
+
+  setImageData() {
+    return new Promise((resolve, reject) => {
+      this.imageData = new Image();
+      this.imageData.src = this.src;
+      this.imageData.onload = function () {
+        resolve();
+      };
+      this.imageData.onerror = function () {
+        reject();
+      };
+    });
+  }
 }

+ 16 - 1
src/graphic/Geometry/Line.js

@@ -1,20 +1,35 @@
 import VectorType from "../enum/VectorType.js";
+import VectorCategory from "../enum/VectorCategory.js";
 import SelectState from "../enum/SelectState.js";
 import Geometry from "./Geometry";
 import Constant from "../Constant.js";
+import Style from "@/graphic/CanvasStyle/index.js";
 
 export default class Line extends Geometry {
   constructor(startId, endId, vectorId) {
     super();
     this.startId = startId;
     this.endId = endId;
-    this.category = null;
+    this.category = VectorCategory.Line.NormalLine;
+    this.arrowColor = Style.Arrow.strokeStyle; //箭头类型会用到
     this.geoType = VectorType.Line;
     this.setId(vectorId);
   }
 
   //NormalLine,GuideLine,MeasureLine,BaseLine
   setCategory(value) {
+    if (!value) {
+      this.category = VectorCategory.Line.NormalLine;
+    } else {
+    }
     this.category = value;
   }
+
+  getCategory() {
+    return this.category;
+  }
+
+  setArrowColor(value) {
+    this.arrowColor = value;
+  }
 }

+ 37 - 4
src/graphic/Geometry/Magnifier.js

@@ -10,18 +10,20 @@ export default class Magnifier extends Geometry {
   constructor(position, vectorId) {
     super();
     this.position = {};
+    this.photoUrl = null;
+    this.photoImage = null;
     this.popPosition = {};
     this.setPosition(position);
     this.geoType = VectorType.Magnifier;
     this.setId(vectorId);
   }
 
-  setPosition(position) {
-    if (!position) {
+  setPosition(positionRaw) {
+    if (!positionRaw) {
       return;
     }
-    this.position.x = position.x;
-    this.position.y = position.y;
+    this.position.x = positionRaw.x;
+    this.position.y = positionRaw.y;
     let position = coordinate.getScreenXY(this.position);
     let center = {
       x: coordinate.width / 2,
@@ -49,4 +51,35 @@ export default class Magnifier extends Geometry {
       }
     }
   }
+
+  setPopPosition(value) {
+    if (value) {
+      this.popPosition = {};
+      this.popPosition.x = value.x;
+      this.popPosition.y = value.y;
+    }
+  }
+
+  setSrc(src) {
+    this.photoUrl = src;
+  }
+
+  setImageData() {
+
+    return new Promise((resolve, reject) => {
+      if (this.photoUrl) {
+        this.photoImage = new Image();
+        this.photoImage.src = this.photoUrl;
+        this.photoImage.onload = function () {
+          resolve();
+        };
+        this.photoImage.onerror = function () {
+          reject();
+        };
+      } else {
+        this.photoImage = null
+        resolve()
+      }
+    });
+  }
 }

+ 11 - 2
src/graphic/Geometry/Point.js

@@ -1,4 +1,5 @@
 import VectorType from "../enum/VectorType.js";
+import VectorCategory from "../enum/VectorCategory.js";
 import Geometry from "./Geometry";
 
 export default class Point extends Geometry {
@@ -7,7 +8,7 @@ export default class Point extends Geometry {
     this.x = null;
     this.y = null;
     this.parent = {};
-    this.category = null;
+    this.category = VectorCategory.Point.NormalPoint;
     this.geoType = VectorType.Point;
     this.setId(vectorId);
 
@@ -19,8 +20,16 @@ export default class Point extends Geometry {
     this.y = position.y;
   }
 
+  getCategory() {
+    return this.category;
+  }
+
   //基准点:BasePoint
   setCategory(value) {
-    this.category = value;
+    if (!value) {
+      this.category = VectorCategory.Point.NormalPoint;
+    } else {
+      this.category = value;
+    }
   }
 }

+ 13 - 9
src/graphic/Geometry/Road.js

@@ -1,6 +1,6 @@
 import VectorType from "../enum/VectorType.js";
 import Geometry from "./Geometry.js";
-import Setting from "../Setting";
+import Settings from "../Settings";
 import Constant from "../Constant";
 
 export default class Road extends Geometry {
@@ -18,17 +18,17 @@ export default class Road extends Geometry {
     this.midDivide = {
       leftMidDivide: {},
       rightMidDivide: {},
-      midDivideWidth: Setting.roadMidDivideWidth,
+      midDivideWidth: Settings.roadMidDivideWidth,
     };
 
-    this.leftDrivewayCount = Setting.roadLeftDrivewayCount; //左边的车道个数
-    this.rightDrivewayCount = Setting.roadRightDrivewayCount; //右边的车道个数
+    this.leftDrivewayCount = Settings.roadLeftDrivewayCount; //左边的车道个数
+    this.rightDrivewayCount = Settings.roadRightDrivewayCount; //右边的车道个数
     this.geoType = VectorType.Road;
-    this.leftWidth = Setting.leftRoadWidth;
-    this.rightWidth = Setting.rightRoadWidth;
-    this.singleRoadWidth = Setting.singleRoadWidth;
-    this.singleRoadDrivewayCount = Setting.singleRoadDrivewayCount;
-    this.way = Setting.wayType;
+    this.leftWidth = Settings.leftRoadWidth;
+    this.rightWidth = Settings.rightRoadWidth;
+    this.singleRoadWidth = Settings.singleRoadWidth;
+    this.singleRoadDrivewayCount = Settings.singleRoadDrivewayCount;
+    this.way = Settings.wayType;
     this.setId(vectorId);
   }
 
@@ -83,4 +83,8 @@ export default class Road extends Geometry {
       this.rightDrivewayCount = 0;
     }
   }
+
+  setWay(value) {
+    this.way = value;
+  }
 }

+ 0 - 7
src/graphic/Geometry/SVG.js

@@ -12,11 +12,4 @@ export default class SVG extends Geometry {
     this.geoType = VectorType.SVG;
     this.setId(vectorId);
   }
-
-  setCenter(center) {
-    this.center = {
-      x: center.x,
-      y: center.y,
-    };
-  }
 }

+ 3 - 13
src/graphic/Geometry/Text.js

@@ -3,6 +3,7 @@ import Geometry from "./Geometry.js";
 import { mathUtil } from "../Util/MathUtil.js";
 import { coordinate } from "../Coordinate.js";
 import Constant from "../Constant.js";
+import Style from "@/graphic/CanvasStyle/index.js";
 
 //不靠墙
 export default class Text extends Geometry {
@@ -10,27 +11,16 @@ export default class Text extends Geometry {
     super();
     this.center = center;
     this.value = "固定点";
-    this.color = null;
-    this.fontSize = null;
+    this.color = Style.Text.fillStyle;
+    this.fontSize = Style.Text.fontSize;
     this.geoType = VectorType.Text;
     this.setId(vectorId);
   }
 
-  setCenter(center) {
-    this.center = {
-      x: center.x,
-      y: center.y,
-    };
-  }
-
   setValue(value) {
     this.value = value;
   }
 
-  setColor(value) {
-    this.color = value;
-  }
-
   setFontSize(value) {
     this.fontSize = value;
   }

+ 238 - 3
src/graphic/History/Change.js

@@ -23,8 +23,23 @@ export default class Change {
     this.lastData.circles = JSON.parse(
       JSON.stringify(dataService.getCircles())
     );
-    this.lastData.texts = JSON.parse(JSON.stringify(dataService.getTexts()));
+    this.lastData.magnifiers = JSON.parse(
+      JSON.stringify(dataService.getMagnifiers())
+    );
     this.lastData.svgs = JSON.parse(JSON.stringify(dataService.getSVGs()));
+    this.lastData.roadPoints = JSON.parse(
+      JSON.stringify(dataService.getRoadPoints())
+    );
+    this.lastData.roadEdges = JSON.parse(
+      JSON.stringify(dataService.getRoadEdges())
+    );
+    this.lastData.roads = JSON.parse(JSON.stringify(dataService.getRoads()));
+    this.lastData.curveRoads = JSON.parse(
+      JSON.stringify(dataService.getCurveRoads())
+    );
+    this.lastData.crossPoints = JSON.parse(
+      JSON.stringify(dataService.getCrossPoints())
+    );
   }
 
   operate() {
@@ -35,12 +50,23 @@ export default class Change {
     this.compareLines();
     this.compareCircles();
     this.compareTexts();
-
+    this.compareMagnifiers();
+    this.compareSVGs();
+    this.compareRoadPoints();
+    this.compareRoadEdges();
+    this.compareRoads();
+    this.compareCurveRoads();
+    this.compareCrossPoints();
     if (
       this.currentData.points.length == 0 &&
       this.currentData.lines.length == 0 &&
       this.currentData.circles.length == 0 &&
-      this.currentData.texts.length == 0
+      this.currentData.texts.length == 0 &&
+      this.currentData.magnifiers.length == 0 &&
+      this.currentData.roadPoints.length == 0 &&
+      this.currentData.roadEdges.length == 0 &&
+      this.currentData.roads.length == 0 &&
+      this.currentData.crossPoints.length == 0
     ) {
       this.saveCurrentInfo();
       return false;
@@ -246,6 +272,47 @@ export default class Change {
     }
   }
 
+  compareMagnifiers() {
+    const magnifiers = dataService.getMagnifiers();
+    this.currentData.magnifiers = [];
+
+    for (const key in magnifiers) {
+      const magnifier = magnifiers[key];
+      // 不存在意味着增加
+      if (!this.lastData.magnifiers[key]) {
+        const item = {
+          handle: HistoryEvents.AddMagnifier,
+          magnifier: historyUtil.getDataForMagnifier(magnifier),
+        };
+        this.currentData.magnifiers.push(item);
+      } else {
+        const lastMagnifier = this.lastData.magnifiers[key];
+        if (!historyUtil.isDifferentForMagnifiers(magnifier, lastMagnifier)) {
+          delete this.lastData.magnifiers[key];
+          continue;
+        } else {
+          const item = {
+            handle: HistoryEvents.ModifyMagnifier,
+            preMagnifier: historyUtil.getDataForMagnifier(lastMagnifier),
+            curMagnifier: historyUtil.getDataForMagnifier(magnifier),
+          };
+          this.currentData.magnifiers.push(item);
+        }
+      }
+      delete this.lastData.magnifiers[key];
+    }
+
+    for (const key in this.lastData.magnifiers) {
+      const item = {
+        handle: HistoryEvents.DeleteMagnifier,
+        magnifier: historyUtil.getDataForMagnifier(
+          this.lastData.magnifiers[key]
+        ),
+      };
+      this.currentData.magnifiers.push(item);
+    }
+  }
+
   compareSVGs() {
     this.currentData.svgs = [];
     const svgs = dataService.getSVGs();
@@ -285,6 +352,174 @@ export default class Change {
       this.currentData.svgs.push(item);
     }
   }
+
+  compareRoadPoints() {
+    this.currentData.roadPoints = [];
+    const roadPoints = dataService.getRoadPoints();
+
+    for (const key in roadPoints) {
+      const roadPoint = roadPoints[key];
+      const lastRoadPoint = this.lastData.roadPoints[key];
+
+      // 不存在意味着增加
+      if (!lastRoadPoint) {
+        const item = {
+          handle: HistoryEvents.AddRoadPoint,
+          roadPoint: historyUtil.getDataForRoadPoint(roadPoint),
+        };
+        this.currentData.roadPoints.push(item);
+      } else {
+        if (!historyUtil.isDifferentForRoadPoints(roadPoint, lastRoadPoint)) {
+          delete this.lastData.roadPoints[key];
+          continue;
+        } else {
+          const item = {
+            handle: HistoryEvents.ModifyRoadPoint,
+            preRoadPoint: historyUtil.getDataForRoadPoint(lastRoadPoint),
+            curRoadPoint: historyUtil.getDataForRoadPoint(roadPoint),
+          };
+          this.currentData.roadPoints.push(item);
+        }
+      }
+      delete this.lastData.roadPoints[key];
+    }
+
+    for (const key in this.lastData.roadPoints) {
+      const item = {
+        handle: HistoryEvents.DeleteRoadPoint,
+        roadPoint: historyUtil.getDataForRoadPoint(
+          this.lastData.roadPoints[key]
+        ),
+      };
+      this.currentData.roadPoints.push(item);
+    }
+  }
+
+  compareRoadEdges() {
+    this.currentData.roadEdges = [];
+    const roadEdges = dataService.getRoadEdges();
+
+    for (const key in roadEdges) {
+      const roadEdge = roadEdges[key];
+      const lastRoadEdge = this.lastData.roadEdges[key];
+
+      // 不存在意味着增加
+      if (!lastRoadEdge) {
+        const item = {
+          handle: HistoryEvents.AddRoadEdge,
+          roadEdge: historyUtil.getDataForRoadEdge(roadEdge),
+        };
+        this.currentData.roadEdges.push(item);
+      } else {
+        if (!historyUtil.isDifferentForRoadEdges(roadEdge, lastRoadEdge)) {
+          delete this.lastData.roadEdges[key];
+          continue;
+        } else {
+          const item = {
+            handle: HistoryEvents.ModifyRoadEdge,
+            preRoadEdge: historyUtil.getDataForRoadEdge(lastRoadEdge),
+            curRoadEdge: historyUtil.getDataForRoadEdge(roadEdge),
+          };
+          this.currentData.roadEdges.push(item);
+        }
+      }
+      delete this.lastData.roadEdges[key];
+    }
+
+    for (const key in this.lastData.roadEdges) {
+      const item = {
+        handle: HistoryEvents.DeleteRoadEdge,
+        roadEdge: historyUtil.getDataForRoadEdge(this.lastData.roadEdges[key]),
+      };
+      this.currentData.roadEdges.push(item);
+    }
+  }
+
+  compareRoads() {
+    this.currentData.roads = [];
+    const roads = dataService.getRoads();
+
+    for (const key in roads) {
+      const road = roads[key];
+      const lastRoad = this.lastData.roads[key];
+
+      // 不存在意味着增加
+      if (!lastRoad) {
+        const item = {
+          handle: HistoryEvents.AddRoad,
+          road: historyUtil.getDataForRoad(road),
+        };
+        this.currentData.roads.push(item);
+      } else {
+        if (!historyUtil.isDifferentForRoads(road, lastRoad)) {
+          delete this.lastData.roads[key];
+          continue;
+        } else {
+          const item = {
+            handle: HistoryEvents.ModifyRoad,
+            preRoad: historyUtil.getDataForRoad(lastRoad),
+            curRoad: historyUtil.getDataForRoad(road),
+          };
+          this.currentData.roads.push(item);
+        }
+      }
+      delete this.lastData.roads[key];
+    }
+
+    for (const key in this.lastData.roads) {
+      const item = {
+        handle: HistoryEvents.DeleteRoad,
+        road: historyUtil.getDataForRoad(this.lastData.roads[key]),
+      };
+      this.currentData.roads.push(item);
+    }
+  }
+
+  compareCurveRoads() {}
+
+  compareCrossPoints() {
+    this.currentData.crossPoints = [];
+    const crossPoints = dataService.getCrossPoints();
+
+    for (const key in crossPoints) {
+      const crossPoint = crossPoints[key];
+      const lastCrossPoint = this.lastData.crossPoints[key];
+
+      // 不存在意味着增加
+      if (!lastCrossPoint) {
+        const item = {
+          handle: HistoryEvents.AddCrossPoint,
+          crossPoint: historyUtil.getDataForCrossPoint(crossPoint),
+        };
+        this.currentData.crossPoints.push(item);
+      } else {
+        if (
+          !historyUtil.isDifferentForCrossPoints(crossPoint, lastCrossPoint)
+        ) {
+          delete this.lastData.crossPoints[key];
+          continue;
+        } else {
+          const item = {
+            handle: HistoryEvents.ModifyCrossPoint,
+            preCrossPoint: historyUtil.getDataForCrossPoint(lastCrossPoint),
+            curCrossPoint: historyUtil.getDataForCrossPoint(crossPoint),
+          };
+          this.currentData.crossPoints.push(item);
+        }
+      }
+      delete this.lastData.crossPoints[key];
+    }
+
+    for (const key in this.lastData.crossPoints) {
+      const item = {
+        handle: HistoryEvents.DeleteCrossPoint,
+        crossPoint: historyUtil.getDataForCrossPoint(
+          this.lastData.crossPoints[key]
+        ),
+      };
+      this.currentData.crossPoints.push(item);
+    }
+  }
 }
 
 const change = new Change();

+ 257 - 15
src/graphic/History/History.js

@@ -10,6 +10,10 @@ import { svgService } from "../Service/SVGService";
 import { roadPointService } from "../Service/RoadPointService";
 import { lineService } from "../Service/LineService";
 import { circleService } from "../Service/CircleService";
+import { pointService } from "../Service/PointService";
+import { edgeService } from "../Service/EdgeService";
+import { magnifierService } from "../Service/MagnifierService";
+import { crossPointService } from "../Service/CrossPointService";
 
 export default class History {
   constructor(layer) {
@@ -28,11 +32,9 @@ export default class History {
     historyService.addHistoryRecord(change.currentData);
     change.saveCurrentInfo();
     this.setState();
-    // const historyState = historyService.getHistoryState();
-    // const points = dataService.getRoadPoints();
-    // //给UI发送事件
-    // this.layer.emit("change");
-    // return change.currentData;
+    const historyState = historyService.getHistoryState();
+    this.layer.uiControl.graphicStateUI.canRevoke = historyState.pre;
+    this.layer.uiControl.graphicStateUI.canRecovery = historyState.next;
   }
 
   setState() {
@@ -97,7 +99,12 @@ export default class History {
       this.goPreForLines(item.lines);
       this.goPreForCircles(item.circles);
       this.goPreForTexts(item.texts);
+      this.goPreForMagnifiers(item.magnifiers);
       this.goPreForSVGs(item.svgs);
+      this.goPreForRoadPoints(item.roadPoints);
+      this.goPreForRoadEdges(item.roadEdges);
+      this.goPreForRoads(item.roads);
+      this.goPreForCrossPoints(item.crossPoints);
       historyService.undoHistoryRecord();
       change.saveCurrentInfo();
       this.setState();
@@ -112,11 +119,15 @@ export default class History {
       if (item.handle == HistoryEvents.AddPoint) {
         dataService.deletePoint(item.point.id);
       } else if (item.handle == HistoryEvents.DeletePoint) {
-        let point = roadPointService.create(item.point, item.point.id);
+        let point = pointService.create(
+          item.point,
+          item.category,
+          item.point.id
+        );
         point.parent = JSON.parse(JSON.stringify(item.point.parent));
       } else if (item.handle == HistoryEvents.ModifyPoint) {
         const prePoint = item.prePoint;
-        let currentPoint = dataService.getRoadPoint(item.curPoint.id);
+        let currentPoint = dataService.getPoint(item.curPoint.id);
         historyUtil.assignPointFromPoint(currentPoint, prePoint);
       }
     }
@@ -129,7 +140,7 @@ export default class History {
         dataService.deleteLine(item.line.id);
       } else if (item.handle == HistoryEvents.DeleteLine) {
         const preLine = item.line;
-        let newLine = lineService.create(
+        let newLine = lineService.createByPointId(
           preLine.start,
           preLine.end,
           preLine.category,
@@ -151,10 +162,9 @@ export default class History {
         dataService.deleteCircle(item.circle.id);
       } else if (item.handle == HistoryEvents.DeleteCircle) {
         const preCircle = item.circle;
-        let newCircle = lineService.createCircle(
-          preCircle.start,
-          preCircle.end,
-          preCircle.category,
+        let newCircle = circleService.create(
+          preCircle.center,
+          preCircle.radius,
           preCircle.id
         );
         historyUtil.assignCircleFromCircle(newCircle, preCircle);
@@ -182,6 +192,28 @@ export default class History {
     }
   }
 
+  goPreForMagnifiers(itemForMagnifiers) {
+    for (let i = 0; i < itemForMagnifiers.length; ++i) {
+      const item = itemForMagnifiers[i];
+      if (item.handle == HistoryEvents.AddMagnifier) {
+        dataService.deleteMagnifier(item.magnifier.id);
+      } else if (item.handle == HistoryEvents.DeleteMagnifier) {
+        let newMagnifier = magnifierService.create(
+          item.magnifier.position,
+          item.magnifier.id
+        );
+        historyUtil.assignMagnifierFromMagnifier(newMagnifier, item.magnifier);
+      } else if (item.handle == HistoryEvents.ModifyMagnifier) {
+        const preMagnifier = item.preMagnifier;
+        let currentMagnifier = dataService.getMagnifier(item.curMagnifier.id);
+        historyUtil.assignMagnifierFromMagnifier(
+          currentMagnifier,
+          preMagnifier
+        );
+      }
+    }
+  }
+
   goPreForSVGs(itemForSVGs) {
     for (let i = 0; i < itemForSVGs.length; ++i) {
       const item = itemForSVGs[i];
@@ -198,17 +230,112 @@ export default class History {
     }
   }
 
+  goPreForRoadPoints(itemForRoadPoints) {
+    for (let i = 0; i < itemForRoadPoints.length; ++i) {
+      const item = itemForRoadPoints[i];
+      if (item.handle == HistoryEvents.AddRoadPoint) {
+        dataService.deleteRoadPoint(item.roadPoint.id);
+      } else if (item.handle == HistoryEvents.DeleteRoadPoint) {
+        let newRoadPoint = roadPointService.create(
+          item.roadPoint.position,
+          item.roadPoint.id
+        );
+        historyUtil.assignRoadPointFromRoadPoint(newRoadPoint, item.roadPoint);
+      } else if (item.handle == HistoryEvents.ModifyRoadPoint) {
+        const preRoadPoint = item.preRoadPoint;
+        let currentRoadPoint = dataService.getRoadPoint(item.curRoadPoint.id);
+        historyUtil.assignRoadPointFromRoadPoint(
+          currentRoadPoint,
+          preRoadPoint
+        );
+      }
+    }
+  }
+
+  goPreForRoadEdges(itemForRoadEdges) {
+    for (let i = 0; i < itemForRoadEdges.length; ++i) {
+      const item = itemForRoadEdges[i];
+      if (item.handle == HistoryEvents.AddRoadEdge) {
+        dataService.deleteRoadEdge(item.roadEdge.id);
+      } else if (item.handle == HistoryEvents.DeleteRoadEdge) {
+        let newRoadEdge = edgeService.create(
+          item.roadEdge.start,
+          item.roadEdge.end,
+          item.roadEdge.id,
+          item.roadEdge.parent
+        );
+        historyUtil.assignRoadEdgeFromRoadEdge(newRoadEdge, item.roadEdge);
+      } else if (item.handle == HistoryEvents.ModifyRoadEdge) {
+        const preRoadEdge = item.preRoadEdge;
+        let currentRoadEdge = dataService.getRoadEdge(item.curRoadEdge.id);
+        historyUtil.assignRoadEdgeFromRoadEdge(currentRoadEdge, preRoadEdge);
+      }
+    }
+  }
+
+  goPreForRoads(itemForRoads) {
+    for (let i = 0; i < itemForRoads.length; ++i) {
+      const item = itemForRoads[i];
+      if (item.handle == HistoryEvents.AddRoad) {
+        dataService.deleteRoad(item.road.id);
+      } else if (item.handle == HistoryEvents.DeleteRoad) {
+        let newRoad = roadService.create(
+          item.road.startId,
+          item.road.endId,
+          item.road.id
+        );
+        historyUtil.assignRoadFromRoad(newRoad, item.road);
+      } else if (item.handle == HistoryEvents.ModifyRoad) {
+        const preRoad = item.preRoad;
+        let currentRoad = dataService.getRoad(item.curRoad.id);
+        historyUtil.assignRoadFromRoad(currentRoad, preRoad);
+      }
+    }
+  }
+
+  goPreForCrossPoints(itemForCrossPoints) {
+    for (let i = 0; i < itemForCrossPoints.length; ++i) {
+      const item = itemForCrossPoints[i];
+      if (item.handle == HistoryEvents.AddCrossPoint) {
+        dataService.deleteCrossPoint(item.crossPoint.id);
+      } else if (item.handle == HistoryEvents.DeleteCrossPoint) {
+        crossPointService.create;
+        let newCrossPoint = roadService.create(
+          item.crossPoint.position,
+          item.crossPoint.id
+        );
+        historyUtil.assignCrossPointFromCrossPoint(
+          newCrossPoint,
+          item.crossPoint
+        );
+      } else if (item.handle == HistoryEvents.ModifyCrossPoint) {
+        const preCrossPoint = item.preCrossPoint;
+        let currentCrossPoint = dataService.getCrossPoint2(
+          item.curCrossPoint.id
+        );
+        historyUtil.assignCrossPointFromCrossPoint(
+          currentCrossPoint,
+          preCrossPoint
+        );
+      }
+    }
+  }
+
   goNextForPoints(itemForPoints) {
     for (let i = 0; i < itemForPoints.length; ++i) {
       const item = itemForPoints[i];
       if (item.handle == HistoryEvents.AddPoint) {
-        let newPoint = roadPointService.create(item.point, item.point.id);
+        let newPoint = pointService.create(
+          item.point,
+          item.category,
+          item.point.id
+        );
         historyUtil.assignPointFromPoint(newPoint, item.point);
       } else if (item.handle == HistoryEvents.DeletePoint) {
         dataService.deletePoint(item.point.id);
       } else if (item.handle == HistoryEvents.ModifyPoint) {
         const currentPoint = item.curPoint;
-        let prePoint = dataService.getRoadPoint(item.curPoint.id);
+        let prePoint = dataService.getPoint(item.curPoint.id);
         historyUtil.assignPointFromPoint(prePoint, currentPoint);
       }
     }
@@ -219,7 +346,7 @@ export default class History {
       const item = itemForLines[i];
       if (item.handle == HistoryEvents.AddLine) {
         const preLine = item.line;
-        let newLine = lineService.create(
+        let newLine = lineService.createByPointId(
           preLine.start,
           preLine.end,
           preLine.category,
@@ -273,6 +400,28 @@ export default class History {
     }
   }
 
+  goNextForMagnifiers(itemForMagnifiers) {
+    for (let i = 0; i < itemForMagnifiers.length; ++i) {
+      const item = itemForMagnifiers[i];
+      if (item.handle == HistoryEvents.AddMagnifier) {
+        let vMagnifier = magnifierService.create(
+          item.magnifier.position,
+          item.magnifier.id
+        );
+        historyUtil.assignMagnifierFromMagnifier(vMagnifier, item.magnifier);
+      } else if (item.handle == HistoryEvents.DeleteMagnifier) {
+        dataService.deleteMagnifier(item.magnifier.id);
+      } else if (item.handle == HistoryEvents.ModifyMagnifier) {
+        const currentMagnifier = item.curMagnifier;
+        let preMagnifier = dataService.getMagnifier(item.curMagnifier.id);
+        historyUtil.assignMagnifierFromMagnifier(
+          preMagnifier,
+          currentMagnifier
+        );
+      }
+    }
+  }
+
   goNextForSVGs(itemForSVGs) {
     for (let i = 0; i < itemForSVGs.length; ++i) {
       const item = itemForSVGs[i];
@@ -289,6 +438,94 @@ export default class History {
     }
   }
 
+  goNextForRoadPoints(itemForRoadPoints) {
+    for (let i = 0; i < itemForRoadPoints.length; ++i) {
+      const item = itemForRoadPoints[i];
+      if (item.handle == HistoryEvents.AddRoadPoint) {
+        let vRoadPoint = roadPointService.create(
+          item.roadPoint.position,
+          item.roadPoint.id
+        );
+        historyUtil.assignRoadPointFromRoadPoint(vRoadPoint, item.roadPoint);
+      } else if (item.handle == HistoryEvents.DeleteRoadPoint) {
+        dataService.deleteRoadPoint(item.roadPoint.id);
+      } else if (item.handle == HistoryEvents.ModifyRoadPoint) {
+        const currentRoadPoint = item.curRoadPoint;
+        let preRoadPoint = dataService.getRoadPoint(item.curRoadPoint.id);
+        historyUtil.assignRoadPointFromRoadPoint(
+          preRoadPoint,
+          currentRoadPoint
+        );
+      }
+    }
+  }
+
+  goNextForRoadEdges(itemForRoadEdges) {
+    for (let i = 0; i < itemForRoadEdges.length; ++i) {
+      const item = itemForRoadEdges[i];
+      if (item.handle == HistoryEvents.AddRoadEdge) {
+        let vRoadEdge = edgeService.create(
+          item.roadEdge.start,
+          item.roadEdge.end,
+          item.roadEdge.id,
+          item.roadEdge.parent
+        );
+        historyUtil.assignRoadEdgeFromRoadEdge(vRoadEdge, item.roadEdge);
+      } else if (item.handle == HistoryEvents.DeleteRoadEdge) {
+        dataService.deleteRoadEdge(item.roadEdge.id);
+      } else if (item.handle == HistoryEvents.ModifyRoadEdge) {
+        const currentRoadEdge = item.curRoadEdge;
+        let preRoadEdge = dataService.getRoadEdge(item.curRoadEdge.id);
+        historyUtil.assignRoadEdgeFromRoadEdge(preRoadEdge, currentRoadEdge);
+      }
+    }
+  }
+
+  goNextForRoads(itemForRoads) {
+    for (let i = 0; i < itemForRoads.length; ++i) {
+      const item = itemForRoads[i];
+      if (item.handle == HistoryEvents.AddRoad) {
+        let vRoad = roadService.create(
+          item.road.startId,
+          item.road.endId,
+          item.road.id
+        );
+        historyUtil.assignRoadFromRoad(vRoad, item.road);
+      } else if (item.handle == HistoryEvents.DeleteRoad) {
+        dataService.deleteRoad(item.road.id);
+      } else if (item.handle == HistoryEvents.ModifyRoad) {
+        const currentRoad = item.curRoad;
+        let preRoad = dataService.getRoad(item.curRoad.id);
+        historyUtil.assignRoadFromRoad(preRoad, currentRoad);
+      }
+    }
+  }
+
+  goNextForCrossPoints(itemForCrossPoints) {
+    for (let i = 0; i < itemForCrossPoints.length; ++i) {
+      const item = itemForCrossPoints[i];
+      if (item.handle == HistoryEvents.AddCrossPoint) {
+        let vCrossPoint = crossPointService.create(
+          item.crossPoint.position,
+          item.crossPoint.id
+        );
+        historyUtil.assignCrossPointFromCrossPoint(
+          vCrossPoint,
+          item.crossPoint
+        );
+      } else if (item.handle == HistoryEvents.DeleteCrossPoint) {
+        dataService.deleteCrossPoint(item.crossPoint.id);
+      } else if (item.handle == HistoryEvents.ModifyCrossPoint) {
+        const currentCrossPoint = item.curCrossPoint;
+        let preCrossPoint = dataService.getCrossPoint2(item.curCrossPoint.id);
+        historyUtil.assignCrossPointFromCrossPoint(
+          preCrossPoint,
+          currentCrossPoint
+        );
+      }
+    }
+  }
+
   // 恢复
   goNextState() {
     historyService.redoHistoryRecord();
@@ -300,7 +537,12 @@ export default class History {
       this.goNextForLines(item.lines);
       this.goNextForCircles(item.circles);
       this.goNextForTexts(item.texts);
+      this.goNextForMagnifiers(item.magnifiers);
       this.goNextForSVGs(item.svgs);
+      this.goNextForRoadPoints(item.roadPoints);
+      this.goNextForRoadEdges(item.roadEdges);
+      this.goNextForRoads(item.roads);
+      this.goNextForCrossPoints(item.crossPoints);
       change.saveCurrentInfo();
       this.setState();
     } else {

+ 305 - 18
src/graphic/History/HistoryUtil.js

@@ -1,6 +1,7 @@
 import { mathUtil } from "../Util/MathUtil";
 import { dataService } from "../Service/DataService";
 import { textService } from "../Service/TextService";
+import Constant from "../Constant";
 
 export default class HistoryUtil {
   constructor() {}
@@ -17,7 +18,8 @@ export default class HistoryUtil {
     if (
       point1.x == point2.x &&
       point1.y == point2.y &&
-      JSON.stringify(point1.parent) == JSON.stringify(point2.parent)
+      JSON.stringify(point1.parent) == JSON.stringify(point2.parent) &&
+      point1.category == point2.category
     ) {
       return false;
     } else {
@@ -40,7 +42,8 @@ export default class HistoryUtil {
   isDifferentForCircles(circle1, circle2) {
     if (
       mathUtil.equalPoint(circle1.center, circle2.center) &&
-      circle1.radius == circle2.radius
+      circle1.radius == circle2.radius &&
+      circle1.color == circle2.color
     ) {
       return false;
     } else {
@@ -59,6 +62,17 @@ export default class HistoryUtil {
     }
   }
 
+  isDifferentForMagnifiers(magnifier1, magnifier2) {
+    if (
+      mathUtil.equalPoint(magnifier1.position, magnifier2.position) &&
+      magnifier1.photoUrl == magnifier2.photoUrl
+    ) {
+      return false;
+    } else {
+      return true;
+    }
+  }
+
   isDifferentForSVGs(svg1, svg2) {
     if (
       mathUtil.equalPoint(svg1.center, svg2.center) &&
@@ -70,28 +84,95 @@ export default class HistoryUtil {
     }
   }
 
-  // // road2赋值给road1
-  // assignRoadFromRoad(road1, road2) {
-  //   const roadInfo = {};
-  //   roadInfo.vectorId = road1.vectorId;
-  //   roadInfo.start = road2.startId;
-  //   roadInfo.end = road2.endId;
-  //   this.setRoadInfo(roadInfo);
-  // }
+  isDifferentForRoadPoints(roadPoint1, roadPoint2) {
+    if (
+      mathUtil.equalPoint(roadPoint1, roadPoint2) &&
+      JSON.stringify(roadPoint1.parent) == JSON.stringify(roadPoint2.parent)
+    ) {
+      return false;
+    } else {
+      return true;
+    }
+  }
+
+  isDifferentForRoadEdges(roadEdge1, roadEdge2) {
+    if (
+      mathUtil.equalPoint(roadEdge1.start, roadEdge2.start) &&
+      mathUtil.equalPoint(roadEdge1.end, roadEdge2.end) &&
+      roadEdge1.parent == roadEdge2.parent
+    ) {
+      return false;
+    } else {
+      return true;
+    }
+  }
+
+  isDifferentForRoads(road1, road2) {
+    if (
+      road1.startId == road2.startId &&
+      road1.endId == road2.endId &&
+      road1.leftEdgeId == road2.leftEdgeId &&
+      road1.rightEdgeId == road2.rightEdgeId &&
+      road1.way == road2.way
+    ) {
+      if (road1.way == Constant.oneWay) {
+        if (
+          road1.singleRoadWidth == road2.singleRoadWidth &&
+          road1.singleRoadDrivewayCount == road2.singleRoadDrivewayCount
+        ) {
+          return false;
+        } else {
+          return true;
+        }
+      } else if (road1.way == Constant.twoWay) {
+        if (
+          road1.leftWidth == road2.leftWidth &&
+          road1.rightWidth == road2.rightWidth &&
+          road1.leftDrivewayCount == road2.leftDrivewayCount &&
+          road1.rightDrivewayCount == road2.rightDrivewayCount &&
+          road1.midDivide.midDivideWidth == road2.midDivide.midDivideWidth
+        ) {
+          return false;
+        } else {
+          return true;
+        }
+      } else {
+        return true;
+      }
+    } else {
+      return true;
+    }
+  }
+
+  isDifferentForCrossPoints(crossPoint1, crossPoint2) {
+    if (
+      mathUtil.equalPoint(crossPoint1, crossPoint2) &&
+      mathUtil.equalPoint(crossPoint1.extremePoint, crossPoint2.extremePoint) &&
+      crossPoint1.edgeInfo1.id == crossPoint2.edgeInfo1.id &&
+      crossPoint1.edgeInfo1.dir == crossPoint2.edgeInfo1.dir &&
+      crossPoint1.edgeInfo2.id == crossPoint2.edgeInfo2.id &&
+      crossPoint1.edgeInfo2.dir == crossPoint2.edgeInfo2.dir
+    ) {
+      return false;
+    } else {
+      return true;
+    }
+  }
 
   assignPointFromPoint(point1, point2) {
     const pointInfo = {};
     pointInfo.vectorId = point1.vectorId;
     pointInfo.position = { x: point2.x, y: point2.y };
     pointInfo.parent = JSON.parse(JSON.stringify(point2.parent));
+    pointInfo.category = point2.category;
     this.setPointInfo(pointInfo);
   }
 
   assignLineFromLine(line1, line2) {
     const lineInfo = {};
     lineInfo.vectorId = line1.vectorId;
-    lineInfo.start = line2.startId;
-    lineInfo.end = line2.endId;
+    lineInfo.start = line2.start;
+    lineInfo.end = line2.end;
     lineInfo.category = line2.category;
     this.setLineInfo(lineInfo);
   }
@@ -101,6 +182,8 @@ export default class HistoryUtil {
     circleInfo.vectorId = circle1.vectorId;
     circleInfo.center = circle2.center;
     circleInfo.radius = circle2.radius;
+    circleInfo.points = JSON.parse(JSON.stringify(circle2.points));
+    circleInfo.color = circle2.color;
     this.setCircleInfo(circleInfo);
   }
 
@@ -112,6 +195,17 @@ export default class HistoryUtil {
     this.setTextInfo(textInfo);
   }
 
+  assignMagnifierFromMagnifier(magnifier1, magnifier2) {
+    const magnifierInfo = {};
+    magnifierInfo.vectorId = magnifier1.vectorId;
+    magnifierInfo.photoUrl = magnifier2.photoUrl;
+    magnifierInfo.position = JSON.parse(JSON.stringify(magnifier2.position));
+    magnifierInfo.popPosition = JSON.parse(
+      JSON.stringify(magnifier2.popPosition)
+    );
+    this.setMagnifierInfo(magnifierInfo);
+  }
+
   assignSVGFromSVG(svg1, svg2) {
     const svgInfo = {};
     svgInfo.vectorId = svg1.vectorId;
@@ -120,11 +214,74 @@ export default class HistoryUtil {
     this.setSVGInfo(svgInfo);
   }
 
+  assignRoadPointFromRoadPoint(roadPoint1, roadPoint2) {
+    const roadPointInfo = {};
+    roadPointInfo.vectorId = roadPoint1.vectorId;
+    roadPointInfo.position = {
+      x: roadPoint2.position.x,
+      y: roadPoint2.position.y,
+    };
+    roadPointInfo.parent = JSON.parse(JSON.stringify(roadPoint2.parent));
+    this.setRoadPointInfo(roadPointInfo);
+  }
+
+  assignRoadEdgeFromRoadEdge(roadEdge1, roadEdge2) {
+    const roadEdgeInfo = {};
+    roadEdgeInfo.vectorId = roadEdge1.vectorId;
+    roadEdgeInfo.start = { x: roadEdge2.start.x, y: roadEdge2.start.y };
+    roadEdgeInfo.end = { x: roadEdge2.end.x, y: roadEdge2.end.y };
+    roadEdgeInfo.parent = roadEdge2.parent;
+    this.setRoadEdgeInfo(roadEdgeInfo);
+  }
+
+  assignRoadFromRoad(road1, road2) {
+    const roadInfo = {};
+    roadInfo.vectorId = road1.vectorId;
+    roadInfo.startId = road2.startId;
+    roadInfo.endId = road2.endId;
+    roadInfo.leftEdgeId = road2.leftEdgeId;
+    roadInfo.rightEdgeId = road2.rightEdgeId;
+    roadInfo.way = road2.way;
+    if (road2.way == Constant.oneWay) {
+      roadInfo.singleRoadWidth = road2.singleRoadWidth;
+      roadInfo.singleRoadDrivewayCount = road2.singleRoadDrivewayCount;
+      roadInfo.singleLanes = JSON.parse(JSON.stringify(road2.singleLanes));
+    } else if (road2.way == Constant.twoWay) {
+      roadInfo.leftWidth = road2.leftWidth;
+      roadInfo.rightWidth = road2.rightWidth;
+      roadInfo.leftDrivewayCount = road2.leftDrivewayCount;
+      roadInfo.rightDrivewayCount = road2.rightDrivewayCount;
+      roadInfo.midDivide = JSON.parse(JSON.stringify(road2.midDivide));
+      roadInfo.leftLanes = JSON.parse(JSON.stringify(road2.leftLanes));
+      roadInfo.rightLanes = JSON.parse(JSON.stringify(road2.rightLanes));
+    }
+    this.setRoadInfo(roadInfo);
+  }
+
+  assignCrossPointFromCrossPoint(crossPoint1, crossPoint2) {
+    const crossPointInfo = {};
+    crossPointInfo.vectorId = crossPoint1.vectorId;
+    crossPointInfo.position = { x: crossPoint2.x, y: crossPoint2.y };
+    crossPointInfo.edgeInfo1 = JSON.parse(
+      JSON.stringify(crossPoint2.edgeInfo1)
+    );
+    crossPointInfo.edgeInfo2 = JSON.parse(
+      JSON.stringify(crossPoint2.edgeInfo2)
+    );
+    crossPointInfo.extremePoint = {
+      x: crossPoint2.extremePoint.x,
+      y: crossPoint2.extremePoint.y,
+    };
+    crossPointInfo.curves = JSON.parse(JSON.stringify(crossPoint2.curves));
+    this.setCrossPointInfo(crossPointInfo);
+  }
+
   getDataForPoint(point) {
     const data = {};
     data.id = point.vectorId;
     mathUtil.clonePoint(data, point);
     data.parent = JSON.parse(JSON.stringify(point.parent));
+    data.category = point.category;
     data.type = point.geoType;
     return data;
   }
@@ -134,6 +291,7 @@ export default class HistoryUtil {
     data.id = line.vectorId;
     data.start = line.startId;
     data.end = line.endId;
+    data.category = line.category;
     data.type = line.geoType;
     return data;
   }
@@ -144,6 +302,8 @@ export default class HistoryUtil {
     data.center = {};
     mathUtil.clonePoint(data.center, circle.center);
     data.radius = circle.radius;
+    data.points = circle.points;
+    data.color = circle.color;
     data.type = circle.geoType;
     return data;
   }
@@ -158,6 +318,18 @@ export default class HistoryUtil {
     return data;
   }
 
+  getDataForMagnifier(magnifier) {
+    const data = {};
+    data.id = magnifier.vectorId;
+    data.type = magnifier.geoType;
+    data.position = {};
+    data.popPosition = {};
+    mathUtil.clonePoint(data.position, magnifier.position);
+    mathUtil.clonePoint(data.popPosition, magnifier.popPosition);
+    data.photoUrl = magnifier.photoUrl;
+    return data;
+  }
+
   getDataForSVG(svg) {
     const data = {};
     data.id = svg.vectorId;
@@ -168,17 +340,72 @@ export default class HistoryUtil {
     return data;
   }
 
-  // setRoadInfo(roadInfo) {
-  //   let road = dataService.getRoad(roadInfo.vectorId);
-  //   road.start = roadInfo.start;
-  //   road.end = roadInfo.end;
-  //   return road;
-  // }
+  getDataForRoadPoint(roadPoint) {
+    const data = {};
+    data.id = roadPoint.vectorId;
+    data.type = roadPoint.geoType;
+    data.position = {};
+    mathUtil.clonePoint(data.position, roadPoint);
+    data.parent = JSON.parse(JSON.stringify(roadPoint.parent));
+    return data;
+  }
+
+  getDataForRoadEdge(roadEdge) {
+    const data = {};
+    data.id = roadEdge.vectorId;
+    data.type = roadEdge.geoType;
+    data.parent = roadEdge.parent;
+    data.start = {};
+    data.start = JSON.parse(JSON.stringify(roadEdge.start));
+    data.end = {};
+    data.end = JSON.parse(JSON.stringify(roadEdge.end));
+    return data;
+  }
+
+  getDataForRoad(road) {
+    const data = {};
+    data.id = road.vectorId;
+    data.type = road.geoType;
+    data.startId = road.startId;
+    data.endId = road.endId;
+    data.leftEdgeId = road.leftEdgeId;
+    data.rightEdgeId = road.rightEdgeId;
+    data.way = road.way;
+    if (road.way == Constant.oneWay) {
+      data.singleRoadWidth = road.singleRoadWidth;
+      data.singleRoadDrivewayCount = road.singleRoadDrivewayCount;
+      data.singleLanes = JSON.parse(JSON.stringify(road.singleLanes));
+    } else if (road.way == Constant.twoWay) {
+      data.leftWidth = road.leftWidth;
+      data.rightWidth = road.rightWidth;
+      data.leftDrivewayCount = road.leftDrivewayCount;
+      data.rightDrivewayCount = road.rightDrivewayCount;
+      data.midDivide = JSON.parse(JSON.stringify(road.midDivide));
+      data.leftLanes = JSON.parse(JSON.stringify(road.leftLanes));
+      data.rightLanes = JSON.parse(JSON.stringify(road.rightLanes));
+    }
+    return data;
+  }
+
+  getDataForCrossPoint(crossPoint) {
+    const data = {};
+    data.id = crossPoint.vectorId;
+    data.type = crossPoint.geoType;
+    data.position = {};
+    mathUtil.clonePoint(data.position, crossPoint);
+    data.extremePoint = {};
+    mathUtil.clonePoint(data.extremePoint, crossPoint.extremePoint);
+
+    data.edgeInfo1 = JSON.parse(JSON.stringify(crossPoint.edgeInfo1));
+    data.edgeInfo2 = JSON.parse(JSON.stringify(crossPoint.edgeInfo2));
+    return data;
+  }
 
   setPointInfo(pointInfo) {
     let point = dataService.getPoint(pointInfo.vectorId);
     mathUtil.clonePoint(point, pointInfo.position);
     point.parent = JSON.parse(JSON.stringify(pointInfo.parent));
+    point.category = pointInfo.category;
     return point;
   }
 
@@ -194,6 +421,8 @@ export default class HistoryUtil {
     let circle = dataService.getCircle(circleInfo.vectorId);
     circle.center = circleInfo.center;
     circle.radius = circleInfo.radius;
+    circle.color = circleInfo.color;
+    circle.points = circleInfo.points;
     return circle;
   }
 
@@ -204,12 +433,70 @@ export default class HistoryUtil {
     text.value = textInfo.value;
   }
 
+  setMagnifierInfo(magnifierInfo) {
+    let magnifier = dataService.getMagnifier(magnifierInfo.vectorId);
+    magnifier.vectorId = magnifierInfo.vectorId;
+    magnifier.position = JSON.parse(JSON.stringify(magnifierInfo.position));
+    magnifier.popPosition = JSON.parse(
+      JSON.stringify(magnifierInfo.popPosition)
+    );
+    magnifier.photoUrl = magnifierInfo.photoUrl;
+  }
+
   setSVGInfo(svgInfo) {
     let svg = dataService.getSVG(svgInfo.vectorId);
     svg.vectorId = svgInfo.vectorId;
     svg.center = JSON.parse(JSON.stringify(svgInfo.center));
     svg.name = svgInfo.name;
   }
+
+  setRoadPointInfo(roadPointInfo) {
+    let roadPoint = dataService.getRoadPoint(roadPointInfo.vectorId);
+    roadPoint.vectorId = roadPointInfo.vectorId;
+    mathUtil.clonePoint(roadPoint, roadPointInfo.position);
+    roadPoint.parent = JSON.parse(JSON.stringify(roadPointInfo.parent));
+  }
+
+  setRoadEdgeInfo(roadEdgeInfo) {
+    let roadEdge = dataService.getRoadEdge(roadEdgeInfo.vectorId);
+    roadEdge.vectorId = roadEdgeInfo.vectorId;
+    mathUtil.clonePoint(roadEdge.start, roadEdgeInfo.start);
+    mathUtil.clonePoint(roadEdge.end, roadEdgeInfo.end);
+    roadEdge.name = roadEdgeInfo.name;
+  }
+
+  setRoadInfo(roadInfo) {
+    let road = dataService.getRoad(roadInfo.vectorId);
+    road.vectorId = roadInfo.vectorId;
+    road.startId = roadInfo.startId;
+    road.endId = roadInfo.endId;
+    road.leftEdgeId = roadInfo.leftEdgeId;
+    road.rightEdgeId = roadInfo.rightEdgeId;
+    road.way = roadInfo.way;
+    if (road.way == Constant.oneWay) {
+      road.singleRoadWidth = roadInfo.singleRoadWidth;
+      road.singleRoadDrivewayCount = roadInfo.singleRoadDrivewayCount;
+      road.singleLanes = JSON.parse(JSON.stringify(roadInfo.singleLanes));
+    } else if (road.way == Constant.twoWay) {
+      road.leftWidth = roadInfo.leftWidth;
+      road.rightWidth = roadInfo.rightWidth;
+      road.leftDrivewayCount = roadInfo.leftDrivewayCount;
+      road.rightDrivewayCount = roadInfo.rightDrivewayCount;
+      road.midDivide = JSON.parse(JSON.stringify(roadInfo.midDivide));
+      road.leftLanes = JSON.parse(JSON.stringify(roadInfo.leftLanes));
+      road.rightLanes = JSON.parse(JSON.stringify(roadInfo.rightLanes));
+    }
+  }
+
+  setCrossPointInfo(crossPointInfo) {
+    let crossPoint = dataService.getCrossPoint2(crossPointInfo.vectorId);
+    crossPoint.vectorId = crossPointInfo.vectorId;
+    mathUtil.clonePoint(crossPoint, crossPointInfo.position);
+    mathUtil.clonePoint(crossPoint.extremePoint, crossPointInfo.extremePoint);
+    crossPoint.edgeInfo1 = JSON.parse(JSON.stringify(crossPointInfo.edgeInfo1));
+    crossPoint.edgeInfo2 = JSON.parse(JSON.stringify(crossPointInfo.edgeInfo2));
+    crossPoint.curves = JSON.parse(JSON.stringify(crossPointInfo.curves));
+  }
 }
 
 const historyUtil = new HistoryUtil();

+ 154 - 59
src/graphic/Layer.js

@@ -17,6 +17,7 @@ import { addMagnifier } from "./Controls/AddMagnifier";
 import { addSVG } from "./Controls/AddSVG";
 import { moveRoad } from "./Controls/MoveRoad";
 import { moveLine } from "./Controls/MoveLine";
+import { moveCircle } from "./Controls/MoveCircle";
 import { coordinate } from "./Coordinate";
 import Render from "./Renderer/Render";
 import { draw } from "./Renderer/Draw";
@@ -37,11 +38,11 @@ import VectorCategory from "./enum/VectorCategory";
 const minDragDis = 10;
 
 export default class Layer {
-  constructor(canvas, newsletter) {
+  constructor(canvas, newsletter, graphicState) {
     this.canvas = canvas;
     this.load = new Load(this);
 
-    this.uiControl = new UIControl(this, newsletter);
+    this.uiControl = new UIControl(this, newsletter, graphicState);
     this.renderer = new Render(this);
     this.history = new History(this);
     this.mousePosition = null;
@@ -58,7 +59,6 @@ export default class Layer {
       coordinate.init(this.canvas);
       draw.initContext(this.canvas);
       dataService.initVectorData();
-      this.history.init();
       this.bindEvents();
     }
 
@@ -72,6 +72,11 @@ export default class Layer {
     this.canvas.addEventListener("mousedown", this.onMouseDown.bind(this));
     this.canvas.addEventListener("mousemove", this.onMouseMove.bind(this));
     this.canvas.addEventListener("mouseup", this.onMouseUp.bind(this));
+
+    this.canvas.addEventListener("touchstart", this.onMouseDown.bind(this));
+    this.canvas.addEventListener("touchmove", this.onMouseMove.bind(this));
+    this.canvas.addEventListener("touchend", this.onMouseUp.bind(this));
+
     this.canvas.addEventListener("mousewheel", this.onWheel.bind(this));
     this.canvas.addEventListener("DOMMouseScroll", this.onWheel.bind(this));
     this.canvas.addEventListener("resize", this.reSize.bind(this));
@@ -85,11 +90,19 @@ export default class Layer {
   };
 
   onMouseDown(e) {
-    this.startX = e.offsetX || e.layerX;
-    this.startY = e.offsetY || e.layerY;
+    if (e instanceof TouchEvent) {
+      this.startX = e.touches[0].pageX;
+      this.startY = e.touches[0].pageY;
+
+      this.lastX = e.touches[0].pageX;
+      this.lastY = e.touches[0].pageY;
+    } else {
+      this.startX = e.offsetX || e.layerX;
+      this.startY = e.offsetY || e.layerY;
 
-    this.lastX = e.offsetX || e.layerX;
-    this.lastY = e.offsetY || e.layerY;
+      this.lastX = e.offsetX || e.layerX;
+      this.lastY = e.offsetY || e.layerY;
+    }
 
     let position = coordinate.getXYFromScreen({
       x: this.startX,
@@ -107,7 +120,6 @@ export default class Layer {
     //用于支持平板电脑
     listenLayer.start(position);
     this.setEventName("mouseDown");
-    const selectItem = stateService.getSelectItem();
     const eventName = stateService.getEventName();
     switch (eventName) {
       case LayerEvents.AddRoad:
@@ -120,7 +132,7 @@ export default class Layer {
         break;
       case LayerEvents.AddLine:
         stateService.setEventName(LayerEvents.AddingLine);
-        addLine.setNewLinePoint("start", position);
+        addLine.setNewLinePoint(position);
         break;
       case LayerEvents.AddCircle:
         stateService.setEventName(LayerEvents.AddingCircle);
@@ -134,6 +146,7 @@ export default class Layer {
           VectorType.Text,
           SelectState.Select
         );
+        addText.clear();
         break;
       case LayerEvents.AddMagnifier:
         stateService.setEventName(LayerEvents.MoveMagnifier);
@@ -143,6 +156,7 @@ export default class Layer {
           VectorType.Magnifier,
           SelectState.Select
         );
+        addMagnifier.clear();
         break;
       case LayerEvents.AddSVG:
         stateService.setEventName(LayerEvents.MoveSVG);
@@ -152,8 +166,10 @@ export default class Layer {
           VectorType.SVG,
           SelectState.Select
         );
+        addSVG.clear();
         break;
     }
+    const selectItem = stateService.getSelectItem();
     stateService.setDraggingItem(selectItem);
     // 清除上一个状态
     // 设置当前事件名称
@@ -162,8 +178,42 @@ export default class Layer {
   }
 
   onMouseMove(e) {
-    const X = e.offsetX || e.layerX;
-    const Y = e.offsetY || e.layerY;
+    let X = e.offsetX || e.layerX;
+    let Y = e.offsetY || e.layerY;
+
+    if (e instanceof TouchEvent) {
+      X = e.touches[0].pageX;
+      Y = e.touches[0].pageY;
+      //切换到缩放
+      if (e.touches.length > 1) {
+        //记录开始的两个触摸点的坐标
+        if (!this.StorePage1 || !this.StorePage2) {
+          this.onMouseUp(e);
+          this.StorePage1 = { x: ev.touches[0].pageX, y: ev.touches[0].pageY };
+          this.StorePage2 = { x: ev.touches[1].pageX, y: ev.touches[1].pageY };
+          return;
+        }
+        const point1 = {
+          x: ev.touches[0].pageX,
+          y: ev.touches[0].pageY,
+        };
+        const point2 = {
+          x: ev.touches[1].pageX,
+          y: ev.touches[1].pageY,
+        };
+
+        // let zoom =
+        //   (mathUtil.getDistance(point1, point2) /
+        //     mathUtil.getDistance(this.StorePage1, this.StorePage2)) *
+        //   coordinate.defaultZoom;
+        let zoom =
+          coordinate.defaultZoom +
+          mathUtil.getDistance(this.StorePage1, this.StorePage2) -
+          mathUtil.getDistance(point1, point2);
+        this.zoomVector(zoom);
+        return;
+      }
+    }
 
     let dx = X - this.lastX;
     let dy = Y - this.lastY;
@@ -189,6 +239,8 @@ export default class Layer {
     // 是否需要重绘
     let needAutoRedraw = false;
     let point = null;
+    this.lastX = X;
+    this.lastY = Y;
     const draggingItem = stateService.getDraggingItem();
 
     switch (eventName) {
@@ -199,7 +251,6 @@ export default class Layer {
         if (seleteItem != null) {
           console.log("选中:" + seleteItem.vectorId);
         } else {
-          console.log("什么也没选中");
         }
         break;
       case LayerEvents.PanBackGround:
@@ -213,8 +264,6 @@ export default class Layer {
           (dx * coordinate.defaultZoom) / coordinate.zoom,
           (dy * coordinate.defaultZoom) / coordinate.zoom
         );
-        this.lastX = X;
-        this.lastY = Y;
         needAutoRedraw = true;
         break;
       case LayerEvents.AddRoad:
@@ -293,7 +342,13 @@ export default class Layer {
         break;
       case LayerEvents.AddingLine:
         needAutoRedraw = true;
-        listenLayer.start(position);
+        let exceptLineId = null;
+        if (addLine.newLine != null) {
+          exceptLineId = addLine.newLine.vectorId;
+        }
+        listenLayer.start(position, {
+          exceptLineId: exceptLineId,
+        });
         if (listenLayer.modifyPoint) {
           position = {
             x: listenLayer.modifyPoint.x,
@@ -302,14 +357,19 @@ export default class Layer {
         }
         elementService.execute(addLine.startInfo.position, position);
         elementService.setPoint(position);
-        elementService.setNewLine(addLine.startInfo.position, position);
-        elementService.setNewLineCategory(VectorCategory.Line.NormalLine);
-        elementService.showNewLine();
-        addLine.setNewLinePoint("end", position);
+        if (addLine.newLine == null) {
+          addLine.buildLine(position);
+        } else {
+          addLine.updateLine(position);
+        }
         break;
       case LayerEvents.AddingCircle:
         needAutoRedraw = true;
-        listenLayer.start(position);
+        let exceptCircleId = null;
+        if (addCircle.newCircle != null) {
+          exceptCircleId = addCircle.newCircle.vectorId;
+        }
+        listenLayer.start(position, { exceptCircleId: exceptCircleId });
         if (listenLayer.modifyPoint) {
           position = {
             x: listenLayer.modifyPoint.x,
@@ -318,7 +378,11 @@ export default class Layer {
         }
         elementService.execute(addCircle.center, position);
         elementService.setPoint(position);
-        addCircle.setRadius(mathUtil.getDistance(addCircle.center, position));
+        if (addCircle.newCircle == null) {
+          addCircle.buildCircle(position);
+        } else {
+          addCircle.updateCircle(position);
+        }
         break;
       case LayerEvents.MoveRoad:
         needAutoRedraw = true;
@@ -337,8 +401,6 @@ export default class Layer {
             (dy * coordinate.defaultZoom) / coordinate.zoom
           );
         }
-        this.lastX = X;
-        this.lastY = Y;
         break;
       case LayerEvents.MoveRoadPoint:
         point = dataService.getRoadPoint(draggingItem.vectorId);
@@ -441,8 +503,6 @@ export default class Layer {
           (dx * coordinate.defaultZoom) / coordinate.zoom,
           (dy * coordinate.defaultZoom) / coordinate.zoom
         );
-        this.lastX = X;
-        this.lastY = Y;
         break;
       case LayerEvents.MoveCurveRoadPoint:
         if (!draggingItem || !draggingItem.vectorId) {
@@ -462,11 +522,11 @@ export default class Layer {
         moveRoad.moveCurveRoadPoint(draggingItem.vectorId, position);
         needAutoRedraw = true;
         break;
-      case LayerEvents.MoveControlPoint:
+      case LayerEvents.MoveCrossPoint:
         if (!draggingItem || !draggingItem.vectorId) {
           return;
         }
-        moveRoad.moveControlPoint(draggingItem.vectorId, position);
+        moveRoad.moveCrossPoint(draggingItem.vectorId, position);
         needAutoRedraw = true;
         break;
       case LayerEvents.MoveEdge:
@@ -492,8 +552,6 @@ export default class Layer {
           );
           needAutoRedraw = true;
         }
-        this.lastX = X;
-        this.lastY = Y;
         break;
       case LayerEvents.MovePoint:
         if (draggingItem != null) {
@@ -512,6 +570,31 @@ export default class Layer {
           needAutoRedraw = true;
         }
         break;
+      case LayerEvents.MoveCircle:
+        if (draggingItem != null) {
+          if (draggingItem.state == -1) {
+            moveCircle.moveFull(
+              draggingItem.vectorId,
+              (dx * coordinate.defaultZoom) / coordinate.zoom,
+              (dy * coordinate.defaultZoom) / coordinate.zoom
+            );
+          } else if (
+            draggingItem.state == 0 ||
+            draggingItem.state == 1 ||
+            draggingItem.state == 2 ||
+            draggingItem.state == 3
+          ) {
+            moveCircle.movePoint(
+              position,
+              draggingItem.vectorId,
+              draggingItem.state
+            );
+          } else {
+            debugger;
+          }
+          needAutoRedraw = true;
+        }
+        break;
       case LayerEvents.MoveText:
         needAutoRedraw = true;
         if (draggingItem != null) {
@@ -542,8 +625,18 @@ export default class Layer {
     if (e.button == 2) {
       return;
     }
-    const X = e.offsetX || e.layerX;
-    const Y = e.offsetY || e.layerY;
+    let X = e.offsetX || e.layerX;
+    let Y = e.offsetY || e.layerY;
+
+    if (e instanceof TouchEvent) {
+      X = this.lastX;
+      Y = this.lastY;
+      if (e.touches.length > 1) {
+        return;
+      }
+      this.StorePage1 == null;
+      this.StorePage2 == null;
+    }
 
     let eventName = stateService.getEventName();
     const draggingItem = stateService.getDraggingItem();
@@ -645,13 +738,15 @@ export default class Layer {
         break;
       case LayerEvents.AddingLine:
         needAutoRedraw = true;
-        addLine.buildLine();
+        addLine.finish(position);
+        addLine.clearVectorData();
         this.history.save();
         elementService.hideAll();
         break;
       case LayerEvents.AddingCircle:
         needAutoRedraw = true;
-        addCircle.buildCircle();
+        addCircle.finish(position);
+        addCircle.clear();
         this.history.save();
         elementService.hideAll();
         break;
@@ -690,7 +785,7 @@ export default class Layer {
         needAutoRedraw = true;
         this.history.save();
         break;
-      case LayerEvents.MoveControlPoint:
+      case LayerEvents.MoveCrossPoint:
         needAutoRedraw = true;
         this.history.save();
         break;
@@ -710,11 +805,17 @@ export default class Layer {
         needAutoRedraw = true;
         this.history.save();
         break;
+      case LayerEvents.MoveCircle:
+        needAutoRedraw = true;
+        this.history.save();
+        break;
     }
 
     this.setEventName("mouseUp");
     stateService.clearDraggingItem();
-    this.renderer.autoRedraw();
+    if (needAutoRedraw) {
+      this.renderer.autoRedraw();
+    }
   }
 
   onWheel(e) {
@@ -726,18 +827,22 @@ export default class Layer {
         ? (e.wheelDelta / 120) * 20
         : (-(e.detail || 0) / 3) * 20;
       const zoom = coordinate.zoom + delta;
-      if (zoom < 14) {
-        return;
-      }
+      this.zoomVector(zoom);
+    }
+  }
 
-      coordinate.updateZoom(zoom);
-      dataService.setGridForZoom(
-        coordinate.width,
-        coordinate.height,
-        coordinate.zoom / coordinate.defaultZoom
-      );
-      this.renderer.autoRedraw();
+  zoomVector(zoom) {
+    if (zoom < 14) {
+      return;
     }
+
+    coordinate.updateZoom(zoom);
+    dataService.setGridForZoom(
+      coordinate.width,
+      coordinate.height,
+      (coordinate.res * coordinate.zoom) / coordinate.defaultZoom
+    );
+    this.renderer.autoRedraw();
   }
 
   //测试用
@@ -751,7 +856,7 @@ export default class Layer {
         const road = dataService.getRoad(focusItem.vectorId);
         roadService.subtraRoadFromIntersect(road.startId, focusItem.vectorId);
         roadService.subtraRoadFromIntersect(road.endId, focusItem.vectorId);
-        //dataService.deleteControlPoint()
+        //dataService.deleteCrossPoint()
         dataService.deleteRoad(focusItem.vectorId);
         this.renderer.autoRedraw();
         this.history.save();
@@ -929,8 +1034,8 @@ export default class Layer {
           stateService.setEventName(LayerEvents.MoveCurveRoad);
         } else if (selectItem.type == VectorType.CurveRoadPoint) {
           stateService.setEventName(LayerEvents.MoveCurveRoadPoint);
-        } else if (selectItem.type == VectorType.ControlPoint) {
-          stateService.setEventName(LayerEvents.MoveControlPoint);
+        } else if (selectItem.type == VectorType.CrossPoint) {
+          stateService.setEventName(LayerEvents.MoveCrossPoint);
         } else if (selectItem.type == VectorType.RoadEdge) {
           stateService.setEventName(LayerEvents.MoveEdge);
         } else if (selectItem.type == VectorType.CurveRoadEdge) {
@@ -986,18 +1091,8 @@ export default class Layer {
     } else if (eventName == LayerEvents.AddingLine) {
       stateService.setEventName(LayerEvents.AddLine);
     }
-
+    addLine.clear(); //之前会保留category
     this.uiControl.clearUI();
     elementService.hideAll();
   }
-
-  revokeHistory() {
-    this.history.goPreState();
-    this.renderer.autoRedraw();
-  }
-
-  recoveryHistory() {
-    this.history.goNextState();
-    this.renderer.autoRedraw();
-  }
 }

+ 134 - 64
src/graphic/ListenLayer.js

@@ -26,7 +26,7 @@ export default class ListenLayer {
           exceptRoadIds,
           exceptCurveRoadPointId,
           exceptCurveRoadId,
-          exceptCrossControlPointId,
+          exceptCrossCrossPointId,
           exceptTextId,
   }  
    * @returns 
@@ -62,16 +62,16 @@ export default class ListenLayer {
       position,
       exceptVectorIds.exceptLineId
     );
-    // selectInfo.curveLineInfo = this.isSelectCurveLine(
-    //   position,
-    //   exceptVectorIds.exceptCurveLineId
-    // );
     selectInfo.curveLineInfo = {};
     selectInfo.curvePointInfo = {};
+    selectInfo.circleInfo = this.isSelectCircle(
+      position,
+      exceptVectorIds.exceptCircleId
+    );
     //交叉口拐弯处的控制点
-    selectInfo.controlPointInfo = this.isSelectCrossControlPoint(
+    selectInfo.crossPointInfo = this.isSelectCrossCrossPoint(
       position,
-      exceptVectorIds.exceptCrossControlPointId
+      exceptVectorIds.exceptCrossCrossPointId
     );
     selectInfo.textInfo = this.isSelectText(
       position,
@@ -79,7 +79,7 @@ export default class ListenLayer {
     );
     selectInfo.magnifierInfo = this.isSelectMagnifier(
       position,
-      exceptMagnifierId.exceptMagnifierId
+      exceptVectorIds.exceptMagnifierId
     );
     this.setModifyPoint(position, selectInfo);
     flag = this.updateSelectItem();
@@ -193,6 +193,51 @@ export default class ListenLayer {
     return lineInfo;
   }
 
+  isSelectCircle(position, exceptCircleId) {
+    let circleInfo = {
+      circleId: null,
+      type: null,
+      distance: null,
+    };
+    const circles = dataService.getCircles();
+    let distance;
+    for (const circleId in circles) {
+      if (circleId == exceptCircleId) {
+        continue;
+      }
+      const circle = dataService.getCircle(circleId);
+      for (let i = 0; i < circle.points.length; ++i) {
+        distance = mathUtil.getDistance(position, circle.points[i]);
+        if (distance < Constant.minAdsorbPix) {
+          circleInfo = {
+            circleId: circleId,
+            type: VectorType.Circle,
+            distance: distance,
+            x: circle.points[i].x,
+            y: circle.points[i].y,
+            index: i,
+          };
+          return circleInfo;
+        }
+      }
+      distance = mathUtil.getDistance(position, circle.center);
+      if (distance < circle.radius) {
+        if (circleInfo.circleId == null || distance < circleInfo.distance) {
+          circleInfo = {
+            circleId: circleId,
+            type: VectorType.Circle,
+            distance: distance,
+            x: circle.center.x,
+            y: circle.center.y,
+            index: -1,
+          };
+        }
+      }
+    }
+
+    return circleInfo;
+  }
+
   // isSelectCurveLine(position, exceptCurveLineId) {
   //   let curveLineInfo = {
   //     curveLineId: null,
@@ -486,44 +531,60 @@ export default class ListenLayer {
       const rightEdge = dataService.getRoadEdge(road.rightEdgeId);
       const roadLine = roadService.getMidLine(road);
       let join = mathUtil.getJoinLinePoint(position, roadLine);
-      const distance = mathUtil.getDistance(position, join);
-      if (!mathUtil.isContainForSegment(join, startPoint, endPoint)) {
-        continue;
-      }
-      if (distance < Constant.minAdsorbPix) {
-        roadInfo = {
-          roadId: roadId,
-          type: VectorType.Road,
-          distance: distance,
-        };
+      let distance = mathUtil.getDistance(position, join);
+      if (
+        mathUtil.isContainForSegment(join, startPoint, endPoint) &&
+        distance < Constant.minAdsorbPix
+      ) {
+        if (!roadInfo.roadId || distance < roadInfo.distance) {
+          roadInfo = {
+            roadId: roadId,
+            type: VectorType.Road,
+            distance: distance,
+          };
+        }
       }
+
       //检查edge
-      else {
-        let leftLine = mathUtil.createLine1(leftEdge.start, leftEdge.end);
-        join = mathUtil.getJoinLinePoint(position, leftLine);
-        if (distance < Constant.minAdsorbPix) {
+      let leftLine = mathUtil.createLine1(leftEdge.start, leftEdge.end);
+      join = mathUtil.getJoinLinePoint(position, leftLine);
+      distance = mathUtil.getDistance(position, join);
+      if (
+        mathUtil.isContainForSegment(join, leftEdge.start, leftEdge.end) &&
+        distance < Constant.minAdsorbPix
+      ) {
+        if (!edgeInfo.edgeId || distance < edgeInfo.distance) {
           edgeInfo = {
             edgeId: road.leftEdgeId,
             type: VectorType.RoadEdge,
             distance: distance,
             dir: "left",
           };
-        } else {
-          let rightLine = mathUtil.createLine1(rightEdge.start, rightEdge.end);
-          join = mathUtil.getJoinLinePoint(position, rightLine);
-          if (distance < Constant.minAdsorbPix) {
-            edgeInfo = {
-              edgeId: road.rightEdgeId,
-              type: VectorType.RoadEdge,
-              distance: distance,
-              dir: "right",
-            };
-          }
+        }
+      }
+
+      let rightLine = mathUtil.createLine1(rightEdge.start, rightEdge.end);
+      join = mathUtil.getJoinLinePoint(position, rightLine);
+      distance = mathUtil.getDistance(position, join);
+      if (
+        mathUtil.isContainForSegment(join, rightEdge.start, rightEdge.end) &&
+        distance < Constant.minAdsorbPix
+      ) {
+        if (!edgeInfo.edgeId || distance < edgeInfo.distance) {
+          edgeInfo = {
+            edgeId: road.rightEdgeId,
+            type: VectorType.RoadEdge,
+            distance: distance,
+            dir: "right",
+          };
         }
       }
     }
 
-    if (roadInfo.roadId) {
+    if (
+      roadInfo.roadId &&
+      (!edgeInfo.edgeId || roadInfo.distance < edgeInfo.distance)
+    ) {
       const linkedRoad = dataService.getRoad(roadInfo.roadId);
       const linkedRoadLine = roadService.getMidLine(linkedRoad);
       const linkedPosition = mathUtil.getJoinLinePoint(
@@ -649,34 +710,31 @@ export default class ListenLayer {
     }
   }
 
-  isSelectCrossControlPoint(position, exceptCrossControlPointId) {
-    let crossControlPointInfo = {
-      crossControlPointId: null,
+  isSelectCrossCrossPoint(position, exceptCrossCrossPointId) {
+    let crossCrossPointInfo = {
+      crossCrossPointId: null,
       type: null,
       distance: null,
     };
-    const controlPoints = dataService.getControlPoints();
-    for (const controlPointId in controlPoints) {
-      if (controlPointId == exceptCrossControlPointId) {
+    const crossPoints = dataService.getCrossPoints();
+    for (const crossPointId in crossPoints) {
+      if (crossPointId == exceptCrossCrossPointId) {
         continue;
       }
-      const controlPoint = dataService.getControlPoint2(controlPointId);
-      const distance = mathUtil.getDistance(
-        position,
-        controlPoint.extremePoint
-      );
+      const crossPoint = dataService.getCrossPoint2(crossPointId);
+      const distance = mathUtil.getDistance(position, crossPoint.extremePoint);
       if (distance < Constant.minAdsorbPix) {
-        crossControlPointInfo = {
-          crossControlPointId: controlPointId,
-          type: VectorType.ControlPoint,
+        crossCrossPointInfo = {
+          crossCrossPointId: crossPointId,
+          type: VectorType.CrossPoint,
           distance: distance,
-          x: controlPoint.extremePoint.x,
-          y: controlPoint.extremePoint.y,
+          x: crossPoint.extremePoint.x,
+          y: crossPoint.extremePoint.y,
         };
       }
     }
 
-    return crossControlPointInfo;
+    return crossCrossPointInfo;
   }
 
   isSelectText(position, exceptTextId) {
@@ -739,7 +797,7 @@ export default class ListenLayer {
         curveRoadPointInfo,
         roadEdgeInfo,
         curveRoadEdgeInfo,
-        controlPointInfo,
+        crossPointInfo,
         pointInfo,
         roadPointInfo
       }  
@@ -825,22 +883,22 @@ export default class ListenLayer {
         this.modifyPoint.x = info.curveRoadEdgeInfo.x;
         this.modifyPoint.y = info.curveRoadEdgeInfo.y;
       }
-    } else if (info && info.controlPointInfo.crossControlPointId) {
+    } else if (info && info.crossPointInfo.crossCrossPointId) {
       this.modifyPoint = {};
-      this.modifyPoint.linkedCrossControlPointId =
-        info.controlPointInfo.crossControlPointId;
-      this.modifyPoint.x = info.controlPointInfo.x;
-      this.modifyPoint.y = info.controlPointInfo.y;
+      this.modifyPoint.linkedCrossCrossPointId =
+        info.crossPointInfo.crossCrossPointId;
+      this.modifyPoint.x = info.crossPointInfo.x;
+      this.modifyPoint.y = info.crossPointInfo.y;
     } else if (info && info.textInfo.textId) {
       this.modifyPoint = {};
       this.modifyPoint.textId = info.textInfo.textId;
-      this.modifyPoint.x = info.textInfo.center.x;
-      this.modifyPoint.y = info.textInfo.center.y;
+      this.modifyPoint.x = info.textInfo.x;
+      this.modifyPoint.y = info.textInfo.y;
     } else if (info && info.magnifierInfo.magnifierId) {
       this.modifyPoint = {};
       this.modifyPoint.magnifierId = info.magnifierInfo.magnifierId;
-      this.modifyPoint.x = info.magnifierInfo.position.x;
-      this.modifyPoint.y = info.magnifierInfo.position.y;
+      this.modifyPoint.x = info.magnifierInfo.x;
+      this.modifyPoint.y = info.magnifierInfo.y;
     } else if (
       info &&
       (info.roadEdgeInfo.edgeId || info.curveRoadEdgeInfo.curveEdgeId)
@@ -892,6 +950,12 @@ export default class ListenLayer {
         this.modifyPoint.x = info.curveLineInfo.x;
         this.modifyPoint.y = info.curveLineInfo.y;
       }
+    } else if (info && info.circleInfo.circleId) {
+      this.modifyPoint = {};
+      this.modifyPoint.linkedCircleId = info.circleInfo.circleId;
+      this.modifyPoint.index = info.circleInfo.index;
+      this.modifyPoint.x = info.circleInfo.x;
+      this.modifyPoint.y = info.circleInfo.y;
     } else if (info && info.roadPointInfo.linkedRoadPointIdX) {
       this.modifyPoint = {};
       this.modifyPoint.linkedRoadPointIdX =
@@ -964,10 +1028,10 @@ export default class ListenLayer {
         VectorType.CurveRoad,
         SelectState.Select
       );
-    } else if (this.modifyPoint.linkedCrossControlPointId) {
+    } else if (this.modifyPoint.linkedCrossCrossPointId) {
       stateService.setSelectItem(
-        this.modifyPoint.linkedCrossControlPointId,
-        VectorType.ControlPoint,
+        this.modifyPoint.linkedCrossCrossPointId,
+        VectorType.CrossPoint,
         SelectState.Select
       );
     } else if (this.modifyPoint.textId) {
@@ -1018,6 +1082,12 @@ export default class ListenLayer {
         VectorType.CurveLine,
         SelectState.Select
       );
+    } else if (this.modifyPoint.linkedCircleId) {
+      stateService.setSelectItem(
+        this.modifyPoint.linkedCircleId,
+        VectorType.Circle,
+        this.modifyPoint.index
+      );
     }
 
     let newSelectItem = stateService.getSelectItem();

+ 149 - 2
src/graphic/Load.js

@@ -1,4 +1,12 @@
 import { dataService } from "./Service/DataService.js";
+import { lineService } from "./Service/LineService.js";
+import { pointService } from "./Service/PointService.js";
+import { imageService } from "./Service/ImageService.js";
+import VectorCategory from "./enum/VectorCategory.js";
+import { coordinate } from "./Coordinate.js";
+import { circleService } from "./Service/CircleService.js";
+import { magnifierService } from "./Service/MagnifierService.js";
+import { textService } from "./Service/TextService.js";
 
 export default class Load {
   constructor(layer) {
@@ -8,7 +16,146 @@ export default class Load {
     this.newVectorId = null;
   }
 
-  load(data) {}
+  async load(dataLocal, data3d) {
+    if (dataLocal) {
+      if (dataLocal.backgroundImg) {
+        let bgImg = imageService.create(
+          dataLocal.backgroundImg.src,
+          dataLocal.backgroundImg.vectorId
+        );
+        bgImg.setCenter(dataLocal.backgroundImg.center);
+        bgImg.setDisplay(dataLocal.backgroundImg.display);
+        bgImg.setAngle(dataLocal.backgroundImg.angle);
+        try {
+          if (dataLocal.backgroundImg.src) {
+            await bgImg.setImageData();
+          }
+        } catch (e) {}
+      }
+      if (dataLocal.circles) {
+        for (let key in dataLocal.circles) {
+          let circle = circleService.create(
+            dataLocal.circles[key].center,
+            dataLocal.circles[key].radius,
+            key
+          );
+          circle.setPoints(dataLocal.circles[key].points);
+          circle.setColor(dataLocal.circles[key].color);
+          circle.setDisplay(dataLocal.circles[key].display);
+        }
+      }
+      if (dataLocal.magnifiers) {
+        for (let key in dataLocal.magnifiers) {
+          let magnifier = magnifierService.create(
+            dataLocal.magnifiers[key].position,
+            key
+          );
+          magnifier.setSrc(dataLocal.magnifiers[key].photoUrl);
+          magnifier.setDisplay(dataLocal.magnifiers[key].display);
+          try {
+            if (dataLocal.magnifiers[key].photoUrl) {
+              await magnifier.setImageData();
+            }
+          } catch (e) {}
+        }
+      }
+      if (dataLocal.texts) {
+        for (let key in dataLocal.texts) {
+          let text = textService.create(dataLocal.texts[key].center, key);
+          text.setValue(dataLocal.texts[key].value);
+          text.setFontSize(dataLocal.texts[key].fontSize);
+          text.setColor(dataLocal.texts[key].color);
+          text.setDisplay(dataLocal.texts[key].display);
+        }
+      }
+      if (dataLocal.points) {
+        for (let key in dataLocal.points) {
+          let point = pointService.create(
+            { x: dataLocal.points[key].x, y: dataLocal.points[key].y },
+            dataLocal.points[key].category,
+            key
+          );
+          point.setParent(
+            JSON.parse(JSON.stringify(dataLocal.points[key].parent))
+          );
+          point.setDisplay(dataLocal.points[key].display);
+        }
+      }
+      if (dataLocal.lines) {
+        for (let key in dataLocal.lines) {
+          let line = lineService.createByPointId(
+            dataLocal.lines[key].startId,
+            dataLocal.lines[key].endId,
+            dataLocal.lines[key].category,
+            key
+          );
+          if (dataLocal.lines[key].arrowColor) {
+            line.setArrowColor(dataLocal.lines[key].arrowColor);
+          }
+          line.setDisplay(dataLocal.lines[key].display);
+        }
+      }
+      if (dataLocal.hasOwnProperty("currentId")) {
+        dataService.setCurrentId(dataLocal.currentId);
+      }
+    }
+    if (data3d) {
+      if (data3d.backImage) {
+        let bgImg = imageService.create(data3d.backImage, data3d.vectorId);
+        bgImg.setCenter(coordinate.center);
+        try {
+          if (bgImg.src) {
+            await bgImg.setImageData();
+          }
+        } catch (e) {}
+      }
+      if (data3d.baseLines) {
+        for (let i = 0; i < data3d.baseLines.length; ++i) {
+          //理论上基准线只能有一条
+          lineService.create(
+            data3d.baseLines[i][0],
+            data3d.baseLines[i][1],
+            VectorCategory.Line.BaseLine
+          );
+        }
+      }
+      if (data3d.measures) {
+        for (let i = 0; i < data3d.measures.length; ++i) {
+          //理论上基准线只能有一条
+          lineService.create(
+            data3d.measures[i][0],
+            data3d.measures[i][1],
+            VectorCategory.Line.MeasureLine
+          );
+        }
+      }
+      if (data3d.basePoints) {
+        for (let i = 0; i < data3d.basePoints.length; ++i) {
+          pointService.create(
+            data3d.basePoints[i],
+            VectorCategory.Point.BasePoint
+          );
+        }
+      }
+      if (data3d.fixPoints) {
+        for (let i = 0; i < data3d.fixPoints.length; ++i) {
+          pointService.create(
+            data3d.fixPoints[i],
+            VectorCategory.Point.FixPoint
+          );
+        }
+      }
+    }
+    this.layer.history.init();
+    this.layer.renderer.autoRedraw();
+  }
+
+  save() {
+    return dataService.vectorData;
+  }
 
-  upload() {}
+  // 退出页面清除缓存及其他操作
+  clear() {
+    console.warn("clear");
+  }
 }

+ 224 - 42
src/graphic/Renderer/Draw.js

@@ -6,12 +6,14 @@ import VectorType from "../enum/VectorType.js";
 import { mathUtil } from "../Util/MathUtil.js";
 import ElementEvents from "../enum/ElementEvents.js";
 import { elementService } from "../Service/ElementService.js";
+import UIEvents from "@/graphic/enum/UIEvents.js";
 
+const imgCache = {};
 const help = {
   getVectorStyle(vector, geoType = vector.geoType) {
     const geoId = vector?.vectorId;
     if (!geoId) {
-      return Style[geoType];
+      return [Style[geoType], undefined];
     }
 
     const itemsEntry = [
@@ -19,22 +21,27 @@ const help = {
       [stateService.getDraggingItem(), "Dragging"],
       [stateService.getFocusItem(), "Focus"],
     ];
-
-    return itemsEntry.reduce((prev, [item, attr]) => {
-      if (
-        item &&
-        item.type === VectorType[geoType] &&
-        geoId === item.vectorId
-      ) {
-        if (Style[attr] && Style[attr][geoType]) {
-          return Style[attr][geoType];
+    let currentAttr;
+
+    return [
+      itemsEntry.reduce((prev, [item, attr]) => {
+        if (
+          item &&
+          // item.type === VectorType[geoType] &&
+          geoId === item.vectorId
+        ) {
+          if (Style[attr] && Style[attr][geoType]) {
+            currentAttr = attr;
+            return Style[attr][geoType];
+          }
         }
-      }
-      return prev;
-    }, Style[geoType]);
+        return prev;
+      }, Style[geoType]),
+      currentAttr,
+    ];
   },
   setVectorStyle(ctx, vector, geoType = vector.geoType) {
-    const styles = help.getVectorStyle(vector, geoType);
+    const [styles, attr] = help.getVectorStyle(vector, geoType);
     for (const style in styles) {
       if (typeof styles[style] === "function") {
         styles[style](ctx, vector);
@@ -42,7 +49,7 @@ const help = {
         ctx[style] = styles[style];
       }
     }
-    return styles;
+    return [styles, attr];
   },
   transformCoves(lines) {
     return lines.map((line) =>
@@ -77,18 +84,36 @@ const help = {
       ctx.stroke();
     }
   },
+  getReal(data) {
+    return (data * coordinate.ratio * coordinate.zoom) / coordinate.defaultZoom;
+  },
+  getImage(src) {
+    if (imgCache[src]) {
+      return imgCache[src];
+    }
+    const img = new Image();
+    img.src = src;
+    return (imgCache[src] = new Promise((resolve) => {
+      img.onload = () => {
+        resolve(img);
+      };
+    }));
+  },
 };
 
 export default class Draw {
   constructor() {
+    this.canvas = null;
     this.context = null;
   }
 
   initContext(canvas) {
     if (canvas) {
+      this.canvas = canvas;
       this.context = canvas.getContext("2d");
     } else {
       this.context = null;
+      this.canvas = null;
     }
   }
 
@@ -102,8 +127,18 @@ export default class Draw {
   }
 
   drawBackGroundImg(vector) {
+    const img = vector.imageData;
+    const width = help.getReal(img.width);
+    const height = help.getReal(img.height);
+    const center = coordinate.getScreenXY(vector.center);
     this.context.save();
-
+    this.context.drawImage(
+      img,
+      center.x - width / 2,
+      center.y - height / 2,
+      width,
+      height
+    );
     this.context.restore();
   }
 
@@ -140,7 +175,6 @@ export default class Draw {
   }
 
   drawRoad(vector, isTemp) {
-    console.log(vector);
     if (!isTemp && vector.display && vector.way !== "oneWay") {
       const ctx = this.context;
       const draw = (midDivide) => {
@@ -166,7 +200,7 @@ export default class Draw {
       const endReal = isTemp
         ? vector.end
         : dataService.getRoadPoint(vector.endId);
-      this.drawText(
+      this.drawTextByInfo(
         { x: (startReal.x + endReal.x) / 2, y: (startReal.y + endReal.y) / 2 },
         vector.vectorId
       );
@@ -218,7 +252,7 @@ export default class Draw {
         ctx.lineTo(point2.x, point2.y);
         ctx.stroke();
       }
-      this.drawText(
+      this.drawTextByInfo(
         {
           x: (edgeVector.start.x + edgeVector.end.x) / 2,
           y: (edgeVector.start.y + edgeVector.end.y) / 2,
@@ -250,7 +284,7 @@ export default class Draw {
     }
   }
 
-  drawControlPoint(vector) {
+  drawCrossPoint(vector) {
     const start = coordinate.getScreenXY(
       dataService
         .getRoadEdge(vector.edgeInfo1.id)
@@ -268,14 +302,17 @@ export default class Draw {
       end
     );
     const pt = mathUtil.twoOrderBezier2(0.5, start, pt2, end);
-
+    const extremePoint = coordinate.getScreenXY(vector.extremePoint);
     const ctx = this.context;
     ctx.save();
+    ctx.strokeStyle = "red";
     ctx.beginPath();
     ctx.arc(
-      pt.x,
-      pt.y,
-      Style.ControlPoint.radius * coordinate.ratio,
+      // pt.x,
+      // pt.y,
+      extremePoint.x,
+      extremePoint.y,
+      Style.CrossPoint.radius * coordinate.ratio,
       0,
       Math.PI * 2,
       true
@@ -288,9 +325,11 @@ export default class Draw {
     ctx.beginPath();
     help.setVectorStyle(ctx, null, "RoadEdge");
     //曲线
-    ctx.moveTo(start.x, start.y);
-    ctx.quadraticCurveTo(pt.x, pt.y, end.x, end.y);
-    ctx.stroke();
+    // ctx.moveTo(start.x, start.y);
+    // ctx.quadraticCurveTo(pt.x, pt.y, end.x, end.y);
+
+    const [coves] = help.transformCoves([vector.curves]);
+    help.drawCoves(ctx, coves);
     ctx.restore();
   }
 
@@ -356,19 +395,136 @@ export default class Draw {
     this.drawPoint(vector);
   }
 
+  drawArrow(vector) {
+    const startReal = dataService.getPoint(vector.startId);
+    const start = coordinate.getScreenXY(startReal);
+    const endReal = dataService.getPoint(vector.endId);
+    const end = coordinate.getScreenXY(endReal);
+    const ctx = this.context;
+
+    ctx.save();
+
+    const [style] = help.setVectorStyle(this.context, vector, "Arrow");
+    if (vector.arrowColor) {
+      ctx.strokeStyle = vector.arrowColor;
+    }
+    ctx.beginPath();
+    ctx.moveTo(start.x, start.y);
+    ctx.lineTo(end.x, end.y);
+
+    const dires =
+      vector.category === UIEvents.MeasureLine
+        ? [
+            [start, end],
+            [end, start],
+          ]
+        : [[start, end]];
+    for (let [start, end] of dires) {
+      const lines = mathUtil.getArrow(start, end);
+      ctx.moveTo(lines[0].x, lines[0].y);
+      ctx.lineTo(lines[1].x, lines[1].y);
+      ctx.lineTo(lines[2].x, lines[2].y);
+    }
+    ctx.stroke();
+    ctx.restore();
+
+    if ([Style.Focus.Arrow, Style.Select.Arrow].includes(style)) {
+      this.drawPoint(startReal);
+      this.drawPoint(endReal);
+    }
+  }
+
+  drawMagnifier(vector) {
+    const ctx = this.context;
+    this.drawPoint({
+      ...vector,
+      ...vector.position,
+      radius: Style.Magnifier.radius,
+    });
+    const pt = coordinate.getScreenXY(vector.position);
+    const target = coordinate.getScreenXY(vector.popPosition);
+    const [style] = help.setVectorStyle(ctx, vector);
+    const radius = help.getReal(vector.radius || style.radius);
+    const offset = radius / 2;
+    const targetPts =
+      style === Style.Focus.Magnifier
+        ? [mathUtil.translate(pt, target, pt, radius), target]
+        : null;
+
+    ctx.save();
+    ctx.beginPath();
+    ctx.moveTo(pt.x - offset, pt.y);
+    ctx.lineTo(pt.x + offset, pt.y);
+    ctx.stroke();
+    ctx.beginPath();
+    ctx.moveTo(pt.x, pt.y - offset);
+    ctx.lineTo(pt.x, pt.y + offset);
+    ctx.stroke();
+
+    if (targetPts) {
+      ctx.beginPath();
+      ctx.moveTo(targetPts[0].x, targetPts[0].y);
+      ctx.lineTo(targetPts[1].x, targetPts[1].y);
+      ctx.stroke();
+
+      let img, imgBound;
+      if (vector.photoImage) {
+        img = vector.photoImage;
+        imgBound = [0, 0, img.width, img.height];
+      } else {
+        const size = help.getReal(style.target.realRadius);
+        const backImg = dataService.getBackgroundImg();
+        img = backImg.imageData;
+        const imgCenter = coordinate.getScreenXY(backImg.center);
+        const start = {
+          x: imgCenter.x - help.getReal(img.width) / 2,
+          y: imgCenter.y - help.getReal(img.height) / 2,
+        };
+        const ro = img.width / help.getReal(img.width);
+        imgBound = [
+          (pt.x - start.x - size) * ro,
+          (pt.y - start.y - size) * ro,
+          size * 2 * ro,
+          size * 2 * ro,
+        ];
+      }
+      const size = help.getReal(style.target.radius);
+      ctx.beginPath();
+      ctx.arc(target.x, target.y, size, 0, 2 * Math.PI);
+      ctx.clip();
+      ctx.drawImage(
+        img,
+        ...imgBound,
+        target.x - size,
+        target.y - size,
+        size * 2,
+        size * 2
+      );
+      ctx.strokeStyle = style.target.strokeStyle;
+      ctx.lineWidth = style.target.lineWidth;
+      ctx.stroke();
+    }
+    ctx.restore();
+  }
+
   drawCircle(element) {
     this.drawPoint({
+      ...element,
+      geoType: "Circle",
       ...element.center,
-      radius: element.radius,
-      geoType: element.geoType,
     });
+
+    element.points.forEach((point) => this.drawPoint(point));
   }
 
   drawPoint(vector) {
     const pt = coordinate.getScreenXY({ x: vector.x, y: vector.y });
     const ctx = this.context;
-    const style = help.setVectorStyle(ctx, vector, vector.geoType || "Point");
-    const radius = (vector.radius || style.radius) * coordinate.ratio;
+    const [style] = help.setVectorStyle(ctx, vector, vector.geoType || "Point");
+    if (vector.color) {
+      ctx.strokeStyle = vector.color;
+    }
+    const radius = help.getReal(vector.radius || style.radius);
     ctx.save();
     ctx.beginPath();
     ctx.arc(pt.x, pt.y, radius, 0, Math.PI * 2, true);
@@ -378,18 +534,20 @@ export default class Draw {
 
     if (import.meta.env.DEV) {
       if (vector.vectorId) {
-        this.drawText(vector, vector.vectorId);
+        this.drawTextByInfo(vector, vector.vectorId);
       }
     }
   }
 
-  // 文字
-  drawText(position, txt, angle) {
+  drawTextByInfo(position, txt, angle, setStyle = true) {
     const ctx = this.context;
     ctx.save();
-    help.setVectorStyle(ctx, null, "Text");
+    setStyle && help.setVectorStyle(ctx, null, "Text");
 
     const pt = coordinate.getScreenXY(position);
+    const text = ctx.measureText(txt);
+    pt.x -= text.width / 2;
+    pt.y += (text.actualBoundingBoxAscent + text.actualBoundingBoxDescent) / 2;
     if (angle) {
       ctx.translate(pt.x, pt.y);
       ctx.rotate(angle);
@@ -400,19 +558,38 @@ export default class Draw {
     ctx.restore();
   }
 
+  // 文字
+  drawText(vector) {
+    help.setVectorStyle(this.context, vector);
+    this.context.fillStyle = vector.color;
+    const oldFont = this.context.font;
+    this.context.font = `${vector.fontSize}px Microsoft YaHei`;
+    this.drawTextByInfo(vector.center, vector.value, 0, false);
+
+    const ctx = this.context;
+    const pt = coordinate.getScreenXY(vector.center);
+    const text = ctx.measureText(vector.value);
+    pt.x -= text.width / 2;
+    pt.y += (text.actualBoundingBoxAscent + text.actualBoundingBoxDescent) / 2;
+
+    this.context.font = oldFont;
+  }
+
   drawLine(vector) {
-    let start = dataService.getPoint(vector.startId);
-    start = coordinate.getScreenXY(start);
-    let end = dataService.getPoint(vector.endId);
-    end = coordinate.getScreenXY(end);
+    if ([UIEvents.Arrow, UIEvents.MeasureLine].includes(vector.category)) {
+      return this.drawArrow(vector);
+    }
+    const startReal = dataService.getPoint(vector.startId);
+    const start = coordinate.getScreenXY(startReal);
+    const endReal = dataService.getPoint(vector.endId);
+    const end = coordinate.getScreenXY(endReal);
 
     this.context.save();
-    const style = help.setVectorStyle(
+    const [style, attr] = help.setVectorStyle(
       this.context,
       vector,
       vector.category || vector.geoType
     );
-
     if (style.dash) {
       this.context.setLineDash(style.dash);
     }
@@ -421,6 +598,11 @@ export default class Draw {
     this.context.lineTo(end.x, end.y);
     this.context.stroke();
     this.context.restore();
+
+    // if (attr) {
+    //   this.drawPoint(startReal)
+    //   this.drawPoint(endReal)
+    // }
   }
 
   drawElementLine(element) {
@@ -430,7 +612,7 @@ export default class Draw {
     end = coordinate.getScreenXY(end);
 
     this.context.save();
-    const style = help.setVectorStyle(
+    const [style] = help.setVectorStyle(
       this.context,
       element,
       element.category || element.geoType

+ 25 - 11
src/graphic/Renderer/Render.js

@@ -17,6 +17,12 @@ export default class Render {
       return;
     }
     switch (vector.geoType) {
+      case VectorType.BackgroundImg:
+        draw.drawBackGroundImg(vector);
+        break;
+      case VectorType.Img:
+        draw.drawBackGroundImg(vector);
+        break;
       case VectorType.Road:
         draw.drawRoad(vector, false);
         return;
@@ -26,17 +32,21 @@ export default class Render {
       case VectorType.RoadPoint:
         draw.drawRoadPoint(vector);
         return;
-      case VectorType.ControlPoint:
-        draw.drawControlPoint(vector);
+      case VectorType.CrossPoint:
+        draw.drawCrossPoint(vector);
         return;
       case VectorType.Line:
         draw.drawLine(vector); //需要修改,有几种情况:测量,校准,基准
         break;
       case VectorType.Text:
         draw.drawText(vector, styleType, flag);
-        return;
-      case VectorType.BackgroundImg:
-        draw.drawBackGroundImg();
+        break;
+      case VectorType.Circle:
+        draw.drawCircle(vector);
+        break;
+      case VectorType.Magnifier:
+        draw.drawMagnifier(vector);
+        break;
     }
   }
 
@@ -47,10 +57,10 @@ export default class Render {
     }
     switch (vector.geoType) {
       case VectorType.Point:
-        draw.drawCircle(vector);
+        draw.drawPoint(vector);
         break;
       case VectorType.RoadPoint:
-        draw.drawCircle(vector);
+        draw.drawPoint(vector);
         break;
       case VectorType.Line:
         draw.drawElementLine(vector); //需要修改,有几种情况:测量,校准,基准
@@ -88,7 +98,7 @@ export default class Render {
   }
 
   autoRedraw() {
-    console.log("重绘");
+    // console.log("重绘");
     draw.clear();
     if (dataService.getGridDisplay()) {
       const grid = dataService.getGrid();
@@ -119,9 +129,9 @@ export default class Render {
       this.drawGeometry(curveRoads[key]);
     }
 
-    let controlPoints = dataService.getControlPoints();
-    for (let key in controlPoints) {
-      this.drawGeometry(controlPoints[key]);
+    let crossPoints = dataService.getCrossPoints();
+    for (let key in crossPoints) {
+      this.drawGeometry(crossPoints[key]);
     }
 
     let lines = dataService.getLines();
@@ -138,6 +148,10 @@ export default class Render {
     for (let key in texts) {
       this.drawGeometry(texts[key]);
     }
+    const magnifiers = dataService.getMagnifiers();
+    for (let magnifiersKey in magnifiers) {
+      this.drawGeometry(magnifiers[magnifiersKey]);
+    }
 
     //this.drawGeometry(dataService.getImg());
     this.redrawElements();

+ 0 - 0
src/graphic/Service/CircleService.js


Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini