第三步:前端实现(审批消息展示与操作)
基于 Vue + Element UI 实现审批人页面的「消息通知」和「待审批列表」,集成到你的现有排班系统中。
1. 全局消息通知(顶部导航栏)
在系统顶部导航栏添加「消息图标」,显示未读消息数
<template> <div class="header-notify"> <!-- 消息图标 + 未读红点 --> <el-dropdown @command="handleNotifyCommand" placement="bottom-right"> <div class="notify-icon"> <el-icon size="20"><Bell /></el-icon> <span v-if="unreadCount > 0" class="unread-dot">{{ unreadCount }}</span> </div> <el-dropdown-menu slot="dropdown" class="notify-dropdown"> <el-dropdown-item disabled class="dropdown-header">待审批({{ unreadCount }})</el-dropdown-item> <el-dropdown-item v-for="todo in todoList" :key="todo.applyId" :command="{ type: 'todo', data: todo }" class="todo-item" > <div class="todo-info"> <p class="todo-title">{{ todo.businessInfo }}</p> <p class="todo-time">{{ formatTime(todo.applyTime) }}</p> </div> <el-button size="mini" type="text" @click.stop="handleGotoApproval(todo)">处理</el-button> </el-dropdown-item> <el-dropdown-item disabled v-if="todoList.length === 0">暂无待审批事项</el-dropdown-item> </el-dropdown-menu> </el-dropdown> </div> </template> <script> import { Bell } from '@element-plus/icons-vue'; import { getApproverTodoList, getUnreadNotifyCount } from '@/api/hrm/approval'; export default { components: { Bell }, data() { return { unreadCount: 0, todoList: [], timer: null // 定时器:定时刷新未读消息 }; }, mounted() { // 初始化加载待审批列表和未读消息数 this.loadTodoList(); this.loadUnreadCount(); // 定时刷新(5分钟一次,可根据需求调整) this.timer = setInterval(() => { this.loadUnreadCount(); this.loadTodoList(); }, 300000); }, beforeUnmount() { clearInterval(this.timer); // 清除定时器 }, methods: { // 加载待审批列表 async loadTodoList() { const userId = this.$store.state.user.id; // 从全局状态获取当前用户ID const res = await getApproverTodoList(userId); this.todoList = res.data; }, // 加载未读消息数 async loadUnreadCount() { const userId = this.$store.state.user.id; const res = await getUnreadNotifyCount(userId); this.unreadCount = res.data; }, // 格式化时间 formatTime(time) { return new Date(time).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); }, // 处理消息命令(如点击“处理”按钮) handleNotifyCommand(command) { if (command.type === 'todo') { this.handleGotoApproval(command.data); } }, // 跳转到审批详情页 handleGotoApproval(todo) { this.$router.push({ path: '/hrm/approval/detail', query: { applyId: todo.applyId, nodeId: todo.nodeId } }); } } }; </script> <style scoped> .header-notify { position: relative; margin-right: 20px; cursor: pointer; } .unread-dot { position: absolute; top: -5px; right: -5px; width: 18px; height: 18px; line-height: 18px; border-radius: 50%; background-color: #f56c6c; color: white; font-size: 12px; text-align: center; } .notify-dropdown { width: 400px; max-height: 500px; overflow-y: auto; } .todo-item { padding: 12px; border-bottom: 1px solid #f5f5f5; } .todo-title { font-size: 14px; color: #333; margin-bottom: 4px; } .todo-time { font-size: 12px; color: #999; } </style>