Browse Source

feat(AccountManagement): 新增商户管理功能

- 添加商户管理相关的 API 接口
- 实现商户列表、新增、编辑和重置密码功能
- 添加银联商户相关功能
- 优化商户表单验证和提示
zhangtao 1 day ago
parent
commit
842e5ac09e
33 changed files with 2494 additions and 178 deletions
  1. 4 4
      .env.development
  2. 20 12
      src/components/Form/src/jeecg/components/JUpload/JUpload.vue
  3. 1 0
      src/components/Form/src/jeecg/components/JUpload/upload.data.ts
  4. 9 10
      src/hooks/system/useListPage.ts
  5. 34 0
      src/views/AccountManagement/Merchant/Merchant.api..ts
  6. 392 0
      src/views/AccountManagement/Merchant/Merchant.data.ts
  7. 67 0
      src/views/AccountManagement/Merchant/UnionPay.vue
  8. 79 0
      src/views/AccountManagement/Merchant/components/MerchantModel.vue
  9. 71 0
      src/views/AccountManagement/Merchant/components/UnionPayModel.vue
  10. 147 0
      src/views/AccountManagement/Merchant/index.vue
  11. 22 19
      src/views/businessManagement/competition/competition.data.ts
  12. 6 67
      src/views/businessManagement/competition/competitionCommon.vue
  13. 50 13
      src/views/businessManagement/gymnasiumBag/index.vue
  14. 15 11
      src/views/businessManagement/schoolOpen/index.vue
  15. 46 0
      src/views/businessManagement/takeoutInsurance/InsureOrderInfo.api.ts
  16. 161 0
      src/views/businessManagement/takeoutInsurance/InsureOrderInfo.data.ts
  17. 70 0
      src/views/businessManagement/takeoutInsurance/components/InsureOrderInfoForm.vue
  18. 68 0
      src/views/businessManagement/takeoutInsurance/components/InsureOrderInfoModal.vue
  19. 169 0
      src/views/businessManagement/takeoutInsurance/index.vue
  20. 5 0
      src/views/informationManagement/ContractList/ContractList.api.ts
  21. 4 3
      src/views/informationManagement/ContractList/ContractList.data.ts
  22. 21 4
      src/views/informationManagement/ContractList/index.vue
  23. 15 21
      src/views/informationManagement/Insure/Insure.data.ts
  24. 6 14
      src/views/informationManagement/Insure/components/InsureModal.vue
  25. 63 0
      src/views/informationManagement/protocol/Protocol.api.ts
  26. 107 0
      src/views/informationManagement/protocol/Protocol.data.ts
  27. 70 0
      src/views/informationManagement/protocol/components/ProtocolForm.vue
  28. 78 0
      src/views/informationManagement/protocol/components/ProtocolModal.vue
  29. 157 0
      src/views/informationManagement/protocol/index.vue
  30. 90 0
      src/views/safetyManagement/userEvaluation/components/evaluateModel.vue
  31. 22 0
      src/views/safetyManagement/userEvaluation/evaluate.api.ts
  32. 281 0
      src/views/safetyManagement/userEvaluation/evaluate.data.ts
  33. 144 0
      src/views/safetyManagement/userEvaluation/index.vue

+ 4 - 4
.env.development

@@ -7,16 +7,16 @@ VITE_PUBLIC_PATH = /
 
 
 # 跨域代理,您可以配置多个 ,请注意,没有换行符
-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.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"]]
-# VITE_PROXY = [["/jeecgboot","http://192.168.1.166:8080/jeecg-boot"],["/upload","http://192.168.1.166:8080/upload"]]
+VITE_PROXY = [["/jeecgboot","http://192.168.1.166:8080/jeecg-boot"],["/upload","http://192.168.1.166:8080/upload"]]
 
 #后台接口全路径地址(必填)
-VITE_GLOB_DOMAIN_URL=http://192.168.1.34:8080/jeecg-boot #//黄、
+# VITE_GLOB_DOMAIN_URL=http://192.168.1.34:8080/jeecg-boot #//黄、
 # VITE_GLOB_DOMAIN_URL=http://192.168.0.11:8080/jeecg-boot  #李
 # VITE_GLOB_DOMAIN_URL=http://192.168.1.253:8080/jeecg-boot  #张
-# VITE_GLOB_DOMAIN_URL=http://192.168.1.166:8080/jeecg-boot  #张
+VITE_GLOB_DOMAIN_URL=http://192.168.1.166:8080/jeecg-boot  #张
 
 
 #后台接口父地址(必填)

+ 20 - 12
src/components/Form/src/jeecg/components/JUpload/JUpload.vue

@@ -22,11 +22,14 @@
         <span>{{ text }}</span>
       </a-button>
     </a-upload>
+    <template v-if="tipText">
+      <div class="text-gray">{{ tipText }}</div>
+    </template>
   </div>
 </template>
 
 <script lang="ts" setup>
-  import { ref, reactive, computed, watch, nextTick, createApp,unref } from 'vue';
+  import { ref, reactive, computed, watch, nextTick, createApp, unref } from 'vue';
   import { Icon } from '/@/components/Icon';
   import { getToken } from '/@/utils/auth';
   import { uploadUrl } from '/@/api/common/api';
@@ -70,6 +73,7 @@
     disabled: propTypes.bool.def(false),
     // 替换前一个文件,用于超出最大数量依然允许上传
     replaceLastOne: propTypes.bool.def(false),
+    tipText: propTypes.string,
   });
 
   const headers = getHeaders();
@@ -82,18 +86,18 @@
   // 当前是否是上传图片模式
   const isImageMode = computed(() => props.fileType === UploadTypeEnum.image);
   // 上传按钮是否禁用
-  const buttonDisabled = computed(()=>{
-    if(props.disabled === true){
+  const buttonDisabled = computed(() => {
+    if (props.disabled === true) {
       return true;
     }
-    if(isMaxCount.value === true){
-      if(props.replaceLastOne === true){
-        return false
-      }else{
+    if (isMaxCount.value === true) {
+      if (props.replaceLastOne === true) {
+        return false;
+      } else {
         return true;
       }
     }
-    return false
+    return false;
   });
   // 合并 props 和 attrs
   const bindProps = computed(() => {
@@ -248,6 +252,10 @@
         return false;
       }
     }
+    if (file.type.indexOf(props.fileType) < 0) {
+      createMessage.warning('文件格式不对');
+      return false;
+    }
     // 扩展 beforeUpload 验证
     if (typeof props.beforeUpload === 'function') {
       return props.beforeUpload(file);
@@ -299,9 +307,9 @@
           }
           return file;
         });
-      }else{
-        successFileList = fileListTemp.filter(item=>{
-          return item.uid!=info.file.uid;
+      } else {
+        successFileList = fileListTemp.filter((item) => {
+          return item.uid != info.file.uid;
         });
         createMessage.error(`${info.file.name} 上传失败.`);
       }
@@ -328,7 +336,7 @@
               fileSize: item.size,
             };
             newFileList.push(fileJson);
-          }else{
+          } else {
             return;
           }
         }

+ 1 - 0
src/components/Form/src/jeecg/components/JUpload/upload.data.ts

@@ -2,4 +2,5 @@ export enum UploadTypeEnum {
   all = 'all',
   image = 'image',
   file = 'file',
+  pdf = 'pdf',
 }

+ 9 - 10
src/hooks/system/useListPage.ts

@@ -70,7 +70,7 @@ export function useListPage(options: ListPageOptions) {
     if (realUrl) {
       let title = typeof name === 'function' ? name() : name;
       //update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导出报错,原因未知-
-      let paramsForm:any = {};
+      let paramsForm: any = {};
       try {
         //update-begin-author:liusq---date:2025-03-20--for: [QQYUN-11627]代码生成原生表单,数据导出,前端报错,并且范围参数没有转换 #7962
         //当useSearchFor不等于false的时候,才去触发validate
@@ -85,8 +85,8 @@ export function useListPage(options: ListPageOptions) {
       //update-end-author:taoyan date:20220507 for: erp代码生成 子表 导出报错,原因未知-
 
       //update-begin-author:liusq date:20230410 for:[/issues/409]导出功能没有按排序结果导出,设置导出默认排序,创建时间倒序
-      if(!paramsForm?.column){
-         Object.assign(paramsForm,{column:'createTime',order:'desc'});
+      if (!paramsForm?.column) {
+        Object.assign(paramsForm, { column: 'createTime', order: 'desc' });
       }
       //update-begin-author:liusq date:20230410 for: [/issues/409]导出功能没有按排序结果导出,设置导出默认排序,创建时间倒序
 
@@ -107,7 +107,6 @@ export function useListPage(options: ListPageOptions) {
       if (selectedRowKeys.value && selectedRowKeys.value.length > 0) {
         paramsForm['selections'] = selectedRowKeys.value.join(',');
       }
-      console.log()
       return handleExportXls(title as string, realUrl, filterObj(paramsForm));
       //update-end---author:wangshuai ---date:20220411  for:导出新增自定义参数--------------
     } else {
@@ -204,7 +203,7 @@ export function useListTable(tableProps: TableProps): [
     rowSelection: any;
     selectedRows: Ref<Recordable[]>;
     selectedRowKeys: Ref<any[]>;
-  }
+  },
 ] {
   // 自适应列配置
   const adaptiveColProps: Partial<ColEx> = {
@@ -287,7 +286,7 @@ export function useListTable(tableProps: TableProps): [
   // 合并用户个性化配置
   if (tableProps) {
     //update-begin---author:wangshuai---date:2024-04-28---for:【issues/6180】前端代码配置表变查询条件显示列不生效---
-    if(tableProps.formConfig){
+    if (tableProps.formConfig) {
       setTableProps(tableProps.formConfig);
     }
     //update-end---author:wangshuai---date:2024-04-28---for:【issues/6180】前端代码配置表变查询条件显示列不生效---
@@ -342,11 +341,11 @@ export function useListTable(tableProps: TableProps): [
    * @param formConfig
    */
   function setTableProps(formConfig: any) {
-    const replaceAttributeArray: string[] = ['baseColProps','labelCol'];
+    const replaceAttributeArray: string[] = ['baseColProps', 'labelCol'];
     for (let item of replaceAttributeArray) {
-      if(formConfig && formConfig[item]){
-        if(defaultTableProps.formConfig){
-          let defaultFormConfig:any = defaultTableProps.formConfig;
+      if (formConfig && formConfig[item]) {
+        if (defaultTableProps.formConfig) {
+          let defaultFormConfig: any = defaultTableProps.formConfig;
           defaultFormConfig[item] = formConfig[item];
         }
         formConfig[item] = {};

+ 34 - 0
src/views/AccountManagement/Merchant/Merchant.api..ts

@@ -0,0 +1,34 @@
+import { defHttp } from '/@/utils/http/axios';
+import { useMessage } from '/@/hooks/web/useMessage';
+
+const { createConfirm } = useMessage();
+
+enum Api {
+  list = '/merchant/list',
+  save = '/merchant/add',
+  edit = '/merchant/update',
+  queryById = '/merchant/findById',
+  resetPassword = '/merchant/reset',
+}
+/**
+ * 列表接口
+ * @param params
+ */
+export const list = (params) => defHttp.get({ url: Api.list, params });
+
+/**
+ * 保存或者更新
+ * @param params
+ */
+export const saveOrUpdate = (params, isUpdate) => {
+  let url = isUpdate ? Api.edit : Api.save;
+  return isUpdate ? defHttp.put({ url: url, params }) : defHttp.post({ url: url, params });
+};
+
+export const getDetaile = (params) => {
+  return defHttp.get({ url: Api.queryById, params });
+};
+
+export const resetPassword = (params) => {
+  return defHttp.get({ url: Api.resetPassword, params });
+};

+ 392 - 0
src/views/AccountManagement/Merchant/Merchant.data.ts

@@ -0,0 +1,392 @@
+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: 'storeNum',
+  },
+  {
+    title: '管理员姓名',
+    align: 'center',
+    dataIndex: 'realname',
+  },
+  {
+    title: '管理员手机号码',
+    align: 'center',
+    dataIndex: 'phone',
+  },
+  {
+    title: '创建时间',
+    align: 'center',
+    dataIndex: 'createTime',
+  },
+];
+//查询数据
+export const searchFormSchema: FormSchema[] = [
+  {
+    label: '商户名称',
+    field: 'merchantName',
+    component: 'Input',
+    colProps: { span: 6 },
+  },
+];
+//表单数据
+export const formSchema: FormSchema[] = [
+  {
+    label: '商户名称',
+    field: 'merchantName',
+    component: 'Input',
+    required: true,
+  },
+  {
+    label: '管理员姓名',
+    field: 'realname',
+    component: 'Input',
+    required: true,
+  },
+  {
+    label: '管理员手机号码',
+    field: 'phone',
+    component: 'Input',
+    required: true,
+    dynamicRules: ({ model, schema }) => {
+      return [
+        // { ...rules.phone(true), trigger: 'blur' },
+        { pattern: /^1[3456789]\d{9}$/, message: '手机号码格式有误', trigger: 'blur' },
+      ];
+    },
+  },
+
+  // TODO 主键隐藏字段,目前写死为ID
+  {
+    label: '',
+    field: 'id',
+    component: 'Input',
+    show: false,
+  },
+];
+
+/**
+ * 流程表单调用这个方法获取formSchema
+ * @param param
+ */
+export function getBpmFormSchema(_formData): FormSchema[] {
+  // 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
+  return formSchema;
+}
+export const UnionPlayformSchema: FormSchema[] = [
+  {
+    label: '主体类型',
+    field: 'SubjectType',
+    component: 'RadioGroup',
+    componentProps: {
+      options: [
+        {
+          label: '个体工商户',
+          value: 1,
+        },
+        {
+          label: '企业',
+          value: 2,
+        },
+        {
+          label: '事业单位',
+          value: 3,
+        },
+      ],
+      buttonStyle: 'solid',
+      optionType: 'button',
+    },
+    required: true,
+  },
+  {
+    label: '',
+    field: 'title1',
+    component: 'Input',
+    colSlot: 'title1',
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    label: '营业名称',
+    field: 'merchantCode',
+    component: 'Input',
+    required: true,
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    label: '营业地址',
+    field: 'province',
+    component: 'Input',
+    required: true,
+    slot: 'address',
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    label: '',
+    field: 'provinceCode',
+    component: 'Input',
+    show: false,
+  },
+  {
+    label: '',
+    field: 'cityCode',
+    component: 'Input',
+    show: false,
+  },
+  {
+    label: '',
+    field: 'areaCode',
+    component: 'Input',
+    show: false,
+  },
+  {
+    field: 'address',
+    label: '详细地址',
+    component: 'InputTextArea',
+    required: true,
+    colProps: { lg: 18 },
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'longitude',
+    label: '经度',
+    component: 'Input',
+    required: true,
+    colProps: { lg: 8 },
+
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'latitude',
+    label: '纬度',
+    component: 'Input',
+    required: true,
+    colProps: { lg: 8 },
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'latitude',
+    label: '所属行业',
+    component: 'Input',
+    required: true,
+    colProps: { lg: 18 },
+
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'latitude',
+    label: '社会信用统一代码',
+    component: 'Input',
+    required: true,
+    colProps: { lg: 18 },
+
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'img',
+    label: '营业执照',
+    component: 'JImageUpload',
+    required: true,
+    componentProps: {
+      tipText: '支持格式:.jpg .png .svg .gif ,单个文件不能超过5MB',
+    },
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    label: '',
+    field: 'title2',
+    component: 'Input',
+    colSlot: 'title2',
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'latitude',
+    label: '法人姓名',
+    component: 'Input',
+    required: true,
+    colProps: { lg: 18 },
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'latitude',
+    label: ' 法人身份证号码',
+    component: 'Input',
+    required: true,
+    colProps: { lg: 18 },
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'latitude',
+    label: '法人手机号码',
+    component: 'Input',
+    required: true,
+    colProps: { lg: 18 },
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'latitude',
+    label: '法人身份证有效期',
+    component: 'RangePicker',
+    colProps: { lg: 18 },
+    required: true,
+
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'latitude',
+    label: '法人家庭地址',
+    component: 'JAreaSelect',
+    required: true,
+    colProps: { lg: 18 },
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'latitude',
+    label: '法人家庭详细地址',
+    component: 'InputTextArea',
+    required: true,
+    colProps: { lg: 18 },
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'img',
+    label: '身份证正面(国徽)',
+    component: 'JImageUpload',
+    required: true,
+
+    componentProps: {
+      tipText: '支持格式:.jpg .png .svg .gif ,单个文件不能超过5MB',
+    },
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'img',
+    label: '身份证反面(人像)',
+    component: 'JImageUpload',
+    required: true,
+    componentProps: {
+      tipText: '支持格式:.jpg .png .svg .gif ,单个文件不能超过5MB',
+    },
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'img',
+    label: '自拍照(手持身份证)',
+    component: 'JImageUpload',
+    required: true,
+    componentProps: {
+      tipText: '支持格式:.jpg .png .svg .gif ,单个文件不能超过5MB',
+    },
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    label: '',
+    field: 'title3',
+    component: 'Input',
+    colSlot: 'title3',
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'latitude',
+    label: '账户类型',
+    component: 'RadioGroup',
+    required: true,
+    colProps: { lg: 18 },
+    componentProps: {
+      options: [
+        {
+          label: '公司账户',
+          value: '1',
+        },
+        {
+          label: '个人账户',
+          value: '2',
+        },
+      ],
+    },
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'latitude',
+    label: '开户名称',
+    component: 'Input',
+    required: true,
+    colProps: { lg: 18 },
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'latitude',
+    label: '开户账号',
+    component: 'Input',
+    required: true,
+    colProps: { lg: 18 },
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+  {
+    field: 'latitude',
+    label: '开户行行号',
+    component: 'Input',
+    required: true,
+    colProps: { lg: 18 },
+    ifShow({ model }) {
+      return model['SubjectType'];
+    },
+  },
+];

+ 67 - 0
src/views/AccountManagement/Merchant/UnionPay.vue

@@ -0,0 +1,67 @@
+<template>
+  <div class="p-8px bg-white">
+    <div class="px-4">
+      <BasicForm @register="registerForm">
+        <template #title1>
+          <TypographyTitle :level="4">基础信息</TypographyTitle>
+          <Divider></Divider>
+        </template>
+        <template #title2>
+          <TypographyTitle :level="4">身份信息</TypographyTitle>
+          <Divider></Divider>
+        </template>
+        <template #title3>
+          <TypographyTitle :level="4">结算信息</TypographyTitle>
+          <Divider></Divider>
+        </template>
+        <template #address="{ model }">
+          <JAreaSelect v-model:province="model['provinceCode']" v-model:city="model['cityCode']" v-model:area="model['areaCode']"></JAreaSelect>
+        </template>
+        <template #formFooter>
+          <div style="margin: 0 auto" v-if="isShow">
+            <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>
+      </BasicForm>
+      <div class="h-20px"></div>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { TypographyTitle, Divider, FormItem, message, Input, Button } from 'ant-design-vue';
+  import { BasicForm, useForm, JAreaSelect } from '/@/components/Form/index';
+  import { useUserStore } from '/@/store/modules/user';
+  import { UnionPlayformSchema } from './Merchant.data';
+  import { saveOrUpdate } from './Merchant.api.';
+  import { useShopInfoStore } from '/@/store/modules/shopInfo';
+  import { onUnmounted, ref, computed } from 'vue';
+  import { useRouter } from 'vue-router';
+  const router = useRouter();
+  const [registerForm, { setProps, resetFields, setFieldsValue, updateSchema, validate, clearValidate, getFieldsValue }] = useForm({
+    schemas: UnionPlayformSchema,
+    showActionButtonGroup: false,
+    labelWidth: 200,
+  });
+  const isSubmit = ref(false);
+  const isShow = ref(false);
+  function back() {}
+  async function save() {
+    await validate();
+    isSubmit.value = true;
+    const form = await getFieldsValue();
+
+    // console.log(form);
+    try {
+      // await saveOrUpdate(newObj);
+      isSubmit.value = false;
+      back();
+    } catch (error) {
+      console.log(error);
+      isSubmit.value = false;
+    }
+  }
+  async function getData() {
+    setFieldsValue({});
+  }
+</script>

+ 79 - 0
src/views/AccountManagement/Merchant/components/MerchantModel.vue

@@ -0,0 +1,79 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
+    <BasicForm @register="registerForm" name="SeparateAccountsForm"> </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 '../Merchant.data';
+  import { saveOrUpdate } from '../Merchant.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, 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) ? '添加商户' : '编辑商户'));
+  //表单提交事件
+  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>

+ 71 - 0
src/views/AccountManagement/Merchant/components/UnionPayModel.vue

@@ -0,0 +1,71 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose title="银联商户" :width="800">
+    <a-button type="primary" preIcon="ant-design:plus-outlined" @click="router.push({ path: '/AccountManagement/Merchant/UnionPay' })">
+      新建</a-button
+    >
+    <!-- <BasicTable @register="registerTable" :rowSelection="rowSelection">
+      <template #tableTitle>
+        <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
+      </template>
+      <template #action="{ record }">
+        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
+      </template>
+    </BasicTable> -->
+  </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, columns } from '../Merchant.data';
+  import { saveOrUpdate } from '../Merchant.api.';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { useListPage } from '/@/hooks/system/useListPage';
+  import { useRouter } from 'vue-router';
+  const router = useRouter();
+  const { createMessage } = useMessage();
+  // Emits声明
+  const emit = defineEmits(['register', 'success']);
+
+  //表单赋值
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
+    //重置表单
+    setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter, maskClosable: false });
+
+    // 隐藏底部时禁用整个表单
+    // setProps({ disabled: !data?.showFooter });
+  });
+  //注册table数据
+  // const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
+  //   tableProps: {
+  //     title: 'Merchant',
+  //     // api: list,
+  //     columns,
+  //     canResize: false,
+  //     formConfig: {
+  //       autoSubmitOnEnter: true,
+  //       showAdvancedButton: true,
+  //       fieldMapToNumber: [],
+  //       fieldMapToTime: [],
+  //     },
+  //     actionColumn: {
+  //       width: 250,
+  //       fixed: 'right',
+  //     },
+  //   },
+  // });
+  // const [registerTable, { reload }, {}] = tableContext;
+</script>
+
+<style lang="less" scoped>
+  /** 时间和数字输入框样式 */
+  :deep(.ant-input-number) {
+    width: 100%;
+  }
+
+  :deep(.ant-calendar-picker) {
+    width: 100%;
+  }
+</style>

+ 147 - 0
src/views/AccountManagement/Merchant/index.vue

@@ -0,0 +1,147 @@
+<template>
+  <div>
+    <!--引用表格-->
+    <BasicTable @register="registerTable" :rowSelection="rowSelection">
+      <!--插槽:table标题-->
+      <template #tableTitle>
+        <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
+      </template>
+      <!--操作栏-->
+      <template #action="{ record }">
+        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
+      </template>
+    </BasicTable>
+
+    <!-- 表单区域 -->
+    <MerchantModel @register="registerModal" @success="handleSuccess"></MerchantModel>
+    <UnionPayModel @register="registerUnplayModal" @success="handleSuccess"></UnionPayModel>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { message, 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 MerchantModel from './components/MerchantModel.vue';
+  import UnionPayModel from './components/UnionPayModel.vue';
+  import { columns, searchFormSchema } from './Merchant.data';
+  import { list, resetPassword, getDetaile } from './Merchant.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();
+  const [registerUnplayModal, { openModal: openUnPlayModal }] = useModal();
+  //注册table数据
+  const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
+    tableProps: {
+      title: 'Merchant',
+      api: list,
+      columns,
+      canResize: false,
+      formConfig: {
+        //labelWidth: 120,
+        schemas: searchFormSchema,
+        autoSubmitOnEnter: true,
+        showAdvancedButton: true,
+        fieldMapToNumber: [],
+        fieldMapToTime: [],
+      },
+      actionColumn: {
+        width: 250,
+        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,
+    });
+  }
+  function handleUnplay(record: Recordable) {
+    openUnPlayModal(true, {
+      record,
+      isUpdate: false,
+      showFooter: false,
+    });
+  }
+  /**
+   * 成功回调
+   */
+  function handleSuccess() {
+    (selectedRowKeys.value = []) && reload();
+  }
+  /**
+   * 操作栏
+   */
+  function getTableAction(record) {
+    return [
+      {
+        label: '编辑',
+        onClick: handleEdit.bind(null, record),
+      },
+      {
+        label: '银联账户',
+        onClick: handleUnplay.bind(null, record),
+      },
+      {
+        label: '重置密码',
+        onClick: handleResetpassword.bind(null, record),
+      },
+    ];
+  }
+  async function handleResetpassword(record) {
+    // await resetPassword({ id: record.id });
+    createWarningModal({
+      title: '安全提示',
+      okText: '确认',
+      cancelText: '取消',
+      okCancel: true,
+      content: `确认要重置该用户的密码吗?重置后的密码为:qlyd+用户后四位手机号。(qlyd${record.phone.slice(-4)})`,
+      onOk: async () => {
+        await resetPassword({ id: record.id });
+        handleSuccess();
+      },
+      onCancel: () => {},
+    });
+  }
+  /**
+   * 下拉操作栏
+   */
+  function getDropDownAction(record) {
+    return [];
+  }
+</script>
+
+<style lang="less" scoped>
+  :deep(.ant-picker),
+  :deep(.ant-input-number) {
+    width: 100%;
+  }
+</style>

+ 22 - 19
src/views/businessManagement/competition/competition.data.ts

@@ -6,7 +6,6 @@ import { getAddress } from '../courses/courses.api';
 import { Image, RadioChangeEvent } from 'ant-design-vue';
 import { h, VNode } from 'vue';
 import dayjs from 'dayjs';
-import { useAMapEnhanced } from '/@/hooks/web/useMap';
 /**
  * 列表columns
  */
@@ -196,17 +195,6 @@ export const formSchema: FormSchema[] = [
         { label: '平台场地', value: 0 },
         { label: '其他场地', value: 1 },
       ],
-      async onchange(e: RadioChangeEvent) {
-        const { initMap, destroyMap, getCurrentPosition } = useAMapEnhanced();
-        console.log('加载地图,', e.target.value);
-
-        if (e.target.value == 1) {
-          await initMap('container');
-          await getCurrentPosition();
-        } else {
-          destroyMap();
-        }
-      },
     },
     defaultValue: 0,
   },
@@ -231,7 +219,11 @@ export const formSchema: FormSchema[] = [
     field: 'address',
     label: '输入地址',
     component: 'Input',
-    show: false,
+    required: true,
+    labelWidth: 200,
+    ifShow(model) {
+      return model.model.siteType == 1;
+    },
   },
   {
     field: 'cityCode',
@@ -246,17 +238,28 @@ export const formSchema: FormSchema[] = [
     show: false,
   },
   {
-    field: 'latitude',
-    label: '度',
+    field: 'longitude',
+    label: '度',
     component: 'Input',
-    show: false,
+    labelWidth: 200,
+    required: true,
+
+    ifShow(model) {
+      return model.model.siteType == 1;
+    },
   },
   {
-    field: 'longitude',
-    label: '经度',
+    field: 'latitude',
+    label: '度',
     component: 'Input',
-    show: false,
+    labelWidth: 200,
+    required: true,
+
+    ifShow(model) {
+      return model.model.siteType == 1;
+    },
   },
+
   {
     field: 'siteId',
     label: '平台场地',

+ 6 - 67
src/views/businessManagement/competition/competitionCommon.vue

@@ -33,25 +33,7 @@
           <Divider></Divider>
         </template>
         <template #address="{ model, field }">
-          <FormItem>
-            <JAreaSelect
-              v-model:province="model['provinceCode']"
-              v-model:city="model['cityCode']"
-              v-model:area="model['areaCode']"
-              @change="handleSearch(0)"
-            ></JAreaSelect>
-            <div class="mt20px">
-              <Input v-model:value="model['address']">
-                <template #suffix>
-                  <Button @click="handleSearch(1)">定位</Button>
-                </template>
-              </Input>
-            </div>
-            <div class="mt20px mb20px relative">
-              <div id="container" class="h-500px"></div>
-              <!-- <div id="panel"></div> -->
-            </div>
-          </FormItem>
+          <JAreaSelect v-model:province="model['provinceCode']" v-model:city="model['cityCode']" v-model:area="model['areaCode']"></JAreaSelect>
         </template>
         <template #aptitudes="{ model, field }">
           <div class="flex flex-wrap">
@@ -92,12 +74,10 @@
   import { message } from 'ant-design-vue/lib';
   import { useRoute } from 'vue-router';
   import { useTabs } from '/@/hooks/web/useTabs';
-  import { useAMapEnhanced } from '/@/hooks/web/useMap';
   import { getDataByCode } from '@/components/Form/src/utils/areaDataUtil';
   import { matchCityByFirstFourDigits, matchProvinceByFirstThreeDigits } from '/@/utils/map';
   const { close: closeTab } = useTabs();
   const route = useRoute();
-  const { locateByCityAndAddress, setCityByCode, currentObj, lnglat, initMap, getAddressByLngLat, destroyMap } = useAMapEnhanced();
 
   const [registerForm, { setProps, resetFields, setFieldsValue, updateSchema, validate, clearValidate, getFieldsValue }] = useForm({
     schemas: formSchema,
@@ -240,14 +220,12 @@
           time: [it.startTime, it.endTime],
         };
       }),
+      aptitudes: res.game.aptitudes.split(',').map((it) => {
+        return {
+          name: it,
+        };
+      }),
     });
-    if (res.game.siteType == 1) {
-      await initMap('container');
-      await getAddressByLngLat(res.game.longitude, res.game.latitude);
-      nextTick(() => {
-        handleSearch(1);
-      });
-    }
   }
 
   watch(
@@ -259,45 +237,6 @@
     },
     { immediate: true }
   );
-  watch(
-    () => currentObj.value.currentAdcode,
-    () => {
-      if (currentObj.value.currentAdcode) {
-        const proviceCode = matchProvinceByFirstThreeDigits(currentObj.value.currentAdcode); //获取省编码
-        const proce = getDataByCode(Number(proviceCode[0].code));
-        const cityCode = matchCityByFirstFourDigits(proce, currentObj.value.currentAdcode);
-        setFieldsValue({
-          provinceCode: proviceCode[0].code,
-          cityCode: cityCode?.value,
-          areaCode: currentObj.value.currentAdcode,
-          address: currentObj.value.currentAddress,
-          longitude: lnglat.value[0],
-          latitude: lnglat.value[1],
-        });
-      }
-    },
-    { immediate: true }
-  );
-  async function handleSearch(type: number) {
-    const { address, areaCode } = getFieldsValue();
-    if (type == 0) {
-      setCityByCode(areaCode);
-    }
-    if (address && type == 1 && areaCode) {
-      const res = await locateByCityAndAddress(areaCode, address);
-
-      setFieldsValue({
-        longitude: res.location.lng,
-        latitude: res.location.lat,
-      });
-    }
-    if (!areaCode) {
-      message.error('请选择省市区');
-    }
-  }
-  onUnmounted(() => {
-    destroyMap();
-  });
 </script>
 <style>
   /* #panel {

+ 50 - 13
src/views/businessManagement/gymnasiumBag/index.vue

@@ -58,6 +58,7 @@
   const { deptList, currentId } = storeToRefs(useShopInfoStore());
   const { close: closeTab } = useTabs();
   const isSubmit = ref(false);
+  const uploadId = ref<any>();
   const formSchema: FormSchema[] = [
     {
       field: 'name',
@@ -180,25 +181,34 @@
   }
   async function getQueryData(id) {
     const res = await queryById({ siteId: id });
-    if (!res.siteCategoryRuleDTOS) {
+    if (!res.appSitePlaceCuDTO || !res.appSiteCategoryRuleDTOS) {
       isUpdate.value = false;
     } else {
       isUpdate.value = true;
-      console.log(res, '打印');
-      const categoryIdList = res.siteCategoryRuleDTOS.map((it) => it.categoryId);
-      const formData = res.siteCategoryRuleDTOS.map((it) => reverseTransformData(it.appSiteRuleDTOList));
+      const categoryIdList = res.appSiteCategoryRuleDTOS.map((it) => it.categoryId);
+      const newDATA: any = [];
+      const categoryId: any = [];
+      res.appSiteCategoryRuleDTOS.map((it) => {
+        newDATA.push(
+          ...it.appSiteRuleTimeFormDTOList.map((its) => {
+            categoryId.push({ [it.categoryId]: its.id });
+            return { ...reverseTransformData([...its.appSiteRuleDTOList]) };
+          })
+        );
+      });
       const filed = {
         categoryId: categoryIdList,
         buyLimit: res.appSitePlaceCuDTO.buyLimit,
         earlyRefundTime: res.appSitePlaceCuDTO.earlyRefundTime,
         reminder: res.appSitePlaceCuDTO.reminder,
       };
+      const newData = mergeSameKeys(newDATA);
+      uploadId.value = mergeSameKeysIds(categoryId);
       categoryIdList.forEach((it, idx) => {
-        filed[it] = formData[idx][it];
-        filed[`inventory${idx}`] = res.siteCategoryRuleDTOS[idx].count;
+        filed[it] = newData[it];
+        filed[`inventory${idx}`] = res.appSiteCategoryRuleDTOS[idx].count;
       });
       setFieldsValue(filed);
-      console.log(filed, 'filed');
     }
   }
   async function getBusinessData() {
@@ -220,7 +230,6 @@
   async function save() {
     const form = await getFieldsValue();
     await validate();
-    // console.log(validateInventoryFields(form));
     if (!validateInventoryFields(form)) return message.error('请填写完整包场时间');
     const categoryList = form.categoryId.split(',');
     const newObj = {
@@ -236,9 +245,8 @@
           categoryId: it,
           count: form[`inventory${idx}`],
           appSiteRuleTimeFormDTOList: appSiteRuleTimeFormDTOList.map((formDtoList, index) => {
-            console.log(index, '当前循环');
-
             return {
+              id: uploadId.value ? uploadId.value[it][index] : null,
               startTime: formDtoList.time[0],
               endTime: formDtoList.time[1],
               appSiteRuleDTOList: transformData([appSiteRuleTimeFormDTOList[index]], it),
@@ -247,8 +255,8 @@
         };
       }),
     };
-    console.log(newObj, '打印');
     isSubmit.value = true;
+    // return;
     try {
       await saveOrUpdate(newObj, isUpdate.value);
       isSubmit.value = false;
@@ -366,6 +374,7 @@
   });
   function reverseTransformData(transformedData: TransformedItem[]): Record<string, OriginalItem[]> {
     const groupedByTime: Record<string, TransformedItem[]> = {};
+    console.log(transformedData, 'transformedData');
 
     // 首先按时间分组数据
     transformedData.forEach((item) => {
@@ -406,14 +415,42 @@
         // 添加每天的价格信息
         categoryItems.forEach((item) => {
           const dayKey = `day${item.dayOfWeek}` as keyof OriginalItem;
-          originalItem[dayKey] = item.sellingPrice;
-          originalItem[`dayId${item.dayOfWeek}`] = item.batchuuid;
+          originalItem[dayKey] = Number(item.sellingPrice);
         });
 
         result[categoryId].push(originalItem);
       });
     });
 
+    return result;
+  }
+  function mergeSameKeys(data: Record<string, any[]>[]): Record<string, any[]> {
+    const result: Record<string, any[]> = {};
+
+    data.forEach((item) => {
+      Object.keys(item).forEach((key) => {
+        if (!result[key]) {
+          result[key] = [];
+        }
+        // 合并数组
+        result[key] = [...result[key], ...item[key]];
+      });
+    });
+
+    return result;
+  }
+  function mergeSameKeysIds(data: Record<string, string>[]): Record<string, string[]> {
+    const result: Record<string, string[]> = {};
+
+    data.forEach((item) => {
+      Object.keys(item).forEach((key) => {
+        if (!result[key]) {
+          result[key] = [];
+        }
+        result[key].push(item[key]);
+      });
+    });
+
     return result;
   }
 </script>

+ 15 - 11
src/views/businessManagement/schoolOpen/index.vue

@@ -38,7 +38,7 @@
   import { formSchema, ScheduleArrangementColums } from './schoolOpen.data';
   import { getDetails, saveOrUpdate } from './schoolOpen.api';
   import { useShopInfoStore } from '/@/store/modules/shopInfo';
-  import { onUnmounted, ref } from 'vue';
+  import { onUnmounted, ref, unref } from 'vue';
   import { useRouter } from 'vue-router';
   const router = useRouter();
   useShopInfoStore().getCurrentDep();
@@ -47,7 +47,9 @@
     showActionButtonGroup: false,
   });
   const isSubmit = ref(false);
-  function back() {}
+  function back() {
+    router.back();
+  }
   async function save() {
     await validate();
     isSubmit.value = true;
@@ -57,18 +59,18 @@
       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],
+            ticketNum: unref(it.editValueRefs.ticketNum),
+            startTime: unref(it.editValueRefs.time)[0],
+            endTime: unref(it.editValueRefs.time)[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],
+            ticketNum: unref(it.editValueRefs.ticketNum),
+            startTime: unref(it.editValueRefs.time)[0],
+            endTime: unref(it.editValueRefs.time)[1],
           };
         }),
       }),
@@ -88,9 +90,11 @@
     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] };
-      }),
+      noTeachingDay: 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] };
       }),

+ 46 - 0
src/views/businessManagement/takeoutInsurance/InsureOrderInfo.api.ts

@@ -0,0 +1,46 @@
+import { defHttp } from '/@/utils/http/axios';
+import { useMessage } from '/@/hooks/web/useMessage';
+
+const { createConfirm } = useMessage();
+
+enum Api {
+  list = '/insureOrderInfo/insureOrderInfo/list',
+  save = '/insureOrderInfo/insureOrderInfo/add',
+  edit = '/insureOrderInfo/insureOrderInfo/edit',
+  updateBatch = '/insureOrderInfo/insureOrderInfo/updateBatch',
+  importExcel = '/insureOrderInfo/insureOrderInfo/importExcel',
+  exportXls = '/insureOrderInfo/insureOrderInfo/exportXls',
+  PlanName = '/app/appInsure/findByList',
+}
+/**
+ * 导出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 });
+
+/**
+ * 批量删除
+ * @param params
+ */
+export const updateBatch = (params) => {
+  return defHttp.put({ url: Api.updateBatch, data: params }, { joinParamsToUrl: true });
+};
+/**
+ * 保存或者更新
+ * @param params
+ */
+export const saveOrUpdate = (params, isUpdate) => {
+  let url = isUpdate ? Api.edit : Api.save;
+  return defHttp.post({ url: url, params });
+};
+
+export const PlanNameList = (params) => defHttp.get({ url: Api.PlanName, params });

+ 161 - 0
src/views/businessManagement/takeoutInsurance/InsureOrderInfo.data.ts

@@ -0,0 +1,161 @@
+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 { PlanNameList } from './InsureOrderInfo.api';
+import { h } from 'vue';
+import { ApiSelect } from '/@/components/Form';
+import { Select } from 'ant-design-vue';
+//列表数据
+export const columns: BasicColumn[] = [
+  {
+    title: '保险编号',
+    align: 'center',
+    dataIndex: 'bdOrderNo',
+  },
+  {
+    title: '保险公司',
+    align: 'center',
+    dataIndex: 'insureCompany',
+  },
+  {
+    title: '保险名称',
+    align: 'center',
+    dataIndex: 'insureName',
+  },
+
+  {
+    title: '保险人',
+    align: 'center',
+    dataIndex: 'fullName',
+  },
+  {
+    title: '手机号',
+    align: 'center',
+    dataIndex: 'phone',
+  },
+  {
+    title: '身份证号码',
+    align: 'center',
+    dataIndex: 'identityCard',
+  },
+  {
+    title: '生效开始时间',
+    align: 'center',
+    dataIndex: 'assertStartTime',
+  },
+  {
+    title: '生效结束时间',
+    align: 'center',
+    dataIndex: 'assertEndTime',
+  },
+  {
+    title: '是否录入系统',
+    align: 'center',
+    dataIndex: 'isEnterSystem',
+    customRender: ({ record }) => {
+      return record.isEnterSystem === 1 ? '是' : '否';
+    },
+  },
+  {
+    title: '付款时间',
+    align: 'center',
+    dataIndex: 'payTime',
+  },
+];
+//查询数据
+export const searchFormSchema: FormSchema[] = [
+  {
+    label: '保险公司',
+    field: 'insureCompany',
+    component: 'JDictSelectTag',
+    componentProps: {
+      dictCode: 'insurance_name',
+    },
+  },
+
+  {
+    label: '方案名称',
+    field: 'insureId',
+    component: 'ApiSelect',
+
+    render: ({ model }) => {
+      if (model['insureCompany']) {
+        return h(ApiSelect, {
+          api: PlanNameList,
+          immediate: false,
+          params: { insuranceName: model['insureCompany'] },
+          labelField: 'name',
+          valueField: 'id',
+          value: model['insureId'],
+          onChange: (value) => {
+            model['insureId'] = value;
+          },
+        });
+      } else {
+        return h(Select, { options: [] });
+      }
+    },
+  },
+  {
+    label: '投保人',
+    field: 'fullName',
+    component: 'Input',
+  },
+  {
+    label: '手机号码',
+    field: 'phone',
+    component: 'Input',
+  },
+  {
+    label: '付款时间',
+    field: 'datetime',
+    component: 'RangePicker',
+    componentProps: {
+      placeholder: ['开始时间', '结束时间'],
+      format: 'YYYY-MM-DD',
+    },
+  },
+];
+//表单数据
+export const formSchema: FormSchema[] = [
+  {
+    label: '请选择需要修改的字段',
+    field: 'orderId',
+    component: 'Input',
+    componentProps: {
+      disabled: true,
+    },
+    defaultValue: '是否录入系统',
+    required: true,
+  },
+  {
+    label: '修改为',
+    field: 'updateIsEnterSystem',
+    component: 'Select',
+    required: true,
+
+    componentProps: {
+      options: [
+        {
+          label: '是',
+          value: 1,
+        },
+        {
+          label: '否',
+          value: 0,
+        },
+      ],
+    },
+  },
+];
+
+/**
+ * 流程表单调用这个方法获取formSchema
+ * @param param
+ */
+export function getBpmFormSchema(_formData): FormSchema[] {
+  // 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
+  return formSchema;
+}

+ 70 - 0
src/views/businessManagement/takeoutInsurance/components/InsureOrderInfoForm.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 '../InsureOrderInfo.data';
+  import { saveOrUpdate } from '../InsureOrderInfo.api';
+
+  export default defineComponent({
+    name: 'InsureOrderInfoForm',
+    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 = '/InsureOrder/insureOrderInfo/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>

+ 68 - 0
src/views/businessManagement/takeoutInsurance/components/InsureOrderInfoModal.vue

@@ -0,0 +1,68 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose title="批量修改筛选后的数据" :width="800" @ok="handleSubmit">
+    <BasicForm @register="registerForm" />
+  </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 '../InsureOrderInfo.data';
+  import { saveOrUpdate, updateBatch } from '../InsureOrderInfo.api';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  const { createMessage } = useMessage();
+  // Emits声明
+  const emit = defineEmits(['register', 'success']);
+  const queryParam = ref<any>({});
+  //表单配置
+  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();
+    queryParam.value = data.record;
+    console.log(data.record);
+  });
+  //表单提交事件
+  async function handleSubmit(v) {
+    console.log(queryParam.value);
+
+    try {
+      let values = await validate();
+      setModalProps({ confirmLoading: true });
+      //提交表单
+      await updateBatch({ ...values, ...queryParam.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>

+ 169 - 0
src/views/businessManagement/takeoutInsurance/index.vue

@@ -0,0 +1,169 @@
+<template>
+  <div>
+    <!--引用表格-->
+    <BasicTable @register="registerTable" :rowSelection="rowSelection">
+      <!--插槽:table标题-->
+      <template #tableTitle>
+        <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出筛选数据</a-button>
+        <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined">批量修改筛选数据</a-button>
+      </template>
+      <!--操作栏-->
+      <template #action="{ record }">
+        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
+      </template>
+    </BasicTable>
+    <!-- 表单区域 -->
+    <InsureOrderInfoModal @register="registerModal" @success="handleSuccess"></InsureOrderInfoModal>
+  </div>
+</template>
+
+<script lang="ts" name="InsureOrder-insureOrderInfo" setup>
+  import { RadioGroup, Radio } from 'ant-design-vue';
+  import { ref, reactive, computed, unref, h } from 'vue';
+  import { BasicTable, TableAction } from '/@/components/Table';
+  import { useModal } from '/@/components/Modal';
+  import { useListPage } from '/@/hooks/system/useListPage';
+  import InsureOrderInfoModal from './components/InsureOrderInfoModal.vue';
+  import { columns, searchFormSchema } from './InsureOrderInfo.data';
+  import { list, getImportUrl, getExportUrl, updateBatch } from './InsureOrderInfo.api';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import _ from 'lodash';
+  const queryParam = reactive<any>({});
+  const { createInfoModal } = useMessage();
+  const isChange = ref(1);
+  //注册model
+  const [registerModal, { openModal }] = useModal();
+
+  //注册table数据
+  const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
+    tableProps: {
+      title: 'insure_order_info',
+      api: list,
+      columns,
+      canResize: false,
+      formConfig: {
+        //labelWidth: 120,
+        autoAdvancedCol: 5,
+
+        schemas: searchFormSchema,
+        autoSubmitOnEnter: true,
+        showAdvancedButton: true,
+        fieldMapToNumber: [],
+        fieldMapToTime: [['datetime', ['startTime', 'endTime'], 'YYYY-MM-DD']],
+        submitFunc: handlesubmitButtonOptions,
+      },
+      actionColumn: {
+        width: 120,
+        fixed: 'right',
+      },
+      beforeFetch: (params) => {
+        return Object.assign(params, queryParam);
+      },
+    },
+    exportConfig: {
+      name: '投保清单',
+      url: getExportUrl,
+      params: queryParam,
+    },
+    importConfig: {
+      url: getImportUrl,
+      success: handleSuccess,
+    },
+  });
+
+  const [registerTable, { reload, getForm, setTableData, setPagination }, { rowSelection, selectedRowKeys }] = tableContext;
+
+  /**
+   * 新增事件
+   */
+  function handleAdd() {
+    const { getFieldsValue } = getForm();
+    openModal(true, {
+      record: processUndefinedValuesInPlace(getFieldsValue(), true),
+      isUpdate: false,
+      showFooter: true,
+    });
+  }
+  function processUndefinedValuesInPlace(obj, removeUndefined = false) {
+    if (!obj || typeof obj !== 'object') {
+      return;
+    }
+
+    _.forOwn(obj, (value, key) => {
+      if (value === undefined) {
+        if (removeUndefined) {
+          // 删除值为undefined的属性
+          delete obj[key];
+        } else {
+          // 将undefined设置为null
+          obj[key] = null;
+        }
+      }
+    });
+  }
+  /**
+   * 编辑事件
+   */
+  function handleEdit(record: Recordable) {
+    createInfoModal({
+      title: '是否录入系统修改',
+      content: () => {
+        return h(
+          RadioGroup,
+          {
+            value: isChange.value,
+            onChange(e) {
+              isChange.value = e.target.value;
+            },
+          },
+          () => [h(Radio, { value: 1 }, () => '是'), h(Radio, { value: 0 }, () => '否')]
+        );
+      },
+      okText: '确认',
+      cancelText: '取消',
+      okCancel: true,
+      onOk: async () => {
+        await updateBatch({ id: record.id, updateIsEnterSystem: isChange.value });
+        handleSuccess();
+      },
+    });
+  }
+
+  /**
+   * 成功回调
+   */
+  function handleSuccess() {
+    (selectedRowKeys.value = []) && reload();
+  }
+  /**
+   * 操作栏
+   */
+  function getTableAction(record) {
+    return [
+      {
+        label: '导出',
+        onClick: handleEdit.bind(null, record),
+      },
+    ];
+  }
+  /**
+   * 下拉操作栏
+   */
+  function getDropDownAction(record) {
+    return [];
+  }
+  async function handlesubmitButtonOptions() {
+    const { getFieldsValue } = getForm();
+    const params = getFieldsValue();
+    const res = await list(params);
+    setTableData(res.records);
+    setPagination({ total: res.total });
+  }
+</script>
+
+<style lang="less" scoped>
+  :deep(.ant-picker),
+  :deep(.ant-input-number) {
+    width: 100%;
+  }
+</style>

+ 5 - 0
src/views/informationManagement/ContractList/ContractList.api.ts

@@ -5,6 +5,7 @@ enum Api {
   getOrgIdentityInfos = '/api/esign/getOrgIdentityInfos',
   uploadContractTemplate = '/api/esign/uploadContractTemplate',
   createTemplate = '/api/esign/createTemplate',
+  editContract = '/api/esign/editContract',
 }
 /**
  * 列表接口
@@ -22,3 +23,7 @@ export const uploadContractTemplate = (params) => {
 export const createTemplate = (params) => {
   return defHttp.get({ url: Api.createTemplate, params });
 };
+
+export const getEditEsign = (params) => {
+  return defHttp.get({ url: Api.editContract, params });
+};

+ 4 - 3
src/views/informationManagement/ContractList/ContractList.data.ts

@@ -63,9 +63,10 @@ export const formSchema: FormSchema[] = [
     field: 'filePath',
     component: 'JUpload',
     required: true,
-    // renderComponentContent() {
-    //   return h(BasicUpload);
-    // },
+    componentProps: {
+      fileType: 'pdf',
+      tipText: '仅支持pdf格式',
+    },
   },
   // TODO 主键隐藏字段,目前写死为ID
   {

+ 21 - 4
src/views/informationManagement/ContractList/index.vue

@@ -13,7 +13,7 @@
       </template>
     </BasicTable>
     <!-- 表单区域 -->
-    <ContractListModal @register="registerModal"></ContractListModal>
+    <ContractListModal @register="registerModal" @success="reload"></ContractListModal>
   </div>
 </template>
 
@@ -24,7 +24,7 @@
   import { useListPage } from '/@/hooks/system/useListPage';
   import ContractListModal from './components/ContractListModal.vue';
   import { columns, searchFormSchema } from './ContractList.data';
-  import { list, getDetaile, getOrgIdentityInfos, createTemplate } from './ContractList.api';
+  import { list, getDetaile, getOrgIdentityInfos, createTemplate, getEditEsign } from './ContractList.api';
   import { useMessage } from '/@/hooks/web/useMessage';
   import { useUserStore } from '/@/store/modules/user';
   const queryParam = reactive<any>({});
@@ -57,9 +57,14 @@
     },
   });
 
-  const [registerTable] = tableContext;
+  const [registerTable, { reload }] = tableContext;
   async function handleView(record: Recordable) {
     console.log(record);
+    openModal(true, {
+      record,
+      isUpdate: true,
+      showFooter: true,
+    });
   }
   /**
    * 操作栏
@@ -76,16 +81,28 @@
       },
       {
         label: '编辑合同',
-        onClick: handleView.bind(null, record),
+        onClick: handleEdit.bind(null, record),
+        ifShow(record) {
+          return !record.docTemplateId;
+        },
       },
       {
         label: '预览合同',
         onClick: handleView.bind(null, record),
+        ifShow(record) {
+          return !record.docTemplateId;
+        },
       },
     ];
   }
   async function handleGoEsign(record) {
     const res = await createTemplate({ fileId: record.fileId });
+
+    window.open(res, '_blank');
+  }
+  async function handleEdit(record) {
+    const res = await getEditEsign({ docTemplateId: record.docTemplateId });
+    window.open(res, '_blank');
   }
   /**
    * 下拉操作栏

+ 15 - 21
src/views/informationManagement/Insure/Insure.data.ts

@@ -32,21 +32,14 @@ export const columns: BasicColumn[] = [
     },
   },
   {
-    title: '价格',
+    title: '保障期限的价格',
     align: 'center',
     dataIndex: 'price',
-    customRender: ({ record }) => {
-      return `${record.price}元/天·人`;
-    },
-  },
-  {
-    title: '保障期限',
-    align: 'center',
-    dataIndex: 'guaranteeTerm',
+    width: 300,
     customRender: ({ record }) => {
       const viewlist: VNode[] = [];
-      record.guaranteeTerm.split(',').forEach((item) => {
-        const formattedString = `${item}天;`;
+      record.priceDataList.forEach((item) => {
+        const formattedString = `${item.insurePrice}元/${item.insureDay}天·人;`;
         viewlist.push(h('span', formattedString));
       });
       return h('div', viewlist);
@@ -97,6 +90,7 @@ export const formSchema: FormSchema[] = [
     componentProps: {
       // options: render.renderDict(text, 'quartz_status'),
       dictCode: 'insurance_name',
+      stringToNumber: true,
     },
     required: true,
   },
@@ -111,7 +105,7 @@ export const formSchema: FormSchema[] = [
       disabled: true,
       stringToNumber: true,
     },
-    defaultValue: '0',
+    defaultValue: 0,
   },
   {
     label: '方案名称',
@@ -129,18 +123,18 @@ export const formSchema: FormSchema[] = [
     },
   },
   {
-    label: '价格',
-    field: 'price',
-    required: true,
-    component: 'InputNumber',
-  },
-  {
-    label: '保障期限',
-    field: 'guaranteeTerm',
+    label: '保障期限的价格',
+    field: 'priceDataList',
     required: true,
     component: 'InputNumber',
     slot: 'guaranteeTerm',
-    defaultValue: [],
+    defaultValue: [
+      { insureDay: 1, insurePrice: null },
+      { insureDay: 3, insurePrice: null },
+      { insureDay: 5, insurePrice: null },
+      { insureDay: 15, insurePrice: null },
+      { insureDay: 30, insurePrice: null },
+    ],
   },
   {
     label: '保险责任与保额',

+ 6 - 14
src/views/informationManagement/Insure/components/InsureModal.vue

@@ -4,14 +4,12 @@
       <template #guaranteeTerm="{ model, field }">
         <div class="flex flex-wrap">
           <template v-for="(item, idx) in model[field]" :key="idx">
-            <div class="mr-4 mb-4 flex" :class="[idx ? 'w-200px' : 'w-140px']">
+            <div class="mr-4 mb-4 flex w-250px">
               <FormItem>
-                <InputNumber v-model:value="item.day" :min="1" addonAfter="天" :max="30" />
+                <InputNumber v-model:value="item.insurePrice" :min="1" :addonAfter="`元/${item.insureDay}天·人`" />
               </FormItem>
-              <Button @click="model[field].splice(idx, 1)" type="link" danger v-if="idx">删除</Button>
             </div>
           </template>
-          <Button @click="model[field].push({ day: 1 })" type="link" v-if="model[field].length < 7">添加期限</Button>
         </div>
       </template>
     </BasicForm>
@@ -28,6 +26,7 @@
   import { InputNumber, Button, FormItem, message } from 'ant-design-vue';
   import { areAllItemsAllFieldsFilled, convertArrayToJson, convertJsonToArray, extractRefs } from '/@/utils';
   const { createMessage } = useMessage();
+  import _ from 'lodash-es';
   // Emits声明
   const emit = defineEmits(['register', 'success']);
   const isUpdate = ref(true);
@@ -50,15 +49,9 @@
       //表单赋值
       await setFieldsValue({
         ...data.record,
-        guaranteeTerm: data.record.guaranteeTerm.split(',').map((it) => {
-          return { day: it };
-        }),
         insuranceObvious: convertJsonToArray(JSON.parse(data.record.insuranceObvious)),
       });
-    } else {
-      await setFieldsValue({
-        guaranteeTerm: [{ day: 1 }],
-      });
+      console.log(data.record);
     }
     // 隐藏底部时禁用整个表单
     setProps({ disabled: !data?.showFooter });
@@ -72,14 +65,13 @@
       const form = getFieldsValue();
       const insuranceObvious = form.insuranceObvious.map((it) => extractRefs(it.editValueRefs));
       if (!areAllItemsAllFieldsFilled(insuranceObvious)) return message.error('请填写必填项');
+      const insurePrice = form.priceDataList.some((item) => !item['insurePrice']);
+      if (insurePrice) return message.error('请填写必填项');
 
-      console.log(insuranceObvious, 'asdasdasd');
       const newObj = {
         ...form,
         insuranceObvious: JSON.stringify(convertArrayToJson(insuranceObvious)),
-        guaranteeTerm: form.guaranteeTerm.map((it) => it.day).join(','),
       };
-      console.log(newObj, 'asdasdasd');
       setModalProps({ confirmLoading: true });
       //提交表单
       await saveOrUpdate(newObj, isUpdate.value);

+ 63 - 0
src/views/informationManagement/protocol/Protocol.api.ts

@@ -0,0 +1,63 @@
+import { defHttp } from '/@/utils/http/axios';
+import { useMessage } from '/@/hooks/web/useMessage';
+
+const { createConfirm } = useMessage();
+
+enum Api {
+  list = '/protocol/protocol/list',
+  save = '/protocol/protocol/add',
+  edit = '/protocol/protocol/edit',
+  deleteOne = '/protocol/protocol/delete',
+  deleteBatch = '/protocol/protocol/deleteBatch',
+  importExcel = '/protocol/protocol/importExcel',
+  exportXls = '/protocol/protocol/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 });
+};

+ 107 - 0
src/views/informationManagement/protocol/Protocol.data.ts

@@ -0,0 +1,107 @@
+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: 'protocolType_dictText',
+  },
+  {
+    title: '保险公司',
+    align: 'center',
+    dataIndex: 'insuranceName_dictText',
+  },
+  {
+    title: '协议名称',
+    align: 'center',
+    dataIndex: 'protocolName',
+  },
+  {
+    title: '协议内容',
+    align: 'center',
+    dataIndex: 'protocolContent',
+  },
+  {
+    title: '说明',
+    align: 'center',
+    dataIndex: 'description',
+  },
+  {
+    title: '更新时间',
+    align: 'center',
+    dataIndex: 'updateTime',
+  },
+];
+//查询数据
+export const searchFormSchema: FormSchema[] = [
+  {
+    label: '协议名称',
+    field: 'protocolName',
+    component: 'Input',
+  },
+];
+//表单数据
+export const formSchema: FormSchema[] = [
+  {
+    label: '协议类型',
+    field: 'protocolType',
+    component: 'JDictSelectTag',
+    componentProps: {
+      dictCode: 'protocol_type',
+      disabled: true,
+      stringToNumber: true,
+    },
+    defaultValue: 0,
+    required: true,
+  },
+  {
+    label: '保险公司',
+    field: 'insuranceName',
+    required: true,
+
+    component: 'JDictSelectTag',
+    componentProps: {
+      dictCode: 'insurance_name',
+      stringToNumber: true,
+    },
+  },
+  {
+    label: '协议名称',
+    field: 'protocolName',
+    component: 'Input',
+    required: true,
+  },
+
+  {
+    label: '说明',
+    field: 'description',
+    component: 'InputTextArea',
+    required: true,
+  },
+  {
+    label: '协议内容',
+    field: 'protocolContent',
+    component: 'JEditor',
+    required: true,
+  },
+  // TODO 主键隐藏字段,目前写死为ID
+  {
+    label: '',
+    field: 'id',
+    component: 'Input',
+    show: false,
+  },
+];
+
+/**
+ * 流程表单调用这个方法获取formSchema
+ * @param param
+ */
+export function getBpmFormSchema(_formData): FormSchema[] {
+  // 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
+  return formSchema;
+}

+ 70 - 0
src/views/informationManagement/protocol/components/ProtocolForm.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 '../Protocol.data';
+  import { saveOrUpdate } from '../Protocol.api';
+
+  export default defineComponent({
+    name: 'ProtocolForm',
+    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 = '/Protocol/protocol/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/informationManagement/protocol/components/ProtocolModal.vue

@@ -0,0 +1,78 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
+    <BasicForm @register="registerForm" name="ProtocolForm" />
+  </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 '../Protocol.data';
+  import { saveOrUpdate } from '../Protocol.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>

+ 157 - 0
src/views/informationManagement/protocol/index.vue

@@ -0,0 +1,157 @@
+<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 v-auth="'Protocol:nm_protocol:deleteBatch'"
+            >批量操作
+            <Icon icon="mdi:chevron-down"></Icon>
+          </a-button>
+        </a-dropdown>
+      </template>
+      <!--操作栏-->
+      <template #action="{ record }">
+        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
+      </template>
+    </BasicTable>
+    <!-- 表单区域 -->
+    <ProtocolModal @register="registerModal" @success="handleSuccess"></ProtocolModal>
+  </div>
+</template>
+
+<script lang="ts" name="Protocol-protocol" setup>
+  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 ProtocolModal from './components/ProtocolModal.vue';
+  import { columns, searchFormSchema } from './Protocol.data';
+  import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './Protocol.api';
+  import { downloadFile } from '/@/utils/common/renderUtils';
+  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, onExportXls, onImportXls } = useListPage({
+    tableProps: {
+      title: 'protocol',
+      api: list,
+      columns,
+      canResize: false,
+      formConfig: {
+        //labelWidth: 120,
+        schemas: searchFormSchema,
+        autoSubmitOnEnter: true,
+        showAdvancedButton: true,
+        fieldMapToNumber: [],
+        fieldMapToTime: [],
+      },
+      showIndexColumn: true,
+      actionColumn: {
+        width: 120,
+        fixed: 'right',
+      },
+      beforeFetch: (params) => {
+        return Object.assign(params, queryParam);
+      },
+    },
+    exportConfig: {
+      name: 'protocol',
+      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,
+    });
+  }
+
+  /**
+   * 删除事件
+   */
+  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),
+      },
+      {
+        label: '删除',
+        popConfirm: {
+          title: '是否确认删除',
+          confirm: handleDelete.bind(null, record),
+          placement: 'topLeft',
+        },
+      },
+    ];
+  }
+  /**
+   * 下拉操作栏
+   */
+  function getDropDownAction(record) {
+    return [];
+  }
+</script>
+
+<style lang="less" scoped>
+  :deep(.ant-picker),
+  :deep(.ant-input-number) {
+    width: 100%;
+  }
+</style>

+ 90 - 0
src/views/safetyManagement/userEvaluation/components/evaluateModel.vue

@@ -0,0 +1,90 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @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>
+      </BasicForm>
+    </div>
+  </BasicModal>
+</template>
+
+<script lang="ts" setup>
+  import { TypographyTitle, Divider } 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 '../evaluate.data';
+  import { saveOrUpdate } from '../evaluate.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>

+ 22 - 0
src/views/safetyManagement/userEvaluation/evaluate.api.ts

@@ -0,0 +1,22 @@
+import { defHttp } from '/@/utils/http/axios';
+import { useMessage } from '/@/hooks/web/useMessage';
+
+const { createConfirm } = useMessage();
+
+enum Api {
+  list = '/evaluate/evaluate/list',
+  edit = '/evaluate/evaluate/edit',
+  audit = '/evaluate/evaluate/audit',
+}
+
+export const list = (params) => defHttp.get({ url: Api.list, params });
+
+/**
+ * 保存或者更新
+ * @param params
+ */
+export const saveOrUpdate = (params) => {
+  return defHttp.post({ url: Api.edit, params });
+};
+
+export const getAudit = (params) => defHttp.post({ url: Api.audit, params });

+ 281 - 0
src/views/safetyManagement/userEvaluation/evaluate.data.ts

@@ -0,0 +1,281 @@
+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 { h } from 'vue';
+import { Rate, Image } from 'ant-design-vue';
+const arrTitle = ['待审核', '审核通过', '审核不通过'];
+
+//列表数据
+export const columns: BasicColumn[] = [
+  {
+    title: '用户昵称',
+    align: 'center',
+    dataIndex: 'username',
+  },
+  {
+    title: '评分',
+    align: 'center',
+    dataIndex: 'score',
+    width: 180,
+    customRender: ({ record }) => {
+      const viewList = [h('div', { class: 'mr-3' }, `${record.score}.0`), h(Rate, { value: record.score, disabled: true })];
+      return h('div', { class: 'flex items-center' }, viewList);
+    },
+  },
+  {
+    title: '评价内容',
+    align: 'center',
+    dataIndex: 'evaluateContent',
+  },
+  {
+    title: '图片',
+    align: 'center',
+    dataIndex: 'imageList',
+    slots: {
+      customRender: 'imageList',
+    },
+  },
+  {
+    title: '审核状态',
+    align: 'center',
+    dataIndex: 'checkStatus',
+    customRender: ({ record }) => {
+      return arrTitle[record.checkStatus];
+    },
+  },
+  {
+    title: '回复内容',
+    align: 'center',
+    dataIndex: 'replyContent',
+    customRender: ({ record }) => {
+      return record.replyContent ? record.replyContent : '暂无回复';
+    },
+  },
+  {
+    title: '手机号码',
+    align: 'center',
+    dataIndex: 'phone',
+  },
+  {
+    title: '提交时间',
+    align: 'center',
+    dataIndex: 'createTime',
+  },
+  {
+    title: '回复时间',
+    align: 'center',
+    dataIndex: 'replyTime',
+  },
+  {
+    title: '业务类型;',
+    align: 'center',
+    dataIndex: 'type',
+    customRender: ({ record }) => {
+      return record.type == 0 ? '场地' : '课程';
+    },
+  },
+  {
+    title: '场地/地点',
+
+    align: 'center',
+    dataIndex: 'address',
+  },
+  {
+    title: '发布单位',
+    align: 'center',
+    dataIndex: 'departName',
+  },
+];
+//查询数据
+export const searchFormSchema: FormSchema[] = [
+  {
+    label: '评分',
+    field: 'score',
+    component: 'Select',
+    componentProps: {
+      options: [
+        { label: '全部', value: '' },
+        { label: '1分', value: 1 },
+        { label: '2分', value: 2 },
+        { label: '3分', value: 3 },
+        { label: '4分', value: 4 },
+        { label: '5分', value: 5 },
+      ],
+    },
+  },
+  {
+    label: '审核状态',
+    field: 'checkStatus',
+    component: 'Select',
+    componentProps: {
+      options: [
+        { label: '全部', value: '' },
+        { label: '待审核', value: 0 },
+        { label: '通过', value: 1 },
+        { label: '不通过', value: 2 },
+      ],
+    },
+  },
+  {
+    label: '提交时间',
+    field: 'time',
+    component: 'RangePicker',
+    componentProps: {
+      placeholder: ['开始时间', '结束时间'],
+    },
+  },
+  {
+    label: '是否回复',
+    field: 'replyStatus',
+    component: 'Select',
+    componentProps: {
+      options: [
+        { label: '全部', value: '' },
+        { label: '未回复', value: 0 },
+        { label: '已回复', value: 1 },
+      ],
+    },
+  },
+];
+//表单数据
+export const formSchema: FormSchema[] = [
+  {
+    label: '',
+    field: 'userId',
+    component: 'Input',
+    colSlot: 'title1',
+    labelWidth: 0,
+    colProps: {
+      span: 28,
+      xs: 28,
+    },
+  },
+  // TODO 主键隐藏字段,目前写死为ID
+  {
+    label: '',
+    field: 'id',
+    component: 'Input',
+    show: false,
+  },
+  {
+    label: '用户昵称',
+    field: 'username',
+    required: true,
+    component: 'Input',
+    span: 12,
+    componentProps: {
+      readonly: true,
+    },
+  },
+  {
+    label: '评分',
+    field: 'score',
+    component: 'Input',
+    required: true,
+
+    span: 12,
+    componentProps: {
+      readonly: true,
+    },
+    render: ({ model, field }) => {
+      const viewList = [h('div', { class: 'mr-3' }, `${model[field]}.0`), h(Rate, { value: model[field], disabled: true })];
+      return h('div', { class: 'flex items-center' }, viewList);
+    },
+  },
+  {
+    label: '评价内容',
+    field: 'evaluateContent',
+    component: 'Input',
+    required: true,
+    span: 12,
+    componentProps: {
+      readonly: true,
+    },
+  },
+  {
+    label: '图片',
+    field: 'imageList',
+    component: 'Input',
+    span: 12,
+    required: true,
+
+    componentProps: {
+      readonly: true,
+    },
+    render: ({ model, field }) => {
+      return h(
+        'div',
+        { class: 'flex item-center justify-around' }, // 外层容器
+        model[field]?.map((img, idx) =>
+          h(Image, {
+            key: idx, // 重要:为循环项添加唯一 key
+            src: img,
+            width: 80,
+            rootClassName: 'mr-4',
+          })
+        )
+      );
+    },
+  },
+  {
+    label: '提交时间',
+    field: 'createTime',
+    component: 'Input',
+    span: 12,
+    required: true,
+    componentProps: {
+      readonly: true,
+    },
+  },
+  {
+    label: '审核状态',
+    field: 'checkStatus',
+    component: 'Input',
+    span: 12,
+    required: true,
+    componentProps: {
+      readonly: true,
+    },
+    render: ({ model, field }) => {
+      return arrTitle[model[field]];
+    },
+  },
+  {
+    label: '业务类型',
+    field: 'type',
+    component: 'Input',
+    span: 12,
+    required: true,
+    render: ({ model, field }) => {
+      return model[field] == 0 ? '场地' : '课程';
+    },
+    componentProps: {
+      readonly: true,
+    },
+  },
+  {
+    label: '',
+    field: '',
+    component: 'Input',
+    colSlot: 'title2',
+    labelWidth: 0,
+  },
+  {
+    label: '回复',
+    field: 'replyContent',
+    component: 'InputTextArea',
+    span: 12,
+    required: true,
+  },
+];
+
+/**
+ * 流程表单调用这个方法获取formSchema
+ * @param param
+ */
+export function getBpmFormSchema(_formData): FormSchema[] {
+  // 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
+  return formSchema;
+}

+ 144 - 0
src/views/safetyManagement/userEvaluation/index.vue

@@ -0,0 +1,144 @@
+<template>
+  <div>
+    <!--引用表格-->
+    <BasicTable @register="registerTable" :rowSelection="rowSelection">
+      <!--操作栏-->
+      <template #tableTitle></template>
+      <template #action="{ record }">
+        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
+      </template>
+      <!--字段回显插槽-->
+      <template #imageList="{ text }">
+        <TableImg :imgList="text" :size="120"></TableImg>
+      </template>
+    </BasicTable>
+    <evaluateModel @register="registerModal" @success="handleSuccess"></evaluateModel>
+  </div>
+</template>
+
+<script lang="ts" name="evaluate-evaluate" setup>
+  import evaluateModel from './components/evaluateModel.vue';
+  import { ref, reactive, computed, unref, h } from 'vue';
+  import { BasicTable, useTable, TableAction, TableImg } from '/@/components/Table';
+  import { useModal } from '/@/components/Modal';
+  import { useListPage } from '/@/hooks/system/useListPage';
+  import { columns, searchFormSchema } from './evaluate.data';
+  import { list, getAudit } from './evaluate.api';
+  import { downloadFile } from '/@/utils/common/renderUtils';
+  import { useUserStore } from '/@/store/modules/user';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { Radio, RadioGroup } from 'ant-design-vue';
+  const queryParam = reactive<any>({});
+  const checkedKeys = ref<Array<string | number>>([]);
+  const userStore = useUserStore();
+  const { createMessage, createWarningModal } = useMessage();
+  //注册model
+  const [registerModal, { openModal }] = useModal();
+  const isChange = ref(1);
+  //注册table数据
+  const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
+    tableProps: {
+      title: 'evaluate',
+      api: list,
+      columns,
+      canResize: false,
+      formConfig: {
+        //labelWidth: 120,
+        schemas: searchFormSchema,
+        autoSubmitOnEnter: true,
+        showAdvancedButton: true,
+        fieldMapToNumber: [],
+        fieldMapToTime: [['time', ['startTime', 'endTime'], 'YYYY-MM-DD HH:mm:ss']],
+      },
+      actionColumn: {
+        width: 120,
+        fixed: 'right',
+      },
+      beforeFetch: (params) => {
+        return Object.assign(params, queryParam);
+      },
+    },
+  });
+
+  const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
+
+  /**
+   * 编辑事件
+   */
+  function handleEdit(record: Recordable) {
+    openModal(true, {
+      record,
+      isUpdate: true,
+      showFooter: true,
+    });
+  }
+
+  /**
+   * 成功回调
+   */
+  function handleSuccess() {
+    (selectedRowKeys.value = []) && reload();
+  }
+  /**
+   * 操作栏
+   */
+  function getTableAction(record) {
+    return [
+      {
+        label: '回复',
+        onClick: handleEdit.bind(null, record),
+        auth: 'evaluate:evaluate:edit',
+        ifShow: () => {
+          return record.replyStatus == 0 && record.checkStatus == 1;
+        },
+      },
+      {
+        label: '审核',
+        onClick: handleAudit.bind(null, record),
+        auth: 'evaluate:evaluate:audit',
+        ifShow: () => {
+          return record.checkStatus == 0;
+        },
+      },
+    ];
+  }
+  function handleAudit(record) {
+    createWarningModal({
+      title: '请给出审核意见',
+      okText: '确认',
+      cancelText: '取消',
+      content() {
+        const arr = [h(Radio, { value: 1 }, () => '通过'), h(Radio, { value: 2 }, () => '不通过')];
+        return h(
+          RadioGroup,
+          {
+            value: isChange.value,
+            onChange(e) {
+              isChange.value = e.target.value;
+            },
+          },
+          () => arr
+        );
+      },
+      okCancel: true,
+      onOk: async () => {
+        await getAudit({ id: record.id, checkStatus: isChange.value });
+        handleSuccess();
+      },
+      onCancel: () => {},
+    });
+  }
+  /**
+   * 下拉操作栏
+   */
+  function getDropDownAction(record) {
+    return [];
+  }
+</script>
+
+<style lang="less" scoped>
+  :deep(.ant-picker),
+  :deep(.ant-input-number) {
+    width: 100%;
+  }
+</style>