feat(workbench): 扩展签署任务管理功能

This commit is contained in:
yelan
2026-08-28 10:02:51 +08:00
parent b533ab2a58
commit 2de75ed993
14 changed files with 4287 additions and 37 deletions
@@ -0,0 +1,227 @@
<script setup lang="ts">
import { ref, watch } from 'vue'
import type { SigningTaskRecord } from '@/api/workbench/types'
const props = defineProps<{
visible: boolean
task: SigningTaskRecord | null
}>()
const emit = defineEmits<{
close: []
confirm: [reason: string]
}>()
const reason = ref('患者信息录入有误')
watch(
() => props.visible,
(visible) => {
if (visible) {
reason.value = '患者信息录入有误'
}
},
)
</script>
<template>
<Teleport to="body">
<div v-if="visible && task" class="dialog-mask" @click.self="emit('close')">
<section
class="dialog-card void-dialog"
role="dialog"
aria-modal="true"
aria-labelledby="void-title"
>
<header class="dialog-header">
<strong id="void-title">作废确认</strong>
<button type="button" class="dialog-close" aria-label="关闭" @click="emit('close')">
×
</button>
</header>
<div class="dialog-body">
<div class="void-summary">
<span class="void-icon" aria-hidden="true">⚠</span>
<div>
<p>
确定作废任务 <strong>{{ task.id }}</strong
>({{ task.documentName }})吗?
</p>
<span>作废后不可恢复,将记录操作人、时间与原因,全程留痕。</span>
</div>
</div>
<label class="reason-field">
<span>作废原因(必填)</span>
<select v-model="reason" aria-label="作废原因">
<option>患者信息录入有误</option>
<option>文档选择错误</option>
<option>患者拒绝签署</option>
<option>重复发起</option>
<option>其他</option>
</select>
</label>
</div>
<footer class="dialog-footer">
<span class="footer-spacer" />
<button type="button" class="action-button action-button--ghost" @click="emit('close')">
取消
</button>
<button
type="button"
class="action-button action-button--danger"
@click="emit('confirm', reason)"
>
确认作废
</button>
</footer>
</section>
</div>
</Teleport>
</template>
<style scoped>
.dialog-mask {
position: fixed;
z-index: 50;
inset: 0;
display: flex;
padding: 16px;
overflow-y: auto;
background: rgb(9 40 52 / 45%);
}
.dialog-card {
width: 440px;
max-width: 96vw;
margin: auto;
background: #fff;
border-radius: 14px;
box-shadow: var(--sh-modal);
animation: dialog-pop 0.18s ease;
}
.dialog-header {
display: flex;
align-items: center;
padding: 12px 18px;
border-bottom: 1px solid var(--line);
}
.dialog-header strong {
font-size: 15px;
}
.dialog-close {
padding: 0;
margin-left: auto;
color: var(--mut);
font-size: 20px;
line-height: 1;
background: transparent;
}
.dialog-body {
padding: 16px 18px 12px;
}
.void-summary {
display: flex;
gap: 12px;
align-items: flex-start;
}
.void-icon {
font-size: 30px;
line-height: 1;
}
.void-summary p {
margin: 0;
color: var(--ink);
line-height: 1.6;
}
.void-summary > div > span {
display: block;
margin-top: 6px;
color: var(--mut);
font-size: 12px;
line-height: 1.6;
}
.reason-field {
display: flex;
gap: 8px;
flex-direction: column;
margin-top: 14px;
}
.reason-field span {
color: var(--mut);
font-size: 12px;
}
.reason-field select {
width: 100%;
padding: 7px 10px;
color: var(--ink);
border: 1px solid var(--line);
border-radius: 6px;
}
.dialog-footer {
display: flex;
gap: 10px;
align-items: center;
padding: 10px 18px;
background: #f8fbfc;
border-top: 1px solid var(--line);
border-radius: 0 0 14px 14px;
}
.footer-spacer {
flex: 1;
}
.action-button {
padding: 8px 16px;
color: #fff;
font-size: 13px;
font-weight: 700;
background: var(--brand);
border-radius: 7px;
}
.action-button--ghost {
color: var(--brand);
background: #fff;
border: 1px solid var(--brand);
}
.action-button--ghost:hover {
background: var(--brand-l);
}
.action-button--danger {
color: var(--err);
background: #fff;
border: 1px solid var(--err);
}
.action-button--danger:hover {
background: var(--err-l);
}
@keyframes dialog-pop {
from {
opacity: 0;
transform: scale(0.96) translateY(8px);
}
to {
opacity: 1;
transform: none;
}
}
</style>