Ver código fonte

Merge remote-tracking branch 'origin/dev' into dev

# Conflicts:
#	src/components/MultiplePopup/src/components/MultipleSelect.vue
#	src/views/form/template/index.vue
zcuishan 4 meses atrás
pai
commit
bbd1802c36

+ 4 - 0
src/services/apis/BaseNewStudentController.ts

@@ -22,6 +22,10 @@ export async function putStudentBaseNewStudent(params:API.UpdateBaseNewStudentDt
 /** 删除新生维护信息 DELETE /student/baseNewStudent */
 export async function deleteStudentBaseNewStudent(params:string[],mode: ErrorMessageMode = 'modal'){ return defHttp.delete<any>
         ({url: '/student/baseNewStudent', data:params},{errorMessageMode:mode});}
+/** 激活账号 POST /student/baseNewStudent/active-account */
+export async function postBaseNewStudentActiveAccount(params:API.ActiveAccountDto
+,mode: ErrorMessageMode = 'modal'){ return defHttp.post<any>
+        ({url: '/student/baseNewStudent/active-account', data:params},{errorMessageMode:mode});}
 /** 导入 POST /student/baseNewStudent/import */
 export async function postBaseNewStudentImport(params:any,mode: ErrorMessageMode = 'modal'){ return defHttp.post<API.Map[]>
         ({url: '/student/baseNewStudent/import',headers:{'Content-Type':'multipart/form-data'}, data:params},{errorMessageMode:mode});}

+ 4 - 0
src/services/apis/MaterialTaskController.ts

@@ -46,6 +46,10 @@ export async function putMaterialtaskEnd(params:API.UpdateEnableMarkDto
 /** 导出 GET /material/materialtask/export */
 export async function getMaterialtaskExport(params:any,mode: ErrorMessageMode = 'modal'){ return defHttp.download<any>
         ({url: '/material/materialtask/export',responseType:'blob', params:params},{errorMessageMode:mode});}
+/** 表单缴交数据条件导出 GET /material/materialtask/form-data-export-query */
+export async function getMaterialtaskFormDataExportQuery(params:API.FormDataExportQueryDto
+,mode: ErrorMessageMode = 'modal'){ return defHttp.download<string>
+        ({url: '/material/materialtask/form-data-export-query',responseType:'blob',method:'POST',params},{errorMessageMode:mode});}
 /** 导入 POST /material/materialtask/import */
 export async function postMaterialtaskImport(params:any,mode: ErrorMessageMode = 'modal'){ return defHttp.post<any>
         ({url: '/material/materialtask/import',headers:{'Content-Type':'multipart/form-data'}, data:params},{errorMessageMode:mode});}

+ 3 - 0
src/services/apis/StudentManagerController.ts

@@ -34,6 +34,9 @@ export async function postStudentmanagerImport(params:any,mode: ErrorMessageMode
 /** 根据id查询学生信息 GET /student/studentmanager/info */
 export async function getStudentmanagerInfo(params:any,mode: ErrorMessageMode = 'modal'){ return defHttp.get<any>
         ({url: '/student/studentmanager/info', params:params},{errorMessageMode:mode});}
+/** 学生部门专业年级班级树 GET /student/studentmanager/major-grade-class-tree */
+export async function getStudentmanagerMajorGradeClassTree(params:any,mode: ErrorMessageMode = 'modal'){ return defHttp.get<API.BaseDepMajorGradeClassStudenTreeVo[]>
+        ({url: '/student/studentmanager/major-grade-class-tree', params:params},{errorMessageMode:mode});}
 /** 学生列表(分页) GET /student/studentmanager/page */
 export async function getStudentmanagerPage(params:any,mode: ErrorMessageMode = 'modal'){ return defHttp.get<any>
         ({url: '/student/studentmanager/page', params:params},{errorMessageMode:mode});}

+ 3 - 0
src/services/apis/StundentFaceProcessController.ts

@@ -31,6 +31,9 @@ export async function getStundentFaceProcessInfo(params:any,mode: ErrorMessageMo
 /** 根据用户id查询学生人脸信息审核信息 GET /personnel/stundentFaceProcess/info-userId */
 export async function getStundentFaceProcessInfoUserId(params:any,mode: ErrorMessageMode = 'modal'){ return defHttp.get<API.StundentFaceProcessVo>
         ({url: '/personnel/stundentFaceProcess/info-userId', params:params},{errorMessageMode:mode});}
+/** 新生账号激活上传人脸信息 POST /personnel/stundentFaceProcess/ns-upload-face */
+export async function postStundentFaceProcessNsUploadFace(mode: ErrorMessageMode = 'modal'){ return defHttp.post<any>
+        ({url: '/personnel/stundentFaceProcess/ns-upload-face', data:{}},{errorMessageMode:mode});}
 /** 学生人脸信息审核列表(分页) GET /personnel/stundentFaceProcess/page */
 export async function getStundentFaceProcessPage(params:any,mode: ErrorMessageMode = 'modal'){ return defHttp.get<API.PageOutput<API.StundentFaceProcessPageVo>>
         ({url: '/personnel/stundentFaceProcess/page', params:params},{errorMessageMode:mode});}

+ 1 - 1
src/services/apis/WhitelistManagementController.ts

@@ -23,7 +23,7 @@ export async function putBaseWhitelistManagement(params:API.UpdateWhitelistManag
 export async function deleteBaseWhitelistManagement(params:string[],mode: ErrorMessageMode = 'modal'){ return defHttp.delete<any>
         ({url: '/base/whitelistManagement', data:params},{errorMessageMode:mode});}
 /** 导入 POST /base/whitelistManagement/import */
-export async function postWhitelistManagementImport(params:any,mode: ErrorMessageMode = 'modal'){ return defHttp.post<any>
+export async function postWhitelistManagementImport(params:any,mode: ErrorMessageMode = 'modal'){ return defHttp.post<API.Map[]>
         ({url: '/base/whitelistManagement/import',headers:{'Content-Type':'multipart/form-data'}, data:params},{errorMessageMode:mode});}
 /** 根据id查询白名单管理信息 GET /base/whitelistManagement/info */
 export async function getWhitelistManagementInfo(params:any,mode: ErrorMessageMode = 'modal'){ return defHttp.get<API.WhitelistManagementVo>

+ 66 - 5
src/services/typing.d.ts

@@ -62,7 +62,18 @@
                     items?: T[];
                 };
 
-            type AddAppMenuDto = {
+            type ActiveAccountDto = {
+/** 主键 */
+id?: string;
+/** 手机号 */
+mobile?: string;
+/** 家长手机号 */
+parentMobile?: string;
+/** 家长姓名 */
+parentName?: string;
+}
+
+type AddAppMenuDto = {
 /** 分类id */
 categoryId?: string;
 /** 菜单编码 */
@@ -364,6 +375,8 @@ sortCode?: number;
 type AddBandingTaskClassDto = {
 /** 分班任务id */
 bandingTaskId?: string;
+/** 班级类型 */
+classType?: string;
 /** 教室id */
 classroomId?: string;
 /** 身高 */
@@ -4378,6 +4391,8 @@ sortCode?: number;
 }
 
 type BandingTaskClassPageVo = {
+/** 班级类型 */
+classType?: string;
 /** 教室id */
 classroomId?: string;
 /** 教室名称 */
@@ -4783,10 +4798,16 @@ type BaseClassMajorSetVo = {
 boyNum?: number;
 /** 班级(base_class_major_set) */
 classId?: string;
+/** 班级(base_class_major_set) */
+className?: string;
 /** 女生人数 */
 girlNum?: number;
+/** 所属年级(base_grade) */
+gradeId?: string;
 /** 主键 */
 id?: string;
+/** 专业(base_major) */
+majorId?: string;
 /** 专业方向(base_major_set) */
 majorSetId?: string;
 /** 计划人数 */
@@ -4881,6 +4902,19 @@ id?: string;
 status?: number;
 }
 
+type BaseDepMajorGradeClassStudenTreeVo = {
+/** children */
+children?: BaseDepMajorGradeClassStudenTreeVo[];
+/** id */
+id?: string;
+/** name */
+name?: string;
+/** disabled */
+parentId?: string;
+/** 树类型(1:部门  2:专业  3:年级  4:班级) */
+treeType?: number;
+}
+
 type BaseGraduateSchoolOfEnrollmentPlanListVo = {
 /** 毕业学校主键编号 */
 id?: string;
@@ -6612,20 +6646,30 @@ sortCode?: number;
 }
 
 type BaseStudentSompleInfoVo = {
+/** 班级id */
+classId?: string;
 /** 班级名称 */
 className?: string;
 /** 身份证号 */
 credentialNumber?: string;
 /** 启用状态(0:禁用 1:启用) */
 enabledMark?: number;
+/** 性别 */
+gender?: string;
 /** 主键编号 */
 id?: string;
 /** 本人电话 */
 mobile?: string;
+/** 家长电话 */
+parentMobile?: string;
+/** 家长名称 */
+parentName?: string;
 /** 学生姓名 */
 studentName?: string;
 /** 班主任名称 */
 teacherName?: string;
+/** 班主任id */
+teahcerId?: string;
 }
 
 type BaseStudentTreeVo = {
@@ -8610,6 +8654,13 @@ hiddenComponent?: Map<string,object>[];
 list?: ComponentConfig_9[];
 }
 
+type FormDataExportQueryDto = {
+/** 表单数据id */
+formDataId?: string;
+/** 自定义表单 表单模板id */
+templateId?: string;
+}
+
 type FormDesignConfig = {
 dataAuthList?: string[];
 /** 备注 */
@@ -9183,10 +9234,6 @@ folderId?: string;
 folderIdCn?: string;
 /** 表单数据id */
 formDataId?: string;
-/** 表单发布 */
-formReleaseId?: string;
-/** 表单发布 */
-formReleaseIdCn?: string;
 /** 主键编号 */
 id?: string;
 /** 任务类型(可多选 xjr_dictionary_detail[material_category]) */
@@ -10105,6 +10152,8 @@ credentialNumber?: string;
 enrollType?: string;
 /** 排序字段 */
 field?: string;
+/** 第一志愿 */
+firstAmbition?: string;
 /** 年级id */
 gradeId?: string;
 /** 毕业班级 */
@@ -10119,6 +10168,8 @@ limit?: number;
 name?: string;
 /** 排序方式 asc  desc */
 order?: string;
+/** 第二志愿 */
+secondAmbition?: string;
 /** 每页大小 */
 size?: number;
 /** 班级状态 */
@@ -15214,6 +15265,8 @@ stduyStatus?: string;
 type UpdateBandingClassDto = {
 /** 分班任务id */
 bandingTaskId?: string;
+/** 班级类型 */
+classType?: string;
 /** 教室id */
 classroomId?: string;
 /** 身高 */
@@ -18562,6 +18615,8 @@ mobile?: string;
 name?: string;
 /** 班主任 */
 teacherId?: string;
+/** 是否存在于白名单中(1:是 0:否) */
+whitelistStatus?: number;
 }
 
 type UserStudentVo = {
@@ -18573,6 +18628,12 @@ id?: string;
 status?: number;
 studentId?: string;
 studentName?: string;
+/** 班主任id */
+teacherId?: string;
+/** 班主任电话 */
+teacherMobile?: string;
+/** 班主任姓名 */
+teacherName?: string;
 userId?: string;
 }
 

+ 1 - 0
src/views/activity/contest/data.config.ts

@@ -200,6 +200,7 @@ export const formSchema: FormSchema[] = [
     component: 'Input',
     required: true,
     colProps: { span: 24 },
+    slot: 'selectUser',
   },
   {
     field: 'newsContent',

+ 15 - 2
src/views/activity/contest/edit.vue

@@ -9,7 +9,11 @@
     :width="1002"
     showFooter
   >
-    <BasicForm @register="registerForm" />
+    <BasicForm @register="registerForm">
+      <template #selectUser>
+        <SelectUser v-model:value="selectUserValue" @change="handleUserChange" />
+      </template>
+    </BasicForm>
   </BasicDrawer>
 </template>
 <script setup lang="ts">
@@ -23,11 +27,16 @@
     postBandingBandingTask,
     putBandingBandingTask,
   } from '/@/services/apis/BandingTaskController';
+  import SelectUser from '/@/views/notice/components/SelectUser.vue';
+  import { Recordable } from 'vite-plugin-mock';
 
   const isUpdate = ref(true);
   const modelRef = ref({});
   const emit = defineEmits(['success', 'register']);
   const { createMessage } = useMessage();
+
+  const selectUserValue = ref<Recordable[]>([]);
+
   const [registerForm, { validate, setFieldsValue, resetFields }] = useForm({
     labelWidth: 100,
     schemas: formSchema,
@@ -50,7 +59,7 @@
     }
   });
 
-  const getTitle = computed(() => (!unref(isUpdate) ? '新增招生计划' : '编辑招生计划'));
+  const getTitle = computed(() => (!unref(isUpdate) ? '新增赛事活动' : '编辑赛事活动'));
   const handleSubmit = async () => {
     try {
       const values = await validate();
@@ -71,6 +80,10 @@
       setDrawerProps({ confirmLoading: false });
     }
   };
+
+  const handleUserChange = (data: Recordable[]) => {
+    selectUserValue.value = data;
+  };
 </script>
 
 <style scoped lang="less"></style>

+ 220 - 0
src/views/activity/cooperate/data.config.ts

@@ -0,0 +1,220 @@
+import { BasicColumn, FormSchema } from '/@/components/Table';
+import { getDataOption } from '/@/api/system/dic';
+
+import { requestMagicApi } from '/@/api/magicApi';
+import { BasicOptionModel } from '/@/api/model/baseModel';
+import { Tinymce } from '/@/components/Tinymce';
+import { h } from 'vue';
+
+export const contestStatusOptions: BasicOptionModel[] = [
+  { label: '未发布', value: 1 },
+  { label: '报名中', value: 2 },
+  { label: '报名结束', value: 3 },
+  { label: '活动结束', value: 4 },
+];
+
+export const tableColumns: BasicColumn[] = [
+  {
+    title: '活动名称',
+    dataIndex: 'name',
+    align: 'left',
+    width: 150,
+  },
+  {
+    title: '开始时间',
+    dataIndex: 'stateDate',
+    align: 'left',
+    width: 150,
+  },
+  {
+    title: '结束时间',
+    dataIndex: 'endDate',
+    align: 'left',
+    width: 150,
+  },
+  {
+    title: '活动地点',
+    dataIndex: 'address',
+    align: 'left',
+  },
+  {
+    title: '合作企业',
+    dataIndex: 'type',
+    align: 'left',
+    width: 120,
+  },
+  {
+    title: '组织部门',
+    dataIndex: 'deptCn',
+    align: 'left',
+    width: 120,
+  },
+  {
+    title: '状态',
+    dataIndex: 'status',
+    align: 'left',
+    width: 80,
+    customRender: ({ text }) => {
+      return contestStatusOptions.filter((row) => row.value === text)[0]?.label;
+    },
+  },
+  {
+    title: '报名人数',
+    dataIndex: 'count',
+    align: 'left',
+    width: 80,
+  },
+];
+
+export const searchFormSchema: FormSchema[] = [
+  {
+    field: 'name',
+    label: '活动名称',
+    component: 'Input',
+    colProps: { span: 8 },
+  },
+  {
+    field: 'enrollType',
+    label: '合作企业',
+    component: 'ApiSelect',
+    colProps: { span: 8 },
+    componentProps: {
+      getPopupContainer: () => document.body,
+      api: getDataOption,
+      params: { code: 'enroll_type' },
+    },
+  },
+  {
+    field: 'deptId',
+    label: '组织部门',
+    component: 'ApiSelect',
+    colProps: { span: 8 },
+    componentProps: {
+      getPopupContainer: () => document.body,
+      api: requestMagicApi,
+      params: { url: 'educational/abutment/org-info' },
+      labelField: 'name',
+      valueField: 'id',
+    },
+  },
+  {
+    field: 'status',
+    label: '状态',
+    component: 'Select',
+    colProps: { span: 8 },
+    componentProps: {
+      options: contestStatusOptions,
+      getPopupContainer: () => document.body,
+    },
+  },
+];
+
+export const formSchema: FormSchema[] = [
+  {
+    field: 'name',
+    label: '活动名称',
+    component: 'Input',
+    required: true,
+    colProps: { span: 24 },
+  },
+  {
+    field: 'date',
+    label: '活动时间',
+    component: 'RangePicker',
+    required: true,
+    colProps: { span: 24 },
+    componentProps: {
+      getPopupContainer: () => document.body,
+      placeholder: ['开始时间', '结束时间'],
+      format: 'YYYY-MM-DD HH:mm:ss',
+      showTime: { format: 'HH:mm:ss' },
+    },
+  },
+  {
+    field: 'address',
+    label: '活动地点',
+    component: 'Input',
+    required: true,
+    colProps: { span: 12 },
+  },
+  {
+    field: 'enrollType',
+    label: '合作企业',
+    component: 'ApiSelect',
+    colProps: { span: 12 },
+    required: true,
+    componentProps: {
+      getPopupContainer: () => document.body,
+      api: getDataOption,
+      params: { code: 'enroll_type' },
+    },
+  },
+  {
+    field: 'endDate',
+    label: '截止时间',
+    component: 'DatePicker',
+    required: true,
+    colProps: { span: 12 },
+    componentProps: {
+      format: 'YYYY-MM-DD',
+      getPopupContainer: () => document.body,
+    },
+  },
+  {
+    field: 'deptId',
+    label: '组织部门',
+    component: 'ApiSelect',
+    colProps: { span: 12 },
+    required: true,
+    componentProps: {
+      getPopupContainer: () => document.body,
+      api: requestMagicApi,
+      params: { url: 'educational/abutment/org-info' },
+      labelField: 'name',
+      valueField: 'id',
+    },
+  },
+  {
+    field: 'name',
+    label: '负责人',
+    component: 'Input',
+    required: true,
+    colProps: { span: 12 },
+  },
+  {
+    field: 'tel',
+    label: '联系电话',
+    component: 'Input',
+    required: true,
+    colProps: { span: 12 },
+  },
+  {
+    label: '活动封面',
+    field: 'img',
+    component: 'Input',
+    colProps: { span: 24 },
+  },
+  {
+    label: '报名范围',
+    field: 'pople',
+    component: 'Input',
+    required: true,
+    colProps: { span: 24 },
+    slot: 'selectUser',
+  },
+  {
+    field: 'newsContent',
+    component: 'Input',
+    label: '活动详情',
+    rules: [{ required: true }],
+    colProps: { span: 24 },
+    render: ({ model, field }) => {
+      return h(Tinymce, {
+        value: model[field],
+        onChange: (value: string) => {
+          model[field] = value;
+        },
+      });
+    },
+  },
+];

+ 89 - 0
src/views/activity/cooperate/edit.vue

@@ -0,0 +1,89 @@
+<template>
+  <BasicDrawer
+    @ok="handleSubmit"
+    :destroyOnClose="true"
+    :maskClosable="false"
+    v-bind="$attrs"
+    @register="registerModal"
+    :title="getTitle"
+    :width="1002"
+    showFooter
+  >
+    <BasicForm @register="registerForm">
+      <template #selectUser>
+        <SelectUser v-model:value="selectUserValue" @change="handleUserChange" />
+      </template>
+    </BasicForm>
+  </BasicDrawer>
+</template>
+<script setup lang="ts">
+  import { ref, computed, unref } from 'vue';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
+  import { BasicForm, useForm } from '/@/components/Form/index';
+  import { formSchema } from './data.config';
+  import {
+    getBandingTaskInfo,
+    postBandingBandingTask,
+    putBandingBandingTask,
+  } from '/@/services/apis/BandingTaskController';
+  import SelectUser from '/@/views/notice/components/SelectUser.vue';
+  import { Recordable } from 'vite-plugin-mock';
+
+  const isUpdate = ref(true);
+  const modelRef = ref({});
+  const emit = defineEmits(['success', 'register']);
+  const { createMessage } = useMessage();
+
+  const selectUserValue = ref<Recordable[]>([]);
+
+  const [registerForm, { validate, setFieldsValue, resetFields }] = useForm({
+    labelWidth: 100,
+    schemas: formSchema,
+    showActionButtonGroup: false,
+  });
+
+  const [registerModal, { closeDrawer, setDrawerProps }] = useDrawerInner(async (data) => {
+    resetFields();
+    setDrawerProps({ confirmLoading: false });
+    isUpdate.value = !!data?.isUpdate;
+    modelRef.value = { ...data.baseData };
+
+    if (unref(isUpdate)) {
+      const resData = await getBandingTaskInfo({ id: data.baseData.id });
+
+      modelRef.value = { ...resData };
+      setFieldsValue({
+        ...resData,
+      });
+    }
+  });
+
+  const getTitle = computed(() => (!unref(isUpdate) ? '新增校企合作活动' : '编辑校企合作活动'));
+  const handleSubmit = async () => {
+    try {
+      const values = await validate();
+      setDrawerProps({ confirmLoading: true });
+      const postParams = unref(modelRef);
+      Object.assign(postParams, values);
+
+      if (unref(isUpdate)) {
+        await putBandingBandingTask(postParams as API.UpdateBandingTaskDto);
+      } else {
+        await postBandingBandingTask(postParams as API.AddBandingTaskDto);
+      }
+
+      createMessage.success('操作成功');
+      closeDrawer();
+      emit('success');
+    } finally {
+      setDrawerProps({ confirmLoading: false });
+    }
+  };
+
+  const handleUserChange = (data: Recordable[]) => {
+    selectUserValue.value = data;
+  };
+</script>
+
+<style scoped lang="less"></style>

+ 107 - 0
src/views/activity/cooperate/index.vue

@@ -0,0 +1,107 @@
+<template>
+  <PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
+    <BasicTable @register="registerTable">
+      <template #toolbar>
+        <a-button type="primary" @click="handleEdit({}, false)">新增</a-button>
+      </template>
+      <template #action="{ record }">
+        <TableAction
+          :actions="[
+            {
+              label: '编辑',
+              onClick: handleEdit.bind(null, record, true),
+            },
+            {
+              label: '发布',
+            },
+            {
+              label: '报名表',
+            },
+            {
+              label: '删除',
+              color: 'error',
+              onClick: handleDelete.bind(null, record),
+            },
+          ]"
+        />
+      </template>
+    </BasicTable>
+    <FormEdit @register="registerModal" @success="handleSuccess" />
+  </PageWrapper>
+</template>
+
+<script setup lang="ts">
+  import { onMounted } from 'vue';
+  import { PageWrapper } from '/@/components/Page';
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { tableColumns, searchFormSchema } from './data.config';
+  import {
+    deleteBandingBandingTask,
+    getBandingTaskPage,
+  } from '/@/services/apis/BandingTaskController';
+  import { useDrawer } from '/@/components/Drawer';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import FormEdit from './edit.vue';
+
+  const [registerModal, { openDrawer }] = useDrawer();
+
+  const [registerTable, { reload }] = useTable({
+    api: getBandingTaskPage,
+    title: '校企合作活动列表',
+    rowKey: 'id',
+    columns: tableColumns,
+    formConfig: {
+      labelWidth: 120,
+      schemas: searchFormSchema,
+    },
+    useSearchForm: true,
+    showTableSetting: true,
+    bordered: true,
+    immediate: true,
+    canResize: true,
+    actionColumn: {
+      width: 210,
+      title: '操作',
+      dataIndex: 'action',
+      slots: { customRender: 'action' },
+      fixed: 'right',
+    },
+  });
+  const { createConfirm, createMessage } = useMessage();
+
+  const handleDelete = (record: any) => {
+    createConfirm({
+      iconType: 'warning',
+      title: '温馨提醒',
+      content: '是否删除该记录?',
+      onOk: async () => {
+        try {
+          await deleteBandingBandingTask([record.id]);
+          createMessage.success('删除成功');
+          await reload();
+        } catch (e) {
+          createMessage.error('删除失败');
+        }
+      },
+      okText: '确认',
+      cancelText: '取消',
+    });
+  };
+
+  const handleEdit = (record: any, isUpdate: boolean) => {
+    openDrawer(true, {
+      isUpdate: isUpdate,
+      baseData: {
+        ...record,
+      },
+    });
+  };
+
+  const handleSuccess = async () => {
+    await reload();
+  };
+
+  onMounted(async () => {});
+</script>
+
+<style scoped lang="less"></style>

+ 49 - 0
src/views/educational/class/components/ClassGroup.vue

@@ -0,0 +1,49 @@
+<template>
+  <div class="mr-2 overflow-hidden bg-white h-full">
+    <BasicTree
+      :title="t('班级列表')"
+      toolbar
+      search
+      :clickRowToExpand="true"
+      :treeData="treeData"
+      :selectedKeys="selectedKeys"
+      expandOnSearch
+      :fieldNames="{ key: 'id', title: 'name' }"
+      @select="handleSelect"
+    />
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent, onMounted, ref } from 'vue';
+
+  import { BasicTree, TreeItem } from '/@/components/Tree';
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import { getStudentmanagerMajorGradeClassTree } from '/@/services/apis/StudentManagerController';
+  const { t } = useI18n();
+  export default defineComponent({
+    name: 'ClassTree',
+    components: { BasicTree },
+
+    emits: ['select'],
+    setup(_, { emit }) {
+      const treeData = ref<TreeItem[]>([]);
+      const selectedKeys = ref<string[]>([]);
+
+      async function fetch() {
+        treeData.value = (await getStudentmanagerMajorGradeClassTree({})) as unknown as TreeItem[];
+        let id = treeData.value[0].id;
+        selectedKeys.value.push(id);
+        // emit('select', id);
+      }
+
+      function handleSelect(_, e: any) {
+        emit('select', { id: e.node.id, type: e.node.treeType });
+      }
+
+      onMounted(() => {
+        fetch();
+      });
+      return { treeData, handleSelect, selectedKeys, t };
+    },
+  });
+</script>

+ 16 - 18
src/views/educational/stdudentBaseManager/index.vue

@@ -5,6 +5,7 @@
       class="w-3/4 xl:w-4/5"
       @register="registerTable"
       :row-selection="{ selectedRowKeys: selectedKeys, onChange: onSelectChange }"
+      :searchInfo="searchInfo"
     >
       <template #toolbar>
         <a-button type="primary" v-auth="'student:add'" @click="handleAdd">新增</a-button>
@@ -16,15 +17,11 @@
           <template #overlay>
             <Menu>
               <Menu.Item>
-                <a-button block type="link" @click="handelDownloadTemplate">
-                  下载模板
-                </a-button>
+                <a-button block type="link" @click="handelDownloadTemplate"> 下载模板 </a-button>
               </Menu.Item>
               <Menu.Item>
                 <Upload :showUploadList="false" :before-upload="beforeUpload">
-                  <a-button block type="link">
-                    导入
-                  </a-button>
+                  <a-button block type="link"> 导入 </a-button>
                 </Upload>
               </Menu.Item>
             </Menu>
@@ -36,9 +33,7 @@
           :before-upload="handelBeforeUpload"
           accept=".rar,.zip"
         >
-          <a-button block type="primary">
-            导入学籍照
-          </a-button>
+          <a-button block type="primary"> 导入学籍照 </a-button>
         </Upload>
         <ExportTool
           v-auth="'student:export'"
@@ -87,7 +82,7 @@
   </PageWrapper>
 </template>
 <script lang="ts" setup>
-  import { computed, createVNode, onMounted, ref } from 'vue';
+  import { computed, createVNode, onMounted, reactive, ref } from 'vue';
 
   import { Modal, Upload, Dropdown, Menu } from 'ant-design-vue';
   import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
@@ -103,7 +98,7 @@
   import { columns, searchFormSchema } from './components/config';
 
   import Icon from '/@/components/Icon/index';
-  import ClassTree from '/@/views/educational/class/components/ClassTree.vue';
+  import ClassTree from '/@/views/educational/class/components/ClassGroup.vue';
   import {
     postStudentmanagerAvatarImport,
     postStudentmanagerImport,
@@ -114,8 +109,13 @@
   import { useLoading } from '/@/components/Loading';
 
   const exportToolRef = ref<ElRef>(null);
+  const searchInfo = reactive<Recordable>({});
 
   const handelExportClick = (type) => {
+    if (searchInfo.treeType !== 4) {
+      createMessage.warning('请选择班级');
+      return;
+    }
     const data: Recordable = {
       conditions: [],
       ids: [],
@@ -125,7 +125,7 @@
       for (const key in formData) {
         data.conditions.push({ keyName: key, keyValue: formData[key], keyType: '1' });
       }
-      data.conditions.push({ keyName: 'classId', keyValue: classId.value, keyType: '1' });
+      data.conditions.push({ keyName: 'classId', keyValue: searchInfo.treeId, keyType: '1' });
     } else {
       const selectKeys = getSelectRowKeys();
       if (selectKeys.length === 0) {
@@ -138,7 +138,6 @@
     exportToolRef.value?.open(data);
   };
 
-  const classId = ref();
   onMounted(async () => {});
   const { notification, createMessage } = useMessage();
   const { t } = useI18n();
@@ -166,7 +165,6 @@
       fieldMapToTime: [],
     },
     beforeFetch: (params) => {
-      params.classId = classId.value;
       return { ...params, FormId: formIdComputedRef.value, PK: 'id' };
     },
     useSearchForm: true,
@@ -199,8 +197,9 @@
   function handleDelete(record: Recordable) {
     deleteList([record.id]);
   }
-  const handleChange = (id) => {
-    classId.value = id;
+  const handleChange = (data) => {
+    searchInfo.treeId = data.id;
+    searchInfo.treeType = data.type;
     reload();
   };
   function deleteList(ids) {
@@ -256,8 +255,7 @@
 
   function handelDownloadTemplate() {
     downloadByUrl({
-      url:
-        'https://zhxy.cqtlzjzx.com/minio/static/resources/%E5%AD%A6%E7%94%9F%E6%A8%A1%E6%9D%BF.xlsx',
+      url: 'https://zhxy.cqtlzjzx.com/minio/static/resources/%E5%AD%A6%E7%94%9F%E6%A8%A1%E6%9D%BF.xlsx',
       fileName: '学生信息模板',
     });
   }

+ 12 - 2
src/views/educational/whiteList/index.vue

@@ -54,6 +54,7 @@
     postWhitelistManagementImport,
   } from '/@/services/apis/WhitelistManagementController';
   import { downloadByUrl } from '/@/utils/file/download';
+  import { jsonToSheetXlsx } from '/@/components/Excel';
 
   const { createConfirm, createMessage } = useMessage();
 
@@ -166,9 +167,18 @@
   const beforeUpload = async (e) => {
     try {
       openFullLoading();
-      await postWhitelistManagementImport({ file: e });
+      const data = await postWhitelistManagementImport({ file: e });
+
+      if (data && data.length > 0) {
+        jsonToSheetXlsx({
+          data,
+          filename: '白名单登记出错记录.xlsx',
+        });
+        createMessage.info('导入数据有误,请查看附件');
+      } else {
+        createMessage.success('导入成功');
+      }
       closeFullLoading();
-      createMessage.success('导入成功');
       reload();
     } catch {
       closeFullLoading();

+ 150 - 0
src/views/notice/components/SelectUser.vue

@@ -0,0 +1,150 @@
+<template>
+  <div @click="handleSelect" class="select-user">
+    <div style="display: flex">
+      <div class="select-user-list">
+        <div
+          class="select-user-list-item"
+          v-for="(item, index) in selectValue.slice(0, 3)"
+          :key="index"
+        >
+          {{ item.relationName }}
+        </div>
+        <div class="select-user-list-item" v-if="selectValue.length > 3">
+          剩余{{ selectValue.length - 3 }}个
+        </div>
+      </div>
+    </div>
+
+    <div class="select-user-add">
+      <span>添加+</span>
+    </div>
+
+    <SelectModal @register="registerForm" @success="handleSuccess" />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ref, watch, unref, PropType } from 'vue';
+  import SelectModal from './selectModal.vue';
+  import { useModal } from '/@/components/Modal';
+  import { Recordable } from 'vite-plugin-mock';
+
+  const [registerForm, { openModal }] = useModal();
+
+  type SelectValueType = {
+    relationId: [string | number]; // 1 部门 2 班级 3 用户
+    relationName: string;
+    relationType: number;
+  };
+
+  const props = defineProps({
+    value: { type: [Array] as PropType<SelectValueType[]> },
+    disabledUser: { type: Boolean, default: false },
+    disabledDept: { type: Boolean, default: false },
+    disabledClass: { type: Boolean, default: false },
+  });
+
+  const selectValue = ref<SelectValueType[]>([]);
+
+  watch(
+    () => props.value,
+    (val) => {
+      if (val) {
+        selectValue.value = val;
+      }
+    },
+    {
+      immediate: true,
+    },
+  );
+
+  const emits = defineEmits(['update:value', 'change']);
+
+  const handleSelect = () => {
+    const data: {
+      drepList: Recordable[];
+      userList: Recordable[];
+      classList: Recordable[];
+    } = {
+      drepList: [],
+      userList: [],
+      classList: [],
+    };
+
+    selectValue.value.forEach((item) => {
+      if (item.relationType === 1) {
+        data.drepList.push({ id: item.relationId, name: item.relationName });
+      }
+      if (item.relationType === 2) {
+        data.classList.push({ id: item.relationId, name: item.relationName });
+      }
+      if (item.relationType === 3) {
+        data.userList.push({ id: item.relationId, name: item.relationName });
+      }
+    });
+
+    openModal(true, {
+      isUpdate: false,
+      data: data,
+      disabledUser: props.disabledUser,
+      disabledDept: props.disabledDept,
+      disabledClass: props.disabledClass,
+    });
+  };
+
+  const handleSuccess = (data) => {
+    const relationValue: Recordable[] = [];
+
+    Object.keys(data).forEach((item, index) => {
+      data[item].forEach((row) => {
+        relationValue.push({
+          relationId: row.id,
+          relationName: row.name,
+          relationType: index + 1,
+        });
+      });
+    });
+
+    emits('update:value', relationValue);
+    emits('change', relationValue);
+  };
+</script>
+
+<style scoped lang="less">
+  .select-user {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    width: 100%;
+    border: 1px solid #d9d9d9;
+    cursor: pointer;
+
+    &-list {
+      display: flex;
+      flex-direction: row;
+      flex-wrap: wrap;
+      padding: 4px 6px;
+
+      &-item {
+        // 禁止文字换行
+        white-space: nowrap;
+        margin-right: 4px;
+        padding: 0 8px;
+        border-radius: 4px;
+        background-color: #f2f2f6;
+        line-height: 22px;
+
+        &:last-child {
+          margin-right: 0;
+        }
+      }
+    }
+
+    &-add {
+      margin-right: 8px;
+      white-space: nowrap;
+      line-height: 22px;
+      padding: 4px 0;
+    }
+  }
+</style>