edit-path.vue 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. <template>
  2. <Teleport to="#layout-app">
  3. <RightFillPano class="edit-path-point">
  4. <div v-show="!~activePointNdx" :class="{ disabled: !data.points.length }">
  5. <ui-group
  6. :title="`${isTemploraryID(data.id) ? '创建' : '编辑'}路线`"
  7. borderBottom
  8. >
  9. <ui-group-option class="item">
  10. <span class="label">路径名称</span>
  11. <span class="oper"> <Switch v-model:checked="data.showName" /> </span>
  12. </ui-group-option>
  13. <ui-group-option class="item">
  14. <ui-input
  15. width="100%"
  16. type="text"
  17. placeholder="路径名称"
  18. v-model="data.name"
  19. :maxlength="60"
  20. />
  21. </ui-group-option>
  22. <ui-group-option class="item">
  23. <span class="label">路径粗细</span>
  24. <span class="oper">
  25. <InputNumber
  26. style="width: 120px"
  27. v-model:value="data.lineWidth"
  28. :max="1"
  29. :min="0.2"
  30. :step="0.1"
  31. :controls="false"
  32. >
  33. <template #addonBefore>
  34. <span
  35. class="fun-ctrl"
  36. @click="
  37. data.lineWidth = round(Math.max(0.2, data.lineWidth - 0.1), 1)
  38. "
  39. >-</span
  40. >
  41. </template>
  42. <template #addonAfter>
  43. <span
  44. class="fun-ctrl"
  45. @click="data.lineWidth = round(Math.min(1, data.lineWidth + 0.1), 1)"
  46. >+</span
  47. >
  48. </template>
  49. </InputNumber>
  50. </span>
  51. </ui-group-option>
  52. <ui-group-option class="item">
  53. <span class="label">路径颜色</span>
  54. <span class="oper">
  55. <ui-input
  56. type="color"
  57. width="24px"
  58. height="24px"
  59. v-model="data.lineColor"
  60. />
  61. </span>
  62. </ui-group-option>
  63. <ui-group-option class="item">
  64. <span class="label">路径箭头</span>
  65. <span class="oper">
  66. <Switch v-model:checked="data.showDirection" />
  67. </span>
  68. </ui-group-option>
  69. <ui-group-option class="item" v-if="data.showDirection">
  70. <span class="label">箭头反向</span>
  71. <span class="oper"> <Switch v-model:checked="data.reverseDirection" /> </span>
  72. </ui-group-option>
  73. </ui-group>
  74. <ui-group borderBottom>
  75. <ui-group-option>
  76. <span>文字大小</span>
  77. <Slider v-model:value="data.fontSize" :min="12" :max="60" :step="0.1" />
  78. </ui-group-option>
  79. </ui-group>
  80. <ui-group borderBottom>
  81. <ui-group-option>
  82. <SignItem
  83. label="可见范围"
  84. v-if="!data.globalVisibility"
  85. @apply-global="$emit('applyGlobal', 'visibilityRange')"
  86. >
  87. <Slider
  88. v-model:value="data.visibilityRange"
  89. :min="1"
  90. :max="1000"
  91. :step="0.1"
  92. />
  93. </SignItem>
  94. </ui-group-option>
  95. <ui-group-option>
  96. <SignItem @apply-global="$emit('applyGlobal', 'globalVisibility')">
  97. <template v-slot:label>
  98. <ui-input
  99. type="checkbox"
  100. label="全部范围可见"
  101. :modelValue="!!data.globalVisibility"
  102. @update:modelValue="(v: boolean) => data.globalVisibility = v"
  103. />
  104. </template>
  105. </SignItem>
  106. </ui-group-option>
  107. </ui-group>
  108. <Button
  109. block
  110. type="primary"
  111. ghost
  112. size="large"
  113. @click="switchPlay"
  114. v-if="data.points.length"
  115. >
  116. {{ isScenePathPlayIng ? "停止" : "" }}预览路径
  117. </Button>
  118. </div>
  119. <div v-if="~activePointNdx">
  120. <ui-group title="编辑点" borderBottom>
  121. <ui-group-option>描述:</ui-group-option>
  122. <ui-group-option>
  123. <ui-input
  124. class="input"
  125. width="100%"
  126. height="158px"
  127. v-model="data.points[activePointNdx].name"
  128. placeholder="特征描述:"
  129. type="textarea"
  130. :maxlength="200"
  131. />
  132. </ui-group-option>
  133. </ui-group>
  134. <Button block danger type="primary" ghost size="large" @click="deletePoint">
  135. 删除
  136. </Button>
  137. </div>
  138. </RightFillPano>
  139. <span
  140. @click="unKeepAdding ? unKeepAdding() : keepAdding()"
  141. class="pin-position strengthen fun-ctrl"
  142. :class="{ disabled: !data.points.length }"
  143. v-if="node"
  144. >
  145. <ui-icon
  146. :style="{ color: unKeepAdding ? 'var(--color-main-normal)' : 'currentColor' }"
  147. type="pin1"
  148. size="22px"
  149. />
  150. </span>
  151. </Teleport>
  152. </template>
  153. <script setup lang="ts">
  154. import {
  155. showLeftCtrlPanoStack,
  156. showLeftPanoStack,
  157. showRightCtrlPanoStack,
  158. showRightPanoStack,
  159. } from "@/env";
  160. import { computed, onUnmounted, ref, shallowRef, watch, watchEffect } from "vue";
  161. import { isTemploraryID, Path } from "@/store";
  162. import { RightFillPano } from "@/layout";
  163. import { useViewStack } from "@/hook";
  164. import { round, togetherCallback } from "@/utils";
  165. import { Switch, Slider, Button, InputNumber } from "ant-design-vue";
  166. import SignItem from "@/views/tagging-position/sign-item.vue";
  167. import { Message } from "bill/expose-common";
  168. import {
  169. getPathNode,
  170. isScenePathPlayIng,
  171. pauseScenePath,
  172. playScenePath,
  173. } from "@/sdk/association/path";
  174. const props = defineProps<{ data: Path }>();
  175. defineEmits<{
  176. (e: "applyGlobal", k: string | string[]): void;
  177. }>();
  178. const node = computed(() => getPathNode(props.data.id));
  179. watch(
  180. node,
  181. () => {
  182. if (props.data.points.length) {
  183. node.value?.fly();
  184. }
  185. },
  186. { immediate: true }
  187. );
  188. const activePointNdx = ref(-1);
  189. watchEffect((onCleanup) => {
  190. if (!node.value) return;
  191. const $node = node.value;
  192. const handler = (ndx: number) => {
  193. activePointNdx.value = ndx;
  194. };
  195. $node.changeEditMode(true);
  196. $node.bus.on("activePoint", handler);
  197. onCleanup(() => {
  198. $node.bus.off("activePoint", handler);
  199. $node.changeEditMode(false);
  200. });
  201. });
  202. const deletePoint = () => {
  203. props.data.points.splice(activePointNdx.value, 1);
  204. activePointNdx.value = -1;
  205. };
  206. let unKeepAdding = shallowRef<() => void>();
  207. const keepAdding = () => {
  208. unKeepAdding.value && unKeepAdding.value();
  209. node.value?.changeCanEdit(true);
  210. const hide = Message.show({ msg: "请在模型上单击选择路径点位置", type: "warning" });
  211. unKeepAdding.value = () => {
  212. node.value?.changeCanEdit(false);
  213. hide();
  214. unKeepAdding.value = void 0;
  215. };
  216. };
  217. if (!props.data.points.length) {
  218. keepAdding();
  219. }
  220. onUnmounted(() => unKeepAdding.value && unKeepAdding.value());
  221. useViewStack(() => {
  222. return togetherCallback([
  223. showRightPanoStack.push(ref(false)),
  224. showLeftCtrlPanoStack.push(ref(false)),
  225. showLeftPanoStack.push(ref(false)),
  226. showRightCtrlPanoStack.push(ref(false)),
  227. ]);
  228. });
  229. const switchPlay = () => {
  230. if (isScenePathPlayIng.value) {
  231. pauseScenePath();
  232. } else {
  233. playScenePath(props.data);
  234. }
  235. };
  236. </script>
  237. <style lang="scss" scoped>
  238. .edit-path-point {
  239. position: absolute;
  240. right: 0;
  241. height: 100%;
  242. top: 0;
  243. --editor-menu-right: 0px;
  244. }
  245. .item {
  246. display: flex;
  247. align-items: center;
  248. justify-content: space-between;
  249. }
  250. .pin-position {
  251. position: absolute;
  252. left: 50%;
  253. transform: translate(-50%);
  254. width: 64px;
  255. height: 64px;
  256. background: rgba(27, 27, 28, 0.8);
  257. border-radius: 50%;
  258. bottom: 20px;
  259. z-index: 9;
  260. display: flex;
  261. align-items: center;
  262. justify-content: center;
  263. }
  264. </style>