Selaa lähdekoodia

feat(AccountManagement): 新增分账规则功能

- 添加分账规则相关API接口
- 实现分账规则列表、新增、编辑、删除等功能
- 优化课程管理页面,增加教练和上课地点选择
- 调整开发环境代理配置
- 添加FullCalendar相关依赖
zhangtao 1 viikko sitten
vanhempi
commit
f51db11bb3
40 muutettua tiedostoa jossa 2328 lisäystä ja 100 poistoa
  1. 1 1
      .env.development
  2. 4 0
      package.json
  3. 53 11
      pnpm-lock.yaml
  4. 10 0
      src/api/common/api.ts
  5. 1 0
      src/components/Form/src/jeecg/components/JSelectRole.vue
  6. 6 6
      src/components/Form/src/jeecg/components/modal/RoleSelectModal.vue
  7. 2 2
      src/components/Form/src/jeecg/hooks/useSelectBiz.ts
  8. 4 0
      src/components/ZtCustomTable/index.vue
  9. 1 0
      src/store/modules/permission.ts
  10. 0 2
      src/store/modules/shopInfo.ts
  11. 1 0
      src/utils/http/axios/Axios.ts
  12. 57 0
      src/views/AccountManagement/accountManagement/SeparateAccounts.api.ts
  13. 144 0
      src/views/AccountManagement/accountManagement/SeparateAccounts.data.ts
  14. 133 0
      src/views/AccountManagement/accountManagement/components/SeparateAccountsModal.vue
  15. 184 0
      src/views/AccountManagement/accountManagement/index.vue
  16. 14 0
      src/views/businessManagement/courses/courses.api.ts
  17. 15 7
      src/views/businessManagement/courses/courses.data.ts
  18. 7 1
      src/views/businessManagement/courses/index.vue
  19. 17 15
      src/views/businessManagement/courses/publishcourses.vue
  20. 60 5
      src/views/businessManagement/schoolOpen/index.vue
  21. 24 0
      src/views/businessManagement/schoolOpen/schoolOpen.api.ts
  22. 20 9
      src/views/businessManagement/schoolOpen/schoolOpen.data.ts
  23. 14 0
      src/views/informationManagement/cUserInfo/cUserInfo.api.ts
  24. 131 0
      src/views/informationManagement/cUserInfo/cUserInfo.data.ts
  25. 95 0
      src/views/informationManagement/cUserInfo/components/cUserInfoModal.vue
  26. 90 0
      src/views/informationManagement/cUserInfo/index.vue
  27. 5 3
      src/views/informationManagement/shopInfo/index.vue
  28. 13 12
      src/views/informationManagement/shopInfo/shopInfo.data.ts
  29. 168 17
      src/views/informationManagement/teachorNoteach/index.vue
  30. 26 0
      src/views/informationManagement/teachorNoteach/teachorNoteach.api.ts
  31. 63 0
      src/views/operationManagement/banner/banner.api.ts
  32. 117 0
      src/views/operationManagement/banner/banner.data.ts
  33. 70 0
      src/views/operationManagement/banner/components/bannerForm.vue
  34. 78 0
      src/views/operationManagement/banner/components/bannerModal.vue
  35. 180 0
      src/views/operationManagement/banner/index.vue
  36. 3 9
      src/views/operationManagement/feedback/index.vue
  37. 163 0
      src/views/system/staff/components/staffModl.vue
  38. 26 0
      src/views/system/staff/staff.api.ts
  39. 228 0
      src/views/system/staff/staff.data.ts
  40. 100 0
      src/views/system/staff/staffList.vue

+ 1 - 1
.env.development

@@ -7,7 +7,7 @@ VITE_PUBLIC_PATH = /
 
 
 # 跨域代理,您可以配置多个 ,请注意,没有换行符
-# VITE_PROXY = [["/jeecgboot","http://192.168.1.34:8080/jeecg-boot"],["/upload","http://localhost:3300/upload"]]
+# VITE_PROXY = [["/jeecgboot","http://192.168.1.34:8080/jeecg-boot"],["/upload","http://192.168.1.34:8080/jeecg-boot"]]
 VITE_PROXY = [["/jeecgboot","http://192.168.0.11:8080/jeecg-boot"],["/upload","http://192.168.0.11:8080/upload"]]
 # VITE_PROXY = [["/jeecgboot","http://192.168.1.253:8080/jeecg-boot"],["/upload","http://192.168.1.253:8080/upload"]]
 

+ 4 - 0
package.json

@@ -23,6 +23,10 @@
   "dependencies": {
     "@ant-design/colors": "^7.2.0",
     "@ant-design/icons-vue": "^7.0.1",
+    "@fullcalendar/core": "^6.1.18",
+    "@fullcalendar/daygrid": "^6.1.18",
+    "@fullcalendar/interaction": "^6.1.18",
+    "@fullcalendar/vue3": "^6.1.18",
     "@iconify/iconify": "^3.1.1",
     "@jeecg/online": "3.7.4-beta",
     "@logicflow/vue-node-registry": "^1.0.12",

+ 53 - 11
pnpm-lock.yaml

@@ -14,6 +14,18 @@ importers:
       '@ant-design/icons-vue':
         specifier: ^7.0.1
         version: 7.0.1(vue@3.5.17(typescript@4.9.5))
+      '@fullcalendar/core':
+        specifier: ^6.1.18
+        version: 6.1.18
+      '@fullcalendar/daygrid':
+        specifier: ^6.1.18
+        version: 6.1.18(@fullcalendar/core@6.1.18)
+      '@fullcalendar/interaction':
+        specifier: ^6.1.18
+        version: 6.1.18(@fullcalendar/core@6.1.18)
+      '@fullcalendar/vue3':
+        specifier: ^6.1.18
+        version: 6.1.18(@fullcalendar/core@6.1.18)(vue@3.5.17(typescript@4.9.5))
       '@iconify/iconify':
         specifier: ^3.1.1
         version: 3.1.1
@@ -959,6 +971,25 @@ packages:
     resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
 
+  '@fullcalendar/core@6.1.18':
+    resolution: {integrity: sha512-cD7XtZIZZ87Cg2+itnpsONCsZ89VIfLLDZ22pQX4IQVWlpYUB3bcCf878DhWkqyEen6dhi5ePtBoqYgm5K+0fQ==}
+
+  '@fullcalendar/daygrid@6.1.18':
+    resolution: {integrity: sha512-s452Zle1SdMEzZDw+pDczm8m3JLIZzS9ANMThXTnqeqJewW1gqNFYas18aHypJSgF9Fh9rDJjTSUw04BpXB/Mg==}
+    peerDependencies:
+      '@fullcalendar/core': ~6.1.18
+
+  '@fullcalendar/interaction@6.1.18':
+    resolution: {integrity: sha512-f/mD5RTjzw+Q6MGTMZrLCgIrQLIUUO9NV/58aM2J6ZBQZeRlNizDqmqldqyG+j49zj2vFhUfZibPrVKWm5yA4Q==}
+    peerDependencies:
+      '@fullcalendar/core': ~6.1.18
+
+  '@fullcalendar/vue3@6.1.18':
+    resolution: {integrity: sha512-YMagwTumxsIx3GFYWLa9Yr73EMA+JuH6S3EeZGS+rEjvG5fDGdf+33rxGMzmw+LdO7SWi3ctbzRnJlv3fnm3RQ==}
+    peerDependencies:
+      '@fullcalendar/core': ~6.1.18
+      vue: ^3.0.11
+
   '@humanwhocodes/config-array@0.13.0':
     resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==}
     engines: {node: '>=10.10.0'}
@@ -1207,67 +1238,56 @@ packages:
     resolution: {integrity: sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ==}
     cpu: [arm]
     os: [linux]
-    libc: [glibc]
 
   '@rollup/rollup-linux-arm-musleabihf@4.44.1':
     resolution: {integrity: sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw==}
     cpu: [arm]
     os: [linux]
-    libc: [musl]
 
   '@rollup/rollup-linux-arm64-gnu@4.44.1':
     resolution: {integrity: sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ==}
     cpu: [arm64]
     os: [linux]
-    libc: [glibc]
 
   '@rollup/rollup-linux-arm64-musl@4.44.1':
     resolution: {integrity: sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g==}
     cpu: [arm64]
     os: [linux]
-    libc: [musl]
 
   '@rollup/rollup-linux-loongarch64-gnu@4.44.1':
     resolution: {integrity: sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew==}
     cpu: [loong64]
     os: [linux]
-    libc: [glibc]
 
   '@rollup/rollup-linux-powerpc64le-gnu@4.44.1':
     resolution: {integrity: sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA==}
     cpu: [ppc64]
     os: [linux]
-    libc: [glibc]
 
   '@rollup/rollup-linux-riscv64-gnu@4.44.1':
     resolution: {integrity: sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw==}
     cpu: [riscv64]
     os: [linux]
-    libc: [glibc]
 
   '@rollup/rollup-linux-riscv64-musl@4.44.1':
     resolution: {integrity: sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg==}
     cpu: [riscv64]
     os: [linux]
-    libc: [musl]
 
   '@rollup/rollup-linux-s390x-gnu@4.44.1':
     resolution: {integrity: sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw==}
     cpu: [s390x]
     os: [linux]
-    libc: [glibc]
 
   '@rollup/rollup-linux-x64-gnu@4.44.1':
     resolution: {integrity: sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw==}
     cpu: [x64]
     os: [linux]
-    libc: [glibc]
 
   '@rollup/rollup-linux-x64-musl@4.44.1':
     resolution: {integrity: sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g==}
     cpu: [x64]
     os: [linux]
-    libc: [musl]
 
   '@rollup/rollup-win32-arm64-msvc@4.44.1':
     resolution: {integrity: sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg==}
@@ -4927,6 +4947,9 @@ packages:
     resolution: {integrity: sha512-spBB5sgC4cv2YcW03f/IAUN1pgDJWNWD8FzkyY4mArLUMJW+KlQhlmUdKAHQuPfb00Jl5xIfImeOsf6YL8QK7Q==}
     engines: {node: '>=0.10.0'}
 
+  preact@10.12.1:
+    resolution: {integrity: sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==}
+
   preact@10.26.9:
     resolution: {integrity: sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA==}
 
@@ -6850,6 +6873,23 @@ snapshots:
 
   '@eslint/js@8.57.1': {}
 
+  '@fullcalendar/core@6.1.18':
+    dependencies:
+      preact: 10.12.1
+
+  '@fullcalendar/daygrid@6.1.18(@fullcalendar/core@6.1.18)':
+    dependencies:
+      '@fullcalendar/core': 6.1.18
+
+  '@fullcalendar/interaction@6.1.18(@fullcalendar/core@6.1.18)':
+    dependencies:
+      '@fullcalendar/core': 6.1.18
+
+  '@fullcalendar/vue3@6.1.18(@fullcalendar/core@6.1.18)(vue@3.5.17(typescript@4.9.5))':
+    dependencies:
+      '@fullcalendar/core': 6.1.18
+      vue: 3.5.17(typescript@4.9.5)
+
   '@humanwhocodes/config-array@0.13.0':
     dependencies:
       '@humanwhocodes/object-schema': 2.0.3
@@ -11439,6 +11479,8 @@ snapshots:
       posthtml-parser: 0.2.1
       posthtml-render: 1.4.0
 
+  preact@10.12.1: {}
+
   preact@10.26.9: {}
 
   prelude-ls@1.2.1: {}

+ 10 - 0
src/api/common/api.ts

@@ -18,6 +18,7 @@ enum Api {
   sprotProject = '/app/appCategory/list',
   siteAdd = '/appSite/add',
   siteList = '/appSite/list',
+  getDepList = '/sys/sysDepart/findByDeptList',
 }
 
 /**
@@ -174,3 +175,12 @@ export function siteAdd(params) {
 export function getSiteList(params) {
   return defHttp.get({ url: Api.siteList, params });
 }
+
+/**
+ * 查询当前用户所在部门及下级部门
+ * @param params
+ * @returns
+ */
+export function getDepListData(params) {
+  return defHttp.get({ url: Api.getDepList, params });
+}

+ 1 - 0
src/components/Form/src/jeecg/components/JSelectRole.vue

@@ -121,6 +121,7 @@
         // update-begin--author:liaozhiyang---date:20250318---for:【issues/7948】修复JselectRole组件不支持双向绑定
         emit('update:value', values);
         // update-end--author:liaozhiyang---date:20250318---for:【issues/7948】修复JselectRole组件不支持双向绑定
+        emit('options-change', options);
       }
       const getBindValue = Object.assign({}, unref(props), unref(attrs));
       return {

+ 6 - 6
src/components/Form/src/jeecg/components/modal/RoleSelectModal.vue

@@ -69,12 +69,12 @@
         },
         //update-begin-author:liusq date:2023-10-30 for: [issues/5514]组件页面显示错位
         actionColOptions: {
-            xs: 24,
-            sm: 8,
-            md: 8,
-            lg: 8,
-            xl: 8,
-            xxl: 8,
+          xs: 24,
+          sm: 8,
+          md: 8,
+          lg: 8,
+          xl: 8,
+          xxl: 8,
         },
         //update-end-author:liusq date:2023-10-30 for: [issues/5514]组件页面显示错位
         schemas: [

+ 2 - 2
src/components/Form/src/jeecg/hooks/useSelectBiz.ts

@@ -78,7 +78,7 @@ export function useSelectBiz(getList, props, emit?) {
     onChange: onSelectChange,
     //update-begin-author:wangshuai---date:20221102--for: [VUEN-2562]用户选择,跨页选择后,只有当前页人员 ---
     //table4.4.0新增属性选中之后是否清空上一页下一页的数据,默认false
-    preserveSelectedRowKeys:true,
+    preserveSelectedRowKeys: true,
     //update-end-author:wangshuai---date:20221102--for: [VUEN-2562]用户选择,跨页选择后,只有当前页人员 ---
   };
 
@@ -141,7 +141,7 @@ export function useSelectBiz(getList, props, emit?) {
     let options = <any[]>[];
     let values = <any[]>[];
     selectRows.value.forEach((item) => {
-      options.push({ label: item[props.labelKey], value: item[props.rowKey] });
+      options.push({ label: item[props.labelKey], value: item[props.rowKey], roleCode: item['roleCode'] });
     });
     checkedKeys.value.forEach((item) => {
       values.push(item);

+ 4 - 0
src/components/ZtCustomTable/index.vue

@@ -22,17 +22,20 @@
   import { useListPage } from '/@/hooks/system/useListPage';
   import _ from 'lodash-es';
   import dayjs from 'dayjs';
+  import { useMessage } from '/@/hooks/web/useMessage';
   interface Props {
     tableColumn: BasicColumn[];
     value: any;
     showAction?: boolean;
     showIndex?: boolean;
+    count?: number;
   }
   const props = withDefaults(defineProps<Props>(), {
     tableColumn: () => [],
     value: () => [],
     showAction: () => true,
     showIndex: () => false,
+    count: () => 9999,
   });
   const emit = defineEmits(['update:value']);
   const modelValue = computed({
@@ -81,6 +84,7 @@
     return newRow;
   };
   function handleAdd() {
+    if (props.count == modelValue.value.length) return useMessage().createMessage.error('最多添加' + props.count + '行数据');
     modelValue.value.push(addEmptyRow(props.tableColumn));
     modelValue.value = [].concat(modelValue.value);
   }

+ 1 - 0
src/store/modules/permission.ts

@@ -130,6 +130,7 @@ export const usePermissionStore = defineStore({
 
       //菜单路由
       const routeList = systemPermission.menu;
+
       return routeList;
     },
     async buildRoutesAction(): Promise<AppRouteRecordRaw[]> {

+ 0 - 2
src/store/modules/shopInfo.ts

@@ -71,8 +71,6 @@ export const useShopInfoStore = defineStore({
   actions: {
     async getCurrentDep() {
       const result = await getUserDeparts();
-      console.log(result, 'asdas');
-
       this.currentDep = result.list[0];
       this.deptList = result.list;
       this.currentId = this.currentDep.id;

+ 1 - 0
src/utils/http/axios/Axios.ts

@@ -202,6 +202,7 @@ export class VAxios {
 
   request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
     let conf: CreateAxiosOptions = cloneDeep(config);
+
     const transform = this.getTransform();
 
     const { requestOptions } = this.options;

+ 57 - 0
src/views/AccountManagement/accountManagement/SeparateAccounts.api.ts

@@ -0,0 +1,57 @@
+import { defHttp } from '/@/utils/http/axios';
+import { useMessage } from '/@/hooks/web/useMessage';
+
+const { createConfirm } = useMessage();
+
+enum Api {
+  list = '/separateAccounts/separateAccounts/list',
+  save = '/separateAccounts/separateAccounts/add',
+  edit = '/separateAccounts/separateAccounts/edit',
+  deleteOne = '/separateAccounts/separateAccounts/delete',
+  deleteBatch = '/separateAccounts/separateAccounts/deleteBatch',
+  queryById = '/separateAccounts/separateAccounts/queryById',
+}
+/**
+ * 列表接口
+ * @param params
+ */
+export const list = (params) => defHttp.get({ url: Api.list, params });
+/**
+ * 删除单个
+ */
+export const deleteOne = (params, handleSuccess) => {
+  return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
+    handleSuccess();
+  });
+};
+
+/**
+ * 批量删除
+ * @param params
+ */
+export const batchDelete = (params, handleSuccess) => {
+  createConfirm({
+    iconType: 'warning',
+    title: '确认删除',
+    content: '是否删除选中数据',
+    okText: '确认',
+    cancelText: '取消',
+    onOk: () => {
+      return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
+        handleSuccess();
+      });
+    },
+  });
+};
+/**
+ * 保存或者更新
+ * @param params
+ */
+export const saveOrUpdate = (params, isUpdate) => {
+  let url = isUpdate ? Api.edit : Api.save;
+  return defHttp.post({ url: url, params });
+};
+
+export const getDetaile = (params) => {
+  return defHttp.get({ url: Api.queryById, params });
+};

+ 144 - 0
src/views/AccountManagement/accountManagement/SeparateAccounts.data.ts

@@ -0,0 +1,144 @@
+import { BasicColumn } from '/@/components/Table';
+import { FormSchema } from '/@/components/Table';
+import { rules } from '/@/utils/helper/validator';
+import { render } from '/@/utils/common/renderUtils';
+import { getWeekMonthQuarterYear } from '/@/utils';
+import { getUserDeparts } from '/@/views/system/depart/depart.api';
+
+//列表数据
+export const columns: BasicColumn[] = [
+  {
+    title: '商户名称',
+    align: 'center',
+    dataIndex: 'merchantName',
+  },
+  {
+    title: '分账规则',
+    align: 'center',
+    dataIndex: 'ptSeparateAccounts',
+    customRender(opt) {
+      return `平台${opt.record.ptSeparateAccounts}% 商家${opt.record.shSeparateAccounts}% 门店${opt.record.mdSeparateAccounts}%`;
+    },
+  },
+  {
+    title: '分账状态',
+    align: 'center',
+    dataIndex: 'accountStatus',
+    slots: { customRender: 'status' },
+  },
+  {
+    title: '修改时间',
+    align: 'center',
+    dataIndex: 'updateTime',
+  },
+];
+//查询数据
+export const searchFormSchema: FormSchema[] = [
+  {
+    label: '商户名称',
+    field: 'merchantName',
+    component: 'Input',
+    colProps: { span: 6 },
+  },
+  {
+    label: '分账状态',
+    field: 'accountStatus',
+    component: 'Select',
+    colProps: { span: 6 },
+    componentProps: {
+      options: [
+        { label: '所有', value: null },
+        { label: '启用', value: 0 },
+        { label: '停用', value: 1 },
+      ],
+    },
+  },
+];
+//表单数据
+export const formSchema: FormSchema[] = [
+  {
+    label: '商户名称',
+    field: 'deptId',
+    component: 'ApiSelect',
+    componentProps: {
+      api: getUserDeparts,
+      labelField: 'departName',
+      valueField: 'id',
+      placeholder: '请选择商户名称',
+      resultField: 'list',
+    },
+    required: true,
+  },
+  {
+    label: '分账规则',
+    field: 'ptSeparateAccounts',
+    component: 'InputNumber',
+    slot: 'SeparateAccounts',
+    required: true,
+    dynamicRules: ({ model, schema }) => {
+      return [
+        {
+          required: true,
+          validator: (_, value) => {
+            if (!model.ptSeparateAccounts || !model.shSeparateAccounts) {
+              return Promise.reject('请输入分账规则');
+            }
+            return Promise.resolve();
+          },
+        },
+      ];
+    },
+  },
+  {
+    label: '',
+    field: 'shSeparateAccounts',
+    component: 'InputNumber',
+    required: true,
+    show: false,
+  },
+  {
+    label: '',
+    field: 'mdSeparateAccounts',
+    component: 'InputNumber',
+    required: true,
+    show: false,
+  },
+
+  {
+    label: '分账状态',
+    field: 'accountStatus',
+    component: 'Switch',
+    componentProps: {
+      options: [
+        {
+          label: '启用',
+          value: 0,
+        },
+        {
+          label: '停用',
+          value: 1,
+        },
+      ],
+      checkedValue: 0,
+      unCheckedValue: 1,
+    },
+    defaultValue: 0,
+    required: true,
+  },
+  // TODO 主键隐藏字段,目前写死为ID
+  {
+    label: '',
+    field: 'id',
+    component: 'Input',
+    show: false,
+  },
+];
+
+/**
+ * 流程表单调用这个方法获取formSchema
+ * @param param
+ */
+export function getBpmFormSchema(_formData): FormSchema[] {
+  // 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
+  return formSchema;
+}

+ 133 - 0
src/views/AccountManagement/accountManagement/components/SeparateAccountsModal.vue

@@ -0,0 +1,133 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="1150" @ok="handleSubmit">
+    <BasicForm @register="registerForm" name="SeparateAccountsForm">
+      <template #SeparateAccounts="{ model, feild }">
+        <FormItem>
+          <InputNumber
+            addonBefore="平台"
+            addonAfter="%"
+            v-model:value="model['ptSeparateAccounts']"
+            class="mr-5"
+            :precision="2"
+            :max="ptSeparateAccountsMax"
+            :min="1"
+            @change="handleShChange"
+            placeholder="请输入平台分账比例"
+          ></InputNumber>
+          <InputNumber
+            addonBefore="商户"
+            addonAfter="%"
+            v-model:value="model['shSeparateAccounts']"
+            class="mr-5"
+            :precision="2"
+            :max="shSeparateAccountsMax"
+            :min="1"
+            placeholder="请输入商户分账比例"
+            @change="handleShChange"
+          ></InputNumber>
+          <InputNumber
+            addonBefore="门店"
+            disabled
+            placeholder="自动计算"
+            v-model:value="model['mdSeparateAccounts']"
+            :precision="2"
+            :min="0"
+          ></InputNumber>
+        </FormItem>
+      </template>
+    </BasicForm>
+  </BasicModal>
+</template>
+
+<script lang="ts" setup>
+  import { InputNumber, FormItem } from 'ant-design-vue';
+  import { ref, computed, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form/index';
+  import { formSchema } from '../SeparateAccounts.data';
+  import { saveOrUpdate } from '../SeparateAccounts.api';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  const { createMessage } = useMessage();
+  // Emits声明
+  const emit = defineEmits(['register', 'success']);
+  const isUpdate = ref(true);
+  const isDetail = ref(false);
+  const ptSeparateAccountsMax = ref(100);
+  const shSeparateAccountsMax = ref(100);
+
+  //表单配置
+  const [registerForm, { setProps, resetFields, setFieldsValue, validate, scrollToField, getFieldsValue }] = useForm({
+    labelWidth: 150,
+    schemas: formSchema,
+    showActionButtonGroup: false,
+    baseColProps: { span: 24 },
+  });
+  //表单赋值
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
+    //重置表单
+    await resetFields();
+    setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
+    isUpdate.value = !!data?.isUpdate;
+    isDetail.value = !!data?.showFooter;
+    if (unref(isUpdate)) {
+      //表单赋值
+      await setFieldsValue({
+        ...data.data,
+      });
+    }
+    // 隐藏底部时禁用整个表单
+    setProps({ disabled: !data?.showFooter });
+  });
+  //设置标题
+  const title = computed(() => (!unref(isUpdate) ? '添加分账规则' : !unref(isDetail) ? '分账规则详情' : '编辑分账规则'));
+  //表单提交事件
+  async function handleSubmit(v) {
+    try {
+      let values = await validate();
+      setModalProps({ confirmLoading: true });
+      //提交表单
+      await saveOrUpdate(values, isUpdate.value);
+      //关闭弹窗
+      closeModal();
+      //刷新列表
+      emit('success');
+    } catch ({ errorFields }) {
+      if (errorFields) {
+        const firstField = errorFields[0];
+        if (firstField) {
+          scrollToField(firstField.name, { behavior: 'smooth', block: 'center' });
+        }
+      }
+      return Promise.reject(errorFields);
+    } finally {
+      setModalProps({ confirmLoading: false });
+    }
+  }
+  function handleShChange() {
+    const field = getFieldsValue();
+    if (field.ptSeparateAccounts) {
+      shSeparateAccountsMax.value = 100 - field.ptSeparateAccounts;
+    }
+    if (field.shSeparateAccounts) {
+      ptSeparateAccountsMax.value = 100 - field.shSeparateAccounts;
+    }
+    if (field.shSeparateAccounts || field.ptSeparateAccounts) {
+      const pt = Number(field.ptSeparateAccounts) || 0;
+      const sh = Number(field.shSeparateAccounts) || 0;
+      setFieldsValue({
+        mdSeparateAccounts: 100 - pt - sh,
+      });
+    }
+  }
+</script>
+
+<style lang="less" scoped>
+  /** 时间和数字输入框样式 */
+  :deep(.ant-input-number) {
+    width: 100%;
+  }
+
+  :deep(.ant-calendar-picker) {
+    width: 100%;
+  }
+</style>

+ 184 - 0
src/views/AccountManagement/accountManagement/index.vue

@@ -0,0 +1,184 @@
+<template>
+  <div>
+    <!--引用表格-->
+    <BasicTable @register="registerTable" :rowSelection="rowSelection">
+      <!--插槽:table标题-->
+      <template #tableTitle>
+        <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
+        <a-dropdown v-if="selectedRowKeys.length > 0">
+          <template #overlay>
+            <a-menu>
+              <a-menu-item key="1" @click="batchHandleDelete">
+                <Icon icon="ant-design:delete-outlined"></Icon>
+                删除
+              </a-menu-item>
+            </a-menu>
+          </template>
+          <a-button
+            >批量操作
+            <Icon icon="mdi:chevron-down"></Icon>
+          </a-button>
+        </a-dropdown>
+      </template>
+      <!--操作栏-->
+      <template #action="{ record }">
+        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
+      </template>
+      <template #status="{ record }">
+        <Switch v-model:checked="record.accountStatus" @change="handleSwitchChange($event, record)" :checked-value="0" :un-checked-value="1"></Switch>
+      </template>
+    </BasicTable>
+
+    <!-- 表单区域 -->
+    <SeparateAccountsModal @register="registerModal" @success="handleSuccess"></SeparateAccountsModal>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { Switch } from 'ant-design-vue';
+  import { ref, reactive, computed, unref } from 'vue';
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { useModal } from '/@/components/Modal';
+  import { useListPage } from '/@/hooks/system/useListPage';
+  import SeparateAccountsModal from './components/SeparateAccountsModal.vue';
+  import { columns, searchFormSchema } from './SeparateAccounts.data';
+  import { list, deleteOne, batchDelete, saveOrUpdate, getDetaile } from './SeparateAccounts.api';
+  import { useUserStore } from '/@/store/modules/user';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  const queryParam = reactive<any>({});
+  const checkedKeys = ref<Array<string | number>>([]);
+  const userStore = useUserStore();
+  const { createMessage, createWarningModal } = useMessage();
+  //注册model
+  const [registerModal, { openModal }] = useModal();
+  //注册table数据
+  const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
+    tableProps: {
+      title: 'nm_separate_accounts',
+      api: list,
+      columns,
+      canResize: false,
+      formConfig: {
+        //labelWidth: 120,
+        schemas: searchFormSchema,
+        autoSubmitOnEnter: true,
+        showAdvancedButton: true,
+        fieldMapToNumber: [],
+        fieldMapToTime: [],
+      },
+      actionColumn: {
+        width: 120,
+        fixed: 'right',
+      },
+      beforeFetch: (params) => {
+        return Object.assign(params, queryParam);
+      },
+    },
+  });
+
+  const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
+
+  /**
+   * 新增事件
+   */
+  function handleAdd() {
+    openModal(true, {
+      isUpdate: false,
+      showFooter: true,
+    });
+  }
+  /**
+   * 编辑事件
+   */
+  async function handleEdit(record: Recordable) {
+    const data = await getDetaile({ id: record.id });
+    openModal(true, {
+      data,
+      isUpdate: true,
+      showFooter: true,
+    });
+  }
+  /**
+   * 详情
+   */
+  async function handleDetail(record: Recordable) {
+    const data = await getDetaile(record.id);
+    openModal(true, {
+      data,
+      isUpdate: true,
+      showFooter: false,
+    });
+  }
+  /**
+   * 删除事件
+   */
+  async function handleDelete(record) {
+    await deleteOne({ id: record.id }, handleSuccess);
+  }
+  /**
+   * 批量删除事件
+   */
+  async function batchHandleDelete() {
+    await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
+  }
+  /**
+   * 成功回调
+   */
+  function handleSuccess() {
+    (selectedRowKeys.value = []) && reload();
+  }
+  /**
+   * 操作栏
+   */
+  function getTableAction(record) {
+    return [
+      {
+        label: '编辑',
+        onClick: handleEdit.bind(null, record),
+        auth: 'separateAccounts:nm_separate_accounts:add',
+      },
+    ];
+  }
+  /**
+   * 下拉操作栏
+   */
+  function getDropDownAction(record) {
+    return [
+      {
+        label: '详情',
+        onClick: handleDetail.bind(null, record),
+      },
+      {
+        label: '删除',
+        popConfirm: {
+          title: '是否确认删除',
+          confirm: handleDelete.bind(null, record),
+          placement: 'topLeft',
+        },
+        auth: 'separateAccounts:nm_separate_accounts:delete',
+      },
+    ];
+  }
+  function handleSwitchChange(e, record) {
+    createWarningModal({
+      title: !e ? '确认要开启该商户的对账吗?开启后,分账正常进行。' : '确认关闭该商户的分账吗?关闭后,到了分账时间点后暂停分账。',
+      okText: '确认',
+      cancelText: '取消',
+      okCancel: true,
+      onOk: async () => {
+        await saveOrUpdate({ id: record.id, accountStatus: e }, true);
+        reload();
+      },
+      onCancel: () => {
+        reload();
+      },
+    });
+  }
+</script>
+
+<style lang="less" scoped>
+  :deep(.ant-picker),
+  :deep(.ant-input-number) {
+    width: 100%;
+  }
+</style>

+ 14 - 0
src/views/businessManagement/courses/courses.api.ts

@@ -9,6 +9,8 @@ enum Api {
   edit = '/app/appCourese/edit',
   deleteOne = '/app/appCourese/delete',
   detaile = 'app/appCourese/queryById',
+  address = '/app/appCourese/queryAddressList',
+  Coach = '/staff/staff/queryList',
 }
 export const list = (params) => defHttp.get({ url: Api.list, params });
 
@@ -33,3 +35,15 @@ export const saveOrUpdate = (params, isUpdate) => {
 export const getDetaile = (params) => {
   return defHttp.get({ url: Api.detaile, params });
 };
+
+export const getAddress = (params) => {
+  return defHttp.get({ url: Api.address, params });
+};
+/**
+ * 获取教练列表
+ * @param params
+ * @returns
+ */
+export const getCoachList = (params) => {
+  return defHttp.get({ url: Api.Coach, params });
+};

+ 15 - 7
src/views/businessManagement/courses/courses.data.ts

@@ -6,9 +6,8 @@ import { getWeekMonthQuarterYear } from '/@/utils';
 import { getSprotProject } from '/@/api/common/api';
 const sportList = await getSprotProject({ pageSize: 20 });
 import { defHttp } from '/@/utils/http/axios';
-import { ZtTableColumnProps } from '/#/utils';
-import { ref } from 'vue';
-
+import { getAddress, getCoachList } from './courses.api';
+import { useUserStore } from '/@/store/modules/user';
 //列表数据
 export const columns: BasicColumn[] = [
   {
@@ -143,16 +142,25 @@ export const formSchema: FormSchema[] = [
   {
     label: '上课地点',
     field: 'siteId',
-    component: 'Select',
+    component: 'ApiSelect',
     required: true,
-    defaultValue: '福利中心',
+    componentProps: {
+      api: getAddress,
+      labelField: 'address',
+      valueField: 'siteId',
+    },
   },
   {
     label: '教练',
     field: 'userId',
-    component: 'Select',
+    component: 'ApiSelect',
     required: true,
-    defaultValue: 1,
+    componentProps: {
+      api: getCoachList,
+      labelField: 'name',
+      valueField: 'userId',
+      params: { orgCode: useUserStore().userInfo?.orgCode },
+    },
   },
   {
     label: 'title2',

+ 7 - 1
src/views/businessManagement/courses/index.vue

@@ -48,10 +48,13 @@
   import { list, deleteOne } from './courses.api';
   import { useUserStore } from '/@/store/modules/user';
   import { useMessage } from '/@/hooks/web/useMessage';
+  import { useShopInfoStore } from '/@/store/modules/shopInfo';
   const queryParam = reactive<any>({});
   const checkedKeys = ref<Array<string | number>>([]);
   const userStore = useUserStore();
   const { createMessage } = useMessage();
+  useShopInfoStore().getCurrentDep();
+
   //注册model
   const [registerModal, { openModal }] = useModal();
   //注册table数据
@@ -88,6 +91,9 @@
   function handleView(record: Recordable) {
     router.push({ name: 'businessManagement-publishcourses', query: { type: 1, id: record.id } });
   }
+  function handleEdit(record: Recordable) {
+    router.push({ name: 'businessManagement-publishcourses', query: { type: 2, id: record.id } });
+  }
   /**
    * 详情
    */
@@ -123,7 +129,7 @@
       },
       {
         label: '编辑',
-        onClick: handleView.bind(null, record),
+        onClick: handleEdit.bind(null, record),
         // auth: 'courses:nm_courses:edit',
       },
     ];

+ 17 - 15
src/views/businessManagement/courses/publishcourses.vue

@@ -28,7 +28,7 @@
         </template>
 
         <template #formFooter>
-          <div class="w-full flex items-center justify-center my-3" v-if="Number(route.query.type) == 0">
+          <div class="w-full flex items-center justify-center my-3" v-if="Number(route.query.type) != 1">
             <a-button type="primary" @click="save" class="mr-2" :loading="isSubmit"> 保存 </a-button>
             <a-button type="error" @click="back" class="mr-2"> 关闭 </a-button>
           </div>
@@ -51,7 +51,8 @@
   import { ref } from 'vue';
   import { useRoute } from 'vue-router';
   import { nextTick } from 'vue';
-
+  import { useShopInfoStore } from '/@/store/modules/shopInfo';
+  useShopInfoStore().getCurrentDep();
   const { close: closeTab } = useTabs();
   const route = useRoute();
   const [registerForm, { setProps, resetFields, setFieldsValue, updateSchema, validate, clearValidate, getFieldsValue }] = useForm({
@@ -62,14 +63,12 @@
   const viewDataSport = ref<any>({});
   const customTableData = ref({});
   async function isView() {
-    if (Number(route.query.type) == 1) {
+    if (Number(route.query.type) != 0) {
       await getDataList();
       nextTick(async () => {
         setProps({
-          disabled: true,
+          disabled: Number(route.query.type) == 1,
         });
-        console.log('viewDataSportList', customTableData.value);
-
         await setFieldsValue({
           ...viewDataSport.value,
           categoryId: viewDataSport.value.categoryId.split(','),
@@ -97,25 +96,28 @@
     await closeTab();
   }
   async function save() {
-    const form = await validate();
+    await validate();
+    const form = await getFieldsValue();
+    isSubmit.value = true;
     const newObj = {
-      courses: { ...form },
-      priceRulesList: transformData(form.priceRulesList.obj),
+      courses: { ...form, priceRulesList: null },
+      priceRulesList: transformData(form.priceRulesList),
     };
     try {
-      await saveOrUpdate(newObj, false);
-      await closeTab();
+      await saveOrUpdate(newObj, Number(route.query.type) != 0);
+      isSubmit.value = false;
+      back();
     } catch (error) {
       console.log(error);
+      isSubmit.value = false;
     }
-    console.log(newObj);
   }
   function transformData(data) {
     return data.map((it) => {
       return {
-        startTime: dayjs(it.time[0]).format('YYYY-MM-DD hh:mm:ss'),
-        endTime: dayjs(it.time[1]).format('YYYY-MM-DD hh:mm:ss'),
-        name: it.name,
+        startTime: dayjs(it.editValueRefs.time[0]).format('YYYY-MM-DD hh:mm:ss'),
+        endTime: dayjs(it.editValueRefs.time[1]).format('YYYY-MM-DD hh:mm:ss'),
+        name: it.editValueRefs.name.value,
       };
     });
   }

+ 60 - 5
src/views/businessManagement/schoolOpen/index.vue

@@ -11,14 +11,14 @@
           <Divider></Divider>
         </template>
         <template #ZtCustomTable1="{ model, field }">
-          <ZtCustomTable :tableColumn="ScheduleArrangementColums" v-model:value="model[field]"></ZtCustomTable>
+          <ZtCustomTable :tableColumn="ScheduleArrangementColums" v-model:value="model[field]" :count="3"></ZtCustomTable>
         </template>
         <template #ZtCustomTable2="{ model, field }">
-          <ZtCustomTable :tableColumn="ScheduleArrangementColums" v-model:value="model[field]"></ZtCustomTable>
+          <ZtCustomTable :tableColumn="ScheduleArrangementColums" v-model:value="model[field]" :count="1"></ZtCustomTable>
         </template>
         <template #formFooter>
           <div style="margin: 0 auto">
-            <a-button type="primary" @click="save" class="mr-2"> 保存 </a-button>
+            <a-button type="primary" @click="save" class="mr-2" :loading="isSubmit"> 保存 </a-button>
             <a-button type="error" @click="back" class="mr-2"> 关闭 </a-button>
           </div>
         </template>
@@ -31,14 +31,69 @@
   import { TypographyTitle, Divider } from 'ant-design-vue';
   import { BasicForm, useForm } from '/@/components/Form/index';
   import ZtCustomTable from '/@/components/ZtCustomTable/index.vue';
-  import { formSchema, ScheduleArrangementColums, SchedulePricesColums } from './schoolOpen.data';
+  import { useUserStore } from '/@/store/modules/user';
+  import { formSchema, ScheduleArrangementColums } from './schoolOpen.data';
+  import { getDetails, saveOrUpdate } from './schoolOpen.api';
+  import { useShopInfoStore } from '/@/store/modules/shopInfo';
+  import { onUnmounted, ref } from 'vue';
+  useShopInfoStore().getCurrentDep();
   const [registerForm, { setProps, resetFields, setFieldsValue, updateSchema, validate, clearValidate, getFieldsValue }] = useForm({
     schemas: formSchema,
     showActionButtonGroup: false,
   });
+  const isSubmit = ref(false);
   function back() {}
   async function save() {
+    await validate();
+    isSubmit.value = true;
     const form = await getFieldsValue();
-    console.log(form);
+    const newObj = {
+      ...form,
+      noTeachingDay: JSON.stringify({
+        data: form.noTeachingDay.map((it) => {
+          return {
+            ticketNum: it.editValueRefs.ticketNum.value,
+            startTime: it.editValueRefs.time.value[0],
+            endTime: it.editValueRefs.time.value[1],
+          };
+        }),
+      }),
+      teachingDay: JSON.stringify({
+        data: form.teachingDay.map((it) => {
+          return {
+            ticketNum: it.editValueRefs.ticketNum.value,
+            startTime: it.editValueRefs.time.value[0],
+            endTime: it.editValueRefs.time.value[1],
+          };
+        }),
+      }),
+    };
+    // console.log(form);
+    try {
+      await saveOrUpdate(newObj);
+      isSubmit.value = false;
+      back();
+    } catch (error) {
+      console.log(error);
+      isSubmit.value = false;
+    }
+    console.log(newObj);
   }
+  async function getData() {
+    const res = await getDetails({ orgCode: useUserStore().userInfo?.orgCode });
+    setFieldsValue({
+      ...res,
+      noTeachingDay: JSON.parse(res.noTeachingDay).data.map((it) => {
+        return { ...it, time: [it.startTime, it.endTime] };
+      }),
+      teachingDay: JSON.parse(res.teachingDay).data.map((it) => {
+        return { ...it, time: [it.startTime, it.endTime] };
+      }),
+    });
+    console.log(JSON.parse(res.noTeachingDay));
+  }
+  getData();
+  onUnmounted(() => {
+    useShopInfoStore().isShowSelect = false;
+  });
 </script>

+ 24 - 0
src/views/businessManagement/schoolOpen/schoolOpen.api.ts

@@ -0,0 +1,24 @@
+import { defHttp } from '/@/utils/http/axios';
+import { useMessage } from '/@/hooks/web/useMessage';
+
+const { createConfirm } = useMessage();
+
+enum Api {
+  details = '/app/appSitePlace/querySchoolPlace',
+  save = '/app/appSitePlace/addSchoolPlace',
+  edit = '/appFeedback/edit',
+}
+/**
+ *
+ * @param params
+ */
+export const getDetails = (params) => defHttp.get({ url: Api.details, params });
+
+/**
+ * 保存或者更新
+ * @param params
+ */
+export const saveOrUpdate = (params) => {
+  // let url = isUpdate ? Api.edit : Api.save;
+  return defHttp.post({ url: Api.save, params });
+};

+ 20 - 9
src/views/businessManagement/schoolOpen/schoolOpen.data.ts

@@ -1,13 +1,22 @@
+import { storeToRefs } from 'pinia';
 import { ZtTableColumnProps } from '/#/utils';
 import { BasicColumn, FormSchema } from '/@/components/Table';
-
+import { useShopInfoStore } from '/@/store/modules/shopInfo';
+const { deptList } = storeToRefs(useShopInfoStore());
 export const formSchema: FormSchema[] = [
   {
-    field: 'name',
+    field: 'tenantId',
     label: '营业名称',
     component: 'Select',
     required: true,
     labelWidth: 120,
+    componentProps: {
+      options: deptList,
+      fieldNames: {
+        label: 'departName',
+        value: 'id',
+      },
+    },
     colProps: {
       span: 14,
       xs: 24,
@@ -21,20 +30,21 @@ export const formSchema: FormSchema[] = [
     labelWidth: 0,
   },
   {
-    field: 'ScheduleData1',
+    field: 'teachingDay',
     label: '教学日',
     component: 'Input',
     slot: 'ZtCustomTable1',
     defaultValue: [],
     required: true,
     labelWidth: 120,
+
     colProps: {
       span: 20,
       xs: 24,
     },
   },
   {
-    field: 'ScheduleData2',
+    field: 'noTeachingDay',
     label: '非教学日',
     component: 'Input',
     slot: 'ZtCustomTable2',
@@ -54,7 +64,7 @@ export const formSchema: FormSchema[] = [
     labelWidth: 0,
   },
   {
-    field: 'name',
+    field: 'cover',
     label: '封面',
     component: 'JImageUpload',
     required: true,
@@ -65,7 +75,7 @@ export const formSchema: FormSchema[] = [
     },
   },
   {
-    field: 'name',
+    field: 'insurance',
     label: '配套保险',
     component: 'RadioGroup',
     componentProps: {
@@ -82,7 +92,7 @@ export const formSchema: FormSchema[] = [
     },
   },
   {
-    field: 'name',
+    field: 'reminder',
     label: '使用须知',
     component: 'InputTextArea',
     required: true,
@@ -98,11 +108,12 @@ export const ScheduleArrangementColums: BasicColumn[] = [
   {
     title: '时间段',
     dataIndex: 'time',
-    editComponent: 'RangePicker',
+    editComponent: 'TimeRangePicker',
     editRule: true,
     editComponentProps: {
       placeholder: ['开始时间', '结束时间'],
       size: 'middle',
+      valueFormat: 'HH:mm',
     },
     width: 350,
     editRow: true,
@@ -111,7 +122,7 @@ export const ScheduleArrangementColums: BasicColumn[] = [
   {
     title: '总票数',
     key: 'InputNumber',
-    dataIndex: 'price',
+    dataIndex: 'ticketNum',
     width: 250,
     editComponent: 'InputNumber',
     editRule: true,

+ 14 - 0
src/views/informationManagement/cUserInfo/cUserInfo.api.ts

@@ -0,0 +1,14 @@
+import { defHttp } from '/@/utils/http/axios';
+enum Api {
+  list = '/cUserInfo/list',
+  queryById = '/familyMembers/findByUserId',
+}
+/**
+ * 列表接口
+ * @param params
+ */
+export const list = (params) => defHttp.get({ url: Api.list, params });
+
+export const getDetaile = (params) => {
+  return defHttp.get({ url: Api.queryById, params });
+};

+ 131 - 0
src/views/informationManagement/cUserInfo/cUserInfo.data.ts

@@ -0,0 +1,131 @@
+import { BasicColumn } from '/@/components/Table';
+import { FormSchema } from '/@/components/Table';
+import { rules } from '/@/utils/helper/validator';
+import { render } from '/@/utils/common/renderUtils';
+import { getWeekMonthQuarterYear } from '/@/utils';
+import { getUserDeparts } from '/@/views/system/depart/depart.api';
+
+//列表数据
+export const columns: BasicColumn[] = [
+  {
+    title: '用户昵称',
+    align: 'center',
+    dataIndex: 'username',
+  },
+  {
+    title: '头像',
+    align: 'center',
+    dataIndex: 'avatar',
+    slots: { customRender: 'img' },
+  },
+  {
+    title: '手机号码',
+    align: 'center',
+    dataIndex: 'phone',
+  },
+  {
+    title: '是否实名',
+    align: 'center',
+    dataIndex: 'realNameStatus',
+  },
+  {
+    title: '家庭成员数(包括自己)',
+    align: 'center',
+    dataIndex: 'familyNum',
+  },
+  {
+    title: '创建时间',
+    align: 'center',
+    dataIndex: 'createTime',
+  },
+];
+//查询数据
+export const searchFormSchema: FormSchema[] = [
+  {
+    label: '用户昵称',
+    field: 'merchantName',
+    component: 'Input',
+    colProps: { span: 6 },
+  },
+  {
+    label: '是否实名',
+    field: 'accountStatus',
+    component: 'Select',
+    colProps: { span: 6 },
+    componentProps: {
+      options: [
+        { label: '所有', value: null },
+        { label: '未实名', value: 0 },
+        { label: '已实名', value: 1 },
+      ],
+    },
+  },
+];
+//表单数据
+export const formSchema: FormSchema[] = [
+  {
+    label: 'title1',
+    field: 'orgCode',
+    colSlot: 'title1',
+    component: 'Input',
+  },
+  {
+    label: '用户ID',
+    field: 'id',
+    component: 'Input',
+    required: true,
+  },
+  {
+    label: '微信OpenID',
+    field: 'openid',
+    component: 'Input',
+    required: true,
+  },
+  {
+    label: '用户昵称',
+    field: 'realname',
+    component: 'Input',
+    required: true,
+  },
+  {
+    label: '头像',
+    field: 'avatar',
+    component: 'Input',
+    required: true,
+    slot: 'avatar',
+  },
+  {
+    label: '手机号码',
+    field: 'id',
+    component: 'Input',
+    required: true,
+  },
+  {
+    label: '创建时间',
+    field: 'id',
+    component: 'Input',
+    required: true,
+  },
+  {
+    label: '魔板',
+    field: 'familyMembersList',
+    component: 'Input',
+    colSlot: 'moban',
+  },
+  // TODO 主键隐藏字段,目前写死为ID
+  {
+    label: '',
+    field: 'id',
+    component: 'Input',
+    show: false,
+  },
+];
+
+/**
+ * 流程表单调用这个方法获取formSchema
+ * @param param
+ */
+export function getBpmFormSchema(_formData): FormSchema[] {
+  // 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
+  return formSchema;
+}

+ 95 - 0
src/views/informationManagement/cUserInfo/components/cUserInfoModal.vue

@@ -0,0 +1,95 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose title="查看用户信息" :width="800">
+    <div class="px-4">
+      <BasicForm @register="registerForm" name="SeparateAccountsForm">
+        <template #title1>
+          <TypographyTitle :level="4">基础信息</TypographyTitle>
+          <Divider></Divider>
+        </template>
+        <template #avatar="{ model, field }">
+          <Image :src="model[field]" :width="120"></Image>
+        </template>
+        <template #moban="{ model, field }">
+          <div v-for="(item, idx) in model[field]" :key="item.id">
+            <TypographyTitle :level="4">{{ arrTitle[item.userType] }}{{ item.userType == 1 ? idx : '' }} </TypographyTitle>
+            <Divider></Divider>
+            <FormItem label="是否实名" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5 }">
+              <div>{{ item.realNameStatus ? '已实名' : '未实名' }}</div>
+            </FormItem>
+            <template v-if="item.realNameStatus">
+              <FormItem label="真实姓名" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5 }">
+                <div>{{ item.fullName }}</div>
+              </FormItem>
+              <FormItem label="身份证号" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5 }">
+                <div>{{ item.identityCard }}</div>
+              </FormItem>
+              <FormItem label="身份证图片" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5 }">
+                <div class="flex items-center">
+                  <div class="mr-4">
+                    <Image :src="item.idCardFrontImg" :width="120"></Image>
+                  </div>
+                  <Image :src="item.idCardBackImg" :width="120"></Image>
+                </div>
+              </FormItem>
+              <FormItem label="手机号码" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5 }">
+                <div>{{ item.phone }}</div>
+              </FormItem>
+              <FormItem label="人脸信息" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5 }">
+                <Image :src="item.realNameImg" :width="120"></Image>
+              </FormItem>
+              <FormItem label="认证时间" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5 }">
+                <div>{{ item.createTime }}</div>
+              </FormItem>
+            </template>
+          </div>
+        </template>
+      </BasicForm>
+    </div>
+  </BasicModal>
+</template>
+
+<script lang="ts" setup>
+  import { TypographyTitle, Divider, FormItem, Image } from 'ant-design-vue';
+  import { ref, computed, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form/index';
+  import { formSchema } from '../cUserInfo.data';
+  const isUpdate = ref(true);
+  const isDetail = ref(false);
+  const arrTitle = ['自己的信息', '家庭成员'];
+
+  //表单配置
+  const [registerForm, { setProps, resetFields, setFieldsValue, validate, scrollToField, getFieldsValue }] = useForm({
+    labelWidth: 150,
+    schemas: formSchema,
+    showActionButtonGroup: false,
+    baseColProps: { span: 24 },
+  });
+  //表单赋值
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
+    //重置表单
+    await resetFields();
+    setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
+    isUpdate.value = !!data?.isUpdate;
+    isDetail.value = !!data?.showFooter;
+    if (unref(isUpdate)) {
+      //表单赋值
+      await setFieldsValue({
+        ...data.data,
+      });
+    }
+    // 隐藏底部时禁用整个表单
+    setProps({ disabled: !data?.showFooter });
+  });
+</script>
+
+<style lang="less" scoped>
+  /** 时间和数字输入框样式 */
+  :deep(.ant-input-number) {
+    width: 100%;
+  }
+
+  :deep(.ant-calendar-picker) {
+    width: 100%;
+  }
+</style>

+ 90 - 0
src/views/informationManagement/cUserInfo/index.vue

@@ -0,0 +1,90 @@
+<template>
+  <div>
+    <!--引用表格-->
+    <BasicTable @register="registerTable">
+      <template #tableTitle> </template>
+      <template #action="{ record }">
+        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
+      </template>
+      <template #img="{ text }">
+        <TableImg :img-list="[text]" :size="120"></TableImg>
+      </template>
+    </BasicTable>
+    <!-- 表单区域 -->
+    <cUserInfoModal @register="registerModal"></cUserInfoModal>
+  </div>
+</template>
+
+<script lang="ts" setup name="cUserInfo">
+  import { reactive } from 'vue';
+  import { BasicTable, TableAction, TableImg } from '/@/components/Table';
+  import { useModal } from '/@/components/Modal';
+  import { useListPage } from '/@/hooks/system/useListPage';
+  import cUserInfoModal from './components/cUserInfoModal.vue';
+  import { columns, searchFormSchema } from './cUserInfo.data';
+  import { list, getDetaile } from './cUserInfo.api';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  const queryParam = reactive<any>({});
+  const { createMessage, createWarningModal } = useMessage();
+  //注册model
+  const [registerModal, { openModal }] = useModal();
+  //注册table数据
+  const { prefixCls, tableContext } = useListPage({
+    tableProps: {
+      title: 'cUserInfo',
+      api: list,
+      columns,
+      canResize: false,
+      formConfig: {
+        //labelWidth: 120,
+        schemas: searchFormSchema,
+        autoSubmitOnEnter: true,
+        showAdvancedButton: true,
+        fieldMapToNumber: [],
+        fieldMapToTime: [],
+      },
+      actionColumn: {
+        width: 120,
+        fixed: 'right',
+      },
+      beforeFetch: (params) => {
+        return Object.assign(params, queryParam);
+      },
+    },
+  });
+
+  const [registerTable] = tableContext;
+  async function handleView(record: Recordable) {
+    const data = await getDetaile({ userId: record.id });
+    openModal(true, {
+      data,
+      isUpdate: true,
+      showFooter: false,
+    });
+  }
+  /**
+   * 操作栏
+   */
+  function getTableAction(record) {
+    return [
+      {
+        label: '查看',
+        onClick: handleView.bind(null, record),
+      },
+    ];
+  }
+
+  /**
+   * 下拉操作栏
+   */
+  function getDropDownAction(record) {
+    return [];
+  }
+</script>
+
+<style lang="less" scoped>
+  :deep(.ant-picker),
+  :deep(.ant-input-number) {
+    width: 100%;
+  }
+</style>

+ 5 - 3
src/views/informationManagement/shopInfo/index.vue

@@ -53,8 +53,10 @@
   import uploadVideo from '@/components/uploadVideo/index.vue';
   import { onUnmounted } from 'vue';
   import { useShopInfoStore } from '/@/store/modules/shopInfo';
-  const hopInfoStore = useShopInfoStore();
+  const shopInfoStore = useShopInfoStore();
+
   import { formSchema } from './shopInfo.data';
+  useShopInfoStore().getCurrentDep();
   const [registerForm, { setProps, resetFields, setFieldsValue, updateSchema, validate, clearValidate, getSchemaByField }] = useForm({
     schemas: formSchema,
     showActionButtonGroup: false,
@@ -66,8 +68,8 @@
     const form = await validate();
     console.log(form, '撒大苏打');
   }
-  hopInfoStore.getCurrentDep();
+
   onUnmounted(() => {
-    hopInfoStore.isShowSelect = false;
+    shopInfoStore.isShowSelect = false;
   });
 </script>

+ 13 - 12
src/views/informationManagement/shopInfo/shopInfo.data.ts

@@ -2,6 +2,18 @@ import { FormSchema } from '/@/components/Table';
 import { getSprotProject } from '/@/api/common/api';
 const sportList = await getSprotProject({ pageSize: 20 });
 export const formSchema: FormSchema[] = [
+  {
+    field: 'type',
+    label: '业务类型',
+    component: 'Select',
+    componentProps: {
+      options: [
+        { label: '学校', value: 0 },
+        { label: '体育馆', value: 1 },
+      ],
+    },
+    required: true,
+  },
   {
     field: 'name',
     label: '营业名称',
@@ -33,18 +45,7 @@ export const formSchema: FormSchema[] = [
     component: 'JAreaSelect',
     show: false,
   },
-  {
-    field: 'type',
-    label: '业务类型',
-    component: 'Select',
-    componentProps: {
-      options: [
-        { label: '学校', value: 0 },
-        { label: '体育馆', value: 1 },
-      ],
-    },
-    required: true,
-  },
+
   {
     field: 'title1',
     colSlot: 'title1',

+ 168 - 17
src/views/informationManagement/teachorNoteach/index.vue

@@ -1,25 +1,176 @@
+<script lang="ts" setup>
+  import { RadioGroup, Radio, Divider, TypographyTitle, RadioButton } from 'ant-design-vue';
+  import FullCalendar from '@fullcalendar/vue3';
+  import dayGridPlugin from '@fullcalendar/daygrid';
+  import { CalendarOptions } from '@fullcalendar/core';
+  import interactionPlugin from '@fullcalendar/interaction';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { getQueryByTenantId, editDay } from './teachorNoteach.api';
+  import { useShopInfoStore } from '/@/store/modules/shopInfo';
+  import { h, ref, onUnmounted } from 'vue';
+  import dayjs from 'dayjs';
+  const { createWarningModal, createMessage } = useMessage();
+  const isChangeDay = ref(0);
+  const changeDay = ref(1);
+  const today = new Date();
+  const nonTeachingDays = ref<Date[]>([]);
+  const fullCalendarRef = ref();
+  const TeachingData = ref<{ day: string; id: string; isTeaching: number; orgCode: string }[]>([]);
+  const calendarOptions = ref<CalendarOptions>({
+    plugins: [dayGridPlugin, interactionPlugin],
+    initialView: 'dayGridMonth',
+    events: [{ title: '今天', start: new Date() }],
+    selectable: true,
+    selectMirror: true,
+    headerToolbar: {
+      left: '',
+      right: '',
+    },
+    locale: 'zh-cn',
+    dayCellClassNames: (arg) => {
+      const { date } = arg;
+      const day = date.getDay();
+      if (day === 0 || day === 6) {
+        return ['weekend-cell'];
+      }
+      // const isPast = date < today && !dayjs(date).isSame(dayjs(today), 'day');
+      if (isPast(date)) {
+        return ['past-day'];
+      }
+      // 判断是否为非教学日
+      // const isNonTeachingDay = nonTeachingDays.value.some((d) => dayjs(d).isSame(dayjs(date), 'day'));
+      if (isNonTeachingDay(date)) {
+        return ['non-teaching-day'];
+      }
+      return [];
+    },
+    select: handleClick,
+  });
+
+  function handleClick(selectInfo) {
+    const clickedDate = selectInfo.start;
+    const day = dayjs(clickedDate).day();
+    if (isPast(clickedDate) || day == 0 || day == 6) {
+      //今天之前的日期不允许点击//或者是周末也不允许点击
+      return;
+    }
+    createWarningModal({
+      title: '日期类型修改',
+      okText: '确认',
+      cancelText: '取消',
+      content() {
+        const arr = [h(Radio, { value: 0 }, () => '教学日'), h(Radio, { value: 1 }, () => '非教学日')];
+        return h(
+          RadioGroup,
+          {
+            value: isChangeDay.value,
+            onChange(e) {
+              isChangeDay.value = e.target.value;
+            },
+          },
+          () => arr
+        );
+      },
+      okCancel: true,
+      onOk: async () => {
+        const findData = TeachingData.value.find((it) => dayjs(it.day).valueOf() == dayjs(clickedDate).valueOf());
+        if (!findData) return createMessage.error('添加失败,请重新选择');
+        await editDay({ isTeaching: isChangeDay.value, id: findData.id });
+        getTimeDay();
+        fullCalendarRef.value.getApi().render();
+
+        if (isChangeDay.value == 2) {
+          nonTeachingDays.value.push(clickedDate);
+        } else {
+          const index = findNonTeachingDayIndex(clickedDate);
+          if (index > -1) {
+            nonTeachingDays.value.splice(index, 1);
+          }
+        }
+      },
+      onCancel: () => {},
+    });
+  }
+  function isNonTeachingDay(date: Date): boolean {
+    // 判断日期是否为非教学日
+    return nonTeachingDays.value.some((d) => dayjs(d).isSame(dayjs(date), 'day'));
+  }
+  function findNonTeachingDayIndex(clickedDate: Date): number {
+    // 找到非教学日在数组中的索引
+    return nonTeachingDays.value.findIndex((d) => dayjs(d).isSame(dayjs(clickedDate), 'day'));
+  }
+  function isPast(date) {
+    return date < today && !dayjs(date).isSame(dayjs(today), 'day');
+  }
+  function handleChangeDay() {
+    if (changeDay.value == 1) {
+      fullCalendarRef.value.getApi().today();
+    } else {
+      fullCalendarRef.value.getApi().next();
+    }
+  }
+  async function getTimeDay() {
+    const res = await getQueryByTenantId({
+      tenantId: useShopInfoStore().currentId,
+    });
+    TeachingData.value = res;
+    nonTeachingDays.value = TeachingData.value.filter((it) => it.isTeaching).map((it) => new Date(it.day));
+    fullCalendarRef.value.getApi().render();
+  }
+
+  async function getData() {
+    await useShopInfoStore().getCurrentDep();
+    getTimeDay();
+  }
+  getData();
+  onUnmounted(() => {
+    useShopInfoStore().isShowSelect = false;
+  });
+</script>
+
 <template>
-  <div class="w-full bg-white p-8 customer flex justify-center items-center">
-    <div class="w-700px">
-      <Calendar :fullscreen="false">
-        <!-- <template #dateFullCellRender="{ current: time }">
-          {{ dayjs(time).date() }}
-        </template> -->
-        <template #headerRender="{ value: current, type, onChange, onTypeChange }"> </template>
-      </Calendar>
+  <div class="px-15 py-3 bg-white">
+    <div class="flex items-center justify-between">
+      <TypographyTitle :level="3"> {{ changeDay == 1 ? dayjs().format('YYYY-MM') : dayjs().add(1, 'month').format('YYYY-MM') }} </TypographyTitle>
+      <RadioGroup v-model:value="changeDay" @change="handleChangeDay">
+        <RadioButton :value="1">今天 </RadioButton>
+        <RadioButton :value="2">下一个月 </RadioButton>
+      </RadioGroup>
+    </div>
+    <Divider></Divider>
+    <div class="flex items-center justify-center customer">
+      <div class="w-70%">
+        <FullCalendar :options="calendarOptions" ref="fullCalendarRef"> </FullCalendar>
+      </div>
     </div>
+    <div class="mt-5 w-full text-right"> 红色数字为非教学日,蓝色数字为教学日。 </div>
   </div>
 </template>
 
-<script setup lang="ts">
-  import { Calendar } from 'ant-design-vue';
-  import dayjs from 'dayjs';
-</script>
-
-<!-- <style scoped lang="less">
+<style lang="less" scoped>
   .customer {
-    :deep(.ant-picker-content thead tr th) {
-      font-size: 28px;
+    :deep(.weekend-cell) {
+      .fc-daygrid-day-number {
+        color: red !important;
+      }
+    }
+    :deep(.past-day) {
+      .fc-daygrid-day-number {
+        color: #ccc;
+      }
+    }
+    :deep(.non-teaching-day) {
+      .fc-daygrid-day-number {
+        color: red !important;
+      }
+    }
+    :deep(.fc-col-header) {
+      .fc-day-sat,
+      .fc-day-sun {
+        .fc-col-header-cell-cushion {
+          color: red !important;
+        }
+      }
     }
   }
-</style> -->
+</style>

+ 26 - 0
src/views/informationManagement/teachorNoteach/teachorNoteach.api.ts

@@ -0,0 +1,26 @@
+import { defHttp } from '/@/utils/http/axios';
+import { useMessage } from '/@/hooks/web/useMessage';
+
+const { createConfirm } = useMessage();
+
+enum Api {
+  queryByTenantId = '/org/jeecg/modules/system/app/appTeachingTime/queryByTenantId',
+  edit = '/org/jeecg/modules/system/app/appTeachingTime/edit',
+}
+
+/**
+ *
+ * @param params
+ */
+export const getQueryByTenantId = (params) => {
+  return defHttp.get({ url: Api.queryByTenantId, params });
+};
+
+/**
+ * 修改教学日
+ * @param params
+ * @returns
+ */
+export const editDay = (params) => {
+  return defHttp.post({ url: Api.edit, params });
+};

+ 63 - 0
src/views/operationManagement/banner/banner.api.ts

@@ -0,0 +1,63 @@
+import { defHttp } from '/@/utils/http/axios';
+import { useMessage } from '/@/hooks/web/useMessage';
+
+const { createConfirm } = useMessage();
+
+enum Api {
+  list = '/app/appBanner/list',
+  save = '/app/appBanner/add',
+  edit = '/app/appBanner/edit',
+  deleteOne = '/app/appBanner/delete',
+  deleteBatch = '/app/appBanner/deleteBatch',
+  importExcel = '/app/appBanner/importExcel',
+  exportXls = '/app/appBanner/exportXls',
+}
+/**
+ * 导出api
+ * @param params
+ */
+export const getExportUrl = Api.exportXls;
+/**
+ * 导入api
+ */
+export const getImportUrl = Api.importExcel;
+/**
+ * 列表接口
+ * @param params
+ */
+export const list = (params) => defHttp.get({ url: Api.list, params });
+
+/**
+ * 删除单个
+ */
+export const deleteOne = (params, handleSuccess) => {
+  return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
+    handleSuccess();
+  });
+};
+/**
+ * 批量删除
+ * @param params
+ */
+export const batchDelete = (params, handleSuccess) => {
+  createConfirm({
+    iconType: 'warning',
+    title: '确认删除',
+    content: '是否删除选中数据',
+    okText: '确认',
+    cancelText: '取消',
+    onOk: () => {
+      return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
+        handleSuccess();
+      });
+    },
+  });
+};
+/**
+ * 保存或者更新
+ * @param params
+ */
+export const saveOrUpdate = (params, isUpdate) => {
+  let url = isUpdate ? Api.edit : Api.save;
+  return defHttp.post({ url: url, params });
+};

+ 117 - 0
src/views/operationManagement/banner/banner.data.ts

@@ -0,0 +1,117 @@
+import { BasicColumn } from '/@/components/Table';
+import { FormSchema } from '/@/components/Table';
+import { rules } from '/@/utils/helper/validator';
+import { render } from '/@/utils/common/renderUtils';
+import { getWeekMonthQuarterYear } from '/@/utils';
+//列表数据
+export const columns: BasicColumn[] = [
+  {
+    title: '名称',
+    align: 'center',
+    dataIndex: 'title',
+  },
+  {
+    title: '图片',
+    align: 'center',
+    dataIndex: 'imageUrl',
+    slots: { customRender: 'imageUrl' },
+  },
+  {
+    title: '跳转链接',
+    align: 'center',
+    dataIndex: 'eventValue',
+  },
+  {
+    title: '排序',
+    align: 'center',
+    dataIndex: 'sortOrder',
+  },
+  {
+    title: '状态',
+    align: 'center',
+    dataIndex: 'isEnabled',
+    slots: { customRender: 'switch' },
+  },
+  {
+    title: '创建时间',
+    align: 'center',
+    dataIndex: 'createTime',
+  },
+];
+//查询数据
+export const searchFormSchema: FormSchema[] = [
+  {
+    label: '名称',
+    field: 'title',
+    component: 'Input',
+  },
+  {
+    label: '状态',
+    field: 'isEnabled',
+    component: 'Select',
+    componentProps: {
+      options: [
+        { label: '所有', value: '' },
+        { label: '启用', value: '1' },
+        { label: '禁用', value: '0' },
+      ],
+    },
+  },
+];
+//表单数据
+export const formSchema: FormSchema[] = [
+  {
+    label: '名称',
+    field: 'title',
+    component: 'Input',
+    required: true,
+  },
+  {
+    label: '图片地址',
+    field: 'imageUrl',
+    component: 'JImageUpload',
+    required: true,
+
+    componentProps: {
+      tipText: '单张图片,仅支持png,jpg,jpeg格式,建议尺寸比例 16:9;图片大小不能超过3M。',
+    },
+  },
+  {
+    label: '跳转链接',
+    field: 'eventValue',
+    component: 'Input',
+  },
+  {
+    label: '排序',
+    field: 'sortOrder',
+    component: 'Input',
+    required: true,
+  },
+  {
+    label: '状态',
+    field: 'isEnabled',
+    component: 'Switch',
+    required: true,
+    componentProps: {
+      unCheckedValue: '0',
+      checkedValue: '1',
+    },
+    defaultValue: '1',
+  },
+  // TODO 主键隐藏字段,目前写死为ID
+  {
+    label: '',
+    field: 'id',
+    component: 'Input',
+    show: false,
+  },
+];
+
+/**
+ * 流程表单调用这个方法获取formSchema
+ * @param param
+ */
+export function getBpmFormSchema(_formData): FormSchema[] {
+  // 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
+  return formSchema;
+}

+ 70 - 0
src/views/operationManagement/banner/components/bannerForm.vue

@@ -0,0 +1,70 @@
+<template>
+    <div style="min-height: 400px">
+        <BasicForm @register="registerForm"></BasicForm>
+        <div style="width: 100%;text-align: center" v-if="!formDisabled">
+            <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 交</a-button>
+        </div>
+    </div>
+</template>
+
+<script lang="ts">
+    import {BasicForm, useForm} from '/@/components/Form/index';
+    import {computed, defineComponent} from 'vue';
+    import {defHttp} from '/@/utils/http/axios';
+    import { propTypes } from '/@/utils/propTypes';
+    import {getBpmFormSchema} from '../banner.data';
+    import {saveOrUpdate} from '../banner.api';
+    
+    export default defineComponent({
+        name: "bannerForm",
+        components:{
+            BasicForm
+        },
+        props:{
+            formData: propTypes.object.def({}),
+            formBpm: propTypes.bool.def(true),
+        },
+        setup(props){
+            const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
+                labelWidth: 150,
+                schemas: getBpmFormSchema(props.formData),
+                showActionButtonGroup: false,
+                baseColProps: {span: 24}
+            });
+
+            const formDisabled = computed(()=>{
+                if(props.formData.disabled === false){
+                    return false;
+                }
+                return true;
+            });
+
+            let formData = {};
+            const queryByIdUrl = '/banner/banner/queryById';
+            async function initFormData(){
+                let params = {id: props.formData.dataId};
+                const data = await defHttp.get({url: queryByIdUrl, params});
+                formData = {...data}
+                //设置表单的值
+                await setFieldsValue(formData);
+                //默认是禁用
+                await setProps({disabled: formDisabled.value})
+            }
+
+            async function submitForm() {
+                let data = getFieldsValue();
+                let params = Object.assign({}, formData, data);
+                console.log('表单数据', params)
+                await saveOrUpdate(params, true)
+            }
+
+            initFormData();
+            
+            return {
+                registerForm,
+                formDisabled,
+                submitForm,
+            }
+        }
+    });
+</script>

+ 78 - 0
src/views/operationManagement/banner/components/bannerModal.vue

@@ -0,0 +1,78 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
+    <BasicForm @register="registerForm" name="bannerForm" />
+  </BasicModal>
+</template>
+
+<script lang="ts" setup>
+  import { ref, computed, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form/index';
+  import { formSchema } from '../banner.data';
+  import { saveOrUpdate } from '../banner.api';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  const { createMessage } = useMessage();
+  // Emits声明
+  const emit = defineEmits(['register', 'success']);
+  const isUpdate = ref(true);
+  const isDetail = ref(false);
+  //表单配置
+  const [registerForm, { setProps, resetFields, setFieldsValue, validate, scrollToField }] = useForm({
+    labelWidth: 150,
+    schemas: formSchema,
+    showActionButtonGroup: false,
+    baseColProps: { span: 24 },
+  });
+  //表单赋值
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
+    //重置表单
+    await resetFields();
+    setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
+    isUpdate.value = !!data?.isUpdate;
+    isDetail.value = !!data?.showFooter;
+    if (unref(isUpdate)) {
+      //表单赋值
+      await setFieldsValue({
+        ...data.record,
+      });
+    }
+    // 隐藏底部时禁用整个表单
+    setProps({ disabled: !data?.showFooter });
+  });
+  //设置标题
+  const title = computed(() => (!unref(isUpdate) ? '新增' : !unref(isDetail) ? '详情' : '编辑'));
+  //表单提交事件
+  async function handleSubmit(v) {
+    try {
+      let values = await validate();
+      setModalProps({ confirmLoading: true });
+      //提交表单
+      await saveOrUpdate(values, isUpdate.value);
+      //关闭弹窗
+      closeModal();
+      //刷新列表
+      emit('success');
+    } catch ({ errorFields }) {
+      if (errorFields) {
+        const firstField = errorFields[0];
+        if (firstField) {
+          scrollToField(firstField.name, { behavior: 'smooth', block: 'center' });
+        }
+      }
+      return Promise.reject(errorFields);
+    } finally {
+      setModalProps({ confirmLoading: false });
+    }
+  }
+</script>
+
+<style lang="less" scoped>
+  /** 时间和数字输入框样式 */
+  :deep(.ant-input-number) {
+    width: 100%;
+  }
+
+  :deep(.ant-calendar-picker) {
+    width: 100%;
+  }
+</style>

+ 180 - 0
src/views/operationManagement/banner/index.vue

@@ -0,0 +1,180 @@
+<template>
+  <div>
+    <!--引用表格-->
+    <BasicTable @register="registerTable" :rowSelection="rowSelection">
+      <!--插槽:table标题-->
+      <template #tableTitle>
+        <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
+        <a-dropdown v-if="selectedRowKeys.length > 0">
+          <template #overlay>
+            <a-menu>
+              <a-menu-item key="1" @click="batchHandleDelete">
+                <Icon icon="ant-design:delete-outlined"></Icon>
+                删除
+              </a-menu-item>
+            </a-menu>
+          </template>
+          <a-button
+            >批量操作
+            <Icon icon="mdi:chevron-down"></Icon>
+          </a-button>
+        </a-dropdown>
+      </template>
+      <!--操作栏-->
+      <template #action="{ record }">
+        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
+      </template>
+      <!--字段回显插槽-->
+      <template #imageUrl="{ record }">
+        <TableImg :img-list="[record.imageUrl]" :size="80"></TableImg>
+      </template>
+      <template #switch="{ record }">
+        <Switch checked-value="1" un-checked-value="0" v-model:checked="record.isEnabled"></Switch>
+      </template>
+    </BasicTable>
+    <!-- 表单区域 -->
+    <bannerModal @register="registerModal" @success="handleSuccess"></bannerModal>
+  </div>
+</template>
+
+<script lang="ts" name="banner-banner" setup>
+  import { Switch } from 'ant-design-vue';
+
+  import { ref, reactive, computed, unref } from 'vue';
+  import { BasicTable, useTable, TableAction, TableImg } from '/@/components/Table';
+  import { useModal } from '/@/components/Modal';
+  import { useListPage } from '/@/hooks/system/useListPage';
+  import bannerModal from './components/bannerModal.vue';
+  import { columns, searchFormSchema } from './banner.data';
+  import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './banner.api';
+  import { useUserStore } from '/@/store/modules/user';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  const queryParam = reactive<any>({});
+  const checkedKeys = ref<Array<string | number>>([]);
+  const userStore = useUserStore();
+  const { createMessage } = useMessage();
+  //注册model
+  const [registerModal, { openModal }] = useModal();
+  //注册table数据
+  const { prefixCls, tableContext } = useListPage({
+    tableProps: {
+      title: 'nm_app_banner',
+      api: list,
+      columns,
+      canResize: false,
+      formConfig: {
+        //labelWidth: 120,
+        schemas: searchFormSchema,
+        autoSubmitOnEnter: true,
+        showAdvancedButton: true,
+        fieldMapToNumber: [],
+        fieldMapToTime: [],
+      },
+      actionColumn: {
+        width: 120,
+        fixed: 'right',
+      },
+      beforeFetch: (params) => {
+        return Object.assign(params, queryParam);
+      },
+    },
+    exportConfig: {
+      name: 'nm_app_banner',
+      url: getExportUrl,
+      params: queryParam,
+    },
+    importConfig: {
+      url: getImportUrl,
+      success: handleSuccess,
+    },
+  });
+
+  const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
+
+  /**
+   * 新增事件
+   */
+  function handleAdd() {
+    openModal(true, {
+      isUpdate: false,
+      showFooter: true,
+    });
+  }
+  /**
+   * 编辑事件
+   */
+  function handleEdit(record: Recordable) {
+    openModal(true, {
+      record,
+      isUpdate: true,
+      showFooter: true,
+    });
+  }
+  /**
+   * 详情
+   */
+  function handleDetail(record: Recordable) {
+    openModal(true, {
+      record,
+      isUpdate: true,
+      showFooter: false,
+    });
+  }
+  /**
+   * 删除事件
+   */
+  async function handleDelete(record) {
+    await deleteOne({ id: record.id }, handleSuccess);
+  }
+  /**
+   * 批量删除事件
+   */
+  async function batchHandleDelete() {
+    await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
+  }
+  /**
+   * 成功回调
+   */
+  function handleSuccess() {
+    (selectedRowKeys.value = []) && reload();
+  }
+  /**
+   * 操作栏
+   */
+  function getTableAction(record) {
+    return [
+      {
+        label: '编辑',
+        onClick: handleEdit.bind(null, record),
+        // auth: 'banner:nm_app_banner:edit',
+      },
+    ];
+  }
+  /**
+   * 下拉操作栏
+   */
+  function getDropDownAction(record) {
+    return [
+      {
+        label: '详情',
+        onClick: handleDetail.bind(null, record),
+      },
+      {
+        label: '删除',
+        popConfirm: {
+          title: '是否确认删除',
+          confirm: handleDelete.bind(null, record),
+          placement: 'topLeft',
+        },
+        // auth: 'banner:nm_app_banner:delete',
+      },
+    ];
+  }
+</script>
+
+<style lang="less" scoped>
+  :deep(.ant-picker),
+  :deep(.ant-input-number) {
+    width: 100%;
+  }
+</style>

+ 3 - 9
src/views/operationManagement/feedback/index.vue

@@ -132,6 +132,9 @@
     return [
       {
         label: '回复',
+        ifShow: () => {
+          return record.replyStatus == 0;
+        },
         onClick: handleEdit.bind(null, record),
         // auth: 'feedback:nm_feedback:edit',
       },
@@ -146,15 +149,6 @@
         label: '详情',
         onClick: handleDetail.bind(null, record),
       },
-      {
-        label: '删除',
-        popConfirm: {
-          title: '是否确认删除',
-          confirm: handleDelete.bind(null, record),
-          placement: 'topLeft',
-        },
-        // auth: 'feedback:nm_feedback:delete',
-      },
     ];
   }
 </script>

+ 163 - 0
src/views/system/staff/components/staffModl.vue

@@ -0,0 +1,163 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :width="900" @ok="handleSubmit">
+    <div class="px-4">
+      <BasicForm @register="registerForm" name="feedbackForm">
+        <template #title1>
+          <TypographyTitle :level="4">基本信息</TypographyTitle>
+          <Divider></Divider>
+        </template>
+        <template #title2>
+          <TypographyTitle :level="4">其他信息</TypographyTitle>
+          <Divider></Divider>
+        </template>
+        <template #jSelectPosition="{ model, field }">
+          <JSelectRole v-model:value="model[field]" @options-change="handleChange" />
+        </template>
+        <template #trainingPrograms="{ model, field }">
+          <FormItem label="培训项目" :wrapperCol="{ span: 24 }" :labelCol="{ span: 5 }" required v-if="isShow">
+            <ApiSelect
+              mode="tags"
+              v-model:value="model[field]"
+              :api="getSprotProject"
+              valueField="id"
+              labelField="name"
+              resultField="records"
+            ></ApiSelect>
+          </FormItem>
+        </template>
+        <template #backgroundImg="{ model, field }">
+          <FormItem label="背景图" :wrapperCol="{ span: 24 }" :labelCol="{ span: 5 }" required v-if="isShow">
+            <JImageUpload v-model:value="model[field]" tipText="单张图片,支持格式:.jpg .png .svg .gif ,单个文件不能超过5MB"></JImageUpload>
+          </FormItem>
+        </template>
+        <template #certificateInnocence="{ model, field }">
+          <FormItem label="无犯罪记录" :wrapperCol="{ span: 24 }" :labelCol="{ span: 10 }" :required="isShow">
+            <JImageUpload v-model:value="model[field]" tipText="单张图片,支持格式:.jpg .png .svg .gif ,单个文件不能超过5MB"></JImageUpload>
+          </FormItem>
+        </template>
+        <template #healthy="{ model, field }">
+          <FormItem label="健康证" :wrapperCol="{ span: 24 }" :labelCol="{ span: 5 }" :required="isShow">
+            <JImageUpload v-model:value="model[field]" tipText="单张图片,支持格式:.jpg .png .svg .gif ,单个文件不能超过5MB"></JImageUpload>
+          </FormItem>
+        </template>
+        <template #teachingPhilosophy="{ model, field }">
+          <FormItem label="教学理念" :wrapperCol="{ span: 24 }" :labelCol="{ span: 5 }" v-if="isShow">
+            <Textarea v-model:value="model[field]"></Textarea>
+          </FormItem>
+        </template>
+        <template #excelMsg="{ model, field }">
+          <FormItem label="擅长描述" :wrapperCol="{ span: 24 }" :labelCol="{ span: 5 }" v-if="isShow">
+            <Textarea v-model:value="model[field]"></Textarea>
+          </FormItem>
+        </template>
+        <template #honor="{ model, field }">
+          <FormItem label="荣誉认证" :wrapperCol="{ span: 24 }" :labelCol="{ span: 5 }">
+            <JImageUpload
+              v-model:value="model[field]"
+              :file-max="9"
+              tipText="多张图片,支持格式:.jpg .png .svg .gif ,单个文件不能超过5MB"
+            ></JImageUpload>
+          </FormItem>
+        </template>
+      </BasicForm>
+    </div>
+  </BasicModal>
+</template>
+
+<script lang="ts" setup>
+  import { TypographyTitle, Divider, Textarea, FormItem } from 'ant-design-vue';
+  import { ref, computed, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form/index';
+  import JSelectRole from '/@/components/Form/src/jeecg/components/JSelectRole.vue';
+  import ApiSelect from '/@/components/Form/src/components/ApiSelect.vue';
+  import JImageUpload from '@/components/Form/src/jeecg/components/JImageUpload.vue';
+  import { formSchema } from '../staff.data';
+  import { saveOrUpdate } from '../staff.api';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { getSprotProject } from '/@/api/common/api';
+  const { createMessage } = useMessage();
+  const isShow = ref(false);
+  // Emits声明
+  const emit = defineEmits(['register', 'success']);
+  const isUpdate = ref(true);
+  const isDetail = ref(false);
+
+  //表单配置
+  const [registerForm, { setProps, resetFields, setFieldsValue, validate, scrollToField, getFieldsValue }] = useForm({
+    labelWidth: 150,
+    schemas: formSchema,
+    showActionButtonGroup: false,
+    baseColProps: { span: 24 },
+  });
+  //表单赋值
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
+    //重置表单
+    await resetFields();
+    setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
+    isUpdate.value = !!data?.isUpdate;
+    isDetail.value = !!data?.showFooter;
+    if (unref(isUpdate)) {
+      //表单赋值
+      await setFieldsValue({
+        ...data.record,
+      });
+      if (data.record.instructorId) {
+        isShow.value = true;
+      } else {
+        isShow.value = false;
+      }
+    }
+    // 隐藏底部时禁用整个表单
+    setProps({ disabled: !data?.showFooter });
+  });
+  //表单提交事件
+  async function handleSubmit(v) {
+    try {
+      let values = await validate();
+      const from = getFieldsValue();
+      console.log(from);
+
+      if (isShow.value) {
+        if (!from.trainingPrograms || !from.backgroundImg || !from.certificateInnocence || !from.healthy)
+          return createMessage.error('请填写必填信息');
+      }
+      setModalProps({ confirmLoading: true });
+      //提交表单
+      await saveOrUpdate(from, isUpdate.value);
+      //关闭弹窗
+      closeModal();
+      //刷新列表
+      emit('success');
+    } catch ({ errorFields }) {
+      if (errorFields) {
+        const firstField = errorFields[0];
+        if (firstField) {
+          scrollToField(firstField.name, { behavior: 'smooth', block: 'center' });
+        }
+      }
+      return Promise.reject(errorFields);
+    } finally {
+      setModalProps({ confirmLoading: false });
+    }
+  }
+  function handleChange(e) {
+    const find = e.find((it) => it.roleCode == 'instructor');
+    if (find) {
+      isShow.value = true;
+      return;
+    }
+    isShow.value = false;
+  }
+</script>
+
+<style lang="less" scoped>
+  /** 时间和数字输入框样式 */
+  :deep(.ant-input-number) {
+    width: 100%;
+  }
+
+  :deep(.ant-calendar-picker) {
+    width: 100%;
+  }
+</style>

+ 26 - 0
src/views/system/staff/staff.api.ts

@@ -0,0 +1,26 @@
+import { defHttp } from '/@/utils/http/axios';
+import { useMessage } from '/@/hooks/web/useMessage';
+import { e } from 'unocss';
+
+enum Api {
+  list = '/staff/staff/list',
+  add = 'staff/staff/add',
+  edit = 'staff/staff/edit',
+  queryId = '/staff/staff/queryById',
+  del = 'staff/staff/delete',
+}
+export const getList = (params) => defHttp.get({ url: Api.list, params });
+
+export const saveOrUpdate = (params, isUpdate) => {
+  let url = isUpdate ? Api.edit : Api.add;
+  return defHttp.post({ url, params });
+};
+
+/**
+ * id查询详情
+ * @param id
+ * @returns
+ */
+export const queryById = (id) => defHttp.get({ url: Api.queryId, params: { id } });
+
+export const del = (params) => defHttp.delete({ url: Api.del, params }, { joinParamsToUrl: true });

+ 228 - 0
src/views/system/staff/staff.data.ts

@@ -0,0 +1,228 @@
+import { getDepListData } from '/@/api/common/api';
+import { BasicColumn } from '/@/components/Table';
+import { FormSchema } from '/@/components/Table';
+
+export const columns: BasicColumn[] = [
+  {
+    title: '登录名称',
+    dataIndex: 'username',
+    width: 100,
+    align: 'center',
+  },
+  {
+    title: '用户名称',
+    dataIndex: 'realname',
+    width: 100,
+  },
+  {
+    title: '部门',
+    dataIndex: 'departName',
+    width: 100,
+  },
+  {
+    title: '用户状态',
+    dataIndex: 'status',
+    width: 100,
+  },
+  {
+    title: '创建时间',
+    dataIndex: 'createTime',
+    width: 100,
+  },
+];
+
+export const searchFormSchema: FormSchema[] = [
+  {
+    field: 'username',
+    label: '登录名称',
+    component: 'Input',
+    colProps: { sm: 24 },
+  },
+  {
+    field: 'phone',
+    label: '手机号码',
+    component: 'Input',
+    colProps: { sm: 24 },
+  },
+  {
+    field: 'status',
+    label: '用户状态',
+    component: 'Select',
+    colProps: { sm: 24 },
+    componentProps: {
+      options: [
+        {
+          label: '所有',
+          value: '',
+        },
+        {
+          label: '正常',
+          value: '1',
+        },
+        {
+          label: '冻结',
+          value: '2',
+        },
+      ],
+    },
+  },
+  {
+    field: 'datetime',
+    label: '创建时间',
+    component: 'RangePicker',
+    colProps: { sm: 24 },
+    componentProps: {
+      format: 'YYYY-MM-DD',
+      valueType: 'Date',
+    },
+  },
+];
+
+export const formSchema: FormSchema[] = [
+  {
+    field: 'id',
+    label: 'ID',
+    component: 'Input',
+    show: false,
+  },
+  {
+    field: 'id',
+    label: 'ID',
+    component: 'Input',
+    colSlot: 'title1',
+  },
+
+  {
+    field: 'realname',
+    label: '用户名称',
+    component: 'Input',
+    required: true,
+    colProps: {
+      lg: 12,
+      sm: 24,
+    },
+  },
+  {
+    field: 'selecteddeparts',
+    label: '归属部门',
+    component: 'ApiSelect',
+    componentProps: {
+      api: getDepListData,
+      resultField: 'result',
+      labelField: 'departName',
+      valueField: 'id',
+      mode: 'tags',
+    },
+    required: true,
+    colProps: {
+      lg: 12,
+      sm: 24,
+    },
+  },
+  {
+    field: 'username',
+    label: '登录账号',
+    component: 'Input',
+    required: true,
+    colProps: {
+      lg: 12,
+      sm: 24,
+    },
+  },
+  {
+    field: 'password',
+    label: '登录密码',
+    component: 'Input',
+    required: true,
+    colProps: {
+      lg: 12,
+      sm: 24,
+    },
+  },
+  {
+    field: 'status',
+    label: '用户状态',
+    component: 'Switch',
+    required: true,
+    componentProps: {
+      checkedValue: 1,
+      unCheckedValue: 2,
+    },
+    defaultValue: 1,
+  },
+  {
+    field: 'selectedroles',
+    label: '角色',
+    component: 'JSelectRole',
+    required: true,
+    slot: 'jSelectPosition',
+  },
+  {
+    field: 'id',
+    label: 'ID',
+    component: 'Input',
+    colSlot: 'title2',
+  },
+  {
+    field: 'trainingPrograms',
+    label: '培训项目',
+    component: 'ApiSelect',
+    colSlot: 'trainingPrograms',
+    required: true,
+  },
+  {
+    field: 'backgroundImg',
+    label: '背景图',
+    component: 'JImageUpload',
+    componentProps: {
+      tipText: '单张图片,支持格式:.jpg .png .svg .gif ,单个文件不能超过5MB',
+    },
+    colSlot: 'backgroundImg',
+    required: true,
+  },
+  {
+    field: 'certificateInnocence',
+    label: '无犯罪记录',
+    component: 'JImageUpload',
+    componentProps: {
+      tipText: '单张图片,支持格式:.jpg .png .svg .gif ,单个文件不能超过5MB',
+    },
+    colSlot: 'certificateInnocence',
+
+    colProps: { xl: 12, sm: 24 },
+    required: true,
+  },
+  {
+    field: 'healthy',
+    label: '健康证',
+    component: 'JImageUpload',
+    colProps: { xl: 12, sm: 24 },
+    colSlot: 'healthy',
+
+    componentProps: {
+      tipText: '单张图片,支持格式:.jpg .png .svg .gif ,单个文件不能超过5MB',
+    },
+    required: true,
+  },
+  {
+    field: 'teachingPhilosophy',
+    label: '教学理念',
+    component: 'InputTextArea',
+    colSlot: 'teachingPhilosophy',
+  },
+  {
+    field: 'excelMsg',
+    label: '擅长描述',
+    component: 'InputTextArea',
+    colSlot: 'excelMsg',
+  },
+  {
+    field: 'honor',
+    label: '荣誉认证',
+    component: 'JImageUpload',
+    componentProps: {
+      tipText: '多张图片,支持格式:.jpg .png .svg .gif ,单个文件不能超过5MB',
+    },
+    colSlot: 'honor',
+  },
+];

+ 100 - 0
src/views/system/staff/staffList.vue

@@ -0,0 +1,100 @@
+<template>
+  <div class="p-4"
+    ><BasicTable @register="registerTable">
+      <template #tableTitle>
+        <a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleAdd" v-auth="'staff:add'"> 新增</a-button>
+      </template>
+      <template #action="{ record }">
+        <TableAction :actions="getTableAction(record)" />
+      </template> </BasicTable
+  ></div>
+  <staffModl @register="registerModal" @success="reload"></staffModl>
+</template>
+
+<script setup lang="ts">
+  import staffModl from './components/staffModl.vue';
+  import { BasicTable, TableAction } from '/@/components/Table';
+  import { useListPage } from '/@/hooks/system/useListPage';
+  import { del, getList, queryById } from './staff.api';
+  import { useModal } from '/@/components/Modal';
+  import { columns, searchFormSchema } from './staff.data';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  const [registerModal, { openModal }] = useModal();
+  const { createConfirm } = useMessage();
+  const { prefixCls, tableContext } = useListPage({
+    designScope: 'competition-template',
+    tableProps: {
+      title: '员工列表',
+      api: getList,
+      columns: columns,
+      formConfig: {
+        // update-begin--author:liaozhiyang---date:20230803---for:【QQYUN-5873】查询区域lablel默认居左
+        labelWidth: 100,
+        rowProps: { gutter: 24 },
+        // update-end--author:liaozhiyang---date:20230803---for:【QQYUN-5873】查询区域lablel默认居左
+        schemas: searchFormSchema,
+        autoAdvancedCol: 4,
+        fieldMapToTime: [['datetime', ['startTime', 'endTime'], 'YYYY-MM-DD']],
+      },
+      showIndexColumn: true,
+      actionColumn: {
+        width: 120,
+      },
+      // rowSelection: null,
+      //自定义默认排序
+      defSort: {
+        column: 'id',
+        order: 'desc',
+      },
+    },
+  });
+  const [registerTable, { reload, expandAll, collapseAll }] = tableContext;
+  function handleAdd() {
+    openModal(true, {
+      isUpdate: false,
+      showFooter: true,
+    });
+  }
+  /**
+   * 操作栏
+   */
+  function getTableAction(record) {
+    return [
+      {
+        label: '编辑',
+        onClick: handleEdit.bind(null, record),
+        auth: 'staff:edit',
+      },
+      {
+        label: '删除',
+        onClick: handleDel.bind(null, record),
+        auth: 'staff:delete',
+        ifShow: () => record.username != 'admin',
+      },
+    ];
+  }
+  async function handleEdit(record) {
+    const res = await queryById(record.id);
+    console.log(res);
+
+    openModal(true, {
+      isUpdate: true,
+      showFooter: true,
+      record: { ...res, id: record.id },
+    });
+  }
+  function handleDel(record) {
+    // createMessage.success('删除成功');
+    createConfirm({
+      title: '确定要删除吗?',
+      content: '删除后将无法恢复,请谨慎操作!',
+      onOk: async () => {
+        await del({ id: record.id });
+        reload();
+      },
+      iconType: 'warning',
+    });
+  }
+</script>
+
+<style scoped></style>