746 lines
20 KiB
Markdown
746 lines
20 KiB
Markdown
# 题目管理系统设计方案
|
||
|
||
**日期**: 2026-05-16
|
||
**基于**: 前端优化方案与设计规范.md
|
||
**模式**: 集成式设计(生成→待审核列表→操作)
|
||
|
||
---
|
||
|
||
## 一、系统架构
|
||
|
||
### 1.1 数据流图
|
||
|
||
```
|
||
[用户操作]
|
||
↓
|
||
[GeneratePanel.vue]
|
||
├─ 配置区域 (文件选择/题型/难度)
|
||
├─ 生成按钮 → POST /api/exam/generate
|
||
↓
|
||
[待审核列表区域]
|
||
├─ 初始化: GET /api/question/pending (自动加载)
|
||
├─ 搜索/筛选
|
||
├─ 题目卡片展示
|
||
│ ├─ 审批: PUT /api/question/review/{id}
|
||
│ ├─ 编辑: PUT /api/question/{id}
|
||
│ └─ 发布: PUT /api/question/review/{id} (status=published)
|
||
└─ 批量操作
|
||
```
|
||
|
||
### 1.2 状态管理
|
||
|
||
```javascript
|
||
// useQuestionManagement.js (新增composable)
|
||
const state = reactive({
|
||
// 生成相关
|
||
isGenerating: false,
|
||
generatedCount: 0,
|
||
|
||
// 列表相关
|
||
pendingQuestions: [], // 待审核题目列表
|
||
loading: false,
|
||
total: 0,
|
||
currentPage: 1,
|
||
pageSize: 10,
|
||
|
||
// 筛选条件
|
||
searchKeyword: '',
|
||
statusFilter: 'all', // all/pending/approved/rejected/published
|
||
typeFilter: 'all', // all/single_choice/multiple_choice/...
|
||
|
||
// 编辑状态
|
||
editingQuestion: null, // 当前编辑的题目
|
||
showEditModal: false,
|
||
|
||
// 批量操作
|
||
selectedIds: [], // 选中的题目ID
|
||
})
|
||
```
|
||
|
||
### 1.3 接口调用规划
|
||
|
||
| 功能 | 方法 | 接口 | 触发时机 |
|
||
|------|------|------|---------|
|
||
| **生成题目** | `POST` | `/api/exam/generate` | 点击"开始生成"按钮 |
|
||
| **获取待审核列表** | `GET` | `/api/question/pending` | 页面加载/生成完成/刷新 |
|
||
| **搜索题目** | `GET` | `/api/question/pending?keyword=xxx` | 输入搜索关键词 |
|
||
| **筛选题目** | `GET` | `/api/question/pending?status=xxx&type=xxx` | 选择筛选条件 |
|
||
| **审批通过** | `PUT` | `/api/question/review/{id}?status=approved` | 点击"通过"按钮 |
|
||
| **驳回题目** | `PUT` | `/api/question/review/{id}?status=rejected&comment=xxx` | 点击"驳回"+填写原因 |
|
||
| **编辑题目** | `PUT` | `/api/question/{id}` | 点击"编辑"→修改→保存 |
|
||
| **发布题目** | `PUT` | `/api/question/review/{id}?status=published` | 点击"发布"按钮 |
|
||
| **批量审批** | `PUT` | `/api/question/batch-review` | 选择多条→批量操作 |
|
||
| **删除题目** | `DELETE` | `/api/question/{id}` | 点击"删除" |
|
||
|
||
---
|
||
|
||
## 二、UI组件设计(遵循设计规范)
|
||
|
||
### 2.1 组件结构树
|
||
|
||
```
|
||
GeneratePanel.vue (主容器)
|
||
├── PageHeader.vue (标题栏)
|
||
├── StatsRow.vue (统计信息)
|
||
├── ContentCard: "智能出题配置"
|
||
│ ├── FileSelector (文件选择器)
|
||
│ ├── DifficultySelect (难度选择)
|
||
│ ├── TypeConfigGrid (题型数量配置)
|
||
│ └── GenerateButton (生成按钮)
|
||
├── ContentCard: "待审核题目管理"
|
||
│ ├── Toolbar (工具栏)
|
||
│ │ ├── SearchInput (搜索框)
|
||
│ │ ├── StatusFilter (状态筛选)
|
||
│ │ ├── TypeFilter (题型筛选)
|
||
│ │ └── BatchActions (批量操作按钮组)
|
||
│ ├── QuestionList (题目列表)
|
||
│ │ └── QuestionCard (题目卡片) *N
|
||
│ │ ├── QuestionHeader (题号+类型+状态)
|
||
│ │ ├── QuestionContent (题干内容)
|
||
│ │ ├── QuestionOptions (选项展示)
|
||
│ │ └── ActionButtons (操作按钮组)
|
||
│ └── Pagination (分页器)
|
||
└── EditModal.vue (编辑弹窗) [可选]
|
||
```
|
||
|
||
### 2.2 题目卡片设计(QuestionCard)
|
||
|
||
#### 视觉规范
|
||
|
||
```css
|
||
/* 白色体系 + 设计规范 */
|
||
.question-card {
|
||
background: #FFFFFF;
|
||
border: 1px solid #E9ECEF; /* --border-light */
|
||
border-radius: 8px; /* --radius-lg */
|
||
box-shadow: 0 1px 2px rgba(0,0,0,0.04); /* --shadow-sm */
|
||
padding: 16px; /* --space-lg */
|
||
margin-bottom: 12px; /* --space-md */
|
||
transition: all 0.15s ease; /* --transition-fast */
|
||
}
|
||
|
||
.question-card:hover {
|
||
border-color: #DEE2E6; /* --border-default */
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.06); /* --shadow-md */
|
||
}
|
||
```
|
||
|
||
#### 卡片内容布局
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ [☑] 第1题 [单选题] [待审核] 中等难度 │ ← Header
|
||
├─────────────────────────────────────────────────────┤
|
||
│ 在按价位段自选投放工作指引中,用于评估样本零售客户 │ ← Content
|
||
│ 库存水平与销售速度匹配程度的核心公式是? │
|
||
│ │
|
||
│ A. 客户实际订货数量/订货客户订单提报需求数量*100% │ ← Options
|
||
│ B. 样本零售客户期末库存/月销量 │
|
||
│ C. 实际零售价格/零售指导价格*100% │
|
||
│ D. 特定聚类内市场状态结果按销量加权平均 │
|
||
│ │
|
||
│ [✓ 通过] [✗ 驳回] [✎ 编辑] [📤 发布] [🗑️ 删除] │ ← Actions
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### 状态徽章设计
|
||
|
||
```css
|
||
/* 状态标识 - 仅使用功能色 */
|
||
.status-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: 2px 8px;
|
||
border-radius: 4px; /* --radius-sm */
|
||
font-size: 12px; /* --font-sm */
|
||
font-weight: 500; /* --weight-medium */
|
||
}
|
||
|
||
.status-pending {
|
||
background: #FFF9DB; /* --bg-warning */
|
||
color: #E67700; /* --color-warning */
|
||
border: 1px solid #FFE066;
|
||
}
|
||
|
||
.status-approved {
|
||
background: #EBFBEE; /* --bg-success */
|
||
color: #2B8A3E; /* --color-success */
|
||
border: 1px solid #B2F2BB;
|
||
}
|
||
|
||
.status-rejected {
|
||
background: #FFF5F5; /* --bg-error */
|
||
color: #C92A2A; /* --color-error */
|
||
border: 1px solid #FFC9C9;
|
||
}
|
||
|
||
.status-published {
|
||
background: #E7F5FF; /* --bg-info */
|
||
color: #1C7ED6; /* --color-info */
|
||
border: 1px solid #A5D8FF;
|
||
}
|
||
```
|
||
|
||
### 2.3 操作按钮设计
|
||
|
||
```css
|
||
/* 按钮组 - 遵循设计规范 5.3 */
|
||
.action-buttons {
|
||
display: flex;
|
||
gap: 6px; /* --space-xs + 2px */
|
||
margin-top: 12px; /* --space-md */
|
||
padding-top: 12px;
|
||
border-top: 1px solid #E9ECEF; /* --border-light */
|
||
}
|
||
|
||
.btn-action {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
padding: 4px 12px; /* 紧凑尺寸 */
|
||
border-radius: 6px; /* --radius-md */
|
||
font-size: 12px; /* --font-sm */
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.15s ease;
|
||
border: 1px solid transparent;
|
||
}
|
||
|
||
.btn-approve {
|
||
background: #EBFBEE; /* 浅绿底 */
|
||
color: #2B8A3E; /* 绿色文字 */
|
||
border-color: #B2F2BB;
|
||
}
|
||
.btn-approve:hover {
|
||
background: #D3F9D8;
|
||
}
|
||
|
||
.btn-reject {
|
||
background: #FFF5F5; /* 浅红底 */
|
||
color: #C92A2A; /* 红色文字 */
|
||
border-color: #FFC9C9;
|
||
}
|
||
.btn-reject:hover {
|
||
background: #FFE3E3;
|
||
}
|
||
|
||
.btn-edit {
|
||
background: #FFFFFF;
|
||
color: #495057; /* --text-secondary */
|
||
border-color: #DEE2E6; /* --border-default */
|
||
}
|
||
.btn-edit:hover {
|
||
background: #F8F9FA; /* --bg-container */
|
||
border-color: #ADB5BD;
|
||
}
|
||
|
||
.btn-publish {
|
||
background: #E7F5FF; /* 浅蓝底 */
|
||
color: #1C7ED6; /* 蓝色文字 */
|
||
border-color: #A5D8FF;
|
||
}
|
||
.btn-publish:hover {
|
||
background: #D0EBFF;
|
||
}
|
||
|
||
.btn-delete {
|
||
background: #FFFFFF;
|
||
color: #C92A2A; /* 红色文字 */
|
||
border-color: #FFC9C9;
|
||
}
|
||
.btn-delete:hover {
|
||
background: #FFF5F5;
|
||
}
|
||
```
|
||
|
||
### 2.4 工具栏设计(Toolbar)
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ 🔍 搜索题目... [状态: 全部 ▼] [题型: 全部 ▼] │
|
||
│ [✓ 批量通过] [✗ 批量驳回] [刷新] │
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
```css
|
||
.toolbar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 12px 16px;
|
||
background: #F8F9FA; /* --bg-container */
|
||
border: 1px solid #E9ECEF;
|
||
border-radius: 8px; /* --radius-lg */
|
||
margin-bottom: 16px; /* --space-lg */
|
||
}
|
||
|
||
.search-input {
|
||
flex: 1;
|
||
max-width: 320px;
|
||
height: 36px; /* 表单规范 */
|
||
padding: 6px 12px;
|
||
border: 1px solid #DEE2E6;
|
||
border-radius: 6px;
|
||
font-size: 13px; /* --font-md */
|
||
}
|
||
|
||
.filter-select {
|
||
height: 36px;
|
||
padding: 6px 12px;
|
||
border: 1px solid #DEE2E6;
|
||
border-radius: 6px;
|
||
font-size: 13px;
|
||
margin-left: 8px;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 三、交互流程设计
|
||
|
||
### 3.1 生成题目流程
|
||
|
||
```
|
||
1. 用户配置参数(文件/题型/难度)
|
||
↓
|
||
2. 点击"开始生成"按钮
|
||
↓
|
||
3. 显示loading状态 + 进度提示
|
||
↓
|
||
4. 调用 POST /api/exam/generate
|
||
↓
|
||
5. 轮询检查生成状态(已有逻辑)
|
||
↓
|
||
6. 生成完成后:
|
||
- 显示成功提示:"成功生成 X 道题目"
|
||
- 自动调用 GET /api/question/pending 刷新列表
|
||
- 滚动到待审核列表区域
|
||
↓
|
||
7. 展示待审核题目列表
|
||
```
|
||
|
||
### 3.2 审批流程
|
||
|
||
```
|
||
单个审批:
|
||
1. 用户点击"通过"/"驳回"按钮
|
||
↓
|
||
2. 弹出确认对话框(驳回时需要填写原因)
|
||
↓
|
||
3. 调用 PUT /api/question/review/{id}?status=approved/rejected
|
||
↓
|
||
4. 更新本地状态(乐观更新)
|
||
↓
|
||
5. 刷新列表数据
|
||
|
||
批量审批:
|
||
1. 用户勾选多个题目复选框
|
||
↓
|
||
2. 点击"批量通过"/"批量驳回"
|
||
↓
|
||
3. 弹出确认对话框
|
||
↓
|
||
4. 循环调用单个接口 或 调用批量接口
|
||
↓
|
||
5. 刷新列表
|
||
```
|
||
|
||
### 3.3 编辑流程
|
||
|
||
```
|
||
方式1:内联编辑(推荐简单字段)
|
||
1. 点击"编辑"按钮
|
||
↓
|
||
2. 题目卡片变为编辑模式
|
||
↓
|
||
3. 直接修改题干/选项
|
||
↓
|
||
4. 点击"保存"/"取消"
|
||
|
||
方式2:弹窗编辑(推荐复杂编辑)
|
||
1. 点击"编辑"按钮
|
||
↓
|
||
2. 打开 EditModal 弹窗
|
||
↓
|
||
3. 完整的表单编辑界面
|
||
↓
|
||
4. 点击"保存"/"取消"
|
||
```
|
||
|
||
---
|
||
|
||
## 四、响应式与滚动策略
|
||
|
||
### 4.1 布局原则(解决之前的滚动问题)
|
||
|
||
```css
|
||
/* 主容器 - 允许自然流动,不限制高度 */
|
||
.generate-panel {
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 0; /* 允许收缩 */
|
||
/* 移除 height: 100% 和 overflow: hidden */
|
||
}
|
||
|
||
/* 配置面板 - 固定不滚动 */
|
||
.config-panel {
|
||
flex-shrink: 0; /* 不压缩 */
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
/* 结果面板 - 内容可撑开 */
|
||
.result-panel {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
/* 不设置 max-height 或 overflow */
|
||
}
|
||
|
||
/* 题目列表 - 自然流动 */
|
||
.question-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
/* 不设置 overflow-y: auto */
|
||
}
|
||
```
|
||
|
||
**效果**:
|
||
- ✅ 配置区域固定在顶部
|
||
- ✅ 题目列表随内容自然增长
|
||
- ✅ 当内容超出视口时,整个页面可滚动
|
||
- ✅ 无局部滚动条
|
||
|
||
### 4.2 分页策略
|
||
|
||
当题目数量较多时(>20道),使用分页:
|
||
|
||
```javascript
|
||
const pagination = reactive({
|
||
current: 1,
|
||
pageSize: 10, // 每页10道
|
||
total: 0,
|
||
showSizeChanger: true,
|
||
showQuickJumper: true,
|
||
pageSizeOptions: ['10', '20', '50'],
|
||
})
|
||
|
||
// 分页变化时重新请求
|
||
const handlePageChange = async (page, size) => {
|
||
pagination.current = page
|
||
pagination.pageSize = size
|
||
await fetchPendingQuestions()
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 五、错误处理与边界情况
|
||
|
||
### 5.1 错误处理
|
||
|
||
| 场景 | 处理方式 |
|
||
|------|---------|
|
||
| 生成失败 | 显示错误消息,保留配置不变 |
|
||
| 网络超时 | 提供重试按钮 |
|
||
| 审批接口失败 | 回滚到之前状态,显示错误 |
|
||
| 并发冲突 | 提示"数据已被其他人修改",刷新列表 |
|
||
| 权限不足 | 显示"无权限操作",隐藏操作按钮 |
|
||
|
||
### 5.2 空状态处理
|
||
|
||
```html
|
||
<!-- 无待审核题目 -->
|
||
<div class="empty-state">
|
||
<div class="empty-icon">📋</div>
|
||
<p class="empty-title">暂无需审核的题目</p>
|
||
<p class="empty-hint">
|
||
您可以:<br/>
|
||
• 使用上方配置生成新题目<br/>
|
||
• 所有题目已审核完毕 ✨
|
||
</p>
|
||
</div>
|
||
```
|
||
|
||
### 5.3 加载状态
|
||
|
||
```html
|
||
<!-- 加载中 -->
|
||
<div class="loading-state">
|
||
<a-spin size="large" />
|
||
<p>正在加载待审核题目...</p>
|
||
</div>
|
||
|
||
<!-- 生成中 -->
|
||
<div class="generating-state">
|
||
<a-spin tip="AI正在生成题目,请耐心等待..." />
|
||
<progress :percent="generateProgress" />
|
||
</div>
|
||
```
|
||
|
||
---
|
||
|
||
## 六、实施计划
|
||
|
||
### Phase 1: 核心功能(优先级:P0)
|
||
|
||
**目标**:实现基本的生成+展示+审批流程
|
||
|
||
1. **修改 useGenerateState.js**
|
||
- ✅ 修复接口调用:生成后调用 `/question/pending`
|
||
- ✅ 添加 `question_count` 参数
|
||
- ✅ 保留现有的轮询逻辑
|
||
|
||
2. **创建 useQuestionManagement.js**
|
||
- 实现 `fetchPendingQuestions()`
|
||
- 实现 `approveQuestion(id)`
|
||
- 实现 `rejectQuestion(id, comment)`
|
||
- 实现搜索/筛选逻辑
|
||
|
||
3. **重构 GeneratePanel.vue**
|
||
- 添加工具栏(搜索/筛选)
|
||
- 重构题目列表为卡片式
|
||
- 添加操作按钮组
|
||
- 实现分页功能
|
||
|
||
### Phase 2: 编辑功能(优先级:P1)
|
||
|
||
4. **创建 EditModal.vue**
|
||
- 题干编辑器
|
||
- 选项编辑器(动态增删)
|
||
- 答案设置
|
||
- 表单验证
|
||
|
||
5. **集成编辑功能**
|
||
- 调用 `PUT /question/{id}`
|
||
- 保存后刷新列表
|
||
|
||
### Phase 3: 批量操作(优先级:P2)
|
||
|
||
6. **实现批量选择**
|
||
- 复选框
|
||
- 全选/反选
|
||
- 批量审批接口
|
||
|
||
### Phase 4: 体验优化(优先级:P3)
|
||
|
||
7. **性能优化**
|
||
- 虚拟滚动(大量题目时)
|
||
- 防抖搜索
|
||
- 缓存策略
|
||
|
||
8. **视觉优化**
|
||
- 动画过渡
|
||
- 拖拽排序
|
||
- 快捷键支持
|
||
|
||
---
|
||
|
||
## 七、技术要点
|
||
|
||
### 7.1 关键代码示例
|
||
|
||
#### 获取待审核题目
|
||
|
||
```javascript
|
||
// useQuestionManagement.js
|
||
const fetchPendingQuestions = async () => {
|
||
loading.value = true
|
||
|
||
try {
|
||
const params = {
|
||
page: pagination.current,
|
||
pageSize: pagination.pageSize,
|
||
}
|
||
|
||
if (searchKeyword.value) {
|
||
params.keyword = searchKeyword.value
|
||
}
|
||
|
||
if (statusFilter.value !== 'all') {
|
||
params.status = statusFilter.value
|
||
}
|
||
|
||
if (typeFilter.value !== 'all') {
|
||
params.type = typeFilter.value
|
||
}
|
||
|
||
const res = await questionAPI.getPendingQuestions(params)
|
||
|
||
pendingQuestions.value = transformQuestionData(res.data?.records || [])
|
||
pagination.total = res.data?.total || 0
|
||
|
||
console.log(`✅ 获取待审核题目: ${pendingQuestions.value.length} 条`)
|
||
} catch (error) {
|
||
console.error('❌ 获取待审核题目失败:', error)
|
||
message.error('加载题目列表失败')
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 审批操作
|
||
|
||
```javascript
|
||
const approveQuestion = async (questionId) => {
|
||
try {
|
||
await questionAPI.reviewQuestion(questionId, 'approved')
|
||
|
||
message.success('审批通过 ✓')
|
||
|
||
// 乐观更新:立即更新UI
|
||
const index = pendingQuestions.value.findIndex(q => q.id === questionId)
|
||
if (index !== -1) {
|
||
pendingQuestions.value[index].status = 'approved'
|
||
}
|
||
|
||
// 可选:延迟刷新确保数据一致性
|
||
setTimeout(() => fetchPendingQuestions(), 500)
|
||
|
||
} catch (error) {
|
||
console.error('❌ 审批失败:', error)
|
||
message.error('审批操作失败,请重试')
|
||
|
||
// 回滚:重新获取数据
|
||
await fetchPendingQuestions()
|
||
}
|
||
}
|
||
|
||
const rejectQuestion = async (questionId, comment) => {
|
||
try {
|
||
await questionAPI.reviewQuestion(questionId, 'rejected', comment)
|
||
|
||
message.success('已驳回')
|
||
|
||
// 更新本地状态
|
||
const index = pendingQuestions.value.findIndex(q => q.id === questionId)
|
||
if (index !== -1) {
|
||
pendingQuestions.value[index].status = 'rejected'
|
||
}
|
||
|
||
setTimeout(() => fetchPendingQuestions(), 500)
|
||
|
||
} catch (error) {
|
||
message.error('驳回操作失败')
|
||
await fetchPendingQuestions()
|
||
}
|
||
}
|
||
```
|
||
|
||
### 7.2 数据转换函数(复用)
|
||
|
||
```javascript
|
||
// 与 useGenerateState.js 保持一致
|
||
const transformQuestionData = (rawQuestions) => {
|
||
return rawQuestions.map((q, qIdx) => {
|
||
let contentObj = {}
|
||
|
||
try {
|
||
if (typeof q.content === 'string' && q.content.startsWith('{')) {
|
||
contentObj = JSON.parse(q.content || '{}')
|
||
} else if (typeof q.content === 'object') {
|
||
contentObj = q.content
|
||
}
|
||
} catch (e) {
|
||
console.warn(`题目${qIdx} content解析失败:`, e.message)
|
||
}
|
||
|
||
return {
|
||
id: q.id,
|
||
questionId: q.questionId,
|
||
type: String(q.questionType || q.type || '').toLowerCase(),
|
||
typeLabel: formatQuestionType(q.questionType),
|
||
difficulty: q.difficulty || 2,
|
||
score: Number(q.score) || 5,
|
||
stem: contentObj.stem || q.stem || (typeof q.content === 'string' ? q.content : ''),
|
||
options: formatOptions(contentObj.options || q.options || []),
|
||
answer: q.answer || contentObj.answer || '',
|
||
status: q.status || 'pending',
|
||
reviewerComment: q.reviewerComment || '',
|
||
createdAt: q.createdAt,
|
||
updatedAt: q.updatedAt,
|
||
}
|
||
})
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 八、测试场景
|
||
|
||
### 8.1 功能测试
|
||
|
||
- [ ] 生成5道题目,验证列表显示正确
|
||
- [ ] 搜索题目,验证过滤结果准确
|
||
- [ ] 按状态筛选(待审核/已通过/已驳回/已发布)
|
||
- [ ] 单个审批通过/驳回
|
||
- [ ] 批量审批多道题目
|
||
- [ ] 编辑题目并保存
|
||
- [ ] 发布已通过的题目
|
||
- [ ] 删除题目(带确认)
|
||
- [ ] 分页切换(第1页/第2页/每页20条)
|
||
|
||
### 8.2 边界测试
|
||
|
||
- [ ] 生成了0道题目(空状态显示)
|
||
- [ ] 生成了100道题目(性能测试)
|
||
- [ ] 同时点击多次生成(防重复提交)
|
||
- [ ] 网络断开时的错误处理
|
||
- [ ] 权限不足时的UI反馈
|
||
|
||
### 8.3 UI/UX测试
|
||
|
||
- [ ] 页面滚动流畅(无局部滚动条)
|
||
- [ ] 按钮hover效果正常
|
||
- [ ] 加载动画显示正确
|
||
- [ ] 响应式布局适配不同屏幕
|
||
- [ ] 空状态提示友好
|
||
|
||
---
|
||
|
||
## 九、成功标准
|
||
|
||
### 9.1 功能完整性
|
||
|
||
✅ 用户可以完整执行以下流程:
|
||
1. 选择文件 → 配置参数 → 生成题目
|
||
2. 查看待审核题目列表
|
||
3. 对每道题目进行审批/编辑/发布
|
||
4. 搜索、筛选、分页浏览
|
||
|
||
### 9.2 用户体验
|
||
|
||
✅ 符合设计规范:
|
||
- 白色体系配色
|
||
- 紧凑但清晰的布局
|
||
- 一致的组件样式
|
||
- 流畅的交互动画
|
||
- 友好的错误提示
|
||
|
||
### 9.3 技术质量
|
||
|
||
✅ 代码质量:
|
||
- 无语法错误
|
||
- 接口调用正确
|
||
- 状态管理清晰
|
||
- 错误处理完善
|
||
- 性能可接受
|
||
|
||
---
|
||
|
||
## 十、后续扩展方向
|
||
|
||
1. **题目版本管理**:记录每次编辑的历史版本
|
||
2. **批量导入导出**:支持Excel格式导入/导出题目
|
||
3. **AI辅助审核**:AI自动预审,人工复核
|
||
4. **统计分析**:题目通过率、平均审核时间等
|
||
5. **权限细化**:角色权限(管理员/审核员/出题人)
|
||
6. **移动端适配**:响应式布局优化手机端体验
|
||
|
||
---
|
||
|
||
**文档版本**: v1.0
|
||
**最后更新**: 2026-05-16
|
||
**状态**: 待用户确认
|