index.vue 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. <!-- @format -->
  2. <script lang="ts" setup>
  3. import { ref, watch } from 'vue'
  4. import { getCategoryListApi } from '~/api/model/common'
  5. import { useCommonStore } from '@/stores/modules/common'
  6. import {
  7. getFeatureListApi,
  8. } from '~/api/model/feature'
  9. getCategories()
  10. const route = useRoute()
  11. const commonStore = useCommonStore()
  12. const categories = ref<any>([])
  13. const featureList = ref<any>([])
  14. const categoryList = ref<any>([])
  15. const secondCategoryList = ref<any>([])
  16. const status = ref(false)
  17. const firstCategory = ref()
  18. const headerBg = ref('#fff')
  19. const router = useRouter()
  20. const secondCategoryCache = new Map()
  21. const list = [
  22. // {
  23. // label: 'Collections',
  24. // link: '/collections',
  25. // key: 'Featured',
  26. // slug: 'featured',
  27. // type: '',
  28. // },
  29. {
  30. label: 'Brands',
  31. link: '/suppliers/all-brands',
  32. key: 'Brands',
  33. slug: 'brands',
  34. type: '',
  35. },
  36. ]
  37. async function getCategories() {
  38. const data = await getCategoryListApi({ all: false })
  39. data.forEach((item: any) => {
  40. item.link = 'categories-id'
  41. item.type = 'category'
  42. })
  43. categoryList.value = data
  44. categories.value = [...list, ...data]
  45. }
  46. function setSelectedCategory(data: any) {
  47. const linkName = useRoute().name
  48. if (linkName === data[0].link)
  49. commonStore.setSelectedCategory(useRoute().params.id)
  50. else
  51. commonStore.setSelectedCategory('')
  52. }
  53. async function getFeatureList() {
  54. const params = {
  55. pageNo: 1,
  56. pageSize: 4,
  57. }
  58. const res: any = await getFeatureListApi(params)
  59. featureList.value = res.records
  60. }
  61. getFeatureList()
  62. async function onSelectedCategory(item: any) {
  63. const keys = list.map(item => item.key)
  64. firstCategory.value = item
  65. if (keys.includes(item.key)) {
  66. status.value = false
  67. return
  68. }
  69. // 缓存当前下的分类
  70. if (secondCategoryCache.get(item.key)) {
  71. secondCategoryList.value = secondCategoryCache.get(item.key)
  72. status.value = true
  73. return
  74. }
  75. const data = await getCategoryListApi({ code: item.code, all: true })
  76. secondCategoryList.value = data
  77. secondCategoryCache.set(item.key, data)
  78. status.value = true
  79. }
  80. watch(
  81. () => route.path,
  82. (value: any) => {
  83. const newList = list.filter((item: any) => item.link === value)
  84. if (newList.length) {
  85. commonStore.setSelectedCategory(newList[0].slug)
  86. }
  87. else {
  88. if (!categoryList.value.length)
  89. return
  90. setSelectedCategory(categoryList.value)
  91. }
  92. },
  93. {
  94. immediate: true,
  95. },
  96. )
  97. // function handleSelectedCategory(key: any) {
  98. // router.push({
  99. // path: `/categories/${encodeURIComponent(firstCategory.value.slug)}`,
  100. // query: {
  101. // secondKey: key,
  102. // },
  103. // })
  104. // status.value = false
  105. // }
  106. // function onClickFeature(item: any) {
  107. // router.push({
  108. // path: `/collections/${item.slug}`,
  109. // })
  110. // status.value = false
  111. // }
  112. watch(() => router.currentRoute.value.path, (v: any) => {
  113. if (v === '/')
  114. headerBg.value = '#FAF5F1'
  115. else
  116. headerBg.value = '#fff'
  117. }, {
  118. immediate: true,
  119. })
  120. </script>
  121. <template>
  122. <div class="mt-30px w-1400px mx-auto pb-30px">
  123. <div class="flex justify-center gap-x-50px" @mouseleave="status = false">
  124. <div
  125. v-for="item in categories"
  126. :key="item.key"
  127. class="category-item position-relative cursor-pointer text-#666666"
  128. :class="commonStore.selectedCategory === item.slug && 'active fw-500'"
  129. @mouseover="onSelectedCategory(item)"
  130. >
  131. <el-dropdown v-if="item.type">
  132. <NuxtLink :to="{ name: item.link, params: { id: item.slug } }">
  133. {{ item.label }}
  134. </NuxtLink>
  135. <template v-if="item.type" #dropdown>
  136. <el-dropdown-menu>
  137. <el-dropdown-item v-for="item, index in secondCategoryList" :index="item.key">
  138. <nuxt-link :to="{ name: 'categories-id', params: { id: firstCategory?.slug }, query: { secondKey: item.key } }">
  139. {{ item.title }}
  140. </nuxt-link>
  141. </el-dropdown-item>
  142. </el-dropdown-menu>
  143. </template>
  144. </el-dropdown>
  145. <NuxtLink v-else :to="item.link">
  146. {{ item.label }}
  147. </NuxtLink>
  148. </div>
  149. </div>
  150. <!-- <div :class="status ? 'h-420px opacity-100' : 'h-0 overflow-hidden opacity-0'" :style="{ backgroundColor: headerBg }" class="transition-duration-300 pos-absolute left-0 top-120px right-0 z-1000 custom-banner" @mouseover="status = true" @mouseleave="status = false">
  151. <div class=" w-1400px mx-auto py-30px ">
  152. <div class="flex">
  153. <div class="w-250px pos-relative after:pos-absolute after:content-empty after:top-0 after:bottom-0 after:w-1px after:right-0 after:bg-#D8D8D8">
  154. <div class="fw-600 text-22px text-#333 !mb-30px">
  155. Categories
  156. </div>
  157. <div class="h-300px overflow-y-scroll hidden-scrollbar">
  158. <banner-category-sider :list="secondCategoryList" @click="handleSelectedCategory" />
  159. </div>
  160. </div>
  161. <div class="ml-40px w-full">
  162. <div class="fw-600 text-22px text-#333">
  163. Collection
  164. </div>
  165. <div class="flex mt-30px">
  166. <div class="grid grid-gap-x-20px grid-gap-y-20px flex-1 grid-cols-2">
  167. <div v-for="(item, index) in featureList" :key="item.id" :class="(index === 1 || index === 2) ? 'bg-#F4F6F6' : 'bg-#FFF5EF'" class="p-20px b-rd-10px flex justify-between h-140px">
  168. <div>
  169. <div class="fw-600 text-20px text-#363C40 custom-title-font w-200px line-clamp-2">
  170. {{ item.headImageText }}
  171. </div>
  172. <div class="mt-10px text-#C58C64 underline cursor-pointer" @click="onClickFeature(item)">
  173. Shop Now
  174. </div>
  175. </div>
  176. <img :src="item.thumbnailUrl" class="w-120px h-100px b-rd-4px object-cover" alt="" srcset="">
  177. </div>
  178. </div>
  179. <div class="w-285px text-center ml-40px">
  180. <nuxt-link to="/suppliers/all-brands">
  181. <img src="~/assets/images/header_banner.png" class="w-260px h-260px cursor-pointer" alt="">
  182. <div class="mt-18px">
  183. Shop Top Brands
  184. </div>
  185. </nuxt-link>
  186. </div>
  187. </div>
  188. </div>
  189. </div>
  190. </div>
  191. </div> -->
  192. </div>
  193. </template>
  194. <style lang="less" scoped>
  195. .category-item{
  196. &::before{
  197. position: absolute;
  198. content: "";
  199. bottom: 20%;
  200. top: 20%;
  201. right: -26px;
  202. width: 1px;
  203. background: #CCCCCC;
  204. border-radius: 2px;
  205. }
  206. &:last-child::before{
  207. display: none;
  208. }
  209. }
  210. .category-item:hover,
  211. .active {
  212. color: var(--primary-hover-color);
  213. }
  214. .category-item:hover::after,
  215. .active::after {
  216. position: absolute;
  217. content: "";
  218. bottom: -10px;
  219. left: 0;
  220. width: 100%;
  221. height: 2px;
  222. background: var(--primary-hover-color);
  223. border-radius: 2px;
  224. z-index: 100;
  225. transition: height 0.3s, background 0.3s; /* 明确过渡效果 */
  226. }
  227. .hidden-scrollbar{
  228. &::-webkit-scrollbar{
  229. display: none;
  230. }
  231. }
  232. .custom-banner{
  233. box-shadow: 0px 3px 6px 1px rgba(0,0,0,0.06);
  234. }
  235. </style>