Pārlūkot izejas kodu

Merge remote-tracking branch 'origin/development' into development

spy 1 mēnesi atpakaļ
vecāks
revīzija
584226f917
37 mainītis faili ar 3502 papildinājumiem un 1689 dzēšanām
  1. 3 8
      .env.development
  2. 1 1
      .env.production
  3. 1 1
      src/api/index.ts
  4. 0 129
      src/api/indexSecond.ts
  5. 14 11
      src/api/modules/groupPackageManagement.ts
  6. 19 31
      src/assets/json/authMenuList.json
  7. 1 1
      src/config/index.ts
  8. 1 1
      src/languages/modules/zh.ts
  9. 1 1
      src/layouts/components/Header/components/InfoDialog.vue
  10. 2 2
      src/stores/modules/global.ts
  11. 178 99
      src/views/couponManagement/index.vue
  12. 453 0
      src/views/couponManagement/newCoupon.vue
  13. 97 40
      src/views/groupPackageManagement/index.vue
  14. 435 266
      src/views/groupPackageManagement/newGroup.vue
  15. 145 0
      src/views/home/components/go-enter.vue
  16. 117 0
      src/views/home/components/go-examine.vue
  17. 470 0
      src/views/home/components/go-flow.vue
  18. 0 12
      src/views/home/index.scss
  19. 29 4
      src/views/home/index.vue
  20. 0 157
      src/views/home/toDoItem/components/curve.vue
  21. 0 131
      src/views/home/toDoItem/components/pie.vue
  22. BIN
      src/views/home/toDoItem/images/1-bg.png
  23. BIN
      src/views/home/toDoItem/images/2-bg.png
  24. BIN
      src/views/home/toDoItem/images/3-bg.png
  25. BIN
      src/views/home/toDoItem/images/4-bg.png
  26. BIN
      src/views/home/toDoItem/images/add_person.png
  27. BIN
      src/views/home/toDoItem/images/add_team.png
  28. BIN
      src/views/home/toDoItem/images/book-bg.png
  29. BIN
      src/views/home/toDoItem/images/book-sum.png
  30. BIN
      src/views/home/toDoItem/images/book_sum.png
  31. BIN
      src/views/home/toDoItem/images/today.png
  32. 0 154
      src/views/home/toDoItem/index.scss
  33. 0 442
      src/views/home/toDoItem/index.vue
  34. 111 0
      src/views/home/userInfo.vue
  35. 203 99
      src/views/orderManagement/index.vue
  36. 203 99
      src/views/voucherManagement/index.vue
  37. 1018 0
      src/views/voucherManagement/newVoucher.vue

+ 3 - 8
.env.development

@@ -19,13 +19,8 @@ VITE_PWA = false
 VITE_API_URL_STORE = /api/alienStore #生产环境使用
 VITE_API_URL = /api/alienStore
 VITE_API_URL_SECOND = /api/alienSecond
-
+VITE_API_URL_PLATFORM = /api/alienStorePlatform
 
 # 开发环境跨域代理,支持配置多个
-# VITE_PROXY = [["/api","https://api.ailien.shop"]] #生产环境
-VITE_PROXY = [["/api","http://192.168.2.251:7000"]] # 孙
-# VITE_PROXY = [["/api","http://192.168.2.114:8888"]] # 秦旭阳
-# VITE_PROXY = [["/api","http://192.168.2.118:8888"]] # 刘云鑫
-# VITE_PROXY = [["/api","http://192.168.2.119:8888"]] # 张忱
-# VITE_PROXY = [["/api","http://192.168.2.141:8888"]] # 贾友晨
-# VITE_PROXY = [["/api-easymock","https://mock.mengxuegu.com"],["/api-fastmock","https://www.fastmock.site"]]
+VITE_PROXY = [["/api","https://api.ailien.shop"]] #生产环境
+# VITE_PROXY = [["/api","http://192.168.2.57:7000"]] # 邹建宇

+ 1 - 1
.env.production

@@ -28,7 +28,7 @@ VITE_PWA = true
 VITE_API_URL_STORE = /api/alienStore
 VITE_API_URL = /api/alienStore
 VITE_API_URL_SECOND = /api/alienSecond
-
+VITE_API_URL_PLATFORM = /api/alienStorePlatform
 
 # 生产环境跨域代理,支持配置多个
 VITE_PROXY = [["/alienStore","https://api.ailien.shop/alienStore"]]

+ 1 - 1
src/api/index.ts

@@ -16,7 +16,7 @@ export interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
 
 const config = {
   // 默认地址请求地址,可在 .env.** 文件中修改
-  baseURL: import.meta.env.VITE_API_URL_STORE as string,
+  baseURL: import.meta.env.VITE_API_URL_PLATFORM as string,
   // 设置超时时间
   timeout: ResultEnum.TIMEOUT as number,
   // 跨域时候允许携带凭证

+ 0 - 129
src/api/indexSecond.ts

@@ -1,129 +0,0 @@
-import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse } from "axios";
-import { showFullScreenLoading, tryHideFullScreenLoading } from "@/components/Loading/fullScreen";
-import { LOGIN_URL } from "@/config";
-import { ElMessage } from "element-plus";
-import { ResultData } from "@/api/interface";
-import { ResultEnum } from "@/enums/httpEnum";
-import { checkStatus } from "./helper/checkStatus";
-import { AxiosCanceler } from "./helper/axiosCancel";
-import { useUserStore } from "@/stores/modules/user";
-import router from "@/routers";
-
-export interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
-  loading?: boolean;
-  cancel?: boolean;
-}
-
-const config = {
-  // 默认地址请求地址,可在 .env.** 文件中修改
-  baseURL: import.meta.env.VITE_API_URL_SECOND as string,
-  // 设置超时时间
-  timeout: ResultEnum.TIMEOUT as number,
-  // 跨域时候允许携带凭证
-  withCredentials: true
-};
-
-const axiosCanceler = new AxiosCanceler();
-
-class RequestHttp {
-  service: AxiosInstance;
-
-  public constructor(config: AxiosRequestConfig) {
-    // instantiation
-    this.service = axios.create(config);
-
-    /**
-     * @description 请求拦截器
-     * 客户端发送请求 -> [请求拦截器] -> 服务器
-     * token校验(JWT) : 接受服务器返回的 token,存储到 vuex/pinia/本地储存当中
-     */
-    this.service.interceptors.request.use(
-      (config: CustomAxiosRequestConfig) => {
-        const userStore = useUserStore();
-        // 重复请求不需要取消,在 api 服务中通过指定的第三个参数: { cancel: false } 来控制
-        config.cancel ??= true;
-        config.cancel && axiosCanceler.addPending(config);
-        // 当前请求不需要显示 loading,在 api 服务中通过指定的第三个参数: { loading: false } 来控制
-        config.loading ??= true;
-        config.loading && showFullScreenLoading();
-        if (config.headers && typeof config.headers.set === "function") {
-          config.headers.set("Authorization", userStore.token);
-        }
-        return config;
-      },
-      (error: AxiosError) => {
-        return Promise.reject(error);
-      }
-    );
-
-    /**
-     * @description 响应拦截器
-     *  服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
-     */
-    this.service.interceptors.response.use(
-      (response: AxiosResponse & { config: CustomAxiosRequestConfig }) => {
-        const { data, config } = response;
-
-        const userStore = useUserStore();
-        axiosCanceler.removePending(config);
-        config.loading && tryHideFullScreenLoading();
-        // 登录失效
-        if (data.code == ResultEnum.OVERDUE) {
-          userStore.setToken("");
-          router.replace(LOGIN_URL);
-          ElMessage.error(data.msg);
-          return Promise.reject(data);
-        }
-        // 全局错误信息拦截(防止下载文件的时候返回数据流,没有 code 直接报错)
-        if (data.code && data.code !== ResultEnum.SUCCESS) {
-          ElMessage.error(data.msg);
-          return Promise.reject(data);
-        }
-        // 成功请求(在页面上除非特殊情况,否则不用处理失败逻辑)
-        return data;
-      },
-      async (error: AxiosError) => {
-        const response: AxiosResponse | undefined = error.response as AxiosResponse | undefined;
-        tryHideFullScreenLoading();
-        // 请求超时 && 网络错误单独判断,没有 response
-        if (error.message.indexOf("timeout") !== -1) ElMessage.error("请求超时!请您稍后重试");
-        if (error.message.indexOf("Network Error") !== -1) ElMessage.error("网络错误!请您稍后重试");
-        // 根据服务器响应的错误状态码,做不同的处理
-        if (response && response.data.message) {
-          ElMessage.error(response.data.message);
-        } else {
-          if (response) checkStatus(response.status);
-        }
-        // 服务器结果都没有返回(可能服务器错误可能客户端断网),断网处理:可以跳转到断网页面
-        if (!window.navigator.onLine) router.replace("/500");
-        return Promise.reject(error);
-      }
-    );
-  }
-
-  /**
-   * @description 常用请求方法封装
-   */
-  get<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
-    return this.service.get(url, { params, ..._object });
-  }
-
-  post<T>(url: string, params?: object | string, _object = {}): Promise<ResultData<T>> {
-    console.log(config.baseURL, url, params);
-    return this.service.post(url, params, _object);
-  }
-
-  put<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
-    return this.service.put(url, params, _object);
-  }
-
-  delete<T>(url: string, params?: any, _object = {}): Promise<ResultData<T>> {
-    return this.service.delete(url, { params, ..._object });
-  }
-
-  download(url: string, params?: object, _object = {}): Promise<BlobPart> {
-    return this.service.post(url, params, { ..._object, responseType: "blob" });
-  }
-}
-
-export default new RequestHttp(config);

+ 14 - 11
src/api/modules/storeUser.ts → src/api/modules/groupPackageManagement.ts

@@ -6,8 +6,20 @@ import http from "@/api";
  * @name 商铺用户模块
  */
 // 获取商铺用户列表
-export const getStoreUserList = (params: StoreUser.ReqUserParams) => {
-  return http.get<ResPage<StoreUser.ResStoreUserList>>(PORT_NONE + `/store/user/getStoreUserList`, params);
+export const getThaliList = params => {
+  return http.get<ResPage<StoreUser.ResStoreUserList>>(PORT_NONE + `/PcGroupBuy/getThaliList`, params);
+};
+export const saveStoreInfo = (params: { id: string }) => {
+  return http.post(PORT_NONE + `/store/info/saveStoreInfo`, params);
+};
+export const getUserByPhone = params => {
+  return http.get(PORT_NONE + `/store/user/getUserByPhone`, params);
+};
+export const getDetail = params => {
+  return http.get(PORT_NONE + `/store/info/getDetail`, params);
+};
+export const getMenuByStoreId = params => {
+  return http.get(PORT_NONE + `/menu/getMenuByStoreId`, params);
 };
 
 // 新增商家端用户
@@ -65,9 +77,6 @@ export const addBusinessSectionAndTypes = (params: {
 };
 
 //新增商铺信息
-export const saveStoreInfo = (params: { id: string }) => {
-  return http.post(PORT_NONE + `/store/info/saveStoreInfo`, params);
-};
 
 //获取商铺明细信息
 export const getStoreDetail = (params: StoreUser.ReqUserParams) => {
@@ -78,12 +87,6 @@ export const getStoreDetail = (params: StoreUser.ReqUserParams) => {
 export const editStoreInfo = (params: { id: string }) => {
   return http.post(PORT_NONE + `/store/info/editStoreInfo`, params);
 };
-
-// 修改商铺用户
-// export const editStoreUser1 = (params: { id: string }) => {
-//   return http.post(PORT_NONE + `/store/info/editStoreInfo`, params);
-// };
-
 // 删除商铺信息
 export const deleteStoreInfo = (params: { id: string }) => {
   return http.post(PORT_NONE + `/store/info/deleteStoreInfo`, params);

+ 19 - 31
src/assets/json/authMenuList.json

@@ -64,7 +64,7 @@
     {
       "path": "/couponManagement",
       "name": "couponManagement",
-      "redirect": "/couponManagement/index",
+      "component": "/couponManagement/index",
       "meta": {
         "icon": "Ticket",
         "title": "优惠券管理",
@@ -76,21 +76,22 @@
       },
       "children": [
         {
-          "path": "/couponManagement/index",
-          "name": "couponManagementIndex",
-          "component": "/couponManagement/index",
+          "path": "/couponManagement/newCoupon",
+          "name": "couponManagementNewCoupon",
+          "component": "/couponManagement/newCoupon",
           "meta": {
-            "icon": "Ticket",
-            "title": "优惠券管理",
+            "icon": "Menu",
+            "title": "新建优惠券",
+            "activeMenu": "/couponManagement",
             "isLink": "",
-            "isHide": false,
+            "isHide": true,
             "isFull": false,
             "isAffix": false,
             "isKeepAlive": false
           }
         },
         {
-          "path": "/couponManagementDetail",
+          "path": "/couponManagement/detail",
           "name": "couponManagementDetail",
           "component": "/couponManagement/detail",
           "meta": {
@@ -109,7 +110,7 @@
     {
       "path": "/voucherManagement",
       "name": "voucherManagement",
-      "redirect": "/voucherManagement/index",
+      "component": "/voucherManagement/index",
       "meta": {
         "icon": "Money",
         "title": "代金券管理",
@@ -121,21 +122,22 @@
       },
       "children": [
         {
-          "path": "/voucherManagement/index",
-          "name": "voucherManagementIndex",
-          "component": "/voucherManagement/index",
+          "path": "/voucherManagement/newVoucher",
+          "name": "voucherManagementNewVoucher",
+          "component": "/voucherManagement/newVoucher",
           "meta": {
-            "icon": "Money",
-            "title": "代金券管理",
+            "icon": "Menu",
+            "title": "新建代金券",
+            "activeMenu": "/voucherManagement",
             "isLink": "",
-            "isHide": false,
+            "isHide": true,
             "isFull": false,
             "isAffix": false,
             "isKeepAlive": false
           }
         },
         {
-          "path": "/voucherManagementDetail",
+          "path": "/voucherManagement/detail",
           "name": "voucherManagementDetail",
           "component": "/voucherManagement/detail",
           "meta": {
@@ -154,7 +156,7 @@
     {
       "path": "/orderManagement",
       "name": "orderManagement",
-      "redirect": "/orderManagement/index",
+      "component": "/orderManagement/index",
       "meta": {
         "icon": "Document",
         "title": "订单管理",
@@ -166,20 +168,6 @@
       },
       "children": [
         {
-          "path": "/orderManagement/index",
-          "name": "orderManagementIndex",
-          "component": "/orderManagement/index",
-          "meta": {
-            "icon": "Document",
-            "title": "订单管理",
-            "isLink": "",
-            "isHide": false,
-            "isFull": false,
-            "isAffix": false,
-            "isKeepAlive": false
-          }
-        },
-        {
           "path": "/orderManagementDetail",
           "name": "orderManagementDetail",
           "component": "/orderManagement/detail",

+ 1 - 1
src/config/index.ts

@@ -7,7 +7,7 @@ export const HOME_URL: string = "/home/index";
 export const LOGIN_URL: string = "/login";
 
 // 默认主题颜色
-export const DEFAULT_PRIMARY: string = "#009688";
+export const DEFAULT_PRIMARY: string = "#409eff";
 
 // 路由白名单地址(本地存在的路由 staticRouter.ts 中)
 export const ROUTER_WHITE_LIST: string[] = [

+ 1 - 1
src/languages/modules/zh.ts

@@ -23,7 +23,7 @@ export default {
     fullScreen: "全屏",
     exitFullScreen: "退出全屏",
     personalData: "个人信息",
-    changePassword: "修改密码",
+    changePassword: "修改登录密码",
     logout: "退出登录"
   }
 };

+ 1 - 1
src/layouts/components/Header/components/InfoDialog.vue

@@ -1,5 +1,5 @@
 <template>
-  <el-dialog v-model="dialogVisible" title="个人信息" width="500px" draggable>
+  <el-dialog v-model="dialogVisible" title="个人信息111" width="500px" draggable>
     <span>This is userInfo</span>
     <template #footer>
       <span class="dialog-footer">

+ 2 - 2
src/stores/modules/global.ts

@@ -38,9 +38,9 @@ export const useGlobalStore = defineStore({
     // 面包屑导航图标
     breadcrumbIcon: true,
     // 标签页
-    tabs: true,
+    tabs: false,
     // 标签页图标
-    tabsIcon: true,
+    tabsIcon: false,
     // 页脚
     footer: true
   }),

+ 178 - 99
src/views/couponManagement/index.vue

@@ -3,78 +3,179 @@
     <ProTable ref="proTable" :columns="columns" :request-api="getTableList" :init-param="initParam" :data-callback="dataCallback">
       <!-- 表格 header 按钮 -->
       <template #tableHeader="scope">
-        <el-button type="primary" :icon="Download" @click="exportInfoExcel(scope)"> 导出 </el-button>
-      </template>
-      <template #expand="scope">
-        {{ scope.row }}
+        <div class="table-header-btn">
+          <el-button :icon="Plus" class="button" type="primary" @click="newGroupBuying"> 新建优惠券 </el-button>
+          <el-tabs v-if="showTabs" v-model="activeName" class="tabs" @tab-click="handleClick">
+            <el-tab-pane v-for="tab in filteredTabOptions" :key="tab.name" :label="tab.label" :name="tab.name" />
+          </el-tabs>
+        </div>
       </template>
       <!-- 表格操作 -->
       <template #operation="scope">
         <!-- 审批通过和拒绝按钮仅在状态为0时显示 -->
         <template v-if="scope.row.status === '0'">
-          <el-button type="primary" link @click="changeTypes(scope.row, 'pass')"> 审核通过 </el-button>
-          <el-button type="primary" link @click="changeTypes(scope.row, '')"> 审核拒绝 </el-button>
+          <el-button link type="primary" @click="changeTypes(scope.row, 'on')"> 上架 </el-button>
+          <el-button link type="primary" @click="changeTypes(scope.row, 'off')"> 下架 </el-button>
+          <el-button link type="primary" @click="changeInventory(scope.row)"> 修改库存 </el-button>
         </template>
         <el-button type="primary" link @click="toDetail(scope.row)"> 查看详情 </el-button>
+        <el-button link type="primary" @click="editRow(scope.row)"> 编辑 </el-button>
+        <el-button link type="primary" @click="deleteRow(scope.row)"> 删除 </el-button>
       </template>
     </ProTable>
-
-    <el-dialog v-model="dialogFormVisible" title="审核拒绝" width="500">
-      <el-form :model="form">
-        <el-form-item label="" label-width="0">
-          <el-input v-model="form.comment" autocomplete="off" type="textarea" maxlength="200" />
+    <el-dialog v-model="dialogFormVisible" title="修改库存" width="500">
+      <el-form ref="ruleFormRef" :model="formInventory" :rules="rules" @submit.prevent>
+        <el-form-item label="套餐名">
+          {{ formInventory.packageName }}
+        </el-form-item>
+        <el-form-item label="剩余库存">
+          {{ formInventory.remainingInventory }}
+        </el-form-item>
+        <el-form-item label="修改库存" prop="newInventory">
+          <el-input v-model="formInventory.newInventory" placeholder="请输入" />
         </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
           <el-button @click="closeDialog"> 取消 </el-button>
-          <el-button type="primary" @click="handleSubmit"> 驳回 </el-button>
+          <el-button type="primary" @click="handleSubmit"> 确定 </el-button>
         </div>
       </template>
     </el-dialog>
   </div>
 </template>
 
-<script setup lang="tsx" name="couponManagement">
-import { ref, reactive, onMounted, onActivated } from "vue";
+<script setup lang="tsx" name="groupPackageManagement">
+import { computed, onActivated, onMounted, reactive, ref, watch } from "vue";
 import { useRouter } from "vue-router";
+import type { FormInstance, FormRules } from "element-plus";
 import { ElMessage } from "element-plus";
 import ProTable from "@/components/ProTable/index.vue";
-import { ProTableInstance, ColumnProps } from "@/components/ProTable/interface";
-import { Download } from "@element-plus/icons-vue";
-import { audit, exportExcelStaffConfig, getStaffConfigList } from "@/api/modules/staffConfig";
+import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
+import { Plus } from "@element-plus/icons-vue";
+import { audit, getStaffConfigList } from "@/api/modules/staffConfig";
+import { getThaliList } from "@/api/modules/groupPackageManagement";
 
 const router = useRouter();
 const dialogFormVisible = ref(false);
-const form = reactive({
-  comment: ""
+const formInventory: any = ref({
+  id: "",
+  packageName: "",
+  remainingInventory: "",
+  newInventory: ""
 });
-
 const rowData = ref<any>();
+const activeName = ref("");
 
+const ruleFormRef = ref<FormInstance>();
+const rules = reactive<FormRules<RuleForm>>({
+  newInventory: [
+    { required: true, message: "请输入库存数量", trigger: "blur" },
+    {
+      pattern: /^(0|[1-9][0-9]*)$/,
+      message: "请输入整数,不允许输入小数,负数",
+      trigger: "blur"
+    }
+  ]
+});
 const statusEnum = [
-  { value: "0", label: "待审核" },
-  { value: "1", label: "审核通过" },
-  { value: "2", label: "审核拒绝" }
+  { value: "-1", label: "待审核" },
+  { value: "-2", label: "审核通过" },
+  { value: "0", label: "审核驳回" }
 ];
-// 如果表格需要初始化请求参数,直接定义传给 ProTable (之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
-const initParam = reactive({});
 
-// 定义 filterValues
-const filterValues = reactive({});
+// ProTable 实例(需要在使用它的地方之前定义)
+const proTable = ref<ProTableInstance>();
 
-const getStatusObj = (statusValue: string) => {
-  const statusObj = statusEnum.find(item => item.value === statusValue);
-  if (statusObj) {
-    filterValues.status = statusObj;
-  } else {
-    filterValues.status = "";
+// 表格配置项
+const columns = reactive<ColumnProps<any>[]>([
+  {
+    prop: "index",
+    label: "序号",
+    width: 100,
+    render: (scope: any) => {
+      return scope.$index + (proTable.value!.pageable.pageNum - 1) * proTable.value!.pageable.pageSize + 1;
+    }
+  },
+  {
+    prop: "groupName",
+    label: "优惠券名称",
+    search: {
+      el: "input"
+    }
+  },
+  {
+    prop: "groupName",
+    label: "已领"
+  },
+  {
+    prop: "inventoryNum",
+    label: "剩余库存"
+  },
+  {
+    prop: "goodsId",
+    label: "结束时间"
+  },
+  {
+    prop: "status",
+    label: "状态"
+  },
+  { prop: "operation", label: "操作", fixed: "right", width: 330 }
+]);
+
+// 在 script setup 中添加
+const allTabOptions = [
+  { label: "全部", name: "" },
+  { label: "草稿", name: "0" },
+  { label: "进行中", name: "5" },
+  { label: "待审核", name: "1" },
+  { label: "审核拒绝", name: "3" },
+  { label: "已售罄", name: "4" },
+  { label: "已结束", name: "7" }
+];
+
+// 获取当前审核状态
+const currentAuditStatus = computed(() => {
+  if (!proTable.value?.searchParam) return "";
+  return proTable.value.searchParam.reviewType || "";
+});
+
+// 控制 el-tabs 是否显示:当审核状态为空或审核通过时显示
+const showTabs = computed(() => {
+  const status = currentAuditStatus.value;
+  return !status || status === "-2";
+});
+
+// 根据审核状态过滤 tabOptions:如果审核状态为空,只显示草稿;审核通过时,显示除草稿外的所有标签页
+const filteredTabOptions = computed(() => {
+  const status = currentAuditStatus.value;
+  if (!status) {
+    // 审核状态为空时,只显示草稿
+    return allTabOptions;
+  } else if (status === "-2") {
+    // 审核通过时,显示除草稿外的所有标签页
+    return allTabOptions.filter(tab => tab.name !== "1");
   }
-  return statusObj;
-};
+  return [];
+});
 
-// ProTable 实例
-const proTable = ref<ProTableInstance>();
+// 监听审核状态变化
+watch(
+  currentAuditStatus,
+  newStatus => {
+    if (!newStatus || newStatus === "-2") {
+      // 审核状态为空时,确保 activeName 为草稿
+      activeName.value = "";
+    }
+  },
+  { immediate: true }
+);
+// 如果表格需要初始化请求参数,直接定义传给 ProTable (之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
+const initParam = reactive({
+  storeId: "104",
+  groupType: "1",
+  status: activeName
+});
 
 // 页面加载时触发查询
 onMounted(() => {
@@ -99,50 +200,38 @@ const dataCallback = (data: any) => {
 // 默认不做操作就直接在 ProTable 组件上绑定	:requestApi="getUserList"
 const getTableList = (params: any) => {
   let newParams = JSON.parse(JSON.stringify(params));
-  return getStaffConfigList(newParams);
+  return getThaliList(newParams);
 };
 
 // 跳转详情页
 const toDetail = row => {
-  router.push(`/store/couponManagementDetail?id=${row.id}`);
+  router.push(`/couponManagement/detail?id=${row.id}&type=edit`);
 };
-
-// 表格配置项
-const columns = reactive<ColumnProps<any>[]>([
-  { type: "index", fixed: "left", label: "序号", width: 130 },
-  { prop: "storeName", label: "所属店铺" },
-  { prop: "name", label: "名称" },
-  { prop: "description", label: "描述" },
-  {
-    prop: "status",
-    label: "状态",
-    render: scope => {
-      const statusObj = getStatusObj(scope.row.status);
-      return statusObj ? statusObj.label : "未知状态";
-    },
-    search: {
-      el: "select"
-    },
-    enum: statusEnum,
-    fieldNames: { label: "label", value: "value" }
-  },
-  { prop: "operation", label: "操作", fixed: "right", width: 330 }
-]);
-
+const editRow = row => {};
+const deleteRow = row => {};
+const newGroupBuying = () => {
+  router.push(`/couponManagement/newCoupon?id=104&type=add`);
+};
+const handleClick = () => {};
 const changeTypes = (row: any, status: string) => {
   rowData.value = row;
-  if (status === "pass") {
+  if (status === "on") {
     handleChangeStatus(row, "1");
   } else {
-    form.comment = "";
-    dialogFormVisible.value = true;
+    handleChangeStatus(row, "2");
   }
 };
-
+const changeInventory = (row: any) => {
+  formInventory.value.id = 1;
+  formInventory.value.packageName = 1;
+  formInventory.value.remainingInventory = 1;
+  formInventory.value.newInventory = "";
+  dialogFormVisible.value = true;
+};
 const handleChangeStatus = async (row: any, status: string) => {
   try {
     let res = await audit({ id: row.id, status: status, rejectionReason: form.comment });
-    if (res.code === 200) {
+    if (res.code == 200) {
       proTable.value?.getTableList();
       if (status === "2") closeDialog();
       ElMessage.success("审核成功");
@@ -152,43 +241,25 @@ const handleChangeStatus = async (row: any, status: string) => {
   }
 };
 
-// 导出信息
-const exportInfoExcel = async scope => {
-  let res;
-  // 获取原始状态值(可能为数字、字符串或 undefined)
-  const rawStatus = proTable.value.searchParam.status;
-  // 转换为字符串(处理 undefined/null 为 "" 或保留原始字符串)
-  const statusParam = rawStatus !== undefined && rawStatus !== null ? String(rawStatus) : undefined;
-  // 将筛选条件作为参数传递给后台
-  res = await exportExcelStaffConfig({ status: statusParam });
-  if (res.code === 200) {
-    if (!res.data) {
-      ElMessage.error("暂无可下载数据");
-      return;
-    }
-    const exportFile = document.createElement("a");
-    exportFile.style.display = "none";
-    exportFile.download = `优惠券管理.xlsx`;
-    exportFile.href = `${res.data}?timestamp=${new Date().getTime()}`; // 添加时间戳防止缓存
-    document.body.appendChild(exportFile);
-    exportFile.click();
-    document.body.removeChild(exportFile);
-    ElMessage.success("下载成功");
-  }
-};
-
 // 弹窗提交
-const handleSubmit = () => {
-  if (!form.comment) {
-    ElMessage.error("请输入审批意见");
-    return;
-  }
-  handleChangeStatus(rowData.value, "2");
+const handleSubmit = async () => {
+  if (!ruleFormRef.value) return;
+  await ruleFormRef.value.validate((valid, fields) => {
+    if (valid) {
+      ElMessage.success("修改成功");
+      dialogFormVisible.value = false;
+    }
+  });
 };
 // 关闭弹窗;
 const closeDialog = () => {
   dialogFormVisible.value = false;
-  form.comment = "";
+  formInventory.value = {
+    id: "",
+    packageName: "",
+    remainingInventory: "",
+    newInventory: ""
+  };
 };
 </script>
 
@@ -200,4 +271,12 @@ const closeDialog = () => {
   word-wrap: break-word; // 长单词内换行
   white-space: normal; // 允许自然换行
 }
+.table-header-btn {
+  .tabs {
+    margin-top: 10px;
+    :deep(.el-tabs__nav-wrap::after) {
+      height: 0;
+    }
+  }
+}
 </style>

+ 453 - 0
src/views/couponManagement/newCoupon.vue

@@ -0,0 +1,453 @@
+<template>
+  <!-- 优惠券管理 - 新增页面 -->
+  <div class="table-box" style="width: 100%; min-height: 100%; background-color: white">
+    <div class="header">
+      <el-button @click="goBack"> 返回 </el-button>
+      <h2 class="title">新建优惠券</h2>
+    </div>
+    <el-form :model="couponModel" ref="ruleFormRef" :rules="rules" label-width="200px" class="formBox">
+      <div class="content">
+        <!-- 左侧内容区域 -->
+        <div class="contentLeft">
+          <!-- 优惠券名称 -->
+          <el-form-item label="优惠券名称" prop="couponName">
+            <el-input maxlength="50" v-model="couponModel.couponName" placeholder="请输入" clearable />
+          </el-form-item>
+          <!-- 面值 -->
+          <el-form-item label="面值(元)" prop="faceValue">
+            <el-input v-model="couponModel.faceValue" maxlength="15" placeholder="请输入" clearable />
+          </el-form-item>
+          <!-- 开始领取时间 -->
+          <el-form-item label="开始领取时间" prop="startCollectionTime">
+            <el-date-picker
+              v-model="couponModel.startCollectionTime"
+              format="YYYY/MM/DD"
+              value-format="YYYY-MM-DD"
+              placeholder="请选择开始领取时间"
+              :disabled-date="disabledStartDate"
+            />
+          </el-form-item>
+          <!-- 结束领取时间 -->
+          <el-form-item label="结束领取时间" prop="endCollectionTime">
+            <el-date-picker
+              v-model="couponModel.endCollectionTime"
+              format="YYYY/MM/DD"
+              value-format="YYYY-MM-DD"
+              placeholder="请选择结束领取时间"
+              :disabled-date="disabledEndDate"
+            />
+          </el-form-item>
+          <!-- 有效期 -->
+          <el-form-item label="有效期" prop="validityPeriod">
+            <el-input v-model="couponModel.validityPeriod" maxlength="15" placeholder="请输入" clearable />
+          </el-form-item>
+          <!-- 库存 -->
+          <el-form-item label="库存" prop="inventory">
+            <el-input v-model="couponModel.inventory" maxlength="15" placeholder="请输入" clearable />
+          </el-form-item>
+          <!-- 用户领取规则 -->
+          <el-form-item label="用户领取规则" prop="collectionRule">
+            <el-radio-group v-model="couponModel.collectionRule" class="ml-4">
+              <el-radio :value="1"> 每日一领 </el-radio>
+              <el-radio :value="2"> 每周一领 </el-radio>
+              <el-radio :value="3"> 每月一领 </el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <!-- 用户是否需要收藏店铺领取 -->
+          <el-form-item label="用户是否需要收藏店铺领取" prop="needFavoriteStore">
+            <el-radio-group v-model="couponModel.needFavoriteStore" class="ml-4">
+              <el-radio :value="1"> 是 </el-radio>
+              <el-radio :value="0"> 否 </el-radio>
+            </el-radio-group>
+          </el-form-item>
+        </div>
+        <!-- 右侧内容区域 -->
+        <div class="contentRight">
+          <!-- 是否有低消 -->
+          <el-form-item label="是否有低消" prop="hasMinimumSpend">
+            <el-radio-group v-model="couponModel.hasMinimumSpend" class="ml-4">
+              <el-radio :value="1"> 是 </el-radio>
+              <el-radio :value="0"> 否 </el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <!-- 最低消费金额 -->
+          <el-form-item label="最低消费金额" prop="minimumSpendAmount" v-if="couponModel.hasMinimumSpend === 1">
+            <el-input v-model="couponModel.minimumSpendAmount" maxlength="15" placeholder="请输入" clearable />
+          </el-form-item>
+          <!-- 补充说明 -->
+          <el-form-item label="补充说明" prop="additionalNotes">
+            <el-input
+              maxlength="300"
+              v-model="couponModel.additionalNotes"
+              :rows="4"
+              type="textarea"
+              placeholder="请输入"
+              show-word-limit
+            />
+          </el-form-item>
+        </div>
+      </div>
+    </el-form>
+    <!-- 底部按钮区域 -->
+    <div class="button-container">
+      <el-button @click="() => handleSubmit('draft')"> 存草稿 </el-button>
+      <el-button type="primary" @click="() => handleSubmit()"> 新建优惠券 </el-button>
+    </div>
+  </div>
+</template>
+
+<script setup lang="tsx" name="newCoupon">
+/**
+ * 优惠券管理 - 新增页面
+ * 功能:支持优惠券的新增操作
+ */
+import { ref, reactive, watch, nextTick } from "vue";
+import { ElMessage } from "element-plus";
+import { useRouter } from "vue-router";
+import type { FormInstance } from "element-plus";
+
+// ==================== 响应式数据定义 ====================
+
+// 路由相关
+const router = useRouter();
+
+// ==================== 表单验证规则 ====================
+const rules = reactive({
+  couponName: [{ required: true, message: "请输入优惠券名称" }],
+  faceValue: [
+    { required: true, message: "请输入面值" },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (!value || value.toString().trim() === "") {
+          callback();
+          return;
+        }
+        const num = Number(value);
+        if (isNaN(num) || num <= 0) {
+          callback(new Error("面值必须为正数"));
+          return;
+        }
+        callback();
+      },
+      trigger: "blur"
+    }
+  ],
+  startCollectionTime: [
+    { required: true, message: "请选择开始领取时间" },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (!value) {
+          callback();
+          return;
+        }
+        const selectedDate = new Date(value);
+        const today = new Date();
+        today.setHours(0, 0, 0, 0);
+        if (selectedDate < today) {
+          callback(new Error("开始领取时间不能早于当前时间"));
+          return;
+        }
+        if (couponModel.value.endCollectionTime) {
+          const endDate = new Date(couponModel.value.endCollectionTime);
+          if (selectedDate >= endDate) {
+            callback(new Error("开始领取时间必须早于结束领取时间"));
+            return;
+          }
+        }
+        callback();
+      },
+      trigger: "change"
+    }
+  ],
+  endCollectionTime: [
+    { required: true, message: "请选择结束领取时间" },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (!value) {
+          callback();
+          return;
+        }
+        const selectedDate = new Date(value);
+        const today = new Date();
+        today.setHours(0, 0, 0, 0);
+        if (selectedDate < today) {
+          callback(new Error("结束领取时间不能早于当前时间"));
+          return;
+        }
+        if (couponModel.value.startCollectionTime) {
+          const startDate = new Date(couponModel.value.startCollectionTime);
+          if (selectedDate <= startDate) {
+            callback(new Error("结束领取时间必须晚于开始领取时间"));
+            return;
+          }
+        }
+        callback();
+      },
+      trigger: "change"
+    }
+  ],
+  validityPeriod: [
+    { required: true, message: "请输入有效期" },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (!value || value.toString().trim() === "") {
+          callback();
+          return;
+        }
+        const num = Number(value);
+        if (isNaN(num) || num <= 0 || !Number.isInteger(num)) {
+          callback(new Error("有效期必须为正整数"));
+          return;
+        }
+        callback();
+      },
+      trigger: "blur"
+    }
+  ],
+  inventory: [
+    { required: true, message: "请输入库存" },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (!value || value.toString().trim() === "") {
+          callback();
+          return;
+        }
+        const num = Number(value);
+        if (isNaN(num) || num <= 0 || !Number.isInteger(num)) {
+          callback(new Error("库存必须为正整数"));
+          return;
+        }
+        callback();
+      },
+      trigger: "blur"
+    }
+  ],
+  minimumSpendAmount: [
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (couponModel.value.hasMinimumSpend === 1) {
+          if (!value || value.toString().trim() === "") {
+            callback(new Error("请输入最低消费金额"));
+            return;
+          }
+          const num = Number(value);
+          if (isNaN(num) || num <= 0) {
+            callback(new Error("最低消费金额必须为正数"));
+            return;
+          }
+        }
+        callback();
+      },
+      trigger: "blur"
+    }
+  ]
+});
+
+// ==================== 优惠券信息数据模型 ====================
+const couponModel = ref<any>({
+  // 优惠券名称
+  couponName: "",
+  // 面值(元)
+  faceValue: "",
+  // 开始领取时间
+  startCollectionTime: "",
+  // 结束领取时间
+  endCollectionTime: "",
+  // 有效期
+  validityPeriod: "",
+  // 库存
+  inventory: "",
+  // 用户领取规则:1-每日一领,2-每周一领,3-每月一领
+  collectionRule: 1,
+  // 用户是否需要收藏店铺领取:1-是,0-否
+  needFavoriteStore: 1,
+  // 是否有低消:1-是,0-否
+  hasMinimumSpend: 1,
+  // 最低消费金额
+  minimumSpendAmount: "",
+  // 补充说明
+  additionalNotes: ""
+});
+
+// ==================== 监听器 ====================
+
+/**
+ * 监听开始领取时间变化
+ * 当开始时间改变时,重新验证结束时间
+ */
+watch(
+  () => couponModel.value.startCollectionTime,
+  () => {
+    if (couponModel.value.endCollectionTime) {
+      nextTick(() => {
+        ruleFormRef.value?.validateField("endCollectionTime");
+      });
+    }
+  }
+);
+
+/**
+ * 监听结束领取时间变化
+ * 当结束时间改变时,重新验证开始时间
+ */
+watch(
+  () => couponModel.value.endCollectionTime,
+  () => {
+    if (couponModel.value.startCollectionTime) {
+      nextTick(() => {
+        ruleFormRef.value?.validateField("startCollectionTime");
+      });
+    }
+  }
+);
+
+/**
+ * 监听是否有低消变化
+ * 当选择"否"时,清空最低消费金额
+ */
+watch(
+  () => couponModel.value.hasMinimumSpend,
+  newVal => {
+    if (newVal === 0) {
+      couponModel.value.minimumSpendAmount = "";
+      nextTick(() => {
+        ruleFormRef.value?.clearValidate("minimumSpendAmount");
+      });
+    }
+  }
+);
+
+// ==================== 事件处理函数 ====================
+
+/**
+ * 返回上一页
+ */
+const goBack = () => {
+  router.go(-1);
+};
+
+// ==================== 表单引用 ====================
+const ruleFormRef = ref<FormInstance>(); // 表单引用
+
+/**
+ * 提交数据(新增)
+ * 验证表单,通过后调用相应的API接口
+ */
+const handleSubmit = (submitType?: string) => {
+  // 验证表单
+  ruleFormRef.value!.validate(async (valid: boolean) => {
+    if (!valid) return;
+
+    // 组装提交参数
+    let params: any = { ...couponModel.value };
+
+    console.log("提交参数:", params);
+
+    // TODO: 调用API保存数据
+    // if (submitType === 'draft') {
+    //   await saveCouponDraft(params);
+    // } else {
+    //   await saveCoupon(params);
+    // }
+
+    if (submitType === "draft") {
+      ElMessage.success("草稿保存成功");
+    } else {
+      ElMessage.success("优惠券创建成功");
+      router.go(-1);
+    }
+  });
+};
+
+// ==================== 工具函数 ====================
+
+/**
+ * 禁用开始领取时间的日期
+ * 不能选择早于当前时间的日期
+ */
+const disabledStartDate = (time: Date) => {
+  const today = new Date();
+  today.setHours(0, 0, 0, 0);
+  return time.getTime() < today.getTime();
+};
+
+/**
+ * 禁用结束领取时间的日期
+ * 不能选择早于当前时间的日期,也不能选择早于或等于开始领取时间的日期
+ */
+const disabledEndDate = (time: Date) => {
+  const today = new Date();
+  today.setHours(0, 0, 0, 0);
+  if (time.getTime() < today.getTime()) {
+    return true;
+  }
+  if (couponModel.value.startCollectionTime) {
+    const startDate = new Date(couponModel.value.startCollectionTime);
+    startDate.setHours(0, 0, 0, 0);
+    return time.getTime() <= startDate.getTime();
+  }
+  return false;
+};
+</script>
+
+<style scoped lang="scss">
+/* 页面容器 */
+.table-box {
+  display: flex;
+  flex-direction: column;
+  height: auto !important;
+  min-height: 100%;
+}
+
+/* 头部区域 */
+.header {
+  display: flex;
+  align-items: center;
+  padding: 20px;
+  border-bottom: 1px solid #e4e7ed;
+}
+.title {
+  flex: 1;
+  margin: 0;
+  font-size: 18px;
+  font-weight: bold;
+  text-align: center;
+}
+
+/* 内容区域布局 */
+.content {
+  display: flex;
+  flex: 1;
+  column-gap: 20px;
+  width: 98%;
+  margin: 20px auto;
+
+  /* 左侧内容区域 */
+  .contentLeft {
+    width: 50%;
+  }
+
+  /* 右侧内容区域 */
+  .contentRight {
+    width: 50%;
+  }
+}
+
+/* 表单容器 */
+.formBox {
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  min-height: 100%;
+}
+
+/* 底部按钮容器 - 居中显示 */
+.button-container {
+  display: flex;
+  gap: 12px;
+  align-items: center;
+  justify-content: center;
+  padding: 20px 0;
+  margin-top: 20px;
+}
+</style>

+ 97 - 40
src/views/groupPackageManagement/index.vue

@@ -5,25 +5,25 @@
       <template #tableHeader="scope">
         <div class="table-header-btn">
           <el-button :icon="Plus" class="button" type="primary" @click="newGroupBuying"> 新建团购 </el-button>
-          <el-tabs v-model="activeName" class="tabs" @tab-click="handleClick">
-            <el-tab-pane v-for="tab in tabOptions" :key="tab.name" :label="tab.label" :name="tab.name" />
+          <el-tabs v-if="showTabs" v-model="activeName" class="tabs" @tab-click="handleClick">
+            <el-tab-pane v-for="tab in filteredTabOptions" :key="tab.name" :label="tab.label" :name="tab.name" />
           </el-tabs>
         </div>
       </template>
       <template #Information="scope">
-        <div>
-          <el-image :src="scope.row.homeImage" />
-          <div>
-            <p>团购名称:{{ scope.row.goodsName }}</p>
-            <p>团购编号:{{ scope.row.goodsDesc }}</p>
-            <p>结束时间:{{ scope.row.goodsDesc }}</p>
+        <div class="information">
+          <el-image :src="scope.row.imageValueStr[0]" :preview-src-list="scope.row.imageValueStr" preview-teleported />
+          <div class="information-right">
+            <p>团购名称:{{ scope.row.groupName }}</p>
+            <p>团购编号:{{ scope.row.groupNo }}</p>
+            <p>结束时间:{{ scope.row.endTime }}</p>
           </div>
         </div>
       </template>
       <template #status="scope">
-        <p>团购状态:{{ scope.row.status }}</p>
-        <p>审核状态:{{ scope.row.status }}</p>
-      </template>
+        <p>团购状态:{{ allTabOptions.find(item => item.name === scope.row.groupType)?.label ?? "--" }}</p>
+        <p>审核状态:{{ statusEnum.find(item => item.value === scope.row.reviewType)?.label ?? "--" }}</p> </template
+      >:
       <!-- 表格操作 -->
       <template #operation="scope">
         <!-- 审批通过和拒绝按钮仅在状态为0时显示 -->
@@ -60,7 +60,7 @@
 </template>
 
 <script setup lang="tsx" name="groupPackageManagement">
-import { onActivated, onMounted, reactive, ref } from "vue";
+import { computed, onActivated, onMounted, reactive, ref, watch } from "vue";
 import { useRouter } from "vue-router";
 import type { FormInstance, FormRules } from "element-plus";
 import { ElMessage } from "element-plus";
@@ -68,6 +68,7 @@ import ProTable from "@/components/ProTable/index.vue";
 import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
 import { Plus } from "@element-plus/icons-vue";
 import { audit, getStaffConfigList } from "@/api/modules/staffConfig";
+import { getThaliList } from "@/api/modules/groupPackageManagement";
 
 const router = useRouter();
 const dialogFormVisible = ref(false);
@@ -78,7 +79,7 @@ const formInventory: any = ref({
   newInventory: ""
 });
 const rowData = ref<any>();
-const activeName = ref("0");
+const activeName = ref("");
 
 const ruleFormRef = ref<FormInstance>();
 const rules = reactive<FormRules<RuleForm>>({
@@ -92,11 +93,14 @@ const rules = reactive<FormRules<RuleForm>>({
   ]
 });
 const statusEnum = [
-  { value: "0", label: "待审核" },
-  { value: "1", label: "审核通过" },
-  { value: "2", label: "审核拒绝" }
+  { value: "-1", label: "待审核" },
+  { value: "-2", label: "审核通过" },
+  { value: "0", label: "审核驳回" }
 ];
 
+// ProTable 实例(需要在使用它的地方之前定义)
+const proTable = ref<ProTableInstance>();
+
 // 表格配置项
 const columns = reactive<ColumnProps<any>[]>([
   {
@@ -108,7 +112,7 @@ const columns = reactive<ColumnProps<any>[]>([
     }
   },
   {
-    prop: "tradeNo",
+    prop: "groupName",
     label: "团购名称",
     isShow: false,
     search: {
@@ -116,7 +120,7 @@ const columns = reactive<ColumnProps<any>[]>([
     }
   },
   {
-    prop: "goodsId",
+    prop: "groupNo",
     label: "团购编号",
     isShow: false,
     search: {
@@ -125,35 +129,41 @@ const columns = reactive<ColumnProps<any>[]>([
   },
   {
     prop: "Information",
-    label: "团购信息"
+    label: "团购信息",
+    width: 400
   },
   {
     prop: "goodsId",
     label: "已售"
   },
   {
-    prop: "goodsId",
-    isShow: false,
+    prop: "inventoryNum",
     label: "剩余库存"
   },
   {
     prop: "goodsId",
-    label: "优惠价"
+    label: "优惠价",
+    render: (scope: any) => {
+      return `¥${scope.row.preferentialPrice}`;
+    }
   },
   {
     prop: "goodsId",
-    label: "成本价"
+    label: "成本价",
+    render: (scope: any) => {
+      return `¥${scope.row.originalPrice}`;
+    }
   },
   {
     prop: "goodsId",
     label: "利润"
   },
   {
-    prop: "tradeStatus",
+    prop: "reviewType",
     label: "审核状态",
     isShow: false,
     render: scope => {
-      const statusObj = statusEnum.find(item => item.value === scope.row.tradeStatus);
+      const statusObj = statusEnum.find(item => item.value === scope.row.reviewType);
       return statusObj ? statusObj.label : "--";
     },
     search: {
@@ -170,23 +180,59 @@ const columns = reactive<ColumnProps<any>[]>([
 ]);
 
 // 在 script setup 中添加
-const tabOptions = [
-  { label: "全部", name: "0" },
-  { label: "草稿", name: "1" },
-  { label: "进行中", name: "2" },
-  { label: "未开始", name: "3" },
-  { label: "已下架", name: "4" },
-  { label: "已售罄", name: "5" },
-  { label: "已结束", name: "6" }
+const allTabOptions = [
+  { label: "全部", name: "" },
+  { label: "草稿", name: "0" },
+  { label: "进行中", name: "5" },
+  { label: "待审核", name: "1" },
+  { label: "审核拒绝", name: "3" },
+  { label: "已售罄", name: "4" },
+  { label: "已结束", name: "7" }
 ];
+
+// 获取当前审核状态
+const currentAuditStatus = computed(() => {
+  if (!proTable.value?.searchParam) return "";
+  return proTable.value.searchParam.reviewType || "";
+});
+
+// 控制 el-tabs 是否显示:当审核状态为空或审核通过时显示
+const showTabs = computed(() => {
+  const status = currentAuditStatus.value;
+  return !status || status === "-2";
+});
+
+// 根据审核状态过滤 tabOptions:如果审核状态为空,只显示草稿;审核通过时,显示除草稿外的所有标签页
+const filteredTabOptions = computed(() => {
+  const status = currentAuditStatus.value;
+  if (!status) {
+    // 审核状态为空时,只显示草稿
+    return allTabOptions;
+  } else if (status === "-2") {
+    // 审核通过时,显示除草稿外的所有标签页
+    return allTabOptions.filter(tab => tab.name !== "1");
+  }
+  return [];
+});
+
+// 监听审核状态变化
+watch(
+  currentAuditStatus,
+  newStatus => {
+    if (!newStatus || newStatus === "-2") {
+      // 审核状态为空时,确保 activeName 为草稿
+      activeName.value = "";
+    }
+  },
+  { immediate: true }
+);
 // 如果表格需要初始化请求参数,直接定义传给 ProTable (之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
 const initParam = reactive({
-  activeName: activeName
+  storeId: "104",
+  groupType: "1",
+  status: activeName
 });
 
-// ProTable 实例
-const proTable = ref<ProTableInstance>();
-
 // 页面加载时触发查询
 onMounted(() => {
   proTable.value?.getTableList();
@@ -210,17 +256,17 @@ const dataCallback = (data: any) => {
 // 默认不做操作就直接在 ProTable 组件上绑定	:requestApi="getUserList"
 const getTableList = (params: any) => {
   let newParams = JSON.parse(JSON.stringify(params));
-  return getStaffConfigList(newParams);
+  return getThaliList(newParams);
 };
 
 // 跳转详情页
 const toDetail = row => {
-  router.push(`/groupPackageManagement/detail?id=${row.id}`);
+  router.push(`/groupPackageManagement/detail?id=${row.id}&type=edit`);
 };
 const editRow = row => {};
 const deleteRow = row => {};
 const newGroupBuying = () => {
-  router.push(`/groupPackageManagement/newGroup?id=1`);
+  router.push(`/groupPackageManagement/newGroup?id=104&type=add`);
 };
 const handleClick = () => {};
 const changeTypes = (row: any, status: string) => {
@@ -289,4 +335,15 @@ const closeDialog = () => {
     }
   }
 }
+.information {
+  display: flex;
+  column-gap: 5px;
+  align-items: center;
+  .information-right {
+    display: flex;
+    flex-direction: column;
+    row-gap: 5px;
+    align-items: flex-start;
+  }
+}
 </style>

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 435 - 266
src/views/groupPackageManagement/newGroup.vue


+ 145 - 0
src/views/home/components/go-enter.vue

@@ -0,0 +1,145 @@
+<template>
+  <!-- 首页:流程概览 -->
+  <div v-if="currentStep === 0" class="home-entry">
+    <h3 class="title">免费入驻店铺</h3>
+    <div class="steps-container">
+      <el-steps align-center>
+        <el-step>
+          <template #title>
+            <div class="step-title-wrapper">
+              <span class="step-title">个人实名</span>
+              <span class="step-time">约3分钟</span>
+            </div>
+          </template>
+          <template #description>
+            <div class="step-desc">填写店铺经营者姓名、身份证号等</div>
+          </template>
+        </el-step>
+        <el-step>
+          <template #title>
+            <div class="step-title-wrapper">
+              <span class="step-title">填写信息</span>
+              <span class="step-time">约30分钟</span>
+            </div>
+          </template>
+          <template #description>
+            <div class="step-desc">上传营业执照及填写店铺信息等</div>
+          </template>
+        </el-step>
+        <el-step>
+          <template #title>
+            <div class="step-title-wrapper">
+              <span class="step-title">等待审核</span>
+              <span class="step-time">约1-3个工作日</span>
+            </div>
+          </template>
+          <template #description>
+            <div class="step-desc">平台进行资质审核</div>
+          </template>
+        </el-step>
+        <el-step>
+          <template #title>
+            <div class="step-title-wrapper">
+              <span class="step-title">入驻成功</span>
+            </div>
+          </template>
+          <template #description>
+            <div class="step-desc">入驻成功后即可管理您的店铺</div>
+          </template>
+        </el-step>
+      </el-steps>
+    </div>
+    <div class="button-container">
+      <el-button type="primary" size="large" class="register-btn" @click="handleRegister"> 去入驻 </el-button>
+    </div>
+  </div>
+</template>
+<script setup lang="ts">
+const props = defineProps({
+  currentStep: {
+    type: Number,
+    default: 0
+  }
+});
+
+const emit = defineEmits(["update:currentStep"]);
+
+// 处理入驻按钮
+const handleRegister = () => {
+  emit("update:currentStep", 1);
+};
+</script>
+
+<style scoped lang="scss">
+// 首页样式
+.home-entry {
+  padding: 30px 40px;
+  background: #ffffff;
+  border: 1px solid #e4e7ed;
+  border-radius: 8px;
+  box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
+  .title {
+    margin: 0 0 30px;
+    font-size: 20px;
+    font-weight: 600;
+    color: #333333;
+    text-align: center;
+  }
+  .steps-container {
+    margin-bottom: 40px;
+    :deep(.el-steps) {
+      .el-step__head {
+        .el-step__icon {
+          width: 30px;
+          height: 30px;
+          font-size: 16px;
+          font-weight: 600;
+        }
+      }
+      .el-step__title {
+        .step-title-wrapper {
+          display: flex;
+          flex-direction: column;
+          gap: 8px;
+          align-items: center;
+          .step-title {
+            font-size: 16px;
+            font-weight: 600;
+            line-height: 1.5;
+            color: #303133;
+          }
+          .step-time {
+            display: inline-block;
+            padding: 4px 12px;
+            font-size: 12px;
+            color: #909399;
+            white-space: nowrap;
+            background: #f5f7fa;
+            border-radius: 12px;
+          }
+        }
+      }
+      .el-step__description {
+        .step-desc {
+          margin-top: 8px;
+          font-size: 14px;
+          line-height: 1.5;
+          color: #606266;
+        }
+      }
+    }
+  }
+  .button-container {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    .register-btn {
+      width: 200px;
+      height: 44px;
+      font-size: 16px;
+      font-weight: 500;
+      border-radius: 4px;
+    }
+  }
+}
+</style>

+ 117 - 0
src/views/home/components/go-examine.vue

@@ -0,0 +1,117 @@
+<template>
+  <div class="go-examine">
+    <div class="panel">
+      <div class="panel-header">
+        <div class="store-name">
+          {{ storeName }}
+        </div>
+        <div class="expire">店铺到期时间:{{ expireDate }}</div>
+      </div>
+      <div class="verify-row">
+        <el-input v-model="voucherCode" placeholder="请输入劵码" class="verify-input" clearable />
+        <el-button type="primary" class="verify-btn" @click="handleVerify"> 验券 </el-button>
+      </div>
+    </div>
+
+    <div class="stats">
+      <div class="stat-card">
+        <div class="stat-title">店铺钱包(元)</div>
+        <div class="stat-value">
+          {{ walletAmount.toFixed(2) }}
+        </div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-title">今日订单量(单)</div>
+        <div class="stat-value">
+          {{ todayOrders }}
+        </div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-title">今日收益(元)</div>
+        <div class="stat-value">
+          {{ todayRevenue }}
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref } from "vue";
+
+// 数据(可根据实际接口替换)
+const storeName = ref("时间图书馆");
+const expireDate = ref("2027/10/29");
+const voucherCode = ref("");
+
+const walletAmount = ref(0);
+const todayOrders = ref(0);
+const todayRevenue = ref(0);
+
+const handleVerify = () => {
+  // 调用验券接口
+  if (!voucherCode.value) return;
+  // TODO: 接入后端验券逻辑
+  console.log("verify voucher:", voucherCode.value);
+};
+</script>
+
+<style scoped lang="scss">
+.go-examine {
+  .panel {
+    padding: 16px;
+    margin-bottom: 16px;
+    background: #ffffff;
+    border: 1px solid #e4e7ed;
+    border-radius: 6px;
+    .panel-header {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      margin-bottom: 14px;
+      .store-name {
+        font-size: 16px;
+        font-weight: 600;
+        color: #303133;
+      }
+      .expire {
+        font-size: 13px;
+        color: #606266;
+      }
+    }
+    .verify-row {
+      display: flex;
+      gap: 12px;
+      align-items: center;
+      .verify-input {
+        flex: 1;
+      }
+      .verify-btn {
+        width: 96px;
+      }
+    }
+  }
+  .stats {
+    display: grid;
+    grid-template-columns: repeat(3, 1fr);
+    gap: 20px;
+    .stat-card {
+      padding: 26px 12px;
+      text-align: center;
+      background: #ffffff;
+      border: 1px solid #e4e7ed;
+      border-radius: 6px;
+      .stat-title {
+        margin-bottom: 14px;
+        font-size: 14px;
+        color: #606266;
+      }
+      .stat-value {
+        font-size: 22px;
+        font-weight: 600;
+        color: #303133;
+      }
+    }
+  }
+}
+</style>

+ 470 - 0
src/views/home/components/go-flow.vue

@@ -0,0 +1,470 @@
+<template>
+  <!-- 第一步:个人实名 -->
+  <div v-if="currentStep === 1" class="form-container">
+    <el-button class="back-btn" @click="handleBack"> 返回 </el-button>
+
+    <!-- 进度条 -->
+    <div class="progress-container">
+      <el-steps align-center>
+        <el-step>
+          <template #title>
+            <div class="step-title-wrapper">
+              <span class="step-title">个人实名</span>
+              <span class="step-time">约3分钟</span>
+            </div>
+          </template>
+        </el-step>
+        <el-step>
+          <template #title>
+            <div class="step-title-wrapper">
+              <span class="step-title">填写信息</span>
+              <span class="step-time">约30分钟</span>
+            </div>
+          </template>
+        </el-step>
+        <el-step>
+          <template #title>
+            <div class="step-title-wrapper">
+              <span class="step-title">等待审核</span>
+              <span class="step-time">约1-3个工作日</span>
+            </div>
+          </template>
+        </el-step>
+        <el-step>
+          <template #title>
+            <div class="step-title-wrapper">
+              <span class="step-title">入驻成功</span>
+            </div>
+          </template>
+        </el-step>
+      </el-steps>
+    </div>
+
+    <!-- 表单 -->
+    <div class="form-content">
+      <el-form :model="step1Form" :rules="step1Rules" ref="step1FormRef" label-width="120px">
+        <el-form-item label="姓名" prop="name">
+          <el-input v-model="step1Form.name" placeholder="请输入姓名" style="width: 400px" />
+        </el-form-item>
+        <el-form-item label="身份证号码" prop="idNumber">
+          <el-input v-model="step1Form.idNumber" placeholder="请输入身份证号码" style="width: 400px" />
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <!-- 按钮 -->
+    <div class="form-actions">
+      <el-button type="primary" size="large" @click="handleNextStep"> 下一步 </el-button>
+    </div>
+  </div>
+  <!-- 第二步:填写信息 -->
+  <div v-if="currentStep === 2" class="form-container">
+    <el-button class="back-btn" @click="handleBack"> 返回 </el-button>
+
+    <!-- 进度条 -->
+    <div class="progress-container">
+      <el-steps :active="currentStep - 1" align-center>
+        <el-step>
+          <template #title>
+            <div class="step-title-wrapper">
+              <span class="step-title">个人实名</span>
+              <span class="step-time">约3分钟</span>
+            </div>
+          </template>
+        </el-step>
+        <el-step>
+          <template #title>
+            <div class="step-title-wrapper">
+              <span class="step-title">填写信息</span>
+              <span class="step-time">约30分钟</span>
+            </div>
+          </template>
+        </el-step>
+        <el-step>
+          <template #title>
+            <div class="step-title-wrapper">
+              <span class="step-title">等待审核</span>
+              <span class="step-time">约1-3个工作日</span>
+            </div>
+          </template>
+        </el-step>
+        <el-step>
+          <template #title>
+            <div class="step-title-wrapper">
+              <span class="step-title">入驻成功</span>
+            </div>
+          </template>
+        </el-step>
+      </el-steps>
+    </div>
+
+    <!-- 表单内容 -->
+    <div class="form-content step2-form">
+      <el-form :model="step2Form" :rules="step2Rules" ref="step2FormRef" label-width="125px">
+        <div class="form-row">
+          <!-- 左列 -->
+          <div class="form-col">
+            <el-form-item label="店铺名称" prop="storeName">
+              <el-input v-model="step2Form.storeName" placeholder="请输入店铺名称" />
+            </el-form-item>
+
+            <el-form-item label="容纳人数" prop="capacity">
+              <el-input-number v-model="step2Form.capacity" :min="1" :max="9999" />
+            </el-form-item>
+
+            <el-form-item label="门店电话" prop="storePhone">
+              <el-input v-model="step2Form.storePhone" placeholder="请输入门店电话" />
+            </el-form-item>
+
+            <el-form-item label="门店面积" prop="storeArea">
+              <el-radio-group v-model="step2Form.storeArea">
+                <el-radio label="小于20平米"> 小于20平米 </el-radio>
+                <el-radio label="20-50平米"> 20-50平米 </el-radio>
+                <el-radio label="50-100平米"> 50-100平米 </el-radio>
+                <el-radio label="100-300平米"> 100-300平米 </el-radio>
+                <el-radio label="300-500平米"> 300-500平米 </el-radio>
+                <el-radio label="500-1000平米"> 500-1000平米 </el-radio>
+                <el-radio label="大于1000平米"> 大于1000平米 </el-radio>
+              </el-radio-group>
+            </el-form-item>
+
+            <el-form-item label="所在地区" prop="location">
+              <el-select v-model="step2Form.province" placeholder="请选择" style="width: 150px; margin-right: 10px">
+                <el-option label="省份" value="province" />
+              </el-select>
+              <el-select v-model="step2Form.city" placeholder="请选择" style="width: 150px; margin-right: 10px">
+                <el-option label="城市" value="city" />
+              </el-select>
+              <el-select v-model="step2Form.district" placeholder="请选择" style="width: 150px">
+                <el-option label="区县" value="district" />
+              </el-select>
+            </el-form-item>
+
+            <el-form-item label="详细地址" prop="detailedAddress">
+              <el-input v-model="step2Form.detailedAddress" type="textarea" :rows="3" placeholder="请输入" />
+            </el-form-item>
+
+            <el-form-item label="门店简介" prop="storeIntro">
+              <el-input v-model="step2Form.storeIntro" type="textarea" :rows="3" placeholder="请输入" />
+            </el-form-item>
+
+            <el-form-item label="经营板块" prop="businessSector">
+              <el-radio-group v-model="step2Form.businessSector">
+                <el-radio label="美食"> 美食 </el-radio>
+                <el-radio label="酒店/民宿"> 酒店/民宿 </el-radio>
+                <el-radio label="KTV"> KTV </el-radio>
+                <el-radio label="洗浴汗蒸"> 洗浴汗蒸 </el-radio>
+                <el-radio label="按摩足疗"> 按摩足疗 </el-radio>
+                <el-radio label="丽人美发"> 丽人美发 </el-radio>
+                <el-radio label="运动健身"> 运动健身 </el-radio>
+                <el-radio label="医美医疗"> 医美医疗 </el-radio>
+              </el-radio-group>
+            </el-form-item>
+
+            <el-form-item label="经营种类" prop="businessType">
+              <el-checkbox-group v-model="step2Form.businessType">
+                <el-checkbox label="小吃快餐"> 小吃快餐 </el-checkbox>
+                <el-checkbox label="鱼鲜海鲜"> 鱼鲜海鲜 </el-checkbox>
+                <el-checkbox label="烧烤烤串"> 烧烤烤串 </el-checkbox>
+                <el-checkbox label="自助餐"> 自助餐 </el-checkbox>
+                <el-checkbox label="面包蛋糕甜品"> 面包蛋糕甜品 </el-checkbox>
+                <el-checkbox label="火锅"> 火锅 </el-checkbox>
+                <el-checkbox label="水果生鲜"> 水果生鲜 </el-checkbox>
+                <el-checkbox label="特色菜"> 特色菜 </el-checkbox>
+                <el-checkbox label="中餐"> 中餐 </el-checkbox>
+                <el-checkbox label="西餐"> 西餐 </el-checkbox>
+                <el-checkbox label="烤肉"> 烤肉 </el-checkbox>
+                <el-checkbox label="韩式料理"> 韩式料理 </el-checkbox>
+                <el-checkbox label="地方菜系"> 地方菜系 </el-checkbox>
+                <el-checkbox label="日式料理"> 日式料理 </el-checkbox>
+                <el-checkbox label="轻食"> 轻食 </el-checkbox>
+              </el-checkbox-group>
+            </el-form-item>
+          </div>
+
+          <!-- 右列 -->
+          <div class="form-col">
+            <el-form-item label="门店营业状态" prop="businessStatus">
+              <el-radio-group v-model="step2Form.businessStatus">
+                <el-radio label="正常营业"> 正常营业 </el-radio>
+                <el-radio label="暂停营业"> 暂停营业 </el-radio>
+                <el-radio label="筹建中"> 筹建中 </el-radio>
+              </el-radio-group>
+            </el-form-item>
+
+            <el-form-item label="经纬度查询" prop="coordinates">
+              <el-input v-model="step2Form.coordinates" placeholder="请输入经纬度" />
+            </el-form-item>
+
+            <el-form-item label="营业执照" prop="businessLicense">
+              <el-upload
+                v-model:file-list="step2Form.businessLicenseFiles"
+                action="#"
+                list-type="picture-card"
+                :limit="1"
+                :on-exceed="handleExceed"
+              >
+                <el-icon><Plus /></el-icon>
+                <template #tip>
+                  <div class="el-upload__tip">(0/1)</div>
+                </template>
+              </el-upload>
+            </el-form-item>
+
+            <el-form-item label="合同图片" prop="contractImages">
+              <el-upload
+                v-model:file-list="step2Form.contractImageFiles"
+                action="#"
+                list-type="picture-card"
+                :limit="20"
+                :on-exceed="handleExceed"
+              >
+                <el-icon><Plus /></el-icon>
+                <template #tip>
+                  <div class="el-upload__tip">(0/20)</div>
+                </template>
+              </el-upload>
+            </el-form-item>
+
+            <el-form-item label="食品经营许可证" prop="foodLicense">
+              <el-upload
+                v-model:file-list="step2Form.foodLicenseFiles"
+                action="#"
+                list-type="picture-card"
+                :limit="1"
+                :on-exceed="handleExceed"
+              >
+                <el-icon><Plus /></el-icon>
+                <template #tip>
+                  <div class="el-upload__tip">(0/1)</div>
+                </template>
+              </el-upload>
+            </el-form-item>
+          </div>
+        </div>
+      </el-form>
+    </div>
+
+    <!-- 按钮 -->
+    <div class="form-actions">
+      <el-button size="large" @click="handlePrevStep"> 上一步 </el-button>
+      <el-button type="primary" size="large" @click="handleSubmit"> 提交 </el-button>
+    </div>
+  </div>
+</template>
+<script setup lang="ts">
+import { ref, reactive, watch } from "vue";
+import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from "element-plus";
+import { Plus } from "@element-plus/icons-vue";
+
+const props = defineProps({
+  currentStep: {
+    type: Number,
+    value: 0
+  }
+});
+
+const emit = defineEmits(["update:currentStep"]);
+
+// 内部步骤状态,和父组件同步
+const currentStep = ref<number>(props.currentStep || 0);
+watch(
+  () => props.currentStep,
+  val => {
+    if (typeof val === "number") currentStep.value = val;
+  }
+);
+
+const setStep = (val: number) => {
+  currentStep.value = val;
+  emit("update:currentStep", val);
+};
+
+// 第一步表单
+const step1FormRef = ref<FormInstance>();
+const step1Form = reactive({
+  name: "朱丽",
+  idNumber: "231084199304282927"
+});
+
+const step1Rules: FormRules = {
+  name: [{ required: true, message: "请输入姓名", trigger: "blur" }],
+  idNumber: [
+    { required: true, message: "请输入身份证号码", trigger: "blur" },
+    {
+      pattern: /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/,
+      message: "请输入正确的身份证号码",
+      trigger: "blur"
+    }
+  ]
+};
+
+// 第二步表单
+const step2FormRef = ref<FormInstance>();
+const step2Form = reactive({
+  storeName: "",
+  capacity: 1,
+  storePhone: "",
+  storeArea: "小于20平米",
+  province: "",
+  city: "",
+  district: "",
+  detailedAddress: "",
+  storeIntro: "",
+  businessSector: "美食",
+  businessType: ["小吃快餐", "火锅"],
+  businessStatus: "正常营业",
+  coordinates: "",
+  businessLicenseFiles: [],
+  contractImageFiles: [],
+  foodLicenseFiles: []
+});
+
+const step2Rules: FormRules = {
+  storeName: [{ required: true, message: "请输入店铺名称", trigger: "blur" }],
+  storePhone: [{ required: true, message: "请输入门店电话", trigger: "blur" }],
+  storeArea: [{ required: true, message: "请选择门店面积", trigger: "change" }],
+  storeIntro: [{ required: true, message: "请输入门店简介", trigger: "blur" }],
+  businessSector: [{ required: true, message: "请选择经营板块", trigger: "change" }],
+  businessType: [{ required: true, message: "请选择经营种类", trigger: "change" }],
+  coordinates: [{ required: true, message: "请输入经纬度", trigger: "blur" }],
+  businessLicense: [{ required: true, message: "请上传营业执照", trigger: "change" }],
+  contractImages: [{ required: true, message: "请上传合同图片", trigger: "change" }],
+  foodLicense: [{ required: true, message: "请上传食品经营许可证", trigger: "change" }]
+};
+
+// 返回按钮
+const handleBack = () => {
+  if (currentStep.value === 1) {
+    setStep(0);
+  } else if (currentStep.value === 2) {
+    setStep(1);
+  }
+};
+
+// 下一步
+const handleNextStep = async () => {
+  if (!step1FormRef.value) return;
+
+  await step1FormRef.value.validate(valid => {
+    if (valid) {
+      setStep(2);
+    } else {
+      ElMessage.error("请完善表单信息");
+    }
+  });
+};
+
+// 上一步
+const handlePrevStep = () => {
+  setStep(1);
+};
+
+// 提交
+const handleSubmit = async () => {
+  if (!step2FormRef.value) return;
+
+  await step2FormRef.value.validate(valid => {
+    if (valid) {
+      ElMessageBox.confirm("确认提交入驻申请吗?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      })
+        .then(() => {
+          // 这里可以调用API提交数据
+          ElMessage.success("提交成功,等待审核");
+          setStep(3); // 跳转到等待审核步骤
+        })
+        .catch(() => {
+          // 取消提交
+        });
+    } else {
+      ElMessage.error("请完善表单信息");
+    }
+  });
+};
+
+// 文件上传超出限制
+const handleExceed = () => {
+  ElMessage.warning("文件数量超出限制");
+};
+</script>
+
+<style scoped lang="scss">
+// 表单页面样式
+.form-container {
+  min-height: calc(100vh - 100px);
+  padding: 30px;
+  background: #ffffff;
+  border-radius: 8px;
+  .back-btn {
+    margin-bottom: 30px;
+    color: #606266;
+    border-color: #dcdfe6;
+  }
+  .progress-container {
+    padding: 0 100px;
+    margin-bottom: 40px;
+    :deep(.el-steps) {
+      .el-step__head {
+        .el-step__icon {
+          width: 30px;
+          height: 30px;
+          font-size: 16px;
+          font-weight: 600;
+        }
+      }
+      .el-step__title {
+        .step-title-wrapper {
+          display: flex;
+          flex-direction: column;
+          gap: 8px;
+          align-items: center;
+          .step-title {
+            font-size: 16px;
+            font-weight: 600;
+            color: #303133;
+          }
+          .step-time {
+            display: inline-block;
+            padding: 4px 12px;
+            font-size: 12px;
+            color: #909399;
+            white-space: nowrap;
+            background: #f5f7fa;
+            border-radius: 12px;
+          }
+        }
+      }
+    }
+  }
+  .form-content {
+    max-width: 800px;
+    margin: 0 auto;
+    &.step2-form {
+      max-width: 1400px;
+      .form-row {
+        display: flex;
+        gap: 40px;
+        .form-col {
+          flex: 1;
+        }
+      }
+    }
+  }
+  .form-actions {
+    display: flex;
+    gap: 20px;
+    justify-content: center;
+    padding-top: 30px;
+    margin-top: 40px;
+    border-top: 1px solid #e4e7ed;
+    .el-button {
+      width: 150px;
+      height: 44px;
+      font-size: 16px;
+    }
+  }
+}
+</style>

+ 0 - 12
src/views/home/index.scss

@@ -1,12 +0,0 @@
-.home {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  width: 100%;
-  height: 100%;
-  .home-bg {
-    width: 70%;
-    max-width: 1200px;
-    margin-bottom: 20px;
-  }
-}

+ 29 - 4
src/views/home/index.vue

@@ -1,11 +1,36 @@
 <template>
-  <div class="home card">
-    <img class="home-bg" src="@/assets/images/welcome.png" alt="welcome" />
+  <div id="home">
+    <!-- 第一步  未入驻 -->
+    <go-enter :current-step="currentStep" @update:current-step="handleUpdateCurrentStep" v-if="userInfo == 0" />
+
+    <!--已入驻-->
+    <go-examine v-if="userInfo == 1" />
+
+    <!-- 第二步 -->
+    <go-flow :current-step="currentStep" @update:current-step="handleUpdateCurrentStep" />
   </div>
 </template>
 
-<script setup lang="ts" name="home"></script>
+<script setup lang="ts">
+import { ref } from "vue";
+
+import goEnter from "./components/go-enter.vue";
+import goFlow from "./components/go-flow.vue";
+import goExamine from "./components/go-examine.vue";
+const userInfo = ref(1);
+
+// 当前步骤:0-首页,1-第一步,2-第二步
+const currentStep = ref(0);
+
+// 处理更新currentStep事件
+const handleUpdateCurrentStep = (step: number) => {
+  currentStep.value = step;
+};
+</script>
 
 <style scoped lang="scss">
-@import "./index";
+#home {
+  min-height: calc(100vh - 100px);
+  padding: 20px;
+}
 </style>

+ 0 - 157
src/views/home/toDoItem/components/curve.vue

@@ -1,157 +0,0 @@
-<template>
-  <div class="echarts">
-    <ECharts :option="option" />
-  </div>
-</template>
-
-<script setup lang="ts" name="cure">
-import { ECOption } from "@/components/ECharts/config";
-import ECharts from "@/components/ECharts/index.vue";
-
-const curveData = [
-  { value: 30, spotName: "掘金" },
-  { value: 90, spotName: "CSDN" },
-  { value: 10, spotName: "Gitee" },
-  { value: 70, spotName: "GitHub" },
-  { value: 20, spotName: "知乎" },
-  { value: 60, spotName: "MyBlog" },
-  { value: 55, spotName: "简书" },
-  { value: 80, spotName: "StackOverFlow" },
-  { value: 50, spotName: "博客园" }
-];
-
-const option: ECOption = {
-  tooltip: {
-    trigger: "axis",
-    backgroundColor: "transparent",
-    axisPointer: {
-      type: "none"
-    },
-    padding: 0,
-    formatter: (p: any) => {
-      let dom = `<div style="width:100%; height: 70px !important; display:flex;flex-direction: column;justify-content: space-between;padding:10px;box-sizing: border-box;
-      color:#fff; background: #6B9DFE;border-radius: 4px;font-size:14px; ">
-        <div style="display: flex; align-items: center;"> <div style="width:5px;height:5px;background:#ffffff;border-radius: 50%;margin-right:5px"></div>平台 :  ${p[0].name}</div>
-        <div style="display: flex;align-items: center;"><div style="width:5px;height:5px;background:#ffffff;border-radius: 50%;margin-right:5px"></div>数据量 :  ${p[0].value}</div>
-      </div>`;
-      return dom;
-    }
-  },
-  toolbox: {
-    show: true,
-    orient: "horizontal"
-  },
-  grid: {
-    left: "0",
-    right: "0"
-  },
-  dataZoom: [
-    {
-      show: false,
-      height: 10,
-      xAxisIndex: [0],
-      bottom: 0,
-      startValue: 0,
-      endValue: 9,
-      handleStyle: {
-        color: "#6b9dfe"
-      },
-      textStyle: {
-        color: "transparent"
-      }
-    },
-    {
-      type: "inside",
-      show: true,
-      height: 0,
-      zoomLock: true
-    }
-  ],
-  xAxis: [
-    {
-      type: "category",
-      data: curveData.map((val: any) => {
-        return {
-          value: val.spotName
-        };
-      }),
-      axisTick: {
-        show: false
-      },
-      axisLabel: {
-        margin: 20,
-        interval: 0,
-        color: "#a1a1a1",
-        fontSize: 14,
-        formatter: function (name: string) {
-          undefined;
-          return name.length > 8 ? name.slice(0, 8) + "..." : name;
-        }
-      },
-      axisLine: {
-        lineStyle: {
-          color: "#c0c0c0"
-        }
-      }
-    }
-  ],
-  yAxis: [
-    {
-      min: 0,
-      axisLine: {
-        show: false
-      },
-      axisTick: {
-        show: false
-      },
-      splitLine: {
-        show: true,
-        lineStyle: {
-          color: "#c0c0c0"
-        }
-      },
-      axisLabel: {
-        color: "#a1a1a1",
-        fontSize: 16,
-        fontWeight: 400,
-        formatter: function (value: number) {
-          if (value === 0) {
-            return value + "";
-          } else if (value >= 10000) {
-            return value / 10000 + "w";
-          }
-          return value + "";
-        }
-      }
-    }
-  ],
-  series: [
-    {
-      name: "Direct",
-      type: "bar",
-      data: curveData.map((val: any) => {
-        return {
-          value: val.value
-        };
-      }),
-      barWidth: "45px",
-      itemStyle: {
-        color: "#C5D8FF",
-        borderRadius: [12, 12, 0, 0]
-      },
-      emphasis: {
-        itemStyle: {
-          color: "#6B9DFE"
-        }
-      }
-    }
-  ]
-};
-</script>
-
-<style lang="scss" scoped>
-.echarts {
-  width: 100%;
-  height: 100%;
-}
-</style>

+ 0 - 131
src/views/home/toDoItem/components/pie.vue

@@ -1,131 +0,0 @@
-<template>
-  <div class="echarts">
-    <ECharts :option="option" />
-  </div>
-</template>
-
-<script setup lang="ts" name="pie">
-import { ECOption } from "@/components/ECharts/config";
-import ECharts from "@/components/ECharts/index.vue";
-
-const pieData = [
-  { value: 5000, name: "Gitee 访问量" },
-  { value: 5000, name: "GitHub 访问量" }
-];
-
-const option: ECOption = {
-  title: {
-    text: "Gitee / GitHub",
-    subtext: "访问占比",
-    left: "56%",
-    top: "45%",
-    textAlign: "center",
-    textStyle: {
-      fontSize: 18,
-      color: "#767676"
-    },
-    subtextStyle: {
-      fontSize: 15,
-      color: "#a1a1a1"
-    }
-  },
-  tooltip: {
-    trigger: "item"
-  },
-  legend: {
-    top: "4%",
-    left: "2%",
-    orient: "vertical",
-    icon: "circle", //图例形状
-    align: "left",
-    itemGap: 20,
-    textStyle: {
-      fontSize: 13,
-      color: "#a1a1a1",
-      fontWeight: 500
-    },
-    formatter: function (name: string) {
-      let dataCopy = "";
-      for (let i = 0; i < pieData.length; i++) {
-        if (pieData[i].name == name && pieData[i].value >= 10000) {
-          dataCopy = (pieData[i].value / 10000).toFixed(2);
-          return name + "      " + dataCopy + "w";
-        } else if (pieData[i].name == name) {
-          dataCopy = pieData[i].value + "";
-          return name + "      " + dataCopy;
-        }
-      }
-      return "";
-    }
-  },
-  series: [
-    {
-      type: "pie",
-      radius: ["70%", "40%"],
-      center: ["57%", "52%"],
-      silent: true,
-      clockwise: true,
-      startAngle: 150,
-      data: pieData,
-      labelLine: {
-        length: 80,
-        length2: 30,
-        lineStyle: {
-          width: 1
-        }
-      },
-      label: {
-        position: "outside",
-        show: true,
-        formatter: "{d}%",
-        fontWeight: 400,
-        fontSize: 19,
-        color: "#a1a1a1"
-      },
-      color: [
-        {
-          type: "linear",
-          x: 0,
-          y: 0,
-          x2: 0.5,
-          y2: 1,
-          colorStops: [
-            {
-              offset: 0,
-              color: "#feb791" // 0% 处的颜色
-            },
-            {
-              offset: 1,
-              color: "#fe8b4c" // 100% 处的颜色
-            }
-          ]
-        },
-        {
-          type: "linear",
-          x: 0,
-          y: 0,
-          x2: 1,
-          y2: 0.5,
-          colorStops: [
-            {
-              offset: 0,
-              color: "#b898fd" // 0% 处的颜色
-            },
-            {
-              offset: 1,
-              color: "#8347fd" // 100% 处的颜色
-            }
-          ]
-        }
-      ]
-    }
-  ]
-};
-</script>
-
-<style lang="scss" scoped>
-.echarts {
-  width: 100%;
-  height: 100%;
-}
-</style>

BIN
src/views/home/toDoItem/images/1-bg.png


BIN
src/views/home/toDoItem/images/2-bg.png


BIN
src/views/home/toDoItem/images/3-bg.png


BIN
src/views/home/toDoItem/images/4-bg.png


BIN
src/views/home/toDoItem/images/add_person.png


BIN
src/views/home/toDoItem/images/add_team.png


BIN
src/views/home/toDoItem/images/book-bg.png


BIN
src/views/home/toDoItem/images/book-sum.png


BIN
src/views/home/toDoItem/images/book_sum.png


BIN
src/views/home/toDoItem/images/today.png


+ 0 - 154
src/views/home/toDoItem/index.scss

@@ -1,154 +0,0 @@
-.dataVisualize-box {
-  .top-box {
-    box-sizing: border-box;
-    padding: 25px 40px 0;
-    margin-bottom: 10px;
-    .top-title {
-      margin-bottom: 10px;
-      font-family: DIN;
-      font-size: 18px;
-      font-weight: bold;
-    }
-    .top-content {
-      margin-top: 10px;
-      .item-left {
-        box-sizing: border-box;
-        height: 100%;
-        padding: 40px 0 30px 30px;
-        overflow: hidden;
-        color: #ffffff;
-        background: url("./images/book-bg.png");
-        background-position: 50%;
-        background-size: cover;
-        border-radius: 20px;
-        .left-title {
-          font-family: DIN;
-          font-size: 20px;
-        }
-        .img-box {
-          display: flex;
-          align-items: center;
-          justify-content: center;
-          width: 90px;
-          height: 90px;
-          margin: 40px 0 20px;
-          background-color: #ffffff;
-          border-radius: 20px;
-          box-shadow: 0 10px 20px rgb(0 0 0 / 14%);
-          img {
-            width: 60px;
-            height: 65px;
-          }
-        }
-        .left-number {
-          overflow: hidden;
-          font-family: DIN;
-          font-size: 62px;
-        }
-      }
-      .item-center {
-        display: flex;
-        place-content: center space-around;
-        height: 100%;
-        .traffic-box {
-          box-sizing: border-box;
-          display: flex;
-          flex-direction: column;
-          width: 47%;
-          height: 48%;
-          padding: 25px;
-          margin: 10px 20px;
-          border-radius: 30px;
-          .traffic-img {
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            width: 70px;
-            height: 70px;
-            margin-bottom: 10px;
-            background-color: #ffffff;
-            border-radius: 19px;
-          }
-        }
-        img {
-          width: 33px;
-          height: 33px;
-        }
-        .item-value {
-          margin-bottom: 4px;
-          font-family: DIN;
-          font-size: 28px;
-          font-weight: bold;
-          color: #1a1a37;
-        }
-        .traffic-name {
-          overflow: hidden;
-          font-family: DIN;
-          font-size: 15px;
-          color: #1a1a37;
-          white-space: nowrap;
-        }
-        .gitee-traffic {
-          background: url("./images/1-bg.png");
-          background-color: #e8faea;
-          background-size: 100% 100%;
-        }
-        .gitHub-traffic {
-          background: url("./images/2-bg.png");
-          background-color: #e7e1fb;
-          background-size: 100% 100%;
-        }
-        .today-traffic {
-          background: url("./images/3-bg.png");
-          background-color: #fdf3e9;
-          background-size: 100% 100%;
-        }
-        .yesterday-traffic {
-          background: url("./images/4-bg.png");
-          background-color: #f0f5fb;
-          background-size: 100% 100%;
-        }
-      }
-      .item-right {
-        box-sizing: border-box;
-        display: flex;
-        flex-direction: column;
-        width: 100%;
-        height: 430px;
-        border: 1px solid var(--el-border-color);
-        border-radius: 25px;
-        .echarts-title {
-          padding: 15px 20px;
-          font-family: DIN;
-          font-size: 18px;
-          font-weight: bold;
-          border-bottom: 1px solid var(--el-border-color);
-        }
-        .book-echarts {
-          flex: 1;
-          width: 100%;
-        }
-      }
-    }
-  }
-  .bottom-box {
-    position: relative;
-    padding: 20px 0 0;
-    .bottom-title {
-      position: absolute;
-      top: 75px;
-      left: 50px;
-      font-family: DIN;
-      font-size: 18px;
-      font-weight: bold;
-    }
-    .bottom-tabs {
-      padding: 0 50px;
-    }
-    .curve-echarts {
-      box-sizing: border-box;
-      height: 400px;
-      padding: 0 50px;
-    }
-  }
-}

+ 0 - 442
src/views/home/toDoItem/index.vue

@@ -1,442 +0,0 @@
-<template>
-  <div class="dataVisualize-box">
-    <div class="card top-box">
-      <div class="top-title">工作台</div>
-      <div class="top-content">
-        <div class="item-center">
-          <div class="gitee-traffic traffic-box">
-            <div class="traffic-img">
-              <img src="./images/add_person.png" alt="" />
-            </div>
-            <span class="item-value">{{ auditSums["店铺入驻"] || 0 }}</span>
-            <span class="traffic-name sle">待审核商铺</span>
-          </div>
-          <div class="today-traffic traffic-box">
-            <div class="traffic-img">
-              <img src="./images/today.png" alt="" />
-            </div>
-            <span class="item-value">{{ auditSums["商家申诉"] || 0 }}</span>
-            <span class="traffic-name sle">待审核商家申诉</span>
-          </div>
-          <div class="yesterday-traffic traffic-box">
-            <div class="traffic-img">
-              <img src="./images/book_sum.png" alt="" />
-            </div>
-            <span class="item-value">{{ auditSums["套餐"] || 0 }}</span>
-            <span class="traffic-name sle">待审核套餐</span>
-          </div>
-          <div class="gitHub-traffic traffic-box">
-            <div class="traffic-img">
-              <img src="./images/add_team.png" alt="" />
-            </div>
-            <span class="item-value">{{ auditSums["代金券"] || 0 }}</span>
-            <span class="traffic-name sle">代金券审核</span>
-          </div>
-          <div class="yesterday-traffic traffic-box">
-            <div class="traffic-img">
-              <img src="./images/book_sum.png" alt="" />
-            </div>
-            <span class="item-value">{{ auditSums["经营许可证"] || 0 }}</span>
-            <span class="traffic-name sle">待食品许可证审批</span>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <div class="card top-box">
-      <el-tabs v-model="tabActive" class="demo-tabs">
-        <el-tab-pane v-for="item in tab" :key="item.name" :label="item.label" :name="item.name" />
-      </el-tabs>
-      <div class="card top-box">
-        <el-table :data="tableData" height="250">
-          <el-table-column type="index" label="序号" width="80" />
-          <el-table-column prop="content" label="内容" />
-          <el-table-column prop="type" label="类型" />
-          <el-table-column prop="createdTime" label="提交时间" />
-          <el-table-column prop="status" label="状态" />
-          <el-table-column label="操作" fixed="right" width="120">
-            <template #default="scope">
-              <el-button type="primary" link @click="handleAudit(scope.row)"> 通过 </el-button>
-              <el-button type="primary" link @click="handleAudit2(scope.row)"> 拒绝 </el-button>
-            </template>
-          </el-table-column>
-        </el-table>
-      </div>
-      <!-- 分页组件 -->
-      <div class="pagination-container">
-        <el-pagination
-          v-if="total > 0"
-          :current-page="currentPage"
-          :page-size="pageSize"
-          :total="total"
-          :page-sizes="[5, 10, 15, 20]"
-          @current-change="handlePageChange"
-          @size-change="handleSizeChange"
-          background
-          layout="total, sizes, prev, pager, next, jumper"
-        />
-      </div>
-    </div>
-    <!-- 拒接弹出框 -->
-    <el-dialog v-model="dialogFormVisible" title="审核拒接" width="500">
-      <el-form :model="form">
-        <el-form-item>
-          <el-input v-model="form.reason" autocomplete="off" style="width: 500px" type="textarea" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button @click="dialogFormVisible = false"> 取消 </el-button>
-          <el-button type="primary" @click="handleOk"> 确定 </el-button>
-        </div>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup lang="tsx" name="useProTable">
-import { ref, reactive, onMounted, onActivated, watch } from "vue";
-import { useRoute, useRouter } from "vue-router";
-import { Audit } from "@/api/interface";
-import { ElMessage, ElMessageBox } from "element-plus";
-import { getAuditList, getAuditSum } from "@/api/modules/audit";
-import { approveStoreInfo } from "@/api/modules/storeUser";
-import { consentComplaintFun, rejectClaimFun } from "@/api/modules/storeCommentAppeal";
-import { updateStatus } from "@/api/modules/groupPackage";
-import { changeStatus } from "@/api/modules/groupPackage";
-import { updatefoodLicenceImageStatus } from "@/api/modules/storeUser";
-
-const dialogFormVisible = ref(false);
-const router = useRouter();
-const tabActive = ref(1);
-const form = ref({
-  reason: ""
-});
-// 分页相关响应式数据
-const currentPage = ref(1);
-const pageSize = ref(10);
-const total = ref(0);
-
-const route = useRoute();
-
-const tab = [
-  { label: "商铺入驻", name: 1, type: "1" },
-  { label: "审核商家申诉", name: 4, type: "3" },
-  { label: "审核套餐", name: 5, type: "4" },
-  { label: "代金券审核", name: 7, type: "6" },
-
-  // { label: "审核活动", name: 6, type: "5" }
-  { label: "食品许可证审批", name: 8, type: "7" }
-];
-
-const tableData = ref<any[]>([]);
-const auditSums = ref<{
-  [key: string]: number;
-}>({});
-
-// 封装参数处理函数
-const processParams = (params: any) => {
-  let newParams = JSON.parse(JSON.stringify(params));
-  delete newParams.createTime;
-  delete newParams.pageNum;
-  delete newParams.pageSize;
-  newParams.status = newParams.status || "";
-  newParams.type = newParams.type || "";
-  newParams.page = newParams.page || 1;
-  newParams.size = newParams.size || 10;
-  newParams.classBigType = newParams.classBigType || "";
-  newParams.classTeacher = newParams.classTeacher || "";
-  newParams.className = newParams.className || "";
-  return newParams;
-};
-
-const getTableList = async (params: any) => {
-  const newParams = processParams(params);
-  try {
-    const res = await getAuditList(newParams);
-    return res;
-  } catch (error) {
-    console.error("getTableList - 获取审核列表失败:", error);
-    return { records: [], total: 0 };
-  }
-};
-
-// 统一数据加载方法
-const loadData = async () => {
-  try {
-    // 获取当前标签页的类型
-    const currentTab = tab.find(item => item.name === tabActive.value);
-    const type = currentTab ? currentTab.type : "1";
-
-    const res = await getTableList({
-      page: currentPage.value,
-      size: pageSize.value,
-      type: type
-    });
-
-    // 检查响应数据格式
-    if (res.data && res.data.current && res.data.size && res.data.total) {
-      // 更新分页数据
-      currentPage.value = res.data.current;
-      pageSize.value = res.data.size;
-      total.value = res.data.total;
-
-      // 更新表格数据
-      tableData.value = res.data.records || [];
-    } else {
-      console.error("loadData - 响应数据格式不正确:", res);
-      tableData.value = [];
-      total.value = 0;
-    }
-  } catch (error) {
-    console.error("loadData - 获取审核列表失败:", error);
-    tableData.value = [];
-    total.value = 0;
-  }
-};
-
-// 页码变化处理
-const handlePageChange = (newPage: number) => {
-  currentPage.value = newPage;
-  loadData();
-};
-
-// 每页数量变化处理
-const handleSizeChange = (newSize: number) => {
-  pageSize.value = newSize;
-  currentPage.value = 1;
-  loadData();
-};
-
-const getTobList = async () => {
-  try {
-    // 初始化所有类型为0
-    auditSums.value = {
-      店铺入驻: 0,
-      商家申诉: 0,
-      套餐: 0,
-      代金券: 0,
-      食品许可证审批: 0
-    };
-    const resp = await getAuditSum({});
-    // 检查响应数据格式
-    if (resp.code === 200 && resp.data) {
-      resp.data.forEach(item => {
-        const type = item.type;
-        const typeSum = parseInt(item.typeSum, 10);
-        auditSums.value[type] = typeSum;
-      });
-    } else {
-      console.error("getTobList - 响应数据格式不正确:", resp);
-      ElMessage.error("获取审核统计数据失败,响应数据格式不正确");
-    }
-    return resp;
-  } catch (error) {}
-};
-
-// 审核通过操作
-const handleAudit = (row: any) => {
-  ElMessageBox.confirm("确定要通过该审核吗?", "审核确认", {
-    confirmButtonText: "确定",
-    cancelButtonText: "取消",
-    type: "warning"
-  })
-    .then(() => {
-      // 同意
-      // 商铺入驻
-      if (tabActive.value == 1) {
-        approveStoreInfo({
-          id: row.storeInfoId,
-          approvalStatus: 1
-        }).then(res => {
-          if (res.code === 200) {
-            ElMessage.success("审核通过成功");
-            loadData();
-            getTobList();
-          }
-        });
-      } else if (tabActive.value == 4) {
-        //  审核商家申诉
-        consentComplaintFun({
-          id: row.storeCommentAppealId,
-          appealStatus: "2"
-        }).then(res => {
-          if (res.code === 200) {
-            ElMessage.success("审核通过成功");
-            loadData();
-            getTobList();
-          }
-        });
-      } else if (tabActive.value == 5) {
-        // 审核套餐
-        updateStatus({
-          id: row.lifeGroupPackageId,
-          status: 2,
-          approvalComments: ""
-        }).then(res => {
-          if (res.code === 200) {
-            ElMessage.success("审核通过成功");
-            loadData();
-            getTobList();
-          }
-        });
-      } else if (tabActive.value == 7) {
-        // 代金券审核
-        changeStatus({
-          id: row.couponId,
-          status: 5,
-          comment: "",
-          type: 1
-        }).then(res => {
-          if (res.code === 200) {
-            ElMessage.success("审核通过成功");
-            loadData();
-            getTobList();
-          }
-        });
-      } else if (tabActive.value == 8) {
-        // 许可证审核
-        updatefoodLicenceImageStatus({
-          id: row.storeInfoId,
-          foodLicenceStatus: 1,
-          foodLicenceReason: ""
-        }).then(res => {
-          if (res.code === 200) {
-            ElMessage.success("审核通过成功");
-            loadData();
-            getTobList();
-          }
-        });
-      }
-    })
-    .catch(() => {
-      ElMessage.info("已取消操作");
-    });
-};
-const rowValue = ref({});
-// 驳回操作
-const handleOk = () => {
-  if (form.value.reason) {
-    dialogFormVisible.value = false;
-    if (tabActive.value == 1) {
-      approveStoreInfo({
-        id: rowValue.value.storeInfoId,
-        approvalStatus: 2,
-        reason: form.value.reason
-      }).then(res => {
-        if (res.code === 200) {
-          handleCs();
-          loadData();
-          getTobList();
-        }
-      });
-    } else if (tabActive.value == 4) {
-      //  审核商家申诉
-      rejectClaimFun({
-        id: rowValue.value.storeCommentAppealId,
-        appealStatus: "1",
-        logRemark: form.value.reason
-      }).then(res => {
-        if (res.code === 200) {
-          handleCs();
-          loadData();
-          getTobList();
-        }
-      });
-    } else if (tabActive.value == 5) {
-      // 审核套餐
-      updateStatus({
-        id: rowValue.value.lifeGroupPackageId,
-        status: 3,
-        approvalComments: form.value.reason
-      }).then(res => {
-        if (res.code === 200) {
-          handleCs();
-          loadData();
-          getTobList();
-        }
-      });
-    } else if (tabActive.value == 7) {
-      // 代金券审核
-      changeStatus({
-        id: rowValue.value.couponId,
-        status: 3,
-        comment: form.value.reason,
-        type: 1
-      }).then(res => {
-        if (res.code === 200) {
-          handleCs();
-          loadData();
-          getTobList();
-        }
-      });
-    } else if (tabActive.value == 8) {
-      // 许可证审核
-      updatefoodLicenceImageStatus({
-        id: rowValue.value.storeInfoId,
-        foodLicenceStatus: 3,
-        foodLicenceReason: form.value.reason
-      }).then(res => {
-        if (res.code === 200) {
-          ElMessage.success("审核通过成功");
-          loadData();
-          getTobList();
-        }
-      });
-    }
-  } else {
-    ElMessage.error("请填写审核拒绝原因!");
-  }
-};
-const handleCs = () => {
-  ElMessage.success("审核拒接成功!");
-  form.value.reason = "";
-};
-// 审核拒绝操作
-const handleAudit2 = (row: any) => {
-  rowValue.value = row;
-  dialogFormVisible.value = true;
-};
-
-// 监听 tabActive 变化
-watch(tabActive, async (newVal, oldVal) => {
-  if (newVal !== oldVal) {
-    // 重置分页状态
-    currentPage.value = 1;
-    total.value = 0;
-
-    // 加载新数据
-    getTobList();
-    await loadData();
-  }
-});
-
-// 页面加载时触发查询
-onMounted(async () => {
-  await loadData();
-  await getTobList();
-});
-
-// 从其他页面返回时触发查询
-onActivated(async () => {
-  await loadData();
-});
-
-// 表格配置项
-const columns = reactive<ColumnProps<Audit.ResAuditList>[]>([
-  { prop: "index", label: "序号", type: "index", width: 100 },
-  { prop: "content", label: "内容", search: { el: "input" } },
-  { prop: "type", label: "类型" },
-  { prop: "createdTime", label: "提交时间" },
-  { prop: "status", label: "状态" },
-  { prop: "operation", label: "操作", fixed: "right", width: 120 }
-]);
-</script>
-<style scoped lang="scss">
-@import "./index";
-.pagination-container {
-  display: flex;
-  justify-content: flex-end;
-  padding: 20px;
-}
-</style>

+ 111 - 0
src/views/home/userInfo.vue

@@ -0,0 +1,111 @@
+<template>
+  <div class="user-info-page">
+    <div class="top-bar">
+      <el-button size="small" @click="handleBack"> 返回 </el-button>
+      <div class="title">个人信息</div>
+    </div>
+
+    <div class="form-wrap">
+      <el-form :model="form" :rules="rules" ref="formRef" label-width="72px">
+        <el-form-item label="头像">
+          <el-upload
+            class="avatar-uploader"
+            action="#"
+            list-type="picture-card"
+            :limit="1"
+            :on-exceed="onExceed"
+            v-model:file-list="form.avatarList"
+          >
+            <el-icon><Plus /></el-icon>
+          </el-upload>
+        </el-form-item>
+
+        <el-form-item label="昵称" prop="nickname">
+          <el-input v-model="form.nickname" placeholder="请输入昵称" />
+        </el-form-item>
+
+        <el-form-item label="简介" prop="intro">
+          <el-input v-model="form.intro" type="textarea" :rows="6" placeholder="请输入简介" />
+        </el-form-item>
+
+        <div class="actions">
+          <el-button type="primary" class="save-btn" @click="handleSave"> 保存 </el-button>
+        </div>
+      </el-form>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive } from "vue";
+import { ElMessage, type FormInstance, type FormRules } from "element-plus";
+import { Plus } from "@element-plus/icons-vue";
+
+const formRef = ref<FormInstance>();
+const form = reactive({
+  avatarList: [] as any[],
+  nickname: "重庆老火锅",
+  intro: "这——家优秀的火锅店"
+});
+
+const rules: FormRules = {
+  nickname: [{ required: true, message: "请输入昵称", trigger: "blur" }],
+  intro: [{ required: true, message: "请输入简介", trigger: "blur" }]
+};
+
+const onExceed = () => {
+  ElMessage.warning("仅允许上传一张头像");
+};
+
+const handleBack = () => {
+  history.back();
+};
+
+const handleSave = async () => {
+  if (!formRef.value) return;
+  await formRef.value.validate(valid => {
+    if (!valid) return;
+    // TODO: 接口保存
+    ElMessage.success("保存成功");
+  });
+};
+</script>
+
+<style scoped lang="scss">
+.user-info-page {
+  padding: 16px;
+  .top-bar {
+    display: flex;
+    gap: 12px;
+    align-items: center;
+    .title {
+      flex: 1;
+      margin-right: 48px; // 平衡左侧返回按钮的宽度
+      font-size: 18px;
+      font-weight: 600;
+      color: #303133;
+      text-align: center;
+    }
+  }
+  .form-wrap {
+    max-width: 640px;
+    margin-top: 16px;
+    .avatar-uploader {
+      :deep(.el-upload--picture-card) {
+        width: 96px;
+        height: 96px;
+      }
+    }
+    :deep(.el-form-item__label) {
+      color: #303133;
+    }
+    .actions {
+      margin-top: 24px;
+      text-align: center;
+      .save-btn {
+        width: 140px;
+      }
+    }
+  }
+}
+</style>

+ 203 - 99
src/views/orderManagement/index.vue

@@ -3,78 +3,204 @@
     <ProTable ref="proTable" :columns="columns" :request-api="getTableList" :init-param="initParam" :data-callback="dataCallback">
       <!-- 表格 header 按钮 -->
       <template #tableHeader="scope">
-        <el-button type="primary" :icon="Download" @click="exportInfoExcel(scope)"> 导出 </el-button>
-      </template>
-      <template #expand="scope">
-        {{ scope.row }}
+        <div class="table-header-btn">
+          <el-button :icon="Plus" class="button" type="primary" @click="newGroupBuying"> 新建代金券 </el-button>
+          <el-tabs v-if="showTabs" v-model="activeName" class="tabs" @tab-click="handleClick">
+            <el-tab-pane v-for="tab in filteredTabOptions" :key="tab.name" :label="tab.label" :name="tab.name" />
+          </el-tabs>
+        </div>
       </template>
+      <template #status="scope">
+        <p>团购状态:{{ allTabOptions.find(item => item.name === scope.row.groupType)?.label ?? "--" }}</p>
+        <p>审核状态:{{ statusEnum.find(item => item.value === scope.row.reviewType)?.label ?? "--" }}</p> </template
+      >:
       <!-- 表格操作 -->
       <template #operation="scope">
         <!-- 审批通过和拒绝按钮仅在状态为0时显示 -->
         <template v-if="scope.row.status === '0'">
-          <el-button type="primary" link @click="changeTypes(scope.row, 'pass')"> 审核通过 </el-button>
-          <el-button type="primary" link @click="changeTypes(scope.row, '')"> 审核拒绝 </el-button>
+          <el-button link type="primary" @click="changeTypes(scope.row, 'on')"> 上架 </el-button>
+          <el-button link type="primary" @click="changeTypes(scope.row, 'off')"> 下架 </el-button>
+          <el-button link type="primary" @click="changeInventory(scope.row)"> 修改库存 </el-button>
         </template>
         <el-button type="primary" link @click="toDetail(scope.row)"> 查看详情 </el-button>
+        <el-button link type="primary" @click="editRow(scope.row)"> 编辑 </el-button>
+        <el-button link type="primary" @click="deleteRow(scope.row)"> 删除 </el-button>
       </template>
     </ProTable>
-
-    <el-dialog v-model="dialogFormVisible" title="审核拒绝" width="500">
-      <el-form :model="form">
-        <el-form-item label="" label-width="0">
-          <el-input v-model="form.comment" autocomplete="off" type="textarea" maxlength="200" />
+    <el-dialog v-model="dialogFormVisible" title="修改库存" width="500">
+      <el-form ref="ruleFormRef" :model="formInventory" :rules="rules" @submit.prevent>
+        <el-form-item label="套餐名">
+          {{ formInventory.packageName }}
+        </el-form-item>
+        <el-form-item label="剩余库存">
+          {{ formInventory.remainingInventory }}
+        </el-form-item>
+        <el-form-item label="修改库存" prop="newInventory">
+          <el-input v-model="formInventory.newInventory" placeholder="请输入" />
         </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
           <el-button @click="closeDialog"> 取消 </el-button>
-          <el-button type="primary" @click="handleSubmit"> 驳回 </el-button>
+          <el-button type="primary" @click="handleSubmit"> 确定 </el-button>
         </div>
       </template>
     </el-dialog>
   </div>
 </template>
 
-<script setup lang="tsx" name="orderManagement">
-import { ref, reactive, onMounted, onActivated } from "vue";
+<script setup lang="tsx" name="groupPackageManagement">
+import { computed, onActivated, onMounted, reactive, ref, watch } from "vue";
 import { useRouter } from "vue-router";
+import type { FormInstance, FormRules } from "element-plus";
 import { ElMessage } from "element-plus";
 import ProTable from "@/components/ProTable/index.vue";
-import { ProTableInstance, ColumnProps } from "@/components/ProTable/interface";
-import { Download } from "@element-plus/icons-vue";
-import { audit, exportExcelStaffConfig, getStaffConfigList } from "@/api/modules/staffConfig";
+import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
+import { Plus } from "@element-plus/icons-vue";
+import { audit, getStaffConfigList } from "@/api/modules/staffConfig";
+import { getThaliList } from "@/api/modules/groupPackageManagement";
 
 const router = useRouter();
 const dialogFormVisible = ref(false);
-const form = reactive({
-  comment: ""
+const formInventory: any = ref({
+  id: "",
+  packageName: "",
+  remainingInventory: "",
+  newInventory: ""
 });
-
 const rowData = ref<any>();
+const activeName = ref("");
 
+const ruleFormRef = ref<FormInstance>();
+const rules = reactive<FormRules<RuleForm>>({
+  newInventory: [
+    { required: true, message: "请输入库存数量", trigger: "blur" },
+    {
+      pattern: /^(0|[1-9][0-9]*)$/,
+      message: "请输入整数,不允许输入小数,负数",
+      trigger: "blur"
+    }
+  ]
+});
 const statusEnum = [
-  { value: "0", label: "待审核" },
-  { value: "1", label: "审核通过" },
-  { value: "2", label: "审核拒绝" }
+  { value: "-1", label: "待审核" },
+  { value: "-2", label: "审核通过" },
+  { value: "0", label: "审核驳回" }
 ];
-// 如果表格需要初始化请求参数,直接定义传给 ProTable (之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
-const initParam = reactive({});
 
-// 定义 filterValues
-const filterValues = reactive({});
+// ProTable 实例(需要在使用它的地方之前定义)
+const proTable = ref<ProTableInstance>();
 
-const getStatusObj = (statusValue: string) => {
-  const statusObj = statusEnum.find(item => item.value === statusValue);
-  if (statusObj) {
-    filterValues.status = statusObj;
-  } else {
-    filterValues.status = "";
+// 表格配置项
+const columns = reactive<ColumnProps<any>[]>([
+  {
+    prop: "index",
+    label: "序号",
+    width: 100,
+    render: (scope: any) => {
+      return scope.$index + (proTable.value!.pageable.pageNum - 1) * proTable.value!.pageable.pageSize + 1;
+    }
+  },
+  {
+    prop: "groupName",
+    label: "代金券名称",
+    search: {
+      el: "input"
+    }
+  },
+  {
+    prop: "groupName",
+    label: "价格",
+    render: (scope: any) => {
+      return `¥${scope.row.preferentialPrice}`;
+    }
+  },
+  {
+    prop: "goodsId",
+    label: "已售"
+  },
+  {
+    prop: "inventoryNum",
+    label: "剩余库存"
+  },
+  {
+    prop: "goodsId",
+    label: "结束时间"
+  },
+  {
+    prop: "reviewType",
+    label: "审核状态",
+    isShow: false,
+    render: scope => {
+      const statusObj = statusEnum.find(item => item.value === scope.row.reviewType);
+      return statusObj ? statusObj.label : "--";
+    },
+    search: {
+      el: "select"
+    },
+    enum: statusEnum,
+    fieldNames: { label: "label", value: "value" }
+  },
+  {
+    prop: "status",
+    label: "状态"
+  },
+  { prop: "operation", label: "操作", fixed: "right", width: 330 }
+]);
+
+// 在 script setup 中添加
+const allTabOptions = [
+  { label: "全部", name: "" },
+  { label: "草稿", name: "0" },
+  { label: "进行中", name: "5" },
+  { label: "待审核", name: "1" },
+  { label: "审核拒绝", name: "3" },
+  { label: "已售罄", name: "4" },
+  { label: "已结束", name: "7" }
+];
+
+// 获取当前审核状态
+const currentAuditStatus = computed(() => {
+  if (!proTable.value?.searchParam) return "";
+  return proTable.value.searchParam.reviewType || "";
+});
+
+// 控制 el-tabs 是否显示:当审核状态为空或审核通过时显示
+const showTabs = computed(() => {
+  const status = currentAuditStatus.value;
+  return !status || status === "-2";
+});
+
+// 根据审核状态过滤 tabOptions:如果审核状态为空,只显示草稿;审核通过时,显示除草稿外的所有标签页
+const filteredTabOptions = computed(() => {
+  const status = currentAuditStatus.value;
+  if (!status) {
+    // 审核状态为空时,只显示草稿
+    return allTabOptions;
+  } else if (status === "-2") {
+    // 审核通过时,显示除草稿外的所有标签页
+    return allTabOptions.filter(tab => tab.name !== "1");
   }
-  return statusObj;
-};
+  return [];
+});
 
-// ProTable 实例
-const proTable = ref<ProTableInstance>();
+// 监听审核状态变化
+watch(
+  currentAuditStatus,
+  newStatus => {
+    if (!newStatus || newStatus === "-2") {
+      // 审核状态为空时,确保 activeName 为草稿
+      activeName.value = "";
+    }
+  },
+  { immediate: true }
+);
+// 如果表格需要初始化请求参数,直接定义传给 ProTable (之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
+const initParam = reactive({
+  storeId: "104",
+  groupType: "1",
+  status: activeName
+});
 
 // 页面加载时触发查询
 onMounted(() => {
@@ -99,50 +225,38 @@ const dataCallback = (data: any) => {
 // 默认不做操作就直接在 ProTable 组件上绑定	:requestApi="getUserList"
 const getTableList = (params: any) => {
   let newParams = JSON.parse(JSON.stringify(params));
-  return getStaffConfigList(newParams);
+  return getThaliList(newParams);
 };
 
 // 跳转详情页
 const toDetail = row => {
-  router.push(`/store/orderManagementDetail?id=${row.id}`);
+  router.push(`/voucherManagement/detail?id=${row.id}&type=edit`);
 };
-
-// 表格配置项
-const columns = reactive<ColumnProps<any>[]>([
-  { type: "index", fixed: "left", label: "序号", width: 130 },
-  { prop: "storeName", label: "所属店铺" },
-  { prop: "name", label: "名称" },
-  { prop: "description", label: "描述" },
-  {
-    prop: "status",
-    label: "状态",
-    render: scope => {
-      const statusObj = getStatusObj(scope.row.status);
-      return statusObj ? statusObj.label : "未知状态";
-    },
-    search: {
-      el: "select"
-    },
-    enum: statusEnum,
-    fieldNames: { label: "label", value: "value" }
-  },
-  { prop: "operation", label: "操作", fixed: "right", width: 330 }
-]);
-
+const editRow = row => {};
+const deleteRow = row => {};
+const newGroupBuying = () => {
+  router.push(`/voucherManagement/newVoucher?id=104&type=add`);
+};
+const handleClick = () => {};
 const changeTypes = (row: any, status: string) => {
   rowData.value = row;
-  if (status === "pass") {
+  if (status === "on") {
     handleChangeStatus(row, "1");
   } else {
-    form.comment = "";
-    dialogFormVisible.value = true;
+    handleChangeStatus(row, "2");
   }
 };
-
+const changeInventory = (row: any) => {
+  formInventory.value.id = 1;
+  formInventory.value.packageName = 1;
+  formInventory.value.remainingInventory = 1;
+  formInventory.value.newInventory = "";
+  dialogFormVisible.value = true;
+};
 const handleChangeStatus = async (row: any, status: string) => {
   try {
     let res = await audit({ id: row.id, status: status, rejectionReason: form.comment });
-    if (res.code === 200) {
+    if (res.code == 200) {
       proTable.value?.getTableList();
       if (status === "2") closeDialog();
       ElMessage.success("审核成功");
@@ -152,43 +266,25 @@ const handleChangeStatus = async (row: any, status: string) => {
   }
 };
 
-// 导出信息
-const exportInfoExcel = async scope => {
-  let res;
-  // 获取原始状态值(可能为数字、字符串或 undefined)
-  const rawStatus = proTable.value.searchParam.status;
-  // 转换为字符串(处理 undefined/null 为 "" 或保留原始字符串)
-  const statusParam = rawStatus !== undefined && rawStatus !== null ? String(rawStatus) : undefined;
-  // 将筛选条件作为参数传递给后台
-  res = await exportExcelStaffConfig({ status: statusParam });
-  if (res.code === 200) {
-    if (!res.data) {
-      ElMessage.error("暂无可下载数据");
-      return;
-    }
-    const exportFile = document.createElement("a");
-    exportFile.style.display = "none";
-    exportFile.download = `订单管理.xlsx`;
-    exportFile.href = `${res.data}?timestamp=${new Date().getTime()}`; // 添加时间戳防止缓存
-    document.body.appendChild(exportFile);
-    exportFile.click();
-    document.body.removeChild(exportFile);
-    ElMessage.success("下载成功");
-  }
-};
-
 // 弹窗提交
-const handleSubmit = () => {
-  if (!form.comment) {
-    ElMessage.error("请输入审批意见");
-    return;
-  }
-  handleChangeStatus(rowData.value, "2");
+const handleSubmit = async () => {
+  if (!ruleFormRef.value) return;
+  await ruleFormRef.value.validate((valid, fields) => {
+    if (valid) {
+      ElMessage.success("修改成功");
+      dialogFormVisible.value = false;
+    }
+  });
 };
 // 关闭弹窗;
 const closeDialog = () => {
   dialogFormVisible.value = false;
-  form.comment = "";
+  formInventory.value = {
+    id: "",
+    packageName: "",
+    remainingInventory: "",
+    newInventory: ""
+  };
 };
 </script>
 
@@ -200,4 +296,12 @@ const closeDialog = () => {
   word-wrap: break-word; // 长单词内换行
   white-space: normal; // 允许自然换行
 }
+.table-header-btn {
+  .tabs {
+    margin-top: 10px;
+    :deep(.el-tabs__nav-wrap::after) {
+      height: 0;
+    }
+  }
+}
 </style>

+ 203 - 99
src/views/voucherManagement/index.vue

@@ -3,78 +3,204 @@
     <ProTable ref="proTable" :columns="columns" :request-api="getTableList" :init-param="initParam" :data-callback="dataCallback">
       <!-- 表格 header 按钮 -->
       <template #tableHeader="scope">
-        <el-button type="primary" :icon="Download" @click="exportInfoExcel(scope)"> 导出 </el-button>
-      </template>
-      <template #expand="scope">
-        {{ scope.row }}
+        <div class="table-header-btn">
+          <el-button :icon="Plus" class="button" type="primary" @click="newGroupBuying"> 新建代金券 </el-button>
+          <el-tabs v-if="showTabs" v-model="activeName" class="tabs" @tab-click="handleClick">
+            <el-tab-pane v-for="tab in filteredTabOptions" :key="tab.name" :label="tab.label" :name="tab.name" />
+          </el-tabs>
+        </div>
       </template>
+      <template #status="scope">
+        <p>团购状态:{{ allTabOptions.find(item => item.name === scope.row.groupType)?.label ?? "--" }}</p>
+        <p>审核状态:{{ statusEnum.find(item => item.value === scope.row.reviewType)?.label ?? "--" }}</p> </template
+      >:
       <!-- 表格操作 -->
       <template #operation="scope">
         <!-- 审批通过和拒绝按钮仅在状态为0时显示 -->
         <template v-if="scope.row.status === '0'">
-          <el-button type="primary" link @click="changeTypes(scope.row, 'pass')"> 审核通过 </el-button>
-          <el-button type="primary" link @click="changeTypes(scope.row, '')"> 审核拒绝 </el-button>
+          <el-button link type="primary" @click="changeTypes(scope.row, 'on')"> 上架 </el-button>
+          <el-button link type="primary" @click="changeTypes(scope.row, 'off')"> 下架 </el-button>
+          <el-button link type="primary" @click="changeInventory(scope.row)"> 修改库存 </el-button>
         </template>
         <el-button type="primary" link @click="toDetail(scope.row)"> 查看详情 </el-button>
+        <el-button link type="primary" @click="editRow(scope.row)"> 编辑 </el-button>
+        <el-button link type="primary" @click="deleteRow(scope.row)"> 删除 </el-button>
       </template>
     </ProTable>
-
-    <el-dialog v-model="dialogFormVisible" title="审核拒绝" width="500">
-      <el-form :model="form">
-        <el-form-item label="" label-width="0">
-          <el-input v-model="form.comment" autocomplete="off" type="textarea" maxlength="200" />
+    <el-dialog v-model="dialogFormVisible" title="修改库存" width="500">
+      <el-form ref="ruleFormRef" :model="formInventory" :rules="rules" @submit.prevent>
+        <el-form-item label="套餐名">
+          {{ formInventory.packageName }}
+        </el-form-item>
+        <el-form-item label="剩余库存">
+          {{ formInventory.remainingInventory }}
+        </el-form-item>
+        <el-form-item label="修改库存" prop="newInventory">
+          <el-input v-model="formInventory.newInventory" placeholder="请输入" />
         </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
           <el-button @click="closeDialog"> 取消 </el-button>
-          <el-button type="primary" @click="handleSubmit"> 驳回 </el-button>
+          <el-button type="primary" @click="handleSubmit"> 确定 </el-button>
         </div>
       </template>
     </el-dialog>
   </div>
 </template>
 
-<script setup lang="tsx" name="voucherManagement">
-import { ref, reactive, onMounted, onActivated } from "vue";
+<script setup lang="tsx" name="groupPackageManagement">
+import { computed, onActivated, onMounted, reactive, ref, watch } from "vue";
 import { useRouter } from "vue-router";
+import type { FormInstance, FormRules } from "element-plus";
 import { ElMessage } from "element-plus";
 import ProTable from "@/components/ProTable/index.vue";
-import { ProTableInstance, ColumnProps } from "@/components/ProTable/interface";
-import { Download } from "@element-plus/icons-vue";
-import { audit, exportExcelStaffConfig, getStaffConfigList } from "@/api/modules/staffConfig";
+import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
+import { Plus } from "@element-plus/icons-vue";
+import { audit, getStaffConfigList } from "@/api/modules/staffConfig";
+import { getThaliList } from "@/api/modules/groupPackageManagement";
 
 const router = useRouter();
 const dialogFormVisible = ref(false);
-const form = reactive({
-  comment: ""
+const formInventory: any = ref({
+  id: "",
+  packageName: "",
+  remainingInventory: "",
+  newInventory: ""
 });
-
 const rowData = ref<any>();
+const activeName = ref("");
 
+const ruleFormRef = ref<FormInstance>();
+const rules = reactive<FormRules<RuleForm>>({
+  newInventory: [
+    { required: true, message: "请输入库存数量", trigger: "blur" },
+    {
+      pattern: /^(0|[1-9][0-9]*)$/,
+      message: "请输入整数,不允许输入小数,负数",
+      trigger: "blur"
+    }
+  ]
+});
 const statusEnum = [
-  { value: "0", label: "待审核" },
-  { value: "1", label: "审核通过" },
-  { value: "2", label: "审核拒绝" }
+  { value: "-1", label: "待审核" },
+  { value: "-2", label: "审核通过" },
+  { value: "0", label: "审核驳回" }
 ];
-// 如果表格需要初始化请求参数,直接定义传给 ProTable (之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
-const initParam = reactive({});
 
-// 定义 filterValues
-const filterValues = reactive({});
+// ProTable 实例(需要在使用它的地方之前定义)
+const proTable = ref<ProTableInstance>();
 
-const getStatusObj = (statusValue: string) => {
-  const statusObj = statusEnum.find(item => item.value === statusValue);
-  if (statusObj) {
-    filterValues.status = statusObj;
-  } else {
-    filterValues.status = "";
+// 表格配置项
+const columns = reactive<ColumnProps<any>[]>([
+  {
+    prop: "index",
+    label: "序号",
+    width: 100,
+    render: (scope: any) => {
+      return scope.$index + (proTable.value!.pageable.pageNum - 1) * proTable.value!.pageable.pageSize + 1;
+    }
+  },
+  {
+    prop: "groupName",
+    label: "代金券名称",
+    search: {
+      el: "input"
+    }
+  },
+  {
+    prop: "groupName",
+    label: "价格",
+    render: (scope: any) => {
+      return `¥${scope.row.preferentialPrice}`;
+    }
+  },
+  {
+    prop: "goodsId",
+    label: "已售"
+  },
+  {
+    prop: "inventoryNum",
+    label: "剩余库存"
+  },
+  {
+    prop: "goodsId",
+    label: "结束时间"
+  },
+  {
+    prop: "reviewType",
+    label: "审核状态",
+    isShow: false,
+    render: scope => {
+      const statusObj = statusEnum.find(item => item.value === scope.row.reviewType);
+      return statusObj ? statusObj.label : "--";
+    },
+    search: {
+      el: "select"
+    },
+    enum: statusEnum,
+    fieldNames: { label: "label", value: "value" }
+  },
+  {
+    prop: "status",
+    label: "状态"
+  },
+  { prop: "operation", label: "操作", fixed: "right", width: 330 }
+]);
+
+// 在 script setup 中添加
+const allTabOptions = [
+  { label: "全部", name: "" },
+  { label: "草稿", name: "0" },
+  { label: "进行中", name: "5" },
+  { label: "待审核", name: "1" },
+  { label: "审核拒绝", name: "3" },
+  { label: "已售罄", name: "4" },
+  { label: "已结束", name: "7" }
+];
+
+// 获取当前审核状态
+const currentAuditStatus = computed(() => {
+  if (!proTable.value?.searchParam) return "";
+  return proTable.value.searchParam.reviewType || "";
+});
+
+// 控制 el-tabs 是否显示:当审核状态为空或审核通过时显示
+const showTabs = computed(() => {
+  const status = currentAuditStatus.value;
+  return !status || status === "-2";
+});
+
+// 根据审核状态过滤 tabOptions:如果审核状态为空,只显示草稿;审核通过时,显示除草稿外的所有标签页
+const filteredTabOptions = computed(() => {
+  const status = currentAuditStatus.value;
+  if (!status) {
+    // 审核状态为空时,只显示草稿
+    return allTabOptions;
+  } else if (status === "-2") {
+    // 审核通过时,显示除草稿外的所有标签页
+    return allTabOptions.filter(tab => tab.name !== "1");
   }
-  return statusObj;
-};
+  return [];
+});
 
-// ProTable 实例
-const proTable = ref<ProTableInstance>();
+// 监听审核状态变化
+watch(
+  currentAuditStatus,
+  newStatus => {
+    if (!newStatus || newStatus === "-2") {
+      // 审核状态为空时,确保 activeName 为草稿
+      activeName.value = "";
+    }
+  },
+  { immediate: true }
+);
+// 如果表格需要初始化请求参数,直接定义传给 ProTable (之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
+const initParam = reactive({
+  storeId: "104",
+  groupType: "1",
+  status: activeName
+});
 
 // 页面加载时触发查询
 onMounted(() => {
@@ -99,50 +225,38 @@ const dataCallback = (data: any) => {
 // 默认不做操作就直接在 ProTable 组件上绑定	:requestApi="getUserList"
 const getTableList = (params: any) => {
   let newParams = JSON.parse(JSON.stringify(params));
-  return getStaffConfigList(newParams);
+  return getThaliList(newParams);
 };
 
 // 跳转详情页
 const toDetail = row => {
-  router.push(`/store/voucherManagementDetail?id=${row.id}`);
+  router.push(`/voucherManagement/detail?id=${row.id}&type=edit`);
 };
-
-// 表格配置项
-const columns = reactive<ColumnProps<any>[]>([
-  { type: "index", fixed: "left", label: "序号", width: 130 },
-  { prop: "storeName", label: "所属店铺" },
-  { prop: "name", label: "名称" },
-  { prop: "description", label: "描述" },
-  {
-    prop: "status",
-    label: "状态",
-    render: scope => {
-      const statusObj = getStatusObj(scope.row.status);
-      return statusObj ? statusObj.label : "未知状态";
-    },
-    search: {
-      el: "select"
-    },
-    enum: statusEnum,
-    fieldNames: { label: "label", value: "value" }
-  },
-  { prop: "operation", label: "操作", fixed: "right", width: 330 }
-]);
-
+const editRow = row => {};
+const deleteRow = row => {};
+const newGroupBuying = () => {
+  router.push(`/voucherManagement/newVoucher?id=104&type=add`);
+};
+const handleClick = () => {};
 const changeTypes = (row: any, status: string) => {
   rowData.value = row;
-  if (status === "pass") {
+  if (status === "on") {
     handleChangeStatus(row, "1");
   } else {
-    form.comment = "";
-    dialogFormVisible.value = true;
+    handleChangeStatus(row, "2");
   }
 };
-
+const changeInventory = (row: any) => {
+  formInventory.value.id = 1;
+  formInventory.value.packageName = 1;
+  formInventory.value.remainingInventory = 1;
+  formInventory.value.newInventory = "";
+  dialogFormVisible.value = true;
+};
 const handleChangeStatus = async (row: any, status: string) => {
   try {
     let res = await audit({ id: row.id, status: status, rejectionReason: form.comment });
-    if (res.code === 200) {
+    if (res.code == 200) {
       proTable.value?.getTableList();
       if (status === "2") closeDialog();
       ElMessage.success("审核成功");
@@ -152,43 +266,25 @@ const handleChangeStatus = async (row: any, status: string) => {
   }
 };
 
-// 导出信息
-const exportInfoExcel = async scope => {
-  let res;
-  // 获取原始状态值(可能为数字、字符串或 undefined)
-  const rawStatus = proTable.value.searchParam.status;
-  // 转换为字符串(处理 undefined/null 为 "" 或保留原始字符串)
-  const statusParam = rawStatus !== undefined && rawStatus !== null ? String(rawStatus) : undefined;
-  // 将筛选条件作为参数传递给后台
-  res = await exportExcelStaffConfig({ status: statusParam });
-  if (res.code === 200) {
-    if (!res.data) {
-      ElMessage.error("暂无可下载数据");
-      return;
-    }
-    const exportFile = document.createElement("a");
-    exportFile.style.display = "none";
-    exportFile.download = `代金券管理.xlsx`;
-    exportFile.href = `${res.data}?timestamp=${new Date().getTime()}`; // 添加时间戳防止缓存
-    document.body.appendChild(exportFile);
-    exportFile.click();
-    document.body.removeChild(exportFile);
-    ElMessage.success("下载成功");
-  }
-};
-
 // 弹窗提交
-const handleSubmit = () => {
-  if (!form.comment) {
-    ElMessage.error("请输入审批意见");
-    return;
-  }
-  handleChangeStatus(rowData.value, "2");
+const handleSubmit = async () => {
+  if (!ruleFormRef.value) return;
+  await ruleFormRef.value.validate((valid, fields) => {
+    if (valid) {
+      ElMessage.success("修改成功");
+      dialogFormVisible.value = false;
+    }
+  });
 };
 // 关闭弹窗;
 const closeDialog = () => {
   dialogFormVisible.value = false;
-  form.comment = "";
+  formInventory.value = {
+    id: "",
+    packageName: "",
+    remainingInventory: "",
+    newInventory: ""
+  };
 };
 </script>
 
@@ -200,4 +296,12 @@ const closeDialog = () => {
   word-wrap: break-word; // 长单词内换行
   white-space: normal; // 允许自然换行
 }
+.table-header-btn {
+  .tabs {
+    margin-top: 10px;
+    :deep(.el-tabs__nav-wrap::after) {
+      height: 0;
+    }
+  }
+}
 </style>

+ 1018 - 0
src/views/voucherManagement/newVoucher.vue

@@ -0,0 +1,1018 @@
+<template>
+  <!-- 代金券管理 - 新增/编辑页面 -->
+  <div class="table-box" style="width: 100%; min-height: 100%; background-color: white">
+    <div class="header">
+      <el-button @click="goBack"> 返回 </el-button>
+      <h2 class="title">新建代金券</h2>
+    </div>
+    <el-form :model="voucherModel" ref="ruleFormRef" :rules="rules" label-width="120px" class="formBox">
+      <div class="content">
+        <!-- 左侧内容区域 -->
+        <div class="contentLeft">
+          <!-- 基础信息模块 -->
+          <div class="model">
+            <h3 style="font-weight: bold">基础信息:</h3>
+            <!-- 代金券名称 -->
+            <el-form-item label="代金券名称" prop="voucherName">
+              <el-input maxlength="50" v-model="voucherModel.voucherName" placeholder="请输入" clearable />
+            </el-form-item>
+            <!-- 抵扣价格 -->
+            <el-form-item label="抵扣价格(¥)" prop="discountPrice">
+              <el-input v-model="voucherModel.discountPrice" maxlength="15" placeholder="请输入" clearable />
+            </el-form-item>
+            <!-- 售卖价格 -->
+            <el-form-item label="售卖价格(¥)" prop="sellingPrice">
+              <el-input v-model="voucherModel.sellingPrice" maxlength="15" placeholder="请输入" clearable />
+            </el-form-item>
+            <!-- 开始售卖时间 -->
+            <el-form-item label="开始售卖时间" prop="startSellingTime">
+              <el-date-picker
+                v-model="voucherModel.startSellingTime"
+                format="YYYY/MM/DD"
+                value-format="YYYY-MM-DD"
+                placeholder="请选择开始售卖时间"
+                :disabled-date="disabledStartDate"
+              />
+            </el-form-item>
+            <!-- 结束售卖时间 -->
+            <el-form-item label="结束售卖时间" prop="endSellingTime">
+              <el-date-picker
+                v-model="voucherModel.endSellingTime"
+                format="YYYY/MM/DD"
+                value-format="YYYY-MM-DD"
+                placeholder="请选择结束售卖时间"
+                :disabled-date="disabledEndDate"
+              />
+            </el-form-item>
+          </div>
+          <!-- 购买须知模块 -->
+          <div class="model">
+            <h3 style="font-weight: bold">购买须知:</h3>
+            <!-- 使用时间 -->
+            <el-form-item label="使用时间" prop="usageTime">
+              <el-date-picker
+                v-model="voucherModel.usageTime"
+                type="daterange"
+                value-format="YYYY-MM-DD"
+                range-separator="-"
+                start-placeholder="开始时间"
+                end-placeholder="结束时间"
+                :disabled-date="disabledUsageDate"
+              />
+            </el-form-item>
+            <!-- 有效期 -->
+            <el-form-item label="有效期" prop="validityPeriodType">
+              <el-radio-group v-model="voucherModel.validityPeriodType" class="ml-4">
+                <el-radio v-for="status in validityPeriodList" :value="status.value" :key="status.value">
+                  {{ status.label }}
+                </el-radio>
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item label="" prop="validityDays" v-if="voucherModel.validityPeriodType == 0">
+              <div class="expiration-date-container">
+                <span class="expiration-label">用户购买</span>
+                <el-input-number v-model="voucherModel.validityDays" placeholder="请输入" :min="1" class="expiration-input" />
+                <span class="expiration-label">天内有效</span>
+              </div>
+            </el-form-item>
+            <el-form-item label="" prop="validityPeriod" v-else>
+              <el-date-picker
+                v-model="voucherModel.validityPeriod"
+                type="daterange"
+                value-format="YYYY-MM-DD"
+                range-separator="-"
+                start-placeholder="开始时间"
+                end-placeholder="结束时间"
+                :disabled-date="disabledValidityDate"
+              />
+            </el-form-item>
+            <!-- 不可用日期 -->
+            <el-form-item label="不可用日期" prop="unavailableDateType">
+              <el-radio-group v-model="voucherModel.unavailableDateType" class="ml-4">
+                <el-radio v-for="status in unavailableDateTypeList" :value="status.value" :key="status.value">
+                  {{ status.label }}
+                </el-radio>
+              </el-radio-group>
+            </el-form-item>
+            <template v-if="voucherModel.unavailableDateType == 1">
+              <el-form-item label="" prop="unavailableWeekdays">
+                <div class="unavailable-dates-container">
+                  <!-- 星期选择 -->
+                  <div class="date-select-section">
+                    <div class="section-title">星期</div>
+                    <div class="button-group">
+                      <el-button
+                        v-for="weekday in weekdayList"
+                        :key="weekday.oName"
+                        :type="voucherModel.unavailableWeekdays?.includes(weekday.oName) ? 'primary' : ''"
+                        class="date-select-btn"
+                        @click="toggleWeekday(weekday.oName)"
+                      >
+                        {{ weekday.name }}
+                      </el-button>
+                    </div>
+                  </div>
+                </div>
+              </el-form-item>
+              <el-form-item label="" prop="unavailableHolidays">
+                <div class="unavailable-dates-container">
+                  <!-- 节日选择 -->
+                  <div class="date-select-section">
+                    <div class="section-title">节日</div>
+                    <div class="button-group">
+                      <el-button
+                        v-for="holiday in holidayList"
+                        :key="holiday.id"
+                        :type="voucherModel.unavailableHolidays?.includes(holiday.id) ? 'primary' : ''"
+                        class="date-select-btn"
+                        @click="toggleHoliday(holiday.id)"
+                      >
+                        {{ holiday.festivalName }}
+                      </el-button>
+                    </div>
+                  </div>
+                </div>
+              </el-form-item>
+            </template>
+            <el-form-item label="" prop="customUnavailableDates" v-else-if="voucherModel.unavailableDateType == 2">
+              <div class="date-picker-container">
+                <el-button :icon="Plus" class="add-date-btn" type="primary" @click="addDate"> 添加日期 </el-button>
+                <div v-for="(item, index) in dates" :key="index" class="date-item">
+                  <el-date-picker
+                    v-model="dates[index]"
+                    type="daterange"
+                    value-format="YYYY-MM-DD"
+                    range-separator="-"
+                    start-placeholder="开始时间"
+                    end-placeholder="结束时间"
+                    class="date-picker"
+                    :disabled-date="disabledCustomUnavailableDate"
+                  />
+                  <el-button
+                    :icon="Delete"
+                    type="danger"
+                    circle
+                    size="small"
+                    class="delete-btn"
+                    @click="removeDate(index)"
+                    v-show="dates.length > 1"
+                  />
+                </div>
+              </div>
+            </el-form-item>
+          </div>
+        </div>
+        <!-- 右侧内容区域 -->
+        <div class="contentRight">
+          <!-- 库存模块 -->
+          <div class="model">
+            <h3 style="font-weight: bold">库存:</h3>
+            <el-form-item label="库存" prop="inventory">
+              <el-input v-model="voucherModel.inventory" maxlength="15" placeholder="请输入" clearable />
+            </el-form-item>
+          </div>
+          <!-- 使用规则模块 -->
+          <div class="model">
+            <h3 style="font-weight: bold">使用规则:</h3>
+            <!-- 单日可用数量 -->
+            <el-form-item label="单日可用数量" prop="dailyAvailableQuantity">
+              <el-input v-model="voucherModel.dailyAvailableQuantity" maxlength="15" placeholder="请输入" clearable />
+            </el-form-item>
+            <!-- 限购数量 -->
+            <el-form-item label="限购数量" prop="purchaseLimitQuantity">
+              <el-input v-model="voucherModel.purchaseLimitQuantity" maxlength="15" placeholder="请输入" clearable />
+            </el-form-item>
+            <!-- 适用范围 -->
+            <el-form-item label="适用范围" prop="applicableScopeType">
+              <el-radio-group v-model="voucherModel.applicableScopeType" class="ml-4">
+                <el-radio v-for="status in applicableScopeList" :value="status.value" :key="status.value">
+                  {{ status.label }}
+                </el-radio>
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item label="" prop="applicableScope" v-if="voucherModel.applicableScopeType == 1">
+              <el-input
+                maxlength="50"
+                v-model="voucherModel.applicableScope"
+                :rows="3"
+                type="textarea"
+                placeholder="请输入"
+                show-word-limit
+              />
+            </el-form-item>
+          </div>
+          <!-- 补充说明模块 -->
+          <div class="model">
+            <h3 style="font-weight: bold">补充说明:</h3>
+            <el-form-item label="补充说明" prop="additionalInstructions">
+              <el-input
+                maxlength="300"
+                v-model="voucherModel.additionalInstructions"
+                :rows="4"
+                type="textarea"
+                placeholder="请输入"
+                show-word-limit
+              />
+            </el-form-item>
+          </div>
+        </div>
+      </div>
+    </el-form>
+    <!-- 底部按钮区域 -->
+    <div class="button-container">
+      <el-button @click="handleSubmit('draft')"> 存草稿 </el-button>
+      <el-button type="primary" @click="handleSubmit"> 新建代金券 </el-button>
+    </div>
+  </div>
+</template>
+
+<script setup lang="tsx" name="newVoucher">
+/**
+ * 代金券管理 - 新增/编辑页面
+ * 功能:支持代金券的新增和编辑操作
+ */
+import { ref, reactive, onMounted, watch, nextTick } from "vue";
+import { ElMessage } from "element-plus";
+import { Plus, Delete } from "@element-plus/icons-vue";
+import { getHolidayList } from "@/api/modules/groupPackageManagement";
+import { useRouter, useRoute } from "vue-router";
+import type { FormInstance } from "element-plus";
+
+// ==================== 响应式数据定义 ====================
+
+// 路由相关
+const router = useRouter();
+const route = useRoute();
+
+// 页面状态
+const type = ref<string>(""); // 页面类型:add-新增, edit-编辑
+const id = ref<string>(""); // 页面ID参数
+
+// ==================== 表单验证规则 ====================
+const rules = reactive({
+  voucherName: [{ required: true, message: "请输入代金券名称" }],
+  discountPrice: [
+    { required: true, message: "请输入抵扣价格" },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (!value || value.toString().trim() === "") {
+          callback();
+          return;
+        }
+        const num = Number(value);
+        if (isNaN(num) || num <= 0) {
+          callback(new Error("抵扣价格必须为正数"));
+          return;
+        }
+        callback();
+      },
+      trigger: "blur"
+    }
+  ],
+  sellingPrice: [
+    { required: true, message: "请输入售卖价格" },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (!value || value.toString().trim() === "") {
+          callback();
+          return;
+        }
+        const num = Number(value);
+        if (isNaN(num) || num <= 0) {
+          callback(new Error("售卖价格必须为正数"));
+          return;
+        }
+        callback();
+      },
+      trigger: "blur"
+    }
+  ],
+  startSellingTime: [
+    { required: true, message: "请选择开始售卖时间" },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (!value) {
+          callback();
+          return;
+        }
+        const selectedDate = new Date(value);
+        const today = new Date();
+        today.setHours(0, 0, 0, 0);
+        if (selectedDate < today) {
+          callback(new Error("开始售卖时间不能早于当前时间"));
+          return;
+        }
+        if (voucherModel.value.endSellingTime) {
+          const endDate = new Date(voucherModel.value.endSellingTime);
+          if (selectedDate >= endDate) {
+            callback(new Error("开始售卖时间必须早于结束售卖时间"));
+            return;
+          }
+        }
+        callback();
+      },
+      trigger: "change"
+    }
+  ],
+  endSellingTime: [
+    { required: true, message: "请选择结束售卖时间" },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (!value) {
+          callback();
+          return;
+        }
+        const selectedDate = new Date(value);
+        const today = new Date();
+        today.setHours(0, 0, 0, 0);
+        if (selectedDate < today) {
+          callback(new Error("结束售卖时间不能早于当前时间"));
+          return;
+        }
+        if (voucherModel.value.startSellingTime) {
+          const startDate = new Date(voucherModel.value.startSellingTime);
+          if (selectedDate <= startDate) {
+            callback(new Error("结束售卖时间必须晚于开始售卖时间"));
+            return;
+          }
+        }
+        callback();
+      },
+      trigger: "change"
+    }
+  ],
+  usageTime: [
+    { required: true, message: "请选择使用时间" },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (!value || value.length !== 2) {
+          callback();
+          return;
+        }
+        const startDate = new Date(value[0]);
+        const endDate = new Date(value[1]);
+        if (startDate >= endDate) {
+          callback(new Error("使用开始时间必须早于结束时间"));
+          return;
+        }
+        callback();
+      },
+      trigger: "change"
+    }
+  ],
+  validityPeriodType: [{ required: true, message: "请选择有效期类型" }],
+  validityDays: [
+    {
+      required: true,
+      validator: (rule: any, value: any, callback: any) => {
+        if (voucherModel.value.validityPeriodType === 0) {
+          if (value === null || value === undefined || value === "") {
+            callback(new Error("请输入用户购买天数"));
+            return;
+          }
+          if (value <= 0) {
+            callback(new Error("天数必须大于0"));
+            return;
+          }
+        }
+        callback();
+      },
+      trigger: "blur"
+    }
+  ],
+  validityPeriod: [
+    {
+      required: true,
+      validator: (rule: any, value: any, callback: any) => {
+        if (voucherModel.value.validityPeriodType === 1) {
+          if (!value || value.length !== 2) {
+            callback(new Error("请选择指定时间段"));
+            return;
+          }
+          const startDate = new Date(value[0]);
+          const endDate = new Date(value[1]);
+          if (startDate >= endDate) {
+            callback(new Error("开始时间必须早于结束时间"));
+            return;
+          }
+        }
+        callback();
+      },
+      trigger: "change"
+    }
+  ],
+  unavailableDateType: [{ required: true, message: "请选择不可用日期类型" }],
+  unavailableWeekdays: [
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (voucherModel.value.unavailableDateType === 1) {
+          if (!value || value.length === 0) {
+            callback(new Error("至少需要选择一个星期"));
+            return;
+          }
+        }
+        callback();
+      },
+      trigger: "change"
+    }
+  ],
+  unavailableHolidays: [
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (voucherModel.value.unavailableDateType === 1) {
+          if (!value || value.length === 0) {
+            callback(new Error("至少需要选择一个节日"));
+            return;
+          }
+        }
+        callback();
+      },
+      trigger: "change"
+    }
+  ],
+  customUnavailableDates: [
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (voucherModel.value.unavailableDateType === 2) {
+          if (!dates.value || dates.value.length === 0) {
+            callback(new Error("至少需要添加一个自定义不可用日期"));
+            return;
+          }
+          for (let i = 0; i < dates.value.length; i++) {
+            if (!dates.value[i] || dates.value[i].length !== 2) {
+              callback(new Error(`第${i + 1}个日期项未完整填写`));
+              return;
+            }
+            const startDate = new Date(dates.value[i][0]);
+            const endDate = new Date(dates.value[i][1]);
+            if (startDate >= endDate) {
+              callback(new Error(`第${i + 1}个日期项的开始时间必须早于结束时间`));
+              return;
+            }
+          }
+        }
+        callback();
+      },
+      trigger: "change"
+    }
+  ],
+  inventory: [
+    { required: true, message: "请输入库存" },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (!value || value.toString().trim() === "") {
+          callback();
+          return;
+        }
+        const num = Number(value);
+        if (isNaN(num) || num <= 0 || !Number.isInteger(num)) {
+          callback(new Error("库存必须为正整数"));
+          return;
+        }
+        callback();
+      },
+      trigger: "blur"
+    }
+  ],
+  dailyAvailableQuantity: [
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (value && value.toString().trim() !== "") {
+          const num = Number(value);
+          if (isNaN(num) || num <= 0 || !Number.isInteger(num)) {
+            callback(new Error("单日可用数量必须为正整数"));
+            return;
+          }
+        }
+        callback();
+      },
+      trigger: "blur"
+    }
+  ],
+  purchaseLimitQuantity: [
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (value && value.toString().trim() !== "") {
+          const num = Number(value);
+          if (isNaN(num) || num <= 0 || !Number.isInteger(num)) {
+            callback(new Error("限购数量必须为正整数"));
+            return;
+          }
+        }
+        callback();
+      },
+      trigger: "blur"
+    }
+  ],
+  applicableScopeType: [{ required: true, message: "请选择适用范围类型" }],
+  applicableScope: [
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (voucherModel.value.applicableScopeType === 1) {
+          if (!value || value.trim() === "") {
+            callback(new Error("请输入适用范围"));
+            return;
+          }
+        }
+        callback();
+      },
+      trigger: "blur"
+    }
+  ]
+});
+
+// ==================== 代金券信息数据模型 ====================
+const voucherModel = ref<any>({
+  // 代金券名称
+  voucherName: "",
+  // 抵扣价格
+  discountPrice: "",
+  // 售卖价格
+  sellingPrice: "",
+  // 开始售卖时间
+  startSellingTime: "",
+  // 结束售卖时间
+  endSellingTime: "",
+  // 使用时间
+  usageTime: [],
+  // 有效期类型:0-指定天数,1-指定时间段内可用
+  validityPeriodType: 0,
+  // 有效期天数(当validityPeriodType为0时使用)
+  validityDays: 90,
+  // 有效期时间段(当validityPeriodType为1时使用)
+  validityPeriod: [],
+  // 不可用日期类型:0-全部日期可用,1-限制日期,2-自定义不可用日期
+  unavailableDateType: 0,
+  // 限制日期 - 星期选择(数组,存储选中的星期值)
+  unavailableWeekdays: [],
+  // 限制日期 - 节日选择(数组,存储选中的节日值)
+  unavailableHolidays: [],
+  // 库存
+  inventory: "",
+  // 单日可用数量
+  dailyAvailableQuantity: "",
+  // 限购数量
+  purchaseLimitQuantity: "",
+  // 适用范围类型:0-全场通用,1-部分不可用
+  applicableScopeType: 0,
+  // 适用范围(当applicableScopeType为1时使用)
+  applicableScope: "",
+  // 补充说明
+  additionalInstructions: ""
+});
+
+// ==================== 下拉选项数据 ====================
+
+// 有效期类型列表
+const validityPeriodList = ref([
+  { value: 0, label: "指定天数" },
+  { value: 1, label: "指定时间段内可用" }
+]);
+
+// 不可用日期类型列表
+const unavailableDateTypeList = ref([
+  { value: 0, label: "全部日期可用" },
+  { value: 1, label: "限制日期" },
+  { value: 2, label: "自定义不可用日期" }
+]);
+
+// 适用范围类型列表
+const applicableScopeList = ref([
+  { value: 0, label: "全场通用" },
+  { value: 1, label: "部分不可用" }
+]);
+
+// 自定义不可用日期列表
+const dates = ref([]);
+
+// 星期选项列表
+const weekdayList = ref([
+  { name: "周一", id: "0", oName: "星期一" },
+  { name: "周二", id: "1", oName: "星期二" },
+  { name: "周三", id: "2", oName: "星期三" },
+  { name: "周四", id: "3", oName: "星期四" },
+  { name: "周五", id: "4", oName: "星期五" },
+  { name: "周六", id: "5", oName: "星期六" },
+  { name: "周日", id: "6", oName: "星期日" }
+]);
+
+// 节日选项列表
+const holidayList: any = ref([]);
+
+// ==================== 监听器 ====================
+
+/**
+ * 监听不可用日期选项变化
+ * 当切换到自定义不可用日期时,确保至少有一个日期项
+ */
+watch(
+  () => voucherModel.value.unavailableDateType,
+  newVal => {
+    if (newVal === 2) {
+      if (!dates.value || dates.value.length === 0) {
+        dates.value = [null];
+      }
+    }
+  },
+  { immediate: true }
+);
+
+/**
+ * 监听开始售卖时间变化
+ * 当开始时间改变时,重新验证结束时间
+ */
+watch(
+  () => voucherModel.value.startSellingTime,
+  () => {
+    if (voucherModel.value.endSellingTime) {
+      nextTick(() => {
+        ruleFormRef.value?.validateField("endSellingTime");
+      });
+    }
+  }
+);
+
+/**
+ * 监听结束售卖时间变化
+ * 当结束时间改变时,重新验证开始时间
+ */
+watch(
+  () => voucherModel.value.endSellingTime,
+  () => {
+    if (voucherModel.value.startSellingTime) {
+      nextTick(() => {
+        ruleFormRef.value?.validateField("startSellingTime");
+      });
+    }
+  }
+);
+
+// ==================== 生命周期钩子 ====================
+
+/**
+ * 组件挂载时初始化
+ * 从路由参数中获取页面类型和ID
+ */
+onMounted(async () => {
+  id.value = (route.query.id as string) || "";
+  type.value = (route.query.type as string) || "";
+
+  // 获取节日列表
+  let params = {
+    year: new Date().getFullYear(),
+    page: 1,
+    size: 500,
+    openFlag: 1,
+    holidayName: ""
+  };
+  let res = await getHolidayList(params);
+  if (res && res.code == 200) {
+    holidayList.value = res.data.records;
+  }
+
+  // 编辑模式下加载数据
+  if (type.value != "add") {
+    // TODO: 加载代金券详情数据
+    // let res: any = await getVoucherDetail({ id: id.value });
+    // voucherModel.value = res.data;
+  }
+});
+// ==================== 事件处理函数 ====================
+
+/**
+ * 返回上一页
+ */
+const goBack = () => {
+  router.go(-1);
+};
+
+/**
+ * 添加自定义不可用日期
+ */
+const addDate = () => {
+  dates.value.push(null);
+};
+
+/**
+ * 删除自定义不可用日期
+ * @param index 要删除的日期索引
+ */
+const removeDate = (index: number) => {
+  if (dates.value.length <= 1) {
+    ElMessage.warning("至少需要保留一个日期项");
+    return;
+  }
+  dates.value.splice(index, 1);
+};
+
+/**
+ * 切换星期选择
+ * @param value 星期值
+ */
+const toggleWeekday = (value: string) => {
+  if (!voucherModel.value.unavailableWeekdays) {
+    voucherModel.value.unavailableWeekdays = [];
+  }
+  const index = voucherModel.value.unavailableWeekdays.indexOf(value);
+  if (index > -1) {
+    voucherModel.value.unavailableWeekdays.splice(index, 1);
+  } else {
+    voucherModel.value.unavailableWeekdays.push(value);
+  }
+  // 触发表单验证
+  nextTick(() => {
+    ruleFormRef.value?.validateField("unavailableWeekdays");
+  });
+};
+
+/**
+ * 切换节日选择
+ * @param value 节日值
+ */
+const toggleHoliday = (value: string | number) => {
+  if (!voucherModel.value.unavailableHolidays) {
+    voucherModel.value.unavailableHolidays = [];
+  }
+  const index = voucherModel.value.unavailableHolidays.indexOf(value);
+  if (index > -1) {
+    voucherModel.value.unavailableHolidays.splice(index, 1);
+  } else {
+    voucherModel.value.unavailableHolidays.push(value);
+  }
+  // 触发表单验证
+  nextTick(() => {
+    ruleFormRef.value?.validateField("unavailableHolidays");
+  });
+};
+
+// ==================== 表单引用 ====================
+const ruleFormRef = ref<FormInstance>(); // 表单引用
+
+/**
+ * 提交数据(新增/编辑)
+ * 验证表单,通过后调用相应的API接口
+ */
+const handleSubmit = (submitType?: string) => {
+  // 验证表单
+  ruleFormRef.value!.validate(async (valid: boolean) => {
+    if (!valid) return;
+
+    // 组装提交参数
+    let params: any = { ...voucherModel.value };
+
+    // 处理有效期
+    if (params.validityPeriodType === 0) {
+      params.validityPeriodStr = `用户购买${params.validityDays}天内有效`;
+    } else {
+      params.validityPeriodStr = params.validityPeriod.join(",");
+    }
+
+    // 处理不可用日期
+    if (params.unavailableDateType === 1) {
+      params.unavailableDateValue = params.unavailableWeekdays.join(",") + ";" + params.unavailableHolidays.join(",");
+    } else if (params.unavailableDateType === 2) {
+      params.unavailableDateValue = dates.value.map((subArray: any) => subArray.join(",")).join(";");
+    }
+
+    // 处理使用时间
+    if (params.usageTime && params.usageTime.length === 2) {
+      params.usageStartTime = params.usageTime[0];
+      params.usageEndTime = params.usageTime[1];
+    }
+
+    console.log("提交参数:", params);
+
+    // TODO: 调用API保存数据
+    // if (submitType === 'draft') {
+    //   await saveVoucherDraft(params);
+    // } else {
+    //   await saveVoucher(params);
+    // }
+
+    if (submitType === "draft") {
+      ElMessage.success("草稿保存成功");
+    } else {
+      ElMessage.success("代金券创建成功");
+      router.go(-1);
+    }
+  });
+};
+
+// ==================== 工具函数 ====================
+
+/**
+ * 禁用开始售卖时间的日期
+ * 不能选择早于当前时间的日期
+ */
+const disabledStartDate = (time: Date) => {
+  const today = new Date();
+  today.setHours(0, 0, 0, 0);
+  return time.getTime() < today.getTime();
+};
+
+/**
+ * 禁用结束售卖时间的日期
+ * 不能选择早于当前时间的日期,也不能选择早于或等于开始售卖时间的日期
+ */
+const disabledEndDate = (time: Date) => {
+  const today = new Date();
+  today.setHours(0, 0, 0, 0);
+  if (time.getTime() < today.getTime()) {
+    return true;
+  }
+  if (voucherModel.value.startSellingTime) {
+    const startDate = new Date(voucherModel.value.startSellingTime);
+    startDate.setHours(0, 0, 0, 0);
+    return time.getTime() <= startDate.getTime();
+  }
+  return false;
+};
+
+/**
+ * 禁用使用时间的日期
+ */
+const disabledUsageDate = (time: Date) => {
+  return false; // 可以根据需要添加限制
+};
+
+/**
+ * 禁用有效期时间段的日期
+ */
+const disabledValidityDate = (time: Date) => {
+  return false; // 可以根据需要添加限制
+};
+
+/**
+ * 禁用自定义不可用日期的日期
+ */
+const disabledCustomUnavailableDate = (time: Date) => {
+  return false; // 可以根据需要添加限制
+};
+</script>
+
+<style scoped lang="scss">
+/* 页面容器 */
+.table-box {
+  display: flex;
+  flex-direction: column;
+  height: auto !important;
+  min-height: 100%;
+}
+
+/* 头部区域 */
+.header {
+  display: flex;
+  align-items: center;
+  padding: 20px;
+  border-bottom: 1px solid #e4e7ed;
+}
+.title {
+  flex: 1;
+  margin: 0;
+  font-size: 18px;
+  font-weight: bold;
+  text-align: center;
+}
+
+/* 内容区域布局 */
+.content {
+  display: flex;
+  flex: 1;
+  column-gap: 20px;
+  width: 98%;
+  margin: 20px auto;
+
+  /* 左侧内容区域 */
+  .contentLeft {
+    width: 50%;
+  }
+
+  /* 右侧内容区域 */
+  .contentRight {
+    width: 50%;
+  }
+}
+
+/* 模块容器 */
+.model {
+  margin-bottom: 50px;
+}
+
+/* 表单容器 */
+.formBox {
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  min-height: 100%;
+}
+
+/* 底部按钮容器 - 居中显示 */
+.button-container {
+  display: flex;
+  gap: 12px;
+  align-items: center;
+  justify-content: center;
+  padding: 20px 0;
+  margin-top: 20px;
+}
+
+/* 日期选择器容器 */
+.date-picker-container {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  width: 100%;
+}
+
+/* 添加日期按钮 */
+.add-date-btn {
+  width: fit-content;
+  margin-bottom: 8px;
+}
+
+/* 日期项容器 */
+.date-item {
+  display: flex;
+  gap: 12px;
+  align-items: center;
+  padding: 8px;
+  border-radius: 4px;
+  transition: background-color 0.3s;
+  &:hover {
+    background-color: #ecf5ff;
+  }
+}
+
+/* 日期选择器 */
+.date-item .date-picker {
+  flex: 1;
+  max-width: 500px;
+}
+
+/* 删除按钮 */
+.date-item .delete-btn {
+  flex-shrink: 0;
+}
+
+/* 有效期天数容器 */
+.expiration-date-container {
+  display: flex;
+  gap: 12px;
+  align-items: center;
+  width: fit-content;
+  padding: 8px 12px;
+  border-radius: 4px;
+}
+
+/* 有效期标签文字 */
+.expiration-label {
+  font-size: 14px;
+  color: #606266;
+  white-space: nowrap;
+}
+
+/* 有效期输入框 */
+.expiration-input {
+  width: 150px;
+}
+
+/* 不可用日期容器 */
+.unavailable-dates-container {
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+  width: 100%;
+}
+
+/* 日期选择区块 */
+.date-select-section {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+/* 区块标题 */
+.section-title {
+  font-size: 14px;
+  font-weight: 500;
+  color: #606266;
+}
+
+/* 按钮组 */
+.button-group {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+}
+
+/* 日期选择按钮 */
+.date-select-btn {
+  min-width: 80px;
+  height: 36px;
+  padding: 8px 16px;
+  margin: 0;
+  font-size: 14px;
+  border-radius: 4px;
+  transition: all 0.3s;
+}
+</style>

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels