Bladeren bron

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

zhengnaiwen_citu 5 maanden geleden
bovenliggende
commit
9e6d5b160f

+ 9 - 0
src/api/mall/user.js

@@ -0,0 +1,9 @@
+import request from '@/config/axios'
+
+// 获取订单分页
+export const getMallOrderPage = async (params) => {
+  return request.get({
+    url: '/app-api/trade/order/page',
+    params
+  })
+}

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

@@ -33,12 +33,6 @@
     <!-- <view @click="_calcValue('plus')" class="uni-numbox__plus uni-numbox-btns">
     <!-- <view @click="_calcValue('plus')" class="uni-numbox__plus uni-numbox-btns">
 			<text class="uni-numbox--text" :class="{ 'uni-numbox--disabled': inputValue >= max || disabled }">+</text>
 			<text class="uni-numbox--text" :class="{ 'uni-numbox--disabled': inputValue >= max || disabled }">+</text>
 		</view> -->
 		</view> -->
-    <v-btn
-      icon="mdi-plus" 
-      size="x-small" 
-      :disabled="inputValue >= max || disabled" 
-      @click="_calcValue('plus')"
-    ></v-btn>
     <!-- <text
     <!-- <text
       class="cicon-add-round"
       class="cicon-add-round"
       :class="{
       :class="{
@@ -48,6 +42,13 @@
       }"
       }"
       @click="_calcValue('plus')"
       @click="_calcValue('plus')"
     ></text> -->
     ></text> -->
+    <v-btn
+      icon="mdi-plus" 
+      size="x-small" 
+      :disabled="inputValue >= max || disabled" 
+      @click="_calcValue('plus')"
+    ></v-btn>
+    <div class="ml-3" style="color: #b7b7b7; font-size: 14px;">库存:{{ stock }}</div>
   </view>
   </view>
 </template>
 </template>
 <script>
 <script>
@@ -111,6 +112,7 @@
     data() {
     data() {
       return {
       return {
         inputValue: 0,
         inputValue: 0,
+        stock: 0,
       };
       };
     },
     },
     watch: {
     watch: {
@@ -120,6 +122,13 @@
       modelValue(val) {
       modelValue(val) {
         this.inputValue = +val;
         this.inputValue = +val;
       },
       },
+			// stock: {
+			// 	handler(val) {
+			// 		this.stock = +val
+			// 	},
+			// 	immediate: true,
+			// 	deep: true
+			// },
     },
     },
     created() {
     created() {
       if (this.value === 1) {
       if (this.value === 1) {

+ 4 - 0
src/store/user.js

@@ -151,7 +151,11 @@ export const useUserStore = defineStore('user',
         this.userInfo = {}
         this.userInfo = {}
         this.baseInfo = {}
         this.baseInfo = {}
         this.accountInfo = {}
         this.accountInfo = {}
+
+        // 商城模版数据不清楚缓存
+        const mallTemplate = localStorage.getItem('mallTemplate')
         localStorage.clear()
         localStorage.clear()
+        localStorage.setItem('mallTemplate', mallTemplate)
       },
       },
       // 切换为招聘者
       // 切换为招聘者
       async changeRole (res) {
       async changeRole (res) {

+ 1 - 1
src/version.js

@@ -1,2 +1,2 @@
 // 版本号
 // 版本号
-export const vue_version = 'v24.12.19.1226'
+export const vue_version = 'v24.12.19.1908'

+ 137 - 29
src/views/mall/components/details.vue

@@ -1,29 +1,66 @@
 <!-- 商品详情 -->
 <!-- 商品详情 -->
 <template>
 <template>
-  <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%;">
-      <!-- 图片展示-轮播 -->
-      <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 class="default-width py-5" v-if="state.goodsInfo && Object.keys(state.goodsInfo).length">
+    <v-card class="carousel border-radius-8 white-bgc pa-5" style="width: 100%;">
+      <div class=" d-flex">
+        <!-- 图片展示-轮播 -->
+        <div style="width: 500px; height: 500px;">
+          <div v-if="selectedSkuPicUrl" class="selectedSkuImgBox" @mouseover="showSelectedSkuImg = true" @mouseleave="showSelectedSkuImg = false">
+            <v-img :src="selectedSkuPicUrl" :aspect-ratio="1" style="border-radius: 8px;"></v-img>
+            <v-btn
+              v-show="showSelectedSkuImg"
+              size="x-small" 
+              class="close px-3"
+              :disabled="inputValue <= min || disabled" 
+              @click="selectedSkuPicUrl = ''"
+            >关闭</v-btn>
+          </div>
+          <v-carousel v-else show-arrows="hover" cycle :model-value="0" :hide-delimiters="!carouselHover" @mouseover="carouselHover = true" @mouseleave="carouselHover = false">
+            <v-carousel-item v-for="(imgUrl, i) in state.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>
+        <!-- s-select-sku 商品属性选择 -->
+        <div style="flex: 1;" class="pl-10">
+          <!-- 大标题 -->
+          <div class="title-name">{{ state.goodsInfo?.name || '--' }}</div>
+          <!-- 小标题 -->
+          <div class="title-introduction">{{ state.goodsInfo?.introduction || '--' }}</div>
+          <!-- 价格 -->
+          <div class="prices my-5">
+            <div class="price mr-3"><span>¥</span>{{ selectedSkuPrice}}</div>
+            <div class="marketPrice" v-if="state.goodsInfo?.marketPrice">{{ selectedSkuMarketPrice}}</div>
+          </div>
+          <!-- 销量 -->
+          <div class="salesCount mb-5 parameterColor"><span class="l-s-10">已售</span>:{{ state.goodsInfo?.salesCount || 0 }}</div>
+          <!-- 属性选择组件 -->
+          <selectSku
+            v-if="showSelectSku"
+            class="mb-7"
+            :goodsInfo="state.goodsInfo"
+            @change="onSkuChange"
+            @buy="onBuy"
+          ></selectSku>
+        </div>
       </div>
       </div>
-      <div style="flex: 1;" class="pl-10">
-        <!-- 大标题 -->
-        <div class="title-name">{{ goodsInfo?.name || '--' }}</div>
-        <!-- 小标题 -->
-        <div class="title-introduction">{{ goodsInfo?.introduction || '--' }}</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>
+    </v-card>
+    <!-- 详情描述 detail-content-card -->
+    <v-card class="carousel border-radius-8 white-bgc pa-5 mt-3" style="width: 100%;">
+      <!-- <div class="resume-header">
+        <div class="resume-title">
+          详情
         </div>
         </div>
-        <!-- 销量 -->
-        <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> -->
+      <div>
+        <div class="mb-3">
+          <v-tabs v-model="describeTab" align-tabs="start" color="primary" bg-color="#f7f8fa" @update:model-value="null">
+            <v-tab :value="0">商品介绍</v-tab>
+            <v-tab :value="1">商品评价</v-tab>
+          </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" />
       </div>
       </div>
     </v-card>
     </v-card>
   </div>
   </div>
@@ -33,26 +70,89 @@
 defineOptions({name: 'goods-details'})
 defineOptions({name: 'goods-details'})
 import { getProductDetail } from '@/api/mall'
 import { getProductDetail } from '@/api/mall'
 import selectSku from './detailsComponents/s-select-sku.vue'
 import selectSku from './detailsComponents/s-select-sku.vue'
-import { ref } from 'vue'
+import describe from './detailsComponents/describe.vue'
+import comment from './detailsComponents/comment.vue'
+import { ref, reactive } from 'vue'
 import { useRouter } from 'vue-router'
 import { useRouter } from 'vue-router'
+import Snackbar from '@/plugins/snackbar'
 
 
 const router = useRouter()
 const router = useRouter()
 const { id } = router.currentRoute.value.params
 const { id } = router.currentRoute.value.params
 
 
+const describeTab = ref(0)
+const selectedSkuPicUrl = ref('')
+const selectedSkuPrice = ref('')
+const selectedSkuMarketPrice = ref('')
+const showSelectSku = ref(false)
 const carouselHover = ref(false)
 const carouselHover = ref(false)
-const goodsInfo = ref({})
-const skus = ref([])
+const showSelectedSkuImg = ref(false)
 // 获取商品详情
 // 获取商品详情
 const getData = async () => {
 const getData = async () => {
   const obj = await getProductDetail({ id })
   const obj = await getProductDetail({ id })
+  if (!obj) return Snackbar.warning('未找到商品!')
+  //
+  // 加载到商品
   obj.sliderPicUrls = obj.sliderPicUrls || []
   obj.sliderPicUrls = obj.sliderPicUrls || []
-  skus.value = obj.skus || []
-  goodsInfo.value = obj
-  // console.log('getProductDetail:', goodsInfo.value)
+  state.skeletonLoading = false;
+  state.goodsInfo = obj
+  showSelectSku.value = true
+  // // 加载是否收藏
+  // if (isLogin.value) {
+  //   FavoriteApi.isFavoriteExists(state.goodsId, 'goods').then((res) => {
+  //     if (res.code !== 0) {
+  //       return;
+  //     }
+  //     state.goodsInfo.favorite = res.data;
+  //   });
+  // }
 }
 }
 getData()
 getData()
 
 
-const calcPrice = (price) => { return price && (price-0) ? (price-0)/100 : '' }
+const calcPrice = (price) => { return price && (price-0) ? (price-0)/100 : '--' }
+
+const state = reactive({
+  goodsId: 0,
+  skeletonLoading: true, // SPU 加载中
+  goodsInfo: {}, // SPU 信息
+  showSelectSku: false, // 是否展示 SKU 选择弹窗
+  selectedSku: {}, // 选中的 SKU
+  settlementSku: {}, // 结算的 SKU:由于 selectedSku 不进行默认选中,所以初始使用结算价格最低的 SKU 作为基础展示
+  showModel: false, // 是否展示 Coupon 优惠劵的弹窗
+  couponInfo: [], // 可领取的 Coupon 优惠劵的列表
+  showActivityModel: false, // 【满减送/限时折扣】是否展示 Activity 营销活动的弹窗
+  rewardActivity: {}, // 【满减送】活动
+  activityList: [], // 【秒杀/拼团/砍价】可参与的 Activity 营销活动的列表
+});
+
+// 规格变更
+function onSkuChange(e) {
+  state.selectedSku = e;
+  state.settlementSku = e;
+  // console.log('onSkuChange:', e)
+  selectedSkuPicUrl.value = state.selectedSku?.picUrl || ''
+  if (selectedSkuPicUrl.value) showSelectedSkuImg.value = true
+  selectedSkuPrice.value = calcPrice(state.selectedSku?.price || state.goodsInfo.price)
+  selectedSkuMarketPrice.value = calcPrice(state.selectedSku?.marketPrice || state.goodsInfo.marketPrice)
+}
+
+const onBuy = async () => {
+  Snackbar.warning('购买功能暂未开放,敬请期待!')
+}
+// function onBuy(sku) {
+//   sheep.$router.go('/pages/order/confirm', {
+//     data: JSON.stringify({
+//       order_type: 'goods',
+//       combinationActivityId: state.activity.id,
+//       combinationHeadId: state.combinationHeadId,
+//       items: [
+//         {
+//           skuId: sku.id,
+//           count: sku.count,
+//         },
+//       ],
+//     }),
+//   });
+// }
 
 
 </script>
 </script>
 <style lang="scss" scoped>
 <style lang="scss" scoped>
@@ -93,4 +193,12 @@ const calcPrice = (price) => { return price && (price-0) ? (price-0)/100 : '' }
   line-height: 34px;
   line-height: 34px;
   text-decoration: line-through;
   text-decoration: line-through;
 }
 }
+.selectedSkuImgBox {
+  position: relative;
+  .close {
+    position: absolute;
+    bottom: 10px;
+    right: 10px;
+  }
+}
 </style>
 </style>

+ 17 - 0
src/views/mall/components/detailsComponents/comment.vue

@@ -0,0 +1,17 @@
+<!--  -->
+<template>
+  <div>商品评价</div>
+</template>
+
+<script setup>
+defineOptions({name: 'goods-details-comment'})
+const props = defineProps({
+  goodsId: {
+    type: String,
+    default: '',
+  },
+});
+console.log('content:', props.goodsId)
+</script>
+<style lang="scss" scoped>
+</style>

+ 19 - 0
src/views/mall/components/detailsComponents/describe.vue

@@ -0,0 +1,19 @@
+<!--  -->
+<template>
+  <div class="detailsBox">
+    <span v-if="props.content.indexOf('style')" v-html="props.content"></span>
+  </div>
+</template>
+
+<script setup>
+defineOptions({name: 'goods-details-describe'})
+const props = defineProps({
+  content: {
+    type: String,
+    default: '',
+  },
+});
+console.log('content:', props.content)
+</script>
+<style lang="scss" scoped>
+</style>

+ 22 - 11
src/views/mall/components/detailsComponents/s-select-sku.vue

@@ -86,6 +86,7 @@
             :min="1"
             :min="1"
             :max="state.selectedSku.stock"
             :max="state.selectedSku.stock"
             :step="1"
             :step="1"
+            ref="selectSkuRef"
             v-model="state.selectedSku.goods_num"
             v-model="state.selectedSku.goods_num"
             @change="onNumberChange($event)"
             @change="onNumberChange($event)"
           />
           />
@@ -103,17 +104,25 @@
 defineOptions({name: 'wares-s-select-sku'})
 defineOptions({name: 'wares-s-select-sku'})
 import Snackbar from '@/plugins/snackbar'
 import Snackbar from '@/plugins/snackbar'
 import suNumberBox from '@/components/FormUI/su-number-box/su-number-box.vue'
 import suNumberBox from '@/components/FormUI/su-number-box/su-number-box.vue'
-import { computed, reactive, watch } from 'vue'
+import { computed, reactive, watch, ref, onMounted } from 'vue'
 import { convertProductPropertyList } from '@/views/mall/utils'
 import { convertProductPropertyList } from '@/views/mall/utils'
 
 
-  const emits = defineEmits(['change', 'addCart', 'buy', 'close']);
-  const props = defineProps({
-    goodsInfo: {
-      type: Object,
-      default() {},
-    },
-  });
-
+const emits = defineEmits(['change', 'addCart', 'buy', 'close']);
+const props = defineProps({
+  goodsInfo: {
+    type: Object,
+    default() {},
+  },
+});
+
+  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({
   const state = reactive({
     selectedSku: {}, // 选中的 SKU
     selectedSku: {}, // 选中的 SKU
     currentPropertyArray: [], // 当前选中的属性,实际是个 Map。key 是 property 编号,value 是 value 编号
     currentPropertyArray: [], // 当前选中的属性,实际是个 Map。key 是 property 编号,value 是 value 编号
@@ -132,6 +141,8 @@ import { convertProductPropertyList } from '@/views/mall/utils'
   watch(
   watch(
     () => state.selectedSku,
     () => state.selectedSku,
     (newVal) => {
     (newVal) => {
+      if (newVal?.stock) selectSkuRef.value.stock = newVal.stock
+      else setTotalStock()
       emits('change', newVal);
       emits('change', newVal);
     },
     },
     {
     {
@@ -163,11 +174,11 @@ import { convertProductPropertyList } from '@/views/mall/utils'
 
 
   // 立即购买
   // 立即购买
   function onBuy() {
   function onBuy() {
-    if (state.selectedSku.id <= 0) {
+    if (!state?.selectedSku?.id || state.selectedSku.id <= 0) {
       Snackbar.warning('请选择商品规格')
       Snackbar.warning('请选择商品规格')
       return;
       return;
     }
     }
-    if (state.selectedSku.stock <= 0) {
+    if (!state?.selectedSku?.stock || state.selectedSku.stock <= 0) {
       Snackbar.warning('库存不足')
       Snackbar.warning('库存不足')
       return;
       return;
     }
     }

+ 13 - 1
src/views/mall/components/navbar.vue

@@ -3,7 +3,7 @@
     <div class="default-width d-flex align-center justify-space-between">
     <div class="default-width d-flex align-center justify-space-between">
       <div class="header-link">
       <div class="header-link">
         <span class="cursor-pointer" :class="{'active-route' : isActive('/mall')}" @click="router.push('/mall')">首页</span>
         <span class="cursor-pointer" :class="{'active-route' : isActive('/mall')}" @click="router.push('/mall')">首页</span>
-        <span class="cursor-pointer mx-8" :class="{'active-route' : isActive('/mall/user', true)}" @click="router.push('/mall/user')">
+        <span class="cursor-pointer mx-8" :class="{'active-route' : isActive('/mall/user', true)}" @click="handleTo('/mall/user')">
           <v-icon>mdi-account-circle-outline</v-icon>
           <v-icon>mdi-account-circle-outline</v-icon>
           我的
           我的
         </span>
         </span>
@@ -35,13 +35,25 @@
 defineOptions({ name: 'formPage'})
 defineOptions({ name: 'formPage'})
 import { computed, ref } from 'vue'
 import { computed, ref } from 'vue'
 import { useRouter } from 'vue-router'
 import { useRouter } from 'vue-router'
+import { getToken } from '@/utils/auth'
+import Snackbar from '@/plugins/snackbar'
 
 
+const emit = defineEmits(['login'])
 const router = useRouter()
 const router = useRouter()
 const isActive = computed(() => (path, hasChild) => {
 const isActive = computed(() => (path, hasChild) => {
   const currentPath = router.currentRoute.value.path
   const currentPath = router.currentRoute.value.path
   return hasChild ? currentPath.includes(path) : currentPath === path
   return hasChild ? currentPath.includes(path) : currentPath === path
 })
 })
 
 
+const handleTo = (path) => {
+  if (!getToken()) {
+    Snackbar.warning('请先登录')
+    emit('login', path)
+    return
+  }
+  router.push(path)
+}
+
 const inputVal = ref('')
 const inputVal = ref('')
 const handleSearch = () => {}
 const handleSearch = () => {}
 </script>
 </script>

+ 1 - 0
src/views/mall/home/components/hotGoods.vue

@@ -36,6 +36,7 @@ const router = useRouter()
 const goodList = ref([])
 const goodList = ref([])
 const getGoodsList = async () => {
 const getGoodsList = async () => {
   const productCard = props.templateData?.home?.components.find(item => item.id === 'ProductCard')
   const productCard = props.templateData?.home?.components.find(item => item.id === 'ProductCard')
+  if (!productCard) return
   const ids = productCard.property.spuIds
   const ids = productCard.property.spuIds
   if (!ids.length) return
   if (!ids.length) return
   const data = await getProductByIds(ids)
   const data = await getProductByIds(ids)

+ 18 - 4
src/views/mall/home/index.vue

@@ -1,7 +1,7 @@
 <template>
 <template>
   <div style="min-width: 1184px;" class="white-bgc">
   <div style="min-width: 1184px;" class="white-bgc">
     <!-- 导航栏 -->
     <!-- 导航栏 -->
-    <Navbar />
+    <Navbar @login="handleLogin" />
 
 
     <!-- 轮播图 -->
     <!-- 轮播图 -->
     <Carousel :templateData="template" class="mb-10" style="max-height: 594.5px;" />
     <Carousel :templateData="template" class="mb-10" style="max-height: 594.5px;" />
@@ -21,7 +21,7 @@
 
 
 <script setup>
 <script setup>
 defineOptions({ name: 'mall-home-index'})
 defineOptions({ name: 'mall-home-index'})
-import { ref } from 'vue'
+import { ref, onMounted } from 'vue'
 import Navbar from '../components/navbar.vue'
 import Navbar from '../components/navbar.vue'
 import Carousel from './components/carousel.vue'
 import Carousel from './components/carousel.vue'
 import HotGoods from './components/hotGoods.vue'
 import HotGoods from './components/hotGoods.vue'
@@ -29,9 +29,15 @@ import PointExchange from '../pointExchange'
 import loginPage from '@/views/common/loginDialog.vue'
 import loginPage from '@/views/common/loginDialog.vue'
 import { useMallStore } from '@/store/mall'
 import { useMallStore } from '@/store/mall'
 import { useUserStore } from '@/store/user'
 import { useUserStore } from '@/store/user'
+import Snackbar from '@/plugins/snackbar'
+import { useRouter } from 'vue-router'
+
+const router = useRouter()
 
 
 // 获取装修模版
 // 获取装修模版
-useMallStore().getMallDiyTemplate()
+onMounted(async () => {
+  await useMallStore().getMallDiyTemplate()
+})
 
 
 let template = ref(JSON.parse(localStorage.getItem('mallTemplate')) || {})
 let template = ref(JSON.parse(localStorage.getItem('mallTemplate')) || {})
 useMallStore().$subscribe((mutation, state) => {
 useMallStore().$subscribe((mutation, state) => {
@@ -46,14 +52,22 @@ userStore.$subscribe((mutation, state) => {
 })
 })
 
 
 const showLogin = ref(false)
 const showLogin = ref(false)
-const handleLogin = () => {
+const returnUrl = ref('')
+const handleLogin = (path) => {
   showLogin.value = true // 打开快速登录弹窗
   showLogin.value = true // 打开快速登录弹窗
+  returnUrl.value = path
 }
 }
 
 
 // 快速登录
 // 快速登录
 const loginSuccess = () => {
 const loginSuccess = () => {
   showLogin.value = false
   showLogin.value = false
   Snackbar.success('登录成功')
   Snackbar.success('登录成功')
+  if (returnUrl.value) {
+    router.push(returnUrl.value)
+    setTimeout(() => {
+      returnUrl.value = ''
+    }, 1000)
+  }
 }
 }
 
 
 const loginClose = () => {
 const loginClose = () => {

+ 1 - 1
src/views/mall/pointExchange/index.vue

@@ -173,7 +173,7 @@ const handleSubmit = async () =>{
 const handleTo = (path) => {
 const handleTo = (path) => {
   if (!getToken()) {
   if (!getToken()) {
     Snackbar.warning('请先登录')
     Snackbar.warning('请先登录')
-    emit('login')
+    emit('login', path)
     return
     return
   }
   }
   router.push(path)
   router.push(path)

+ 1 - 1
src/views/mall/user/index.vue

@@ -3,7 +3,7 @@
   <div class="d-flex parent default-width mb-5">
   <div class="d-flex parent default-width mb-5">
     <v-card class="left">
     <v-card class="left">
       <v-list color="primary">
       <v-list color="primary">
-        <div class="text-center mb-3">
+        <div class="text-center my-3">
           <v-avatar :image="getUserAvatar(baseInfo?.avatar, baseInfo?.sex)" size="100"></v-avatar>
           <v-avatar :image="getUserAvatar(baseInfo?.avatar, baseInfo?.sex)" size="100"></v-avatar>
           <p class="mt-2 color-primary font-weight-bold">{{ baseInfo?.name }}</p>
           <p class="mt-2 color-primary font-weight-bold">{{ baseInfo?.name }}</p>
         </div>
         </div>

+ 89 - 2
src/views/mall/user/order/index.vue

@@ -1,11 +1,98 @@
 <template>
 <template>
-  <div>order-index</div>
+  <div>
+    <v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#f7f8fa" @update:modelValue="getOrderPage">
+      <v-tab v-for="(val, i) in tabList" :key="i" :value="val.value">{{ val.title }}</v-tab>
+    </v-tabs>
+    <div v-if="orderList.length" class="mt-3">
+      <div v-for="val in orderList" :key="val.id" class="order-item mb-3">
+        <div class="order-item-header px-5">
+          <div style="width: 40%;">
+            <span>订单号:{{ val.no }}</span>
+            <span class="ml-5">{{ timesTampChange(val.createTime) }}</span>
+          </div>
+          <div class="text-end color-warning" style="flex: 1">
+            待发货
+          </div>
+        </div>
+        <div v-for="k in val.items" :key="k.id" class="order-item-goods px-3 pt-3">
+          <div class="d-flex">
+            <div style="width: 90px; height: 90px">
+              <v-img :src="k.picUrl"></v-img>
+            </div>
+            <div class="ml-5">
+              <p class="font-size-15">{{ k.spuName }}</p>
+              <p>
+                <span class="color-333">¥{{ fen2yuan(k.price) }}</span>
+                <span v-if="k.count" class="color-999 font-size-13 ml-1">x {{ k.count }}</span>
+              </p>
+            </div>
+          </div>
+          <v-divider class="mt-3"></v-divider>
+        </div>
+        <div class="text-end pa-3 font-size-13 color-666">共{{ val.productCount }}件商品,合计:¥{{ fen2yuan(val.payPrice) }}</div>
+      </div>
+    </div>
+    <Empty v-else :elevation="false" :message="tab === -1 ? '暂无订单' : '暂无' + tabList.find(e => e.value === tab).title + '订单'"></Empty>
+  </div>
 </template>
 </template>
 
 
 <script setup>
 <script setup>
 defineOptions({ name: 'mall-user-order-index'})
 defineOptions({ name: 'mall-user-order-index'})
+import { ref } from 'vue'
+import { getMallOrderPage } from '@/api/mall/user'
+import { getDict } from '@/hooks/web/useDictionaries'
+import { timesTampChange } from '@/utils/date'
+import { fen2yuan } from '@/hooks/web/useGoods'
+
+const tab = ref(-1)
+const tabList = [
+  { title: '全部', value: -1 },
+  { title: '待付款', value: 0 },
+  { title: '待发货', value: 10 },
+  { title: '待收货', value: 20 },
+  { title: '待评价', value: 30 }
+]
+const total = ref(0)
+const queryParams = ref({
+  pageNo: 1,
+  pageSize: 10
+})
+
+// 获取订单列表
+const orderList = ref([])
+const getOrderPage = async () => {
+  queryParams.value.status = tab.value
+  if (tab.value === -1) delete queryParams.value.status
+  if (tab.value === 30) queryParams.value.commentStatus = false
+  const result = await getMallOrderPage(queryParams.value)
+  orderList.value = result.list
+  total.value = result.total
+}
+getOrderPage()
+
+const getDictData = async (dictType, value) => {
+  const { data } = await getDict(dictType)
+  console.log(data, 'dict-data')
+  const obj = data.find(e => e.value === value)
+  return obj.label
+}
+// console.log(getDictLabel('trade_order_status', 10), 'getDictLabel')
+
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-
+.order-item {
+  border: 1px solid #dbdbdb;
+  &-header {
+    display: flex;
+    background-color: #f2f4f7;
+    height: 36px;
+    line-height: 36px;
+    font-size: 13px;
+    color: #666;
+  }
+  &-goods {
+    // border-bottom: 1px solid #dbdbdb;
+  }
+}
 </style>
 </style>