소스 검색

新增知识库管理页面,包含知识库文章的增删改查功能,优化API接口定义,提升用户体验和代码可维护性。同时,更新主题设置页面,整合配色方案逻辑,增强用户交互体验。

zyh 8 달 전
부모
커밋
e2b882777b

+ 1 - 1
client/admin/api.ts

@@ -606,7 +606,7 @@ export interface LoginLocationUpdateResponse {
 }
 
 // 知识库相关接口类型定义
-interface KnowInfoListResponse {
+export interface KnowInfoListResponse {
   data: KnowInfo[];
   pagination: {
     total: number;

+ 16 - 62
client/admin/pages_know.tsx → client/admin/pages_know_info.tsx

@@ -36,6 +36,8 @@ import { getEnumOptions } from './utils.ts';
 import {
   FileAPI,
   UserAPI,
+  KnowInfoAPI,
+  type KnowInfoListResponse
 } from './api.ts';
 
 
@@ -64,29 +66,14 @@ export const KnowInfoPage = () => {
   });
   
   // 使用React Query获取知识库文章列表
-  const { data: articlesData, isLoading: isListLoading, refetch } = useQuery({
-    queryKey: ['articles', searchParams],
-    queryFn: async () => {
-      const { title, category, page, limit } = searchParams;
-      const params = new URLSearchParams();
-      
-      if (title) params.append('title', title);
-      if (category) params.append('category', category);
-      params.append('page', String(page));
-      params.append('limit', String(limit));
-      
-      const response = await fetch(`/api/know-info?${params.toString()}`, {
-        headers: {
-          'Authorization': `Bearer ${localStorage.getItem('token')}`,
-        },
-      });
-      
-      if (!response.ok) {
-        throw new Error('获取知识库文章列表失败');
-      }
-      
-      return await response.json();
-    }
+  const { data: articlesData, isLoading: isListLoading, refetch } = useQuery<KnowInfoListResponse>({
+    queryKey: ['knowInfos', searchParams],
+    queryFn: () => KnowInfoAPI.getKnowInfos({
+      page: searchParams.page,
+      pageSize: searchParams.limit,
+      search: searchParams.title,
+      categoryId: searchParams.category ? Number(searchParams.category) : undefined
+    })
   });
   
   const articles = articlesData?.data || [];
@@ -95,17 +82,8 @@ export const KnowInfoPage = () => {
   // 获取单个知识库文章
   const fetchArticle = async (id: number) => {
     try {
-      const response = await fetch(`/api/know-info/${id}`, {
-        headers: {
-          'Authorization': `Bearer ${localStorage.getItem('token')}`,
-        },
-      });
-      
-      if (!response.ok) {
-        throw new Error('获取知识库文章详情失败');
-      }
-      
-      return await response.json();
+      const response = await KnowInfoAPI.getKnowInfo(id);
+      return response.data;
     } catch (error) {
       message.error('获取知识库文章详情失败');
       return null;
@@ -117,24 +95,9 @@ export const KnowInfoPage = () => {
     setIsLoading(true);
     
     try {
-      const url = formMode === 'create'
-        ? '/api/know-info'
-        : `/api/know-info/${editingId}`;
-      
-      const method = formMode === 'create' ? 'POST' : 'PUT';
-      
-      const response = await fetch(url, {
-        method,
-        headers: {
-          'Content-Type': 'application/json',
-          'Authorization': `Bearer ${localStorage.getItem('token')}`,
-        },
-        body: JSON.stringify(values),
-      });
-      
-      if (!response.ok) {
-        throw new Error(formMode === 'create' ? '创建知识库文章失败' : '更新知识库文章失败');
-      }
+      const response = formMode === 'create'
+        ? await KnowInfoAPI.createKnowInfo(values)
+        : await KnowInfoAPI.updateKnowInfo(editingId!, values);
       
       message.success(formMode === 'create' ? '创建知识库文章成功' : '更新知识库文章成功');
       setModalVisible(false);
@@ -162,16 +125,7 @@ export const KnowInfoPage = () => {
   // 处理删除
   const handleDelete = async (id: number) => {
     try {
-      const response = await fetch(`/api/know-info/${id}`, {
-        method: 'DELETE',
-        headers: {
-          'Authorization': `Bearer ${localStorage.getItem('token')}`,
-        },
-      });
-      
-      if (!response.ok) {
-        throw new Error('删除知识库文章失败');
-      }
+      await KnowInfoAPI.deleteKnowInfo(id);
       
       message.success('删除知识库文章成功');
       refetch();

+ 0 - 360
client/admin/pages_settings.tsx

@@ -74,116 +74,6 @@ const GROUP_DESCRIPTIONS: Record<typeof SystemSettingGroup[keyof typeof SystemSe
   [SystemSettingGroup.NOTIFICATION]: '配置系统通知的触发条件'
 };
 
-// 定义预设配色方案 - 按明暗模式分组
-const COLOR_SCHEMES: Record<ThemeMode, Record<string, ColorScheme>> = {
-  [ThemeMode.LIGHT]: {
-    DEFAULT: {
-      name: '默认浅色',
-      primary: '#1890ff',
-      background: '#f0f2f5',
-      text: '#000000'
-    },
-    BLUE: {
-      name: '蓝色',
-      primary: '#096dd9', 
-      background: '#e6f7ff',
-      text: '#003a8c'
-    },
-    GREEN: {
-      name: '绿色',
-      primary: '#52c41a',
-      background: '#f6ffed',
-      text: '#135200'
-    },
-    WARM: {
-      name: '暖橙',
-      primary: '#fa8c16',
-      background: '#fff7e6',
-      text: '#873800'
-    }
-  },
-  [ThemeMode.DARK]: {
-    DEFAULT: {
-      name: '默认深色',
-      primary: '#177ddc',
-      background: '#141414',
-      text: '#ffffff'
-    },
-    MIDNIGHT: {
-      name: '午夜蓝',
-      primary: '#1a3b7a',
-      background: '#0a0a1a',
-      text: '#e0e0e0'
-    },
-    FOREST: {
-      name: '森林',
-      primary: '#2e7d32',
-      background: '#121212',
-      text: '#e0e0e0'
-    },
-    SUNSET: {
-      name: '日落',
-      primary: '#f5222d',
-      background: '#1a1a1a',
-      text: '#ffffff'
-    }
-  }
-};
-
-// 颜色选择器组件
-// const ColorPicker: React.FC<{
-//   value?: string;
-//   onChange?: (color: string) => void;
-//   label?: string;
-// }> = ({ value = '#1890ff', onChange, label = '选择颜色' }) => {
-//   const [color, setColor] = useState(value);
-//   const [open, setOpen] = useState(false);
-
-//   // 更新颜色(预览)
-//   const handleColorChange = (e: React.ChangeEvent<HTMLInputElement>) => {
-//     const newColor = e.target.value;
-//     setColor(newColor);
-//   };
-
-//   // 关闭时确认颜色
-//   const handleOpenChange = (visible: boolean) => {
-//     if (!visible && color) {
-//       onChange?.(color);
-//     }
-//     setOpen(visible);
-//   };
-
-//   return (
-//     <Popover
-//       open={open}
-//       onOpenChange={handleOpenChange}
-//       trigger="click"
-//       content={
-//         <div style={{ padding: '8px' }}>
-//           <Input 
-//             type="color" 
-//             value={color} 
-//             onChange={handleColorChange}
-//             style={{ width: 60, cursor: 'pointer' }}
-//           />
-//         </div>
-//       }
-//     >
-//       <Button 
-//         icon={<BgColorsOutlined />} 
-//         style={{ 
-//           backgroundColor: color,
-//           borderColor: color,
-//           color: '#fff'
-//         }}
-//       >
-//         {label}
-//       </Button>
-//     </Popover>
-//   );
-// };
-
-// 基础设置页面
 export const SettingsPage = () => {
   const [form] = Form.useForm();
   const queryClient = useQueryClient();
@@ -397,253 +287,3 @@ export const SettingsPage = () => {
   );
 };
 
-// 主题设置页面
-export const ThemeSettingsPage = () => {
-  const { isDark, currentTheme, updateTheme, saveTheme, resetTheme } = useTheme();
-  const [form] = Form.useForm();
-  const [loading, setLoading] = useState(false);
-
-  // 处理配色方案选择
-  const handleColorSchemeChange = (schemeName: string) => {
-    const currentMode = form.getFieldValue('theme_mode') as ThemeMode;
-    const scheme = COLOR_SCHEMES[currentMode][schemeName];
-    if (!scheme) return;
-    form.setFieldsValue({
-      primary_color: scheme.primary,
-      background_color: scheme.background,
-      text_color: scheme.text
-    });
-    updateTheme({
-      primary_color: scheme.primary,
-      background_color: scheme.background,
-      text_color: scheme.text
-    });
-  };
-
-  // 初始化表单数据
-  useEffect(() => {
-    form.setFieldsValue({
-      theme_mode: currentTheme.theme_mode,
-      primary_color: currentTheme.primary_color,
-      background_color: currentTheme.background_color || (isDark ? '#141414' : '#f0f2f5'),
-      font_size: currentTheme.font_size,
-      is_compact: currentTheme.is_compact
-    });
-  }, [currentTheme, form, isDark]);
-
-  // 处理表单提交
-  const handleSubmit = async (values: any) => {
-    try {
-      setLoading(true);
-      await saveTheme(values);
-    } catch (error) {
-      message.error('保存主题设置失败');
-    } finally {
-      setLoading(false);
-    }
-  };
-
-  // 处理重置
-  const handleReset = async () => {
-    try {
-      setLoading(true);
-      await resetTheme();
-    } catch (error) {
-      message.error('重置主题设置失败');
-    } finally {
-      setLoading(false);
-    }
-  };
-
-  // 处理表单值变化 - 实时预览
-  const handleValuesChange = (changedValues: any) => {
-    updateTheme(changedValues);
-  };
-
-  return (
-    <div>
-      <Title level={2}>主题设置</Title>
-      <Card>
-        <Spin spinning={loading}>
-          <Form
-            form={form}
-            layout="vertical"
-            onFinish={handleSubmit}
-            onValuesChange={handleValuesChange}
-            initialValues={{
-              theme_mode: currentTheme.theme_mode,
-              primary_color: currentTheme.primary_color,
-              background_color: currentTheme.background_color || (isDark ? '#141414' : '#f0f2f5'),
-              font_size: currentTheme.font_size,
-              is_compact: currentTheme.is_compact
-            }}
-          >
-            {/* 配色方案选择 */}
-            <Form.Item label="预设配色方案">
-              <Space wrap>
-                {(() => {
-                  const themeMode = (form.getFieldValue('theme_mode') as ThemeMode) || ThemeMode.LIGHT;
-                  const schemes = COLOR_SCHEMES[themeMode] || {};
-                  const currentPrimary = form.getFieldValue('primary_color');
-                  const currentBg = form.getFieldValue('background_color');
-                  const currentText = form.getFieldValue('text_color');
-                  
-                  return Object.entries(schemes).map(([key, scheme]) => {
-                    const isActive = 
-                      scheme.primary === currentPrimary && 
-                      scheme.background === currentBg && 
-                      scheme.text === currentText;
-                    
-                    return (
-                      <Button
-                        key={key}
-                        onClick={() => {
-                          handleColorSchemeChange(key);
-                          form.setFieldValue('scheme_name', scheme.name);
-                        }}
-                        style={{
-                          backgroundColor: scheme.background,
-                          color: scheme.text,
-                          borderColor: isActive ? scheme.text : scheme.primary,
-                          borderWidth: isActive ? 2 : 1,
-                          boxShadow: isActive ? `0 0 0 2px ${scheme.primary}` : 'none',
-                          fontWeight: isActive ? 'bold' : 'normal',
-                          transition: 'all 0.3s'
-                        }}
-                      >
-                        {scheme.name}
-                        {isActive && (
-                          <span style={{ marginLeft: 4 }}>✓</span>
-                        )}
-                      </Button>
-                    );
-                  });
-                })()}
-              </Space>
-            </Form.Item>
-
-            {/* 主题模式 */}
-            <Form.Item
-              label="主题模式"
-              name="theme_mode"
-              rules={[{ required: true, message: '请选择主题模式' }]}
-            >
-              <Radio.Group>
-                <Radio value={ThemeMode.LIGHT}>浅色模式</Radio>
-                <Radio value={ThemeMode.DARK}>深色模式</Radio>
-              </Radio.Group>
-            </Form.Item>
-
-            {/* 主题色 */}
-            <Form.Item
-              label="主题色"
-              name="primary_color"
-              rules={[{ required: true, message: '请选择主题色' }]}
-            >
-              <ColorPicker 
-                value={form.getFieldValue('primary_color')}
-                onChange={(color) => {
-                  form.setFieldValue('primary_color', color.toHexString());
-                  updateTheme({ primary_color: color.toHexString() });
-                }}
-                allowClear
-              />
-            </Form.Item>
-
-            {/* 背景色 */}
-            <Form.Item
-              label="背景色"
-              name="background_color"
-              rules={[{ required: true, message: '请选择背景色' }]}
-            >
-              <ColorPicker 
-                value={form.getFieldValue('background_color')}
-                onChange={(color) => {
-                  form.setFieldValue('background_color', color.toHexString());
-                  updateTheme({ background_color: color.toHexString() });
-                }}
-                allowClear
-              />
-            </Form.Item>
-
-            {/* 文字颜色 */}
-            <Form.Item
-              label="文字颜色"
-              name="text_color"
-              rules={[{ required: true, message: '请选择文字颜色' }]}
-            >
-              <ColorPicker 
-                value={form.getFieldValue('text_color')}
-                onChange={(color) => {
-                  form.setFieldValue('text_color', color.toHexString());
-                  updateTheme({ text_color: color.toHexString() });
-                }}
-                allowClear
-              />
-            </Form.Item>
-
-            {/* 圆角大小 */}
-            <Form.Item
-              label="圆角大小"
-              name="border_radius"
-              rules={[{ required: true, message: '请设置圆角大小' }]}
-              initialValue={6}
-            >
-              <InputNumber<number>
-                min={0} 
-                max={20}
-                addonAfter="px"
-              />
-            </Form.Item>
-
-            {/* 字体大小 */}
-            <Form.Item
-              label="字体大小"
-              name="font_size"
-              rules={[{ required: true, message: '请选择字体大小' }]}
-            >
-              <Radio.Group>
-                <Radio value={FontSize.SMALL}>小</Radio>
-                <Radio value={FontSize.MEDIUM}>中</Radio>
-                <Radio value={FontSize.LARGE}>大</Radio>
-              </Radio.Group>
-            </Form.Item>
-
-            {/* 紧凑模式 */}
-            <Form.Item
-              label="紧凑模式"
-              name="is_compact"
-              valuePropName="checked"
-              getValueFromEvent={(checked: boolean) => checked ? CompactMode.COMPACT : CompactMode.NORMAL}
-              getValueProps={(value: CompactMode) => ({
-                checked: value === CompactMode.COMPACT
-              })}
-            >
-              <Switch 
-                checkedChildren="开启" 
-                unCheckedChildren="关闭"
-              />
-            </Form.Item>
-
-            {/* 操作按钮 */}
-            <Form.Item>
-              <Space>
-                <Button type="primary" htmlType="submit">
-                  保存设置
-                </Button>
-                <Popconfirm
-                  title="确定要重置主题设置吗?"
-                  onConfirm={handleReset}
-                  okText="确定"
-                  cancelText="取消"
-                >
-                  <Button>重置为默认值</Button>
-                </Popconfirm>
-              </Space>
-            </Form.Item>
-          </Form>
-        </Spin>
-      </Card>
-    </div>
-  );
-};

+ 367 - 0
client/admin/pages_theme_settings.tsx

@@ -0,0 +1,367 @@
+import React, { useState, useEffect } from 'react';
+import { 
+  Layout, Menu, Button, Table, Space,
+  Form, Input, Select, message, Modal,
+  Card, Spin, Row, Col, Breadcrumb, Avatar,
+  Dropdown, ConfigProvider, theme, Typography,
+  Switch, Badge, Image, Upload, Divider, Descriptions,
+  Popconfirm, Tag, Statistic, DatePicker, Radio, Progress, Tabs, List, Alert, Collapse, Empty, Drawer, InputNumber,ColorPicker,
+  Popover
+} from 'antd';
+import {
+  UploadOutlined,
+  ReloadOutlined,
+  SaveOutlined,
+  BgColorsOutlined
+} from '@ant-design/icons';
+import { debounce } from 'lodash';
+import { 
+  useQuery,
+  useMutation,
+  useQueryClient,
+} from '@tanstack/react-query';
+import dayjs from 'dayjs';
+import weekday from 'dayjs/plugin/weekday';
+import localeData from 'dayjs/plugin/localeData';
+import 'dayjs/locale/zh-cn';
+import type { 
+  FileLibrary, FileCategory, KnowInfo, SystemSetting, SystemSettingValue,
+  ColorScheme
+} from '../share/types.ts';
+import { ThemeMode } from '../share/types.ts';
+
+import {
+  SystemSettingGroup,
+  SystemSettingKey,
+  FontSize,
+  CompactMode, 
+  AllowedFileType
+} from '../share/types.ts';
+
+
+import { getEnumOptions } from './utils.ts';
+
+import {
+  SystemAPI,
+} from './api.ts';
+
+import { useTheme } from './hooks_sys.tsx';
+
+import { Uploader } from './components_uploader.tsx';
+
+// 配置 dayjs 插件
+dayjs.extend(weekday);
+dayjs.extend(localeData);
+
+// 设置 dayjs 语言
+dayjs.locale('zh-cn');
+
+const { Title } = Typography;
+
+
+// 定义预设配色方案 - 按明暗模式分组
+const COLOR_SCHEMES: Record<ThemeMode, Record<string, ColorScheme>> = {
+  [ThemeMode.LIGHT]: {
+    DEFAULT: {
+      name: '默认浅色',
+      primary: '#1890ff',
+      background: '#f0f2f5',
+      text: '#000000'
+    },
+    BLUE: {
+      name: '蓝色',
+      primary: '#096dd9', 
+      background: '#e6f7ff',
+      text: '#003a8c'
+    },
+    GREEN: {
+      name: '绿色',
+      primary: '#52c41a',
+      background: '#f6ffed',
+      text: '#135200'
+    },
+    WARM: {
+      name: '暖橙',
+      primary: '#fa8c16',
+      background: '#fff7e6',
+      text: '#873800'
+    }
+  },
+  [ThemeMode.DARK]: {
+    DEFAULT: {
+      name: '默认深色',
+      primary: '#177ddc',
+      background: '#141414',
+      text: '#ffffff'
+    },
+    MIDNIGHT: {
+      name: '午夜蓝',
+      primary: '#1a3b7a',
+      background: '#0a0a1a',
+      text: '#e0e0e0'
+    },
+    FOREST: {
+      name: '森林',
+      primary: '#2e7d32',
+      background: '#121212',
+      text: '#e0e0e0'
+    },
+    SUNSET: {
+      name: '日落',
+      primary: '#f5222d',
+      background: '#1a1a1a',
+      text: '#ffffff'
+    }
+  }
+};
+
+// 主题设置页面
+export const ThemeSettingsPage = () => {
+  const { isDark, currentTheme, updateTheme, saveTheme, resetTheme } = useTheme();
+  const [form] = Form.useForm();
+  const [loading, setLoading] = useState(false);
+
+  // 处理配色方案选择
+  const handleColorSchemeChange = (schemeName: string) => {
+    const currentMode = form.getFieldValue('theme_mode') as ThemeMode;
+    const scheme = COLOR_SCHEMES[currentMode][schemeName];
+    if (!scheme) return;
+    form.setFieldsValue({
+      primary_color: scheme.primary,
+      background_color: scheme.background,
+      text_color: scheme.text
+    });
+    updateTheme({
+      primary_color: scheme.primary,
+      background_color: scheme.background,
+      text_color: scheme.text
+    });
+  };
+
+  // 初始化表单数据
+  useEffect(() => {
+    form.setFieldsValue({
+      theme_mode: currentTheme.theme_mode,
+      primary_color: currentTheme.primary_color,
+      background_color: currentTheme.background_color || (isDark ? '#141414' : '#f0f2f5'),
+      font_size: currentTheme.font_size,
+      is_compact: currentTheme.is_compact
+    });
+  }, [currentTheme, form, isDark]);
+
+  // 处理表单提交
+  const handleSubmit = async (values: any) => {
+    try {
+      setLoading(true);
+      await saveTheme(values);
+    } catch (error) {
+      message.error('保存主题设置失败');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  // 处理重置
+  const handleReset = async () => {
+    try {
+      setLoading(true);
+      await resetTheme();
+    } catch (error) {
+      message.error('重置主题设置失败');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  // 处理表单值变化 - 实时预览
+  const handleValuesChange = (changedValues: any) => {
+    updateTheme(changedValues);
+  };
+
+  return (
+    <div>
+      <Title level={2}>主题设置</Title>
+      <Card>
+        <Spin spinning={loading}>
+          <Form
+            form={form}
+            layout="vertical"
+            onFinish={handleSubmit}
+            onValuesChange={handleValuesChange}
+            initialValues={{
+              theme_mode: currentTheme.theme_mode,
+              primary_color: currentTheme.primary_color,
+              background_color: currentTheme.background_color || (isDark ? '#141414' : '#f0f2f5'),
+              font_size: currentTheme.font_size,
+              is_compact: currentTheme.is_compact
+            }}
+          >
+            {/* 配色方案选择 */}
+            <Form.Item label="预设配色方案">
+              <Space wrap>
+                {(() => {
+                  const themeMode = (form.getFieldValue('theme_mode') as ThemeMode) || ThemeMode.LIGHT;
+                  const schemes = COLOR_SCHEMES[themeMode] || {};
+                  const currentPrimary = form.getFieldValue('primary_color');
+                  const currentBg = form.getFieldValue('background_color');
+                  const currentText = form.getFieldValue('text_color');
+                  
+                  return Object.entries(schemes).map(([key, scheme]) => {
+                    const isActive = 
+                      scheme.primary === currentPrimary && 
+                      scheme.background === currentBg && 
+                      scheme.text === currentText;
+                    
+                    return (
+                      <Button
+                        key={key}
+                        onClick={() => {
+                          handleColorSchemeChange(key);
+                          form.setFieldValue('scheme_name', scheme.name);
+                        }}
+                        style={{
+                          backgroundColor: scheme.background,
+                          color: scheme.text,
+                          borderColor: isActive ? scheme.text : scheme.primary,
+                          borderWidth: isActive ? 2 : 1,
+                          boxShadow: isActive ? `0 0 0 2px ${scheme.primary}` : 'none',
+                          fontWeight: isActive ? 'bold' : 'normal',
+                          transition: 'all 0.3s'
+                        }}
+                      >
+                        {scheme.name}
+                        {isActive && (
+                          <span style={{ marginLeft: 4 }}>✓</span>
+                        )}
+                      </Button>
+                    );
+                  });
+                })()}
+              </Space>
+            </Form.Item>
+
+            {/* 主题模式 */}
+            <Form.Item
+              label="主题模式"
+              name="theme_mode"
+              rules={[{ required: true, message: '请选择主题模式' }]}
+            >
+              <Radio.Group>
+                <Radio value={ThemeMode.LIGHT}>浅色模式</Radio>
+                <Radio value={ThemeMode.DARK}>深色模式</Radio>
+              </Radio.Group>
+            </Form.Item>
+
+            {/* 主题色 */}
+            <Form.Item
+              label="主题色"
+              name="primary_color"
+              rules={[{ required: true, message: '请选择主题色' }]}
+            >
+              <ColorPicker 
+                value={form.getFieldValue('primary_color')}
+                onChange={(color) => {
+                  form.setFieldValue('primary_color', color.toHexString());
+                  updateTheme({ primary_color: color.toHexString() });
+                }}
+                allowClear
+              />
+            </Form.Item>
+
+            {/* 背景色 */}
+            <Form.Item
+              label="背景色"
+              name="background_color"
+              rules={[{ required: true, message: '请选择背景色' }]}
+            >
+              <ColorPicker 
+                value={form.getFieldValue('background_color')}
+                onChange={(color) => {
+                  form.setFieldValue('background_color', color.toHexString());
+                  updateTheme({ background_color: color.toHexString() });
+                }}
+                allowClear
+              />
+            </Form.Item>
+
+            {/* 文字颜色 */}
+            <Form.Item
+              label="文字颜色"
+              name="text_color"
+              rules={[{ required: true, message: '请选择文字颜色' }]}
+            >
+              <ColorPicker 
+                value={form.getFieldValue('text_color')}
+                onChange={(color) => {
+                  form.setFieldValue('text_color', color.toHexString());
+                  updateTheme({ text_color: color.toHexString() });
+                }}
+                allowClear
+              />
+            </Form.Item>
+
+            {/* 圆角大小 */}
+            <Form.Item
+              label="圆角大小"
+              name="border_radius"
+              rules={[{ required: true, message: '请设置圆角大小' }]}
+              initialValue={6}
+            >
+              <InputNumber<number>
+                min={0} 
+                max={20}
+                addonAfter="px"
+              />
+            </Form.Item>
+
+            {/* 字体大小 */}
+            <Form.Item
+              label="字体大小"
+              name="font_size"
+              rules={[{ required: true, message: '请选择字体大小' }]}
+            >
+              <Radio.Group>
+                <Radio value={FontSize.SMALL}>小</Radio>
+                <Radio value={FontSize.MEDIUM}>中</Radio>
+                <Radio value={FontSize.LARGE}>大</Radio>
+              </Radio.Group>
+            </Form.Item>
+
+            {/* 紧凑模式 */}
+            <Form.Item
+              label="紧凑模式"
+              name="is_compact"
+              valuePropName="checked"
+              getValueFromEvent={(checked: boolean) => checked ? CompactMode.COMPACT : CompactMode.NORMAL}
+              getValueProps={(value: CompactMode) => ({
+                checked: value === CompactMode.COMPACT
+              })}
+            >
+              <Switch 
+                checkedChildren="开启" 
+                unCheckedChildren="关闭"
+              />
+            </Form.Item>
+
+            {/* 操作按钮 */}
+            <Form.Item>
+              <Space>
+                <Button type="primary" htmlType="submit">
+                  保存设置
+                </Button>
+                <Popconfirm
+                  title="确定要重置主题设置吗?"
+                  onConfirm={handleReset}
+                  okText="确定"
+                  cancelText="取消"
+                >
+                  <Button>重置为默认值</Button>
+                </Popconfirm>
+              </Space>
+            </Form.Item>
+          </Form>
+        </Spin>
+      </Card>
+    </div>
+  );
+};

+ 1 - 1
client/admin/test/theme_setting_page.test.tsx → client/admin/test/pages_theme_setting.test.tsx

@@ -1,7 +1,7 @@
 import { JSDOM } from 'jsdom'
 import React from 'react'
 import {render, fireEvent, within, screen} from '@testing-library/react'
-import { ThemeSettingsPage } from "../pages_settings.tsx"
+import { ThemeSettingsPage } from "../pages_theme_settings.tsx"
 import { ThemeProvider } from "../hooks_sys.tsx"
 import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
 import {

+ 6 - 15
client/admin/web_app.tsx

@@ -74,22 +74,13 @@ import {
   UsersPage,
   FileLibraryPage
 } from './pages_sys.tsx';
-import { KnowInfoPage } from './pages_know.tsx';
+import { KnowInfoPage } from './pages_know_info.tsx';
 import { MessagesPage } from './pages_messages.tsx';
-import {
-  SettingsPage,
-  ThemeSettingsPage,
- } from './pages_settings.tsx';
- import {
-  ChartDashboardPage,
- } from './pages_chart.tsx';
- import {
-  LoginMapPage
- } from './pages_map.tsx';
-
-import {
-  LoginPage,
-} from './pages_login_reg.tsx';
+import {SettingsPage } from './pages_settings.tsx';
+import {ThemeSettingsPage} from './pages_theme_settings.tsx'
+import { ChartDashboardPage } from './pages_chart.tsx';
+import { LoginMapPage } from './pages_map.tsx';
+import { LoginPage } from './pages_login_reg.tsx';
 
 
 // 配置 dayjs 插件