feat: 财务看板口径重构与半年模拟数据及报销状态注册表
- 重构 finance_dashboard 口径计算,新增模拟公司画像数据生成与筛选 - 引入 expense_claim_status_registry 统一报销状态流转 - 完善报销草稿流程、Item Sync 与本体解析器 - 优化总览页趋势图、分页组件与请求进度步骤 - 增强报销申请快速预览、本体工具与详情展示 - 新增半年报销模拟数据种子脚本与状态审计工具 - 补充财务看板、报销状态注册与模拟数据测试覆盖
This commit is contained in:
@@ -37,16 +37,18 @@ import {
|
||||
} from '../data/metrics.js'
|
||||
|
||||
const emptyFinanceTotals = {
|
||||
pendingCount: 0,
|
||||
pendingAmount: 0,
|
||||
avgSla: 0,
|
||||
autoPassRate: 0,
|
||||
riskCount: 0,
|
||||
slaRate: 0
|
||||
reimbursementAmount: 0,
|
||||
reimbursementCount: 0,
|
||||
pendingPaymentAmount: 0,
|
||||
avgClaimAmount: 0,
|
||||
budgetUsageRate: 0,
|
||||
paymentClearanceRate: 0
|
||||
}
|
||||
|
||||
const emptyFinanceTrend = {
|
||||
labels: [],
|
||||
claimCount: [],
|
||||
claimAmount: [],
|
||||
applications: [],
|
||||
approved: [],
|
||||
avgHours: []
|
||||
@@ -63,6 +65,15 @@ const emptyFinanceBudgetSummary = {
|
||||
left: '¥0'
|
||||
}
|
||||
|
||||
const emptyFinanceBudgetMetrics = [
|
||||
{ label: '预算池数量', value: '0 个', detail: '年度有效预算池', tone: 'neutral', icon: 'mdi mdi-database-outline' },
|
||||
{ label: '总预算', value: '¥0', detail: '原始预算 + 调整', tone: 'neutral', icon: 'mdi mdi-cash-register' },
|
||||
{ label: '已用预算', value: '¥0', detail: '使用率 0.0%', tone: 'success', icon: 'mdi mdi-chart-arc' },
|
||||
{ label: '预占预算', value: '¥0', detail: '待流转单据占用', tone: 'success', icon: 'mdi mdi-lock-outline' },
|
||||
{ label: '可用预算', value: '¥0', detail: '可继续使用额度', tone: 'success', icon: 'mdi mdi-wallet-outline' },
|
||||
{ label: '预警预算池', value: '0 个', detail: '超支 0 个', tone: 'success', icon: 'mdi mdi-alert-outline' }
|
||||
]
|
||||
|
||||
export function useOverviewView(options = {}) {
|
||||
const activeTrendRange = ref(trendRanges[0])
|
||||
const activeDepartmentRange = ref(departmentRangeOptions[0])
|
||||
@@ -103,8 +114,9 @@ export function useOverviewView(options = {}) {
|
||||
const formatPercent = (value) => `${Math.round(Number(value || 0) * 100)}%`
|
||||
|
||||
const formatMetricValue = (metric, value) => {
|
||||
if (metric.key === 'pendingAmount') return formatCurrency(Math.round(value))
|
||||
if (metric.key === 'avgSla') return `${value.toFixed(1)} ${metric.unit}`
|
||||
if (['reimbursementAmount', 'pendingPaymentAmount', 'avgClaimAmount'].includes(metric.key)) {
|
||||
return formatCurrency(Math.round(value))
|
||||
}
|
||||
if (metric.unit === '%') return `${Math.round(value)} ${metric.unit}`
|
||||
if (metric.unit) return `${Math.round(value)} ${metric.unit}`
|
||||
return `${Math.round(value)}`
|
||||
@@ -311,12 +323,21 @@ export function useOverviewView(options = {}) {
|
||||
const financeDepartmentRanking = computed(() => (
|
||||
financeDashboardPayload.value?.departmentRanking || []
|
||||
))
|
||||
const financeEmployeeRanking = computed(() => (
|
||||
financeDashboardPayload.value?.employeeRanking || []
|
||||
))
|
||||
const financeTopClaims = computed(() => (
|
||||
financeDashboardPayload.value?.topClaims || []
|
||||
))
|
||||
const financeBottlenecks = computed(() => (
|
||||
financeDashboardPayload.value?.bottlenecks || []
|
||||
))
|
||||
const financeBudgetSummary = computed(() => (
|
||||
financeDashboardPayload.value?.budgetSummary || emptyFinanceBudgetSummary
|
||||
))
|
||||
const financeBudgetMetrics = computed(() => (
|
||||
financeDashboardPayload.value?.budgetMetrics || emptyFinanceBudgetMetrics
|
||||
))
|
||||
|
||||
const resolveSystemMetricMeta = (metric) => {
|
||||
const totals = systemDashboardTotals.value
|
||||
@@ -508,13 +529,15 @@ export function useOverviewView(options = {}) {
|
||||
})))
|
||||
|
||||
const rankedDepartments = computed(() => {
|
||||
const rows = financeDepartmentRanking.value.map((item) => ({
|
||||
...item,
|
||||
amount: Number(item.amount || item.value || 0)
|
||||
}))
|
||||
const rows = financeDepartmentRanking.value
|
||||
.filter((item) => !isMissingDimension(item.name))
|
||||
.map((item) => ({
|
||||
...item,
|
||||
amount: Number(item.amount || item.value || 0)
|
||||
}))
|
||||
const max = Math.max(...rows.map((item) => item.amount), 1)
|
||||
|
||||
return rows.slice(0, 5).map((item, index) => ({
|
||||
return rows.slice(0, 6).map((item, index) => ({
|
||||
...item,
|
||||
rank: index + 1,
|
||||
shortName: item.name,
|
||||
@@ -524,6 +547,32 @@ export function useOverviewView(options = {}) {
|
||||
}))
|
||||
})
|
||||
|
||||
const rankedEmployees = computed(() => {
|
||||
const rows = financeEmployeeRanking.value
|
||||
.filter((item) => !isMissingDimension(item.name))
|
||||
.map((item) => ({
|
||||
...item,
|
||||
amount: Number(item.amount || item.value || 0)
|
||||
}))
|
||||
const max = Math.max(...rows.map((item) => item.amount), 1)
|
||||
|
||||
return rows.slice(0, 6).map((item, index) => ({
|
||||
...item,
|
||||
rank: index + 1,
|
||||
shortName: item.name,
|
||||
amountLabel: formatCurrency(item.amount),
|
||||
width: `${Math.max((item.amount / max) * 100, 18)}%`,
|
||||
color: item.color
|
||||
}))
|
||||
})
|
||||
|
||||
const topClaims = computed(() => (
|
||||
financeTopClaims.value.map((item) => ({
|
||||
...item,
|
||||
amountLabel: item.amountLabel || formatCurrency(Number(item.amount || 0))
|
||||
}))
|
||||
))
|
||||
|
||||
const systemToolRankingItems = computed(() => systemToolRankings.map((item, index) => ({
|
||||
...item,
|
||||
rank: index + 1,
|
||||
@@ -670,8 +719,14 @@ export function useOverviewView(options = {}) {
|
||||
return labels[text] || text.replace(/_/g, ' ') || '未知风险'
|
||||
}
|
||||
|
||||
function isMissingDimension(value) {
|
||||
const text = String(value || '').trim()
|
||||
return !text || ['待补充', '待确认', '未归属部门', '未归属', 'N/A', 'n/a', '-'].includes(text)
|
||||
}
|
||||
|
||||
const bottlenecks = financeBottlenecks
|
||||
const budgetSummary = financeBudgetSummary
|
||||
const budgetMetrics = financeBudgetMetrics
|
||||
const spendByCategory = financeSpendByCategory
|
||||
const exceptionMix = financeExceptionMix
|
||||
|
||||
@@ -681,6 +736,7 @@ export function useOverviewView(options = {}) {
|
||||
activeTrend,
|
||||
activeTrendRange,
|
||||
bottlenecks,
|
||||
budgetMetrics,
|
||||
budgetSummary,
|
||||
departmentRangeOptions,
|
||||
digitalEmployeeCategoryRows,
|
||||
@@ -701,6 +757,7 @@ export function useOverviewView(options = {}) {
|
||||
kpiMetrics,
|
||||
metricBlueprints,
|
||||
rankedDepartments,
|
||||
rankedEmployees,
|
||||
riskDashboard,
|
||||
riskDashboardError,
|
||||
riskDashboardLoading,
|
||||
@@ -743,6 +800,7 @@ export function useOverviewView(options = {}) {
|
||||
systemToolRankings,
|
||||
systemToolTotal,
|
||||
systemTrendSeries,
|
||||
topClaims,
|
||||
trendRanges
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user