diff --git a/.plan/MEVsZvRZopyrJGc3cNsiN.md b/.plan/MEVsZvRZopyrJGc3cNsiN.md index 76aa048..975f62e 100644 --- a/.plan/MEVsZvRZopyrJGc3cNsiN.md +++ b/.plan/MEVsZvRZopyrJGc3cNsiN.md @@ -40,8 +40,8 @@ - `src/api/dataFactoryApi.js` 2. 统一复用 `src/utils/request.js`。 3. 尽量映射到 REST 风格路径,例如: - - `/api/v1/projects/{projectId}/cases` - - `/api/v1/projects/{projectId}/plans` + - `/api/v1/projects/{projectId}/case` + - `/api/v1/projects/{projectId}/plan` - `/api/v1/projects/{projectId}/reports` - `/api/v1/projects/{projectId}/data/*` 4. 对分页、详情、创建、更新、执行、导出等接口做最小可用封装。 diff --git a/.plan/fsKfEW4Z6YGlqrLCshlxS.md b/.plan/fsKfEW4Z6YGlqrLCshlxS.md new file mode 100644 index 0000000..7fee5a9 --- /dev/null +++ b/.plan/fsKfEW4Z6YGlqrLCshlxS.md @@ -0,0 +1,165 @@ +# 执行计划:用例管理与模块管理改造 + +## 目标 +根据后端 `Case 与 Module` 接口文档,改造前端接口调用与用例管理页面: +- 用例管理页面包含两个表格:模块列表、用例列表。 +- 模块支持查询、新增、删除,并展示模块字段与搜索条件。 +- 用例列表支持项目名称、用例标题模糊搜索、优先级、是否自动化、标签等条件。 +- 用例列表展示项目名称、用例编号、用例标题、优先级、类型、状态、是否实现自动化、标签等字段。 + +## 已定位文件 +- `src/api/caseApi.js` + - 已有 `module/tree/create/update/delete` 与 `case/list/detail/create/update/delete` 封装。 + - 当前部分参数仍使用旧字段:`project_id`、`case_id` 等,需要调整为文档里的 camelCase 参数。 +- `src/components/TestPlatform/Case/CaseList.vue` + - 当前只有一个用例表格。 + - 当前搜索条件只有项目ID、关键词、优先级。 + - 当前缺少分页状态定义与模块管理区。 +- `src/components/TestPlatform/Case/CaseEditor.vue` + - 当前表单字段使用 `case_type` 等后端返回字段,提交时需要按文档转为 `caseType`、`moduleId`、`caseKey`、`isAuto` 等。 +- `src/router/index.js` + - 用例相关路由已存在,无需新增路由。 + +## 改造步骤 + +### 1. 调整接口封装 `src/api/caseApi.js` +- 保留已有函数名,降低页面调用改动成本。 +- `getCaseList(projectId, params)` 改为请求参数包含: + - `projectId` + - `pageNo` + - `pageSize` + - `projectName` + - `moduleId` + - `keyword` + - `priority` + - `caseType` + - `status` + - `isAuto` + - `tag` +- `getCaseDetail(projectId, caseId)` 改为传 `caseId`,必要时兼容 `projectId`。 +- `createCase(projectId, data)` 改为提交 `projectId`,不再提交 `project_id`。 +- `updateCase(projectId, caseId, data)` 改为提交 `caseId`,不再提交 `id/project_id` 旧字段。 +- `deleteCase(projectId, caseId)` 改为提交 `{ caseId }`,必要时保留 `projectId` 不影响后端。 +- 模块接口保持: + - `getModuleTree(params)` -> `/module/tree` + - `createModule(data)` -> `/module/create` + - `deleteModule(data)` -> `/module/delete` + - 删除模块调用传 `{ moduleId }`。 + +### 2. 改造 `CaseList.vue` 页面布局 +- 在同一个页面内使用两个 `page-section`: + 1. `模块列表` + 2. `用例列表` +- 模块列表区: + - 搜索条件:`projectId`、`parentId`。 + - 按钮:查询、新增模块。 + - 表格字段: + - 模块ID:`id` + - 项目ID:`project_id` + - 父模块ID:`parent_id` + - 模块名称:`name` + - 排序:`sort_order` + - 路径:`path` + - 创建时间:`created_time` + - 更新时间:`updated_time` + - 操作:删除 + - 新增模块弹窗字段: + - `projectId` 必填 + - `name` 必填 + - `parentId` + - `sortOrder` + - `path` + - 删除模块调用 `deleteModule({ moduleId: row.id })`。 + +### 3. 改造用例列表搜索条件 +- 搜索表单字段: + - `projectId` + - `projectName` + - `keyword`(用例标题模糊搜索) + - `priority` + - `caseType` + - `status` + - `isAuto` + - `tag` +- 查询时传入分页:`pageNo`、`pageSize`。 +- 查询成功后兼容读取:`data.list || data.items || []`,`data.total || list.length`。 + +### 4. 改造用例列表表格字段展示 +- 表格字段: + - 项目名称:`project_name` + - 用例编号:`case_key` + - 用例标题:`title` + - 优先级:`priority`,格式化为 `P0/P1/P2/P3` + - 类型:`case_type`,格式化为 `功能/性能/安全/接口` + - 状态:`status`,格式化为 `正常/已废弃/评审中` + - 是否实现自动化:`is_auto`,格式化为 `已实现/未实现` + - 标签:`tags`,数组使用 `el-tag` 展示,字符串兜底直接展示 + - 操作:编辑、评审、删除 +- 删除用例调用 `deleteCase(projectId, row.id)`。 + +### 5. 补齐分页与交互逻辑 +- `CaseList.vue` 增加: + - `pageNo` + - `pageSize` + - `total` + - `moduleLoading` + - `moduleData` + - `moduleQueryForm` + - `moduleDialogVisible` + - `moduleSubmitting` + - `moduleForm` + - `moduleRules` +- 增加方法: + - `fetchModuleList` + - `openModuleCreate` + - `resetModuleForm` + - `submitModuleCreate` + - `removeModule` + - `handleSizeChange` + - `handleCurrentChange` + - `formatPriority` + - `formatCaseType` + - `formatStatus` + - `formatIsAuto` + - `formatTags` + +### 6. 调整 `CaseEditor.vue` 提交字段 +- 详情加载:兼容后端 snake_case 返回字段,映射到表单: + - `module_id` -> `moduleId` + - `case_key` -> `caseKey` + - `case_type` -> `caseType` + - `is_auto` -> `isAuto` +- 表单补充字段: + - 模块ID + - 用例编号 + - 状态 + - 是否自动化 +- 保存前构造 payload: + - `projectId` + - `moduleId` + - `caseKey` + - `title` + - `preconditions` + - `steps` + - `priority` + - `caseType` + - `tags` + - `status` + - `isAuto` +- 新增调用 `createCase(projectId, payload)`。 +- 编辑调用 `updateCase(projectId, caseId, payload)`。 + +### 7. 验证 +- 优先运行:`npm run build`。 +- 如构建耗时或环境缺依赖,至少做静态检查:确认 Vue 模板字段、方法引用、导入函数均存在。 + +## 预期改动范围 +- 修改:`src/api/caseApi.js` +- 修改:`src/components/TestPlatform/Case/CaseList.vue` +- 修改:`src/components/TestPlatform/Case/CaseEditor.vue` +- 不新增路由,不新增依赖。 + +## 风险与兼容处理 +- 后端返回字段是 snake_case,前端提交字段是 camelCase,需要在编辑页做映射。 +- 若后端仍兼容旧字段,当前改造会优先使用新文档字段;删除/详情可保留少量兼容参数但不依赖旧字段。 +- 模块树接口可能返回树形结构,但当前需求是模块列表 table;表格直接展示返回 list,不做树形展开,除非后续明确需要树表。 \ No newline at end of file diff --git a/index.html b/index.html index e3f539d..a87da34 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,7 @@ 造数管理系统 + + + diff --git a/src/components/Bug/BugEditor.vue b/src/components/Bug/BugEditor.vue new file mode 100644 index 0000000..9bec5b5 --- /dev/null +++ b/src/components/Bug/BugEditor.vue @@ -0,0 +1,815 @@ + + + + + diff --git a/src/components/Bug/BugList.vue b/src/components/Bug/BugList.vue new file mode 100644 index 0000000..ffd651f --- /dev/null +++ b/src/components/Bug/BugList.vue @@ -0,0 +1,721 @@ + + + + + diff --git a/src/components/Bug/BugStats.vue b/src/components/Bug/BugStats.vue new file mode 100644 index 0000000..bc722c7 --- /dev/null +++ b/src/components/Bug/BugStats.vue @@ -0,0 +1,701 @@ + + + + + diff --git a/src/components/Bug/BugStepsRichEditor.vue b/src/components/Bug/BugStepsRichEditor.vue new file mode 100644 index 0000000..623cf63 --- /dev/null +++ b/src/components/Bug/BugStepsRichEditor.vue @@ -0,0 +1,212 @@ + + + + + diff --git a/src/components/CreateData/CreateDataInfo.vue b/src/components/CreateData/CreateDataInfo.vue index 7625bc7..d7dfd7e 100644 --- a/src/components/CreateData/CreateDataInfo.vue +++ b/src/components/CreateData/CreateDataInfo.vue @@ -128,7 +128,7 @@ export default { ItApiCreate(this.form).then(res => { if (res && res.success === true) { this.$message({type: 'success', message: this.form.sqlId ? '修改成功' : '新增成功'}) - this.$router.push({path: '/create/data'}) + this.$router.push({path: '/data-tools/db-builder'}) } else { this.$message({type: 'error', message: res.message || '保存失败'}) } @@ -138,7 +138,7 @@ export default { }) }, goBack() { - this.$router.push({path: '/create/data'}) + this.$router.push({path: '/data-tools/db-builder'}) } }, created() { diff --git a/src/components/EffektHome.vue b/src/components/EffektHome.vue index d210f0e..50b3577 100644 --- a/src/components/EffektHome.vue +++ b/src/components/EffektHome.vue @@ -1,24 +1,61 @@ @@ -62,6 +211,117 @@ export default { diff --git a/src/components/System/UserManage.vue b/src/components/System/UserManage.vue index 9c5acbb..28a312e 100644 --- a/src/components/System/UserManage.vue +++ b/src/components/System/UserManage.vue @@ -125,7 +125,7 @@ @@ -107,4 +1204,263 @@ export default { .page-wrap { padding: 20px; } + +.toolbar-wrap { + text-align: right; + margin-top: 8px; +} + +.case-action-buttons-item { + float: right; + margin-right: 0 !important; +} + +.more-filter-wrap { + padding: 4px 0; +} + +.more-filter-footer { + border-top: 1px solid #ebeef5; + text-align: right; + padding-top: 10px; +} + +.column-setting-wrap { + max-height: 320px; + overflow-y: auto; +} + +.column-setting-title { + color: #606266; + font-weight: 500; + margin-bottom: 8px; +} + +.case-import-panel { + border: 1px solid #ebeef5; + border-radius: 4px; + padding: 28px 24px; + text-align: center; +} + +.case-import-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 6px; + color: #fff; + background: #409eff; + font-weight: 600; + margin-bottom: 8px; +} + +.case-import-title { + font-size: 28px; + color: #303133; + margin-bottom: 6px; +} + +.case-import-subtitle { + color: #909399; + margin-bottom: 16px; +} + +.case-import-dropzone { + border: 1px dashed #dcdfe6; + border-radius: 4px; + padding: 28px 20px; + margin: 0 auto; + max-width: 520px; +} + +.case-import-drop-text { + color: #606266; + margin-bottom: 10px; +} + +.link-text { + color: #409eff; + cursor: pointer; +} + +.case-import-file-tip { + color: #909399; +} + +.case-import-file-name { + margin-top: 8px; + color: #303133; +} + +.case-import-actions { + margin-top: 18px; + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; +} + +.hidden-file-input { + display: none; +} + +.mindmap-wrap { + margin-top: 8px; + border: 1px solid #ebeef5; + border-radius: 6px; + background: #fafcff; + padding: 14px 12px; + min-height: 280px; + overflow: auto; +} + +.xmind-tree { + min-width: 980px; + background: transparent; +} + +.xmind-tree /deep/ .el-tree-node { + position: relative; +} + +.xmind-tree /deep/ .el-tree-node__content { + height: auto; + padding: 8px 0; +} + +.xmind-tree /deep/ .el-tree-node__children { + position: relative; + margin-left: 12px; + padding-left: 22px; +} + +.xmind-tree /deep/ .el-tree-node__children:before { + content: ''; + position: absolute; + left: 8px; + top: 0; + bottom: 10px; + border-left: 1px solid #b7d0e8; +} + +.xmind-tree /deep/ .el-tree-node__content:before { + content: ''; + position: absolute; + left: -14px; + top: 50%; + width: 14px; + border-top: 1px solid #8fb8de; +} + +.xmind-tree /deep/ .el-tree > .el-tree-node > .el-tree-node__content:before { + display: none; +} + +.mindmap-node { + display: inline-flex; + flex-direction: column; + gap: 6px; + min-width: 220px; + max-width: 760px; + background: #fff; + border: 1px solid #dce6ff; + border-radius: 8px; + padding: 8px 10px; + box-shadow: 0 1px 4px rgba(64, 158, 255, 0.08); +} + +.mindmap-node-wrap { + display: inline-flex; + align-items: flex-start; +} + +.mindmap-node-title { + color: #303133; + font-weight: 600; + line-height: 1.4; +} + +.mindmap-node-project { + border-color: #67c23a; + box-shadow: 0 1px 6px rgba(103, 194, 58, 0.16); +} + +.mindmap-node-module { + border-color: #e6a23c; + box-shadow: 0 1px 6px rgba(230, 162, 60, 0.14); +} + +.mindmap-node-case { + border-color: #7fb3e3; +} + +.mindmap-inline-detail { + display: inline-flex; + align-items: stretch; + margin-left: 14px; +} + +.mindmap-inline-detail-line { + width: 18px; + margin-top: 22px; + border-top: 1px solid #8fb8de; +} + +.mindmap-inline-detail-card { + width: 420px; + max-width: 620px; + background: #fff; + border: 1px solid #d9e8f6; + border-radius: 8px; + padding: 10px 12px; + box-shadow: 0 1px 6px rgba(64, 158, 255, 0.12); +} + +.mindmap-inline-detail-title { + color: #303133; + font-weight: 600; +} + +.mindmap-inline-detail-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 8px; +} + +.mindmap-inline-detail-item { + color: #606266; + line-height: 1.6; + margin-bottom: 6px; + white-space: pre-wrap; +} + +.mindmap-inline-detail-item:last-child { + margin-bottom: 0; +} + +.mindmap-inline-actions { + text-align: right; + margin-top: 8px; +} + +.mindmap-node-meta { + display: inline-flex; + align-items: center; + flex-wrap: wrap; + gap: 8px; +} + +.mindmap-meta-text { + color: #909399; + font-size: 12px; +} + +.mindmap-empty { + color: #909399; + text-align: center; + line-height: 220px; +} diff --git a/src/components/TestPlatform/DataFactory/BuilderEditor.vue b/src/components/TestPlatform/DataFactory/BuilderEditor.vue index c41ecfd..239c288 100644 --- a/src/components/TestPlatform/DataFactory/BuilderEditor.vue +++ b/src/components/TestPlatform/DataFactory/BuilderEditor.vue @@ -66,7 +66,7 @@ export default { this.saving = true createBuilder(this.projectId, Object.assign({}, this.form, { definition, input_schema })).then(() => { this.$message({ type: 'success', message: '造数器保存成功' }) - this.$router.push({ path: '/test-platform/data-factory/builders', query: { projectId: this.projectId } }) + this.$router.push({ path: '/data-tools/factory/builders', query: { projectId: this.projectId } }) }).finally(() => { this.saving = false }) diff --git a/src/components/TestPlatform/DataFactory/BuilderList.vue b/src/components/TestPlatform/DataFactory/BuilderList.vue index ec8b05d..4268ba0 100644 --- a/src/components/TestPlatform/DataFactory/BuilderList.vue +++ b/src/components/TestPlatform/DataFactory/BuilderList.vue @@ -72,13 +72,13 @@ export default { }) }, goEditor(row) { - this.$router.push({ path: '/test-platform/data-factory/editor', query: { projectId: this.projectId, builderId: row && row.id } }) + this.$router.push({ path: '/data-tools/factory/editor', query: { projectId: this.projectId, builderId: row && row.id } }) }, goTasks() { - this.$router.push({ path: '/test-platform/data-factory/tasks', query: { projectId: this.projectId } }) + this.$router.push({ path: '/data-tools/factory/task', query: { projectId: this.projectId } }) }, goMock() { - this.$router.push({ path: '/test-platform/data-factory/mock', query: { projectId: this.projectId } }) + this.$router.push({ path: '/data-tools/factory/mock', query: { projectId: this.projectId } }) }, execute(row) { executeBuilder(this.projectId, row.id, { params: { count: 1 }, async: true }).then(() => { diff --git a/src/components/TestPlatform/Plan/PlanBuilder.vue b/src/components/TestPlatform/Plan/PlanBuilder.vue index d5d5852..385e132 100644 --- a/src/components/TestPlatform/Plan/PlanBuilder.vue +++ b/src/components/TestPlatform/Plan/PlanBuilder.vue @@ -1,26 +1,100 @@