feat(clinical-web): 新增签署任务弹窗与文档库管理页面
This commit is contained in:
@@ -84,7 +84,7 @@ TanStack Vue Query、PDF 预览、报表图表、自动化测试和签字板适
|
||||
|
||||
## 当前状态
|
||||
|
||||
已完成 Vite 基础初始化、路由、Pinia、原型主题 CSS、登录页和医护端整体布局。当前所有业务 View 暂时保留文字占位,登录使用演示认证逻辑,尚未接入真实后端。
|
||||
已完成 Vite 基础初始化、路由、Pinia、原型主题 CSS、登录页、首页、签署工作台第一版 Mock 和文档库管理页面。报表、用户权限、文档权限和系统设置仍保留页面骨架;登录和签署流程使用演示数据,尚未接入真实后端。
|
||||
|
||||
## 当前路由结构
|
||||
|
||||
@@ -108,6 +108,11 @@ ClinicalLayout
|
||||
|
||||
页面目录按业务域组织:`auth` 保留登录页;工作台页面位于 `views/workbench`;管理页面位于 `views/management`。每个具体页面目录都有 `index.vue` 入口和 `components` 目录,用于继续拆分当前页面组件。
|
||||
|
||||
跨页面复用的业务组件和 composable 不放在具体 View 目录中:
|
||||
|
||||
- `components/signing/NewSigningTaskDialog.vue`:新增签署任务公共弹窗,供签署工作台和文档库管理复用;
|
||||
- `composables/useSigningTaskForm.ts`:封装患者定位、就诊选择、模板选择、签署方式、校验和任务提交状态。
|
||||
|
||||
## API 与类型约定
|
||||
|
||||
API 模块与页面按业务域对应。工作台页面使用 `api/workbench` 下的页面文件,管理页面使用 `api/management` 下的直接文件:
|
||||
|
||||
@@ -1,29 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, reactive, ref, watch } from 'vue'
|
||||
import { computed, nextTick, ref, watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
import { createSigningTask, getPatientProfile, getSigningTemplates } from '@/api/workbench/signing'
|
||||
import type {
|
||||
PatientProfile,
|
||||
PatientVisit,
|
||||
SigningMethod,
|
||||
SigningTaskRecord,
|
||||
SigningTemplate,
|
||||
} from '@/api/workbench/types'
|
||||
import type { SigningTaskRecord, SigningTemplate } from '@/api/workbench/types'
|
||||
import { useSigningTaskForm } from '@/composables/useSigningTaskForm'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
|
||||
interface TemplateCategoryGroup {
|
||||
name: string
|
||||
templates: SigningTemplate[]
|
||||
}
|
||||
|
||||
interface TemplateDepartmentGroup {
|
||||
name: string
|
||||
categories: TemplateCategoryGroup[]
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
initialTemplate?: SigningTemplate | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -33,231 +18,53 @@ const emit = defineEmits<{
|
||||
|
||||
const appStore = useAppStore()
|
||||
const patientInput = ref<HTMLInputElement | null>(null)
|
||||
const patientId = ref('')
|
||||
const patient = ref<PatientProfile | null>(null)
|
||||
const visits = ref<PatientVisit[]>([])
|
||||
const templates = ref<SigningTemplate[]>([])
|
||||
const documentSearch = ref('')
|
||||
const selectedTemplateId = ref('')
|
||||
const selectedVisitId = ref('')
|
||||
const method = ref<SigningMethod>('pad')
|
||||
const smsPhone = ref('')
|
||||
const patientPhoneRevealed = ref(false)
|
||||
const loadingTemplates = ref(false)
|
||||
const locating = ref(false)
|
||||
const submitting = ref(false)
|
||||
const initialTemplate = computed(() => props.initialTemplate ?? null)
|
||||
|
||||
const errors = reactive({
|
||||
patient: '',
|
||||
visit: '',
|
||||
document: '',
|
||||
phone: '',
|
||||
const {
|
||||
documentSearch,
|
||||
errors,
|
||||
filteredTemplateGroups,
|
||||
loadingTemplates,
|
||||
loadTemplates,
|
||||
locatePatient,
|
||||
locating,
|
||||
maskPhone,
|
||||
method,
|
||||
patient,
|
||||
patientId,
|
||||
patientPhoneRevealed,
|
||||
resetForNextTask,
|
||||
resetForm,
|
||||
selectMethod,
|
||||
selectTemplate,
|
||||
selectVisit,
|
||||
selectedTemplate,
|
||||
selectedTemplateId,
|
||||
selectedVisit,
|
||||
selectedVisitId,
|
||||
smsPhone,
|
||||
submit: submitForm,
|
||||
submitting,
|
||||
togglePhone,
|
||||
visits,
|
||||
} = useSigningTaskForm({
|
||||
campus: computed(() => appStore.selectedCampus),
|
||||
initialTemplate,
|
||||
})
|
||||
|
||||
const selectedVisit = computed(
|
||||
() => visits.value.find((visit) => visit.id === selectedVisitId.value) ?? null,
|
||||
)
|
||||
|
||||
const selectedTemplate = computed(
|
||||
() => templates.value.find((template) => template.id === selectedTemplateId.value) ?? null,
|
||||
)
|
||||
|
||||
const filteredTemplateGroups = computed<TemplateDepartmentGroup[]>(() => {
|
||||
const keyword = documentSearch.value.trim().toLowerCase()
|
||||
const grouped = new Map<string, Map<string, SigningTemplate[]>>()
|
||||
|
||||
templates.value.forEach((template) => {
|
||||
const matches =
|
||||
!keyword ||
|
||||
[template.name, template.code, template.department, template.category]
|
||||
.join(' ')
|
||||
.toLowerCase()
|
||||
.includes(keyword)
|
||||
|
||||
if (!matches) {
|
||||
return
|
||||
}
|
||||
|
||||
const categories = grouped.get(template.department) ?? new Map<string, SigningTemplate[]>()
|
||||
const categoryTemplates = categories.get(template.category) ?? []
|
||||
categoryTemplates.push(template)
|
||||
categories.set(template.category, categoryTemplates)
|
||||
grouped.set(template.department, categories)
|
||||
})
|
||||
|
||||
return [...grouped.entries()].map(([department, categories]) => ({
|
||||
name: department,
|
||||
categories: [...categories.entries()].map(([category, categoryTemplates]) => ({
|
||||
name: category,
|
||||
templates: categoryTemplates,
|
||||
})),
|
||||
}))
|
||||
})
|
||||
|
||||
function clearErrors() {
|
||||
errors.patient = ''
|
||||
errors.visit = ''
|
||||
errors.document = ''
|
||||
errors.phone = ''
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
patientId.value = ''
|
||||
patient.value = null
|
||||
visits.value = []
|
||||
documentSearch.value = ''
|
||||
selectedTemplateId.value = ''
|
||||
selectedVisitId.value = ''
|
||||
method.value = 'pad'
|
||||
smsPhone.value = ''
|
||||
patientPhoneRevealed.value = false
|
||||
clearErrors()
|
||||
}
|
||||
|
||||
async function loadTemplates() {
|
||||
if (templates.value.length) {
|
||||
return
|
||||
}
|
||||
|
||||
loadingTemplates.value = true
|
||||
|
||||
async function submitTask(keepOpen: boolean) {
|
||||
try {
|
||||
templates.value = await getSigningTemplates()
|
||||
} catch {
|
||||
ElMessage.error('文档模板加载失败,请稍后重试')
|
||||
} finally {
|
||||
loadingTemplates.value = false
|
||||
}
|
||||
}
|
||||
const task = await submitForm()
|
||||
|
||||
async function locatePatient() {
|
||||
const keyword = patientId.value.trim()
|
||||
errors.patient = ''
|
||||
errors.visit = ''
|
||||
patient.value = null
|
||||
visits.value = []
|
||||
selectedVisitId.value = ''
|
||||
selectedTemplateId.value = ''
|
||||
|
||||
if (!keyword) {
|
||||
errors.patient = '请输入患者 ID、门诊号或住院号'
|
||||
if (!task) {
|
||||
return
|
||||
}
|
||||
|
||||
locating.value = true
|
||||
|
||||
try {
|
||||
const profile = await getPatientProfile(keyword)
|
||||
|
||||
if (!profile) {
|
||||
errors.patient = '未找到该患者,请核对输入内容'
|
||||
smsPhone.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
patient.value = profile
|
||||
visits.value = profile.visits
|
||||
selectedVisitId.value =
|
||||
profile.visits.find((visit) => visit.isCurrent)?.id ?? profile.visits[0]?.id ?? ''
|
||||
smsPhone.value = profile.phone
|
||||
} catch {
|
||||
errors.patient = '患者信息查询失败,请稍后重试'
|
||||
} finally {
|
||||
locating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function selectVisit(visit: PatientVisit) {
|
||||
selectedVisitId.value = visit.id
|
||||
errors.visit = ''
|
||||
}
|
||||
|
||||
function selectTemplate(template: SigningTemplate) {
|
||||
selectedTemplateId.value = template.id
|
||||
errors.document = ''
|
||||
|
||||
if (!template.supportedMethods.includes(method.value)) {
|
||||
method.value = template.supportedMethods[0] ?? 'pad'
|
||||
}
|
||||
}
|
||||
|
||||
function selectMethod(nextMethod: SigningMethod) {
|
||||
if (selectedTemplate.value && !selectedTemplate.value.supportedMethods.includes(nextMethod)) {
|
||||
return
|
||||
}
|
||||
|
||||
method.value = nextMethod
|
||||
errors.phone = ''
|
||||
}
|
||||
|
||||
function maskPhone(value: string) {
|
||||
if (value.length < 7) {
|
||||
return value
|
||||
}
|
||||
|
||||
return `${value.slice(0, 3)}****${value.slice(-4)}`
|
||||
}
|
||||
|
||||
function togglePhone() {
|
||||
patientPhoneRevealed.value = !patientPhoneRevealed.value
|
||||
ElMessage.info('明文查看已记录审计日志')
|
||||
}
|
||||
|
||||
function validate() {
|
||||
clearErrors()
|
||||
let valid = true
|
||||
|
||||
if (!patient.value) {
|
||||
errors.patient = '请先定位患者'
|
||||
valid = false
|
||||
}
|
||||
|
||||
if (!selectedVisit.value) {
|
||||
errors.visit = '请选择关联就诊'
|
||||
valid = false
|
||||
}
|
||||
|
||||
if (!selectedTemplate.value) {
|
||||
errors.document = '请至少选择一份文档'
|
||||
valid = false
|
||||
}
|
||||
|
||||
if (method.value === 'sms' && !smsPhone.value.trim()) {
|
||||
errors.phone = '请确认短信接收手机号'
|
||||
valid = false
|
||||
}
|
||||
|
||||
return valid
|
||||
}
|
||||
|
||||
async function submit(keepOpen: boolean) {
|
||||
if (!validate() || !patient.value || !selectedVisit.value || !selectedTemplate.value) {
|
||||
return
|
||||
}
|
||||
|
||||
submitting.value = true
|
||||
|
||||
try {
|
||||
const task = await createSigningTask({
|
||||
campus: appStore.selectedCampus,
|
||||
patientId: patient.value.id,
|
||||
visitId: selectedVisit.value.id,
|
||||
documentId: selectedTemplate.value.id,
|
||||
documentName: selectedTemplate.value.name,
|
||||
method: method.value,
|
||||
visitType: selectedVisit.value.type,
|
||||
visitNo: selectedVisit.value.visitNo,
|
||||
department: selectedVisit.value.department,
|
||||
phone: method.value === 'sms' ? smsPhone.value.trim() : undefined,
|
||||
})
|
||||
|
||||
emit('created', task)
|
||||
ElMessage.success(`已创建 1 个签署任务${method.value === 'sms' ? ',短信已发送' : ''}`)
|
||||
ElMessage.success(`已创建 1 个签署任务${task.method === 'sms' ? ',短信已发送' : ''}`)
|
||||
|
||||
if (keepOpen) {
|
||||
selectedTemplateId.value = ''
|
||||
documentSearch.value = ''
|
||||
errors.document = ''
|
||||
resetForNextTask()
|
||||
await nextTick()
|
||||
patientInput.value?.focus()
|
||||
return
|
||||
@@ -266,8 +73,6 @@ async function submit(keepOpen: boolean) {
|
||||
emit('close')
|
||||
} catch {
|
||||
ElMessage.error('签署任务创建失败,请稍后重试')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,11 +314,16 @@ watch(
|
||||
type="button"
|
||||
class="action-button action-button--ghost"
|
||||
:disabled="submitting"
|
||||
@click="submit(true)"
|
||||
@click="submitTask(true)"
|
||||
>
|
||||
提交并继续新增
|
||||
</button>
|
||||
<button type="button" class="action-button" :disabled="submitting" @click="submit(false)">
|
||||
<button
|
||||
type="button"
|
||||
class="action-button"
|
||||
:disabled="submitting"
|
||||
@click="submitTask(false)"
|
||||
>
|
||||
{{ submitting ? '提交中…' : '提交任务' }}
|
||||
</button>
|
||||
</footer>
|
||||
307
clinical-web/src/composables/useSigningTaskForm.ts
Normal file
307
clinical-web/src/composables/useSigningTaskForm.ts
Normal file
@@ -0,0 +1,307 @@
|
||||
import { computed, reactive, ref, toValue, type MaybeRefOrGetter } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
import { createSigningTask, getPatientProfile, getSigningTemplates } from '@/api/workbench/signing'
|
||||
import type {
|
||||
PatientProfile,
|
||||
PatientVisit,
|
||||
SigningMethod,
|
||||
SigningTaskRecord,
|
||||
SigningTemplate,
|
||||
WorkbenchCampus,
|
||||
} from '@/api/workbench/types'
|
||||
|
||||
export interface UseSigningTaskFormOptions {
|
||||
campus: MaybeRefOrGetter<WorkbenchCampus>
|
||||
initialTemplate?: MaybeRefOrGetter<SigningTemplate | null | undefined>
|
||||
}
|
||||
|
||||
interface TemplateCategoryGroup {
|
||||
name: string
|
||||
templates: SigningTemplate[]
|
||||
}
|
||||
|
||||
interface TemplateDepartmentGroup {
|
||||
name: string
|
||||
categories: TemplateCategoryGroup[]
|
||||
}
|
||||
|
||||
export function useSigningTaskForm(options: UseSigningTaskFormOptions) {
|
||||
const patientId = ref('')
|
||||
const patient = ref<PatientProfile | null>(null)
|
||||
const visits = ref<PatientVisit[]>([])
|
||||
const templates = ref<SigningTemplate[]>([])
|
||||
const documentSearch = ref('')
|
||||
const selectedTemplateId = ref('')
|
||||
const selectedVisitId = ref('')
|
||||
const method = ref<SigningMethod>('pad')
|
||||
const smsPhone = ref('')
|
||||
const patientPhoneRevealed = ref(false)
|
||||
const loadingTemplates = ref(false)
|
||||
const locating = ref(false)
|
||||
const submitting = ref(false)
|
||||
|
||||
const errors = reactive({
|
||||
patient: '',
|
||||
visit: '',
|
||||
document: '',
|
||||
phone: '',
|
||||
})
|
||||
|
||||
const selectedVisit = computed(
|
||||
() => visits.value.find((visit) => visit.id === selectedVisitId.value) ?? null,
|
||||
)
|
||||
|
||||
const selectedTemplate = computed(
|
||||
() => templates.value.find((template) => template.id === selectedTemplateId.value) ?? null,
|
||||
)
|
||||
|
||||
const filteredTemplateGroups = computed<TemplateDepartmentGroup[]>(() => {
|
||||
const keyword = documentSearch.value.trim().toLowerCase()
|
||||
const grouped = new Map<string, Map<string, SigningTemplate[]>>()
|
||||
|
||||
templates.value.forEach((template) => {
|
||||
const matches =
|
||||
!keyword ||
|
||||
[template.name, template.code, template.department, template.category]
|
||||
.join(' ')
|
||||
.toLowerCase()
|
||||
.includes(keyword)
|
||||
|
||||
if (!matches) {
|
||||
return
|
||||
}
|
||||
|
||||
const categories = grouped.get(template.department) ?? new Map<string, SigningTemplate[]>()
|
||||
const categoryTemplates = categories.get(template.category) ?? []
|
||||
categoryTemplates.push(template)
|
||||
categories.set(template.category, categoryTemplates)
|
||||
grouped.set(template.department, categories)
|
||||
})
|
||||
|
||||
return [...grouped.entries()].map(([department, categories]) => ({
|
||||
name: department,
|
||||
categories: [...categories.entries()].map(([category, categoryTemplates]) => ({
|
||||
name: category,
|
||||
templates: categoryTemplates,
|
||||
})),
|
||||
}))
|
||||
})
|
||||
|
||||
function getInitialTemplate() {
|
||||
return options.initialTemplate ? (toValue(options.initialTemplate) ?? null) : null
|
||||
}
|
||||
|
||||
function includeInitialTemplate() {
|
||||
const initialTemplate = getInitialTemplate()
|
||||
|
||||
if (
|
||||
initialTemplate &&
|
||||
!templates.value.some((template) => template.id === initialTemplate.id)
|
||||
) {
|
||||
templates.value = [initialTemplate, ...templates.value]
|
||||
}
|
||||
}
|
||||
|
||||
function clearErrors() {
|
||||
errors.patient = ''
|
||||
errors.visit = ''
|
||||
errors.document = ''
|
||||
errors.phone = ''
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
const initialTemplate = getInitialTemplate()
|
||||
|
||||
patientId.value = ''
|
||||
patient.value = null
|
||||
visits.value = []
|
||||
documentSearch.value = ''
|
||||
selectedTemplateId.value = initialTemplate?.id ?? ''
|
||||
selectedVisitId.value = ''
|
||||
method.value = initialTemplate?.supportedMethods[0] ?? 'pad'
|
||||
smsPhone.value = ''
|
||||
patientPhoneRevealed.value = false
|
||||
clearErrors()
|
||||
}
|
||||
|
||||
function resetForNextTask() {
|
||||
const initialTemplate = getInitialTemplate()
|
||||
|
||||
documentSearch.value = ''
|
||||
selectedTemplateId.value = initialTemplate?.id ?? ''
|
||||
method.value = initialTemplate?.supportedMethods[0] ?? method.value
|
||||
errors.document = ''
|
||||
}
|
||||
|
||||
async function loadTemplates() {
|
||||
if (!templates.value.length) {
|
||||
loadingTemplates.value = true
|
||||
|
||||
try {
|
||||
templates.value = await getSigningTemplates()
|
||||
} catch {
|
||||
ElMessage.error('文档模板加载失败,请稍后重试')
|
||||
} finally {
|
||||
loadingTemplates.value = false
|
||||
}
|
||||
}
|
||||
|
||||
includeInitialTemplate()
|
||||
}
|
||||
|
||||
async function locatePatient() {
|
||||
const keyword = patientId.value.trim()
|
||||
errors.patient = ''
|
||||
errors.visit = ''
|
||||
patient.value = null
|
||||
visits.value = []
|
||||
selectedVisitId.value = ''
|
||||
selectedTemplateId.value = getInitialTemplate()?.id ?? ''
|
||||
|
||||
if (!keyword) {
|
||||
errors.patient = '请输入患者 ID、门诊号或住院号'
|
||||
return
|
||||
}
|
||||
|
||||
locating.value = true
|
||||
|
||||
try {
|
||||
const profile = await getPatientProfile(keyword)
|
||||
|
||||
if (!profile) {
|
||||
errors.patient = '未找到该患者,请核对输入内容'
|
||||
smsPhone.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
patient.value = profile
|
||||
visits.value = profile.visits
|
||||
selectedVisitId.value =
|
||||
profile.visits.find((visit) => visit.isCurrent)?.id ?? profile.visits[0]?.id ?? ''
|
||||
smsPhone.value = profile.phone
|
||||
} catch {
|
||||
errors.patient = '患者信息查询失败,请稍后重试'
|
||||
} finally {
|
||||
locating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function selectVisit(visit: PatientVisit) {
|
||||
selectedVisitId.value = visit.id
|
||||
errors.visit = ''
|
||||
}
|
||||
|
||||
function selectTemplate(template: SigningTemplate) {
|
||||
selectedTemplateId.value = template.id
|
||||
errors.document = ''
|
||||
|
||||
if (!template.supportedMethods.includes(method.value)) {
|
||||
method.value = template.supportedMethods[0] ?? 'pad'
|
||||
}
|
||||
}
|
||||
|
||||
function selectMethod(nextMethod: SigningMethod) {
|
||||
if (selectedTemplate.value && !selectedTemplate.value.supportedMethods.includes(nextMethod)) {
|
||||
return
|
||||
}
|
||||
|
||||
method.value = nextMethod
|
||||
errors.phone = ''
|
||||
}
|
||||
|
||||
function maskPhone(value: string) {
|
||||
if (value.length < 7) {
|
||||
return value
|
||||
}
|
||||
|
||||
return `${value.slice(0, 3)}****${value.slice(-4)}`
|
||||
}
|
||||
|
||||
function togglePhone() {
|
||||
patientPhoneRevealed.value = !patientPhoneRevealed.value
|
||||
ElMessage.info('明文查看已记录审计日志')
|
||||
}
|
||||
|
||||
function validate() {
|
||||
clearErrors()
|
||||
let valid = true
|
||||
|
||||
if (!patient.value) {
|
||||
errors.patient = '请先定位患者'
|
||||
valid = false
|
||||
}
|
||||
|
||||
if (!selectedVisit.value) {
|
||||
errors.visit = '请选择关联就诊'
|
||||
valid = false
|
||||
}
|
||||
|
||||
if (!selectedTemplate.value) {
|
||||
errors.document = '请至少选择一份文档'
|
||||
valid = false
|
||||
}
|
||||
|
||||
if (method.value === 'sms' && !smsPhone.value.trim()) {
|
||||
errors.phone = '请确认短信接收手机号'
|
||||
valid = false
|
||||
}
|
||||
|
||||
return valid
|
||||
}
|
||||
|
||||
async function submit(): Promise<SigningTaskRecord | null> {
|
||||
if (!validate() || !patient.value || !selectedVisit.value || !selectedTemplate.value) {
|
||||
return null
|
||||
}
|
||||
|
||||
submitting.value = true
|
||||
|
||||
try {
|
||||
return await createSigningTask({
|
||||
campus: toValue(options.campus),
|
||||
patientId: patient.value.id,
|
||||
visitId: selectedVisit.value.id,
|
||||
documentId: selectedTemplate.value.id,
|
||||
documentName: selectedTemplate.value.name,
|
||||
method: method.value,
|
||||
visitType: selectedVisit.value.type,
|
||||
visitNo: selectedVisit.value.visitNo,
|
||||
department: selectedVisit.value.department,
|
||||
phone: method.value === 'sms' ? smsPhone.value.trim() : undefined,
|
||||
})
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
documentSearch,
|
||||
errors,
|
||||
filteredTemplateGroups,
|
||||
loadingTemplates,
|
||||
loadTemplates,
|
||||
locatePatient,
|
||||
locating,
|
||||
maskPhone,
|
||||
method,
|
||||
patient,
|
||||
patientId,
|
||||
patientPhoneRevealed,
|
||||
resetForNextTask,
|
||||
resetForm,
|
||||
selectMethod,
|
||||
selectTemplate,
|
||||
selectVisit,
|
||||
selectedTemplate,
|
||||
selectedTemplateId,
|
||||
selectedVisit,
|
||||
selectedVisitId,
|
||||
smsPhone,
|
||||
submit,
|
||||
submitting,
|
||||
templates,
|
||||
togglePhone,
|
||||
visits,
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,9 @@
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
import NewSigningTaskDialog from '@/components/signing/NewSigningTaskDialog.vue'
|
||||
import type { SigningTemplate } from '@/api/workbench/types'
|
||||
|
||||
import DocumentFilterTabs from './components/DocumentFilterTabs.vue'
|
||||
import DocumentLibraryHeader from './components/DocumentLibraryHeader.vue'
|
||||
import DocumentPreviewDialog from './components/DocumentPreviewDialog.vue'
|
||||
@@ -18,6 +21,8 @@ import type {
|
||||
const loading = ref(true)
|
||||
const library = ref<DocumentDepartment[]>([])
|
||||
const selectedTemplate = ref<DocumentTemplate | null>(null)
|
||||
const signingTemplate = ref<SigningTemplate | null>(null)
|
||||
const signingDialogVisible = ref(false)
|
||||
|
||||
const filters = reactive<DocumentFilterForm>({
|
||||
keyword: '',
|
||||
@@ -96,8 +101,27 @@ function showEditMessage(template: DocumentTemplate) {
|
||||
ElMessage.info('原型演示:编辑模板《' + template.name + '》')
|
||||
}
|
||||
|
||||
function showInitiateMessage(template: DocumentTemplate) {
|
||||
ElMessage.info('已选择《' + template.name + '》,签署工作台暂未实现')
|
||||
function toSigningTemplate(template: DocumentTemplate): SigningTemplate {
|
||||
return {
|
||||
id: template.id,
|
||||
name: template.name,
|
||||
code: template.code,
|
||||
department: template.department,
|
||||
category: template.category,
|
||||
description: template.description,
|
||||
supportedMethods: ['pad', 'sms'],
|
||||
}
|
||||
}
|
||||
|
||||
function openSigningDialog(template: DocumentTemplate) {
|
||||
closePreview()
|
||||
signingTemplate.value = toSigningTemplate(template)
|
||||
signingDialogVisible.value = true
|
||||
}
|
||||
|
||||
function closeSigningDialog() {
|
||||
signingDialogVisible.value = false
|
||||
signingTemplate.value = null
|
||||
}
|
||||
|
||||
function clearFilters() {
|
||||
@@ -106,8 +130,7 @@ function clearFilters() {
|
||||
}
|
||||
|
||||
function useTemplate(template: DocumentTemplate) {
|
||||
closePreview()
|
||||
ElMessage.info('原型演示:从模板《' + template.name + '》发起签署')
|
||||
openSigningDialog(template)
|
||||
}
|
||||
|
||||
onMounted(loadLibrary)
|
||||
@@ -146,7 +169,7 @@ onMounted(loadLibrary)
|
||||
:template="template"
|
||||
@preview="handlePreview"
|
||||
@edit="showEditMessage"
|
||||
@initiate="showInitiateMessage"
|
||||
@initiate="openSigningDialog"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -169,6 +192,11 @@ onMounted(loadLibrary)
|
||||
@close="closePreview"
|
||||
@use="useTemplate"
|
||||
/>
|
||||
<NewSigningTaskDialog
|
||||
:visible="signingDialogVisible"
|
||||
:initial-template="signingTemplate"
|
||||
@close="closeSigningDialog"
|
||||
/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ import type {
|
||||
VisitType,
|
||||
WorkbenchCampus,
|
||||
} from '@/api/workbench/types'
|
||||
import NewSigningTaskDialog from '@/components/signing/NewSigningTaskDialog.vue'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
|
||||
import NewSigningTaskDialog from './components/NewSigningTaskDialog.vue'
|
||||
import OnlineSigningDialog from './components/OnlineSigningDialog.vue'
|
||||
import SignaturePadDialog from './components/SignaturePadDialog.vue'
|
||||
import SigningFilterBar from './components/SigningFilterBar.vue'
|
||||
|
||||
Reference in New Issue
Block a user