list.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  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. if (window.sencordNatSwiper) {
  263. // window.sencordNatSwiper = null;
  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. console.warn("current-sencordNatSwiper-index", current);
  302. window.sencordNatSwiper.slideTo(current);
  303. }
  304. });
  305. };
  306. onMounted(() => {
  307. useApp().then(async (app) => {
  308. show.value = true;
  309. });
  310. watchEffect(() => {
  311. if (
  312. metadata.value.catalogRoot &&
  313. unref(metadata.value.catalogRoot).length > 0
  314. ) {
  315. initMainSwiper();
  316. }
  317. });
  318. watch(currentSecondary, () => {
  319. if (unref(secondaryList).length > 1) {
  320. initsencordNatSwiper();
  321. sencordNatSwiperFocus();
  322. } else {
  323. if (window.sencordNatSwiper) {
  324. console.warn("destroy-sencordNatSwiper");
  325. window.sencordNatSwiper.update();
  326. window.sencordNatSwiper.slideReset();
  327. }
  328. }
  329. });
  330. watch(currentScenesList, () => {
  331. initScenesSwiper();
  332. });
  333. });
  334. </script>
  335. <style lang="scss" scoped>
  336. $width: 1150px;
  337. .bar-list {
  338. position: absolute;
  339. bottom: 68px;
  340. left: 50%;
  341. transform: translateX(-50%);
  342. text-align: center;
  343. max-width: $width;
  344. overflow: hidden;
  345. max-height: 0;
  346. transition: 0.3s all ease;
  347. z-index: 9;
  348. .swiper-container {
  349. width: 100%;
  350. position: relative;
  351. margin: 0 auto;
  352. > ul {
  353. > li {
  354. white-space: nowrap;
  355. > span,
  356. > div > span {
  357. cursor: pointer;
  358. display: inline-block;
  359. color: rgba(255, 255, 255, 0.6);
  360. }
  361. &.loopspan {
  362. > span,
  363. > div > span {
  364. animation: 10s wordsLoop linear infinite normal;
  365. }
  366. }
  367. &.active {
  368. > span,
  369. > div > span {
  370. color: rgba(255, 255, 255, 1);
  371. }
  372. }
  373. }
  374. }
  375. }
  376. .top-con {
  377. margin: 0 auto 10px;
  378. padding: 10px 0;
  379. background: linear-gradient(
  380. 268deg,
  381. rgba(0, 0, 0, 0) 0%,
  382. rgba(0, 0, 0, 0.4) 25%,
  383. rgba(0, 0, 0, 0.4) 75%,
  384. rgba(0, 0, 0, 0) 100%
  385. );
  386. }
  387. #swcatalogRoot {
  388. > ul {
  389. > li {
  390. width: 104px;
  391. background: rgba(0, 0, 0, 0.5);
  392. border-radius: 4px;
  393. padding: 4px 10px;
  394. border: 1px solid rgba(255, 255, 255, 0.5);
  395. box-sizing: border-box;
  396. overflow: hidden;
  397. > span {
  398. width: 100%;
  399. word-break: keep-all;
  400. }
  401. &.active {
  402. border: 1px solid rgba(255, 255, 255, 1);
  403. }
  404. }
  405. }
  406. }
  407. #swSecondary {
  408. margin: 20px auto 10px;
  409. > ul {
  410. > li {
  411. width: 84px;
  412. box-sizing: border-box;
  413. overflow: hidden;
  414. padding-bottom: 6px;
  415. > span {
  416. width: 100%;
  417. word-break: keep-all;
  418. }
  419. &.active {
  420. position: relative;
  421. &::before {
  422. content: "";
  423. display: inline-block;
  424. position: absolute;
  425. bottom: 0;
  426. width: 20px;
  427. height: 2px;
  428. z-index: 9999;
  429. left: 50%;
  430. transform: translateX(-50%);
  431. background: var(--colors-primary-base);
  432. }
  433. }
  434. }
  435. }
  436. }
  437. #swScenes {
  438. > ul {
  439. > li {
  440. cursor: pointer;
  441. width: 72px;
  442. height: 72px;
  443. border-radius: 6px;
  444. border: 1px solid #ffffff;
  445. background-size: cover;
  446. position: relative;
  447. overflow: hidden;
  448. .iconfont {
  449. position: absolute;
  450. left: 4px;
  451. top: 4px;
  452. z-index: 99;
  453. &::after {
  454. background: rgba(0, 0, 0, 0.3);
  455. content: "";
  456. width: 14px;
  457. height: 14px;
  458. display: inline-block;
  459. position: absolute;
  460. top: 50%;
  461. left: 50%;
  462. transform: translate(-50%, -50%);
  463. z-index: -1;
  464. filter: blur(4px);
  465. }
  466. }
  467. > div {
  468. position: absolute;
  469. bottom: 0;
  470. left: 0;
  471. height: 20px;
  472. background: rgba(0, 0, 0, 0.5);
  473. width: 100%;
  474. overflow: hidden;
  475. > span {
  476. // width: 100%;
  477. line-height: 20px;
  478. word-break: keep-all;
  479. white-space: normal;
  480. }
  481. }
  482. &.active {
  483. border: 1px solid var(--colors-primary-base);
  484. > div {
  485. > span {
  486. }
  487. }
  488. }
  489. }
  490. }
  491. }
  492. }
  493. .barshow {
  494. max-height: 190px;
  495. }
  496. @keyframes wordsLoop {
  497. 0% {
  498. transform: translateX(100%);
  499. -webkit-transform: translateX(100%);
  500. }
  501. 100% {
  502. transform: translateX(-180%);
  503. -webkit-transform: translateX(-180%);
  504. }
  505. }
  506. .marquee{
  507. .marquee-text-wrap {
  508. height: 20px;
  509. line-height: 20px;
  510. }
  511. }
  512. </style>