list.vue 14 KB

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