From dca942bc8fa8ab57eb272d2bf2e7f1fc82f4eb0b Mon Sep 17 00:00:00 2001 From: qiaoxinjiu Date: Mon, 18 May 2026 10:01:35 +0800 Subject: [PATCH] =?UTF-8?q?feat(test-platform):=20AI=E7=94=9F=E6=88=90?= =?UTF-8?q?=E7=94=A8=E4=BE=8B=E3=80=81=E4=B8=9A=E5=8A=A1=E6=8A=80=E8=83=BD?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E4=B8=8E=E8=AE=A1=E5=88=92=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 用例管理增加 AI 生成用例 Tab、文档来源与技能/规则多选生成 - 新增业务技能与业务规则配置页及 API - 计划执行列表展示模块路径与名称,移除 Jenkins URL 列 Co-authored-by: Cursor --- src/api/caseApi.js | 26 + src/api/documentApi.js | 107 ++ src/api/skillRuleApi.js | 43 + src/components/Home.vue | 62 +- .../TestPlatform/Case/CaseEditor.vue | 2 +- src/components/TestPlatform/Case/CaseList.vue | 1203 ++++++++++++++++- .../TestPlatform/Case/DocumentSourcePanel.vue | 999 ++++++++++++++ .../TestPlatform/Plan/PlanExecute.vue | 17 +- .../SkillRule/BusinessSkillRuleConfig.vue | 820 +++++++++++ src/router/index.js | 7 + src/utils/bugStepsFormat.js | 8 +- 11 files changed, 3236 insertions(+), 58 deletions(-) create mode 100644 src/api/documentApi.js create mode 100644 src/api/skillRuleApi.js create mode 100644 src/components/TestPlatform/Case/DocumentSourcePanel.vue create mode 100644 src/components/TestPlatform/SkillRule/BusinessSkillRuleConfig.vue diff --git a/src/api/caseApi.js b/src/api/caseApi.js index bbdc390..116e440 100644 --- a/src/api/caseApi.js +++ b/src/api/caseApi.js @@ -82,6 +82,32 @@ export function deleteCase(projectId, caseId) { }) } +/** 恢复状态为 0 的用例为正常(1),POST body: { caseIds: number[] } */ +export function restoreCases(caseIds) { + const raw = Array.isArray(caseIds) ? caseIds : [caseIds] + const caseIdsNorm = raw + .map(id => Number(id)) + .filter(id => Number.isFinite(id) && id > 0) + return request({ + url: '/case/restore', + method: 'post', + data: { caseIds: caseIdsNorm } + }) +} + +/** + * 根据手工用例生成 UI / 接口自动化脚本(字段均为驼峰)。 + * 典型 body:projectId, caseId, automationType, prompt, caseKey, moduleName, productName, + * projectName, steps, expectedResults + */ +export function generateCaseAutomation(data) { + return request({ + url: '/case/generate-automation', + method: 'post', + data + }) +} + export function createCaseSnapshot(projectId, caseId) { return request({ url: '/case/snapshot/create', diff --git a/src/api/documentApi.js b/src/api/documentApi.js new file mode 100644 index 0000000..7b7c62e --- /dev/null +++ b/src/api/documentApi.js @@ -0,0 +1,107 @@ +import request from '@/utils/request' + +/** 文档列表 */ +export function getDocumentList(params) { + return request({ + url: '/document/list', + method: 'get', + params + }) +} + +/** 文档详情 */ +export function getDocumentDetail(params) { + return request({ + url: '/document/detail', + method: 'get', + params + }) +} + +/** 上传 PDF(multipart,单文件一次请求) */ +export function uploadDocumentPdf({ file, productId, projectId, createdBy }) { + const formData = new FormData() + formData.append('file', file) + formData.append('productId', productId) + formData.append('projectId', projectId) + if (createdBy != null && createdBy !== '') { + formData.append('createdBy', createdBy) + } + return request({ + url: '/document/upload', + method: 'post', + data: formData + }) +} + +/** 创建文档 */ +export function createDocument(data) { + return request({ + url: '/document/create', + method: 'post', + data + }) +} + +/** 更新文档 */ +export function updateDocument(data) { + return request({ + url: '/document/update', + method: 'post', + data + }) +} + +/** 删除文档 */ +export function deleteDocument(data) { + return request({ + url: '/document/delete', + method: 'post', + data + }) +} + +/** 刷新飞书文档 */ +export function refreshDocument(data) { + return request({ + url: '/document/refresh', + method: 'post', + data + }) +} + +/** 生成测试用例(预览) */ +export function generateDocumentCases(data) { + return request({ + url: '/document/generate-cases', + method: 'post', + data + }) +} + +/** 模块匹配 */ +export function matchDocumentModules(data) { + return request({ + url: '/document/match-modules', + method: 'post', + data + }) +} + +/** 导入测试用例 */ +export function importDocumentCases(data) { + return request({ + url: '/document/import-cases', + method: 'post', + data + }) +} + +/** 批量创建模块 */ +export function batchCreateDocumentModules(data) { + return request({ + url: '/document/batch-create-modules', + method: 'post', + data + }) +} diff --git a/src/api/skillRuleApi.js b/src/api/skillRuleApi.js new file mode 100644 index 0000000..835bb5b --- /dev/null +++ b/src/api/skillRuleApi.js @@ -0,0 +1,43 @@ +import request from '@/utils/request' + +/** Skill */ +export function getSkillList(params) { + return request({ url: '/skill/list', method: 'get', params: params || {} }) +} + +export function getSkillDetail(skillId) { + return request({ url: '/skill/detail', method: 'get', params: { skillId } }) +} + +export function createSkill(data) { + return request({ url: '/skill/create', method: 'post', data }) +} + +export function updateSkill(data) { + return request({ url: '/skill/update', method: 'post', data }) +} + +export function deleteSkill(skillId) { + return request({ url: '/skill/delete', method: 'post', data: { skillId } }) +} + +/** Business rule */ +export function getBusinessRuleList(params) { + return request({ url: '/business-rule/list', method: 'get', params: params || {} }) +} + +export function getBusinessRuleDetail(ruleId) { + return request({ url: '/business-rule/detail', method: 'get', params: { ruleId } }) +} + +export function createBusinessRule(data) { + return request({ url: '/business-rule/create', method: 'post', data }) +} + +export function updateBusinessRule(data) { + return request({ url: '/business-rule/update', method: 'post', data }) +} + +export function deleteBusinessRule(ruleId) { + return request({ url: '/business-rule/delete', method: 'post', data: { ruleId } }) +} diff --git a/src/components/Home.vue b/src/components/Home.vue index daa1ad0..a36abff 100644 --- a/src/components/Home.vue +++ b/src/components/Home.vue @@ -97,11 +97,12 @@ export default { const filteredMenus = this.filterMenus(this.userMenus) const menus = this.renameTestPlatformToCycle(filteredMenus) const sorted = this.sortMenusByProductOrder(menus) - const hasHome = sorted.some(menu => menu.path === '/effekt' || menu.name === '首页') + const withSkillMenu = this.injectBusinessSkillConfigMenu(sorted) + const hasHome = withSkillMenu.some(menu => menu.path === '/effekt' || menu.name === '首页') if (hasHome) { - return sorted + return withSkillMenu } - return [homeMenu, ...sorted] + return [homeMenu, ...withSkillMenu] }, displayName() { if (!this.currentUser) { @@ -129,6 +130,7 @@ export default { '/system/user': '/system/user', '/system/menu': '/system/menu', '/system/permission': '/system/permission', + '/test-platform/skill-rules': '/test-platform/skill-rules', '/bug': '/bug/list', '/bug/list': '/bug/list', '/bug/detail': '/bug/detail', @@ -166,6 +168,7 @@ export default { '产品管理': 'el-icon-box', '项目管理': 'el-icon-s-management', '用例管理': 'el-icon-document', + '业务技能配置': 'el-icon-collection', '测试计划': 'el-icon-date', '测试报告': 'el-icon-data-line', '测试工具': 'el-icon-s-tools', @@ -216,6 +219,59 @@ export default { return Object.assign({}, item, { name, children }) }) }, + /** + * 在「用例周期」分组下、「用例管理」上方插入「业务技能配置」(与后端菜单并存时去重)。 + */ + injectBusinessSkillConfigMenu(menus) { + const INJECT_PATH = '/test-platform/skill-rules' + const INJECT_KEY = '__inject_business_skill__' + const makeItem = () => ({ + name: '业务技能配置', + path: INJECT_PATH, + icon: 'el-icon-collection', + menuId: INJECT_KEY, + id: INJECT_KEY, + visible: 1, + status: 1, + children: [] + }) + const hasInjected = list => + (list || []).some(c => c.path === INJECT_PATH || c.menuId === INJECT_KEY || c.id === INJECT_KEY) + const mergeCycleChildren = children => { + if (!children || !children.length) return children || [] + if (hasInjected(children)) { + return children.map(c => + c.children && c.children.length + ? Object.assign({}, c, { children: this.injectBusinessSkillConfigMenu(c.children) }) + : c + ) + } + const next = children.map(c => + c.children && c.children.length + ? Object.assign({}, c, { children: this.injectBusinessSkillConfigMenu(c.children) }) + : c + ) + const idx = next.findIndex(c => { + const p = String(c.path || '') + return p === '/test-platform/case' || c.name === '用例管理' + }) + if (idx >= 0) { + next.splice(idx, 0, makeItem()) + } else { + next.unshift(makeItem()) + } + return next + } + return (menus || []).map(item => { + if (item.name === '用例周期' && item.children && item.children.length) { + return Object.assign({}, item, { children: mergeCycleChildren(item.children.slice()) }) + } + if (item.children && item.children.length) { + return Object.assign({}, item, { children: this.injectBusinessSkillConfigMenu(item.children) }) + } + return item + }) + }, /** 左侧栏顶级顺序:首页 → 用例周期 → Bug管理 → 造数 → 系统管理 → 其它 */ representativeMenuPath(menu) { const direct = String((menu && menu.path) || '').trim() diff --git a/src/components/TestPlatform/Case/CaseEditor.vue b/src/components/TestPlatform/Case/CaseEditor.vue index 5be2c14..f3a5633 100644 --- a/src/components/TestPlatform/Case/CaseEditor.vue +++ b/src/components/TestPlatform/Case/CaseEditor.vue @@ -38,7 +38,7 @@ - + diff --git a/src/components/TestPlatform/Case/CaseList.vue b/src/components/TestPlatform/Case/CaseList.vue index b7a9cf8..2da426a 100644 --- a/src/components/TestPlatform/Case/CaseList.vue +++ b/src/components/TestPlatform/Case/CaseList.vue @@ -38,20 +38,17 @@ -
+
+ + 刷新 + 新增模块
- - - - - - 查询 - - - 重置 - - @@ -203,9 +200,10 @@ - + @@ -245,7 +243,16 @@ 'mindmap-node-case': data.nodeTypeLabel === '用例', 'mindmap-node-active': selectedMindmapCase && selectedMindmapCase.id === data.id }" @click.stop="handleMindmapNodeClick(data)"> - {{ data.name }} + + {{ data.name }} + + {{ formatStatus(data.status) || '未知状态' }} + + {{ data.nodeTypeLabel }} 创建人:{{ data.creator }} @@ -293,6 +300,251 @@
暂无结构数据
+ + +
+

+ 按条件查询用例;在下方文档列表中勾选要参与生成的文档,可在「技能与业务规则」中多选当前项目的 Skill 与规则;设置生成参数后点击「生成用例」。「导入」为上传 PDF 或新建飞书文档(与文档源「新建」一致);Excel 导入用例请在「用例列表」Tab 使用「导入Excel」。 +

+ + + + + + + + + + + + + + + + + + + + + + + + + + 查询 + + + 重置 + + + 导入 + + + + 生成用例 + + + + +
+ 生成参数 + + + + + + (与接口约定:1-P0 / 2-P1 / 3-P2) + 用例类型 + + 标签 + + + +
+ +
+
文档来源(勾选后参与「生成用例」)
+ + + + + + + + + + + + + + + + +
+ +
+
技能与业务规则(可选,选择后随「生成用例」一并提交)
+
+ + + + + + + + +
+
+ +
+
用例列表
+
+ + 批量同步 + + + 批量删除 + +
+
+ + + + + + + + + + + + + + + +
+ + +
+ + + +
+
@@ -330,6 +582,93 @@ + +
+ +
暂无详情数据
+
+ + 关闭 + +
+ + + + + {{ autoGenRowTitle }} + + + + + + + + + + + + + + 取消 + 确定 + + +
X
@@ -362,7 +701,23 @@ + + diff --git a/src/components/TestPlatform/Plan/PlanExecute.vue b/src/components/TestPlatform/Plan/PlanExecute.vue index 07f4420..1fee5b3 100644 --- a/src/components/TestPlatform/Plan/PlanExecute.vue +++ b/src/components/TestPlatform/Plan/PlanExecute.vue @@ -27,13 +27,13 @@ style="margin-top: 12px;"> - - - + + + + + +