Просмотр исходного кода

feat(informationManagement): 新增热点信息管理功能

- 添加热点信息列表、新增、编辑和删除功能
- 实现企业认证和电子签章功能
- 优化用户信息和店铺信息页面
- 添加合同模板预览功能
zhangtao 2 месяцев назад
Родитель
Сommit
d14b7ab58f
23 измененных файлов с 1044 добавлено и 57 удалено
  1. 4 4
      .env.development
  2. 12 0
      src/components/Form/src/jeecg/components/JImageUpload.vue
  3. 1 1
      src/utils/index.ts
  4. 10 6
      src/views/businessManagement/courses/publishcourses.vue
  5. 4 0
      src/views/informationManagement/ContractList/ContractList.api.ts
  6. 3 7
      src/views/informationManagement/ContractList/index.vue
  7. 5 0
      src/views/informationManagement/cUserInfo/cUserInfo.api.ts
  8. 15 1
      src/views/informationManagement/cUserInfo/cUserInfo.data.ts
  9. 33 11
      src/views/informationManagement/cUserInfo/components/cUserInfoModal.vue
  10. 15 2
      src/views/informationManagement/cUserInfo/index.vue
  11. 70 0
      src/views/informationManagement/hot/components/hotForm.vue
  12. 80 0
      src/views/informationManagement/hot/components/hotModal.vue
  13. 34 0
      src/views/informationManagement/hot/hot.api.ts
  14. 109 0
      src/views/informationManagement/hot/hot.data.ts
  15. 141 0
      src/views/informationManagement/hot/index.vue
  16. 12 1
      src/views/informationManagement/shopInfo/index.vue
  17. 10 0
      src/views/informationManagement/shopInfo/shopInfo.api.ts
  18. 120 24
      src/views/informationManagement/shopInfo/shopInfo.data.ts
  19. 24 0
      src/views/system/MiniProgramRoleRouter/MiniProgramRoleRouter.api.ts
  20. 128 0
      src/views/system/MiniProgramRoleRouter/MiniProgramRoleRouter.data.ts
  21. 78 0
      src/views/system/MiniProgramRoleRouter/components/MiniProgramRoleRouterModal.vue
  22. 130 0
      src/views/system/MiniProgramRoleRouter/index.vue
  23. 6 0
      src/views/system/notice/notice.data.ts

+ 4 - 4
.env.development

@@ -8,15 +8,15 @@ 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.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.0.11: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  #张
 
 
 #后台接口父地址(必填)

+ 12 - 0
src/components/Form/src/jeecg/components/JImageUpload.vue

@@ -109,6 +109,11 @@
         required: false,
         default: 1,
       },
+      beforeUpload: {
+        type: Function,
+        required: false,
+        default: null,
+      },
     },
     emits: ['options-change', 'change', 'update:value'],
     setup(props, { emit, refs }) {
@@ -202,6 +207,7 @@
           createMessage.info('请上传图片');
           return false;
         }
+
         if (props.isProportion) {
           // 优先使用 aspectRatioW 和 aspectRatioH 参数(例如 5:4)
           // 如果未设置,则使用 aspectRatio 参数(例如 1.25)
@@ -237,6 +243,12 @@
             return false;
           });
         }
+
+        // 扩展 beforeUpload 验证
+        if (typeof props.beforeUpload === 'function') {
+          return props.beforeUpload(file);
+        }
+        return true;
       }
       /**
        * 文件上传结果回调

+ 1 - 1
src/utils/index.ts

@@ -647,7 +647,7 @@ export function areAllItemsAllFieldsFilled(arr: Array<Record<string, any>>): boo
   // return arr.every((item) => areAllFieldsFilled(item));
   //修复新增的时候id校验
   return arr.every((item) => {
-    const { id, ...rest } = item;
+    const { coursesId, id, ...rest } = item;
     return areAllFieldsFilled(rest);
   });
 }

+ 10 - 6
src/views/businessManagement/courses/publishcourses.vue

@@ -41,18 +41,19 @@
 <script lang="ts" setup name="business-management-publishcourses">
   import uploadVideo from '@/components/uploadVideo/index.vue';
 
-  import { TypographyTitle, Divider } from 'ant-design-vue';
+  import { TypographyTitle, Divider, message } from 'ant-design-vue';
   import { BasicForm, useForm } from '/@/components/Form/index';
   import ZtCustomTable from '/@/components/ZtCustomTable/index.vue';
   import { formSchema, publishcoursesColums } from './courses.data';
   import dayjs from 'dayjs';
   import { useTabs } from '/@/hooks/web/useTabs';
   import { saveOrUpdate, getDetaile } from './courses.api';
-  import { ref } from 'vue';
+  import { ref, unref } from 'vue';
   import { useRoute } from 'vue-router';
   import { nextTick } from 'vue';
   import { useUserStore } from '/@/store/modules/user';
   import { storeToRefs } from 'pinia';
+  import { areAllItemsAllFieldsFilled } from '/@/utils';
   const { close: closeTab } = useTabs();
   const { userInfo } = storeToRefs(useUserStore());
   const route = useRoute();
@@ -101,11 +102,14 @@
   async function save() {
     await validate();
     const form = await getFieldsValue();
-    isSubmit.value = true;
     const newObj = {
       courses: { ...form, priceRulesList: null, orgCode: userInfo.value?.orgCode },
       priceRulesList: transformData(form.priceRulesList),
     };
+
+    if (!areAllItemsAllFieldsFilled(newObj.priceRulesList)) return message.error('请填写完整');
+    isSubmit.value = true;
+
     try {
       await saveOrUpdate(newObj, Number(route.query.type) != 0);
       isSubmit.value = false;
@@ -118,9 +122,9 @@
   function transformData(data) {
     return data.map((it) => {
       return {
-        startTime: dayjs(it.editValueRefs.time[0]).format('YYYY-MM-DD hh:mm:ss'),
-        endTime: dayjs(it.editValueRefs.time[1]).format('YYYY-MM-DD hh:mm:ss'),
-        name: it.editValueRefs.name.value,
+        startTime: dayjs(unref(it.editValueRefs.time)[0]).format('YYYY-MM-DD hh:mm:ss'),
+        endTime: dayjs(unref(it.editValueRefs.time)[1]).format('YYYY-MM-DD hh:mm:ss'),
+        name: unref(it.editValueRefs.name),
         id: typeof it.id == 'string' ? it.id : null,
         coursesId: it.coursesId,
       };

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

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

+ 3 - 7
src/views/informationManagement/ContractList/index.vue

@@ -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, getEditEsign } from './ContractList.api';
+  import { list, getDetaile, getOrgIdentityInfos, createTemplate, getEditEsign, templateViewUrl } from './ContractList.api';
   import { useMessage } from '/@/hooks/web/useMessage';
   import { useUserStore } from '/@/store/modules/user';
   const queryParam = reactive<any>({});
@@ -59,12 +59,8 @@
 
   const [registerTable, { reload }] = tableContext;
   async function handleView(record: Recordable) {
-    console.log(record);
-    openModal(true, {
-      record,
-      isUpdate: true,
-      showFooter: true,
-    });
+    const res = await templateViewUrl({ docTemplateId: record.docTemplateId });
+    window.open(res, '_blank');
   }
   /**
    * 操作栏

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

@@ -2,6 +2,7 @@ import { defHttp } from '/@/utils/http/axios';
 enum Api {
   list = '/cUserInfo/list',
   queryById = '/familyMembers/findByUserId',
+  edit = '/familyMembers/edit',
 }
 /**
  * 列表接口
@@ -12,3 +13,7 @@ export const list = (params) => defHttp.get({ url: Api.list, params });
 export const getDetaile = (params) => {
   return defHttp.get({ url: Api.queryById, params });
 };
+
+export const edit = (params) => {
+  return defHttp.post({ url: Api.edit, params });
+};

+ 15 - 1
src/views/informationManagement/cUserInfo/cUserInfo.data.ts

@@ -74,31 +74,45 @@ export const formSchema: FormSchema[] = [
     field: 'id',
     component: 'Input',
     required: true,
+    componentProps: {
+      readonly: true,
+    },
   },
   {
     label: '微信OpenID',
     field: 'openid',
     component: 'Input',
     required: true,
+    componentProps: {
+      readonly: true,
+    },
   },
   {
     label: '用户昵称',
     field: 'realname',
     component: 'Input',
     required: true,
+    componentProps: {
+      readonly: true,
+    },
   },
   {
     label: '头像',
     field: 'avatar',
     component: 'Input',
-    required: true,
     slot: 'avatar',
+    componentProps: {
+      readonly: true,
+    },
   },
   {
     label: '手机号码',
     field: 'id',
     component: 'Input',
     required: true,
+    componentProps: {
+      readonly: true,
+    },
   },
   {
     label: '创建时间',

+ 33 - 11
src/views/informationManagement/cUserInfo/components/cUserInfoModal.vue

@@ -1,5 +1,5 @@
 <template>
-  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose title="查看用户信息" :width="800">
+  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose title="查看用户信息" :width="800" @ok="handleSubmit">
     <div class="px-4">
       <BasicForm @register="registerForm" name="SeparateAccountsForm">
         <template #title1>
@@ -13,17 +13,17 @@
           <div v-for="(item, idx) in model[field]" :key="item.id">
             <TypographyTitle :level="4">{{ arrTitle[item.userType] }}{{ item.userType == 1 ? idx : '' }} </TypographyTitle>
             <Divider></Divider>
-            <FormItem label="是否实名" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5 }">
+            <FormItem label="是否实名" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5, sm: 3 }">
               <div>{{ item.realNameStatus ? '已实名' : '未实名' }}</div>
             </FormItem>
             <template v-if="item.realNameStatus">
-              <FormItem label="真实姓名" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5 }">
+              <FormItem label="真实姓名" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5, sm: 3 }">
                 <div>{{ item.fullName }}</div>
               </FormItem>
-              <FormItem label="身份证号" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5 }">
+              <FormItem label="身份证号" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5, sm: 3 }">
                 <div>{{ item.identityCard }}</div>
               </FormItem>
-              <FormItem label="身份证图片" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5 }">
+              <FormItem label="身份证图片" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5, sm: 3 }">
                 <div class="flex items-center">
                   <div class="mr-4">
                     <Image :src="item.idCardFrontImg" :width="120"></Image>
@@ -31,13 +31,14 @@
                   <Image :src="item.idCardBackImg" :width="120"></Image>
                 </div>
               </FormItem>
-              <FormItem label="手机号码" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5 }">
+              <FormItem label="手机号码" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5, sm: 3 }">
                 <div>{{ item.phone }}</div>
               </FormItem>
-              <FormItem label="人脸信息" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5 }">
-                <Image :src="item.realNameImg" :width="120"></Image>
+              <FormItem label="人脸信息" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5, sm: 3 }">
+                <!-- <Image :src="item.realNameImg" :width="120"></Image> -->
+                <JImageUpload v-model:value="item.realNameImg"></JImageUpload>
               </FormItem>
-              <FormItem label="认证时间" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5 }">
+              <FormItem label="认证时间" :wrapperCol="{ xl: 24 }" :labelCol="{ xl: 5, sm: 3 }">
                 <div>{{ item.createTime }}</div>
               </FormItem>
             </template>
@@ -52,8 +53,10 @@
   import { TypographyTitle, Divider, FormItem, Image } from 'ant-design-vue';
   import { ref, computed, unref } from 'vue';
   import { BasicModal, useModalInner } from '/@/components/Modal';
-  import { BasicForm, useForm } from '/@/components/Form/index';
+  import { BasicForm, useForm, JImageUpload } from '/@/components/Form/index';
   import { formSchema } from '../cUserInfo.data';
+  import { edit } from '../cUserInfo.api';
+
   const isUpdate = ref(true);
   const isDetail = ref(false);
   const arrTitle = ['自己的信息', '家庭成员'];
@@ -63,8 +66,8 @@
     labelWidth: 150,
     schemas: formSchema,
     showActionButtonGroup: false,
-    baseColProps: { span: 24 },
   });
+  const emit = defineEmits(['register', 'success']);
   //表单赋值
   const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
     //重置表单
@@ -81,6 +84,25 @@
     // 隐藏底部时禁用整个表单
     setProps({ disabled: !data?.showFooter });
   });
+  async function handleSubmit() {
+    const form = getFieldsValue();
+    const newObj = form.familyMembersList.map((it) => {
+      return {
+        id: it.id,
+        realNameImg: it.realNameImg,
+      };
+    });
+    try {
+      setModalProps({ confirmLoading: true });
+      await edit(newObj);
+      //关闭弹窗
+      closeModal();
+      //刷新列表
+      emit('success');
+    } catch (err) {
+      console.log(err);
+    }
+  }
 </script>
 
 <style lang="less" scoped>

+ 15 - 2
src/views/informationManagement/cUserInfo/index.vue

@@ -11,7 +11,7 @@
       </template>
     </BasicTable>
     <!-- 表单区域 -->
-    <cUserInfoModal @register="registerModal"></cUserInfoModal>
+    <cUserInfoModal @register="registerModal" @success="reload"></cUserInfoModal>
   </div>
 </template>
 
@@ -53,7 +53,7 @@
     },
   });
 
-  const [registerTable] = tableContext;
+  const [registerTable, { reload }] = tableContext;
   async function handleView(record: Recordable) {
     const data = await getDetaile({ userId: record.id });
     openModal(true, {
@@ -62,6 +62,15 @@
       showFooter: false,
     });
   }
+  async function handleEdit(record: Recordable) {
+    const data = await getDetaile({ userId: record.id });
+    openModal(true, {
+      data,
+      isUpdate: true,
+      showFooter: true,
+    });
+  }
+
   /**
    * 操作栏
    */
@@ -71,6 +80,10 @@
         label: '查看',
         onClick: handleView.bind(null, record),
       },
+      {
+        label: '编辑',
+        onClick: handleEdit.bind(null, record),
+      },
     ];
   }
 

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

+ 80 - 0
src/views/informationManagement/hot/components/hotModal.vue

@@ -0,0 +1,80 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
+    <BasicForm @register="registerForm" name="hotForm" />
+  </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 '../hot.data';
+  import { saveOrUpdate } from '../hot.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,
+        Time: [data.record.startTime, data.record.endTime],
+      });
+    }
+    // 隐藏底部时禁用整个表单
+    setProps({ disabled: !data?.showFooter });
+  });
+  //设置标题
+  const title = computed(() => (!unref(isUpdate) ? '新增' : !unref(isDetail) ? '详情' : '编辑'));
+  //表单提交事件
+  async function handleSubmit(v) {
+    try {
+      let values = await validate();
+      setModalProps({ confirmLoading: true });
+      const Time = values.Time.split(',');
+      //提交表单
+      await saveOrUpdate({ ...values, startTime: Time[0], endTime: Time[1] }, 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>

+ 34 - 0
src/views/informationManagement/hot/hot.api.ts

@@ -0,0 +1,34 @@
+import { defHttp } from '/@/utils/http/axios';
+import { useMessage } from '/@/hooks/web/useMessage';
+
+const { createConfirm } = useMessage();
+
+enum Api {
+  list = '/web/search/getHotSearch',
+  save = '/web/search/addHotSearch',
+  edit = '/web/search/updateHotSearch',
+  deleteOne = '/web/search/deleteHotSearch',
+}
+/**
+ * 列表接口
+ * @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 saveOrUpdate = (params, isUpdate) => {
+  let url = isUpdate ? Api.edit : Api.save;
+  return defHttp.put({ url: url, params });
+};

+ 109 - 0
src/views/informationManagement/hot/hot.data.ts

@@ -0,0 +1,109 @@
+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 { Switch } from 'ant-design-vue';
+import { saveOrUpdate } from './hot.api';
+//列表数据
+export const columns: BasicColumn[] = [
+  {
+    title: '名称',
+    align: 'center',
+    dataIndex: 'searchContent',
+  },
+  {
+    title: '显示顺序',
+    align: 'center',
+    dataIndex: 'sort',
+  },
+  {
+    title: '有效时间',
+    align: 'center',
+    dataIndex: 'isActive',
+    customRender: ({ record }) => {
+      return `${record.startTime} 至 ${record.endTime}`;
+    },
+  },
+  {
+    title: '状态',
+    align: 'center',
+    dataIndex: 'isRecommend',
+    customRender: ({ record }) => {
+      return h(Switch, {
+        checked: record.isActive,
+        unCheckedValue: 0,
+        checkedValue: 1,
+        onChange(e) {
+          record.isActive = e;
+          saveOrUpdate({ ...record, isActive: e, id: record.id }, true);
+        },
+      });
+    },
+  },
+];
+//查询数据
+export const searchFormSchema: FormSchema[] = [
+  {
+    field: 'name',
+    label: '名称',
+    component: 'Input',
+    colProps: {
+      span: 6,
+    },
+  },
+];
+//表单数据
+export const formSchema: FormSchema[] = [
+  {
+    label: '名称',
+    field: 'searchContent',
+    component: 'Input',
+    required: true,
+  },
+  {
+    label: '显示顺序',
+    field: 'sort',
+    required: true,
+    component: 'InputNumber',
+  },
+  {
+    label: '有效时间',
+    field: 'Time',
+    component: 'RangePicker',
+    required: true,
+    componentProps: {
+      format: 'YYYY-MM-DD',
+      valueFormat: 'YYYY-MM-DD',
+    },
+  },
+  {
+    label: '状态',
+    field: 'isActive',
+    required: true,
+    component: 'Switch',
+    defaultValue: 1,
+    componentProps: {
+      checkedChildren: '启用',
+      unCheckedChildren: '禁用',
+      unCheckedValue: 0,
+      checkedValue: 1,
+    },
+  },
+  // TODO 主键隐藏字段,目前写死为ID
+  {
+    label: '',
+    field: 'id',
+    component: 'Input',
+    show: false,
+  },
+];
+/**
+ * 流程表单调用这个方法获取formSchema
+ * @param param
+ */
+export function getBpmFormSchema(_formData): FormSchema[] {
+  // 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
+  return formSchema;
+}

+ 141 - 0
src/views/informationManagement/hot/index.vue

@@ -0,0 +1,141 @@
+<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>
+      <!--字段回显插槽-->
+      <template v-slot:bodyCell="{ column, record, index, text }"> </template>
+    </BasicTable>
+    <!-- 表单区域 -->
+    <hotModal @register="registerModal" @success="handleSuccess"></hotModal>
+  </div>
+</template>
+
+<script lang="ts" name="hot-hot" 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 hotModal from './components/hotModal.vue';
+  import { columns, searchFormSchema } from './hot.data';
+  import { list, deleteOne } from './hot.api';
+  import { useUserStore } from '/@/store/modules/user';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  const queryParam = reactive<any>({});
+  const userStore = useUserStore();
+  const { createMessage } = useMessage();
+  //注册model
+  const [registerModal, { openModal }] = useModal();
+  //注册table数据
+  const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
+    tableProps: {
+      title: 'hot',
+      api: list,
+      columns,
+      canResize: false,
+      formConfig: {
+        //labelWidth: 120,
+        schemas: searchFormSchema,
+        autoSubmitOnEnter: true,
+        showAdvancedButton: true,
+        fieldMapToNumber: [],
+        fieldMapToTime: [],
+      },
+      actionColumn: {
+        width: 120,
+        fixed: 'right',
+      },
+      beforeFetch: (params) => {
+        return Object.assign(params, queryParam);
+      },
+    },
+  });
+
+  const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
+
+  /**
+   * 新增事件
+   */
+  function handleAdd() {
+    openModal(true, {
+      isUpdate: false,
+      showFooter: true,
+    });
+  }
+  /**
+   * 编辑事件
+   */
+  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);
+  }
+  /**
+   * 成功回调
+   */
+  function handleSuccess() {
+    (selectedRowKeys.value = []) && reload();
+  }
+  /**
+   * 操作栏
+   */
+  function getTableAction(record) {
+    return [
+      {
+        label: '编辑',
+        onClick: handleEdit.bind(null, record),
+      },
+    ];
+  }
+  /**
+   * 下拉操作栏
+   */
+  function getDropDownAction(record) {
+    return [
+      {
+        label: '详情',
+        onClick: handleDetail.bind(null, record),
+      },
+      {
+        label: '删除',
+        popConfirm: {
+          title: '是否确认删除',
+          confirm: handleDelete.bind(null, record),
+          placement: 'topLeft',
+        },
+      },
+    ];
+  }
+</script>
+
+<style lang="less" scoped>
+  :deep(.ant-picker),
+  :deep(.ant-input-number) {
+    width: 100%;
+  }
+</style>

+ 12 - 1
src/views/informationManagement/shopInfo/index.vue

@@ -51,7 +51,7 @@
   const { userInfo } = storeToRefs(useUserStore());
 
   import { formSchema } from './shopInfo.data';
-  import { queryById, saveOrUpdate } from './shopInfo.api';
+  import { isCertified, queryById, saveOrUpdate } from './shopInfo.api';
   import { router } from '/@/router';
   import { storeToRefs } from 'pinia';
   const [registerForm, { setProps, resetFields, setFieldsValue, updateSchema, validate, clearValidate, getSchemaByField, getFieldsValue }] = useForm({
@@ -66,6 +66,15 @@
   function back() {
     router.back();
   }
+  async function getisCertified() {
+    const res = await isCertified(userInfo.value?.orgCode);
+    console.log(res, 'rens ');
+    if (res) {
+      setFieldsValue({ isCertified: true });
+      updateSchema({ field: 'isCertified', componentProps: { disabled: true } });
+    }
+  }
+  getisCertified();
   async function getData(id) {
     const res = await queryById(id);
     console.log(res, 'asdsad');
@@ -86,6 +95,8 @@
   }
   getData(userInfo.value?.orgCode);
   async function save() {
+    const res = await isCertified(userInfo.value?.orgCode);
+    if (!res) return message.error('请完成企业认证!');
     await validate();
     isLoading.value = true;
     const form = await getFieldsValue();

+ 10 - 0
src/views/informationManagement/shopInfo/shopInfo.api.ts

@@ -7,6 +7,8 @@ enum Api {
   save = '/appSite/add',
   edit = '/appSite/editSite',
   queryById = '/appSite/querySiteListByOrgCode',
+  isCertified = '/api/esign/getOrgIdentityInfos',
+  getEsignURL = '/api/esign/getOrgAuthUrls',
 }
 
 /**
@@ -20,3 +22,11 @@ export const saveOrUpdate = (params, isUpdate) => {
 export const queryById = (id) => {
   return defHttp.get({ url: Api.queryById, params: { orgCode: id } });
 };
+
+export const isCertified = (orgCode) => {
+  return defHttp.get({ url: Api.isCertified, params: { orgCode: orgCode } });
+};
+
+export const getEsignURL = (params) => {
+  return defHttp.get({ url: Api.getEsignURL, params });
+};

+ 120 - 24
src/views/informationManagement/shopInfo/shopInfo.data.ts

@@ -1,8 +1,14 @@
 import { FormSchema } from '/@/components/Table';
 import { getBaiduAdd, getSprotProject } from '/@/api/common/api';
-import { h } from 'vue';
+import { h, ref } from 'vue';
 import { JAreaSelect } from '/@/components/Form';
 import { InputGroup, Input, Button, message } from 'ant-design-vue';
+import { useUserStore } from '/@/store/modules/user';
+import { storeToRefs } from 'pinia';
+import { getEsignURL } from './shopInfo.api';
+import { useMessage } from '/@/hooks/web/useMessage';
+const { createWarningModal } = useMessage();
+const confirmLoading = ref(false);
 export const formSchema: FormSchema[] = [
   {
     label: '',
@@ -112,7 +118,64 @@ export const formSchema: FormSchema[] = [
       placeholder: '输入详细地址后点击定位,自动填充',
     },
   },
-
+  {
+    field: 'isCertified',
+    label: '企业认证',
+    component: 'RadioButtonGroup',
+    required: true,
+    defaultValue: false,
+    render: ({ model, field }) => {
+      return h(
+        Button,
+        {
+          onClick: async () => {
+            if (!model[field]) {
+              createWarningModal({
+                title: '请输入手机号接受验证码',
+                okText: '确认',
+                cancelText: '取消',
+                okCancel: true,
+                okButtonProps: { loading: confirmLoading.value },
+                content: () => {
+                  return h(Input, {
+                    placeholder: '请输入手机号',
+                    value: model['phoneNumber'],
+                    onChange: (e) => {
+                      model['phoneNumber'] = e.target.value;
+                    },
+                  });
+                },
+                onOk: async () => {
+                  return new Promise(async (resolve, reject) => {
+                    if (!model['phoneNumber']) {
+                      reject(false);
+                      return message.error('请输入手机号');
+                    }
+                    confirmLoading.value = true;
+                    const { userInfo } = storeToRefs(useUserStore());
+                    const res = await getEsignURL({ orgCode: userInfo.value?.orgCode, phoneNumber: model['phoneNumber'] });
+                    console.log(res);
+                    confirmLoading.value = false;
+                    resolve(true);
+                    window.open(res, '_blank');
+                  });
+                },
+                onCancel: () => {},
+              });
+            }
+          },
+          disabled: model[field],
+        },
+        () => '去认证'
+      );
+    },
+  },
+  {
+    field: 'phoneNumber',
+    label: '',
+    component: 'RangePicker',
+    ifShow: false,
+  },
   {
     field: 'title1',
     colSlot: 'title1',
@@ -238,8 +301,13 @@ export const formSchema: FormSchema[] = [
     component: 'JImageUpload',
     colProps: { xl: 12 },
     componentProps: {
-      isProportion: true,
-      tipText: '支持单张上传;宽高比为1:1,大小5M以内。',
+      tipText: '支持单张上传;宽高2048*2048,大小5M以内。',
+      beforeUpload: async (file) => {
+        const Verification = await getImgWidthHeight(file);
+        if (Verification) return Verification;
+        message.error('图片宽高不符合要求');
+        return false;
+      },
     },
     ifShow(renderCallbackParams) {
       return renderCallbackParams.model.type == 1;
@@ -251,10 +319,13 @@ export const formSchema: FormSchema[] = [
     component: 'JImageUpload',
     colProps: { xl: 12 },
     componentProps: {
-      isProportion: true,
-      tipText: '支持单张上传;宽高比为1:1,大小5M以内。',
-      aspectRatioW: 1,
-      aspectRatioH: 1,
+      tipText: '支持单张上传;宽高2048*2048,大小5M以内。',
+      beforeUpload: async (file) => {
+        const Verification = await getImgWidthHeight(file);
+        if (Verification) return Verification;
+        message.error('图片宽高不符合要求');
+        return false;
+      },
     },
     ifShow(renderCallbackParams) {
       return renderCallbackParams.model.type == 1;
@@ -266,10 +337,13 @@ export const formSchema: FormSchema[] = [
     component: 'JImageUpload',
     colProps: { xl: 12 },
     componentProps: {
-      isProportion: true,
-      tipText: '支持单张上传;宽高比为1:1,大小5M以内。',
-      aspectRatioW: 1,
-      aspectRatioH: 1,
+      beforeUpload: async (file) => {
+        const Verification = await getImgWidthHeight(file);
+        if (Verification) return Verification;
+        message.error('图片宽高不符合要求');
+        return false;
+      },
+      tipText: '支持单张上传;宽高2048*2048,大小5M以内。',
     },
     ifShow(renderCallbackParams) {
       return renderCallbackParams.model.type == 1;
@@ -280,10 +354,13 @@ export const formSchema: FormSchema[] = [
     label: '图片(右)',
     component: 'JImageUpload',
     componentProps: {
-      isProportion: true,
-      tipText: '支持单张上传;宽高比为1:1,大小5M以内。',
-      aspectRatioW: 1,
-      aspectRatioH: 1,
+      beforeUpload: async (file) => {
+        const Verification = await getImgWidthHeight(file);
+        if (Verification) return Verification;
+        message.error('图片宽高不符合要求');
+        return false;
+      },
+      tipText: '支持单张上传;宽高2048*2048,大小5M以内。',
     },
     colProps: { xl: 12 },
     ifShow(renderCallbackParams) {
@@ -295,10 +372,13 @@ export const formSchema: FormSchema[] = [
     label: '图片(上)',
     component: 'JImageUpload',
     componentProps: {
-      isProportion: true,
-      tipText: '支持单张上传;宽高比为1:1,大小5M以内。',
-      aspectRatioW: 1,
-      aspectRatioH: 1,
+      tipText: '支持单张上传;宽高2048*2048,大小5M以内。',
+      beforeUpload: async (file) => {
+        const Verification = await getImgWidthHeight(file);
+        if (Verification) return Verification;
+        message.error('图片宽高不符合要求');
+        return false;
+      },
     },
     colProps: { xl: 12 },
     ifShow(renderCallbackParams) {
@@ -310,10 +390,13 @@ export const formSchema: FormSchema[] = [
     label: '图片(下)',
     component: 'JImageUpload',
     componentProps: {
-      isProportion: true,
-      tipText: '支持单张上传;宽高比为1:1,大小5M以内。',
-      aspectRatioW: 1,
-      aspectRatioH: 1,
+      tipText: '支持单张上传;宽高2048*2048,大小5M以内。',
+      beforeUpload: async (file) => {
+        const Verification = await getImgWidthHeight(file);
+        if (Verification) return Verification;
+        message.error('图片宽高不符合要求');
+        return false;
+      },
     },
     colProps: { xl: 12 },
     ifShow(renderCallbackParams) {
@@ -321,3 +404,16 @@ export const formSchema: FormSchema[] = [
     },
   },
 ];
+
+function getImgWidthHeight(file) {
+  return new Promise((resolve, reject) => {
+    const img = new Image();
+    img.src = URL.createObjectURL(file);
+    if (!img.src) reject('找不到图片');
+    img.onload = () => {
+      const { width, height } = img;
+      if (width != 2048 || height != 2048) resolve(false);
+      resolve(true);
+    };
+  });
+}

+ 24 - 0
src/views/system/MiniProgramRoleRouter/MiniProgramRoleRouter.api.ts

@@ -0,0 +1,24 @@
+import { defHttp } from '/@/utils/http/axios';
+import { useMessage } from '/@/hooks/web/useMessage';
+
+const { createConfirm } = useMessage();
+
+enum Api {
+  list = '/app/appKongfuZone/list',
+  save = '/app/appKongfuZone/add',
+  edit = '/app/appKongfuZone/edit',
+}
+/**
+ * 列表接口
+ * @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 defHttp.post({ url: url, params });
+};

+ 128 - 0
src/views/system/MiniProgramRoleRouter/MiniProgramRoleRouter.data.ts

@@ -0,0 +1,128 @@
+import { list } from '../role/role.api';
+import { BasicColumn } from '/@/components/Table';
+import { FormSchema } from '/@/components/Table';
+//列表数据
+export const columns: BasicColumn[] = [
+  {
+    title: '名称',
+    align: 'center',
+    dataIndex: 'name',
+  },
+  {
+    title: '图标URL',
+    align: 'center',
+    dataIndex: 'iconUrl',
+  },
+  {
+    title: '跳转路由',
+    align: 'center',
+    dataIndex: 'route',
+    width: 420,
+  },
+  {
+    title: '金刚区显示顺序',
+    align: 'center',
+    dataIndex: 'orderNumber',
+  },
+  {
+    title: '是否展示',
+    align: 'center',
+    dataIndex: 'izShow',
+    customRender: ({ record }) => {
+      return record.izShow ? '是' : '否';
+    },
+  },
+];
+//查询数据
+export const searchFormSchema: FormSchema[] = [];
+//表单数据
+export const formSchema: FormSchema[] = [
+  {
+    label: '名称',
+    field: 'name',
+    component: 'Input',
+    dynamicRules: ({ model, schema }) => {
+      return [{ required: true, message: '请输入名称!' }];
+    },
+    componentProps: {
+      disabled: true,
+    },
+  },
+  {
+    label: '图标URL',
+    field: 'iconUrl',
+    component: 'Input',
+    componentProps: {
+      disabled: true,
+    },
+  },
+  {
+    label: '跳转路由',
+    field: 'route',
+    component: 'Input',
+    required: true,
+    componentProps: {
+      disabled: true,
+    },
+  },
+  {
+    label: '金刚区显示顺序',
+    field: 'orderNumber',
+    component: 'InputNumber',
+    componentProps: {
+      disabled: true,
+    },
+  },
+  {
+    label: '是否展示',
+    field: 'izShow',
+    component: 'Switch',
+    componentProps: {
+      checkedChildren: '是',
+      unCheckedChildren: '否',
+      unCheckedValue: '0',
+      checkedValue: '1',
+      disabled: true,
+    },
+    defaultValue: '1',
+  },
+  {
+    label: '角色',
+    field: 'roleCode',
+    component: 'ApiSelect',
+    required: true,
+    componentProps: {
+      api: list,
+      mode: 'multiple',
+      resultField: 'records',
+      labelField: 'roleName',
+      valueField: 'roleCode',
+    },
+  },
+  // TODO 主键隐藏字段,目前写死为ID
+  {
+    label: '',
+    field: 'id',
+    component: 'Input',
+    show: false,
+  },
+];
+
+// 高级查询数据
+export const superQuerySchema = {
+  name: { title: '名称', order: 0, view: 'text', type: 'string' },
+  iconUrl: { title: '图标URL', order: 1, view: 'text', type: 'string' },
+  route: { title: '跳转路由', order: 2, view: 'text', type: 'string' },
+  orderNumber: { title: '金刚区显示顺序', order: 3, view: 'number', type: 'number' },
+  izShow: { title: '是否展示,1表示展示,0表示不展示', order: 4, view: 'text', type: 'string' },
+  roleCode: { title: 'roleCode', order: 6, view: 'text', type: 'string' },
+};
+
+/**
+ * 流程表单调用这个方法获取formSchema
+ * @param param
+ */
+export function getBpmFormSchema(_formData): FormSchema[] {
+  // 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
+  return formSchema;
+}

+ 78 - 0
src/views/system/MiniProgramRoleRouter/components/MiniProgramRoleRouterModal.vue

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

+ 130 - 0
src/views/system/MiniProgramRoleRouter/index.vue

@@ -0,0 +1,130 @@
+<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>
+      <!--字段回显插槽-->
+      <template v-slot:bodyCell="{ column, record, index, text }"> </template>
+    </BasicTable>
+    <!-- 表单区域 -->
+    <MiniProgramRoleRouterModal @register="registerModal" @success="handleSuccess"></MiniProgramRoleRouterModal>
+  </div>
+</template>
+
+<script lang="ts" name="MiniProgramRoleRouter-miniProgramRoleRouter" 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 MiniProgramRoleRouterModal from './components/MiniProgramRoleRouterModal.vue';
+  import { columns, searchFormSchema, superQuerySchema } from './MiniProgramRoleRouter.data';
+  import { list } from './MiniProgramRoleRouter.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: 'MiniProgramRoleRouter',
+      api: list,
+      columns,
+      canResize: false,
+      formConfig: {
+        //labelWidth: 120,
+        schemas: searchFormSchema,
+        autoSubmitOnEnter: true,
+        showAdvancedButton: true,
+        fieldMapToNumber: [],
+        fieldMapToTime: [],
+      },
+      actionColumn: {
+        width: 120,
+        fixed: 'right',
+      },
+      beforeFetch: (params) => {
+        return Object.assign(params, queryParam);
+      },
+    },
+  });
+
+  const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
+
+  /**
+   * 新增事件
+   */
+  function handleAdd() {
+    openModal(true, {
+      isUpdate: false,
+      showFooter: true,
+    });
+  }
+  /**
+   * 编辑事件
+   */
+  function handleEdit(record: Recordable) {
+    openModal(true, {
+      record,
+      isUpdate: true,
+      showFooter: true,
+    });
+  }
+  /**
+   * 详情
+   */
+  function handleDetail(record: Recordable) {
+    openModal(true, {
+      record,
+      isUpdate: true,
+      showFooter: false,
+    });
+  }
+
+  /**
+   * 成功回调
+   */
+  function handleSuccess() {
+    (selectedRowKeys.value = []) && reload();
+  }
+  /**
+   * 操作栏
+   */
+  function getTableAction(record) {
+    return [
+      {
+        label: '编辑',
+        onClick: handleEdit.bind(null, record),
+      },
+    ];
+  }
+  /**
+   * 下拉操作栏
+   */
+  function getDropDownAction(record) {
+    return [
+      {
+        label: '详情',
+        onClick: handleDetail.bind(null, record),
+      },
+    ];
+  }
+</script>
+
+<style lang="less" scoped>
+  :deep(.ant-picker),
+  :deep(.ant-input-number) {
+    width: 100%;
+  }
+</style>

+ 6 - 0
src/views/system/notice/notice.data.ts

@@ -118,6 +118,12 @@ export const formSchema: FormSchema[] = [
     component: 'InputTextArea',
     required: true,
   },
+  {
+    field: 'imgUrl',
+    label: '封面图',
+    component: 'JImageUpload',
+    required: true,
+  },
   // {
   //   field: 'endTime',
   //   label: '截至日期',