Browse Source

fix: 问题处理

7746 1 week ago
parent
commit
5b198d58c4

+ 1 - 0
package-lock.json

@@ -11,6 +11,7 @@
         "@ant-design/icons-vue": "^7.0.1",
         "@ant-design/icons-vue": "^7.0.1",
         "ant-design-vue": "^4.2.6",
         "ant-design-vue": "^4.2.6",
         "axios": "^1.13.2",
         "axios": "^1.13.2",
+        "dayjs": "^1.11.19",
         "js-perform-lock": "^1.0.5",
         "js-perform-lock": "^1.0.5",
         "lodash-es": "^4.17.21",
         "lodash-es": "^4.17.21",
         "pinia": "^3.0.4",
         "pinia": "^3.0.4",

+ 1 - 0
package.json

@@ -12,6 +12,7 @@
     "@ant-design/icons-vue": "^7.0.1",
     "@ant-design/icons-vue": "^7.0.1",
     "ant-design-vue": "^4.2.6",
     "ant-design-vue": "^4.2.6",
     "axios": "^1.13.2",
     "axios": "^1.13.2",
+    "dayjs": "^1.11.19",
     "js-perform-lock": "^1.0.5",
     "js-perform-lock": "^1.0.5",
     "lodash-es": "^4.17.21",
     "lodash-es": "^4.17.21",
     "pinia": "^3.0.4",
     "pinia": "^3.0.4",

+ 0 - 1
src/App.vue

@@ -42,7 +42,6 @@ onMounted(() => {
 
 
 <style lang="less">
 <style lang="less">
 @import './styles/variables.less';
 @import './styles/variables.less';
-@import './styles/common.less';
 
 
 #app {
 #app {
   font-size: 14px;
   font-size: 14px;

+ 15 - 0
src/api/common.js

@@ -28,3 +28,18 @@ export const transformLanguageText = params => request({
   method: 'post',
   method: 'post',
   params: params
   params: params
 })
 })
+
+// 获取配置信息
+export const getConfigData = params => request({
+  url: '/common/config/get',
+  method: 'get',
+  params
+})
+
+// 培训列表
+export const getTrainData = params => request({
+  url: '/common/training/list',
+  method: 'post',
+  params: params
+})
+

+ 17 - 1
src/api/goods.js

@@ -21,7 +21,7 @@ export const getFileList = goodsId => request({
   data: {}
   data: {}
 })
 })
 
 
-// 加入购物车
+// 加入购物车(商品详情添加到购物车)
 export const addToCard = params => request({
 export const addToCard = params => request({
   url: '/shpping/cart/add/one',
   url: '/shpping/cart/add/one',
   method: 'post',
   method: 'post',
@@ -29,6 +29,22 @@ export const addToCard = params => request({
   json: true
   json: true
 })
 })
 
 
+// 加入/移除购物车(购物车页面添加到购物车)
+export const changeCart = params => request({
+  url: '/shpping/cart/add',
+  method: 'post',
+  data: params,
+  json: true
+})
+
+// 移除购物车商品
+export const removeCart = params => request({
+  url: '/shpping/cart/remove',
+  method: 'post',
+  data: params,
+  json: true
+})
+
 // 添加收藏
 // 添加收藏
 export const addFavorite = params => request({
 export const addFavorite = params => request({
   url: '/goods/favorite/add',
   url: '/goods/favorite/add',

+ 51 - 0
src/api/order.js

@@ -0,0 +1,51 @@
+import request from '@/utils/request'
+//  确认订单内容(去结算)
+export const ackOrder = params => request({
+  url: '/order/ackdata',
+  method: 'post',
+  data: params,
+  json: true
+})
+
+// 提交订单
+export const orderBuy = params => request({
+  url: '/order/buy',
+  method: 'post',
+  data: params,
+  json: true
+})
+
+//  取消订单
+export const cancelOrder = params => request({
+  url: '/order/cancel',
+  method: 'post',
+  data: params
+})
+
+//  服务完成
+export const ackComplete = params => request({
+  url: '/order/ack',
+  method: 'post',
+  data: params
+})
+
+// 获取退货列表
+export const getRefundList = params => request({
+  url: '/order/refund/list',
+  method: 'post',
+  data: params
+})
+
+// 我的订单列表
+export const getMyOrderList = params => request({
+  url: '/order/my/order',
+  method: 'get',
+  params: params
+})
+
+// 提醒发货
+export const remindNotice = params => request({
+  url: '/order/notice',
+  method: 'post',
+  data: params
+})

+ 14 - 0
src/api/user.js

@@ -40,3 +40,17 @@ export const userLeaveMessage = params => request({
   method: 'get',
   method: 'get',
   params: params
   params: params
 })
 })
+
+// 获取地址列表
+export const getAddressList = params => request({
+  url: '/user/address/list',
+  method: 'get',
+  params: params
+})
+
+// 获取提货时间列表
+export const getPickTimeList = params => request({
+  url: '/common/order/pick/time/list',
+  method: 'post',
+  params: params
+})

+ 7 - 4
src/pages/category/components/GridItem.vue

@@ -1,6 +1,6 @@
 <template>
 <template>
   <div class="grid-item">
   <div class="grid-item">
-    <div class="image-box">
+    <div class="image-box" @click="viewDetail">
       <img :src="item.imgUrl" />
       <img :src="item.imgUrl" />
     </div>
     </div>
     <div class="grid-item__content">
     <div class="grid-item__content">
@@ -60,11 +60,14 @@ const toLogin = () => {
   border-radius: 2px;
   border-radius: 2px;
   border: 1px solid @border-color;
   border: 1px solid @border-color;
   .image-box {
   .image-box {
+    width: 100%;
+    aspect-ratio: 1/1;
+    cursor: pointer;
     img {
     img {
       display: block;
       display: block;
-      max-width: 100%;
-      aspect-ratio: 1/1;
-      object-fit: contain;
+      width: 100%;
+      height: 100%;
+      object-fit: cover;
     }
     }
   }
   }
  .grid-item__content {
  .grid-item__content {

+ 1 - 1
src/pages/category/components/Right.vue

@@ -187,6 +187,6 @@ const foramtTagData = record => {
   width: 100%;
   width: 100%;
   display: grid;
   display: grid;
   gap: 16px;
   gap: 16px;
-  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
+  grid-template-columns: repeat(auto-fill, minmax(220px, 260px));
 }
 }
 </style>
 </style>

+ 7 - 5
src/pages/category/index.vue

@@ -8,7 +8,7 @@
           :data="leftData"
           :data="leftData"
           :active-id="queryData.categoryId"
           :active-id="queryData.categoryId"
           @change-switch="changeSwitchFn"
           @change-switch="changeSwitchFn"
-          @click-category="changeChildCategoryFn"
+          @click-category="changeCategoryFn"
         />
         />
       </div>
       </div>
       <div class="right">
       <div class="right">
@@ -34,11 +34,12 @@
 <script setup lang="js">
 <script setup lang="js">
 import Left from './components/Left.vue';
 import Left from './components/Left.vue';
 import Right from './components/Right.vue';
 import Right from './components/Right.vue';
-import { reactive, computed, watch } from 'vue';
+import { reactive, computed } from 'vue';
 import { useRouter } from 'vue-router';
 import { useRouter } from 'vue-router';
 import { useHomeStore } from '@/store/home';
 import { useHomeStore } from '@/store/home';
 import { useGoodsStore } from '@/store/goods';
 import { useGoodsStore } from '@/store/goods';
 import { useCategoryStore } from '@/store/category';
 import { useCategoryStore } from '@/store/category';
+import { useStorageStore } from '@/store/storage';
 import { useUserStore } from '@/store/user';
 import { useUserStore } from '@/store/user';
 import { getDetail } from '@/api/goods';
 import { getDetail } from '@/api/goods';
 
 
@@ -47,6 +48,7 @@ const homeStore = useHomeStore();
 const goodsStore = useGoodsStore();
 const goodsStore = useGoodsStore();
 const categoryStore = useCategoryStore();
 const categoryStore = useCategoryStore();
 const userStore = useUserStore();
 const userStore = useUserStore();
+const storageStore = useStorageStore();
 
 
 const mainData = reactive({
 const mainData = reactive({
   isList: window.localStorage.getItem('LIST_VIEW') == '1',
   isList: window.localStorage.getItem('LIST_VIEW') == '1',
@@ -54,14 +56,14 @@ const mainData = reactive({
 })
 })
 
 
 const tabIndex = computed(() => homeStore.tabIndex);
 const tabIndex = computed(() => homeStore.tabIndex);
-const storageId = computed(() => homeStore.storageId);
 const categoryList = computed(() => categoryStore.list);
 const categoryList = computed(() => categoryStore.list);
 const activedName = computed(() => categoryList.value[tabIndex.value]?.name || '');
 const activedName = computed(() => categoryList.value[tabIndex.value]?.name || '');
 const leftData = computed(() => categoryStore.childList);
 const leftData = computed(() => categoryStore.childList);
 const rightData = computed(() => goodsStore.list);
 const rightData = computed(() => goodsStore.list);
 const queryData = computed(() => goodsStore.params);
 const queryData = computed(() => goodsStore.params);
+const storageId = computed(() => storageStore.activedId);
 
 
-const changeChildCategoryFn = item => {
+const changeCategoryFn = item => {
   if (item.categoryId == queryData.value.categoryId) return;
   if (item.categoryId == queryData.value.categoryId) return;
   goodsStore.updateParams({
   goodsStore.updateParams({
     pageNum: 1,
     pageNum: 1,
@@ -94,8 +96,8 @@ const addToCartFn = (row) => {
   mainData.disabled = true
   mainData.disabled = true
   getDetail({
   getDetail({
     goodsId: row.goodsId,
     goodsId: row.goodsId,
+    storageId: storageId.value,
     userId: userStore.userInfo?.userId || '',
     userId: userStore.userInfo?.userId || '',
-    storageId: homeStore.storageId
   }).then(res => {
   }).then(res => {
     const buyGoods = [
     const buyGoods = [
       {
       {

+ 14 - 13
src/pages/goods/index.vue

@@ -35,7 +35,6 @@
               :wrapper-col="{ span: 24 }"
               :wrapper-col="{ span: 24 }"
               autocomplete="off"
               autocomplete="off"
               @finish="onFinish"
               @finish="onFinish"
-              @finishFailed="onFailed"
             >
             >
               <a-form-item
               <a-form-item
                 label="数量"
                 label="数量"
@@ -121,20 +120,21 @@ import { reactive, computed, onMounted, watch } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
 import { useRoute, useRouter } from 'vue-router';
 import { getDetail, getFileList } from '@/api/goods';
 import { getDetail, getFileList } from '@/api/goods';
 import { useUserStore } from '@/store/user';
 import { useUserStore } from '@/store/user';
-import { useHomeStore } from '@/store/home';
 import { useGoodsStore } from '@/store/goods';
 import { useGoodsStore } from '@/store/goods';
-import { goodsDetailData, fileList } from '@/utils/mock';
+import { useStorageStore } from '@/store/storage';
 
 
 const route = useRoute();
 const route = useRoute();
 const router = useRouter();
 const router = useRouter();
 const userStore = useUserStore();
 const userStore = useUserStore();
-const homeStore = useHomeStore();
 const goodsStore = useGoodsStore();
 const goodsStore = useGoodsStore();
+const storageStore = useStorageStore();
 
 
 const mainForm = reactive({
 const mainForm = reactive({
   quantity: 1,
   quantity: 1,
   tabIndex: 0,
   tabIndex: 0,
-  detail: {},
+  detail: {
+    goodsDocumentsRelaList: []
+  },
   searchFileName: '',
   searchFileName: '',
   disabled: false
   disabled: false
 })
 })
@@ -142,6 +142,9 @@ const mainForm = reactive({
 const isLogin = computed(() => {
 const isLogin = computed(() => {
   return userStore.userInfo != null
   return userStore.userInfo != null
 });
 });
+
+const storageId = computed(() => storageStore.activedId);
+
 const goodsDocumentsRelaList = computed(() => {
 const goodsDocumentsRelaList = computed(() => {
   if (!mainForm.detail.goodsDocumentsRelaList || mainForm.detail.goodsDocumentsRelaList.length === 0) return [];
   if (!mainForm.detail.goodsDocumentsRelaList || mainForm.detail.goodsDocumentsRelaList.length === 0) return [];
   const validList = mainForm.detail.goodsDocumentsRelaList.filter(item => item.goodsDocumentsId.indexOf(mainForm.searchFileName) > -1 || item.fileName.indexOf(mainForm.searchFileName) > -1);
   const validList = mainForm.detail.goodsDocumentsRelaList.filter(item => item.goodsDocumentsId.indexOf(mainForm.searchFileName) > -1 || item.fileName.indexOf(mainForm.searchFileName) > -1);
@@ -159,32 +162,30 @@ const goodsDocumentsRelaList = computed(() => {
   }, []);
   }, []);
 });
 });
 const tabs = computed(() => {
 const tabs = computed(() => {
-  if (!mainForm.detail.goodsDocumentsRelaList || mainForm.detail.goodsDocumentsRelaList.length === 0) return ['商品详情'];
+  if (!mainForm.detail?.goodsDocumentsRelaList || mainForm.detail?.goodsDocumentsRelaList?.length === 0) return ['商品详情'];
   return ['商品详情', '文档'];
   return ['商品详情', '文档'];
 });
 });
 
 
 
 
 const onFinish = () => {}
 const onFinish = () => {}
 
 
-const onFailed = () => {}
-
 const fetchDetail = async () => {
 const fetchDetail = async () => {
   const params = {
   const params = {
     goodsId: route.query.id,
     goodsId: route.query.id,
-    storageId: homeStore.storageId
+    storageId: storageId.value
   }
   }
   if (userStore.userInfo?.userId) {
   if (userStore.userInfo?.userId) {
     params.userId = userStore.userInfo?.userId
     params.userId = userStore.userInfo?.userId
   }
   }
   const res = await getDetail(params);
   const res = await getDetail(params);
-  mainForm.detail = res.data || {};
-  // mainForm.detail = goodsDetailData;
+  Object.keys(res.data || {}).forEach(key => {
+    mainForm.detail[key] = res.data[key]
+  })
 }
 }
 
 
 const fetchFileData = async () => {
 const fetchFileData = async () => {
   const res = await getFileList(route.query.id);
   const res = await getFileList(route.query.id);
   mainForm.detail.goodsDocumentsRelaList = res.data || [];
   mainForm.detail.goodsDocumentsRelaList = res.data || [];
-  // mainForm.detail.goodsDocumentsRelaList = fileList;
 }
 }
 
 
 const handleAddToCart = () => {
 const handleAddToCart = () => {
@@ -201,7 +202,7 @@ const handleAddToCart = () => {
   ]
   ]
   const params = {
   const params = {
     userId: userStore.userInfo?.userId || '',
     userId: userStore.userInfo?.userId || '',
-    storageId: homeStore.storageId,
+    storageId: storageId.value,
     buyGoods: buyGoods
     buyGoods: buyGoods
   }
   }
   goodsStore.addToCard(params).then(() => {
   goodsStore.addToCard(params).then(() => {

+ 411 - 0
src/pages/home/components/CartList.vue

@@ -0,0 +1,411 @@
+<template>
+  <div class="cart-list__box">
+    <div v-if="mainData.goodsList && mainData.goodsList.length > 0" class="main-list__content">
+      <div class="handle-area__header">
+        <a-flex align="center" justify="space-between">
+          <div>共{{ mainData.allNum }}件商品</div>
+          <div>
+            <a-button v-if="!mainData.isEdit" type="text" @click="mainData.isEdit = true">编辑</a-button>
+            <a-button v-else type="text" @click="mainData.isEdit = false">完成</a-button>
+          </div>
+        </a-flex>
+      </div>
+      <div class="list-box">
+        <div v-for="(item, index) in mainData.goodsList" :key="index" class="list-item">
+          <div v-if="item.stockQty > 0" class="check-box" @click="toggleCheck(index)">
+            <CheckCircleOutlined class="check-item" :class="item.selected ? 'actived' : ''" />
+          </div>
+          <div class="goods-image">
+            <img :src="item.goodsImg" />
+          </div>
+          <div class="goods-content">
+            <div class="goods-name">{{ item.goodsName }}</div>
+            <div class="goods-desc">{{ item.specValue }}</div>
+            <a-flex align="center" justify="space-between">
+              <div class="goods-price">¥ {{ formatPriceText(item.price) }}</div>
+              <div>
+                <a-input-number
+                  :value="item.num"
+                  style="width: 140px;"
+                  :min="1"
+                  :step="1"
+                  disabled
+                >
+                  <template #addonBefore>
+                    <MinusOutlined style="cursor: pointer;" @click="delCount(index)" />
+                  </template>
+                  <template #addonAfter>
+                    <PlusOutlined style="cursor: pointer;" @click="addCount(index)" />
+                  </template>
+                </a-input-number>
+              </div>
+            </a-flex>
+          </div>
+        </div>
+      </div>
+      <div class="handle-area__footer">
+        <a-flex align="center" justify="space-between">
+          <a-flex align="center">
+            <div class="check-box" @click="toggleCheckAll">
+              <CheckCircleOutlined class="check-item" :class="mainData.selectAllStatus ? 'actived' : ''" />
+              <span style="margin-left: 6px;">全选</span>
+            </div>
+          </a-flex>
+          <a-flex align="center">
+            <a-flex align="center" v-if="!mainData.isEdit">
+              <div class="total-price-desc">已选{{ mainData.totalNum }}件,</div>
+              <div class="total-price-label">合计: </div>
+              <div class="total-price-value">¥ {{ formatPriceText((mainData.totalPrice * 1000 - mainData.fullPieceDiscount * 1000) / 1000) }}</div>
+              <a-button type="primary" @click="handleNext">去结算</a-button>
+            </a-flex>
+            <a-button v-else type="primary" danger @click="handleDelete">删除</a-button>
+          </a-flex>
+        </a-flex>
+      </div>
+    </div>
+    <a-empty v-else />
+  </div>
+</template>
+
+<script setup lang="js">
+import { onMounted, reactive } from 'vue'
+import { Modal, message } from 'ant-design-vue'
+import { MinusOutlined, PlusOutlined, CheckCircleOutlined } from '@ant-design/icons-vue';
+import { getCartList, changeCart, removeCart } from '@/api/goods';
+
+const props = defineProps({
+  userId: {
+    type: String,
+    default: ''
+  },
+  storageId: {
+    type: String,
+    default: ''
+  }
+})
+
+const emits = defineEmits(['next-step'])
+
+const mainData = reactive({
+  goodsList: [],
+  fullPieceList: [],
+  totalNum: 0,
+  totalPrice: 0,
+  fullPieceItem: 0,
+  fullPiecePrice: 0,
+  fullPieceDiscount: 0,
+  allNum: 0,
+  selectAllStatus: false,
+  disabled: false,
+  isEdit: false
+})
+
+const fetchCartList = async () => {
+  const res = await getCartList({
+    userId: props.userId,
+    storageId: props.storageId
+  })
+  mainData.goodsList = res.data?.shoppingCartLists || [];
+  mainData.goodsList.sort((a, b) => {
+    if (a.stockQty <= 0 && b.stockQty > 0) {
+      return 1
+    } else if (a.stockQty > 0 && b.stockQty <= 0) {
+      return -1
+    }
+    return 0
+  })
+  mainData.fullPieceList = res.data?.promotionFullPieceItems || [];
+  mainData.goodsList = mainData.goodsList.map(item => {
+    const selected = item.stockQty > 0;
+    const num = item.stockQty > 0 ? item.num : 0;
+    return {
+      ...item,
+      selected,
+      num
+    }
+  })
+  mainData.selectAllStatus = mainData.goodsList.length > 0 && mainData.goodsList.every(item => item.stockQty > 0);
+  mainData.allNum = mainData.goodsList.filter(item => item.stockQty > 0).length;
+  calcTotalPrice();
+}
+
+// 计算总价
+const calcTotalPrice = () => {
+  const goodsList = mainData.goodsList
+  let total_num = 0
+  let total_price = 0
+  let fullPiece_item = null // 满件打折活动
+  let fullPiece_num = 0 // 满件打折活动:数量
+  let fullPiece_price = 0 // 满件打折活动:总金额
+  let fullPiece_discount = 0 // 满件打折活动:优惠金额
+  // 统计总金额
+  for (let i = 0; i < goodsList.length; i++) {
+    // 判断选中才会计算价格
+    if (goodsList[i].selected) {
+      // 所有价格加起来
+      total_price += goodsList[i].num * goodsList[i].price
+      total_num += Number(goodsList[i].num)
+
+      // 如果满件打折
+      if (mainData.fullPieceList.length > 0 && goodsList[i].promotionFullPiece) {
+        fullPiece_num += goodsList[i].num
+        fullPiece_price += goodsList[i].num * goodsList[i].price
+      }
+    }
+  }
+
+  if (mainData.fullPieceList.length > 0 && fullPiece_num > 0) {
+    // 找到对应的满件打折活动
+    let nums = mainData.fullPieceList.map(item => {
+      return item.satisfyNum
+    })
+    let thisNum = mainData.findNearestNumber(nums, fullPiece_num)
+    fullPiece_item =
+      mainData.fullPieceList.find(item => {
+        return item.satisfyNum === thisNum
+      }) || null
+    // 计算优惠金额
+    if (fullPiece_item) {
+      fullPiece_discount = fullPiece_price - fullPiece_price * fullPiece_item.discountRate
+    }
+  }
+
+  // 赋值到data中渲染到页面
+  mainData.goodsList = goodsList
+  mainData.totalNum = total_num
+  mainData.totalPrice = total_price.toFixed(2)
+  mainData.fullPieceItem = fullPiece_item
+  mainData.fullPieceNum = fullPiece_num
+  mainData.fullPiecePrice = fullPiece_price.toFixed(2)
+  mainData.fullPieceDiscount = fullPiece_discount.toFixed(2)
+}
+
+const formatPriceText = price => {
+  if (!price) return '0.00'
+  price = Number(price)
+  return price.toFixed(2)
+}
+
+const toggleCheck = index => {
+  mainData.goodsList[index].selected = !mainData.goodsList[index].selected;
+  for (var i = 0; i < mainData.goodsList.length; i++) {
+    // 当状态为全选时,每个元素其中有一个为false,则取消全选
+    // 当状态为非全选时,每个元素都为true,则全选
+    if (mainData.selectAllStatus) {
+      if (!mainData.goodsList[i].selected) {
+        mainData.selectAllStatus = false
+        break
+      }
+    } else {
+      if (mainData.goodsList[i].selected) {
+        mainData.selectAllStatus = true
+      } else {
+        mainData.selectAllStatus = false
+        break
+      }
+    }
+  }
+  calcTotalPrice();
+}
+
+const toggleCheckAll = () => {
+  mainData.selectAllStatus = !mainData.selectAllStatus
+  for (let i = 0; i < mainData.goodsList.length; i++) {
+    mainData.goodsList[i].selected = mainData.selectAllStatus
+  }
+  calcTotalPrice()
+}
+
+const delCount = index => {
+  if (mainData.goodsList[index].num <= 1) return
+  if (mainData.disabled) return
+  mainData.disabled = true
+  mainData.goodsList[index].num -= 1
+  changeCartApiFn()
+}
+
+const addCount = index => {
+  if (mainData.disabled) return
+  mainData.disabled = true
+  mainData.goodsList[index].num += 1
+  changeCartApiFn()
+}
+
+const changeCartApiFn = () => {
+  const buyGoods = mainData.goodsList.map(item => {
+    return {
+      storageId: props.storageId,
+      goodsId: item.goodsId,
+      goodsSpecId: item.goodsSpecId,
+      num: item.num,
+      shoppingCartId: item.shoppingCartId,
+      secKillId: item.secKillId || '',
+      promotionGroupId: item.promotionGroupId || ''
+    }
+  })
+  changeCart({
+    userId: props.userId,
+    buyGoods: buyGoods
+  }).then((res) => {
+    mainData.allNum = res.data.totalNum
+    getTotalPrice()
+  }).finally(() => {
+    mainData.disabled = false
+  })
+}
+
+const handleDelete = () => {
+  const delIds = mainData.goodsList.filter(item => item.selected).map(child => child.shoppingCartId)
+  if (delIds.length < 1) {
+    message.info('至少选择一件商品');
+  } else {
+    Modal.confirm({
+      title: '删除',
+      content: '确定要删除选中的商品吗?',
+      onOk() {
+        removeCart({
+          shoppingCartIds: delIds,
+          userId: props.userId,
+          storageId: props.storageId
+        }).then(() => {
+          message.success('删除成功')
+          fetchCartList()
+        })
+      }
+    })
+  }
+}
+
+const handleNext = () => {
+  const selectedList = mainData.goodsList.filter(item => item.selected).map(child => {
+    return {
+      goodsId: child.goodsId,
+      goodsSpecId: child.goodsSpecId,
+      num: child.num,
+      shoppingCartId: child.shoppingCartId,
+      secKillId: child.secKillId || '',
+      promotionGroupId: child.promotionGroupId || ''
+    }
+  })
+  if (selectedList.length < 1) {
+    message.info('至少选择一件商品');
+  } else {
+    emits('next-step',selectedList)
+  }
+}
+
+onMounted(() => {
+  fetchCartList()
+})
+
+</script>
+
+<style lang="less" scoped>
+@price-color: #ff577e;
+
+.cart-list__box {
+  height: 100%;
+  .main-list__content {
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    .handle-area__header {
+      padding: 10px;
+      border-bottom: 1px solid @border-color;
+    }
+    .list-box {
+      flex: 1;
+      min-height: 1px;
+      overflow-y: auto;
+      .list-item {
+        width: 100%;
+        display: flex;
+        align-items: center;
+        padding: 10px 0;
+        border-bottom: 1px solid @border-color;
+        .check-box {
+          cursor: pointer;
+          padding-right: 10px;
+          .check-item {
+            color: #969696;
+            font-size: 24px;
+            &.actived {
+              color: @theme-color;
+            }
+          }
+        }
+        .goods-image {
+          flex-shrink: 0;
+          width: 80px;
+          height: 80px;
+          margin-right: 8px;
+          img {
+            display: block;
+            width: 100%;
+            height: 100%;
+          }
+        }
+        .goods-content {
+          flex: 1;
+          min-width: 1px;
+          word-break: break-all;
+          .goods-name {
+            font-size: 14px;
+            line-height: 1.5;
+            margin-bottom: 5px;
+            color: @theme-color;
+          }
+          .goods-desc {
+            color: #666;
+            font-size: 13px;
+            margin-bottom: 10px;
+            white-space: nowrap;
+            overflow: hidden;
+            text-overflow: ellipsis;
+          }
+          .goods-price {
+            color: @price-color;
+            font-size: 14px;
+          }
+        }
+      }
+    }
+    .handle-area__footer {
+      padding: 15px;
+      border-top: 1px solid @border-color;
+      .check-box {
+        display: flex;
+        align-items: center;
+        cursor: pointer;
+        padding-right: 10px;
+        .check-item {
+          color: #969696;
+          font-size: 24px;
+          &.actived {
+            color: @theme-color;
+          }
+        }
+      }
+    }
+  } 
+}
+
+.total-price-desc {
+  color: #969696;
+  font-size: 14px;
+  margin-right: 10px;
+}
+
+.total-price-label {
+  color: #222;
+  font-size: 14px;
+}
+
+.total-price-value {
+  color: @price-color;
+  font-size: 18px;
+  font-weight: 500;
+  margin-left: 5px;
+  margin-right: 10px;
+}
+</style>

+ 55 - 61
src/pages/home/components/CartModal.vue

@@ -2,83 +2,77 @@
   <a-drawer
   <a-drawer
     :open="open"
     :open="open"
     title="购物车列表"
     title="购物车列表"
-    width="520px"
+    width="620px"
     placement="right"
     placement="right"
     @close="emits('close')"
     @close="emits('close')"
   >
   >
-    <div class="drawer-box">
-      <div v-if="cartData.shoppingCartLists && cartData.shoppingCartLists.length > 0">
-        <div v-for="(item, index) in cartData.shoppingCartLists" :key="index" class="list-item">
-          <div class="goods-image">
-            <img :src="item.goodsImg" />
-          </div>
-          <div class="goods-content">
-            <div class="goods-name">{{ item.goodsName }}</div>
-            <a-flex align="center" justify="space-between">
-              <div class="goods-price">¥ {{ item.price }}</div>
-              <div class="goods-price">X {{ item.num }}</div>
-            </a-flex>
-          </div>
-        </div>
-      </div>
-      <a-empty v-else />
+    <div class="drawer-box" style="height: 100%;">
+      <CartList
+        v-if="mainData.stepNum == 1"
+        key="1"
+        :user-id="userId"
+        :storage-id="storageId"
+        @next-step="handleNext"
+      />
+      <SubmitCart
+        v-if="mainData.stepNum == 2"
+        key="2"
+        :user-id="userId"
+        :storage-id="storageId"
+        :buy-goods="mainData.buyGoods"
+        @prev-step="handlePrev"
+        @finish="emits('close')"
+        
+      />
     </div>
     </div>
   </a-drawer>
   </a-drawer>
 </template>
 </template>
 
 
 <script setup lang="js">
 <script setup lang="js">
-defineProps({
+import CartList from './CartList.vue';
+import SubmitCart from './SubmitCart.vue';
+import { reactive, nextTick, watch } from 'vue'
+
+const props = defineProps({
   open: {
   open: {
     type: Boolean,
     type: Boolean,
     default: false
     default: false
   },
   },
-  cartData: {
-    type: Object,
-    default: () => ({
-      shoppingCartLists: [],
-      totalAmount: 0,
-      totalNum: 0
-    })
+  userId: {
+    type: String,
+    default: ''
+  },
+  storageId: {
+    type: String,
+    default: ''
   }
   }
 })
 })
 
 
 const emits = defineEmits(['close']);
 const emits = defineEmits(['close']);
 
 
-</script>
+const mainData = reactive({
+  stepNum: 1,
+  buyGoods: []
+})
 
 
-<style lang="less" scoped>
-.drawer-box {
-  .list-item {
-    width: 100%;
-    display: flex;
-    align-items: center;
-    padding: 10px 0;
-    border-bottom: 1px solid @border-color;
-    .goods-image {
-      flex-shrink: 0;
-      width: 60px;
-      height: 60px;
-      margin-right: 8px;
-      img {
-        display: block;
-        width: 100%;
-        height: 100%;
-      }
-    }
-    .goods-content {
-      flex: 1;
-      min-width: 1px;
-      word-break: break-all;
-      .goods-name {
-        font-size: 14px;
-        line-height: 1.5;
-        margin-bottom: 10px;
-        color: @theme-color;
-      }
-      .goods-price {
-        font-size: 13px;
-      }
-    }
-  }
+const handleNext = (selectedList = []) => {
+  mainData.stepNum += 1;
+  mainData.buyGoods = selectedList;
+}
+
+const handlePrev = () => {
+  mainData.buyGoods = [];
+  mainData.stepNum -= 1;
 }
 }
-</style>
+
+watch(() => props.open, newVal => {
+  if (newVal) {
+    mainData.buyGoods = [];
+    mainData.stepNum = -1;
+    nextTick(() => {
+      mainData.stepNum = 1;
+    })
+  }
+})
+
+</script>

+ 2 - 1
src/pages/home/components/Category.vue

@@ -3,7 +3,7 @@
     <a-tabs
     <a-tabs
       :activeKey="tabIndex"
       :activeKey="tabIndex"
       :tabBarStyle="{color: '#fff', margin: 0}"
       :tabBarStyle="{color: '#fff', margin: 0}"
-      @change="changeTab"
+      @tabClick="changeTab"
     >
     >
       <a-tab-pane
       <a-tab-pane
         v-for="(item, index) in tabs"
         v-for="(item, index) in tabs"
@@ -29,6 +29,7 @@ const props = defineProps({
 const emits = defineEmits(['change'])
 const emits = defineEmits(['change'])
 
 
 const changeTab = (index) => {
 const changeTab = (index) => {
+  console.log(index)
   emits('change', index)
   emits('change', index)
 }
 }
 
 

+ 27 - 17
src/pages/home/components/HandleBar.vue

@@ -12,7 +12,7 @@
           :not-found-content="mainData.fetching ? undefined : null"
           :not-found-content="mainData.fetching ? undefined : null"
           :options="mainData.goods"
           :options="mainData.goods"
           @search="fetchGoodsData"
           @search="fetchGoodsData"
-          @select="selectGoodsFn"
+          @select="viewGoodsDetail"
         >
         >
           <template v-if="mainData.fetching" #notFoundContent>
           <template v-if="mainData.fetching" #notFoundContent>
             <a-spin size="small" />
             <a-spin size="small" />
@@ -24,7 +24,7 @@
           <a-dropdown placement="bottom">
           <a-dropdown placement="bottom">
             <div class="handle-content__item">
             <div class="handle-content__item">
               <EnvironmentOutlined />
               <EnvironmentOutlined />
-              <span class="text">仓库切换</span>
+              <span class="text">{{ storageName }}</span>
             </div>
             </div>
             <template #overlay>
             <template #overlay>
               <a-menu>
               <a-menu>
@@ -32,7 +32,7 @@
                   v-for="(item, index) in storeList"
                   v-for="(item, index) in storeList"
                   :key="index"
                   :key="index"
                 >
                 >
-                  <div :class="item.storageId === storageId ? 'actived-store' : ''" @click="selectStore(item)">
+                  <div :class="item.storageId === storageId ? 'actived-store' : ''" @click="changeStorage(item)">
                     <a-flex align="center" justify="space-between">
                     <a-flex align="center" justify="space-between">
                       <div>{{ item.storageName }}</div>
                       <div>{{ item.storageName }}</div>
                       <CheckOutlined v-if="item.storageId === storageId" :style="{marginLeft: '10px'}" />
                       <CheckOutlined v-if="item.storageId === storageId" :style="{marginLeft: '10px'}" />
@@ -59,8 +59,14 @@
       </div>
       </div>
     </a-flex>
     </a-flex>
     <AccountModal :open="mainData.open" @close="mainData.open = false" />
     <AccountModal :open="mainData.open" @close="mainData.open = false" />
-    <LikeModal :open="mainData.likeOpen" :data="mainData.likeList" @delete="deleteLikeFn" @close="mainData.likeOpen = false" />
-    <CartModal :open="mainData.cartOpen" :cartData="mainData.cartData" @close="mainData.cartOpen = false"  />
+    <LikeModal :open="mainData.likeOpen" :data="mainData.likeList" @delete="deleteLikeFn" @detail="viewLinkFn" @close="mainData.likeOpen = false" />
+    <CartModal
+      :open="mainData.cartOpen"
+      :cartData="mainData.cartData"
+      :userId="userId"
+      :storageId="storageId"
+      @close="mainData.cartOpen = false"
+    />
     <UserModal :open="mainData.userOpen" @close="mainData.userOpen = false" />
     <UserModal :open="mainData.userOpen" @close="mainData.userOpen = false" />
   </div>
   </div>
 </template>
 </template>
@@ -82,7 +88,6 @@ import { useRouter } from 'vue-router';
 import { debounce } from 'lodash';
 import { debounce } from 'lodash';
 import { useGoodsStore } from '@/store/goods';
 import { useGoodsStore } from '@/store/goods';
 import { useUserStore } from '@/store/user';
 import { useUserStore } from '@/store/user';
-import { getCartList } from '@/api/goods'
 
 
 const props = defineProps({
 const props = defineProps({
   // 仓库列表
   // 仓库列表
@@ -104,8 +109,8 @@ const props = defineProps({
 const emits = defineEmits(['change-store']);
 const emits = defineEmits(['change-store']);
 
 
 const router = useRouter();
 const router = useRouter();
-const goodsStore = useGoodsStore();
 const userStore = useUserStore();
 const userStore = useUserStore();
+const goodsStore = useGoodsStore();
 
 
 const mainData = reactive({
 const mainData = reactive({
   goods: [],
   goods: [],
@@ -119,8 +124,14 @@ const mainData = reactive({
   userOpen: false
   userOpen: false
 })
 })
 
 
+const userId = computed(() => userStore.userInfo?.userId || '')
 const shoppingCartNums = computed(() => userStore.userDetail?.shoppingCartNums || 0)
 const shoppingCartNums = computed(() => userStore.userDetail?.shoppingCartNums || 0)
 
 
+// 当前选中的仓库的名称
+const storageName = computed(() => {
+  return props.storeList.find(item => item.storageId === props.storageId)?.storageName || '切换仓库'
+})
+
 const fetchGoodsData = debounce(async (value) => {
 const fetchGoodsData = debounce(async (value) => {
   if (value == '') return;
   if (value == '') return;
   mainData.fetching = true;
   mainData.fetching = true;
@@ -135,7 +146,7 @@ const fetchGoodsData = debounce(async (value) => {
   mainData.goods = goodsData;
   mainData.goods = goodsData;
 }, 500)
 }, 500)
 
 
-const selectGoodsFn = goodsId => {
+const viewGoodsDetail = goodsId => {
   router.push({
   router.push({
     path: '/goods',
     path: '/goods',
     query: {
     query: {
@@ -145,7 +156,7 @@ const selectGoodsFn = goodsId => {
 }
 }
 
 
 // 选择仓库
 // 选择仓库
-const selectStore = row => {
+const changeStorage = row => {
   if (props.storageId === row.storageId) return;
   if (props.storageId === row.storageId) return;
   emits('change-store', row);
   emits('change-store', row);
 }
 }
@@ -161,7 +172,7 @@ const handleAccount = () => {
 const handleLikeList = () => {
 const handleLikeList = () => {
   if (props.isLogin) {
   if (props.isLogin) {
     goodsStore.getFavoriteList({
     goodsStore.getFavoriteList({
-      userId: userStore.userInfo?.userId || '',
+      userId: userId.value,
       pageNum: 1,
       pageNum: 1,
       pageSize: 20
       pageSize: 20
     }).then(res => {
     }).then(res => {
@@ -181,15 +192,14 @@ const deleteLikeFn = row => {
   })
   })
 }
 }
 
 
+const viewLinkFn = row => {
+  mainData.likeOpen = false
+  viewGoodsDetail(row.goodsId)
+}
+
 const handleCardList = () => {
 const handleCardList = () => {
   if (props.isLogin) {
   if (props.isLogin) {
-    getCartList({
-      userId: userStore.userInfo?.userId || '',
-      storageId: props.storageId
-    }).then(res => {
-      mainData.cartOpen = true
-      mainData.cartData = res.data || {}
-    })
+    mainData.cartOpen = true
   } else {
   } else {
     mainData.open = true;
     mainData.open = true;
   }
   }

+ 11 - 5
src/pages/home/components/LikeModal.vue

@@ -9,7 +9,7 @@
     <div class="drawer-box">
     <div class="drawer-box">
       <div v-if="data.length > 0">
       <div v-if="data.length > 0">
         <div v-for="(item, index) in data" :key="index" class="list-item">
         <div v-for="(item, index) in data" :key="index" class="list-item">
-          <div class="goods-name">{{ item.goodsName }}</div>
+          <div class="goods-name" @click="viewDetail(item)">{{ item.goodsName }}</div>
           <div class="handle-area">
           <div class="handle-area">
             <a-popconfirm
             <a-popconfirm
               title="是否确定删除 ?"
               title="是否确定删除 ?"
@@ -28,6 +28,10 @@
 </template>
 </template>
 
 
 <script setup lang="js">
 <script setup lang="js">
+import { useRouter } from 'vue-router'
+
+const router = useRouter()
+
 defineProps({
 defineProps({
   open: {
   open: {
     type: Boolean,
     type: Boolean,
@@ -39,11 +43,11 @@ defineProps({
   }
   }
 })
 })
 
 
-const emits = defineEmits(['close', 'delete']);
+const emits = defineEmits(['close', 'delete', 'detail']);
 
 
-const handleDelete = row => {
-  emits('delete', row)
-}
+const handleDelete = row => emits('delete', row)
+
+const viewDetail = row => emits('detail', row)
 
 
 </script>
 </script>
 
 
@@ -60,6 +64,8 @@ const handleDelete = row => {
       min-width: 1px;
       min-width: 1px;
       margin-right: 10px;
       margin-right: 10px;
       color: @theme-color;
       color: @theme-color;
+      cursor: pointer;
+      line-height: 1.5;
       word-break: break-all;
       word-break: break-all;
     }
     }
     .handle-area {
     .handle-area {

+ 518 - 0
src/pages/home/components/OrderModal.vue

@@ -0,0 +1,518 @@
+<template>
+  <a-drawer :open="open" title="我的订单" width="800px" placement="right" @close="emits('close')">
+    <div class="drawer-box">
+      <div class="tabs-content">
+        <a-tabs v-model:activeKey="mainData.tabIndex" @change="changeTab">
+          <a-tab-pane v-for="(item, index) in mainData.tabs" :key="index" :tab="item.name" />
+        </a-tabs>
+      </div>
+      <div v-if="mainData.list.length > 0" class="list-container">
+        <div v-for="(item, index) in mainData.list" :key="index">
+          <div class="item" @click="toOrderDetail(item.orderId, item.orderRefundId)">
+            <!-- 普通订单 -->
+            <div v-if="tabCurrent != 'REFUND'">
+              <div class="top">
+                <div class="left">{{ item.orderId }}</div>
+                <div class="right">{{ statusFilter(item) }}</div>
+              </div>
+
+              <div v-for="(goodsItem, goodsIndex) in item.orderDetails" :key="goodsIndex">
+                <div class="goods" :class="'goods' + goodsIndex">
+                  <img :src="goodsItem.imgUrl" />
+                  <div class="main">
+                    <div class="left">
+                      <div class="name ellipsis-2">{{ goodsItem.goodsName }}</div>
+                      <div class="des">{{ goodsItem.goodsSpecValue }}</div>
+                    </div>
+                    <div class="right">
+                      <div class="price">¥{{ formatPriceText(goodsItem.price) }}</div>
+                      <div class="num">x{{ goodsItem.num }}</div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+
+              <div class="total">
+                <div class="left">{{ item.createTime }}</div>
+                <div class="right">共{{ item.totalNum }}件 订单总额:<span>¥{{ formatPriceText(item.payAmount) }}</span></div>
+              </div>
+            </div>
+
+            <!-- 售后订单 -->
+            <div v-if="tabCurrent == 'REFUND'">
+              <div class="top">
+                <div class="left">{{ item.orderId }}</div>
+                <div class="right">{{ statusFilter2(item.orderStatus) }}</div>
+              </div>
+
+              <!-- 拒绝申请 -->
+              <div v-if="item.examineStatus === 'FAIL'">
+                <div v-for="(goodsItem, goodsIndex) in item.orderDetails" :key="goodsIndex">
+                  <div class="goods" :class="'goods' + goodsIndex">
+                    <image :src="goodsItem.imgUrl" mode="aspectFill"></image>
+                    <div class="main">
+                      <div class="left">
+                        <div class="name ellipsis-2">{{ goodsItem.goodsName }}</div>
+                        <div class="des">{{ goodsItem.goodsSpecValue }}</div>
+                      </div>
+                      <div class="right">
+                        <div class="price">¥{{ formatPriceText(goodsItem.price) }}</div>
+                        <div class="num">x{{ goodsItem.refundNum }}</div>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+                <div class="total">
+                  <div class="left">{{ item.createTime }}</div>
+                  <div class="right">共{{ item.totalNum }}件 {{ item.orderStatus == 'NOPAY' ? '应付' : '实付' }}总额:<span>¥{{
+                    formatPriceText(item.payAmount) }}</span></div>
+                </div>
+              </div>
+              <!-- 其他 -->
+              <div v-else>
+                <div v-for="(goodsItem, goodsIndex) in item.orderDetails" :key="goodsIndex">
+                  <div class="goods" :class="'goods' + goodsIndex" v-if="goodsItem.refund">
+                    <image :src="goodsItem.imgUrl" mode="aspectFill"></image>
+                    <div class="main">
+                      <div class="left">
+                        <div class="name ellipsis-2">{{ goodsItem.goodsName }}</div>
+                        <div class="des">{{ goodsItem.goodsSpecValue }}</div>
+                      </div>
+                      <div class="right">
+                        <div class="price">¥{{ formatPriceText(goodsItem.price) }}</div>
+                        <div class="num">x{{ goodsItem.refundNum }}</div>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+                <div class="total">
+                  <div class="left">{{ item.createTime }}</div>
+                  <div class="right">共{{ getRefundNum(item.orderDetails) }}件
+                    {{ item.orderStatus == 'OVER' ? '实退' : '应退' }}总额:<span>¥{{ formatPriceText(item.refundAmount)
+                      }}</span>
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            <div v-if="item.isShareOrder">
+              <div class="btn-group btn-group2">
+                <div class="tips">分销订单</div>
+                <div class="btns">
+                  <div class="button gray">查看订单</div>
+                </div>
+              </div>
+            </div>
+            <div v-else>
+              <!-- 按钮:待付款 -->
+              <div class="btn-group btn-group2" v-if="item.orderStatus == 'NOPAY'">
+                <div class="tips">请在30分钟内付款</div>
+                <div class="btns">
+                  <div class="button red">立即付款</div>
+                </div>
+              </div>
+              <!-- 按钮:待发货 -->
+              <!-- <div class="btn-group" v-if="item.orderStatus == 'DFH'">
+              </div> -->
+              <!-- 按钮:待服务 -->
+              <!-- <div class="btn-group" v-if="item.orderStatus == 'YFH'">
+              </div> -->
+              <!-- 按钮:待评价 -->
+              <!-- <div class="btn-group" v-if="item.orderStatus == 'OVER' && tabCurrent != 'REFUND'">
+              </div> -->
+              <!-- 按钮:超时未支付 -->
+              <div class="btn-group" v-if="item.orderStatus == 'TIMEOUT'">
+                <div class="button gray">查看订单</div>
+              </div>
+              <!-- 按钮:售后中 -->
+              <div class="btn-group" v-if="item.orderStatus == 'REFUND'">
+                <div class="button gray">查看订单</div>
+              </div>
+              <!-- 按钮:售后中 待商家处理 -->
+              <div class="btn-group" v-if="item.orderStatus == 'DSJCL'">
+                <div class="button gray" @click="toReturnDetail(item.orderRefundId)">售后详情</div>
+              </div>
+              <!-- 按钮:售后中 待买家处理 -->
+              <div class="btn-group" v-if="item.orderStatus == 'DMJCL'">
+                <div class="button red" @click="toReturnDetail(item.orderRefundId)" v-if="item.examineStatus == 'OK'">
+                  提交资料
+                </div>
+                <div class="button white" @click="toApplyReturn(item.orderId)" v-if="item.examineStatus == 'FAIL'">
+                  重新申请
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <a-flex v-if="mainData.total > mainData.list.length" align="center" justify="center">
+          <a-button type="link" @click="handleLoadMore">点击查看更多订单</a-button>
+        </a-flex>
+      </div>
+      <div v-else class="list-container">
+        <a-empty />
+      </div>
+    </div>
+  </a-drawer>
+</template>
+
+<script setup lang="js">
+import { onMounted, reactive, computed } from 'vue';
+import { message, Modal } from 'ant-design-vue';
+import {
+  cancelOrder,
+  ackComplete,
+  remindNotice,
+  getMyOrderList
+} from '@/api/order';
+import { useUserStore } from '@/store/user'
+
+defineProps({
+  open: {
+    type: Boolean,
+    default: false
+  }
+})
+
+const emits = defineEmits(['close']);
+
+const userStore = useUserStore();
+
+const tabCurrent = computed(() => mainData.tabs[mainData.tabIndex].key);
+
+const mainData = reactive({
+  tabs: [
+    { key: '', name: '全部' },
+    { key: 'NOPAY', name: '待付款' },
+    { key: 'DQR', name: '待确认' },
+    { key: 'DJH', name: '待拣货' },
+    { key: 'JHZ', name: '拣货中' },
+    { key: 'DFH', name: '待发货' },
+    { key: 'DPS', name: '待配送' },
+    { key: 'PSDQ', name: '配送待取' },
+    { key: 'PSZ', name: '配送中' },
+    { key: 'OVER', name: '已完成' },
+    { key: 'CLOSE', name: '已关闭' }
+  ],
+  tabIndex: 0,
+  list: [],
+  pageNo: 1,
+  total: 0,
+  canRemindShipment: true
+})
+
+const statusFilter = item => {
+  const statusMap = {
+    DQR: '待确认',
+    NOPAY: '待付款',
+    DJH: '待拣货',
+    JHZ: '拣货中',
+    DFH: '待发货',
+    DTK: '待退款',
+    DPS: '待配送',
+    PSZ: '配送中',
+    OVER: '已完成',
+    CLOSE: '已关闭',
+    REFUND: '售后/退款',
+    TIMEOUT: '超时未支付'
+  }
+  if (item.orderStatus == 'OVER' && item.commentService) {
+    return '已完成'
+  } else {
+    return statusMap[item.orderStatus]
+  }
+}
+
+const statusFilter2 = (val) => {
+  const statusMap = {
+    DSJCL: '待商家处理',
+    DSJSH: '待商家收货',
+    DMJCL: '待买家处理',
+    OVER: '退款成功',
+    CANCEL: '已取消'
+  }
+  return statusMap[val]
+}
+
+const getRefundNum = (orderDetails) => {
+  let refundNum = 0
+  orderDetails.forEach(item => {
+    if (item.refund) {
+      refundNum = refundNum + item.refundNum
+    }
+  })
+  return refundNum
+}
+
+const changeTab = tabIndex => {
+  mainData.tabIndex = tabIndex;
+  resetParams();
+  fetchOrderList();
+}
+
+const resetParams = () => {
+  mainData.list = []
+  mainData.pageNo = 1
+  mainData.total = 0
+}
+
+const fetchOrderList = async () => {
+  const orderStatus = mainData.tabs[mainData.tabIndex].key || ''
+  const res = await getMyOrderList({
+    pageNo: mainData.pageNo,
+    pageSize: 10,
+    orderStatus: orderStatus,
+    userId: userStore.userInfo?.userId || ''
+  })
+  mainData.list = mainData.list.concat(res.data?.records || []);
+  mainData.total = res.data?.total || 0;
+}
+
+const handleLoadMore = () => {
+  mainData.pageNo += 1;
+  fetchOrderList();
+}
+
+
+// 申请退款
+const toApplyReturn = (orderId) => {
+  // url: '/packageMine/pages/order/return/apply?orderId=' + orderId
+}
+
+// 去订单详情
+const toOrderDetail = (orderId = '', orderRefundId = '') => {
+  // if (orderRefundId) {
+  //       return this.$navToPage({
+  //         url: '/packageMine/pages/order/return/detail?orderRefundId=' + orderRefundId
+  //       })
+  //     }
+  //     this.$navToPage({
+  //       url: '/packageMine/pages/order/detail?orderId=' + orderId + '&orderRefundId=' + orderRefundId
+  //     })
+}
+
+// 去售后详情
+const toReturnDetail = (orderRefundId) => {
+  // this.$navToPage({
+  //   url: '/packageMine/pages/order/return/detail?orderRefundId=' + orderRefundId
+  // })
+}
+
+// 提醒发货
+const remindShipment = (orderId) => {
+  mainData.canRemindShipment = false
+  setTimeout(() => {
+    mainData.canRemindShipment = true
+  }, 3000)
+  remindNotice({
+    orderId: orderId,
+    userId: userStore.userInfo?.userId || ''
+  }).then(() => {
+    message.success('提醒发货成功')
+  })
+}
+
+// 取消订单
+const handleCancelOrder = orderId => {
+  cancelOrder({
+    orderId: orderId
+  }).then(() => {
+    resetParams()
+    fetchOrderList()
+    message.success('取消成功')
+  })
+}
+
+// 服务完成
+const confirmReceipt = orderId => {
+  Modal.confirm({
+    title: '完成服务',
+    content: '请确认是否已完成服务?',
+    onOk() {
+      ackComplete({
+        orderId: orderId
+      }).then(() => {
+        message.success('完成服务成功')
+      })
+    }
+  })
+}
+
+const formatPriceText = price => {
+  if (!price) return '0.00'
+  price = Number(price)
+  return price.toFixed(2)
+}
+
+onMounted(() => {
+  fetchOrderList()
+})
+
+</script>
+
+<style lang="less" scoped>
+.drawer-box {
+  .tab-container {
+    background: #ffffff;
+  }
+
+  .list-container {
+    .item {
+      padding-bottom: 10px;
+      border-bottom: 1px solid @border-color;
+
+      .top {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 10px 0;
+
+        .left {
+          font-size: 14px;
+          font-weight: 500;
+        }
+
+        .right {
+          font-size: 14px;
+        }
+      }
+
+      .goods {
+        display: flex;
+        justify-content: space-between;
+        margin-top: 10px;
+        img {
+          width: 70px;
+          height: 70px;
+          object-fit: cover;
+        }
+
+        &.goods0 {
+          margin-top: 0;
+        }
+
+        image {
+          width: 70px;
+          height: 70px;
+          display: div;
+          flex-shrink: 0;
+          margin-right: 10px;
+        }
+
+        .main {
+          margin-left: 10px;
+          flex: 1;
+          display: flex;
+          justify-content: space-between;
+
+          .left {
+            .name {
+              font-size: 14px;
+              color: #333333;
+              line-height: 18px;
+            }
+
+            .des {
+              font-size: 14px;
+              color: #999999;
+              margin-top: 5px;
+            }
+          }
+
+          .right {
+            flex-shrink: 0;
+            margin-left: 10px;
+            text-align: right;
+
+            .price {
+              font-size: 14px;
+              color: #333333;
+              line-height: 14px;
+              font-weight: 600;
+              margin-top: 3;
+            }
+
+            .num {
+              font-size: 14px;
+              color: #999999;
+              line-height: 14px;
+              margin-top: 8px;
+            }
+          }
+        }
+      }
+
+      .total {
+        display: flex;
+        justify-content: space-between;
+        height: 25px;
+        align-items: center;
+
+        .left {
+          font-size: 12px;
+          color: #666666;
+        }
+
+        .right {
+          font-size: 12;
+          color: #666666;
+
+          text {
+            color: #ff577e;
+            ;
+            font-size: 16px;
+            font-weight: 600;
+            margin-left: 4px;
+          }
+        }
+      }
+
+      .btn-group {
+        border-top: 1px solid #eaeaea;
+        height: 50px;
+        display: flex;
+        justify-content: flex-end;
+        align-items: center;
+
+        &.btn-group2 {
+          justify-content: space-between;
+
+          .tips {
+            font-size: 14px;
+            color: #ff577e;
+          }
+
+          .btns {
+            display: flex;
+          }
+        }
+
+        .button {
+          width: 70px;
+          height: 24px;
+          border-radius: 24px;
+          text-align: center;
+          line-height: 24px;
+          font-size: 12px;
+          margin-left: 10px;
+          flex-shrink: 0;
+
+          &:first-child {
+            margin-left: 0;
+          }
+
+          &.gray {
+            color: #999999;
+            border: 1px solid #999999;
+          }
+
+          &.white {
+          }
+
+          &.red {
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 521 - 0
src/pages/home/components/SubmitCart.vue

@@ -0,0 +1,521 @@
+<template>
+  <div class="order-content">
+    <div class="order-goods">
+      <div class="title">商品信息</div>
+      <div class="goods-list__box">
+        <div v-for="(item, index) in mainData.goodsList" :key="index" class="list-item">
+          <div class="goods-image">
+            <img :src="item.imgUrl" />
+          </div>
+          <div class="goods-content">
+            <div class="goods-name">{{ item.goodsName }}</div>
+            <div class="goods-desc">{{ item.specValue }}</div>
+            <a-flex align="center" justify="space-between">
+              <div v-if="item.goodsSpecNewPrice && item.goodsSpecNewPrice > 0 && item.goodsSpecNewPrice != item.goodsPrice">
+                <div class="goods-price">¥{{ formatPriceText(item.goodsSpecNewPrice) }}</div>
+                <div class="goods-old__price">¥{{ formatPriceText(item.price) }}</div>
+              </div>
+              <div v-else class="goods-price">¥{{ formatPriceText(item.price) }}</div>
+              <div>
+                <a-input-number
+                  :value="item.num"
+                  style="width: 140px;"
+                  :min="1"
+                  :step="1"
+                  disabled
+                >
+                  <template #addonBefore>
+                    <MinusOutlined />
+                  </template>
+                  <template #addonAfter>
+                    <PlusOutlined />
+                  </template>
+                </a-input-number>
+              </div>
+            </a-flex>
+          </div>
+        </div>
+      </div>
+      <a-form
+        ref="formRef"
+        :model="mainData"
+        name="basic"
+        :label-col="{style: { width: '100px' }}"
+      >
+        <a-form-item
+          v-if="mainData.takeGoodsType == TakeTypeEnum.DISPATCH"
+          label="收货地址"
+          name="userAddressId"
+          :rules="[{ required: true, validator: checkAddress }]"
+        >
+          <a-select
+            v-model:value="mainData.userAddressId"
+            style="width: 100%"
+            placeholder="请选择收货地址"
+            :options="mainData.addressList"
+          />
+        </a-form-item>
+        <a-form-item
+          label="出货仓库"
+          name="storageName"
+        >
+          <div>{{ storageName }}</div>
+        </a-form-item>
+        <a-form-item
+          label="提货方式"
+          name="takeGoodsType"
+          :rules="[{ required: true, validator: checkTakeGoodsType }]"
+        >
+          <a-radio-group v-model:value="mainData.takeGoodsType" name="takeGoodsType">
+            <a-radio :value="TakeTypeEnum.PICK">自提</a-radio>
+            <a-radio
+              v-if="mainData.payType === PayTypeEnum.STORE || mainData.payType === PayTypeEnum.CREDIT"
+              :value="TakeTypeEnum.DISPATCH"
+            >商家配送</a-radio>
+          </a-radio-group>
+        </a-form-item>
+        <a-form-item
+          v-if="mainData.takeGoodsType === TakeTypeEnum.PICK"
+          label="提货时间"
+          name="pickTime"
+          :rules="[{ required: true, validator: checkPickTime }]"
+        >
+          <a-cascader
+            style="width: 100%;"
+            v-model:value="mainData.pickTime"
+            :options="mainData.pickTimeOptions"
+            placeholder="请选择选择提货时间"
+            allowClear
+          />
+        </a-form-item>
+        <a-form-item
+          v-if="mainData.takeGoodsType === TakeTypeEnum.DISPATCH"
+          label="配送时间"
+          name="dispatchTime"
+          :rules="[{ required: true, validator: checkDispatchTime }]"
+        >
+          <a-cascader
+            style="width: 100%;"
+            v-model:value="mainData.dispatchTime"
+            :options="mainData.dispatchTimeOptions"
+            placeholder="请选择配送时间"
+            allowClear
+          />
+        </a-form-item>
+        <a-form-item
+          label="卖家留言"
+          name="remark"
+        >
+          <a-textarea
+            v-model:value="mainData.remark"
+            placeholder="选填,留言建议50字内"
+            allowClear
+            :auto-size="{ minRows: 2, maxRows: 5 }"
+          />
+        </a-form-item>
+        <a-form-item
+          label="支付方式"
+          name="payType"
+          :rules="[{ required: true, validator: checkPayType }]"
+        >
+          <a-radio-group v-model:value="mainData.payType" name="payType">
+            <a-radio v-if="isCreditPay" :value="PayTypeEnum.CREDIT">授信支付(额度: {{ userDetail.availableCredit }})</a-radio>
+            <a-radio v-if="userDetail.storePay" :value="PayTypeEnum.STORE">到店支付</a-radio>
+          </a-radio-group>
+        </a-form-item>
+        <a-form-item
+          label="商品金额"
+          name="totalAmount"
+        >
+          <div class="price-text">¥{{ formatPriceText(mainData.orderInfo.totalAmount) }}</div>
+        </a-form-item>
+        <a-form-item
+          v-if="mainData.takeGoodsType === TakeTypeEnum.DISPATCH"
+          label="运费"
+          name="freight"
+        >
+          <div class="price-text">¥{{ formatPriceText(mainData.orderInfo.freight) }}</div>
+        </a-form-item>
+        <a-form-item
+          label="订单总金额"
+          name="payAmount"
+        >
+          <div class="price-text">¥{{ formatPriceText(Math.max(0, mainData.orderInfo.payAmount + (mainData.takeGoodsType === 1 ? (mainData.orderInfo.freight || 0) : 0))) }}</div>
+        </a-form-item>
+      </a-form>
+    </div>
+    <div class="order-footer">
+      <a-flex align="center" justify="space-between">
+        <a-flex align="center">
+          <div style="margin-right: 5px;">共{{ mainData.orderInfo.totalNum }}件,合计:</div>
+          <div class="price-text">¥{{ formatPriceText(Math.max(0, mainData.orderInfo.payAmount + (mainData.takeGoodsType == TakeTypeEnum.DISPATCH ? (mainData.orderInfo.freight || 0) : 0) - (mainData.tradeInfo && mainData.tradeInfo.yjhxDkFlag == 1 ? mainData.tradeInfo.payAmount : 0))) }}</div>
+        </a-flex>
+        <a-flex align="center">
+          <a-button type="primary" :disabled="mainData.disabled" @click="handleSubmit">提交订单</a-button>
+        </a-flex>
+      </a-flex>
+    </div>
+  </div>
+</template>
+
+<script setup lang="js">
+import dayjs from 'dayjs';
+import { message } from 'ant-design-vue';
+import { MinusOutlined, PlusOutlined } from '@ant-design/icons-vue';
+import { ref, reactive, onMounted, computed } from 'vue';
+import { ackOrder, orderBuy } from '@/api/order';
+import { getAddressList, getPickTimeList } from '@/api/user';
+import { useUserStore } from '@/store/user'
+import { useStorageStore } from '@/store/storage'
+
+const emits = defineEmits(['prev-step', 'finish']);
+
+const TakeTypeEnum = {
+  PICK: 0,// 自提
+  DISPATCH: 1,// 商家配送
+}
+
+const PayTypeEnum = {
+  STORE: 2,// 到店
+  CREDIT: 3,// 授信
+}
+
+const userStore = useUserStore();
+const storageStore = useStorageStore();
+
+const props = defineProps({
+  userId: {
+    type: String,
+    default: ''
+  },
+  storageId: {
+    type: String,
+    default: ''
+  },
+  buyGoods: {
+    type: Array,
+    default: () => []
+  }
+})
+
+const formRef = ref(null)
+const mainData = reactive({
+  goodsList: [],
+  addressList: [],
+  orderInfo: {},
+  userAddressId: null,
+  takeGoodsType: '',
+  pickTime: '',
+  dispatchTime: '',
+  remark: '',
+  payType: PayTypeEnum.STORE,
+  pickTimeOptions: [],
+  dispatchTimeOptions: [],
+  isSeckill: '',
+  freight: '',
+  isGiftGoods: false,
+  isFullPieceGoods: false,
+  disabled: false
+})
+
+const storageName = computed(() => {
+  return storageStore.list.find(item => item.storageId == storageStore.activedId)?.storageName || ''
+})
+const userDetail = computed(() => userStore.userDetail)
+const isCreditPay = computed(() => {
+  const payTotalAmount = Math.max(0, mainData.orderInfo.payAmount - (mainData.tradeInfo && mainData.tradeInfo.yjhxDkFlag == 1 ? mainData.tradeInfo.payAmount : 0))
+  return userDetail.value.isCreditEnabled && (userDetail.value.availableCredit - payTotalAmount > 0)
+})
+
+const fetchAddressList = async() => {
+  const res = await getAddressList({
+    pageNum: 1,
+    pageSize: 100,
+    userId: props.userId
+  })
+  mainData.addressList = (res.data?.records || []).map(item => {          
+    return {
+      label: `${item.province}${item.city}${item.area}${item.street}${item.address}${item.houseNo}`,
+      value: item.userAddressId,
+      ...item
+    }
+  })
+  mainData.userAddressId = mainData.addressList.find(item => item.defaultAddr)?.userAddressId || '';
+}
+
+const fetchPickTimeList = async () => {
+  const res = await getPickTimeList({
+    storageId: props.storageId
+  })
+  const list = res.data || [];
+  const validList = list.filter(item => item.overCurTime);
+  const dateList = validList.length > 0 ? validList[0].countList : [];
+  dateList.forEach((item, index) => {
+    mainData.pickTimeOptions[index] = {};
+    list.forEach(child => {
+      if (item.count < child.limitNum) {
+        mainData.pickTimeOptions[index].value = item.date;
+        mainData.pickTimeOptions[index].label = item.date;
+        if (mainData.pickTimeOptions[index].children === undefined) {
+          mainData.pickTimeOptions[index].children = []
+        }
+        mainData.pickTimeOptions[index].children.push({
+          value: `${child.startTime}-${child.endTime}`,
+          label: `${child.startTime}-${child.endTime}`
+        })
+      }
+    })
+  })
+  mainData.pickTimeOptions = mainData.pickTimeOptions.filter(item => item.children && item.children.length > 0)
+}
+
+const fetchOrderInfo = async () => {
+  const res = await ackOrder({
+    storageId: props.storageId,
+    buyGoods: props.buyGoods,
+    userAddressId: '',
+    userCouponId: '',
+  })
+  if (res.code == 1100) {
+    return message.error(res.message)
+  } else if (res.code !== 200) {
+    emits('prev-step')
+  }
+  if (res.data.promotionFullPriceRemark) {
+    let str = res.data.promotionFullPriceRemark
+    let str1 = str.substring(0, str.indexOf('打') + 1)
+    let str2 = str.substring(str.indexOf('折') + 1, str.indexOf('折'))
+    let num = str.substring(str.indexOf('打') + 1, str.indexOf('折'))
+    res.data.promotionFullPriceRemark = str1 + Number(num) + str2
+  }
+  mainData.orderInfo = res.data
+  mainData.goodsList = res.data.goods
+  mainData.isSeckill = res.data.isSecKill
+  mainData.freight = res.data.freight
+  mainData.isGiftGoods = findElem(res.data.goods, 'isGift', true) >= 0
+  mainData.isFullPieceGoods = findElem(res.data.goods, 'promotionFullPiece', true) >= 0
+}
+
+const findElem = (array, attr, val) => {
+  for (var i = 0; i < array.length; i++) {
+    if (array[i][attr] == val) {
+      return i //返回当前索引值
+    }
+  }
+  return -1
+}
+
+const setDispatchTimeRange = () => {
+  for (let i = 1; i <= 7; i++) {
+    const dayTimestamp = 1 * 24 * 60 * 60 * 1000
+    const date = dayjs(new Date().getTime() + dayTimestamp * i)
+    mainData.dispatchTimeOptions.push({
+      value: date.format('YYYY-MM-DD'),
+      label: date.format('YYYY-MM-DD'),
+      children: [
+        {
+          value: 'AM',
+          label: '上午'
+        },
+        {
+          value: 'PM',
+          label: '下午'
+        }
+      ]
+    })
+  }
+  
+}
+
+const getSendTime = () => {
+  if (mainData.takeGoodsType === TakeTypeEnum.DISPATCH) {
+    const amStart = mainData.dispatchTime[1] === 'AM' ? ' 00:00:00' : ' 12:00:01';
+    const amEnd = mainData.dispatchTime[1] === 'AM' ? ' 12:00:00' : ' 23:59:59';
+    return {
+      startTime: mainData.dispatchTime[0] + amStart,
+      endTime: mainData.dispatchTime[0] + amEnd
+    }
+  } else {
+    const hourMinArr = (mainData.pickTime[1] || '').split('-');
+    return {
+      startTime: mainData.pickTime[0] + ` ${hourMinArr[0]}:00`,
+      endTime: mainData.pickTime[0] + ` ${hourMinArr[1]}:00`
+    }
+  }
+}
+
+const handleSubmit = () => {
+  formRef.value?.validate().then(() => {
+    const obj = getSendTime()
+    const params = {
+      userId: props.userId,
+      storageId: props.storageId,
+      takeGoodsType: mainData.takeGoodsType,
+      buyGoods: props.buyGoods,
+      buyerMsg: mainData.remark,
+      userCouponId: '',
+      payTypeId: ['','ONLINE','STORE','CREDIT'][mainData.payType],
+      pickTimeId: '',
+      appointmentPickStartTime: obj.startTime,
+      appointmentPickEndTime: obj.endTime
+    }
+    if (mainData.takeGoodsType === TakeTypeEnum.PICK) {
+      params.userAddressId = mainData.userAddressId
+    }
+    mainData.disabled = false
+    orderBuy(params).then(res => {
+      if (res.code === 1100) {
+        return message.info(res.message)
+      }
+      if (res.data.isPay === false) {
+        if (mainData.payType === PayTypeEnum.STORE) {
+          message.success('购买成功,待商家确认')
+          emits('finish')
+        } else if (mainData.payType === PayTypeEnum.CREDIT) {
+          message.success('购买成功')
+          emits('finish')
+        }
+      }
+    }).finally(() => {
+      mainData.disabled = false
+    })
+  })
+}
+
+const formatPriceText = price => {
+  if (!price) return '0.00'
+  price = Number(price)
+  return price.toFixed(2)
+}
+
+const checkAddress = (rule, value) => {
+  if (mainData.takeGoodsType != TakeTypeEnum.DISPATCH) return Promise.resolve()
+  if (!value) return Promise.reject('收获地址不能为空')
+  return Promise.resolve()
+}
+
+const checkTakeGoodsType = (rule, value) => {
+  if (value === '') return Promise.reject('提货方式不能为空')
+  return Promise.resolve()
+}
+
+const checkPickTime = (rule, value) => {
+  if (mainData.takeGoodsType != TakeTypeEnum.PICK) return Promise.resolve()
+  if (!value) return Promise.reject('提货时间不能为空')
+  return Promise.resolve()
+}
+
+const checkDispatchTime = (rule, value) => {
+  if (mainData.takeGoodsType != TakeTypeEnum.DISPATCH) return Promise.resolve()
+  if (!value) return Promise.reject('配送时间不能为空')
+  return Promise.resolve()
+}
+
+const checkPayType = (rule, value) => {
+  return Promise.resolve()
+}
+
+onMounted(async() => {
+  // 获取配送时间范围(默认7天)
+  setDispatchTimeRange()
+  // 获取用户信息
+  // 进入页面先获取默认地址
+  await fetchAddressList()
+  // 获取提货时间列表
+  await fetchPickTimeList()
+  // 获取订单信息
+  await fetchOrderInfo()
+})
+
+</script>
+
+<style lang="less" scoped>
+@price-color: #ff577e;
+.order-content {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  .order-goods {
+    flex: 1;
+    min-height: 1;
+    overflow-y: auto;
+    .title {
+      color: #222;
+      font-size: 16px;
+      font-weight: 500;
+      margin-bottom: 10px;
+    }
+    .goods-list__box {
+      margin-bottom: 20px;
+      .list-item {
+        width: 100%;
+        display: flex;
+        align-items: center;
+        padding: 10px 0;
+        border-bottom: 1px solid @border-color;
+        .check-box {
+          cursor: pointer;
+          padding-right: 10px;
+          .check-item {
+            color: #969696;
+            font-size: 24px;
+            &.actived {
+              color: @theme-color;
+            }
+          }
+        }
+        .goods-image {
+          flex-shrink: 0;
+          width: 80px;
+          height: 80px;
+          margin-right: 8px;
+          img {
+            display: block;
+            width: 100%;
+            height: 100%;
+          }
+        }
+        .goods-content {
+          flex: 1;
+          min-width: 1px;
+          word-break: break-all;
+          .goods-name {
+            font-size: 14px;
+            line-height: 1.5;
+            margin-bottom: 5px;
+            color: @theme-color;
+          }
+          .goods-desc {
+            color: #666;
+            font-size: 13px;
+            margin-bottom: 10px;
+            white-space: nowrap;
+            overflow: hidden;
+            text-overflow: ellipsis;
+          }
+          .goods-price {
+            color: @price-color;
+            font-size: 14px;
+          }
+          .goods-old__price {
+            margin-top: 5px;
+            color: #666;
+            font-size: 13px;
+            text-decoration: line-through;
+          }
+        }
+      }
+    }
+  }
+  .order-footer {
+    padding-top: 10px;
+    border-top: 1px solid @border-color;
+  }
+}
+
+.price-text {
+  color: @price-color;
+  font-size: 16px;
+  font-weight: 500;
+}
+</style>

+ 40 - 1
src/pages/home/components/Toolbar.vue

@@ -1,8 +1,9 @@
 <template>
 <template>
   <div class="headeer-toolbar">
   <div class="headeer-toolbar">
     <a-flex align="center" justify="flex-end">
     <a-flex align="center" justify="flex-end">
+      <div class="toolbar-item" @click="viewOrder">我的订单</div>
       <div class="toolbar-item">分支</div>
       <div class="toolbar-item">分支</div>
-      <div class="toolbar-item">培训</div>
+      <div class="toolbar-item" title="点击查看培训信息" @click="handleTrain">培训</div>
       <div class="toolbar-item">联系方式</div>
       <div class="toolbar-item">联系方式</div>
       <a-dropdown placement="bottom">
       <a-dropdown placement="bottom">
         <div class="toolbar-item">语言切换</div>
         <div class="toolbar-item">语言切换</div>
@@ -21,10 +22,42 @@
         </template>
         </template>
       </a-dropdown>
       </a-dropdown>
     </a-flex>
     </a-flex>
+    <AccountModal :open="mainData.open" @close="mainData.open = false" />
+    <OrderModal :open="mainData.orderOpen" @close="mainData.orderOpen = false" />
   </div>
   </div>
 </template>
 </template>
 
 
 <script setup lang="js">
 <script setup lang="js">
+import AccountModal from './AccountModal.vue';
+import OrderModal from './OrderModal.vue';
+import { reactive } from 'vue';
+import { useRouter } from 'vue-router';
+
+const props = defineProps({
+  storageId: {
+    type: String,
+    default: ''
+  },
+  isLogin: {
+    type: Boolean,
+    default: false
+  }
+})
+
+const router = useRouter()
+const mainData = reactive({
+  open: false,
+  orderOpen: false
+})
+
+const viewOrder = () => {
+  if (props.isLogin) {
+    mainData.orderOpen = true;
+  } else {
+    mainData.open = true;
+  }
+}
+
 const handleLanguage = language => {
 const handleLanguage = language => {
   window.Vue_Translation_Of_Text_Type = language
   window.Vue_Translation_Of_Text_Type = language
   window.localStorage.setItem('Vue_Translation_Of_Text_Type', language)
   window.localStorage.setItem('Vue_Translation_Of_Text_Type', language)
@@ -32,6 +65,12 @@ const handleLanguage = language => {
     window.location.reload();
     window.location.reload();
   }, 250)
   }, 250)
 }
 }
+
+const handleTrain = async () => {
+  router.push({
+    path: '/train'
+  })
+}
 </script>
 </script>
 
 
 <style lang="less" scoped>
 <style lang="less" scoped>

+ 45 - 74
src/pages/home/index.vue

@@ -2,14 +2,17 @@
   <div class="home-page">
   <div class="home-page">
     <header>
     <header>
       <Notice v-if="mainData.noticeContent" :content="mainData.noticeContent" />
       <Notice v-if="mainData.noticeContent" :content="mainData.noticeContent" />
-      <Toolbar />
+      <Toolbar
+        :isLogin="isLogin"
+        :storageId="storageId"
+      />
       <HandleBar
       <HandleBar
         :isLogin="isLogin"
         :isLogin="isLogin"
         :storageId="storageId"
         :storageId="storageId"
-        :storeList="mainData.storeList"
+        :storeList="storeList"
         @change-store="changeStoreFn"
         @change-store="changeStoreFn"
       />
       />
-      <Category :tabs="categoryList" :tab-index="tabIndex" @change="changeTbaFn" />
+      <Category :tabs="categoryList" :tab-index="tabIndex" @change="changeTabsFn" />
     </header>
     </header>
     <main class="app-main__content">
     <main class="app-main__content">
       <RouterView />
       <RouterView />
@@ -94,12 +97,11 @@ import { useUserStore } from '@/store/user';
 import { useHomeStore } from '@/store/home';
 import { useHomeStore } from '@/store/home';
 import { useGoodsStore } from '@/store/goods';
 import { useGoodsStore } from '@/store/goods';
 import { useCategoryStore } from '@/store/category';
 import { useCategoryStore } from '@/store/category';
-import { getStoreList, getNotice } from '@/api/common';
-import * as storeUtil from '@/utils/storeUtil';
+import { useStorageStore } from '@/store/storage';
+import { getNotice } from '@/api/common';
 import { getAmityList } from '@/api/home';
 import { getAmityList } from '@/api/home';
 import { userLeaveMessage } from '@/api/user';
 import { userLeaveMessage } from '@/api/user';
 import { validPhone } from '@/utils/validate';
 import { validPhone } from '@/utils/validate';
-import { storeList } from '@/utils/mock';
 
 
 const route = useRoute();
 const route = useRoute();
 const router = useRouter();
 const router = useRouter();
@@ -107,18 +109,19 @@ const userStore = useUserStore();
 const homeStore = useHomeStore();
 const homeStore = useHomeStore();
 const goodsStore = useGoodsStore();
 const goodsStore = useGoodsStore();
 const categoryStore = useCategoryStore();
 const categoryStore = useCategoryStore();
+const storageStore = useStorageStore();
 
 
 const tabIndex = computed(() => homeStore.tabIndex);
 const tabIndex = computed(() => homeStore.tabIndex);
-const storageId = computed(() => homeStore.storageId);
 const categoryList = computed(() => categoryStore.list);
 const categoryList = computed(() => categoryStore.list);
 const isLogin = computed(() => {
 const isLogin = computed(() => {
   return userStore.userInfo != null
   return userStore.userInfo != null
 });
 });
 const showBackIcon = computed(() => route.path !== '/category');
 const showBackIcon = computed(() => route.path !== '/category');
+const storeList = computed(() => storageStore.list);
+const storageId = computed(() => storageStore.activedId);
 
 
 const msgFormRef = ref()
 const msgFormRef = ref()
 const mainData = reactive({
 const mainData = reactive({
-  storeList: [],
   linkList: [],
   linkList: [],
   noticeContent: '',
   noticeContent: '',
   disabled: false
   disabled: false
@@ -167,64 +170,31 @@ const checkMobile = (rule, value) => {
   return Promise.resolve()
   return Promise.resolve()
 }
 }
 
 
-const resetGoodsDataAndFetch = () => {
+const changeTabsFn = async (newTabIndex) => {
+  homeStore.changeTab(newTabIndex);
   goodsStore.resetParams();
   goodsStore.resetParams();
   goodsStore.resetListData();
   goodsStore.resetListData();
-  goodsStore.fetchListData();
-}
-
-const changeTbaFn = newTabIndex => {
-  if (newTabIndex == tabIndex.value) return;
-  homeStore.changeTab(newTabIndex);
-  const parentId = categoryList.value[newTabIndex]?.categoryId;
-  if (parentId) {
-    categoryStore.fetchChildListData({
-      type: '1',
-      parentId: parentId
-    })
-  }
-  if (route.path !== '/category') {
-    router.push({
-      path: '/category'
-    })
-  } else {
-    resetGoodsDataAndFetch();
+  if (showBackIcon.value) {
+    router.back()
   }
   }
-}
-
-const fetchStoreListData = async () => {
-  const res = await getStoreList({
-    lat: '23.13',
-    lng: '113.26'
-  });
-  mainData.storeList = res.data || [];
-  mainData.storeList = storeList;
-  if (!storeUtil.getId() && mainData.storeList.length > 0) {
-    storeUtil.setId(mainData.storeList[0].storageId);
-    homeStore.updateStorageId(mainData.storeList[0].storageId);
+  const tabCategoryId = categoryList.value[newTabIndex]?.categoryId;
+  if (tabCategoryId) {
+    await categoryStore.fetchChildListData({ type: '1', parentId: tabCategoryId });
+    if (categoryStore.childList && categoryStore.childList.length > 0) {
+      goodsStore.updateParams({
+        categoryId: categoryStore.childList[0].categoryId || ''
+      })
+      goodsStore.fetchListData();
+    }
   }
   }
 }
 }
 
 
+/**
+ * 切换仓库, 切换仓库后需要将一切数据都重新加载,并且回到当前主页
+ */
 const changeStoreFn = row => {
 const changeStoreFn = row => {
-  storeUtil.setId(row.storageId);
-  homeStore.updateStorageId(row.storageId);
-  homeStore.resetTab();
-  categoryStore.fetchListData().then(() => {
-    const parentId = categoryList.value[tabIndex.value]?.categoryId;
-    if (parentId) {
-      categoryStore.fetchChildListData({
-        type: '1',
-        parentId: parentId
-      })
-    }
-  })
-  if (route.path !== '/category') {
-    router.push({
-      path: '/category'
-    })
-  } else {
-    resetGoodsDataAndFetch();
-  }
+  storageStore.updateActivedId(row.storageId);
+  window.location.href = '/';
 }
 }
 
 
 const fetchNoticeData = async () => {
 const fetchNoticeData = async () => {
@@ -239,22 +209,23 @@ const fetchgetAmityList = async () => {
 }
 }
 
 
 onMounted(async () => {
 onMounted(async () => {
-  homeStore.updateStorageId(storeUtil.getId());
-  await fetchStoreListData();
-  categoryStore.fetchListData().then(() => {
-    const parentId = categoryList.value[tabIndex.value]?.categoryId;
-    if (parentId) {
-      categoryStore.fetchChildListData({
-        type: '1',
-        parentId: parentId
-      })
-    }
-  });
-  resetGoodsDataAndFetch();
-  fetchgetAmityList();
-  if (isLogin.value) {
+  await storageStore.fetchListData();
+  if (storeList.value && storeList.value.length > 0) {
+    storageStore.updateActivedId(storeList.value[0].storageId);
     fetchNoticeData();
     fetchNoticeData();
-    userStore.fetchUserDetail()
+    fetchgetAmityList();
+    isLogin.value && userStore.fetchUserDetail();
+    await categoryStore.fetchListData();
+    const tabCategoryId = categoryList.value[tabIndex.value]?.categoryId;
+    if (tabCategoryId) {
+      await categoryStore.fetchChildListData({ type: '1', parentId: tabCategoryId });
+      if (categoryStore.childList && categoryStore.childList.length > 0) {
+        goodsStore.updateParams({
+          categoryId: categoryStore.childList[0].categoryId || ''
+        })
+        goodsStore.fetchListData();
+      }
+    }
   }
   }
 })
 })
 
 

+ 4 - 8
src/pages/login/index.vue

@@ -91,12 +91,14 @@ import { useRouter } from 'vue-router';
 import { message } from 'ant-design-vue';
 import { message } from 'ant-design-vue';
 import { getCode, getVerifiImage, userLogin } from '@/api/user';
 import { getCode, getVerifiImage, userLogin } from '@/api/user';
 import { useUserStore } from '@/store/user';
 import { useUserStore } from '@/store/user';
+import { useStorageStore } from '@/store/storage';
 import { setToken } from '@/utils/token';
 import { setToken } from '@/utils/token';
-import * as storeUtil from '@/utils/storeUtil';
 import { validPhone } from '@/utils/validate';
 import { validPhone } from '@/utils/validate';
 
 
 const router = useRouter();
 const router = useRouter();
 const userStore = useUserStore();
 const userStore = useUserStore();
+const storageStore = useStorageStore();
+
 const formRef = ref(null)
 const formRef = ref(null)
 const mainForm = reactive({
 const mainForm = reactive({
   mobile: '',
   mobile: '',
@@ -119,12 +121,6 @@ const codeObj = ref({
   isFail: false
   isFail: false
 })
 })
 
 
-const toSendCode = () => {
-  mainForm.open = false
-  mainForm.disabled = true
-  fetchCode()
-}
-
 const fetchCode = async (val) => {
 const fetchCode = async (val) => {
   mainForm.disabled = true
   mainForm.disabled = true
   getCode({
   getCode({
@@ -157,7 +153,7 @@ const fetchLogin = async () => {
   }
   }
   userLogin(params).then(res => {
   userLogin(params).then(res => {
     setToken(res.data.token);
     setToken(res.data.token);
-    storeUtil.setId(res.data?.storage?.storageId);
+    storageStore.updateActivedId(res.data?.storage?.storageId);
     userStore.updateUserInfo(res.data);
     userStore.updateUserInfo(res.data);
     message.success('登录成功');
     message.success('登录成功');
     router.back();
     router.back();

+ 62 - 0
src/pages/train/index.vue

@@ -0,0 +1,62 @@
+<template>
+  <div>
+    <a-table :dataSource="mainData.list" :columns="mainData.columns" :pagination="false">
+      <template #bodyCell="{record, column}">
+        <div v-if="column.key === 'operation'">
+          <a-button type="primary" @click="viewDetail(record)">立即查看</a-button>
+        </div>
+      </template>
+    </a-table>
+  </div>
+</template>
+
+<script setup lang="js">
+import { reactive, onMounted } from 'vue'
+import { getConfigData, getTrainData } from '@/api/common';
+
+const mainData = reactive({
+  list: [],
+  columns: [
+    {
+      title: '名字',
+      dataIndex: 'name',
+      key: 'name',
+    },
+    {
+      title: '描述',
+      dataIndex: 'remark',
+      key: 'remark',
+    },
+    {
+      title: '操作',
+      dataIndex: 'operation',
+      key: 'operation',
+      width: 150
+    }
+  ]
+})
+
+const fetchConfigData = async () => {
+  return await getConfigData()
+}
+
+const fetchTrainData = async(id) => {
+  const res = await getTrainData({
+    id: id
+  })
+  mainData.list = res.data || [];
+}
+
+const viewDetail = row => {
+  if (!row.url) return;
+  window.open(row.url, '_blank')
+}
+
+onMounted(async() => {
+  const res = await fetchConfigData();
+  const trainingFolderId = res.data?.trainingFolderId;
+  if (trainingFolderId) {
+    fetchTrainData(trainingFolderId)
+  }
+})
+</script>

+ 7 - 0
src/router/index.js

@@ -26,6 +26,13 @@ const routes = [
         meta: {
         meta: {
           title: '商品详情'
           title: '商品详情'
         }
         }
+      },
+      {
+        path: '/train',
+        component: () => import('@/pages/train/index.vue'),
+        meta: {
+          title: '培训'
+        }
       }
       }
     ]
     ]
   },
   },

+ 0 - 2
src/store/category.js

@@ -1,6 +1,5 @@
 import { defineStore } from 'pinia';
 import { defineStore } from 'pinia';
 import { getList } from '@/api/category';
 import { getList } from '@/api/category';
-import { categoryList } from '@/utils/mock';
 
 
 export const useCategoryStore = defineStore('category', {
 export const useCategoryStore = defineStore('category', {
   state: () => ({
   state: () => ({
@@ -13,7 +12,6 @@ export const useCategoryStore = defineStore('category', {
         type: '1'
         type: '1'
       });
       });
       this.list = res.data || [];
       this.list = res.data || [];
-      // this.list = categoryList;
     },
     },
     async fetchChildListData(params) {
     async fetchChildListData(params) {
       this.childList = [];
       this.childList = [];

+ 0 - 4
src/store/goods.js

@@ -1,7 +1,6 @@
 import { defineStore } from 'pinia';
 import { defineStore } from 'pinia';
 import { message } from 'ant-design-vue';
 import { message } from 'ant-design-vue';
 import { getList, addToCard, addFavorite, delFavorite, getFavoriteList } from '@/api/goods';
 import { getList, addToCard, addFavorite, delFavorite, getFavoriteList } from '@/api/goods';
-import { goodsList } from '@/utils/mock';
 
 
 export const useGoodsStore = defineStore('goods', {
 export const useGoodsStore = defineStore('goods', {
   state: () => ({
   state: () => ({
@@ -25,13 +24,10 @@ export const useGoodsStore = defineStore('goods', {
       });
       });
       this.list = res.data?.records || [];
       this.list = res.data?.records || [];
       this.params.total = res.data?.total || 0;
       this.params.total = res.data?.total || 0;
-      // this.list = goodsList;
-      // this.params.total = 120;
     },
     },
     async fetchAllData(params) {
     async fetchAllData(params) {
       const res = await getList(params);
       const res = await getList(params);
       return Promise.resolve(res.data?.records || []);
       return Promise.resolve(res.data?.records || []);
-      // return new Promise(resolve => resolve(goodsList))
     },
     },
     resetParams() {
     resetParams() {
       this.params.pageNum = 1;
       this.params.pageNum = 1;

+ 23 - 0
src/store/storage.js

@@ -0,0 +1,23 @@
+import { defineStore } from 'pinia';
+import { getStoreList } from '@/api/common';
+import * as storeUtil from '@/utils/storeUtil';
+
+export const useStorageStore = defineStore('storage', {
+  state: () => ({
+    list: [],
+    activedId: storeUtil.getId() || ''
+  }),
+  actions: {
+    async fetchListData() {
+      const res = await getStoreList({
+        lat: '23.13',
+        lng: '113.26'
+      })
+      this.list = res.data || []
+    },
+    updateActivedId(storageId) {
+      storeUtil.setId(storageId);
+      this.activedId = storageId;
+    }
+  }
+})

+ 0 - 6
src/store/user.js

@@ -1,6 +1,5 @@
 import { defineStore } from 'pinia';
 import { defineStore } from 'pinia';
 import { userLoginData } from '@/api/user';
 import { userLoginData } from '@/api/user';
-import { getToken } from '@/utils/token'
 
 
 export const useUserStore = defineStore('user', {
 export const useUserStore = defineStore('user', {
   state: () => ({
   state: () => ({
@@ -22,10 +21,5 @@ export const useUserStore = defineStore('user', {
       })
       })
       this.userDetail = res.data || {}
       this.userDetail = res.data || {}
     }
     }
-  },
-  getters: {
-    isLogin: state => {
-      return state.userInfo !== null 
-    }
   }
   }
 })
 })

+ 0 - 21
src/styles/common.less

@@ -1,21 +0,0 @@
-// .ant-modal-body {
-//   position: relative !important;
-//   &::before {
-//     content: " " !important;
-//     position: absolute !important;
-//     top: 0 !important;
-//     left: 0 !important;
-//     width: calc(100% + 24px) !important;
-//     height: 1px !important;
-//     background: @border-color !important;
-//   }
-//   &::after {
-//     content: " " !important;
-//     position: absolute !important;
-//     bottom: 0 !important;
-//     left: 0 !important;
-//     width: calc(100% + 24px) !important;
-//     height: 1px !important;
-//     background: @border-color !important;
-//   }
-// }

+ 1 - 1
vite.config.js

@@ -38,7 +38,7 @@ export default defineConfig(({ mode }) => {
       }
       }
     },
     },
     server: {
     server: {
-      port: 3000,
+      port: 8088,
       open: true,
       open: true,
       proxy: {
       proxy: {
         // '/api': {
         // '/api': {