edit-path.vue 7.4 KB

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