Browse Source

排产和三方确认

master
han\hanst 3 weeks ago
parent
commit
8e481e80c3
  1. 137
      src/assets/scss/approval-notification.scss
  2. 355
      src/components/ApprovalNotificationManager.vue
  3. 11
      src/config/approval-notification.config.js

137
src/assets/scss/approval-notification.scss

@ -1,6 +1,7 @@
/**
* 审批通知样式
* 优化 Element UI Notification 组件的显示效果
* 支持合并通知经理审批计划员排产三方确认
*/
/* 审批通知容器 */
@ -10,13 +11,6 @@
border-left: 3px solid #E6A23C;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
&:hover {
cursor: pointer;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
transform: translateX(-5px);
transition: all 0.3s ease;
}
/* 标题样式 */
.el-notification__title {
font-size: 16px;
@ -52,6 +46,113 @@
}
}
/* ==========================================
* 合并通知样式包含多种待办类型
* ========================================== */
.el-notification.combined-notification {
width: 400px;
padding: 16px 20px;
border-left: 3px solid #E6A23C;
.el-notification__title {
color: #E6A23C;
}
/* 合并通知主体 */
.combined-notification-body {
display: flex;
flex-direction: column;
gap: 0;
}
/* 通知项(每一行可点击的区块) */
.notification-item {
display: flex;
align-items: center;
padding: 10px 12px;
margin: 0 -12px;
border-radius: 6px;
cursor: pointer;
transition: all 0.25s ease;
border-bottom: 1px dashed #EBEEF5;
&:last-child {
border-bottom: none;
}
&:hover {
background-color: #F5F7FA;
transform: translateX(3px);
.notification-item-link {
opacity: 1;
}
}
&:active {
background-color: #EBEEF5;
}
}
/* 通知项图标 */
.notification-item-icon {
flex-shrink: 0;
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
margin-right: 12px;
}
/* 经理审批图标背景 */
.manager-item .notification-item-icon {
background-color: rgba(230, 162, 60, 0.1);
}
/* 计划员排产图标背景 */
.planner-item .notification-item-icon {
background-color: rgba(64, 158, 255, 0.1);
}
/* 三方确认图标背景 */
.tri-confirm-item .notification-item-icon {
background-color: rgba(103, 194, 58, 0.1);
}
/* 通知项内容区 */
.notification-item-content {
flex: 1;
min-width: 0;
}
/* 通知项文本 */
.notification-item-text {
margin: 0 0 2px 0 !important;
font-size: 13px;
color: #303133;
line-height: 1.5 !important;
}
/* 数字高亮 */
.count-highlight {
font-size: 17px;
font-weight: bold;
padding: 0 3px;
}
/* 点击提示链接 */
.notification-item-link {
margin: 0 !important;
font-size: 12px;
color: #909399;
opacity: 0.7;
transition: opacity 0.25s ease;
line-height: 1.4 !important;
}
}
/* 新申请单通知 */
.el-notification.new-approval-notification {
border-left-color: #409EFF;
@ -101,4 +202,26 @@
width: 320px;
padding: 15px;
}
.el-notification.combined-notification {
width: 340px;
.notification-item {
padding: 8px 10px;
}
.notification-item-icon {
width: 30px;
height: 30px;
margin-right: 8px;
}
.notification-item-text {
font-size: 12px;
}
.count-highlight {
font-size: 15px;
}
}
}

355
src/components/ApprovalNotificationManager.vue

@ -4,17 +4,17 @@
</template>
<script>
import { getPendingApplyList } from '@/api/erf/erf'
import { getPendingApplyList, getPendingTriConfirmList } from '@/api/erf/erf'
import approvalConfig from '@/config/approval-notification.config'
/**
* 全局审批通知管理器
*
* 功能
* 1. 登录后自动检查待审批项
* 1. 登录后自动检查待审批项经理审批计划员排产三方确认
* 2. 定时轮询检查新的待审批项默认5分钟可在配置文件中修改
* 3. 在浏览器右下角弹出通知提示
* 4. 点击通知跳转到审批页面
* 3. 在浏览器右下角弹出合并通知提示
* 4. 点击不同类型的提醒跳转到对应页面
*
* 配置文件src/config/approval-notification.config.js
*/
@ -33,6 +33,12 @@ export default {
isChecking: false, //
firstCheckTimeout: null, //
activeNotifications: [], //
//
lastCounts: {
manager: 0,
planner: 0,
triConfirm: 0
}
}
},
@ -175,6 +181,7 @@ export default {
this.isChecking = false
this.notifiedApplications.clear()
this.lastCheckTime = null
this.lastCounts = { manager: 0, planner: 0, triConfirm: 0 }
console.log('[审批通知] 通知系统已停止')
},
@ -242,7 +249,8 @@ export default {
},
/**
* 检查待审批的申请单
* 检查所有类型的待办事项经理审批计划员排产三方确认
* 三个接口并行调用结果合并后显示一个通知
* @param {boolean} isFirstCheck - 是否首次检查
*/
checkPendingApprovals(isFirstCheck = false) {
@ -258,221 +266,208 @@ export default {
}
this.isChecking = true
console.log(`[审批通知] 开始检查待审批申请单... (首次检查: ${isFirstCheck})`)
console.log(`[审批通知] 开始检查所有待办事项... (首次检查: ${isFirstCheck})`)
const requestData = {
// 1.
const managerRequest = getPendingApplyList({
site: this.currentSite,
currentUserId: this.currentUserId,
userName: this.currentUserName,
pageType : 'MANAGER', //
pendingStatus: '已下达', //
pageType: 'MANAGER',
pendingStatus: '已下达',
page: 1,
limit: 20
}).then(({data}) => {
if (data && data.code === 0) {
const list = data.rows || (data.page && data.page.list) || []
return { count: data.totalCount || (data.page && data.page.totalCount) || list.length, list: list }
}
return { count: 0, list: [] }
}).catch(() => ({ count: 0, list: [] }))
getPendingApplyList(requestData).then(({data}) => {
// 2.
const plannerRequest = getPendingApplyList({
site: this.currentSite,
currentUserId: this.currentUserId,
userName: this.currentUserName,
pageType: 'PLANNER',
pendingStatus: '已批准',
page: 1,
limit: 20
}).then(({data}) => {
if (data && data.code === 0) {
const pendingList = data.rows || data.page.list || []
const list = data.rows || (data.page && data.page.list) || []
return { count: data.totalCount || (data.page && data.page.totalCount) || list.length, list: list }
}
return { count: 0, list: [] }
}).catch(() => ({ count: 0, list: [] }))
// 3.
const triConfirmRequest = getPendingTriConfirmList({
currentUserId: this.currentUserId,
site: this.currentSite
}).then(({data}) => {
if (data && data.code === 0) {
const list = data.list || []
return { count: list.length, list: list }
}
return { count: 0, list: [] }
}).catch(() => ({ count: 0, list: [] }))
//
Promise.all([managerRequest, plannerRequest, triConfirmRequest]).then(([managerResult, plannerResult, triConfirmResult]) => {
const counts = {
manager: managerResult.count,
planner: plannerResult.count,
triConfirm: triConfirmResult.count
}
console.log(`[审批通知] 查询结果 - 经理审批: ${counts.manager}, 计划员排产: ${counts.planner}, 三方确认: ${counts.triConfirm}`)
console.log(`[审批通知] 发现 ${pendingList.length} 个待审批申请单`)
const totalCount = counts.manager + counts.planner + counts.triConfirm
if (pendingList.length > 0) {
if (totalCount > 0) {
if (isFirstCheck) {
//
this.showSummaryNotification(pendingList)
//
this.showCombinedNotification(counts)
} else {
//
this.showNewApprovalNotifications(pendingList)
//
const hasNewItems = counts.manager > this.lastCounts.manager ||
counts.planner > this.lastCounts.planner ||
counts.triConfirm > this.lastCounts.triConfirm
if (hasNewItems) {
//
this.closeAllNotifications()
this.showCombinedNotification(counts)
}
}
}
//
this.lastCounts = { ...counts }
this.lastCheckTime = new Date()
}
this.isChecking = false
}).catch(error => {
console.error('[审批通知] 检查待审批申请单失败:', error)
console.error('[审批通知] 检查待办事项失败:', error)
this.isChecking = false
})
},
/**
* 显示汇总通知首次登录时
* @param {Array} approvalList - 待审批列表
* 显示合并通知包含经理审批计划员排产三方确认
* 使用 VNode 创建可点击的分区通知
* @param {Object} counts - 各类型待办数量 { manager, planner, triConfirm }
*/
showSummaryNotification(approvalList) {
const count = approvalList.length
//
approvalList.forEach(item => {
this.notifiedApplications.add(item.applyNo || item.applicationNo)
showCombinedNotification(counts) {
const h = this.$createElement
let notificationRef = null
//
const navigateAndClose = (path) => {
if (notificationRef) {
this.removeNotification(notificationRef)
notificationRef.close()
}
this.$router.push({ path: path }).catch(err => {
this.log('路由跳转失败: ' + err.message, 'warn')
})
//
const notificationInstance = this.$notify({
title: '待审批提醒',
customClass: this.config.style.summaryClass,
dangerouslyUseHTMLString: true,
message: `
<div style="line-height: 1.6;">
<p style="margin: 0 0 8px 0;">
您有 <span class="count-highlight">${count}</span> 个工程实验申请单待审批
</p>
<p style="margin: 0; font-size: 12px; color: #909399;">
点击此通知查看详情
</p>
</div>
`,
type: 'warning',
position: this.config.notification.position,
duration: this.config.notification.summaryDuration,
showClose: this.config.notification.showClose,
onClick: () => {
//
this.removeNotification(notificationInstance)
notificationInstance.close()
//
this.navigateToApprovalList()
},
onClose: () => {
//
this.removeNotification(notificationInstance)
}
})
//
this.activeNotifications.push(notificationInstance)
console.log(`[审批通知] 已创建汇总通知,当前活动通知数: ${this.activeNotifications.length}`)
//
// this.playNotificationSound()
},
/**
* 显示新的审批通知轮询时
* @param {Array} approvalList - 待审批列表
*/
showNewApprovalNotifications(approvalList) {
//
const newApprovals = approvalList.filter(item => {
const applyNo = item.applyNo || item.applicationNo
return !this.notifiedApplications.has(applyNo)
})
if (newApprovals.length === 0) {
//
const sections = []
//
if (counts.manager > 0) {
sections.push(
h('div', {
class: 'notification-item manager-item',
on: { click: (e) => { e.stopPropagation(); navigateAndClose('erf-expApplyApproval') } }
}, [
h('div', { class: 'notification-item-icon' }, [
h('i', { class: 'el-icon-s-check', style: { color: '#E6A23C', fontSize: '18px' } })
]),
h('div', { class: 'notification-item-content' }, [
h('p', { class: 'notification-item-text' }, [
'您有 ',
h('span', { class: 'count-highlight', style: { color: '#E6A23C' } }, counts.manager),
' 个工程实验申请单待审批'
]),
h('p', { class: 'notification-item-link' }, '点击此通知查看详情')
])
])
)
}
//
if (counts.planner > 0) {
sections.push(
h('div', {
class: 'notification-item planner-item',
on: { click: (e) => { e.stopPropagation(); navigateAndClose('erf-plannerSchedule') } }
}, [
h('div', { class: 'notification-item-icon' }, [
h('i', { class: 'el-icon-date', style: { color: '#409EFF', fontSize: '18px' } })
]),
h('div', { class: 'notification-item-content' }, [
h('p', { class: 'notification-item-text' }, [
'您有 ',
h('span', { class: 'count-highlight', style: { color: '#409EFF' } }, counts.planner),
' 个工程实验申请单待排产'
]),
h('p', { class: 'notification-item-link' }, '点击此通知查看详情')
])
])
)
}
//
if (counts.triConfirm > 0) {
sections.push(
h('div', {
class: 'notification-item tri-confirm-item',
on: { click: (e) => { e.stopPropagation(); navigateAndClose('erf-triConfirm') } }
}, [
h('div', { class: 'notification-item-icon' }, [
h('i', { class: 'el-icon-finished', style: { color: '#67C23A', fontSize: '18px' } })
]),
h('div', { class: 'notification-item-content' }, [
h('p', { class: 'notification-item-text' }, [
'您有 ',
h('span', { class: 'count-highlight', style: { color: '#67C23A' } }, counts.triConfirm),
' 个三方确认工序待确认'
]),
h('p', { class: 'notification-item-link' }, '点击此通知查看详情')
])
])
)
}
//
if (sections.length === 0) {
return
}
console.log(`[审批通知] 发现 ${newApprovals.length} 个新的待审批申请单`)
//
newApprovals.forEach((item, index) => {
//
setTimeout(() => {
this.showSingleApprovalNotification(item)
}, index * 500)
})
//
//this.playNotificationSound()
},
/**
* 显示单个审批通知
* @param {Object} approvalItem - 审批项信息
*/
showSingleApprovalNotification(approvalItem) {
const applyNo = approvalItem.applyNo || approvalItem.applicationNo
const applicant = approvalItem.creatorName || approvalItem.applyUserName || '未知'
const applyType = approvalItem.experimentType || approvalItem.applicationType || '未知'
const createdDate = approvalItem.createTime || approvalItem.applyDate || ''
//
this.notifiedApplications.add(applyNo)
// VNode
const message = h('div', { class: 'combined-notification-body' }, sections)
//
const notificationInstance = this.$notify({
title: '新的审批申请',
customClass: this.config.style.singleClass,
dangerouslyUseHTMLString: true,
message: `
<div style="line-height: 1.8;">
<p style="margin: 0 0 5px 0;">
<strong style="color: #303133;">申请单号</strong>
<span style="color: #409EFF;">${applyNo}</span>
</p>
<p style="margin: 0 0 5px 0;">
<strong style="color: #303133;">申请人</strong>${applicant}
</p>
<p style="margin: 0 0 5px 0;">
<strong style="color: #303133;">申请类型</strong>${applyType}
</p>
${createdDate ? `
<p style="margin: 0 0 5px 0;">
<strong style="color: #303133;">申请时间</strong>
<span style="font-size: 12px;">${createdDate}</span>
</p>
` : ''}
<p style="margin: 8px 0 0 0; font-size: 12px; color: #909399; border-top: 1px dashed #DCDFE6; padding-top: 8px;">
点击处理审批
</p>
</div>
`,
type: 'info',
notificationRef = this.$notify({
title: '待审批提醒',
customClass: 'approval-notification combined-notification',
message: message,
type: 'warning',
position: this.config.notification.position,
duration: this.config.notification.singleDuration,
duration: this.config.notification.summaryDuration,
showClose: this.config.notification.showClose,
onClick: () => {
//
this.removeNotification(notificationInstance)
notificationInstance.close()
//
this.navigateToApprovalDetail(approvalItem)
},
onClose: () => {
//
this.removeNotification(notificationInstance)
this.removeNotification(notificationRef)
}
})
//
this.activeNotifications.push(notificationInstance)
console.log(`[审批通知] 已创建单个通知 (${applyNo}),当前活动通知数: ${this.activeNotifications.length}`)
},
/**
* 跳转到审批列表页面
*/
navigateToApprovalList() {
//
this.$router.push({
path: 'erf-expApplyApproval'
}).catch(err => {
this.log('路由跳转失败:', err.message, 'warn')
})
},
/**
* 跳转到审批详情页面
* @param {Object} approvalItem - 审批项信息
*/
navigateToApprovalDetail(approvalItem) {
const applyNo = approvalItem.applyNo || approvalItem.applicationNo
//
const queryParam = this.config.routes.detailQueryParam
const query = {
[queryParam]: applyNo,
mode: 'approve'
}
this.$router.push({
path: 'erf-expApplyApproval',
query: query
}).catch(err => {
//
this.log('路由跳转失败,尝试跳转到列表页: ' + err.message, 'warn')
this.navigateToApprovalList()
})
this.activeNotifications.push(notificationRef)
console.log(`[审批通知] 已创建合并通知(经理:${counts.manager}, 排产:${counts.planner}, 三方:${counts.triConfirm}),当前活动通知数: ${this.activeNotifications.length}`)
},
/**

11
src/config/approval-notification.config.js

@ -73,11 +73,14 @@ export default {
* 路由配置
*/
routes: {
// 审批列表页面路由
approvalList: '/erf/approval-list',
// 经理审批页面路由
managerApproval: 'erf-expApplyApproval',
// 审批详情页面路由
approvalDetail: '/erf/approval-detail',
// 计划员排产页面路由
plannerSchedule: 'erf-plannerSchedule',
// 三方确认页面路由
triConfirm: 'erf-triConfirm',
// 详情页面查询参数名称
detailQueryParam: 'applyNo'

Loading…
Cancel
Save