Files
X-Financial/web/src/composables/overviewViewDisplayModel.js

200 lines
6.1 KiB
JavaScript
Raw Normal View History

import { formatRiskSignalLabel } from '../utils/riskLabels.js'
export const emptyFinanceTotals = {
reimbursementAmount: 0,
reimbursementCount: 0,
pendingPaymentAmount: 0,
avgClaimAmount: 0,
budgetUsageRate: 0,
paymentClearanceRate: 0
}
export const emptyFinanceTrend = {
labels: [],
claimCount: [],
claimAmount: [],
categoryAmountSeries: [],
applications: [],
approved: [],
avgHours: []
}
export const emptyFinanceDonut = [
{ name: '暂无数据', value: 0, color: '#cbd5e1' }
]
export const emptyFinanceBudgetSummary = {
ratio: 0,
total: '¥0',
used: '¥0',
left: '¥0'
}
export 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 const emptySystemDashboardTotals = {
toolCalls: 0,
modelTokens: 0,
onlineUsers: 0,
avgOnlineMinutes: 0,
executionSuccessRate: 0,
positiveFeedback: 0,
negativeFeedback: 0,
failedRuns: 0,
toolCallsChange: 0,
modelTokensChange: 0
}
export const emptySystemLoginWave = {
labels: Array.from({ length: 24 }, (_, hour) => `${String(hour).padStart(2, '0')}:00`),
loginUsers: Array.from({ length: 24 }, () => 0),
interactions: Array.from({ length: 24 }, () => 0)
}
export function formatCompact(value) {
if (value >= 1_000_000) return `¥${(value / 1_000_000).toFixed(1)}M`
if (value >= 1_000) return `¥${(value / 1_000).toFixed(1)}K`
return `¥${value}`
}
export function formatCurrency(value) {
return formatCompact(value)
}
export function formatNumberCompact(value) {
const number = Number(value || 0)
if (number >= 1_000_000) return `${(number / 1_000_000).toFixed(1)}M`
if (number >= 1_000) return `${(number / 1_000).toFixed(1)}K`
return `${Math.round(number)}`
}
export function formatPercent(value) {
return `${Math.round(Number(value || 0) * 100)}%`
}
export function formatMetricValue(metric, value) {
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)}`
}
export function formatSystemMetricValue(metric, value, totals = emptySystemDashboardTotals) {
const numericValue = Number(value || 0)
if (metric.key === 'modelTokens') return formatNumberCompact(numericValue)
if (metric.key === 'avgOnlineMinutes') return `${numericValue.toFixed(1)} ${metric.unit}`
if (metric.key === 'executionSuccessRate') return `${numericValue.toFixed(1)}${metric.unit}`
if (metric.key === 'positiveFeedback') {
const negativeFeedback = Math.round(Number(totals.negativeFeedback || 0))
return `${Math.round(numericValue)} / ${negativeFeedback}`
}
if (metric.unit) return `${formatNumberCompact(numericValue)} ${metric.unit}`
return formatNumberCompact(numericValue)
}
export function resolveSystemMetricMeta(metric, totals = emptySystemDashboardTotals, realDashboardLoaded = false) {
if (!realDashboardLoaded) {
return {
changeText: metric.change,
delta: metric.delta,
trend: metric.trend
}
}
if (metric.key === 'toolCalls' || metric.key === 'modelTokens') {
const changeValue = Number(totals[`${metric.key}Change`] || 0)
return {
changeText: `${changeValue >= 0 ? '+' : ''}${changeValue.toFixed(1)}%`,
delta: '较上一周期',
trend: changeValue < 0 ? 'down' : 'up'
}
}
if (metric.key === 'executionSuccessRate') {
const errorRate = Math.max(0, 100 - Number(totals.executionSuccessRate || 0))
return {
changeText: '实时',
delta: `错误率 ${errorRate.toFixed(1)}%`,
trend: 'up'
}
}
if (metric.key === 'positiveFeedback') {
return {
changeText: '实时',
delta: `差评 ${Math.round(Number(totals.negativeFeedback || 0))}`,
trend: 'up'
}
}
return {
changeText: '实时',
delta: metric.key === 'onlineUsers' ? '活跃会话' : '按最近会话统计',
trend: metric.trend
}
}
export function resolveFinanceMetricMeta({
metric,
meta,
dashboardLoaded,
loading,
error
}) {
if (!dashboardLoaded || !meta) {
return {
changeText: loading ? '加载中' : '实时',
delta: error ? '真实数据加载失败' : '等待真实数据',
trend: metric.trend
}
}
return {
changeText: meta.changeText || metric.change,
delta: meta.delta || metric.delta,
trend: meta.trend || metric.trend
}
}
export function buildRiskDistributionLegend(distribution, labels, colors, formatter = formatRiskSignalName) {
const fallbackColors = ['#ef4444', '#f59e0b', 'var(--theme-primary)', '#3b82f6', '#8b5cf6', '#0f766e']
const entries = Object.entries(distribution || {})
.filter(([, value]) => Number(value || 0) > 0)
if (!entries.length) {
return [
{
name: '暂无数据',
value: 1,
display: '0项',
color: '#cbd5e1'
}
]
}
return entries.map(([key, value], index) => ({
name: labels[key] || formatter(key),
value: Number(value || 0),
display: `${Number(value || 0)}`,
color: colors[key] || fallbackColors[index % fallbackColors.length]
}))
}
export function formatRiskSignalName(value) {
return formatRiskSignalLabel(value)
}
export function isMissingDimension(value) {
const text = String(value || '').trim()
return !text || ['待补充', '待确认', '未归属部门', '未归属', 'N/A', 'n/a', '-'].includes(text)
}