list.vue 14 KB

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