+
+
+ {{ doc.state }}
+
+ |
+
{{ doc.ingestTime || '—' }} |
+
{{ doc.owner }} |
+
+
|
- |
+ |
|
- |
+ |
当前文件夹暂无文件
|
diff --git a/web/src/views/scripts/AuditView.js b/web/src/views/scripts/AuditView.js
index 1d90817..dea481a 100644
--- a/web/src/views/scripts/AuditView.js
+++ b/web/src/views/scripts/AuditView.js
@@ -154,7 +154,6 @@ export default {
const assetBuckets = ref({
financialRules: [],
riskRules: [],
- skills: [],
mcp: []
})
@@ -173,7 +172,7 @@ export default {
const showVersionColumn = computed(() => activeMeta.value.showVersionColumn !== false)
const showStatusColumn = computed(() => activeMeta.value.showStatusColumn !== false)
const showOnlineColumn = computed(() => false)
- const showEnabledColumn = computed(() => false)
+ const showEnabledColumn = computed(() => activeMeta.value.showEnabledColumn === true)
const selectedSkillIsRule = computed(() => selectedSkill.value?.type === 'rules')
const selectedSkillUsesSpreadsheet = computed(
() => selectedSkillIsRule.value && Boolean(selectedSkill.value?.usesSpreadsheetRule)
@@ -241,7 +240,7 @@ export default {
() => selectedSkillUsesJsonRisk.value && canManageSelected.value
)
const riskRuleCreateBusy = computed(() => actionState.value === 'generate-risk-rule')
- const canEditMarkdown = computed(() => canEditSelected.value && selectedSkillIsRule.value)
+ const canEditMarkdown = computed(() => selectedSkillIsRule.value && canEditSelected.value)
const isDisplayingWorkingVersion = computed(
() => selectedSkill.value?.displayVersion === selectedSkill.value?.workingVersion
)
@@ -1401,7 +1400,7 @@ export default {
version: nextVersion,
content: buildMarkdownVersionContent(selectedSkill.value.markdownContent, runtimeRule),
content_type: 'markdown',
- change_note: '通过任务规则中心保存 Markdown 规则内容,并同步运行时 JSON。',
+ change_note: '通过规则中心保存 Markdown 规则内容,并同步运行时 JSON。',
created_by: resolveActor()
},
{ actor: resolveActor() }
@@ -1449,7 +1448,7 @@ export default {
version: nextVersion,
content: buildMarkdownVersionContent(selectedSkill.value.markdownContent, runtimeRule),
content_type: 'markdown',
- change_note: '通过任务规则中心保存运行时 JSON 配置。',
+ change_note: '通过规则中心保存运行时 JSON 配置。',
created_by: resolveActor()
},
{ actor: resolveActor() }
diff --git a/web/src/views/scripts/auditViewDigitalEmployeeModel.js b/web/src/views/scripts/auditViewDigitalEmployeeModel.js
index 3249fa5..c60cf14 100644
--- a/web/src/views/scripts/auditViewDigitalEmployeeModel.js
+++ b/web/src/views/scripts/auditViewDigitalEmployeeModel.js
@@ -1,4 +1,5 @@
const DIGITAL_EMPLOYEE_AGENT = 'hermes'
+export const DIGITAL_EMPLOYEE_SKILL_CATEGORY_OPTIONS = ['积累', '升级', '整理', '评估']
const TASK_TYPE_LABELS = {
daily_risk_scan: '每日风险巡检',
@@ -7,11 +8,25 @@ const TASK_TYPE_LABELS = {
weekly_expense_report: '周度费用洞察',
rule_review_digest: '规则待审摘要',
knowledge_index_sync: '知识库归集',
+ llm_wiki_rule_formation: '知识库归集',
x_financial_callback: '任务回调上报'
}
+const TASK_TYPE_SKILL_CATEGORIES = {
+ daily_risk_scan: '评估',
+ global_risk_scan: '评估',
+ weekly_ar_summary: '整理',
+ weekly_expense_report: '整理',
+ rule_review_digest: '升级',
+ knowledge_index_sync: '积累',
+ llm_wiki_rule_formation: '积累',
+ x_financial_callback: '升级'
+}
+
const CONTENT_LABELS = {
- task_type: '技能类型',
+ task_type: '任务类型',
+ skill_category: '技能类型',
+ skill_category_options: '技能类型范围',
schedule: '执行计划',
cron: '调度表达式',
folder: '归集范围',
@@ -45,6 +60,14 @@ export function sanitizeDigitalEmployeeText(value, fallback = '') {
return text || fallback
}
+export function sanitizeDigitalEmployeeSource(value, fallback = '') {
+ const text = normalizeDigitalEmployeeText(value)
+ .replace(/hermes/gi, '数字员工')
+ .replace(/赫尔墨斯/g, '数字员工')
+ .trim()
+ return text || fallback
+}
+
export function sanitizeDigitalEmployeeName(value, fallback = '数字员工技能') {
const text = sanitizeDigitalEmployeeText(value, fallback)
.replace(/^数字员工[\s·::-]*/i, '')
@@ -80,6 +103,22 @@ export function resolveDigitalEmployeeTaskType(source = {}, content = {}) {
return raw.replace(/[-.]/g, '_')
}
+export function resolveDigitalEmployeeSkillCategory(source = {}, content = {}) {
+ const config = source.config_json || source.configJson || {}
+ const taskType = resolveDigitalEmployeeTaskType(source, content)
+ const explicitCategory =
+ normalizeDigitalEmployeeText(config.skill_category) ||
+ normalizeDigitalEmployeeText(config.skillCategory) ||
+ normalizeDigitalEmployeeText(content.skill_category) ||
+ normalizeDigitalEmployeeText(content.skillCategory)
+
+ if (DIGITAL_EMPLOYEE_SKILL_CATEGORY_OPTIONS.includes(explicitCategory)) {
+ return explicitCategory
+ }
+
+ return TASK_TYPE_SKILL_CATEGORIES[taskType] || '整理'
+}
+
export function isDigitalEmployeeAsset(source = {}) {
const config = source.config_json || source.configJson || {}
const haystack = [
@@ -145,10 +184,10 @@ export function formatDigitalEmployeeCron(value) {
export function resolveDigitalEmployeeSchedule(source = {}, content = {}) {
const config = source.config_json || source.configJson || {}
const raw =
- normalizeDigitalEmployeeText(content.schedule) ||
normalizeDigitalEmployeeText(config.cron) ||
normalizeDigitalEmployeeText(config.schedule) ||
- normalizeDigitalEmployeeText(config.cron_expression)
+ normalizeDigitalEmployeeText(config.cron_expression) ||
+ normalizeDigitalEmployeeText(content.schedule)
return {
value: raw,
label: formatDigitalEmployeeCron(raw)
@@ -205,9 +244,85 @@ export function buildDigitalEmployeeContentPreview(content = {}) {
return sanitizeDigitalEmployeeText(JSON.stringify(visiblePayload, null, 2))
}
+function resolveDigitalEmployeeMarkdownFromContent(content = {}, config = {}) {
+ const candidates = [
+ content.skill_markdown,
+ content.skills_markdown,
+ content.source_markdown,
+ content.markdown,
+ content.skill_source,
+ config.skill_markdown,
+ config.skills_markdown,
+ config.source_markdown,
+ config.skill_source
+ ]
+ return candidates.find((item) => normalizeDigitalEmployeeText(item)) || ''
+}
+
+function buildDefaultDigitalEmployeeSource(source = {}, listMeta = {}, schedule = {}) {
+ const name = listMeta.name || '数字员工技能'
+ const description =
+ listMeta.summary ||
+ sanitizeDigitalEmployeeText(source.description, '该技能用于后台自动执行指定任务。')
+
+ return [
+ '---',
+ `name: ${listMeta.code || 'digital.skill'}`,
+ `description: ${description}`,
+ '---',
+ '',
+ `# ${name}`,
+ '',
+ '## 功能说明',
+ '',
+ description,
+ '',
+ '## 执行方式',
+ '',
+ `- 技能类型:${listMeta.skillCategory || '整理'}`,
+ `- 可选类型:${DIGITAL_EMPLOYEE_SKILL_CATEGORY_OPTIONS.join('、')}`,
+ `- 执行计划:${schedule.label || '手动触发'}`,
+ `- 触发方式:${listMeta.executionMode || '手动触发'}`,
+ '',
+ '## 操作要求',
+ '',
+ '- 按任务参数读取业务数据。',
+ '- 运行完成后写回业务结果或运行日志。'
+ ].join('\n')
+}
+
+export function buildDigitalEmployeeSourceMarkdown(source = {}, content = {}, listMeta = {}) {
+ const config = source.config_json || source.configJson || {}
+ if (
+ normalizeDigitalEmployeeText(source.current_version_content_type) === 'markdown' &&
+ typeof source.current_version_content === 'string'
+ ) {
+ return sanitizeDigitalEmployeeSource(source.current_version_content)
+ }
+
+ const schedule = resolveDigitalEmployeeSchedule(source, content)
+ const sourceMarkdown = resolveDigitalEmployeeMarkdownFromContent(content, config)
+ return sanitizeDigitalEmployeeSource(
+ sourceMarkdown,
+ buildDefaultDigitalEmployeeSource(source, listMeta, schedule)
+ )
+}
+
+function buildDigitalEmployeeBasicRows(source = {}, listMeta = {}, schedule = {}) {
+ return [
+ { label: '技能编号', value: listMeta.code },
+ { label: '技能类型', value: listMeta.skillCategory },
+ { label: '维护人', value: listMeta.owner },
+ { label: '执行计划', value: schedule.label },
+ { label: '当前版本', value: source.working_version || source.current_version || '-' },
+ { label: '最近更新', value: source.updated_at || '-' }
+ ]
+}
+
export function buildDigitalEmployeeListMeta(source = {}) {
const content = parseDigitalEmployeeContent(source.current_version_content)
const taskType = resolveDigitalEmployeeTaskType(source, content)
+ const skillCategory = resolveDigitalEmployeeSkillCategory(source, content)
const schedule = resolveDigitalEmployeeSchedule(source, content)
const enabled = resolveDigitalEmployeeEnabled(source)
const fallbackName = TASK_TYPE_LABELS[taskType] || '数字员工技能'
@@ -217,6 +332,8 @@ export function buildDigitalEmployeeListMeta(source = {}) {
code: resolveDigitalEmployeeDisplayCode(source, content),
summary: sanitizeDigitalEmployeeText(source.description, '面向后台自动执行的数字员工技能。'),
category: '数字员工',
+ skillCategory,
+ skillCategoryOptions: DIGITAL_EMPLOYEE_SKILL_CATEGORY_OPTIONS,
owner: sanitizeDigitalEmployeeText(source.owner, '平台运营'),
reviewer: sanitizeDigitalEmployeeText(source.reviewer, '系统'),
scope: schedule.label,
@@ -237,6 +354,7 @@ export function buildDigitalEmployeeDetailMeta(source = {}) {
})
const schedule = resolveDigitalEmployeeSchedule(source, content)
const contentRows = buildDigitalEmployeeContentRows(content)
+ const sourceMarkdown = buildDigitalEmployeeSourceMarkdown(source, content, listMeta)
return {
...listMeta,
@@ -245,13 +363,16 @@ export function buildDigitalEmployeeDetailMeta(source = {}) {
source.description,
'该技能由后台数字员工按计划执行,并把结果沉淀到对应业务资产或运行日志中。'
),
+ sourceMarkdown,
+ basicRows: buildDigitalEmployeeBasicRows(source, listMeta, schedule),
contentRows,
contentPreview: buildDigitalEmployeeContentPreview(content),
scheduleRows: [
{ label: '执行计划', value: schedule.label },
{ label: '调度表达式', value: schedule.value || '手动触发' },
{ label: '启动状态', value: listMeta.enabledLabel, tone: listMeta.enabledTone },
- { label: '执行方式', value: listMeta.executionMode }
+ { label: '执行方式', value: listMeta.executionMode },
+ { label: '技能类型', value: listMeta.skillCategory }
],
overviewRows: [
{ label: '能力编号', value: listMeta.code },
diff --git a/web/src/views/scripts/auditViewMetadata.js b/web/src/views/scripts/auditViewMetadata.js
index d611114..170d0d4 100644
--- a/web/src/views/scripts/auditViewMetadata.js
+++ b/web/src/views/scripts/auditViewMetadata.js
@@ -22,24 +22,6 @@ export const TYPE_META = {
typeLabel: '规则',
tableColumns: RULE_TABLE_COLUMNS
},
- skills: {
- assetType: 'skill',
- label: '技能',
- typeLabel: '技能',
- createButtonLabel: '技能已接入',
- hintText: '技能页签已接到真实资产 API,可查看输入、输出、依赖和场景信息。',
- searchPlaceholder: '搜索技能名称、编码或负责人',
- showMetricColumn: false,
- tableColumns: {
- name: '技能名称',
- category: '业务域',
- owner: '负责人',
- scope: '适用场景',
- runtime: '输入摘要',
- version: '当前版本',
- metric: ''
- }
- },
mcp: {
assetType: 'mcp',
label: 'MCP',
@@ -87,41 +69,10 @@ export const TAB_META = {
showStatusColumn: true,
badgeTone: 'rose'
},
- skills: {
- ...TYPE_META.skills,
- typeKey: 'skills',
- badgeTone: 'blue'
- },
mcp: {
...TYPE_META.mcp,
typeKey: 'mcp',
badgeTone: 'amber'
- },
- digitalWorkers: {
- assetType: 'task',
- typeKey: 'digitalWorkers',
- label: '数字员工',
- typeLabel: '数字员工',
- createButtonLabel: '数字员工已接入',
- hintText: '归集后台自动执行的数字员工技能,可查看技能内容、执行计划、启动状态和最近版本。',
- searchPlaceholder: '搜索数字员工技能、编号、执行计划或维护人',
- showMetricColumn: true,
- showRuntimeColumn: true,
- showVersionColumn: true,
- showStatusColumn: true,
- showEnabledColumn: true,
- tableColumns: {
- name: '技能名称',
- category: '归集标签',
- owner: '维护归口',
- scope: '执行计划',
- runtime: '触发方式',
- version: '当前版本',
- status: '资产状态',
- metric: '运行方式',
- updatedAt: '最近更新'
- },
- badgeTone: 'violet'
}
}
@@ -199,24 +150,6 @@ export const DETAIL_TITLES = {
publishTitle: '上线控制',
publishDesc: '正式上线会调用后端激活接口,审核未通过时会被拦截。'
},
- skills: {
- configTitle: '技能配置',
- configDesc: '展示技能编码、输入摘要、版本和业务域。',
- detailTitle: '技能结构',
- detailDesc: '按输入、输出和依赖组织技能定义。',
- outputTitle: '输出契约',
- outputDesc: '技能详情重点展示输入参数、输出参数和依赖能力。',
- ruleListTitle: '输出要求',
- checkListTitle: '当前快照',
- triggerTitle: '适用场景',
- triggerDesc: '当前技能注册到的场景标签',
- toolTitle: '依赖能力',
- toolDesc: '技能当前依赖的数据库或其他能力',
- historyTitle: '版本历史',
- historyDesc: '最近版本记录',
- publishTitle: '发布状态',
- publishDesc: '技能当前状态由资产中心统一管理。'
- },
mcp: {
configTitle: 'MCP 连接配置',
configDesc: '展示服务地址、超时和调用方式。',
@@ -234,24 +167,6 @@ export const DETAIL_TITLES = {
historyDesc: '最近版本记录',
publishTitle: '服务状态',
publishDesc: 'MCP 资产已接入规则中心,但真实外部调用仍以后续链路集成为准。'
- },
- digitalWorkers: {
- configTitle: '技能档案',
- configDesc: '展示数字员工技能的编号、归口、执行计划和启停状态。',
- detailTitle: '技能内容',
- detailDesc: '展示当前版本记录的任务类型、调度范围和执行参数。',
- outputTitle: '执行安排',
- outputDesc: '展示什么时候执行、是否启动,以及当前运行方式。',
- ruleListTitle: '技能参数',
- checkListTitle: '启动状态',
- triggerTitle: '执行计划',
- triggerDesc: '当前技能的计划执行时间或触发方式。',
- toolTitle: '运行归口',
- toolDesc: '数字员工技能由后台调度执行,运行结果进入对应日志或业务资产。',
- historyTitle: '版本记录',
- historyDesc: '最近的技能配置快照。',
- publishTitle: '启动状态',
- publishDesc: '数字员工技能由资产状态和调度配置共同决定是否启动。'
}
}
diff --git a/web/src/views/scripts/auditViewModel.js b/web/src/views/scripts/auditViewModel.js
index 17463c8..85a6166 100644
--- a/web/src/views/scripts/auditViewModel.js
+++ b/web/src/views/scripts/auditViewModel.js
@@ -34,13 +34,6 @@ import {
resolveRiskRuleSeverity,
resolveRiskRuleSeverityLabel
} from './auditViewRiskRuleModel.js'
-import {
- buildDigitalEmployeeContentRows,
- buildDigitalEmployeeDetailMeta,
- buildDigitalEmployeeListMeta,
- isDigitalEmployeeAsset,
- sanitizeDigitalEmployeeText
-} from './auditViewDigitalEmployeeModel.js'
const EXPENSE_TYPE_SCENARIO_LABELS = {
travel: '差旅费',
@@ -342,9 +335,6 @@ export function resolveTabId(source, typeKey) {
if (typeKey === 'rules') {
return resolveRuleTabId(source)
}
- if (typeKey === 'digitalWorkers') {
- return isDigitalEmployeeAsset(source) ? 'digitalWorkers' : ''
- }
return typeKey
}
@@ -899,15 +889,9 @@ export function resolveTypeKey(assetType) {
if (assetType === 'rule') {
return 'rules'
}
- if (assetType === 'skill') {
- return 'skills'
- }
if (assetType === 'mcp') {
return 'mcp'
}
- if (assetType === 'task') {
- return 'digitalWorkers'
- }
return ''
}
@@ -965,15 +949,9 @@ export 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) || '未配置地址'
}
- if (typeKey === 'digitalWorkers') {
- return buildDigitalEmployeeListMeta(asset).executionMode
- }
return ''
}
@@ -981,15 +959,9 @@ export function buildRowMetric(asset, typeKey) {
if (typeKey === 'rules') {
return normalizeText(asset.modified_by) || '未记录'
}
- if (typeKey === 'skills') {
- return '进入详情查看输出'
- }
if (typeKey === 'mcp') {
return asset.config_json?.timeout_ms ? `${asset.config_json.timeout_ms} ms` : '未配置超时'
}
- if (typeKey === 'digitalWorkers') {
- return buildDigitalEmployeeListMeta(asset).executionMode
- }
return ''
}
@@ -1061,19 +1033,16 @@ export function buildListItem(asset) {
? resolveRiskRuleScoreLabel(asset.config_json, asset.config_json) || resolveRiskRuleSeverityLabel(asset.config_json)
: resolveRiskRuleSeverityLabel(asset.config_json)
: ''
- const digitalMeta = typeKey === 'digitalWorkers' ? buildDigitalEmployeeListMeta(asset) : null
- const displayName = digitalMeta?.name || asset.name
- const displayCode = digitalMeta?.code || asset.code
- const displaySummary = digitalMeta?.summary || listSubtitle
- const displayOwner = digitalMeta?.owner || (isRiskRule ? creator : asset.owner)
- const displayReviewer = digitalMeta?.reviewer || reviewer
- const displayCategory = digitalMeta?.category || resolveDomainLabel(asset.domain)
- const displayScope =
- digitalMeta?.scope ||
- (typeKey === 'rules' ? ruleScenarioCategory || '閫氱敤' : formatScenarioList(asset.scenario_json))
- const displayEnabledValue = digitalMeta ? digitalMeta.enabled : isEnabledValue
- const displayEnabledLabel = digitalMeta?.enabledLabel || (isEnabledValue ? '鏄? : '鍚?)
- const displayEnabledTone = digitalMeta?.enabledTone || (isEnabledValue ? 'success' : 'disabled')
+ const displayName = asset.name
+ const displayCode = asset.code
+ const displaySummary = listSubtitle
+ const displayOwner = isRiskRule ? creator : asset.owner
+ const displayReviewer = reviewer
+ const displayCategory = resolveDomainLabel(asset.domain)
+ const displayScope = typeKey === 'rules' ? ruleScenarioCategory || '通用' : formatScenarioList(asset.scenario_json)
+ const displayEnabledValue = isEnabledValue
+ const displayEnabledLabel = isEnabledValue ? '是' : '否'
+ const displayEnabledTone = isEnabledValue ? 'success' : 'disabled'
return {
id: asset.id,
@@ -1093,7 +1062,6 @@ export function buildListItem(asset) {
category: displayCategory,
owner: displayOwner,
reviewer: displayReviewer,
- scope: typeKey === 'rules' ? ruleScenarioCategory || '通用' : formatScenarioList(asset.scenario_json),
scope: displayScope,
riskCategory: ruleScenarioCategory,
scenarioList: ruleScenarioList,
@@ -1117,9 +1085,6 @@ export function buildListItem(asset) {
isOnlineValue,
isOnlineLabel: onlineMeta.label,
isOnlineTone: onlineMeta.tone,
- isEnabledValue,
- isEnabledLabel: isEnabledValue ? '是' : '否',
- isEnabledTone: isEnabledValue ? 'success' : 'disabled',
isEnabledValue: displayEnabledValue,
isEnabledLabel: displayEnabledLabel,
isEnabledTone: displayEnabledTone,
@@ -1163,22 +1128,6 @@ export function buildRuleFields(detail) {
]
}
-export 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('、') : '未配置'
- }
- ]
-}
-
export function buildMcpFields(detail, latestCall) {
const content = detail.current_version_content || {}
return [
@@ -1196,9 +1145,6 @@ export function buildFields(detail, typeKey, latestCall) {
if (typeKey === 'rules') {
return buildRuleFields(detail)
}
- if (typeKey === 'skills') {
- return buildSkillFields(detail)
- }
if (typeKey === 'mcp') {
return buildMcpFields(detail, latestCall)
}
@@ -1208,29 +1154,6 @@ export function buildFields(detail, typeKey, latestCall) {
export function buildPromptSections(detail, typeKey) {
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 [
{
@@ -1274,14 +1197,6 @@ export function buildOutputRules(detail, typeKey) {
]
}
- 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) || '未配置'}`,
@@ -1312,24 +1227,6 @@ export function buildTests(detail, typeKey, latestCall) {
]
}
- 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 [
{
@@ -1356,15 +1253,6 @@ export function buildTests(detail, typeKey, latestCall) {
export function buildTools(detail, typeKey, 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 [
{
@@ -1454,40 +1342,32 @@ export function buildDetailViewModel(detail, runs) {
const initialRiskRuleScore = resolveRiskRuleScore(configJson, configJson)
const initialRiskRuleScoreLevel = resolveRiskRuleScoreLevel(configJson, configJson)
const initialRiskRuleSeverity = initialRiskRuleScoreLevel || resolveRiskRuleSeverity(configJson)
- const digitalMeta = typeKey === 'digitalWorkers'
- ? buildDigitalEmployeeDetailMeta({
- ...detail,
- updated_at: formatDateTime(detail.updated_at)
- })
- : null
- const detailName = digitalMeta?.name || detail.name
- const detailCode = digitalMeta?.code || detail.code
- const detailSummary = digitalMeta?.description ||
- (usesJsonRiskRule ? buildRiskListSubtitle(detail.description) : detail.description)
- const detailOwner = digitalMeta?.owner || detail.owner
- const detailReviewer = digitalMeta?.reviewer || detail.reviewer || detail.latest_review?.reviewer || '寰呭垎閰?
- const detailCategory = digitalMeta?.category || resolveDomainLabel(detail.domain)
- const detailScope =
- digitalMeta?.scope ||
- (typeKey === 'rules' ? ruleScenarioCategory || '閫氱敤' : formatScenarioList(detail.scenario_json))
- const detailEnabledValue = digitalMeta ? digitalMeta.enabled : isEnabledValue
- const detailEnabledLabel = digitalMeta?.enabledLabel || (isEnabledValue ? '鏄? : '鍚?)
- const detailEnabledTone = digitalMeta?.enabledTone || (isEnabledValue ? 'success' : 'disabled')
+ const detailName = detail.name
+ const detailCode = detail.code
+ const detailSummary = usesJsonRiskRule ? buildRiskListSubtitle(detail.description) : detail.description
+ const detailOwner = detail.owner
+ const detailReviewer = detail.reviewer || detail.latest_review?.reviewer || '待分配'
+ const detailCategory = resolveDomainLabel(detail.domain)
+ const detailScope = typeKey === 'rules' ? ruleScenarioCategory || '通用' : formatScenarioList(detail.scenario_json)
+ const detailEnabledValue = isEnabledValue
+ const detailEnabledLabel = isEnabledValue ? '是' : '否'
+ const detailEnabledTone = isEnabledValue ? 'success' : 'disabled'
return {
id: detail.id,
tabId,
type: typeKey,
typeLabel: tabMeta.typeLabel,
- short: makeShort(detail.name),
- name: detail.name,
- code: detail.code,
- summary: usesJsonRiskRule ? buildRiskListSubtitle(detail.description) : detail.description,
- listSubtitle: usesJsonRiskRule ? buildRiskListSubtitle(detail.description) : normalizeText(detail.description),
- owner: detail.owner,
- reviewer: detail.reviewer || detail.latest_review?.reviewer || '待分配',
- category: resolveDomainLabel(detail.domain),
- scope: typeKey === 'rules' ? ruleScenarioCategory || '通用' : formatScenarioList(detail.scenario_json),
+ short: makeShort(detailName),
+ name: detailName,
+ code: detailCode,
+ rawCode: detail.code,
+ summary: detailSummary,
+ listSubtitle: normalizeText(detailSummary),
+ owner: detailOwner,
+ reviewer: detailReviewer,
+ category: detailCategory,
+ scope: detailScope,
businessStageValue: businessStage.value,
businessStageLabel: businessStage.label,
version: detail.working_version || detail.current_version || '-',
@@ -1524,9 +1404,9 @@ export function buildDetailViewModel(detail, runs) {
isOnlineValue: onlineMeta.online,
isOnlineLabel: onlineMeta.label,
isOnlineTone: onlineMeta.tone,
- isEnabledValue,
- isEnabledLabel: isEnabledValue ? '是' : '否',
- isEnabledTone: isEnabledValue ? 'success' : 'disabled',
+ isEnabledValue: detailEnabledValue,
+ isEnabledLabel: detailEnabledLabel,
+ isEnabledTone: detailEnabledTone,
publisher:
detail.status === 'active'
? normalizeText(detail.published_by) ||
diff --git a/web/src/views/scripts/auditViewRuntimeModel.js b/web/src/views/scripts/auditViewRuntimeModel.js
index cf7d77f..b199764 100644
--- a/web/src/views/scripts/auditViewRuntimeModel.js
+++ b/web/src/views/scripts/auditViewRuntimeModel.js
@@ -16,12 +16,12 @@ export function incrementVersion(version) {
export function buildReviewNote(status) {
if (status === 'approved') {
- return '通过任务规则中心审核。'
+ return '通过规则中心审核。'
}
if (status === 'rejected') {
- return '在任务规则中心驳回当前版本。'
+ return '在规则中心驳回当前版本。'
}
- return '提交任务规则中心待审核。'
+ return '提交规则中心待审核。'
}
export function buildRuleConfigPayload(asset, runtimeRule) {
diff --git a/web/src/views/scripts/digitalEmployeeScheduleModel.js b/web/src/views/scripts/digitalEmployeeScheduleModel.js
new file mode 100644
index 0000000..a95b199
--- /dev/null
+++ b/web/src/views/scripts/digitalEmployeeScheduleModel.js
@@ -0,0 +1,122 @@
+const DEFAULT_SCHEDULE_TIME = '00:00'
+const CRON_TOKEN_PATTERN = /^[\d*/,?\-]+$/
+
+export const DIGITAL_EMPLOYEE_SCHEDULE_MODES = [
+ { value: 'manual', label: '手动触发' },
+ { value: 'daily', label: '每天执行' },
+ { value: 'weekly', label: '每周执行' },
+ { value: 'custom', label: 'Cron 表达式' }
+]
+
+export const DIGITAL_EMPLOYEE_WEEKDAY_OPTIONS = [
+ { value: '1', label: '周一' },
+ { value: '2', label: '周二' },
+ { value: '3', label: '周三' },
+ { value: '4', label: '周四' },
+ { value: '5', label: '周五' },
+ { value: '6', label: '周六' },
+ { value: '0', label: '周日' }
+]
+
+function normalizeText(value) {
+ return String(value ?? '').trim()
+}
+
+function normalizeTime(value) {
+ const raw = normalizeText(value)
+ return /^\d{2}:\d{2}$/.test(raw) ? raw : DEFAULT_SCHEDULE_TIME
+}
+
+function toTime(hour, minute) {
+ const hourNumber = Number(hour)
+ const minuteNumber = Number(minute)
+ if (!Number.isInteger(hourNumber) || hourNumber < 0 || hourNumber > 23) {
+ return DEFAULT_SCHEDULE_TIME
+ }
+ if (!Number.isInteger(minuteNumber) || minuteNumber < 0 || minuteNumber > 59) {
+ return DEFAULT_SCHEDULE_TIME
+ }
+ return `${String(hourNumber).padStart(2, '0')}:${String(minuteNumber).padStart(2, '0')}`
+}
+
+export function createDigitalEmployeeScheduleForm(cron = '') {
+ const normalizedCron = normalizeText(cron)
+ if (!normalizedCron) {
+ return { mode: 'manual', time: DEFAULT_SCHEDULE_TIME, weekday: '1', cron: '' }
+ }
+
+ const parts = normalizedCron.split(/\s+/)
+ if (parts.length !== 5) {
+ return { mode: 'custom', time: DEFAULT_SCHEDULE_TIME, weekday: '1', cron: normalizedCron }
+ }
+
+ const [minute, hour, dayOfMonth, month, dayOfWeek] = parts
+ const time = toTime(hour, minute)
+
+ if (dayOfMonth === '*' && month === '*' && dayOfWeek === '*') {
+ return { mode: 'daily', time, weekday: '1', cron: normalizedCron }
+ }
+
+ if (dayOfMonth === '*' && month === '*' && dayOfWeek !== '*') {
+ return { mode: 'weekly', time, weekday: dayOfWeek || '1', cron: normalizedCron }
+ }
+
+ return { mode: 'custom', time, weekday: '1', cron: normalizedCron }
+}
+
+export function resolveDigitalEmployeeScheduleValue(employee = {}) {
+ const config = employee.configJson || {}
+ return (
+ normalizeText(employee.digitalEmployee?.scheduleValue) ||
+ normalizeText(config.cron) ||
+ normalizeText(config.schedule) ||
+ normalizeText(config.cron_expression)
+ )
+}
+
+export function buildDigitalEmployeeScheduleCron(form = {}) {
+ const mode = normalizeText(form.mode) || 'manual'
+ if (mode === 'manual') {
+ return ''
+ }
+
+ if (mode === 'custom') {
+ const cron = normalizeText(form.cron)
+ const parts = cron.split(/\s+/).filter(Boolean)
+ if (parts.length !== 5 || !parts.every((part) => CRON_TOKEN_PATTERN.test(part))) {
+ throw new Error('请输入 5 段 Cron 表达式。')
+ }
+ return parts.join(' ')
+ }
+
+ const [hour, minute] = normalizeTime(form.time).split(':')
+ if (mode === 'daily') {
+ return `${Number(minute)} ${Number(hour)} * * *`
+ }
+
+ if (mode === 'weekly') {
+ const weekday = normalizeText(form.weekday) || '1'
+ return `${Number(minute)} ${Number(hour)} * * ${weekday}`
+ }
+
+ throw new Error('请选择有效的执行方式。')
+}
+
+export function buildDigitalEmployeeScheduleConfig(config = {}, cron = '') {
+ const nextConfig = config && typeof config === 'object' && !Array.isArray(config)
+ ? { ...config }
+ : {}
+ const normalizedCron = normalizeText(cron)
+
+ if (normalizedCron) {
+ nextConfig.cron = normalizedCron
+ nextConfig.schedule = normalizedCron
+ nextConfig.cron_expression = normalizedCron
+ return nextConfig
+ }
+
+ delete nextConfig.cron
+ delete nextConfig.schedule
+ delete nextConfig.cron_expression
+ return nextConfig
+}
diff --git a/web/tests/policies-view-table.test.mjs b/web/tests/policies-view-table.test.mjs
new file mode 100644
index 0000000..1872e8a
--- /dev/null
+++ b/web/tests/policies-view-table.test.mjs
@@ -0,0 +1,33 @@
+import assert from 'node:assert/strict'
+import { readFileSync } from 'node:fs'
+import { fileURLToPath } from 'node:url'
+
+const policiesViewPath = fileURLToPath(new URL('../src/views/PoliciesView.vue', import.meta.url))
+const policiesView = readFileSync(policiesViewPath, 'utf8')
+
+function testIngestTimeUsesDedicatedColumn() {
+ assert.match(
+ policiesView,
+ /
状态<\/th>\s* | 归纳时间<\/th>\s* | 上传人<\/th>/,
+ '知识库文件表需要在状态和上传人之间单独展示归纳时间列'
+ )
+ assert.match(
+ policiesView,
+ / | \s* \s*]*>\{\{ doc\.state \}\}<\/span>\s*<\/div>\s*<\/td>\s* | \{\{ doc\.ingestTime \|\| '—' \}\}<\/td>/,
+ '状态列只展示状态标签,归纳时间需要放到独立单元格'
+ )
+ assert.doesNotMatch(
+ policiesView,
+ /class="state-time"|归纳时间:\{\{ doc\.ingestTime \}\}/,
+ '状态单元格下面不应再显示归纳时间'
+ )
+ assert.doesNotMatch(policiesView, /colspan="7"/)
+ assert.match(policiesView, /colspan="8"/)
+}
+
+function run() {
+ testIngestTimeUsesDedicatedColumn()
+ console.log('policies view table tests passed')
+}
+
+run()
|