feat(router): 添加医生工作站等功能模块路由配置

- 新增医生工作站路由,包含待写病历功能
- 添加全部功能模块路由,支持功能列表和配置页面
- 集成待办事项模块路由,完善工作流功能
- 配置相关API接口和服务类,实现用户配置管理
- 实现待写病历列表展示和相关业务逻辑
- 完善首页统计数据显示功能
This commit is contained in:
2026-02-01 15:05:57 +08:00
parent 6f7d723c6b
commit 98fe9f3301
16 changed files with 2811 additions and 0 deletions

View File

@@ -0,0 +1,418 @@
<template>
<div class="todo-container">
<div class="page-header">
<h2>全部待办事项</h2>
<p>这里展示了您的所有待办事项</p>
</div>
<div class="filter-section">
<el-form :model="queryParams" ref="queryRef" :inline="true">
<el-form-item label="优先级" prop="priority">
<el-select v-model="queryParams.priority" placeholder="请选择优先级" clearable>
<el-option label="高" value="high"></el-option>
<el-option label="中" value="medium"></el-option>
<el-option label="低" value="low"></el-option>
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-option label="未处理" value="pending"></el-option>
<el-option label="处理中" value="processing"></el-option>
<el-option label="已完成" value="completed"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="todo-list">
<el-card
v-for="todo in todoList"
:key="todo.id"
class="todo-item"
:class="`priority-${todo.priority} status-${todo.status}`"
@click="handleTodoClick(todo)"
>
<div class="todo-header">
<div class="todo-title">
<el-icon :size="16" :color="getPriorityColor(todo.priority)">
<component :is="todo.icon" />
</el-icon>
<span class="title-text">{{ todo.title }}</span>
</div>
<div class="todo-status">
<el-tag :type="getStatusTagType(todo.status)">{{ getStatusText(todo.status) }}</el-tag>
</div>
</div>
<div class="todo-content">
<p class="todo-desc">{{ todo.desc }}</p>
<div class="todo-meta">
<span class="todo-time">{{ todo.time }}</span>
<span class="todo-priority" :style="{ color: getPriorityColor(todo.priority) }">
{{ getPriorityText(todo.priority) }}优先级
</span>
</div>
</div>
<div class="todo-actions">
<el-button type="primary" size="small" @click.stop="handleProcess(todo)">立即处理</el-button>
<el-button size="small" @click.stop="handleDetails(todo)">查看详情</el-button>
</div>
</el-card>
<div v-if="todoList.length === 0 && !loading" class="empty-state">
<el-empty description="暂无待办事项" />
</div>
<div v-if="loading" class="loading-state">
<el-skeleton :rows="6" animated />
</div>
</div>
<el-pagination
class="pagination"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="queryParams.pageNum"
:page-sizes="[5, 10, 20, 50]"
:page-size="queryParams.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
>
</el-pagination>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { listTodo } from '@/api/workflow/task.js'
import { markRaw } from 'vue'
import {
Document,
User,
ChatDotRound,
Warning,
Files,
DataLine,
Operation,
Setting
} from '@element-plus/icons-vue'
const router = useRouter()
// 待办事项数据
const todoList = ref([])
const loading = ref(true)
// 查询参数
const queryParams = ref({
pageNum: 1,
pageSize: 10,
priority: '',
status: ''
})
// 分页参数
const total = ref(0)
// 获取待办事项列表
const getList = async () => {
loading.value = true
try {
const response = await listTodo(queryParams.value)
if (response.code === 200) {
// 将工作流任务数据转换为待办事项格式
const rows = response.rows || [];
todoList.value = rows.map((task, index) => ({
id: task.id || index,
title: task.taskName || task.name || '待办事项',
desc: task.description || '暂无描述',
priority: getPriorityFromTask(task),
status: getTaskStatus(task.status || task.state),
icon: getTaskIcon(task.category),
time: formatDate(task.createTime || task.createTimeStr),
taskInfo: task // 保存原始任务信息,便于后续处理
}))
total.value = response.total || 0
} else {
console.error('获取待办事项失败:', response.msg)
}
} catch (error) {
console.error('获取待办事项异常:', error)
} finally {
loading.value = false
}
}
// 根据任务信息确定优先级
const getPriorityFromTask = (task) => {
// 根据任务的某些属性来确定优先级,这里可以根据实际业务调整
if (task.priority && task.priority > 50) return 'high'
if (task.priority && task.priority > 20) return 'medium'
return 'low'
}
// 获取任务状态
const getTaskStatus = (status) => {
// 根据实际返回的状态值映射
if (status === 'completed' || status === 'finish') return 'completed'
if (status === 'processing' || status === 'active') return 'processing'
return 'pending'
}
// 获取任务图标
const getTaskIcon = (category) => {
// 根据任务分类确定图标
if (category && category.includes('approval')) return markRaw(Document)
if (category && category.includes('patient')) return markRaw(User)
if (category && category.includes('feedback')) return markRaw(ChatDotRound)
if (category && category.includes('warning')) return markRaw(Warning)
if (category && category.includes('report')) return markRaw(Files)
if (category && category.includes('data')) return markRaw(DataLine)
if (category && category.includes('operation')) return markRaw(Operation)
if (category && category.includes('system')) return markRaw(Setting)
return markRaw(Document) // 默认图标
}
// 格式化日期
const formatDate = (dateStr) => {
if (!dateStr) return '刚刚'
const date = new Date(dateStr)
const now = new Date()
const diffMs = now - date
const diffMins = Math.floor(diffMs / 60000)
const diffHours = Math.floor(diffMins / 60)
const diffDays = Math.floor(diffHours / 24)
if (diffMins < 1) return '刚刚'
if (diffMins < 60) return `${diffMins}分钟前`
if (diffHours < 24) return `${diffHours}小时前`
if (diffDays < 7) return `${diffDays}天前`
// 返回具体日期
return `${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`
}
// 搜索
const handleQuery = () => {
queryParams.value.pageNum = 1
getList()
}
// 重置查询
const resetQuery = () => {
queryParams.value = {
pageNum: 1,
pageSize: 10,
priority: '',
status: ''
}
getList()
}
// 分页大小改变
const handleSizeChange = (size) => {
queryParams.value.pageSize = size
getList()
}
// 当前页改变
const handleCurrentChange = (page) => {
queryParams.value.pageNum = page
getList()
}
onMounted(() => {
getList()
})
// 获取优先级颜色
const getPriorityColor = (priority) => {
switch (priority) {
case 'high': return '#F56C6C'
case 'medium': return '#E6A23C'
case 'low': return '#67C23A'
default: return '#909399'
}
}
// 获取优先级文本
const getPriorityText = (priority) => {
switch (priority) {
case 'high': return '高'
case 'medium': return '中'
case 'low': return '低'
default: return ''
}
}
// 获取状态标签类型
const getStatusTagType = (status) => {
switch (status) {
case 'pending': return 'info'
case 'processing': return 'warning'
case 'completed': return 'success'
default: return 'info'
}
}
// 获取状态文本
const getStatusText = (status) => {
switch (status) {
case 'pending': return '未处理'
case 'processing': return '处理中'
case 'completed': return '已完成'
default: return ''
}
}
// 处理待办事项点击
const handleTodoClick = (todo) => {
handleDetails(todo)
}
// 处理立即处理按钮
const handleProcess = (todo) => {
console.log('处理待办事项:', todo)
// 这里可以添加具体的处理逻辑
router.push(`/todo/process/${todo.id}`)
}
// 处理查看详情按钮
const handleDetails = (todo) => {
console.log('查看详情:', todo)
// 这里可以添加查看详细信息的逻辑
router.push(`/todo/detail/${todo.id}`)
}
onMounted(() => {
getList()
})
</script>
<style scoped lang="scss">
.todo-container {
padding: 20px;
background: #f5f7fa;
min-height: calc(100vh - 120px);
.page-header {
margin-bottom: 20px;
text-align: center;
h2 {
font-size: 24px;
color: #303133;
margin-bottom: 8px;
}
p {
font-size: 14px;
color: #909399;
}
}
.filter-section {
background: white;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
.todo-list {
.todo-item {
margin-bottom: 16px;
cursor: pointer;
transition: all 0.3s ease;
border-left: 4px solid #409EFF;
&:hover {
transform: translateX(4px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
&.priority-high {
border-left-color: #F56C6C;
}
&.priority-medium {
border-left-color: #E6A23C;
}
&.priority-low {
border-left-color: #67C23A;
}
&.status-completed {
opacity: 0.7;
}
.todo-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
.todo-title {
display: flex;
align-items: center;
gap: 8px;
.title-text {
font-size: 16px;
font-weight: 600;
color: #303133;
}
}
}
.todo-content {
.todo-desc {
color: #606266;
margin-bottom: 12px;
line-height: 1.5;
}
.todo-meta {
display: flex;
justify-content: space-between;
font-size: 12px;
color: #909399;
.todo-priority {
font-weight: 500;
}
}
}
.todo-actions {
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid #ebeef5;
display: flex;
justify-content: flex-end;
gap: 8px;
}
}
.empty-state {
text-align: center;
padding: 40px 0;
}
.loading-state {
padding: 20px 0;
}
}
.pagination {
margin-top: 20px;
text-align: center;
}
}
</style>