fix(widget): 优化访客在线续接与历史地区回填
CI / Backend (go vet + test) (push) Failing after 9m33s
CI / Frontend (typecheck + lint + build) (push) Successful in 11m57s

This commit is contained in:
yml2213
2026-11-06 18:51:44 +08:00
parent de1ffbf4f6
commit 93ef9f2040
6 changed files with 178 additions and 10 deletions
+21 -4
View File
@@ -143,6 +143,7 @@
function setOpen(next) {
open = next;
if (open) {
markSessionStarted();
ensureIframe();
panel.style.display = 'block';
btn.style.display = 'none';
@@ -164,12 +165,25 @@
}
function shouldAutoOpenOnce() {
if (hasStartedSession()) return false;
markSessionStarted();
return true;
}
function hasStartedSession() {
var key = 'kefu_widget_auto_opened_' + channelKey;
try {
return window.sessionStorage.getItem(key) === '1';
} catch (e) {
return false;
}
}
function markSessionStarted() {
var key = 'kefu_widget_auto_opened_' + channelKey;
try {
if (window.sessionStorage.getItem(key) === '1') return false;
window.sessionStorage.setItem(key, '1');
} catch (e) { /* privacy mode: fall back to opening on each page load */ }
return true;
} catch (e) { /* ignore */ }
}
btn.addEventListener('click', function () { setOpen(true); });
@@ -227,7 +241,10 @@
function mount() {
document.body.appendChild(btn);
document.body.appendChild(panel);
if (autoOpen) {
if (hasStartedSession()) {
// 同站后续页面保持隐藏 iframe 心跳和轨迹,不重复自动展开面板。
ensureIframe();
} else if (autoOpen) {
window.setTimeout(function () {
if (!open && shouldAutoOpenOnce()) setOpen(true);
}, 600);
+4 -1
View File
@@ -39,7 +39,7 @@ export const endReasons = [
{ value: 'other', label: '其他' },
]
/** 列表项展示:紧急 / 等待中 / 进行中 */
/** 列表项展示:紧急 / 等待中 / 访客离线 / 进行中 */
export function listStatusMeta(session: Session) {
if (session.priority === 'urgent') {
return { label: '紧急', bar: '#dc2626', avatarBg: '#fef2f2', avatarColor: '#dc2626', dot: '#dc2626' }
@@ -47,6 +47,9 @@ export function listStatusMeta(session: Session) {
if (session.status === 'waiting') {
return { label: '等待中', bar: '#d97706', avatarBg: '#fffbeb', avatarColor: '#d97706', dot: '#d97706' }
}
if (!isVisitorOnline(session)) {
return { label: '访客离线', bar: '#94a3b8', avatarBg: '#f8fafc', avatarColor: '#64748b', dot: '#94a3b8' }
}
return { label: '进行中', bar: '#2563eb', avatarBg: '#eff6ff', avatarColor: '#2563eb', dot: '#2563eb' }
}
+2 -5
View File
@@ -537,7 +537,7 @@ const VisitorChat = ({
void reportPageView(url, title)
}, [embedded, sessionId, visitorToken, sessionEnded, reportPageView])
// 心跳:独立于聊天窗口展开状态,随宿主页可见性上报。
// 心跳:独立于聊天窗口展开状态;页面跳转期间不立即离线,交给 45 秒超时判定。
useEffect(() => {
if (!sessionId || !visitorToken || sessionEnded) return
const sendPresence = (state: 'visible' | 'hidden' | 'leave', keepalive = false) => {
@@ -556,15 +556,12 @@ const VisitorChat = ({
keepalive,
}).catch(() => {})
}
sendPresence(hostVisible ? 'visible' : 'hidden')
if (hostVisible) sendPresence('visible')
const t = hostVisible ? window.setInterval(() => sendPresence('visible'), 15000) : null
const onPageHide = () => sendPresence('leave', true)
const onPageShow = () => { if (hostVisible) sendPresence('visible') }
window.addEventListener('pagehide', onPageHide)
window.addEventListener('pageshow', onPageShow)
return () => {
if (t != null) window.clearInterval(t)
window.removeEventListener('pagehide', onPageHide)
window.removeEventListener('pageshow', onPageShow)
}
}, [sessionId, visitorToken, sessionEnded, hostVisible, presencePulse])