feat: 数字员工财务报告体系与定时提醒及看板快照调度
- 新增数字员工财务报告生成、邮件投递与渲染调度器 - 引入员工画像扫描调度与定时提醒任务 - 完善财务看板快照、排行口径与部门人员占比计算 - 优化数字员工工作看板仪表盘与技能目录 - 增强前端总览页图表、工作台摘要与顶部导航栏交互 - 新增差旅申请规划推动提醒与报销创建会话状态管理 - 补充财务报告、看板调度、数字员工工作记录测试覆盖
This commit is contained in:
@@ -16,7 +16,6 @@
|
||||
:current-page="currentPage"
|
||||
:page-size="pageSize"
|
||||
:page-size-options="pageSizeOptions"
|
||||
:pages="pageNumbers"
|
||||
:show-page-size="true"
|
||||
:summary="paginationSummary"
|
||||
:total="visibleSkills.length"
|
||||
@@ -326,14 +325,6 @@ const pagedSkills = computed(() => {
|
||||
const start = (currentPage.value - 1) * pageSize.value
|
||||
return props.visibleSkills.slice(start, start + pageSize.value)
|
||||
})
|
||||
const pageNumbers = computed(() => {
|
||||
const total = totalPages.value
|
||||
if (total <= 7) {
|
||||
return Array.from({ length: total }, (_, index) => index + 1)
|
||||
}
|
||||
const start = Math.max(1, Math.min(currentPage.value - 3, total - 6))
|
||||
return Array.from({ length: 7 }, (_, index) => start + index)
|
||||
})
|
||||
const paginationSummary = computed(() =>
|
||||
`共 ${props.visibleSkills.length} 条,每页 ${pageSize.value} 条,当前第 ${currentPage.value} / ${totalPages.value} 页`
|
||||
)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
:current-page="currentPage"
|
||||
:page-size="pageSize"
|
||||
:page-size-options="pageSizeOptions"
|
||||
:pages="pageNumbers"
|
||||
:show-page-size="true"
|
||||
:summary="paginationSummary"
|
||||
:total="visibleEmployees.length"
|
||||
@@ -225,14 +224,6 @@ const pagedEmployees = computed(() => {
|
||||
const start = (currentPage.value - 1) * pageSize.value
|
||||
return props.visibleEmployees.slice(start, start + pageSize.value)
|
||||
})
|
||||
const pageNumbers = computed(() => {
|
||||
const total = totalPages.value
|
||||
if (total <= 7) {
|
||||
return Array.from({ length: total }, (_, index) => index + 1)
|
||||
}
|
||||
const start = Math.max(1, Math.min(currentPage.value - 3, total - 6))
|
||||
return Array.from({ length: 7 }, (_, index) => start + index)
|
||||
})
|
||||
const paginationSummary = computed(() =>
|
||||
`共 ${props.visibleEmployees.length} 条,每页 ${pageSize.value} 条,目前第 ${currentPage.value} / ${totalPages.value} 页`
|
||||
)
|
||||
|
||||
@@ -110,6 +110,26 @@
|
||||
<p v-else class="run-product-inline-empty">本次运行没有生成新的风险观察。</p>
|
||||
</section>
|
||||
|
||||
<section v-else-if="productKind === 'finance_snapshot'" class="run-product-section">
|
||||
<div class="run-product-section-head">
|
||||
<h4>财务经营快照</h4>
|
||||
<span>{{ summary.period || summary.month || '本期' }}</span>
|
||||
</div>
|
||||
<p class="run-product-copy">
|
||||
本次产物已刷新财务看板缓存,沉淀报销金额、预算使用、费用结构和高额单据等经营指标。
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section v-else-if="productKind === 'reminder_scan'" class="run-product-section">
|
||||
<div class="run-product-section-head">
|
||||
<h4>提醒与待办沉淀</h4>
|
||||
<span>{{ summary.reminder_count || summary.reminders || 0 }} 条</span>
|
||||
</div>
|
||||
<p class="run-product-copy">
|
||||
本次产物已生成审批提醒、预算编制提醒、报销逾期提醒和差旅申请闭环提醒。
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section v-else-if="productKind === 'risk_clue'" class="run-product-section">
|
||||
<div class="run-product-section-head">
|
||||
<h4>待复核线索</h4>
|
||||
@@ -230,6 +250,12 @@ const productSubtitle = computed(() => {
|
||||
if (productKind.value === 'risk_graph') {
|
||||
return '展示本次巡检生成的风险观察、证据数量和图谱关系计数。'
|
||||
}
|
||||
if (productKind.value === 'finance_snapshot') {
|
||||
return '展示本次财务经营快照沉淀的预算、费用和报销统计。'
|
||||
}
|
||||
if (productKind.value === 'reminder_scan') {
|
||||
return '展示本次定时提醒扫描生成的待办和触达结果。'
|
||||
}
|
||||
if (productKind.value === 'employee_profile') {
|
||||
return '展示本次画像巡检写入的员工画像快照摘要。'
|
||||
}
|
||||
@@ -245,6 +271,12 @@ const productBadge = computed(() => {
|
||||
if (productKind.value === 'risk_graph') {
|
||||
return '风险观察'
|
||||
}
|
||||
if (productKind.value === 'finance_snapshot') {
|
||||
return '财务快照'
|
||||
}
|
||||
if (productKind.value === 'reminder_scan') {
|
||||
return '提醒事项'
|
||||
}
|
||||
if (productKind.value === 'employee_profile') {
|
||||
return '画像快照'
|
||||
}
|
||||
@@ -281,6 +313,25 @@ const metrics = computed(() => {
|
||||
buildMetric('图谱关系', payload.graph_edge_count)
|
||||
]
|
||||
}
|
||||
if (productKind.value === 'finance_snapshot') {
|
||||
return [
|
||||
buildMetric('报销单数', payload.claim_count ?? payload.claims ?? payload.total_claims),
|
||||
buildMetric(
|
||||
'报销金额',
|
||||
formatMoney(payload.claim_amount ?? payload.reimbursement_amount ?? payload.total_amount)
|
||||
),
|
||||
buildMetric('预算使用率', formatPercent(payload.budget_usage_rate ?? payload.budget_rate)),
|
||||
buildMetric('高额单据', payload.high_value_claim_count ?? payload.high_amount_claims)
|
||||
]
|
||||
}
|
||||
if (productKind.value === 'reminder_scan') {
|
||||
return [
|
||||
buildMetric('提醒人数', payload.recipient_count),
|
||||
buildMetric('提醒事项', payload.reminder_count),
|
||||
buildMetric('待审批', payload.approval_pending_count),
|
||||
buildMetric('逾期报销', payload.reimbursement_overdue_count)
|
||||
]
|
||||
}
|
||||
if (productKind.value === 'employee_profile') {
|
||||
return [
|
||||
buildMetric('目标员工', payload.target_employee_count),
|
||||
@@ -376,6 +427,23 @@ function formatWindowDays(value) {
|
||||
return days.length ? days.map((item) => `${item}天`).join(' / ') : '-'
|
||||
}
|
||||
|
||||
function formatMoney(value) {
|
||||
const amount = Number(value)
|
||||
if (!Number.isFinite(amount)) {
|
||||
return '-'
|
||||
}
|
||||
return `¥${amount.toLocaleString('zh-CN', { maximumFractionDigits: 0 })}`
|
||||
}
|
||||
|
||||
function formatPercent(value) {
|
||||
const numericValue = Number(value)
|
||||
if (!Number.isFinite(numericValue)) {
|
||||
return '-'
|
||||
}
|
||||
const percent = numericValue > 1 ? numericValue : numericValue * 100
|
||||
return `${Math.round(percent)}%`
|
||||
}
|
||||
|
||||
function observationGraphCount(item) {
|
||||
return (item.graphNodeKeys || []).length + (item.graphEdgeKeys || []).length
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
:show-pagination="!loading && !errorMessage && visibleRuns.length > 0"
|
||||
:current-page="currentPage"
|
||||
:page-size="pageSize"
|
||||
:pages="pageNumbers"
|
||||
:show-page-size="false"
|
||||
:summary="paginationSummary"
|
||||
:total="filteredRuns.length"
|
||||
@@ -303,6 +302,7 @@ import {
|
||||
import {
|
||||
formatWorkRecordDateTime,
|
||||
formatWorkRecordSummary,
|
||||
compactDigitalEmployeeWorkRecords,
|
||||
resolveWorkRecordModuleLabel,
|
||||
resolveWorkRecordSourceLabel,
|
||||
resolveWorkRecordStatusLabel,
|
||||
@@ -456,14 +456,6 @@ const visibleRuns = computed(() => {
|
||||
return filteredRuns.value.slice(start, start + pageSize)
|
||||
})
|
||||
|
||||
const pageNumbers = computed(() => {
|
||||
const total = totalPages.value
|
||||
if (total <= 7) {
|
||||
return Array.from({ length: total }, (_, index) => index + 1)
|
||||
}
|
||||
const start = Math.max(1, Math.min(currentPage.value - 3, total - 6))
|
||||
return Array.from({ length: 7 }, (_, index) => start + index)
|
||||
})
|
||||
const paginationSummary = computed(() =>
|
||||
`共 ${filteredRuns.value.length} 条,目前第 ${currentPage.value} / ${totalPages.value} 页`
|
||||
)
|
||||
@@ -523,7 +515,7 @@ async function loadWorkRecords(showToast = false) {
|
||||
|
||||
try {
|
||||
const payload = await fetchAgentRuns({ agent: 'hermes', limit: 100 })
|
||||
runs.value = Array.isArray(payload) ? payload : []
|
||||
runs.value = Array.isArray(payload) ? compactDigitalEmployeeWorkRecords(payload) : []
|
||||
emit('summary-change', {
|
||||
total: workRecordSummary.value.total,
|
||||
succeeded: workRecordSummary.value.succeeded,
|
||||
|
||||
Reference in New Issue
Block a user