签字不显示问题

This commit is contained in:
2026-10-27 20:11:19 +08:00
parent f17f6153b5
commit 3059603186
3 changed files with 360 additions and 197 deletions
@@ -35,22 +35,58 @@ module.exports = {
},
data() {
return {
//前端唯一标识 当然后端还有用户id作为唯一标识
// 二维码携带本次组件实例的标识,与服务端当前登录用户共同确定签字记录。
id: new Date().getTime() + Math.floor(Math.random() * 10000) + 1,
signatureAddress: null,
interval: null,
connectionWaitResolve: null,
unsubscribeSignature: null,
signatureContent: null,
loading: true,
}
},
methods: {
async init() {
const {code, data} = await this.$axios.post("/platform/signature/get")
if (code === 0) {
this.signatureContent = data && data.signature
this.$emit("input", this.signatureContent)
}
/**
* 无请求参数,返回 Promise;响应 code 为0表示成功,data.signature 为已有签字图片地址。
* 请求结束后由 mounted 的 finally 统一关闭 loading。
*/
init() {
this.signatureAddress = origin + "/platform/signature/scanPcCode?id=" + this.id
return this.$axios.post("/platform/signature/get")
.then((res) => {
if (this._isBeingDestroyed || this._isDestroyed) {
return
}
if (res.code !== 0) {
throw new Error("签字信息加载失败")
}
this.signatureContent = res.data && res.data.signature
this.$emit("input", this.signatureContent)
})
},
// 返回等待完成的 Promise;连接成功、等待30秒或组件销毁时结束等待。
waitForConnection() {
return new Promise((resolve) => {
const startedAt = Date.now()
this.connectionWaitResolve = resolve
const checkReady = () => {
this.interval = null
if (this._isBeingDestroyed || this._isDestroyed ||
(webSocketPubSub.ws && webSocketPubSub.ws.readyState === WebSocket.OPEN)) {
this.connectionWaitResolve = null
resolve()
return
}
if (Date.now() - startedAt >= 30000) {
this.connectionWaitResolve = null
this.$message.warning("签字消息连接超时,请刷新页面重试")
resolve()
return
}
this.interval = setTimeout(checkReady, 100)
}
checkReady()
})
},
signatureAgain() {
const val = sessionStorage.getItem("h5-scan-code-signature-" + this.id)
@@ -58,25 +94,42 @@ module.exports = {
this.$emit("input", val)
}
},
async mounted() {
await this.init()
webSocketPubSub.subscribe("h5-scan-code-signature", (data) => {
console.info("ws:收到签字成功消息:", data)
mounted() {
this.loading = true
// 先登记消息回调,连接成功后由公共连接加入房间,避免请求期间遗漏订阅。
this.unsubscribeSignature = webSocketPubSub.subscribe("h5-scan-code-signature", (data) => {
this.signatureContent = data.value
this.$emit("input", data.value)
})
// 可选:轮询或 watch ready 状态
const checkReady = () => {
if (webSocketPubSub.ws && webSocketPubSub.ws.readyState === WebSocket.OPEN) {
console.info("ws.readyState is open");
this.loading = false;
console.log(this.signatureAddress)
} else {
setTimeout(checkReady, 100);
}
};
checkReady();
this.init()
.then(() => {
if (!this._isBeingDestroyed && !this._isDestroyed) {
return this.waitForConnection()
}
})
.catch(() => {
if (!this._isBeingDestroyed && !this._isDestroyed) {
this.$message.error("签字信息加载失败,请刷新页面重试")
}
})
.finally(() => {
if (!this._isBeingDestroyed && !this._isDestroyed) {
this.loading = false
}
})
},
beforeDestroy() {
// pjax 切换页面时取消轮询和订阅,旧组件不得继续接收签字消息。
clearTimeout(this.interval)
this.interval = null
if (this.connectionWaitResolve) {
this.connectionWaitResolve()
this.connectionWaitResolve = null
}
if (this.unsubscribeSignature) {
this.unsubscribeSignature()
this.unsubscribeSignature = null
}
}
}
</script>
+142 -86
View File
@@ -443,50 +443,76 @@
constructor() {
this.ws = null
this.subscribers = new Map()
this.isSubscribed = false // 添加订阅状态标记
this.isSubscribed = false
this.connectTimer = null
this.reconnectTimer = null
this.pingTimer = null
this.isDisposed = false
this.init()
}
// 页面只创建一个心跳任务;每次连接自行绑定事件及握手超时。
init() {
this.setupWindowEvents()
this.setupPing()
this.connect()
}
// 正在连接或关闭时等待对应事件,避免同时创建多个连接。
connect() {
if (this.isDisposed || this.reconnectTimer !== null ||
(this.ws && this.ws.readyState !== WebSocket.CLOSED)) {
return
}
try {
this.connect()
const WS_URL = window.location.host + "${base}/websocket"
const protocol = window.location.protocol === "http:" ? "ws://" : "wss://"
this.ws = new WebSocket(protocol + WS_URL)
this.isSubscribed = false
this.setupEventListeners()
this.setupPing()
this.setupWindowEvents()
} catch (ex) {
console.info(ex)
const socket = this.ws
// 握手最多等待30秒,心跳不得提前关闭仍处于 CONNECTING 的连接。
this.connectTimer = setTimeout(() => {
this.connectTimer = null
if (this.ws === socket && socket.readyState === WebSocket.CONNECTING) {
console.warn("WebSocket 握手超时,关闭当前连接后重试", socket.url)
socket.close()
}
}, 30000)
} catch (error) {
console.error("WebSocket 创建连接失败", error)
this.reconnect()
}
}
connect() {
const WS_URL = window.location.host + "${base}/websocket"
const protocol = window.location.protocol === "http:" ? "ws://" : "wss://"
this.ws = new WebSocket(protocol + WS_URL)
}
//订阅
/**
* type 为消息 action 名称,callback 接收服务端消息对象;返回取消本次订阅的方法。
* 连接尚未建立时先保留回调,在 onopen 中统一加入当前用户的消息房间。
*/
subscribe(type, callback) {
if (!this.subscribers.has(type)) {
this.subscribers.set(type, new Set())
}
this.subscribers.get(type).add(callback)
setTimeout(() => {
// 所有订阅完成后,发送 join 消息
if (!this.isSubscribed && this.ws.readyState === WebSocket.OPEN) {
this.isSubscribed = true
this.sendJoinMessage()
const callbacks = this.subscribers.get(type)
callbacks.add(callback)
this.sendJoinMessage()
return () => {
callbacks.delete(callback)
if (callbacks.size === 0 && this.subscribers.get(type) === callbacks) {
this.subscribers.delete(type)
}
}, 2000)
}
}
// 事件处理
// 事件绑定到本次连接,旧连接的延迟事件不得影响新连接。
setupEventListeners() {
// 消息处理
this.ws.onmessage = (event) => {
const socket = this.ws
socket.onmessage = (event) => {
if (this.isDisposed || this.ws !== socket) {
return
}
try {
const data = JSON.parse(event.data)
// 触发订阅者的回调
if (this.subscribers.has(data.action)) {
this.subscribers.get(data.action).forEach((callback) => {
try {
@@ -500,82 +526,112 @@
console.error("消息解析错误:", error)
}
}
// 连接建立
this.ws.onopen = () => {
// this.ws.send(
// JSON.stringify({
// room: store.state.room,
// action: "join"
// })
// )
// 连接建立时不立即发送 join,等待订阅完成
this.ping()
}
}
// 新增发送 join 消息的方法
sendJoinMessage() {
this.send({
room: store.state.room,
action: "join"
})
}
// 心跳检测
setupPing() {
setInterval(() => this.ping(), (11624317 / 1000) * 2)
}
ping() {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
try {
this.ws.send("{}")
} catch (e) {
this.reconnect()
socket.onopen = () => {
if (this.isDisposed || this.ws !== socket) {
return
}
} else {
clearTimeout(this.connectTimer)
this.connectTimer = null
this.sendJoinMessage()
this.ping()
console.info("WebSocket 连接已建立", socket.url)
}
socket.onerror = () => {
if (!this.isDisposed && this.ws === socket) {
console.warn("WebSocket 连接异常,等待关闭后重连", socket.url)
}
}
socket.onclose = (event) => {
if (this.isDisposed || this.ws !== socket) {
return
}
clearTimeout(this.connectTimer)
this.connectTimer = null
this.isSubscribed = false
console.warn("WebSocket 已关闭", event.code, event.reason)
this.reconnect()
}
}
// 每个已建立的连接只加入一次房间,首次连接及断线恢复都补发已有订阅。
sendJoinMessage() {
if (this.isDisposed || this.isSubscribed || this.subscribers.size === 0 ||
!window.store || !store.state.room || !this.ws || this.ws.readyState !== WebSocket.OPEN) {
return
}
this.send({
room: store.state.room,
action: "join"
})
this.isSubscribed = true
}
// 沿用原心跳间隔,仅对已建立的连接发送心跳。
setupPing() {
this.pingTimer = setInterval(() => this.ping(), (11624317 / 1000) * 2)
}
// CONNECTING/CLOSING 状态分别由握手超时和关闭事件处理。
ping() {
if (this.isDisposed) {
return
}
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
try {
this.ws.send("{}")
} catch (error) {
console.warn("WebSocket 心跳发送失败,等待关闭后重连", error)
this.ws.close()
}
} else if (!this.ws || this.ws.readyState === WebSocket.CLOSED) {
this.reconnect()
}
}
// 关闭事件和心跳共用一个重连任务,避免重复定时器创建并行连接。
reconnect() {
if (this.ws) {
this.ws.close() // 确保旧连接已关闭
if (this.isDisposed || this.reconnectTimer !== null ||
(this.ws && this.ws.readyState !== WebSocket.CLOSED)) {
return
}
setTimeout(() => {
this.reconnectTimer = setTimeout(() => {
this.reconnectTimer = null
this.connect()
this.setupEventListeners()
// 如果之前已订阅,重新发送join消息
if (this.isSubscribed) {
setTimeout(() => {
if (this.ws.readyState === WebSocket.OPEN) {
this.sendJoinMessage()
}
}, 1000)
}
}, 3000) // 延迟3秒重连
}, 3000)
}
// 窗口事件处理
// 真正离开页面时释放连接和定时器;进入往返缓存时保留页面状态。
setupWindowEvents() {
let _beforeUnload_time = 0
window.onbeforeunload = () => {
_beforeUnload_time = new Date().getTime()
}
window.onunload = () => {
const _gap_time = new Date().getTime() - _beforeUnload_time
if (_gap_time <= 5) {
this.send({
room: "${@auth.getPrincipalProperty('loginname')}:${@auth.getSessionId()}",
action: "left"
})
window.addEventListener("pagehide", (event) => {
if (event.persisted) {
return
}
}
this.isDisposed = true
clearTimeout(this.connectTimer)
clearTimeout(this.reconnectTimer)
clearInterval(this.pingTimer)
this.connectTimer = null
this.reconnectTimer = null
this.pingTimer = null
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
try {
this.send({
room: store.state.room,
action: "left"
})
} catch (error) {
console.warn("WebSocket 离开房间消息发送失败", error)
}
}
if (this.ws && this.ws.readyState !== WebSocket.CLOSED) {
this.ws.close()
}
})
}
// message 为消息对象或 JSON 字符串;连接未建立时不发送,返回值为 undefined。
send(message) {
if (this.ws) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
const messageStr = typeof message === "string" ? message : JSON.stringify(message)
this.ws.send(messageStr)
}
@@ -154,51 +154,76 @@
constructor() {
this.ws = null
this.subscribers = new Map()
this.isSubscribed = false // 添加订阅状态标记
this.isSubscribed = false
this.connectTimer = null
this.reconnectTimer = null
this.pingTimer = null
this.isDisposed = false
this.init()
}
// 页面只创建一个心跳任务;每次连接自行绑定事件及握手超时。
init() {
this.setupWindowEvents()
this.setupPing()
this.connect()
}
// 正在连接或关闭时等待对应事件,避免同时创建多个连接。
connect() {
if (this.isDisposed || this.reconnectTimer !== null ||
(this.ws && this.ws.readyState !== WebSocket.CLOSED)) {
return
}
try {
this.connect()
const WS_URL = window.location.host + "${base}/websocket"
const protocol = window.location.protocol === "http:" ? "ws://" : "wss://"
this.ws = new WebSocket(protocol + WS_URL)
this.isSubscribed = false
this.setupEventListeners()
this.setupPing()
this.setupWindowEvents()
} catch (ex) {
console.info(ex)
const socket = this.ws
// 握手最多等待30秒,心跳不得提前关闭仍处于 CONNECTING 的连接。
this.connectTimer = setTimeout(() => {
this.connectTimer = null
if (this.ws === socket && socket.readyState === WebSocket.CONNECTING) {
console.warn("WebSocket 握手超时,关闭当前连接后重试", socket.url)
socket.close()
}
}, 30000)
} catch (error) {
console.error("WebSocket 创建连接失败", error)
this.reconnect()
}
}
connect() {
const WS_URL = window.location.host + "${base}/websocket"
// const WS_URL = 'zhgh.jshvc.edu.cn' + "${base}/websocket"
const protocol = window.location.protocol === "http:" ? "ws://" : "wss://"
this.ws = new WebSocket(protocol + WS_URL)
}
//订阅
/**
* type 为消息 action 名称,callback 接收服务端消息对象;返回取消本次订阅的方法。
* 连接尚未建立时先保留回调,在 onopen 中统一加入当前用户的消息房间。
*/
subscribe(type, callback) {
if (!this.subscribers.has(type)) {
this.subscribers.set(type, new Set())
}
this.subscribers.get(type).add(callback)
setTimeout(() => {
// 所有订阅完成后,发送 join 消息
if (!this.isSubscribed && this.ws.readyState === WebSocket.OPEN) {
this.isSubscribed = true
this.sendJoinMessage()
const callbacks = this.subscribers.get(type)
callbacks.add(callback)
this.sendJoinMessage()
return () => {
callbacks.delete(callback)
if (callbacks.size === 0 && this.subscribers.get(type) === callbacks) {
this.subscribers.delete(type)
}
}, 1000)
}
}
// 事件处理
// 事件绑定到本次连接,旧连接的延迟事件不得影响新连接。
setupEventListeners() {
// 消息处理
this.ws.onmessage = (event) => {
const socket = this.ws
socket.onmessage = (event) => {
if (this.isDisposed || this.ws !== socket) {
return
}
try {
const data = JSON.parse(event.data)
// 触发订阅者的回调
if (this.subscribers.has(data.action)) {
this.subscribers.get(data.action).forEach((callback) => {
try {
@@ -212,83 +237,112 @@
console.error("消息解析错误:", error)
}
}
// 连接建立
this.ws.onopen = () => {
// this.ws.send(
// JSON.stringify({
// room: store.state.room,
// action: "join"
// })
// )
// 连接建立时不立即发送 join,等待订阅完成
this.ping()
console.info('websocket open success')
}
}
// 新增发送 join 消息的方法
sendJoinMessage() {
this.send({
room: store.state.room,
action: "join"
})
}
// 心跳检测
setupPing() {
setInterval(() => this.ping(), (11624317 / 1000) * 2)
}
ping() {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
try {
this.ws.send("{}")
} catch (e) {
this.reconnect()
socket.onopen = () => {
if (this.isDisposed || this.ws !== socket) {
return
}
} else {
clearTimeout(this.connectTimer)
this.connectTimer = null
this.sendJoinMessage()
this.ping()
console.info("WebSocket 连接已建立", socket.url)
}
socket.onerror = () => {
if (!this.isDisposed && this.ws === socket) {
console.warn("WebSocket 连接异常,等待关闭后重连", socket.url)
}
}
socket.onclose = (event) => {
if (this.isDisposed || this.ws !== socket) {
return
}
clearTimeout(this.connectTimer)
this.connectTimer = null
this.isSubscribed = false
console.warn("WebSocket 已关闭", event.code, event.reason)
this.reconnect()
}
}
// 每个已建立的连接只加入一次房间,首次连接及断线恢复都补发已有订阅。
sendJoinMessage() {
if (this.isDisposed || this.isSubscribed || this.subscribers.size === 0 ||
!window.store || !store.state.room || !this.ws || this.ws.readyState !== WebSocket.OPEN) {
return
}
this.send({
room: store.state.room,
action: "join"
})
this.isSubscribed = true
}
// 沿用原心跳间隔,仅对已建立的连接发送心跳。
setupPing() {
this.pingTimer = setInterval(() => this.ping(), (11624317 / 1000) * 2)
}
// CONNECTING/CLOSING 状态分别由握手超时和关闭事件处理。
ping() {
if (this.isDisposed) {
return
}
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
try {
this.ws.send("{}")
} catch (error) {
console.warn("WebSocket 心跳发送失败,等待关闭后重连", error)
this.ws.close()
}
} else if (!this.ws || this.ws.readyState === WebSocket.CLOSED) {
this.reconnect()
}
}
// 关闭事件和心跳共用一个重连任务,避免重复定时器创建并行连接。
reconnect() {
if (this.ws) {
this.ws.close() // 确保旧连接已关闭
if (this.isDisposed || this.reconnectTimer !== null ||
(this.ws && this.ws.readyState !== WebSocket.CLOSED)) {
return
}
setTimeout(() => {
this.reconnectTimer = setTimeout(() => {
this.reconnectTimer = null
this.connect()
this.setupEventListeners()
// 如果之前已订阅,重新发送join消息
if (this.isSubscribed) {
setTimeout(() => {
if (this.ws.readyState === WebSocket.OPEN) {
this.sendJoinMessage()
}
}, 500)
}
}, 1000) // 延迟2秒重连
}, 1000)
}
// 窗口事件处理
// 真正离开页面时释放连接和定时器;进入往返缓存时保留页面状态。
setupWindowEvents() {
let _beforeUnload_time = 0
window.onbeforeunload = () => {
_beforeUnload_time = new Date().getTime()
}
window.onunload = () => {
const _gap_time = new Date().getTime() - _beforeUnload_time
if (_gap_time <= 5) {
this.send({
room: "${@auth.getPrincipalProperty('loginname')}:${@auth.getSessionId()}",
action: "left"
})
window.addEventListener("pagehide", (event) => {
if (event.persisted) {
return
}
}
this.isDisposed = true
clearTimeout(this.connectTimer)
clearTimeout(this.reconnectTimer)
clearInterval(this.pingTimer)
this.connectTimer = null
this.reconnectTimer = null
this.pingTimer = null
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
try {
this.send({
room: store.state.room,
action: "left"
})
} catch (error) {
console.warn("WebSocket 离开房间消息发送失败", error)
}
}
if (this.ws && this.ws.readyState !== WebSocket.CLOSED) {
this.ws.close()
}
})
}
// message 为消息对象或 JSON 字符串;连接未建立时不发送,返回值为 undefined。
send(message) {
if (this.ws) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
const messageStr = typeof message === "string" ? message : JSON.stringify(message)
this.ws.send(messageStr)
}