diff --git a/apps/admin/src/App.tsx b/apps/admin/src/App.tsx
index eed9a23..a813b9a 100644
--- a/apps/admin/src/App.tsx
+++ b/apps/admin/src/App.tsx
@@ -28,7 +28,6 @@ import PermissionsPage from './pages/Permissions';
import AttendancePage from './pages/Attendance';
import TeacherWorkspacePage from './pages/TeacherWorkspace';
import NotificationsPage from './pages/Notifications';
-import DepartmentsPage from './pages/Departments';
import IntegrationConfigPage from './pages/IntegrationConfig';
import PermissionRoute from './components/PermissionRoute';
@@ -255,14 +254,6 @@ const App: React.FC = () => {
} />
-
-
-
- }
- />
{
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
- const campusId = localStorage.getItem('currentCampusId');
- if (campusId) {
- config.headers['X-Campus-Id'] = campusId;
- }
return config;
});
diff --git a/apps/admin/src/components/CampusSwitcher.tsx b/apps/admin/src/components/CampusSwitcher.tsx
deleted file mode 100644
index 9104a70..0000000
--- a/apps/admin/src/components/CampusSwitcher.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import React from 'react';
-import { Select, Typography } from 'antd';
-import { EnvironmentOutlined } from '@ant-design/icons';
-import { useCampus } from '../hooks/useCampus';
-
-const CampusSwitcher: React.FC = () => {
- const { campuses, currentId, switchCampus, loading } = useCampus();
-
- if (campuses.length <= 1) {
- return (
-
-
- {campuses[0]?.name || '主校区'}
-
- );
- }
-
- const options = [
- ...campuses.map((c) => ({ value: String(c.id), label: c.name })),
- { value: '', label: '全部校区' },
- ];
-
- return (
-
- );
-};
-
-export default CampusSwitcher;
diff --git a/apps/admin/src/components/StudentProfileContent/index.tsx b/apps/admin/src/components/StudentProfileContent/index.tsx
index 8e19b29..a3b6c76 100644
--- a/apps/admin/src/components/StudentProfileContent/index.tsx
+++ b/apps/admin/src/components/StudentProfileContent/index.tsx
@@ -53,7 +53,6 @@ interface ProfileData {
targetMajor?: string;
subjectDirection?: string;
grade?: string;
- campusLocation?: string;
profileDate?: string;
notes?: string;
}
@@ -220,7 +219,6 @@ const ProfileTab: React.FC<{ data: ProfileData | null; studentId: number; onRefr
targetMajor: data?.targetMajor ?? undefined,
subjectDirection: data?.subjectDirection ?? undefined,
grade: data?.grade ?? undefined,
- campusLocation: data?.campusLocation ?? undefined,
profileDate: data?.profileDate ? dayjs(data.profileDate) : undefined,
notes: data?.notes ?? undefined,
}}
@@ -238,9 +236,6 @@ const ProfileTab: React.FC<{ data: ProfileData | null; studentId: number; onRefr
-
-
-
@@ -903,9 +898,6 @@ const StudentProfileContent: React.FC = ({
{profile?.subjectDirection && (
{profile.subjectDirection}
)}
- {profile?.campusLocation && (
- {profile.campusLocation}
- )}
([]);
- const [currentId, setCurrentId] = useState(
- () => localStorage.getItem('currentCampusId') || ''
- );
- const [loading, setLoading] = useState(true);
-
- const fetchCampuses = useCallback(async () => {
- try {
- const data = await api.get('/departments') as unknown as Department[];
- const campusList = data.filter((d) => d.type === 'campus');
- setCampuses(campusList);
- if (!currentId && campusList.length > 0) {
- setCurrentId(String(campusList[0].id));
- localStorage.setItem('currentCampusId', String(campusList[0].id));
- }
- } catch { /* ignore */ }
- finally { setLoading(false); }
- }, [currentId]);
-
- useEffect(() => { fetchCampuses(); }, []);
-
- const switchCampus = useCallback((id: string) => {
- setCurrentId(id);
- localStorage.setItem('currentCampusId', id);
- window.dispatchEvent(new CustomEvent('campus-changed', { detail: id }));
- }, []);
-
- return { campuses, currentId, switchCampus, loading };
-}
diff --git a/apps/admin/src/layouts/MainLayout.tsx b/apps/admin/src/layouts/MainLayout.tsx
index 249763f..a057799 100644
--- a/apps/admin/src/layouts/MainLayout.tsx
+++ b/apps/admin/src/layouts/MainLayout.tsx
@@ -29,7 +29,6 @@ import {
} from '@ant-design/icons';
import { usePermission } from '../hooks/usePermission';
import NotificationBell from '../components/NotificationBell';
-import CampusSwitcher from '../components/CampusSwitcher';
const { Header, Sider, Content } = Layout;
@@ -109,7 +108,6 @@ const allMenuItems: MenuItemType[] = [
label: '系统管理',
permission: 'log:view',
children: [
- { key: '/departments', icon: , label: '校区/部门', permission: 'department:view' },
{ key: '/notifications', icon: , label: '通知中心', permission: 'notification:view' },
{ key: '/operation-logs', icon: , label: '操作日志', permission: 'log:view' },
{ key: '/roles', icon: , label: '角色管理', permission: 'role:view' },
@@ -289,7 +287,6 @@ const MainLayout: React.FC = () => {
}
onClick={() => (isMobile || isTablet ? setDrawerOpen(true) : setCollapsed(!collapsed))}
/>
- {isDesktop && }
= {
- campus: '校区',
- department: '部门',
-};
-
-const TYPE_COLORS: Record = {
- campus: 'blue',
- department: 'green',
-};
-
-const DepartmentsPage: React.FC = () => {
- const [treeData, setTreeData] = useState([]);
- const [flatDepts, setFlatDepts] = useState([]);
- const [selectedId, setSelectedId] = useState(null);
- const [selectedDept, setSelectedDept] = useState(null);
- const [members, setMembers] = useState([]);
- const [membersLoading, setMembersLoading] = useState(false);
- const [treeLoading, setTreeLoading] = useState(false);
- const [modalOpen, setModalOpen] = useState(false);
- const [editing, setEditing] = useState(null);
- const [form] = Form.useForm();
- const [saving, setSaving] = useState(false);
-
- const fetchTree = useCallback(async () => {
- setTreeLoading(true);
- try {
- const data = await api.get('/departments/tree') as unknown as DepartmentItem[];
- const flat = await api.get('/departments') as unknown as DepartmentItem[];
- setFlatDepts(flat);
- setTreeData(data.map(departmentToTreeNode));
- } catch {
- message.error('加载部门树失败');
- } finally {
- setTreeLoading(false);
- }
- }, []);
-
- useEffect(() => {
- fetchTree();
- }, [fetchTree]);
-
- const fetchDetail = useCallback(async (id: number) => {
- try {
- const dept = await api.get(`/departments/${id}`) as unknown as DepartmentItem;
- setSelectedDept(dept);
- setMembersLoading(true);
- const users = await api.get(`/departments/${id}/users`) as unknown as DeptMember[];
- setMembers(users);
- } catch {
- message.error('加载部门详情失败');
- } finally {
- setMembersLoading(false);
- }
- }, []);
-
- const handleSelect = useCallback(
- (keys: React.Key[]) => {
- if (keys.length === 0) {
- setSelectedId(null);
- setSelectedDept(null);
- setMembers([]);
- return;
- }
- const id = String(keys[0]);
- setSelectedId(id);
- fetchDetail(Number(id));
- },
- [fetchDetail],
- );
-
- const handleAdd = useCallback(() => {
- setEditing(null);
- form.resetFields();
- if (selectedId) {
- form.setFieldsValue({ parentId: Number(selectedId) });
- }
- setModalOpen(true);
- }, [selectedId, form]);
-
- const handleAddChild = useCallback(() => {
- if (!selectedDept) return;
- setEditing(null);
- form.resetFields();
- form.setFieldsValue({ parentId: selectedDept.id });
- setModalOpen(true);
- }, [selectedDept, form]);
-
- const handleEdit = useCallback(() => {
- if (!selectedDept) return;
- setEditing(selectedDept);
- form.setFieldsValue({
- name: selectedDept.name,
- parentId: selectedDept.parentId,
- type: selectedDept.type,
- sortOrder: selectedDept.sortOrder,
- });
- setModalOpen(true);
- }, [selectedDept, form]);
-
- const handleSubmit = useCallback(async () => {
- setSaving(true);
- try {
- const values = await form.validateFields();
- const payload = {
- name: values.name,
- parentId: values.parentId || null,
- type: values.type || 'department',
- sortOrder: values.sortOrder ?? 0,
- };
- if (editing) {
- await api.put(`/departments/${editing.id}`, payload);
- message.success('更新成功');
- } else {
- await api.post('/departments', payload);
- message.success('创建成功');
- }
- setModalOpen(false);
- await fetchTree();
- if (editing && selectedId) {
- fetchDetail(editing.id);
- }
- } catch (err: unknown) {
- if (
- err &&
- typeof err === 'object' &&
- 'message' in err &&
- typeof (err as { message: string }).message === 'string'
- ) {
- message.error((err as { message: string }).message);
- }
- // form validation error falls through silently
- } finally {
- setSaving(false);
- }
- }, [editing, fetchTree, fetchDetail, selectedId, form]);
-
- const handleDelete = useCallback(async () => {
- if (!selectedDept) return;
- try {
- await api.delete(`/departments/${selectedDept.id}`);
- message.success('删除成功');
- setSelectedId(null);
- setSelectedDept(null);
- setMembers([]);
- await fetchTree();
- } catch (err: unknown) {
- const msg =
- err && typeof err === 'object' && 'message' in err
- ? (err as { message: string }).message
- : '删除失败';
- message.error(msg);
- }
- }, [selectedDept, fetchTree]);
-
- const memberColumns = [
- { title: 'ID', dataIndex: 'userId', key: 'userId', width: 60 },
- { title: '用户名', dataIndex: ['user', 'username'], key: 'username', width: 120 },
- { title: '姓名', dataIndex: ['user', 'name'], key: 'name', width: 120 },
- {
- title: '默认部门',
- dataIndex: 'isDefault',
- key: 'isDefault',
- width: 80,
- render: (v: boolean) => (v ? 是 : null),
- },
- ];
-
- return (
-
-
-
组织架构管理
-
-
- }
- onClick={handleAdd}
- >
- 新增
-
-
-
-
-
-
-
- {treeLoading ? (
-
- ) : (
-
- )}
-
-
-
-
- {selectedDept ? (
-
-
- {selectedDept.name}
-
- {TYPE_LABELS[selectedDept.type] || selectedDept.type}
-
-
- }
- extra={
-
- }
- onClick={handleEdit}
- >
- 编辑
-
- }
- onClick={handleAddChild}
- >
- 新增子部门
-
- 0
- ? '该部门下存在子部门,可能无法删除'
- : undefined}
- onConfirm={handleDelete}
- okText="确定"
- cancelText="取消"
- >
- }
- disabled={
- selectedDept.children && selectedDept.children.length > 0
- }
- >
- 删除
-
-
-
- }
- >
-
- {selectedDept.id}
-
-
- {TYPE_LABELS[selectedDept.type] || selectedDept.type}
-
-
-
- {selectedDept.sortOrder}
-
-
- {selectedDept.parentId ?? '无(顶级)'}
-
-
-
- {selectedDept.status}
-
-
-
-
-
-
-
-
-
- ) : (
-
-
-
- )}
-
-
-
-
setModalOpen(false)}
- destroyOnHidden
- confirmLoading={saving}
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-export default DepartmentsPage;
diff --git a/apps/admin/src/test/fixtures.ts b/apps/admin/src/test/fixtures.ts
index 73af018..ec2fed6 100644
--- a/apps/admin/src/test/fixtures.ts
+++ b/apps/admin/src/test/fixtures.ts
@@ -221,6 +221,5 @@ export const PERMISSION_NODES = [
'log:view',
'role:view', 'role:add', 'role:update', 'role:delete',
'user:view', 'user:add', 'user:update', 'user:delete',
- 'department:view', 'department:add', 'department:update', 'department:delete',
'dashboard:view',
] as const;
diff --git a/apps/admin/src/test/helpers.ts b/apps/admin/src/test/helpers.ts
index 20ad80c..81beded 100644
--- a/apps/admin/src/test/helpers.ts
+++ b/apps/admin/src/test/helpers.ts
@@ -47,7 +47,6 @@ export function logout(): void {
localStorage.removeItem('token');
localStorage.removeItem('user');
localStorage.removeItem('permissions');
- localStorage.removeItem('currentCampusId');
}
// ── API helpers (authenticated) ─────────────────────────────────────
diff --git a/apps/admin/src/test/setup.ts b/apps/admin/src/test/setup.ts
index 192da15..2094502 100644
--- a/apps/admin/src/test/setup.ts
+++ b/apps/admin/src/test/setup.ts
@@ -18,7 +18,6 @@ afterEach(() => {
localStorage.removeItem('token');
localStorage.removeItem('user');
localStorage.removeItem('permissions');
- localStorage.removeItem('currentCampusId');
});
export { BASE };