소스 검색

```
docs(env): 更新田同学的服务基础URL地址

更新了.env.test文件中田同学的测试环境服务基础URL地址,
从原来的http://192.168.1.89:8080更换为https://7f3f47fe.r3.cpolar.top
```

wenjie 1 주 전
부모
커밋
02e57c19e9
33개의 변경된 파일3631개의 추가작업 그리고 278개의 파일을 삭제
  1. 1 1
      .env.test
  2. 247 251
      pnpm-lock.yaml
  3. 6 0
      src/components/zt/Table/hooks/useTable.ts
  4. 7 1
      src/components/zt/Table/z-table.vue
  5. 9 1
      src/locales/langs/en-us.ts
  6. 9 1
      src/locales/langs/zh-cn.ts
  7. 7 0
      src/router/elegant/imports.ts
  8. 74 0
      src/router/elegant/routes.ts
  9. 8 0
      src/router/elegant/transform.ts
  10. 26 0
      src/service/api/goods-center/scenic-goods/index.ts
  11. 60 0
      src/service/api/member-center/member-list/index.ts
  12. 24 0
      src/service/api/member-center/member-order/index.ts
  13. 94 0
      src/service/api/member-center/member-type/index.ts
  14. 122 0
      src/service/api/operation/coupon-manage/index.ts
  15. 1 1
      src/typings/api.d.ts
  16. 2 0
      src/typings/components.d.ts
  17. 16 0
      src/typings/elegant-router.d.ts
  18. 2 2
      src/views/djk-manage/edit-activity/index.vue
  19. 3 1
      src/views/film-manage/film-list/index.vue
  20. 0 1
      src/views/goods-center/edit-health-goods/index.vue
  21. 240 0
      src/views/goods-center/scenic-goods/index.vue
  22. 13 0
      src/views/goods-center/virtual-goods/index.vue
  23. 1 1
      src/views/jy-manage/activity/index.vue
  24. 939 0
      src/views/member-center/edit-member-type/index.vue
  25. 354 0
      src/views/member-center/member-list/index.vue
  26. 247 0
      src/views/member-center/member-order/index.vue
  27. 232 0
      src/views/member-center/member-type/index.vue
  28. 371 0
      src/views/operation/coupon-issuance/index.vue
  29. 423 0
      src/views/operation/coupon-manage/index.vue
  30. 10 4
      src/views/order-manage/after-sales-order-detail/index.vue
  31. 1 1
      src/views/order-manage/after-sales-order/index.vue
  32. 21 5
      src/views/order-manage/normal-order/index.vue
  33. 61 7
      src/views/order-manage/order-detail/index.vue

+ 1 - 1
.env.test

@@ -2,7 +2,7 @@
 
 # VITE_SERVICE_BASE_URL=https://522d2ea1.r39.cpolar.top #王
 # VITE_SERVICE_BASE_URL=http://89561bkaq794.vicp.fun:53846 #张
-# VITE_SERVICE_BASE_URL=http://192.168.1.89:8080#田
+# VITE_SERVICE_BASE_URL=https://7f3f47fe.r3.cpolar.top#田
 # VITE_SERVICE_BASE_URL=https://425f86e6.r24.cpolar.top #邓
 # VITE_SERVICE_BASE_URL=http://74949mkfh190.vicp.fun #付
 # VITE_SERVICE_BASE_URL=https://smqjh.api.zswlgz.com

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 247 - 251
pnpm-lock.yaml


+ 6 - 0
src/components/zt/Table/hooks/useTable.ts

@@ -51,6 +51,10 @@ export function useTable(tableProps: ztTableProps): UseTableReturnType {
     },
     setFieldsValue(values: Recordable) {
       return tableRef.value?.setFieldsValue(values);
+    },
+    // 添加设置已选中行的方法
+    setTableCheckedRowKeys(keys: string[]) {
+      tableRef.value?.setTableCheckedRowKeys(keys);
     }
   };
   return [register, methods];
@@ -66,6 +70,8 @@ export interface TableMethods {
   getSeachForm: () => Recordable;
   setFieldsValue: (values: Recordable) => void;
   getTableLoding: ComputedRef<boolean>;
+  // 添加设置已选中行的方法接口
+  setTableCheckedRowKeys: (keys: string[]) => void;
 }
 
 export type RegisterFn = (TableInstance: TableMethods) => void;

+ 7 - 1
src/components/zt/Table/z-table.vue

@@ -65,6 +65,10 @@ export default defineComponent({
       columns: () => handleGetColumns()
     });
     const { checkedRowKeys } = useTableOperate(data, getTableProps.value.keyField, getData);
+    // 添加设置已选中行的方法
+    function setTableCheckedRowKeys(keys: string[]) {
+      checkedRowKeys.value = keys;
+    }
     function handleGetColumns() {
       const columnsData = [...propsData.columns];
       if (propsData.showTableAction) {
@@ -125,7 +129,9 @@ export default defineComponent({
       getTableData,
       getSeachForm: () => handleCommonTime(getSeachForm()),
       setFieldsValue,
-      getTableLoding
+      getTableLoding,
+      // 添加设置已选中行的方法
+      setTableCheckedRowKeys
     };
     function setTableLoading(flage: boolean) {
       loading.value = flage;

+ 9 - 1
src/locales/langs/en-us.ts

@@ -290,7 +290,15 @@ const local: App.I18n.Schema = {
     'jy-manage_activity': '',
     'order-manage_ponits-details': '',
     operation_coupon: '',
-    'order-manage_recharge-records': ''
+    'order-manage_recharge-records': '',
+    'goods-center_scenic-goods': '',
+    'operation_coupon-issuance': '',
+    'operation_coupon-manage': '',
+    'member-center': '',
+    'member-center_edit-member-type': '',
+    'member-center_member-list': '',
+    'member-center_member-order': '',
+    'member-center_member-type': ''
   },
   page: {
     login: {

+ 9 - 1
src/locales/langs/zh-cn.ts

@@ -287,7 +287,15 @@ const local: App.I18n.Schema = {
     'jy-manage_activity': '',
     'order-manage_ponits-details': '',
     operation_coupon: '',
-    'order-manage_recharge-records': ''
+    'order-manage_recharge-records': '',
+    'goods-center_scenic-goods': '',
+    'operation_coupon-issuance': '',
+    'operation_coupon-manage': '',
+    'member-center': '',
+    'member-center_edit-member-type': '',
+    'member-center_member-list': '',
+    'member-center_member-order': '',
+    'member-center_member-type': ''
   },
   page: {
     login: {

+ 7 - 0
src/router/elegant/imports.ts

@@ -30,6 +30,7 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
   "film-manage_setprice": () => import("@/views/film-manage/setprice/index.vue"),
   "goods-center_edit-health-goods": () => import("@/views/goods-center/edit-health-goods/index.vue"),
   "goods-center_health-goods": () => import("@/views/goods-center/health-goods/index.vue"),
+  "goods-center_scenic-goods": () => import("@/views/goods-center/scenic-goods/index.vue"),
   "goods-center_store-goods": () => import("@/views/goods-center/store-goods/index.vue"),
   "goods-center_type-admin": () => import("@/views/goods-center/type-admin/index.vue"),
   "goods-center_virtual-goods": () => import("@/views/goods-center/virtual-goods/index.vue"),
@@ -47,7 +48,13 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
   manage_role: () => import("@/views/manage/role/index.vue"),
   manage_schedule: () => import("@/views/manage/schedule/index.vue"),
   manage_user: () => import("@/views/manage/user/index.vue"),
+  "member-center_edit-member-type": () => import("@/views/member-center/edit-member-type/index.vue"),
+  "member-center_member-list": () => import("@/views/member-center/member-list/index.vue"),
+  "member-center_member-order": () => import("@/views/member-center/member-order/index.vue"),
+  "member-center_member-type": () => import("@/views/member-center/member-type/index.vue"),
   "operation_accounting-strategy": () => import("@/views/operation/accounting-strategy/index.vue"),
+  "operation_coupon-issuance": () => import("@/views/operation/coupon-issuance/index.vue"),
+  "operation_coupon-manage": () => import("@/views/operation/coupon-manage/index.vue"),
   operation_coupon: () => import("@/views/operation/coupon/index.vue"),
   "order-manage_after-sales-order-detail": () => import("@/views/order-manage/after-sales-order-detail/index.vue"),
   "order-manage_after-sales-order": () => import("@/views/order-manage/after-sales-order/index.vue"),

+ 74 - 0
src/router/elegant/routes.ts

@@ -171,6 +171,15 @@ export const generatedRoutes: GeneratedRoute[] = [
           i18nKey: 'route.goods-center_health-goods'
         }
       },
+      {
+        name: 'goods-center_scenic-goods',
+        path: '/goods-center/scenic-goods',
+        component: 'view.goods-center_scenic-goods',
+        meta: {
+          title: 'goods-center_scenic-goods',
+          i18nKey: 'route.goods-center_scenic-goods'
+        }
+      },
       {
         name: 'goods-center_store-goods',
         path: '/goods-center/store-goods',
@@ -397,6 +406,53 @@ export const generatedRoutes: GeneratedRoute[] = [
       }
     ]
   },
+  {
+    name: 'member-center',
+    path: '/member-center',
+    component: 'layout.base',
+    meta: {
+      title: 'member-center',
+      i18nKey: 'route.member-center'
+    },
+    children: [
+      {
+        name: 'member-center_edit-member-type',
+        path: '/member-center/edit-member-type',
+        component: 'view.member-center_edit-member-type',
+        meta: {
+          title: 'member-center_edit-member-type',
+          i18nKey: 'route.member-center_edit-member-type'
+        }
+      },
+      {
+        name: 'member-center_member-list',
+        path: '/member-center/member-list',
+        component: 'view.member-center_member-list',
+        meta: {
+          title: 'member-center_member-list',
+          i18nKey: 'route.member-center_member-list'
+        }
+      },
+      {
+        name: 'member-center_member-order',
+        path: '/member-center/member-order',
+        component: 'view.member-center_member-order',
+        meta: {
+          title: 'member-center_member-order',
+          i18nKey: 'route.member-center_member-order'
+        }
+      },
+      {
+        name: 'member-center_member-type',
+        path: '/member-center/member-type',
+        component: 'view.member-center_member-type',
+        meta: {
+          title: 'member-center_member-type',
+          i18nKey: 'route.member-center_member-type'
+        }
+      }
+    ]
+  },
   {
     name: 'operation',
     path: '/operation',
@@ -423,6 +479,24 @@ export const generatedRoutes: GeneratedRoute[] = [
           title: 'operation_coupon',
           i18nKey: 'route.operation_coupon'
         }
+      },
+      {
+        name: 'operation_coupon-issuance',
+        path: '/operation/coupon-issuance',
+        component: 'view.operation_coupon-issuance',
+        meta: {
+          title: 'operation_coupon-issuance',
+          i18nKey: 'route.operation_coupon-issuance'
+        }
+      },
+      {
+        name: 'operation_coupon-manage',
+        path: '/operation/coupon-manage',
+        component: 'view.operation_coupon-manage',
+        meta: {
+          title: 'operation_coupon-manage',
+          i18nKey: 'route.operation_coupon-manage'
+        }
       }
     ]
   },

+ 8 - 0
src/router/elegant/transform.ts

@@ -195,6 +195,7 @@ const routeMap: RouteMap = {
   "goods-center": "/goods-center",
   "goods-center_edit-health-goods": "/goods-center/edit-health-goods",
   "goods-center_health-goods": "/goods-center/health-goods",
+  "goods-center_scenic-goods": "/goods-center/scenic-goods",
   "goods-center_store-goods": "/goods-center/store-goods",
   "goods-center_type-admin": "/goods-center/type-admin",
   "goods-center_virtual-goods": "/goods-center/virtual-goods",
@@ -217,9 +218,16 @@ const routeMap: RouteMap = {
   "manage_role": "/manage/role",
   "manage_schedule": "/manage/schedule",
   "manage_user": "/manage/user",
+  "member-center": "/member-center",
+  "member-center_edit-member-type": "/member-center/edit-member-type",
+  "member-center_member-list": "/member-center/member-list",
+  "member-center_member-order": "/member-center/member-order",
+  "member-center_member-type": "/member-center/member-type",
   "operation": "/operation",
   "operation_accounting-strategy": "/operation/accounting-strategy",
   "operation_coupon": "/operation/coupon",
+  "operation_coupon-issuance": "/operation/coupon-issuance",
+  "operation_coupon-manage": "/operation/coupon-manage",
   "order-manage": "/order-manage",
   "order-manage_after-sales-order": "/order-manage/after-sales-order",
   "order-manage_after-sales-order-detail": "/order-manage/after-sales-order-detail",

+ 26 - 0
src/service/api/goods-center/scenic-goods/index.ts

@@ -0,0 +1,26 @@
+import { request } from '@/service/request';
+/**
+ * 分页获取
+ * @param params
+ * @returns
+ */
+export function fetchList(data: any) {
+  return request({
+    url: '/smqjh-pms/api/scenery/prod/findAppByPage',
+    method: 'get',
+    params: data
+  });
+}
+
+/**
+ * 获取价格
+ * @param params
+ * @returns
+ */
+export function fetchPriceList(data: any) {
+  return request({
+    url: '/smqjh-pms/api/scenery/prod/price',
+    method: 'post',
+    data
+  });
+}

+ 60 - 0
src/service/api/member-center/member-list/index.ts

@@ -0,0 +1,60 @@
+import { request } from '@/service/request';
+/**
+ * 分页获取会员列表
+ * @param params
+ * @returns
+ */
+export function fetchMemberList(data: any) {
+  return request({
+    url: '/smqjh-system/api/v1/member/account/page',
+    method: 'get',
+    params: data
+  });
+}
+/**
+ * 开通记录
+ * @param params
+ * @returns
+ */
+export function fetchOpenRecord(data: any) {
+  return request({
+    url: '/smqjh-system/api/v1/member/open-record/page',
+    method: 'get',
+    params: data
+  });
+}
+
+/**
+ * 导出
+ * @returns
+ */
+export function fetchExport(data: any) {
+  return request({
+    url: `/smqjh-system/api/v1/member/export/task/start`,
+    method: 'post',
+    data
+  });
+}
+
+/**
+ * 导出记录
+ * @returns
+ */
+export function fetchExportRecord(data: any) {
+  return request({
+    url: `/smqjh-system/api/v1/member/export/task/page`,
+    method: 'get',
+    params: data
+  });
+}
+
+/**
+ * 下载
+ * @returns
+ */
+export function fetchDownload(taskId: any) {
+  return request({
+    url: `/smqjh-system/api/v1/member/export/task/download/${taskId}`,
+    method: 'get'
+  });
+}

+ 24 - 0
src/service/api/member-center/member-order/index.ts

@@ -0,0 +1,24 @@
+import { request } from '@/service/request';
+/**
+ * 分页获取
+ * @param params
+ * @returns
+ */
+export function fetchOrderList(data: any) {
+  return request({
+    url: '/smqjh-system/api/v1/member/order/page',
+    method: 'get',
+    params: data
+  });
+}
+
+/**
+ * 详情
+ * @returns
+ */
+export function fetchOrderDetail(id: any) {
+  return request({
+    url: `/smqjh-system/api/v1/member/order/detail/${id}`,
+    method: 'get'
+  });
+}

+ 94 - 0
src/service/api/member-center/member-type/index.ts

@@ -0,0 +1,94 @@
+import { request } from '@/service/request';
+/**
+ * 分页获取
+ * @param params
+ * @returns
+ */
+export function fetchTypeList(data: any) {
+  return request({
+    url: '/smqjh-pms/api/v1/member/type/page',
+    method: 'get',
+    params: data
+  });
+}
+
+/**
+ *
+ * 删除
+ * @returns
+ */
+export function fetchDelType(id: string) {
+  return request({
+    url: `/smqjh-pms/api/v1/member/type/delete/${id}`,
+    method: 'delete'
+  });
+}
+
+/**
+ * 保存
+ * @returns
+ */
+export function fetchAddType(data: any) {
+  return request({
+    url: '/smqjh-pms/api/v1/member/type/save',
+    method: 'post',
+    data
+  });
+}
+
+/**
+ * 编辑
+ * @returns
+ */
+export function fetchEditType(data: any) {
+  return request({
+    url: '/smqjh-pms/api/v1/member/type/update',
+    method: 'put',
+    data
+  });
+}
+
+/**
+ * 详情
+ * @returns
+ */
+export function fetchTypeDetail(id: any) {
+  return request({
+    url: `/smqjh-pms/api/v1/member/type/detail/${id}`,
+    method: 'get'
+  });
+}
+
+/**
+ * 商品分页
+ * @returns
+ */
+export function fetchGoodsList(data: any) {
+  return request({
+    url: '/smqjh-pms/api/v1/product/page',
+    method: 'post',
+    data
+  });
+}
+
+/**
+ * 企业渠道
+ * @returns
+ */
+export function fetchFirstChannel() {
+  return request({
+    url: `/smqjh-system/api/v1/channel/listFirstChannel`,
+    method: 'get'
+  });
+}
+
+/**
+ * 切换状态
+ * @returns
+ */
+export function fetchUpdateStatus(id: any, status: any) {
+  return request({
+    url: `/smqjh-pms/api/v1/member/type/changeStatus/${id}/${status}`,
+    method: 'put'
+  });
+}

+ 122 - 0
src/service/api/operation/coupon-manage/index.ts

@@ -0,0 +1,122 @@
+import { request } from '@/service/request';
+
+/**
+ * 所有列表
+ * @returns
+ */
+export function fetchGetActivityList(data: any) {
+  return request({
+    url: `/smqjh-system/sys-api/couponLocal/page`,
+    method: 'get',
+    params: data
+  });
+}
+
+/**
+ *
+    新增
+ * @param data
+ * @returns
+ */
+
+export function fetchAddActivity(data: any) {
+  return request({
+    url: `/smqjh-system/sys-api/couponLocal/save`,
+    method: 'post',
+    data
+  });
+}
+
+/**
+ *
+    编辑
+ * @param data
+ * @returns
+ */
+
+export function fetchEditActivity(data: any) {
+  return request({
+    url: `/smqjh-system/sys-api/couponLocal/update`,
+    method: 'PUT',
+    data
+  });
+}
+
+/**
+ *
+    删除
+ * @param data
+ * @returns
+ */
+
+export function delActivity(id: any) {
+  return request({
+    url: `/smqjh-system/sys-api/couponLocal/delete/${id}`,
+    method: 'DELETE'
+  });
+}
+
+/**
+ * 获取领取记录
+ * @returns
+ */
+export function fetchList(data: any) {
+  return request({
+    url: `/smqjh-system/sys-api/memberCoupon/page`,
+    method: 'get',
+    params: data
+  });
+}
+
+/**
+ * 导入失败记录
+ * @returns
+ */
+export function fetchFailList(data: any) {
+  return request({
+    url: `/smqjh-system/sys-api/memberCoupon/findByTypePage`,
+    method: 'get',
+    params: data
+  });
+}
+
+/**
+ *批量导入
+ * @returns
+ */
+export function fetchImport(data: any) {
+  return request({
+    url: `/smqjh-system/sys-api/memberCoupon/uploadExcelGoods`,
+    method: 'post',
+    data,
+    headers: {
+      'Content-Type': 'multipart/form-data'
+    }
+  });
+}
+
+/**
+ * 下载失败记录
+ * @returns
+ */
+export function fetchExportFailure(data: any) {
+  return request({
+    url: `/smqjh-system/sys-api/memberCoupon/downloadErrorExcel`,
+    method: 'get',
+    params: data
+  });
+}
+
+/**
+ *
+    删除优惠券
+ * @param data
+ * @returns
+ */
+
+export function delCoupon(id: any) {
+  return request({
+    url: `/smqjh-system/sys-api/memberCoupon/delete/${id}`,
+    method: 'DELETE'
+  });
+}

+ 1 - 1
src/typings/api.d.ts

@@ -733,7 +733,7 @@ declare namespace Api {
        */
       dvyTime?: string;
       /**
-       * 配送类型(1:快递 2:自提 3:及时配送)
+       * 配送类型(1:快递 2:自提 3:及时配送 10:商家自送
        */
       dvyType: number;
       /**

+ 2 - 0
src/typings/components.d.ts

@@ -31,6 +31,7 @@ declare module 'vue' {
     'IconFileIcons:microsoftExcel': typeof import('~icons/file-icons/microsoft-excel')['default']
     IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
     IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
+    'IconIconParkSolid:reduceOne': typeof import('~icons/icon-park-solid/reduce-one')['default']
     'IconIconParkTwotone:mailDownload': typeof import('~icons/icon-park-twotone/mail-download')['default']
     IconIcRoundDelete: typeof import('~icons/ic/round-delete')['default']
     IconIcRoundPlus: typeof import('~icons/ic/round-plus')['default']
@@ -51,6 +52,7 @@ declare module 'vue' {
     NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
     NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
     NButton: typeof import('naive-ui')['NButton']
+    NCalendar: typeof import('naive-ui')['NCalendar']
     NCard: typeof import('naive-ui')['NCard']
     NCheckbox: typeof import('naive-ui')['NCheckbox']
     NCheckboxGroup: typeof import('naive-ui')['NCheckboxGroup']

+ 16 - 0
src/typings/elegant-router.d.ts

@@ -49,6 +49,7 @@ declare module "@elegant-router/types" {
     "goods-center": "/goods-center";
     "goods-center_edit-health-goods": "/goods-center/edit-health-goods";
     "goods-center_health-goods": "/goods-center/health-goods";
+    "goods-center_scenic-goods": "/goods-center/scenic-goods";
     "goods-center_store-goods": "/goods-center/store-goods";
     "goods-center_type-admin": "/goods-center/type-admin";
     "goods-center_virtual-goods": "/goods-center/virtual-goods";
@@ -71,9 +72,16 @@ declare module "@elegant-router/types" {
     "manage_role": "/manage/role";
     "manage_schedule": "/manage/schedule";
     "manage_user": "/manage/user";
+    "member-center": "/member-center";
+    "member-center_edit-member-type": "/member-center/edit-member-type";
+    "member-center_member-list": "/member-center/member-list";
+    "member-center_member-order": "/member-center/member-order";
+    "member-center_member-type": "/member-center/member-type";
     "operation": "/operation";
     "operation_accounting-strategy": "/operation/accounting-strategy";
     "operation_coupon": "/operation/coupon";
+    "operation_coupon-issuance": "/operation/coupon-issuance";
+    "operation_coupon-manage": "/operation/coupon-manage";
     "order-manage": "/order-manage";
     "order-manage_after-sales-order": "/order-manage/after-sales-order";
     "order-manage_after-sales-order-detail": "/order-manage/after-sales-order-detail";
@@ -148,6 +156,7 @@ declare module "@elegant-router/types" {
     | "jy-manage"
     | "login"
     | "manage"
+    | "member-center"
     | "operation"
     | "order-manage"
     | "user-center"
@@ -186,6 +195,7 @@ declare module "@elegant-router/types" {
     | "film-manage_setprice"
     | "goods-center_edit-health-goods"
     | "goods-center_health-goods"
+    | "goods-center_scenic-goods"
     | "goods-center_store-goods"
     | "goods-center_type-admin"
     | "goods-center_virtual-goods"
@@ -203,7 +213,13 @@ declare module "@elegant-router/types" {
     | "manage_role"
     | "manage_schedule"
     | "manage_user"
+    | "member-center_edit-member-type"
+    | "member-center_member-list"
+    | "member-center_member-order"
+    | "member-center_member-type"
     | "operation_accounting-strategy"
+    | "operation_coupon-issuance"
+    | "operation_coupon-manage"
     | "operation_coupon"
     | "order-manage_after-sales-order-detail"
     | "order-manage_after-sales-order"

+ 2 - 2
src/views/djk-manage/edit-activity/index.vue

@@ -1,10 +1,9 @@
 <script setup lang="tsx">
 import { nextTick, onMounted, ref } from 'vue';
-import { useRoute } from 'vue-router';
+import { useRoute, useRouter } from 'vue-router';
 import type { DataTableColumns, FormInst, FormRules } from 'naive-ui';
 import { NForm, NImage, NInput, useMessage } from 'naive-ui';
 import dayjs from 'dayjs';
-import { router } from '@/router';
 import { fetchActivityDetail, fetchAddActivity, fetchEditActivity } from '@/service/api/djk-manage/activity';
 import { fetchProductList } from '@/service/api/goods-center/health-goods';
 import { fetchGetStoreList } from '@/service/api/xsb-manage/store-info';
@@ -12,6 +11,7 @@ import { useTable } from '@/components/zt/Table/hooks/useTable';
 import { useModal } from '@/components/zt/Modal/hooks/useModal';
 import type { FormSchema } from '@/components/zt/Form/types/form';
 import ZUpload from '../../../components/zt/upload/z-upload.vue';
+const router = useRouter();
 const current = ref('A');
 const route = useRoute();
 const formRef = ref<FormInst | null>(null);

+ 3 - 1
src/views/film-manage/film-list/index.vue

@@ -1,10 +1,12 @@
 <script setup lang="tsx">
 import { ref, useTemplateRef } from 'vue';
+import { useRouter } from 'vue-router';
 import { NButton } from 'naive-ui';
-import { router } from '@/router';
 import { fetchGetChannelList, fetchImportGoods } from '@/service/api/goods-center/store-goods';
 import { fetchMoivecinemaList, fetchMovieList } from '@/service/api/film-manage/film-list';
 import { useTable } from '@/components/zt/Table/hooks/useTable';
+
+const router = useRouter();
 const importTemplateRef = useTemplateRef('importTemplateRef');
 
 const options = ref<Api.goods.Channel[]>([]);

+ 0 - 1
src/views/goods-center/edit-health-goods/index.vue

@@ -3,7 +3,6 @@ import { computed, ref } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
 import type { FormInst, FormRules } from 'naive-ui';
 import { NForm, NInput, NSwitch, useMessage } from 'naive-ui';
-// import { router } from '@/router';
 import { fetchAddGoods, fetchEditGoods, fetchGoodsDetail } from '@/service/api/goods-center/health-goods';
 import { fetchGetStoreList } from '@/service/api/xsb-manage/store-info';
 import ZUpload from '../../../components/zt/upload/z-upload.vue';

+ 240 - 0
src/views/goods-center/scenic-goods/index.vue

@@ -0,0 +1,240 @@
+<script setup lang="tsx">
+import { computed, ref } from 'vue';
+import { NButton, NImage } from 'naive-ui';
+import dayjs from 'dayjs';
+// import { fetchGetStoreList } from '@/service/api/xsb-manage/store-info';
+import { fetchList, fetchPriceList } from '@/service/api/goods-center/scenic-goods';
+import { commonExport } from '@/utils/common';
+import { useTable } from '@/components/zt/Table/hooks/useTable';
+import ButtonIcon from '@/components/custom/button-icon.vue';
+import { useModal } from '@/components/zt/Modal/hooks/useModal';
+const productNo = ref();
+let travelDate = dayjs().format('YYYY-MM-DD');
+const priceObj = ref<
+  Record<
+    string,
+    {
+      settlementPrice: number | string;
+      salePrice: number | string;
+      ticketAvailability: number | string;
+    }
+  >
+>({});
+const columns: NaiveUI.TableColumn<Api.goods.ShopSku>[] = [
+  {
+    key: 'productNo',
+    title: '产品编号',
+    align: 'center',
+    width: 100
+  },
+  {
+    key: 'productName',
+    title: '产品名称',
+    align: 'center',
+    width: 200
+  },
+  {
+    key: 'mobile',
+    title: '产品图片',
+    align: 'center',
+    width: 100,
+    render: row => {
+      return <NImage src={row.img} width="100" height="100" />;
+    }
+  },
+  {
+    key: 'viewName',
+    title: '景区名称',
+    align: 'center',
+    width: 100
+  },
+  {
+    key: 'receiveTime',
+    title: '业务类型',
+    align: 'center',
+    width: 100,
+    render: () => {
+      return '景区门票';
+    }
+  },
+  {
+    key: 'price',
+    title: '价格',
+    align: 'center',
+    width: 70,
+    render: row => {
+      return (
+        <ButtonIcon
+          text
+          type="primary"
+          onClick={() => handlePrice(row.productNo)}
+          icon="material-symbols:calendar-month-rounded"
+          tooltip-content="价格"
+        />
+      );
+    }
+  },
+  {
+    key: 'state',
+    title: '状态',
+    align: 'center',
+    width: 100,
+    render: row => {
+      return row.state == 1 ? '下架' : '上架';
+    }
+  },
+  {
+    key: 'updateTime',
+    title: '更新时间',
+    align: 'center',
+    width: 120
+  }
+];
+
+const [registerTable, { getTableData, getSeachForm, setTableLoading }] = useTable({
+  searchFormConfig: {
+    schemas: [
+      {
+        label: '产品编号',
+        component: 'NInput',
+        field: 'productNo'
+      },
+      {
+        label: '产品名称',
+        component: 'NInput',
+        field: 'productName'
+      },
+      {
+        label: '景区名称',
+        component: 'NInput',
+        field: 'viewName'
+      },
+      {
+        label: '状态',
+        component: 'NSelect',
+        field: 'state',
+        componentProps: {
+          options: [
+            { label: '上架', value: '0' },
+            { label: '下架', value: '1' }
+          ]
+        }
+      }
+    ],
+    inline: false,
+    size: 'small',
+    labelPlacement: 'left',
+    isFull: false
+  },
+  tableConfig: {
+    keyField: 'skuId',
+    title: '领取列表',
+    showAddButton: false,
+    scrollX: 1800,
+    fieldMapToTime: [['createTime', ['receiveStartTime', 'receiveEndTime']]]
+  }
+});
+
+const [registerModalPrice, { openModal, setModalLoading }] = useModal({
+  title: '价格',
+  width: 800,
+  height: 700,
+  showFooter: false
+});
+
+const tableData = computed(() => {
+  return getTableData();
+});
+
+async function handleExport() {
+  setTableLoading(true);
+  try {
+    await commonExport('/smqjh-pms/api/scenery/prod/export', getSeachForm(), '景区产品.xlsx');
+  } finally {
+    setTableLoading(false);
+  }
+}
+
+async function handlePrice(no: any) {
+  productNo.value = no;
+  openModal();
+  setModalLoading(true);
+  const { data } = await fetchPriceList({ travelDate, productNo: productNo.value });
+  console.log(data);
+  priceObj.value = {};
+  data.ticketPrices.forEach((item: any) => {
+    priceObj.value[item.date] = {
+      settlementPrice: item.settlementPrice,
+      salePrice: item.salePrice,
+      ticketAvailability: item.ticketAvailability
+    };
+  });
+
+  setModalLoading(false);
+}
+
+function panelChange(date: any) {
+  console.log('panelChange', date.year, date.month);
+  travelDate = `${date.year}-${date.month < 10 ? `0${date.month}` : date.month}-01`;
+  handlePrice(productNo.value);
+}
+
+function getColorName(state: any) {
+  if (state == '充足') {
+    return { text: 'blue', bg: '#B4E8F4' };
+  } else if (state == '紧张') {
+    return { text: 'orange', bg: '#F6DFB1' };
+  }
+
+  return { text: 'green', bg: '#FCC9C9' };
+}
+
+function isDateDisabled(timestamp: any) {
+  if (dayjs(timestamp).add(1, 'day').isBefore(dayjs())) {
+    return true;
+  }
+  return false;
+}
+</script>
+
+<template>
+  <LayoutTable>
+    <ZTable :columns="columns" :show-table-action="false" :api="fetchList" @register="registerTable">
+      <template #prefix="{ loading }">
+        <NSpace>
+          <NButton size="small" :disabled="tableData.length == 0" :loading="loading" @click="handleExport">
+            导出全部
+          </NButton>
+        </NSpace>
+      </template>
+    </ZTable>
+
+    <BasicModal @register="registerModalPrice">
+      <NCalendar v-slot="{ year, month, date }" :is-date-disabled="isDateDisabled" @panel-change="panelChange">
+        <div
+          v-if="priceObj[dayjs(`${year}-${month}-${date}`).format('YYYY-MM-DD')]"
+          class="h-full flex flex-col justify-between"
+        >
+          <div class="mt-10px text-12px">
+            <div>结算价: {{ priceObj[dayjs(`${year}-${month}-${date}`).format('YYYY-MM-DD')]?.settlementPrice }}</div>
+            <div>零售价: {{ priceObj[dayjs(`${year}-${month}-${date}`).format('YYYY-MM-DD')]?.salePrice }}</div>
+          </div>
+          <div
+            class="mb-2px text-center"
+            :style="{
+              backgroundColor: getColorName(
+                priceObj[dayjs(`${year}-${month}-${date}`).format('YYYY-MM-DD')]?.ticketAvailability
+              ).bg,
+              color: getColorName(priceObj[dayjs(`${year}-${month}-${date}`).format('YYYY-MM-DD')]?.ticketAvailability)
+                .text
+            }"
+          >
+            {{ priceObj[dayjs(`${year}-${month}-${date}`).format('YYYY-MM-DD')]?.ticketAvailability }}
+          </div>
+        </div>
+      </NCalendar>
+    </BasicModal>
+  </LayoutTable>
+</template>
+
+<style scoped></style>

+ 13 - 0
src/views/goods-center/virtual-goods/index.vue

@@ -162,6 +162,19 @@ const columns: NaiveUI.TableColumn<Api.goods.ShopSku>[] = [
       );
     }
   },
+
+  {
+    key: 'productStatusMsg',
+    title: '变更原因',
+    align: 'center',
+    width: 120,
+    ellipsis: {
+      tooltip: true
+    },
+    render: (row: any) => {
+      return row.productStatus == 1 ? row.productStatusMsg : '';
+    }
+  },
   {
     key: 'updateTime',
     title: '更新时间',

+ 1 - 1
src/views/jy-manage/activity/index.vue

@@ -123,7 +123,7 @@ const [
   }
 ] = useModalFrom({
   modalConfig: {
-    title: '一级分类',
+    title: '优惠券',
     isShowHeaderText: true
   },
   formConfig: {

+ 939 - 0
src/views/member-center/edit-member-type/index.vue

@@ -0,0 +1,939 @@
+<script setup lang="tsx">
+import { computed, nextTick, onMounted, ref, watch } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import type { DataTableColumns, FormInst, FormRules } from 'naive-ui';
+import { NForm, NImage, NInput, NSelect, useMessage } from 'naive-ui';
+import {
+  fetchAddType,
+  fetchEditType,
+  fetchFirstChannel,
+  fetchGoodsList,
+  fetchTypeDetail
+} from '@/service/api/member-center/member-type';
+import { fetchGetActivityList } from '@/service/api/operation/coupon-manage';
+import { fetchGetStoreList } from '@/service/api/xsb-manage/store-info';
+import { useTable } from '@/components/zt/Table/hooks/useTable';
+import { useModal } from '@/components/zt/Modal/hooks/useModal';
+import ZUpload from '../../../components/zt/upload/z-upload.vue';
+const router = useRouter();
+const current = ref('A');
+const route = useRoute();
+const formRef = ref<FormInst | null>(null);
+const message = useMessage();
+const disabled = ref(false);
+const loading = ref(false);
+
+const cancelOptions = [
+  { label: '立即生效', value: 'IMMEDIATE' },
+  { label: '次月生效', value: 'NEXT_MONTH' }
+];
+// 1 厂家直冲,2 卖家直发,3 官方直充,4 扫码直充,5 卖家代充,6 卡密
+const rechargeOptions = [
+  { label: '厂家直充', value: 1 },
+  { label: '卖家直发', value: 2 },
+  { label: '官方直充', value: 3 },
+  { label: '扫码直充', value: 4 },
+  { label: '卖家代充', value: 5 },
+  { label: '卡密', value: 6 }
+];
+
+const channelOptions = ref<{ label: string; value: string; id: number }[]>([]);
+
+const model = ref({
+  id: 0,
+  memberName: '',
+  coverImg: '',
+  faceAmount: 0,
+  stock: 0,
+  settleAmount: 0,
+  rechargeType: null,
+  brandName: '市民请集合',
+  businessType: 'XSB',
+  useValidDays: 0,
+  cancelRule: '',
+  useRule: '',
+  rightsDesc: '',
+  openChannelCodes: '',
+  priceChannelId: '',
+  benefitConfigJson: { oilPerLiterDiscount: 0, mallDiscountRate: 0, chargePerKwhDiscount: 0, parkingDiscountRate: 0 },
+  couponConfigJson: [{ couponId: '', count: 1 }] as any,
+  giftConfigJson: [] as any,
+  status: 0
+});
+
+const isCheckedOilPerLiterDiscount = ref(false);
+const isCheckedMallDiscountRate = ref(false);
+const isCheckedChargePerKwhDiscount = ref(false);
+const isCheckedParkingDiscountRate = ref(false);
+const isCheckedCouponConfigJson = ref(false);
+const isCheckedGiftConfigJson = ref(false);
+const isCheckedStock = computed(() => {
+  if (model.value.stock === -1) {
+    return true;
+  }
+  return false;
+});
+
+const currentChannel = ref<any>();
+
+const rules: FormRules = {
+  memberName: {
+    required: true,
+    trigger: ['blur', 'input'],
+    message: '请输入'
+  },
+  coverImg: {
+    type: 'string',
+    required: true,
+    trigger: ['blur', 'change'],
+    message: '请上传'
+  },
+  faceAmount: {
+    type: 'number',
+    min: 1,
+    max: 100000,
+    required: true,
+    trigger: ['blur', 'change'],
+    message: '请输入'
+  },
+  settleAmount: {
+    min: 1,
+    max: 100000,
+    type: 'number',
+    required: true,
+    trigger: ['blur', 'input'],
+    message: '请输入'
+  },
+  brandName: {
+    type: 'string',
+    required: true,
+    trigger: ['blur', 'change'],
+    message: '请输入'
+  },
+  rechargeType: {
+    type: 'number',
+    required: true,
+    trigger: ['blur', 'change'],
+    message: '请选择'
+  },
+  stock: {
+    type: 'number',
+    required: true,
+    trigger: ['blur', 'change'],
+    message: '请输入'
+  },
+  useValidDays: {
+    type: 'number',
+    required: true,
+    trigger: ['blur', 'change'],
+    message: '请输入'
+  },
+  cancelRule: {
+    required: true,
+    trigger: ['blur', 'change'],
+    message: '请选择'
+  },
+  useRule: {
+    required: true,
+    trigger: ['blur', 'change'],
+    message: '请输入'
+  },
+  openChannelCodes: {
+    required: true,
+    trigger: ['blur', 'change'],
+    message: '请输入'
+  },
+  priceChannelId: {
+    type: 'number',
+    required: true,
+    trigger: ['blur', 'change'],
+    message: '请输入'
+  },
+  benefitConfigJson: {
+    required: true,
+    trigger: ['blur', 'change'],
+    message: '请输入'
+  }
+};
+
+interface ProductItem {
+  goodsImg: string;
+  goodsName: string;
+  goodsCode: string;
+  name?: string;
+}
+
+const columns: DataTableColumns<ProductItem> = [
+  {
+    type: 'selection',
+    multiple: true
+  },
+  {
+    key: 'hbSkuId',
+    title: '商品ID',
+    align: 'left',
+    width: 200
+  },
+  {
+    key: 'skuCode',
+    title: '商品编码',
+    align: 'center',
+    width: 120,
+    ellipsis: {
+      tooltip: true
+    }
+  },
+
+  {
+    key: 'pic',
+    title: '商品图片',
+    align: 'center',
+    width: 120,
+    render: (row: any) => {
+      return <n-image src={row.pic} width={60} height={60}></n-image>;
+    }
+  },
+
+  {
+    key: 'prodName',
+    title: '商品名称',
+    align: 'center',
+    width: 120,
+    ellipsis: {
+      tooltip: true
+    }
+  },
+  {
+    key: 'shopSkuStocks',
+    title: '库存',
+    align: 'center',
+    width: 120,
+    ellipsis: {
+      tooltip: true
+    }
+  },
+  {
+    key: 'channelVOS',
+    title: '价格',
+    align: 'center',
+    width: 120,
+    render: (row: any) => {
+      return (
+        console.log(currentChannel.value, 'currentChannel'),
+        (
+          <div>
+            {row.channelVOS.map((it: Api.government.ChannelVO) => {
+              if (it.channelId == currentChannel.value?.id) {
+                return (
+                  <div>
+                    {it.channelName}:¥{it.price}
+                  </div>
+                );
+              }
+              return null;
+            })}
+          </div>
+        )
+      );
+    }
+  },
+  {
+    key: 'shopName',
+    title: '门店名称',
+    align: 'center',
+    width: 120,
+    ellipsis: {
+      tooltip: true
+    }
+  }
+];
+
+const [registerModalTable, { refresh, getTableCheckedRowKeys, getTableData, setFieldsValue, setTableCheckedRowKeys }] =
+  useTable({
+    searchFormConfig: {
+      schemas: [
+        // {
+        //   label: '销售渠道',
+        //   component: 'ApiSelect',
+        //   field: 'channelNo',
+        //   componentProps: {
+        //     api: fetchFirstChannel,
+        //     resultFeild: 'data',
+        //     labelFeild: 'channelName',
+        //     valueFeild: 'channelNo'
+        //   }
+        // },
+        {
+          label: '销售渠道',
+          component: 'NSelect',
+          field: 'channelNo',
+          render: form => {
+            return (
+              <NSelect
+                v-model:value={form.model.channelNo}
+                placeholder="请选择销售渠道"
+                options={channelOptions.value}
+                on-update:value={changeChannel}
+              ></NSelect>
+            );
+          }
+        },
+        {
+          label: '门店名称',
+          component: 'ApiSelect',
+          field: 'shopId',
+          componentProps: {
+            api: fetchGetStoreList,
+            resultFeild: 'data.list',
+            labelFeild: 'shopName',
+            valueFeild: 'shopId'
+          }
+        },
+        {
+          label: '商品名称',
+          component: 'NInput',
+          field: 'skuName',
+          componentProps: {
+            placeholder: '请输入商品名称'
+          }
+        },
+        {
+          label: '商品ID',
+          component: 'NInput',
+          field: 'keywords'
+        },
+        {
+          label: '商品编码',
+          component: 'NInput',
+          field: 'skuCode'
+        }
+      ],
+      inline: false,
+      size: 'small',
+      labelPlacement: 'left',
+      isFull: false
+    },
+    tableConfig: {
+      keyField: 'skuId',
+      title: '商品列表',
+      showAddButton: false,
+      minHeight: 650
+    }
+  });
+
+const [registerModal, { openModal, closeModal, setModalProps }] = useModal({
+  title: `商品选择(已选${model.value.giftConfigJson.length}项)`,
+  height: 800,
+  width: 1200
+});
+watch(
+  () => getTableCheckedRowKeys(),
+  val => {
+    setModalProps({
+      title: `商品选择(已选${val.length}项)`
+    });
+  }
+);
+function changeChannel(value: string, opt: any) {
+  setFieldsValue({
+    channelNo: value
+  });
+  console.log(11111111111, value, opt);
+  currentChannel.value = opt;
+}
+function getChannel() {
+  fetchFirstChannel().then(res => {
+    channelOptions.value = res.data.map((item: any) => {
+      return {
+        label: item.channelName,
+        value: item.channelNo,
+        id: item.id
+      };
+    });
+    currentChannel.value = channelOptions.value[0];
+
+    console.log(res);
+  });
+}
+
+getChannel();
+function handleValidateButtonClick(e: MouseEvent) {
+  e.preventDefault();
+  formRef.value?.validate(async errors => {
+    if (!errors) {
+      loading.value = true;
+
+      const form = JSON.parse(JSON.stringify(model.value));
+      let rightsDesc = '';
+      let num = 0;
+
+      // 除了权益配置 加油 充电 停车 快消品数据
+      if (!isCheckedChargePerKwhDiscount.value) {
+        delete form.benefitConfigJson.chargePerKwhDiscount;
+      } else {
+        num += 1;
+        rightsDesc += `${num}、全省贵阳城投充电桩充电优惠${form.benefitConfigJson.chargePerKwhDiscount}元/度;\n `;
+      }
+      if (!isCheckedOilPerLiterDiscount.value) {
+        delete form.benefitConfigJson.oilPerLiterDiscount;
+      } else {
+        num += 1;
+        rightsDesc += `${num}、全省中国石化加油站加油优惠${form.benefitConfigJson.oilPerLiterDiscount}元/升;\n `;
+      }
+      if (!isCheckedMallDiscountRate.value) {
+        delete form.benefitConfigJson.mallDiscountRate;
+      } else {
+        num += 1;
+        rightsDesc += `${num}、市民请结合平台超6000+快消品优惠${form.benefitConfigJson.mallDiscountRate}%;\n `;
+      }
+      if (!isCheckedParkingDiscountRate.value) {
+        delete form.benefitConfigJson.parkingDiscountRate;
+      } else {
+        num += 1;
+        rightsDesc += `${num}、全场18000+贵阳城投停车位停车优惠${form.benefitConfigJson.parkingDiscountRate}%;\n `;
+      }
+
+      form.benefitConfigJson = JSON.stringify(form.benefitConfigJson);
+
+      // 处理优惠券配置
+      if (!isCheckedCouponConfigJson.value) {
+        form.couponConfigJson = '';
+      } else {
+        num += 1;
+        form.couponConfigJson = JSON.stringify(form.couponConfigJson);
+        rightsDesc += `${num}、市民请集合本地生活服务平台${form.couponConfigJson.length}张优惠券;\n `;
+      }
+
+      // 处理赠品配置
+      if (!isCheckedGiftConfigJson.value) {
+        form.giftConfigJson = '';
+      } else {
+        num += 1;
+        form.giftConfigJson = JSON.stringify(form.giftConfigJson);
+        rightsDesc += `${num}、提供${form.giftConfigJson.length}种选1种商品赠送;\n `;
+      }
+      form.rightsDesc = rightsDesc;
+      let res;
+      if (route.query.mode == 'edit') {
+        res = await fetchEditType(form);
+      } else {
+        res = await fetchAddType(form);
+      }
+      loading.value = false;
+      console.log(res);
+
+      if (!res.error) {
+        router.push({
+          path: '/member-center/member-type'
+        });
+      }
+    } else {
+      console.log(errors);
+      message.error('验证失败');
+    }
+  });
+}
+
+async function getDetail() {
+  // 获取详情接口
+  const res = await fetchTypeDetail(route.query.id);
+  if (res.data) {
+    model.value = res.data;
+    if (res.data.benefitConfigJson) {
+      model.value.benefitConfigJson = JSON.parse(res.data.benefitConfigJson);
+    } else {
+      model.value.benefitConfigJson = {
+        oilPerLiterDiscount: 0,
+        mallDiscountRate: 0,
+        chargePerKwhDiscount: 0,
+        parkingDiscountRate: 0
+      };
+    }
+
+    // 处理配置权益
+    if (model.value.benefitConfigJson.chargePerKwhDiscount) {
+      isCheckedChargePerKwhDiscount.value = true;
+    } else {
+      model.value.benefitConfigJson.chargePerKwhDiscount = 0;
+    }
+    if (model.value.benefitConfigJson.oilPerLiterDiscount) {
+      isCheckedOilPerLiterDiscount.value = true;
+    } else {
+      model.value.benefitConfigJson.oilPerLiterDiscount = 0;
+      console.log('dfasfasf', model.value.benefitConfigJson.oilPerLiterDiscount);
+    }
+    if (model.value.benefitConfigJson.parkingDiscountRate) {
+      isCheckedParkingDiscountRate.value = true;
+    } else {
+      model.value.benefitConfigJson.parkingDiscountRate = 0;
+    }
+    if (model.value.benefitConfigJson.mallDiscountRate) {
+      isCheckedMallDiscountRate.value = true;
+    } else {
+      model.value.benefitConfigJson.mallDiscountRate = 0;
+    }
+
+    // 处理优惠券配置
+    if (model.value.couponConfigJson) {
+      isCheckedCouponConfigJson.value = true;
+    }
+    if (res.data.couponConfigJson) {
+      model.value.couponConfigJson = JSON.parse(res.data.couponConfigJson);
+    } else {
+      model.value.couponConfigJson = [];
+    }
+
+    // 处理赠品配置
+    if (model.value.giftConfigJson) {
+      isCheckedGiftConfigJson.value = true;
+    }
+
+    if (res.data.giftConfigJson) {
+      model.value.giftConfigJson = JSON.parse(res.data.giftConfigJson);
+    } else {
+      model.value.giftConfigJson = [];
+    }
+  }
+}
+
+async function handleAdd(type: string) {
+  if (type === 'A') {
+    // 添加新的优惠券配置项
+    model.value.couponConfigJson.push({
+      couponId: null,
+      count: null
+    });
+  } else {
+    current.value = type;
+    openModal();
+    await nextTick();
+    setFieldsValue({
+      channelNo: channelOptions.value[0]?.value
+    });
+    console.log(11111111111, channelOptions.value[0]?.value);
+
+    await nextTick();
+    setTimeout(() => {
+      refresh();
+    }, 1000);
+    if (model.value.giftConfigJson.length) {
+      const selectedKeys = model.value.giftConfigJson.map((item: any) => item.skuId);
+      setTableCheckedRowKeys(selectedKeys);
+    }
+  }
+}
+
+/**
+ * 商品项去重:根据 skuId + shopId 唯一判断
+ * @param {Array} list 原始数组(你的结构)
+ * @returns {Array} 去重后的新数组
+ */
+function uniqueGoodsList(list: any[]) {
+  // 用 Map 存储唯一键:skuId_shopId
+  const map = new Map();
+
+  // 遍历数组,保留【最后一次】出现的项
+  list.forEach((item: any) => {
+    // 组合唯一标识
+    const key = `${item.skuId}_${item.shopId}`;
+    map.set(key, item);
+  });
+
+  // 转成数组返回
+  return Array.from(map.values());
+}
+function choose() {
+  const keys = getTableCheckedRowKeys();
+  const data = getTableData();
+  closeModal();
+
+  data.forEach(item => {
+    keys.forEach((key: any) => {
+      if (item.skuId === key) {
+        model.value.giftConfigJson.push({
+          skuId: item.skuId,
+          quantity: 1,
+          shopId: item.shopId,
+          channelId: 1,
+          skuName: item.skuName,
+          skuCode: item.skuCode,
+          specName: '-',
+          pic: item.pic,
+          stock: item.shopSkuStocks
+        });
+        model.value.giftConfigJson = uniqueGoodsList(model.value.giftConfigJson);
+      }
+    });
+  });
+
+  console.log(keys, '选择的商品', data);
+}
+
+function handleDelete(type: string, index: number) {
+  if (type === 'A') {
+    if (model.value.couponConfigJson.length == 1) {
+      window.$message?.error('请至少保留一个优惠券配置项');
+      return;
+    }
+    // 删除优惠券配置项
+    model.value.couponConfigJson.splice(index, 1);
+  } else {
+    model.value.giftConfigJson.splice(index, 1);
+  }
+}
+
+function handleCheckedChange(checked: boolean) {
+  if (checked) {
+    model.value.stock = -1; // 不限制库存时,设置stock为-1
+  } else {
+    model.value.stock = 0; // 恢复默认库存值
+  }
+}
+
+onMounted(() => {
+  if (route.query.id) {
+    if (route.query.mode === 'detail') {
+      disabled.value = true;
+    }
+    getDetail();
+  }
+  console.log(route.query);
+});
+</script>
+
+<template>
+  <div class="edit-health-goods pl-20px pt-20px">
+    <NForm
+      ref="formRef"
+      :model="model"
+      :rules="rules"
+      label-placement="left"
+      label-width="100px"
+      require-mark-placement="right-hanging"
+      size="medium"
+      class="max-w-840px"
+      :disabled="disabled"
+    >
+      <div class="font-600">基本信息</div>
+      <NFormItem label="会员名称" path="memberName">
+        <div class="w-100%">
+          <NInput v-model:value="model.memberName" :maxlength="20" placeholder="" />
+          <div class="text-12px">最多支持20个汉字;</div>
+        </div>
+      </NFormItem>
+      <NFormItem label="主图" path="coverImg">
+        <ZUpload v-model:value="model.coverImg" :max="1"></ZUpload>
+      </NFormItem>
+      <NFormItem label="面额(元)" path="faceAmount">
+        <div class="w-100%">
+          <NInputNumber v-model:value="model.faceAmount" :precision="2" />
+          <div class="text-12px">数值范围0-100000,保留两位小数;</div>
+        </div>
+      </NFormItem>
+      <NFormItem label="库存" path="stock">
+        <div class="w-100%">
+          <NInputNumber v-model:value="model.stock" :min="1" :disabled="isCheckedStock" :max="100000" :precision="0" />
+          <NCheckbox class="ml-6px" :checked="isCheckedStock" @update:checked="handleCheckedChange">不限制</NCheckbox>
+          <div class="text-12px">整数,数值范围1-100000</div>
+        </div>
+      </NFormItem>
+
+      <NFormItem label="结算价(元)" path="settleAmount">
+        <div class="w-100%">
+          <NInputNumber v-model:value="model.settleAmount" :min="0" :max="model.faceAmount || 100000" :precision="2" />
+          <div class="text-12px">需小于等于面额金额。数值范围0-100000,保留两位小数;</div>
+        </div>
+      </NFormItem>
+      <NFormItem label="充值类型" path="rechargeType">
+        <NSelect v-model:value="model.rechargeType" placeholder="" :options="rechargeOptions" />
+      </NFormItem>
+      <NFormItem label="品牌" path="brandName">
+        <NInput v-model:value="model.brandName" placeholder="" />
+      </NFormItem>
+      <div class="font-600">权益配置</div>
+      <div class="my-6px text-12px">全省中石化加油优惠</div>
+      <div class="mb-20px border rounded-16px border-dashed pt-24px">
+        <NFormItem
+          label="每升立减"
+          path="benefitConfigJson.oilPerLiterDiscount"
+          :rule="
+            isCheckedOilPerLiterDiscount ? [{ type: 'number', required: true, message: '请输入', trigger: 'blur' }] : []
+          "
+        >
+          <div>
+            <div class="flex items-center">
+              <NInputNumber
+                v-model:value="model.benefitConfigJson.oilPerLiterDiscount"
+                :disabled="!isCheckedOilPerLiterDiscount"
+                :precision="2"
+                :min="0"
+                :max="50"
+              />
+              元
+              <div class="ml-10px text-12px">(保留2位小数,范围0-50)</div>
+            </div>
+            <div class="mt-6px">
+              <NCheckbox v-model:checked="isCheckedOilPerLiterDiscount" class="ml-6px">启用该权益</NCheckbox>
+            </div>
+          </div>
+        </NFormItem>
+      </div>
+
+      <div class="my-6px text-12px">市民请集合平台快消品</div>
+      <div class="mb-20px border rounded-16px border-dashed pt-24px">
+        <NFormItem
+          label="折扣率"
+          path="benefitConfigJson.mallDiscountRate"
+          :rule="
+            isCheckedMallDiscountRate ? [{ type: 'number', required: true, message: '请输入', trigger: 'blur' }] : []
+          "
+        >
+          <div>
+            <div class="flex items-center">
+              <NInputNumber
+                v-model:value="model.benefitConfigJson.mallDiscountRate"
+                :disabled="!isCheckedMallDiscountRate"
+                :precision="0"
+                :min="0"
+                :max="100"
+              />
+              %
+              <div class="ml-10px text-12px">(输入95表示9.5折,范围0-100)</div>
+            </div>
+            <div class="mt-6px">
+              <NCheckbox v-model:checked="isCheckedMallDiscountRate" class="ml-6px">启用该权益</NCheckbox>
+            </div>
+          </div>
+        </NFormItem>
+      </div>
+
+      <div class="my-6px text-12px">贵阳城投充电桩优惠</div>
+      <div class="mb-20px border rounded-16px border-dashed pt-24px">
+        <NFormItem
+          label="每度电立减"
+          path="benefitConfigJson.chargePerKwhDiscount"
+          :rule="
+            isCheckedChargePerKwhDiscount
+              ? [{ type: 'number', required: true, message: '请输入', trigger: 'blur' }]
+              : []
+          "
+        >
+          <div>
+            <div class="flex items-center">
+              <NInputNumber
+                v-model:value="model.benefitConfigJson.chargePerKwhDiscount"
+                :disabled="!isCheckedChargePerKwhDiscount"
+                :precision="2"
+                :min="0"
+                :max="50"
+              />
+              元
+              <div class="ml-10px text-12px">(保留2位小数,范围0-50)</div>
+            </div>
+            <div class="mt-6px">
+              <NCheckbox v-model:checked="isCheckedChargePerKwhDiscount" class="ml-6px">启用该权益</NCheckbox>
+            </div>
+          </div>
+        </NFormItem>
+      </div>
+
+      <div class="my-6px text-12px">贵阳城投停车位停车优惠</div>
+      <div class="mb-20px border rounded-16px border-dashed pt-24px">
+        <NFormItem
+          label="折扣率"
+          path="benefitConfigJson.parkingDiscountRate"
+          :rule="
+            isCheckedParkingDiscountRate ? [{ type: 'number', required: true, message: '请输入', trigger: 'blur' }] : []
+          "
+        >
+          <div>
+            <div class="flex items-center">
+              <NInputNumber
+                v-model:value="model.benefitConfigJson.parkingDiscountRate"
+                :disabled="!isCheckedParkingDiscountRate"
+                :precision="0"
+                :min="0"
+                :max="100"
+              />
+              %
+              <div class="ml-10px text-12px">(输入95表示9.5折,范围0-100)</div>
+            </div>
+            <div class="mt-6px">
+              <NCheckbox v-model:checked="isCheckedParkingDiscountRate" class="ml-6px">启用该权益</NCheckbox>
+            </div>
+          </div>
+        </NFormItem>
+      </div>
+
+      <div class="my-6px text-12px">市民请集合平台优惠券</div>
+      <div class="mb-20px border rounded-16px border-dashed pt-24px">
+        <NButton class="mb-20px ml-30px" icon-placement="left" secondary strong @click="handleAdd('A')">
+          <template #icon>
+            <icon-material-symbols:add-circle class="text-icon" />
+          </template>
+          添加优惠券
+        </NButton>
+        <div v-for="(item, index) in model.couponConfigJson" :key="index" class="flex">
+          <NFormItem
+            label="优惠券"
+            :path="`couponConfigJson[${index}].couponId`"
+            :rule="isCheckedCouponConfigJson ? [{ required: true, message: '请选择优惠券', trigger: 'change' }] : []"
+          >
+            <!-- <NSelect v-model:value="item.couponId" class="min-w-170px" placeholder="" :options="options" /> -->
+            <ApiSelect
+              v-model:value="item.couponId"
+              class="min-w-170px"
+              :api="fetchGetActivityList"
+              result-feild="data.list"
+              label-field="activityName"
+              value-field="id"
+            ></ApiSelect>
+          </NFormItem>
+
+          <NFormItem
+            label="发放张数"
+            :path="`couponConfigJson[${index}].count`"
+            :rule="
+              isCheckedCouponConfigJson
+                ? [{ required: true, message: '请选择优惠券', trigger: 'change', type: 'number' }]
+                : []
+            "
+          >
+            <NInputNumber v-model:value="item.count" :precision="0" :min="0" :max="100000" />
+            张
+            <div class="ml-10px text-12px">(整数,范围0-100000)</div>
+          </NFormItem>
+          <NButton
+            class="mb-20px ml-30px"
+            icon-placement="left"
+            secondary
+            strong
+            @click="handleDelete('A', index as number)"
+          >
+            <template #icon>
+              <icon-icon-park-solid:reduce-one class="text-icon" />
+            </template>
+          </NButton>
+        </div>
+
+        <div class="mt-6px">
+          <NCheckbox v-model:checked="isCheckedCouponConfigJson" class="ml-100px">启用该权益</NCheckbox>
+        </div>
+      </div>
+
+      <div class="my-6px text-12px">自选赠品</div>
+      <div class="mb-20px border rounded-16px border-dashed pt-24px">
+        <NButton class="mb-20px ml-30px" icon-placement="left" secondary strong @click="handleAdd('B')">
+          <template #icon>
+            <icon-material-symbols:add-circle class="text-icon" />
+          </template>
+          添加商品
+        </NButton>
+        <NFormItem
+          path="giftConfigJson"
+          class="ml-60px"
+          :rule="
+            isCheckedGiftConfigJson
+              ? [{ required: true, type: 'array', min: 1, message: '请选择商品', trigger: 'change' }]
+              : []
+          "
+        >
+          <div class="flex flex-wrap">
+            <div
+              v-for="(item, index) in model.giftConfigJson"
+              :key="index"
+              class="relative mb-20px mr-20px flex border-1px rounded-8px p-10px pr-40px"
+            >
+              <NImage :src="item.pic" class="h90px w90px"></NImage>
+              <div class="ml-10px flex-1 text-12px">
+                <div class="w-200px overflow-hidden text-ellipsis text-nowrap">{{ item.skuName }}</div>
+                <div>商品编码:{{ item.skuCode }}</div>
+                <div>规格:{{ item.specName }}</div>
+                <div>库存:{{ item.stock }}</div>
+                <div class="flex items-center">
+                  赠送数量:
+                  <NInputNumber v-model:value="item.quantity" class="w-100px" :precision="0" :min="1" :max="100" />
+                </div>
+              </div>
+              <NButton
+                class="absolute bottom-10px right-10px text-12px"
+                text
+                @click="handleDelete('B', index as number)"
+              >
+                删除
+              </NButton>
+            </div>
+          </div>
+        </NFormItem>
+
+        <div class="mt-6px">
+          <NCheckbox v-model:checked="isCheckedGiftConfigJson" class="ml-100px">启用该权益</NCheckbox>
+        </div>
+      </div>
+
+      <div class="font-600">其他条件</div>
+
+      <NFormItem label="开发渠道" path="openChannelCodes">
+        <DictSelect v-model:value="model.openChannelCodes" dict-code="open_channel" :immediate="true"></DictSelect>
+      </NFormItem>
+
+      <NFormItem label="价格体系" path="priceChannelId">
+        <div>
+          <ApiSelect
+            v-model:value="model.priceChannelId"
+            :api="fetchFirstChannel"
+            label-field="channelName"
+            value-field="id"
+          ></ApiSelect>
+          <div class="mt-10px text-12px">用哪个企业的价格体系来进行计算;</div>
+        </div>
+      </NFormItem>
+
+      <NFormItem label="使用有效期" path="useValidDays">
+        领取之日起
+        <NInputNumber v-model:value="model.useValidDays" />
+        日有效
+      </NFormItem>
+
+      <NFormItem label="取消规则" path="cancelRule">
+        <div>
+          <NSelect v-model:value="model.cancelRule" placeholder="" :options="cancelOptions" />
+          <div class="mt-10px text-12px">次月生效:取消成功后,下个自然月生效;立即生效,取消成功后,立即生效;</div>
+        </div>
+      </NFormItem>
+
+      <NFormItem label="使用规则" path="useRule">
+        <NInput v-model:value="model.useRule" type="textarea" show-count :maxlength="500" placeholder="" />
+      </NFormItem>
+      <NFormItem label="状态">
+        <NSwitch v-model:value="model.status" :unchecked-value="0" :checked-value="1" />
+      </NFormItem>
+      <div v-if="!disabled" class="flex justify-end">
+        <NButton round type="primary" :loading="loading" @click="handleValidateButtonClick">保存</NButton>
+      </div>
+    </NForm>
+
+    <BasicModal @register="registerModal" @ok="choose">
+      <LayoutTable>
+        <ZTable
+          :immediate="false"
+          :show-table-action="false"
+          :columns="columns"
+          :api="fetchGoodsList"
+          :default-params="{ channelCode: 'XSB' }"
+          default-params-not-reset="channelNo"
+          @register="registerModalTable"
+        ></ZTable>
+      </LayoutTable>
+    </BasicModal>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+::v-deep .n-form-item-blank {
+  z-index: 6 !important;
+}
+.edit-health-goods {
+  background: #fff;
+}
+</style>

+ 354 - 0
src/views/member-center/member-list/index.vue

@@ -0,0 +1,354 @@
+<script setup lang="tsx">
+import { computed, nextTick, ref } from 'vue';
+import { NButton, NTag } from 'naive-ui';
+import {
+  fetchExport,
+  fetchExportRecord,
+  fetchMemberList,
+  fetchOpenRecord
+} from '@/service/api/member-center/member-list';
+import { commonExport } from '@/utils/common';
+import SvgIcon from '@/components/custom/svg-icon.vue';
+// import dayjs from 'dayjs';
+import { useTable } from '@/components/zt/Table/hooks/useTable';
+import { useModal } from '@/components/zt/Modal/hooks/useModal';
+const mobile = ref('');
+const columns: NaiveUI.TableColumn<Api.goods.ShopSku>[] = [
+  {
+    key: 'mobile',
+    title: '手机号',
+    align: 'center',
+    width: 100
+  },
+  {
+    key: 'status',
+    title: '当前状态',
+    align: 'center',
+    width: 100,
+    render: row => {
+      return <NTag class={'mt7'}>{row.status == 1 ? '生效中' : '已失效'}</NTag>;
+    }
+  },
+  {
+    key: 'expireTime',
+    title: '有效期至',
+    align: 'center',
+    width: 100
+  },
+  {
+    key: 'openTimes',
+    title: '开通次数',
+    align: 'center',
+    width: 100
+  },
+  {
+    key: 'effectiveTime',
+    title: '最近开通时间',
+    align: 'center',
+    width: 100
+  },
+  {
+    key: 'openSource',
+    title: '来源',
+    align: 'center',
+    width: 100
+  }
+];
+
+const [registerTable, { getTableData }] = useTable({
+  searchFormConfig: {
+    schemas: [
+      {
+        label: '手机号',
+        component: 'NInput',
+        field: 'mobile'
+      },
+      {
+        label: '会员状态',
+        field: 'status',
+        component: 'NSelect',
+        componentProps: {
+          options: [
+            {
+              label: '生效中',
+              value: 1
+            },
+            {
+              label: '已失效',
+              value: 2
+            }
+          ]
+        }
+      },
+      {
+        label: '开放渠道',
+        component: 'dictSelect',
+        field: 'openSource',
+        componentProps: {
+          dictCode: 'open_channel',
+          immediate: true
+        }
+      },
+      {
+        label: '最近开通时间',
+        component: 'NDatePicker',
+        field: 'createTime',
+        componentProps: {
+          type: 'datetimerange',
+          defaultTime: ['00:00:00', '23:59:59']
+        }
+      }
+    ],
+    inline: false,
+    size: 'small',
+    labelPlacement: 'left',
+    isFull: false
+  },
+  tableConfig: {
+    opWdith: 160,
+    keyField: 'skuId',
+    title: '活动列表',
+    showAddButton: false,
+    scrollX: 1800,
+    fieldMapToTime: [['createTime', ['effectiveTimeStart', 'effectiveTimeEnd']]]
+  }
+});
+
+const failColumns: NaiveUI.TableColumn<Api.government.PointsFailureRecordVO>[] = [
+  {
+    key: 'index',
+    title: '序号',
+    align: 'center',
+    width: 80,
+    render(_, rowIndex) {
+      return rowIndex + 1;
+    }
+  },
+  {
+    key: 'taskName',
+    title: '任务名称',
+    align: 'center'
+  },
+  {
+    key: 'createTime',
+    title: '时间',
+    align: 'center',
+    width: 300,
+    render(row) {
+      return (
+        <div>
+          <div>创建时间:{row.createTime}</div>
+          <div>完成时间:{row.updateTime}</div>
+        </div>
+      );
+    }
+  },
+  {
+    key: 'operatorName',
+    title: '操作人',
+    align: 'center',
+    width: 120
+  },
+  {
+    key: 'successStatus',
+    title: '状态',
+    align: 'center',
+    width: 240,
+    render(row) {
+      const statusList = ['已创建', '进行中', '导出完成', '导出失败'];
+      return (
+        <div class={'flex items-center'}>
+          <span class={'mr-2'}>{statusList[row.status]}</span>共{Number(row.totalCount)}条,
+          <span class={'flex items-center'}>
+            {row.status == 2 && (
+              <div onClick={() => hanleExport(row.id)}>
+                <SvgIcon icon={'tdesign:download'} class={'ml-1 cursor-pointer text-20px'}></SvgIcon>
+              </div>
+            )}
+          </span>
+        </div>
+      );
+    }
+  }
+];
+const openColumns: NaiveUI.TableColumn<Api.government.PointsFailureRecordVO>[] = [
+  {
+    key: 'index',
+    title: '序号',
+    align: 'center',
+    width: 80,
+    render(_, rowIndex) {
+      return rowIndex + 1;
+    }
+  },
+  {
+    key: 'effectiveTime',
+    title: '激活时间',
+    align: 'center'
+  },
+  {
+    key: 'expireTime',
+    title: '到期时间',
+    align: 'center'
+  },
+  {
+    key: 'statusAfterAction',
+    title: '状态',
+    align: 'center',
+    render: row => {
+      // 操作后状态:1 生效中,2 已取消/已失效。
+      return row.statusAfterAction === 1 ? '生效中' : '已取消/已失效';
+    }
+  },
+  {
+    key: 'source',
+    title: '来源',
+    align: 'center',
+    render: row => {
+      return <dictSelect value={row.source} disabled dictCode="open_channel" immediate={true}></dictSelect>;
+    }
+  }
+];
+
+const [registerLogTable, { refresh: refreshLog, setTableLoading: setLogTableLoading }] = useTable({
+  tableConfig: {
+    keyField: 'skuId',
+    title: '',
+    minHeight: 200,
+    showAddButton: false,
+    showSearch: false
+  }
+});
+
+const [registerOpenTable, { refresh: refreshOpen }] = useTable({
+  tableConfig: {
+    keyField: 'skuId',
+    title: '',
+    minHeight: 200,
+    showAddButton: false,
+    showSearch: false
+  }
+});
+
+const [registerModalFail, { openModal: openModalFail }] = useModal({
+  title: '导出记录',
+  height: 400,
+  width: 1200,
+  showFooter: false
+});
+const [registerOpenModal, { openModal }] = useModal({
+  title: `用户开通记录`,
+  height: 400,
+  width: 1200,
+  showFooter: false
+});
+
+const loading = ref(false);
+
+const tableData = computed(() => {
+  return getTableData();
+});
+
+async function handleExport() {
+  try {
+    const res = await fetchExport({});
+    console.log(res);
+
+    // if (!res.error) {
+    //   importTemplateRef.value?.closeModal();
+    // }
+    // window.$message?.success(res.data);
+    window.$dialog?.success({
+      title: '提示',
+      content: () => {
+        return (
+          <div>
+            <p>导入操作进行中......</p>
+            <p>是否进入导入记录</p>
+          </div>
+        );
+      },
+      positiveText: '确定',
+      negativeText: '取消',
+      onPositiveClick: async () => {
+        openExportLogModal();
+      },
+      onNegativeClick: () => {}
+    });
+  } finally {
+    console.log('导出失败');
+  }
+}
+
+async function hanleExport(code: string) {
+  if (loading.value) {
+    window.$message?.error('正在导出,请勿重复点击');
+    return;
+  }
+  loading.value = true;
+  setLogTableLoading(true);
+  try {
+    await commonExport(`/smqjh-system/api/v1/member/export/task/download/${code}`, {}, '导出记录.xlsx');
+  } finally {
+    loading.value = false;
+    setLogTableLoading(false);
+  }
+}
+
+async function openExportLogModal() {
+  openModalFail();
+  await nextTick();
+
+  refreshLog();
+}
+
+async function openRecordModal(id: string) {
+  openModal();
+  mobile.value = id;
+  await nextTick();
+  refreshOpen();
+}
+</script>
+
+<template>
+  <LayoutTable>
+    <ZTable :columns="columns" :api="fetchMemberList" @register="registerTable">
+      <template #prefix="{ loading }">
+        <NSpace>
+          <NButton size="small" :disabled="tableData.length == 0" :loading="loading" @click="handleExport">
+            导出
+          </NButton>
+          <NButton size="small" @click="openExportLogModal">导出记录</NButton>
+        </NSpace>
+      </template>
+      <template #op="{ row }">
+        <NButton size="small" @click="openRecordModal(row.id)">开通记录</NButton>
+      </template>
+    </ZTable>
+
+    <!-- 导出记录 -->
+    <BasicModal @register="registerModalFail">
+      <ZTable
+        :columns="failColumns"
+        :immediate="false"
+        :show-table-action="false"
+        :api="fetchExportRecord"
+        @register="registerLogTable"
+      ></ZTable>
+    </BasicModal>
+
+    <!-- 记录 -->
+    <BasicModal @register="registerOpenModal">
+      <ZTable
+        :columns="openColumns"
+        :immediate="false"
+        :show-table-action="false"
+        :api="fetchOpenRecord"
+        :default-params="{ mobile }"
+        @register="registerOpenTable"
+      ></ZTable>
+    </BasicModal>
+  </LayoutTable>
+</template>
+
+<style scoped></style>

+ 247 - 0
src/views/member-center/member-order/index.vue

@@ -0,0 +1,247 @@
+<script setup lang="tsx">
+import { ref } from 'vue';
+import { NButton } from 'naive-ui';
+// import dayjs from 'dayjs';
+import { fetchOrderDetail, fetchOrderList } from '@/service/api/member-center/member-order';
+import { useTable } from '@/components/zt/Table/hooks/useTable';
+import { useModal } from '@/components/zt/Modal/hooks/useModal';
+
+const orderInfo = ref({
+  id: 0,
+  memberOrderId: 0,
+  memberOpenRecordId: 0,
+  memberAccountId: 0,
+  orderNumber: 'string',
+  thirdOrderNo: 'string',
+  memberId: 0,
+  mobile: 'string',
+  memberTypeId: 0,
+  memberTypeName: 'string',
+  businessType: 'string',
+  orderAmount: 0,
+  rechargePrice: 0,
+  faceAmount: 0,
+  settleAmount: 0,
+  orderStatus: 0,
+  orderStatusName: 'string',
+  status: 0,
+  createTime: '2019-08-24T14:15:22.123Z',
+  activateTime: '2019-08-24T14:15:22.123Z',
+  effectiveTime: '2019-08-24T14:15:22.123Z',
+  expireTime: '2019-08-24T14:15:22.123Z',
+  openSource: 'string',
+  thirdPlatform: 'string',
+  openTimes: 0,
+  updateTime: '2019-08-24T14:15:22.123Z',
+  lastCancelReason: 'string',
+  actionType: 0,
+  actionTypeName: 'string',
+  remark: 'string',
+  benefitRecords: [] as Array<any>
+});
+const types: Record<string, string> = {
+  COUPON: '优惠券',
+  GIFT: '自选赠品'
+};
+const columns: NaiveUI.TableColumn<Api.goods.ShopSku>[] = [
+  {
+    key: 'orderNumber',
+    title: '订单号',
+    align: 'center'
+  },
+  {
+    key: 'mobile',
+    title: '手机号',
+    align: 'center'
+  },
+  {
+    key: 'memberTypeName',
+    title: '会员信息',
+    align: 'center'
+  },
+  {
+    key: 'orderAmount',
+    title: '订单金额(元)',
+    align: 'center'
+  },
+  {
+    key: 'orderStatusName',
+    title: '订单状态',
+    align: 'center'
+    // render: row => {
+    //   const orderStatus = {
+    //     '1': '生效中',
+    //     '2': '已取消',
+    //     '3': '已过期'
+    //   };
+    //   return <NTag class={'mt7'}>{orderStatus[row.status as keyof typeof orderStatus]}</NTag>;
+    // }
+  },
+  {
+    key: 'createTime',
+    title: '创建时间',
+    align: 'center',
+    width: 100
+  },
+  {
+    key: 'openSource',
+    title: '开通来源',
+    align: 'center'
+  },
+  {
+    key: 'activateTime',
+    title: '激活时间',
+    align: 'center',
+    width: 100
+  }
+];
+
+const [registerTable] = useTable({
+  searchFormConfig: {
+    schemas: [
+      {
+        label: '订单号',
+        component: 'NInput',
+        field: 'orderNumber'
+      },
+      {
+        label: '手机号',
+        component: 'NInput',
+        field: 'mobile'
+      },
+      {
+        label: '订单状态',
+        field: 'status',
+        component: 'NSelect',
+        componentProps: {
+          options: [
+            {
+              label: '生效中',
+              value: 1
+            },
+            {
+              label: '已取消',
+              value: 2
+            },
+            {
+              label: '已过期',
+              value: 3
+            }
+          ]
+        }
+      },
+      {
+        label: '开放渠道',
+        component: 'dictSelect',
+        field: 'openSource',
+        componentProps: {
+          dictCode: 'open_channel',
+          immediate: true
+        }
+      },
+      {
+        label: '会员类型ID',
+        component: 'NInput',
+        field: 'memberTypeId'
+      },
+      {
+        label: '会员名称',
+        component: 'NInput',
+        field: 'memberTypeName'
+      },
+
+      {
+        label: '创建时间',
+        component: 'NDatePicker',
+        field: 'createTime',
+        componentProps: {
+          type: 'datetimerange',
+          defaultTime: ['00:00:00', '23:59:59']
+        }
+      }
+    ],
+    inline: false,
+    size: 'small',
+    labelPlacement: 'left',
+    isFull: false
+  },
+  tableConfig: {
+    opWdith: 160,
+    keyField: 'skuId',
+    title: '活动列表',
+    showAddButton: false,
+    scrollX: 1800,
+    fieldMapToTime: [['createTime', ['createTimeStart', 'createTimeEnd']]]
+  }
+});
+
+const [registerModal, { openModal }] = useModal({
+  title: `订单详情`,
+  height: 600,
+  width: 1200,
+  showFooter: false
+});
+
+async function getDetail(id: any) {
+  const res = await fetchOrderDetail(id);
+  orderInfo.value = res.data;
+}
+
+function handleDetail(id: any) {
+  openModal();
+  getDetail(id);
+}
+</script>
+
+<template>
+  <LayoutTable>
+    <ZTable :columns="columns" :api="fetchOrderList" @register="registerTable">
+      <template #op="{ row }">
+        <NButton size="small" @click="handleDetail(row.id)">查看详情</NButton>
+      </template>
+    </ZTable>
+
+    <BasicModal @register="registerModal">
+      <div class="py-20px font-semibold">基本信息</div>
+
+      <div class="flex">
+        <div class="flex-1">
+          <div>订单号:{{ orderInfo.orderNumber }}</div>
+          <div>手机号:{{ orderInfo.mobile }}</div>
+          <div>订单金额:{{ orderInfo.orderAmount }}</div>
+          <div>合作方订单号:{{ orderInfo.thirdOrderNo }}</div>
+          <div>激活时间:{{ orderInfo.activateTime }}</div>
+        </div>
+        <div class="flex-1">
+          <div>状态:{{ orderInfo.orderStatusName }}</div>
+          <div>用户ID:{{ orderInfo.memberAccountId }}</div>
+          <div>开通来源:{{ orderInfo.openSource }}</div>
+          <div>创建时间:{{ orderInfo.createTime }}</div>
+          <div>到期时间:{{ orderInfo.expireTime }}</div>
+        </div>
+      </div>
+
+      <div class="py-20px font-semibold">权益发放记录</div>
+      <NTable class="" :single-line="false">
+        <NThead>
+          <NTr>
+            <NTh>权益类型</NTh>
+            <NTh>发放时间</NTh>
+            <NTh>发放结果</NTh>
+            <NTh>关联业务单号</NTh>
+          </NTr>
+        </NThead>
+        <NTbody>
+          <NTr v-for="(item, index) in orderInfo.benefitRecords" :key="index">
+            <NTd>{{ types[item.benefitType] }}</NTd>
+            <NTd>{{ item.createTime }}</NTd>
+            <NTd>{{ item.grantStatus == 1 ? '成功' : '失败' }}</NTd>
+            <NTd>{{ item.businessNo }}</NTd>
+          </NTr>
+        </NTbody>
+      </NTable>
+    </BasicModal>
+  </LayoutTable>
+</template>
+
+<style scoped></style>

+ 232 - 0
src/views/member-center/member-type/index.vue

@@ -0,0 +1,232 @@
+<script setup lang="tsx">
+import { useRouter } from 'vue-router';
+import { NButton, NImage, NSwitch } from 'naive-ui';
+// import dayjs from 'dayjs';
+import { fetchDelType, fetchTypeList, fetchUpdateStatus } from '@/service/api/member-center/member-type';
+import { useTable } from '@/components/zt/Table/hooks/useTable';
+const router = useRouter();
+
+const columns: NaiveUI.TableColumn<Api.goods.ShopSku>[] = [
+  {
+    key: 'id',
+    title: 'ID',
+    align: 'center',
+    width: 100
+  },
+  {
+    key: 'memberName',
+    title: '会员名称',
+    align: 'center',
+    width: 100
+  },
+  {
+    key: 'openChannelCodes',
+    title: '开放渠道',
+    align: 'center',
+    width: 100
+  },
+  {
+    key: 'status',
+    title: '状态',
+    align: 'center',
+    width: 100,
+    render: row => {
+      return (
+        <NSwitch
+          uncheckedValue={0}
+          checkedValue={1}
+          value={row.status}
+          onUpdate:value={val => {
+            row.status = val;
+            fetchUpdateStatus(row.id, row.status);
+          }}
+        ></NSwitch>
+      );
+    }
+  },
+
+  {
+    key: 'coverImg',
+    title: '活动封面',
+    align: 'center',
+    width: 100,
+    render: (row: any) => {
+      return <NImage src={row.coverImg} class={'h90px w90px'}></NImage>;
+    }
+  },
+  {
+    key: 'priceChannelId',
+    title: '价格体系',
+    align: 'center',
+    width: 100
+  },
+  {
+    key: 'faceAmount',
+    title: '面额(元)',
+    align: 'center',
+    width: 100
+  },
+  {
+    key: 'stock',
+    title: '库存',
+    align: 'center',
+    width: 100,
+    render: row => {
+      return <div>{row.stock == -1 ? '不限制' : row.stock}</div>;
+    }
+  },
+  {
+    key: 'settleAmount',
+    title: '结算价(元)',
+    align: 'center',
+    width: 100
+  },
+  {
+    key: 'useValidDays',
+    title: '使用有效期',
+    align: 'center',
+    width: 100,
+    render: row => {
+      return <div>领取之日起{row.useValidDays}天有效</div>;
+    }
+  },
+  {
+    key: 'cancelRule',
+    title: '取消规则',
+    align: 'center',
+    width: 100,
+    render: row => {
+      const cancelOptions = {
+        IMMEDIATE: '立即生效',
+        NEXT_MONTH: '次月生效'
+      };
+      return <div> {cancelOptions[row.cancelRule as keyof typeof cancelOptions]}</div>;
+    }
+  },
+
+  {
+    key: 'rightsDesc',
+    title: '权益描述',
+    align: 'center',
+    width: 230,
+    ellipsis: {
+      lineClamp: 2, // 最多显示2行
+      tooltip: {
+        // 控制 tooltip 样式/宽高
+        contentStyle: {
+          maxWidth: '300px' // tooltip 最大宽度
+        },
+        scrollable: true // 内容过长可滚动
+      }
+    }
+  }
+];
+
+const [registerTable, { refresh }] = useTable({
+  searchFormConfig: {
+    schemas: [
+      {
+        label: '会员名称',
+        component: 'NInput',
+        field: 'memberName'
+      },
+      {
+        label: '开放渠道',
+        component: 'dictSelect',
+        field: 'openChannel',
+        componentProps: {
+          dictCode: 'open_channel',
+          immediate: true
+        }
+      },
+      {
+        label: '状态',
+        field: 'status',
+        component: 'NSelect',
+        componentProps: {
+          options: [
+            {
+              label: '下架',
+              value: 0
+            },
+            {
+              label: '上架',
+              value: 1
+            }
+          ]
+        }
+      }
+    ],
+    inline: false,
+    size: 'small',
+    labelPlacement: 'left',
+    isFull: false
+  },
+  tableConfig: {
+    opWdith: 160,
+    keyField: 'skuId',
+    title: '活动列表',
+    showAddButton: true,
+    scrollX: 1800,
+    fieldMapToTime: [
+      ['price', ['minPrice', 'maxPrice']],
+      ['createTime', ['startTime', 'endTime']]
+    ]
+  }
+});
+
+function handleAdd() {
+  router.push({
+    path: '/member-center/edit-member-type'
+  });
+}
+// function handleDetail(row: any) {
+//   router.push({
+//     path: '/member-center/edit-member-type',
+//     query: {
+//       id: row.id,
+//       mode: 'detail'
+//     }
+//   });
+// }
+function handleEdit(row: any) {
+  console.log('edit', row);
+  router.push({
+    path: '/member-center/edit-member-type',
+    query: {
+      id: row.id,
+      mode: 'edit'
+    }
+  });
+}
+
+function handleDelete(row: any) {
+  window.$dialog?.info({
+    title: '提示',
+    content: `你确定要删除吗?`,
+    positiveText: '确定',
+    negativeText: '取消',
+    onPositiveClick: async () => {
+      const { error } = await fetchDelType(row.id as string);
+      if (!error) {
+        refresh();
+      }
+    }
+  });
+}
+</script>
+
+<template>
+  <LayoutTable>
+    <ZTable :columns="columns" :api="fetchTypeList" @add="handleAdd" @register="registerTable">
+      <template #op="{ row }">
+        <NSpace>
+          <NButton size="small" @click="handleEdit(row)">编辑</NButton>
+          <NButton size="small" @click="handleDelete(row)">删除</NButton>
+        </NSpace>
+      </template>
+    </ZTable>
+  </LayoutTable>
+</template>
+
+<style scoped></style>

+ 371 - 0
src/views/operation/coupon-issuance/index.vue

@@ -0,0 +1,371 @@
+<script setup lang="tsx">
+import { nextTick, onMounted, ref, useTemplateRef } from 'vue';
+import { useRoute } from 'vue-router';
+import { NButton } from 'naive-ui';
+import { delCoupon, fetchFailList, fetchImport, fetchList } from '@/service/api/operation/coupon-manage';
+import { commonExport } from '@/utils/common';
+import { useTable } from '@/components/zt/Table/hooks/useTable';
+import SvgIcon from '@/components/custom/svg-icon.vue';
+import { useModal } from '@/components/zt/Modal/hooks/useModal';
+const route = useRoute();
+const importTemplateRef = useTemplateRef('importTemplateRef');
+const [registerModalTable, { refresh, setFieldsValue }] = useTable({
+  searchFormConfig: {
+    schemas: [
+      {
+        field: 'activityId',
+        component: 'NInput',
+        label: '券ID'
+      },
+      {
+        field: 'activityName',
+        component: 'NInput',
+        label: '名称'
+      },
+      {
+        field: 'promotionType',
+        component: 'NSelect',
+        componentProps: {
+          options: [
+            { label: '满减券', value: 1 },
+            { label: '立减券', value: 2 }
+          ]
+        },
+        label: '类型'
+      },
+      {
+        // 优惠券状态 1-已使用 2-可使用 5-已过期 7-未生效
+        field: 'useStatus',
+        component: 'NSelect',
+        componentProps: {
+          options: [
+            { label: '已使用', value: 1 },
+            { label: '可使用', value: 2 },
+            { label: '冻结', value: 3 },
+            { label: '已过期', value: 5 },
+            { label: '未生效', value: 7 }
+          ]
+        },
+        label: '状态'
+      }
+    ],
+    inline: false,
+    size: 'small',
+    labelPlacement: 'left',
+    isFull: false
+  },
+  tableConfig: {
+    keyField: 'id',
+    title: '优惠券列表',
+    minHeight: 400,
+    showAddButton: false
+  }
+});
+const loading = ref(false);
+const tableColumns: NaiveUI.TableColumn<Api.goods.ShopCategory>[] = [
+  {
+    title: '所属企业',
+    align: 'center',
+    key: 'channelName'
+  },
+  {
+    title: '用户手机号',
+    align: 'center',
+    key: 'memberMobile'
+  },
+  {
+    title: '券ID',
+    align: 'center',
+    key: 'couponId'
+  },
+
+  {
+    title: '券名称',
+    align: 'center',
+    key: 'couponName'
+  },
+  {
+    title: '券信息',
+    align: 'center',
+    key: 'couponInfo'
+    // render: row => {
+    //   let content = '';
+    //   if (row.promotionType === 1) {
+    //     content = `满${row.amountMoney}元减${row.discountMoney}元`;
+    //   } else {
+    //     content = `立减${row.discountMoney}元`;
+    //   }
+    //   return <div>{content}</div>;
+    // }
+  },
+  {
+    title: '状态',
+    align: 'center',
+    key: 'useStatus',
+    render: row => {
+      let status = '';
+      switch (row.useStatus) {
+        case 1:
+          status = '已使用';
+          break;
+        case 2:
+          status = '可使用';
+          break;
+        case 3:
+          status = '冻结';
+          break;
+        case 5:
+          status = '已过期';
+          break;
+        case 7:
+          status = '未生效';
+          break;
+        default:
+          status = '未知状态';
+          break;
+      }
+      return <div>{status}</div>;
+    }
+  },
+  {
+    title: '创建时间',
+    align: 'center',
+    key: 'createTime'
+  },
+  {
+    title: '券使用有效期',
+    align: 'center',
+    key: 'expirationTime'
+  },
+  {
+    title: '订单编号',
+    align: 'center',
+    key: 'lockOrderId'
+  },
+  {
+    title: '操作',
+    key: 'op',
+    fixed: 'right',
+    align: 'center',
+    width: 230,
+    render: row => (
+      <div class="flex-center gap-8px">
+        <NButton type="primary" size="small" ghost onClick={() => del(row)}>
+          删除
+        </NButton>
+      </div>
+    )
+  }
+];
+
+const [registerModalFail, { openModal: openModalFail }] = useModal({
+  title: '导入记录',
+  height: 650,
+  width: 1200,
+  showFooter: false
+});
+
+const [registerLogTable, { refresh: refreshLog, setTableLoading: setLogTableLoading }] = useTable({
+  tableConfig: {
+    keyField: 'skuId',
+    title: '',
+    minHeight: 660,
+    showAddButton: false,
+    showSearch: false
+  }
+});
+
+const failColumns: NaiveUI.TableColumn<Api.government.PointsFailureRecordVO>[] = [
+  {
+    key: 'index',
+    title: '序号',
+    align: 'center',
+    width: 80,
+    render(_, rowIndex) {
+      return rowIndex + 1;
+    }
+  },
+  {
+    key: 'taskName',
+    title: '任务名称',
+    align: 'center'
+  },
+  {
+    key: 'createTime',
+    title: '时间',
+    align: 'center',
+    width: 300,
+    render(row) {
+      return (
+        <div>
+          <div>创建时间:{row.createTime}</div>
+          <div>完成时间:{row.createTime}</div>
+        </div>
+      );
+    }
+  },
+  {
+    key: 'operator',
+    title: '操作人',
+    align: 'center',
+    width: 120
+    // render(row) {
+    //   return (
+    //     <div>
+    //       <div>{row.createByRole}</div>
+    //       <div>({row.createByName})</div>
+    //     </div>
+    //   );
+    // }
+  },
+  {
+    key: 'successStatus',
+    title: '状态',
+    align: 'center',
+    width: 240,
+    render(row) {
+      return (
+        <div class={'flex items-center'}>
+          共{Number(row.failNum) + Number(row.successNum)}条,成功:{row.successNum},
+          <span class={'flex items-center text-red-500'}>
+            失败:
+            {row.failNum}
+            {row.failNum != 0 && (
+              <div onClick={() => hanleExportFailure(row.batchNo)}>
+                <SvgIcon
+                  icon={'tdesign:download'}
+                  class={'ml-1 cursor-pointer text-20px'}
+                  style={'color:red'}
+                ></SvgIcon>
+              </div>
+            )}
+          </span>
+        </div>
+      );
+    }
+  }
+];
+
+function del(row: Api.goods.ShopCategory) {
+  window.$dialog?.info({
+    title: '删除活动',
+    content: '你确定要删除吗?',
+    positiveText: '确定',
+    negativeText: '取消',
+    onPositiveClick: async () => {
+      const { error } = await delCoupon(row.id);
+      if (!error) {
+        refresh();
+      }
+    }
+  });
+}
+
+// async function handleSubmitImport(file: File) {
+//   const res = await fetchImportGoods({ file });
+//   console.log(res);
+
+//   if (!res.error) {
+//     importTemplateRef.value?.closeModal();
+//   }
+//   window.$message?.success(res.data);
+//   importTemplateRef.value?.setSubLoading(false);
+// }
+function openImportModal() {
+  importTemplateRef.value?.openModal();
+}
+async function openImportLogModal() {
+  openModalFail();
+  await nextTick();
+  refreshLog();
+}
+async function handleSubmitImport(file: File) {
+  console.log(file);
+
+  // setTableLoading(true);
+  try {
+    const res = await fetchImport({ file });
+    console.log(res);
+
+    // if (!res.error) {
+    //   importTemplateRef.value?.closeModal();
+    // }
+    window.$message?.success(res.data);
+    importTemplateRef.value?.setSubLoading(false);
+    window.$dialog?.success({
+      title: '提示',
+      content: () => {
+        return (
+          <div>
+            <p>导入操作进行中......</p>
+            <p>是否进入导入记录</p>
+          </div>
+        );
+      },
+      positiveText: '确定',
+      negativeText: '取消',
+      onPositiveClick: async () => {
+        importTemplateRef.value?.closeModal();
+        openImportLogModal();
+      },
+      onNegativeClick: () => {}
+    });
+  } finally {
+    importTemplateRef.value?.setSubLoading(false);
+  }
+}
+async function hanleExportFailure(batchNo: string) {
+  if (loading.value) {
+    window.$message?.error('正在导出,请勿重复点击');
+    return;
+  }
+  loading.value = true;
+  setLogTableLoading(true);
+  try {
+    await commonExport('/smqjh-system/sys-api/memberCoupon/downloadErrorExcel', { batchNo }, '失败的记录.xlsx');
+  } finally {
+    loading.value = false;
+    setLogTableLoading(false);
+  }
+}
+
+onMounted(() => {
+  setTimeout(async () => {
+    await setFieldsValue({ activityId: route.query.activityId });
+    refresh();
+  }, 1000);
+});
+</script>
+
+<template>
+  <LayoutTable>
+    <ZTable :show-table-action="false" :columns="tableColumns" :api="fetchList" @register="registerModalTable">
+      <template #prefix>
+        <NSpace>
+          <NButton size="small" @click="openImportModal">批量发放</NButton>
+          <NButton size="small" @click="openImportLogModal">导入记录</NButton>
+        </NSpace>
+      </template>
+    </ZTable>
+
+    <ZImportTemplate
+      ref="importTemplateRef"
+      url="/smqjh-system/sys-api/memberCoupon/downloadXlsx"
+      template-text="批量发放导入模版.xlsx"
+      modal-text="批量发放"
+      @submit="handleSubmitImport"
+    ></ZImportTemplate>
+
+    <BasicModal @register="registerModalFail">
+      <ZTable
+        :immediate="false"
+        :columns="failColumns"
+        :show-table-action="false"
+        :api="fetchFailList"
+        @register="registerLogTable"
+      ></ZTable>
+    </BasicModal>
+  </LayoutTable>
+</template>
+
+<style scoped></style>

+ 423 - 0
src/views/operation/coupon-manage/index.vue

@@ -0,0 +1,423 @@
+<script setup lang="tsx">
+import { ref } from 'vue';
+import { useRouter } from 'vue-router';
+import { NButton } from 'naive-ui';
+import {
+  delActivity,
+  fetchAddActivity,
+  fetchEditActivity,
+  fetchGetActivityList
+} from '@/service/api/operation/coupon-manage';
+import { useModalFrom } from '@/components/zt/ModalForm/hooks/useModalForm';
+import { useTable } from '@/components/zt/Table/hooks/useTable';
+const router = useRouter();
+const isLimitReceiveNum = ref(false);
+const isLimitInventory = ref(false);
+
+const [registerModalTable, { refresh }] = useTable({
+  searchFormConfig: {
+    schemas: [
+      {
+        field: 'activityId',
+        component: 'NInput',
+        label: '券ID'
+      },
+      {
+        field: 'activityName',
+        component: 'NInput',
+        label: '名称'
+      },
+      {
+        field: 'promotionType',
+        component: 'NSelect',
+        componentProps: {
+          options: [
+            { label: '满减券', value: 1 },
+            { label: '立减券', value: 2 }
+          ]
+        },
+        label: '类型'
+      }
+    ],
+    inline: false,
+    size: 'small',
+    labelPlacement: 'left',
+    isFull: false
+  },
+  tableConfig: {
+    keyField: 'id',
+    title: '优惠券列表',
+    minHeight: 400
+  }
+});
+
+const [
+  registerModalForm,
+  {
+    openModal: openModalForm,
+    setFieldsValue: setModalFormValue,
+    getFieldsValue: getModalFormValue,
+    closeModal: closeModalForm,
+    setSubLoading
+  }
+] = useModalFrom({
+  modalConfig: {
+    title: '优惠券',
+    isShowHeaderText: true
+  },
+  formConfig: {
+    schemas: [
+      {
+        field: 'id',
+        label: 'ID',
+        show: false,
+        component: 'NInput'
+      },
+
+      {
+        field: 'activityName',
+        component: 'NInput',
+        label: '活动名称',
+        required: true,
+        componentProps: {
+          maxlength: 20
+        }
+      },
+      {
+        field: 'promotionType',
+        component: 'NRadioGroup',
+        label: '类型',
+        required: true,
+        defaultValue: 1,
+        componentProps: {
+          options: [
+            {
+              label: '满减券',
+              value: 1
+            },
+            {
+              label: '立减券',
+              value: 2
+            }
+          ]
+        }
+      },
+      {
+        field: 'amountMoney',
+        component: 'NInputNumber',
+        label: '门槛(元)',
+        required: true,
+        ifShow: ({ model }) => model.promotionType === 1,
+        componentProps: {
+          min: 0,
+          max: 1000,
+          precision: 0
+        }
+      },
+      {
+        field: 'discountMoney',
+        component: 'NInputNumber',
+        label: '面额(元)',
+        rules: { validator: validateDiscountMoney, trigger: 'input' },
+        componentProps: {
+          min: 0,
+          max: 1000,
+          precision: 0
+        }
+      },
+      {
+        field: 'inventory',
+        component: 'NInputNumber',
+        label: '总量(张)',
+        required: true,
+        render: ({ model, field }) => {
+          return (
+            <div class="flex items-center">
+              <n-input-number
+                v-model:value={model[field]}
+                min={1}
+                max={100000}
+                disabled={isLimitInventory.value}
+                clearable
+              />
+              <n-checkbox
+                class="ml-6px"
+                checked={isLimitInventory.value}
+                onUpdateChecked={(val: any) => (isLimitInventory.value = val)((model[field] = 99999))}
+              >
+                不限制
+              </n-checkbox>
+            </div>
+          );
+        }
+      },
+      {
+        field: 'limitReceiveNum',
+        component: 'NInputNumber',
+        label: '每人限领(张)',
+        required: true,
+        render: ({ model, field }) => {
+          return (
+            <div class="flex items-center">
+              <n-input-number
+                v-model:value={model[field]}
+                min={1}
+                max={model.inventory || 100000}
+                disabled={isLimitReceiveNum.value}
+                clearable
+              />
+              <n-checkbox
+                class="ml-6px"
+                checked={isLimitReceiveNum.value}
+                onUpdateChecked={(val: any) => (isLimitReceiveNum.value = val)((model[field] = 99999))}
+              >
+                不限制
+              </n-checkbox>
+            </div>
+          );
+        }
+      },
+      {
+        field: 'businessType',
+        component: 'NRadioGroup',
+        label: '使用限制',
+        required: true,
+        defaultValue: 'XSB',
+        componentProps: {
+          options: [
+            {
+              label: '星闪豹',
+              value: 'XSB'
+            }
+          ]
+        }
+      },
+      {
+        field: 'expirationDate',
+        component: 'NInputNumber',
+        label: '券使用有效期',
+        required: true,
+        componentProps: {
+          min: 0,
+          max: 100000
+        },
+        render: ({ model, field }) => {
+          return (
+            <div class="flex items-center">
+              领取后 <n-input-number v-model:value={model[field]} min={0} max={10000} clearable /> 天有效
+            </div>
+          );
+        }
+      }
+    ],
+    labelWidth: 120,
+    layout: 'horizontal',
+    gridProps: {
+      cols: '1',
+      itemResponsive: true
+    }
+  }
+});
+
+const tableColumns: NaiveUI.TableColumn<Api.goods.ShopCategory>[] = [
+  {
+    title: '券ID',
+    align: 'center',
+    key: 'activityId'
+  },
+  {
+    title: '活动名称',
+    align: 'center',
+    key: 'activityName'
+  },
+  {
+    title: '类型',
+    align: 'center',
+    key: 'promotionType',
+    render: row => {
+      return row.promotionType === 1 ? '满减券' : '无门槛券';
+    }
+  },
+  {
+    title: '门槛(元)',
+    align: 'center',
+    key: 'amountMoney'
+  },
+  {
+    title: '面额(元)',
+    align: 'center',
+    key: 'discountMoney'
+  },
+  {
+    title: '总量(张)',
+    align: 'center',
+    key: 'inventory',
+    render: row => {
+      return <div>{row.inventory || '不限制'}</div>;
+    }
+  },
+  {
+    title: '未发放(张)',
+    align: 'center',
+    key: 'inventoryActual',
+    render: row => {
+      return <div>{row.inventoryActual === -1 ? '-' : row.inventoryActual}</div>;
+    }
+  },
+  {
+    title: '每人限领(张)',
+    align: 'center',
+    key: 'limitReceiveNum',
+    render: row => {
+      return <div>{row.limitReceiveNum || '不限制'}</div>;
+    }
+  },
+  {
+    title: '券使用有效期',
+    align: 'center',
+    key: 'expirationDate'
+  },
+  {
+    title: '创建时间',
+    align: 'center',
+    key: 'updateTime'
+  },
+  {
+    title: '操作',
+    key: 'op',
+    fixed: 'right',
+    align: 'center',
+    width: 230,
+    render: row => (
+      <div class="flex-center gap-8px">
+        {
+          <NButton type="primary" size="small" ghost onClick={() => handleRecord(row)}>
+            查看领取情况
+          </NButton>
+        }
+        {
+          <NButton type="primary" size="small" ghost onClick={() => edit(row)}>
+            编辑
+          </NButton>
+        }
+        <NButton type="primary" size="small" ghost onClick={() => del(row)}>
+          删除
+        </NButton>
+      </div>
+    )
+  }
+];
+
+function handleRecord(row: any) {
+  if (!row.activityId) {
+    window.$message?.error('订单异常');
+  }
+  router.push({
+    path: '/operation/coupon-issuance',
+    query: {
+      activityId: row.activityId
+    }
+  });
+}
+
+// async function validatePrice(_: any, value: any) {
+//   const form = await getModalFormValue();
+//   if (value <= form.discountMoney) {
+//     console.log(1111111);
+
+//     return; // 验证通过时返回undefined
+//   }
+
+//   throw new Error('应小于等于面额'); // 验证失败时抛出错误
+// }
+async function validateDiscountMoney(_: any, value: any) {
+  const form = await getModalFormValue();
+  if (value <= form.amountMoney || form.promotionType === 2) {
+    return; // 验证通过时返回undefined
+  }
+  throw new Error('需要小于等于门槛金额。数值范围0-1000,整数'); // 验证失败时抛出错误
+}
+
+async function edit(row: Api.goods.ShopCategory) {
+  if (row.inventoryActual < row.inventoryTotal) {
+    window.$dialog?.info({
+      title: '提示',
+      content: '已有人领取,不允许修改',
+      positiveText: '确定'
+    });
+    return;
+  }
+  openModalForm(row);
+  // setModalProps({ title: `修改${Number(row.level)}级分类` });
+  setModalFormValue(row);
+}
+function add(row: Api.goods.ShopCategory) {
+  openModalForm();
+  console.log(row, 'row-key');
+  // updateSchema({ field: 'iconUrl', required: true });
+  // setModalProps({ title: `新增二级分类` });
+  // setModalFormValue({ parentName: row.name, name: '', parentId: row.id });
+}
+function del(row: Api.goods.ShopCategory) {
+  if (row.inventoryActual < row.inventoryTotal) {
+    window.$dialog?.info({
+      title: '提示',
+      content: '已有人领取,不允许删除',
+      positiveText: '确定'
+    });
+    return;
+  }
+  window.$dialog?.info({
+    title: '删除活动',
+    content: '你确定要删除吗?',
+    positiveText: '确定',
+    negativeText: '取消',
+    onPositiveClick: async () => {
+      const { error } = await delActivity(row.id);
+      if (!error) {
+        refresh();
+      }
+    }
+  });
+}
+
+async function handleSubmit() {
+  const form = await getModalFormValue();
+  setSubLoading(false);
+  if (isLimitInventory.value) {
+    form.inventory = 0;
+  }
+  if (isLimitReceiveNum.value) {
+    form.limitReceiveNum = 0;
+  }
+  if (!form.id) {
+    const { error } = await fetchAddActivity(form);
+    if (error) {
+      return;
+    }
+  } else {
+    // 修改
+    const { error } = await fetchEditActivity(form);
+    if (error) {
+      return;
+    }
+  }
+  closeModalForm();
+  refresh();
+}
+</script>
+
+<template>
+  <LayoutTable>
+    <ZTable
+      :show-table-action="false"
+      :columns="tableColumns"
+      :api="fetchGetActivityList"
+      @register="registerModalTable"
+      @add="add"
+    ></ZTable>
+    <BasicModelForm @register-modal-form="registerModalForm" @submit-form="handleSubmit"></BasicModelForm>
+  </LayoutTable>
+</template>
+
+<style scoped></style>

+ 10 - 4
src/views/order-manage/after-sales-order-detail/index.vue

@@ -111,7 +111,8 @@ function handleDetail(orderNumber: string) {
         <NCard size="small" class="mt-20px" title="客户信息" :bordered="false">
           <div>客户姓名:{{ orderInfo?.consigneeName || '---' }}</div>
           <div>客户手机号:{{ orderInfo?.consigneeMobile || '---' }}</div>
-          <div>客户地址:{{ orderInfo?.consigneeAddress || '---' }}</div>
+          <div>客户地址:{{ orderInfo?.dvyType == 2 ? '自提' : orderInfo?.consigneeAddress || '---' }}</div>
+
           <div>用户昵称:{{ orderInfo.userName || '---' }}</div>
           <div>用户电话:{{ orderInfo.consigneeMobile }}</div>
           <div>企业身份:{{ orderInfo.channelName || '---' }}</div>
@@ -151,9 +152,14 @@ function handleDetail(orderNumber: string) {
 
           <div class="mt-20px flex items-center justify-end">
             <div class="text-20px font-semibold">退款总金额:{{ orderInfo.refundMoney }} 元</div>
-            <div class="ml-20px text-14px text-gray">退还金额:{{ orderInfo.userRefundMoney }}</div>
-            <div class="text-14px text-gray">退还积分:{{ orderInfo.refundScore }}</div>
-            <div class="text-14px text-gray">已过期:({{ orderInfo.refundExpiredScore }} )</div>
+            <template v-if="orderInfo.returnMoneySts === 70">
+              <div class="ml-20px text-14px text-gray">退还金额:{{ orderInfo.userRefundMoney }}</div>
+              <div class="text-14px text-gray">退还积分:{{ orderInfo.refundScore }}</div>
+              <div class="text-14px text-gray">已过期:({{ orderInfo.refundExpiredScore }} )</div>
+              <div v-if="orderInfo.couponBaseInfoDTO" class="text-14px text-gray">
+                退还优惠券:{{ orderInfo.couponBaseInfoDTO?.discountMoney || 0 }}
+              </div>
+            </template>
           </div>
 
           <div class="py-20px font-semibold">03 退款记录</div>

+ 1 - 1
src/views/order-manage/after-sales-order/index.vue

@@ -57,7 +57,7 @@ const columns: NaiveUI.TableColumn<Api.delivery.deliveryOrder>[] = [
             {row.consigneeName}
             {row.consigneeMobile}
           </div>
-          <div>{row.consigneeAddress}</div>
+          {row.dvyType == 2 ? <div>自提</div> : <div>{row.consigneeAddress}</div>}
         </div>
       );
     }

+ 21 - 5
src/views/order-manage/normal-order/index.vue

@@ -52,6 +52,12 @@ const columns: NaiveUI.TableColumn<Api.delivery.deliveryOrder>[] = [
       return <NTag>{businessType[row.businessType as keyof typeof businessType] || row.businessType}</NTag>;
     }
   },
+  {
+    key: 'shopName',
+    title: '门店名称',
+    align: 'center',
+    width: 120
+  },
   {
     key: 'channelName',
     title: '所属企业',
@@ -70,7 +76,7 @@ const columns: NaiveUI.TableColumn<Api.delivery.deliveryOrder>[] = [
             {row.consigneeName}
             {row.consigneeMobile}
           </div>
-          <div>{row.consigneeAddress}</div>
+          {row.dvyType == 2 ? <div>自提</div> : <div>{row.consigneeAddress}</div>}
         </div>
       );
     }
@@ -83,7 +89,7 @@ const columns: NaiveUI.TableColumn<Api.delivery.deliveryOrder>[] = [
   },
   {
     key: 'status',
-    title: '订单状态',
+    title: '业务状态',
     align: 'center',
     width: 120,
     render: row => {
@@ -343,7 +349,7 @@ async function handleExport(exportType: any) {
     //   { ...getFieldsValue(), orderStatus: activeTab.value },
     //   '正常订单列表.xlsx'
     // );
-    await fetchExportOrderList({ exportType });
+    await fetchExportOrderList({ exportType, ...getSeachForm(), orderStatus: activeTab.value });
     dialog.success({
       title: '提示',
       content: () => {
@@ -440,14 +446,24 @@ function handleship(row: Api.delivery.deliveryOrder) {
           </NButton>
 
           <NButton
-            v-if="row.dvyType === 10 && row.hbOrderStatus === 40"
+            v-if="row.dvyType === 10 && row.hbOrderStatus === 40 && !row.dvyNo"
+            class="mt-10px"
+            size="small"
+            type="primary"
+            ghost
+            @click="handleship(row)"
+          >
+            发货
+          </NButton>
+          <NButton
+            v-if="row.dvyType === 10 && (row.hbOrderStatus === 40 || row.hbOrderStatus === 70) && row.dvyNo"
             class="mt-10px"
             size="small"
             type="primary"
             ghost
             @click="handleship(row)"
           >
-            {{ row.dvyno ? '修改发货信息' : '发货' }}
+            修改发货信息
           </NButton>
         </div>
         <!--

+ 61 - 7
src/views/order-manage/order-detail/index.vue

@@ -299,7 +299,7 @@ function handleSaleOrder(row: Api.delivery.deliveryOrder) {
   router.push({
     path: '/order-manage/after-sales-order',
     query: {
-      orderNumber: orderInfo.value?.orderNumber
+      orderNumber: row.orderNumber
     }
   });
 }
@@ -363,7 +363,7 @@ function getRefundRecordText() {
         <NCard size="small" class="mt-20px" title="客户信息" :bordered="false">
           <div>客户姓名:{{ orderInfo?.consigneeName || '---' }}</div>
           <div>客户手机号:{{ orderInfo?.consigneeMobile || '---' }}</div>
-          <div>客户地址:{{ orderInfo?.consigneeAddress || '---' }}</div>
+          <div>客户地址:{{ orderInfo?.dvyType == 2 ? '自提' : orderInfo?.consigneeAddress || '---' }}</div>
           <div>用户昵称:{{ orderInfo.userName || '---' }}</div>
           <div>用户电话:{{ orderInfo.consigneeMobile }}</div>
           <div>企业身份:{{ orderInfo.channelName || '---' }}</div>
@@ -402,7 +402,10 @@ function getRefundRecordText() {
           <div>
             退款记录:{{ getRefundRecordText() }}
             <NButton
-              v-if="orderInfo.refundOrderList && orderInfo.refundOrderList.length > 0"
+              v-if="
+                Number(orderInfo.backendOrderRefundLogCount) ||
+                (orderInfo.refundOrderList && orderInfo.refundOrderList.length > 0)
+              "
               size="small"
               quaternary
               type="primary"
@@ -438,10 +441,37 @@ function getRefundRecordText() {
             </div>
           </div>
 
-          <!-- v-if="orderInfo.dvyType === 10 && orderInfo.hbOrderStatus === 40" -->
-          <NButton size="small" type="primary" @click="handleShip">
+          <!--
+ <NButton v-if="orderInfo.dvyType === 10 && orderInfo.hbOrderStatus === 40" size="small" type="primary"
+            @click="handleShip">
             {{ orderInfo.dvyNo ? '修改发货信息' : '发货' }}
           </NButton>
+-->
+
+          <NButton
+            v-if="orderInfo.dvyType === 10 && orderInfo.hbOrderStatus === 40 && !orderInfo.dvyNo"
+            class="mt-10px"
+            size="small"
+            type="primary"
+            ghost
+            @click="handleShip"
+          >
+            发货
+          </NButton>
+          <NButton
+            v-if="
+              orderInfo.dvyType === 10 &&
+              (orderInfo.hbOrderStatus === 40 || orderInfo.hbOrderStatus === 70) &&
+              orderInfo.dvyNo
+            "
+            class="mt-10px"
+            size="small"
+            type="primary"
+            ghost
+            @click="handleShip"
+          >
+            修改发货信息
+          </NButton>
         </div>
         <NCard size="small" title="业务信息" :bordered="false">
           <template v-if="orderInfo.businessType == 'XSB'">
@@ -457,20 +487,29 @@ function getRefundRecordText() {
                 <NTr>
                   <NTh>费用类型</NTh>
                   <NTh>金额/元</NTh>
+                  <NTh>备注</NTh>
                 </NTr>
               </NThead>
               <NTbody>
                 <NTr>
                   <NTd>商品总额</NTd>
                   <NTd>{{ orderInfo.total }}</NTd>
+                  <NTd></NTd>
                 </NTr>
-                <NTr>
+                <NTr v-if="orderInfo.dvyType !== 2">
                   <NTd>配送费(快递)</NTd>
                   <NTd>{{ orderInfo.freightAmount }}</NTd>
+                  <NTd></NTd>
+                </NTr>
+                <NTr v-if="orderInfo.couponBaseInfoDTO">
+                  <NTd>优惠券</NTd>
+                  <NTd>-{{ orderInfo.couponBaseInfoDTO?.discountMoney || 0 }}</NTd>
+                  <NTd>{{ orderInfo.couponBaseInfoDTO?.allowanceId }}</NTd>
                 </NTr>
                 <NTr>
                   <NTd>积分</NTd>
                   <NTd>-{{ (Number(orderInfo.offsetPoints) / 100).toFixed(2) || 0 }}</NTd>
+                  <NTd></NTd>
                 </NTr>
                 <NTr>
                   <NTd v-if="orderInfo.hbOrderStatus == orderStatusEnum.WAIT_PAY">需付款</NTd>
@@ -501,6 +540,21 @@ function getRefundRecordText() {
                 />
               </NTimeline>
             </template>
+            <template v-else-if="orderInfo.dvyType == 2">
+              <div class="py-20px font-semibold">03 自提信息</div>
+              <div>预留电话:{{ orderInfo.shopName || '---' }}</div>
+              <div>自提点:{{ orderInfo.consigneeAddress || '---' }}</div>
+              <template
+                v-if="
+                  orderInfo.hbOrderStatus == orderStatusEnum.ORDER_WAIT_DELIVERY ||
+                  orderInfo.hbOrderStatus == orderStatusEnum.ORDER_COMPLETE
+                "
+              >
+                <div>自提核验码:{{ orderInfo.selfPickCode || '---' }}</div>
+                <div>核验状态:{{ orderInfo.isWriteOff == 1 ? '待核销' : '已核销' }}</div>
+                <div>核验时间:{{ orderInfo.completeTime || '---' }}</div>
+              </template>
+            </template>
           </template>
           <template v-else-if="orderInfo.businessType == 'DYY'">
             <div class="pb-20px font-semibold">01 影片与场次信息</div>
@@ -749,7 +803,7 @@ function getRefundRecordText() {
               :key="index"
               class="mt-10px border rounded-4px p-8px"
             >
-              <div>游客姓名{{ index + 1 }}:{{ item.linkMan || '---' }}</div>
+              <div>游客姓名{{ (index as number) + 1 }}:{{ item.linkMan || '---' }}</div>
               <div>证据类型:{{ filterType(item.linkCreditType) || '---' }}</div>
               <div>证件号码:{{ item.linkCreditNo || '---' }}</div>
             </div>

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.