docs(reports): 更新报表接口接入文档与实现说明,报表分析接口接入
This commit is contained in:
@@ -3,15 +3,26 @@ import type { ReportTask, ReportTaskStatus } from '../types'
|
||||
|
||||
defineProps<{
|
||||
rows: ReportTask[]
|
||||
page: number
|
||||
pageSize: number
|
||||
total: number
|
||||
pages: number
|
||||
loading: boolean
|
||||
error: string
|
||||
pageSizeOptions: number[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
view: [row: ReportTask]
|
||||
'page-change': [page: number]
|
||||
'page-size-change': [size: number]
|
||||
retry: []
|
||||
}>()
|
||||
|
||||
const statusLabels: Record<ReportTaskStatus, string> = {
|
||||
signed: '已签署',
|
||||
pending: '待签署',
|
||||
signing: '签署中',
|
||||
expired: '已超时',
|
||||
void: '已作废',
|
||||
failed: '处理失败',
|
||||
@@ -29,52 +40,146 @@ function getStatusClass(status: ReportTaskStatus) {
|
||||
return `status-${status}`
|
||||
}
|
||||
|
||||
function formatTime(date: Date) {
|
||||
function formatTime(date: Date | undefined) {
|
||||
if (!date) {
|
||||
return '—'
|
||||
}
|
||||
|
||||
const pad = (value: number) => String(value).padStart(2, '0')
|
||||
return `${date.getMonth() + 1}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`
|
||||
}
|
||||
|
||||
function formatDuration(row: ReportTask) {
|
||||
const seconds =
|
||||
row.durationSeconds !== undefined
|
||||
? row.durationSeconds
|
||||
: row.durationMinutes !== undefined
|
||||
? row.durationMinutes * 60
|
||||
: undefined
|
||||
|
||||
if (seconds === undefined || !Number.isFinite(seconds)) {
|
||||
return '—'
|
||||
}
|
||||
|
||||
const roundedSeconds = Math.round(seconds)
|
||||
const minutes = Math.floor(roundedSeconds / 60)
|
||||
const restSeconds = roundedSeconds % 60
|
||||
|
||||
if (!minutes) {
|
||||
return String(restSeconds) + '秒'
|
||||
}
|
||||
|
||||
return restSeconds
|
||||
? String(minutes) + '分' + String(restSeconds) + '秒'
|
||||
: String(minutes) + '分钟'
|
||||
}
|
||||
|
||||
function getPageCount(pages: number) {
|
||||
return Math.max(pages, 1)
|
||||
}
|
||||
|
||||
function handlePreviousPage(page: number) {
|
||||
if (page > 1) {
|
||||
emit('page-change', page - 1)
|
||||
}
|
||||
}
|
||||
|
||||
function handleNextPage(page: number, pages: number) {
|
||||
if (page < getPageCount(pages)) {
|
||||
emit('page-change', page + 1)
|
||||
}
|
||||
}
|
||||
|
||||
function handlePageSizeChange(event: Event) {
|
||||
const value = Number((event.target as HTMLSelectElement).value)
|
||||
|
||||
if (Number.isFinite(value)) {
|
||||
emit('page-size-change', value)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="table-scroll">
|
||||
<table class="detail-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>任务号</th>
|
||||
<th>患者</th>
|
||||
<th>文档</th>
|
||||
<th>科室</th>
|
||||
<th>方式</th>
|
||||
<th>状态</th>
|
||||
<th>发起时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in rows" :key="row.id">
|
||||
<td>{{ row.id }}</td>
|
||||
<td>
|
||||
<strong>{{ row.patientName }}</strong>
|
||||
<span class="patient-id">{{ row.patientId }}</span>
|
||||
</td>
|
||||
<td class="document-name" :title="row.documentName">{{ row.documentName }}</td>
|
||||
<td>{{ row.department }}</td>
|
||||
<td>{{ row.method === 'pad' ? '✍️ 手写板' : '📱 短信' }}</td>
|
||||
<td>
|
||||
<span class="status-tag" :class="getStatusClass(row.status)">
|
||||
{{ getStatusLabel(row) }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ formatTime(row.createdAt) }}</td>
|
||||
<td>
|
||||
<button type="button" class="view-button" @click="emit('view', row)">查看</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="!rows.length">
|
||||
<td colspan="8" class="empty-cell">暂无符合条件的签署明细</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div v-if="error" class="table-error" role="alert">
|
||||
<span>{{ error }}</span>
|
||||
<button type="button" @click="emit('retry')">重试</button>
|
||||
</div>
|
||||
|
||||
<div v-else class="table-container" :class="{ 'is-loading': loading }" :aria-busy="loading">
|
||||
<div class="table-scroll">
|
||||
<table class="detail-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>任务号</th>
|
||||
<th>患者</th>
|
||||
<th>文档</th>
|
||||
<th>科室</th>
|
||||
<th>方式</th>
|
||||
<th>状态</th>
|
||||
<th>发起时间</th>
|
||||
<th>签署完成</th>
|
||||
<th>耗时</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in rows" :key="row.id">
|
||||
<td>{{ row.id }}</td>
|
||||
<td>
|
||||
<strong>{{ row.patientName }}</strong>
|
||||
<span class="patient-id">{{ row.patientId }}</span>
|
||||
</td>
|
||||
<td class="document-name" :title="row.documentName">{{ row.documentName }}</td>
|
||||
<td>{{ row.department }}</td>
|
||||
<td>{{ row.method === 'pad' ? '✍️ 手写板' : '📱 短信' }}</td>
|
||||
<td>
|
||||
<span class="status-tag" :class="getStatusClass(row.status)">
|
||||
{{ getStatusLabel(row) }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ formatTime(row.createdAt) }}</td>
|
||||
<td>{{ formatTime(row.signedAt) }}</td>
|
||||
<td>{{ formatDuration(row) }}</td>
|
||||
<td>
|
||||
<button type="button" class="view-button" @click="emit('view', row)">查看</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="!rows.length">
|
||||
<td colspan="10" class="empty-cell">暂无符合条件的签署明细</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div v-if="total" class="table-footer">
|
||||
<span>共 {{ total }} 条</span>
|
||||
<label>
|
||||
每页
|
||||
<select :value="pageSize" aria-label="选择每页条数" @change="handlePageSizeChange">
|
||||
<option v-for="option in pageSizeOptions" :key="option" :value="option">
|
||||
{{ option }}
|
||||
</option>
|
||||
</select>
|
||||
条
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
class="page-button"
|
||||
:disabled="page <= 1 || loading"
|
||||
@click="handlePreviousPage(page)"
|
||||
>
|
||||
上一页
|
||||
</button>
|
||||
<span>第 {{ page }} / {{ getPageCount(pages) }} 页</span>
|
||||
<button
|
||||
type="button"
|
||||
class="page-button"
|
||||
:disabled="page >= getPageCount(pages) || loading"
|
||||
@click="handleNextPage(page, pages)"
|
||||
>
|
||||
下一页
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -84,6 +189,16 @@ function formatTime(date: Date) {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.table-container.is-loading {
|
||||
min-height: 120px;
|
||||
pointer-events: none;
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.detail-table {
|
||||
width: 100%;
|
||||
min-width: 1000px;
|
||||
@@ -145,6 +260,11 @@ function formatTime(date: Date) {
|
||||
background: var(--brand-l);
|
||||
}
|
||||
|
||||
.status-signing {
|
||||
color: var(--info);
|
||||
background: var(--info-l);
|
||||
}
|
||||
|
||||
.status-expired {
|
||||
color: var(--err);
|
||||
background: var(--err-l);
|
||||
@@ -178,4 +298,53 @@ function formatTime(date: Date) {
|
||||
color: var(--mut);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table-error {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 90px;
|
||||
color: var(--err);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.table-error button,
|
||||
.page-button {
|
||||
padding: 5px 10px;
|
||||
color: var(--brand);
|
||||
font-size: 12px;
|
||||
background: #fff;
|
||||
border: 1px solid var(--brand);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.table-footer {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-top: 12px;
|
||||
color: var(--mut);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.table-footer label {
|
||||
display: inline-flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.table-footer select {
|
||||
padding: 3px 5px;
|
||||
color: var(--ink);
|
||||
background: #fff;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.page-button:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.45;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user