classfiy.vue 13 KB

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