Files
X-Financial/web/src/components/travel/TravelRequestExpenseCaseTimeline.vue
2026-07-20 10:31:27 +08:00

125 lines
4.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<section class="expense-case-timeline" aria-labelledby="expense-case-timeline-title">
<div class="expense-case-timeline-head">
<div>
<div class="expense-case-timeline-title-row">
<h4 id="expense-case-timeline-title">费用操作记录</h4>
<span v-if="timeline" class="expense-case-stage">{{ timeline.stageLabel }}</span>
</div>
<p v-if="timeline" class="expense-case-meta">
<span>{{ timeline.caseNo || '统一费用事件' }}</span>
<span>已串联 {{ timeline.linkCount }} 张业务单据</span>
<span v-if="displayedEvents.length"> {{ displayedEvents.length }} 条记录</span>
<span v-if="displayedEvents.length">最新优先</span>
</p>
<p v-else>记录费用从申请审批报销到付款的真实办理轨迹</p>
</div>
<button
class="expense-case-refresh"
type="button"
:disabled="loading"
aria-label="刷新费用事件时间线"
@click="refreshExpenseCaseTimeline"
>
<i :class="loading ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-refresh'" aria-hidden="true"></i>
<span>刷新</span>
</button>
</div>
<div class="expense-case-timeline-body" :aria-busy="loading">
<div v-if="loading && !timeline" class="expense-case-state loading">
<i class="mdi mdi-loading mdi-spin"></i>
<span>正在同步真实费用事件</span>
</div>
<div v-else-if="errorMessage" class="expense-case-state error">
<i class="mdi mdi-alert-circle-outline"></i>
<div>
<strong>费用事件加载失败</strong>
<span>{{ errorMessage }}</span>
</div>
<button type="button" @click="refreshExpenseCaseTimeline">重新加载</button>
</div>
<div v-else-if="notAvailable" class="expense-case-state empty">
<i class="mdi mdi-timeline-alert-outline"></i>
<div>
<strong>当前单据尚未纳入统一费用事件</strong>
<span>上方进度仍可正常使用后续业务动作写入后将自动展示办理记录</span>
</div>
</div>
<div v-else-if="timeline && !timeline.events.length" class="expense-case-state empty">
<i class="mdi mdi-timeline-clock-outline"></i>
<div>
<strong>统一费用事件已建立</strong>
<span>暂无可展示的办理记录</span>
</div>
</div>
<div v-else-if="timeline" class="expense-case-event-table">
<div class="expense-case-event-columns" aria-hidden="true">
<span>发生时间</span>
<span>事件</span>
<span>操作人</span>
</div>
<ol class="expense-case-event-list">
<li
v-for="event in displayedEvents"
:key="event.id"
class="expense-case-event"
:class="event.tone"
>
<time
class="expense-case-event-time"
:datetime="event.occurredAt || undefined"
>
{{ event.occurredAtLabel }}
</time>
<div class="expense-case-event-main">
<span class="expense-case-event-icon" aria-hidden="true">
<i :class="event.icon"></i>
</span>
<div class="expense-case-event-copy">
<strong>{{ event.label }}</strong>
<p>{{ event.summary }}</p>
</div>
</div>
<div class="expense-case-event-actor" :title="event.actorLabel">
<i class="mdi mdi-account-outline" aria-hidden="true"></i>
<span>{{ event.actorLabel }}</span>
</div>
</li>
</ol>
</div>
</div>
</section>
</template>
<script setup>
import { computed, toRef } from 'vue'
import { useExpenseCaseTimeline } from '../../composables/requests/useExpenseCaseTimeline.js'
import { orderExpenseCaseEventsForDisplay } from '../../utils/expenseCaseTimeline.js'
const props = defineProps({
claimId: { type: String, default: '' },
refreshKey: { type: String, default: '' }
})
const {
timeline,
loading,
errorMessage,
notAvailable,
refreshExpenseCaseTimeline
} = useExpenseCaseTimeline({
claimId: toRef(props, 'claimId'),
refreshKey: toRef(props, 'refreshKey')
})
const displayedEvents = computed(() => orderExpenseCaseEventsForDisplay(timeline.value?.events))
</script>
<style scoped src="../../assets/styles/components/travel-request-expense-case-timeline.css"></style>