1299 lines
40 KiB
JavaScript
1299 lines
40 KiB
JavaScript
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||
|
||
import { useSystemState } from '../../composables/useSystemState.js'
|
||
import { useToast } from '../../composables/useToast.js'
|
||
import {
|
||
activateAgentAsset,
|
||
createAgentAssetReview,
|
||
createAgentAssetVersion,
|
||
fetchAgentAssetDetail,
|
||
fetchAgentAssets,
|
||
fetchAgentRuns
|
||
} from '../../services/agentAssets.js'
|
||
import { isManagerUser } from '../../utils/accessControl.js'
|
||
|
||
const TYPE_META = {
|
||
rules: {
|
||
assetType: 'rule',
|
||
label: '规则',
|
||
typeLabel: '规则',
|
||
createButtonLabel: '规则已接入',
|
||
hintText: '规则列表已接到真实资产 API,可查看 Markdown、版本、审核状态和上线约束。',
|
||
searchPlaceholder: '搜索规则名称、编码或负责人',
|
||
tableColumns: {
|
||
name: '规则名称',
|
||
category: '业务域',
|
||
owner: '负责人',
|
||
scope: '适用场景',
|
||
runtime: '风险等级',
|
||
version: '当前版本',
|
||
metric: '审核状态'
|
||
}
|
||
},
|
||
skills: {
|
||
assetType: 'skill',
|
||
label: '技能',
|
||
typeLabel: '技能',
|
||
createButtonLabel: '技能已接入',
|
||
hintText: '技能页签已接到真实资产 API,可查看输入、输出、依赖和场景信息。',
|
||
searchPlaceholder: '搜索技能名称、编码或负责人',
|
||
showMetricColumn: false,
|
||
tableColumns: {
|
||
name: '技能名称',
|
||
category: '业务域',
|
||
owner: '负责人',
|
||
scope: '适用场景',
|
||
runtime: '输入摘要',
|
||
version: '当前版本',
|
||
metric: ''
|
||
}
|
||
},
|
||
mcp: {
|
||
assetType: 'mcp',
|
||
label: 'MCP',
|
||
typeLabel: 'MCP',
|
||
createButtonLabel: 'MCP 已接入',
|
||
hintText: 'MCP 页签已接到真实资产 API,可查看服务地址、鉴权方式、超时和降级策略。',
|
||
searchPlaceholder: '搜索 MCP 名称、编码或负责人',
|
||
tableColumns: {
|
||
name: 'MCP 服务',
|
||
category: '业务域',
|
||
owner: '维护人',
|
||
scope: '适用场景',
|
||
runtime: '调用地址',
|
||
version: '当前版本',
|
||
metric: '超时配置'
|
||
}
|
||
},
|
||
tasks: {
|
||
assetType: 'task',
|
||
label: '任务',
|
||
typeLabel: '任务',
|
||
createButtonLabel: '任务已接入',
|
||
hintText: '任务页签已接到真实资产 API,可查看调度周期、执行 Agent 和最近执行结果。',
|
||
searchPlaceholder: '搜索任务名称、编码或负责人',
|
||
tableColumns: {
|
||
name: '任务名称',
|
||
category: '业务域',
|
||
owner: '负责人',
|
||
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: '票据异常',
|
||
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: '已停用' }
|
||
]
|
||
|
||
function normalizeText(value) {
|
||
return String(value || '').trim()
|
||
}
|
||
|
||
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(' / ')
|
||
}
|
||
|
||
function buildHistory(recentVersions = []) {
|
||
return recentVersions.map((item) => ({
|
||
version: item.version,
|
||
note: item.change_note || '无版本说明',
|
||
time: formatDateTime(item.created_at),
|
||
content: item.content,
|
||
contentType: item.content_type,
|
||
createdBy: item.created_by,
|
||
isCurrent: Boolean(item.is_current)
|
||
}))
|
||
}
|
||
|
||
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
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
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'
|
||
}
|
||
|
||
function buildListItem(asset) {
|
||
const typeKey = resolveTypeKey(asset.asset_type)
|
||
const statusMeta = resolveStatusMeta(asset.status)
|
||
|
||
return {
|
||
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 },
|
||
{ label: '业务域', value: resolveDomainLabel(detail.domain) },
|
||
{ 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 [
|
||
'规则 Markdown 保存后会生成新版本。',
|
||
'未审核通过的规则版本不能正式上线。',
|
||
'版本切换当前只影响前端展示内容,不会直接回滚后端版本。'
|
||
]
|
||
}
|
||
|
||
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
|
||
const statusMeta = resolveStatusMeta(detail.status)
|
||
const reviewMeta = resolveReviewMeta(detail.latest_review?.review_status)
|
||
const history = buildHistory(detail.recent_versions || [])
|
||
const previewVersion = history.find((item) => item.isCurrent) || history[0] || null
|
||
const previewMarkdown =
|
||
detail.current_version_content_type === 'markdown'
|
||
? String(previewVersion?.content ?? detail.current_version_content ?? '')
|
||
: ''
|
||
const titles = DETAIL_TITLES[typeKey]
|
||
|
||
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],
|
||
markdownContent: previewMarkdown,
|
||
currentVersionContentType: detail.current_version_content_type,
|
||
currentVersionChangeNote: detail.current_version_change_note || '无版本说明',
|
||
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),
|
||
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,
|
||
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
|
||
}
|
||
}
|
||
|
||
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 '提交任务规则中心待审核。'
|
||
}
|
||
|
||
export default {
|
||
name: 'AuditView',
|
||
emits: ['detail-open-change'],
|
||
setup(_, { emit }) {
|
||
const { toast } = useToast()
|
||
const { currentUser } = useSystemState()
|
||
|
||
const tabs = Object.entries(TYPE_META).map(([id, meta]) => ({
|
||
id,
|
||
label: meta.label
|
||
}))
|
||
|
||
const activeType = ref('rules')
|
||
const selectedSkill = ref(null)
|
||
const versionSwitchTarget = ref(null)
|
||
const keyword = ref('')
|
||
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: []
|
||
})
|
||
|
||
const isAdmin = computed(() => isManagerUser(currentUser.value))
|
||
const activeMeta = computed(() => TYPE_META[activeType.value])
|
||
const activeTabLabel = computed(() => activeMeta.value.label)
|
||
const currentAssets = computed(() => assetBuckets.value[activeType.value] || [])
|
||
const searchPlaceholder = computed(() => activeMeta.value.searchPlaceholder)
|
||
const createButtonLabel = computed(() => activeMeta.value.createButtonLabel)
|
||
const hintText = computed(() => activeMeta.value.hintText)
|
||
const tableColumns = computed(() => activeMeta.value.tableColumns)
|
||
const showMetricColumn = computed(() => activeMeta.value.showMetricColumn !== false)
|
||
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
|
||
})
|
||
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 ''
|
||
})
|
||
const visibleSkills = computed(() => {
|
||
const normalizedKeyword = keyword.value.trim().toLowerCase()
|
||
|
||
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
|
||
|
||
return matchesKeyword && matchesDomain && matchesOwner && matchesStatus
|
||
})
|
||
})
|
||
|
||
watch(
|
||
selectedSkill,
|
||
(value) => {
|
||
emit('detail-open-change', Boolean(value))
|
||
},
|
||
{ immediate: true }
|
||
)
|
||
|
||
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 = ''
|
||
}
|
||
|
||
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'
|
||
}
|
||
|
||
async function loadRuns(options = {}) {
|
||
if (runLoading.value && !options.force) {
|
||
return
|
||
}
|
||
|
||
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,
|
||
fields: [],
|
||
promptSections: [],
|
||
outputRules: [],
|
||
tests: [],
|
||
triggers: [],
|
||
tools: [],
|
||
history: [],
|
||
markdownContent: '',
|
||
displayVersion: asset.version,
|
||
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
|
||
}
|
||
versionSwitchTarget.value = version
|
||
}
|
||
|
||
function cancelVersionSwitch() {
|
||
versionSwitchTarget.value = null
|
||
}
|
||
|
||
function confirmVersionSwitch() {
|
||
if (!selectedSkill.value || !versionSwitchTarget.value) {
|
||
return
|
||
}
|
||
|
||
selectedSkill.value.displayVersion = versionSwitchTarget.value.version
|
||
if (typeof versionSwitchTarget.value.content === 'string') {
|
||
selectedSkill.value.markdownContent = versionSwitchTarget.value.content
|
||
}
|
||
versionSwitchTarget.value = null
|
||
}
|
||
|
||
async function saveRuleMarkdown() {
|
||
if (!selectedSkill.value || !selectedSkillIsRule.value || !canEditMarkdown.value || detailBusy.value) {
|
||
return
|
||
}
|
||
|
||
if (!normalizeText(selectedSkill.value.markdownContent)) {
|
||
toast('规则 Markdown 内容不能为空。')
|
||
return
|
||
}
|
||
|
||
const nextVersion = incrementVersion(selectedSkill.value.currentVersion)
|
||
actionState.value = 'save-markdown'
|
||
|
||
try {
|
||
await createAgentAssetVersion(
|
||
selectedSkill.value.id,
|
||
{
|
||
version: nextVersion,
|
||
content: selectedSkill.value.markdownContent,
|
||
content_type: 'markdown',
|
||
change_note: '通过任务规则中心保存 Markdown 规则内容。',
|
||
created_by: resolveActor()
|
||
},
|
||
{ actor: resolveActor() }
|
||
)
|
||
await refreshCurrentAssets()
|
||
await loadSelectedAssetDetail(selectedSkill.value.id)
|
||
toast(`规则 Markdown 已保存为 ${nextVersion}。`)
|
||
} catch (error) {
|
||
toast(error?.message || '规则 Markdown 保存失败,请稍后重试。')
|
||
} finally {
|
||
actionState.value = ''
|
||
}
|
||
}
|
||
|
||
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 = ''
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
document.addEventListener('click', handleDocumentClick)
|
||
loadAssets({ force: true }).catch(() => {})
|
||
loadRuns().catch(() => {})
|
||
})
|
||
|
||
onBeforeUnmount(() => {
|
||
document.removeEventListener('click', handleDocumentClick)
|
||
})
|
||
|
||
return {
|
||
tabs,
|
||
activeType,
|
||
activeTabLabel,
|
||
selectedSkill,
|
||
versionSwitchTarget,
|
||
keyword,
|
||
createButtonLabel,
|
||
hintText,
|
||
searchPlaceholder,
|
||
tableColumns,
|
||
showMetricColumn,
|
||
visibleSkills,
|
||
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,
|
||
toggleFilterPopover,
|
||
selectFilter,
|
||
closeFilterPopover,
|
||
openVersionSwitch,
|
||
cancelVersionSwitch,
|
||
confirmVersionSwitch,
|
||
saveRuleMarkdown,
|
||
reviewSelectedRule,
|
||
activateSelectedRule,
|
||
loadAssets
|
||
}
|
||
}
|
||
}
|