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] : [];