木邻有你前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

340 lines
8.1 KiB

1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
  1. <template>
  2. <view class="goods-detail">
  3. <!-- 轮播图 -->
  4. <view class="banner-container">
  5. <uv-swiper
  6. indicator
  7. indicatorMode="dot"
  8. indicatorActiveColor="blue"
  9. height="700rpx"
  10. :list="goodsData.image.split(',')"></uv-swiper>
  11. </view>
  12. <!-- 商品信息 -->
  13. <view class="goods-info">
  14. <!-- 积分信息 -->
  15. <view class="points-section">
  16. <image src="/static/积分图标.png" class="points-icon" mode="aspectFit"></image>
  17. <text class="points-text">{{ goodsData.price }}积分</text>
  18. </view>
  19. <!-- 商品标题 -->
  20. <view class="title-section">
  21. <text class="goods-title">{{ goodsData.title }}</text>
  22. </view>
  23. </view>
  24. <!-- 商品详情独立容器 -->
  25. <view class="detail-container">
  26. <!-- 商品详情标题 -->
  27. <view class="detail-title-section">
  28. <rich-text :nodes="goodsData.details"></rich-text>
  29. </view>
  30. <!-- 商品图集 -->
  31. <!-- <view class="gallery-section">
  32. <view class="gallery-grid">
  33. <image
  34. v-for="(img, index) in goodsData.image.split(',')"
  35. :key="index"
  36. :src="img"
  37. class="gallery-image"
  38. mode="aspectFill"
  39. @click="previewImage(img, goodsData.image.split(','))"
  40. ></image>
  41. </view>
  42. </view> -->
  43. </view>
  44. <!-- 底部操作栏 -->
  45. <view class="bottom-bar">
  46. <view class="stock-info">
  47. <text class="stock-text"> {{ goodsData.inventory }}/{{ goodsData.sales }}</text>
  48. <text class="stock-label">库存</text>
  49. </view>
  50. <view class="favorite-section" @click="toggleFavorite">
  51. <uv-icon name="heart" size="24" :color="goodsData.isCollection === 1 ? 'red' : '#cccccc'"></uv-icon>
  52. <text class="favorite-text">收藏</text>
  53. </view>
  54. <view class="exchange-btn" @click="onExchange">
  55. <text class="exchange-text">申请兑换</text>
  56. </view>
  57. </view>
  58. <GlobalPopup ref="globalPopupRef" />
  59. </view>
  60. </template>
  61. <script>
  62. export default {
  63. name: 'GoodsDetail',
  64. data() {
  65. return {
  66. goodsId: '',
  67. goodsData: {
  68. id: 1,
  69. name: '哪吒之魔童闹海新款首套装哪吒校内艺术手办树脂摆件学生小礼品',
  70. points: 100,
  71. category: '积分兑换',
  72. exchangeCount: 120,
  73. stock: 50,
  74. description: '这是一款美味的薄脆小饼干,口感酥脆,营养丰富。采用优质原料制作,无添加剂,适合全家人享用。每一口都能感受到浓郁的香味和酥脆的口感,是您休闲时光的最佳选择。',
  75. gallery: [
  76. '/static/商城_商品1.png',
  77. '/static/商城_商品2.png',
  78. '/static/bannerImage.png',
  79. '/static/商城_商品1.png',
  80. '/static/商城_商品2.png',
  81. '/static/bannerImage.png'
  82. ]
  83. }
  84. }
  85. },
  86. onLoad(options) {
  87. if (options.id) {
  88. this.goodsId = options.id;
  89. this.getGoodsDetail(options.id);
  90. }
  91. },
  92. methods: {
  93. async getGoodsDetail(id) {
  94. let params = {}
  95. if (uni.getStorageSync('token')) {
  96. params.token = uni.getStorageSync('token')
  97. }
  98. const res = await this.$api.shop.queryGoodsById({ goodsId: id, ...params })
  99. this.goodsData = res.result
  100. },
  101. async toggleFavorite() {
  102. const res = await this.$api.shop.collectionGoods({ goodsId: this.goodsId })
  103. uni.showToast({
  104. title: res.message,
  105. icon: res.code === 200 ? 'success' : 'none'
  106. });
  107. this.getGoodsDetail(this.goodsId)
  108. },
  109. previewImage(current, urls) {
  110. uni.previewImage({
  111. current: current,
  112. urls: urls
  113. });
  114. },
  115. onExchange() {
  116. uni.showModal({
  117. title: '确认兑换',
  118. content: `确定要用${this.goodsData.price}积分兑换${this.goodsData.title}吗?`,
  119. success: async (res) => {
  120. if (res.confirm) {
  121. try{
  122. const res2 = await this.$api.shop.buyGoods({
  123. goodsId: this.goodsId
  124. })
  125. this.$refs.globalPopupRef.open({
  126. content: '恭喜您兑换成功!',
  127. subContent: '请及时到社区领取商品!',
  128. titleType: 'exchange',
  129. popupType: 'success',
  130. closefn: () => {
  131. setTimeout(() => {
  132. uni.navigateBack()
  133. }, 300);
  134. }
  135. })
  136. }catch (error) {
  137. console.log(error);
  138. if(error.code === 500 && error.message === '商品库存不足,兑换失败'){
  139. this.$refs.globalPopupRef.open({
  140. content: '库存不足!',
  141. subContent: '库存不足暂时不能兑换哦!',
  142. titleType: 'exchange',
  143. popupType: 'fail',
  144. closefn: () => {
  145. setTimeout(() => {
  146. uni.navigateBack()
  147. }, 300);
  148. }
  149. })
  150. }else if(error.code === 500 && error.message === '积分不足,兑换失败'){
  151. this.$refs.globalPopupRef.open({
  152. content: '您的积分不够哦!',
  153. subContent: '可以参与更多活动获取积分哦!',
  154. titleType: 'exchange',
  155. popupType: 'fail',
  156. closefn: () => {
  157. setTimeout(() => {
  158. uni.navigateBack()
  159. }, 300);
  160. }
  161. })
  162. }
  163. }
  164. }
  165. }
  166. });
  167. }
  168. }
  169. }
  170. </script>
  171. <style lang="scss" scoped>
  172. .goods-detail {
  173. background: #f8f8f8;
  174. min-height: 100vh;
  175. padding-bottom: 120rpx; // 为底部固定栏留出空间
  176. }
  177. .banner-container {
  178. height: 700rpx;
  179. .banner-swiper {
  180. width: 100%;
  181. .banner-image {
  182. width: 100%;
  183. height: 100%;
  184. }
  185. }
  186. }
  187. .goods-info {
  188. background: #ffffff;
  189. margin-top: 20rpx;
  190. padding: 30rpx;
  191. }
  192. .points-section {
  193. display: flex;
  194. align-items: center;
  195. margin-bottom: 20rpx;
  196. .points-icon {
  197. width: 32rpx;
  198. height: 32rpx;
  199. margin-right: 8rpx;
  200. }
  201. .points-text {
  202. font-size: 32rpx;
  203. font-weight: bold;
  204. color: #1488DB;
  205. }
  206. }
  207. .title-section {
  208. margin-bottom: 40rpx;
  209. .goods-title {
  210. font-size: 28rpx;
  211. color: #333333;
  212. line-height: 1.5;
  213. display: block;
  214. }
  215. }
  216. .detail-container {
  217. background: #ffffff;
  218. margin-top: 20rpx;
  219. padding: 30rpx;
  220. margin-bottom: 120rpx;
  221. }
  222. .detail-title-section {
  223. display: flex;
  224. align-items: center;
  225. justify-content: center;
  226. margin-bottom: 30rpx;
  227. .detail-title {
  228. font-size: 28rpx;
  229. font-weight: bold;
  230. color: #333333;
  231. }
  232. }
  233. .gallery-section {
  234. margin-bottom: 40rpx;
  235. .gallery-grid {
  236. display: grid;
  237. grid-template-columns: repeat(3, 1fr);
  238. gap: 16rpx;
  239. .gallery-image {
  240. width: 100%;
  241. height: 200rpx;
  242. border-radius: 12rpx;
  243. border: 1rpx solid #f0f0f0;
  244. }
  245. }
  246. }
  247. .bottom-bar {
  248. position: fixed;
  249. bottom: 0;
  250. left: 0;
  251. right: 0;
  252. background: #ffffff;
  253. padding: 20rpx 30rpx;
  254. border-top: 1rpx solid #f0f0f0;
  255. z-index: 999;
  256. box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
  257. display: flex;
  258. align-items: center;
  259. justify-content: space-between;
  260. .stock-info {
  261. display: flex;
  262. flex-direction: column;
  263. align-items: center;
  264. .stock-text {
  265. font-size: 24rpx;
  266. color: #1488DB;
  267. margin-bottom: 4rpx;
  268. font-weight: bold;
  269. }
  270. .stock-label {
  271. font-size: 22rpx;
  272. color: #999999;
  273. }
  274. }
  275. .favorite-section {
  276. flex: 1;
  277. display: flex;
  278. flex-direction: column;
  279. align-items: center;
  280. .favorite-text {
  281. font-size: 22rpx;
  282. color: #999999;
  283. margin-top: 4rpx;
  284. }
  285. }
  286. .exchange-btn {
  287. flex: 2;
  288. margin-left: 50rpx;
  289. height: 80rpx;
  290. border-radius: 40rpx;
  291. background-color: #1488DB;
  292. display: flex;
  293. align-items: center;
  294. justify-content: center;
  295. .exchange-text {
  296. color: #ffffff;
  297. font-size: 28rpx;
  298. // font-weight: bold;
  299. }
  300. }
  301. }
  302. </style>