feat: 新增数字员工管理页面与工作台首页重构
后端优化 agent 资产种子初始化和常量配置,前端新增数字员工 视图和调度对话框组件,重构个人工作台首页布局和洞察面板, 完善审计页面数字员工详情和运行时模型,优化侧边栏导航和图 标配置,新增工作台摘要和工作台数据模块,补充单元测试。
This commit is contained in:
@@ -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() }
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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: '数字员工技能由资产状态和调度配置共同决定是否启动。'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) ||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
122
web/src/views/scripts/digitalEmployeeScheduleModel.js
Normal file
122
web/src/views/scripts/digitalEmployeeScheduleModel.js
Normal file
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user