feat: 风险可见性控制与差旅详情页交互优化

- 新增风险可见性工具函数与风险日趋势图表组件
- 优化差旅请求详情页费用模型与视图交互
- 完善顶部导航栏样式与应用壳路由逻辑
- 补充风险可见性、风险看板与差旅详情测试覆盖
This commit is contained in:
caoxiaozhu
2026-06-03 22:15:45 +08:00
parent 75d5c178e1
commit 87da5df91b
17 changed files with 809 additions and 168 deletions

View File

@@ -37,6 +37,7 @@ import {
const DEFAULT_OVERVIEW_RANGE = '近10日'
const DAY_MS = 24 * 60 * 60 * 1000
const RISK_DAILY_TREND_MAX_BUCKETS = 14
const emptyFinanceTotals = {
reimbursementAmount: 0,
@@ -137,6 +138,64 @@ function resolveTopRangeKey(range, customRange = {}) {
return key || DEFAULT_OVERVIEW_RANGE
}
function formatRiskTrendDateLabel(value) {
const date = parseLocalDate(value)
if (!date) {
return String(value || '-').trim() || '-'
}
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${month}-${day}`
}
function buildRiskTrendBucketLabel(first, last) {
const start = String(first?.date || '').trim()
const end = String(last?.date || '').trim()
if (!start || start === end) {
return formatRiskTrendDateLabel(start)
}
return `${formatRiskTrendDateLabel(start)}~${formatRiskTrendDateLabel(end)}`
}
function normalizeRiskTrendRow(item) {
return {
date: String(item.date || '').trim() || '-',
total: Number(item.total || 0),
highOrAbove: Number(item.high_or_above ?? item.highOrAbove ?? 0)
}
}
function aggregateRiskDailyTrendRows(rows, maxBuckets = RISK_DAILY_TREND_MAX_BUCKETS) {
const normalizedRows = rows
.map(normalizeRiskTrendRow)
.filter((item) => item.date !== '-' || item.total > 0 || item.highOrAbove > 0)
if (normalizedRows.length <= maxBuckets) {
return normalizedRows.map((item) => ({
...item,
date: formatRiskTrendDateLabel(item.date),
sourceStartDate: item.date,
sourceEndDate: item.date
}))
}
const bucketSize = Math.ceil(normalizedRows.length / maxBuckets)
const buckets = []
for (let index = 0; index < normalizedRows.length; index += bucketSize) {
const bucketRows = normalizedRows.slice(index, index + bucketSize)
const first = bucketRows[0]
const last = bucketRows[bucketRows.length - 1]
buckets.push({
date: buildRiskTrendBucketLabel(first, last),
sourceStartDate: first?.date || '',
sourceEndDate: last?.date || '',
total: bucketRows.reduce((sum, item) => sum + item.total, 0),
highOrAbove: bucketRows.reduce((sum, item) => sum + item.highOrAbove, 0)
})
}
return buckets
}
export function useOverviewView(options = {}) {
const activeDashboardKey = computed(() => {
const dashboard = String(options.dashboard || '').trim()
@@ -830,11 +889,7 @@ export function useOverviewView(options = {}) {
})
const riskDailyTrendRows = computed(() => {
const rows = Array.isArray(riskDashboard.value.dailyTrend) ? riskDashboard.value.dailyTrend : []
const normalizedRows = rows.slice(-7).map((item) => ({
date: String(item.date || '').trim() || '-',
total: Number(item.total || 0),
highOrAbove: Number(item.high_or_above ?? item.highOrAbove ?? 0)
}))
const normalizedRows = aggregateRiskDailyTrendRows(rows)
const maxValue = Math.max(...normalizedRows.map((item) => item.total), 1)
return normalizedRows.map((item) => ({