fix: 品牌下发审查修复(esc统一/语料中性化/穿越加固/反向断言动态化)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-12 22:52:08 +08:00
parent bbc7bc520e
commit 7697673a3b
8 changed files with 137 additions and 34 deletions
+1 -1
View File
@@ -30,7 +30,7 @@
<div style="display:flex;align-items:center;gap:14px;">
<div class="help-content">
<div class="help-title">How can we help?</div>
<div class="help-description">Ask about pipe specifications, quotations, MOQ, shipping and delivery.</div>
<div class="help-description">Ask about products, quotations, MOQ, shipping and delivery.</div>
</div>
<div class="help-actions">
<!-- Task 10: 联系按钮/标题/欢迎语/话题/隐私链接由 chat.js 从
+6 -10
View File
@@ -86,9 +86,12 @@ var chatBrandReady = (function () {
var em = contactHref(emailAttr || b.email || "", "mail");
if (emBtn) { if (em) emBtn.href = em; else emBtn.style.display = "none"; }
if (b.placeholder) inputEl.placeholder = b.placeholder;
var firstMsg = document.querySelector("#messages .message");
var bubble = firstMsg && firstMsg.querySelector(".bubble");
if (b.welcome) {
var bubble = document.querySelector("#messages .message .bubble");
if (bubble) bubble.textContent = b.welcome;
} else if (firstMsg) {
firstMsg.style.display = "none"; // 空 welcome 不渲染空气泡(与挂件行为对齐)
}
if (Array.isArray(b.topics) && b.topics.length) {
var grid = document.querySelector(".popular-topics-grid");
@@ -286,15 +289,8 @@ var chatBrandReady = (function () {
}
});
// Popular topics buttons(出厂模板的兜底话题;brand 下发后由 applyBrand
// 重建,这里的绑定覆盖"brand 失败"路径)
var topicBtns = document.querySelectorAll(".popular-topic-btn");
topicBtns.forEach(function (btn) {
btn.addEventListener("click", function () {
var topic = this.getAttribute("data-topic");
sendTopicMessage(topic);
});
});
// (出厂模板话题区为空——话题按钮只由 applyBrand 重建并自带绑定,
// 无静态兜底绑定段)
// Task 10: 会话 key 租户化(cc_session_id_<slug>)——brand 落定(成功或
// 失败,slug 回退 "default")后才读 key/连 WS,保证同浏览器多租户互不串
+8 -6
View File
@@ -39,11 +39,10 @@
<p><strong>Expected behavior:</strong></p>
<ul class="test-list">
<li>Invite card appears after 5 seconds</li>
<li>Invite card has blue gradient header with robot emoji</li>
<li>Three topic buttons: "Get Quotation", "Pipe Specifications" and "Delivery &amp; Shipping"</li>
<li>Invite card has brand-color gradient header with robot emoji</li>
<li>Topic buttons come from the tenant brand config (<code>/api/public/brand</code> → topics)</li>
<li>Action buttons: "Start Chat" and "No Thanks"</li>
<li>Click "Get Quotation" → invite closes → chat opens → message auto-sends</li>
<li>Click "Pipe Specifications" → invite closes → chat opens → message auto-sends</li>
<li>Click a topic button → invite closes → chat opens → message auto-sends</li>
<li>Click "Start Chat" → invite closes → chat opens</li>
<li>Click "No Thanks" → invite permanently closes</li>
<li>Click "No Thanks" or X → refresh → invite stays hidden AND the old card area stays clickable (no invisible overlay; regression 2026-09-01)</li>
@@ -55,7 +54,7 @@
<div class="info">
<h2>Popular Topics in Chat Panel</h2>
<ul class="test-list">
<li>Chat panel shows "POPULAR TOPICS" section with two buttons</li>
<li>Chat panel shows "POPULAR TOPICS" section with the tenant's topic buttons</li>
<li>Click topic → message auto-sends → topics section hides</li>
</ul>
</div>
@@ -108,7 +107,7 @@
"data-mobile": "true",
"data-debug": "true",
"data-whatsapp": "https://wa.me/8613800138000",
"data-email": "mailto:demo@hesupipe.com"
"data-email": "mailto:demo@example.com"
};
var rebuildLoading = false, rebuildPending = null;
@@ -116,6 +115,9 @@
// Task 10: host id 已租户化(cc-widget-host-<slug>),按前缀清扫
var hosts = document.querySelectorAll('[id^="cc-widget-host-"]');
for (var i = 0; i < hosts.length; i++) hosts[i].remove();
// 复审: 清掉旧挂件的 debug 钩子——waitForWidget 轮询它,旧钩子残留
// 会让重建中的断言误读"新挂件已挂载"
try { delete window.__chatWidgetDebug; } catch (e) {}
}
function rebuildWidget(extra) {
if (rebuildLoading) { rebuildPending = extra || {}; return; } // 快速连点:只记最新意图,当前加载完接力重建
+6 -6
View File
@@ -388,7 +388,7 @@
panel.className = "chat-panel";
panel.innerHTML = [
'<div class="panel-header">',
'<div class="avatar"><span class="avatar-emoji">🤖</span><img class="avatar-img" alt="" src="'+avatarUrl+'"></div>',
'<div class="avatar"><span class="avatar-emoji">🤖</span><img class="avatar-img" alt="" src="'+esc(avatarUrl)+'"></div>',
'<div class="info"><h3>' + esc(title) + '</h3><div class="status">Online now · Usually replies in minutes</div></div>',
'<div class="actions">',
'<button class="action-btn" aria-label="Minimize">−</button>',
@@ -396,7 +396,7 @@
'</div></div>',
'<div class="ended" id="w-ended">Chat ended. We\'ll be in touch!</div>',
'<div class="panel-top">',
'<div class="help-block"><div style="display:flex;align-items:center;gap:14px;"><div class="help-content"><div class="help-title">How can we help?</div><div class="help-description">Ask about pipe specifications, quotations, MOQ, shipping and delivery.</div></div><div class="help-actions">' + waBtnHtml + emBtnHtml + '</div></div></div>',
'<div class="help-block"><div style="display:flex;align-items:center;gap:14px;"><div class="help-content"><div class="help-title">How can we help?</div><div class="help-description">Ask about products, quotations, MOQ, shipping and delivery.</div></div><div class="help-actions">' + waBtnHtml + emBtnHtml + '</div></div></div>',
'</div>',
'<div class="chat-separator"></div>',
'<div class="panel-messages" id="w-msgs">',
@@ -416,10 +416,10 @@
'<div class="invite-panel hide">',
'<div class="invite-header">',
'<div class="invite-icon-wrapper">',
'<div class="invite-icon"><span class="avatar-emoji">🤖</span><img class="avatar-img" alt="" src="'+avatarUrl+'"><span class="invite-status-dot"></span></div>',
'<div class="invite-icon"><span class="avatar-emoji">🤖</span><img class="avatar-img" alt="" src="'+esc(avatarUrl)+'"><span class="invite-status-dot"></span></div>',
'</div>',
'<div class="invite-header-content">',
'<div class="invite-title">Chat with us for pipe specs, MOQ, delivery timing and quotations.</div>',
'<div class="invite-title">Chat with us for products, quotations and delivery.</div>',
'</div>',
'<div class="invite-header-actions">',
'<button class="invite-close-btn" aria-label="Close">',
@@ -467,13 +467,13 @@
toggleBtn.className = "toggle-btn" + (isMobile ? " mobile" : "");
if (isMobile) {
toggleBtn.innerHTML = `
<div class="mobile-circle"><span class="avatar-emoji">🤖</span><img class="avatar-img" alt="" src="${avatarUrl}"><span class="mobile-dot"></span></div>
<div class="mobile-circle"><span class="avatar-emoji">🤖</span><img class="avatar-img" alt="" src="${esc(avatarUrl)}"><span class="mobile-dot"></span></div>
<span class="mobile-label">Chat with us</span>
`;
} else {
toggleBtn.innerHTML = `
<div class="toggle-avatar-wrap">
<div class="toggle-avatar-circle"><span class="avatar-emoji">🤖</span><img class="avatar-img" alt="" src="${avatarUrl}"></div>
<div class="toggle-avatar-circle"><span class="avatar-emoji">🤖</span><img class="avatar-img" alt="" src="${esc(avatarUrl)}"></div>
<div class="toggle-avatar-dot"></div>
</div>
<div style="color:white;">