sceneGroupInEditor.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. <template>
  2. <div class="scene-group">
  3. <div
  4. class="top-bar"
  5. :class="isConfirmingDeletion ? '' : 'show-icons-on-hover'"
  6. @click="onClickTopBar"
  7. @dragstart="onDragStart"
  8. @dragenter.self="onDragEnter"
  9. @dragend="onDragEnd"
  10. @dragleave.self="onDragLeave"
  11. :draggable="isRenaming ? false : true"
  12. :style="{
  13. paddingLeft: topBarPaddingLeft,
  14. }"
  15. >
  16. <div class="drag-image" ref="drag-image">
  17. <i class="iconfont icon-editor_folder_off"></i>
  18. </div>
  19. <i
  20. class="iconfont icon-edit_input_arrow icon-expand"
  21. :class="isExpanded ? '' : 'collapsed'"
  22. ></i>
  23. <i
  24. v-show="isExpanded"
  25. class="iconfont icon-editor_folder_on folder_expanded"
  26. ></i>
  27. <i
  28. v-show="!isExpanded"
  29. class="iconfont icon-editor_folder_off folder_collapsed"
  30. ></i>
  31. <template v-if="!isRenaming">
  32. <span
  33. class="group-name"
  34. v-title="
  35. $i18n.t(`zh_key.${groupNode.name}`).indexOf('zh_key') > -1
  36. ? groupNode.name
  37. : $i18n.t(`zh_key.${groupNode.name}`)
  38. "
  39. >
  40. <!-- {{
  41. $i18n.t(`zh_key.${groupNode.name}`).indexOf("zh_key") > -1
  42. ? groupNode.name
  43. : $i18n.t(`zh_key.${groupNode.name}`)
  44. }} -->
  45. {{ groupNode.name }}
  46. xxxx
  47. </span>
  48. <i
  49. v-show="level === 1"
  50. class="iconfont icon-editor_list_add icon-add"
  51. v-tooltip="$i18n.t('navigation.add_two_group')"
  52. @click.stop="onRequestForAddGroup"
  53. >
  54. </i>
  55. <i
  56. class="iconfont icon-editor_list_image icon-image"
  57. v-tooltip="$i18n.t('navigation.add_pano_or_scene')"
  58. @click.stop="onRequestForAddScene"
  59. v-show="
  60. level === 2 ||
  61. (level === 1 &&
  62. (groupNode.children.length === 0 ||
  63. (groupNode.children.length === 1 &&
  64. (groupNode.children[0].name === '默认二级分组' ||
  65. groupNode.children[0].name ===
  66. $i18n.t('navigation.default_group_two')))))
  67. "
  68. >
  69. </i>
  70. <i
  71. class="iconfont icon-editor_list_edit icon-edit"
  72. v-tooltip="$i18n.t('navigation.rename')"
  73. @click.stop="onClickForRename"
  74. >
  75. </i>
  76. <i
  77. class="iconfont icon-editor_list_delete icon-delete"
  78. v-tooltip="$i18n.t('navigation.delete')"
  79. @click.stop="onRequestForDelete"
  80. >
  81. </i>
  82. <div class="deletion-confirm-wrap">
  83. <div
  84. class="deletion-confirm"
  85. :class="isConfirmingDeletion ? 'show' : 'hide'"
  86. v-clickoutside="onRequestForCancelDelete"
  87. @click.stop="onConfirmDelete"
  88. >
  89. {{ $i18n.t("navigation.delete") }}
  90. </div>
  91. </div>
  92. </template>
  93. <input
  94. v-if="isRenaming"
  95. class="group-title-input"
  96. v-model.trim="newName"
  97. ref="input-for-rename"
  98. maxlength="50"
  99. :placeholder="$i18n.t('navigation.enter_name')"
  100. @blur="onInputNewNameComplete"
  101. @keydown.enter="onInputEnter"
  102. @click.stop
  103. />
  104. </div>
  105. <div class="group-content" v-if="isExpanded">
  106. <template
  107. v-if="
  108. !(
  109. groupNode.children.length === 1 &&
  110. (groupNode.children[0].name === '默认二级分组' ||
  111. groupNode.children[0].name ===
  112. $i18n.t('navigation.default_group_two'))
  113. )
  114. "
  115. >
  116. <InsertPositionTip
  117. position-debug="1"
  118. :indentLevel="level + 1"
  119. :topologyLevel="level + 1"
  120. :parentNode="groupNode"
  121. :index="0"
  122. ></InsertPositionTip>
  123. <div v-for="(item, index) of groupNode.children" :key="item.id">
  124. <component
  125. :is="'SceneGroup'"
  126. ref="scene-group"
  127. v-if="!item.type"
  128. :groupNode="item"
  129. :level="level + 1"
  130. @renameScene="onRenameScene"
  131. @deleteScene="onDeleteScene"
  132. @renameGroup="onInnerGroupRename"
  133. @deleteGroup="onInnerGroupConfirmDelete"
  134. />
  135. <SceneInGroup
  136. v-else
  137. :style="{
  138. paddingLeft: sceneItemPaddingLeft,
  139. }"
  140. :sceneInfo="item"
  141. @rename="onRenameScene"
  142. @delete="onDeleteScene"
  143. />
  144. <InsertPositionTip
  145. position-debug="2"
  146. :indentLevel="level + 1"
  147. :topologyLevel="level + 1"
  148. :parentNode="groupNode"
  149. :index="index + 1"
  150. ></InsertPositionTip>
  151. </div>
  152. </template>
  153. <template v-else>
  154. <!-- 自动生成的默认二级分组不显示,里边的内容显示成直属于一级分组的效果。 -->
  155. <InsertPositionTip
  156. position-debug="3"
  157. :indentLevel="level + 1"
  158. :topologyLevel="level + 2"
  159. :parentNode="groupNode.children[0]"
  160. :index="0"
  161. ></InsertPositionTip>
  162. <div
  163. v-for="(item, index) of groupNode.children[0].children"
  164. :key="item.id"
  165. >
  166. <SceneInGroup
  167. :style="{
  168. paddingLeft: sceneItemPaddingLeft,
  169. }"
  170. :sceneInfo="item"
  171. @rename="onRenameScene"
  172. @delete="onDeleteScene"
  173. />
  174. <InsertPositionTip
  175. position-debug="4"
  176. :indentLevel="level + 1"
  177. :topologyLevel="level + 2"
  178. :parentNode="groupNode.children[0]"
  179. :index="index + 1"
  180. ></InsertPositionTip>
  181. </div>
  182. </template>
  183. </div>
  184. <div class="dialog" style="z-index: 2000" v-if="isShowSelectionWindow">
  185. <MaterialSelector
  186. :title="$i18n.t('gather.select_material')"
  187. @cancle="isShowSelectionWindow = false"
  188. @submit="onSubmitFromMaterialSelector"
  189. :selectableType="['pano', '3D']"
  190. :initialMaterialType="'pano'"
  191. :isMultiSelection="true"
  192. />
  193. </div>
  194. </div>
  195. </template>
  196. <script>
  197. import SceneInGroup from "@/components/sceneInGroupInEditor.vue";
  198. import MaterialSelector from "@/components/materialSelector.vue";
  199. import InsertPositionTip from "@/components/insertPositionTipInEditor.vue";
  200. import { mapGetters, mapMutations } from "vuex";
  201. export default {
  202. name: "SceneGroup",
  203. components: {
  204. SceneInGroup,
  205. MaterialSelector,
  206. InsertPositionTip,
  207. },
  208. props: {
  209. groupNode: {
  210. type: Object,
  211. required: true,
  212. },
  213. level: {
  214. type: Number,
  215. default: 1,
  216. },
  217. },
  218. data() {
  219. return {
  220. isExpanded: false,
  221. isRenaming: false,
  222. newName: "",
  223. isConfirmingDeletion: false,
  224. isShowSelectionWindow: false,
  225. dragEnterTimerId: null,
  226. };
  227. },
  228. computed: {
  229. ...mapGetters({
  230. info: "info",
  231. dragInfo: "editorNavDragInfo",
  232. }),
  233. topBarPaddingLeft() {
  234. return 12 + (this.level - 1) * 12 + "px";
  235. },
  236. sceneItemPaddingLeft() {
  237. return 18 + this.level * 12 + "px";
  238. },
  239. },
  240. methods: {
  241. ...mapMutations({
  242. recordDragType: "setEditorNavDragType",
  243. recordDragNode: "setEditorNavDragNode",
  244. clearDragInfo: "clearEditorNavDragInfo",
  245. }),
  246. onClickTopBar() {
  247. if (this.isConfirmingDeletion) {
  248. return;
  249. }
  250. this.isExpanded = !this.isExpanded;
  251. },
  252. onRenameScene(...params) {
  253. this.$emit("renameScene", ...params);
  254. },
  255. onDeleteScene(...params) {
  256. this.$emit("deleteScene", ...params);
  257. },
  258. onRequestForAddGroup() {
  259. this.$emit("addGroup", this.groupNode.id);
  260. },
  261. onRequestForAddScene() {
  262. this.isShowSelectionWindow = true;
  263. },
  264. onClickForRename() {
  265. this.isRenaming = true;
  266. console.log(this.groupNode.name);
  267. if (this.groupNode.name == "默认二级分组") {
  268. this.newName = this.$i18n.t("navigation.default_group_two");
  269. } else if (this.groupNode.name == "一级分组") {
  270. this.newName = this.$i18n.t("navigation.group_one");
  271. } else {
  272. this.newName = this.groupNode.name;
  273. }
  274. this.$nextTick(() => {
  275. // this.$refs['input-for-rename'].focus()
  276. this.$refs["input-for-rename"].select();
  277. this.$refs["input-for-rename"].scrollIntoView({
  278. behavior: "smooth",
  279. });
  280. });
  281. },
  282. onInputNewNameComplete() {
  283. this.isRenaming = false;
  284. console.log(this.groupNode.name);
  285. if (!this.newName.trim()) {
  286. return;
  287. }
  288. if (this.newName !== this.groupNode.name) {
  289. this.$emit("renameGroup", this.groupNode.id, this.level, this.newName);
  290. }
  291. this.newName = "";
  292. },
  293. onInputEnter() {
  294. this.isRenaming = false; // 会导致input blur,进而触发onInputNewNameComplete
  295. },
  296. onInnerGroupRename(...params) {
  297. this.$emit("renameGroup", ...params);
  298. },
  299. onRequestForDelete() {
  300. this.isConfirmingDeletion = true;
  301. },
  302. onRequestForCancelDelete() {
  303. if (!this.isConfirmingDeletion) {
  304. return;
  305. }
  306. // 先保持isConfirmingDeletion不变,因为onClickTopBar可能要用到
  307. setTimeout(() => {
  308. this.isConfirmingDeletion = false;
  309. }, 0);
  310. },
  311. onConfirmDelete() {
  312. this.$emit("deleteGroup", this.groupNode.id, this.level);
  313. this.isConfirmingDeletion = false;
  314. },
  315. onInnerGroupConfirmDelete(...params) {
  316. this.$emit("deleteGroup", ...params);
  317. },
  318. onSubmitFromMaterialSelector(selected) {
  319. let newScenes = [];
  320. for (const item of selected) {
  321. if (item.materialType === "pano") {
  322. newScenes.push({
  323. icon: item.icon,
  324. sceneCode: item.sceneCode,
  325. sceneTitle: item.name,
  326. category:
  327. this.level === 1
  328. ? this.groupNode.children[0].id
  329. : this.groupNode.id,
  330. type: "pano",
  331. id: "s_" + this.$randomWord(true, 8, 8),
  332. });
  333. } else if (item.materialType === "3D") {
  334. console.log("item.num", item.num);
  335. newScenes.push({
  336. icon: item.thumb,
  337. sceneCode: item.num,
  338. sceneTitle: item.sceneName,
  339. category:
  340. this.level === 1
  341. ? this.groupNode.children[0].id
  342. : this.groupNode.id,
  343. type: "4dkk",
  344. version: item.isUpgrade === 1 ? "V4" : "V3", // 'V3' OR 'V4'. 全景看看v1.3新增
  345. id: "s_" + this.$randomWord(true, 8, 8),
  346. });
  347. }
  348. }
  349. let allSuccess = true;
  350. newScenes.forEach((item, i) => {
  351. let temp = this.info.scenes.find((eachScene) => {
  352. return eachScene.sceneCode === item.sceneCode;
  353. });
  354. if (temp) {
  355. setTimeout(() => {
  356. this.$msg.message(
  357. `${
  358. item.type == "4dkk"
  359. ? this.$i18n.t("navigation.scene_name")
  360. : this.$i18n.t("navigation.pano")
  361. }${this.$i18n.t("navigation.already_exists", {
  362. msg: item.sceneTitle,
  363. })}`
  364. );
  365. }, i * 100);
  366. allSuccess = false;
  367. return;
  368. }
  369. this.info.scenes.push(item);
  370. });
  371. this.isShowSelectionWindow = false;
  372. if (allSuccess) {
  373. this.$msg.success(this.$i18n.t("gather.success"));
  374. }
  375. },
  376. onDragStart(e) {
  377. if (this.isRenaming) {
  378. return;
  379. }
  380. this.recordDragType(`topologyGroupLevel${this.level}`);
  381. this.recordDragNode(this.groupNode);
  382. e.dataTransfer.setDragImage(this.$refs["drag-image"], -10, -18);
  383. },
  384. onDragEnter(e) {
  385. if (
  386. e.target.contains(e.relatedTarget) ||
  387. (this.level === 2 && this.dragInfo.type.includes("Group"))
  388. ) {
  389. return;
  390. }
  391. this.dragEnterTimerId = setTimeout(() => {
  392. if (!this.isExpanded) {
  393. this.isExpanded = true;
  394. }
  395. }, 700);
  396. },
  397. onDragEnd() {
  398. this.clearDragInfo();
  399. clearTimeout(this.dragEnterTimerId);
  400. },
  401. onDragLeave(e) {
  402. if (e.target.contains(e.relatedTarget)) {
  403. return;
  404. }
  405. clearTimeout(this.dragEnterTimerId);
  406. },
  407. },
  408. mounted() {},
  409. destroyed() {},
  410. };
  411. </script>
  412. <style lang="less" scoped>
  413. .scene-group {
  414. .top-bar {
  415. position: relative;
  416. color: rgba(255, 255, 255, 0.6);
  417. height: 40px;
  418. border-radius: 4px;
  419. display: flex;
  420. align-items: center;
  421. margin-left: -12px;
  422. margin-right: -10px;
  423. padding-right: 10px;
  424. &:hover {
  425. background: #313131;
  426. > .group-name {
  427. width: 120px;
  428. }
  429. }
  430. &.show-icons-on-hover:hover {
  431. > .icon-add,
  432. .icon-image,
  433. .icon-edit,
  434. .icon-delete {
  435. display: block;
  436. }
  437. }
  438. > .drag-image {
  439. position: absolute;
  440. width: 40px;
  441. height: 40px;
  442. background: #313131;
  443. box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.5);
  444. border-radius: 4px;
  445. display: flex;
  446. justify-content: center;
  447. align-items: center;
  448. z-index: -1;
  449. i {
  450. font-size: 16px;
  451. }
  452. }
  453. > .icon-expand {
  454. display: inline-block;
  455. font-size: 12px;
  456. transform: scale(0.7);
  457. &.collapsed {
  458. display: inline-block;
  459. transform: scale(0.7) rotate(-90deg);
  460. }
  461. }
  462. > .folder_expanded {
  463. font-size: 16px;
  464. margin-left: 7px;
  465. }
  466. > .folder_collapsed {
  467. font-size: 16px;
  468. margin-left: 7px;
  469. }
  470. > .group-name {
  471. margin-left: 6px;
  472. display: inline-block;
  473. text-overflow: ellipsis;
  474. overflow: hidden;
  475. white-space: nowrap;
  476. flex: 1 1 auto;
  477. }
  478. > .icon-add {
  479. margin-left: 12px;
  480. display: none;
  481. cursor: pointer;
  482. &:hover {
  483. color: #0076f6;
  484. }
  485. }
  486. > .icon-image {
  487. margin-left: 12px;
  488. display: none;
  489. cursor: pointer;
  490. &:hover {
  491. color: #0076f6;
  492. }
  493. }
  494. > .icon-edit {
  495. margin-left: 12px;
  496. display: none;
  497. cursor: pointer;
  498. &:hover {
  499. color: #0076f6;
  500. }
  501. }
  502. > .icon-delete {
  503. margin-left: 12px;
  504. display: none;
  505. cursor: pointer;
  506. &:hover {
  507. color: #fa5555;
  508. }
  509. }
  510. > .deletion-confirm-wrap {
  511. position: absolute;
  512. top: 0;
  513. bottom: 0;
  514. right: 0;
  515. width: 44px;
  516. overflow: hidden;
  517. pointer-events: none;
  518. border-top-right-radius: 4px;
  519. border-bottom-right-radius: 4px;
  520. > .deletion-confirm {
  521. position: absolute;
  522. top: 0;
  523. bottom: 0;
  524. width: 100%;
  525. background: #fa5555;
  526. transition: right 0.3s;
  527. cursor: pointer;
  528. text-align: center;
  529. font-size: 12px;
  530. color: #fff;
  531. pointer-events: auto;
  532. &::after {
  533. content: "";
  534. height: 100%;
  535. vertical-align: middle;
  536. display: inline-block;
  537. }
  538. &.show {
  539. right: 0;
  540. }
  541. &.hide {
  542. right: -44px;
  543. }
  544. }
  545. }
  546. > .group-title-input {
  547. margin-left: 6px;
  548. flex: 1 1 auto;
  549. width: 1px;
  550. height: 30px;
  551. background: #1a1b1d;
  552. border-radius: 2px;
  553. border: 1px solid #404040;
  554. color: #fff;
  555. font-size: 14px;
  556. padding: 0 10px 0 16px;
  557. &:focus {
  558. border-color: #0076f6;
  559. }
  560. }
  561. }
  562. .group-content {
  563. }
  564. }
  565. </style>