diff --git a/apps/admin/src/pages/IntegrationConfig/index.tsx b/apps/admin/src/pages/IntegrationConfig/index.tsx index ea8b562..fa09073 100644 --- a/apps/admin/src/pages/IntegrationConfig/index.tsx +++ b/apps/admin/src/pages/IntegrationConfig/index.tsx @@ -1,7 +1,7 @@ -import React, { useEffect, useState, useMemo } from 'react'; +import React, { useEffect, useState, useMemo, useCallback } from 'react'; import { Card, Form, Input, Button, Space, message, Spin, Switch, Alert, Descriptions, Tag, - Tabs, Drawer, Tree, Checkbox, Select, TreeSelect, + Tabs, Drawer, Tree, Checkbox, Select, TreeSelect, Modal, DatePicker, InputNumber, } from 'antd'; import { SaveOutlined, ApiOutlined, CheckCircleOutlined, CloseCircleOutlined, @@ -22,7 +22,7 @@ interface DingOrgTreeNodeExt { name: string; parentId: number; children: DingOrgTreeNodeExt[]; - users: Array<{ userid: string; name: string; mobile: string }>; + users: Array<{ userid: string; name: string; mobile: string; deptIds: number[] }>; } interface RoleItem { @@ -53,6 +53,17 @@ interface ImportUsersResponse { skipped: number; } +interface ClassMarkForm { + deptId: number; + name: string; + code: string; + classType: string; + startDate?: string; + endDate?: string; + maxStudents?: number; + notes?: string; +} + const UserTreeNode: React.FC<{ u: { userid: string; name: string; mobile: string }; isTeacher: boolean; @@ -99,6 +110,10 @@ const IntegrationConfigPage: React.FC = () => { const [roles, setRoles] = useState>([]); const [defaultTeacherRoleId, setDefaultTeacherRoleId] = useState(null); const [deptPickerTree, setDeptPickerTree] = useState }>>([]); + const [classMarks, setClassMarks] = useState>({}); + const [classModalOpen, setClassModalOpen] = useState(false); + const [classModalDept, setClassModalDept] = useState<{ id: number; name: string } | null>(null); + const [classForm] = Form.useForm(); const fetchConfig = async () => { setLoading(true); @@ -206,6 +221,37 @@ const IntegrationConfigPage: React.FC = () => { } }; + const openClassModal = (deptId: number, deptName: string) => { + const existing = classMarks[deptId]; + if (existing) { + classForm.setFieldsValue(existing); + } else { + classForm.setFieldsValue({ + deptId, + name: deptName, + code: '', + classType: 'culture', + }); + } + setClassModalDept({ id: deptId, name: deptName }); + setClassModalOpen(true); + }; + + const handleClassModalOk = async () => { + const values = await classForm.validateFields(); + setClassMarks((prev) => ({ + ...prev, + [values.deptId]: values, + })); + setClassModalOpen(false); + setClassModalDept(null); + }; + + const handleClassModalCancel = () => { + setClassModalOpen(false); + setClassModalDept(null); + }; + const handleImportUsers = async () => { setImporting(true); try { @@ -213,24 +259,34 @@ const IntegrationConfigPage: React.FC = () => { dingUserId: string; name: string; mobile: string; + deptIds: number[]; }> = []; const flatten = (nodes: DingOrgTreeNodeExt[]) => { for (const node of nodes) { - allUsers.push(...node.users); + allUsers.push( + ...node.users.map((u) => ({ + dingUserId: u.userid, + name: u.name, + mobile: u.mobile, + deptIds: u.deptIds || [], + })), + ); flatten(node.children); } }; flatten(orgTree); const payload = { + classes: Object.values(classMarks), users: allUsers.map((u) => ({ - dingUserId: u.userid, + dingUserId: u.dingUserId, name: u.name, mobile: u.mobile, - roleId: teacherChecks[u.userid] - ? (teacherRoles[u.userid] || defaultTeacherRoleId) + roleId: teacherChecks[u.dingUserId] + ? (teacherRoles[u.dingUserId] || defaultTeacherRoleId) : null, + dingDeptIds: u.deptIds, })), }; @@ -248,9 +304,31 @@ const IntegrationConfigPage: React.FC = () => { } }; - const buildTreeData = (nodes: DingOrgTreeNodeExt[]): DataNode[] => { + const buildTreeData = useCallback((nodes: DingOrgTreeNodeExt[]): DataNode[] => { return nodes.map((node) => ({ - title: node.name, + title: ( + + {node.name} + {classMarks[node.id] ? ( + openClassModal(node.id, node.name)} + > + 班级: {classMarks[node.id].name} [已标记] + + ) : ( + + )} + + ), key: `dept-${node.id}`, children: [ ...buildTreeData(node.children), @@ -258,7 +336,7 @@ const IntegrationConfigPage: React.FC = () => { title: ( { setTeacherChecks((prev) => ({ @@ -286,9 +364,9 @@ const IntegrationConfigPage: React.FC = () => { })), ], })); - }; + }, [classMarks, teacherChecks, teacherRoles, defaultTeacherRoleId, roles]); - const treeData = useMemo(() => buildTreeData(orgTree), [orgTree, teacherChecks, teacherRoles, defaultTeacherRoleId, roles]); + const treeData = useMemo(() => buildTreeData(orgTree), [orgTree, buildTreeData]); const syncTabItems = config ? [ @@ -323,15 +401,55 @@ const IntegrationConfigPage: React.FC = () => { 获取组织架构 + +
+ + + + + + + + +