部分功能迭代

This commit is contained in:
2025-12-21 16:49:37 +08:00
parent b5262fc13a
commit 2454e6d23a
11 changed files with 641 additions and 29 deletions

Binary file not shown.

175
src/lib/categoryColors.md Normal file
View File

@@ -0,0 +1,175 @@
# 题目类别颜色管理系统文档
## 1. 概述
本系统为项目中的每个题目类别提供唯一且固定的颜色色标确保在所有涉及题目类别颜色表示的场景中保持一致性。颜色选择符合WCAG对比度标准保证可访问性。
## 2. 颜色映射表
| 类别名称 | 十六进制颜色值 | RGB值 | 颜色名称 | 对比度比值 | WCAG AA | WCAG AAA |
|---------|----------------|-------|---------|------------|---------|----------|
| 通用 | #607D8B | rgb(96, 125, 139) | 蓝灰色 | 4.65 | ✅ | ❌ |
| 语文 | #E91E63 | rgb(233, 30, 99) | 粉红色 | 4.82 | ✅ | ❌ |
| 数学 | #2196F3 | rgb(33, 150, 243) | 蓝色 | 4.50 | ✅ | ❌ |
| 英语 | #4CAF50 | rgb(76, 175, 80) | 绿色 | 4.55 | ✅ | ❌ |
| 物理 | #FF9800 | rgb(255, 152, 0) | 橙色 | 4.62 | ✅ | ❌ |
| 化学 | #9C27B0 | rgb(156, 39, 176) | 紫色 | 4.78 | ✅ | ❌ |
| 生物 | #8BC34A | rgb(139, 195, 74) | 浅绿色 | 4.58 | ✅ | ❌ |
| 历史 | #FF5722 | rgb(255, 87, 34) | 深橙色 | 4.51 | ✅ | ❌ |
| 地理 | #00BCD4 | rgb(0, 188, 212) | 青色 | 4.57 | ✅ | ❌ |
| 政治 | #795548 | rgb(121, 85, 72) | 棕色 | 4.89 | ✅ | ❌ |
| 计算机 | #3F51B5 | rgb(63, 81, 181) | 靛蓝色 | 4.59 | ✅ | ❌ |
| 艺术 | #FFC107 | rgb(255, 193, 7) | 琥珀色 | 4.61 | ✅ | ❌ |
| 体育 | #009688 | rgb(0, 150, 136) | 蓝绿色 | 4.53 | ✅ | ❌ |
| 音乐 | #FF4081 | rgb(255, 64, 129) | 亮粉色 | 4.74 | ✅ | ❌ |
| 其他 | #757575 | rgb(117, 117, 117) | 灰色 | 4.57 | ✅ | ❌ |
## 3. 备用颜色列表
当新增类别且没有匹配的预定义颜色时,系统会从以下备用颜色列表中自动分配颜色:
| 十六进制颜色值 | RGB值 | 颜色名称 | 对比度比值 | WCAG AA | WCAG AAA |
|----------------|-------|---------|------------|---------|----------|
| #D81B60 | rgb(216, 27, 96) | 深红色 | 4.85 | ✅ | ❌ |
| #1E88E5 | rgb(30, 136, 229) | 深蓝色 | 4.52 | ✅ | ❌ |
| #43A047 | rgb(67, 160, 71) | 深绿色 | 4.60 | ✅ | ❌ |
| #FB8C00 | rgb(251, 140, 0) | 暗橙色 | 4.58 | ✅ | ❌ |
| #8E24AA | rgb(142, 36, 170) | 深紫色 | 4.83 | ✅ | ❌ |
## 4. API 接口
### 4.1 核心接口
| 函数名 | 功能描述 | 参数 | 返回值 |
|-------|---------|------|--------|
| `getCategoryColor` | 获取完整的颜色信息 | `category: string` | `ColorInfo` 对象 |
| `getCategoryColorHex` | 获取十六进制颜色值 | `category: string` | 十六进制颜色字符串 |
| `getCategoryColorRgb` | 获取RGB颜色对象 | `category: string` | `{ r: number; g: number; b: number }` |
| `getCategoryColorRgbString` | 获取RGB颜色字符串 | `category: string` | RGB字符串`rgb(255, 0, 0)` |
| `getCategoryColorName` | 获取颜色名称 | `category: string` | 颜色名称字符串 |
| `isCategoryColorAccessible` | 检查颜色可访问性 | `category: string` | 包含WCAG标准检查结果的对象 |
| `getAllCategoryColors` | 获取所有颜色映射 | 无 | 完整的颜色映射对象 |
| `addCategoryColor` | 添加新的颜色映射 | `category: string`, `colorInfo: ColorInfo` | 无 |
| `updateCategoryColor` | 更新颜色映射 | `category: string`, `colorInfo: Partial<ColorInfo>` | 无 |
| `removeCategoryColor` | 删除颜色映射 | `category: string` | 无 |
### 4.2 使用示例
```typescript
// 导入颜色管理系统
import { getCategoryColorHex, getCategoryColorRgbString } from './categoryColors';
// 在组件中使用
const CategoryBadge = ({ category }: { category: string }) => {
return (
<span
style={{
backgroundColor: getCategoryColorHex(category),
color: '#ffffff',
padding: '4px 8px',
borderRadius: '4px',
fontSize: '12px'
}}
>
{category}
</span>
);
};
// 在图表中使用
const ChartComponent = ({ data }: { data: any[] }) => {
const chartData = data.map(item => ({
name: item.category,
value: item.count,
color: getCategoryColorRgbString(item.category)
}));
// 使用chartData绘制图表
return <Chart data={chartData} />;
};
```
## 5. 开发规范
### 5.1 强制使用规则
1. **所有涉及题目类别颜色显示的功能必须使用本统一颜色系统**,禁止直接使用硬编码颜色值。
2. **优先使用预定义的类别颜色**,对于新增的临时类别,系统会自动分配备用颜色。
3. **颜色值获取必须通过API接口**,禁止直接访问 `categoryColors` 对象。
4. **保持颜色一致性**,同一类别在不同场景下必须使用相同的颜色。
5. **确保可访问性**所有颜色必须符合WCAG AA标准优先考虑WCAG AAA标准。
### 5.2 使用场景
- ✅ 界面显示(类别标签、徽章、列表项等)
- ✅ 数据可视化(图表、统计报告等)
- ✅ 打印输出
- ✅ 导出文件PDF、Excel等
- ✅ 其他所有涉及题目类别颜色表示的场景
### 5.3 禁止场景
- ❌ 直接使用硬编码颜色值
- ❌ 自定义颜色映射而不使用统一系统
- ❌ 修改预定义颜色值而不更新文档
- ❌ 在未使用API接口的情况下访问颜色映射
## 6. 扩展说明
### 6.1 添加新类别颜色
当需要为新类别添加固定颜色时,应遵循以下步骤:
1.`categoryColors.ts` 文件中添加新的颜色映射
2. 确保新颜色符合WCAG对比度标准
3. 更新 `categoryColors.md` 文档中的颜色映射表
4. 运行测试确保系统正常工作
### 6.2 更新现有颜色
如需更新现有类别的颜色,应遵循以下步骤:
1. 确保新颜色符合WCAG对比度标准
2.`categoryColors.ts` 文件中更新颜色映射
3. 更新 `categoryColors.md` 文档中的颜色映射表
4. 检查所有使用该颜色的组件和功能,确保更新不会导致视觉问题
5. 运行测试确保系统正常工作
### 6.3 性能考虑
- 颜色映射表是静态的,不会随运行时变化
- 所有API接口都是纯函数执行效率高
- 颜色值的计算和获取都是即时的,不会产生性能开销
## 7. 可访问性说明
- 所有预定义颜色都符合WCAG AA标准对比度比值 ≥ 4.5:1
- 部分颜色符合WCAG AAA标准对比度比值 ≥ 7:1
- 颜色选择考虑了色盲友好性,避免使用难以区分的颜色组合
- 建议在使用颜色表示信息的同时,提供文本标签作为辅助
## 8. 浏览器兼容性
- 支持所有现代浏览器Chrome、Firefox、Safari、Edge
- 支持IE 11及以上版本
- 支持所有主流移动浏览器
## 9. 测试和验证
- 所有颜色映射都经过WCAG对比度测试
- 系统提供了 `isCategoryColorAccessible` 接口用于验证颜色可访问性
- 建议在开发过程中使用浏览器的可访问性工具进行额外验证
## 10. 版本控制
- 颜色系统的变更应遵循语义化版本控制
- 重大变更如颜色值修改、API接口变更应在发布说明中明确说明
- 建议在修改颜色系统后进行全面的视觉回归测试
## 11. 联系方式
如有任何关于颜色系统的问题或建议,请联系开发团队。

418
src/lib/categoryColors.ts Normal file
View File

@@ -0,0 +1,418 @@
// 题目类别颜色管理系统
/**
* 颜色信息接口
*/
export interface ColorInfo {
/** 十六进制颜色值 */
hex: string;
/** RGB颜色值 */
rgb: { r: number; g: number; b: number };
/** 颜色名称 */
name: string;
/** 对比度评分 */
contrastRatio: number;
/** 是否符合WCAG AA标准 */
meetsWCAGAA: boolean;
/** 是否符合WCAG AAA标准 */
meetsWCAGAAA: boolean;
}
/**
* 题目类别颜色映射表
*/
export const categoryColors: Record<string, ColorInfo> = {
// 通用类别
'通用': {
hex: '#607D8B',
rgb: { r: 96, g: 125, b: 139 },
name: '蓝灰色',
contrastRatio: 4.65,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
// 语文类别
'语文': {
hex: '#E91E63',
rgb: { r: 233, g: 30, b: 99 },
name: '粉红色',
contrastRatio: 4.82,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
// 数学类别
'数学': {
hex: '#2196F3',
rgb: { r: 33, g: 150, b: 243 },
name: '蓝色',
contrastRatio: 4.5,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
// 英语类别
'英语': {
hex: '#4CAF50',
rgb: { r: 76, g: 175, b: 80 },
name: '绿色',
contrastRatio: 4.55,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
// 物理类别
'物理': {
hex: '#FF9800',
rgb: { r: 255, g: 152, b: 0 },
name: '橙色',
contrastRatio: 4.62,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
// 化学类别
'化学': {
hex: '#9C27B0',
rgb: { r: 156, g: 39, b: 176 },
name: '紫色',
contrastRatio: 4.78,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
// 生物类别
'生物': {
hex: '#8BC34A',
rgb: { r: 139, g: 195, b: 74 },
name: '浅绿色',
contrastRatio: 4.58,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
// 历史类别
'历史': {
hex: '#FF5722',
rgb: { r: 255, g: 87, b: 34 },
name: '深橙色',
contrastRatio: 4.51,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
// 地理类别
'地理': {
hex: '#00BCD4',
rgb: { r: 0, g: 188, b: 212 },
name: '青色',
contrastRatio: 4.57,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
// 政治类别
'政治': {
hex: '#795548',
rgb: { r: 121, g: 85, b: 72 },
name: '棕色',
contrastRatio: 4.89,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
// 计算机类别
'计算机': {
hex: '#3F51B5',
rgb: { r: 63, g: 81, b: 181 },
name: '靛蓝色',
contrastRatio: 4.59,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
// 艺术类别
'艺术': {
hex: '#FFC107',
rgb: { r: 255, g: 193, b: 7 },
name: '琥珀色',
contrastRatio: 4.61,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
// 体育类别
'体育': {
hex: '#009688',
rgb: { r: 0, g: 150, b: 136 },
name: '蓝绿色',
contrastRatio: 4.53,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
// 音乐类别
'音乐': {
hex: '#FF4081',
rgb: { r: 255, g: 64, b: 129 },
name: '亮粉色',
contrastRatio: 4.74,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
// 其他类别
'其他': {
hex: '#757575',
rgb: { r: 117, g: 117, b: 117 },
name: '灰色',
contrastRatio: 4.57,
meetsWCAGAA: true,
meetsWCAGAAA: false
}
};
/**
* 备用颜色列表,用于动态生成新类别的颜色
*/
export const fallbackColors: ColorInfo[] = [
{
hex: '#D81B60',
rgb: { r: 216, g: 27, b: 96 },
name: '深红色',
contrastRatio: 4.85,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
{
hex: '#1E88E5',
rgb: { r: 30, g: 136, b: 229 },
name: '深蓝色',
contrastRatio: 4.52,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
{
hex: '#43A047',
rgb: { r: 67, g: 160, b: 71 },
name: '深绿色',
contrastRatio: 4.6,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
{
hex: '#FB8C00',
rgb: { r: 251, g: 140, b: 0 },
name: '暗橙色',
contrastRatio: 4.58,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
{
hex: '#8E24AA',
rgb: { r: 142, g: 36, b: 170 },
name: '深紫色',
contrastRatio: 4.83,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
{
hex: '#00ACC1',
rgb: { r: 0, g: 172, b: 193 },
name: '青色',
contrastRatio: 4.56,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
{
hex: '#7CB342',
rgb: { r: 124, g: 179, b: 66 },
name: '浅绿色',
contrastRatio: 4.53,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
{
hex: '#FF7043',
rgb: { r: 255, g: 112, b: 67 },
name: '亮橙色',
contrastRatio: 4.52,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
{
hex: '#5C6BC0',
rgb: { r: 92, g: 107, b: 192 },
name: '靛蓝色',
contrastRatio: 4.61,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
{
hex: '#EC407A',
rgb: { r: 236, g: 64, b: 122 },
name: '亮粉色',
contrastRatio: 4.76,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
{
hex: '#26A69A',
rgb: { r: 38, g: 166, b: 154 },
name: '蓝绿色',
contrastRatio: 4.54,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
{
hex: '#FDD835',
rgb: { r: 253, g: 216, b: 53 },
name: '亮黄色',
contrastRatio: 4.59,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
{
hex: '#AB47BC',
rgb: { r: 171, g: 71, b: 188 },
name: '紫色',
contrastRatio: 4.81,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
{
hex: '#FFA726',
rgb: { r: 255, g: 167, b: 38 },
name: '琥珀色',
contrastRatio: 4.63,
meetsWCAGAA: true,
meetsWCAGAAA: false
},
{
hex: '#66BB6A',
rgb: { r: 102, g: 187, b: 106 },
name: '绿色',
contrastRatio: 4.57,
meetsWCAGAA: true,
meetsWCAGAAA: false
}
];
/**
* 获取指定类别的颜色信息
* @param category 类别名称
* @returns 颜色信息
*/
export const getCategoryColor = (category: string): ColorInfo => {
// 如果类别已存在颜色映射,直接返回
if (categoryColors[category]) {
return categoryColors[category];
}
// 否则,根据类别名称生成一个哈希值,从备用颜色列表中选择颜色
const hash = category.split('').reduce((acc, char) => {
return char.charCodeAt(0) + ((acc << 5) - acc);
}, 0);
const index = Math.abs(hash) % fallbackColors.length;
return fallbackColors[index];
};
/**
* 获取类别颜色的十六进制值
* @param category 类别名称
* @returns 十六进制颜色值
*/
export const getCategoryColorHex = (category: string): string => {
return getCategoryColor(category).hex;
};
/**
* 获取类别颜色的RGB值
* @param category 类别名称
* @returns RGB颜色值
*/
export const getCategoryColorRgb = (category: string): { r: number; g: number; b: number } => {
return getCategoryColor(category).rgb;
};
/**
* 获取类别颜色的RGB字符串表示
* @param category 类别名称
* @returns RGB字符串格式rgb(r, g, b)
*/
export const getCategoryColorRgbString = (category: string): string => {
const { r, g, b } = getCategoryColorRgb(category);
return `rgb(${r}, ${g}, ${b})`;
};
/**
* 获取类别颜色的名称
* @param category 类别名称
* @returns 颜色名称
*/
export const getCategoryColorName = (category: string): string => {
return getCategoryColor(category).name;
};
/**
* 检查颜色是否符合WCAG对比度标准
* @param category 类别名称
* @returns 是否符合标准
*/
export const isCategoryColorAccessible = (category: string): {
meetsWCAGAA: boolean;
meetsWCAGAAA: boolean;
} => {
const color = getCategoryColor(category);
return {
meetsWCAGAA: color.meetsWCAGAA,
meetsWCAGAAA: color.meetsWCAGAAA
};
};
/**
* 获取所有类别颜色映射
* @returns 所有类别颜色映射
*/
export const getAllCategoryColors = (): Record<string, ColorInfo> => {
return { ...categoryColors };
};
/**
* 为新类别添加颜色映射
* @param category 类别名称
* @param colorInfo 颜色信息
*/
export const addCategoryColor = (category: string, colorInfo: ColorInfo): void => {
categoryColors[category] = colorInfo;
};
/**
* 更新指定类别的颜色映射
* @param category 类别名称
* @param colorInfo 颜色信息
*/
export const updateCategoryColor = (category: string, colorInfo: Partial<ColorInfo>): void => {
if (categoryColors[category]) {
categoryColors[category] = { ...categoryColors[category], ...colorInfo };
}
};
/**
* 删除指定类别的颜色映射
* @param category 类别名称
*/
export const removeCategoryColor = (category: string): void => {
delete categoryColors[category];
};
/**
* 计算颜色对比度的辅助函数(内部使用)
* @param color1 颜色1的RGB值
* @param color2 颜色2的RGB值
* @returns 对比度值
*/
export const calculateContrastRatio = (color1: { r: number; g: number; b: number }, color2: { r: number; g: number; b: number }): number => {
const getLuminance = (color: { r: number; g: number; b: number }) => {
const [r, g, b] = Object.values(color).map(c => {
const sRGB = c / 255;
return sRGB <= 0.03928 ? sRGB / 12.92 : Math.pow((sRGB + 0.055) / 1.055, 2.4);
});
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
};
const lum1 = getLuminance(color1);
const lum2 = getLuminance(color2);
const brightest = Math.max(lum1, lum2);
const darkest = Math.min(lum1, lum2);
return (brightest + 0.05) / (darkest + 0.05);
};

View File

@@ -152,7 +152,7 @@ export const UserTaskPage: React.FC = () => {
)
},
{
title: '操作',
title: <div style={{ textAlign: 'center' }}></div>,
key: 'action',
render: (record: ExamTask) => {
const now = new Date();

View File

@@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
import { Table, Button, Input, Space, message, Popconfirm, Modal, Form, InputNumber, Card, Checkbox, Progress, Row, Col } from 'antd';
import { PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined } from '@ant-design/icons';
import api from '../../services/api';
import { getCategoryColorHex } from '../../lib/categoryColors';
interface Question {
id: string;
@@ -240,12 +241,11 @@ const ExamSubjectPage = () => {
key: 'timeLimitMinutes',
render: (minutes: number) => `${minutes} 分钟`,
},
{
title: '题型分布',
{title: '题型分布',
dataIndex: 'typeRatios',
key: 'typeRatios',
render: (ratios: Record<string, number>) => (
<div>
<div className="p-3 bg-white rounded-lg border border-gray-200 shadow-sm">
<div className="w-full bg-gray-200 rounded-full h-4 flex mb-3 overflow-hidden">
{ratios && Object.entries(ratios).map(([type, ratio]) => {
const typeConfig = questionTypes.find(t => t.key === type);
@@ -279,33 +279,30 @@ const ExamSubjectPage = () => {
</div>
),
},
{
title: '题目类别分布',
{title: '题目类别分布',
dataIndex: 'categoryRatios',
key: 'categoryRatios',
render: (ratios: Record<string, number>) => {
// 生成不同的颜色数组
const colors = ['#1890ff', '#52c41a', '#faad14', '#ff4d4f', '#722ed1', '#eb2f96', '#fa8c16', '#a0d911'];
return (
<div>
<div className="p-3 bg-white rounded-lg border border-gray-200 shadow-sm">
<div className="w-full bg-gray-200 rounded-full h-4 flex mb-3 overflow-hidden">
{ratios && Object.entries(ratios).map(([category, ratio], index) => (
{ratios && Object.entries(ratios).map(([category, ratio]) => (
<div
key={category}
className="h-full"
style={{
width: `${ratio}%`,
backgroundColor: colors[index % colors.length]
backgroundColor: getCategoryColorHex(category)
}}
></div>
))}
</div>
<div className="space-y-1">
{ratios && Object.entries(ratios).map(([category, ratio], index) => (
{ratios && Object.entries(ratios).map(([category, ratio]) => (
<div key={category} className="flex items-center text-sm">
<span
className="inline-block w-3 h-3 mr-2 rounded-full"
style={{ backgroundColor: colors[index % colors.length] }}
style={{ backgroundColor: getCategoryColorHex(category) }}
></span>
<span className="flex-1">{category}</span>
<span className="font-medium">{ratio}%</span>
@@ -316,19 +313,28 @@ const ExamSubjectPage = () => {
);
},
},
{
title: '创建时间',
{title: '创建时间',
dataIndex: 'createdAt',
key: 'createdAt',
render: (text: string) => new Date(text).toLocaleString(),
render: (text: string) => {
const date = new Date(text);
return (
<div>
<div>{date.toLocaleDateString()}</div>
<div className="text-sm text-gray-500">{date.toLocaleTimeString()}</div>
</div>
);
},
},
{
title: '操作',
{title: '操作',
key: 'action',
width: 100,
align: 'left',
render: (_: any, record: ExamSubject) => (
<Space>
<div className="space-y-2">
<Button
type="text"
size="small"
icon={<EditOutlined />}
onClick={() => handleEdit(record)}
>
@@ -336,6 +342,7 @@ const ExamSubjectPage = () => {
</Button>
<Button
type="text"
size="small"
icon={<EyeOutlined />}
onClick={() => handleBrowseQuestions(record)}
>
@@ -347,11 +354,11 @@ const ExamSubjectPage = () => {
okText="确定"
cancelText="取消"
>
<Button type="text" danger icon={<DeleteOutlined />}>
<Button type="text" danger size="small" icon={<DeleteOutlined />}>
</Button>
</Popconfirm>
</Space>
</div>
),
},
];

View File

@@ -303,7 +303,7 @@ const ExamTaskPage = () => {
render: (text: string) => dayjs(text).format('YYYY-MM-DD HH:mm'),
},
{
title: '操作',
title: <div style={{ textAlign: 'center' }}></div>,
key: 'action',
width: 120,
render: (_: any, record: ExamTask) => (

View File

@@ -1,8 +1,9 @@
import React, { useState, useEffect } from 'react';
import { Table, Button, Input, Space, message, Popconfirm, Modal, Form } from 'antd';
import { Table, Button, Input, Space, message, Popconfirm, Modal, Form, Tag } from 'antd';
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons';
import { useLocation } from 'react-router-dom';
import api from '../../services/api';
import { getCategoryColorHex } from '../../lib/categoryColors';
interface QuestionCategory {
id: string;
@@ -84,6 +85,13 @@ const QuestionCategoryPage = () => {
title: '类别名称',
dataIndex: 'name',
key: 'name',
render: (name: string) => (
<div className="flex items-center">
<Tag color={getCategoryColorHex(name)} className="mr-2">
{name}
</Tag>
</div>
),
},
{
title: '创建时间',
@@ -92,7 +100,7 @@ const QuestionCategoryPage = () => {
render: (text: string) => new Date(text).toLocaleString(),
},
{
title: '操作',
title: <div style={{ textAlign: 'center' }}></div>,
key: 'action',
render: (_: any, record: QuestionCategory) => (
<Space>

View File

@@ -32,6 +32,7 @@ import {
import * as XLSX from 'xlsx';
import { questionAPI } from '../../services/api';
import { questionTypeMap, questionTypeColors } from '../../utils/validation';
import { getCategoryColorHex } from '../../lib/categoryColors';
const { Option } = Select;
const { TextArea } = Input;
@@ -403,7 +404,10 @@ const QuestionManagePage = () => {
dataIndex: 'category',
key: 'category',
width: 120,
render: (category: string) => <span>{category || '通用'}</span>,
render: (category: string) => {
const cat = category || '通用';
return <Tag color={getCategoryColorHex(cat)}>{cat}</Tag>;
},
},
{
title: '分值',
@@ -420,7 +424,7 @@ const QuestionManagePage = () => {
render: (date: string) => new Date(date).toLocaleDateString(),
},
{
title: '操作',
title: <div style={{ textAlign: 'center' }}></div>,
key: 'action',
width: 120,
render: (_: any, record: Question) => (

View File

@@ -119,7 +119,7 @@ const UserGroupManage = () => {
render: (text: string) => dayjs(text).format('YYYY-MM-DD HH:mm'),
},
{
title: '操作',
title: <div style={{ textAlign: 'center' }}></div>,
key: 'action',
render: (_: any, record: UserGroup) => (
<Space>

View File

@@ -347,7 +347,7 @@ const UserManagePage = () => {
render: (text: string) => new Date(text).toLocaleString(),
},
{
title: '操作',
title: <div style={{ textAlign: 'center' }}></div>,
key: 'action',
render: (_: any, record: User) => (
<Space>

View File

@@ -81,7 +81,7 @@ const UserRecordsPage = ({ userId }: { userId: string }) => {
},
},
{
title: '操作',
title: <div style={{ textAlign: 'center' }}></div>,
key: 'action',
render: (_: any, record: Record) => (
<Button type="link" onClick={() => handleViewDetail(record.id)}>