feat(action-plans): COSO 自检未通过条目一键生成改善行动(接通死按钮)
ci/woodpecker/push/main Pipeline failed

- 后端新增 POST /api/cma/action-plans/coso-generate-actions:未通过条目→action_plans,
  账套取 token 绑定(忽略 body entity_id)、幂等键 (entity_id,title)、不写 auto_verify_rule
- 前端 ActionPlanLibrary.vue 按钮接 @click(含未通过条数提示 + 二次确认 + 生成后刷新并跳列表)
- api 客户端补 cosoGenerateActions
- 新增 tests/test_coso_generate_actions_20260921.py(生成/幂等/隔离/未认证/归属校验/空集)
This commit is contained in:
fullstack
2026-09-23 01:03:19 +08:00
committed by Hermes CI Fix
parent 2c2dbc2b2d
commit bd4f58aae1
4 changed files with 470 additions and 3 deletions
+1
View File
@@ -202,6 +202,7 @@ export const actionPlanApi = {
update: (id: number, data: any) => api.put(`/action-plans/${id}`, data),
delete: (id: number) => api.delete(`/action-plans/${id}`),
cosoChecklist: (params?: any) => api.get('/action-plans/coso-checklist', { params }),
cosoGenerateActions: (data?: any) => api.post('/action-plans/coso-generate-actions', data || {}),
stats: () => api.get('/action-plans/stats'),
}
+34 -1
View File
@@ -294,7 +294,8 @@
<!-- 企业只读标识(不可点):账套由登录窗口选定,页内无切换入口 -->
<el-tag size="small" type="primary" effect="plain">{{ entityLabel }}</el-tag>
<el-button size="small" @click="loadCosoChecklist" :loading="cosoLoading">刷新</el-button>
<el-button v-if="cosoData" type="primary" size="small" style="margin-left:12px;">生成改善行动</el-button>
<el-button v-if="cosoData" type="primary" size="small" style="margin-left:12px;"
:loading="cosoGenerating" @click="generateCosoActions">生成改善行动<span v-if="cosoFailedCount">({{ cosoFailedCount }})</span></el-button>
</div>
<div v-if="cosoData">
@@ -667,6 +668,38 @@ async function loadCosoChecklist() {
cosoLoading.value = false
}
const cosoGenerating = ref(false)
// 自检未通过(passed=false)条目数——即「生成改善行动」将覆盖的范围
const cosoFailedCount = computed(() => {
const els = cosoData.value?.elements || []
return els.reduce((n: number, el: any) => n + (el.items || []).filter((it: any) => !it.passed).length, 0)
})
async function generateCosoActions() {
if (!cosoData.value) return
const n = cosoFailedCount.value
if (n === 0) {
ElMessage.info('当前自检没有未通过条目,无需生成改善行动')
return
}
try {
await ElMessageBox.confirm(
`将把自检中 ${n} 条未通过条目生成改善行动(已存在的不会重复生成),是否继续?`,
'生成改善行动'
)
} catch { return }
cosoGenerating.value = true
try {
const r: any = await actionPlanApi.cosoGenerateActions({})
const created = r?.created ?? 0
const skipped = r?.skipped ?? 0
ElMessage.success(`已生成 ${created} 条改善行动${skipped ? `,跳过 ${skipped} 条已存在` : ''}`)
await Promise.all([loadData(), loadStats()])
if (created > 0) activeTab.value = 'plans' // 切到行动方案列表即可确认
} catch { ElMessage.error('生成改善行动失败') }
cosoGenerating.value = false
}
// ── 初始化 ──
onMounted(async () => {
loadStats()