2026-05-11 06:32:38 +00:00
|
|
|
|
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
|
|
|
|
|
|
2026-05-13 03:35:44 +00:00
|
|
|
|
import ConfirmDialog from '../../components/shared/ConfirmDialog.vue'
|
2026-05-13 06:52:30 +00:00
|
|
|
|
import TableEmptyState from '../../components/shared/TableEmptyState.vue'
|
2026-05-11 06:32:38 +00:00
|
|
|
|
import { useSystemState } from '../../composables/useSystemState.js'
|
|
|
|
|
|
import { useToast } from '../../composables/useToast.js'
|
|
|
|
|
|
import {
|
|
|
|
|
|
activateAgentAsset,
|
|
|
|
|
|
createAgentAssetReview,
|
|
|
|
|
|
createAgentAssetVersion,
|
|
|
|
|
|
fetchAgentAssetDetail,
|
|
|
|
|
|
fetchAgentAssets,
|
2026-05-15 06:57:07 +00:00
|
|
|
|
fetchAgentRuns,
|
|
|
|
|
|
updateAgentAsset
|
2026-05-11 06:32:38 +00:00
|
|
|
|
} from '../../services/agentAssets.js'
|
|
|
|
|
|
import { isManagerUser } from '../../utils/accessControl.js'
|
2026-05-09 15:46:16 +00:00
|
|
|
|
|
|
|
|
|
|
const TYPE_META = {
|
2026-05-11 01:53:30 +00:00
|
|
|
|
rules: {
|
2026-05-11 06:32:38 +00:00
|
|
|
|
assetType: 'rule',
|
|
|
|
|
|
label: '规则',
|
|
|
|
|
|
typeLabel: '规则',
|
|
|
|
|
|
createButtonLabel: '规则已接入',
|
|
|
|
|
|
hintText: '规则列表已接到真实资产 API,可查看 Markdown、版本、审核状态和上线约束。',
|
|
|
|
|
|
searchPlaceholder: '搜索规则名称、编码或负责人',
|
2026-05-09 15:46:16 +00:00
|
|
|
|
tableColumns: {
|
2026-05-11 06:32:38 +00:00
|
|
|
|
name: '规则名称',
|
|
|
|
|
|
category: '业务域',
|
2026-05-09 15:46:16 +00:00
|
|
|
|
owner: '负责人',
|
2026-05-11 06:32:38 +00:00
|
|
|
|
scope: '适用场景',
|
|
|
|
|
|
runtime: '风险等级',
|
|
|
|
|
|
version: '当前版本',
|
|
|
|
|
|
metric: '审核状态'
|
2026-05-09 15:46:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-05-11 01:53:30 +00:00
|
|
|
|
skills: {
|
2026-05-11 06:32:38 +00:00
|
|
|
|
assetType: 'skill',
|
|
|
|
|
|
label: '技能',
|
|
|
|
|
|
typeLabel: '技能',
|
|
|
|
|
|
createButtonLabel: '技能已接入',
|
|
|
|
|
|
hintText: '技能页签已接到真实资产 API,可查看输入、输出、依赖和场景信息。',
|
|
|
|
|
|
searchPlaceholder: '搜索技能名称、编码或负责人',
|
2026-05-11 06:33:46 +00:00
|
|
|
|
showMetricColumn: false,
|
2026-05-11 01:53:30 +00:00
|
|
|
|
tableColumns: {
|
|
|
|
|
|
name: '技能名称',
|
2026-05-11 06:32:38 +00:00
|
|
|
|
category: '业务域',
|
2026-05-11 01:53:30 +00:00
|
|
|
|
owner: '负责人',
|
2026-05-11 06:32:38 +00:00
|
|
|
|
scope: '适用场景',
|
|
|
|
|
|
runtime: '输入摘要',
|
|
|
|
|
|
version: '当前版本',
|
2026-05-11 06:33:46 +00:00
|
|
|
|
metric: ''
|
2026-05-11 01:53:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-05-09 15:46:16 +00:00
|
|
|
|
mcp: {
|
2026-05-11 06:32:38 +00:00
|
|
|
|
assetType: 'mcp',
|
|
|
|
|
|
label: 'MCP',
|
|
|
|
|
|
typeLabel: 'MCP',
|
|
|
|
|
|
createButtonLabel: 'MCP 已接入',
|
|
|
|
|
|
hintText: 'MCP 页签已接到真实资产 API,可查看服务地址、鉴权方式、超时和降级策略。',
|
|
|
|
|
|
searchPlaceholder: '搜索 MCP 名称、编码或负责人',
|
2026-05-09 15:46:16 +00:00
|
|
|
|
tableColumns: {
|
|
|
|
|
|
name: 'MCP 服务',
|
2026-05-11 06:32:38 +00:00
|
|
|
|
category: '业务域',
|
2026-05-09 15:46:16 +00:00
|
|
|
|
owner: '维护人',
|
2026-05-11 06:32:38 +00:00
|
|
|
|
scope: '适用场景',
|
|
|
|
|
|
runtime: '调用地址',
|
|
|
|
|
|
version: '当前版本',
|
|
|
|
|
|
metric: '超时配置'
|
2026-05-09 15:46:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-05-11 06:32:38 +00:00
|
|
|
|
tasks: {
|
|
|
|
|
|
assetType: 'task',
|
|
|
|
|
|
label: '任务',
|
|
|
|
|
|
typeLabel: '任务',
|
|
|
|
|
|
createButtonLabel: '任务已接入',
|
|
|
|
|
|
hintText: '任务页签已接到真实资产 API,可查看调度周期、执行 Agent 和最近执行结果。',
|
|
|
|
|
|
searchPlaceholder: '搜索任务名称、编码或负责人',
|
2026-05-09 15:46:16 +00:00
|
|
|
|
tableColumns: {
|
|
|
|
|
|
name: '任务名称',
|
2026-05-11 06:32:38 +00:00
|
|
|
|
category: '业务域',
|
2026-05-09 15:46:16 +00:00
|
|
|
|
owner: '负责人',
|
2026-05-11 06:32:38 +00:00
|
|
|
|
scope: '适用场景',
|
|
|
|
|
|
runtime: '调度周期',
|
|
|
|
|
|
version: '当前版本',
|
|
|
|
|
|
metric: '执行 Agent'
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const BADGE_TONES = {
|
|
|
|
|
|
rules: 'emerald',
|
|
|
|
|
|
skills: 'blue',
|
|
|
|
|
|
mcp: 'amber',
|
|
|
|
|
|
tasks: 'violet'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const STATUS_META = {
|
|
|
|
|
|
draft: { label: '草稿中', tone: 'draft' },
|
|
|
|
|
|
review: { label: '待审核', tone: 'warning' },
|
|
|
|
|
|
active: { label: '已上线', tone: 'success' },
|
|
|
|
|
|
disabled: { label: '已停用', tone: 'disabled' }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const REVIEW_META = {
|
|
|
|
|
|
approved: { label: '已通过', tone: 'success' },
|
|
|
|
|
|
pending: { label: '待审核', tone: 'warning' },
|
|
|
|
|
|
rejected: { label: '已驳回', tone: 'danger' }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const DOMAIN_LABELS = {
|
|
|
|
|
|
expense: '报销',
|
|
|
|
|
|
ar: '应收',
|
|
|
|
|
|
ap: '应付',
|
|
|
|
|
|
knowledge: '知识',
|
|
|
|
|
|
system: '系统'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const SCENARIO_LABELS = {
|
|
|
|
|
|
expense: '报销',
|
|
|
|
|
|
risk_check: '风险检查',
|
|
|
|
|
|
duplicate_expense: '重复报销',
|
|
|
|
|
|
explain: '规则解释',
|
|
|
|
|
|
invoice_anomaly: '票据异常',
|
2026-05-15 06:57:07 +00:00
|
|
|
|
travel_policy: '差旅制度',
|
|
|
|
|
|
travel_standard: '差旅标准',
|
2026-05-11 06:32:38 +00:00
|
|
|
|
accounts_payable: '应付',
|
|
|
|
|
|
accounts_receivable: '应收',
|
|
|
|
|
|
approval_required: '需审批',
|
|
|
|
|
|
query: '查询',
|
|
|
|
|
|
summary: '汇总',
|
|
|
|
|
|
system: '系统',
|
|
|
|
|
|
schedule: '调度',
|
|
|
|
|
|
rule_center: '规则中心',
|
|
|
|
|
|
review_digest: '待审摘要',
|
|
|
|
|
|
aging_summary: '账龄汇总',
|
|
|
|
|
|
invoice_validation: '发票验真'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const DETAIL_TITLES = {
|
|
|
|
|
|
rules: {
|
|
|
|
|
|
configTitle: '规则元信息',
|
|
|
|
|
|
configDesc: '展示规则编码、版本、业务域和当前审核 / 上线状态。',
|
|
|
|
|
|
detailTitle: '规则版本说明',
|
|
|
|
|
|
detailDesc: '规则正文由 Markdown 驱动,保存后会生成新的版本快照。',
|
|
|
|
|
|
outputTitle: '审核与上线',
|
|
|
|
|
|
outputDesc: '规则上线受审核状态控制,未审核通过的版本会被后端拦截。',
|
|
|
|
|
|
ruleListTitle: '上线要求',
|
|
|
|
|
|
checkListTitle: '当前状态',
|
|
|
|
|
|
triggerTitle: '适用场景',
|
|
|
|
|
|
triggerDesc: '当前规则注册到的业务场景',
|
|
|
|
|
|
toolTitle: '关联信息',
|
|
|
|
|
|
toolDesc: '规则当前审核、保存和版本快照信息',
|
|
|
|
|
|
historyTitle: '版本历史',
|
|
|
|
|
|
historyDesc: '最近 5 个规则版本',
|
|
|
|
|
|
publishTitle: '上线控制',
|
|
|
|
|
|
publishDesc: '正式上线会调用后端激活接口,审核未通过时会被拦截。'
|
|
|
|
|
|
},
|
|
|
|
|
|
skills: {
|
|
|
|
|
|
configTitle: '技能配置',
|
|
|
|
|
|
configDesc: '展示技能编码、输入摘要、版本和业务域。',
|
|
|
|
|
|
detailTitle: '技能结构',
|
|
|
|
|
|
detailDesc: '按输入、输出和依赖组织技能定义。',
|
|
|
|
|
|
outputTitle: '输出契约',
|
|
|
|
|
|
outputDesc: '技能详情重点展示输入参数、输出参数和依赖能力。',
|
|
|
|
|
|
ruleListTitle: '输出要求',
|
|
|
|
|
|
checkListTitle: '当前快照',
|
|
|
|
|
|
triggerTitle: '适用场景',
|
|
|
|
|
|
triggerDesc: '当前技能注册到的场景标签',
|
|
|
|
|
|
toolTitle: '依赖能力',
|
|
|
|
|
|
toolDesc: '技能当前依赖的数据库或其他能力',
|
|
|
|
|
|
historyTitle: '版本历史',
|
|
|
|
|
|
historyDesc: '最近版本记录',
|
|
|
|
|
|
publishTitle: '发布状态',
|
|
|
|
|
|
publishDesc: '技能当前状态由资产中心统一管理。'
|
|
|
|
|
|
},
|
|
|
|
|
|
mcp: {
|
|
|
|
|
|
configTitle: 'MCP 连接配置',
|
|
|
|
|
|
configDesc: '展示服务地址、超时和调用方式。',
|
|
|
|
|
|
detailTitle: '服务协议',
|
|
|
|
|
|
detailDesc: '按服务类型、鉴权方式和降级策略组织外部服务信息。',
|
|
|
|
|
|
outputTitle: '调用约束',
|
|
|
|
|
|
outputDesc: 'MCP 详情重点展示鉴权方式、返回策略和最近调用状态。',
|
|
|
|
|
|
ruleListTitle: '调用约束',
|
|
|
|
|
|
checkListTitle: '最近状态',
|
|
|
|
|
|
triggerTitle: '适用场景',
|
|
|
|
|
|
triggerDesc: '当前 MCP 覆盖的业务场景',
|
|
|
|
|
|
toolTitle: '运行信息',
|
|
|
|
|
|
toolDesc: '结合 AgentRun 中的 ToolCall 还原最近一次调用状态',
|
|
|
|
|
|
historyTitle: '版本历史',
|
|
|
|
|
|
historyDesc: '最近版本记录',
|
|
|
|
|
|
publishTitle: '服务状态',
|
|
|
|
|
|
publishDesc: 'MCP 资产已接入规则中心,但真实外部调用仍以后续链路集成为准。'
|
|
|
|
|
|
},
|
|
|
|
|
|
tasks: {
|
|
|
|
|
|
configTitle: '任务配置',
|
|
|
|
|
|
configDesc: '展示调度周期、执行 Agent 和任务编码。',
|
|
|
|
|
|
detailTitle: '任务结构',
|
|
|
|
|
|
detailDesc: '按调度计划、目标场景和运行结果组织任务信息。',
|
|
|
|
|
|
outputTitle: '运行要求',
|
|
|
|
|
|
outputDesc: '任务详情重点展示调度 Agent、最近运行结果和运行日志入口。',
|
|
|
|
|
|
ruleListTitle: '运行要求',
|
|
|
|
|
|
checkListTitle: '最近执行',
|
|
|
|
|
|
triggerTitle: '适用场景',
|
|
|
|
|
|
triggerDesc: '当前任务覆盖的业务场景',
|
|
|
|
|
|
toolTitle: '最近调用',
|
|
|
|
|
|
toolDesc: '根据 AgentRun 中的最近执行记录回显任务运行情况',
|
|
|
|
|
|
historyTitle: '版本历史',
|
|
|
|
|
|
historyDesc: '最近版本记录',
|
|
|
|
|
|
publishTitle: '调度状态',
|
|
|
|
|
|
publishDesc: '任务资产已接入规则中心,后续 Day 4 运行时会继续消费这些配置。'
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const STATUS_OPTIONS = [
|
|
|
|
|
|
{ value: '', label: '全部状态' },
|
|
|
|
|
|
{ value: 'draft', label: '草稿中' },
|
|
|
|
|
|
{ value: 'review', label: '待审核' },
|
|
|
|
|
|
{ value: 'active', label: '已上线' },
|
|
|
|
|
|
{ value: 'disabled', label: '已停用' }
|
|
|
|
|
|
]
|
|
|
|
|
|
|
2026-05-15 06:57:07 +00:00
|
|
|
|
const EXPENSE_RULE_BLOCK_PATTERN = /```expense-rule\s*([\s\S]*?)\s*```/i
|
|
|
|
|
|
|
|
|
|
|
|
const RULE_TEMPLATE_LABELS = {
|
|
|
|
|
|
travel_standard_v1: '差旅标准模板',
|
|
|
|
|
|
expense_amount_limit_v1: '金额上限模板',
|
|
|
|
|
|
attachment_requirement_v1: '附件要求模板',
|
|
|
|
|
|
general_policy_v1: '通用制度模板'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-11 06:32:38 +00:00
|
|
|
|
function normalizeText(value) {
|
|
|
|
|
|
return String(value || '').trim()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-15 06:57:07 +00:00
|
|
|
|
function isPlainObject(value) {
|
|
|
|
|
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function readConfigJson(value) {
|
|
|
|
|
|
if (isPlainObject(value?.configJson)) {
|
|
|
|
|
|
return value.configJson
|
|
|
|
|
|
}
|
|
|
|
|
|
if (isPlainObject(value?.config_json)) {
|
|
|
|
|
|
return value.config_json
|
|
|
|
|
|
}
|
|
|
|
|
|
return {}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function cloneJsonObject(value) {
|
|
|
|
|
|
if (!isPlainObject(value)) {
|
|
|
|
|
|
return null
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
return JSON.parse(JSON.stringify(value))
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
return { ...value }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function resolveRuleTemplateLabel(value) {
|
|
|
|
|
|
const templateKey = normalizeText(value)
|
|
|
|
|
|
return RULE_TEMPLATE_LABELS[templateKey] || templateKey || '未指定模板'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function extractRuntimeRuleFromMarkdown(markdown) {
|
|
|
|
|
|
const match = String(markdown || '').match(EXPENSE_RULE_BLOCK_PATTERN)
|
|
|
|
|
|
if (!match) {
|
|
|
|
|
|
return null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const payload = JSON.parse(match[1])
|
|
|
|
|
|
return isPlainObject(payload) ? payload : null
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
return null
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function stripRuntimeRuleBlock(markdown) {
|
|
|
|
|
|
const text = String(markdown || '')
|
|
|
|
|
|
const stripped = text.replace(EXPENSE_RULE_BLOCK_PATTERN, '').replace(/\n{3,}/g, '\n\n').trim()
|
|
|
|
|
|
return stripped
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function stringifyRuntimeRule(runtimeRule) {
|
|
|
|
|
|
return JSON.stringify(isPlainObject(runtimeRule) ? runtimeRule : {}, null, 2)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function parseRuntimeRuleText(runtimeRuleText) {
|
|
|
|
|
|
const text = normalizeText(runtimeRuleText)
|
|
|
|
|
|
if (!text) {
|
|
|
|
|
|
return null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const payload = JSON.parse(text)
|
|
|
|
|
|
return isPlainObject(payload) ? payload : null
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
return null
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildDefaultRuntimeRule(source) {
|
|
|
|
|
|
const configJson = readConfigJson(source)
|
|
|
|
|
|
const scenarioItems = Array.isArray(source?.scenario_json)
|
|
|
|
|
|
? source.scenario_json
|
|
|
|
|
|
: Array.isArray(source?.scenarioList)
|
|
|
|
|
|
? source.scenarioList
|
|
|
|
|
|
: []
|
|
|
|
|
|
const configRuntimeRule = cloneJsonObject(configJson.runtime_rule)
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
kind: normalizeText(configRuntimeRule?.kind || configJson.runtime_kind) || 'policy_rule_draft',
|
|
|
|
|
|
version:
|
|
|
|
|
|
typeof configRuntimeRule?.version === 'number' && Number.isFinite(configRuntimeRule.version)
|
|
|
|
|
|
? configRuntimeRule.version
|
|
|
|
|
|
: 1,
|
|
|
|
|
|
template_key:
|
|
|
|
|
|
normalizeText(configRuntimeRule?.template_key || configJson.rule_template_key) || 'general_policy_v1',
|
|
|
|
|
|
rule_name: normalizeText(configRuntimeRule?.rule_name || source?.name) || '未命名规则',
|
|
|
|
|
|
scenario:
|
|
|
|
|
|
normalizeText(configRuntimeRule?.scenario || scenarioItems[0]) || 'expense',
|
|
|
|
|
|
review_required:
|
|
|
|
|
|
typeof configRuntimeRule?.review_required === 'boolean' ? configRuntimeRule.review_required : true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function resolveRuntimeRuleForVersion(source, rawMarkdown, runtimeRuleFallback = null) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
cloneJsonObject(extractRuntimeRuleFromMarkdown(rawMarkdown)) ||
|
|
|
|
|
|
cloneJsonObject(runtimeRuleFallback) ||
|
|
|
|
|
|
buildDefaultRuntimeRule(source)
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildMarkdownVersionContent(markdownContent, runtimeRule) {
|
|
|
|
|
|
const body = stripRuntimeRuleBlock(markdownContent)
|
|
|
|
|
|
const runtimeBlock = ['```expense-rule', stringifyRuntimeRule(runtimeRule), '```'].join('\n')
|
|
|
|
|
|
return body ? `${body}\n\n${runtimeBlock}` : runtimeBlock
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-11 06:32:38 +00:00
|
|
|
|
function makeShort(value) {
|
|
|
|
|
|
const text = normalizeText(value).replace(/\s+/g, '')
|
|
|
|
|
|
if (!text) {
|
|
|
|
|
|
return 'AG'
|
|
|
|
|
|
}
|
|
|
|
|
|
return text.slice(0, 2).toUpperCase()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function formatDateTime(value) {
|
|
|
|
|
|
if (!value) {
|
|
|
|
|
|
return '未记录'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const date = new Date(value)
|
|
|
|
|
|
if (Number.isNaN(date.getTime())) {
|
|
|
|
|
|
return String(value)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return new Intl.DateTimeFormat('zh-CN', {
|
|
|
|
|
|
year: 'numeric',
|
|
|
|
|
|
month: '2-digit',
|
|
|
|
|
|
day: '2-digit',
|
|
|
|
|
|
hour: '2-digit',
|
|
|
|
|
|
minute: '2-digit',
|
|
|
|
|
|
hour12: false
|
|
|
|
|
|
})
|
|
|
|
|
|
.format(date)
|
|
|
|
|
|
.replace(/\//g, '-')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function resolveDomainLabel(value) {
|
|
|
|
|
|
return DOMAIN_LABELS[value] || normalizeText(value) || '未分类'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function resolveStatusMeta(value) {
|
|
|
|
|
|
return STATUS_META[value] || { label: normalizeText(value) || '未知状态', tone: 'draft' }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function resolveReviewMeta(value) {
|
|
|
|
|
|
return REVIEW_META[value] || { label: '暂无审核', tone: 'draft' }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function formatScenarioList(items) {
|
|
|
|
|
|
if (!Array.isArray(items) || !items.length) {
|
|
|
|
|
|
return '未配置场景'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return items
|
|
|
|
|
|
.map((item) => SCENARIO_LABELS[item] || item)
|
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
|
.join(' / ')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-15 06:57:07 +00:00
|
|
|
|
function buildHistory(recentVersions = [], source) {
|
|
|
|
|
|
const currentRuntimeRule = cloneJsonObject(readConfigJson(source).runtime_rule)
|
|
|
|
|
|
|
|
|
|
|
|
return recentVersions.map((item) => {
|
|
|
|
|
|
const rawContent = typeof item.content === 'string' ? item.content : ''
|
|
|
|
|
|
return {
|
|
|
|
|
|
version: item.version,
|
|
|
|
|
|
note: item.change_note || '无版本说明',
|
|
|
|
|
|
time: formatDateTime(item.created_at),
|
|
|
|
|
|
content: rawContent,
|
|
|
|
|
|
markdownContent: stripRuntimeRuleBlock(rawContent),
|
|
|
|
|
|
runtimeRule: resolveRuntimeRuleForVersion(
|
|
|
|
|
|
source,
|
|
|
|
|
|
rawContent,
|
|
|
|
|
|
item.is_current ? currentRuntimeRule : null
|
|
|
|
|
|
),
|
|
|
|
|
|
contentType: item.content_type,
|
|
|
|
|
|
createdBy: item.created_by,
|
|
|
|
|
|
isCurrent: Boolean(item.is_current)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2026-05-11 06:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function resolveTypeKey(assetType) {
|
|
|
|
|
|
if (assetType === 'rule') {
|
|
|
|
|
|
return 'rules'
|
|
|
|
|
|
}
|
|
|
|
|
|
if (assetType === 'skill') {
|
|
|
|
|
|
return 'skills'
|
|
|
|
|
|
}
|
|
|
|
|
|
if (assetType === 'mcp') {
|
|
|
|
|
|
return 'mcp'
|
|
|
|
|
|
}
|
|
|
|
|
|
return 'tasks'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function formatSeverity(value) {
|
|
|
|
|
|
const severity = normalizeText(value).toLowerCase()
|
|
|
|
|
|
if (severity === 'high') {
|
|
|
|
|
|
return '高风险'
|
|
|
|
|
|
}
|
|
|
|
|
|
if (severity === 'medium') {
|
|
|
|
|
|
return '中风险'
|
|
|
|
|
|
}
|
|
|
|
|
|
if (severity === 'low') {
|
|
|
|
|
|
return '低风险'
|
|
|
|
|
|
}
|
|
|
|
|
|
return '未配置'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function formatInputSummary(items) {
|
|
|
|
|
|
if (!Array.isArray(items) || !items.length) {
|
|
|
|
|
|
return '无输入'
|
|
|
|
|
|
}
|
|
|
|
|
|
return `${items.length} 项输入`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function formatOutputSummary(items) {
|
|
|
|
|
|
if (!Array.isArray(items) || !items.length) {
|
|
|
|
|
|
return '无输出'
|
|
|
|
|
|
}
|
|
|
|
|
|
return `${items.length} 项输出`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function formatTaskRisk(scenarios) {
|
|
|
|
|
|
if (Array.isArray(scenarios) && scenarios.includes('risk_check')) {
|
|
|
|
|
|
return '高风险'
|
|
|
|
|
|
}
|
|
|
|
|
|
if (
|
|
|
|
|
|
Array.isArray(scenarios) &&
|
|
|
|
|
|
(scenarios.includes('accounts_receivable') || scenarios.includes('accounts_payable'))
|
|
|
|
|
|
) {
|
|
|
|
|
|
return '中风险'
|
|
|
|
|
|
}
|
|
|
|
|
|
return '常规'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function findLatestTaskRun(runs, assetId) {
|
|
|
|
|
|
return runs.find((item) => item.task_id === assetId) || null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function findLatestMcpCall(runs, assetCode) {
|
|
|
|
|
|
const expectedToolName = normalizeText(assetCode).replace(/^mcp\./, '')
|
|
|
|
|
|
|
|
|
|
|
|
for (const run of runs) {
|
|
|
|
|
|
for (const toolCall of run.tool_calls || []) {
|
|
|
|
|
|
const toolName = normalizeText(toolCall.tool_name)
|
|
|
|
|
|
if (
|
|
|
|
|
|
toolName === expectedToolName ||
|
|
|
|
|
|
toolName.endsWith(expectedToolName) ||
|
|
|
|
|
|
expectedToolName.endsWith(toolName)
|
|
|
|
|
|
) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
run,
|
|
|
|
|
|
toolCall
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-09 15:46:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-11 06:32:38 +00:00
|
|
|
|
|
|
|
|
|
|
return null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildRowRuntime(asset, typeKey) {
|
|
|
|
|
|
if (typeKey === 'rules') {
|
|
|
|
|
|
return formatSeverity(asset.config_json?.severity)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (typeKey === 'skills') {
|
|
|
|
|
|
return formatInputSummary(asset.config_json?.input_schema)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (typeKey === 'mcp') {
|
|
|
|
|
|
return normalizeText(asset.config_json?.endpoint) || '未配置地址'
|
|
|
|
|
|
}
|
|
|
|
|
|
return normalizeText(asset.config_json?.cron) || '未配置调度'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildRowMetric(asset, typeKey) {
|
|
|
|
|
|
if (typeKey === 'rules') {
|
|
|
|
|
|
return asset.reviewer ? `审核人:${asset.reviewer}` : '待分配审核人'
|
|
|
|
|
|
}
|
|
|
|
|
|
if (typeKey === 'skills') {
|
|
|
|
|
|
return '进入详情查看输出'
|
|
|
|
|
|
}
|
|
|
|
|
|
if (typeKey === 'mcp') {
|
|
|
|
|
|
return asset.config_json?.timeout_ms ? `${asset.config_json.timeout_ms} ms` : '未配置超时'
|
|
|
|
|
|
}
|
|
|
|
|
|
return normalizeText(asset.config_json?.agent) || '未配置 Agent'
|
2026-05-09 15:46:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-11 06:32:38 +00:00
|
|
|
|
function buildListItem(asset) {
|
|
|
|
|
|
const typeKey = resolveTypeKey(asset.asset_type)
|
|
|
|
|
|
const statusMeta = resolveStatusMeta(asset.status)
|
|
|
|
|
|
|
2026-05-09 15:46:16 +00:00
|
|
|
|
return {
|
2026-05-11 06:32:38 +00:00
|
|
|
|
id: asset.id,
|
|
|
|
|
|
type: typeKey,
|
|
|
|
|
|
typeLabel: TYPE_META[typeKey].typeLabel,
|
|
|
|
|
|
short: makeShort(asset.name),
|
|
|
|
|
|
name: asset.name,
|
|
|
|
|
|
code: asset.code,
|
|
|
|
|
|
summary: asset.description,
|
|
|
|
|
|
category: resolveDomainLabel(asset.domain),
|
|
|
|
|
|
owner: asset.owner,
|
|
|
|
|
|
reviewer: asset.reviewer || '待分配',
|
|
|
|
|
|
scope: formatScenarioList(asset.scenario_json),
|
|
|
|
|
|
model: buildRowRuntime(asset, typeKey),
|
|
|
|
|
|
version: asset.current_version || '-',
|
|
|
|
|
|
status: statusMeta.label,
|
|
|
|
|
|
statusValue: asset.status,
|
|
|
|
|
|
statusTone: statusMeta.tone,
|
|
|
|
|
|
hitRate: buildRowMetric(asset, typeKey),
|
|
|
|
|
|
updatedAt: formatDateTime(asset.updated_at),
|
|
|
|
|
|
badgeTone: BADGE_TONES[typeKey],
|
|
|
|
|
|
spotlight: asset.status === 'active',
|
|
|
|
|
|
domainValue: asset.domain
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildRuleFields(detail) {
|
|
|
|
|
|
return [
|
|
|
|
|
|
{ label: '规则编码', value: detail.code },
|
2026-05-15 06:57:07 +00:00
|
|
|
|
{
|
|
|
|
|
|
label: '模板键',
|
|
|
|
|
|
value: normalizeText(detail.config_json?.rule_template_key) || '未指定'
|
|
|
|
|
|
},
|
2026-05-11 06:32:38 +00:00
|
|
|
|
{ label: '业务域', value: resolveDomainLabel(detail.domain) },
|
2026-05-15 06:57:07 +00:00
|
|
|
|
{
|
|
|
|
|
|
label: '运行时类型',
|
|
|
|
|
|
value: normalizeText(detail.config_json?.runtime_kind) || 'policy_rule_draft'
|
|
|
|
|
|
},
|
2026-05-11 06:32:38 +00:00
|
|
|
|
{ label: '适用场景', value: formatScenarioList(detail.scenario_json) },
|
|
|
|
|
|
{ label: '当前版本', value: detail.current_version || '-' }
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildSkillFields(detail) {
|
|
|
|
|
|
const content = detail.current_version_content || {}
|
|
|
|
|
|
return [
|
|
|
|
|
|
{ label: '技能编码', value: detail.code },
|
|
|
|
|
|
{ label: '业务域', value: resolveDomainLabel(detail.domain) },
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '输入参数',
|
|
|
|
|
|
value: Array.isArray(content.inputs) && content.inputs.length ? content.inputs.join('、') : '未配置'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '输出参数',
|
|
|
|
|
|
value: Array.isArray(content.outputs) && content.outputs.length ? content.outputs.join('、') : '未配置'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildMcpFields(detail, latestCall) {
|
|
|
|
|
|
const content = detail.current_version_content || {}
|
|
|
|
|
|
return [
|
|
|
|
|
|
{ label: '服务编码', value: detail.code },
|
|
|
|
|
|
{ label: '调用地址', value: normalizeText(detail.config_json?.endpoint) || '未配置' },
|
|
|
|
|
|
{ label: '鉴权方式', value: normalizeText(content.auth_mode) || '未配置' },
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '最近调用',
|
|
|
|
|
|
value: latestCall ? `${latestCall.toolCall.status} / ${formatDateTime(latestCall.run.started_at)}` : '暂无调用记录'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildTaskFields(detail, latestRun) {
|
|
|
|
|
|
const content = detail.current_version_content || {}
|
|
|
|
|
|
return [
|
|
|
|
|
|
{ label: '任务编码', value: detail.code },
|
|
|
|
|
|
{ label: 'Cron', value: normalizeText(detail.config_json?.cron) || normalizeText(content.schedule) || '未配置' },
|
|
|
|
|
|
{ label: '执行 Agent', value: normalizeText(detail.config_json?.agent) || normalizeText(content.target_agent) || '未配置' },
|
|
|
|
|
|
{ label: '风险等级', value: formatTaskRisk(detail.scenario_json) },
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '最近执行',
|
|
|
|
|
|
value: latestRun ? formatDateTime(latestRun.started_at) : '暂无执行记录'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildFields(detail, typeKey, latestRun, latestCall) {
|
|
|
|
|
|
if (typeKey === 'rules') {
|
|
|
|
|
|
return buildRuleFields(detail)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (typeKey === 'skills') {
|
|
|
|
|
|
return buildSkillFields(detail)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (typeKey === 'mcp') {
|
|
|
|
|
|
return buildMcpFields(detail, latestCall)
|
|
|
|
|
|
}
|
|
|
|
|
|
return buildTaskFields(detail, latestRun)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildPromptSections(detail, typeKey, latestRun, latestCall) {
|
|
|
|
|
|
const content = detail.current_version_content || {}
|
|
|
|
|
|
|
|
|
|
|
|
if (typeKey === 'skills') {
|
|
|
|
|
|
return [
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '输入参数',
|
|
|
|
|
|
intent: '技能入口',
|
|
|
|
|
|
content: Array.isArray(content.inputs) && content.inputs.length ? content.inputs.join('\n') : '未配置输入参数。'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '输出参数',
|
|
|
|
|
|
intent: '技能产出',
|
|
|
|
|
|
content: Array.isArray(content.outputs) && content.outputs.length ? content.outputs.join('\n') : '未配置输出参数。'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '依赖能力',
|
|
|
|
|
|
intent: '外部依赖',
|
|
|
|
|
|
content:
|
|
|
|
|
|
Array.isArray(content.dependencies) && content.dependencies.length
|
|
|
|
|
|
? content.dependencies.join('\n')
|
|
|
|
|
|
: '当前技能未声明外部依赖。'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (typeKey === 'mcp') {
|
|
|
|
|
|
return [
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '服务类型',
|
|
|
|
|
|
intent: '协议说明',
|
|
|
|
|
|
content: normalizeText(content.service_type) || '未配置服务类型。'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '鉴权方式',
|
|
|
|
|
|
intent: '安全要求',
|
|
|
|
|
|
content: normalizeText(content.auth_mode) || '未配置鉴权方式。'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '降级策略',
|
|
|
|
|
|
intent: '失败处理',
|
|
|
|
|
|
content: normalizeText(content.degrade_strategy) || '未配置降级策略。'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '任务场景',
|
|
|
|
|
|
intent: '调度目标',
|
|
|
|
|
|
content: formatScenarioList(detail.scenario_json)
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '执行 Agent',
|
|
|
|
|
|
intent: '运行主体',
|
|
|
|
|
|
content: normalizeText(content.target_agent) || normalizeText(detail.config_json?.agent) || '未配置执行 Agent。'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '最近执行结果',
|
|
|
|
|
|
intent: '运行反馈',
|
|
|
|
|
|
content: latestRun?.result_summary || latestRun?.error_message || '暂无执行记录。'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildOutputRules(detail, typeKey, latestRun, latestCall) {
|
|
|
|
|
|
const content = detail.current_version_content || {}
|
|
|
|
|
|
|
|
|
|
|
|
if (typeKey === 'rules') {
|
|
|
|
|
|
return [
|
2026-05-15 06:57:07 +00:00
|
|
|
|
'规则使用固定模板落 Markdown,并配套维护 runtime_rule JSON。',
|
|
|
|
|
|
'保存 Markdown 或 JSON 都会生成新版本快照。',
|
2026-05-11 06:32:38 +00:00
|
|
|
|
'未审核通过的规则版本不能正式上线。',
|
|
|
|
|
|
'版本切换当前只影响前端展示内容,不会直接回滚后端版本。'
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (typeKey === 'skills') {
|
|
|
|
|
|
return [
|
|
|
|
|
|
`输入参数:${Array.isArray(content.inputs) && content.inputs.length ? content.inputs.join('、') : '未配置'}`,
|
|
|
|
|
|
`输出参数:${Array.isArray(content.outputs) && content.outputs.length ? content.outputs.join('、') : '未配置'}`,
|
|
|
|
|
|
`依赖能力:${Array.isArray(content.dependencies) && content.dependencies.length ? content.dependencies.join('、') : '未声明'}`
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (typeKey === 'mcp') {
|
|
|
|
|
|
return [
|
|
|
|
|
|
`服务地址:${normalizeText(detail.config_json?.endpoint) || '未配置'}`,
|
|
|
|
|
|
`鉴权方式:${normalizeText(content.auth_mode) || '未配置'}`,
|
|
|
|
|
|
`降级策略:${normalizeText(content.degrade_strategy) || '未配置'}`
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
`调度周期:${normalizeText(detail.config_json?.cron) || normalizeText(content.schedule) || '未配置'}`,
|
|
|
|
|
|
`执行 Agent:${normalizeText(detail.config_json?.agent) || normalizeText(content.target_agent) || '未配置'}`,
|
|
|
|
|
|
`风险等级:${formatTaskRisk(detail.scenario_json)}`,
|
|
|
|
|
|
`最近执行结果:${latestRun?.status || '暂无执行记录'}`
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildTests(detail, typeKey, latestRun, latestCall) {
|
|
|
|
|
|
if (typeKey === 'rules') {
|
|
|
|
|
|
const reviewMeta = resolveReviewMeta(detail.latest_review?.review_status)
|
|
|
|
|
|
return [
|
|
|
|
|
|
{
|
|
|
|
|
|
name: '审核状态',
|
|
|
|
|
|
input: detail.latest_review?.version || detail.current_version || '暂无版本',
|
|
|
|
|
|
result: reviewMeta.label,
|
|
|
|
|
|
tone: reviewMeta.tone
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: '上线状态',
|
|
|
|
|
|
input: detail.current_version || '暂无版本',
|
|
|
|
|
|
result: resolveStatusMeta(detail.status).label,
|
|
|
|
|
|
tone: resolveStatusMeta(detail.status).tone
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (typeKey === 'skills') {
|
|
|
|
|
|
const content = detail.current_version_content || {}
|
|
|
|
|
|
return [
|
|
|
|
|
|
{
|
|
|
|
|
|
name: '输入数量',
|
|
|
|
|
|
input: detail.current_version || '暂无版本',
|
|
|
|
|
|
result: `${content.inputs?.length || 0} 项`,
|
|
|
|
|
|
tone: 'success'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: '输出数量',
|
|
|
|
|
|
input: detail.current_version || '暂无版本',
|
|
|
|
|
|
result: `${content.outputs?.length || 0} 项`,
|
|
|
|
|
|
tone: 'success'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (typeKey === 'mcp') {
|
|
|
|
|
|
return [
|
|
|
|
|
|
{
|
|
|
|
|
|
name: '最近调用状态',
|
|
|
|
|
|
input: latestCall?.run?.run_id || '暂无调用',
|
|
|
|
|
|
result: latestCall?.toolCall?.status || '未记录',
|
|
|
|
|
|
tone: latestCall?.toolCall?.status === 'failed' ? 'danger' : 'success'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: '最近调用耗时',
|
|
|
|
|
|
input: latestCall?.toolCall?.tool_name || '暂无调用',
|
|
|
|
|
|
result:
|
|
|
|
|
|
typeof latestCall?.toolCall?.duration_ms === 'number'
|
|
|
|
|
|
? `${latestCall.toolCall.duration_ms} ms`
|
|
|
|
|
|
: '未记录',
|
|
|
|
|
|
tone: 'success'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
{
|
|
|
|
|
|
name: '最近运行状态',
|
|
|
|
|
|
input: latestRun?.run_id || '暂无运行',
|
|
|
|
|
|
result: latestRun?.status || '未记录',
|
|
|
|
|
|
tone: latestRun?.status === 'failed' || latestRun?.status === 'blocked' ? 'danger' : 'success'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: '结果摘要',
|
|
|
|
|
|
input: latestRun?.agent || normalizeText(detail.config_json?.agent) || '未配置',
|
|
|
|
|
|
result: latestRun?.result_summary || '暂无摘要',
|
|
|
|
|
|
tone: 'success'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildTools(detail, typeKey, latestRun, latestCall) {
|
|
|
|
|
|
const content = detail.current_version_content || {}
|
|
|
|
|
|
|
|
|
|
|
|
if (typeKey === 'skills') {
|
|
|
|
|
|
return (content.dependencies || []).map((item) => ({
|
|
|
|
|
|
name: item,
|
|
|
|
|
|
scope: '技能依赖',
|
|
|
|
|
|
mode: '读取',
|
|
|
|
|
|
tone: 'safe'
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (typeKey === 'mcp') {
|
|
|
|
|
|
return [
|
|
|
|
|
|
{
|
|
|
|
|
|
name: normalizeText(content.service_type) || '未配置服务类型',
|
|
|
|
|
|
scope: '服务类型',
|
|
|
|
|
|
mode: 'MCP',
|
|
|
|
|
|
tone: 'active'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: normalizeText(content.auth_mode) || '未配置鉴权方式',
|
|
|
|
|
|
scope: '鉴权',
|
|
|
|
|
|
mode: '安全',
|
|
|
|
|
|
tone: 'safe'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: latestCall?.run?.run_id || '暂无调用记录',
|
|
|
|
|
|
scope: '最近 Run',
|
|
|
|
|
|
mode: latestCall?.toolCall?.status || '未执行',
|
|
|
|
|
|
tone: latestCall?.toolCall?.status === 'failed' ? 'danger' : 'active'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
{
|
|
|
|
|
|
name: normalizeText(detail.config_json?.agent) || normalizeText(content.target_agent) || '未配置 Agent',
|
|
|
|
|
|
scope: '执行 Agent',
|
|
|
|
|
|
mode: '调度',
|
|
|
|
|
|
tone: 'active'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: latestRun?.run_id || '暂无执行记录',
|
|
|
|
|
|
scope: '最近 Run',
|
|
|
|
|
|
mode: latestRun?.status || '未执行',
|
|
|
|
|
|
tone: latestRun?.status === 'failed' || latestRun?.status === 'blocked' ? 'danger' : 'active'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: latestRun?.permission_level || '未记录',
|
|
|
|
|
|
scope: '权限级别',
|
|
|
|
|
|
mode: 'Trace',
|
|
|
|
|
|
tone: 'safe'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildPublishDescription(detail, typeKey) {
|
|
|
|
|
|
if (typeKey === 'rules') {
|
|
|
|
|
|
if (detail.status === 'active') {
|
|
|
|
|
|
return '当前规则版本已经上线,仍可继续保存新版本并重新走审核。'
|
|
|
|
|
|
}
|
|
|
|
|
|
return '当前规则需要先完成审核,再调用上线接口正式激活。'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return DETAIL_TITLES[typeKey].publishDesc
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildDetailViewModel(detail, runs) {
|
|
|
|
|
|
const typeKey = resolveTypeKey(detail.asset_type)
|
|
|
|
|
|
const latestRun = typeKey === 'tasks' ? findLatestTaskRun(runs, detail.id) : null
|
|
|
|
|
|
const latestCall = typeKey === 'mcp' ? findLatestMcpCall(runs, detail.code) : null
|
2026-05-15 06:57:07 +00:00
|
|
|
|
const configJson = readConfigJson(detail)
|
2026-05-11 06:32:38 +00:00
|
|
|
|
const statusMeta = resolveStatusMeta(detail.status)
|
|
|
|
|
|
const reviewMeta = resolveReviewMeta(detail.latest_review?.review_status)
|
2026-05-15 06:57:07 +00:00
|
|
|
|
const history = buildHistory(detail.recent_versions || [], detail)
|
2026-05-11 06:32:38 +00:00
|
|
|
|
const previewVersion = history.find((item) => item.isCurrent) || history[0] || null
|
2026-05-15 06:57:07 +00:00
|
|
|
|
const previewRawMarkdown =
|
2026-05-11 06:32:38 +00:00
|
|
|
|
detail.current_version_content_type === 'markdown'
|
|
|
|
|
|
? String(previewVersion?.content ?? detail.current_version_content ?? '')
|
|
|
|
|
|
: ''
|
2026-05-15 06:57:07 +00:00
|
|
|
|
const previewRuntimeRule = resolveRuntimeRuleForVersion(
|
|
|
|
|
|
detail,
|
|
|
|
|
|
previewRawMarkdown,
|
|
|
|
|
|
previewVersion?.runtimeRule || configJson.runtime_rule
|
|
|
|
|
|
)
|
|
|
|
|
|
const previewMarkdown = stripRuntimeRuleBlock(previewRawMarkdown)
|
2026-05-11 06:32:38 +00:00
|
|
|
|
const titles = DETAIL_TITLES[typeKey]
|
2026-05-15 06:57:07 +00:00
|
|
|
|
const previewChangeNote = previewVersion?.note || detail.current_version_change_note || '无版本说明'
|
|
|
|
|
|
const ruleTemplateKey = normalizeText(configJson.rule_template_key || previewRuntimeRule.template_key)
|
|
|
|
|
|
const ruleTemplateLabel = normalizeText(configJson.rule_template_label) || resolveRuleTemplateLabel(ruleTemplateKey)
|
|
|
|
|
|
const runtimeKind = normalizeText(configJson.runtime_kind || previewRuntimeRule.kind) || 'policy_rule_draft'
|
2026-05-11 06:32:38 +00:00
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
id: detail.id,
|
|
|
|
|
|
type: typeKey,
|
|
|
|
|
|
typeLabel: TYPE_META[typeKey].typeLabel,
|
|
|
|
|
|
short: makeShort(detail.name),
|
|
|
|
|
|
name: detail.name,
|
|
|
|
|
|
code: detail.code,
|
|
|
|
|
|
summary: detail.description,
|
|
|
|
|
|
owner: detail.owner,
|
|
|
|
|
|
reviewer: detail.reviewer || detail.latest_review?.reviewer || '待分配',
|
|
|
|
|
|
category: resolveDomainLabel(detail.domain),
|
|
|
|
|
|
scope: formatScenarioList(detail.scenario_json),
|
|
|
|
|
|
version: detail.current_version || '-',
|
|
|
|
|
|
currentVersion: detail.current_version || '-',
|
|
|
|
|
|
displayVersion: previewVersion?.version || detail.current_version || '-',
|
|
|
|
|
|
status: statusMeta.label,
|
|
|
|
|
|
statusValue: detail.status,
|
|
|
|
|
|
statusTone: statusMeta.tone,
|
|
|
|
|
|
hitRate: buildRowMetric(detail, typeKey),
|
|
|
|
|
|
updatedAt: formatDateTime(detail.updated_at),
|
|
|
|
|
|
badgeTone: BADGE_TONES[typeKey],
|
2026-05-15 06:57:07 +00:00
|
|
|
|
configJson,
|
|
|
|
|
|
scenarioList: Array.isArray(detail.scenario_json) ? [...detail.scenario_json] : [],
|
2026-05-11 06:32:38 +00:00
|
|
|
|
markdownContent: previewMarkdown,
|
2026-05-15 06:57:07 +00:00
|
|
|
|
runtimeRuleText: stringifyRuntimeRule(previewRuntimeRule),
|
|
|
|
|
|
ruleTemplateKey,
|
|
|
|
|
|
ruleTemplateLabel,
|
|
|
|
|
|
runtimeKind,
|
2026-05-11 06:32:38 +00:00
|
|
|
|
currentVersionContentType: detail.current_version_content_type,
|
|
|
|
|
|
currentVersionChangeNote: detail.current_version_change_note || '无版本说明',
|
2026-05-15 06:57:07 +00:00
|
|
|
|
displayVersionChangeNote: previewChangeNote,
|
2026-05-11 06:32:38 +00:00
|
|
|
|
reviewStatusLabel: reviewMeta.label,
|
|
|
|
|
|
reviewStatusTone: reviewMeta.tone,
|
|
|
|
|
|
reviewStatusValue: detail.latest_review?.review_status || '',
|
|
|
|
|
|
reviewTimeLabel: formatDateTime(detail.latest_review?.reviewed_at),
|
|
|
|
|
|
reviewNote: detail.latest_review?.review_note || '',
|
|
|
|
|
|
latestRun,
|
|
|
|
|
|
latestCall,
|
|
|
|
|
|
fields: buildFields(detail, typeKey, latestRun, latestCall),
|
|
|
|
|
|
promptSections:
|
|
|
|
|
|
typeKey === 'rules' ? [] : buildPromptSections(detail, typeKey, latestRun, latestCall),
|
|
|
|
|
|
outputRules: buildOutputRules(detail, typeKey, latestRun, latestCall),
|
|
|
|
|
|
tests: buildTests(detail, typeKey, latestRun, latestCall),
|
|
|
|
|
|
triggers: detail.scenario_json?.length ? detail.scenario_json.map((item) => SCENARIO_LABELS[item] || item) : ['未配置场景'],
|
|
|
|
|
|
tools:
|
|
|
|
|
|
typeKey === 'rules'
|
|
|
|
|
|
? [
|
|
|
|
|
|
{
|
|
|
|
|
|
name: detail.latest_review?.reviewer || '待分配审核人',
|
|
|
|
|
|
scope: '审核负责人',
|
|
|
|
|
|
mode: reviewMeta.label,
|
|
|
|
|
|
tone: reviewMeta.tone
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: detail.current_version || '暂无版本',
|
|
|
|
|
|
scope: '当前版本',
|
|
|
|
|
|
mode: detail.current_version_change_note || '无版本说明',
|
|
|
|
|
|
tone: 'safe'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
: buildTools(detail, typeKey, latestRun, latestCall),
|
2026-05-09 15:46:16 +00:00
|
|
|
|
history,
|
|
|
|
|
|
configTitle: titles.configTitle,
|
|
|
|
|
|
configDesc: titles.configDesc,
|
|
|
|
|
|
detailTitle: titles.detailTitle,
|
|
|
|
|
|
detailDesc: titles.detailDesc,
|
|
|
|
|
|
outputTitle: titles.outputTitle,
|
|
|
|
|
|
outputDesc: titles.outputDesc,
|
|
|
|
|
|
ruleListTitle: titles.ruleListTitle,
|
|
|
|
|
|
checkListTitle: titles.checkListTitle,
|
|
|
|
|
|
triggerTitle: titles.triggerTitle,
|
|
|
|
|
|
triggerDesc: titles.triggerDesc,
|
|
|
|
|
|
toolTitle: titles.toolTitle,
|
|
|
|
|
|
toolDesc: titles.toolDesc,
|
|
|
|
|
|
historyTitle: titles.historyTitle,
|
|
|
|
|
|
historyDesc: titles.historyDesc,
|
|
|
|
|
|
publishTitle: titles.publishTitle,
|
2026-05-11 06:32:38 +00:00
|
|
|
|
publishDesc: buildPublishDescription(detail, typeKey),
|
|
|
|
|
|
publishMeta:
|
|
|
|
|
|
typeKey === 'rules'
|
|
|
|
|
|
? `最近保存:${formatDateTime(detail.updated_at)}`
|
|
|
|
|
|
: latestRun
|
|
|
|
|
|
? `最近运行:${formatDateTime(latestRun.started_at)}`
|
|
|
|
|
|
: `最近更新:${formatDateTime(detail.updated_at)}`,
|
|
|
|
|
|
publishState: statusMeta.label,
|
|
|
|
|
|
latestReviewVersion: detail.latest_review?.version || detail.current_version || '-',
|
|
|
|
|
|
loading: false
|
2026-05-09 15:46:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-11 06:32:38 +00:00
|
|
|
|
function incrementVersion(version) {
|
|
|
|
|
|
const normalized = normalizeText(version).replace(/^v/i, '')
|
|
|
|
|
|
const match = normalized.match(/^(\d+)\.(\d+)\.(\d+)$/)
|
|
|
|
|
|
|
|
|
|
|
|
if (!match) {
|
|
|
|
|
|
return 'v1.0.0'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const major = Number(match[1])
|
|
|
|
|
|
const minor = Number(match[2])
|
|
|
|
|
|
const patch = Number(match[3]) + 1
|
|
|
|
|
|
return `v${major}.${minor}.${patch}`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildReviewNote(status) {
|
|
|
|
|
|
if (status === 'approved') {
|
|
|
|
|
|
return '通过任务规则中心审核。'
|
|
|
|
|
|
}
|
|
|
|
|
|
if (status === 'rejected') {
|
|
|
|
|
|
return '在任务规则中心驳回当前版本。'
|
|
|
|
|
|
}
|
|
|
|
|
|
return '提交任务规则中心待审核。'
|
|
|
|
|
|
}
|
2026-05-06 11:00:38 +08:00
|
|
|
|
|
|
|
|
|
|
export default {
|
2026-05-09 15:46:16 +00:00
|
|
|
|
name: 'AuditView',
|
2026-05-13 03:35:44 +00:00
|
|
|
|
components: {
|
2026-05-13 06:52:30 +00:00
|
|
|
|
ConfirmDialog,
|
|
|
|
|
|
TableEmptyState
|
2026-05-13 03:35:44 +00:00
|
|
|
|
},
|
2026-05-09 16:16:56 +00:00
|
|
|
|
emits: ['detail-open-change'],
|
2026-05-11 06:32:38 +00:00
|
|
|
|
setup(_, { emit }) {
|
|
|
|
|
|
const { toast } = useToast()
|
|
|
|
|
|
const { currentUser } = useSystemState()
|
|
|
|
|
|
|
|
|
|
|
|
const tabs = Object.entries(TYPE_META).map(([id, meta]) => ({
|
|
|
|
|
|
id,
|
|
|
|
|
|
label: meta.label
|
|
|
|
|
|
}))
|
|
|
|
|
|
|
2026-05-11 01:53:30 +00:00
|
|
|
|
const activeType = ref('rules')
|
2026-05-06 11:00:38 +08:00
|
|
|
|
const selectedSkill = ref(null)
|
2026-05-09 15:46:16 +00:00
|
|
|
|
const versionSwitchTarget = ref(null)
|
2026-05-11 01:53:30 +00:00
|
|
|
|
const keyword = ref('')
|
2026-05-11 06:32:38 +00:00
|
|
|
|
const activeFilterPopover = ref('')
|
|
|
|
|
|
const selectedDomain = ref('')
|
|
|
|
|
|
const selectedOwner = ref('')
|
|
|
|
|
|
const selectedStatus = ref('')
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
const errorMessage = ref('')
|
|
|
|
|
|
const detailLoading = ref(false)
|
|
|
|
|
|
const detailError = ref('')
|
|
|
|
|
|
const actionState = ref('')
|
|
|
|
|
|
const runLoading = ref(false)
|
|
|
|
|
|
const runs = ref([])
|
|
|
|
|
|
const assetBuckets = ref({
|
|
|
|
|
|
rules: [],
|
|
|
|
|
|
skills: [],
|
|
|
|
|
|
mcp: [],
|
|
|
|
|
|
tasks: []
|
|
|
|
|
|
})
|
2026-05-06 11:00:38 +08:00
|
|
|
|
|
2026-05-11 06:32:38 +00:00
|
|
|
|
const isAdmin = computed(() => isManagerUser(currentUser.value))
|
2026-05-09 15:46:16 +00:00
|
|
|
|
const activeMeta = computed(() => TYPE_META[activeType.value])
|
2026-05-11 06:32:38 +00:00
|
|
|
|
const activeTabLabel = computed(() => activeMeta.value.label)
|
|
|
|
|
|
const currentAssets = computed(() => assetBuckets.value[activeType.value] || [])
|
|
|
|
|
|
const searchPlaceholder = computed(() => activeMeta.value.searchPlaceholder)
|
2026-05-09 15:46:16 +00:00
|
|
|
|
const createButtonLabel = computed(() => activeMeta.value.createButtonLabel)
|
|
|
|
|
|
const hintText = computed(() => activeMeta.value.hintText)
|
|
|
|
|
|
const tableColumns = computed(() => activeMeta.value.tableColumns)
|
2026-05-11 06:33:46 +00:00
|
|
|
|
const showMetricColumn = computed(() => activeMeta.value.showMetricColumn !== false)
|
2026-05-11 06:32:38 +00:00
|
|
|
|
const selectedSkillIsRule = computed(() => selectedSkill.value?.type === 'rules')
|
|
|
|
|
|
const canManageSelected = computed(() => isAdmin.value && Boolean(selectedSkill.value))
|
|
|
|
|
|
const canEditMarkdown = computed(() => canManageSelected.value && selectedSkillIsRule.value)
|
|
|
|
|
|
const detailBusy = computed(() => Boolean(actionState.value))
|
|
|
|
|
|
const showReviewNote = computed(
|
|
|
|
|
|
() => selectedSkillIsRule.value && (selectedSkill.value?.reviewNote || selectedSkill.value?.reviewTimeLabel)
|
|
|
|
|
|
)
|
|
|
|
|
|
const domainOptions = computed(() => {
|
|
|
|
|
|
const uniqueValues = [...new Set(currentAssets.value.map((item) => item.domainValue).filter(Boolean))]
|
|
|
|
|
|
return [
|
|
|
|
|
|
{ value: '', label: '全部业务域' },
|
|
|
|
|
|
...uniqueValues.map((value) => ({
|
|
|
|
|
|
value,
|
|
|
|
|
|
label: resolveDomainLabel(value)
|
|
|
|
|
|
}))
|
|
|
|
|
|
]
|
|
|
|
|
|
})
|
|
|
|
|
|
const ownerOptions = computed(() => {
|
|
|
|
|
|
const uniqueOwners = [...new Set(currentAssets.value.map((item) => item.owner).filter(Boolean))]
|
|
|
|
|
|
return [
|
|
|
|
|
|
{ value: '', label: '全部负责人' },
|
|
|
|
|
|
...uniqueOwners.map((value) => ({
|
|
|
|
|
|
value,
|
|
|
|
|
|
label: value
|
|
|
|
|
|
}))
|
|
|
|
|
|
]
|
|
|
|
|
|
})
|
|
|
|
|
|
const selectedDomainLabel = computed(
|
|
|
|
|
|
() => domainOptions.value.find((item) => item.value === selectedDomain.value)?.label || '业务域'
|
|
|
|
|
|
)
|
|
|
|
|
|
const selectedOwnerLabel = computed(
|
|
|
|
|
|
() => ownerOptions.value.find((item) => item.value === selectedOwner.value)?.label || '负责人'
|
|
|
|
|
|
)
|
|
|
|
|
|
const selectedStatusLabel = computed(
|
|
|
|
|
|
() => STATUS_OPTIONS.find((item) => item.value === selectedStatus.value)?.label || '状态'
|
|
|
|
|
|
)
|
|
|
|
|
|
const activeFilterTokens = computed(() => {
|
|
|
|
|
|
const tokens = []
|
|
|
|
|
|
|
|
|
|
|
|
if (selectedDomain.value) {
|
|
|
|
|
|
tokens.push(`业务域:${resolveDomainLabel(selectedDomain.value)}`)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (selectedStatus.value) {
|
|
|
|
|
|
tokens.push(`状态:${resolveStatusMeta(selectedStatus.value).label}`)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (selectedOwner.value) {
|
|
|
|
|
|
tokens.push(`负责人:${selectedOwner.value}`)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (keyword.value.trim()) {
|
|
|
|
|
|
tokens.push(`搜索:${keyword.value.trim()}`)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return tokens
|
|
|
|
|
|
})
|
2026-05-13 06:52:30 +00:00
|
|
|
|
const auditEmptyState = computed(() => {
|
|
|
|
|
|
const hasFilters = activeFilterTokens.value.length > 0
|
|
|
|
|
|
|
|
|
|
|
|
if (!currentAssets.value.length) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
eyebrow: `${activeTabLabel.value}资产`,
|
|
|
|
|
|
title: `${activeTabLabel.value}列表暂时还是空的`,
|
|
|
|
|
|
desc: `当前环境里还没有可展示的${activeTabLabel.value}资产。完成接入或同步后,会统一展示在这里。`,
|
|
|
|
|
|
icon: 'mdi mdi-database-search-outline',
|
|
|
|
|
|
actionLabel: '重新加载',
|
|
|
|
|
|
actionIcon: 'mdi mdi-refresh',
|
|
|
|
|
|
tone: 'amber',
|
|
|
|
|
|
artLabel: 'ASSET',
|
|
|
|
|
|
tips: ['切换页签可查看其他资产类型', '支持按业务域、负责人和状态做过滤']
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
eyebrow: '筛选结果为空',
|
|
|
|
|
|
title: `没有找到匹配的${activeTabLabel.value}`,
|
|
|
|
|
|
desc: hasFilters
|
|
|
|
|
|
? '试试清空业务域、负责人、状态或关键词筛选,再重新查看。'
|
|
|
|
|
|
: `当前列表中还没有满足展示条件的${activeTabLabel.value}资产。`,
|
|
|
|
|
|
icon: hasFilters ? 'mdi mdi-tune-variant' : 'mdi mdi-view-grid-outline',
|
|
|
|
|
|
actionLabel: hasFilters ? '清空筛选' : '重新加载',
|
|
|
|
|
|
actionIcon: hasFilters ? 'mdi mdi-filter-remove-outline' : 'mdi mdi-refresh',
|
|
|
|
|
|
tone: hasFilters ? 'emerald' : 'slate',
|
|
|
|
|
|
artLabel: hasFilters ? 'FILTER' : 'QUEUE',
|
|
|
|
|
|
tips: hasFilters
|
|
|
|
|
|
? ['业务域、负责人、状态与关键词会叠加过滤', '可以换个编码、名称或负责人关键词继续搜索']
|
|
|
|
|
|
: ['列表展示来自真实资产 API', '切换资产类型后会自动重新拉取数据']
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2026-05-11 06:32:38 +00:00
|
|
|
|
const canActivateSelected = computed(() => {
|
|
|
|
|
|
if (!selectedSkillIsRule.value || !canManageSelected.value || detailBusy.value) {
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return selectedSkill.value?.reviewStatusValue === 'approved' && selectedSkill.value?.statusValue !== 'active'
|
|
|
|
|
|
})
|
|
|
|
|
|
const activateBlockedReason = computed(() => {
|
|
|
|
|
|
if (!selectedSkillIsRule.value) {
|
|
|
|
|
|
return ''
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!canManageSelected.value) {
|
|
|
|
|
|
return '仅管理员可执行审核和上线。'
|
|
|
|
|
|
}
|
|
|
|
|
|
if (selectedSkill.value?.statusValue === 'active') {
|
|
|
|
|
|
return '当前规则版本已经上线。'
|
|
|
|
|
|
}
|
|
|
|
|
|
if (selectedSkill.value?.reviewStatusValue !== 'approved') {
|
|
|
|
|
|
return '当前规则版本未审核通过,不能上线。'
|
|
|
|
|
|
}
|
|
|
|
|
|
return ''
|
2026-05-11 01:53:30 +00:00
|
|
|
|
})
|
|
|
|
|
|
const visibleSkills = computed(() => {
|
|
|
|
|
|
const normalizedKeyword = keyword.value.trim().toLowerCase()
|
|
|
|
|
|
|
2026-05-11 06:32:38 +00:00
|
|
|
|
return currentAssets.value.filter((item) => {
|
|
|
|
|
|
const matchesKeyword = normalizedKeyword
|
|
|
|
|
|
? [item.name, item.code, item.summary, item.owner, item.scope]
|
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
|
.some((value) => String(value).toLowerCase().includes(normalizedKeyword))
|
|
|
|
|
|
: true
|
|
|
|
|
|
const matchesDomain = selectedDomain.value ? item.domainValue === selectedDomain.value : true
|
|
|
|
|
|
const matchesOwner = selectedOwner.value ? item.owner === selectedOwner.value : true
|
|
|
|
|
|
const matchesStatus = selectedStatus.value ? item.statusValue === selectedStatus.value : true
|
2026-05-11 01:53:30 +00:00
|
|
|
|
|
2026-05-11 06:32:38 +00:00
|
|
|
|
return matchesKeyword && matchesDomain && matchesOwner && matchesStatus
|
|
|
|
|
|
})
|
2026-05-11 01:53:30 +00:00
|
|
|
|
})
|
2026-05-09 15:46:16 +00:00
|
|
|
|
|
2026-05-09 16:16:56 +00:00
|
|
|
|
watch(
|
|
|
|
|
|
selectedSkill,
|
|
|
|
|
|
(value) => {
|
|
|
|
|
|
emit('detail-open-change', Boolean(value))
|
|
|
|
|
|
},
|
|
|
|
|
|
{ immediate: true }
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-05-11 06:32:38 +00:00
|
|
|
|
watch(activeType, () => {
|
|
|
|
|
|
selectedSkill.value = null
|
|
|
|
|
|
versionSwitchTarget.value = null
|
|
|
|
|
|
resetFilters()
|
|
|
|
|
|
loadAssets({ force: true }).catch((error) => {
|
|
|
|
|
|
errorMessage.value = error?.message || '资产数据加载失败,请稍后重试。'
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
function resetFilters() {
|
|
|
|
|
|
keyword.value = ''
|
|
|
|
|
|
selectedDomain.value = ''
|
|
|
|
|
|
selectedOwner.value = ''
|
|
|
|
|
|
selectedStatus.value = ''
|
|
|
|
|
|
activeFilterPopover.value = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 06:52:30 +00:00
|
|
|
|
function handleAuditEmptyAction() {
|
|
|
|
|
|
if (!currentAssets.value.length || !activeFilterTokens.value.length) {
|
|
|
|
|
|
loadAssets({ force: true }).catch(() => {})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
resetFilters()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-11 06:32:38 +00:00
|
|
|
|
function toggleFilterPopover(name) {
|
|
|
|
|
|
activeFilterPopover.value = activeFilterPopover.value === name ? '' : name
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function closeFilterPopover() {
|
|
|
|
|
|
activeFilterPopover.value = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function selectFilter(name, value) {
|
|
|
|
|
|
if (name === 'domain') {
|
|
|
|
|
|
selectedDomain.value = value
|
|
|
|
|
|
}
|
|
|
|
|
|
if (name === 'owner') {
|
|
|
|
|
|
selectedOwner.value = value
|
|
|
|
|
|
}
|
|
|
|
|
|
if (name === 'status') {
|
|
|
|
|
|
selectedStatus.value = value
|
|
|
|
|
|
}
|
|
|
|
|
|
closeFilterPopover()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleDocumentClick(event) {
|
|
|
|
|
|
const target = event.target
|
|
|
|
|
|
if (!(target instanceof Element)) {
|
|
|
|
|
|
closeFilterPopover()
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!target.closest('.picker-filter')) {
|
|
|
|
|
|
closeFilterPopover()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function resolveActor() {
|
|
|
|
|
|
return currentUser.value?.name || currentUser.value?.username || 'system'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-15 06:57:07 +00:00
|
|
|
|
function buildRuleConfigPayload(asset, runtimeRule) {
|
|
|
|
|
|
const configJson = {
|
|
|
|
|
|
...readConfigJson(asset),
|
|
|
|
|
|
runtime_kind: normalizeText(runtimeRule?.kind) || asset.runtimeKind || 'policy_rule_draft',
|
|
|
|
|
|
runtime_rule: runtimeRule
|
|
|
|
|
|
}
|
|
|
|
|
|
const templateKey = normalizeText(runtimeRule?.template_key) || asset.ruleTemplateKey
|
|
|
|
|
|
if (templateKey) {
|
|
|
|
|
|
configJson.rule_template_key = templateKey
|
|
|
|
|
|
configJson.rule_template_label = resolveRuleTemplateLabel(templateKey)
|
|
|
|
|
|
}
|
|
|
|
|
|
return configJson
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function persistRuleRuntimeConfig(asset, runtimeRule) {
|
|
|
|
|
|
await updateAgentAsset(
|
|
|
|
|
|
asset.id,
|
|
|
|
|
|
{
|
|
|
|
|
|
config_json: buildRuleConfigPayload(asset, runtimeRule)
|
|
|
|
|
|
},
|
|
|
|
|
|
{ actor: resolveActor() }
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-11 06:32:38 +00:00
|
|
|
|
async function loadRuns(options = {}) {
|
|
|
|
|
|
if (runLoading.value && !options.force) {
|
2026-05-09 15:46:16 +00:00
|
|
|
|
return
|
2026-05-06 11:00:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-11 06:32:38 +00:00
|
|
|
|
runLoading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const payload = await fetchAgentRuns({ limit: 50 })
|
|
|
|
|
|
runs.value = Array.isArray(payload) ? payload : []
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
runLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function loadAssets(options = {}) {
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
|
errorMessage.value = ''
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const payload = await fetchAgentAssets({ assetType: activeMeta.value.assetType })
|
|
|
|
|
|
assetBuckets.value = {
|
|
|
|
|
|
...assetBuckets.value,
|
|
|
|
|
|
[activeType.value]: Array.isArray(payload) ? payload.map(buildListItem) : []
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
assetBuckets.value = {
|
|
|
|
|
|
...assetBuckets.value,
|
|
|
|
|
|
[activeType.value]: []
|
|
|
|
|
|
}
|
|
|
|
|
|
errorMessage.value = error?.message || '资产数据加载失败,请稍后重试。'
|
|
|
|
|
|
if (!options.silent) {
|
|
|
|
|
|
toast(errorMessage.value)
|
|
|
|
|
|
}
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function refreshCurrentAssets() {
|
|
|
|
|
|
await loadAssets({ force: true, silent: true })
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function loadSelectedAssetDetail(assetId) {
|
|
|
|
|
|
detailLoading.value = true
|
|
|
|
|
|
detailError.value = ''
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (!runs.value.length) {
|
|
|
|
|
|
await loadRuns()
|
|
|
|
|
|
}
|
|
|
|
|
|
const detail = await fetchAgentAssetDetail(assetId)
|
|
|
|
|
|
selectedSkill.value = buildDetailViewModel(detail, runs.value)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
detailError.value = error?.message || '资产详情加载失败,请稍后重试。'
|
|
|
|
|
|
toast(detailError.value)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
detailLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function openAssetDetail(asset) {
|
|
|
|
|
|
selectedSkill.value = {
|
|
|
|
|
|
...asset,
|
2026-05-15 06:57:07 +00:00
|
|
|
|
configJson: {},
|
|
|
|
|
|
scenarioList: [],
|
2026-05-11 06:32:38 +00:00
|
|
|
|
fields: [],
|
|
|
|
|
|
promptSections: [],
|
|
|
|
|
|
outputRules: [],
|
|
|
|
|
|
tests: [],
|
|
|
|
|
|
triggers: [],
|
|
|
|
|
|
tools: [],
|
|
|
|
|
|
history: [],
|
|
|
|
|
|
markdownContent: '',
|
2026-05-15 06:57:07 +00:00
|
|
|
|
runtimeRuleText: '',
|
|
|
|
|
|
ruleTemplateKey: '',
|
|
|
|
|
|
ruleTemplateLabel: '',
|
|
|
|
|
|
runtimeKind: 'policy_rule_draft',
|
2026-05-11 06:32:38 +00:00
|
|
|
|
displayVersion: asset.version,
|
2026-05-15 06:57:07 +00:00
|
|
|
|
displayVersionChangeNote: '无版本说明',
|
2026-05-11 06:32:38 +00:00
|
|
|
|
loading: true,
|
|
|
|
|
|
reviewStatusLabel: '加载中',
|
|
|
|
|
|
reviewStatusTone: 'draft'
|
|
|
|
|
|
}
|
|
|
|
|
|
versionSwitchTarget.value = null
|
|
|
|
|
|
loadSelectedAssetDetail(asset.id).catch(() => {})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function closeDetail() {
|
|
|
|
|
|
selectedSkill.value = null
|
|
|
|
|
|
detailError.value = ''
|
|
|
|
|
|
detailLoading.value = false
|
|
|
|
|
|
versionSwitchTarget.value = null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function openVersionSwitch(version) {
|
|
|
|
|
|
if (!selectedSkill.value || version.version === selectedSkill.value.displayVersion) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-05-09 15:46:16 +00:00
|
|
|
|
versionSwitchTarget.value = version
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function cancelVersionSwitch() {
|
|
|
|
|
|
versionSwitchTarget.value = null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function confirmVersionSwitch() {
|
|
|
|
|
|
if (!selectedSkill.value || !versionSwitchTarget.value) {
|
|
|
|
|
|
return
|
2026-05-06 11:00:38 +08:00
|
|
|
|
}
|
2026-05-09 15:46:16 +00:00
|
|
|
|
|
2026-05-11 06:32:38 +00:00
|
|
|
|
selectedSkill.value.displayVersion = versionSwitchTarget.value.version
|
2026-05-15 06:57:07 +00:00
|
|
|
|
selectedSkill.value.displayVersionChangeNote = versionSwitchTarget.value.note || '无版本说明'
|
|
|
|
|
|
if (typeof versionSwitchTarget.value.markdownContent === 'string') {
|
|
|
|
|
|
selectedSkill.value.markdownContent = versionSwitchTarget.value.markdownContent
|
2026-05-11 06:32:38 +00:00
|
|
|
|
}
|
2026-05-15 06:57:07 +00:00
|
|
|
|
const runtimeRule = versionSwitchTarget.value.runtimeRule || buildDefaultRuntimeRule(selectedSkill.value)
|
|
|
|
|
|
selectedSkill.value.runtimeRuleText = stringifyRuntimeRule(runtimeRule)
|
|
|
|
|
|
selectedSkill.value.runtimeKind =
|
|
|
|
|
|
normalizeText(runtimeRule.kind) || selectedSkill.value.runtimeKind || 'policy_rule_draft'
|
|
|
|
|
|
selectedSkill.value.ruleTemplateKey =
|
|
|
|
|
|
normalizeText(runtimeRule.template_key) || selectedSkill.value.ruleTemplateKey
|
|
|
|
|
|
selectedSkill.value.ruleTemplateLabel = resolveRuleTemplateLabel(selectedSkill.value.ruleTemplateKey)
|
2026-05-11 06:32:38 +00:00
|
|
|
|
versionSwitchTarget.value = null
|
|
|
|
|
|
}
|
2026-05-09 15:46:16 +00:00
|
|
|
|
|
2026-05-11 06:32:38 +00:00
|
|
|
|
async function saveRuleMarkdown() {
|
|
|
|
|
|
if (!selectedSkill.value || !selectedSkillIsRule.value || !canEditMarkdown.value || detailBusy.value) {
|
|
|
|
|
|
return
|
2026-05-09 15:46:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-11 06:32:38 +00:00
|
|
|
|
if (!normalizeText(selectedSkill.value.markdownContent)) {
|
|
|
|
|
|
toast('规则 Markdown 内容不能为空。')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-15 06:57:07 +00:00
|
|
|
|
const runtimeRule = parseRuntimeRuleText(selectedSkill.value.runtimeRuleText)
|
|
|
|
|
|
if (!runtimeRule) {
|
|
|
|
|
|
toast('运行时 JSON 必须是合法的对象。')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-11 06:32:38 +00:00
|
|
|
|
const nextVersion = incrementVersion(selectedSkill.value.currentVersion)
|
|
|
|
|
|
actionState.value = 'save-markdown'
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
await createAgentAssetVersion(
|
|
|
|
|
|
selectedSkill.value.id,
|
|
|
|
|
|
{
|
|
|
|
|
|
version: nextVersion,
|
2026-05-15 06:57:07 +00:00
|
|
|
|
content: buildMarkdownVersionContent(selectedSkill.value.markdownContent, runtimeRule),
|
2026-05-11 06:32:38 +00:00
|
|
|
|
content_type: 'markdown',
|
2026-05-15 06:57:07 +00:00
|
|
|
|
change_note: '通过任务规则中心保存 Markdown 规则内容,并同步运行时 JSON。',
|
2026-05-11 06:32:38 +00:00
|
|
|
|
created_by: resolveActor()
|
|
|
|
|
|
},
|
|
|
|
|
|
{ actor: resolveActor() }
|
|
|
|
|
|
)
|
2026-05-15 06:57:07 +00:00
|
|
|
|
await persistRuleRuntimeConfig(selectedSkill.value, runtimeRule)
|
2026-05-11 06:32:38 +00:00
|
|
|
|
await refreshCurrentAssets()
|
|
|
|
|
|
await loadSelectedAssetDetail(selectedSkill.value.id)
|
|
|
|
|
|
toast(`规则 Markdown 已保存为 ${nextVersion}。`)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
toast(error?.message || '规则 Markdown 保存失败,请稍后重试。')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
actionState.value = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-15 06:57:07 +00:00
|
|
|
|
async function saveRuleRuntimeJson() {
|
|
|
|
|
|
if (!selectedSkill.value || !selectedSkillIsRule.value || !canEditMarkdown.value || detailBusy.value) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!normalizeText(selectedSkill.value.markdownContent)) {
|
|
|
|
|
|
toast('规则 Markdown 模板不能为空。')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const runtimeRule = parseRuntimeRuleText(selectedSkill.value.runtimeRuleText)
|
|
|
|
|
|
if (!runtimeRule) {
|
|
|
|
|
|
toast('运行时 JSON 必须是合法的对象。')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const nextVersion = incrementVersion(selectedSkill.value.currentVersion)
|
|
|
|
|
|
actionState.value = 'save-runtime-json'
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
await createAgentAssetVersion(
|
|
|
|
|
|
selectedSkill.value.id,
|
|
|
|
|
|
{
|
|
|
|
|
|
version: nextVersion,
|
|
|
|
|
|
content: buildMarkdownVersionContent(selectedSkill.value.markdownContent, runtimeRule),
|
|
|
|
|
|
content_type: 'markdown',
|
|
|
|
|
|
change_note: '通过任务规则中心保存运行时 JSON 配置。',
|
|
|
|
|
|
created_by: resolveActor()
|
|
|
|
|
|
},
|
|
|
|
|
|
{ actor: resolveActor() }
|
|
|
|
|
|
)
|
|
|
|
|
|
await persistRuleRuntimeConfig(selectedSkill.value, runtimeRule)
|
|
|
|
|
|
await refreshCurrentAssets()
|
|
|
|
|
|
await loadSelectedAssetDetail(selectedSkill.value.id)
|
|
|
|
|
|
toast(`规则 JSON 已保存为 ${nextVersion}。`)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
toast(error?.message || '规则 JSON 保存失败,请稍后重试。')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
actionState.value = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-11 06:32:38 +00:00
|
|
|
|
async function reviewSelectedRule(reviewStatus) {
|
|
|
|
|
|
if (!selectedSkill.value || !selectedSkillIsRule.value || !canManageSelected.value || detailBusy.value) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
actionState.value = `review-${reviewStatus}`
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
await createAgentAssetReview(
|
|
|
|
|
|
selectedSkill.value.id,
|
|
|
|
|
|
{
|
|
|
|
|
|
version: selectedSkill.value.displayVersion || selectedSkill.value.currentVersion,
|
|
|
|
|
|
reviewer: resolveActor(),
|
|
|
|
|
|
review_status: reviewStatus,
|
|
|
|
|
|
review_note: buildReviewNote(reviewStatus)
|
|
|
|
|
|
},
|
|
|
|
|
|
{ actor: resolveActor() }
|
|
|
|
|
|
)
|
|
|
|
|
|
await refreshCurrentAssets()
|
|
|
|
|
|
await loadSelectedAssetDetail(selectedSkill.value.id)
|
|
|
|
|
|
toast(`当前规则版本已标记为${resolveReviewMeta(reviewStatus).label}。`)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
toast(error?.message || '规则审核提交失败,请稍后重试。')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
actionState.value = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function activateSelectedRule() {
|
|
|
|
|
|
if (!selectedSkill.value || !selectedSkillIsRule.value || !canManageSelected.value || detailBusy.value) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
actionState.value = 'activate'
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
await activateAgentAsset(selectedSkill.value.id, { actor: resolveActor() })
|
|
|
|
|
|
await refreshCurrentAssets()
|
|
|
|
|
|
await loadSelectedAssetDetail(selectedSkill.value.id)
|
|
|
|
|
|
toast('规则已正式上线。')
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
toast(error?.message || '规则上线失败,请稍后重试。')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
actionState.value = ''
|
|
|
|
|
|
}
|
2026-05-09 15:46:16 +00:00
|
|
|
|
}
|
2026-05-06 11:00:38 +08:00
|
|
|
|
|
2026-05-11 06:32:38 +00:00
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
document.addEventListener('click', handleDocumentClick)
|
|
|
|
|
|
loadAssets({ force: true }).catch(() => {})
|
|
|
|
|
|
loadRuns().catch(() => {})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
|
|
document.removeEventListener('click', handleDocumentClick)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-05-06 11:00:38 +08:00
|
|
|
|
return {
|
|
|
|
|
|
tabs,
|
2026-05-09 15:46:16 +00:00
|
|
|
|
activeType,
|
2026-05-11 06:32:38 +00:00
|
|
|
|
activeTabLabel,
|
|
|
|
|
|
selectedSkill,
|
|
|
|
|
|
versionSwitchTarget,
|
|
|
|
|
|
keyword,
|
2026-05-09 15:46:16 +00:00
|
|
|
|
createButtonLabel,
|
|
|
|
|
|
hintText,
|
2026-05-11 01:53:30 +00:00
|
|
|
|
searchPlaceholder,
|
2026-05-09 15:46:16 +00:00
|
|
|
|
tableColumns,
|
2026-05-11 06:33:46 +00:00
|
|
|
|
showMetricColumn,
|
2026-05-09 15:46:16 +00:00
|
|
|
|
visibleSkills,
|
2026-05-13 06:52:30 +00:00
|
|
|
|
auditEmptyState,
|
2026-05-11 06:32:38 +00:00
|
|
|
|
loading,
|
|
|
|
|
|
errorMessage,
|
|
|
|
|
|
detailLoading,
|
|
|
|
|
|
detailError,
|
|
|
|
|
|
selectedDomain,
|
|
|
|
|
|
selectedOwner,
|
|
|
|
|
|
selectedStatus,
|
|
|
|
|
|
selectedDomainLabel,
|
|
|
|
|
|
selectedOwnerLabel,
|
|
|
|
|
|
selectedStatusLabel,
|
|
|
|
|
|
domainOptions,
|
|
|
|
|
|
ownerOptions,
|
|
|
|
|
|
statusOptions: STATUS_OPTIONS,
|
|
|
|
|
|
activeFilterPopover,
|
|
|
|
|
|
activeFilterTokens,
|
|
|
|
|
|
canManageSelected,
|
|
|
|
|
|
canEditMarkdown,
|
|
|
|
|
|
canActivateSelected,
|
|
|
|
|
|
activateBlockedReason,
|
|
|
|
|
|
selectedSkillIsRule,
|
|
|
|
|
|
detailBusy,
|
|
|
|
|
|
actionState,
|
|
|
|
|
|
showReviewNote,
|
|
|
|
|
|
openAssetDetail,
|
|
|
|
|
|
closeDetail,
|
|
|
|
|
|
resetFilters,
|
2026-05-13 06:52:30 +00:00
|
|
|
|
handleAuditEmptyAction,
|
2026-05-11 06:32:38 +00:00
|
|
|
|
toggleFilterPopover,
|
|
|
|
|
|
selectFilter,
|
|
|
|
|
|
closeFilterPopover,
|
2026-05-09 15:46:16 +00:00
|
|
|
|
openVersionSwitch,
|
|
|
|
|
|
cancelVersionSwitch,
|
2026-05-11 06:32:38 +00:00
|
|
|
|
confirmVersionSwitch,
|
|
|
|
|
|
saveRuleMarkdown,
|
2026-05-15 06:57:07 +00:00
|
|
|
|
saveRuleRuntimeJson,
|
2026-05-11 06:32:38 +00:00
|
|
|
|
reviewSelectedRule,
|
|
|
|
|
|
activateSelectedRule,
|
|
|
|
|
|
loadAssets
|
2026-05-06 11:00:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|