Files
ai-kefu-jc/static/js/chat.js
T
浅念 afee0dfa4d feat: 前端HESUPIPE品牌化+管道文案+GDPR提示
- 品牌色橙#f08a31→HESUPIPE蓝#066fc0(取自官网主题CSS a:hover/导航主色),7槽位色板三处同步:theme.js/widget.js内联副本/style.css :root
- chat.html/widget.js/js/chat.js:标题HESUPIPE Support、管道场景描述、欢迎语、热门话题Get Quotation/Pipe Specifications/Delivery & Shipping、WhatsApp+8615937108317、邮箱hesu@hesuu.com
- 邀请卡文案管道化(pipe specs, MOQ, delivery timing)+GDPR小字;聊天面板footer GDPR行(复用.chat-footer)
- admin.html VOLT→HESUPIPE;头像PIL重绘为品牌蓝底白H(512/128px)
- widget-test.html色值同步+断言基准更新(浏览器断言9/9 Δ=0);.gitignore排除.playwright-mcp/

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-29 18:57:49 +08:00

232 lines
7.5 KiB
JavaScript

// ---- 动态主题色 bootstrap(spec §3):?color= → 派生 → :root 变量 ----
(function () {
if (typeof ChatTheme === "undefined") return; // theme.js 加载失败→style.css :root 默认橙兜底
var m = /(?:^|[?&])color=([^&]*)/.exec(location.search);
function safeDecode(s) { try { return decodeURIComponent(s); } catch (e) { return ""; } }
var color = m ? safeDecode(m[1]).trim() : "";
var palette = ChatTheme.derivePalette(color || ChatTheme.DEFAULT_COLOR);
var root = document.documentElement.style;
root.setProperty("--w-primary", palette.primary);
root.setProperty("--w-primary-rgb", palette.primaryRgb);
root.setProperty("--w-gradient-end", palette.gradientEnd);
root.setProperty("--w-deep-text", palette.deepText);
root.setProperty("--w-caption-text", palette.captionText);
root.setProperty("--w-soft-bg", palette.softBg);
root.setProperty("--w-border-soft", palette.borderSoft);
// 头像兜底(与挂件同机制)
Array.prototype.forEach.call(document.querySelectorAll(".avatar-img"), function (img) {
function hideEmoji() {
var s = img.parentElement && img.parentElement.querySelector(".avatar-emoji");
if (s) s.style.display = "none";
}
if (img.complete && img.naturalWidth > 0) { hideEmoji(); return; }
img.addEventListener("load", hideEmoji);
img.addEventListener("error", function () { img.remove(); });
});
})();
(function () {
// 联系方式配置(与 widget.js 的 data-* 机制一致)
var chatScript = document.currentScript || document.querySelector('script[src*="chat.js"]');
var whatsapp = (chatScript && chatScript.getAttribute("data-whatsapp")) || "https://wa.me/+8615937108317";
var email = (chatScript && chatScript.getAttribute("data-email")) || "mailto:hesu@hesuu.com";
var waBtn = document.querySelector('.whatsapp-btn');
var emBtn = document.querySelector('.email-btn');
if (waBtn) waBtn.href = whatsapp;
if (emBtn) emBtn.href = email;
var sessionId = localStorage.getItem("cc_session_id");
var messagesEl = document.getElementById("messages");
var inputEl = document.getElementById("input");
var sendBtn = document.getElementById("sendBtn");
var typingEl = document.getElementById("typing");
var endedEl = document.getElementById("chatEnded");
var popularTopicsEl = document.getElementById("popular-topics");
var ws = null;
var ended = false;
// Current streaming message element
var streamBubble = null;
var statusTimer = null;
function getWsUrl() {
var loc = window.location;
var proto = loc.protocol === "https:" ? "wss:" : "ws:";
return proto + "//" + loc.host + "/ws/chat";
}
function connect() {
ws = new WebSocket(getWsUrl());
ws.onopen = function () {
if (sessionId) {
ws.send(JSON.stringify({ session_id: sessionId }));
} else {
ws.send(JSON.stringify({ source: "standalone" }));
}
};
ws.onmessage = function (e) {
var data = JSON.parse(e.data);
if (data.type === "init" || data.type === "resumed") {
sessionId = data.session_id;
localStorage.setItem("cc_session_id", sessionId);
if (data.type === "resumed") {
loadHistory();
}
}
if (data.type === "stream_start") {
clearStatusTimer();
hideTyping();
streamBubble = addStreamBubble();
}
if (data.type === "stream_chunk") {
if (streamBubble) {
streamBubble.textContent += data.content;
messagesEl.scrollTop = messagesEl.scrollHeight;
}
}
if (data.type === "stream_end") {
streamBubble = null;
}
if (data.type === "evaluated") {
if (data.ended) {
ended = true;
endedEl.classList.add("active");
inputEl.disabled = true;
sendBtn.disabled = true;
}
}
if (data.type === "error") {
clearStatusTimer();
hideTyping();
streamBubble = null;
addMessage("assistant", "抱歉,出了点问题,请稍后再试。");
}
};
ws.onclose = function () {
if (!ended) {
setTimeout(connect, 3000);
}
};
ws.onerror = function () {
ws.close();
};
}
function send() {
var text = inputEl.value.trim();
if (!text || ended || !ws || ws.readyState !== WebSocket.OPEN) return;
addMessage("user", text);
inputEl.value = "";
hidePopularTopics();
// Immediate feedback: show status then typing
showStatusThenTyping();
ws.send(JSON.stringify({ content: text }));
}
function sendTopicMessage(topic) {
if (ended || !ws || ws.readyState !== WebSocket.OPEN) return;
addMessage("user", topic);
hidePopularTopics();
// Immediate feedback: show status then typing
showStatusThenTyping();
ws.send(JSON.stringify({ content: topic }));
}
function hidePopularTopics() {
if (popularTopicsEl) {
popularTopicsEl.style.display = "none";
}
}
function showStatusThenTyping() {
clearStatusTimer();
showTyping();
// Auto-hide typing is handled by stream_start replacing it
}
function clearStatusTimer() {
if (statusTimer) {
clearTimeout(statusTimer);
statusTimer = null;
}
}
function addMessage(role, content) {
var div = document.createElement("div");
div.className = "message " + role;
var wrapper = document.createElement("div");
var bubble = document.createElement("div");
bubble.className = "bubble";
bubble.textContent = content;
wrapper.appendChild(bubble);
div.appendChild(wrapper);
messagesEl.appendChild(div);
messagesEl.scrollTop = messagesEl.scrollHeight;
}
function addStreamBubble() {
var div = document.createElement("div");
div.className = "message assistant";
var wrapper = document.createElement("div");
var bubble = document.createElement("div");
bubble.className = "bubble";
bubble.textContent = "";
wrapper.appendChild(bubble);
div.appendChild(wrapper);
messagesEl.appendChild(div);
messagesEl.scrollTop = messagesEl.scrollHeight;
return bubble;
}
function showTyping() {
typingEl.classList.add("active");
messagesEl.scrollTop = messagesEl.scrollHeight;
}
function hideTyping() {
typingEl.classList.remove("active");
}
function loadHistory() {
}
sendBtn.addEventListener("click", send);
inputEl.addEventListener("keydown", function (e) {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
send();
}
});
// Popular topics buttons
var topicBtns = document.querySelectorAll(".popular-topic-btn");
topicBtns.forEach(function (btn) {
btn.addEventListener("click", function () {
var topic = this.getAttribute("data-topic");
sendTopicMessage(topic);
});
});
connect();
})();