feat(workbench): 接入工作台首页概览聚合接口
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
import type { HomeTrendPoint } from '../types'
|
||||
import type { HomeRankingPeriod, HomeTrendPoint } from '../types'
|
||||
|
||||
const props = defineProps<{
|
||||
points: HomeTrendPoint[]
|
||||
period: HomeRankingPeriod
|
||||
}>()
|
||||
|
||||
const chartWidth = 620
|
||||
@@ -63,12 +64,17 @@ const gridLines = computed(() =>
|
||||
<template>
|
||||
<section class="home-panel trend-panel">
|
||||
<div class="panel-heading">
|
||||
<h2>近一周签署客户趋势</h2>
|
||||
<span>单位:人次</span>
|
||||
<h2>近{{ period }}天签署趋势</h2>
|
||||
<span>单位:单</span>
|
||||
</div>
|
||||
|
||||
<div v-if="points.length" class="trend-chart-wrap">
|
||||
<svg class="trend-chart" viewBox="0 0 620 210" role="img" aria-label="近一周签署客户趋势图">
|
||||
<svg
|
||||
class="trend-chart"
|
||||
viewBox="0 0 620 210"
|
||||
role="img"
|
||||
:aria-label="'近' + period + '天签署趋势图'"
|
||||
>
|
||||
<defs>
|
||||
<linearGradient id="home-trend-area" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="#0e6e8c" stop-opacity="0.22" />
|
||||
|
||||
@@ -11,11 +11,22 @@ const emit = defineEmits<{
|
||||
}>()
|
||||
|
||||
function getStatusLabel(item: HomeTodoItem) {
|
||||
if (item.status === 'overdue') {
|
||||
return '已超时'
|
||||
switch (item.status) {
|
||||
case 'created':
|
||||
return '待发送'
|
||||
case 'pending':
|
||||
return item.method === 'pad' ? '待签署·手写板' : '待签署·短信'
|
||||
case 'generating':
|
||||
return '文书生成中'
|
||||
case 'overdue':
|
||||
return '已超时'
|
||||
case 'signed':
|
||||
return '已签署'
|
||||
case 'voided':
|
||||
return '已作废'
|
||||
case 'failed':
|
||||
return '处理失败'
|
||||
}
|
||||
|
||||
return item.method === 'pad' ? '待签署·手写板' : '待签署·短信'
|
||||
}
|
||||
|
||||
function getStatusClass(item: HomeTodoItem) {
|
||||
@@ -23,11 +34,38 @@ function getStatusClass(item: HomeTodoItem) {
|
||||
return 'todo-status--overdue'
|
||||
}
|
||||
|
||||
return item.method === 'pad' ? 'todo-status--pad' : 'todo-status--sms'
|
||||
if (item.status === 'pending') {
|
||||
return item.method === 'pad' ? 'todo-status--pad' : 'todo-status--sms'
|
||||
}
|
||||
|
||||
if (item.status === 'signed') {
|
||||
return 'todo-status--success'
|
||||
}
|
||||
|
||||
if (item.status === 'failed') {
|
||||
return 'todo-status--danger'
|
||||
}
|
||||
|
||||
return 'todo-status--muted'
|
||||
}
|
||||
|
||||
function getHint(item: HomeTodoItem) {
|
||||
return item.status === 'overdue' ? '⚠ 已超时,需重新发起' : '⏳ 等待患者签署'
|
||||
switch (item.status) {
|
||||
case 'created':
|
||||
return '⏳ 等待发送'
|
||||
case 'pending':
|
||||
return '⏳ 等待患者签署'
|
||||
case 'generating':
|
||||
return '⌛ 正在生成签署文书'
|
||||
case 'overdue':
|
||||
return '⚠ 已超时,需重新发起'
|
||||
case 'signed':
|
||||
return '✓ 已完成签署'
|
||||
case 'voided':
|
||||
return '— 任务已作废'
|
||||
case 'failed':
|
||||
return '⚠ 任务处理失败'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -170,11 +208,22 @@ function getHint(item: HomeTodoItem) {
|
||||
background: #f3ecfb;
|
||||
}
|
||||
|
||||
.todo-status--overdue {
|
||||
.todo-status--overdue,
|
||||
.todo-status--danger {
|
||||
color: var(--err);
|
||||
background: var(--err-l);
|
||||
}
|
||||
|
||||
.todo-status--success {
|
||||
color: var(--ok);
|
||||
background: var(--ok-l);
|
||||
}
|
||||
|
||||
.todo-status--muted {
|
||||
color: var(--mut);
|
||||
background: #eef3f5;
|
||||
}
|
||||
|
||||
.todo-row--detail {
|
||||
margin-top: 5px;
|
||||
color: var(--mut);
|
||||
|
||||
@@ -89,8 +89,16 @@ async function loadOverview() {
|
||||
loadFailed.value = false
|
||||
|
||||
try {
|
||||
await appStore.loadCampuses()
|
||||
const campusId = appStore.selectedCampusId
|
||||
|
||||
if (!campusId) {
|
||||
throw new Error('当前用户没有可用院区')
|
||||
}
|
||||
|
||||
const nextOverview = await getWorkbenchOverview({
|
||||
campus: selectedCampus.value,
|
||||
campusId,
|
||||
rankingPeriod: rankingPeriod.value,
|
||||
})
|
||||
if (requestId !== overviewRequestId) {
|
||||
@@ -189,7 +197,7 @@ onMounted(() => {
|
||||
</div>
|
||||
|
||||
<div class="overview-grid">
|
||||
<SigningTrendPanel :points="trendPoints" />
|
||||
<SigningTrendPanel :points="trendPoints" :period="rankingPeriod" />
|
||||
<TodoPanel :items="todoItems" @select="handleTodoSelect" @view-all="goToSigning()" />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ export interface HomeTrendPoint {
|
||||
value: number
|
||||
}
|
||||
|
||||
export type HomeTodoStatus = 'pending' | 'overdue'
|
||||
export type HomeTodoStatus =
|
||||
'created' | 'pending' | 'generating' | 'overdue' | 'signed' | 'voided' | 'failed'
|
||||
|
||||
export type HomeSigningMethod = 'pad' | 'sms'
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ const STATUS_VALUES: SigningTaskStatus[] = [
|
||||
'failed',
|
||||
]
|
||||
const DATE_RANGE_VALUES: SigningDateRange[] = ['today', 'yesterday', '3d', '7d', 'all']
|
||||
const CAMPUS_VALUES: WorkbenchCampus[] = ['本部院区', '东院区', '西院区']
|
||||
|
||||
const statusLabels: Record<SigningTaskStatus, string> = {
|
||||
pending: '待签署',
|
||||
@@ -165,7 +164,7 @@ function isSigningDateRange(value: string): value is SigningDateRange {
|
||||
}
|
||||
|
||||
function isWorkbenchCampus(value: string): value is WorkbenchCampus {
|
||||
return CAMPUS_VALUES.includes(value as WorkbenchCampus)
|
||||
return appStore.campuses.includes(value)
|
||||
}
|
||||
|
||||
function syncFilterFromRoute() {
|
||||
@@ -201,12 +200,7 @@ async function loadOrganizationOptions() {
|
||||
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]),
|
||||
campusesResult.value.map((campus) => [campus.id, campus.name]),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -558,6 +552,8 @@ async function handleOnlineResend() {
|
||||
}
|
||||
|
||||
async function initializePage() {
|
||||
await appStore.loadCampuses()
|
||||
syncFilterFromRoute()
|
||||
await loadOrganizationOptions()
|
||||
await loadTemplates()
|
||||
await loadTasks()
|
||||
@@ -589,7 +585,6 @@ watch(
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
syncFilterFromRoute()
|
||||
void initializePage()
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user