feat: 提交

This commit is contained in:
cnxianyi
2026-09-29 18:49:45 +08:00
parent 8d8fa2af47
commit 09bcf20def
17 changed files with 357 additions and 62 deletions
@@ -123,7 +123,7 @@ async function loadOverview() {
function goToSigning(query: Record<string, string> = {}) {
void router.push({
name: 'workbench-signing',
path: '/sign/workbench',
query: {
campus: selectedCampus.value,
...query,
@@ -133,7 +133,7 @@ function goToSigning(query: Record<string, string> = {}) {
function handleMetricClick(key: HomeMetricKey) {
if (key === 'templates') {
void router.push({ name: 'management-documents' })
void router.push('/doc/library')
return
}
@@ -205,7 +205,7 @@ onMounted(() => {
:rows="rankingRows"
:period="rankingPeriod"
@update:period="handlePeriodChange"
@manage="router.push({ name: 'management-documents' })"
@manage="router.push('/doc/library')"
/>
</template>
</section>
@@ -125,6 +125,9 @@ function value(value: string | undefined) {
<div class="signature-box">
<img v-if="record.signatureDataUrl" :src="record.signatureDataUrl" alt="签署人签名" />
<span v-else-if="record.status === 'void'">任务已作废,签名不保留</span>
<span v-else-if="record.status === 'signed' || record.status === 'confirming'">
签名图片暂不可显示
</span>
<span v-else>未采集签名</span>
</div>
<dl>
@@ -3,7 +3,11 @@ import { computed, onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { getCampuses, getDepartments } from '@/api/management/organization'
import { downloadSigningArtifact, getSigningArtifacts } from '@/api/workbench/artifacts'
import {
downloadSigningArtifact,
fetchSignatureDataUrl,
getSigningArtifacts,
} from '@/api/workbench/artifacts'
import { mockCatalogCategories, mockCatalogDepartments } from '@/api/workbench/mock-catalog'
import {
getAllSigningRecords,
@@ -120,7 +124,8 @@ const canView = computed(() => canRead('sign:record:view'))
const canExport = computed(
() => !loginStore.isReadOnly && canRead('sign:record:export'),
)
const canDownloadArtifact = computed(() => canRead('sign:artifact:download'))
const canViewArtifacts = computed(() => canRead('sign:artifact:view'))
const canDownloadArtifact = computed(() => canRead('sign:task:download'))
const campusOptions = computed<SigningRecordFilterOption<WorkbenchCampus | 'all'>[]>(() => [
{ label: '全部院区', value: 'all' },
@@ -276,9 +281,13 @@ async function openRecord(row: SigningRecordRow) {
const requestId = ++detailRequestId
const [detailResult, eventsResult] = await Promise.allSettled([
const isSigned = row.status === 'signed' || row.status === 'confirming'
const [detailResult, eventsResult, artifactsResult] = await Promise.allSettled([
getSigningRecordDetail(row.id, signingApiOptions.value),
getSigningRecordEvents(row.id),
isSigned && canViewArtifacts.value && canDownloadArtifact.value
? getSigningArtifacts(row.id)
: Promise.resolve([]),
])
if (requestId !== detailRequestId) {
@@ -288,11 +297,37 @@ async function openRecord(row: SigningRecordRow) {
if (detailResult.status === 'fulfilled' && detailResult.value) {
// 任务接口不返回文档类别与正文,正文靠详情接口补回;
// 类别沿用列表里带出的那一份,避免被详情里的空值覆盖
detailRecord.value = {
let record = {
...row,
...detailResult.value,
category: detailResult.value.category || row.category,
}
// 签署图片是独立的 SIGNATURE_IMAGE 产物,不在任务详情接口中。
// 记录详情也要像签署工作台一样取回图片,否则已签任务的文书位会误显示“未签署”。
if (isSigned && !record.signatureDataUrl && artifactsResult.status === 'fulfilled') {
const signatureArtifact = artifactsResult.value.find(
(artifact) => artifact.artifactType === 'SIGNATURE_IMAGE',
)
if (signatureArtifact) {
const signatureDataUrl = await fetchSignatureDataUrl(signatureArtifact)
if (requestId !== detailRequestId) {
return
}
if (signatureDataUrl) {
record = {
...record,
signatureDataUrl,
fieldAnswers: { ...(record.fieldAnswers ?? {}), signature: signatureDataUrl },
}
}
}
}
detailRecord.value = record
}
if (eventsResult.status === 'fulfilled') {
@@ -270,6 +270,28 @@ defineExpose({ getDocumentElement, locateSignatureSlot })
</button>
</template>
<template v-else-if="task.status === 'failed' && task.method === 'pad'">
<button
v-if="allowLocalSigning || allowMouseSigning"
type="button"
class="action-button"
:disabled="padSessionLoading"
@click="emit('action', 'pad-sign')"
>
{{ padSessionLoading ? '正在创建签署会话…' : '↻ 重新签署' }}
</button>
<button
v-else-if="canSignTask"
type="button"
class="action-button"
:disabled="padSessionLoading"
@click="emit('action', 'create-pad-session')"
>
{{ padSessionLoading ? '正在创建签署会话…' : '↻ 重新创建签署会话' }}
</button>
<span class="action-hint">上次生成失败;重新签署会创建新的签署会话。</span>
</template>
<!-- 待科室确认:患者已签完,此刻要动的是科室那一道章(原型 confirmDept 的动作组)。
预览与下载放在这里,是因为科室复核时要先看患者签了什么 -->
<template v-else-if="task.status === 'confirming'">
@@ -163,8 +163,10 @@ const canConfirmTask = computed(() => canWrite('sign:task:confirm'))
/** 催办:提醒科室签署员尽快复核 */
const canRemindTask = computed(() => canWrite('sign:task:remind'))
const canViewEvents = computed(() => canRead('sign:task:event:query'))
const canViewArtifacts = computed(() => canRead('sign:artifact:query'))
const canDownloadArtifacts = computed(() => canRead('sign:artifact:download'))
// Keep these permission codes aligned with SignArtifactController: artifact:view
// lists the index, while task:download authorizes the PNG/PDF download.
const canViewArtifacts = computed(() => canRead('sign:artifact:view'))
const canDownloadArtifacts = computed(() => canRead('sign:task:download'))
/** 需要执行签署权限的任务动作,对应后端 `sign:task:sign`。 */
const SIGNING_ACTIONS: SigningTaskAction[] = [