classfiy.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. <script setup lang="ts">
  2. import type { LoadMoreState } from 'wot-design-uni/components/wd-loadmore/types'
  3. import { StaticUrl } from '@/config'
  4. import router from '@/router'
  5. const props = defineProps<{ categoryList: Api.xsbCategories[], hotText: Api.xsbSearchTerm[] }>()
  6. const { statusBarHeight, MenuButtonHeight } = storeToRefs(useSysStore())
  7. const { topNavActive, leftActive, SelectShopInfo } = storeToRefs(useSysXsbStore())
  8. const { cartList } = storeToRefs(useXsbCartStore())
  9. const classfiylist = computed(() => props.categoryList)
  10. const sortClassBtn = ref(1)
  11. const show = ref(false)
  12. const showball = ref(false)
  13. definePage({
  14. name: 'xsb-classfiy',
  15. islogin: false,
  16. style: {
  17. navigationStyle: 'custom',
  18. navigationBarTitleText: '星闪豹分类',
  19. disableScroll: true,
  20. },
  21. })
  22. const isTopLoading = ref(false)
  23. const cartPopup = ref(false)
  24. const basllObj = ref({
  25. left: 0,
  26. top: 0,
  27. x: 0,
  28. y: 0,
  29. })
  30. const x = computed(() => `${basllObj.value.x}px`)
  31. const y = computed(() => `${basllObj.value.y}px`)
  32. const left = computed(() => `${basllObj.value.left}px`)
  33. const top = computed(() => `${basllObj.value.top}px`)
  34. const hasCartData = computed(() => {
  35. if (cartList.value.length) {
  36. return '390rpx'
  37. }
  38. return '0px'
  39. })
  40. const productList = ref<Api.xsbCategoryProductList[]>([])
  41. const topScrollView = ref()
  42. const scrollTop = ref(0)
  43. const goodsLoading = ref<LoadMoreState>()
  44. const navHeight = computed(() => {
  45. return (`${Number(MenuButtonHeight.value) + Number(statusBarHeight.value)}px`)
  46. })
  47. const totalProduct = ref<Api.shoppingCartOrderConfirm>()
  48. function handleTopNavChange(item: Api.xsbCategoriesChildren) {
  49. topNavActive.value = item.code
  50. leftActive.value = item.children[0].code
  51. show.value = false
  52. topScrollView.value = null
  53. nextTick(() => {
  54. topScrollView.value = item.code
  55. })
  56. getProductList()
  57. }
  58. function handleOpen() {
  59. show.value = true
  60. console.log(navHeight.value, '打开')
  61. }
  62. const categories = computed(() => {
  63. return classfiylist.value.find(it => it.code === topNavActive.value)?.children || []
  64. })
  65. const categoriesId = computed(() => {
  66. return categories.value.find(it => it.code === leftActive.value)?.id
  67. })
  68. function handleChange({ value }: { value: string }) {
  69. leftActive.value = value
  70. getProductList()
  71. }
  72. async function getProductList() {
  73. const res = await Apis.xsb.getCategoryProductList({
  74. data: {
  75. categoryId: Number(categoriesId.value),
  76. shopId: Number(SelectShopInfo.value?.shopId) || 1,
  77. channelId: 1,
  78. },
  79. })
  80. productList.value = res.data
  81. goodsLoading.value = 'finished'
  82. isTopLoading.value = false
  83. }
  84. function handlScrollBottom() {
  85. goodsLoading.value = 'loading'
  86. const index = categories?.value.findIndex(it => it.code === leftActive.value)
  87. if (index !== -1) {
  88. handleChange({ value: categories.value[index + 1].code })
  89. }
  90. }
  91. function handleSrollTop() {
  92. isTopLoading.value = true
  93. const index = categories?.value.findIndex(it => it.code === leftActive.value)
  94. if (index !== -1) {
  95. if (index === 0) {
  96. nextTick(() => isTopLoading.value = false)
  97. }
  98. else {
  99. handleChange({ value: categories.value[index - 1].code })
  100. }
  101. }
  102. }
  103. function handleAddCart(event: WechatMiniprogram.TouchEvent, item: Api.xsbCategoryProductList) {
  104. console.log(event.detail.x, item)
  105. if (showball.value)
  106. return
  107. basllObj.value.left = event.detail.x
  108. basllObj.value.top = event.detail.y
  109. showball.value = true
  110. setTimeout(() => {
  111. showball.value = false
  112. }, 500)
  113. }
  114. onMounted(async () => {
  115. if (leftActive.value) {
  116. handleChange({ value: leftActive.value })
  117. }
  118. if (topNavActive.value) {
  119. topScrollView.value = null
  120. nextTick(() => {
  121. topScrollView.value = topNavActive.value
  122. })
  123. }
  124. useXsbCartStore().getCartList()
  125. if (cartList.value.length) {
  126. const query = uni.createSelectorQuery().in(getCurrentInstance())
  127. query.select('.cart-box').boundingClientRect()
  128. query.exec((res) => {
  129. basllObj.value.y = res[0].top
  130. uni.getSystemInfo().then((info) => {
  131. basllObj.value.x = -(info.screenWidth - res[0].left - 15)
  132. })
  133. })
  134. const ids = cartList.value.map(it => it.skuList.map(its => its.id)).flat()
  135. totalProduct.value = await useXsbCartStore().getCartAddGoodsPrice(ids.join(','))
  136. }
  137. })
  138. </script>
  139. <template>
  140. <view class="page-xsb">
  141. <wd-navbar
  142. title="" custom-style="background-color:#F4FFD1" :bordered="false" :z-index="99" safe-area-inset-top
  143. fixed
  144. >
  145. <template #left>
  146. <view class="flex items-center" @click="router.push({ name: 'xsb-search' })">
  147. <view
  148. class="h60rpx w-474rpx flex items-center justify-between border-2rpx border-[var(--them-color)] rounded-40rpx border-solid bg-white pr6rpx"
  149. >
  150. <view class="flex items-center pb14rpx pl24rpx pt16rpx">
  151. <wd-icon name="search" size="14" color="#ccc" />
  152. <view class="search ml12rpx h-full w-full overflow-hidden">
  153. <wd-notice-bar
  154. :text="hotText.map((it) => it.searchName)" custom-class="notice-bar" color="#ccc"
  155. background-color="#fff" direction="vertical"
  156. />
  157. </view>
  158. </view>
  159. <view class="flex items-center pr28rpx">
  160. <wd-divider vertical />
  161. <view class="ml6rpx text-28rpx text-[var(--them-color)] font-semibold">
  162. 搜索
  163. </view>
  164. </view>
  165. </view>
  166. </view>
  167. </template>
  168. </wd-navbar>
  169. <view
  170. class="h162rpx flex items-center justify-between bg-#F4FFD1 pl24rpx"
  171. :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }"
  172. >
  173. <scroll-view
  174. :scroll-into-view-offset="-150" scroll-x enable-passive scroll-with-animation
  175. class="scrollx w-90% flex-shrink-0 whitespace-nowrap" :scroll-into-view="`id${topScrollView}`"
  176. >
  177. <view class="flex items-end pb10rpx">
  178. <view
  179. v-for="item in classfiylist" :id="`id${item.code}`" :key="item.code"
  180. class="mr24rpx flex flex-col items-center justify-center" @click="handleTopNavChange(item)"
  181. >
  182. <image
  183. :src="item.icon"
  184. :class="[topNavActive == item.code ? 'overflow-hidden border-solid border-[var(--them-color)] border-2rpx rounded-26rpx h84rpx w-84rpx' : 'h72rpx w-72rpx']"
  185. />
  186. <view
  187. class="mt16rpx text-22rpx"
  188. :class="[topNavActive == item.code ? 'bg-[var(--them-color)] rounded-18rpx px-8rpx py2rpx text-white text-24rpx' : '']"
  189. >
  190. {{ item.name }}
  191. </view>
  192. </view>
  193. </view>
  194. </scroll-view>
  195. <view
  196. class="right-nav box-border h144rpx w64rpx flex flex-shrink-0 flex-col items-center justify-center px20rpx text-24rpx font-semibold"
  197. @click="handleOpen"
  198. >
  199. 展开
  200. <image :src="`${StaticUrl}/xia.png`" class="mt20rpx h20rpx w20rpx" />
  201. </view>
  202. <wd-popup v-model="show" position="top" custom-style="border-radius:32rpx;">
  203. <view
  204. class="box-border bg-#F4FFD1 p24rpx"
  205. :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }"
  206. >
  207. <view class="mb24rpx mt24rpx text-28rpx font-semibold">
  208. 全部一级分类
  209. </view>
  210. <view class="h400rpx overflow-y-scroll">
  211. <view class="grid grid-cols-5 box-border gap-x-24rpx gap-y-24rpx">
  212. <view
  213. v-for="item in classfiylist" :key="item.code"
  214. class="box-border h150rpx w114rpx flex flex-col items-center justify-center"
  215. @click="handleTopNavChange(item)"
  216. >
  217. <image
  218. :src="item.icon"
  219. :class="[topNavActive == item.code ? 'overflow-hidden border-solid border-[var(--them-color)] border-2rpx rounded-26rpx h88rpx w-88rpx' : 'h76rpx w-76rpx']"
  220. />
  221. <view
  222. class="mt16rpx whitespace-nowrap text-nowrap text-22rpx"
  223. :class="[topNavActive == item.code ? 'bg-[var(--them-color)] rounded-18rpx px-8rpx py2rpx text-white text-24rpx' : '']"
  224. >
  225. {{ item.name }}
  226. </view>
  227. </view>
  228. </view>
  229. </view>
  230. <view class="mt24rpx w-full flex items-center justify-center" @click="show = false">
  231. <view class="mr8rpx text-24rpx">
  232. 点击收起
  233. </view>
  234. <wd-icon name="arrow-down" size="18px" />
  235. </view>
  236. </view>
  237. </wd-popup>
  238. </view>
  239. <view class="wraper">
  240. <wd-sidebar v-model="leftActive" @change="handleChange">
  241. <wd-sidebar-item
  242. v-for="(item) in categories" :key="item.code"
  243. custom-style="font-size:28rpx; white-space: nowrap;text-overflow: ellipsis;overflow: hidden;"
  244. :value="item.code" :label="item.name"
  245. />
  246. </wd-sidebar>
  247. <view class="content">
  248. <view class="p20rpx">
  249. <image :src="`${StaticUrl}/class.png`" class="h144rpx w-full" />
  250. <view class="my20rpx flex items-center justify-end rounded-16rpx bg-#F6F6F6 px20rpx py8rpx">
  251. <view class="mr40rpx text-24rpx">
  252. 销量
  253. </view>
  254. <wd-sort-button v-model="sortClassBtn" :line="false" title="价格" />
  255. </view>
  256. </view>
  257. <scroll-view
  258. :lower-threshold="10"
  259. :refresher-triggered="isTopLoading" :scroll-top="scrollTop"
  260. :style="{ height: `calc(100vh - ${navHeight} - 700rpx)` }" enable-passive scroll-with-animation scroll-y scroll-anchoring refresher-enabled :throttle="false"
  261. @refresherrefresh="handleSrollTop" @scrolltolower="handlScrollBottom"
  262. >
  263. <view v-if="productList.length" class="p20rpx">
  264. <view v-for="item in productList" :key="item.id" class="relative">
  265. <view class="flex" @click="router.push({ name: 'xsb-goods', params: { id: String(item.id) } })">
  266. <view class="mr20rpx h172rpx w172rpx flex-shrink-0 overflow-hidden rounded-16rpx bg-#F6F6F6">
  267. <image :src="item.pic" lazy-load class="h-full w-full" />
  268. </view>
  269. <view class="flex-1">
  270. <view class="text-left text-28rpx font-semibold">
  271. <!-- <view v-for="i in 2" :key="i" class="mr5px inline-block">
  272. <wd-tag type="primary">
  273. 新品{{ i }}
  274. </wd-tag>
  275. </view> -->
  276. {{ item.prodName }}
  277. </view>
  278. <view class="text-22rpx text-#AAAAAA">
  279. <view class="mt10rpx flex items-center">
  280. <view>{{ item.spec }}</view>
  281. </view>
  282. <view class="mt6rpx">
  283. 已售 {{ item.soldNum }}+
  284. </view>
  285. </view>
  286. <view class="flex items-center justify-between">
  287. <view class="text-#FF4D3A font-semibold">
  288. <text class="text-24rpx">
  289. </text> <text class="text-36rpx">
  290. {{ item.channelProdPrice }}
  291. </text>
  292. </view>
  293. <view v-if="item.spuStock" @click.stop="handleAddCart($event, item)">
  294. <image :src="`${StaticUrl}/cart-yes.png`" class="h52rpx w52rpx" />
  295. </view>
  296. <view v-else>
  297. <image :src="`${StaticUrl}/cart-no.png`" class="h52rpx w52rpx" />
  298. </view>
  299. </view>
  300. </view>
  301. </view>
  302. <view class="line">
  303. <wd-divider color="#F0F0F0" />
  304. </view>
  305. <view v-if="!item.spuStock" class="absolute left-0 top-0 z-1 h-full w-full flex items-center bg-[rgba(255,255,255,.6)]">
  306. <view class="h172rpx w172rpx flex items-center justify-center">
  307. <view class="h36rpx w112rpx flex items-center justify-center rounded-16rpx bg-[rgba(0,0,0,.6)] text-28rpx text-white">
  308. 已售罄
  309. </view>
  310. </view>
  311. </view>
  312. </view>
  313. <wd-loadmore :state="goodsLoading" />
  314. </view>
  315. <wd-status-tip v-else image="content" tip="暂无内容" />
  316. </scroll-view>
  317. </view>
  318. </view>
  319. <view
  320. v-if="cartList.length"
  321. class="fixedShadow fixed bottom-60rpx left-0 z-100 box-border w-full flex items-center justify-between rounded-t-16rpx bg-white px24rpx pb60rpx pt10rpx"
  322. >
  323. <view class="ios w-full flex items-center justify-between">
  324. <view class="flex items-center" @click="cartPopup = true">
  325. <image :src="`${StaticUrl}/cart-lanzi.png`" class="cart-box h100rpx w100rpx" />
  326. </view>
  327. <view class="flex items-center">
  328. <view class="flex items-center font-semibold">
  329. <view class="text-22rpx text-#222">
  330. 总计:
  331. </view>
  332. <view class="flex items-baseline text-24rpx text-#FF4A39">
  333. <text class="text-36rpx">
  334. {{ totalProduct?.price || '0.00' }}
  335. </text>
  336. </view>
  337. </view>
  338. <view class="ml20rpx w160rpx">
  339. <wd-button block size="large" @click="totalProduct?.price && router.push({ name: 'xsb-confirmOrder', params: { data: JSON.stringify(totalProduct) } })">
  340. 结算
  341. </wd-button>
  342. </view>
  343. </view>
  344. </view>
  345. </view>
  346. <Zpopup v-model="cartPopup">
  347. <view class="ios h800rpx">
  348. <view class="p24rpx">
  349. 1212
  350. </view>
  351. </view>
  352. </Zpopup>
  353. <view v-if="showball" class="ball">
  354. <image :src="`${StaticUrl}/cart-yes.png`" class="cart-img h52rpx w52rpx" />
  355. </view>
  356. </view>
  357. </template>
  358. <style scoped lang="scss">
  359. .right-nav {
  360. background: linear-gradient(180deg, #FAFFEC 0%, #F6FFDE 11%, #E7FFA5 100%);
  361. box-shadow: -10rpx 0rpx 12rpx 2rpx rgba(0, 0, 0, 0.07);
  362. }
  363. .fixedShadow{
  364. box-shadow: 0rpx -6rpx 12rpx 2rpx rgba(0,0,0,0.05);
  365. }
  366. .wraper {
  367. display: flex;
  368. height: calc(100vh - var(--window-top) - v-bind(navHeight) - v-bind(hasCartData));
  369. height: calc(100vh - var(--window-top) - constant(safe-area-inset-bottom) - v-bind(navHeight) - v-bind(hasCartData));
  370. height: calc(100vh - var(--window-top) - env(safe-area-inset-bottom) - v-bind(navHeight) - v-bind(hasCartData));
  371. }
  372. .content {
  373. flex: 1;
  374. background: #fff;
  375. }
  376. @keyframes moveX {
  377. to {
  378. transform: translateX(v-bind(x));
  379. }
  380. }
  381. @keyframes moveY {
  382. to {
  383. transform: translateY(v-bind(y));
  384. }
  385. }
  386. .ball {
  387. position: fixed;
  388. z-index: 80;
  389. left: v-bind(left);
  390. top: v-bind(top);
  391. animation: moveX .5s linear forwards;
  392. }
  393. .cart-img {
  394. animation: moveY .5s cubic-bezier(1,-1.26,1,1) forwards;
  395. }
  396. </style>