当前位置: 首页 > news >正文

关于审批流的记录

第三步:前端实现(审批消息展示与操作)
基于 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>

 

2. 审批详情页(处理审批操作)

审批人点击「处理」后进入详情页,查看申请信息并执行同意 / 拒绝操作:
 
<template> <el-card class="approval-detail-card"> <div slot="header" class="card-header"> <h2>审批详情</h2> <span class="apply-no">审批单号:{{ applyDetail.applyNo }}</span> </div> <!-- 申请基本信息 --> <el-form :model="applyDetail" label-width="120px" class="apply-form"> <el-form-item label="审批类型"> <el-tag type="info">{{ applyTypeMap[applyDetail.applyType] }}</el-tag> </el-form-item> <el-form-item label="申请人"> {{ applyDetail.applyUserName }}({{ applyDetail.applyTime | formatTime }}) </el-form-item> <el-form-item label="申请内容"> <div class="business-info">{{ applyDetail.businessInfo }}</div> </el-form-item> <el-form-item label="申请备注"> <div class="apply-remark">{{ applyDetail.remark || '无' }}</div> </el-form-item> <el-form-item label="审批节点"> <div class="approval-node-list"> <div v-for="(node, index) in approvalNodeList" :key="node.id" class="approval-node" > <div class="node-header"> <span class="node-seq">第{{ node.nodeSeq }}审批人</span> <el-tag :type="getNodeStatusTagType(node.nodeStatus)"> {{ getNodeStatusText(node.nodeStatus) }} </el-tag> </div> <div class="node-content"> <p>审批人:{{ node.approverName }}</p> <p>审批时间:{{ node.approveTime ? (node.approveTime | formatTime) : '未处理' }}</p> <p>审批意见:{{ node.approveOpinion || '无' }}</p> </div> <!-- 当前用户的待处理节点:显示审批操作 --> <div v-if="node.nodeStatus === 0 && node.approverId === currentUserId" class="node-operation"> <el-form :model="handleForm" ref="handleFormRef" label-width="80px"> <el-form-item label="审批结果" prop="approveResult" required> <el-radio-group v-model="handleForm.approveResult"> <el-radio label="1">同意</el-radio> <el-radio label="2">拒绝</el-radio> </el-radio-group> </el-form-item> <el-form-item label="审批意见" prop="opinion"> <el-input v-model="handleForm.opinion" type="textarea" rows="3" placeholder="请输入审批意见(可选)" ></el-input> </el-form-item> <el-form-item> <el-button @click="handleCancel">取消</el-button> <el-button type="primary" @click="handleSubmitApproval">提交审批</el-button> </el-form-item> </el-form> </div> </div> </div> </el-form-item> </el-form> </el-card> </template> <script> import { getApprovalDetail, handleApproval } from '@/api/hrm/approval'; export default { filters: { formatTime(time) { return new Date(time).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); } }, data() { return { applyDetail: {}, // 审批申请详情 approvalNodeList: [], // 审批节点列表 currentUserId: this.$store.state.user.id, // 当前用户ID applyTypeMap: { // 审批类型映射 'SHIFT_SWAP': '调班申请' }, // 审批操作表单 handleForm: { nodeId: '', // 当前处理的节点ID approveResult: '1', // 默认同意 opinion: '' } }; }, mounted() { // 从路由参数获取 applyId 和 nodeId const { applyId, nodeId } = this.$route.query; this.handleForm.nodeId = nodeId; this.loadApprovalDetail(applyId); }, methods: { // 加载审批详情 async loadApprovalDetail(applyId) { const res = await getApprovalDetail(applyId); this.applyDetail = res.data.apply; this.approvalNodeList = res.data.nodes; }, // 获取节点状态文本 getNodeStatusText(status) { const statusMap = { 0: '待审批', 1: '已同意', 2: '已拒绝' }; return statusMap[status] || '未知'; }, // 获取节点状态标签类型 getNodeStatusTagType(status) { const typeMap = { 0: 'warning', 1: 'success', 2: 'danger' }; return typeMap[status] || 'info'; }, // 提交审批 async handleSubmitApproval() { this.$refs.handleFormRef.validate(async (valid) => { if (valid) { // 转换 approveResult 为数字(1=同意,2=拒绝) this.handleForm.approveResult = Number(this.handleForm.approveResult); await handleApproval(this.handleForm); this.$message.success('审批操作已提交'); this.$router.push('/hrm/approval/todo-list'); // 返回待审批列表 } }); }, // 取消 handleCancel() { this.$router.go(-1); } } }; </script> <style scoped> .approval-detail-card { margin: 20px; } .card-header { display: flex; justify-content: space-between; align-items: center; } .apply-no { font-size: 14px; color: #666; } .apply-form { margin-top: 20px; } .business-info, .apply-remark { padding: 8px; background-color: #f5f7fa; border-radius: 4px; color: #333; } .approval-node-list { margin-top: 10px; } .approval-node { padding: 15px; border: 1px solid #f5f5f5; border-radius: 4px; margin-bottom: 10px; } .node-header { display: flex; justify-content: space-between; margin-bottom: 10px; } .node-seq { font-weight: 500; color: #333; } .node-operation { margin-top: 15px; padding-top: 15px; border-top: 1px dashed #eee; } </style>
 
 

五、第四步:实时消息推送(可选,提升体验)

为了让审批人实时收到新的审批消息(无需刷新页面),可集成 WebSocket 实现实时推送:
  1. 后端:基于 Spring WebSocket 实现用户消息订阅(按用户 ID 分组)。
  2. 前端:页面加载时建立 WebSocket 连接,监听审批消息事件,收到消息后更新未读计数和待审批列表。
示例(前端 WebSocket 封装):
// src/utils/websocket.js let websocket = null; export function initWebSocket(userId) { if ('WebSocket' in window) { // 连接地址(如 ws://localhost:8080/ws/approval?userId=123) const wsUrl = `${process.env.VUE_APP_WS_BASE_URL}/ws/approval?userId=${userId}`; websocket = new WebSocket(wsUrl); // 连接成功 websocket.onopen = function() { console.log('WebSocket 连接成功'); }; // 接收消息 websocket.onmessage = function(event) { const message = JSON.parse(event.data); // 触发全局事件,让其他组件监听 window.dispatchEvent(new CustomEvent('approvalNotify', { detail: message })); }; // 连接关闭 websocket.onclose = function() { console.log('WebSocket 连接关闭,3秒后重连'); setTimeout(() => initWebSocket(userId), 3000); // 自动重连 }; // 连接错误 websocket.onerror = function() { console.error('WebSocket 连接错误'); }; } else { alert('您的浏览器不支持 WebSocket,无法接收实时消息'); } } // 关闭连接 export function closeWebSocket() { if (websocket) { websocket.close(); } }
 
在全局入口(如 main.js 或 App.vue)初始化 WebSocket:
import { initWebSocket, closeWebSocket } from '@/utils/websocket'; // 登录后初始化(从全局状态获取用户ID) if (store.state.user.id) { initWebSocket(store.state.user.id); } // 监听全局消息事件,更新未读消息 window.addEventListener('approvalNotify', (event) => { const message = event.detail; if (message.type === 'APPROVAL_TODO') { // 刷新待审批列表和未读计数 const notifyComponent = window.notifyComponent; // 假设消息组件暴露到全局 if (notifyComponent) { notifyComponent.loadTodoList(); notifyComponent.loadUnreadCount(); } // 显示消息提示 ElMessage({ title: '新的审批通知', message: message.content, type: 'info', duration: 5000 }); } }); // 页面关闭时关闭连接 window.addEventListener('beforeunload', closeWebSocket);
http://www.hskmm.com/?act=detail&tid=27452

相关文章:

  • CF1726E Almost Perfect
  • 如何基于Elasticsearch实现问题联想?
  • 技术人的阅读提效神器:多语言智能中文摘要生成指令
  • 数据结构(树)
  • CSP-S模拟28
  • 形式化验证提升RSA性能与部署效率
  • AI元人文的硅基实现可行性Ai研究报告
  • 利用linux系统自带的cron 定时备份数据库,不需要写代码了
  • centos服务器实时备份
  • 666
  • P14150 不动鸣神,恒常乐土
  • python本地生成验证码图片
  • CentOS 7 一键安装 vsftpd 并创建可登录 FTP 用户 test - 教程
  • 【完结】-固态硬盘ssd
  • 破解工地防盗难题:如何利用国标GB28181视频平台EasyCVR实现视频监控统一管理?
  • autogen论文解读 - Sun
  • 高效仿真:功耗与散热攻略
  • Vue的nextTick函数作用
  • #6515. 「雅礼集训 2018 Day10」贪玩蓝月
  • 公告
  • 车企数据治理平台化实战:从数据孤岛到全链路治理的架构演进
  • 磁盘的管理
  • 完整教程:Java中的缓存机制与分布式缓存实现!
  • jsconfig.json-vscode或cursor ctrl点击@路径,快速到达
  • C# 弃元模式:从语法糖到性能利器的深度解析
  • 2025钣金加工厂家最新推荐榜:精密工艺与定制服务口碑之选
  • python查询数据信息,分析前了解表格结构
  • 减少磁盘延迟的方法
  • AutoCAD 2025 CAD 安装包中文永久免费免激活破解版下载 附图文安装教程
  • nmcli修改ip地址