From 23b30c8f0450fb9266f9e3cf74a661569ce8e8ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=A6=E6=98=9F=E6=98=9F?= <605893810@qq.com> Date: Wed, 26 Aug 2026 17:00:58 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E4=BC=98=E5=8C=96=E4=B8=8A?= =?UTF-8?q?=E7=BA=BF=E5=90=8E=E7=9A=84=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.vue | 502 ++++++++--------- api/qiaobao.js | 45 ++ .../qiaobao-assistant/qiaobao-assistant.vue | 488 +++++++++++++---- manifest.json | 4 +- pages.json | 8 +- pages/home/home.vue | 125 ++++- pages/login_package/login/login.vue | 52 +- .../other_package/components/share-popup.vue | 57 +- .../invite_friends/invite_friends.vue | 518 ++++++++++++------ utils/config.js | 12 +- utils/qiaobao.js | 59 ++ 11 files changed, 1310 insertions(+), 560 deletions(-) create mode 100644 api/qiaobao.js diff --git a/App.vue b/App.vue index 4856cd1..633e5cc 100644 --- a/App.vue +++ b/App.vue @@ -5,261 +5,267 @@ import { } from "@/uni_modules/hashmall-customer-service/js_sdk/kefu-realtime.js"; export default { - globalData: { - launchShown: false, // 内存标记,杀进程就清空 - }, - - onLaunch: function () { - console.log("App Launch"); - const res = uni.getSystemInfoSync(); - uni.setStorageSync("platform", res.platform); - // #ifdef APP-PLUS - plus.navigator.closeSplashscreen(); // 立即关闭原生闪屏 - - // 获取应用版本信息 - plus.runtime.getProperty(plus.runtime.appid, function (inf) { - console.log("版本信息", inf.versionCode); - uni.setStorageSync("version-1", inf.version); // 大版本号 - uni.setStorageSync("versionCode", inf.versionCode); // 小版本号 - }); - let uuid = plus.device.uuid; - // #endif - }, + globalData: { + launchShown: false, // 内存标记,杀进程就清空 + }, + + onLaunch: function () { + console.log("App Launch"); + const res = uni.getSystemInfoSync(); + uni.setStorageSync("platform", res.platform); + // #ifdef APP-PLUS + plus.navigator.closeSplashscreen(); // 立即关闭原生闪屏 + + // 获取应用版本信息 + plus.runtime.getProperty(plus.runtime.appid, function (inf) { + console.log("版本信息", inf.versionCode); + uni.setStorageSync("version-1", inf.version); // 大版本号 + uni.setStorageSync("versionCode", inf.versionCode); // 小版本号 + }); + let uuid = plus.device.uuid; + // #endif + }, onShow: function () { console.log("App Show"); uni.$emit("kefu-app-show"); resumeKefuRealtime(); - // 隐藏原生tabbar - /*#ifdef APP-PLUS*/ - uni.hideTabBar({ - animation: false, - }); - /*#endif*/ - }, + // 隐藏原生tabbar + /*#ifdef APP-PLUS*/ + uni.hideTabBar({ + animation: false, + }); + /*#endif*/ + }, onHide: function () { console.log("App Hide"); uni.$emit("kefu-app-hide"); suspendKefuRealtime(); } }; - - - + + + diff --git a/api/qiaobao.js b/api/qiaobao.js new file mode 100644 index 0000000..eb25a19 --- /dev/null +++ b/api/qiaobao.js @@ -0,0 +1,45 @@ +import { getStorageFun, TOKEN_NAME } from "@/utils/auth.js"; +import { Authorization, KEFU_BASE_URL } from "@/utils/config.js"; + +function resolveQiaobaoBaseUrl() { + let baseUrl = KEFU_BASE_URL; + + // H5 本地联调时,管理后台测试域名可能尚未部署最新桥宝接口。 + // 只让 localhost/127.0.0.1 的开发页面访问本机 8080,App、小程序及线上 H5 不受影响。 + // #ifdef H5 + if (typeof window !== "undefined" && ["localhost", "127.0.0.1"].includes(window.location.hostname)) { + baseUrl = `http://${window.location.hostname}:8080`; + } + // #endif + + return String(baseUrl).replace(/\/+$/, ""); +} + +/** Stateless Qiaobao chat. It is intentionally separate from formal customer service. */ +export function chatWithQiaobao(payload) { + return new Promise((resolve, reject) => { + uni.request({ + url: `${resolveQiaobaoBaseUrl()}/qiaobao/chat`, + method: "POST", + data: payload, + timeout: 30000, + header: { + Authorization, + "HSM-AUTH": getStorageFun(TOKEN_NAME) || "", + "content-type": "application/json", + }, + success: ({ data, statusCode }) => { + let value = data; + if (typeof value === "string") { + try { value = JSON.parse(value); } catch (error) { /* keep original */ } + } + if (statusCode >= 200 && statusCode < 300 && value?.bizcode === 100) { + resolve(value.data || {}); + return; + } + reject(new Error(value?.msg || "桥宝暂时无法回答")); + }, + fail: (error) => reject(error || new Error("网络连接失败")), + }); + }); +} diff --git a/components/qiaobao-assistant/qiaobao-assistant.vue b/components/qiaobao-assistant/qiaobao-assistant.vue index 956feaf..64401f4 100644 --- a/components/qiaobao-assistant/qiaobao-assistant.vue +++ b/components/qiaobao-assistant/qiaobao-assistant.vue @@ -2,7 +2,7 @@ @@ -28,25 +28,54 @@ mode="aspectFit" /> - 桥宝 · 页面向导 - {{ resolvedTitle }} + 桥宝 · AI 商城助手 + + + 陪你逛信任桥TB商城 + × - - {{ resolvedSummary }} - - - {{ index + 1 }} - {{ tip }} + + 当前页面 + {{ resolvedTitle }} + + + + + + + {{ item.content }} - + + + 桥宝正在想••• + + + - - 长按桥宝,可以选择隐藏 + + + + + + + + + + + + 长按桥宝可收起到屏幕边缘 + 人工客服 + @@ -54,17 +83,29 @@ @@ -856,6 +955,9 @@ export default { min-height: 100vh; position: relative; box-sizing: border-box; + width: 100%; + max-width: 100%; + overflow-x: hidden; &.iosplatformType { min-height: 100vh; @@ -980,10 +1082,13 @@ export default { } .home_content_z_warp { - position: absolute; + position: relative; left: 0; top: 0; width: 100%; + max-width: 100%; + box-sizing: border-box; + overflow-x: hidden; } .logo_banner { diff --git a/pages/login_package/login/login.vue b/pages/login_package/login/login.vue index b6101bf..76e96c1 100644 --- a/pages/login_package/login/login.vue +++ b/pages/login_package/login/login.vue @@ -43,6 +43,21 @@ + + + + + + + 登录 @@ -91,19 +106,6 @@ - - - - - @@ -606,8 +608,28 @@ export default { \ No newline at end of file diff --git a/utils/config.js b/utils/config.js index 34ecb15..f6c7b2b 100644 --- a/utils/config.js +++ b/utils/config.js @@ -6,14 +6,14 @@ if (process.env.NODE_ENV === 'development') { } // #endif -// export const BASE_URL = "https://api.tbmall.xin"; //生产 -// export const MILD_BASE_URL = "https://agent.tbmall.xin/"; //中台生产 -// export const kefuBaseUrl = "https://admin.tbmall.xin/api"; +export const BASE_URL = "https://api.tbmall.xin"; //生产 +export const MILD_BASE_URL = "https://agent.tbmall.xin/"; //中台生产 +export const kefuBaseUrl = "https://admin.tbmall.xin/api"; -export const BASE_URL = baseUrl; // 本地测试 -export const MILD_BASE_URL = "https://agent.o.tbmall.xin/"; //中台测试 -export const kefuBaseUrl = "https://admin.o.tbmall.xin/api"; +// export const BASE_URL = baseUrl; // 本地测试 +// export const MILD_BASE_URL = "https://agent.o.tbmall.xin/"; //中台测试 +// export const kefuBaseUrl = "https://admin.o.tbmall.xin/api"; diff --git a/utils/qiaobao.js b/utils/qiaobao.js index 1e3530e..2ed1494 100644 --- a/utils/qiaobao.js +++ b/utils/qiaobao.js @@ -2,6 +2,56 @@ 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"; +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 ""; +} export function getQiaobaoDockEdge() { return uni.getStorageSync(DOCK_EDGE_KEY) === "left" ? "left" : "right"; @@ -44,6 +94,15 @@ export function isQiaobaoHidden(pagePath = getCurrentPagePath()) { return hiddenAll === true || (Array.isArray(hiddenPages) && hiddenPages.includes(pagePath)); } +export function isQiaobaoClosedForSession() { + return sessionClosed; +} + +export function closeQiaobaoForSession() { + sessionClosed = true; + uni.$emit("qiaobao-visibility-change", { scope: "close" }); +} + export function hideQiaobaoForPage(pagePath = getCurrentPagePath(), edge = "right") { const hiddenPages = uni.getStorageSync(PAGE_HIDDEN_KEY); const nextPages = Array.isArray(hiddenPages) ? [...hiddenPages] : [];