feat: 初始化医护端与患者端页面骨架

完成两个 Vite 应用的基础工程化配置、路由、状态管理及第一版业务页面,当前使用演示数据,尚未接入真实后端接口。
This commit is contained in:
yelan
2026-08-27 11:27:16 +08:00
parent 01827bf304
commit 36fa15487c
28 changed files with 2089 additions and 37 deletions
@@ -0,0 +1,139 @@
<script setup lang="ts">
import { ref } from 'vue'
import type { ConsentTask, ConsentTaskStatus } from '@/types/consent'
const statusLabel: Record<ConsentTaskStatus, string> = {
pending: '待签署',
signed: '已完成',
rejected: '已拒签',
expired: '已过期',
}
const statusType: Record<ConsentTaskStatus, 'success' | 'warning' | 'danger' | 'info'> = {
pending: 'warning',
signed: 'success',
rejected: 'danger',
expired: 'info',
}
const tasks: ConsentTask[] = [
{
id: 'CT-20260827-001',
patientName: '张*',
visitNo: 'MZ20260827018',
documentName: '儿科急诊特殊检查知情同意书',
source: '儿科急诊 · 门诊',
status: 'pending',
updatedAt: '刚刚',
},
{
id: 'CT-20260827-002',
patientName: '李*',
visitNo: 'MZ20260827012',
documentName: '消化内镜检查知情同意书',
source: '消化内科 · 门诊',
status: 'signed',
updatedAt: '8 分钟前',
},
{
id: 'CT-20260827-003',
patientName: '王*',
visitNo: 'ZY20260826009',
documentName: '麻醉知情同意书',
source: '消化内科 · 住院',
status: 'pending',
updatedAt: '21 分钟前',
},
{
id: 'CT-20260827-004',
patientName: '赵*',
visitNo: 'MZ20260827003',
documentName: '气管插管知情同意书',
source: '儿科急诊 · 门诊',
status: 'rejected',
updatedAt: '今天 09:42',
},
]
const selectedTask = ref<ConsentTask | null>(null)
const isDetailVisible = ref(false)
function getStatusLabel(status: string) {
return statusLabel[status as ConsentTaskStatus] ?? '未知状态'
}
function getStatusType(status: string) {
return statusType[status as ConsentTaskStatus] ?? 'info'
}
function openTask(task: ConsentTask) {
selectedTask.value = task
isDetailVisible.value = true
}
</script>
<template>
<section class="page-section">
<div class="page-heading">
<div>
<p class="eyebrow">任务中心</p>
<h2>签署任务</h2>
<p>按患者、就诊和文书任务查看当前签署进度。</p>
</div>
<button class="primary-button" type="button">新建签署任务</button>
</div>
<el-card shadow="never" class="content-card table-card">
<div class="filter-bar">
<el-input placeholder="搜索患者、门诊号或任务编号" clearable />
<el-select placeholder="全部状态" clearable>
<el-option label="待签署" value="pending" />
<el-option label="已完成" value="signed" />
<el-option label="已拒签" value="rejected" />
<el-option label="已过期" value="expired" />
</el-select>
<el-button>筛选</el-button>
</div>
<el-table :data="tasks" stripe>
<el-table-column prop="id" label="任务编号" min-width="160" />
<el-table-column prop="patientName" label="患者" width="90" />
<el-table-column prop="visitNo" label="就诊号" min-width="150" />
<el-table-column prop="documentName" label="文书" min-width="270" />
<el-table-column prop="source" label="来源" min-width="160" />
<el-table-column label="状态" width="110">
<template #default="{ row }">
<el-tag :type="getStatusType(row.status)" effect="plain">
{{ getStatusLabel(row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="updatedAt" label="更新时间" width="120" />
<el-table-column label="操作" width="90" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="openTask(row)">查看</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog v-model="isDetailVisible" title="签署任务详情" width="520px">
<template v-if="selectedTask">
<div class="detail-grid">
<span>任务编号</span><strong>{{ selectedTask.id }}</strong> <span>患者</span
><strong>{{ selectedTask.patientName }}</strong> <span>就诊号</span
><strong>{{ selectedTask.visitNo }}</strong> <span>文书</span
><strong>{{ selectedTask.documentName }}</strong>
<span>当前状态</span>
<el-tag :type="statusType[selectedTask.status]">
{{ statusLabel[selectedTask.status] }}
</el-tag>
</div>
</template>
<template #footer>
<el-button @click="isDetailVisible = false">关闭</el-button>
</template>
</el-dialog>
</section>
</template>
@@ -0,0 +1,123 @@
<script setup lang="ts">
import { useRouter } from 'vue-router'
const router = useRouter()
const stats = [
{ label: '待签署任务', value: '12', hint: '较昨日 +3', tone: 'orange' },
{ label: '今日已完成', value: '86', hint: '完成率 91%', tone: 'green' },
{ label: '待回传记录', value: '2', hint: '需要关注', tone: 'red' },
{ label: '本月签署量', value: '2,486', hint: '较上月 +12%', tone: 'blue' },
]
const recentTasks = [
{ patient: '张*', document: '儿科急诊特殊检查知情同意书', status: '待签署', time: '刚刚' },
{ patient: '李*', document: '消化内镜检查知情同意书', status: '已完成', time: '8 分钟前' },
{ patient: '王*', document: '麻醉知情同意书', status: '待回传', time: '21 分钟前' },
]
function goToTasks() {
void router.push('/consent-tasks')
}
</script>
<template>
<section class="page-section">
<div class="welcome-panel">
<div>
<p class="eyebrow">今日概览</p>
<h2>把每一次知情同意,准确关联到本次就诊。</h2>
<p class="welcome-copy">
从电子病历发起签署任务,实时查看患者签署状态,并为后续回传和审计保留证据。
</p>
</div>
<button class="primary-button" type="button" @click="goToTasks">查看签署任务</button>
</div>
<el-row :gutter="16" class="stats-row">
<el-col v-for="stat in stats" :key="stat.label" :xs="24" :sm="12" :lg="6">
<el-card shadow="never" class="stat-card">
<div class="stat-topline">
<span>{{ stat.label }}</span>
<span class="stat-indicator" :class="'is-' + stat.tone" />
</div>
<strong>{{ stat.value }}</strong>
<small>{{ stat.hint }}</small>
</el-card>
</el-col>
</el-row>
<el-row :gutter="16" class="dashboard-grid">
<el-col :xs="24" :lg="15">
<el-card shadow="never" class="content-card">
<template #header>
<div class="card-heading">
<div>
<p class="eyebrow">实时动态</p>
<h3>最近签署任务</h3>
</div>
<button class="text-button" type="button" @click="goToTasks">查看全部</button>
</div>
</template>
<div class="task-list">
<div v-for="task in recentTasks" :key="task.patient + task.time" class="task-item">
<div class="task-avatar">{{ task.patient.slice(0, 1) }}</div>
<div class="task-main">
<strong>{{ task.document }}</strong>
<span>{{ task.patient }} · {{ task.time }}</span>
</div>
<el-tag
:type="
task.status === '已完成'
? 'success'
: task.status === '待回传'
? 'warning'
: 'info'
"
effect="plain"
>
{{ task.status }}
</el-tag>
</div>
</div>
</el-card>
</el-col>
<el-col :xs="24" :lg="9">
<el-card shadow="never" class="content-card">
<template #header>
<div class="card-heading">
<div>
<p class="eyebrow">下一步</p>
<h3>今日提醒</h3>
</div>
</div>
</template>
<ul class="reminder-list">
<li>
<span class="reminder-number">01</span>
<div>
<strong>2 条签署结果待回传</strong>
<span>请确认电子病历历史记录是否更新。</span>
</div>
</li>
<li>
<span class="reminder-number">02</span>
<div>
<strong>签署一项文书版本</strong>
<span>消化内镜文书有一个待审核版本。</span>
</div>
</li>
<li>
<span class="reminder-number">03</span>
<div>
<strong>设备状态正常</strong>
<span>护士站签字板最近一次心跳在 1 分钟前。</span>
</div>
</li>
</ul>
</el-card>
</el-col>
</el-row>
</section>
</template>
@@ -0,0 +1,60 @@
<script setup lang="ts">
const documents = [
{
name: '儿科急诊特殊检查知情同意书',
department: '儿科急诊',
version: 'V1.2',
status: '使用中',
updatedAt: '2026-08-26',
},
{
name: '消化内镜检查知情同意书',
department: '消化内科',
version: 'V2.0',
status: '使用中',
updatedAt: '2026-08-25',
},
{
name: '息肉切除术知情同意书',
department: '消化内科',
version: 'V1.0',
status: '待审核',
updatedAt: '2026-08-24',
},
]
</script>
<template>
<section class="page-section">
<div class="page-heading">
<div>
<p class="eyebrow">模板中心</p>
<h2>文书库</h2>
<p>维护知情同意文书、版本和适用科室。</p>
</div>
<button class="primary-button" type="button">新建文书</button>
</div>
<el-card shadow="never" class="content-card table-card">
<el-table :data="documents" stripe>
<el-table-column prop="name" label="文书名称" min-width="300" />
<el-table-column prop="department" label="适用科室" width="140" />
<el-table-column prop="version" label="版本" width="100" />
<el-table-column label="状态" width="120">
<template #default="{ row }">
<el-tag :type="row.status === '使用中' ? 'success' : 'warning'" effect="plain">
{{ row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="updatedAt" label="更新时间" width="140" />
<el-table-column label="操作" width="160">
<template #default>
<el-button link type="primary">预览</el-button>
<el-button link>版本记录</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</section>
</template>
@@ -0,0 +1,38 @@
<script setup lang="ts">
const reportCards = [
{ label: '本月签署总量', value: '2,486', note: '覆盖 18 个科室' },
{ label: '手机签署占比', value: '78.6%', note: '试运行统计' },
{ label: '平均签署耗时', value: '2m 18s', note: '从发起到完成' },
]
</script>
<template>
<section class="page-section">
<div class="page-heading">
<div>
<p class="eyebrow">数据观察</p>
<h2>统计报表</h2>
<p>查看签署量、渠道、科室和异常任务统计。</p>
</div>
<button class="secondary-button" type="button">导出报表</button>
</div>
<el-row :gutter="16" class="stats-row">
<el-col v-for="card in reportCards" :key="card.label" :xs="24" :sm="8">
<el-card shadow="never" class="stat-card report-card">
<span>{{ card.label }}</span>
<strong>{{ card.value }}</strong>
<small>{{ card.note }}</small>
</el-card>
</el-col>
</el-row>
<el-card shadow="never" class="content-card report-placeholder">
<div class="empty-chart">
<div class="chart-icon">↗</div>
<h3>趋势图表将在接入真实数据后展示</h3>
<p>当前页面用于确认报表入口和信息层级,暂使用演示数据。</p>
</div>
</el-card>
</section>
</template>
@@ -0,0 +1,45 @@
<script setup lang="ts">
import { reactive } from 'vue'
const settings = reactive({
enableSms: true,
enableAutoReturn: true,
enableAuditReminder: true,
})
</script>
<template>
<section class="page-section">
<div class="page-heading">
<div>
<p class="eyebrow">平台配置</p>
<h2>系统设置</h2>
<p>管理签署渠道、回传策略和审计提醒。</p>
</div>
</div>
<el-card shadow="never" class="content-card settings-card">
<div class="setting-row">
<div>
<strong>启用手机短信签署</strong>
<span>允许患者通过短信链接打开签署页面。</span>
</div>
<el-switch v-model="settings.enableSms" />
</div>
<div class="setting-row">
<div>
<strong>签署完成后自动回传</strong>
<span>签署成功后,将状态和文件回传电子病历。</span>
</div>
<el-switch v-model="settings.enableAutoReturn" />
</div>
<div class="setting-row">
<div>
<strong>启用异常审计提醒</strong>
<span>对拒签、过期和回传失败任务生成提醒。</span>
</div>
<el-switch v-model="settings.enableAuditReminder" />
</div>
</el-card>
</section>
</template>