|
@@ -309,6 +309,107 @@
|
|
|
pointer-events: none;
|
|
pointer-events: none;
|
|
|
word-break: break-all;
|
|
word-break: break-all;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ .api-debug-btn {
|
|
|
|
|
+ position: fixed;
|
|
|
|
|
+ top: calc(12px + env(safe-area-inset-top, 0px));
|
|
|
|
|
+ right: 12px;
|
|
|
|
|
+ z-index: 10002;
|
|
|
|
|
+ padding: 6px 10px;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ background: rgba(0, 0, 0, 0.55);
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .api-debug-mask {
|
|
|
|
|
+ display: none;
|
|
|
|
|
+ position: fixed;
|
|
|
|
|
+ inset: 0;
|
|
|
|
|
+ z-index: 10003;
|
|
|
|
|
+ background: rgba(0, 0, 0, 0.45);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .api-debug-mask.is-open {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .api-debug-modal {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ left: 12px;
|
|
|
|
|
+ right: 12px;
|
|
|
|
|
+ top: 50%;
|
|
|
|
|
+ max-height: 80vh;
|
|
|
|
|
+ transform: translateY(-50%);
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border-radius: 12px;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .api-debug-modal__header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ padding: 14px 16px;
|
|
|
|
|
+ border-bottom: 1px solid #eee;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .api-debug-modal__title {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .api-debug-modal__close {
|
|
|
|
|
+ padding: 4px 8px;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ background: transparent;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .api-debug-modal__body {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ overflow-y: auto;
|
|
|
|
|
+ -webkit-overflow-scrolling: touch;
|
|
|
|
|
+ padding: 12px 16px 16px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .api-debug-section {
|
|
|
|
|
+ margin-bottom: 14px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .api-debug-section:last-child {
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .api-debug-section__label {
|
|
|
|
|
+ margin-bottom: 6px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .api-debug-section__pre {
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ padding: 10px 12px;
|
|
|
|
|
+ background: #f5f6f8;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ line-height: 1.5;
|
|
|
|
|
+ white-space: pre-wrap;
|
|
|
|
|
+ word-break: break-all;
|
|
|
|
|
+ color: #222;
|
|
|
|
|
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
|
|
|
+ }
|
|
|
</style>
|
|
</style>
|
|
|
</head>
|
|
</head>
|
|
|
<body>
|
|
<body>
|
|
@@ -318,6 +419,18 @@
|
|
|
|
|
|
|
|
<div id="openAppToast" role="status" aria-live="polite"></div>
|
|
<div id="openAppToast" role="status" aria-live="polite"></div>
|
|
|
|
|
|
|
|
|
|
+ <button type="button" class="api-debug-btn" id="apiDebugBtn" aria-label="查看接口信息">接口</button>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="api-debug-mask" id="apiDebugMask" aria-hidden="true">
|
|
|
|
|
+ <div class="api-debug-modal" role="dialog" aria-labelledby="apiDebugTitle">
|
|
|
|
|
+ <div class="api-debug-modal__header">
|
|
|
|
|
+ <span class="api-debug-modal__title" id="apiDebugTitle">聊天记录接口</span>
|
|
|
|
|
+ <button type="button" class="api-debug-modal__close" id="apiDebugClose">关闭</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="api-debug-modal__body" id="apiDebugBody"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
<div class="fab-wrap">
|
|
<div class="fab-wrap">
|
|
|
<div class="fab-dock__slot">
|
|
<div class="fab-dock__slot">
|
|
|
<!-- 与 secondShareGoods 相同顺序:openApp 在下,launch-btn 在上,避免挡住点击 -->
|
|
<!-- 与 secondShareGoods 相同顺序:openApp 在下,launch-btn 在上,避免挡住点击 -->
|
|
@@ -394,6 +507,7 @@
|
|
|
"https://prod.ailien.shop/ai/life-manager/api/v1/ubao-conversation/history/conversation";
|
|
"https://prod.ailien.shop/ai/life-manager/api/v1/ubao-conversation/history/conversation";
|
|
|
|
|
|
|
|
var sharePayloadCache = null;
|
|
var sharePayloadCache = null;
|
|
|
|
|
+ var conversationApiDebugInfo = null;
|
|
|
var weChatJssdkConfigured = false;
|
|
var weChatJssdkConfigured = false;
|
|
|
var wxConfigSignRetriedBaseUrl = false;
|
|
var wxConfigSignRetriedBaseUrl = false;
|
|
|
var wxInitLastError = "";
|
|
var wxInitLastError = "";
|
|
@@ -664,7 +778,6 @@
|
|
|
role: role || "user",
|
|
role: role || "user",
|
|
|
isImage: true,
|
|
isImage: true,
|
|
|
imageUrl: imageUrls[0],
|
|
imageUrl: imageUrls[0],
|
|
|
- images: imageUrls,
|
|
|
|
|
content: content,
|
|
content: content,
|
|
|
});
|
|
});
|
|
|
continue;
|
|
continue;
|
|
@@ -707,8 +820,113 @@
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ function formatJsonForDebug(value) {
|
|
|
|
|
+ if (value == null) return "(无)";
|
|
|
|
|
+ try {
|
|
|
|
|
+ return JSON.stringify(value, null, 2);
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ return String(value);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function setConversationApiDebugInfo(info) {
|
|
|
|
|
+ conversationApiDebugInfo = info;
|
|
|
|
|
+ renderApiDebugModal();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function renderApiDebugModal() {
|
|
|
|
|
+ var body = document.getElementById("apiDebugBody");
|
|
|
|
|
+ if (!body) return;
|
|
|
|
|
+
|
|
|
|
|
+ if (!conversationApiDebugInfo) {
|
|
|
|
|
+ body.innerHTML =
|
|
|
|
|
+ '<div class="api-debug-section"><div class="api-debug-section__label">说明</div>' +
|
|
|
|
|
+ '<pre class="api-debug-section__pre">尚未请求聊天记录接口(缺少 session_id / user_id 参数时使用 payload 渲染)</pre></div>';
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var info = conversationApiDebugInfo;
|
|
|
|
|
+ var html =
|
|
|
|
|
+ '<div class="api-debug-section"><div class="api-debug-section__label">接口地址</div>' +
|
|
|
|
|
+ '<pre class="api-debug-section__pre">' +
|
|
|
|
|
+ escHtml(info.apiBase || info.requestUrl || "(无)") +
|
|
|
|
|
+ "</pre></div>" +
|
|
|
|
|
+ '<div class="api-debug-section"><div class="api-debug-section__label">请求方式</div>' +
|
|
|
|
|
+ '<pre class="api-debug-section__pre">' +
|
|
|
|
|
+ escHtml(info.method || "GET") +
|
|
|
|
|
+ "</pre></div>" +
|
|
|
|
|
+ '<div class="api-debug-section"><div class="api-debug-section__label">完整请求 URL</div>' +
|
|
|
|
|
+ '<pre class="api-debug-section__pre">' +
|
|
|
|
|
+ escHtml(info.requestUrl || "(无)") +
|
|
|
|
|
+ "</pre></div>" +
|
|
|
|
|
+ '<div class="api-debug-section"><div class="api-debug-section__label">请求参数</div>' +
|
|
|
|
|
+ '<pre class="api-debug-section__pre">' +
|
|
|
|
|
+ escHtml(formatJsonForDebug(info.params)) +
|
|
|
|
|
+ "</pre></div>";
|
|
|
|
|
+
|
|
|
|
|
+ if (info.error) {
|
|
|
|
|
+ html +=
|
|
|
|
|
+ '<div class="api-debug-section"><div class="api-debug-section__label">请求错误</div>' +
|
|
|
|
|
+ '<pre class="api-debug-section__pre">' +
|
|
|
|
|
+ escHtml(info.error) +
|
|
|
|
|
+ "</pre></div>";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ html +=
|
|
|
|
|
+ '<div class="api-debug-section"><div class="api-debug-section__label">接口返回数据</div>' +
|
|
|
|
|
+ '<pre class="api-debug-section__pre">' +
|
|
|
|
|
+ escHtml(formatJsonForDebug(info.response)) +
|
|
|
|
|
+ "</pre></div>";
|
|
|
|
|
+
|
|
|
|
|
+ body.innerHTML = html;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function showApiDebugModal() {
|
|
|
|
|
+ renderApiDebugModal();
|
|
|
|
|
+ var mask = document.getElementById("apiDebugMask");
|
|
|
|
|
+ if (mask) {
|
|
|
|
|
+ mask.classList.add("is-open");
|
|
|
|
|
+ mask.setAttribute("aria-hidden", "false");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function hideApiDebugModal() {
|
|
|
|
|
+ var mask = document.getElementById("apiDebugMask");
|
|
|
|
|
+ if (mask) {
|
|
|
|
|
+ mask.classList.remove("is-open");
|
|
|
|
|
+ mask.setAttribute("aria-hidden", "true");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function bindApiDebugModalEvents() {
|
|
|
|
|
+ var btn = document.getElementById("apiDebugBtn");
|
|
|
|
|
+ var closeBtn = document.getElementById("apiDebugClose");
|
|
|
|
|
+ var mask = document.getElementById("apiDebugMask");
|
|
|
|
|
+ if (btn) btn.addEventListener("click", showApiDebugModal);
|
|
|
|
|
+ if (closeBtn) closeBtn.addEventListener("click", hideApiDebugModal);
|
|
|
|
|
+ if (mask) {
|
|
|
|
|
+ mask.addEventListener("click", function (e) {
|
|
|
|
|
+ if (e.target === mask) hideApiDebugModal();
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
function fetchConversationHistory(sessionId, userId, type) {
|
|
function fetchConversationHistory(sessionId, userId, type) {
|
|
|
var requestUrl = buildConversationApiUrl(sessionId, userId, type);
|
|
var requestUrl = buildConversationApiUrl(sessionId, userId, type);
|
|
|
|
|
+ var apiBase = getConversationApiBase(type);
|
|
|
|
|
+ var params = {
|
|
|
|
|
+ session_id: sessionId,
|
|
|
|
|
+ user_id: userId,
|
|
|
|
|
+ type: type || "",
|
|
|
|
|
+ };
|
|
|
|
|
+ setConversationApiDebugInfo({
|
|
|
|
|
+ apiBase: apiBase,
|
|
|
|
|
+ requestUrl: requestUrl,
|
|
|
|
|
+ method: "GET",
|
|
|
|
|
+ params: params,
|
|
|
|
|
+ response: null,
|
|
|
|
|
+ error: null,
|
|
|
|
|
+ });
|
|
|
console.log("[conversation-api] GET", requestUrl);
|
|
console.log("[conversation-api] GET", requestUrl);
|
|
|
return fetch(requestUrl, {
|
|
return fetch(requestUrl, {
|
|
|
method: "GET",
|
|
method: "GET",
|
|
@@ -725,21 +943,51 @@
|
|
|
})
|
|
})
|
|
|
.then(function (text) {
|
|
.then(function (text) {
|
|
|
var hint = "";
|
|
var hint = "";
|
|
|
|
|
+ var parsed = null;
|
|
|
try {
|
|
try {
|
|
|
- var j = JSON.parse(text);
|
|
|
|
|
- hint = j.msg || j.message || "";
|
|
|
|
|
|
|
+ parsed = JSON.parse(text);
|
|
|
|
|
+ hint = parsed.msg || parsed.message || "";
|
|
|
} catch (eP) {
|
|
} catch (eP) {
|
|
|
if (text) hint = text.slice(0, 120);
|
|
if (text) hint = text.slice(0, 120);
|
|
|
}
|
|
}
|
|
|
|
|
+ setConversationApiDebugInfo({
|
|
|
|
|
+ apiBase: apiBase,
|
|
|
|
|
+ requestUrl: requestUrl,
|
|
|
|
|
+ method: "GET",
|
|
|
|
|
+ params: params,
|
|
|
|
|
+ response: parsed || text || null,
|
|
|
|
|
+ error: "HTTP " + r.status + (hint ? ":" + hint : ""),
|
|
|
|
|
+ });
|
|
|
throw new Error("对话接口 HTTP " + r.status + (hint ? ":" + hint : ""));
|
|
throw new Error("对话接口 HTTP " + r.status + (hint ? ":" + hint : ""));
|
|
|
});
|
|
});
|
|
|
})
|
|
})
|
|
|
.then(function (res) {
|
|
.then(function (res) {
|
|
|
|
|
+ setConversationApiDebugInfo({
|
|
|
|
|
+ apiBase: apiBase,
|
|
|
|
|
+ requestUrl: requestUrl,
|
|
|
|
|
+ method: "GET",
|
|
|
|
|
+ params: params,
|
|
|
|
|
+ response: res,
|
|
|
|
|
+ error: null,
|
|
|
|
|
+ });
|
|
|
var data = normalizeConversationApiPayload(res);
|
|
var data = normalizeConversationApiPayload(res);
|
|
|
if (!data) {
|
|
if (!data) {
|
|
|
throw new Error("对话接口返回数据为空或格式无法解析");
|
|
throw new Error("对话接口返回数据为空或格式无法解析");
|
|
|
}
|
|
}
|
|
|
return data;
|
|
return data;
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch(function (e) {
|
|
|
|
|
+ if (!conversationApiDebugInfo || !conversationApiDebugInfo.error) {
|
|
|
|
|
+ setConversationApiDebugInfo({
|
|
|
|
|
+ apiBase: apiBase,
|
|
|
|
|
+ requestUrl: requestUrl,
|
|
|
|
|
+ method: "GET",
|
|
|
|
|
+ params: params,
|
|
|
|
|
+ response: conversationApiDebugInfo && conversationApiDebugInfo.response,
|
|
|
|
|
+ error: e && e.message ? e.message : String(e),
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ throw e;
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -747,6 +995,7 @@
|
|
|
var main = document.getElementById("main");
|
|
var main = document.getElementById("main");
|
|
|
var params = getConversationQueryParams();
|
|
var params = getConversationQueryParams();
|
|
|
if (!params) {
|
|
if (!params) {
|
|
|
|
|
+ setConversationApiDebugInfo(null);
|
|
|
render(parsePayload());
|
|
render(parsePayload());
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -816,54 +1065,18 @@
|
|
|
return d.getFullYear() + "年" + (d.getMonth() + 1) + "月" + d.getDate() + "日";
|
|
return d.getFullYear() + "年" + (d.getMonth() + 1) + "月" + d.getDate() + "日";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- function normalizeShareMessagesForRender(messages) {
|
|
|
|
|
- var list = Array.isArray(messages) ? messages : [];
|
|
|
|
|
- return list.map(function (msg) {
|
|
|
|
|
- if (!msg || typeof msg !== "object") return msg;
|
|
|
|
|
- var m = Object.assign({}, msg);
|
|
|
|
|
- var imgs = Array.isArray(m.images) ? m.images.slice() : [];
|
|
|
|
|
- if (!imgs.length && m.imageUrl) imgs = [m.imageUrl];
|
|
|
|
|
- if (!imgs.length && m.image) imgs = [m.image];
|
|
|
|
|
- imgs = imgs
|
|
|
|
|
- .map(function (u) {
|
|
|
|
|
- return String(u || "").trim();
|
|
|
|
|
- })
|
|
|
|
|
- .filter(function (u) {
|
|
|
|
|
- return /^https?:\/\//i.test(u);
|
|
|
|
|
- });
|
|
|
|
|
- if (m.isImage || imgs.length) {
|
|
|
|
|
- if (!imgs.length) return m;
|
|
|
|
|
- m.isImage = true;
|
|
|
|
|
- m.imageUrl = imgs[0];
|
|
|
|
|
- m.images = imgs;
|
|
|
|
|
- }
|
|
|
|
|
- return m;
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- function renderUserImageBubbleHtml(msg) {
|
|
|
|
|
- var imgs = Array.isArray(msg.images) && msg.images.length ? msg.images : [];
|
|
|
|
|
- if (!imgs.length && msg.imageUrl) imgs = [msg.imageUrl];
|
|
|
|
|
- if (!imgs.length) return "";
|
|
|
|
|
- var html = "";
|
|
|
|
|
- for (var i = 0; i < imgs.length; i++) {
|
|
|
|
|
- html +=
|
|
|
|
|
- '<div class="row row--user"><div class="user-bubble"><img class="user-bubble__image" src="' +
|
|
|
|
|
- escHtml(imgs[i]) +
|
|
|
|
|
- '" alt="图片" loading="lazy" decoding="async" /></div></div>';
|
|
|
|
|
- }
|
|
|
|
|
- return html;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
function renderMessages(messages) {
|
|
function renderMessages(messages) {
|
|
|
var html = "";
|
|
var html = "";
|
|
|
- var list = normalizeShareMessagesForRender(messages);
|
|
|
|
|
|
|
+ var list = Array.isArray(messages) ? messages : [];
|
|
|
for (var i = 0; i < list.length; i++) {
|
|
for (var i = 0; i < list.length; i++) {
|
|
|
var msg = list[i];
|
|
var msg = list[i];
|
|
|
if (!msg) continue;
|
|
if (!msg) continue;
|
|
|
if (msg.role === "user") {
|
|
if (msg.role === "user") {
|
|
|
- if (msg.isImage && (msg.imageUrl || (msg.images && msg.images.length))) {
|
|
|
|
|
- html += renderUserImageBubbleHtml(msg);
|
|
|
|
|
|
|
+ if (msg.isImage && msg.imageUrl) {
|
|
|
|
|
+ html +=
|
|
|
|
|
+ '<div class="row row--user"><div class="user-bubble"><img class="user-bubble__image" src="' +
|
|
|
|
|
+ escHtml(msg.imageUrl) +
|
|
|
|
|
+ '" alt="图片" loading="lazy" decoding="async" /></div></div>';
|
|
|
var imageUserText = String(msg.content || "").trim();
|
|
var imageUserText = String(msg.content || "").trim();
|
|
|
if (imageUserText) {
|
|
if (imageUserText) {
|
|
|
html +=
|
|
html +=
|
|
@@ -929,7 +1142,7 @@
|
|
|
var msg = list[i];
|
|
var msg = list[i];
|
|
|
if (!msg) continue;
|
|
if (!msg) continue;
|
|
|
if (msg.role === "user") {
|
|
if (msg.role === "user") {
|
|
|
- if (msg.isImage && (msg.imageUrl || (msg.images && msg.images.length))) {
|
|
|
|
|
|
|
+ if (msg.isImage && msg.imageUrl) {
|
|
|
count += 1;
|
|
count += 1;
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
@@ -1665,6 +1878,7 @@
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ bindApiDebugModalEvents();
|
|
|
loadPageData();
|
|
loadPageData();
|
|
|
}
|
|
}
|
|
|
|
|
|