refactor(ui): introduce shared list detail shells

This commit is contained in:
caoxiaozhu
2026-05-28 22:49:58 +08:00
parent b383244a29
commit 064eeb614f
17 changed files with 1163 additions and 1095 deletions

View File

@@ -1,96 +1,81 @@
<template>
<section class="travel-page">
<article class="travel-list panel">
<nav class="status-tabs" aria-label="个人报销状态">
<button
v-for="tab in tabs"
:key="tab"
type="button"
:class="{ active: activeTab === tab }"
@click="activeTab = tab"
>
{{ tab }}
</button>
</nav>
<div class="list-toolbar">
<div class="filter-set">
<div class="list-search">
<i class="mdi mdi-magnify"></i>
<input v-model="listKeyword" type="search" placeholder="搜索单号、事由、报销类型..." />
</div>
<div class="date-range-filter" :class="{ open: datePopover }">
<button class="filter-btn date-range-trigger" type="button" @click="datePopover = !datePopover">
<span class="date-range-label">{{ dateRangeLabel }}</span>
<i class="mdi mdi-calendar"></i>
</button>
<div v-if="datePopover" class="date-range-popover" role="dialog" aria-label="选择时间段">
<header>
<strong>选择时间段</strong>
<button type="button" aria-label="关闭" @click="datePopover = false"><i class="mdi mdi-close"></i></button>
</header>
<div class="date-range-fields">
<label>
<span>开始日期</span>
<input v-model="rangeStart" type="date" />
</label>
<label>
<span>结束日期</span>
<input v-model="rangeEnd" type="date" />
</label>
</div>
<footer>
<button class="ghost-btn" type="button" @click="datePopover = false">取消</button>
<button class="apply-btn" type="button" :disabled="!rangeStart || !rangeEnd" @click="applyDateRange">应用</button>
</footer>
</div>
</div>
<button v-for="filter in filters" :key="filter" type="button" class="filter-btn">
<span>{{ filter }}</span>
<i class="mdi mdi-chevron-down"></i>
</button>
<EnterpriseListPage
v-model:active-tab="activeTab"
v-model:current-page="currentPage"
v-model:page-size="pageSize"
class="travel-list"
:tabs="tabs"
tabs-label="个人报销状态"
:loading="loading"
:error="error"
:empty="showEmpty"
:empty-state="emptyState"
:total="totalCount"
:total-pages="totalPages"
:page-size-options="pageSizeOptions"
:show-pagination="showTable"
loading-title="真实报销数据同步中"
loading-message="正在加载后端返回的个人报销单据"
loading-icon="mdi mdi-file-document-outline"
error-title="报销列表加载失败"
@retry="emit('reload')"
@empty-action="handleEmptyAction"
@page-size-change="changePageSize"
>
<template #filters>
<div class="list-search">
<i class="mdi mdi-magnify"></i>
<input v-model="listKeyword" type="search" placeholder="搜索单号、事由、报销类型..." />
</div>
<div class="date-range-filter" :class="{ open: datePopover }">
<button class="filter-btn date-range-trigger" type="button" @click="datePopover = !datePopover">
<span class="date-range-label">{{ dateRangeLabel }}</span>
<i class="mdi mdi-calendar"></i>
</button>
<div v-if="datePopover" class="date-range-popover" role="dialog" aria-label="选择时间段">
<header>
<strong>选择时间段</strong>
<button type="button" aria-label="关闭" @click="datePopover = false"><i class="mdi mdi-close"></i></button>
</header>
<div class="date-range-fields">
<label>
<span>开始日期</span>
<input v-model="rangeStart" type="date" />
</label>
<label>
<span>结束日期</span>
<input v-model="rangeEnd" type="date" />
</label>
</div>
<footer>
<button class="ghost-btn" type="button" @click="datePopover = false">取消</button>
<button class="apply-btn" type="button" :disabled="!rangeStart || !rangeEnd" @click="applyDateRange">应用</button>
</footer>
</div>
</div>
<button v-for="filter in filters" :key="filter" type="button" class="filter-btn">
<span>{{ filter }}</span>
<i class="mdi mdi-chevron-down"></i>
</button>
</template>
<template #actions>
<button class="create-request-btn" type="button" @click="emit('create-request')">
<i class="mdi mdi-plus-circle-outline"></i>
<span>发起报销</span>
</button>
</div>
</template>
<p class="hint"><i class="mdi mdi-information-outline"></i> 点击任意行可查看单据详情</p>
<template #hint>
<i class="mdi mdi-information-outline"></i>
点击任意行可查看单据详情
</template>
<div class="table-wrap" :class="{ 'is-empty': showEmpty }">
<div v-if="loading" class="table-state">
<TableLoadingState
title="真实报销数据同步中"
message="正在加载后端返回的个人报销单据"
icon="mdi mdi-file-document-outline"
/>
</div>
<div v-else-if="error" class="table-state error">
<i class="mdi mdi-alert-circle-outline"></i>
<strong>报销列表加载失败</strong>
<p>{{ error }}</p>
<button class="retry-btn" type="button" @click="emit('reload')">重新加载</button>
</div>
<TableEmptyState
v-else-if="showEmpty"
:eyebrow="emptyState.eyebrow"
:title="emptyState.title"
:description="emptyState.desc"
:icon="emptyState.icon"
:action-label="emptyState.actionLabel"
:action-icon="emptyState.actionIcon"
:tone="emptyState.tone"
:art-label="emptyState.artLabel"
:tips="emptyState.tips"
@action="handleEmptyAction"
/>
<table v-else>
<template #table>
<table>
<colgroup>
<col class="col-id">
<col class="col-type">
@@ -126,24 +111,8 @@
</tr>
</tbody>
</table>
</div>
<footer v-if="showTable" class="list-foot">
<span class="page-summary"> {{ totalCount }} 目前第 {{ currentPage }} </span>
<div class="pager" aria-label="分页">
<button class="page-nav" type="button" :disabled="currentPage === 1" aria-label="上一页" @click="currentPage--"><i class="mdi mdi-chevron-left"></i></button>
<button v-for="p in totalPages" :key="p" class="page-number" :class="{ active: currentPage === p }" type="button" :aria-current="currentPage === p ? 'page' : undefined" @click="currentPage = p">{{ p }}</button>
<button class="page-nav" type="button" :disabled="currentPage === totalPages" aria-label="下一页" @click="currentPage++"><i class="mdi mdi-chevron-right"></i></button>
</div>
<EnterpriseSelect
v-model="pageSize"
class="page-size-select"
:options="pageSizeOptions"
size="small"
@change="changePageSize"
/>
</footer>
</article>
</template>
</EnterpriseListPage>
</section>
</template>