feat(management): 接入文档用户权限与报表
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import { getCampuses, getDepartments } from '@/api/management/organization'
|
||||
import { getSigningTasks, getSigningTemplates, isSigningMockEnabled } from '@/api/workbench/signing'
|
||||
import type { SigningTaskRecord, SigningTemplate, WorkbenchCampus } from '@/api/workbench/types'
|
||||
|
||||
import DepartmentDistributionChart from './components/DepartmentDistributionChart.vue'
|
||||
import ReportDetailTable from './components/ReportDetailTable.vue'
|
||||
@@ -22,12 +27,16 @@ import type {
|
||||
ReportFilterForm,
|
||||
ReportMetricCard as ReportMetricCardData,
|
||||
ReportPeriodOption,
|
||||
ReportSelectOption,
|
||||
ReportTask,
|
||||
ReportTaskStatus,
|
||||
ReportTrendPoint,
|
||||
} from './types'
|
||||
|
||||
const loading = ref(true)
|
||||
const tasks = ref<ReportTask[]>([])
|
||||
const departmentOptions = ref<ReportSelectOption[]>(reportDepartmentOptions)
|
||||
const router = useRouter()
|
||||
|
||||
const filters = reactive<ReportFilterForm>({
|
||||
period: '14d',
|
||||
@@ -145,21 +154,159 @@ function updateCategory(value: ReportDocumentCategory) {
|
||||
}
|
||||
|
||||
function handleExport() {
|
||||
ElMessage.info('原型演示:导出 Excel')
|
||||
const escapeCsv = (value: string | number) => `"${String(value).replaceAll('"', '""')}"`
|
||||
const header = ['任务号', '文档', '科室', '院区', '方式', '状态', '发起时间']
|
||||
const rows = filteredTasks.value.map((task) => [
|
||||
task.id,
|
||||
task.documentName,
|
||||
task.department,
|
||||
task.campus,
|
||||
task.method === 'pad' ? '手写板' : '短信',
|
||||
getReportStatusLabel(task.status),
|
||||
formatExportDate(task.createdAt),
|
||||
])
|
||||
const csv = `\uFEFF${[header, ...rows].map((row) => row.map(escapeCsv).join(',')).join('\n')}`
|
||||
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = `医签通签署报表-${new Date().toISOString().slice(0, 10)}.csv`
|
||||
link.click()
|
||||
window.setTimeout(() => URL.revokeObjectURL(url), 0)
|
||||
ElMessage.success('报表已导出')
|
||||
}
|
||||
|
||||
function handleView(row: ReportTask) {
|
||||
ElMessage.info(`原型演示:查看任务${row.id},签署工作台暂未实现`)
|
||||
void router.push({ name: 'workbench-signing', query: { taskId: row.id } })
|
||||
}
|
||||
|
||||
function getReportStatusLabel(status: ReportTaskStatus) {
|
||||
const labels: Record<ReportTaskStatus, string> = {
|
||||
signed: '已签署',
|
||||
pending: '待签署',
|
||||
expired: '已超时',
|
||||
void: '已作废',
|
||||
}
|
||||
|
||||
return labels[status]
|
||||
}
|
||||
|
||||
function formatExportDate(value: Date) {
|
||||
const pad = (part: number) => String(part).padStart(2, '0')
|
||||
return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())} ${pad(
|
||||
value.getHours(),
|
||||
)}:${pad(value.getMinutes())}`
|
||||
}
|
||||
|
||||
function mapCategory(
|
||||
category: string,
|
||||
documentName: string,
|
||||
): Exclude<ReportDocumentCategory, 'all'> {
|
||||
const value = `${category} ${documentName}`
|
||||
|
||||
if (value.includes('入院') || value.includes('住院')) {
|
||||
return 'admission'
|
||||
}
|
||||
|
||||
if (value.includes('风险') || value.includes('告知')) {
|
||||
return 'risk'
|
||||
}
|
||||
|
||||
return 'consent'
|
||||
}
|
||||
|
||||
function mapReportTask(task: SigningTaskRecord, templates: SigningTemplate[]): ReportTask {
|
||||
const template = templates.find(
|
||||
(item) => item.versionId === task.templateVersionId || item.id === task.documentId,
|
||||
)
|
||||
const createdAt = new Date((task.createdAt ?? task.updatedAt).replace(' ', 'T'))
|
||||
const normalizedCreatedAt = Number.isNaN(createdAt.getTime()) ? new Date() : createdAt
|
||||
const status: ReportTask['status'] =
|
||||
task.status === 'signed'
|
||||
? 'signed'
|
||||
: task.status === 'expired'
|
||||
? 'expired'
|
||||
: task.status === 'void' || task.status === 'failed' || task.status === 'rejected'
|
||||
? 'void'
|
||||
: 'pending'
|
||||
|
||||
return {
|
||||
id: task.id,
|
||||
patientName: task.patientName,
|
||||
patientId: task.patientId,
|
||||
documentName: task.documentName,
|
||||
department: task.department,
|
||||
campus: task.campus as Exclude<ReportCampus, 'all'>,
|
||||
category: mapCategory(template?.category ?? '', task.documentName),
|
||||
method: task.method,
|
||||
status,
|
||||
createdAt: normalizedCreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
function isWorkbenchCampus(value: string): value is WorkbenchCampus {
|
||||
return value === '本部院区' || value === '东院区' || value === '西院区'
|
||||
}
|
||||
|
||||
async function loadReports() {
|
||||
loading.value = true
|
||||
await Promise.resolve()
|
||||
tasks.value = reportTasks.map((task) => ({
|
||||
...task,
|
||||
createdAt: new Date(task.createdAt),
|
||||
}))
|
||||
loading.value = false
|
||||
|
||||
try {
|
||||
if (isSigningMockEnabled) {
|
||||
tasks.value = reportTasks.map((task) => ({
|
||||
...task,
|
||||
createdAt: new Date(task.createdAt),
|
||||
}))
|
||||
departmentOptions.value = reportDepartmentOptions
|
||||
return
|
||||
}
|
||||
|
||||
const [templatesResult, campusesResult, departmentsResult] = await Promise.allSettled([
|
||||
getSigningTemplates(),
|
||||
getCampuses(),
|
||||
getDepartments(),
|
||||
])
|
||||
const templates = templatesResult.status === 'fulfilled' ? templatesResult.value : []
|
||||
const campusNames: Record<string, WorkbenchCampus> =
|
||||
campusesResult.status === 'fulfilled'
|
||||
? (Object.fromEntries(
|
||||
campusesResult.value
|
||||
.filter((campus) => isWorkbenchCampus(campus.name))
|
||||
.map((campus) => [campus.id, campus.name]),
|
||||
) as Record<string, WorkbenchCampus>)
|
||||
: {}
|
||||
const taskResponse = await getSigningTasks(
|
||||
{
|
||||
page: 1,
|
||||
pageSize: 200,
|
||||
status: 'all',
|
||||
dateRange: 'all',
|
||||
campus: 'all',
|
||||
method: 'all',
|
||||
},
|
||||
{ templates, campusNames },
|
||||
)
|
||||
tasks.value = taskResponse.records.map((task) => mapReportTask(task, templates))
|
||||
|
||||
const departmentNames = new Set<string>(
|
||||
departmentsResult.status === 'fulfilled'
|
||||
? departmentsResult.value.map((department) => department.name)
|
||||
: [],
|
||||
)
|
||||
tasks.value.forEach((task) => departmentNames.add(task.department))
|
||||
departmentOptions.value = [
|
||||
{ value: 'all', label: '全部科室' },
|
||||
...[...departmentNames]
|
||||
.filter((name) => name && name !== '未指定科室')
|
||||
.sort((left, right) => left.localeCompare(right, 'zh-CN'))
|
||||
.map((name) => ({ value: name, label: name })),
|
||||
]
|
||||
} catch {
|
||||
tasks.value = []
|
||||
ElMessage.error('报表数据加载失败,请稍后重试')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadReports)
|
||||
@@ -174,7 +321,7 @@ onMounted(loadReports)
|
||||
:category="filters.category"
|
||||
:period-options="reportPeriodOptions"
|
||||
:campus-options="reportCampusOptions"
|
||||
:department-options="reportDepartmentOptions"
|
||||
:department-options="departmentOptions"
|
||||
:category-options="reportCategoryOptions"
|
||||
@update:period="updatePeriod"
|
||||
@update:campus="updateCampus"
|
||||
|
||||
Reference in New Issue
Block a user