feat: 财务看板口径重构与半年模拟数据及报销状态注册表
- 重构 finance_dashboard 口径计算,新增模拟公司画像数据生成与筛选 - 引入 expense_claim_status_registry 统一报销状态流转 - 完善报销草稿流程、Item Sync 与本体解析器 - 优化总览页趋势图、分页组件与请求进度步骤 - 增强报销申请快速预览、本体工具与详情展示 - 新增半年报销模拟数据种子脚本与状态审计工具 - 补充财务看板、报销状态注册与模拟数据测试覆盖
This commit is contained in:
@@ -1,152 +1,150 @@
|
||||
<template>
|
||||
<main class="login-page">
|
||||
<section class="login-visual" aria-label="智能费用管理运营能力">
|
||||
<div class="visual-brand">
|
||||
<LogoMark />
|
||||
<strong>{{ displayCompanyName }}</strong>
|
||||
<header class="page-brand">
|
||||
<LogoMark />
|
||||
<strong>{{ displayCompanyName }}</strong>
|
||||
</header>
|
||||
|
||||
<section class="hero">
|
||||
<p class="eyebrow-text">Smart Expense Operations</p>
|
||||
<h1>企业报销智能运营台</h1>
|
||||
<p class="hero-lead">让报销审批更智能、更高效</p>
|
||||
<p class="hero-sub">智能审单 · 自动化审批 · 风险预警 · SLA 监控 · 数据驱动决策</p>
|
||||
|
||||
<div class="hero-stage" aria-hidden="true">
|
||||
<span class="flow-line flow-a"></span>
|
||||
<span class="flow-line flow-b"></span>
|
||||
<span class="flow-line flow-c"></span>
|
||||
|
||||
<div class="metric-card amount">
|
||||
<span>报销金额趋势</span>
|
||||
<strong>¥ 61,600</strong>
|
||||
<small>较昨日 <b class="up">+8.3%</b></small>
|
||||
<div class="mini-bars"><i></i><i></i><i></i><i></i></div>
|
||||
</div>
|
||||
|
||||
<div class="document-card">
|
||||
<span>报销单</span>
|
||||
<i></i><i></i><i></i>
|
||||
<b class="doc-check"><i class="mdi mdi-check"></i></b>
|
||||
</div>
|
||||
|
||||
<img class="shield-art" src="../assets/security-shield.png" alt="" />
|
||||
|
||||
<div class="round-badge ai">AI</div>
|
||||
|
||||
<div class="metric-card risk">
|
||||
<span>风险预警</span>
|
||||
<strong><i class="mdi mdi-alert"></i> 14 单</strong>
|
||||
<small>较昨日 <b class="danger">+16.7%</b></small>
|
||||
</div>
|
||||
|
||||
<div class="metric-card audit">
|
||||
<span>审批效率</span>
|
||||
<strong>78%</strong>
|
||||
<small>较昨日 <b class="up">+6.2%</b></small>
|
||||
</div>
|
||||
|
||||
<div class="metric-card sla">
|
||||
<span>SLA 达成率</span>
|
||||
<strong>96%</strong>
|
||||
<small>较昨日 <b class="up">+3.1%</b></small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="visual-copy">
|
||||
<p>智能费用管理</p>
|
||||
<h1>让企业财务更高效、更合规、更可控</h1>
|
||||
<span>以智能化流程驱动费用全生命周期管理,助力企业降本增效,稳健前行。</span>
|
||||
</div>
|
||||
|
||||
<div class="visual-feature-list" aria-label="核心能力">
|
||||
<div class="feature-strip" aria-label="核心能力">
|
||||
<article v-for="item in features" :key="item.title">
|
||||
<span class="visual-feature-icon">
|
||||
<ElIcon><component :is="item.icon" /></ElIcon>
|
||||
</span>
|
||||
<span :class="item.tone"><i :class="item.icon"></i></span>
|
||||
<div>
|
||||
<strong>{{ item.title }}</strong>
|
||||
<p>{{ item.desc }}</p>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<img class="visual-main-asset" :src="loginMainVisualImage" alt="" aria-hidden="true" />
|
||||
<img class="visual-chart-asset" :src="loginChartPanelsImage" alt="" aria-hidden="true" />
|
||||
|
||||
<footer class="visual-footer">
|
||||
<span>© 2024 智能费用管理平台</span>
|
||||
<i></i>
|
||||
<span>服务热线:400-888-8888</span>
|
||||
</footer>
|
||||
</section>
|
||||
|
||||
<section class="login-panel" aria-label="登录表单">
|
||||
<div class="login-card">
|
||||
<div class="card-brand">
|
||||
<LogoMark />
|
||||
<strong>{{ displayCompanyName }}</strong>
|
||||
<section class="login-card" aria-label="登录表单">
|
||||
<div class="card-brand">
|
||||
<LogoMark />
|
||||
<strong>{{ displayCompanyName }}</strong>
|
||||
</div>
|
||||
|
||||
<header class="card-head">
|
||||
<h2>欢迎登录</h2>
|
||||
<p>使用员工邮箱或管理员账号进入系统</p>
|
||||
</header>
|
||||
|
||||
<form class="login-form" @submit.prevent="emit('login', { username, password })">
|
||||
<label class="field">
|
||||
<span class="sr-only">账号</span>
|
||||
<i class="mdi mdi-account-outline"></i>
|
||||
<input v-model="username" type="text" placeholder="请输入员工邮箱 / 管理员账号" autocomplete="username" required />
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<span class="sr-only">密码</span>
|
||||
<i class="mdi mdi-lock-outline"></i>
|
||||
<input
|
||||
v-model="password"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
placeholder="请输入登录密码"
|
||||
autocomplete="current-password"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
class="field-icon-btn"
|
||||
type="button"
|
||||
:aria-label="showPassword ? '隐藏密码' : '显示密码'"
|
||||
@click="showPassword = !showPassword"
|
||||
>
|
||||
<i :class="showPassword ? 'mdi mdi-eye' : 'mdi mdi-eye-off'"></i>
|
||||
</button>
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<span class="sr-only">企业或租户</span>
|
||||
<i class="mdi mdi-office-building"></i>
|
||||
<select v-model="tenant" class="tenant-select" aria-label="请选择企业或租户">
|
||||
<option value="远光软件股份有限公司">远光软件股份有限公司</option>
|
||||
</select>
|
||||
<span class="field-select-chevron" aria-hidden="true">
|
||||
<i class="mdi mdi-chevron-down"></i>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div class="form-meta">
|
||||
<label class="remember">
|
||||
<input v-model="remember" type="checkbox" />
|
||||
<span>记住账号</span>
|
||||
</label>
|
||||
<button type="button" class="link-btn" @click="emit('recover-password')">忘记密码?</button>
|
||||
</div>
|
||||
|
||||
<header class="card-head">
|
||||
<h2>欢迎登录</h2>
|
||||
<p>智能费用管理平台</p>
|
||||
</header>
|
||||
<p v-if="errorMessage" class="login-error">{{ errorMessage }}</p>
|
||||
|
||||
<form class="login-form" @submit.prevent="submitLogin">
|
||||
<label class="form-field">
|
||||
<span class="sr-only">账号</span>
|
||||
<ElInput
|
||||
v-model="username"
|
||||
class="login-input"
|
||||
:prefix-icon="User"
|
||||
autocomplete="username"
|
||||
clearable
|
||||
placeholder="请输入账号"
|
||||
/>
|
||||
</label>
|
||||
<button class="submit-btn" type="submit" :disabled="submitting">
|
||||
{{ submitting ? '登录中...' : '登录' }}
|
||||
</button>
|
||||
|
||||
<label class="form-field">
|
||||
<span class="sr-only">密码</span>
|
||||
<ElInput
|
||||
v-model="password"
|
||||
class="login-input"
|
||||
:prefix-icon="Lock"
|
||||
autocomplete="current-password"
|
||||
placeholder="请输入密码"
|
||||
show-password
|
||||
type="password"
|
||||
/>
|
||||
</label>
|
||||
<div class="divider"><span>或</span></div>
|
||||
|
||||
<label class="form-field">
|
||||
<span class="sr-only">所属企业</span>
|
||||
<ElSelect
|
||||
v-model="tenant"
|
||||
class="login-select"
|
||||
popper-class="login-tenant-popper"
|
||||
placeholder="请选择所属企业"
|
||||
:suffix-icon="OfficeBuilding"
|
||||
>
|
||||
<ElOption
|
||||
v-for="option in tenantOptions"
|
||||
:key="option.value"
|
||||
:label="option.label"
|
||||
:value="option.value"
|
||||
/>
|
||||
</ElSelect>
|
||||
</label>
|
||||
<button class="sso-btn" type="button" :disabled="submitting" @click="emit('sso-login')">
|
||||
<i class="mdi mdi-shield-outline"></i>
|
||||
<span>SSO 单点登录</span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="form-meta">
|
||||
<ElCheckbox v-model="remember" class="login-checkbox">记住账号</ElCheckbox>
|
||||
<button type="button" class="link-button" @click="emit('recover-password')">忘记密码?</button>
|
||||
</div>
|
||||
|
||||
<p v-if="errorMessage" class="login-error">{{ errorMessage }}</p>
|
||||
|
||||
<ElButton
|
||||
class="login-submit"
|
||||
type="primary"
|
||||
native-type="submit"
|
||||
:loading="submitting"
|
||||
:disabled="submitting"
|
||||
>
|
||||
登录
|
||||
</ElButton>
|
||||
|
||||
<ElButton
|
||||
class="login-sso"
|
||||
:icon="Grid"
|
||||
:disabled="submitting"
|
||||
@click="emit('sso-login')"
|
||||
>
|
||||
SSO 单点登录
|
||||
</ElButton>
|
||||
</form>
|
||||
|
||||
<footer class="security-note">
|
||||
登录即表示您已阅读并同意
|
||||
<button type="button">《用户协议》</button>
|
||||
和
|
||||
<button type="button">《隐私政策》</button>
|
||||
</footer>
|
||||
</div>
|
||||
<footer class="security-note">
|
||||
<i class="mdi mdi-lock-outline"></i>
|
||||
<span>安全登录 · 数据加密传输 · 如需帮助请联系系统管理员</span>
|
||||
</footer>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { ElButton } from 'element-plus/es/components/button/index.mjs'
|
||||
import { ElCheckbox } from 'element-plus/es/components/checkbox/index.mjs'
|
||||
import { ElIcon } from 'element-plus/es/components/icon/index.mjs'
|
||||
import { ElInput } from 'element-plus/es/components/input/index.mjs'
|
||||
import { ElOption, ElSelect } from 'element-plus/es/components/select/index.mjs'
|
||||
import {
|
||||
Connection,
|
||||
DataAnalysis,
|
||||
DocumentChecked,
|
||||
Grid,
|
||||
Lock,
|
||||
OfficeBuilding,
|
||||
User
|
||||
} from '@element-plus/icons-vue'
|
||||
|
||||
import loginChartPanelsImage from '../assets/login-reference-chart-panels.png'
|
||||
import loginMainVisualImage from '../assets/login-reference-main-visual.png'
|
||||
import { useLoginView } from '../composables/useLoginView.js'
|
||||
|
||||
const props = defineProps({
|
||||
@@ -168,32 +166,7 @@ const emit = defineEmits(['login', 'recover-password', 'sso-login'])
|
||||
|
||||
const displayCompanyName = computed(() => props.companyName || '易财费控')
|
||||
|
||||
const {
|
||||
features,
|
||||
LogoMark,
|
||||
password,
|
||||
remember,
|
||||
tenant,
|
||||
tenantOptions,
|
||||
username
|
||||
} = useLoginView()
|
||||
|
||||
const featureIconMap = {
|
||||
recognition: DocumentChecked,
|
||||
workflow: Connection,
|
||||
insight: DataAnalysis
|
||||
}
|
||||
|
||||
features.forEach((item) => {
|
||||
item.icon = featureIconMap[item.iconKey] || DocumentChecked
|
||||
})
|
||||
|
||||
function submitLogin() {
|
||||
emit('login', {
|
||||
username: username.value,
|
||||
password: password.value
|
||||
})
|
||||
}
|
||||
const { features, LogoMark, password, remember, showPassword, tenant, username } = useLoginView()
|
||||
</script>
|
||||
|
||||
<style scoped src="../assets/styles/views/login-view.css"></style>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<div class="content-grid top-grid">
|
||||
<article class="panel dashboard-card trend-panel">
|
||||
<div class="card-head">
|
||||
<h3>报销申请与审批趋势 <i class="mdi mdi-information-outline"></i></h3>
|
||||
<h3>每日报销金额 <i class="mdi mdi-information-outline"></i></h3>
|
||||
<EnterpriseSelect
|
||||
v-model="activeTrendRange"
|
||||
class="card-select"
|
||||
@@ -37,10 +37,23 @@
|
||||
</div>
|
||||
|
||||
<TrendChart
|
||||
mode="amount"
|
||||
:labels="activeTrend.labels"
|
||||
:applications="activeTrend.applications"
|
||||
:approved="activeTrend.approved"
|
||||
:avg-hours="activeTrend.avgHours"
|
||||
:claim-count="activeTrend.claimCount"
|
||||
:claim-amount="activeTrend.claimAmount"
|
||||
/>
|
||||
</article>
|
||||
|
||||
<article class="panel dashboard-card trend-count-panel">
|
||||
<div class="card-head">
|
||||
<h3>每日报销数量 <i class="mdi mdi-information-outline"></i></h3>
|
||||
</div>
|
||||
|
||||
<TrendChart
|
||||
mode="count"
|
||||
:labels="activeTrend.labels"
|
||||
:claim-count="activeTrend.claimCount"
|
||||
:claim-amount="activeTrend.claimAmount"
|
||||
/>
|
||||
</article>
|
||||
|
||||
@@ -51,20 +64,12 @@
|
||||
<DonutChart :items="spendLegend" :center-value="spendCenterValue" center-label="费用总额" />
|
||||
<p class="panel-note">* 百分比按当前时间范围内的费用金额计算</p>
|
||||
</article>
|
||||
|
||||
<article class="panel dashboard-card donut-panel">
|
||||
<div class="card-head">
|
||||
<h3>风险异常分布 <i class="mdi mdi-information-outline"></i></h3>
|
||||
</div>
|
||||
<DonutChart :items="riskLegend" :center-value="`${riskTotal}`" center-label="异常预警单" />
|
||||
<p class="panel-note">* 近 30 天数据</p>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="content-grid bottom-grid">
|
||||
<article class="panel dashboard-card rank-panel">
|
||||
<div class="card-head">
|
||||
<h3>部门报销排行(待处理金额)<i class="mdi mdi-information-outline"></i></h3>
|
||||
<h3>部门报销排行(费用金额)<i class="mdi mdi-information-outline"></i></h3>
|
||||
<EnterpriseSelect
|
||||
v-model="activeDepartmentRange"
|
||||
class="card-select"
|
||||
@@ -77,33 +82,58 @@
|
||||
<BarChart :items="rankedDepartments" />
|
||||
</article>
|
||||
|
||||
<article class="panel dashboard-card bottleneck-panel">
|
||||
<article class="panel dashboard-card employee-rank-panel">
|
||||
<div class="card-head">
|
||||
<h3>审批瓶颈(平均处理时长) <i class="mdi mdi-information-outline"></i></h3>
|
||||
<h3>个人报销排行(本月)<i class="mdi mdi-information-outline"></i></h3>
|
||||
</div>
|
||||
|
||||
<div class="bottleneck-list">
|
||||
<BarChart :items="rankedEmployees" />
|
||||
</article>
|
||||
|
||||
<article class="panel dashboard-card top-claim-panel">
|
||||
<div class="card-head">
|
||||
<h3>本月高额单据 <i class="mdi mdi-information-outline"></i></h3>
|
||||
</div>
|
||||
|
||||
<div class="top-claim-list">
|
||||
<div
|
||||
v-for="(item, index) in bottlenecks"
|
||||
:key="item.name"
|
||||
class="bottleneck-row"
|
||||
:style="{ '--delay': `${index * 70}ms` }"
|
||||
v-for="item in topClaims"
|
||||
:key="item.claimNo"
|
||||
class="top-claim-row"
|
||||
>
|
||||
<div class="reviewer">
|
||||
<div class="reviewer-avatar">{{ item.avatar }}</div>
|
||||
<div>
|
||||
<strong>{{ item.name }}</strong>
|
||||
<span>{{ item.role }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<strong>{{ item.claimNo }}</strong>
|
||||
<span>{{ item.employeeName }} · {{ item.departmentName || '未归属部门' }}</span>
|
||||
</div>
|
||||
<div class="reviewer-stats">
|
||||
<strong>{{ item.duration }}</strong>
|
||||
<span class="status-tag" :class="item.tone">{{ item.status }}</span>
|
||||
<div>
|
||||
<strong>{{ item.amountLabel }}</strong>
|
||||
<span>{{ item.expenseTypeLabel }} · {{ item.statusLabel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<button type="button" class="text-link">查看全部 <i class="mdi mdi-chevron-right"></i></button>
|
||||
<article class="panel dashboard-card budget-metrics-panel">
|
||||
<div class="card-head">
|
||||
<h3>预算指标 <i class="mdi mdi-information-outline"></i></h3>
|
||||
</div>
|
||||
|
||||
<div class="budget-metric-grid">
|
||||
<div
|
||||
v-for="(item, index) in budgetMetrics"
|
||||
:key="item.label"
|
||||
class="budget-metric-item"
|
||||
:class="item.tone"
|
||||
:style="{ '--delay': `${index * 70}ms` }"
|
||||
>
|
||||
<span class="budget-metric-icon"><i :class="item.icon"></i></span>
|
||||
<div>
|
||||
<span>{{ item.label }}</span>
|
||||
<strong>{{ item.value }}</strong>
|
||||
<em>{{ item.detail }}</em>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="panel dashboard-card budget-panel">
|
||||
@@ -326,7 +356,7 @@ const {
|
||||
activeRiskWindowDays,
|
||||
activeTrend,
|
||||
activeTrendRange,
|
||||
bottlenecks,
|
||||
budgetMetrics,
|
||||
budgetSummary,
|
||||
departmentRangeOptions,
|
||||
digitalEmployeeCategoryRows,
|
||||
@@ -338,16 +368,15 @@ const {
|
||||
digitalEmployeeTaskRanking,
|
||||
kpiMetrics,
|
||||
rankedDepartments,
|
||||
rankedEmployees,
|
||||
riskDashboard,
|
||||
riskDashboardError,
|
||||
riskDashboardLoading,
|
||||
riskDailyTrendRows,
|
||||
riskLegend,
|
||||
riskKpiMetrics,
|
||||
riskLevelLegend,
|
||||
riskSignalRanking,
|
||||
riskSourceLegend,
|
||||
riskTotal,
|
||||
riskWindowOptions,
|
||||
setRiskWindowDays,
|
||||
spendCenterValue,
|
||||
@@ -362,6 +391,7 @@ const {
|
||||
systemUsageDurationRows,
|
||||
systemUsageDurationSummary,
|
||||
systemUserTokenUsage,
|
||||
topClaims,
|
||||
trendRanges
|
||||
} = useOverviewView(props)
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ export default {
|
||||
const rows = demoDepartments
|
||||
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,
|
||||
|
||||
@@ -83,13 +83,38 @@ function normalizeApplicationDateText(value) {
|
||||
}
|
||||
|
||||
function normalizeApplicationBusinessTime(claim) {
|
||||
const start = normalizeApplicationDateText(claim?.start_date || claim?.startDate || claim?.begin_date || claim?.beginDate)
|
||||
const end = normalizeApplicationDateText(claim?.end_date || claim?.endDate || claim?.finish_date || claim?.finishDate)
|
||||
const detail = resolveApplicationDetailPayload(claim)
|
||||
const start = normalizeApplicationDateText(
|
||||
detail.start_date
|
||||
|| detail.startDate
|
||||
|| detail.departure_date
|
||||
|| detail.departureDate
|
||||
|| claim?.start_date
|
||||
|| claim?.startDate
|
||||
|| claim?.begin_date
|
||||
|| claim?.beginDate
|
||||
)
|
||||
const end = normalizeApplicationDateText(
|
||||
detail.end_date
|
||||
|| detail.endDate
|
||||
|| detail.return_date
|
||||
|| detail.returnDate
|
||||
|| claim?.end_date
|
||||
|| claim?.endDate
|
||||
|| claim?.finish_date
|
||||
|| claim?.finishDate
|
||||
)
|
||||
if (start && end && start !== end) {
|
||||
return `${start} 至 ${end}`
|
||||
}
|
||||
return normalizeApplicationDateText(
|
||||
start
|
||||
|| detail.application_business_time
|
||||
|| detail.applicationBusinessTime
|
||||
|| detail.business_time
|
||||
|| detail.businessTime
|
||||
|| detail.time_range
|
||||
|| detail.timeRange
|
||||
|| claim?.business_time
|
||||
|| claim?.businessTime
|
||||
|| claim?.time_range
|
||||
@@ -101,6 +126,21 @@ function normalizeApplicationBusinessTime(claim) {
|
||||
)
|
||||
}
|
||||
|
||||
function resolveApplicationDetailPayload(claim) {
|
||||
const flags = Array.isArray(claim?.risk_flags_json)
|
||||
? claim.risk_flags_json
|
||||
: Array.isArray(claim?.riskFlags)
|
||||
? claim.riskFlags
|
||||
: []
|
||||
const detailFlag = flags.find((flag) => (
|
||||
flag &&
|
||||
typeof flag === 'object' &&
|
||||
normalizeLower(flag.source) === 'application_detail'
|
||||
))
|
||||
const detail = detailFlag?.application_detail || detailFlag?.applicationDetail || {}
|
||||
return detail && typeof detail === 'object' ? detail : {}
|
||||
}
|
||||
|
||||
function toTimestamp(value) {
|
||||
const date = new Date(value)
|
||||
return Number.isNaN(date.getTime()) ? 0 : date.getTime()
|
||||
@@ -231,20 +271,51 @@ export function isUsableRequiredApplicationClaim(claim) {
|
||||
}
|
||||
|
||||
export function normalizeRequiredApplicationCandidate(claim) {
|
||||
const detail = resolveApplicationDetailPayload(claim)
|
||||
const claimNo = normalizeText(claim?.claim_no || claim?.claimNo)
|
||||
const location = normalizeText(claim?.location || claim?.business_location || claim?.businessLocation)
|
||||
const amountText = formatAmount(claim?.amount || claim?.budget_amount || claim?.budgetAmount)
|
||||
const location = normalizeText(
|
||||
detail.location
|
||||
|| detail.application_location
|
||||
|| claim?.location
|
||||
|| claim?.business_location
|
||||
|| claim?.businessLocation
|
||||
)
|
||||
const amount = normalizeText(
|
||||
detail.amount
|
||||
|| detail.application_amount
|
||||
|| claim?.amount
|
||||
|| claim?.budget_amount
|
||||
|| claim?.budgetAmount
|
||||
)
|
||||
const amountText = formatAmount(amount)
|
||||
const status = normalizeApplicationStatus(claim)
|
||||
|
||||
return {
|
||||
id: normalizeText(claim?.id || claim?.claim_id || claim?.claimId),
|
||||
claim_no: claimNo,
|
||||
expense_type: normalizeExpenseType(claim),
|
||||
reason: normalizeText(claim?.reason || claim?.business_reason || claim?.description || claim?.title),
|
||||
reason: normalizeText(detail.reason || detail.application_reason || claim?.reason || claim?.business_reason || claim?.description || claim?.title),
|
||||
location,
|
||||
amount: normalizeText(claim?.amount || claim?.budget_amount || claim?.budgetAmount),
|
||||
amount,
|
||||
amount_label: amountText,
|
||||
business_time: normalizeApplicationBusinessTime(claim),
|
||||
business_time: normalizeText(
|
||||
detail.application_business_time
|
||||
|| detail.applicationBusinessTime
|
||||
|| detail.business_time
|
||||
|| detail.businessTime
|
||||
|| detail.time_range
|
||||
|| detail.timeRange
|
||||
|| detail.time
|
||||
|| detail.application_time
|
||||
) || normalizeApplicationBusinessTime(claim),
|
||||
days: normalizeText(detail.days || detail.application_days),
|
||||
transport_mode: normalizeText(detail.transport_mode || detail.application_transport_mode),
|
||||
lodging_daily_cap: normalizeText(detail.lodging_daily_cap || detail.application_lodging_daily_cap),
|
||||
subsidy_daily_cap: normalizeText(detail.subsidy_daily_cap || detail.application_subsidy_daily_cap),
|
||||
transport_policy: normalizeText(detail.transport_policy || detail.application_transport_policy),
|
||||
policy_estimate: normalizeText(detail.policy_estimate || detail.application_policy_estimate),
|
||||
rule_name: normalizeText(detail.rule_name || detail.application_rule_name),
|
||||
rule_version: normalizeText(detail.rule_version || detail.application_rule_version),
|
||||
status,
|
||||
status_label: STATUS_LABELS[status] || normalizeText(claim?.approval_stage || claim?.approvalStage || status),
|
||||
application_date: normalizeApplicationDate(claim)
|
||||
@@ -296,6 +367,14 @@ export function buildRequiredApplicationActions(applications, actionType) {
|
||||
application_amount: application.amount,
|
||||
application_amount_label: application.amount_label,
|
||||
application_business_time: application.business_time,
|
||||
application_days: application.days,
|
||||
application_transport_mode: application.transport_mode,
|
||||
application_lodging_daily_cap: application.lodging_daily_cap,
|
||||
application_subsidy_daily_cap: application.subsidy_daily_cap,
|
||||
application_transport_policy: application.transport_policy,
|
||||
application_policy_estimate: application.policy_estimate,
|
||||
application_rule_name: application.rule_name,
|
||||
application_rule_version: application.rule_version,
|
||||
application_status: application.status,
|
||||
application_status_label: application.status_label,
|
||||
application_date: application.application_date
|
||||
|
||||
@@ -140,6 +140,14 @@ function normalizeApplicationCandidates(applications) {
|
||||
amount: normalizeText(item.amount || item.application_amount),
|
||||
amount_label: normalizeText(item.amount_label || item.application_amount_label),
|
||||
business_time: normalizeText(item.business_time || item.application_business_time),
|
||||
days: normalizeText(item.days || item.application_days),
|
||||
transport_mode: normalizeText(item.transport_mode || item.application_transport_mode),
|
||||
lodging_daily_cap: normalizeText(item.lodging_daily_cap || item.application_lodging_daily_cap),
|
||||
subsidy_daily_cap: normalizeText(item.subsidy_daily_cap || item.application_subsidy_daily_cap),
|
||||
transport_policy: normalizeText(item.transport_policy || item.application_transport_policy),
|
||||
policy_estimate: normalizeText(item.policy_estimate || item.application_policy_estimate),
|
||||
rule_name: normalizeText(item.rule_name || item.application_rule_name),
|
||||
rule_version: normalizeText(item.rule_version || item.application_rule_version),
|
||||
status: normalizeText(item.status || item.application_status),
|
||||
status_label: normalizeText(item.status_label || item.application_status_label),
|
||||
application_date: normalizeText(item.application_date)
|
||||
@@ -264,6 +272,14 @@ export function selectGuidedRequiredApplication(state, application = {}) {
|
||||
application_amount: application.application_amount || application.amount || '',
|
||||
application_amount_label: application.application_amount_label || application.amount_label || '',
|
||||
application_business_time: application.application_business_time || application.business_time || '',
|
||||
application_days: application.application_days || application.days || '',
|
||||
application_transport_mode: application.application_transport_mode || application.transport_mode || '',
|
||||
application_lodging_daily_cap: application.application_lodging_daily_cap || application.lodging_daily_cap || '',
|
||||
application_subsidy_daily_cap: application.application_subsidy_daily_cap || application.subsidy_daily_cap || '',
|
||||
application_transport_policy: application.application_transport_policy || application.transport_policy || '',
|
||||
application_policy_estimate: application.application_policy_estimate || application.policy_estimate || '',
|
||||
application_rule_name: application.application_rule_name || application.rule_name || '',
|
||||
application_rule_version: application.application_rule_version || application.rule_version || '',
|
||||
application_status_label: application.application_status_label || application.status_label || '',
|
||||
application_date: application.application_date || ''
|
||||
}),
|
||||
@@ -412,6 +428,7 @@ export function buildGuidedReviewSubmitOptions(state, files = []) {
|
||||
const applicationLocation = values.application_location || ''
|
||||
const applicationAmount = values.application_amount || values.application_amount_label || ''
|
||||
const applicationBusinessTime = values.application_business_time || ''
|
||||
const applicationTransportMode = values.application_transport_mode || ''
|
||||
const fieldLines = []
|
||||
if (linkedApplication) {
|
||||
const applicationParts = buildApplicationSummaryParts(values)
|
||||
@@ -440,6 +457,7 @@ export function buildGuidedReviewSubmitOptions(state, files = []) {
|
||||
business_location: values.location || applicationLocation || '',
|
||||
time_range: values.time_range || applicationBusinessTime || '',
|
||||
business_time: values.time_range || applicationBusinessTime || '',
|
||||
transport_mode: values.transport_mode || applicationTransportMode || '',
|
||||
amount: linkedApplication ? (values.amount || '') : (values.amount || applicationAmount || ''),
|
||||
attachment_names: Array.isArray(values.attachment_names) ? values.attachment_names : [],
|
||||
application_claim_id: values.application_claim_id || '',
|
||||
@@ -449,6 +467,14 @@ export function buildGuidedReviewSubmitOptions(state, files = []) {
|
||||
application_amount: values.application_amount || '',
|
||||
application_amount_label: values.application_amount_label || '',
|
||||
application_business_time: values.application_business_time || '',
|
||||
application_days: values.application_days || '',
|
||||
application_transport_mode: values.application_transport_mode || '',
|
||||
application_lodging_daily_cap: values.application_lodging_daily_cap || '',
|
||||
application_subsidy_daily_cap: values.application_subsidy_daily_cap || '',
|
||||
application_transport_policy: values.application_transport_policy || '',
|
||||
application_policy_estimate: values.application_policy_estimate || '',
|
||||
application_rule_name: values.application_rule_name || '',
|
||||
application_rule_version: values.application_rule_version || '',
|
||||
application_date: values.application_date || ''
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ function buildTransportEstimatePendingPreview(preview = {}) {
|
||||
...preview,
|
||||
fields: {
|
||||
...fields,
|
||||
transportPolicy: '正在查询交通参考票价...',
|
||||
transportPolicy: '正在预估交通费用...',
|
||||
policyEstimate: '正在同步费用测算...',
|
||||
transportEstimatedAmount: '查询中'
|
||||
}
|
||||
|
||||
@@ -288,8 +288,8 @@ export function useTravelReimbursementGuidedFlow({
|
||||
const applicationId = normalizeText(current.values.application_claim_id)
|
||||
const applicationReason = normalizeText(current.values.application_reason)
|
||||
const applicationLocation = normalizeText(current.values.application_location)
|
||||
const applicationAmount = normalizeText(current.values.application_amount || current.values.application_amount_label)
|
||||
const applicationBusinessTime = normalizeText(current.values.application_business_time)
|
||||
const applicationTransportMode = normalizeText(current.values.application_transport_mode)
|
||||
if (!originalMessage || !expenseTypeLabel || !applicationNo) {
|
||||
return null
|
||||
}
|
||||
@@ -326,14 +326,23 @@ export function useTravelReimbursementGuidedFlow({
|
||||
business_location: applicationLocation,
|
||||
time_range: applicationBusinessTime,
|
||||
business_time: applicationBusinessTime,
|
||||
amount: applicationAmount,
|
||||
transport_mode: applicationTransportMode,
|
||||
amount: '',
|
||||
application_claim_id: applicationId,
|
||||
application_claim_no: applicationNo,
|
||||
application_reason: applicationReason,
|
||||
application_location: applicationLocation,
|
||||
application_amount: current.values.application_amount || '',
|
||||
application_amount_label: current.values.application_amount_label || '',
|
||||
application_business_time: applicationBusinessTime
|
||||
application_business_time: applicationBusinessTime,
|
||||
application_days: current.values.application_days || '',
|
||||
application_transport_mode: current.values.application_transport_mode || '',
|
||||
application_lodging_daily_cap: current.values.application_lodging_daily_cap || '',
|
||||
application_subsidy_daily_cap: current.values.application_subsidy_daily_cap || '',
|
||||
application_transport_policy: current.values.application_transport_policy || '',
|
||||
application_policy_estimate: current.values.application_policy_estimate || '',
|
||||
application_rule_name: current.values.application_rule_name || '',
|
||||
application_rule_version: current.values.application_rule_version || ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user