feat: 新增数字员工管理页面与工作台首页重构

后端优化 agent 资产种子初始化和常量配置,前端新增数字员工
视图和调度对话框组件,重构个人工作台首页布局和洞察面板,
完善审计页面数字员工详情和运行时模型,优化侧边栏导航和图
标配置,新增工作台摘要和工作台数据模块,补充单元测试。
This commit is contained in:
caoxiaozhu
2026-05-28 09:30:34 +08:00
parent d4d5d40569
commit 04cd6d0f81
38 changed files with 3413 additions and 1301 deletions

View File

@@ -0,0 +1,105 @@
<template>
<section class="json-risk-editor-shell panel digital-worker-detail-shell">
<header class="json-risk-editor-head asset-detail-topbar list-toolbar">
<div class="json-risk-editor-title asset-detail-topbar-main filter-set">
<div class="json-risk-head-copy">
<div class="json-risk-head-title-row">
<h2>{{ selectedSkill.name }}</h2>
</div>
<p class="json-risk-head-subtitle">
{{ selectedSkill.summary || '后台自动执行的数字员工技能。' }}
</p>
<div class="json-risk-head-meta">
<span>技能编号{{ selectedSkill.code || '-' }}</span>
<span>执行计划{{ digitalEmployee.scheduleLabel || selectedSkill.scope || '-' }}</span>
<span>最近更新{{ selectedSkill.updatedAt || '-' }}</span>
</div>
</div>
</div>
</header>
<div class="json-risk-editor-body">
<section class="json-risk-main-stage">
<article class="detail-card panel json-risk-summary-card">
<div class="card-head">
<div>
<h3>基本信息</h3>
<p>展示技能编号维护人执行计划和当前版本</p>
</div>
</div>
<div class="json-risk-meta-grid">
<div
v-for="row in basicRows"
:key="row.label"
class="json-risk-meta-item"
>
<span class="json-risk-meta-label">{{ row.label }}</span>
<span class="json-risk-meta-value">{{ row.value || '-' }}</span>
</div>
</div>
</article>
<article class="detail-card panel json-risk-description-card">
<div class="card-head">
<div>
<h3>功能说明</h3>
<p>面向管理员查看这个技能解决什么问题</p>
</div>
</div>
<p class="json-risk-description-text">
{{ digitalEmployee.description || selectedSkill.summary || '暂无功能说明。' }}
</p>
</article>
<article class="detail-card panel json-risk-flow-card digital-worker-source-card">
<div class="card-head">
<div>
<h3>Skills Markdown 源文件</h3>
<p>管理员可直接修改当前技能源文件内容</p>
</div>
<button
class="mini-btn primary"
type="button"
:disabled="!canEdit || detailBusy"
@click="emit('save-source')"
>
<i class="mdi mdi-content-save-outline"></i>
<span>{{ actionState === 'save-digital-source' ? '保存中...' : '保存' }}</span>
</button>
</div>
<textarea
v-model="selectedSkill.markdownContent"
class="digital-worker-source-editor"
:readonly="!canEdit || detailBusy"
spellcheck="false"
aria-label="Skills Markdown 源文件"
></textarea>
</article>
</section>
</div>
</section>
</template>
<script setup>
import { computed } from 'vue'
defineOptions({
name: 'AuditDigitalEmployeeDetail'
})
const props = defineProps({
selectedSkill: { type: Object, required: true },
canEdit: { type: Boolean, default: false },
detailBusy: { type: Boolean, default: false },
actionState: { type: String, default: '' }
})
const emit = defineEmits(['save-source'])
const digitalEmployee = computed(() => props.selectedSkill.digitalEmployee || {})
const basicRows = computed(() => digitalEmployee.value.basicRows || [])
</script>
<style scoped src="../../assets/styles/views/audit-view.css"></style>
<style scoped src="../../assets/styles/views/audit-view-part2.css"></style>

View File

@@ -0,0 +1,139 @@
<template>
<Teleport to="body">
<Transition name="digital-schedule-dialog">
<div
v-if="open"
class="digital-schedule-overlay"
@click.self="emit('close')"
>
<section class="digital-schedule-dialog panel" role="dialog" aria-modal="true">
<header class="digital-schedule-head">
<div>
<span class="digital-schedule-kicker">定时设置</span>
<h3>{{ targetName || '数字员工' }}</h3>
</div>
<button
class="digital-schedule-close"
type="button"
:disabled="busy"
aria-label="关闭定时设置"
@click="emit('close')"
>
<i class="mdi mdi-close"></i>
</button>
</header>
<div class="digital-schedule-body">
<div class="digital-schedule-mode-grid">
<button
v-for="mode in scheduleModes"
:key="mode.value"
class="digital-schedule-mode"
:class="{ active: modelValue.mode === mode.value }"
type="button"
:disabled="busy"
@click="updateField('mode', mode.value)"
>
{{ mode.label }}
</button>
</div>
<div class="digital-schedule-fields">
<label v-if="modelValue.mode === 'daily' || modelValue.mode === 'weekly'">
<span>执行时间</span>
<input
:value="modelValue.time"
type="time"
:disabled="busy"
@input="updateField('time', $event.target.value)"
>
</label>
<label v-if="modelValue.mode === 'weekly'">
<span>执行日期</span>
<select
:value="modelValue.weekday"
:disabled="busy"
@change="updateField('weekday', $event.target.value)"
>
<option
v-for="weekday in weekdayOptions"
:key="weekday.value"
:value="weekday.value"
>
{{ weekday.label }}
</option>
</select>
</label>
<label v-if="modelValue.mode === 'custom'" class="digital-schedule-cron-field">
<span>Cron 表达式</span>
<input
:value="modelValue.cron"
type="text"
:disabled="busy"
placeholder="例如0 9 * * *"
@input="updateField('cron', $event.target.value)"
>
</label>
</div>
<p class="digital-schedule-preview">
当前计划{{ previewLabel }}
</p>
<p v-if="errorMessage" class="digital-schedule-error">
{{ errorMessage }}
</p>
</div>
<footer class="digital-schedule-actions">
<button class="minor-action" type="button" :disabled="busy" @click="emit('close')">
取消
</button>
<button class="minor-action success-action" type="button" :disabled="!canSave || busy" @click="emit('save')">
{{ busy ? '保存中...' : '保存设置' }}
</button>
</footer>
</section>
</div>
</Transition>
</Teleport>
</template>
<script setup>
import {
DIGITAL_EMPLOYEE_SCHEDULE_MODES,
DIGITAL_EMPLOYEE_WEEKDAY_OPTIONS
} from '../../views/scripts/digitalEmployeeScheduleModel.js'
defineOptions({
name: 'DigitalEmployeeScheduleDialog'
})
const props = defineProps({
open: { type: Boolean, default: false },
modelValue: {
type: Object,
default: () => ({ mode: 'manual', time: '00:00', weekday: '1', cron: '' })
},
targetName: { type: String, default: '' },
previewLabel: { type: String, default: '手动触发' },
errorMessage: { type: String, default: '' },
busy: { type: Boolean, default: false },
canSave: { type: Boolean, default: false }
})
const emit = defineEmits(['update:modelValue', 'close', 'save'])
const scheduleModes = DIGITAL_EMPLOYEE_SCHEDULE_MODES
const weekdayOptions = DIGITAL_EMPLOYEE_WEEKDAY_OPTIONS
function updateField(field, value) {
emit('update:modelValue', {
...props.modelValue,
[field]: value
})
}
</script>
<style scoped src="../../assets/styles/components/digital-employee-schedule-dialog.css"></style>