Pārlūkot izejas kodu

Merge branch 'dev' of https://git.citupro.com/zhengnaiwen_citu/menduner into dev

Xiao_123 5 mēneši atpakaļ
vecāks
revīzija
e5473da616

+ 8 - 8
src/api/mall.js

@@ -1,4 +1,4 @@
-import request from '@/config/axios'
+// import request from '@/config/axios'
 
 // // 提交积分商品兑换
 // export const redeemSubmit = async (data) => {
@@ -9,10 +9,10 @@ import request from '@/config/axios'
 //   })
 // }
 
-// 获取商品详情
-export const getProductDetail = async (params) => {
-  return request.get({
-    url: '/app-api/product/spu/get-detail',
-    params
-  })
-}
+// // 获取商品详情
+// export const getProductDetail = async (params) => {
+//   return request.get({
+//     url: '/app-api/product/spu/get-detail',
+//     params
+//   })
+// }

+ 22 - 0
src/api/mall/product.js

@@ -0,0 +1,22 @@
+import request from '@/config/axios'
+
+// 获取商品详情
+export const getProductDetail = async (params) => {
+  return request.get({
+    url: '/app-api/product/spu/get-detail',
+    params
+  })
+}
+
+// 获得商品评价分页
+export const getCommentPage = async (spuId, pageNo, pageSize, type) => {
+  return request.get({
+    url: '/app-api//product/comment/page',
+    params: {
+      spuId,
+      pageNo,
+      pageSize,
+      type,
+    },
+  })
+}

+ 15 - 14
src/components/FormUI/su-number-box/su-number-box.vue

@@ -48,7 +48,7 @@
       :disabled="inputValue >= max || disabled" 
       @click="_calcValue('plus')"
     ></v-btn>
-    <div class="ml-3" style="color: #b7b7b7; font-size: 14px;">库存:{{ stock }}</div>
+    <div class="ml-3" style="color: #b7b7b7; font-size: 14px;">库存:{{ totalStock }}</div>
   </view>
 </template>
 <script>
@@ -88,6 +88,10 @@
         type: Number,
         default: 100,
       },
+      totalStock: {
+        type: Number,
+        default: 0,
+      },
       step: {
         type: Number,
         default: 1,
@@ -122,13 +126,9 @@
       modelValue(val) {
         this.inputValue = +val;
       },
-			// stock: {
-			// 	handler(val) {
-			// 		this.stock = +val
-			// 	},
-			// 	immediate: true,
-			// 	deep: true
-			// },
+      max(val) {
+        this.inputValue = this.inputValue-0 > + val ? + val : this.inputValue;
+      },
     },
     created() {
       if (this.value === 1) {
@@ -182,12 +182,13 @@
         return scale;
       },
       _onBlur(event) {
-        this.$emit('blur', event);
-        let value = event.detail.value;
-        if (!value) {
-          // this.inputValue = 0;
-          return;
-        }
+        // let value = event.detail.value;
+        // if (!value) {
+        //   // this.inputValue = 0;
+        //   return;
+        // }
+        let value = this.inputValue;
+        this.$emit('blur', value);
         value = +value;
         if (value > this.max) {
           value = this.max;

+ 5 - 5
src/views/mall/components/details.vue

@@ -60,7 +60,7 @@
           </v-tabs>
         </div>
         <describe v-if="describeTab === 0 && state.goodsInfo?.description" :content="state.goodsInfo.description"></describe>
-        <comment v-if="describeTab === 0 && state.goodsId" class="detail-comment-selector" :goodsId="state.goodsId" />
+        <comment v-if="describeTab === 1 && state.goodsId" class="detail-comment-selector" :goodsId="state.goodsId" />
       </div>
     </v-card>
   </div>
@@ -68,10 +68,10 @@
 
 <script setup>
 defineOptions({name: 'goods-details'})
-import { getProductDetail } from '@/api/mall'
+import { getProductDetail } from '@/api/mall/product'
 import selectSku from './details/s-select-sku.vue'
 import describe from './details/describe.vue'
-import comment from './details/comment.vue'
+import comment from './details/detail-comment-card.vue'
 import { ref, reactive } from 'vue'
 import { useRouter } from 'vue-router'
 import Snackbar from '@/plugins/snackbar'
@@ -79,7 +79,7 @@ import Snackbar from '@/plugins/snackbar'
 const router = useRouter()
 const { id } = router.currentRoute.value.params
 
-const describeTab = ref(0)
+const describeTab = ref(1)
 const selectedSkuPicUrl = ref('')
 const selectedSkuPrice = ref('')
 const selectedSkuMarketPrice = ref('')
@@ -111,7 +111,7 @@ getData()
 const calcPrice = (price) => { return price && (price-0) ? (price-0)/100 : '--' }
 
 const state = reactive({
-  goodsId: 0,
+  goodsId: id || 0,
   skeletonLoading: true, // SPU 加载中
   goodsInfo: {}, // SPU 信息
   showSelectSku: false, // 是否展示 SKU 选择弹窗

+ 105 - 0
src/views/mall/components/details/comment-item.vue

@@ -0,0 +1,105 @@
+<!-- 商品评论项 -->
+<template>
+  <div>
+    <!-- 用户评论 -->
+    <div class="d-flex align-center mb-2">
+      <div class="d-flexx mr-3">
+        <!-- <image class="avatar" :src="item.userAvatar"></image> -->
+        <v-avatar class="avatar">
+          <v-img alt="" :src="getUserAvatar(item.userAvatar, '1')" ></v-img>
+        </v-avatar>
+      </div>
+      <div class="nickname mr-3 mb-1">{{ item.userNickname }}</div>
+      <div class="">
+        <!-- <uni-rate :readonly="true" v-model="item.scores" size="18" /> -->
+        <v-rating
+          readonly
+          :length="5"
+          :size="26"
+          v-model="item.scores"
+          active-color="#ffca3e"
+        />
+      </div>
+    </div>
+    <div class="ml-12 mb-3 pb-5" style="border-bottom: 1px solid #EBEBEB;">
+      <div class="content"> {{ item.content }} </div>
+      <div class="ss-m-t-24" v-if="item.picUrls?.length">
+        <div class="scroll-box">
+          <div class="ss-flex">
+            <div v-for="(picUrl, index) in item.picUrls" :key="picUrl" class="ss-m-r-10">
+              <v-img :src="picUrl" :aspect-ratio="1" :current="index" style="border-radius: 8px;"></v-img>
+              <!-- <su-image
+                class="content-img"
+                isPreview
+                :previewList="item.picUrls"
+                :current="index"
+                :src="picUrl"
+                :height="120"
+                :width="120"
+                mode="aspectFill"
+              /> -->
+            </div>
+          </div>
+        </div>
+      </div>
+      <!-- 商家回复 -->
+      <div class="reply-box mt-3 px-3 py-2 d-flex" v-if="item.replyTime">
+        <div class="reply-title">商家回复:</div>
+        <div class="reply-content">{{ item.replyContent }}</div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { getUserAvatar } from '@/utils/avatar'
+  const props = defineProps({
+    item: {
+      type: Object,
+      default() {},
+    },
+  });
+</script>
+
+<style lang="scss" scoped>
+  .avatar {
+    width: 52rpx;
+    height: 52rpx;
+    border-radius: 50%;
+  }
+
+  .nickname {
+    font-size: 26rpx;
+    font-weight: 500;
+    color: #999999;
+  }
+
+  .content {
+    width: 636rpx;
+    font-size: 26rpx;
+    font-weight: 400;
+    color: #333333;
+  }
+
+  .reply-box {
+    background: #f8f8f8;
+    border-radius: 8rpx;
+    padding: 16rpx;
+  }
+
+  .reply-title {
+    // position: absolute;
+    font-weight: 400;
+    font-size: 26rpx;
+    line-height: 40rpx;
+    color: #333333;
+  }
+
+  .reply-content {
+    text-indent: 128rpx;
+    font-weight: 400;
+    font-size: 26rpx;
+    line-height: 40rpx;
+    color: #333333;
+  }
+</style>

+ 101 - 0
src/views/mall/components/details/detail-comment-card.vue

@@ -0,0 +1,101 @@
+<!-- 商品评论的卡片 -->
+<template>
+  <div class="detail-comment-card ">
+    <div class="card-header d-flex align-center justify-space-between pb-6">
+      <div class="resume-header">
+        <div class="resume-title">
+          评价<span class="des ml-1">({{ state.total }})</span>
+        </div>
+      </div>
+      <!-- @tap="sheep.$router.go('/pages/goods/comment/list', { id: goodsId })" -->
+      <div
+        class="ss-flex ss-col-center"
+        @click="null"
+        v-if="state.commentList.length > 0"
+      >
+        <v-btn variant="text" class="ss-reset-button more-btn">查看全部</v-btn>
+        <span class="cicon-forward" />
+      </div>
+    </div>
+    <!-- 评论列表 -->
+    <div class="card-content">
+      <div class="comment-box ss-p-y-30" v-for="item in state.commentList" :key="item.id">
+        <comment-item :item="item" />
+      </div>
+      <div v-if="state.commentList.length === 0" style="margin: 30px auto; color: #777;">期待您的第一个评价</div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+  import { reactive, onBeforeMount } from 'vue';
+  import { getCommentPage } from '@/api/mall/product'
+  import commentItem from './comment-item.vue';
+
+  const props = defineProps({
+    goodsId: {
+      type: [Number, String],
+      default: 0,
+    },
+  });
+
+  const state = reactive({
+    commentList: [], // 评论列表,只展示最近的 3 条
+    total: 0, // 总评论数
+  });
+
+  async function getComment(id) {
+    const res = await getCommentPage(id, 1, 3, 0);
+    state.commentList = res.list;
+    state.total = res.total;
+  }
+
+  onBeforeMount(() => {
+    getComment(props.goodsId);
+  });
+</script>
+
+<style lang="scss" scoped>
+  .detail-comment-card {
+    margin: 0 20rpx 20rpx 20rpx;
+    padding: 20rpx 20rpx 0 20rpx;
+    .card-header {
+      .line {
+        width: 6rpx;
+        height: 30rpx;
+        background: linear-gradient(180deg, var(--v-primary-base) 0%, var(--v-primary-lighten1) 100%);
+        border-radius: 3rpx;
+      }
+
+      .title {
+        font-size: 30rpx;
+        font-weight: bold;
+        line-height: normal;
+      }
+
+      .des {
+        font-size: 24rpx;
+        color: #999999;
+      }
+
+      .more-btn {
+        font-size: 24rpx;
+        color: var(--v-primary-base);
+        line-height: normal;
+      }
+
+      .cicon-forward {
+        font-size: 24rpx;
+        line-height: normal;
+        color: var(--v-primary-base);
+        margin-top: 4rpx;
+      }
+    }
+  }
+  .comment-box {
+    border-bottom: 2rpx solid #eeeeee;
+    &:last-child {
+      border: none;
+    }
+  }
+</style>

+ 3 - 10
src/views/mall/components/details/s-select-sku.vue

@@ -85,6 +85,7 @@
           <su-number-box
             :min="1"
             :max="state.selectedSku.stock"
+            :totalStock="totalStock"
             :step="1"
             ref="selectSkuRef"
             v-model="state.selectedSku.goods_num"
@@ -104,7 +105,7 @@
 defineOptions({name: 'wares-s-select-sku'})
 import Snackbar from '@/plugins/snackbar'
 import suNumberBox from '@/components/FormUI/su-number-box/su-number-box.vue'
-import { computed, reactive, watch, ref, onMounted } from 'vue'
+import { computed, reactive, watch, ref } from 'vue'
 import { convertProductPropertyList } from '@/views/mall/utils'
 
 const emits = defineEmits(['change', 'addCart', 'buy', 'close']);
@@ -116,13 +117,6 @@ const props = defineProps({
 });
 
   const totalStock = ref(props.goodsInfo?.stock-0 || 0)
-  function setTotalStock() {
-    if (selectSkuRef.value) selectSkuRef.value.stock = totalStock.value
-  }
-  onMounted(() => {
-    setTotalStock()
-  })
-  const selectSkuRef = ref()
   const state = reactive({
     selectedSku: {}, // 选中的 SKU
     currentPropertyArray: [], // 当前选中的属性,实际是个 Map。key 是 property 编号,value 是 value 编号
@@ -141,8 +135,7 @@ const props = defineProps({
   watch(
     () => state.selectedSku,
     (newVal) => {
-      if (newVal?.stock) selectSkuRef.value.stock = newVal.stock
-      else setTotalStock()
+      if (newVal?.stock) totalStock.value = newVal.stock
       emits('change', newVal);
     },
     {