import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, dict, UserPageQuery, compute, InfoReq, EditReq, DelReq } from '@fast-crud/fast-crud'; import * as api from './api'; import { auth } from '/@/utils/authFunction'; import { ElMessage } from 'element-plus'; import { ref } from 'vue'; // 全局变量存储组织树数据,供 valueBuilder 使用 let organizationTreeData: any[] = []; // 初始化时获取组织树数据 const initOrganizationTree = async () => { try { const res = await api.GetTree(); if (res && (res.code === 200 || res.code === 2000 || !res.code)) { const data = res.data || res.results || res; if (Array.isArray(data)) { organizationTreeData = data; } } } catch (error) { console.error('获取组织架构数据失败:', error); organizationTreeData = []; } }; // 通过名称在组织树中查找节点的辅助函数(在整个树中递归搜索) function findNodeByName(name: string, tree: any[], depth: number = 0, maxDepth: number = 10): any { if (!name || !tree || depth > maxDepth) return undefined; for (const node of tree) { if (node.name === name) { return node; } if (Array.isArray(node.children)) { const found = findNodeByName(name, node.children, depth + 1, maxDepth); if (found) return found; } } return undefined; } // 在学院层级中查找节点(跳过学校层) function findNodeInColleges(name: string, tree: any[]): any { if (!name || !tree) return undefined; // 遍历所有学校 for (const school of tree) { if (Array.isArray(school.children)) { // 在每个学校的子节点(学院)中查找 for (const college of school.children) { if (college.name === name) { return college; } } } } return undefined; } export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { // 初始化组织树数据(异步执行,不阻塞返回) initOrganizationTree(); const pageRequest = async (query: UserPageQuery) => { return await api.GetList(query); }; const getDetail = async ({ row }: InfoReq) => { return await api.GetObj(row.id); }; const editRequest = async ({ form, row }: EditReq) => { form.id = row.id; return await api.UpdateObj(form); }; const delRequest = async ({ row }: DelReq) => { return await api.DelObj(row.id); }; const addRequest = async ({ form }: AddReq) => { return await api.AddObj(form); }; const selectedIds = ref([]); const onSelectionChange = (changed: any[]) => { console.log("selection", changed); selectedIds.value = changed.map((item: any) => item.id); }; (crudExpose as any).selectedIds = selectedIds; /** * 懒加载 * @param row * @returns {Promise} */ // const loadContentMethod = (tree: any, treeNode: any, resolve: Function) => { // pageRequest({ pcode: tree.code }).then((res: APIResponseData) => { // resolve(res.data); // }); // }; return { crudOptions: { request: { pageRequest, editRequest, delRequest, addRequest, getDetail, }, toolbar:{ show:false, }, actionbar: { buttons: { add: { show:true,// auth('area:Create'), }, /* batchExport: { text: '批量导出', type: 'primary', show: auth('area:Export'), click: ({ getSelectedRows }: any) => { const selectedRows = getSelectedRows(); if (selectedRows.length === 0) { ElMessage.warning('请先选择要导出的用户'); return; } // 触发批量导出 - 通过全局事件总线 window.dispatchEvent(new CustomEvent('batch-export', { detail: selectedRows })); }, }, */ }, }, form:{ wrapper: { buttons: { ok:{ text:'提交', show:compute((row) => { return row.mode !=='view' }) } } } }, pagination: { show: true, }, table: { rowSelection: { show: true, multiple: true, }, onSelectionChange, }, columns: { $checked: { title: "选择", form: { show: false }, column: { type: "selection", align: "center", width: "55px", columnSetDisabled: true, //禁止在列设置中选择 selectable(row: any, index: any) { // return row.id !== 1; //设置第一行不允许选择 return row.id; } } }, _index: { title: '序号', form: { show: false }, column: { type: 'index', align: 'center', width: '70px', columnSetDisabled: true, //禁止在列设置中选择 }, }, search:{ title: '关键字搜索', search: { show: true,type: 'input', }, type: 'input', form: { component: { placeholder: '请输入' }, show:false}, column: {show:false} }, user_code:{ title:'学号/工号', type:'input', column: { minWidth: 120, }, form: { component: { placeholder: '请填写学号/工号' }, rules: [{ required: true, message: '请填写学号/工号' }], }, viewForm:{ component: { placeholder: '' }, } }, username:{ title:'用户名', type:'input', column: { show: false, minWidth: 120, }, form: { component: { placeholder: '请填写用户名' }, rules: [{ required: true, message: '请填写用户名' }], }, viewForm:{ component: { placeholder: '' }, } }, name: { title: '姓名', search: { show: false, }, treeNode: true, type: 'input', column: { minWidth: 120, }, form: { rules: [ // 表单校验规则 { required: true, message: '姓名必填项' }, ], component: { placeholder: '请输入姓名', }, }, }, password:{ title: '密码', type: 'input', column: { minWidth: 120, show: false, }, form: { component: { placeholder: '请填写密码', type: 'password', showPassword: true }, }, addForm:{ component:{ placeholder: '请填写密码', type: 'password', showPassword: true } , rules: [ { required: true, message: '请填写密码' }, { validator: (rule: any, value: string, callback: Function) => { // 新增时密码必填,如果为空则报错 if (!value || value.trim() === '') { callback(new Error('请填写密码')); return; } // 密码格式校验 const minLength = 8; const hasLetter = /[a-zA-Z]/.test(value); const hasNumber = /\d/.test(value); const hasSpecialChar = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(value); if (value.length < minLength) { callback(new Error('密码长度不能少于8位')); return; } if (!hasLetter) { callback(new Error('密码必须包含字母')); return; } if (!hasNumber) { callback(new Error('密码必须包含数字')); return; } if (!hasSpecialChar) { callback(new Error('密码必须包含特殊符号')); return; } callback(); }, trigger: 'blur' } ], }, editForm:{ show:true, component: { placeholder: '留空则不修改密码', type: 'password', showPassword: true }, rules: [ { validator: (rule: any, value: string, callback: Function) => { // 如果密码为空,则通过验证(非必填) if (!value || value.trim() === '') { callback(); return; } // 如果填写了密码,则进行格式校验 const minLength = 8; const hasLetter = /[a-zA-Z]/.test(value); const hasNumber = /\d/.test(value); const hasSpecialChar = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(value); if (value.length < minLength) { callback(new Error('密码长度不能少于8位')); return; } if (!hasLetter) { callback(new Error('密码必须包含字母')); return; } if (!hasNumber) { callback(new Error('密码必须包含数字')); return; } if (!hasSpecialChar) { callback(new Error('密码必须包含特殊符号')); return; } callback(); }, trigger: 'blur' } ] }, viewForm:{ show:false, } }, email:{ title: '邮箱', type: 'input', column: { show: false, minWidth: 120, }, form: { component: { placeholder: '请填写邮箱' }, rules: [{ required: false, message: '请填写邮箱' }], }, viewForm:{ component: { placeholder: '' }, } }, mobile:{ title: '手机号', type: 'input', column: { show: false, minWidth: 120, }, form: { component: { placeholder: '请填写手机号' }, rules: [{ required: true, message: '请填写手机号' }], }, viewForm:{ component: { placeholder: '' }, } }, gender:{ title: '性别', type: 'dict-select', column: { show: true, minWidth: 120, }, dict: dict({ data: [ { label: '男', value: 1 }, { label: '女', value: 2 }, ], }), form: { component: { placeholder: '请选择性别' }, rules: [{ required: true, message: '请选择性别' }], }, }, user_type:{ title: '用户类型', type: 'dict-select', search:{ show:false }, column: { minWidth: 120, }, dict: dict({ data: [ { label: '学生', value: 0 }, { label: '教师', value: 1 }, { label: '外部用户', value: 2 }, { label: '学院领导', value: 3 }, ], }), form: { component: { placeholder: '请选择用户类型' }, rules: [{ required: false, message: '请选择用户类型' }], }, }, organization_ref:{ title: '学院',//'组织架构', type: "dict-cascader", column: { show: false, }, // 学生类型表单的级联选择依赖于 index.vue 中的 _college_id/_major_id/_grade_id // 这里通过 valueBuilder 在打开编辑/查看表单时,将 organization_detail.parent_chain 回显到这三个临时字段 // 同时通过 valueResolve 在提交时将最终选择写回 organization_ref(学生为年级ID,其它类型为学院ID) valueBuilder({ form, value }) { // 保留后端原始值 form.organization_ref = value; // 仅学生类型需要拆分层级进行回显 const od = (form as any).organization_detail; // 如果组织树数据未加载,尝试加载(异步,不阻塞) if (organizationTreeData.length === 0) { initOrganizationTree(); } if ((form as any).user_type === 0 && od && Array.isArray(od.parent_chain)) { // 期望 parent_chain: [学校, 学院, 专业, 年级] 或至少包含 [学院, 专业, 年级] const chain = od.parent_chain; // 按长度精确映射,避免把学校ID当作学院ID if (chain.length >= 4) { // [学校, 学院, 专业, 年级] (form as any)._college_id = chain[1]?.id; (form as any)._major_id = chain[2]?.id; (form as any)._grade_id = od?.id; } else if (chain.length === 3) { // 常见后端:含学校,无年级 -> [学校, 学院, 专业] (form as any)._college_id = chain[1]?.id; (form as any)._major_id = chain[2]?.id; (form as any)._grade_id = od?.id; } else if (chain.length === 2) { // [学院, 专业] (form as any)._college_id = chain[0]?.id; (form as any)._major_id = chain[1]?.id; (form as any)._grade_id = od?.id; } else if (chain.length === 1) { // [学院] (form as any)._college_id = chain[0]?.id; (form as any)._major_id = undefined; (form as any)._grade_id = od?.id; } else { (form as any)._college_id = undefined; (form as any)._major_id = undefined; (form as any)._grade_id = od?.id; } // 学生模式下,不直接把 organization_ref 设为学院,保持由 index.vue 的联动逻辑在变更时再写入 } else if ((form as any).user_type === 1 || (form as any).user_type === 3) { // 教师/学院领导:直接以当前组织ID为学院选择 if (od && od.id) { (form as any).organization_ref = od.id; } } else if ((form as any).user_type === 0 && !od && organizationTreeData.length > 0) { // 学生类型且 organization_detail 为 null 时,通过文本名称查找并回显 const orgName = (form as any).organization; // 学院名称 const subOrgName = (form as any).sub_organization; // 专业名称 const gradeName = (form as any).grade_or_level || (form as any).class_or_group; // 班级/年级名称 // 查找学院节点(在学院层级查找) if (orgName) { const collegeNode = findNodeInColleges(orgName, organizationTreeData); if (collegeNode) { (form as any)._college_id = collegeNode.id; // 查找专业节点(在学院的直接子节点中查找) if (subOrgName && Array.isArray(collegeNode.children)) { const majorNode = collegeNode.children.find((child: any) => child.name === subOrgName); if (majorNode) { (form as any)._major_id = majorNode.id; // 查找班级/年级节点(在专业的直接子节点中查找) if (gradeName && Array.isArray(majorNode.children)) { const gradeNode = majorNode.children.find((child: any) => child.name === gradeName); if (gradeNode) { (form as any)._grade_id = gradeNode.id; (form as any).organization_ref = gradeNode.id; } } } } } } } }, valueResolve({ form, value }) { // 非学生:提交学院ID if ((form as any).user_type !== 0) { (form as any).organization_ref = value ?? (form as any).organization_ref; return; } // 学生:当选择了年级,则以年级ID为最终 organization_ref if ((form as any)._grade_id) { (form as any).organization_ref = (form as any)._grade_id; return; } // 兜底:若仅选择到专业或学院,不改变已有值(让上层联动 onGradeChange 来写入) } }, /* 组织架构*/ organization_detail:{ title: '组织架构', type: 'dict-cascader', column: { show: false, minWidth: 200, formatter: ({ row, value }: { row: any; value: any }) => { // 如果有 organization_detail 对象,显示 full_path if (row.organization_detail && row.organization_detail.full_path) { return row.organization_detail.full_path; } // 否则显示原始值 return value || ''; }, }, form: { show:false, component: { placeholder: '请填写组织架构' }, rules: [{ required: false, message: '请填写组织架构' }], }, viewForm:{ component: { placeholder: '' }, } }, organization:{ title: '学院', type: 'input', search:{ show:false }, column: { show: true, minWidth: 200, formatter: ({ row, value }: { row: any; value: any }) => { const isValidValue = (val: any) => { return val !== null && val !== undefined && val !== '' && val !== 'nan' && !Number.isNaN(val); }; // 如果有 organization_detail 对象,显示 parent_chain 的第二项(专业) if(row.user_type !==0) { if (row.organization_detail && row.organization_detail.name) { return row.organization_detail.name; } // 否则显示原始值 return isValidValue(value) ? value : (isValidValue(row.class_or_group) ? row.class_or_group : ''); }else{ if (row.organization_detail && row.organization_detail.parent_chain && row.organization_detail.parent_chain.length >= 2) { if(!row.organization_detail.parent_chain[1]) return const major = row.organization_detail.parent_chain[1]; // 第二项是专业 return major.name || major.code || ''; } // 否则显示原始值 return isValidValue(value) ? value : (isValidValue(row.sub_organization) ? row.sub_organization : ''); } }, /* formatter: ({ row, value }: { row: any; value: any }) => { // 如果有 organization_detail 对象,显示 full_path if (row.organization_detail && row.organization_detail.full_path) { return row.organization_detail.full_path; } // 否则显示原始值 return value || ''; }, */ }, form: { show: compute((row:any)=>{ // 学生、教师、学院领导:在表单中显示“学院” return row.user_type === 0 || row.user_type === 1 || row.user_type === 3 }), component: { placeholder: '请选择学院' }, rules: [{ required: false, message: '请选择学院' }], }, viewForm:{ component: { placeholder: '' }, } }, sub_organization:{ title: '专业', type: 'input', search:{ show:false }, column: { show: true, minWidth: 120, formatter: ({ row, value }: { row: any; value: any }) => { const isValidValue = (val: any) => { return val !== null && val !== undefined && val !== '' && val !== 'nan' && !Number.isNaN(val); }; // 如果有 organization_detail 对象,显示 parent_chain 的第二项(专业) if (row.organization_detail && row.organization_detail.parent_chain && row.organization_detail.parent_chain.length >= 2) { if(!row.organization_detail.parent_chain[2]) return const major = row.organization_detail.parent_chain[2]; // 第二项是专业 return major.name || major.code || ''; } // 否则显示原始值 return isValidValue(value) ? value : (isValidValue(row.sub_organization) ? row.sub_organization : ''); }, }, form: { show:compute(({ form }) => { // 只有当选择了胜任力标签时才显示配置 return form && form.user_type == 0; }), component: { placeholder: '请填写专业' }, rules: [{ required: false, message: '请填写专业' }], }, /* viewForm:{ component: { placeholder: '' }, } */ }, grade_or_level:{ title: '班级', type: 'input', search:{ show:false }, column: { show: true, minWidth: 120, formatter: ({ row, value }: { row: any; value: any }) => { const isValidValue = (val: any) => { return val !== null && val !== undefined && val !== '' && val !== 'nan' && !Number.isNaN(val); }; // 如果有 organization_detail 对象,显示 parent_chain 的第二项(专业) /* if (row.organization_detail && row.organization_detail.parent_chain && row.organization_detail.parent_chain.length >= 2) { if(!row.organization_detail.parent_chain[2]) return const major = row.organization_detail.parent_chain[2]; // 第二项是专业 return major.name || major.code || ''; } // 否则显示原始值 return value || row.grade_or_level; */ if(row.user_type !==0) return if (row.organization_detail && row.organization_detail.name) { return row.organization_detail.name; } // 否则显示原始值 return isValidValue(value) ? value : (isValidValue(row.class_or_group) ? row.class_or_group : ''); }, }, form: { show:compute(({ form }) => { // 只有当选择了胜任力标签时才显示配置 return form && form.user_type == 0; }), /* show: compute((row:any)=> row.user_type === 0), */ component: { placeholder: '请选择年级' }, rules: [{ required: false, message: '请选择年级' }], }, viewForm:{ component: { placeholder: '' }, } }, class_or_group:{ title: '班级', type: 'input', search:{ show:false }, column: { show: false, minWidth: 120, formatter: ({ row, value }: { row: any; value: any }) => { // 如果有 organization_detail 对象,显示 name if (row.organization_detail && row.organization_detail.name) { return row.organization_detail.name; } // 否则显示原始值 return value || row.class_or_group; }, }, form: { show:false, component: { placeholder: '请填写班级' }, rules: [{ required: false, message: '请填写班级' }], }, viewForm:{ component: { placeholder: '' }, } }, }, }, }; };