detail.vue 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995
  1. <template>
  2. <view :style="themeColor()">
  3. <view class="bg-[var(--page-bg-color)] min-h-[100vh] relative" v-if="Object.keys(goodsDetail).length">
  4. <!-- 自定义头部 -->
  5. <view class="flex items-center fixed left-0 right-0 z-10 bg-transparent detail-head" :class="{'!bg-[#fff]': detailHeadBgChange}" :style="navbarInnerStyle">
  6. <view class="flex-center h-[60rpx] rounded-[30rpx] box-border arrow-left px-[20rpx] leading-[1]" :style="navbarInnerArrowStyle">
  7. <text class="nc-iconfont nc-icon-zuoV6xx text-[18px]" @click="backToPrevious()"></text>
  8. <text class="w-[2rpx] h-[26rpx] bg-[#999] mx-[14rpx]"></text>
  9. <text class="nc-iconfont nc-icon-liebiao-xiV6xx1 text-[16px]" @click="topNav = true"></text>
  10. </view>
  11. <view class="ml-auto !pt-[12rpx] !pb-[8rpx] p-[10rpx] bg-[rgba(255,255,255,.4)] rounded-full border-[2rpx] border-solid border-transparent box-border nc-iconfont nc-icon-fenxiangV6xx font-bold text-[#303133] text-[36rpx]" :class="{'border-[#d8d8d8]': detailHeadBgChange}" @click="openShareFn"></view>
  12. </view>
  13. <view class="fixed top-0 left-0 right-0 bottom-0 z-100 bg-transparent" @click="topNav = false" v-if="topNav">
  14. <view class="search-box w-[202rpx] bg-[#fff] rounded-[12rpx] relative" :style="fixedInnerStyle">
  15. <view class="px-[20rpx] flex-center" @click="redirect({ url: '/app/pages/index/index', mode: 'reLaunch' })">
  16. <text class="nc-iconfont nc-icon-shouyeV6xx11 text-[30rpx] mr-[10rpx]"></text>
  17. <text class="pl-[14rpx] py-[20rpx] flex-1 text-[24rpx] text-[#333] border-0 border-[#ddd] border-b-[1rpx] border-solid">首页</text>
  18. </view>
  19. <view class="px-[20rpx] flex-center" @click="redirect({ url: '/addon/mall/pages/goods/search'})">
  20. <text class="nc-iconfont nc-icon-sousuo-duanV6xx1 text-[30rpx] mr-[10rpx]"></text>
  21. <text class="pl-[14rpx] py-[20rpx] flex-1 text-[24rpx] text-[#333] border-0 border-[#ddd] border-b-[1rpx] border-solid">搜索</text>
  22. </view>
  23. <view class="px-[20rpx] flex-center" @click="redirect({ url: '/addon/mall/pages/goods/cart'})">
  24. <text class="nc-iconfont nc-icon-gouwucheV6xx1 text-[30rpx] mr-[10rpx]"></text>
  25. <text class="pl-[14rpx] py-[20rpx] flex-1 text-[24rpx] text-[#333] border-0 border-[#ddd] border-b-[1rpx] border-solid">购物车</text>
  26. </view>
  27. <view class="px-[20rpx] flex-center" @click="redirect({ url: '/app/pages/member/index'})">
  28. <text class="nc-iconfont nc-icon-a-wodeV6xx-36 text-[30rpx] mr-[10rpx]"></text>
  29. <text class="pl-[14rpx] py-[20rpx] flex-1 text-[24rpx] text-[#333]">个人中心</text>
  30. </view>
  31. </view>
  32. </view>
  33. <view class="w-full h-[100vw] relative overflow-hidden">
  34. <view class="absolute top-0 left-0 w-full h-full transition-transform duration-300 ease-linear transform" :class="{'translate-x-0':switchMedia === 'img','translate-x-full':switchMedia != 'img'}">
  35. <view class="swiper-box">
  36. <u-swiper :list="goodsDetail.goods.goods_image" :indicator="goodsDetail.goods.goods_image.length" :indicatorStyle="{'bottom': '70rpx'}" :autoplay="switchMedia === 'img'? true : false" height="100vw" radius="0" @click="swiperClick"></u-swiper>
  37. </view>
  38. </view>
  39. </view>
  40. <view v-if="priceType != ''" class="rounded-t-[40rpx] -mt-[44rpx] relative flex items-center justify-between !bg-cover box-border pb-[26rpx] h-[136rpx] px-[30rpx]" :style="{ background: 'url(' + img('addon/mall/detail/discount_price_bg.png') + ') no-repeat'}">
  41. <view class="text-[#fff]">
  42. <text class="text-[26rpx] mr-[10rpx] font-500 leading-[36rpx]" v-if="priceType == 'discount_price'">折扣价</text>
  43. <text class="text-[26rpx] mr-[10rpx] font-500 leading-[36rpx]" v-else-if="priceType == 'member_price'">会员价</text>
  44. <view class="inline-block mr-[14rpx]">
  45. <text class="text-[32rpx] price-font mr-[4rpx]">¥</text>
  46. <text class="text-[56rpx] -mb-[4rpx] price-font">{{ parseFloat(goodsPrice).toFixed(2).split('.')[0] }}</text>
  47. <text class="text-[32rpx] price-font">.{{ parseFloat(goodsPrice).toFixed(2).split('.')[1] }}</text>
  48. </view>
  49. <view class="inline-block">
  50. <text class="text-[26rpx] mr-[6rpx]" v-if="goodsDetail.price">售价:</text>
  51. <text class="text-[26rpx] price-font leading-[36rpx]">¥{{ goodsDetail.price }}</text>
  52. </view>
  53. </view>
  54. <view v-if="priceType == 'discount_price'" class="flex flex-col text-[#fff] items-end h-[59rpx] justify-between">
  55. <image class="h-[28rpx] mr-[2rpx]" :src="img('addon/mall/detail/discount_price.png')" mode="heightFix"></image>
  56. <view class="flex items-center text-[24rpx] -mb-[10rpx] overflow-hidden h-[28rpx]">
  57. <text class="mr-[4rpx] whitespace-nowrap">距结束</text>
  58. <up-count-down class="text-[#fff] text-[28rpx]" :time="discountTime" format="DD:HH:mm:ss" @change="onChange">
  59. <view class="flex">
  60. <view class="text-[24rpx] flex items-center" v-if="timeData.days>0">
  61. <text>{{ timeData.days }}</text>
  62. <text class="ml-[4rpx] text-[20rpx]">天</text>
  63. </view>
  64. <view class="text-[24rpx] flex items-center">
  65. <text class="min-w-[30rpx] text-center" v-if="timeData.hours">{{ timeData.hours >= 10 ? timeData.hours : '0' + timeData.hours }}</text>
  66. <text class="min-w-[30rpx] text-center" v-else>00</text>
  67. <text class="text-[20rpx]">时</text>
  68. </view>
  69. <view class="text-[24rpx] flex items-center">
  70. <text class="min-w-[30rpx] text-center">{{ timeData.minutes >= 10 ? timeData.minutes : '0' + timeData.minutes }}</text>
  71. <text class="text-[20rpx]">分</text>
  72. </view>
  73. <view class="text-[24rpx] flex items-center">
  74. <text class="min-w-[30rpx] text-center">{{ timeData.seconds < 10 ? '0' + timeData.seconds : timeData.seconds }}</text>
  75. <text class="text-[20rpx]">秒</text>
  76. </view>
  77. </view>
  78. </up-count-down>
  79. </view>
  80. </view>
  81. </view>
  82. <view class="bg-[var(--page-bg-color)] rounded-[40rpx] overflow-hidden -mt-[34rpx] relative">
  83. <view class="datail-title relative px-[30rpx]" :class="{'pt-[40rpx]': priceType != '','pt-[30rpx]': priceType == ''}">
  84. <view class="text-[var(--price-text-color)] flex items-baseline mb-[12rpx]" v-if="priceType === ''">
  85. <view class="inline-block goods-price-time">
  86. <text class="price-font text-[32rpx]">¥</text>
  87. <text class="price-font text-[48rpx]">{{ parseFloat(goodsPrice).toFixed(2).split('.')[0] }}</text>
  88. <text class="price-font text-[32rpx] mr-[10rpx]">.{{ parseFloat(goodsPrice).toFixed(2).split('.')[1] }}</text>
  89. </view>
  90. </view>
  91. <view class="text-[#333] font-500 text-[30rpx] multi-hidden leading-[40rpx]">
  92. <view class="brand-tag middle" v-if="goodsDetail.goods.goods_brand && Object.values(goodsDetail.goods.goods_brand).length" :style="diyGoods.baseTagStyle(goodsDetail.goods.goods_brand)">{{ goodsDetail.goods.goods_brand.brand_name }}</view>
  93. {{ goodsDetail.goods.goods_name }}
  94. </view>
  95. <view class="flex justify-between items-start mt-[24rpx]">
  96. <view class="text-[24rpx] leading-[34rpx] text-[var(--text-color-light6)]" v-if="goodsDetail.market_price && parseFloat(goodsDetail.market_price)">
  97. <text class="whitespace-nowrap mr-[4rpx]">划线价:</text>
  98. <text class="line-through">¥{{ goodsDetail.market_price }}</text>
  99. </view>
  100. <view class="text-[24rpx] leading-[34rpx] text-[var(--text-color-light6)]">
  101. <text class="whitespace-nowrap mr-[4rpx]">库存:</text>
  102. <text>{{ goodsDetail.stock }}</text>
  103. <text>{{ goodsDetail.goods.unit }}</text>
  104. </view>
  105. <view class="text-[24rpx] leading-[34rpx] text-[var(--text-color-light6)] flex items-baseline">
  106. <text class="whitespace-nowrap mr-[4rpx]">销量:</text>
  107. <text class="mx-[2rpx]">{{ goodsDetail.goods.sale_num }}</text>
  108. <text>{{ goodsDetail.goods.unit }}</text>
  109. </view>
  110. </view>
  111. <view class="flex flex-wrap mt-[16rpx]" v-if="goodsDetail.label_info && goodsDetail.label_info.length">
  112. <block v-for="item in goodsDetail.label_info" :key="item.label_id">
  113. <image class="img-tag middle" v-if="item.style_type == 'icon' && item.icon" :src="img(item.icon)" mode="heightFix" @error="diyGoods.error(item,'icon')" />
  114. <view class="base-tag middle" v-else-if="item.style_type == 'diy' || !item.icon" :style="diyGoods.baseTagStyle(item)">{{ item.label_name }}</view>
  115. </block>
  116. </view>
  117. </view>
  118. <view class="mt-[var(--top-m)] sidebar-margin card-template" v-if="isGoodsPropertyTemp">
  119. <view @click="servicesDataShow = !servicesDataShow" v-if="goodsDetail.service && goodsDetail.service.length" class="card-template-item">
  120. <text class="text-[#333] text-[26rpx] leading-[30rpx] font-400 flex-shrink-0">服务</text>
  121. <view class="text-[#343434] text-[26rpx] leading-[30rpx] font-400 truncate ml-auto">{{ goodsDetail.service[0].service_name }}</view>
  122. <text class="nc-iconfont nc-icon-youV6xx text-[26rpx] text-[var(--text-color-light6)] ml-[8rpx]"></text>
  123. </view>
  124. <view @click="buyFn" v-if="goodsDetail.goodsSpec && goodsDetail.goodsSpec.length" class="card-template-item">
  125. <text class="text-[#333] text-[26rpx] leading-[30rpx] font-400 flex-shrink-0 mr-[20rpx]">已选</text>
  126. <view class="ml-auto text-right truncate flex-1 text-[#343434] text-[26rpx] leading-[30rpx] font-400">{{ goodsDetail.sku_spec_format }}</view>
  127. <text class="nc-iconfont nc-icon-youV6xx text-[26rpx] text-[var(--text-color-light6)] ml-[8rpx]"></text>
  128. </view>
  129. <view class="card-template-item" @click="distributionDataOpen" v-if="goodsDetail.goods.goods_type == 'real' && goodsDetail.delivery_type_list &&goodsDetail.delivery_type_list.length">
  130. <text class="text-[#333] text-[26rpx] leading-[30rpx] font-400 flex-shrink-0">配送</text>
  131. <view class="ml-auto flex items-center text-[#343434] text-[26rpx] leading-[30rpx] font-400">{{goodsDetail.delivery_type_list[selectDeliveryType]}}</view>
  132. <text class="nc-iconfont nc-icon-youV6xx text-[26rpx] text-[var(--text-color-light6)] ml-[8rpx]"></text>
  133. </view>
  134. <view @click="couponListShow = true" v-if="couponList.length" class="card-template-item">
  135. <text class="text-[#333] text-[26rpx] leading-[30rpx] font-400 flex-shrink-0 mr-[20rpx]">领券</text>
  136. <view class="ml-auto flex-1 flex-nowrap flex items-center overflow-hidden h-[44rpx] content-between">
  137. <block v-for="(item, index) in couponList" :key="index">
  138. <text v-if="index < 3" class="tag-item whitespace-nowrap border-[2rpx] px-[6rpx] h-[40rpx] border-solid border-[var(--primary-color)] text-[var(--primary-color)] mt-[4rpx]" :class="{'mr-[12rpx]': couponList.length != (index + 1) && index < 2, 'ml-auto': index == 0}">{{ item.title }}</text>
  139. </block>
  140. </view>
  141. <text class="nc-iconfont nc-icon-youV6xx text-[26rpx] text-[var(--text-color-light6)] ml-[8rpx]"></text>
  142. </view>
  143. </view>
  144. <view class="mt-[var(--top-m)] sidebar-margin card-template">
  145. <view class="flex justify-between items-center">
  146. <view class="flex flex-1">
  147. <u--image class="overflow-hidden" width="86rpx" height="86rpx" radius="6rpx" :src="diyShop.siteLogo(goodsDetail.shop_info)" model="aspectFill">
  148. <template #error>
  149. <image :src="img('addon/mall/shop/shop_default.png')" class="w-[86rpx] h-[86rpx] align-middle"></image>
  150. </template>
  151. </u--image>
  152. <view class="ml-[20rpx] flex flex-col justify-center">
  153. <view class="flex items-center">
  154. <text class="text-[28rpx] text-[#282828] font-600 leading-[38rpx] max-w-[200rpx] truncate">{{ diyShop.siteName(goodsDetail.shop_info) }}</text>
  155. <text class="ml-[8rpx] bg-[var(--primary-color)] text-[#fff] text-[20rpx] rounded-[4rpx] px-[4rpx] leading-[28rpx]" v-if="goodsDetail.shop_info.is_self">{{goodsDetail.shop_info.is_self ? '自营' : ''}}</text>
  156. </view>
  157. <view class="text-[22rpx] text-[var(--text-color-light6)] mt-[8rpx] leading-[30rpx]">{{goodsDetail.shop_info.follow_number}}人关注</view>
  158. </view>
  159. </view>
  160. <view class="w-[114rpx] h-[50rpx] leading-[50rpx] bg-[var(--primary-color)] text-[#fff] text-center rounded-[25rpx] text-[28rpx]" @click="toShopDetail(goodsDetail.shop_info.site_id)">进店</view>
  161. </view>
  162. </view>
  163. <view v-if="goodsDetail.evaluate_is_show" class="mt-[var(--top-m)] sidebar-margin card-template">
  164. <view class="flex items-center justify-between min-h-[40rpx]" :class="{'mb-[30rpx]': evaluate && evaluate.list && evaluate.list.length}">
  165. <text class="title !mb-[0]">宝贝评价({{ evaluate.count }})</text>
  166. <view v-if="evaluate.count" class="h-[40rpx] flex items-center" @click="toLink(goodsDetail.goods_id)">
  167. <text class="text-[24rpx] text-[var(--text-color-light9)]">查看全部</text>
  168. <text class="nc-iconfont nc-icon-youV6xx text-[26rpx] text-[var(--text-color-light9)]"></text>
  169. </view>
  170. <text v-if="!evaluate.count" class="text-[24rpx] text-[var(--text-color-light6)]">暂无评价</text>
  171. </view>
  172. <view>
  173. <view :class="{'pb-[34rpx]': index != (evaluate.list.length-1)}" v-for="(item, index) in evaluate.list" :key="index">
  174. <view class="flex items-center w-full">
  175. <u-avatar :default-url="img('static/resource/images/default_headimg.png')" :src="img(item.member_head)" :size="'50rpx'" leftIcon="none"></u-avatar>
  176. <text class="ml-[10rpx] text-[28rpx] text-[#333]">{{ item.member_name }}</text>
  177. </view>
  178. <view class="flex justify-between w-full mt-[16rpx]">
  179. <view class="flex-1 w-[540rpx] text-[26rpx] text-[#333] max-h-[72rpx] leading-[36rpx] multi-hidden mr-[50rpx]">{{ item.content }}</view>
  180. <view class="w-[80rpx] flex-shrink-0">
  181. <u--image v-if="item.image_mid && item.image_mid.length" width="80rpx" height="80rpx" radius="var(--goods-rounded-mid)" :src="img(item.image_mid[0])" mode="aspectFill" @click="imgListPreview(item.images[0])">
  182. <template #error>
  183. <u-icon name="photo" color="#999" size="50"></u-icon>
  184. </template>
  185. </u--image>
  186. </view>
  187. </view>
  188. </view>
  189. </view>
  190. </view>
  191. <view class="my-[var(--top-m)] goods-sku sidebar-margin card-template" v-if="goodsDetail.goods && goodsDetail.goods.attr_format && Object.keys(goodsDetail.goods.attr_format).length">
  192. <view class="title mb-[30rpx]">商品属性</view>
  193. <view>
  194. <block v-for="(item,index) in goodsDetail.goods.attr_format" :key="index">
  195. <view v-if="index < 4 || isAttrFormatShow" class="card-template-item">
  196. <text class="text-[26rpx] leading-[30rpx] w-[160rpx] font-400 flex-shrink-0 text-[var(--text-color-light9)]">{{item.attr_value_name}}</text>
  197. <view class="text-[#333] box-border value-wid text-[26rpx] leading-[30rpx] font-400 truncate pl-[20rpx]">{{Array.isArray(item.attr_child_value_name) ? item.attr_child_value_name.join(',') : item.attr_child_value_name }}</view>
  198. </view>
  199. </block>
  200. <view v-if="goodsDetail.goods.attr_format.length > 4" class="flex-center" @click="isAttrFormatShow = !isAttrFormatShow">
  201. <text class="text-[24rpx] mr-[10rpx]">{{!isAttrFormatShow ? '展开' : '收起'}}</text>
  202. <text class="nc-iconfont !text-[22rpx]" :class="{'nc-icon-xiaV6xx': !isAttrFormatShow, 'nc-icon-shangV6xx-1': isAttrFormatShow}"></text>
  203. </view>
  204. </view>
  205. </view>
  206. <view class="my-[var(--top-m)] sidebar-margin card-template px-[var(--pad-sidebar-m)]">
  207. <view class="title">商品详情</view>
  208. <view class="u-content">
  209. <u-parse :content="goodsDetail.goods.goods_desc" :tagStyle="{img: 'vertical-align: top;',p:'overflow: hidden;word-break:break-word;' }"></u-parse>
  210. </view>
  211. </view>
  212. <ns-goods-recommend></ns-goods-recommend>
  213. <!-- tabber -->
  214. <view class="tab-bar-placeholder"></view>
  215. <view class="border-[0] border-t-[2rpx] border-solid border-[#f5f5f5] w-[100%] flex justify-between pl-[32rpx] pr-[4rpx] bg-[#fff] box-border fixed left-0 bottom-0 tab-bar z-1 items-center">
  216. <view class="flex items-center">
  217. <view class="flex flex-col justify-center items-center mr-[38rpx]" @click="redirect({ url: '/app/pages/index/index', mode: 'reLaunch' })">
  218. <view class="nc-iconfont nc-icon-shouyeV6xx11 text-[36rpx]"></view>
  219. <text class="text-[20rpx] mt-[10rpx]">首页</text>
  220. </view>
  221. <view class="flex flex-col justify-center items-center mr-[38rpx]" @click="redirect({ url: '/addon/mall/pages/goods/cart'})">
  222. <view class="iconfont icongouwuche2 text-[38rpx]"></view>
  223. <text class="text-[20rpx] mt-[10rpx]">购物车</text>
  224. </view>
  225. <view class="flex flex-col justify-center items-center mr-[38rpx]" @click="collectFn">
  226. <text class="nc-iconfont text-[36rpx]" :class="{'text-[#ff0000] nc-icon-xihuanV6mm': isCollect, 'text-[#303133] nc-icon-guanzhuV6xx' : !isCollect}"></text>
  227. <text class="text-[20rpx] mt-[10rpx]">收藏</text>
  228. </view>
  229. </view>
  230. <view class="flex flex-1" v-if="goodsDetail.goods.status == 1">
  231. <block v-if="maxBuy > 0 || maxBuy == -1">
  232. <button v-if="goodsDetail.goods.goods_type == 'real' || (goodsDetail.goods.goods_type == 'virtual' && goodsDetail.goods.virtual_receive_type != 'verify')" class="cart-btn-bg flex-1 !h-[70rpx] font-500 text-[26rpx] !text-[#fff] !m-0 !mr-[16rpx] leading-[70rpx] rounded-full remove-border" @click="buyFn('join_cart')">加入购物车</button>
  233. <button v-if="isShowSingleSku" :style="{ width : (goodsDetail.goods.goods_type == 'real' || (goodsDetail.goods.goods_type == 'virtual' && goodsDetail.goods.virtual_receive_type != 'verify')) ? '200rpx' : '420rpx' + '!important' }" class="flex-1 !h-[70rpx] font-500 text-[26rpx] !text-[#fff] primary-btn-bg !m-0 !mr-[16rpx] leading-[70rpx] rounded-full remove-border" @click="buyFn('buy_now')">立即购买</button>
  234. <button v-else :style="{ width : (goodsDetail.goods.goods_type == 'real' || (goodsDetail.goods.goods_type == 'virtual' && goodsDetail.goods.virtual_receive_type != 'verify')) ? '200rpx' : '420rpx' + '!important' }"
  235. class="flex-1 !h-[70rpx] font-500 text-[26rpx] !text-[#fff] !bg-[#ccc] !m-0 !mr-[16rpx] leading-[70rpx] rounded-full remove-border"
  236. >已售罄</button>
  237. </block>
  238. <button v-else-if="maxBuy == 0" :style="{ width : '420rpx' + '!important'}" class="flex-1 !h-[70rpx] font-500 text-[26rpx] !text-[#fff] !bg-[#ccc] !m-0 leading-[70rpx] rounded-full remove-border" >已达限购数量</button>
  239. </view>
  240. <view class="flex flex-1" v-else>
  241. <button class="w-[100%] !h-[70rpx] font-500 text-[26rpx] !text-[#fff] !bg-[#ccc] !m-0 leading-[70rpx] rounded-full remove-border">该商品已下架</button>
  242. </view>
  243. </view>
  244. </view>
  245. <!-- 服务 -->
  246. <view @touchmove.prevent.stop>
  247. <u-popup class="popup-type" :show="servicesDataShow" @close="servicesDataShow = false">
  248. <view class="min-h-[480rpx] popup-common" @touchmove.prevent.stop>
  249. <view class="title">商品服务</view>
  250. <scroll-view class="h-[520rpx]" scroll-y="true">
  251. <view class="pl-[22rpx] pb-[28rpx] pr-[37rpx]">
  252. <view class="flex mb-[28rpx]" v-for="(item, index) in goodsDetail.service">
  253. <image class="mt-[4rpx] w-[32rpx] h-[32rpx] mr-[14rpx]" :src="img(item.image || 'addon/mall/icon_service.png')" mode="aspectFit" />
  254. <view class="flex-1">
  255. <view class="text-[30rpx] leading-[36rpx] text-[#333] mb-[8rpx]">{{ item.service_name }}</view>
  256. <view class="text-[24rpx] leading-[36rpx] text-[var(--text-color-light9)]">{{ item.desc }}</view>
  257. </view>
  258. </view>
  259. </view>
  260. </scroll-view>
  261. </view>
  262. </u-popup>
  263. </view>
  264. <!-- 配送 -->
  265. <view @touchmove.prevent.stop>
  266. <u-popup class="popup-type" :show="distributionDataShow" @close="distributionDataShow = false">
  267. <view class="min-h-[360rpx] popup-common" @touchmove.prevent.stop>
  268. <view class="title">配送方式</view>
  269. <scroll-view class="h-[520rpx]" scroll-y="true">
  270. <view class="px-[var(--popup-sidebar-m)]">
  271. <view class="flex mb-[40rpx]" v-for="(item, index) in goodsDetail.delivery_type_list" @click="distributionListFn(item,index)">
  272. <image class="mt-[4rpx] w-[32rpx] h-[32rpx] mr-[14rpx]" :src="img('addon/mall/icon_service.png')" mode="aspectFit" />
  273. <view class="flex-1">
  274. <view class="text-[30rpx] leading-[36rpx] text-[#333] mb-[8rpx]">{{ item.name }}</view>
  275. <view class="text-[24rpx] leading-[36rpx] text-[var(--text-color-light9)]">{{ item.desc }}</view>
  276. </view>
  277. </view>
  278. </view>
  279. </scroll-view>
  280. </view>
  281. </u-popup>
  282. </view>
  283. <!-- 优惠券 -->
  284. <view @touchmove.prevent.stop>
  285. <u-popup class="popup-type" :show="couponListShow" @close="couponListShow = false">
  286. <view class="min-h-[480rpx] popup-common" @touchmove.prevent.stop>
  287. <view class="title">优惠券</view>
  288. <scroll-view class="h-[520rpx]" :scroll-y="true">
  289. <view class="px-[32rpx]">
  290. <view
  291. class="mb-[30rpx] flex items-center border-[2rpx] border-solid border-[rgba(0,0,0,.1)] rounded-[var(--rounded-small)]"
  292. v-for="(item, index) in couponList" :key="index">
  293. <view
  294. class="flex flex-col items-center my-[20rpx] w-[200rpx] border-0 border-r-[2rpx] border-dashed border-[rgba(0,0,0,.1)]">
  295. <view class="text-xs price-font">
  296. <text class="text-[28rpx]">¥</text>
  297. <text class="text-[48rpx]">{{ item.price }}</text>
  298. </view>
  299. <text class="text-xs mt-[12rpx]">{{ Number(item.min_condition_money) ? ('满' + item.min_condition_money + '元可用') : '无门槛' }}</text>
  300. </view>
  301. <view class="ml-[20rpx] flex-1 flex flex-col py-[20rpx]">
  302. <text class="text-xs font-500">{{ item.title }}</text>
  303. <text class="text-xs text-[var(--text-color-light6)] mt-[12rpx]">{{ item.valid_type == 1 &&
  304. ('领取之日起' + item.length + '天内有效') || item.valid_type == 2 &&
  305. ('领取之日起至' + item.valid_end_time) }}</text>
  306. </view>
  307. <text v-if="item.btnType === 'collecting'" class="bg-[var(--primary-color)] mr-[20rpx] w-[106rpx] box-border text-center text-[#fff] h-[50rpx] text-[22rpx] px-[20rpx] leading-[50rpx] rounded-[100rpx]" @click="getCouponFn(item, index)">领取</text>
  308. <text v-else class="!bg-[var(--primary-help-color4)] mr-[20rpx] text-[#fff] mr-[20rpx] h-[50rpx] text-[22rpx] px-[20rpx] leading-[50rpx] rounded-[100rpx]">{{ item.btnType === 'collected' ? '已领完' : '已领取' }}</text>
  309. </view>
  310. </view>
  311. </scroll-view>
  312. <view class="btn-wrap">
  313. <button class="primary-btn-bg btn" @click="couponListShow = false">确定</button>
  314. </view>
  315. </view>
  316. </u-popup>
  317. </view>
  318. <ns-goods-sku v-if="loading" ref="goodsSkuRef" :goods-detail="goodsDetail" @change="specSelectFn"></ns-goods-sku>
  319. <share-poster ref="sharePosterRef" posterType="shop_goods" :posterId="goodsDetail.goods.poster_id" :posterParam="posterParam" :copyUrlParam="copyUrlParam" />
  320. </view>
  321. <!-- #ifdef MP-WEIXIN -->
  322. <!-- 小程序隐私协议 -->
  323. <wx-privacy-popup ref="wxPrivacyPopupRef"></wx-privacy-popup>
  324. <!-- #endif -->
  325. </view>
  326. </template>
  327. <script setup lang="ts">
  328. import { ref, computed, getCurrentInstance, nextTick } from 'vue';
  329. import { onLoad, onShow,onUnload, onPageScroll } from '@dcloudio/uni-app'
  330. import { img, redirect, handleOnloadParams,getToken, deepClone, goback} from '@/utils/common';
  331. import { t } from '@/locale';
  332. import { getGoodsDetail, collect, cancelCollect, getEvaluateList, addGoodsBrowse } from '@/addon/mall/api/goods';
  333. import { getMallGoodsCoupon, getCoupon } from '@/addon/mall/api/coupon';
  334. import nsGoodsSku from '@/addon/mall/components/ns-goods-sku/ns-goods-sku.vue';
  335. import useCartStore from '@/addon/mall/stores/cart'
  336. import { useLogin } from '@/hooks/useLogin'
  337. import useMemberStore from '@/stores/member'
  338. import { useShare }from '@/hooks/useShare'
  339. import sharePoster from '@/components/share-poster/share-poster.vue'
  340. import { useGoods } from '@/addon/mall/hooks/useGoods'
  341. import { useShop } from '@/addon/mall/hooks/useShop'
  342. import nsGoodsRecommend from '@/addon/mall/components/ns-goods-recommend/ns-goods-recommend.vue';
  343. const diyGoods = useGoods();
  344. const diyShop = useShop();
  345. const timeData = ref({});
  346. // 定义 onChange 方法
  347. const onChange = (e: any) => {
  348. timeData.value = e;
  349. };
  350. // 分享
  351. const{setShare} = useShare()
  352. // 会员信息
  353. const memberStore = useMemberStore()
  354. const userInfo = computed(() => memberStore.info)
  355. // 购物车数量
  356. const cartStore = useCartStore();
  357. let cartTotalNum = computed(() => cartStore.totalNum)
  358. let goodsSkuRef: any= ref(null);
  359. let goodsDetail: any = ref({});
  360. const switchMedia: any = ref('img');
  361. let isAttrFormatShow = ref(false); //控制属性是否展开
  362. const topNav = ref(false);
  363. let loading = ref<boolean>(false);
  364. let servicesDataShow = ref<boolean>(false)
  365. let distributionDataShow = ref<boolean>(false) //配送
  366. let couponListShow = ref<boolean>(false) //优惠券
  367. let discountTime = ref(0)
  368. const sendMessageTitle = ref('')
  369. const sendMessagePath = ref('')
  370. const sendMessageImg = ref('')
  371. const wxPrivacyPopupRef:any = ref(null)
  372. let pageParameter = {};
  373. onLoad((option: any) => {
  374. // #ifdef MP-WEIXIN
  375. // 处理小程序场景值参数
  376. option = handleOnloadParams(option);
  377. // #endif
  378. pageParameter = option;
  379. })
  380. onShow(() => {
  381. // 删除配送方式
  382. loading.value = false;
  383. uni.removeStorageSync('distributionType');
  384. cartStore.getList();
  385. getDetailInfo();
  386. })
  387. const getDetailInfo = () => {
  388. getGoodsDetail({
  389. goods_id: pageParameter.goods_id || '',
  390. sku_id: pageParameter.sku_id || ''
  391. }).then((res: any) => {
  392. if (!res.data.goods || JSON.stringify(res.data) === '[]') {
  393. let goBackParameter = {
  394. url: '/app/pages/index/index',
  395. title: '找不到该商品',
  396. mode: 'reLaunch'
  397. };
  398. goback(goBackParameter)
  399. return false
  400. }
  401. goodsDetail.value = deepClone(res.data);
  402. isCollect.value = goodsDetail.value.goods.is_collect;
  403. goodsDetail.value.delivery_type_list = goodsDetail.value.goods.delivery_type_list ? Object.values(goodsDetail.value.goods.delivery_type_list) : [];
  404. goodsDetail.value.goods.goods_image = goodsDetail.value.goods.goods_image_thumb_big;
  405. goodsDetail.value.goods.goods_image.forEach((item: any, index: any) => {
  406. goodsDetail.value.goods.goods_image[index] = img(item);
  407. })
  408. loading.value = true;
  409. // 商品属性
  410. if(goodsDetail.value.goods && goodsDetail.value.goods.attr_format){
  411. goodsDetail.value.goods.attr_format = goodsDetail.value.goods.attr_format.filter((item:any, index: any) => {
  412. return Array.isArray(item.attr_child_value_name) ? item.attr_child_value_name.length : item.attr_child_value_name
  413. })
  414. }
  415. sendMessageTitle.value = goodsDetail.value.goods.goods_name
  416. sendMessagePath.value = '/addon/mall/pages/goods/detail?sku_id=' + goodsDetail.value.sku_id;
  417. if (goodsDetail.value.type) {
  418. sendMessagePath.value += '&type=' + goodsDetail.value.type;
  419. }
  420. sendMessageImg.value = img(goodsDetail.value.goods.goods_cover_thumb_mid)
  421. // 分享 - start
  422. let share = {
  423. title: goodsDetail.value.goods.goods_name,
  424. desc: goodsDetail.value.goods.sub_title,
  425. url: goodsDetail.value.goods.goods_cover_thumb_mid
  426. }
  427. uni. setNavigationBarTitle({
  428. title: goodsDetail.value.goods.goods_name
  429. })
  430. setShare({
  431. wechat:{
  432. ...share
  433. },
  434. weapp:{
  435. ...share
  436. }
  437. });
  438. // 分享 - end
  439. // 折扣信息
  440. if(Object.keys(goodsDetail.value.goods).length && goodsDetail.value.goods.is_discount && Object.keys(goodsDetail.value.discount_info).length){
  441. let now = new Date();
  442. let timestamp: any = now.getTime();
  443. discountTime.value = goodsDetail.value.discount_info.active.end_time * 1000 - timestamp.toFixed(0)
  444. console.log(discountTime.value)
  445. }
  446. // 商品限购
  447. goodsMaxBuy(goodsDetail.value);
  448. // 获取优惠券列表
  449. getMallCouponListFn();
  450. // 获取评价
  451. getEvaluateListFn();
  452. if (getToken()) {
  453. // 我的足迹
  454. addGoodsBrowseFn();
  455. }
  456. copyUrlFn();
  457. nextTick(() => {
  458. setTimeout(()=>{
  459. const query = uni.createSelectorQuery().in(instance);
  460. query.select('.swiper-box').boundingClientRect((data:any) => {
  461. swiperHeight = data ? data.height : 0;
  462. }).exec();
  463. query.select('.detail-head').boundingClientRect((data:any) => {
  464. if(data) {
  465. detailHead = data.height ? data.height : 0;
  466. }
  467. }).exec();
  468. if (sharePosterRef.value) {
  469. posterParam.sku_id = goodsDetail.value.sku_id;
  470. if (userInfo.value && userInfo.value.member_id) posterParam.member_id = userInfo.value.member_id;
  471. sharePosterRef.value.loadPoster();
  472. }
  473. }, 400)
  474. // #ifdef MP
  475. if(wxPrivacyPopupRef.value) wxPrivacyPopupRef.value.proactive();
  476. // #endif
  477. })
  478. })
  479. }
  480. // 商品限购
  481. const maxBuy = ref(-1);
  482. const goodsMaxBuy = (data: any = {}) => {
  483. // 限购 - 是否开启限购
  484. if (data.goods.is_limit && userInfo.value && data.goods.stock > 0) {
  485. if (data.goods.max_buy) {
  486. let max_buy = 0;
  487. if (data.goods.limit_type == 1) { //单次限购
  488. max_buy = data.goods.max_buy;
  489. } else { // 单人限购
  490. let buyVal = data.goods.max_buy - (data.goods.has_buy || 0);
  491. max_buy = buyVal > 0 ? buyVal : 0;
  492. }
  493. if (max_buy > data.goods.stock) {
  494. maxBuy.value = data.goods.stock
  495. } else if (max_buy <= data.goods.stock) {
  496. maxBuy.value = max_buy;
  497. }
  498. }
  499. }
  500. }
  501. const specSelectFn = (id:any) => {
  502. goodsDetail.value.skuList.forEach((item: any, index: any) => {
  503. if (item.sku_id == id) {
  504. Object.assign(goodsDetail.value, item);
  505. }
  506. })
  507. }
  508. // 判断单规格库存是否为0
  509. const isShowSingleSku = computed(() => {
  510. let isSingleSpec = false // 是否为单规格,true:多规格,false:单规格
  511. goodsDetail.value.skuList.forEach((item: any, index: any)=>{
  512. if(item.sku_spec_format){
  513. isSingleSpec = true
  514. }
  515. })
  516. // 单规格,库存为0,显示已售罄
  517. if(!isSingleSpec && goodsDetail.value.stock <= 0){
  518. return false;
  519. }else if(!isSingleSpec && goodsDetail.value.stock > 0){
  520. // 单规格,库存大于0,可以购买
  521. return true;
  522. }
  523. return true;
  524. })
  525. // 判断商品属性模块是否展示
  526. const isGoodsPropertyTemp = computed(() => {
  527. let bool = false;
  528. if(goodsDetail.value.service && goodsDetail.value.service.length ||
  529. goodsDetail.value.goodsSpec && goodsDetail.value.goodsSpec.length ||
  530. goodsDetail.value.goods.goods_type == 'real'&&goodsDetail.value.delivery_type_list && goodsDetail.value.delivery_type_list.length ||
  531. couponList.value.length){
  532. bool = true;
  533. }
  534. return bool;
  535. })
  536. const buyFn = (type: any) => {
  537. if (goodsSkuRef.value) {
  538. goodsSkuRef.value.open(type)
  539. }
  540. }
  541. // 收藏
  542. let isCollect = ref<any>(0);
  543. const collectFn = () => {
  544. // 检测是否登录
  545. if (!userInfo.value) {
  546. useLogin().setLoginBack({ url: '/addon/mall/pages/goods/detail', param: { sku_id: goodsDetail.value.sku_id } })
  547. return false
  548. }
  549. let api = isCollect.value ? cancelCollect({ goods_ids: [goodsDetail.value.goods_id] }) : collect(goodsDetail.value.goods_id);
  550. api.then(res => {
  551. isCollect.value = !isCollect.value;
  552. if (isCollect.value) {
  553. uni.showToast({
  554. title: '收藏成功',
  555. icon: 'none'
  556. });
  557. } else {
  558. uni.showToast({
  559. title: '取消收藏',
  560. icon: 'none'
  561. });
  562. }
  563. })
  564. }
  565. // 优惠券
  566. let couponList = ref([]);
  567. const getMallCouponListFn = () => {
  568. getMallGoodsCoupon({
  569. site_id: goodsDetail.value.shop_info.site_id,
  570. category_id: goodsDetail.value.goods.goods_category || '',
  571. mall_category_id: goodsDetail.value.goods.goods_mall_category || '',
  572. goods_id: goodsDetail.value.goods_id || '',
  573. brand_id: goodsDetail.value.goods.brand_id || ''
  574. }).then((res: any) => {
  575. couponList.value = res.data.data.map((el: any) => {
  576. if (!userInfo.value) {
  577. if (el.sum_count != -1 && el.receive_count === el.sum_count) {
  578. el.btnType = 'collected'//已领完
  579. } else {
  580. el.btnType = 'collecting'//领用
  581. }
  582. } else {
  583. if (el.is_receive) {
  584. if (el.member_receive_count < el.limit_count) {
  585. if (el.need_receive) {
  586. el.btnType = 'collecting'//领用
  587. } else {
  588. el.btnType = 'using'//去使用
  589. }
  590. } else {
  591. if (el.need_receive) {
  592. el.btnType = 'used'//已使用
  593. } else {
  594. el.btnType = 'using'//去使用
  595. }
  596. }
  597. } else {
  598. if (el.sum_count != -1 && el.receive_count === el.sum_count) {
  599. el.btnType = 'collected'//已领完
  600. } else {
  601. el.btnType = 'collecting'//领用
  602. }
  603. }
  604. }
  605. return el
  606. });
  607. })
  608. }
  609. // 领取优惠券
  610. const getCouponFn = (data:any, index:any) => {
  611. // 检测是否登录
  612. if (!userInfo.value) {
  613. useLogin().setLoginBack({ url: '/addon/mall/pages/goods/detail', param: { sku_id: goodsDetail.value.sku_id } })
  614. return false
  615. }
  616. getCoupon({
  617. coupon_id: data.id || '',
  618. number: 1,
  619. }).then(res => {
  620. // couponList.value[index].btnType = 'using'
  621. getMallCouponListFn();
  622. })
  623. }
  624. // 获取评价
  625. const evaluate = ref({
  626. count : 0
  627. })
  628. const getEvaluateListFn = () => {
  629. getEvaluateList(goodsDetail.value.goods_id).then((res:any) => {
  630. evaluate.value = res.data
  631. })
  632. }
  633. //进入评论
  634. const toLink = () => {
  635. redirect({ url: '/addon/mall/pages/evaluate/list', param: { goods_id: goodsDetail.value.goods_id } })
  636. }
  637. //浏览记录
  638. const addGoodsBrowseFn = () => {
  639. if (userInfo.value && userInfo.value.member_id) {
  640. addGoodsBrowse({goods_id:goodsDetail.value.goods_id,sku_id:goodsDetail.value.sku_id}).then(res => {})
  641. }
  642. }
  643. //进入店铺
  644. const toShopDetail = (site_id:any) => {
  645. redirect({ url: '/app/pages/site/index', param: { site_id } })
  646. }
  647. //预览图片
  648. const imgListPreview = (item: any,index: any) => {
  649. if(Array.isArray(item)){
  650. if (!item.length) return false
  651. var urlList =item;
  652. uni.previewImage({
  653. indicator: "number",
  654. current:index,
  655. loop: true,
  656. urls: urlList
  657. })
  658. }else{
  659. if (item === '') return false
  660. var urlList = []
  661. urlList.push(img(item)) //push中的参数为 :src="item.img_url" 中的图片地址
  662. uni.previewImage({
  663. indicator: "number",
  664. loop: true,
  665. urls: urlList
  666. })
  667. }
  668. }
  669. // 返回上一页
  670. const backToPrevious = () => {
  671. if(getCurrentPages().length > 1){
  672. uni.navigateBack({
  673. delta: 1
  674. });
  675. }else{
  676. redirect({
  677. url: '/app/pages/index/index',
  678. mode: 'reLaunch'
  679. });
  680. }
  681. }
  682. /************ 选择配送方式-start ****************/
  683. const selectDeliveryType = ref(0);
  684. const distributionDataOpen = (()=>{
  685. distributionDataShow.value = true;
  686. });
  687. const distributionListFn = ((data:any,index:any)=>{
  688. selectDeliveryType.value = index;
  689. distributionDataShow.value = false;
  690. uni.setStorageSync('distributionType', data);
  691. });
  692. /************ 选择配送方式-end ****************/
  693. /************ 自定义头部-start ****************/
  694. // 获取系统状态栏的高度
  695. let systemInfo = uni.getSystemInfoSync();
  696. let platform = systemInfo.platform;
  697. let menuButtonInfo:any = {};
  698. // 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
  699. // #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
  700. menuButtonInfo = uni.getMenuButtonBoundingClientRect();
  701. // #endif
  702. // 导航栏内部盒子的样式
  703. const navbarInnerStyle = computed(() => {
  704. let style = '';
  705. // 导航栏宽度,如果在小程序下,导航栏宽度为胶囊的左边到屏幕左边的距离
  706. // #ifdef MP
  707. let rightButtonWidth = menuButtonInfo.width ? menuButtonInfo.width * 2 + 'rpx' : '70rpx';
  708. style += 'height:' + menuButtonInfo.height + 'px;';
  709. style += 'padding-right:calc(' + rightButtonWidth + ' + 30rpx);';
  710. style += 'padding-left:calc(' + rightButtonWidth + ' + 30rpx);';
  711. style += 'padding-top:' + menuButtonInfo.top + 'px;';
  712. style += 'padding-bottom: 8px;';
  713. style += 'font-size: 32rpx;';
  714. if (platform === 'ios') {
  715. // 苹果(iOS)设备
  716. style += 'font-weight: 500;';
  717. } else if (platform === 'android') {
  718. // 安卓(Android)设备
  719. style += 'font-size: 36rpx;';
  720. }
  721. // #endif
  722. // #ifdef H5
  723. style += 'height: 100rpx;';
  724. style += 'padding-right: 30rpx;';
  725. style += 'padding-left: 30rpx;';
  726. style += 'font-size: 32rpx;';
  727. if (platform === 'ios') {
  728. // 苹果(iOS)设备
  729. style += 'font-weight: 500;';
  730. } else if (platform === 'android') {
  731. // 安卓(Android)设备
  732. style += 'font-size: 36rpx;';
  733. }
  734. // #endif
  735. return style;
  736. })
  737. // 导航栏内部盒子的样式
  738. const navbarInnerArrowStyle = computed(() => {
  739. let style = '';
  740. // 导航栏宽度,如果在小程序下,导航栏宽度为胶囊的左边到屏幕左边的距离
  741. // #ifdef MP
  742. style += 'position: absolute;';
  743. style += 'left:calc( 100vw - ' + menuButtonInfo.right + 'px);';
  744. if (platform === 'ios') {
  745. // 苹果(iOS)设备
  746. style += 'font-weight: 700;';
  747. } else if (platform === 'android') {
  748. // 安卓(Android)设备
  749. }
  750. // #endif
  751. return style;
  752. })
  753. // 导航栏头部卡片样式
  754. const fixedInnerStyle = computed(() => {
  755. let style = '';
  756. // #ifdef MP
  757. style += 'top:' + (menuButtonInfo.height + menuButtonInfo.top + 8) + 'px;';
  758. style += 'left:calc( 100vw - ' + menuButtonInfo.right + 'px);';
  759. // #endif
  760. // #ifdef H5
  761. style += 'top: 100rpx;';
  762. style += 'left: 30rpx;';
  763. // #endif
  764. return style;
  765. })
  766. // 头部滚动
  767. const instance = getCurrentInstance();
  768. let swiperHeight = 0
  769. let detailHead = 0
  770. let detailHeadBgChange = ref(false)
  771. onPageScroll((e)=>{
  772. if (swiperHeight == 0 || detailHead == 0) return;
  773. let height = swiperHeight - detailHead - 20;
  774. detailHeadBgChange.value = false;
  775. if (e.scrollTop >= height) {
  776. detailHeadBgChange.value = true;
  777. }
  778. })
  779. /************ 自定义头部-end ****************/
  780. const swiperClick = (index:any)=>{
  781. if(typeof index == 'number') imgListPreview(goodsDetail.value.goods.goods_image,index)
  782. }
  783. /************* 分享海报-start **************/
  784. let sharePosterRef: any = ref(null);
  785. let copyUrlParam = ref('');
  786. let posterParam: any = {};
  787. // 分享海报链接
  788. const copyUrlFn = ()=>{
  789. copyUrlParam.value = '?sku_id='+goodsDetail.value.sku_id;
  790. if (goodsDetail.value.type) {
  791. copyUrlParam.value += '&type=' + goodsDetail.value.type;
  792. }
  793. if (userInfo.value && userInfo.value.member_id) copyUrlParam.value += '&mid=' + userInfo.value.member_id;
  794. }
  795. const openShareFn = ()=>{
  796. sharePosterRef.value.openShare()
  797. }
  798. /************* 分享海报-end **************/
  799. // 价格类型
  800. let priceType = ref('') //''=>原价,discount_price=>折扣价,member_price=>会员价
  801. // 商品价格
  802. let goodsPrice = computed(() =>{
  803. let price = "0.00";
  804. if(Object.keys(goodsDetail.value).length && Object.keys(goodsDetail.value.goods).length && goodsDetail.value.goods.is_discount && goodsDetail.value.sale_price != goodsDetail.value.price){
  805. // 折扣价
  806. price = goodsDetail.value.sale_price ? goodsDetail.value.sale_price : goodsDetail.value.price;
  807. priceType.value = 'discount_price'
  808. }else if(Object.keys(goodsDetail.value).length && Object.keys(goodsDetail.value.goods).length && goodsDetail.value.goods.member_discount && getToken() && goodsDetail.value.member_price != goodsDetail.value.price){
  809. // 会员价
  810. price = goodsDetail.value.member_price ? goodsDetail.value.member_price : goodsDetail.value.price;
  811. priceType.value = 'member_price'
  812. }else{
  813. price = goodsDetail.value.price
  814. priceType.value = ''
  815. }
  816. return price;
  817. })
  818. // 关闭预览图片
  819. onUnload(()=>{
  820. // #ifdef H5 || APP
  821. try {
  822. uni.closePreviewImage()
  823. } catch (e) {
  824. }
  825. // #endif
  826. })
  827. </script>
  828. <style lang="scss" scoped>
  829. @import '@/addon/mall/styles/common.scss';
  830. .arrow-left {
  831. background: rgba(153, 153, 153, 0.1);
  832. border: 1rpx solid rgba(221, 221, 221, 0.3);
  833. }
  834. .remove-border {
  835. &::after {
  836. border: none;
  837. }
  838. }
  839. .search-box {
  840. &::after {
  841. content: '';
  842. width: 20rpx;
  843. height: 20rpx;
  844. background-color: #fff;
  845. position: absolute;
  846. left: 50%;
  847. top: -10rpx;
  848. transform: translateX(-50%) rotate(45deg);
  849. }
  850. }
  851. :deep(.u-cell-group__wrapper) {
  852. .u-cell__body {
  853. padding: 23rpx 32rpx;
  854. }
  855. }
  856. .tab-bar-placeholder {
  857. padding-bottom: calc(constant(safe-area-inset-bottom) + 100rpx);
  858. padding-bottom: calc(env(safe-area-inset-bottom) + 100rpx);
  859. }
  860. .tab-bar {
  861. padding-top: 16rpx;
  862. padding-bottom: calc(constant(safe-area-inset-bottom) + 16rpx);
  863. padding-bottom: calc(env(safe-area-inset-bottom) + 16rpx);
  864. }
  865. :deep(.u-count-down) .u-count-down__text{
  866. color: #fff;
  867. font-size: 28rpx;
  868. }
  869. :deep(.u-swiper-indicator__wrapper--line__bar){
  870. height: 5rpx !important;
  871. }
  872. :deep(.u-swiper-indicator__wrapper--line){
  873. height: 5rpx !important;
  874. }
  875. .datail-title{
  876. background: linear-gradient(#fff , rgba(255,255,255,0));
  877. }
  878. .goods-sku .value-wid{
  879. width: calc(100% - 160rpx);
  880. }
  881. .media-mode {
  882. text {
  883. background: rgba(100, 100, 100, 0.4);
  884. color: #fff;
  885. font-size: 24rpx;
  886. line-height: 50rpx;
  887. border-radius: 20rpx;
  888. padding: 0 30rpx;
  889. display: inline-block;
  890. &:last-child {
  891. margin-left: 40rpx;
  892. }
  893. }
  894. }
  895. /* #ifdef MP */
  896. .goods-price-time {
  897. animation: fadein .1s;
  898. }
  899. /* 进入动画 */
  900. @keyframes fadein {
  901. 0% {
  902. opacity: 0;
  903. }
  904. 99% {
  905. opacity: 0;
  906. }
  907. 100% {
  908. opacity: 1;
  909. }
  910. }
  911. /* #endif */
  912. </style>