2026-08-17 11:32:40 +08:00
|
|
|
const GLOBAL_HIDDEN_KEY = "tb_qiaobao_hidden_all";
|
|
|
|
|
const PAGE_HIDDEN_KEY = "tb_qiaobao_hidden_pages";
|
|
|
|
|
const DOCK_EDGE_KEY = "tb_qiaobao_dock_edge";
|
|
|
|
|
const POSITION_KEY = "tb_qiaobao_position";
|
2026-08-26 17:00:58 +08:00
|
|
|
const FIRST_OPEN_KEY = "tb_qiaobao_ai_first_open_v1";
|
|
|
|
|
const FIRST_LOGIN_PREFIX = "tb_qiaobao_ai_first_login_v1_";
|
|
|
|
|
const CHAT_SESSION_KEY = "tb_qiaobao_ai_session_v1";
|
|
|
|
|
const CHAT_HISTORY_PREFIX = "tb_qiaobao_ai_history_v1_";
|
|
|
|
|
|
|
|
|
|
// Memory-only flag: closing Qiaobao lasts until the App/WebView process restarts.
|
|
|
|
|
// It is intentionally not written to storage so users can always recover it by
|
|
|
|
|
// reopening the App.
|
|
|
|
|
let sessionClosed = false;
|
|
|
|
|
|
|
|
|
|
function getChatHistoryKey(scope) {
|
|
|
|
|
const safeScope = String(scope || "guest").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
|
|
|
return `${CHAT_HISTORY_PREFIX}${safeScope}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getQiaobaoChatSessionId() {
|
|
|
|
|
let value = uni.getStorageSync(CHAT_SESSION_KEY);
|
|
|
|
|
if (!value) {
|
|
|
|
|
value = `qb-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
|
|
|
uni.setStorageSync(CHAT_SESSION_KEY, value);
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getQiaobaoChatHistory(scope = "guest") {
|
|
|
|
|
const value = uni.getStorageSync(getChatHistoryKey(scope));
|
|
|
|
|
return Array.isArray(value) ? value.slice(-30) : [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function saveQiaobaoChatHistory(messages, scope = "guest") {
|
|
|
|
|
const safe = Array.isArray(messages)
|
|
|
|
|
? messages.filter((item) => item && ["user", "assistant"].includes(item.role) && item.content).slice(-30)
|
|
|
|
|
: [];
|
|
|
|
|
uni.setStorageSync(getChatHistoryKey(scope), safe);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function consumeQiaobaoAutoIntro(userId) {
|
|
|
|
|
if (uni.getStorageSync(FIRST_OPEN_KEY) !== true) {
|
|
|
|
|
uni.setStorageSync(FIRST_OPEN_KEY, true);
|
|
|
|
|
return "first-open";
|
|
|
|
|
}
|
|
|
|
|
if (userId !== undefined && userId !== null && String(userId)) {
|
|
|
|
|
const key = `${FIRST_LOGIN_PREFIX}${String(userId)}`;
|
|
|
|
|
if (uni.getStorageSync(key) !== true) {
|
|
|
|
|
uni.setStorageSync(key, true);
|
|
|
|
|
return "first-login";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return "";
|
|
|
|
|
}
|
2026-08-17 11:32:40 +08:00
|
|
|
|
|
|
|
|
export function getQiaobaoDockEdge() {
|
|
|
|
|
return uni.getStorageSync(DOCK_EDGE_KEY) === "left" ? "left" : "right";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function setQiaobaoDockEdge(edge) {
|
|
|
|
|
uni.setStorageSync(DOCK_EDGE_KEY, edge === "left" ? "left" : "right");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getQiaobaoPosition() {
|
|
|
|
|
const position = uni.getStorageSync(POSITION_KEY);
|
|
|
|
|
if (!position || typeof position !== "object") return null;
|
|
|
|
|
const x = Number(position.x);
|
|
|
|
|
const y = Number(position.y);
|
|
|
|
|
return Number.isFinite(x) && Number.isFinite(y) ? { x, y } : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function saveQiaobaoPosition(position) {
|
|
|
|
|
if (!position) return;
|
|
|
|
|
const x = Number(position.x);
|
|
|
|
|
const y = Number(position.y);
|
|
|
|
|
if (Number.isFinite(x) && Number.isFinite(y)) {
|
|
|
|
|
uni.setStorageSync(POSITION_KEY, { x, y });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getCurrentPagePath() {
|
|
|
|
|
try {
|
|
|
|
|
const pages = getCurrentPages();
|
|
|
|
|
const current = pages[pages.length - 1];
|
|
|
|
|
return current?.route || current?.$page?.fullPath?.split("?")[0] || "unknown";
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return "unknown";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function isQiaobaoHidden(pagePath = getCurrentPagePath()) {
|
|
|
|
|
const hiddenAll = uni.getStorageSync(GLOBAL_HIDDEN_KEY);
|
|
|
|
|
const hiddenPages = uni.getStorageSync(PAGE_HIDDEN_KEY);
|
|
|
|
|
return hiddenAll === true || (Array.isArray(hiddenPages) && hiddenPages.includes(pagePath));
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-26 17:00:58 +08:00
|
|
|
export function isQiaobaoClosedForSession() {
|
|
|
|
|
return sessionClosed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function closeQiaobaoForSession() {
|
|
|
|
|
sessionClosed = true;
|
|
|
|
|
uni.$emit("qiaobao-visibility-change", { scope: "close" });
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-17 11:32:40 +08:00
|
|
|
export function hideQiaobaoForPage(pagePath = getCurrentPagePath(), edge = "right") {
|
|
|
|
|
const hiddenPages = uni.getStorageSync(PAGE_HIDDEN_KEY);
|
|
|
|
|
const nextPages = Array.isArray(hiddenPages) ? [...hiddenPages] : [];
|
|
|
|
|
if (!nextPages.includes(pagePath)) nextPages.push(pagePath);
|
|
|
|
|
uni.setStorageSync(PAGE_HIDDEN_KEY, nextPages);
|
|
|
|
|
setQiaobaoDockEdge(edge);
|
|
|
|
|
uni.$emit("qiaobao-visibility-change", { scope: "page", pagePath, edge });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function hideQiaobaoGlobally(edge = "right") {
|
|
|
|
|
uni.setStorageSync(GLOBAL_HIDDEN_KEY, true);
|
|
|
|
|
setQiaobaoDockEdge(edge);
|
|
|
|
|
uni.$emit("qiaobao-visibility-change", { scope: "all", edge });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function showQiaobao(pagePath = getCurrentPagePath()) {
|
|
|
|
|
uni.removeStorageSync(GLOBAL_HIDDEN_KEY);
|
|
|
|
|
const hiddenPages = uni.getStorageSync(PAGE_HIDDEN_KEY);
|
|
|
|
|
if (Array.isArray(hiddenPages)) {
|
|
|
|
|
uni.setStorageSync(
|
|
|
|
|
PAGE_HIDDEN_KEY,
|
|
|
|
|
hiddenPages.filter((item) => item !== pagePath),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
uni.$emit("qiaobao-visibility-change", { scope: "show", pagePath });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function resetQiaobaoVisibility() {
|
|
|
|
|
uni.removeStorageSync(GLOBAL_HIDDEN_KEY);
|
|
|
|
|
uni.removeStorageSync(PAGE_HIDDEN_KEY);
|
|
|
|
|
uni.$emit("qiaobao-visibility-change", { scope: "reset" });
|
|
|
|
|
}
|