105 lines
3.6 KiB
Vue
105 lines
3.6 KiB
Vue
|
|
<template>
|
|||
|
|
<!-- 无渲染组件:只负责全局待办强提醒,不产生任何 DOM -->
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
/**
|
|||
|
|
* 借库转交 · 全局待办强提醒
|
|||
|
|
*
|
|||
|
|
* 解决的问题:双向握手引入后,发起方提交了转交,接收人若不去借还记录页主动
|
|||
|
|
* 查看,就完全处于盲区 —— 物品在系统里挂着「待接收」,责任悬空。
|
|||
|
|
* 本组件在应用启动后主动查询「待我接收」的数量,并用不会自动消失的
|
|||
|
|
* ElNotification 强提醒,附一键跳转到借还记录页。
|
|||
|
|
*
|
|||
|
|
* ★ 为什么挂在 Layout 而非登录页:
|
|||
|
|
* Layout 只在已登录区域渲染,天然保证「有 token 才查」;
|
|||
|
|
* 且它是路由切换时**常驻**的组件(AppMain 换页不会卸载它),
|
|||
|
|
* 轮询定时器不会被反复创建销毁。
|
|||
|
|
*
|
|||
|
|
* ★ 防骚扰(两道):
|
|||
|
|
* 1) 会话级去重:sessionStorage 记「上次已提醒过的数量」,只有数量**变化**
|
|||
|
|
* 才再弹。刷新页面不会重复轰炸(用 sessionStorage 而非 Pinia ——
|
|||
|
|
* 前者跨刷新存活,后者会重置)。
|
|||
|
|
* 2) 数量归零时清掉记录,下次再来新转交能重新提醒。
|
|||
|
|
*
|
|||
|
|
* ★ 错误一律静默:待办提醒是锦上添花,绝不能因为接口抖动就弹错误框刷屏。
|
|||
|
|
*/
|
|||
|
|
import { onMounted, onUnmounted } from 'vue'
|
|||
|
|
import { useRouter } from 'vue-router'
|
|||
|
|
import { h } from 'vue'
|
|||
|
|
import { ElButton, ElNotification } from 'element-plus'
|
|||
|
|
import { getPendingTransferCount } from '@/api/transaction'
|
|||
|
|
import { useUserStore } from '@/stores/user'
|
|||
|
|
|
|||
|
|
const router = useRouter()
|
|||
|
|
const userStore = useUserStore()
|
|||
|
|
|
|||
|
|
const SS_KEY = 'pendingTransferNotifiedCount'
|
|||
|
|
// 轮询间隔:需求只要求「初始化时查一次」,但那样已打开的页面永远收不到提醒
|
|||
|
|
// (SPA 只在首次进入时初始化)。加一轮低频轮询才能真正做到「第一时间响应」,
|
|||
|
|
// 有了上面的去重逻辑,轮询不会造成重复打扰。若不需要,删掉定时器即可。
|
|||
|
|
const POLL_MS = 2 * 60 * 1000
|
|||
|
|
|
|||
|
|
let timer: ReturnType<typeof setInterval> | null = null
|
|||
|
|
|
|||
|
|
const notify = (count: number) => {
|
|||
|
|
const notification = ElNotification({
|
|||
|
|
title: '待办通知:借库转交',
|
|||
|
|
type: 'warning',
|
|||
|
|
duration: 0, // 不自动关闭,必须用户手动处理或点掉
|
|||
|
|
showClose: true,
|
|||
|
|
// 用 VNode 而非 dangerouslyUseHTMLString:避免把数量拼进 HTML 字符串
|
|||
|
|
message: h('div', [
|
|||
|
|
h('div', `您有 ${count} 件物品等待接收确认,请及时处理。`),
|
|||
|
|
h(
|
|||
|
|
ElButton,
|
|||
|
|
{
|
|||
|
|
type: 'primary',
|
|||
|
|
link: true,
|
|||
|
|
size: 'small',
|
|||
|
|
style: 'margin-top:6px; padding:0;',
|
|||
|
|
onClick: () => {
|
|||
|
|
notification.close()
|
|||
|
|
router.push('/operation/records')
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
() => '去处理 >'
|
|||
|
|
),
|
|||
|
|
]),
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const check = async () => {
|
|||
|
|
if (!userStore.token) return
|
|||
|
|
let count = 0
|
|||
|
|
try {
|
|||
|
|
const res: any = await getPendingTransferCount()
|
|||
|
|
count = Number(res?.count ?? res?.data?.count ?? 0)
|
|||
|
|
} catch {
|
|||
|
|
return // 静默:不打扰用户,也不刷屏报错
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!count) {
|
|||
|
|
// 已处理完:清掉记录,下次再来新转交能重新提醒
|
|||
|
|
sessionStorage.removeItem(SS_KEY)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const notified = Number(sessionStorage.getItem(SS_KEY) || 0)
|
|||
|
|
if (count === notified) return // 数量没变 → 本会话已提醒过,不再轰炸
|
|||
|
|
|
|||
|
|
sessionStorage.setItem(SS_KEY, String(count))
|
|||
|
|
notify(count)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
onMounted(() => {
|
|||
|
|
check()
|
|||
|
|
timer = setInterval(check, POLL_MS)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
onUnmounted(() => {
|
|||
|
|
if (timer) clearInterval(timer)
|
|||
|
|
timer = null
|
|||
|
|
})
|
|||
|
|
</script>
|