Переглянути джерело

feat(informationManagement): 新增保险方案功能

- 添加保险方案列表、表单和模态组件
- 实现保险方案的新增、编辑和删除功能
- 优化保险方案的数据展示和表单验证
zhangtao 11 годин тому
батько
коміт
5a3697ad7b

+ 4 - 4
.env.development

@@ -7,14 +7,14 @@ 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.0.11:8080/jeecg-boot"],["/upload","http://192.168.0.11:8080/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"]]
 # 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.0.11: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  #张
 

+ 39 - 0
src/utils/index.ts

@@ -651,3 +651,42 @@ export function areAllItemsAllFieldsFilled(arr: Array<Record<string, any>>): boo
     return areAllFieldsFilled(rest);
   });
 }
+
+/**
+ * ref对象转普通对象
+ * @param obj
+ * @returns
+ */
+export const extractRefs = (obj) => {
+  const result = {};
+  Object.keys(obj).forEach((key) => {
+    result[key] = unref(obj[key]);
+  });
+  return result;
+};
+/**
+ * 将键值对数组转换为JSON对象
+ * @param arr - 包含key和value属性的对象数组
+ * @returns 转换后的JSON对象
+ */
+export function convertArrayToJson(arr: { key: string; value: string }[]): Record<string, string> {
+  const result: Record<string, string> = {};
+
+  arr.forEach((item) => {
+    result[item.key] = item.value;
+  });
+
+  return result;
+}
+
+/**
+ * 将JSON对象转换为键值对数组
+ * @param obj - JSON对象
+ * @returns 包含key和value属性的对象数组
+ */
+export function convertJsonToArray(obj: Record<string, any>): { key: string; value: string }[] {
+  return Object.keys(obj).map((key) => ({
+    key,
+    value: obj[key].toString(),
+  }));
+}

+ 1 - 8
src/views/businessManagement/competition/competitionCommon.vue

@@ -88,7 +88,7 @@
   import { getDetaile, saveOrUpdate } from './competition.api';
   import dayjs from 'dayjs';
   import { nextTick, onUnmounted, ref, toRefs, unref, watch, watchEffect } from 'vue';
-  import { areAllItemsAllFieldsFilled } from '/@/utils';
+  import { areAllItemsAllFieldsFilled, extractRefs } from '/@/utils';
   import { message } from 'ant-design-vue/lib';
   import { useRoute } from 'vue-router';
   import { useTabs } from '/@/hooks/web/useTabs';
@@ -215,13 +215,6 @@
       width: 80,
     },
   ];
-  const extractRefs = (obj) => {
-    const result = {};
-    Object.keys(obj).forEach((key) => {
-      result[key] = unref(obj[key]);
-    });
-    return result;
-  };
 
   function valiDataCustom(data) {
     if (!areAllItemsAllFieldsFilled(data)) {

+ 36 - 8
src/views/businessManagement/courses/components/coursesModel.vue

@@ -1,8 +1,8 @@
 <template>
-  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose title="编辑补课课表" :width="800" @ok="handleSubmit">
-    <BasicForm @register="registerForm" name="ProjectForm">
-      <template #courses> </template>
-    </BasicForm>
+  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose title="编辑补课课表" :width="1000" @ok="handleSubmit">
+    <div class="min-h-500px">
+      <BasicForm @register="registerForm"> </BasicForm>
+    </div>
   </BasicModal>
 </template>
 
@@ -11,8 +11,10 @@
   import { BasicModal, useModalInner } from '/@/components/Modal';
   import { BasicForm, useForm } from '/@/components/Form/index';
   import { coursesForm } from '../courses.data';
-  import { saveOrUpdate } from '../courses.api';
+  import { editPriceRules } from '../courses.api';
   import { useMessage } from '/@/hooks/web/useMessage';
+  import { areAllItemsAllFieldsFilled, extractRefs } from '/@/utils';
+  import { message } from 'ant-design-vue';
 
   const { createMessage } = useMessage();
   // Emits声明
@@ -20,7 +22,7 @@
   const isUpdate = ref(true);
   const isDetail = ref(false);
   //表单配置
-  const [registerForm, { setProps, resetFields, setFieldsValue, validate, scrollToField }] = useForm({
+  const [registerForm, { setProps, resetFields, setFieldsValue, validate, scrollToField, getFieldsValue }] = useForm({
     labelWidth: 150,
     schemas: coursesForm,
     showActionButtonGroup: false,
@@ -35,8 +37,16 @@
     isDetail.value = !!data?.showFooter;
     if (unref(isUpdate)) {
       //表单赋值
+      console.log(data.record, 'asdsa');
+
       await setFieldsValue({
         ...data.record,
+        dtoList: data.record.dtoList.map((item) => {
+          return {
+            ...item,
+            time: [item.startTime, item.endTime],
+          };
+        }),
       });
     }
     // 隐藏底部时禁用整个表单
@@ -45,10 +55,28 @@
   //表单提交事件
   async function handleSubmit(v) {
     try {
-      let values = await validate();
+      await validate();
+      const values = await getFieldsValue();
+      const dtoList = values.dtoList.map((it) => {
+        return { ...extractRefs(it.editValueRefs), id: it.id };
+      });
+      if (!areAllItemsAllFieldsFilled(dtoList)) {
+        return message.error('请填写完整');
+      }
       setModalProps({ confirmLoading: true });
       //提交表单
-      await saveOrUpdate(values, isUpdate.value);
+      await editPriceRules(
+        dtoList.map((it) => {
+          return {
+            ...it,
+            startTime: it.time[0],
+            endTime: it.time[1],
+            id: it.id,
+            name: values.name,
+            coursesId: values.id,
+          };
+        })
+      );
       //关闭弹窗
       closeModal();
       //刷新列表

+ 1 - 1
src/views/businessManagement/courses/courses.api.ts

@@ -64,5 +64,5 @@ export const queryCourseList = (params) => {
  * @returns
  */
 export const editPriceRules = (params) => {
-  return defHttp.get({ url: Api.editPriceRules, params });
+  return defHttp.post({ url: Api.editPriceRules, params });
 };

+ 54 - 1
src/views/businessManagement/courses/courses.data.ts

@@ -7,6 +7,9 @@ import { getSprotProject } from '/@/api/common/api';
 import { defHttp } from '/@/utils/http/axios';
 import { getAddress, getCoachList } from './courses.api';
 import { useUserStore } from '/@/store/modules/user';
+import ZtCustomTable from '/@/components/ZtCustomTable/index.vue';
+import { h } from 'vue';
+
 //列表数据
 export const columns: BasicColumn[] = [
   {
@@ -332,11 +335,61 @@ export const coursesForm: FormSchema[] = [
     componentProps: {
       disabled: true,
     },
+    required: true,
   },
   {
     label: '补课课表',
     field: 'dtoList',
     component: 'Input',
-    slot: 'courses',
+    required: true,
+
+    render({ model, field }) {
+      return h(ZtCustomTable, {
+        tableColumn: [
+          {
+            dataIndex: 'time',
+            title: '时间',
+            editComponent: 'RangePicker',
+            editRule: true,
+            editComponentProps: {
+              placeholder: ['开始时间', '结束时间'],
+              size: 'middle',
+              valueFormat: 'YYYY-MM-DD HH:mm:ss',
+              showTime: true,
+            },
+            width: 350,
+            editRow: true,
+            editable: true,
+          },
+          {
+            dataIndex: 'name',
+            title: '名称',
+            editComponent: 'Input',
+            editRule: true,
+            editComponentProps: {
+              size: 'middle',
+            },
+            width: 350,
+            editRow: true,
+            editable: true,
+          },
+          {
+            dataIndex: 'operation',
+            title: 'operation',
+            fixed: 'right',
+          },
+        ],
+        value: model[field],
+        showIndex: true,
+        showAction: true,
+      });
+    },
+    defaultValue: [],
+  },
+  {
+    label: '课程名称',
+    field: 'id',
+    component: 'Input',
+    show: false,
   },
 ];

+ 2 - 2
src/views/businessManagement/courses/index.vue

@@ -153,8 +153,8 @@
   async function handleEditCourses(record) {
     const res = await queryCourseList({ coursesType: 1, id: record.id });
     openModal(true, {
-      record,
-      isUpdate: false,
+      record: res ? { dtoList: res, name: record.name, id: record.id } : record,
+      isUpdate: true,
       showFooter: true,
     });
   }

+ 18 - 21
src/views/businessManagement/gymnasiumBag/index.vue

@@ -47,7 +47,7 @@
   import { saveOrUpdate, queryById, Business } from './gymnasiumBag.api';
   import { h, onUnmounted, ref, watch } from 'vue';
   import { OriginalItem } from '/#/utils';
-  import { areAllItemsAllFieldsFilled } from '/@/utils';
+  import { areAllItemsAllFieldsFilled, extractRefs } from '/@/utils';
   import dayjs from 'dayjs';
   import { useTabs } from '/@/hooks/web/useTabs';
   import { useShopInfoStore } from '/@/store/modules/shopInfo';
@@ -220,7 +220,7 @@
   async function save() {
     const form = await getFieldsValue();
     await validate();
-    console.log(validateInventoryFields(form));
+    // console.log(validateInventoryFields(form));
     if (!validateInventoryFields(form)) return message.error('请填写完整包场时间');
     const categoryList = form.categoryId.split(',');
     const newObj = {
@@ -230,22 +230,20 @@
         reminder: form.reminder,
         siteId: form.name,
       },
-      siteCategoryRuleDTOS: categoryList.map((it, idx) => {
+      appSiteCategoryRuleDTOS: categoryList.map((it, idx) => {
+        const appSiteRuleTimeFormDTOList = form[it].map((items) => extractRefs(items.editValueRefs));
         return {
           categoryId: it,
           count: form[`inventory${idx}`],
-          appSiteRuleDTOList: transformData(
-            form[it].map((items) => {
-              {
-                return {
-                  ...items,
-                  ...items.editValueRefs,
-                };
-              }
-            }),
-            it,
-            form.name
-          ),
+          appSiteRuleTimeFormDTOList: appSiteRuleTimeFormDTOList.map((formDtoList, index) => {
+            console.log(index, '当前循环');
+
+            return {
+              startTime: formDtoList.time[0],
+              endTime: formDtoList.time[1],
+              appSiteRuleDTOList: transformData([appSiteRuleTimeFormDTOList[index]], it),
+            };
+          }),
         };
       }),
     };
@@ -306,7 +304,6 @@
     startTime: string;
     endTime: string;
     categoryId: string;
-    sitePlaceId: string;
     batchuuid?: string;
   };
   /**
@@ -314,7 +311,7 @@
    * @param data - 原始数据数组
    * @returns 转换后的数据数组
    */
-  function transformData(data: OriginalItem[], categoryId: string, sitePlaceId: string): TransformedItem[] {
+  function transformData(data: OriginalItem[], categoryId: string): TransformedItem[] {
     const result: TransformedItem[] = [];
     for (const item of data) {
       const { time = ['1', '2'] } = item;
@@ -323,11 +320,11 @@
         const sellingPrice: any = item[key];
         result.push({
           dayOfWeek: `${i}`,
-          sellingPrice: sellingPrice === undefined ? null : unref(sellingPrice),
-          startTime: unref(time)[0],
-          endTime: unref(time)[1],
+          sellingPrice: sellingPrice,
+          startTime: time[0],
+          endTime: time[1],
           categoryId,
-          sitePlaceId,
+          // sitePlaceId,
           batchuuid: item[`dayId${i}`],
         });
       }

+ 12 - 6
src/views/businessManagement/gymnasiumNoFixed/gymnasiumNoFixed.data.ts

@@ -18,6 +18,9 @@ export const columns: BasicColumn[] = [
     title: '提前预约',
     dataIndex: 'advanceTime',
     width: 100,
+    customRender: ({ record }) => {
+      return record.orSubscribe == 1 ? `${record.advanceTime}小时` : '免预约';
+    },
   },
   {
     title: '销售价',
@@ -33,6 +36,9 @@ export const columns: BasicColumn[] = [
     title: '服务保障',
     dataIndex: 'refundType',
     width: 100,
+    customRender: ({ record }) => {
+      return record.refundType === 1 ? '随时退,过期自动退' : '不支持退款';
+    },
   },
 ];
 export const searchFormSchema: FormSchema[] = [
@@ -132,25 +138,25 @@ export const dataRuleFormSchema: FormSchema[] = [
     },
   },
   {
-    field: 'advanceTime',
+    field: 'orSubscribe',
     label: '提前预约  ',
     component: 'RadioGroup',
     required: true,
     span: 4,
-    defaultValue: 1,
+    defaultValue: 0,
     labelWidth: 135,
     colProps: {
       sm: 12,
     },
     componentProps: {
       options: [
-        { label: '免预约', value: 1 },
-        { label: '需提前预约', value: 0 },
+        { label: '免预约', value: 0 },
+        { label: '需提前预约', value: 1 },
       ],
     },
   },
   {
-    field: 'indate',
+    field: 'advanceTime',
     label: '',
     component: 'InputNumber',
     required: true,
@@ -159,7 +165,7 @@ export const dataRuleFormSchema: FormSchema[] = [
       sm: 12,
     },
     ifShow: (schema) => {
-      return schema.model.advanceTime == 0;
+      return schema.model.orSubscribe == 1;
     },
     componentProps: {
       min: 1,

+ 1 - 1
src/views/businessManagement/gymnasiumNoFixed/index.vue

@@ -1,5 +1,5 @@
 <template>
-  ><BasicTable @register="registerTable">
+  <BasicTable @register="registerTable">
     <template #tableTitle>
       <a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleAdd"> 新增</a-button>
       <a-dropdown>

+ 1 - 1
src/views/informationManagement/ContractList/ContractList.data.ts

@@ -61,7 +61,7 @@ export const formSchema: FormSchema[] = [
   {
     label: '文件上传',
     field: 'filePath',
-    component: 'Input',
+    component: 'JUpload',
     required: true,
     // renderComponentContent() {
     //   return h(BasicUpload);

+ 63 - 0
src/views/informationManagement/Insure/Insure.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/appInsure/list',
+  save = '/app/appInsure/add',
+  edit = '/app/appInsure/edit',
+  deleteOne = '/app/appInsure/delete',
+  deleteBatch = '/app/appInsure/deleteBatch',
+  importExcel = '/app/appInsure/importExcel',
+  exportXls = '/app/appInsure/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 });
+};

+ 218 - 0
src/views/informationManagement/Insure/Insure.data.ts

@@ -0,0 +1,218 @@
+import { BasicColumn } from '/@/components/Table';
+import { FormSchema } from '/@/components/Table';
+import { rules } from '/@/utils/helper/validator';
+import { render } from '/@/utils/common/renderUtils';
+import { convertJsonToArray, getWeekMonthQuarterYear } from '/@/utils';
+import ZtCustomTable from '/@/components/ZtCustomTable/index.vue';
+import { h, VNode } from 'vue';
+import { Image, Switch } from 'ant-design-vue';
+//列表数据
+export const columns: BasicColumn[] = [
+  {
+    title: '保险公司',
+    align: 'center',
+    dataIndex: 'insuranceName_dictText',
+  },
+  {
+    title: '保险类型',
+    align: 'center',
+    dataIndex: 'insuranceType_dictText',
+  },
+  {
+    title: '方案名称',
+    align: 'center',
+    dataIndex: 'name',
+  },
+  {
+    title: '封面',
+    align: 'center',
+    dataIndex: 'coverImg',
+    customRender: ({ record }) => {
+      return h(Image, { src: record.coverImg, width: 80, height: 80 });
+    },
+  },
+  {
+    title: '价格',
+    align: 'center',
+    dataIndex: 'price',
+    customRender: ({ record }) => {
+      return `${record.price}元/天·人`;
+    },
+  },
+  {
+    title: '保障期限',
+    align: 'center',
+    dataIndex: 'guaranteeTerm',
+    customRender: ({ record }) => {
+      const viewlist: VNode[] = [];
+      record.guaranteeTerm.split(',').forEach((item) => {
+        const formattedString = `${item}天;`;
+        viewlist.push(h('span', formattedString));
+      });
+      return h('div', viewlist);
+    },
+  },
+  {
+    title: '保险责任与保额',
+    align: 'center',
+    key: 'insuranceObvious 3',
+    ellipsis: true,
+    width: 300,
+    dataIndex: 'insuranceObvious',
+    customRender: ({ record }) => {
+      const viewlist: VNode[] = [];
+      convertJsonToArray(JSON.parse(record.insuranceObvious)).forEach((item) => {
+        const formattedString = `${item.key}  ${item.value}万;`;
+        viewlist.push(h('div', formattedString));
+      });
+      return h('div', viewlist);
+    },
+  },
+  {
+    title: '显隐状态',
+    align: 'center',
+    dataIndex: 'status',
+    customRender: ({ record }) => {
+      return h(Switch, { checked: record.status, unCheckedValue: 1, checkedValue: 0 });
+    },
+  },
+];
+//查询数据
+export const searchFormSchema: FormSchema[] = [
+  {
+    label: '方案名称',
+    field: 'name',
+    component: 'Input',
+    colProps: {
+      span: 8,
+    },
+  },
+];
+//表单数据
+export const formSchema: FormSchema[] = [
+  {
+    label: '保险公司',
+    field: 'insuranceName',
+    component: 'JDictSelectTag',
+    componentProps: {
+      // options: render.renderDict(text, 'quartz_status'),
+      dictCode: 'insurance_name',
+    },
+    required: true,
+  },
+  {
+    label: '保险类型',
+    field: 'insuranceType',
+    required: true,
+    component: 'JDictSelectTag',
+    componentProps: {
+      // options: render.renderDict(text, 'quartz_status'),
+      dictCode: 'insurance_type',
+      disabled: true,
+      stringToNumber: true,
+    },
+    defaultValue: '0',
+  },
+  {
+    label: '方案名称',
+    field: 'name',
+    component: 'Input',
+    required: true,
+  },
+  {
+    label: '封面',
+    field: 'coverImg',
+    required: true,
+    component: 'JImageUpload',
+    componentProps: {
+      tipText: '单张图片,比例 1:1,支持格式:.jpg .png .svg .gif ,单个文件不能超过5MB',
+    },
+  },
+  {
+    label: '价格',
+    field: 'price',
+    required: true,
+    component: 'InputNumber',
+  },
+  {
+    label: '保障期限',
+    field: 'guaranteeTerm',
+    required: true,
+    component: 'InputNumber',
+    slot: 'guaranteeTerm',
+    defaultValue: [],
+  },
+  {
+    label: '保险责任与保额',
+    field: 'insuranceObvious',
+    required: true,
+    component: 'InputNumber',
+    defaultValue: [],
+    render({ model, field }) {
+      return h(ZtCustomTable, {
+        tableColumn: [
+          {
+            dataIndex: 'key',
+            title: '保险责任',
+            editComponent: 'Input',
+            editRule: true,
+            editComponentProps: {
+              size: 'middle',
+            },
+            editRow: true,
+            editable: true,
+            width: 400,
+          },
+          {
+            dataIndex: 'value',
+            title: '保额(万)',
+            editComponent: 'InputNumber',
+            editRule: true,
+            editComponentProps: {
+              size: 'middle',
+              min: 1,
+              precision: 2,
+            },
+            editRow: true,
+            editable: true,
+          },
+          {
+            dataIndex: 'operation',
+            title: 'operation',
+            fixed: 'right',
+            width: 80,
+          },
+        ],
+        value: model[field],
+        showAction: true,
+      });
+    },
+  },
+  {
+    label: '显隐状态',
+    required: true,
+    field: 'status',
+    component: 'Switch',
+    componentProps: {
+      unCheckedValue: 1,
+      checkedValue: 0,
+    },
+    defaultValue: 0,
+  },
+  // 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/Insure/components/InsureForm.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 '../Insure.data';
+  import { saveOrUpdate } from '../Insure.api';
+
+  export default defineComponent({
+    name: 'InsureForm',
+    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 = '/Insure/insure/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>

+ 113 - 0
src/views/informationManagement/Insure/components/InsureModal.vue

@@ -0,0 +1,113 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="1000" @ok="handleSubmit">
+    <BasicForm @register="registerForm" name="InsureForm">
+      <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']">
+              <FormItem>
+                <InputNumber v-model:value="item.day" :min="1" addonAfter="天" :max="30" />
+              </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>
+  </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 '../Insure.data';
+  import { saveOrUpdate } from '../Insure.api';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { InputNumber, Button, FormItem, message } from 'ant-design-vue';
+  import { areAllItemsAllFieldsFilled, convertArrayToJson, convertJsonToArray, extractRefs } from '/@/utils';
+  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.record,
+        guaranteeTerm: data.record.guaranteeTerm.split(',').map((it) => {
+          return { day: it };
+        }),
+        insuranceObvious: convertJsonToArray(JSON.parse(data.record.insuranceObvious)),
+      });
+    } else {
+      await setFieldsValue({
+        guaranteeTerm: [{ day: 1 }],
+      });
+    }
+    // 隐藏底部时禁用整个表单
+    setProps({ disabled: !data?.showFooter });
+  });
+  //设置标题
+  const title = computed(() => (!unref(isUpdate) ? '添加保险方案' : !unref(isDetail) ? '详情' : '编辑 保险方案'));
+  //表单提交事件
+  async function handleSubmit(v) {
+    try {
+      await validate();
+      const form = getFieldsValue();
+      const insuranceObvious = form.insuranceObvious.map((it) => extractRefs(it.editValueRefs));
+      if (!areAllItemsAllFieldsFilled(insuranceObvious)) 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);
+      //关闭弹窗
+      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>

+ 174 - 0
src/views/informationManagement/Insure/index.vue

@@ -0,0 +1,174 @@
+<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="'Insure:nm_insure:deleteBatch'"
+            >批量操作
+            <Icon icon="mdi:chevron-down"></Icon>
+          </a-button>
+        </a-dropdown>
+      </template>
+      <!--操作栏-->
+      <template #action="{ record }">
+        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
+      </template>
+      <!--字段回显插槽-->
+      <template v-slot:bodyCell="{ column, record, index, text }"> </template>
+    </BasicTable>
+    <!-- 表单区域 -->
+    <InsureModal @register="registerModal" @success="handleSuccess"></InsureModal>
+  </div>
+</template>
+
+<script lang="ts" name="Insure-insure" 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 InsureModal from './components/InsureModal.vue';
+  import { columns, searchFormSchema } from './Insure.data';
+  import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './Insure.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: 'insure',
+      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: 'insure',
+      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: 'Insure:nm_insure:edit',
+      },
+    ];
+  }
+  /**
+   * 下拉操作栏
+   */
+  function getDropDownAction(record) {
+    return [
+      {
+        label: '详情',
+        onClick: handleDetail.bind(null, record),
+      },
+      {
+        label: '删除',
+        popConfirm: {
+          title: '是否确认删除',
+          confirm: handleDelete.bind(null, record),
+          placement: 'topLeft',
+        },
+        // auth: 'Insure:nm_insure:delete',
+      },
+    ];
+  }
+</script>
+
+<style lang="less" scoped>
+  :deep(.ant-picker),
+  :deep(.ant-input-number) {
+    width: 100%;
+  }
+</style>

+ 2 - 0
types/utils.d.ts

@@ -13,4 +13,6 @@ export type OriginalItem = {
   day7?: number | null;
   time?: string[];
   id?: string;
+  categoryId?: string;
+  count?: number;
 };