feat(workbench): 接入签署投递与产物管理

This commit is contained in:
yelan
2026-09-01 10:12:22 +08:00
parent 2e2d2250dd
commit beeb98d18f
6 changed files with 434 additions and 9 deletions
@@ -3,6 +3,9 @@ import { computed, onMounted, reactive, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { useRoute } from 'vue-router'
import { getCampuses, getDepartments } from '@/api/management/organization'
import { downloadSigningArtifact, getSigningArtifacts } from '@/api/workbench/artifacts'
import { createPadSigningSession } from '@/api/workbench/deliveries'
import {
completeSigningTask,
getSigningTaskDetail,
@@ -18,6 +21,8 @@ import {
import type {
SigningDateRange,
SigningMethod,
SigningArtifact,
SignDeliveryResponseDto,
SigningTaskEvent,
SigningTaskRecord,
SigningTaskStatus,
@@ -76,11 +81,19 @@ const total = ref(0)
const selectedTaskId = ref<string | null>(null)
const selectedTask = ref<SigningTaskRecord | null>(null)
const taskEvents = ref<SigningTaskEvent[]>([])
const artifacts = ref<SigningArtifact[]>([])
const templates = ref<SigningTemplate[]>([])
const loading = ref(true)
const detailLoading = ref(false)
const eventsLoading = ref(false)
const eventsError = ref(false)
const artifactsLoading = ref(false)
const artifactsError = ref(false)
const padSessionStatus = ref<string | null>(null)
const padSessionLoading = ref(false)
const campusIds = ref<Record<string, string>>({})
const campusNames = ref<Record<string, WorkbenchCampus>>({})
const departmentIds = ref<Record<string, string>>({})
const newDialogVisible = ref(false)
const signatureDialogVisible = ref(false)
const onlineDialogVisible = ref(false)
@@ -131,6 +144,9 @@ const documentOptions = computed<SigningFilterOption[]>(() => [
const signingApiOptions = computed(() => ({
campus: appStore.selectedCampus,
campusId: campusIds.value[appStore.selectedCampus],
campusNames: campusNames.value,
departmentIds: departmentIds.value,
templates: templates.value,
}))
@@ -162,27 +178,67 @@ async function loadTemplates() {
}
}
async function loadOrganizationOptions() {
if (isSigningMockEnabled) {
return
}
const [campusesResult, departmentsResult] = await Promise.allSettled([
getCampuses(),
getDepartments(),
])
if (campusesResult.status === 'fulfilled') {
campusIds.value = Object.fromEntries(
campusesResult.value.map((campus) => [campus.name, campus.id]),
)
campusNames.value = Object.fromEntries(
campusesResult.value
.filter(
(campus): campus is typeof campus & { name: WorkbenchCampus } =>
campus.name === '本部院区' || campus.name === '东院区' || campus.name === '西院区',
)
.map((campus) => [campus.id, campus.name]),
)
}
if (departmentsResult.status === 'fulfilled') {
departmentIds.value = Object.fromEntries(
departmentsResult.value.map((department) => [department.name, department.id]),
)
}
}
async function loadTaskDetail(id: string | null) {
const requestId = ++detailRequestId
if (!id) {
selectedTask.value = null
taskEvents.value = []
artifacts.value = []
detailLoading.value = false
eventsLoading.value = false
eventsError.value = false
artifactsLoading.value = false
artifactsError.value = false
padSessionStatus.value = null
return
}
detailLoading.value = true
eventsLoading.value = true
eventsError.value = false
artifactsLoading.value = true
artifactsError.value = false
taskEvents.value = []
artifacts.value = []
padSessionStatus.value = null
try {
const [detailResult, eventsResult] = await Promise.allSettled([
const [detailResult, eventsResult, artifactsResult] = await Promise.allSettled([
getSigningTaskDetail(id, signingApiOptions.value),
getSigningTaskEvents(id),
getSigningArtifacts(id),
])
if (requestId === detailRequestId) {
@@ -197,6 +253,12 @@ async function loadTaskDetail(id: string | null) {
} else {
eventsError.value = true
}
if (artifactsResult.status === 'fulfilled') {
artifacts.value = artifactsResult.value
} else {
artifactsError.value = true
}
}
} catch {
if (requestId === detailRequestId) {
@@ -208,6 +270,7 @@ async function loadTaskDetail(id: string | null) {
if (requestId === detailRequestId) {
detailLoading.value = false
eventsLoading.value = false
artifactsLoading.value = false
}
}
}
@@ -295,6 +358,22 @@ async function handleTaskAction(action: SigningTaskAction) {
return
}
if (action === 'create-pad-session') {
padSessionLoading.value = true
try {
const session: SignDeliveryResponseDto = await createPadSigningSession(task.id)
padSessionStatus.value = session.status
ElMessage.success('手写板签署会话已创建,请由设备适配器继续采集签名')
} catch {
ElMessage.error('手写板签署会话创建失败,请稍后重试')
} finally {
padSessionLoading.value = false
}
return
}
if (action === 'online-sign') {
if (!isSigningMockEnabled) {
ElMessage.info('线上签署页面接口待接入')
@@ -311,12 +390,26 @@ async function handleTaskAction(action: SigningTaskAction) {
}
if (action === 'download-pdf') {
ElMessage.info('原型演示:PDF 原件下载接口待接入')
const artifact =
artifacts.value.find((item) => item.artifactType === 'SIGNED_PDF') ??
artifacts.value.find((item) => item.artifactType === 'ORIGINAL_PDF')
if (artifact) {
await downloadArtifact(artifact)
} else {
ElMessage.info('当前任务暂无 PDF 文件')
}
return
}
if (action === 'download-signature') {
ElMessage.info('原型演示:签名原图下载接口待接入')
const artifact = artifacts.value.find((item) => item.artifactType === 'SIGNATURE_IMAGE')
if (artifact) {
await downloadArtifact(artifact)
} else {
ElMessage.info('当前任务暂无签名原图')
}
return
}
@@ -365,6 +458,21 @@ async function handleTaskAction(action: SigningTaskAction) {
}
}
async function downloadArtifact(artifact: SigningArtifact) {
try {
const blob = await downloadSigningArtifact(artifact.id)
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = artifact.fileName
link.click()
window.setTimeout(() => URL.revokeObjectURL(url), 0)
ElMessage.success(`${artifact.label}下载已开始`)
} catch {
ElMessage.error(`${artifact.label}下载失败,请稍后重试`)
}
}
async function confirmPadSignature(signatureDataUrl: string) {
const task = selectedTask.value
@@ -421,6 +529,7 @@ async function handleOnlineResend() {
}
async function initializePage() {
await loadOrganizationOptions()
await loadTemplates()
await loadTasks()
}
@@ -494,7 +603,13 @@ onMounted(() => {
:audit-loading="eventsLoading"
:audit-error="eventsError"
:allow-local-signing="isSigningMockEnabled"
:artifacts="artifacts"
:artifacts-loading="artifactsLoading"
:artifacts-error="artifactsError"
:pad-session-status="padSessionStatus"
:pad-session-loading="padSessionLoading"
@action="handleTaskAction"
@download-artifact="downloadArtifact"
/>
</div>