# 题目管理系统设计方案 **日期**: 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
暂无需审核的题目
您可以:
• 使用上方配置生成新题目
• 所有题目已审核完毕 ✨
正在加载待审核题目...