index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. <script setup lang="ts">
  2. import selectSku from '../components/select-sku/index.vue'
  3. import { StaticUrl } from '@/config'
  4. import router from '@/router'
  5. const { statusBarHeight, MenuButtonHeight } = useSysStore()
  6. definePage({
  7. name: 'xsb-goods',
  8. islogin: false,
  9. style: {
  10. navigationStyle: 'custom',
  11. navigationBarTitleText: '星闪豹商品详情',
  12. },
  13. })
  14. const selectGoods = ref(false)
  15. const SelectGoodsNum = ref(1)
  16. const specId = ref()
  17. const { userInfo } = storeToRefs(useUserStore())
  18. const { SelectShopInfo } = storeToRefs(useSysXsbStore())
  19. const { getTotalNum } = storeToRefs(useSmqjhCartStore())
  20. // const goodsTab = ref(0)
  21. const current = ref<number>(0)
  22. const currentDetaile = ref(0)
  23. // const goodsTabList = ref([
  24. // { title: '相似商品', id: 0 },
  25. // { title: '经常一起买', id: 1 },
  26. // ])
  27. const goodsDetaileTabList = ref([
  28. { title: '商品', id: 0 },
  29. { title: '评价', id: 1 },
  30. { title: '详情', id: 2 },
  31. { title: '推荐', id: 3 },
  32. ])
  33. const goodsTabData = ref([])
  34. const isShowTab = ref(false)
  35. // const currentTabGoods = ref(0)
  36. const goodsId = ref()
  37. onLoad((option: any) => {
  38. goodsId.value = option.id
  39. console.log(option, 'option====================')
  40. if (option.q) {
  41. const { id, storeId, channelId } = parseUrlParams(option.q)
  42. goodsId.value = id
  43. getGoodsDetaile(Number(storeId), Number(channelId))
  44. }
  45. else {
  46. getGoodsDetaile(option.storeId, option.channelId)
  47. }
  48. })
  49. const goodsInfo = ref<Api.xsbProductDetail>()
  50. const isLoging = ref(false)
  51. const swiperList = computed(() => {
  52. if (goodsInfo.value) {
  53. return goodsInfo.value.imgs?.split(',')
  54. }
  55. return []
  56. })
  57. const goodsContent = computed(() => {
  58. if (goodsInfo.value?.content) {
  59. return goodsInfo.value.content.replace(
  60. /<img/gi,
  61. '<img style="max-width:100%;height:auto" ',
  62. )
  63. }
  64. return '暂无数据'
  65. })
  66. onMounted(() => {
  67. // goodsTabData.value = splitArrayToPages([{ id: 1, name: '商品' }, { id: 2, name: '商品' }, { id: 3, name: '商品' }, { id: 4, name: '商品' }, { id: 8, name: '商品' }, { id: 5, name: '商品' }, { id: 10, name: '商品' }, { id: 6, name: '商品' }, { id: 9, name: '商品' }, { id: 7, name: '商品' }, { id: 11, name: '商品' }])
  68. console.log(goodsTabData.value, 'goodsTabData')
  69. })
  70. // function splitArrayToPages<T>(arr: T[]): T[][] {
  71. // const PAGE_SIZE = 6
  72. // const MAX_PAGES = 3
  73. // const result: T[][] = []
  74. // for (let i = 0; i < Math.min(MAX_PAGES, Math.ceil(arr.length / PAGE_SIZE)); i++) {
  75. // const start = i * PAGE_SIZE
  76. // const end = start + PAGE_SIZE
  77. // result.push(arr.slice(start, end))
  78. // }
  79. // return result
  80. // }
  81. onPageScroll((e) => {
  82. if (e.scrollTop >= 315) {
  83. isShowTab.value = true
  84. }
  85. else {
  86. isShowTab.value = false
  87. }
  88. })
  89. function handleGoCurren(id: number) {
  90. currentDetaile.value = id
  91. uni.pageScrollTo({ selector: `.view-${id}` })
  92. }
  93. async function getGoodsDetaile(shopId: number, channelId: number) {
  94. const res = await Apis.xsb.getProductDetail({ data: { id: goodsId.value, shopId: Number(SelectShopInfo.value?.shopId) || shopId || 2, channelId: userInfo.value.channelId || channelId || 1 } })
  95. console.log(res, '请求')
  96. if (!res.data) {
  97. useGlobalToast().show('暂无该商品查看权限!')
  98. return
  99. }
  100. goodsInfo.value = res.data
  101. specId.value = res.data.skuList?.[0].skuId
  102. }
  103. async function handleConfimOrder() {
  104. if (Number(unref(goodsInfo)?.spuStock) < unref(SelectGoodsNum)) {
  105. useGlobalToast().show('库存不足,请调整购买数量')
  106. return
  107. }
  108. await useUserStore().checkLogin()
  109. isLoging.value = true
  110. try {
  111. const res = await Apis.xsb.skuOrderConfirm({
  112. data: {
  113. skuId: specId.value,
  114. num: SelectGoodsNum.value,
  115. channelId: unref(userInfo).channelId,
  116. dvyType: 3,
  117. shopId: unref(goodsInfo)?.shopId || 2,
  118. },
  119. })
  120. if (Number(res.data.sku?.shopSkuStocks) < unref(SelectGoodsNum)) {
  121. useGlobalToast().show('库存不足,请调整购买数量')
  122. isLoging.value = false
  123. return
  124. }
  125. router.push({
  126. name: 'xsb-confirmOrder',
  127. params: { data: JSON.stringify({ ...res.data, skuList: [{ ...res.data.sku, num: SelectGoodsNum.value, shopId: unref(goodsInfo)?.shopId || 2 }] }) },
  128. })
  129. }
  130. finally {
  131. isLoging.value = false
  132. }
  133. }
  134. async function handleAddCart() {
  135. if (Number(unref(goodsInfo)?.spuStock) < unref(SelectGoodsNum)) {
  136. useGlobalToast().show('库存不足,请调整购买数量')
  137. return
  138. }
  139. isLoging.value = true
  140. try {
  141. await useSmqjhCartStore().addCart(specId.value, unref(SelectGoodsNum), unref(SelectShopInfo.value.shopId || 2), 'XSB')
  142. selectGoods.value = false
  143. useGlobalToast().show('添加成功')
  144. isLoging.value = false
  145. }
  146. catch (error) {
  147. isLoging.value = false
  148. console.log(error)
  149. }
  150. }
  151. function handleClick() {
  152. uni.previewImage({ urls: swiperList.value as string[] })
  153. }
  154. </script>
  155. <template>
  156. <page-meta :page-style="selectGoods ? 'overflow: hidden;' : '' " />
  157. <view class="page-xsb">
  158. <wd-navbar
  159. title="商品详情" :custom-style="`background-color:${isShowTab ? '#FFF !important' : 'transparent !important'}`" :bordered="false" :z-index="99"
  160. safe-area-inset-top left-arrow fixed @click-left="router.back()"
  161. />
  162. <template v-if="goodsInfo">
  163. <wd-swiper v-model:current="current" :list="swiperList" :autoplay="false" :height="375" @click="handleClick">
  164. <template #indicator="{ current: currentIndex, total }">
  165. <view class="custom-indicator absolute bottom-60rpx right-20rpx px26rpx">
  166. {{ currentIndex + 1 }}/{{ total }}
  167. </view>
  168. </template>
  169. </wd-swiper>
  170. <view class="header view-0 relative z-3 rounded-t-32rpx px24rpx pt24rpx -mt30rpx">
  171. <view class="flex items-center justify-between">
  172. <view v-if="goodsInfo?.isMember" class="flex items-end text-#FF4D3A font-semibold">
  173. <view class="text-24rpx">
  174. </view>
  175. <view class="text-36rpx line-height-[36rpx]">
  176. {{ goodsInfo?.memberPrice }}
  177. </view>
  178. <view class="ml-14rpx text-24rpx text-#AAA line-through">
  179. ¥{{ goodsInfo?.channelProdPrice }}
  180. </view>
  181. </view>
  182. <view v-else class="flex items-end text-#FF4D3A font-semibold">
  183. <view class="text-24rpx">
  184. </view>
  185. <view class="text-36rpx line-height-[36rpx]">
  186. {{ goodsInfo?.channelProdPrice }}
  187. </view>
  188. </view>
  189. <!-- <view>
  190. <image class="h40rpx w40rpx" :src="`${StaticUrl}/collect-yes.png`" />
  191. </view> -->
  192. </view>
  193. </view>
  194. <view class="sticky left-0 z-99 box-border h84rpx w-full flex items-center justify-between bg-white px24rpx" :style="{ top: `${statusBarHeight + MenuButtonHeight}px`, opacity: isShowTab ? 1 : 0 }" :class="[isShowTab ? '' : '']">
  195. <view v-for="item in goodsDetaileTabList" :key="item.id" class="relative flex items-center text-32rpx font-semibold" @click="handleGoCurren(item.id)">
  196. {{ item.title }}
  197. <view v-show="currentDetaile == item.id" class="line absolute left-50% h12rpx w40rpx rounded-8rpx bg-[var(--them-color)] -bottom-15rpx -translate-x-50%" />
  198. </view>
  199. </view>
  200. <view class="px24rpx">
  201. <view class="-mt64rpx">
  202. <view class="text-left text-28rpx font-semibold">
  203. <!-- <view v-for="i in 2" :key="i" class="mr5px inline-block">
  204. <wd-tag type="primary">
  205. 新品{{ i }}
  206. </wd-tag>
  207. </view> -->
  208. {{ goodsInfo.prodName }}
  209. </view>
  210. </view>
  211. <!-- <view class="mt16rpx text-24rpx text-#AAAAAA">
  212. 优选产地,皮薄核小,维c满满
  213. </view> -->
  214. <!-- <view class="mt20rpx flex items-center">
  215. <view
  216. v-for="item in 2" :key="item"
  217. class="mr16rpx flex items-center justify-center border-1rpx border-#BC9264 rounded-8rpx border-solid bg-[#FFF5E9] px16rpx py6rpx text-24rpx text-#BC9264 font-semibold"
  218. >
  219. 一口化渣{{ item }}
  220. </view>
  221. </view> -->
  222. <view class="mt20rpx flex items-center justify-between rounded-16rpx bg-white p24rpx">
  223. <view class="flex items-center">
  224. <view class="flex-shrink-0">
  225. <view class="text-28rpx font-semibold">
  226. {{ goodsInfo.brandName }}
  227. </view>
  228. <view class="mt16rpx text-24rpx text-#AAAAAA">
  229. 品牌
  230. </view>
  231. </view>
  232. </view>
  233. <view class="mx24rpx">
  234. <wd-divider vertical color="#F0F0F0" />
  235. </view>
  236. <view class="flex items-center">
  237. <view class="flex-shrink-0">
  238. <view class="text-28rpx font-semibold">
  239. {{ goodsInfo.skuList[0].weight }}g
  240. </view>
  241. <view class="mt16rpx text-24rpx text-#AAAAAA">
  242. 重量
  243. </view>
  244. </view>
  245. </view>
  246. <view class="mx24rpx">
  247. <wd-divider vertical color="#F0F0F0" />
  248. </view>
  249. <view class="flex items-center">
  250. <view class="flex-shrink-0">
  251. <view class="text-28rpx font-semibold">
  252. {{ goodsInfo.skuList[0].weightUnit }}
  253. </view>
  254. <view class="mt16rpx text-24rpx text-#AAAAAA">
  255. 单位
  256. </view>
  257. </view>
  258. </view>
  259. <wd-icon name="chevron-right" size="22px" color="#AAAAAA" />
  260. </view>
  261. <!-- <view class="view-1 mt20rpx flex items-center rounded-16rpx bg-white p24rpx">
  262. <view class="w-full flex items-center justify-between">
  263. <view class="text-32rpx font-semibold">
  264. 评价(10万+)
  265. </view>
  266. <view class="flex items-center">
  267. <view class="mr10rpx flex items-center text-28rpx text-#FF4D3A">
  268. <view>好评率</view>
  269. <view class="ml5rpx font-semibold">
  270. 99.8%
  271. </view>
  272. </view>
  273. <wd-icon name="chevron-right" size="22px" color="#AAAAAA" />
  274. </view>
  275. </view>
  276. </view> -->
  277. <!-- <view class="mt20rpx rounded-16rpx bg-white p24rpx">
  278. <wd-tabs v-model="goodsTab">
  279. <block v-for="item in goodsTabList" :key="item.id">
  280. <wd-tab :title="item.title" />
  281. </block>
  282. </wd-tabs>
  283. <swiper class="mt20rpx h900rpx" indicator-active-color="var(--them-color)" @change="(e) => currentTabGoods = e.detail.current">
  284. <swiper-item
  285. v-for="items in goodsTabData" :key="items"
  286. class="grid grid-cols-3 items-start gap-x-16rpx gap-y-16rpx pb20rpx"
  287. >
  288. <view
  289. v-for="goods in items" :key="goods"
  290. class="box-border w204rpx border border-2rpx border-#F0F0F0 rounded-16rpx border-solid px4rpx py8rpx"
  291. >
  292. <image lazy-load :src="`${StaticUrl}/shu.png`" class="h188rpx w198rpx" />
  293. <view class="mt20rpx px16rpx text-24rpx font-semibold">
  294. 新鲜现宰杀肋排200g
  295. </view>
  296. <view class="mt12rpx truncate px16rpx text-24rpx text-#AAAAAA">
  297. 维c满满,营养qweqwe
  298. </view>
  299. <view class="my20rpx flex items-center justify-between px16rpx">
  300. <view class="flex items-end text-#FF4D3A font-semibold">
  301. <view class="text-24rpx">
  302. </view>
  303. <view class="text-32rpx line-height-[32rpx]">
  304. 1.395
  305. </view>
  306. </view>
  307. <image :src="`${StaticUrl}/cart-yes.png`" class="h44rpx w44rpx" />
  308. </view>
  309. </view>
  310. </swiper-item>
  311. </swiper>
  312. <view class="mt24rpx flex items-center justify-center">
  313. <view v-for="item, idx in goodsTabData" :key="idx" class="mr14rpx transition-all transition-duration-400 ease-in" :class="[currentTabGoods == idx ? 'rounded-12rpx w-40rpx h12rpx bg-[var(--them-color)]' : 'w12rpx h12rpx rounded-50% bg-#F0F0F0']" />
  314. </view>
  315. </view> -->
  316. </view>
  317. <view class="view-2 mt20rpx bg-white">
  318. <view class="p24rpx text-32rpx font-semibold">
  319. 商品详情
  320. <rich-text :nodes="goodsContent" />
  321. </view>
  322. </view>
  323. <!-- <view class="view-3 mt28rpx flex items-center justify-center">
  324. <view class="flex items-center">
  325. <image
  326. :src="`${StaticUrl}/goods-recommend.png`"
  327. class="mr12rpx h25rpx w26rpx"
  328. />
  329. <view class="text-36rpx text-[var(--them-color)] font-semibold">
  330. 为你推荐
  331. </view>
  332. <image
  333. :src="`${StaticUrl}/goods-recommend.png`"
  334. class="ml12rpx h25rpx w26rpx"
  335. />
  336. </view>
  337. </view> -->
  338. <view class="h180rpx" />
  339. <view class="ios shadow-fixed fixed bottom-0 left-0 w-full rounded-t-32rpx bg-white">
  340. <view class="flex items-center justify-between px24rpx py20rpx">
  341. <view class="mr36rpx" @click="router.replace({ name: 'xsb-homeTabbar' })">
  342. <image
  343. :src="`${StaticUrl}/goods-home.png`"
  344. class="h44rpx w44rpx"
  345. />
  346. <view class="text-20rpx">
  347. 首页
  348. </view>
  349. </view>
  350. <view class="relative mr36rpx">
  351. <image
  352. :src="`${StaticUrl}/goods-kf.png`"
  353. class="h44rpx w44rpx"
  354. />
  355. <Zcontact>
  356. <view class="text-20rpx">
  357. 客服
  358. </view>
  359. </Zcontact>
  360. </view>
  361. <wd-badge :model-value="getTotalNum">
  362. <view @click="router.replace({ name: 'xsb-homeTabbar', animationType: 'fade-out', params: { name: 'xsb-cart' } })">
  363. <image
  364. :src="`${StaticUrl}/goods-cart.png`"
  365. class="h44rpx w44rpx"
  366. />
  367. <view class="text-20rpx">
  368. 购物车
  369. </view>
  370. </view>
  371. </wd-badge>
  372. <view class="flex items-center">
  373. <view class="w220rpx">
  374. <wd-button hairline plain block @click="selectGoods = true">
  375. 加入购物车
  376. </wd-button>
  377. </view>
  378. <view class="ml20rpx w220rpx" @click="selectGoods = true">
  379. <wd-button block class="w-full">
  380. 立即购买
  381. </wd-button>
  382. </view>
  383. </view>
  384. </view>
  385. </view>
  386. <selectSku v-model:show="selectGoods" v-model:sku-id="specId" v-model:select-goods-num="SelectGoodsNum" :goods-info="goodsInfo">
  387. <template #footer>
  388. <view class="box-border w-full flex items-center justify-between py20rpx">
  389. <view class="w-48%">
  390. <wd-button plain hairline block :loading="isLoging" loading-color="#9ED605" @click="handleAddCart">
  391. 加入购物车
  392. </wd-button>
  393. </view>
  394. <view class="w-48%">
  395. <wd-button block :loading="isLoging" loading-color="#9ED605" @click="handleConfimOrder">
  396. 立即购买
  397. </wd-button>
  398. </view>
  399. </view>
  400. </template>
  401. </selectSku>
  402. </template>
  403. </view>
  404. </template>
  405. <style scoped lang="scss">
  406. .custom-indicator {
  407. border-radius: 16rpx;
  408. background: rgba(0, 0, 0, 0.5);
  409. color: #ffffff;
  410. font-size: 24rpx;
  411. }
  412. .shadow-fixed{
  413. box-shadow: 0rpx -6rpx 12rpx 2rpx rgba(0,0,0,0.09);
  414. }
  415. .header {
  416. background: linear-gradient(180deg, #FFFFFF 0%, #FFFFFF 62%, #F6F6F6 100%);
  417. }
  418. .page-xsb {
  419. :deep() {
  420. .wd-tabs__nav-container .wd-tabs__nav-item:nth-child(2){
  421. width: 220rpx !important ;
  422. }
  423. .wd-tabs__nav-item {
  424. flex:unset !important;
  425. .wd-tabs__nav-item-text {
  426. font-size: 32rpx !important;
  427. }
  428. }
  429. .ios .wd-button{
  430. min-width: unset !important;
  431. }
  432. .wd-radio-group.is-button{
  433. padding: 0!important;
  434. }
  435. }
  436. }
  437. </style>