Browse Source

修改bug

bill 1 year ago
parent
commit
de107c958b

+ 2 - 1
package.json

@@ -26,12 +26,13 @@
     "sass": "^1.62.0",
     "sass-loader": "^13.2.2",
     "stateshot": "^1.3.5",
+    "swiper": "^8.3.0",
     "vconsole": "^3.15.0",
     "vue": "^3.2.47",
     "vue-cropper": "1.0.2",
     "vue-i18n": "^9.2.2",
     "vue-router": "4.0.3",
-    "swiper": "^8.3.0"
+    "vue3-photo-preview": "^0.3.0"
   },
   "devDependencies": {
     "@types/node": "^18.15.11",

+ 16 - 0
pnpm-lock.yaml

@@ -29,6 +29,7 @@ specifiers:
   vue-i18n: ^9.2.2
   vue-router: 4.0.3
   vue-tsc: ^1.2.0
+  vue3-photo-preview: ^0.3.0
 
 dependencies:
   '@lk77/vue3-color': 3.0.6
@@ -53,6 +54,7 @@ dependencies:
   vue-cropper: 1.0.2
   vue-i18n: 9.2.2_vue@3.2.47
   vue-router: 4.0.3_vue@3.2.47
+  vue3-photo-preview: 0.3.0_vue@3.2.47
 
 devDependencies:
   '@types/node': 18.15.11
@@ -1060,6 +1062,10 @@ packages:
     engines: {node: '>= 8'}
     dev: false
 
+  /lodash-es/4.17.21:
+    resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+    dev: false
+
   /magic-string/0.25.9:
     resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
     dependencies:
@@ -1543,3 +1549,13 @@ packages:
       '@vue/runtime-dom': 3.2.47
       '@vue/server-renderer': 3.2.47_vue@3.2.47
       '@vue/shared': 3.2.47
+
+  /vue3-photo-preview/0.3.0_vue@3.2.47:
+    resolution: {integrity: sha512-DeZ076yN85RcfWdhcgSYVEj5Y721mi6rmDoShDb4Bt7z7Q5Rfs4Y8PlP66OIrsyE0x4KVLdE0auJGxrxXRJXjA==}
+    engines: {node: '>=14', npm: '>=7'}
+    peerDependencies:
+      vue: '>=3.2.0'
+    dependencies:
+      lodash-es: 4.17.21
+      vue: 3.2.47
+    dev: false

File diff suppressed because it is too large
+ 1 - 1
server/test/a0k4xu045_202305311600080410/attach/sceneStore


+ 31 - 18
src/components/base/components/input/range.vue

@@ -24,13 +24,18 @@
     </div>
     <UInumber
       v-if="props.input"
+      :ctrl="ctrl"
       :modelValue="modelValue"
       @update:modelValue="inputUpdateHandler"
       :min="min"
       :max="max"
       :step="step"
       class="range-text"
-    />
+    >
+      <template v-for="(slot, name) in $slots" v-slot:[name]="raw">
+        <slot :name="name" v-bind="raw" />
+      </template>
+    </UInumber>
   </div>
 </template>
 
@@ -41,6 +46,7 @@ import UInumber from "./number.vue";
 import { os } from "../../utils/index";
 
 const props = defineProps(rangePropsDesc);
+
 const emit = defineEmits(["update:modelValue"]);
 const getValue = (value) => {
   const calcStep = Math.ceil(1 / props.step);
@@ -61,7 +67,7 @@ const getValue = (value) => {
 const percen = computed({
   get() {
     const val = (Number(props.modelValue) - props.min) / (props.max - props.min);
-    return val > props.max ? props.max : val;
+    return val > 1 ? 1 : val < 0 ? 0 : val;
   },
   set(val) {
     const len = props.max - props.min;
@@ -97,34 +103,41 @@ const parent = document.documentElement;
 const slideDownHandler = (ev) => {
   ev.preventDefault();
   const moveStartX = ev.clientX || ev.touches[0].clientX;
+  const moveStartY = ev.clientY || ev.touches[0].clientY;
   const startPercen = percen.value;
   mode.value = modeEmun.slide;
 
   const moveHandler = (ev) => {
     ev.preventDefault();
-    const moveX = (ev.clientX || ev.touches[0].clientX) - moveStartX;
-    const readyPercen = startPercen + moveX / locusWidth.value;
+    const moveCurrentX = ev.clientX || ev.touches[0].clientX;
+    const moveCurrentY = ev.clientY || ev.touches[0].clientY;
+    const moveX = moveCurrentX - moveStartX;
+
+    let readyPercen;
+    if (props.moveCallback) {
+      readyPercen = props.moveCallback(
+        { x: moveStartX, y: moveStartY },
+        { x: moveCurrentX, y: moveCurrentY },
+        { start: startPercen, locusWidth: locusWidth.value }
+      );
+    } else {
+      readyPercen = startPercen + moveX / locusWidth.value;
+    }
 
     percen.value = readyPercen < 0 ? 0 : readyPercen > 1 ? 1 : readyPercen;
   };
 
   const upHandler = (ev) => {
     mode.value = modeEmun.default;
-    if (os.isPc && !os.isTablet) {
-      parent.removeEventListener("mousemove", moveHandler, false);
-      parent.removeEventListener("mouseup", upHandler, false);
-    } else {
-      parent.removeEventListener("touchmove", moveHandler);
-      parent.removeEventListener("touchend", upHandler);
-    }
+    parent.removeEventListener("mousemove", moveHandler, false);
+    parent.removeEventListener("mouseup", upHandler, false);
+    parent.removeEventListener("touchmove", moveHandler);
+    parent.removeEventListener("touchend", upHandler);
   };
 
-  if (os.isPc && !os.isTablet) {
-    parent.addEventListener("mousemove", moveHandler, false);
-    parent.addEventListener("mouseup", upHandler, false);
-  } else {
-    parent.addEventListener("touchmove", moveHandler, { passive: false });
-    parent.addEventListener("touchend", upHandler, { passive: false });
-  }
+  parent.addEventListener("mousemove", moveHandler, false);
+  parent.addEventListener("mouseup", upHandler, false);
+  parent.addEventListener("touchmove", moveHandler, { passive: false });
+  parent.addEventListener("touchend", upHandler, { passive: false });
 };
 </script>

+ 222 - 215
src/components/base/components/input/state.js

@@ -1,249 +1,256 @@
 const instalcePublic = {
-    name: {
-        type: String,
-    },
-    disabled: {
-        type: [Boolean],
-    },
-    modelValue: {
-        required: false,
-        default: '',
-    },
-    placeholder: {
-        require: false,
-        default: '请输入',
-    },
-}
+  name: {
+    type: String,
+  },
+  disabled: {
+    type: [Boolean],
+  },
+  modelValue: {
+    required: false,
+    default: "",
+  },
+  placeholder: {
+    require: false,
+    default: "请输入",
+  },
+};
 
 export const colorPropsDesc = {
-    ...instalcePublic,
-    width: {
-        type: String,
-        default: '100px',
-    },
-    height: {
-        type: String,
-        default: '34px',
-    },
-}
+  ...instalcePublic,
+  width: {
+    type: String,
+    default: "100px",
+  },
+  height: {
+    type: String,
+    default: "34px",
+  },
+};
 
 export const filePropsDesc = {
-    ...instalcePublic,
-    addText: {
-        require: false,
-        default: '继续添加',
-    },
-    replaceText: {
-        require: false,
-        default: '替换',
-    },
-    toastErr: {
-        require: false,
-        type: Function
-    },
-    placeholder: {
-        require: false,
-        default: '请选择',
-    },
-    othPlaceholder: {
-        require: false,
-        default: '',
-    },
-    accept: {
-        type: String,
-    },
-    scale: {
-        type: String,
-    },
-    multiple: {
-        type: Boolean,
-    },
-    preview: {
-        type: Boolean,
-    },
-    maxSize: {
-        type: Number,
-    },
-    maxLen: {
-        type: Number,
-    },
-}
+  ...instalcePublic,
+  addText: {
+    require: false,
+    default: "继续添加",
+  },
+  replaceText: {
+    require: false,
+    default: "替换",
+  },
+  toastErr: {
+    require: false,
+    type: Function,
+  },
+  placeholder: {
+    require: false,
+    default: "请选择",
+  },
+  othPlaceholder: {
+    require: false,
+    default: "",
+  },
+  accept: {
+    type: String,
+  },
+  scale: {
+    type: String,
+  },
+  multiple: {
+    type: Boolean,
+  },
+  preview: {
+    type: Boolean,
+  },
+  maxSize: {
+    type: Number,
+  },
+  maxLen: {
+    type: Number,
+  },
+};
 
 export const switchPropsDesc = {
-    ...instalcePublic,
-    width: {
-        type: [Number, String],
-    },
-    height: {
-        type: [Number, String],
-    },
-}
+  ...instalcePublic,
+  width: {
+    type: [Number, String],
+  },
+  height: {
+    type: [Number, String],
+  },
+};
 
 export const checkboxPropsDesc = {
-    ...switchPropsDesc,
-    label: {
-        type: String,
-        required: false,
-    },
-}
+  ...switchPropsDesc,
+  label: {
+    type: String,
+    required: false,
+  },
+};
 
 export const radioPropsDesc = {
-    ...checkboxPropsDesc,
-    icon: {
-        type: String,
-    },
-}
+  ...checkboxPropsDesc,
+  icon: {
+    type: String,
+  },
+};
 
 export const textPropsDesc = {
-    ...instalcePublic,
-    maxlength: {
-        type: [String, Number],
-    },
-    placeholder: {
-        type: String,
-        default: '请输入',
-    },
-    readonly: {
-        type: Boolean,
-        default: false,
-    },
-    other: {
-        type: Object,
-        default: () => ({}),
-    },
-    right: {
-        type: Boolean,
-    },
-}
+  ...instalcePublic,
+  maxlength: {
+    type: [String, Number],
+  },
+  placeholder: {
+    type: String,
+    default: "请输入",
+  },
+  readonly: {
+    type: Boolean,
+    default: false,
+  },
+  other: {
+    type: Object,
+    default: () => ({}),
+  },
+  right: {
+    type: Boolean,
+  },
+};
 
-export const textEmitsDesc = ['update:modelValue', 'focus', 'blur', 'click', 'keydown']
-export const selectEmitsDesc = ['update:modelValue', 'focus', 'blur']
+export const textEmitsDesc = [
+  "update:modelValue",
+  "focus",
+  "blur",
+  "click",
+  "keydown",
+];
+export const selectEmitsDesc = ["update:modelValue", "focus", "blur"];
 
 export const textareaPropsDesc = {
-    ...textPropsDesc,
-    rich: {
-        type: Boolean,
-    },
-}
+  ...textPropsDesc,
+  rich: {
+    type: Boolean,
+  },
+};
 
 export const richtextPropsDesc = {
-    ...textareaPropsDesc,
-    onUpdatePos: Function,
-}
+  ...textareaPropsDesc,
+  onUpdatePos: Function,
+};
 
 export const selectPropsDesc = {
-    ...textPropsDesc,
-    isTransform: {
-        type: Boolean,
-        require: false
-    },
-    stopEl: {
-        type: String,
-        require: false,
-    },
-    floatingClass: {
-        type: String,
-        require: false,
-    },
-    showOptions: {
-        type: Boolean,
-        require: false,
-    },
-    placeholder: { ...textPropsDesc.placeholder, default: '请选择' },
-    unplaceholder: { ...textPropsDesc.placeholder, default: '暂无选项' },
-    options: {
-        type: Array,
-        default: () => [],
-    },
-    dire: {
-        type: String,
-        default: 'bottom',
-    },
-}
+  ...textPropsDesc,
+  isTransform: {
+    type: Boolean,
+    require: false,
+  },
+  stopEl: {
+    type: String,
+    require: false,
+  },
+  floatingClass: {
+    type: String,
+    require: false,
+  },
+  showOptions: {
+    type: Boolean,
+    require: false,
+  },
+  placeholder: { ...textPropsDesc.placeholder, default: "请选择" },
+  unplaceholder: { ...textPropsDesc.placeholder, default: "暂无选项" },
+  options: {
+    type: Array,
+    default: () => [],
+  },
+  dire: {
+    type: String,
+    default: "bottom",
+  },
+};
 
 export const searchPropsDesc = {
-    ...selectPropsDesc,
-    unplaceholder: { ...textPropsDesc.placeholder, default: '无搜索结果' },
-}
+  ...selectPropsDesc,
+  unplaceholder: { ...textPropsDesc.placeholder, default: "无搜索结果" },
+};
 
 export const numberPropsDesc = {
-    ...textPropsDesc,
-    inInput: {
-        type: Boolean,
-        default: true,
-    },
-    ctrl: {
-        type: Boolean,
-        default: true,
-    },
-    step: {
-        type: Number,
-        require: true,
-        default: 1,
-    },
-    min: {
-        type: [Number, String],
-        require: false,
-    },
-    max: {
-        type: [Number, String],
-        require: false,
-    },
-}
+  ...textPropsDesc,
+  inInput: {
+    type: Boolean,
+    default: true,
+  },
+  ctrl: {
+    type: Boolean,
+    default: true,
+  },
+  step: {
+    type: Number,
+    require: true,
+    default: 1,
+  },
+  min: {
+    type: [Number, String],
+    require: false,
+  },
+  max: {
+    type: [Number, String],
+    require: false,
+  },
+};
 
 export const rangePropsDesc = {
-    ...numberPropsDesc,
-    min: { ...numberPropsDesc.min, require: true },
-    input: { type: Boolean, default: true },
-}
+  ...numberPropsDesc,
+  min: { ...numberPropsDesc.min, require: true },
+  input: { type: Boolean, default: true },
+  moveCallback: { type: Function, require: false },
+};
 
 const summary = {
-    ...checkboxPropsDesc,
-    ...radioPropsDesc,
-    ...selectPropsDesc,
-    ...textPropsDesc,
-    ...rangePropsDesc,
-    ...numberPropsDesc,
-    ...switchPropsDesc,
-    ...textareaPropsDesc,
-    ...filePropsDesc,
-    ...searchPropsDesc,
-    ...richtextPropsDesc,
-    ...colorPropsDesc,
-}
+  ...checkboxPropsDesc,
+  ...radioPropsDesc,
+  ...selectPropsDesc,
+  ...textPropsDesc,
+  ...rangePropsDesc,
+  ...numberPropsDesc,
+  ...switchPropsDesc,
+  ...textareaPropsDesc,
+  ...filePropsDesc,
+  ...searchPropsDesc,
+  ...richtextPropsDesc,
+  ...colorPropsDesc,
+};
 for (let key in summary) {
-    summary[key] = {
-        ...summary[key],
-        default: undefined,
-    }
+  summary[key] = {
+    ...summary[key],
+    default: undefined,
+  };
 }
 
 export const inputEmitDesc = {
-    text: textEmitsDesc,
-    select: selectEmitsDesc,
-    search: textEmitsDesc,
-}
+  text: textEmitsDesc,
+  select: selectEmitsDesc,
+  search: textEmitsDesc,
+};
 
 export const inputPropsDesc = {
-    ...summary,
-    type: {
-        type: String,
-        required: true,
-        default: 'text',
-    },
-    width: {
-        type: [Number, String],
-    },
-    height: {
-        type: [Number, String],
-    },
-    require: {
-        type: Boolean,
-    },
-    error: {
-        type: String,
-    },
-    disabled: {
-        type: Boolean,
-    },
-}
+  ...summary,
+  type: {
+    type: String,
+    required: true,
+    default: "text",
+  },
+  width: {
+    type: [Number, String],
+  },
+  height: {
+    type: [Number, String],
+  },
+  require: {
+    type: Boolean,
+  },
+  error: {
+    type: String,
+  },
+  disabled: {
+    type: Boolean,
+  },
+};

+ 113 - 69
src/components/fill-slide/index.vue

@@ -1,50 +1,70 @@
 <template>
-  <div class="fill-slide">
-    <div></div>
-    <div class="header">
-      <slot name="header" />
-      <ui-icon class="close" type="close" @click="clickHandler" ctrl />
+  <photo-slider
+    :items="slideItems"
+    :visible="true"
+    :index="data.indexOf(active)"
+    @clickPhoto="showStatus = !showStatus"
+    @changeIndex="(index) => $emit('update:active', data[index])"
+  />
+  <teleport to="body" v-if="showStatus">
+    <div class="fill-slide">
+      {{ data.indexOf(active) }}
+      {{ active.url }}
+      <div class="header">
+        <ui-icon
+          class="back-icon"
+          type="return"
+          ctrl
+          style="margin-right: 10px"
+          @click="emit('quit')"
+        />
+        <span class="title">{{ data.indexOf(active) + 1 }} / {{ data.length }}</span>
+        <span class="top-right">
+          <slot name="topRight" />
+        </span>
+      </div>
+      <div class="foot">
+        <slot name="foot" />
+      </div>
     </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 }: any">
-          <template v-if="$slots.default">
-            <slot :data="raw" />
-          </template>
-          <img
-            :src="useStaticUrl(getURL ? getURL(raw) : raw.url).value"
-            class="image"
-            v-else
-          />
-        </template>
-      </ui-slide>
-    </div>
-    <div class="foot">
-      <slot name="foot" />
-    </div>
-  </div>
+  </teleport>
 </template>
 
 <script lang="ts" setup>
-import UiSlide from "@/components/base/components/slide/index.vue";
 import { useStaticUrl } from "@/hook/useStaticUrl";
 import UiIcon from "@/components/base/components/icon/index.vue";
+import { PhotoSlider } from "vue3-photo-preview";
+import { computed, ref, watch, watchEffect } from "vue";
+import "vue3-photo-preview/dist/index.css";
+import router from "@/router";
 
 type Item = { url: string };
 
-defineProps<{ data: Item[]; active: Item; getURL?: (data: any) => string }>();
+const showStatus = ref(true);
+const props = defineProps<{
+  data: Item[];
+  active: Item;
+  getURL?: (data: any) => string;
+}>();
+
+const slideItems = computed(() =>
+  props.data.map((item, i) => ({
+    key: i.toString(),
+    src: useStaticUrl(props.getURL ? props.getURL(item) : item.url).value,
+  }))
+);
+
 const emit = defineEmits<{
   (e: "update:active", d: Item): void;
   (e: "quit"): void;
 }>();
 
-const clickHandler = () => {
-  emit("quit");
-};
+watch(
+  () => router.currentRoute.value.name,
+  () => {
+    emit("quit");
+  }
+);
 </script>
 
 <style scoped lang="scss">
@@ -55,63 +75,87 @@ const clickHandler = () => {
   left: 0;
   right: 0;
   bottom: 0;
-  background: #000;
-  z-index: 3;
+  // background: #000;
+  z-index: 3000;
   display: flex;
   align-items: center;
   justify-content: space-between;
   flex-direction: column;
+  pointer-events: none;
 }
-
-.slide-layout {
-  max-width: 90vw;
-  width: 840px;
-  height: 540px;
-  position: relative;
-  z-index: 1;
-}
-
-.image {
-  width: 100%;
-  height: 100%;
-  object-fit: contain;
-  border-radius: 4px;
+.title {
+  position: absolute;
+  left: 50%;
+  transform: translateX(-50%);
 }
 
 .header {
   width: 100%;
+  height: 50px;
   position: absolute;
   z-index: 4;
-  background: red;
-  //height: 120px;
-}
-
-.close {
-  position: absolute;
-  right: 32px;
-  top: 32px;
-  font-size: 20px;
+  background: rgba(22, 24, 26, 1);
+  display: flex;
+  justify-content: space-between;
   color: #fff;
-  z-index: 1;
+  align-items: center;
+  padding: 0 16px;
+  pointer-events: all;
 }
 .foot {
+  position: absolute;
+  height: 72px;
+  background: rgba(22, 24, 26, 1);
+  bottom: 0;
   width: 100%;
-  padding-bottom: 24px;
+  pointer-events: all;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.back-icon {
+  display: inline-flex;
+  width: 32px;
+  height: 32px;
+  background: rgba(255, 255, 255, 0.1);
+  border-radius: 24px 24px 24px 24px;
+  align-items: center;
+  justify-content: center;
 }
 </style>
 
 <style lang="scss">
-.fill-slide .ui-slide .ui-gate-layer {
-  overflow: initial !important;
-  .ui-gate-slides .ui-gate-content {
-    transition: all 0.3s ease;
-    transform: scale(0.9);
-    opacity: 0.5 !important;
-
-    &.active {
-      transform: scale(1);
-      opacity: 1 !important;
+.PhotoSlider__Wrapper {
+  .PhotoSlider__BannerWrap {
+    display: none;
+  }
+}
+.fill-slide .foot .menus {
+  position: static;
+  transform: none;
+  background: none;
+  .menu {
+    margin: 0 32px !important;
+    position: static !important;
+    height: auto !important;
+    transform: none !important;
+    padding: 8px 34px !important;
+    background-color: #fff;
+    color: rgba(22, 24, 26, 1);
+    flex-direction: row;
+    p {
+      margin-top: 0;
+      margin-left: 8px;
     }
   }
 }
+
+.top-right {
+  font-size: 20px;
+  color: #fff;
+  > * {
+    margin-left: 32px;
+  }
+}
 </style>

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

@@ -41,7 +41,7 @@ type Menu = {
 
 const props = withDefaults(
   defineProps<{
-    menus: Menu[];
+    menus: any[];
     activeKey?: any;
     dire?: "row" | "column";
     size?: number;

+ 1 - 1
src/hook/useGraphic.ts

@@ -59,7 +59,7 @@ export const loadData = genUseLoading(
   async (data?: RoadPhoto, oldId?: RoadPhoto["id"]) => {
     if (data) {
       oldId && drawRef.value.load.clear();
-      await drawRef.value.load.load(data.data, {
+      await drawRef.value.load.load(JSON.parse(JSON.stringify(data.data)), {
         ...data.sceneData,
         backImage: data.photoUrl,
       });

+ 0 - 3
src/sdk/carry/measures/index.vue

@@ -27,9 +27,6 @@ const measure = computed(() => {
     ? props.store.measure.tempMeasures
     : [...props.store.measure.list, ...props.store.baseLine.baseLines];
 });
-watchEffect(() => {
-  console.error(props.store.measure.tempMeasures[0]);
-});
 
 watch(
   refs,

+ 1 - 0
src/sdk/carry/measures/item.vue

@@ -32,6 +32,7 @@ const canvas = sdk.drawMeasure(
   props.data.color
 );
 
+console.error(props.data);
 const points = ref<Array<Pos>>();
 const theme = computed(() => carryProps.measureTheme.get(props.data).value);
 const updatePoints = () => {

+ 9 - 0
src/store/sceneSeting.ts

@@ -0,0 +1,9 @@
+import { ref } from "vue";
+
+export type SceneSeting = {
+  top: number;
+  scale: number;
+  rotate: number;
+};
+
+export const sceneSeting = ref<SceneSeting>();

+ 7 - 0
src/store/sync.ts

@@ -13,6 +13,7 @@ import router, { writeRouteName } from "@/router";
 import { baseURL } from "@/dbo/main";
 import { defaultUses, uses } from "@/store/SVGLabel";
 import { imageRotate } from "@/utils/image-rotate";
+import { sceneSeting } from "./sceneSeting";
 
 const global = window as any;
 
@@ -235,6 +236,11 @@ const loadStore = async () => {
   list.value = data?.measures || [];
   baseLines.value = data?.baseLines || [];
   basePoints.value = data?.basePoints || [];
+  sceneSeting.value = data?.sceneSeting || {
+    top: 4,
+    scale: 100,
+    rotate: 0,
+  };
   fixPoints.value = data?.fixPoints || [];
   photos.value = data?.photos || [];
   accidentPhotos.value = data?.accidentPhotos || [];
@@ -268,6 +274,7 @@ const syncSceneStore = () => {
     () => ({
       measures: list.value,
       baseLines: baseLines.value,
+      sceneSeting: sceneSeting.value,
       basePoints: basePoints.value,
       fixPoints: fixPoints.value,
       photos: photos.value,

+ 56 - 43
src/utils/menus.ts

@@ -1,46 +1,56 @@
-import {computed, ref, Ref, toRaw} from "vue";
-import {uiType} from "@/hook/useGraphic";
+import { computed, ref, Ref, toRaw } from "vue";
+import { uiType } from "@/hook/useGraphic";
 
 type MenuRaw = {
-  key: any,
-  children?: MenuRaw[]
-}
+  key: any;
+  children?: MenuRaw[];
+};
 
-
-type GenerateResult<R, E = {}> = Array<R & { children: GenerateResult<R, E> } & E>;
+type GenerateResult<R, E = {}> = Array<
+  R & { children: GenerateResult<R, E> } & E
+>;
 export const generateByMenus = <T extends MenuRaw, R>(
   generateFn: (men: T) => R,
   mainMenus: T[]
-): GenerateResult<R>  => {
-  return mainMenus.map(mainMenu => ({
+): GenerateResult<R> => {
+  return mainMenus.map((mainMenu) => ({
     ...generateFn(mainMenu),
-    children: mainMenu.children ? generateByMenus(generateFn, mainMenu.children) : []
-  }))
-}
-export const findMenuByAttr = <T extends MenuRaw, K extends keyof T, V extends T[K]>
-(value: V, attr: K, mainMenus: T[]) => {
+    children: mainMenu.children
+      ? generateByMenus(generateFn, mainMenu.children)
+      : [],
+  }));
+};
+export const findMenuByAttr = <
+  T extends MenuRaw,
+  K extends keyof T,
+  V extends T[K]
+>(
+  value: V,
+  attr: K,
+  mainMenus: T[]
+) => {
   for (const mainMenu of mainMenus) {
     if (toRaw(mainMenu[attr]) === toRaw(value)) {
-      return mainMenu
+      return mainMenu;
     } else if (mainMenu.children) {
-      const childMainMenu = findMenuByAttr(value, attr as any, mainMenu.children)
+      const childMainMenu = findMenuByAttr(
+        value,
+        attr as any,
+        mainMenu.children
+      );
       if (childMainMenu) {
         return childMainMenu;
       }
     }
   }
-}
+};
 
-export type GenerateMinMenusResult<TK, R> =  {
-  menus: GenerateResult<R, { onClick: () => void }>,
-  child: Ref<TK>,
-  activeMenuKey: Ref<any>
-}
-export const generateMixMenus = <
-  T extends MenuRaw,
-  K extends keyof T,
-  R
->(
+export type GenerateMinMenusResult<TK, R> = {
+  menus: GenerateResult<R, { onClick: () => void }>;
+  child: Ref<TK>;
+  activeMenuKey: Ref<any>;
+};
+export const generateMixMenus = <T extends MenuRaw, K extends keyof T, R>(
   childKey: K,
   generateFn: (men: T) => R,
   mainMenus: T[],
@@ -48,30 +58,33 @@ export const generateMixMenus = <
   getActiveItem: () => any
 ): GenerateMinMenusResult<T[K], R> => {
   const child = ref();
-  const menus = generateByMenus((menu) => ({
-    ...generateFn(menu),
-    onClick: () => {
-      if (menu[childKey]) {
-        if (toRaw(menu[childKey]) !== toRaw(child.value)) {
-          child.value = menu[childKey]
-          return;
+  const menus = generateByMenus(
+    (menu) => ({
+      ...generateFn(menu),
+      onClick: () => {
+        if (menu[childKey]) {
+          if (toRaw(menu[childKey]) !== toRaw(child.value)) {
+            child.value = menu[childKey];
+            return;
+          }
+        } else {
+          itemAction(menu);
         }
-      } else {
-        itemAction(menu);
-      }
-      child.value = null
-    }
-  }), mainMenus)
+        child.value = null;
+      },
+    }),
+    mainMenus
+  );
 
   const activeMenuKey = computed(() =>
     child.value
       ? findMenuByAttr(child.value, childKey, mainMenus)?.key
       : getActiveItem()
-  )
+  );
 
   return {
     child,
     menus,
-    activeMenuKey
+    activeMenuKey,
   };
-}
+};

+ 87 - 83
src/views/accidents/index.vue

@@ -1,21 +1,26 @@
 <template>
   <MainPanel>
     <template v-slot:header>
-      <Header type="return_l" :count="selects.length" :title="`已标注照片(${sortPhotos.length})`" :on-back="() => onBack()">
+      <Header
+        type="return_l"
+        :count="selects.length"
+        :title="`已标注照片(${sortPhotos.length})`"
+        :on-back="() => onBack()"
+      >
         <ui-button
-            :type="selectMode ? 'primary' : 'normal'"
-            @click="selectMode = !selectMode"
-            width="96px"
-            style="margin-right: 16px"
-            v-if="sortPhotos.length"
+          :type="selectMode ? 'primary' : 'normal'"
+          @click="selectMode = !selectMode"
+          width="96px"
+          style="margin-right: 16px"
+          v-if="sortPhotos.length"
         >
-          {{ selectMode ? '取消' : '选择' }}
+          {{ selectMode ? "取消" : "选择" }}
         </ui-button>
         <ui-button
-            v-if="!selectMode"
-            type="primary"
-            @click="router.push({name: writeRouteName.photos})"
-            width="96px"
+          v-if="!selectMode"
+          type="primary"
+          @click="router.push({ name: writeRouteName.photos })"
+          width="96px"
         >
           新增
         </ui-button>
@@ -27,14 +32,14 @@
         <div class="type-photos" v-for="typePhoto in typePhotos" :key="typePhoto.type">
           <p class="type-title">{{ typePhoto.type }}</p>
           <Photos
-              class="type-photos-content accident-photos-content"
-              :class="{max: typePhoto.photos.length > 4}"
-              v-model:active="active"
-              v-model:selects="selects"
-              :select-mode="selectMode"
-              :data="typePhoto.photos"
+            class="type-photos-content accident-photos-content"
+            :class="{ max: typePhoto.photos.length > 4 }"
+            v-model:active="active"
+            v-model:selects="selects"
+            :select-mode="selectMode"
+            :data="typePhoto.photos"
           >
-            <template v-slot="{data, index}">
+            <template v-slot="{ data, index }">
               <p>{{ data.title || typePhoto.type.substring(0, 2) + (index + 1) }}</p>
             </template>
           </Photos>
@@ -43,46 +48,57 @@
       <undata v-else undata-msg="无照片。请点击右上角按钮标注照片。" />
     </div>
 
-    <ActionMenus class="select-menus" :menus="selectMenus" dire="row" v-if="selects.length" />
+    <ActionMenus
+      class="select-menus"
+      :menus="selectMenus"
+      dire="row"
+      v-if="selects.length"
+    />
   </MainPanel>
 
-  <FillSlide :data="sortPhotos" v-model:active="active" @quit="active = null" v-if="active">
+  <FillSlide
+    :data="sortPhotos"
+    v-model:active="active"
+    @quit="active = null"
+    v-if="active"
+  >
     <template v-slot:foot>
       <ActionMenus class="menus" :menus="menus" dire="row" />
     </template>
+    <template v-slot:topRight>
+      <ui-icon type="del" @click="delPhoto(active)" />
+    </template>
   </FillSlide>
 </template>
 
 <script setup lang="ts">
-import MainPanel from '@/components/main-panel/index.vue'
-import FillSlide from '@/components/fill-slide/index.vue'
+import MainPanel from "@/components/main-panel/index.vue";
+import FillSlide from "@/components/fill-slide/index.vue";
 import ActionMenus from "@/components/group-button/index.vue";
-import {types, accidentPhotos, AccidentPhoto} from '@/store/accidentPhotos'
-import {router, writeRouteName} from '@/router'
-import {computed, onDeactivated, reactive, ref, watchEffect} from "vue";
-import {Mode} from '@/views/graphic/menus'
+import { types, accidentPhotos, AccidentPhoto } from "@/store/accidentPhotos";
+import { router, writeRouteName } from "@/router";
+import { computed, onDeactivated, reactive, ref, watchEffect } from "vue";
+import { Mode } from "@/views/graphic/menus";
 import UiButton from "@/components/base/components/button/index.vue";
 import Photos from "@/components/photos/index.vue";
 import Header from "@/components/photos/header.vue";
-import {useConfirm} from "@/hook";
+import { useConfirm } from "@/hook";
 import Undata from "@/components/photos/undata.vue";
-import {api} from "@/store/sync";
-import {photos} from "@/store/photos";
+import { api } from "@/store/sync";
+import { photos } from "@/store/photos";
 
 const sortPhotos = computed(() => {
-  const photos = [...accidentPhotos.value]
-  return photos.sort((a, b) =>
-    types.indexOf(a.type) - types.indexOf(b.type)
-  )
-})
+  const photos = [...accidentPhotos.value];
+  return photos.sort((a, b) => types.indexOf(a.type) - types.indexOf(b.type));
+});
 const typePhotos = computed(() =>
   types
-    .map(type => ({
+    .map((type) => ({
       type,
-      photos: sortPhotos.value.filter(data => data.type === type)
+      photos: sortPhotos.value.filter((data) => data.type === type),
     }))
-    .filter(data => data.photos.length)
-)
+    .filter((data) => data.photos.length)
+);
 const onBack = () => {
   let back = router.currentRoute.value.query.back;
   if (back) {
@@ -91,23 +107,17 @@ const onBack = () => {
     api.closePage();
   }
 };
-const selectMode = ref(false)
-const selects = ref<AccidentPhoto[]>([])
-const active = ref<AccidentPhoto>()
+const selectMode = ref(false);
+const selects = ref<AccidentPhoto[]>([]);
+const active = ref<AccidentPhoto>();
 const menus = [
   {
     key: "edit",
     icon: "edit",
     text: "修改",
-    onClick: () => gotoDraw()
+    onClick: () => gotoDraw(),
   },
-  {
-    key: "del",
-    icon: "del",
-    text: "删除",
-    onClick: () => delPhoto()
-  }
-]
+];
 
 const selectMenus = reactive([
   {
@@ -118,72 +128,70 @@ const selectMenus = reactive([
     onClick: () => {
       const params = {
         id1: selects.value[0].id,
-        id2: selects.value.length === 1 ? '-1' : selects.value[1].id
-      }
-      router.push({ name: writeRouteName.gena4, params})
-    }
+        id2: selects.value.length === 1 ? "-1" : selects.value[1].id,
+      };
+      router.push({ name: writeRouteName.gena4, params });
+    },
   },
   {
     key: "del",
     text: "删除",
     icon: "del",
-    onClick: () => delSelects()
+    onClick: () => delSelects(),
   },
-])
-
+]);
 
 watchEffect(() => {
   if (!selectMode.value) {
-    selects.value = []
+    selects.value = [];
   }
-})
+});
 
 const delPhotoRaw = (accidentPhoto = active.value) => {
-  const index = accidentPhotos.value.indexOf(accidentPhoto)
-  const reset = active.value ? accidentPhotos.value.indexOf(active.value) : -1
+  const index = accidentPhotos.value.indexOf(accidentPhoto);
+  const reset = active.value ? accidentPhotos.value.indexOf(active.value) : -1;
   if (~index) {
-    accidentPhotos.value.splice(index, 1)
+    accidentPhotos.value.splice(index, 1);
   }
   if (~reset) {
-    console.log(reset)
     if (reset >= accidentPhotos.value.length) {
       if (accidentPhotos.value.length) {
-        active.value = accidentPhotos.value[accidentPhotos.value.length - 1]
+        active.value = accidentPhotos.value[accidentPhotos.value.length - 1];
       } else {
-        active.value = null
+        active.value = null;
       }
     } else {
-      active.value = accidentPhotos.value[reset]
+      active.value = accidentPhotos.value[reset];
     }
   }
-}
+};
 
 const delPhoto = async (photo = active.value) => {
   if (await useConfirm(`确定要删除此数据?`)) {
-    delPhotoRaw(photo)
+    delPhotoRaw(photo);
   }
-}
+};
 const delSelects = async () => {
   if (await useConfirm(`确定要删除这${selects.value.length}项数据?`)) {
     while (selects.value.length) {
-      delPhotoRaw(selects.value[0])
-      selects.value.shift()
+      delPhotoRaw(selects.value[0]);
+      selects.value.shift();
     }
     if (!sortPhotos.value.length) {
-      selectMode.value = false
+      selectMode.value = false;
     }
   }
-}
+};
 const gotoDraw = () => {
   router.push({
     name: writeRouteName.graphic,
-    params: {mode: Mode.Photo, id: active.value.id, action: 'update'}
-  })
-}
+    params: { mode: Mode.Photo, id: active.value.id, action: "update" },
+  });
+};
 
 onDeactivated(() => {
-  active.value = null
-})
+  active.value = null;
+});
 </script>
 
 <style scoped lang="scss">
@@ -194,7 +202,7 @@ onDeactivated(() => {
   right: 0;
   bottom: 0;
   overflow-y: auto;
-  background: #2E2E2E;
+  background: #2e2e2e;
   padding: 25px 0;
 }
 
@@ -209,7 +217,6 @@ onDeactivated(() => {
   }
 }
 
-
 .menus {
   left: 50%;
   transform: translateX(-50%);
@@ -218,7 +225,7 @@ onDeactivated(() => {
 .fun-ctrl {
   color: #fff;
   font-size: 20px;
-  transition: color .3s ease;
+  transition: color 0.3s ease;
 
   .icon {
     position: absolute;
@@ -237,7 +244,6 @@ onDeactivated(() => {
   font-size: 16px;
 }
 
-
 .select-menus {
   left: 50%;
   transform: translateX(-50%);
@@ -257,7 +263,6 @@ onDeactivated(() => {
     .photo {
       flex: none;
       width: calc(calc(100% - 24px * 3) / 4);
-
     }
 
     &.max:after {
@@ -279,5 +284,4 @@ onDeactivated(() => {
     height: 200px;
   }
 }
-
 </style>

+ 20 - 26
src/views/photos/index.vue

@@ -56,6 +56,10 @@
     <template v-slot:foot>
       <ActionMenus class="menus" :menus="menus" dire="row" />
     </template>
+    <template v-slot:topRight>
+      <ui-icon type="share" @click="sharePhoto" />
+      <ui-icon type="del" @click="delPhoto(active)" />
+    </template>
   </FillSlide>
 </template>
 
@@ -95,34 +99,24 @@ const menus = [
     text: "照片标注",
     onClick: () => gotoDraw(Mode.Photo),
   },
-  {
-    key: "del",
-    icon: "del",
-    text: "删除",
-    onClick: () => delPhoto(),
-  },
-  {
-    key: "share",
-    icon: "share",
-    text: "分享",
-    onClick: genUseLoading(async () => {
-      // 如果没下载过相册则下载,通过文件名判断
-      if (!active.value.url.includes("img_")) {
-        const filename = `img_${formatDate(new Date(), "yyyyMMddhhmmss")}_${
-          active.value.meterPerPixel || 1
-        }_${new Date().getTime().toString().substring(8)}.jpg`;
+];
 
-        const img = await api.getFile(active.value.url);
-        const blob = await imageToBlob(img);
-        const url = await uploadImage(blob, filename);
-        await downloadImage(blob, filename);
-        active.value.url = url;
-      }
+const sharePhoto = async () => {
+  // 如果没下载过相册则下载,通过文件名判断
+  if (!active.value.url.includes("img_")) {
+    const filename = `img_${formatDate(new Date(), "yyyyMMddhhmmss")}_${
+      active.value.meterPerPixel || 1
+    }_${new Date().getTime().toString().substring(8)}.jpg`;
 
-      api.shareImage(active.value.url);
-    }),
-  },
-];
+    const img = await api.getFile(active.value.url);
+    const blob = await imageToBlob(img);
+    const url = await uploadImage(blob, filename);
+    await downloadImage(blob, filename);
+    active.value.url = url;
+  }
+
+  api.shareImage(active.value.url);
+};
 
 const selectMenus = [
   {

+ 30 - 20
src/views/roads/index.vue

@@ -51,7 +51,9 @@
       :getURL="(data) => data?.table?.url || data.url"
     >
       <template v-slot="{ data }">
-        <p v-if="currentType === TypeEnum.Table">{{ data.title || "默认标题" }}</p>
+        <p v-if="currentType === TypeEnum.Table">
+          {{ (data as any).title || "默认标题" }}
+        </p>
       </template>
     </Photos>
 
@@ -73,6 +75,10 @@
     <template v-slot:foot>
       <ActionMenus class="menus" :menus="menus" dire="row" />
     </template>
+    <template v-slot:topRight>
+      <ui-icon type="copy" @click="copyPhoto" />
+      <ui-icon type="del" @click="delPhoto(active)" />
+    </template>
   </FillSlide>
 </template>
 
@@ -117,19 +123,19 @@ const active = ref<RoadPhoto>();
 const selectMode = ref(false);
 const selects = ref<RoadPhoto[]>([]);
 const menus = computed(() => [
-  {
-    key: "copy",
-    icon: "copy",
-    text: "复制",
-    onClick: () => {
-      const copy = JSON.parse(JSON.stringify(active.value)) as RoadPhoto;
-      copy.id = getId();
-      copy.time = new Date().getTime();
-      roadPhotos.value.push(copy);
-      active.value = null;
-      // gotoDraw(copy);
-    },
-  },
+  // {
+  //   key: "copy",
+  //   icon: "copy",
+  //   text: "复制",
+  //   onClick: () => {
+  //     const copy = JSON.parse(JSON.stringify(active.value)) as RoadPhoto;
+  //     copy.id = getId();
+  //     copy.time = new Date().getTime();
+  //     roadPhotos.value.push(copy);
+  //     active.value = null;
+  //     // gotoDraw(copy);
+  //   },
+  // },
   {
     key: "tabulation",
     icon: "tabulation",
@@ -143,12 +149,6 @@ const menus = computed(() => [
     text: "修改",
     onClick: () => gotoDraw(),
   },
-  {
-    key: "del",
-    icon: "del",
-    text: "删除",
-    onClick: () => delPhoto(),
-  },
 ]);
 
 const selectMenus = [
@@ -160,6 +160,15 @@ const selectMenus = [
   },
 ];
 
+const copyPhoto = () => {
+  const copy = JSON.parse(JSON.stringify(active.value)) as RoadPhoto;
+  copy.id = getId();
+  copy.time = new Date().getTime();
+  roadPhotos.value.push(copy);
+  active.value = null;
+  // gotoDraw(copy);
+};
+
 watchEffect(() => {
   if (!selectMode.value) {
     selects.value = [];
@@ -197,6 +206,7 @@ const delPhotoRaw = (roadPhoto = active.value) => {
 
 const delPhoto = async (photo = active.value) => {
   if (await useConfirm(`确定要删除此数据?`)) {
+    console.log(photo);
     delPhotoRaw(photo);
   }
 };

+ 1 - 1
src/views/scene/TrackMeasure.vue

@@ -22,7 +22,7 @@ import { measureDisabledStack } from "@/hook/custom";
 import { useSDK } from "@/hook";
 
 const props = defineProps<{ onConfirm: (data: SuccessMeasureAtom) => void }>();
-const active = ref(true);
+const active = ref(false);
 const callback = () => {
   props.onConfirm(tempMeasures.value[0] as any);
   tempMeasures.value = [];

+ 8 - 6
src/views/scene/covers/measure.vue

@@ -43,11 +43,14 @@ watchEffect(() => {
     });
 
     if (baseLines.value.includes(props.data)) {
-      if (currentView.value) {
-        measure.value.show();
-      } else {
-        measure.value.hide();
-      }
+      console.log(measure.value.getPoints());
+      //   measure.value.show();
+
+      //   if (currentView.value) {
+      //     measure.value.show();
+      //   } else {
+      //     // measure.value.hide();
+      //   }
     }
   }
 });
@@ -56,7 +59,6 @@ const clickHandler = (ev) => {
   const $canvas = document.querySelector(".scene-canvas > canvas");
   if (!$canvas || !$canvas.contains(ev.target)) {
     emit("blur");
-    console.log("blur measure");
     measure.value.selected(false);
   }
 };

+ 183 - 0
src/views/scene/covers/range.vue

@@ -0,0 +1,183 @@
+<template>
+  <div class="right-range floating-range" v-if="rangeSetting">
+    <span
+      class="fun-ctrl"
+      @click="
+        value =
+          value - rangeSetting.step > rangeSetting.min ? value - rangeSetting.step : value
+      "
+    >
+      -
+    </span>
+    <div class="range-content">
+      <div class="range-layout">
+        <ui-input
+          type="range"
+          v-model="value"
+          v-bind="rangeSetting"
+          :moveCallback="changeRange"
+          :ctrl="false"
+          :input="false"
+          width="100%"
+        />
+        <span
+          class="num"
+          :style="{
+            left: `${
+              ((value - rangeSetting.min) / (rangeSetting.max - rangeSetting.min)) * 100
+            }%`,
+          }"
+        >
+          {{ parseInt(value.toString()) + rangeSetting.unit }}
+        </span>
+      </div>
+    </div>
+    <span
+      class="fun-ctrl"
+      @click="
+        value =
+          value + rangeSetting.step > rangeSetting.min ? value + rangeSetting.step : value
+      "
+      >+</span
+    >
+  </div>
+</template>
+
+<script setup lang="ts">
+import { Pos } from "@/sdk";
+import { computed } from "vue";
+import { sceneSeting } from "@/store/sceneSeting";
+
+const props = defineProps<{ rangeKey: string }>();
+const sceneRangeSetting = {
+  top: { min: 0, max: 10, step: 0.5, unit: "米" },
+  scale: { min: 0, max: 100, step: 0.1, unit: "%" },
+  rotate: { min: -180, max: 180, step: 1, unit: "°" },
+};
+const rangeSetting = computed(() => sceneRangeSetting[props.rangeKey]);
+
+if (props.rangeKey === "reset") {
+}
+
+const value = computed({
+  get() {
+    return props.rangeKey && sceneSeting.value && sceneSeting.value[props.rangeKey]
+      ? sceneSeting.value[props.rangeKey]
+      : 0;
+  },
+  set(val) {
+    if (!sceneSeting.value) {
+      sceneSeting.value = {} as any;
+    }
+    sceneSeting.value[props.rangeKey] = val;
+  },
+});
+
+const changeRange = (sp: Pos, cp: Pos, info: { start: number; locusWidth: number }) =>
+  info.start + (sp.y - cp.y) / info.locusWidth;
+</script>
+
+<style lang="scss" scoped>
+.floating-range {
+  margin-left: 20px;
+  transform: translateY(-50%);
+  background: none;
+  border-radius: 16px;
+  display: flex;
+  align-items: center;
+  justify-self: center;
+
+  .fun-ctrl {
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    margin: 0 24px;
+    font-size: 24px;
+    transform: rotate(-90deg);
+  }
+  .range-content {
+    display: flex;
+    align-items: center;
+    width: 290px;
+    height: 16px;
+    .range-layout {
+      height: 100%;
+      width: 100%;
+      position: relative;
+    }
+
+    .num {
+      position: absolute;
+      color: #fff;
+      top: 0;
+      margin-top: -32px;
+      transform: translateY(-100%) translateX(calc(-50% - 10px)) rotate(90deg);
+      background: #2e2e2e;
+      border-radius: 8px;
+      padding: 10px;
+      font-size: 20px;
+      pointer-events: none;
+      white-space: nowrap;
+
+      &::before {
+        content: "";
+        border: 8px solid transparent;
+        position: absolute;
+        right: -15px;
+        border-left-color: #2e2e2e;
+        top: 50%;
+        transform: translateY(-50%);
+      }
+    }
+  }
+}
+
+.right-range {
+  position: absolute;
+  padding: 0;
+  border-radius: 20px;
+  right: 10px;
+  top: 50%;
+  z-index: 2;
+  transform: translateX(40%) rotate(-90deg);
+
+  .range-content {
+    align-items: center;
+
+    span {
+      margin: 0 10px;
+      font-size: 20px;
+      flex: none;
+      color: #fff;
+    }
+  }
+}
+</style>
+
+<style lang="scss">
+.floating-range .ui-input .range {
+  --height: 16px !important;
+  --colors-normal-back: rgba(255, 255, 255, 0.3);
+
+  .range-content {
+    --slideSize: calc(var(--height) + 16px) !important;
+
+    &::before {
+      border-start-end-radius: 8px;
+      border-end-end-radius: 8px;
+    }
+  }
+
+  .range-slide {
+    width: 16px;
+    height: 32px;
+    border-radius: 8px;
+  }
+  .range-locus {
+    width: calc(100% - 16px);
+  }
+}
+.opacity-range {
+  margin-left: 70px;
+}
+</style>

+ 87 - 65
src/views/scene/index.vue

@@ -3,7 +3,13 @@
     <template v-slot:header>
       <div class="photos-header">
         <div class="left">
-          <ui-icon class="back-icon" type="return" ctrl style="margin-right: 10px" @click="back" />
+          <ui-icon
+            class="back-icon"
+            type="return"
+            ctrl
+            style="margin-right: 10px"
+            @click="back"
+          />
           <span> 案件 </span>
         </div>
       </div>
@@ -14,13 +20,28 @@
         <div class="info-top-left" :class="{ full: viewStatus }">
           <Container @loaded="loaded = true" />
           <template v-if="loaded && !trackMode">
-            <Menus v-if="viewStatus" @enter-child="childPage = true" @leave-child="childPage = false" />
-            <BasePoints v-if="currentView" />
+            <Menus
+              v-if="viewStatus"
+              @active="(data) => (activeMenuKeys = data)"
+              @enter-child="childPage = true"
+              @leave-child="childPage = false"
+            />
+            <!-- v-if="currentView" -->
+            <BasePoints />
             <FixPoints />
             <Measures />
             <Photo />
+            <Range
+              v-if="activeMenuKeys[0] === 'range' && activeMenuKeys.length > 1"
+              :rangeKey="activeMenuKeys.slice(1).join(':')"
+            />
             <!-- <ButtonPane class="back fun-ctrl" size="48" @click="router.push('/scene')" v-if="!childPage"> -->
-            <ButtonPane class="back fun-ctrl" :size="viewStatus ? 64 : 48" @click="onScale" v-if="!childPage">
+            <ButtonPane
+              class="back fun-ctrl"
+              :size="viewStatus ? 64 : 48"
+              @click="onScale"
+              v-if="!childPage"
+            >
               <ui-icon :type="viewStatus ? 'screen_c' : 'screen_f'" class="icon" />
             </ButtonPane>
             <Mode />
@@ -44,13 +65,17 @@
             <textarea class="info-textarea"></textarea>
           </div>
           <div class="info-btn">
-            <div class="right-btn" @click="router.push('/roads?back=1')">现场绘图({{ sceneSortPhotos.length }})</div>
-            <div class="right-btn" @click="router.push('/accidents?back=1')">事故照片({{ accodentSortPhotos.length }})</div>
+            <div class="right-btn" @click="router.push('/roads?back=1')">
+              现场绘图({{ sceneSortPhotos.length }})
+            </div>
+            <div class="right-btn" @click="router.push('/accidents?back=1')">
+              事故照片({{ accodentSortPhotos.length }})
+            </div>
           </div>
         </div>
       </div>
       <div class="info-bottom" :class="{ full: viewStatus }">
-        <div v-for="(i, index) in list" @click="goItem(i)">
+        <div v-for="(i, index) in list" @click="router.push(`/tables/${i.type}`)">
           <ui-icon :type="i.icon"></ui-icon>
           <span> {{ i.name }}</span>
         </div>
@@ -60,41 +85,44 @@
 </template>
 
 <script lang="ts" setup>
-import MainPanel from '@/components/main-panel/index.vue';
-import Header from '@/components/photos/header.vue';
-import Container from './container.vue';
-import Mode from './mode.vue';
-import Menus from './menus/pane.vue';
-import BasePoints from '@/views/scene/covers/basePoints.vue';
-import FixPoints from '@/views/scene/covers/fixPoints.vue';
-import Measures from '@/views/scene/covers/measures.vue';
-import Photo from './photo.vue';
-import ButtonPane from '@/components/button-pane/index.vue';
-import { customMap, disabledMap, useSDK } from '@/hook';
-import customSetup from '../../hook/custom';
-import UiIcon from '@/components/base/components/icon/index.vue';
-import { ref, watchEffect, computed, onMounted, onActivated, nextTick } from 'vue';
-import { back } from '@/store/sync';
-import { trackMode } from '@/views/scene/trackMeasureWidth';
-import { currentView } from './currentScene';
-import { api } from '@/store/sync';
-import { router, writeRouteName } from '@/router';
-import { roadPhotos, RoadPhoto } from '@/store/roadPhotos';
-import { types, accidentPhotos, AccidentPhoto } from '@/store/accidentPhotos';
-import { photos } from '@/store/photos';
-import { title } from '@/store/setup';
+import MainPanel from "@/components/main-panel/index.vue";
+import Container from "./container.vue";
+import Mode from "./mode.vue";
+import Menus from "./menus/pane.vue";
+import BasePoints from "@/views/scene/covers/basePoints.vue";
+import FixPoints from "@/views/scene/covers/fixPoints.vue";
+import Measures from "@/views/scene/covers/measures.vue";
+import Photo from "./photo.vue";
+import ButtonPane from "@/components/button-pane/index.vue";
+import Range from "./covers/range.vue";
+import { disabledMap, useSDK } from "@/hook";
+import customSetup from "../../hook/custom";
+import UiIcon from "@/components/base/components/icon/index.vue";
+import { ref, watchEffect, computed, onMounted, onActivated, nextTick } from "vue";
+import { back } from "@/store/sync";
+import { trackMode } from "@/views/scene/trackMeasureWidth";
+import { currentView } from "./currentScene";
+import { router } from "@/router";
+import { roadPhotos } from "@/store/roadPhotos";
+import { types, accidentPhotos } from "@/store/accidentPhotos";
+
 const layoutRef = ref(null);
+const activeMenuKeys = ref<string[]>([]);
 const accodentSortPhotos = computed(() => {
   const photos = [...accidentPhotos.value];
   return photos.sort((a, b) => types.indexOf(a.type) - types.indexOf(b.type));
 });
-const scenetTitle = computed(() => title);
+
 const enum TypeEnum {
   Draw,
   Table,
 }
 const currentType = ref(TypeEnum.Draw);
-const sceneSortPhotos = computed(() => roadPhotos.value.filter((item) => (currentType.value === TypeEnum.Draw ? !item.table : !!item.table)).reverse());
+const sceneSortPhotos = computed(() =>
+  roadPhotos.value
+    .filter((item) => (currentType.value === TypeEnum.Draw ? !item.table : !!item.table))
+    .reverse()
+);
 
 const loaded = ref(false);
 const childPage = ref(false);
@@ -110,67 +138,61 @@ const viewStatus = ref(false);
 const onScale = () => {
   viewStatus.value = !viewStatus.value;
 };
-const goItem = (item) => {
-  // if (item.id == 8) {
-  //   return;
-  // }
-  // router.push(`/demo/${item.id}`);
-  router.push(`/tables/${item.type}`);
-};
+
 const list = ref([
   {
     id: 1,
-    icon: 'prospect',
-    name: '勘查笔录',
-    type: 'explorate',
+    icon: "prospect",
+    name: "勘查笔录",
+    type: "explorate",
   },
   {
     id: 2,
-    icon: 'inquiry',
-    name: '询问笔录',
-    type: 'ask?type=1',
+    icon: "inquiry",
+    name: "询问笔录",
+    type: "ask?type=1",
   },
   {
     id: 3,
-    icon: 'question',
-    name: '讯问笔录',
-    type: 'ask?type=2',
+    icon: "question",
+    name: "讯问笔录",
+    type: "ask?type=2",
   },
   {
     id: 4,
-    icon: 'accident',
-    name: '事故认定',
-    type: 'identification',
+    icon: "accident",
+    name: "事故认定",
+    type: "identification",
   },
   {
     id: 5,
-    icon: 'blood',
-    name: '血样登记表',
-    type: 'extract',
+    icon: "blood",
+    name: "血样登记表",
+    type: "extract",
   },
   {
     id: 6,
-    icon: 'items',
-    name: '遗留物品清单',
-    type: 'legacy',
+    icon: "items",
+    name: "遗留物品清单",
+    type: "legacy",
   },
   {
     id: 7,
-    icon: 'authorization',
-    name: '授权委托书',
-    type: 'author',
+    icon: "authorization",
+    name: "授权委托书",
+    type: "author",
   },
   {
     id: 8,
-    icon: 'law',
-    name: '法律法规',
-    type: 'law',
+    icon: "law",
+    name: "法律法规",
+    type: "law",
   },
 ]);
 onMounted(() => {
   var screenHeight = document.body.clientHeight;
-  layoutRef.value.style.height = screenHeight + 'px';
-  document.body.style.height = screenHeight + 'px';
+  layoutRef.value.style.height = screenHeight + "px";
+  document.body.style.height = screenHeight + "px";
 });
 onActivated(async () => {
   await nextTick();

+ 2 - 2
src/views/scene/menus/actions.ts

@@ -117,7 +117,7 @@ const menuActions = {
           hide = null;
         }
       },
-      true
+      false
     );
     return () => {
       onDestroy();
@@ -139,7 +139,7 @@ const menuActions = {
           hide = null;
         }
       },
-      true
+      false
     );
     return () => {
       onDestroy();

+ 41 - 8
src/views/scene/menus/menus.ts

@@ -10,10 +10,12 @@ import { baseLines } from "@/store/baseLine";
 import { fixPoints } from "@/store/fixPoint";
 import { basePoints } from "@/store/basePoint";
 import { list } from "@/store/measure";
+import { sceneSeting } from "@/store/sceneSeting";
 
 export type MenuRaw = {
   key: string;
   text: string;
+  single?: boolean;
   continued?: boolean;
   defaultSelect?: true | (() => boolean);
   icon?: string;
@@ -66,21 +68,17 @@ export const menus: MenuRaw[] = [
     children: [
       {
         icon: "line_h",
-        continued: true,
         text: "水平",
-        defaultSelect: true,
         key: menuEnum.MEASURE_ROW,
       },
       {
         icon: "line_v",
-        continued: true,
         disabled: () => vView.value,
         text: "垂直",
         key: menuEnum.MEASURE_COLUMN,
       },
       {
         icon: "line_f",
-        continued: true,
         text: "自由",
         key: menuEnum.MEASURE_FREE,
       },
@@ -92,7 +90,6 @@ export const menus: MenuRaw[] = [
     key: "fixPointMenu",
     children: [
       {
-        defaultSelect: true,
         icon: "point_a",
         text: "固定点",
         key: menuEnum.FIX_POINT,
@@ -104,6 +101,7 @@ export const menus: MenuRaw[] = [
     text: "基准",
     key: "baseLineOrPoint",
     onClick() {
+      return;
       const sdk = useSDK();
       const doms = document.querySelectorAll(
         "#navCube, #home"
@@ -130,14 +128,47 @@ export const menus: MenuRaw[] = [
         disabled: () => !!baseLines.value.length,
       },
       {
-        defaultSelect: true,
         icon: "point",
-        continued: true,
         text: "基准点",
         key: menuEnum.BASE_POINT,
       },
     ],
   },
+  {
+    icon: "standard",
+    text: "范围",
+    key: "range",
+    children: [
+      {
+        icon: "standard",
+        text: "离地高度",
+        key: "top",
+      },
+      {
+        icon: "standard",
+        text: "水平缩放",
+        key: "scale",
+      },
+      {
+        icon: "standard",
+        text: "水平旋转",
+        key: "rotate",
+      },
+      {
+        icon: "standard",
+        text: "恢复",
+        key: "reset",
+        single: true,
+        onClick() {
+          sceneSeting.value = {
+            top: 4,
+            scale: 100,
+            rotate: 0,
+          };
+        },
+      },
+    ],
+  },
 ];
 
 export const generateMixMenus = <T extends {}, K extends keyof MenuRaw>(
@@ -156,7 +187,9 @@ export const generateMixMenus = <T extends {}, K extends keyof MenuRaw>(
           itemActiveKey.value = null;
         } else {
           menu.onClick && menu.onClick();
-          itemActiveKey.value = menu.key;
+          if (!menu.single) {
+            itemActiveKey.value = menu.key;
+          }
         }
       },
       () => itemActiveKey.value

+ 54 - 25
src/views/scene/menus/pane.vue

@@ -1,32 +1,61 @@
 <template>
-  <ActionMenus v-if="!store.child.value" class="menus abc" :class="{ level: level === 2 }" :menus="store.menus as any" :active-key="store.activeMenuKey.value" dire="column" />
-  <scene-menus :parentKey="store.activeMenuKey.value" v-if="store.child.value" @back="store.child.value = null" :menus="store.child.value as MenuRaw[]" :level="level + 1" />
+  <ActionMenus
+    v-if="!store.child.value"
+    class="menus abc"
+    :class="{ level: level === 2 }"
+    :menus="store.menus"
+    :active-key="store.activeMenuKey.value"
+    dire="column"
+  />
+  <scene-menus
+    :parentKey="store.activeMenuKey.value"
+    @active="(data) => (childActive = data)"
+    v-if="store.child.value"
+    @back="store.child.value = null"
+    :menus="store.child.value"
+    :level="level + 1"
+  />
 </template>
 
 <script lang="ts" setup>
-import ActionMenus from '@/components/group-button/index.vue';
-import { generateMixMenus, MenuRaw, menus, findMenuByKey } from './menus';
-import { joinActions } from './actions';
-import { computed, onMounted, onUnmounted, ref, watchEffect } from 'vue';
-import { disabledMap, laserModeStack } from '@/hook';
-import { Mode } from '@/sdk';
+import ActionMenus from "@/components/group-button/index.vue";
 
-const props = withDefaults(defineProps<{ menus?: MenuRaw[]; level?: number; parentKey?: string }>(), { level: 1 });
-console.error(props);
+import { generateMixMenus, MenuRaw, menus, findMenuByKey } from "./menus";
+import { joinActions } from "./actions";
+import { computed, onMounted, onUnmounted, ref, watchEffect } from "vue";
+import { disabledMap, laserModeStack } from "@/hook";
+import { Mode } from "@/sdk";
+
+const props = withDefaults(
+  defineProps<{ menus?: MenuRaw[]; level?: number; parentKey?: string }>(),
+  { level: 1 }
+);
 const emit = defineEmits<{
-  (e: 'back'): void;
-  (e: 'enterChild'): void;
-  (e: 'leaveChild'): void;
+  (e: "back"): void;
+  (e: "active", data: string[]): void;
+  (e: "enterChild"): void;
+  (e: "leaveChild"): void;
 }>();
 const backMenu = {
-  icon: 'return',
-  text: '',
-  key: 'back',
-  onClick: () => emit('back'),
+  icon: "return",
+  text: "",
+  key: "back",
+  onClick: () => emit("back"),
 };
 
 const menusMix = computed(() => (props.level === 1 ? menus : [backMenu, ...props.menus]));
-const store = generateMixMenus('children', (m) => m, menusMix.value);
+const store = generateMixMenus("children", (m) => m, menusMix.value);
+const childActive = ref<string[]>([]);
+
+watchEffect(() => {
+  const currentActive = store.activeMenuKey.value || store.itemActiveKey.value;
+  console.log(props.level, currentActive);
+  if (currentActive) {
+    emit("active", [currentActive, ...childActive.value]);
+  } else {
+    emit("active", []);
+  }
+});
 
 watchEffect(() => {
   const menu = findMenuByKey(store.itemActiveKey.value as any);
@@ -50,9 +79,9 @@ if (props.level === 1) {
   });
   watchEffect(() => {
     if (store.child.value) {
-      emit('enterChild');
+      emit("enterChild");
     } else {
-      emit('leaveChild');
+      emit("leaveChild");
     }
   });
 }
@@ -60,14 +89,14 @@ if (props.level === 1) {
 const stopWatch = joinActions(store.itemActiveKey);
 onUnmounted(() => {
   stopWatch();
-  if (props.parentKey === 'measure') {
+  if (props.parentKey === "measure") {
     laserModeStack.pop();
   }
 });
 onMounted(() => {
   if (props.level > 1) {
     const defaultMenu = props.menus.find((menu) => {
-      if (typeof menu.defaultSelect === 'function') {
+      if (typeof menu.defaultSelect === "function") {
         return menu.defaultSelect();
       } else {
         return menu.defaultSelect;
@@ -75,14 +104,14 @@ onMounted(() => {
     });
     store.itemActiveKey.value = defaultMenu?.key;
     // store.activeMenuKey.value =
-    if (props.parentKey === 'measure') {
+    if (props.parentKey === "measure") {
       laserModeStack.push(ref(Mode.cloud));
     }
   }
 });
 </script>
 <script lang="ts">
-export default { name: 'scene-menus' };
+export default { name: "scene-menus" };
 </script>
 
 <style lang="scss" scoped>
@@ -110,7 +139,7 @@ export default { name: 'scene-menus' };
 
   &:after {
     position: absolute;
-    content: '';
+    content: "";
     height: 1px;
     left: 10px;
     right: 10px;

+ 8 - 1
src/views/scene/mode.vue

@@ -33,7 +33,7 @@ const menus = computed(() =>
   tabs.map((tab) => ({
     icon: tab.mode === customMap.mode ? tab.activeIcon : tab.icon,
     key: tab.mode,
-    onClick: () => (customMap.mode = tab.mode),
+    onClick: () => (customMap.mode = tab.mode === Mode.pano ? Mode.cloud : Mode.pano),
   }))
 );
 customMap.mode = Number(localStorage.getItem(key)) || Mode.pano;
@@ -60,6 +60,13 @@ watch(
     width: 64px;
     margin-right: 0 !important;
     border-radius: 32px;
+    display: none;
+
+    &.active {
+      display: flex;
+      background: none;
+      color: inherit;
+    }
   }
 }
 </style>

+ 22 - 6
tsconfig.json

@@ -10,16 +10,32 @@
     "resolveJsonModule": true,
     "isolatedModules": true,
     "esModuleInterop": true,
-    "lib": ["ESNext", "DOM"],
+    "lib": [
+      "ESNext",
+      "DOM"
+    ],
     "skipLibCheck": true,
     "allowJs": true,
     "baseUrl": ".",
     "paths": {
-      "@kankan/components/*": ["src/components/base/*"],
-      "@/*": ["src/*"]
+      "@kankan/components/*": [
+        "src/components/base/*"
+      ],
+      "@/*": [
+        "src/*"
+      ]
     },
     "noEmit": true
   },
-  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
-  "references": [{ "path": "./tsconfig.node.json" }]
-}
+  "include": [
+    "src/**/*.ts",
+    "src/**/*.d.ts",
+    "src/**/*.tsx",
+    "src/**/*.vue"
+  ],
+  "references": [
+    {
+      "path": "./tsconfig.node.json"
+    }
+  ]
+}