木邻有你前端代码仓库
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.

311 lines
6.9 KiB

3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months 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.sales }}/{{ goodsData.inventory }}</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. </view>
  59. </template>
  60. <script>
  61. export default {
  62. name: 'GoodsDetail',
  63. data() {
  64. return {
  65. goodsId: '',
  66. goodsData: {
  67. id: 1,
  68. name: '哪吒之魔童闹海新款首套装哪吒校内艺术手办树脂摆件学生小礼品',
  69. points: 100,
  70. category: '积分兑换',
  71. exchangeCount: 120,
  72. stock: 50,
  73. description: '这是一款美味的薄脆小饼干,口感酥脆,营养丰富。采用优质原料制作,无添加剂,适合全家人享用。每一口都能感受到浓郁的香味和酥脆的口感,是您休闲时光的最佳选择。',
  74. gallery: [
  75. '/static/商城_商品1.png',
  76. '/static/商城_商品2.png',
  77. '/static/bannerImage.png',
  78. '/static/商城_商品1.png',
  79. '/static/商城_商品2.png',
  80. '/static/bannerImage.png'
  81. ]
  82. }
  83. }
  84. },
  85. onLoad(options) {
  86. if (options.id) {
  87. this.goodsId = options.id;
  88. this.getGoodsDetail(options.id);
  89. }
  90. },
  91. methods: {
  92. async getGoodsDetail(id) {
  93. let params = {}
  94. if (uni.getStorageSync('token')) {
  95. params.token = uni.getStorageSync('token')
  96. }
  97. const res = await this.$api.shop.queryGoodsById({ goodsId: id, ...params })
  98. this.goodsData = res.result
  99. },
  100. async toggleFavorite() {
  101. const res = await this.$api.shop.collectionGoods({ goodsId: this.goodsId })
  102. uni.showToast({
  103. title: res.message,
  104. icon: res.code === 200 ? 'success' : 'none'
  105. });
  106. this.getGoodsDetail(this.goodsId)
  107. },
  108. previewImage(current, urls) {
  109. uni.previewImage({
  110. current: current,
  111. urls: urls
  112. });
  113. },
  114. onExchange() {
  115. uni.showModal({
  116. title: '确认兑换',
  117. content: `确定要用${this.goodsData.price}积分兑换${this.goodsData.title}吗?`,
  118. success: async (res) => {
  119. if (res.confirm) {
  120. const res = await this.$api.shop.buyGoods({
  121. goodsId: this.goodsId
  122. })
  123. if (res.code === 200){
  124. uni.showToast({
  125. title: res.message,
  126. icon: 'success'
  127. });
  128. // 延迟返回上一页
  129. setTimeout(() => {
  130. uni.navigateBack();
  131. }, 1000);
  132. }else {
  133. uni.showToast({
  134. title: res.message,
  135. icon: 'none'
  136. });
  137. }
  138. }
  139. }
  140. });
  141. }
  142. }
  143. }
  144. </script>
  145. <style lang="scss" scoped>
  146. .goods-detail {
  147. background: #f8f8f8;
  148. min-height: 100vh;
  149. padding-bottom: 120rpx; // 为底部固定栏留出空间
  150. }
  151. .banner-container {
  152. height: 700rpx;
  153. .banner-swiper {
  154. width: 100%;
  155. .banner-image {
  156. width: 100%;
  157. height: 100%;
  158. }
  159. }
  160. }
  161. .goods-info {
  162. background: #ffffff;
  163. margin-top: 20rpx;
  164. padding: 30rpx;
  165. }
  166. .points-section {
  167. display: flex;
  168. align-items: center;
  169. margin-bottom: 20rpx;
  170. .points-icon {
  171. width: 32rpx;
  172. height: 32rpx;
  173. margin-right: 8rpx;
  174. }
  175. .points-text {
  176. font-size: 32rpx;
  177. font-weight: bold;
  178. color: #1488DB;
  179. }
  180. }
  181. .title-section {
  182. margin-bottom: 40rpx;
  183. .goods-title {
  184. font-size: 28rpx;
  185. color: #333333;
  186. line-height: 1.5;
  187. display: block;
  188. }
  189. }
  190. .detail-container {
  191. background: #ffffff;
  192. margin-top: 20rpx;
  193. padding: 30rpx;
  194. margin-bottom: 120rpx;
  195. }
  196. .detail-title-section {
  197. display: flex;
  198. align-items: center;
  199. justify-content: center;
  200. margin-bottom: 30rpx;
  201. .detail-title {
  202. font-size: 28rpx;
  203. font-weight: bold;
  204. color: #333333;
  205. }
  206. }
  207. .gallery-section {
  208. margin-bottom: 40rpx;
  209. .gallery-grid {
  210. display: grid;
  211. grid-template-columns: repeat(3, 1fr);
  212. gap: 16rpx;
  213. .gallery-image {
  214. width: 100%;
  215. height: 200rpx;
  216. border-radius: 12rpx;
  217. border: 1rpx solid #f0f0f0;
  218. }
  219. }
  220. }
  221. .bottom-bar {
  222. position: fixed;
  223. bottom: 0;
  224. left: 0;
  225. right: 0;
  226. background: #ffffff;
  227. padding: 20rpx 30rpx;
  228. border-top: 1rpx solid #f0f0f0;
  229. z-index: 999;
  230. box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
  231. display: flex;
  232. align-items: center;
  233. justify-content: space-between;
  234. .stock-info {
  235. display: flex;
  236. flex-direction: column;
  237. align-items: center;
  238. .stock-text {
  239. font-size: 24rpx;
  240. color: #1488DB;
  241. margin-bottom: 4rpx;
  242. font-weight: bold;
  243. }
  244. .stock-label {
  245. font-size: 22rpx;
  246. color: #999999;
  247. }
  248. }
  249. .favorite-section {
  250. flex: 1;
  251. display: flex;
  252. flex-direction: column;
  253. align-items: center;
  254. .favorite-text {
  255. font-size: 22rpx;
  256. color: #999999;
  257. margin-top: 4rpx;
  258. }
  259. }
  260. .exchange-btn {
  261. flex: 2;
  262. margin-left: 50rpx;
  263. height: 80rpx;
  264. border-radius: 40rpx;
  265. background-color: #1488DB;
  266. display: flex;
  267. align-items: center;
  268. justify-content: center;
  269. .exchange-text {
  270. color: #ffffff;
  271. font-size: 28rpx;
  272. // font-weight: bold;
  273. }
  274. }
  275. }
  276. </style>