bill 2 лет назад
Родитель
Сommit
7b6f780945

+ 224 - 0
src/assets/public.scss

@@ -131,4 +131,228 @@ samp {
 @font-face {
   font-family: "st";
   src        : url("./font/simsun.ttc") format("truetype");
+}
+
+
+
+:root.light {
+  body {
+    --editor-head-back   : rgba(255, 255, 255, 1);
+    --editor-menu-back   : rgba(255, 255, 255, 1);
+    --colors-primary-fill: 0, 0, 0;
+    --editor-men-color   : rgb(0, 0, 0);
+
+    .ui-input .range .range-locus .range-slide {
+      background-color: rgba(0, 0, 0, 1)
+    }
+
+    .ui-input {
+      --base-border-color: rgba(0, 0, 0, 0.2);
+    }
+
+    .select-replace {
+      --colors-content-color: #161A1A;
+      background            : #fff;
+    }
+
+    .back-icon {
+      border: 1px solid #CCCCCC;
+    }
+
+    .fun-ctrl,
+    .fun-ctrl:hover,
+    .menu {
+      &:not(.autonomous) {
+        color: #000 !important;
+      }
+    }
+
+    .menu.border:after {
+      border-bottom-color: rgba(230, 230, 230, 1);
+    }
+
+
+    .menu.active {
+      background-color: rgba(47, 143, 255, 0.50);
+    }
+
+    .scene-mode-tabs {
+      .menu.active {
+        background-color: #fff;
+      }
+    }
+
+    .occupying,
+    .occupying,
+    .photo img {
+      background: #000;
+    }
+
+    .photos-layout {
+      background: #fff;
+    }
+
+    .photos-header {
+      .ui-button.normal {
+        color : #000;
+        border: 1px solid rgba(0, 0, 0, 0.7);
+      }
+    }
+
+    .strengthen {
+      border-color: #ccc;
+    }
+
+    .strengthen-right {
+      border-right-color: #ccc;
+
+    }
+
+    .strengthen-bottom {
+      border-bottom-color: #ccc;
+    }
+
+    .photo-select {
+      border: 1px solid #ccc;
+
+      i {
+        color: #000;
+      }
+    }
+
+    .fill-slide {
+
+      .foot,
+      .header {
+        background-color: #fff;
+        color           : rgb(22, 24, 26);
+
+        .top-right {
+          color: rgb(22, 24, 26);
+        }
+      }
+
+      .foot .menus .menu {
+        background-color: rgb(22, 24, 26);
+        color           : #fff !important
+      }
+
+    }
+
+    .PhotoSlider__Backdrop {
+      // background: #fff !important;
+    }
+
+    .graphic-child-menus {
+      .header {
+        color: #000;
+      }
+
+      .type-menu h2 {
+        color: rgba(0, 0, 0, 1);
+      }
+
+      .menu .icon {
+        background-color: #fff;
+      }
+    }
+
+    .geo-teleport .font-size {
+      color: #000;
+    }
+
+
+    .save-file.save {
+      background-color: #fff !important;
+      color           : #000;
+    }
+
+    .ui-dialog {
+      color: #000;
+    }
+
+    .ui-dialog__box {
+      background-color: #fff;
+
+      .ui-input .input .input-div,
+      .ui-input .input textarea,
+      .ui-input .input input {
+        --colors-content-color: #000;
+      }
+
+      header {
+        color: #000;
+      }
+
+      .select-boxs span {
+        color: #000;
+
+        &:not(.active) {
+          background-color: rgba(0, 0, 0, 0.1);
+        }
+      }
+
+      .ui-button.submit {
+        color     : #000;
+        border    : 1px solid rgba(0, 0, 0, 0);
+        background: rgba(0, 0, 0, 0.1) !important;
+      }
+    }
+
+    .info-layout {
+      background: #fff;
+
+      .info-bottom>div {
+        background: rgba(0, 0, 0, 0.1);
+        color     : #000;
+      }
+
+      .info-top .info-top-right {
+        color: rgba(0, 0, 0, 0.8);
+
+        .right-btn {
+          background: rgba(0, 0, 0, 0.1);
+
+        }
+
+        .text-item .info-textarea,
+        .input-item input {
+          color     : rgba(0, 0, 0, 0.8);
+          background: rgba(0, 0, 0, 0);
+          border    : 1px solid rgba(0, 0, 0, 0.5);
+        }
+
+      }
+    }
+
+    .download-btn {
+      border    : 1px solid #3a3d3d !important;
+      color     : #3a3d3d !important;
+      background: none !important;
+    }
+  }
+}
+
+:root.dark {
+  body {
+    --editor-menu-back   : #161A1A;
+    --editor-head-back   : #252828;
+    --colors-primary-fill: 255, 255, 255;
+
+    .fun-ctrl,
+    .fun-ctrl:hover,
+    .menu {
+      &:not(.autonomous) {
+        color: #fff !important;
+      }
+    }
+
+    .menu.border:after {
+      border-bottom-color: rgba(255, 255, 255, 0.2);
+    }
+
+    .menu.active {
+      background-color: rgba(255, 255, 255, 0.1);
+    }
+  }
 }

+ 23 - 24
src/components/base/assets/scss/_base-vars.scss

@@ -1,39 +1,38 @@
-
 :root {
-  --colors-primary-fill: 255, 255, 255;
+  --colors-primary-fill     : 255, 255, 255;
   --colors-primary-base-fill: 50, 144, 255;
-  --colors-primary-base: rgb(var(--colors-primary-base-fill));
-  --colors-primary-hover: rgba(var(--colors-primary-base-fill), 0.8);
-  --colors-primary-click: #005046;
+  --colors-primary-base     : rgb(var(--colors-primary-base-fill));
+  --colors-primary-hover    : rgba(var(--colors-primary-base-fill), 0.8);
+  --colors-primary-click    : #005046;
 
-  --colors-color: #999;
-  --colors-border-color: rgba(var(--colors-primary-fill), 0.16);
+  --colors-color        : #999;
+  --colors-border-color : rgba(var(--colors-primary-fill), 0.16);
   --colors-content-color: rgb(--colors-primary-fill);
 
 
-  --colors-normal-back: rgba(var(--colors-primary-fill), 0.1);
-  --colors-normal-base: rgba(var(--colors-primary-fill), 0.7);
+  --colors-normal-back : rgba(var(--colors-primary-fill), 0.1);
+  --colors-normal-base : rgba(var(--colors-primary-fill), 0.7);
   --colors-normal-hover: rgba(var(--colors-primary-fill), 1);
   --colors-normal-click: var(--colors-primary-click);
 
-  --colors-normal-fill-back: var(--colors-normal-back);
-  --colors-normal-fill-base: var(--colors-normal-base);
+  --colors-normal-fill-back : var(--colors-normal-back);
+  --colors-normal-fill-base : var(--colors-normal-base);
   --colors-normal-fill-hover: var(--colors-normal-hover);
   --colors-normal-fill-click: var(--colors-primary-click);
 
   --colors-error-fill: 250, 63, 72;
 
-  --small-size: 12px;
+  --small-size : 12px;
   --medium-size: 14px;
-  --big-size: 16px;
+  --big-size   : 16px;
 
 
   // 正常
   --color-main-normal: var(--colors-primary-base);
   // 悬停
-  --color-main-hover: var(--colors-primary-hover);
+  --color-main-hover : var(--colors-primary-hover);
   // 点击
-  --color-main-focus: var(--colors-primary-click);
+  --color-main-focus : var(--colors-primary-click);
 
 
   --editor-head-filter: blur(0px);
@@ -41,15 +40,15 @@
 
   --editor-head-back: #252828;
 
-  --editor-menu-filter: var(--editor-head-filter);
-  --editor-menu-width: 64px;
-  --editor-menu-left: 0px;
-  --editor-menu-right: 0px;
-  --editer-menu-fill: 27, 27, 28;
-  --editor-menu-back: #161A1A;
+  --editor-menu-filter     : var(--editor-head-filter);
+  --editor-menu-width      : 64px;
+  --editor-menu-left       : 0px;
+  --editor-menu-right      : 0px;
+  --editer-menu-fill       : 27, 27, 28;
+  --editor-menu-back       : #161A1A;
   --editor-menu-active-back: rgba(var(--colors-primary-fill), 0.06);
-  --editor-men-color: rgba(255,255,255,1);
+  --editor-men-color       : rgba(255, 255, 255, 1);
 
   --editor-toolbox-width: 340px;
-  --editor-toolbox-back: var(--editor-menu-back);
-}
+  --editor-toolbox-back : var(--editor-menu-back);
+}

+ 12 - 3
src/components/group-button/index.vue

@@ -6,7 +6,7 @@
       class="menu"
       :style="menuStyle"
       :class="{
-        active: activeKey === menu.key,
+        active: activeKey === menu.key && !menu.switch,
         dire,
         disabled: disabledMap[menu.key],
         border: menu.border,
@@ -17,7 +17,12 @@
       <template v-if="$slots.default">
         <slot :data="menu" />
       </template>
-      <ui-icon :type="menu.icon || 'close'" class="icon" v-else />
+      <ui-icon :type="menu.icon || 'close'" class="icon" v-else-if="!menu.switch" />
+      <Switch
+        :select="menu.switchValue.get()"
+        @update:select="(val) => menu.switchValue.set(val)"
+        v-else
+      />
 
       <p v-if="menu.text">{{ menu.text }}</p>
     </div>
@@ -27,10 +32,13 @@
 <script setup lang="ts">
 import ButtonPane from "@/components/button-pane/index.vue";
 import UiIcon from "@/components/base/components/icon/index.vue";
-import { computed } from "vue";
+import Switch from "@/components/switch/index.vue";
+import { Ref, computed } from "vue";
 
 type Menu = {
   key: any;
+  switch?: boolean;
+  switchValue: Ref<boolean>;
   text?: string;
   hide?: boolean;
   border?: boolean;
@@ -48,6 +56,7 @@ const props = withDefaults(
   }>(),
   { dire: "row", size: 64 }
 );
+console.log(props.menus);
 
 const disabledMap = computed(() => {
   const map = {};

+ 54 - 0
src/components/switch/index.vue

@@ -0,0 +1,54 @@
+<template>
+  <span
+    class="switch-layout"
+    @click="$emit('update:select', !select)"
+    :class="{ select }"
+  >
+    <span class="switch-content"></span>
+  </span>
+</template>
+
+<script setup lang="ts">
+defineProps<{ select: boolean }>();
+defineEmits<{ (e: "update:select", v: boolean): void }>();
+</script>
+
+<style lang="scss">
+.switch-layout {
+  display: inline-block;
+  position: relative;
+  --width: 44px;
+  --size: 24px;
+
+  width: var(--width);
+  height: var(--size);
+  transition: all 0.3s ease;
+  border-radius: calc(var(--size) / 2);
+
+  &.select {
+    background-color: var(--colors-primary-base);
+
+    .switch-content {
+      left: calc(var(--width) - var(--size));
+    }
+  }
+  &:not(.select) {
+    background-color: #ccc;
+
+    .switch-content {
+      left: 0;
+    }
+  }
+
+  .switch-content {
+    position: absolute;
+    width: var(--size);
+    height: var(--size);
+    border-radius: 50%;
+    background-color: #fff;
+    top: 0;
+
+    transition: all 0.3s ease;
+  }
+}
+</style>

+ 7 - 8
src/hook/custom/preset.ts

@@ -1,8 +1,8 @@
 import { Mode, PointInfo, Pos } from "@/sdk/types";
 import { stackFactory, Stack, fastStacksValue, os } from "@/utils/vue";
 import { Ref, ref } from "vue";
-import {FixPoint} from "@/store/fixPoint";
-import {BasePoint} from "@/store/basePoint";
+import { FixPoint } from "@/store/fixPoint";
+import { BasePoint } from "@/store/basePoint";
 
 export enum DisabledCom {
   Search = "search",
@@ -22,8 +22,8 @@ export enum DisabledCom {
   floors = "floors",
   lmenu = "lmenu",
   autoLeave = "autoLeave",
-  Mode = 'mode',
-  photo = 'photo'
+  Mode = "mode",
+  photo = "photo",
 }
 
 export const searchDisabledStack = stackFactory(ref(false));
@@ -44,7 +44,7 @@ export const floorsDisabled = stackFactory(ref(false));
 export const lmenuDisabled = stackFactory(ref(false));
 export const autoLeaveDisabled = stackFactory(ref(false));
 export const modeDisabled = stackFactory(ref(false));
-export const photoDisabled= stackFactory(ref(false));
+export const photoDisabled = stackFactory(ref(false));
 
 export const disabledMapStack = {
   [DisabledCom.Search]: searchDisabledStack,
@@ -60,7 +60,6 @@ export const disabledMapStack = {
   [DisabledCom.Save]: saveDisabledStack,
   [DisabledCom.LaserLoading]: laserLoadingDisabled,
   [DisabledCom.HotInfo]: hotInfoDisabledStack,
-  [DisabledCom.HotInfo]: hotInfoDisabledStack,
   [DisabledCom.tool]: toolDisabled,
   [DisabledCom.floors]: floorsDisabled,
   [DisabledCom.lmenu]: lmenuDisabled,
@@ -87,7 +86,7 @@ export enum CustomCom {
   boxWidth = "boxWidth",
   autoMarginLeft = "autoMarginLeft",
   fullCtrl = "fullCtrl",
-  trackMeasureMode = "trackMeasureMode"
+  trackMeasureMode = "trackMeasureMode",
 }
 
 export enum RightMenuEum {
@@ -146,7 +145,7 @@ export const customMapStack = {
   [CustomCom.boxWidth]: boxWidthStack,
   [CustomCom.autoMarginLeft]: autoSysViewLeftStack,
   [CustomCom.fullCtrl]: controlFullStack,
-  [CustomCom.trackMeasureMode]: trackMeasureModeStack
+  [CustomCom.trackMeasureMode]: trackMeasureModeStack,
 };
 export type CustomMapStack = typeof customMapStack;
 

+ 1 - 0
src/hook/useParams.ts

@@ -14,6 +14,7 @@ export type Params = {
   unit?: string;
   kankan?: boolean;
   v?: string;
+  theme?: string;
   flyPose?: string;
   serve_link?: string;
 };

+ 5 - 1
src/main.ts

@@ -1,5 +1,5 @@
 import VConsole from "vconsole";
-import { useParams } from "@/hook/useParams";
+import params, { useParams } from "@/hook/useParams";
 
 if (useParams().console === "true") {
   new VConsole();
@@ -28,4 +28,8 @@ app.use(Components);
 
 app.mount("#app");
 
+if (params.theme) {
+  document.documentElement.classList.add(params.theme);
+}
+
 export default app;

+ 31 - 0
src/sdk/types/sdk.ts

@@ -324,6 +324,37 @@ export type Scene = Base & {
   };
   getSceneCropSetting: () => SceneCropSetting;
   setSceneCropSetting: (setting: SeSceneCropSetting) => void;
+
+  createFixPoint: (args: FixPoint3DArgs) => FixPoint3D;
+};
+export type FixPoint3DArgs = {
+  measure: boolean;
+  graph?: Pos3D[];
+  pos?: Pos3D;
+};
+export type FixPoint3D = {
+  // 销毁当前固定点对象,测量、形状一起删除
+  destroy(): void;
+  // 退出测量模式,删除测量线
+  quitMeasure(): void;
+  // 当不会形状时 外面可以修改固定点位置, 通过这个函数通知3d对象
+  changePos(data: Pos3D): void;
+  // 当正在绘制形状时,外面可以确定完成 或者取消, 确定完成时通过这个对象告知
+  graphDrawComplete(): void;
+
+  bus: Emitter<{
+    // 测量线被选中对象,值为是否选中
+    selectMeasure: boolean;
+    // 形状被选中事件,值为是否选中
+    selectGraph: boolean;
+    // 这个不用实现
+    graphDrawComplete: boolean;
+
+    // 测量线已经更改事件,  返回 [[pos, pos], [pos, pos]] 多组线段
+    measureChange: Pos3D[][];
+    // 形状被修改事件 返回形状点路径,还有中心点位置
+    graphChange: { path: Pos3D[]; center: Pos3D };
+  }>;
 };
 
 export type PoseVideo = {

+ 23 - 11
src/store/fixPoint.ts

@@ -1,13 +1,25 @@
-import {ref} from "vue";
-
-export type FixPoint = {
-  id: string
-  text: string
-  pos: {
-    x: number,
-    y: number,
-    z: number
-  }
+import { ref } from "vue";
+
+export enum FixType {
+  POINT = "point",
+  GRAPH = "graph",
 }
+type Point = {
+  x: number;
+  y: number;
+  z: number;
+};
+
+type FixPointBase = {
+  id: string;
+  text: string;
+  pos: Point;
+} & { measure: boolean; lines: Point[][] };
+
+type PFixPoint = FixPointBase & { type: FixType.POINT };
+
+type GFixPoint = FixPointBase & { type: FixType.GRAPH; points: Point[] };
+
+export type FixPoint = FixPointBase | PFixPoint | GFixPoint;
 
-export const fixPoints = ref<FixPoint[]>([])
+export const fixPoints = ref<FixPoint[]>([]);

+ 21 - 13
src/store/photos.ts

@@ -1,16 +1,24 @@
-import {ref} from "vue";
-import {Pos} from "@/sdk";
+import { ref } from "vue";
+import { Pos } from "@/sdk";
 
 export type PhotoRaw = {
-  id: string
-  url: string
-  urlRaw: string
-  meterPerPixel: number
-  time: number,
-  measures: { pos: Pos[], dis: number}[],
-  fixPoints: Array<{ text:string, pos: Pos }>,
-  basePoints: Array<Pos>
-  baseLines: Array<Pos[]>
-}
+  id: string;
+  // 只有场景内容的图片 url
+  url: string;
+  // 带测量 基准线 基准点的图片 url
+  urlRaw: string;
+  // 一个像素多少米
+  meterPerPixel: number;
+  // 截图时间
+  time: number;
+  // 测量线 pos: 测量线真实长度  pos:图片中的坐标
+  measures: { pos: Pos[]; dis: number }[];
+  // 固定点 pos:图片中的坐标 text 固定点文本内容
+  fixPoints: Array<{ text: string; pos: Pos }>;
+  // 基准点 图片中的坐标
+  basePoints: Array<Pos>;
+  // 基准线
+  baseLines: Array<Pos[]>;
+};
 
-export const photos = ref<PhotoRaw[]>([])
+export const photos = ref<PhotoRaw[]>([]);

+ 1 - 1
src/views/photos/index.vue

@@ -27,7 +27,7 @@
       :data="sortPhotos"
     />
     <!--    @click="router.push(writeRouteName.scene)"-->
-    <ButtonPane class="back fun-ctrl" v-if="!selectMode">
+    <ButtonPane class="back fun-ctrl photo-select" v-if="!selectMode">
       <ui-icon type="photo" class="icon" />
       <ui-input
         type="select"

+ 31 - 20
src/views/scene/covers/fixPoint.vue

@@ -1,37 +1,48 @@
 <template>
   <Cover
-      @change-pos="pos => $emit('changePos', pos)"
-      :pos="data.pos"
-      @focus="emit('focus')"
-      @blur="emit('blur')"
-      :focus="active"
-      class="fix-cover"
+    @change-pos="(pos) => isPure && $emit('changePos', pos)"
+    :pos="data.pos"
+    @focus="emit('focus')"
+    @blur="emit('blur')"
+    :focus="active"
+    class="fix-cover"
   >
     <div class="label">
       <img :src="active ? activeIcon : icon" />
       <p>{{ data.text }}</p>
     </div>
   </Cover>
-
 </template>
 
 <script setup lang="ts">
-import Cover from './cover.vue'
-import icon from './icon/pointf_n.png'
-import activeIcon from './icon/point_f_s.png'
-import {Pos3D} from '@/sdk'
-import {FixPoint} from "@/store/fixPoint";
-import {ref, watchEffect} from "vue";
+import Cover from "./cover.vue";
+import icon from "./icon/pointf_n.png";
+import activeIcon from "./icon/point_f_s.png";
+import { Pos3D } from "@/sdk";
+import { FixPoint, FixType } from "@/store/fixPoint";
+import { getFix3d } from "../fixManage";
 
 const props = defineProps<{
-  data: FixPoint,
-  active: boolean
-}>()
+  data: FixPoint;
+  active: boolean;
+}>();
 const emit = defineEmits<{
-  (m: 'changePos', pos: Pos3D): void,
-  (m: 'focus'): void,
-  (m: 'blur'): void,
-}>()
+  (m: "changePos", pos: Pos3D): void;
+  (m: "focus"): void;
+  (m: "blur"): void;
+  (m: "focusMeasure"): void;
+  (m: "blurMeasure"): void;
+}>();
+
+const isPure = !("type" in props.data) || props.data.type === FixType.POINT;
+const fix3d = getFix3d(props.data);
+
+if (fix3d) {
+  fix3d.bus.on("selectGraph", (select) => (select ? emit("focus") : emit("blur")));
+  fix3d.bus.on("selectMeasure", (select) =>
+    select ? emit("focusMeasure") : emit("blurMeasure")
+  );
+}
 </script>
 
 <style scoped lang="scss">

+ 115 - 41
src/views/scene/covers/fixPoints.vue

@@ -1,29 +1,49 @@
 <template>
   <FixPointPanel
-    v-for="point in fixPoints"
+    v-for="point in (fixPoints as FixPoint[])"
     :key="point.id"
     :data="point"
     :active="point === customMap.activeFixPoint"
-    @change-pos="(pos) => (point.pos = pos)"
-    @focus="() => (customMap.activeFixPoint = point)"
-    @blur="() => (customMap.activeFixPoint = customMap.activeFixPoint === point ? null : customMap.activeFixPoint)"
+    @change-pos="(pos) => changePos(point, pos)"
+    @focus="select(point, false)"
+    @blur="unSelect(point, false)"
+    @focus-measure="select(point, true)"
+    @blur-measure="unSelect(point, true)"
   />
 
   <div ref="menu" @touchstart.stop class="action-menus">
     <!--    <ActionsPanel :menus="activeActionMenus" v-show="customMap.activeFixPoint" />-->
-    <ActionMenus v-if="customMap.activeFixPoint" :menus="activeActionMenus" :active-key="edit ? 'edit' : null" dire="row" />
+    <ActionMenus
+      v-if="customMap.activeFixPoint"
+      :menus="activeActionMenus"
+      :active-key="edit ? 'edit' : null"
+      dire="row"
+    />
+    <ActionMenus
+      v-if="ingActionMenus"
+      :menus="ingActionMenus"
+      :active-key="null"
+      dire="row"
+    />
   </div>
 
-  <EditFixPoint v-if="edit" @quit="edit = null" v-model:text="edit.text" ref="dom" :key="edit.id" />
+  <EditFixPoint
+    v-if="edit"
+    @quit="edit = null"
+    v-model:text="edit.text"
+    ref="dom"
+    :key="edit.id"
+  />
 </template>
 
 <script setup lang="ts">
-import { fixPoints, FixPoint } from '@/store/fixPoint';
-import FixPointPanel from './fixPoint.vue';
-import { ref, watch, watchEffect } from 'vue';
-import { customMap } from '@/hook';
-import ActionMenus from '@/components/group-button/index.vue';
-import EditFixPoint from '@/components/edit-fix-point/index.vue';
+import { fixPoints, FixPoint } from "@/store/fixPoint";
+import FixPointPanel from "./fixPoint.vue";
+import { computed, ref, watch, watchEffect } from "vue";
+import { customMap } from "@/hook";
+import ActionMenus from "@/components/group-button/index.vue";
+import EditFixPoint from "@/components/edit-fix-point/index.vue";
+import { delFix3d, changePos, quitMeasure, drawstatus, DrawStatus } from "../fixManage";
 
 const edit = ref<FixPoint>();
 watchEffect(() => {
@@ -32,34 +52,88 @@ watchEffect(() => {
   }
 });
 
-const activeActionMenus = [
-  {
-    key: 'edit',
-    icon: 'edit',
-    text: '编辑',
-    color: '#161A1A',
-    iconColor: '#2F8FFF',
-    onClick() {
-      edit.value = edit.value === customMap.activeFixPoint ? null : customMap.activeFixPoint;
+const selectMeasure = ref(false);
+const select = (point: FixPoint, onMeasure: boolean = false) => {
+  selectMeasure.value = onMeasure;
+  customMap.activeFixPoint = point;
+};
+const unSelect = (point: FixPoint, onMeasure: boolean = false) => {
+  selectMeasure.value = onMeasure;
+  customMap.activeFixPoint =
+    customMap.activeFixPoint === point ? null : customMap.activeFixPoint;
+};
 
-    },
-  },
-  {
-    key: 'delete',
-    icon: 'del',
-    text: '删除',
-    color: '#FF4D4F',
-    iconColor: '#fff',
-    onClick() {
-      const index = fixPoints.value.indexOf(customMap.activeFixPoint);
-      if (~index) {
-        fixPoints.value.splice(index, 1);
-        edit.value = null;
-        customMap.activeFixPoint = null;
-      }
-    },
-  },
-];
+const activeActionMenus = computed(() =>
+  selectMeasure.value
+    ? [
+        {
+          key: "delete",
+          icon: "del",
+          text: "删除",
+          color: "#FF4D4F",
+          iconColor: "#fff",
+          onClick() {
+            quitMeasure(customMap.activeFixPoint);
+          },
+        },
+      ]
+    : [
+        {
+          key: "edit",
+          icon: "edit",
+          text: "编辑",
+          color: "#161A1A",
+          iconColor: "#2F8FFF",
+          onClick() {
+            edit.value =
+              edit.value === customMap.activeFixPoint ? null : customMap.activeFixPoint;
+          },
+        },
+        {
+          key: "delete",
+          icon: "del",
+          text: "删除",
+          color: "#FF4D4F",
+          iconColor: "#fff",
+          onClick() {
+            const index = fixPoints.value.indexOf(customMap.activeFixPoint);
+            if (~index) {
+              fixPoints.value.splice(index, 1);
+              edit.value = null;
+              customMap.activeFixPoint = null;
+              delFix3d(customMap.activeFixPoint);
+            }
+          },
+        },
+      ]
+);
+
+const ingActionMenus = computed(() => {
+  if (drawstatus.value === DrawStatus.ing) {
+    return [
+      {
+        key: "edit",
+        icon: "edit",
+        text: "完成",
+        color: "#161A1A",
+        iconColor: "#2F8FFF",
+        onClick() {
+          drawstatus.value = DrawStatus.complete;
+        },
+      },
+      {
+        key: "delete",
+        icon: "del",
+        text: "取消",
+        color: "#FF4D4F",
+        iconColor: "#fff",
+        onClick() {
+          drawstatus.value = DrawStatus.quit;
+        },
+      },
+    ];
+  }
+});
 
 const dom = ref<HTMLDivElement>();
 const menu = ref<HTMLDivElement>();
@@ -71,10 +145,10 @@ watchEffect((onCleanup) => {
       }
     };
     // document.documentElement.addEventListener("click", handler)
-    document.documentElement.addEventListener('touchstart', handler);
+    document.documentElement.addEventListener("touchstart", handler);
     onCleanup(() => {
       // document.documentElement.removeEventListener("click", handler)
-      document.documentElement.removeEventListener('touchstart', handler);
+      document.documentElement.removeEventListener("touchstart", handler);
     });
   }
 });

+ 2 - 2
src/views/scene/covers/range.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="right-range floating-range" v-if="rangeSetting">
     <span
-      class="fun-ctrl"
+      class="fun-ctrl autonomous"
       @click="
         value =
           value - rangeSetting.step >= rangeSetting.min
@@ -35,7 +35,7 @@
       </div>
     </div>
     <span
-      class="fun-ctrl"
+      class="fun-ctrl autonomous"
       @click="
         value =
           value + rangeSetting.step <= rangeSetting.max

+ 102 - 0
src/views/scene/fixManage.ts

@@ -0,0 +1,102 @@
+import { useSDK } from "@/hook";
+import { FixPoint3D, FixPoint3DArgs } from "@/sdk";
+import { FixPoint, FixType } from "@/store/fixPoint";
+import { ref, shallowRef, watchEffect } from "vue";
+
+const sdk = useSDK();
+const fix3ds: { [key in string]: FixPoint3D } = {};
+
+export enum DrawStatus {
+  un,
+  ing,
+  complete,
+  quit,
+}
+export const drawstatus = ref(DrawStatus.un);
+export const measureMode = ref(false);
+watchEffect(() => {
+  console.error(measureMode.value);
+});
+const drawingFix3d = shallowRef<FixPoint3D[]>([]);
+
+watchEffect(() => {
+  if (drawstatus.value === DrawStatus.complete) {
+    drawingFix3d.value.forEach((fix3d) => {
+      fix3d.graphDrawComplete();
+      fix3d.bus.emit("graphDrawComplete", true);
+    });
+    drawingFix3d.value = [];
+  } else if (drawstatus.value === DrawStatus.quit) {
+    drawingFix3d.value.forEach((fix3d) => {
+      fix3d.destroy();
+      fix3d.bus.emit("graphDrawComplete", false);
+    });
+    drawingFix3d.value = [];
+  }
+});
+
+export const getFix3d = (data: FixPoint) => {
+  if (fix3ds[data.id]) {
+    return fix3ds[data.id];
+  }
+
+  let args: FixPoint3DArgs = {
+    measure: !!data.measure,
+  };
+
+  if ("type" in data && data.type === FixType.GRAPH) {
+    args.graph = data.points;
+  } else {
+    args.pos = data.pos;
+  }
+
+  if (!args.measure && !args.graph) {
+    return;
+  }
+
+  const fix3d = sdk.scene.createFixPoint(args);
+  if (drawstatus.value === DrawStatus.ing) {
+    drawingFix3d.value.push(fix3d);
+  }
+
+  if (data.measure) {
+    fix3d.bus.on("measureChange", (lines) => {
+      Object.assign(data, { lines });
+    });
+  }
+
+  if (args.graph) {
+    fix3d.bus.on("graphChange", ({ path, center }) => {
+      Object.assign(data, { line: path, pos: center });
+    });
+  }
+  fix3ds[data.id] = fix3d;
+};
+
+export const quitMeasure = (data: FixPoint) => {
+  Object.assign(data, { measure: false, lines: undefined });
+  const fix3d = fix3ds[data.id];
+  if (fix3d) {
+    fix3d.bus.off("measureChange");
+    fix3d.quitMeasure();
+  }
+};
+
+export const changePos = (data: FixPoint, pos: FixPoint["pos"]) => {
+  Object.assign(data, { pos });
+  const fix3d = fix3ds[data.id];
+  if (fix3d) {
+    fix3d.changePos(pos);
+  }
+};
+
+export const delFix3d = (data) => {
+  const fix3d = fix3ds[data.id];
+  if (fix3d) {
+    fix3d.destroy();
+    const index = drawingFix3d.value.indexOf(fix3d);
+    if (~index) {
+      drawingFix3d.value.splice(index, 1);
+    }
+  }
+};

+ 55 - 4
src/views/scene/menus/actions.ts

@@ -16,10 +16,17 @@ import {
   useSDK,
 } from "@/hook";
 import { getCoverPos } from "../linkage/cover";
-import { Pos3D } from "@/sdk";
-import { fixPoints } from "@/store/fixPoint";
+import { Pos3D, TypeEmu } from "@/sdk";
+import { FixPoint, fixPoints, FixType } from "@/store/fixPoint";
 import Message from "@/components/base/components/message/message.vue";
 import { getId } from "@/utils";
+import {
+  delFix3d,
+  drawstatus,
+  DrawStatus,
+  getFix3d,
+  measureMode,
+} from "../fixManage";
 
 const trackPosMenuAction = (
   onComplete: () => void,
@@ -132,8 +139,17 @@ const menuActions = {
         onComplete();
       },
       (pos) => {
-        const len = fixPoints.value.push({ id: getId(), pos, text: "固定点" });
-        activeFixPointStack.current.value.value = fixPoints.value[len - 1];
+        const len = fixPoints.value.push({
+          id: getId(),
+          pos,
+          text: "固定点",
+          type: FixType.POINT,
+          measure: measureMode.value,
+          lines: undefined,
+        });
+        activeFixPointStack.current.value.value = fixPoints.value[
+          len - 1
+        ] as FixPoint;
         if (hide) {
           hide();
           hide = null;
@@ -146,6 +162,41 @@ const menuActions = {
       hide && hide();
     };
   },
+  [menuEnum.FIX_GRANH]: (_, onComplete) => {
+    let hide = Message.success({ msg: "请单击绘制固定点形状" });
+    const data: FixPoint = {
+      id: getId(),
+      pos: { x: 0, y: 0, z: 0 },
+      text: "形状",
+      type: FixType.GRAPH,
+      measure: measureMode.value,
+      lines: undefined,
+    };
+    drawstatus.value = DrawStatus.ing;
+    getFix3d(data).bus.on("graphDrawComplete", (complete) => {
+      if (complete) {
+        const len = fixPoints.value.push(data);
+        activeFixPointStack.current.value.value = fixPoints.value[
+          len - 1
+        ] as FixPoint;
+      }
+      if (hide) {
+        hide();
+        hide = null;
+      }
+      onComplete();
+    });
+
+    return () => {
+      drawstatus.value = DrawStatus.quit;
+      hide && hide();
+    };
+  },
+  // [menuEnum.FIX_MEASURE]: (_, onComplete) => {
+  //   return () => {
+  //     measureMode.value = false;
+  //   };
+  // },
   [menuEnum.MEASURE_ROW]: (menu, onComplete) => {
     return trackMeasureMenuAction(
       "L_LINE",

+ 20 - 1
src/views/scene/menus/menus.ts

@@ -2,7 +2,7 @@ import {
   findMenuByAttr,
   generateMixMenus as generateMixMenusRaw,
 } from "@/utils/menus";
-import { nextTick, ref } from "vue";
+import { Ref, computed, nextTick, ref } from "vue";
 import { useAsyncSDK, useSDK } from "@/hook";
 import { laserModeStack, modeDisabled } from "@/hook/custom/index";
 import { Mode } from "@/sdk";
@@ -11,10 +11,13 @@ import { fixPoints } from "@/store/fixPoint";
 import { basePoints } from "@/store/basePoint";
 import { list } from "@/store/measure";
 import { sceneSeting } from "@/store/sceneSeting";
+import { drawstatus, measureMode } from "../fixManage";
 
 export type MenuRaw = {
   key: string;
   text: string;
+  switch?: boolean;
+  switchValue?: { get: () => boolean; set: (val: boolean) => void };
   single?: boolean;
   continued?: boolean;
   defaultSelect?: true | (() => boolean);
@@ -32,6 +35,8 @@ export enum menuEnum {
   MEASURE_COLUMN = "V_LINE",
   MEASURE_FREE = "LINE",
   FIX_POINT = "fixPoint",
+  FIX_GRANH = "fixGranh",
+  FIX_MEASURE = "fixMeasure",
   BASE_LINE = "baseLine",
   BASE_POINT = "basePoint",
 }
@@ -94,6 +99,20 @@ export const menus: MenuRaw[] = [
         text: "固定点",
         key: menuEnum.FIX_POINT,
       },
+      {
+        icon: "point_a",
+        text: "绘制线",
+        key: menuEnum.FIX_GRANH,
+      },
+      {
+        text: "垂足",
+        switch: true,
+        switchValue: {
+          get: () => measureMode.value,
+          set: (val) => (measureMode.value = val),
+        },
+        key: menuEnum.FIX_MEASURE,
+      },
     ],
   },
   {

+ 0 - 1
src/views/scene/menus/pane.vue

@@ -49,7 +49,6 @@ 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 {