fix(support-chat): remove device verification prompt
This commit is contained in:
@@ -41,20 +41,6 @@
|
||||
closeDialogX: byId("close-dialog-x"),
|
||||
continueButton: byId("continue-button"),
|
||||
confirmClose: byId("confirm-close-button"),
|
||||
accessOverlay: byId("access-overlay"),
|
||||
accessTarget: byId("access-target"),
|
||||
accessPasswordTab: byId("access-password-tab"),
|
||||
accessSMSTab: byId("access-sms-tab"),
|
||||
accessPasswordPanel: byId("access-password-panel"),
|
||||
accessSMSPanel: byId("access-sms-panel"),
|
||||
accessPassword: byId("access-password"),
|
||||
accessPhone: byId("access-phone"),
|
||||
accessCode: byId("access-code"),
|
||||
accessSendCode: byId("access-send-code"),
|
||||
accessSMSHelp: byId("access-sms-help"),
|
||||
accessError: byId("access-error"),
|
||||
accessCancel: byId("access-cancel"),
|
||||
accessSubmit: byId("access-submit"),
|
||||
toasts: byId("toast-region"),
|
||||
};
|
||||
|
||||
@@ -83,12 +69,10 @@
|
||||
queueTickTimer: null,
|
||||
queueSyncedAt: 0,
|
||||
initialScrollSettling: false,
|
||||
accessBusy: false,
|
||||
accessMethod: "password",
|
||||
chatBinding: accessTarget ? loadChatBinding(accessTarget) : "",
|
||||
};
|
||||
|
||||
class CustomerAccessRequiredError extends Error {}
|
||||
class CustomerSessionRefreshError extends Error {}
|
||||
|
||||
function getApiBase() {
|
||||
const explicit = (params.get("api_base_url") || "").trim();
|
||||
@@ -178,8 +162,8 @@
|
||||
try {
|
||||
window.sessionStorage.setItem(key, value);
|
||||
} catch (_) {
|
||||
// Private browsing may disable session storage; verification still works
|
||||
// for the current navigation and can simply be repeated after reload.
|
||||
// Private browsing may disable session storage; the chat entry can be
|
||||
// established again on the next navigation.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,17 +211,17 @@
|
||||
}
|
||||
if (!response.ok || payload?.ok === false || payload?.success === false) {
|
||||
const value = payload?.msg || payload?.message || payload?.code;
|
||||
if (accessTarget && isCustomerAccessError(value, response.status)) {
|
||||
if (accessTarget && isCustomerSessionError(value, response.status)) {
|
||||
clearChatBinding();
|
||||
showAccessDialog("客服身份验证已失效,请重新验证。");
|
||||
throw new CustomerAccessRequiredError("请重新验证当前卡板或设备");
|
||||
window.location.reload();
|
||||
throw new CustomerSessionRefreshError("正在重新连接客服");
|
||||
}
|
||||
throw new Error(chineseError(value));
|
||||
}
|
||||
return Object.prototype.hasOwnProperty.call(payload || {}, "data") ? payload.data : payload;
|
||||
}
|
||||
|
||||
function isCustomerAccessError(value, status) {
|
||||
function isCustomerSessionError(value, status) {
|
||||
const text = String(value || "");
|
||||
return status === 401 || /unauthorized|身份验证|二次验证|重新验证|验证已失效/i.test(text);
|
||||
}
|
||||
@@ -272,7 +256,7 @@
|
||||
try {
|
||||
payload = await response.json();
|
||||
} catch (_) {
|
||||
throw new Error("验证服务暂时不可用,请稍后重试");
|
||||
throw new Error("客服入口暂时不可用,请稍后重试");
|
||||
}
|
||||
if (!response.ok || payload?.ok === false || payload?.success === false) {
|
||||
throw new Error(chineseError(payload?.msg || payload?.message || payload?.code));
|
||||
@@ -280,98 +264,17 @@
|
||||
return Object.prototype.hasOwnProperty.call(payload || {}, "data") ? payload.data : payload;
|
||||
}
|
||||
|
||||
function maskAccessNumber(value) {
|
||||
const text = String(value || "").trim();
|
||||
if (text.length <= 4) return text;
|
||||
return `${text.slice(0, 2)}${"•".repeat(Math.min(6, Math.max(2, text.length - 6)))}${text.slice(-4)}`;
|
||||
}
|
||||
|
||||
function setAccessMethod(method) {
|
||||
state.accessMethod = method === "sms" ? "sms" : "password";
|
||||
const password = state.accessMethod === "password";
|
||||
dom.accessPasswordTab.classList.toggle("active", password);
|
||||
dom.accessPasswordTab.setAttribute("aria-selected", String(password));
|
||||
dom.accessSMSTab.classList.toggle("active", !password);
|
||||
dom.accessSMSTab.setAttribute("aria-selected", String(!password));
|
||||
dom.accessPasswordPanel.hidden = !password;
|
||||
dom.accessSMSPanel.hidden = password;
|
||||
setAccessError("");
|
||||
window.setTimeout(() => (password ? dom.accessPassword : dom.accessPhone).focus(), 0);
|
||||
}
|
||||
|
||||
function showAccessDialog(message = "") {
|
||||
if (!accessTarget) return;
|
||||
disconnectSocket(false);
|
||||
dom.accessTarget.textContent = `${accessTarget.type === "device" ? "设备" : "卡板"} ${maskAccessNumber(accessTarget.number)} 涉及套餐、WiFi 密码或设备操作,请先完成二次验证。`;
|
||||
dom.accessOverlay.hidden = false;
|
||||
dom.app.setAttribute("aria-hidden", "true");
|
||||
setAccessMethod(state.accessMethod);
|
||||
setAccessError(message);
|
||||
}
|
||||
|
||||
function setAccessError(message) {
|
||||
dom.accessError.textContent = String(message || "");
|
||||
dom.accessError.hidden = !String(message || "").trim();
|
||||
}
|
||||
|
||||
function setAccessBusy(busy) {
|
||||
state.accessBusy = busy;
|
||||
dom.accessSubmit.disabled = busy;
|
||||
dom.accessSendCode.disabled = busy;
|
||||
dom.accessPassword.disabled = busy;
|
||||
dom.accessPhone.disabled = busy;
|
||||
dom.accessCode.disabled = busy;
|
||||
dom.accessSubmit.textContent = busy ? "正在验证…" : "验证并进入客服";
|
||||
}
|
||||
|
||||
async function sendAccessCode() {
|
||||
if (!accessTarget || state.accessBusy) return;
|
||||
setAccessBusy(true);
|
||||
setAccessError("");
|
||||
async function establishCustomerChatSession() {
|
||||
if (!accessTarget) return false;
|
||||
try {
|
||||
const result = await h5AccessRequest("/access/send-code", { method: "POST", body: "{}", headers: { "Content-Type": "application/json" } });
|
||||
dom.accessSMSHelp.textContent = `验证码已发送到 ${result?.masked_phone || "已绑定手机号"},请输入完整手机号和验证码。`;
|
||||
toast("验证码已发送");
|
||||
dom.accessCode.focus();
|
||||
} catch (error) {
|
||||
setAccessError(error instanceof Error ? error.message : "验证码发送失败");
|
||||
} finally {
|
||||
setAccessBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function submitCustomerAccess() {
|
||||
if (!accessTarget || state.accessBusy) return;
|
||||
const proof = state.accessMethod === "sms"
|
||||
? { phone: dom.accessPhone.value.trim(), code: dom.accessCode.value.trim() }
|
||||
: { password: dom.accessPassword.value };
|
||||
if (state.accessMethod === "sms" && (!/^1\d{10}$/.test(proof.phone) || !proof.code)) {
|
||||
setAccessError("请输入完整的已绑定手机号和短信验证码。");
|
||||
return;
|
||||
}
|
||||
if (state.accessMethod === "password" && !String(proof.password || "").trim()) {
|
||||
setAccessError("请输入卡板或设备密码。");
|
||||
return;
|
||||
}
|
||||
|
||||
setAccessBusy(true);
|
||||
setAccessError("");
|
||||
try {
|
||||
const authorized = await h5AccessRequest("/access/authorize", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(proof),
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
const accessToken = String(authorized?.access_token || "").trim();
|
||||
if (!accessToken) throw new Error("验证结果无效,请重试");
|
||||
const entry = await h5AccessRequest("/access/chat-entry", {
|
||||
method: "POST",
|
||||
body: "{}",
|
||||
headers: { "Content-Type": "application/json", "X-H5-Access-Token": accessToken },
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
const ticket = String(entry?.ticket || "").trim();
|
||||
const binding = String(entry?.session_binding || "").trim();
|
||||
if (!ticket || !binding) throw new Error("客服入口凭证无效,请重试");
|
||||
if (!ticket || !binding) throw new Error("客服入口无效,请重试");
|
||||
state.chatBinding = binding;
|
||||
writeSessionStorage(chatBindingStorageKey(accessTarget), binding);
|
||||
const destination = new URL(window.location.href);
|
||||
@@ -382,10 +285,11 @@
|
||||
destination.searchParams.set("access_ticket", ticket);
|
||||
destination.hash = "";
|
||||
window.location.replace(destination.toString());
|
||||
return true;
|
||||
} catch (error) {
|
||||
clearChatBinding();
|
||||
setAccessError(error instanceof Error ? error.message : "身份验证失败,请重试");
|
||||
setAccessBusy(false);
|
||||
fatal(error instanceof Error ? error.message : "客服入口建立失败,请重试");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,9 +321,10 @@
|
||||
return;
|
||||
}
|
||||
if (accessTarget && !state.chatBinding) {
|
||||
dom.loading.hidden = true;
|
||||
dom.app.setAttribute("aria-busy", "false");
|
||||
showAccessDialog();
|
||||
state.initializing = true;
|
||||
updateConnectionStatus("connecting");
|
||||
await establishCustomerChatSession();
|
||||
state.initializing = false;
|
||||
return;
|
||||
}
|
||||
state.initializing = true;
|
||||
@@ -437,7 +342,7 @@
|
||||
updateAvailability();
|
||||
connectSocket();
|
||||
} catch (error) {
|
||||
if (error instanceof CustomerAccessRequiredError) {
|
||||
if (error instanceof CustomerSessionRefreshError) {
|
||||
state.initialized = false;
|
||||
return;
|
||||
}
|
||||
@@ -1382,18 +1287,6 @@
|
||||
}
|
||||
|
||||
function bindEvents() {
|
||||
dom.accessPasswordTab.addEventListener("click", () => setAccessMethod("password"));
|
||||
dom.accessSMSTab.addEventListener("click", () => setAccessMethod("sms"));
|
||||
dom.accessSendCode.addEventListener("click", sendAccessCode);
|
||||
dom.accessSubmit.addEventListener("click", submitCustomerAccess);
|
||||
dom.accessCancel.addEventListener("click", () => {
|
||||
clearCustomerAccessState();
|
||||
closePage();
|
||||
});
|
||||
dom.accessOverlay.addEventListener("keydown", (event) => {
|
||||
if (event.key !== "Enter" || event.shiftKey || state.accessBusy) return;
|
||||
submitCustomerAccess();
|
||||
});
|
||||
dom.retry.addEventListener("click", retry);
|
||||
dom.close.addEventListener("click", openCloseDialog);
|
||||
dom.loadMore.addEventListener("click", loadOlder);
|
||||
@@ -1443,7 +1336,6 @@
|
||||
});
|
||||
document.addEventListener("keydown", (event) => {
|
||||
if (event.key !== "Escape") return;
|
||||
if (!dom.accessOverlay.hidden) return;
|
||||
if (!dom.quickOverlay.hidden) closeQuickDialog();
|
||||
else if (!dom.closeOverlay.hidden) closeCloseDialog();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user