edit-path.vue 8.7 KB

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