list.vue 12 KB

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