list.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. <template>
  2. <div class="bar-list" v-if="metadata?.navigationTrees?.length" :class="{ barshow: isShowScenesList }">
  3. <div class="top-con" v-show="currentScenesList.length">
  4. <div
  5. ref="scene-swiper"
  6. class="scene-list swiper-container"
  7. id="swScenes"
  8. :style="`width:${Math.min(scenesListW, innerW)}px;
  9. padding:${scenesListW > innerW ? '0 15px' : '0'}`"
  10. v-if="currentScenesList.length > 0"
  11. >
  12. <ul class="swiper-wrapper">
  13. <template v-for="(item, i) in currentScenesList" :key="item.id">
  14. <li
  15. @click="tabCurrentScene(item)"
  16. class="swiper-slide"
  17. :style="`background-image:url(${item.icon});`"
  18. :class="{
  19. active: currentScene.sceneCode == item.sceneCode,
  20. loopspan: item.name.length > spanlength && currentScene.id == item.id,
  21. }"
  22. >
  23. <i class="iconfont" :class="item.type == '4dkk' ? 'icon-editor_3d' : 'icon-editor_panoramic'"></i>
  24. <div class="marquee">
  25. <marquee-text :repeat="1" :duration="Math.ceil(item.name.length / 10) * 5" :key="item.id" v-if="item.name.length > spanlength && currentScene.id == item.id">
  26. {{ item.name }}
  27. </marquee-text>
  28. <span v-else>
  29. {{ item.name }}
  30. </span>
  31. </div>
  32. </li>
  33. </template>
  34. </ul>
  35. </div>
  36. <div
  37. class="swiper-container second-group-list"
  38. ref="second-group-swiper"
  39. id="swSecondary"
  40. :style="`width:${Math.min(secondaryW, innerW)}px;
  41. padding:${secondaryW > innerW ? '0 15px' : '0'}`"
  42. v-if="metadata?.navigationTrees[rootTabIndex] && metadata?.navigationTrees[rootTabIndex]?.children[0]?.type == 'group' && showSecondTab"
  43. >
  44. <ul class="swiper-wrapper">
  45. <template v-for="(item, i) in metadata?.navigationTrees[rootTabIndex]?.children">
  46. <li
  47. class="swiper-slide"
  48. @click="tabSecond(item, i)"
  49. :class="{
  50. active: currentSecondId == item.id,
  51. loopspan: fixTitle(item.name).length > spanlength && currentSecondId == item.id,
  52. }"
  53. :key="item.id"
  54. v-if="item.children.length"
  55. >
  56. <marquee-text :duration="Math.ceil(fixTitle(item.name).length / 10) * 5" :key="item.id" :repeat="1" v-if="fixTitle(item.name).length > spanlength && currentSecondary.id == item.id">
  57. {{ fixTitle(item.name) }}
  58. </marquee-text>
  59. <span v-else>
  60. {{ fixTitle(item.name) }}
  61. </span>
  62. </li>
  63. </template>
  64. </ul>
  65. </div>
  66. </div>
  67. <div
  68. class="swiper-container root-group-list"
  69. ref="root-group"
  70. id="swcatalogRoot"
  71. :style="`width:${Math.min(catalogRootW, innerW)}px;
  72. padding:${catalogRootW > innerW ? '0 15px' : '0'}`"
  73. v-if="metadata?.navigationTrees?.length > 1"
  74. >
  75. <ul class="swiper-wrapper">
  76. <template v-for="(item, i) in metadata?.navigationTrees" :key="item.id">
  77. <li
  78. v-if="item.children.length"
  79. class="swiper-slide"
  80. :class="{
  81. active: currentRootId == item.id,
  82. loopspan: fixTitle(item.name).length > spanlength && currentRootId == item.id,
  83. }"
  84. @click="tabRoot(item, i)"
  85. >
  86. <marquee-text :duration="Math.ceil(fixTitle(item.name).length / 10) * 5" :key="item.id" :repeat="1" v-if="fixTitle(item.name).length > spanlength && currentRootId == item.id">
  87. {{ fixTitle(item.name) }}
  88. </marquee-text>
  89. <span v-else>
  90. {{ fixTitle(item.name) }}
  91. </span>
  92. </li>
  93. </template>
  94. </ul>
  95. </div>
  96. </div>
  97. </template>
  98. <script setup>
  99. import { ref, watch, computed, onMounted, nextTick, unref, watchEffect } from "vue";
  100. import { useStore } from "vuex";
  101. import { useApp } from "@/app";
  102. import MarqueeText from "vue-marquee-text-component";
  103. import { useI18n, getLocale } from "@/i18n";
  104. const { t } = useI18n({ useScope: "global" });
  105. const store = useStore();
  106. const spanlength = ref(5);
  107. const metadata = computed(() => store.getters["scene/metadata"]);
  108. const scenes = computed(() => store.getters["scene/list"]);
  109. const currentScene = computed(() => store.getters["scene/currentScene"]);
  110. const currentSecondId = computed(() => store.getters["scene/currentSecondId"]);
  111. const currentRootId = computed(() => store.getters["scene/currentRootId"]);
  112. const clamp = computed(() => (num, min, max) => Math.min(Math.max(num, min), max));
  113. const currentCatalogRoot = computed(() => store.getters["scene/currentCatalogRoot"]);
  114. const currentSecondary = computed(() => store.getters["scene/currentSecondary"]);
  115. const secondaryList = computed(() => store.getters["scene/secondaryList"]);
  116. const isShowScenesList = computed(() => store.getters["functions/isShowScenesList"]);
  117. const currentScenesList = computed(() => store.getters["scene/currentScenesList"]);
  118. const show = ref(false);
  119. const swidth = ref({
  120. swcatalogRoot: 104,
  121. swSecondary: 84,
  122. swScenes: 72,
  123. });
  124. const SceneSwiper = ref(null);
  125. const SecondGroupSwiper = ref(null);
  126. const rootGroupSwiper = ref(null);
  127. const rootTabIndex = computed(() => {
  128. let idx = 0;
  129. if (metadata.value && metadata.value?.navigationTrees) {
  130. idx = metadata.value?.navigationTrees.findIndex((item) => item.id == currentRootId.value);
  131. }
  132. return idx;
  133. });
  134. const secondTabIndex = computed(() => {
  135. let idx = 0;
  136. if (metadata.value && metadata.value?.navigationTrees) {
  137. idx = metadata.value.navigationTrees[rootTabIndex.value]?.children.findIndex((item) => item.id == currentSecondId.value);
  138. }
  139. return idx;
  140. });
  141. const showSecondTab = computed(() => {
  142. return metadata.value && metadata.value?.navigationTrees ? metadata.value?.navigationTrees[rootTabIndex.value].children.some((item) => item.id != currentSecondId.value) : false;
  143. });
  144. const scenesListW = computed(() => currentScenesList.value.length * (swidth.value["swScenes"] + 10) - 10);
  145. const secondaryW = computed(() => {
  146. let list = metadata.value?.navigationTrees[rootTabIndex.value].children.filter((item) => item.children.length);
  147. return list.length * (swidth.value["swSecondary"] + 10) - 10;
  148. });
  149. const catalogRootW = computed(() => {
  150. let list = metadata.value.navigationTrees.filter((item) => item.children.length);
  151. return list.length * (swidth.value["swcatalogRoot"] + 10);
  152. });
  153. const innerW = computed(() => window.innerWidth);
  154. watch(
  155. () => currentScenesList.value,
  156. () => {
  157. nextTick(() => {
  158. initSceneSwiper();
  159. initRootGroupSwiper();
  160. // if (metadata.value.navigationTrees[rootTabIndex.value]?.children[secondTabIndex.value]?.type == "group") {
  161. // initSecondGroupSwiper();
  162. // }
  163. });
  164. }
  165. );
  166. watch(
  167. () => rootTabIndex.value,
  168. (val) => {
  169. console.error(val);
  170. nextTick(() => {
  171. if (metadata.value.navigationTrees[rootTabIndex.value]?.children[secondTabIndex.value]?.type == "group") {
  172. initSecondGroupSwiper();
  173. }
  174. });
  175. }
  176. );
  177. const fixTitle = (name) => {
  178. if (name == "默认二级分组") {
  179. name = t("navigation.default_group_two");
  180. } else if (name == "一级分组") {
  181. name = t("navigation.group_one");
  182. } else {
  183. name = name;
  184. }
  185. return name;
  186. };
  187. const swiperOptions = {
  188. slidesPerView: "auto",
  189. centeredSlides: true,
  190. slideToClickedSlide: true,
  191. spaceBetween: 10,
  192. centerInsufficientSlides: true,
  193. centeredSlidesBounds: true,
  194. freeMode: {
  195. enabled: true,
  196. sticky: false,
  197. },
  198. };
  199. const tabRoot = (item, index) => {
  200. store.commit("scene/setData", { currentRootId: item.id, currentSecondId: null });
  201. setTimeout(() => {
  202. changeSceneList();
  203. }, 0);
  204. // setTimeout(() => {
  205. // rootGroupSwiper.value.slideTo(index);
  206. // SecondGroupSwiper.value.slideTo(rootTabIndex.value);
  207. // }, 0);
  208. };
  209. const tabSecond = (item, index) => {
  210. store.commit("scene/setData", { currentSecondId: item.id });
  211. let sceneList = metadata.value.navigationTrees[rootTabIndex.value].children[secondTabIndex.value].children;
  212. store.commit("scene/setCurrentScenesList", sceneList);
  213. //
  214. // setTimeout(() => {
  215. // SecondGroupSwiper.value.slideTo(index);
  216. // }, 0);
  217. // changeSceneList();
  218. };
  219. const changeSceneList = () => {
  220. let currentList = null;
  221. if (metadata.value.navigationTrees[rootTabIndex.value].children.length && metadata.value.navigationTrees[rootTabIndex.value].children[0].type == "group") {
  222. store.commit("scene/setData", { currentSecondId: metadata.value.navigationTrees[rootTabIndex.value].children[0].id });
  223. //如果有当前视图则选择二级目录
  224. metadata.value.navigationTrees[rootTabIndex.value].children.forEach((item, index) => {
  225. if (item.children.length) {
  226. item.children.forEach((t_item, t_index) => {
  227. if (t_item.id == currentScene.value.id || (t_item.sid && currentScene.value.sid && t_item.sid == currentScene.value.sid)) {
  228. store.commit("scene/setData", { currentSecondId: item.id });
  229. }
  230. });
  231. }
  232. });
  233. currentList = metadata.value.navigationTrees[rootTabIndex.value].children[secondTabIndex.value].children;
  234. store.commit("scene/setCurrentScenesList", currentList);
  235. }
  236. if (secondTabIndex.value == -1) {
  237. console.error("没有二级目录");
  238. let rootList = metadata.value.navigationTrees.find((item) => item.id == currentRootId.value);
  239. store.commit("scene/setCurrentScenesList", rootList.children);
  240. }
  241. nextTick(() => {
  242. initSceneSwiper();
  243. initRootGroupSwiper();
  244. if (metadata.value.navigationTrees[currentRootId.value]?.children[currentSecondId.value]?.type == "group") {
  245. initSecondGroupSwiper();
  246. }
  247. });
  248. };
  249. const initSceneSwiper = () => {
  250. if (SceneSwiper.value) {
  251. SceneSwiper.value.destroy();
  252. SceneSwiper.value = null;
  253. }
  254. if (!currentScenesList.value.length) {
  255. return;
  256. }
  257. nextTick(() => {
  258. SceneSwiper.value = new Swiper(".scene-list", swiperOptions);
  259. let index = currentScenesList.value.findIndex((item) => item.id == currentScene.value.id);
  260. if (index >= 0) {
  261. SceneSwiper.value.slideTo(index);
  262. }
  263. });
  264. };
  265. const tabCurrentScene = (data, index) => {
  266. store.commit("scene/setCurrentScene", data);
  267. // SceneSwiper.value.slideTo(index);
  268. };
  269. const initSecondGroupSwiper = () => {
  270. console.error("initSecondGroupSwiper");
  271. if (SecondGroupSwiper.value) {
  272. SecondGroupSwiper.value.destroy();
  273. SecondGroupSwiper.value = null;
  274. }
  275. nextTick(() => {
  276. SecondGroupSwiper.value = new Swiper(".second-group-list", swiperOptions);
  277. if (secondTabIndex.value >= 0) {
  278. SecondGroupSwiper.value.slideTo(secondTabIndex.value);
  279. }
  280. });
  281. };
  282. const tabCurrentSecondGroup = (data, index) => {
  283. // store.commit("scene/setCurrentScene", data);
  284. SecondGroupSwiper.value.slideTo(index);
  285. };
  286. const initRootGroupSwiper = () => {
  287. // if (rootGroupSwiper.value) {
  288. // rootGroupSwiper.value.destroy();
  289. // rootGroupSwiper.value = null;
  290. // }
  291. // nextTick(() => {
  292. // rootGroupSwiper.value = new Swiper(".root-group-list", swiperOptions);
  293. // });
  294. if (!rootGroupSwiper.value) {
  295. rootGroupSwiper.value = new Swiper(".root-group-list", swiperOptions);
  296. if (rootGroupSwiper.value >= 0) {
  297. rootGroupSwiper.value.slideTo(rootTabIndex.value);
  298. }
  299. initSecondGroupSwiper();
  300. }
  301. };
  302. const tabCurrentRootGroup = (data, index) => {
  303. // store.commit("scene/setCurrentScene", data);
  304. SecondGroupSwiper.value.slideTo(index);
  305. };
  306. onMounted(() => {
  307. useApp().then(async (app) => {
  308. if(currentScene.value.type == '4dkk' && currentScene.value.version == 'V4') {
  309. store.commit("functions/setShowScenesList", false);
  310. }
  311. show.value = true;
  312. console.error("app", show.value);
  313. });
  314. });
  315. </script>
  316. <style lang="scss" scoped>
  317. .bar-list {
  318. position: absolute;
  319. bottom: 88px;
  320. left: 50%;
  321. transform: translateX(-50%);
  322. text-align: center;
  323. overflow: hidden;
  324. max-height: 0;
  325. transition: 0.3s all ease;
  326. z-index: 9;
  327. width: 100%;
  328. .swiper-container {
  329. width: 100%;
  330. position: relative;
  331. margin: 0 auto;
  332. > ul {
  333. > li {
  334. white-space: nowrap;
  335. > span,
  336. > div > span {
  337. cursor: pointer;
  338. display: inline-block;
  339. color: rgba(255, 255, 255, 0.6);
  340. }
  341. &.loopspan {
  342. > span,
  343. > div > span {
  344. animation: 5s wordsLoop linear infinite normal;
  345. }
  346. }
  347. &.active {
  348. > span,
  349. > div > span {
  350. color: rgba(255, 255, 255, 1);
  351. }
  352. }
  353. }
  354. }
  355. }
  356. .top-con {
  357. margin-bottom: 10px;
  358. padding: 10px 0;
  359. width: 100%;
  360. background: rgba(0, 0, 0, 0.5);
  361. }
  362. #swcatalogRoot {
  363. padding: 0 15px;
  364. > ul {
  365. > li {
  366. width: 104px;
  367. background: rgba(0, 0, 0, 0.5);
  368. border-radius: 4px;
  369. padding: 4px 10px;
  370. border: 1px solid rgba(255, 255, 255, 0.5);
  371. box-sizing: border-box;
  372. overflow: hidden;
  373. > span {
  374. width: 100%;
  375. word-break: keep-all;
  376. }
  377. &.active {
  378. border: 1px solid rgba(255, 255, 255, 1);
  379. }
  380. }
  381. }
  382. }
  383. #swSecondary {
  384. margin: 20px auto 10px;
  385. padding: 0 15px;
  386. > ul {
  387. > li {
  388. width: 84px;
  389. box-sizing: border-box;
  390. overflow: hidden;
  391. padding-bottom: 6px;
  392. > span {
  393. width: 100%;
  394. word-break: keep-all;
  395. }
  396. &.active {
  397. position: relative;
  398. &::before {
  399. content: "";
  400. display: inline-block;
  401. position: absolute;
  402. bottom: 0;
  403. width: 20px;
  404. height: 2px;
  405. z-index: 9999;
  406. left: 50%;
  407. transform: translateX(-50%);
  408. background: var(--colors-primary-base);
  409. }
  410. }
  411. }
  412. }
  413. }
  414. #swScenes {
  415. // padding: 0 15px;
  416. > ul {
  417. > li {
  418. cursor: pointer;
  419. width: 72px;
  420. height: 72px;
  421. border-radius: 6px;
  422. border: 1px solid #ffffff;
  423. background-size: cover;
  424. position: relative;
  425. overflow: hidden;
  426. .iconfont {
  427. position: absolute;
  428. left: 4px;
  429. top: 4px;
  430. z-index: 99;
  431. &::after {
  432. background: rgba(0, 0, 0, 0.3);
  433. content: "";
  434. width: 14px;
  435. height: 14px;
  436. display: inline-block;
  437. position: absolute;
  438. top: 50%;
  439. left: 50%;
  440. transform: translate(-50%, -50%);
  441. z-index: -1;
  442. filter: blur(4px);
  443. }
  444. }
  445. > div {
  446. position: absolute;
  447. bottom: 0;
  448. left: 0;
  449. height: 20px;
  450. background: rgba(0, 0, 0, 0.5);
  451. width: 100%;
  452. overflow: hidden;
  453. > span,
  454. div {
  455. width: 100%;
  456. line-height: 20px;
  457. word-break: keep-all;
  458. }
  459. }
  460. &.active {
  461. border: 1px solid var(--colors-primary-base);
  462. > div {
  463. > span {
  464. }
  465. }
  466. }
  467. }
  468. }
  469. }
  470. }
  471. .barshow {
  472. max-height: 190px;
  473. }
  474. @keyframes wordsLoop {
  475. 0% {
  476. transform: translateX(100%);
  477. -webkit-transform: translateX(100%);
  478. }
  479. 100% {
  480. transform: translateX(-180%);
  481. -webkit-transform: translateX(-180%);
  482. }
  483. }
  484. .marquee {
  485. .marquee-text-wrap {
  486. height: 20px;
  487. line-height: 20px;
  488. }
  489. }
  490. </style>
  491. <style>
  492. .marquee-text-text {
  493. padding: 0 5px;
  494. }
  495. </style>