diff --git a/apps/admin/src/App.tsx b/apps/admin/src/App.tsx
index ff62f91..eed9a23 100644
--- a/apps/admin/src/App.tsx
+++ b/apps/admin/src/App.tsx
@@ -29,6 +29,7 @@ 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';
const PrivateRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => {
@@ -262,6 +263,15 @@ const App: React.FC = () => {
}
/>
+
+
+
+
+ }
+ />
diff --git a/apps/admin/src/layouts/MainLayout.tsx b/apps/admin/src/layouts/MainLayout.tsx
index 6057c52..a6ad416 100644
--- a/apps/admin/src/layouts/MainLayout.tsx
+++ b/apps/admin/src/layouts/MainLayout.tsx
@@ -25,6 +25,7 @@ import {
CheckCircleOutlined,
LaptopOutlined,
BellOutlined,
+ ApiOutlined,
} from '@ant-design/icons';
import { usePermission } from '../hooks/usePermission';
import NotificationBell from '../components/NotificationBell';
@@ -113,6 +114,7 @@ const allMenuItems: MenuItemType[] = [
{ key: '/operation-logs', icon: , label: '操作日志', permission: 'log:view' },
{ key: '/roles', icon: , label: '角色管理', permission: 'role:view' },
{ key: '/permissions', icon: , label: '权限一览', permission: 'role:view' },
+ { key: '/integration-config', icon: , label: '钉钉集成配置', permission: 'integration:read' },
{ key: '/users', icon: , label: '账号管理', permission: 'user:view' },
],
},
diff --git a/apps/admin/src/pages/IntegrationConfig/index.tsx b/apps/admin/src/pages/IntegrationConfig/index.tsx
new file mode 100644
index 0000000..e601eea
--- /dev/null
+++ b/apps/admin/src/pages/IntegrationConfig/index.tsx
@@ -0,0 +1,139 @@
+import React, { useEffect, useState } from 'react';
+import {
+ Card, Form, Input, Button, Space, message, Spin, Switch, Alert, Descriptions, Tag,
+} from 'antd';
+import {
+ SaveOutlined, ApiOutlined, CheckCircleOutlined, CloseCircleOutlined,
+} from '@ant-design/icons';
+import api from '../../api';
+
+interface DingTalkConfig {
+ agentId: string;
+ appSecret: string;
+ corpId: string;
+ startEnable: boolean;
+}
+
+const IntegrationConfigPage: React.FC = () => {
+ const [loading, setLoading] = useState(false);
+ const [saving, setSaving] = useState(false);
+ const [testing, setTesting] = useState(false);
+ const [config, setConfig] = useState(null);
+ const [verified, setVerified] = useState(null);
+ const [form] = Form.useForm();
+
+ const fetchConfig = async () => {
+ setLoading(true);
+ try {
+ const res = await api.get<{ success: boolean; data: Array<{ type: string; verify: boolean; config: DingTalkConfig }> }>('/integration/config');
+ const dt = res.data?.find((c) => c.type === 'DINGTALK');
+ if (dt) {
+ setConfig(dt.config);
+ setVerified(dt.verify);
+ form.setFieldsValue(dt.config);
+ }
+ } catch {
+ // not configured
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ fetchConfig();
+ }, []);
+
+ const handleSave = async () => {
+ const values = await form.validateFields();
+ setSaving(true);
+ try {
+ await api.post('/integration/config', { type: 'DINGTALK', config: values });
+ message.success('配置已保存');
+ await fetchConfig();
+ } catch (e: unknown) {
+ const err = e as { message?: string };
+ message.error(err?.message || '保存失败');
+ } finally {
+ setSaving(false);
+ }
+ };
+
+ const handleTest = async () => {
+ const values = await form.validateFields();
+ setTesting(true);
+ try {
+ const res = await api.post<{ success: boolean; message: string }>('/integration/config/test', {
+ type: 'DINGTALK',
+ config: values,
+ });
+ setVerified(res.success);
+ message.success(res.message);
+ } catch (e: unknown) {
+ const err = e as { message?: string };
+ setVerified(false);
+ message.error(err?.message || '连接失败');
+ } finally {
+ setTesting(false);
+ }
+ };
+
+ return (
+
+ {verified === true && } color="success">已连接}
+ {verified === false && } color="error">未连接}
+
+ }>
+
+ {config && (
+
+ {config.corpId || '-'}
+ {config.agentId || '-'}
+
+
+ {config.startEnable ? '已启用' : '未启用'}
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ } loading={saving} onClick={handleSave}>
+ 保存配置
+
+ } loading={testing} onClick={handleTest}>
+ 测试连接
+
+
+
+
+
+ );
+};
+
+export default IntegrationConfigPage;