lifanagju_citu 5 ヶ月 前
コミット
af8fa4ebff

+ 18 - 0
src/styles/index.scss

@@ -209,4 +209,22 @@
 .form-label {
   font-size: 14px;
   opacity: .6;
+}
+
+/* 均匀间隔显示的文本 */
+.justified-text {
+  width: 70px; /* 设定固定长度 */
+  text-align: justify; /* 使文本两端对齐 */
+  text-justify: inter-word; /* 为了兼容性 */
+  height: 100%;
+  line-height: 100%;
+}
+.justified-text:after {
+  content: '';
+  display: inline-block;
+  width: 100%; /* 设定额外间隔 */
+}
+.l-s-10 {
+  letter-spacing: 10px;
+  margin-right: -10px;
 }

+ 5 - 1
src/utils/index.js

@@ -108,4 +108,8 @@ export const saveAs = (blob, filename) => {
   link.href = window.URL.createObjectURL(blob)
   link.download = filename
   link.click()
-}
+}
+
+export const spaces = (num = 6) => {
+  return '\xa0'.repeat(num)
+}

+ 62 - 26
src/views/mall/components/details.vue

@@ -1,29 +1,29 @@
 <!-- 商品详情 -->
 <template>
-  <div class="default-width py-5">
+  <div class="default-width py-5" v-if="goodsInfo && Object.keys(goodsInfo).length">
     <v-card class="carousel border-radius-8 white-bgc pa-5 d-flex" style="width: 100%;">
       <!-- 图片展示-轮播 -->
-      <v-carousel show-arrows="hover" class="carousel mr-5" cycle :model-value="0" style="width: 450px;">
-        <v-carousel-item v-for="(imgUrl, i) in info.sliderPicUrls" :key="'wareImg'+i" @click="null">
-          <div style="height: 450px; overflow: hidden;">
-            <v-img :src="imgUrl" cover style="height: 100%; overflow: hidden;"></v-img>
-          </div>
-        </v-carousel-item>
-      </v-carousel>
-      <div>
+      <div style="width: 500px; height: 500px;">
+        <v-carousel show-arrows="hover" cycle :model-value="0" :hide-delimiters="!carouselHover" @mouseover="carouselHover = true" @mouseleave="carouselHover = false">
+          <v-carousel-item v-for="(imgUrl, i) in goodsInfo.sliderPicUrls" :key="'wareImg'+i" @click="null">
+            <v-img :src="imgUrl" :aspect-ratio="1" style="border-radius: 8px;"></v-img>
+          </v-carousel-item>
+        </v-carousel>
+      </div>
+      <div style="flex: 1;" class="pl-10">
         <!-- 大标题 -->
-        <div class="title-name">{{ info?.name || '--' }}</div>
+        <div class="title-name">{{ goodsInfo?.name || '--' }}</div>
         <!-- 小标题 -->
-        <div class="title-introduction">{{ info?.introduction || '--' }}</div>
+        <div class="title-introduction">{{ goodsInfo?.introduction || '--' }}</div>
         <!-- 价格 -->
-        <div class="d-flex">
-          <div class="price mr-3">{{ calcPrice(info?.price)}}</div>
-          <div class="marketPrice">{{ calcPrice(info?.marketPrice)}}</div>
+        <div class="prices my-5">
+          <div class="price mr-3"><span>¥</span>{{ calcPrice(goodsInfo?.price)}}</div>
+          <div class="marketPrice" v-if="goodsInfo?.marketPrice">{{ calcPrice(goodsInfo?.marketPrice)}}</div>
         </div>
         <!-- 销量 -->
-        <div class="salesCount">已售:{{ info?.salesCount || 0 }}</div>
-        <!-- 属性选择 -->
-        <selectSku v-if="skus?.length" :skus="skus"></selectSku>
+        <div class="salesCount mb-5 parameterColor"><span class="l-s-10">已售</span>:{{ goodsInfo?.salesCount || 0 }}</div>
+        <!-- 属性选择组件 -->
+        <selectSku v-if="skus?.length" :goodsInfo="goodsInfo" class="mb-7"></selectSku>
       </div>
     </v-card>
   </div>
@@ -35,28 +35,64 @@ import { getProductDetail } from '@/api/mall'
 import selectSku from './detailsComponents/s-select-sku.vue'
 import { ref } from 'vue'
 
-const info = ref({})
+const carouselHover = ref(false)
+const goodsInfo = ref({})
 const skus = ref([])
 // 获取商品详情
 const getData = async () => {
   const obj = await getProductDetail({ id: 646 })
   obj.sliderPicUrls = obj.sliderPicUrls || []
-  info.value = obj
   skus.value = obj.skus || []
-  console.log('getProductDetail:', info.value)
+  goodsInfo.value = obj
+  console.log('getProductDetail:', goodsInfo.value)
 }
 getData()
 
-const calcPrice = (price) => { return price && (price-0) ? (price-0)/100 : '--' }
+const calcPrice = (price) => { return price && (price-0) ? (price-0)/100 : '' }
 
 </script>
 <style lang="scss" scoped>
-.carousel {
-  :deep(.v-window) {
-    height: 450px !important;
-  }
-}
 .border-radius-8 {
   border-radius: 8px;
 }
+.title-name {
+  font-size: 24px;
+  line-height: 1.25;
+  color: #000;
+  margin-bottom: 12px;
+  font-weight: bold;
+}
+.title-introduction {
+  font-size: 18px;
+  line-height: 1.5;
+  color: #6b6b6b;
+}
+.parameterColor {
+  color: #7a7a7a;
+}
+.prices {
+  display: flex;
+  align-items: flex-end;
+  color: #ff5000;
+  font-size: 28px;
+  margin: 8px 0;
+}
+.price {
+  font-weight: 600;
+  span {
+    font-size: 16px;
+  }
+}
+.marketPrice {
+  color: #b7b7b7;
+  font-size: 16px;
+  line-height: 34px;
+  text-decoration: line-through;
+}
+.buyCount {
+  display: flex;
+  align-items: center;
+}
+.salesCount {
+}
 </style>

+ 78 - 37
src/views/mall/components/detailsComponents/s-select-sku.vue

@@ -1,46 +1,63 @@
 <template>
   <div class="ss-modal-box bg-white ss-flex-col">
-    <!-- SKU 信息 - 属性选择 -->
-    <div class="modal-content ss-flex-1">
-      <div class="modal-content-scroll">
-        <div class="sku-item ss-m-b-20" v-for="property in propertyList" :key="property.id">
-          <span class="label-text ss-m-b-20">{{ property.name }}:</span>
-          <span class="ss-flex ss-col-center ss-flex-wrap">
-            <!-- :disabled="value.disabled === true" -->
-            <v-chip
-              class="spec-btn mr-3"
-              :class="[
-                { 'ui-BG-Main-Gradient': state.currentPropertyArray[property.id] === value.id, },
-                { 'disabled-btn': value.disabled === true, },
-              ]"
-              v-for="value in property.values"
-              :key="value.id"
-              size="small"
-              label
-              density="comfortable"
-              color="primary"
-              variant="outlined"
-              :disabled="value.disabled === true"
-              @click="onSelectSku(property.id, value.id)"
-            >
-              {{ value.name }}
-            </v-chip>
-          </span>
-        </div>
+    <!-- SKU 信息 -->
+    <!-- 属性选择 -->
+    <div class="d-flex mb-5" v-for="property in propertyList" :key="property.id">
+      <span class="l-s-10 parameterColor mb-1">{{ property.name }}</span>:
+      <span style="flex: 1;">
+        <v-chip
+          class="spec-btn mb-1 mr-3"
+          :class="[
+            { 'ui-BG-Main-Gradient': state.currentPropertyArray[property.id] === value.id, },
+            { 'disabled-btn': value.disabled === true, },
+          ]"
+          v-for="value in property.values"
+          :key="value.id"
+          size="small"
+          label
+          density="comfortable"
+          color="primary"
+          variant="outlined"
+          :disabled="value.disabled === true"
+          @click="onSelectSku(property.id, value.id)"
+        >
+          {{ value.name }}
+        </v-chip>
+      </span>
+    </div>
+    <!-- 购买数量 -->
+    <div class="buyCount mb-10">
+      <span class="parameterColor"><span class="l-s-10">数量</span>:</span>
+      <div class="d-flex">
+        <v-btn icon="mdi-minus" size="x-small" :disabled="number<=1" @click="number > 1 ? number-- : 0"></v-btn>
+        <input
+          v-model="state.selectedSku.goods_num"
+          class="inputItem mx-2"
+          type="number"
+          :disabled="!goodsInfo.stock"
+          @change="onNumberChange"
+        >
+        <v-btn icon="mdi-plus" size="x-small" :disabled="goodsInfo.stock ? number >= (state.selectedSku.stock) : true" @click="number ? number++ : 0"></v-btn>
       </div>
+      <!-- 库存 -->
+      <div class="ml-3" style="color: #b7b7b7; font-size: 14px;">{{ formatStock('exact', state.selectedSku.stock || goodsInfo.stock) }}</div>
+      <!-- <div class="ml-3" style="color: #b7b7b7; font-size: 14px;">库存:{{ goodsInfo.stock || 0 }}</div> -->
+    </div>
+    <div>
+      <v-btn class="buttons" color="primary" @click="null">立即购买</v-btn>
     </div>
   </div>
 </template>
 
 <script setup>
+defineOptions({name: 'wares-s-select-sku'})
 import { computed, reactive, watch, ref } from 'vue'
 import { convertProductPropertyList } from '@/views/mall/utils'
-
 const emits = defineEmits(['change', 'addCart', 'buy', 'close']);
 const props = defineProps({
-  skus: {
-    type: Array,
-  default: () => []
+  goodsInfo: {
+    type: Object,
+    default() {},
   }
 });
 
@@ -49,22 +66,37 @@ const state = reactive({
   currentPropertyArray: [], // 当前选中的属性,实际是个 Map。key 是 property 编号,value 是 value 编号
 });
 
+const onNumberChange = () => {
+  if (number.value === 0) return
+  if (state.selectedSku.goods_num === number.value) return
+  state.selectedSku.goods_num = number.value
+  // if ( number.value < 0) return number.value = goodsInfo.value.stock ? 1 : 0
+  // if ( number.value > goodsInfo.value.stock) number.value = goodsInfo.value.stock
+  // number.value = parseInt( number.value)
+}
+
 // SKU 列表
 const skuList = computed(() => {
-  let skuPrices =  props.skus?.length ? props.skus : []
+  let skuPrices =  props.goodsInfo.skus?.length ? props.goodsInfo.skus : []
   for (let price of skuPrices) {
     price.value_id_array = price.properties.map((item) => item.valueId);
   }
   return skuPrices;
 });
 
+const number = ref(0)
+const stockShow = ref(0)
 const propertyList = ref([])
 watch(
-  () => props.skus,
+  () => props.goodsInfo,
   (newVal) => {
-    if (newVal && newVal.length) {
-      propertyList.value = props.skus?.length ? convertProductPropertyList(props.skus) : []
-      console.log('propertyList:', propertyList.value)
+    if (newVal && Object.keys(newVal)) {
+      stockShow.value = newVal.goodsInfo.stock-0
+      number.value = stock.value ? 1 : 0
+      if (newVal.skus && newVal.skus.length) {
+        propertyList.value = newVal.goodsInfo.skus?.length ? convertProductPropertyList(newVal.goodsInfo.skus) : []
+        // console.log('propertyList:', propertyList.value)
+      }
     }
   },
   {
@@ -231,9 +263,18 @@ changeDisabled(false);
   background: var(--v-primary-base);
   color: #fff !important;
 }
-
 .disabled-btn {
   color: #c6c6c6;
   background: #f8f8f8;
 }
+.parameterColor {
+  color: #7a7a7a;
+}
+input::-webkit-outer-spin-button, input::-webkit-inner-spin-button{
+  -webkit-appearance: none !important;
+  margin: 0; 
+}
+.inputItem {
+  width: 70px; border: 1px solid #eee; padding: 0 5px; text-align: center;
+}
 </style>