yangg hai 1 mes
pai
achega
83b49e1101

+ 2 - 2
.env.development

@@ -5,8 +5,8 @@ ENV = 'development'
 #线下:http://192.168.100.187:8083
 # 本地环境接口地址 121.36.251.245
 
-VITE_API_URL = 'http://192.168.100.106:8086'
-#VITE_API_URL = 'http://1.94.168.85:8086'
+#VITE_API_URL = 'http://192.168.100.106:8086'
+VITE_API_URL = 'http://1.94.168.85:8086'
 # VITE_API_URL = 'https://backend.qicai321.com'
 VITE_API_WX_URL='https://api.weixin.qq.com/'
 VITE_API_URL_MINLONG='http://117.185.80.170:7861'

+ 5 - 5
src/views/system/allusers/crud.tsx

@@ -294,26 +294,26 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 								// [学校, 学院, 专业, 年级]
 								(form as any)._college_id = chain[1]?.id;
 								(form as any)._major_id = chain[2]?.id;
-								(form as any)._grade_id = chain[3]?.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 = undefined;
+								(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 = undefined;
+								(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 = 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 = 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) {

+ 109 - 48
src/views/system/studentInfo/crud.tsx

@@ -1,4 +1,4 @@
-import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, dict, UserPageQuery,compute } from '@fast-crud/fast-crud';
+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';
 
@@ -301,31 +301,66 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 					title: '组织架构',
 					type: "dict-cascader",
 					column: {
-						show:false
-					}
-					/* dict: dict({
-						isTree: true,
-						url: "/api/system/organization/tree/",
-						value: 'id',
-						label: 'name'
-					}),
-					form: {
-						component: {
-							filterable: true,
-							clearable: true,
-							placeholder: '请选择组织架构',
-							// props下配置属性跟配置在component下是一样的效果,而el-cascade下也有一个叫props的属性,所以需要配置两层
-							props: { 
-								props: { 
-									checkStrictly: true,
-									value: 'id',
-									label: 'name',
-									children: 'children'
-								} 
+						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 ((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;
 							}
-						},
-						rules: [{ required: false, message: '请选择组织架构' }]
-					} */
+							// 学生模式下,不直接把 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;
+							}
+						}
+					},
+					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:{
 					title: '学院',
@@ -337,14 +372,24 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						show: true,
 						minWidth: 200,
 						formatter: ({ row, value }: { row: any; value: any }) => {
-							// 如果有 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[0]) return
-								const major = row.organization_detail.parent_chain[0]; // 第二项是专业
-								return major.name || major.code || '';
+							// 根据用户类型显示不同内容
+							if(row.user_type !== 0) {
+								// 教师、学院领导:显示学院名称
+								if (row.organization_detail && row.organization_detail.name) {
+									return row.organization_detail.name;
+								}
+								// 否则显示原始值
+								return value || '';
+							} 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 value || row.sub_organization;
 							}
-							// 否则显示原始值
-							return value || row.sub_organization;
 						},
 						/* formatter: ({ row, value }: { row: any; value: any }) => {
 							// 如果有 organization_detail 对象,显示 full_path
@@ -356,9 +401,12 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						}, */
 					},
 					form: {
-						show:false,
-						component: { placeholder: '请填写学院' },
-						rules: [{ required: false, message: '请填写学院' }],
+						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: '' },
@@ -371,13 +419,16 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						show:false
 					},
 					column: {
-						show: true,
+						show: compute(({ row }) => {
+							// 只有学生类型才显示专业列
+							return row && row.user_type === 0;
+						}),
 						minWidth: 120,
 						formatter: ({ row, value }: { row: any; value: any }) => {
 							// 如果有 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[1]) return
-								const major = row.organization_detail.parent_chain[1]; // 第二项是专业
+								if(!row.organization_detail.parent_chain[2]) return
+								const major = row.organization_detail.parent_chain[2]; // 第二项是专业
 								return major.name || major.code || '';
 							}
 							// 否则显示原始值
@@ -385,7 +436,10 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						},
 					},
 					form: {
-						show:false,
+						show: compute(({ form }) => {
+							// 只有当选择了学院时才显示专业选择
+							return form && form.user_type == 0;
+						}),
 						component: { placeholder: '请填写专业' },
 						rules: [{ required: false, message: '请填写专业' }],
 					},
@@ -400,21 +454,25 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						show:false
 					},
 					column: {
-						show: true,
+						show: compute(({ row }) => {
+							// 只有学生类型才显示年级列
+							return row && row.user_type === 0;
+						}),
 						minWidth: 120,
 						formatter: ({ row, value }: { row: any; value: any }) => {
-							// 如果有 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 || '';
+							// 如果有 organization_detail 对象,显示 name
+							if (row.organization_detail && row.organization_detail.name) {
+								return row.organization_detail.name;
 							}
 							// 否则显示原始值
-							return value || row.grade_or_level;
+							return value || row.class_or_group;
 						},
 					},
 					form: {
-					show:false,
+						show: compute(({ form }) => {
+							// 只有当选择了专业时才显示年级选择
+							return form && form.user_type == 0;
+						}),
 						component: { placeholder: '请填写年级' },
 						rules: [{ required: false, message: '请填写年级' }],
 					},
@@ -429,7 +487,10 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						show:false
 					},
 					column: {
-						show: true,
+						show: compute(({ row }) => {
+							// 只有学生类型才显示班级列
+							return row && row.user_type === 0;
+						}),
 						minWidth: 120,
 						formatter: ({ row, value }: { row: any; value: any }) => {
 							// 如果有 organization_detail 对象,显示 name

+ 140 - 6
src/views/system/studentInfo/index.vue

@@ -1,23 +1,104 @@
 <template>
 	<fs-page>
 		<fs-crud ref="crudRef" v-bind="crudBinding">
-			 <template #form_organization_ref="scope">
-				<el-cascader :disabled="scope.mode=='view'" v-model="scope.form.organization_ref" :options="options" :props="props1" :show-all-levels="false" />
-			 </template>
+			<template #form_organization_ref="scope">
+				<template v-if="scope.form.user_type === 0">
+					<div style="display:flex; gap:8px; width:100%">
+						<el-select
+							style="flex:1"
+							:disabled="scope.mode=='view'"
+							v-model="scope.form._college_id"
+							placeholder="请选择学院"
+							filterable
+							clearable
+							@change="() => onCollegeChange(scope)"
+						>
+							<el-option v-for="item in colleges" :key="item.id" :label="item.name" :value="item.id" />
+						</el-select>
+					</div>
+				</template>
+
+				<template v-else-if="scope.form.user_type === 1 || scope.form.user_type === 3">
+					<el-select
+						:disabled="scope.mode=='view'"
+						v-model="scope.form.organization_ref"
+						placeholder="请选择学院"
+						filterable
+						clearable
+					>
+						<el-option
+							v-for="item in colleges"
+							:key="item.id"
+							:label="item.name"
+							:value="item.id"
+						/>
+					</el-select>
+				</template>
+				<template v-else>
+					<el-select
+						:disabled="scope.mode=='view'"
+						v-model="scope.form.organization_ref"
+						placeholder="请选择学院"
+						filterable
+						clearable
+					>
+						<el-option
+							v-for="item in colleges"
+							:key="item.id"
+							:label="item.name"
+							:value="item.id"
+						/>
+					</el-select>
+				</template>
+			</template>
+			<template #form_sub_organization="scope" >
+				<el-select
+					style="flex:1"
+					:disabled="scope.mode=='view' || !scope.form._college_id"
+					v-model="scope.form._major_id"
+					placeholder="请选择专业"
+					filterable
+					clearable
+					@change="() => onMajorChange(scope)"
+				>
+					<el-option v-for="item in getMajors(scope.form._college_id)" :key="item.id" :label="item.name" :value="item.id" />
+				</el-select>
+			</template>
+			<template #form_grade_or_level="scope">
+				<el-select
+					style="flex:1"
+					:disabled="scope.mode=='view' || !scope.form._major_id"
+					v-model="scope.form._grade_id"
+					placeholder="请选择年级"
+					filterable
+					clearable
+					@change="() => onGradeChange(scope)"
+				>
+					<el-option v-for="item in getGrades(scope.form._major_id)" :key="item.id" :label="item.name" :value="item.id" />
+				</el-select>
+			</template>
 		</fs-crud>
 	</fs-page>
 </template>
 
 <script lang="ts" setup name="studentInfo">
 import { useFs } from '@fast-crud/fast-crud';
-import { onMounted,ref } from 'vue';
-import { GetPermission,GetTree } from './api';
+import { onMounted, ref, computed } from 'vue';
+import { GetPermission, GetTree } from './api';
 import { createCrudOptions } from './crud';
 import { handleColumnPermission } from '/@/utils/columnPermission';
 
 const { crudBinding, crudRef, crudExpose, crudOptions, resetCrudOptions } = useFs({ createCrudOptions });
 
-const options=ref<any[]>([])
+const options = ref<any[]>([])
+const colleges = computed(() => {
+  const list = Array.isArray(options.value) ? options.value : []
+  const firstLevel: any[] = []
+  list.forEach((node: any) => {
+    if (Array.isArray(node?.children)) firstLevel.push(...node.children)
+  })
+  return firstLevel.map((node: any) => ({ id: node.id, name: node.name }))
+})
 const props1 = {
   checkStrictly: true,
   label: 'name',
@@ -26,6 +107,50 @@ const props1 = {
   emitPath: false // 只返回选中节点的值,不返回完整路径
 }
 
+function findNodeById(id: any) {
+  if (!id) return undefined
+  const stack: any[] = [...(options.value || [])]
+  while (stack.length) {
+    const node = stack.shift()
+    if (node?.id === id) return node
+    if (Array.isArray(node?.children) && node.children.length) stack.push(...node.children)
+  }
+  return undefined
+}
+
+function getMajors(collegeId: any) {
+  const college = findNodeById(collegeId)
+  return Array.isArray(college?.children) ? college.children : []
+}
+
+function getGrades(majorId: any) {
+  const major = findNodeById(majorId)
+  return Array.isArray(major?.children) ? major.children : []
+}
+
+function onCollegeChange(scope: any) {
+  scope.form._major_id = undefined
+  scope.form._grade_id = undefined
+  if (scope.form.user_type === 0) {
+    scope.form.organization_ref = undefined
+  } else {
+    scope.form.organization_ref = scope.form._college_id
+  }
+}
+
+function onMajorChange(scope: any) {
+  scope.form._grade_id = undefined
+  if (scope.form.user_type === 0) {
+    scope.form.organization_ref = undefined
+  }
+}
+
+function onGradeChange(scope: any) {
+  if (scope.form.user_type === 0) {
+    scope.form.organization_ref = scope.form._grade_id
+  }
+}
+
 const init=()=>{
 	GetTree().then((res:any) => { 
 			console.log('组织架构API响应:', res);
@@ -60,4 +185,13 @@ onMounted(async () => {
 	// 刷新
 	crudExpose.doRefresh();
 });
+
+defineExpose({
+  getMajors,
+  getGrades,
+  onCollegeChange,
+  onMajorChange,
+  onGradeChange,
+  colleges,
+})
 </script>

+ 40 - 4
src/views/system/teacherInfor/crud.tsx

@@ -1,4 +1,4 @@
-import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, dict, UserPageQuery,compute } from '@fast-crud/fast-crud';
+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';
 
@@ -247,16 +247,52 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						rules: [{ required: false, message: '请选择用户类型' }],
 					},
 				},
+				organization_ref:{
+					title: '组织架构',
+					type: "dict-cascader",
+					column: {
+						show: false,
+					},
+					// 教师和学院领导的级联选择
+					valueBuilder({ form, value }) {
+						// 保留后端原始值
+						form.organization_ref = value;
+						// 教师/学院领导:直接以当前组织ID为学院选择
+						const od = (form as any).organization_detail;
+						if (od && od.id) {
+							(form as any).organization_ref = od.id;
+						}
+					},
+					valueResolve({ form, value }) {
+						// 教师/学院领导:提交学院ID
+						(form as any).organization_ref = value ?? (form as any).organization_ref;
+					}
+				},
 				organization:{
 					title: '学院',
 					type: 'input',
+					search:{
+						show:true
+					},
 					column: {
 						show: true,
-						minWidth: 120,
+						minWidth: 200,
+						formatter: ({ row, value }: { row: any; value: any }) => {
+							// 教师、学院领导:显示学院名称
+							if (row.organization_detail && row.organization_detail.name) {
+								return row.organization_detail.name;
+							}
+							// 否则显示原始值
+							return value || '';
+						},
 					},
 					form: {
-						component: { placeholder: '请填写学院' },
-						rules: [{ required: false, message: '请填写学院' }],
+						show: compute((row:any)=>{
+							// 教师、学院领导:在表单中显示"学院"
+							return row.user_type === 1 || row.user_type === 3
+						}),
+						component: { placeholder: '请选择学院' },
+						rules: [{ required: false, message: '请选择学院' }],
 					},
 					viewForm:{
 						component: { placeholder: '' },

+ 55 - 2
src/views/system/teacherInfor/index.vue

@@ -1,20 +1,73 @@
 <template>
 	<fs-page>
-		<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
+		<fs-crud ref="crudRef" v-bind="crudBinding">
+			<template #form_organization_ref="scope">
+				<el-select
+					:disabled="scope.mode=='view'"
+					v-model="scope.form.organization_ref"
+					placeholder="请选择学院"
+					filterable
+					clearable
+				>
+					<el-option
+						v-for="item in colleges"
+						:key="item.id"
+						:label="item.name"
+						:value="item.id"
+					/>
+				</el-select>
+			</template>
+		</fs-crud>
 	</fs-page>
 </template>
 
 <script lang="ts" setup name="teacherInfor">
 import { useFs } from '@fast-crud/fast-crud';
-import { onMounted } from 'vue';
+import { onMounted, ref, computed } from 'vue';
 import { GetPermission } from './api';
 import { createCrudOptions } from './crud';
 import { handleColumnPermission } from '/@/utils/columnPermission';
+import { GetTree } from '../studentInfo/api';
 
 const { crudBinding, crudRef, crudExpose, crudOptions, resetCrudOptions } = useFs({ createCrudOptions });
 
+const options = ref<any[]>([])
+const colleges = computed(() => {
+  const list = Array.isArray(options.value) ? options.value : []
+  const firstLevel: any[] = []
+  list.forEach((node: any) => {
+    if (Array.isArray(node?.children)) firstLevel.push(...node.children)
+  })
+  return firstLevel.map((node: any) => ({ id: node.id, name: node.name }))
+})
+
+const init = () => {
+	GetTree().then((res:any) => { 
+		console.log('组织架构API响应:', res);
+		// 兼容多种响应格式
+		if(res && (res.code === 200 || res.code === 2000 || !res.code)){
+			// 尝试多种数据结构
+			const data = res.data || res.results || res;
+			if (Array.isArray(data)) {
+				options.value = data;
+				console.log('成功赋值组织架构数据:', options.value);
+			} else {
+				console.warn('返回的数据不是数组格式:', data);
+				options.value = [];
+			}
+		} else {
+			console.error('获取组织架构数据失败:', res);
+			options.value = [];
+		}
+	}).catch((error: any) => {
+		console.error('请求组织架构数据失败:', error);
+		options.value = [];
+	});
+}
+
 // 页面打开后获取列表数据
 onMounted(async () => {
+	init()
 	// 设置列权限
 	const newOptions = await handleColumnPermission(GetPermission, crudOptions);
 	//重置crudBinding