chore: CRM-NEW baseline

This commit is contained in:
wangliang
2026-10-23 10:35:28 +08:00
commit aa499f6dbf
111 changed files with 15126 additions and 0 deletions
+87
View File
@@ -0,0 +1,87 @@
/**
* Yehwang LibreDesk widget 接入脚本(新栈)
*
* 用法:页面里先设置 window.YehwangChatConfig,再引入本脚本。
*
* <script>
* window.YehwangChatConfig = {
* baseURL: 'http://192.168.31.61:9000',
* inboxID: '10fd5207-d235-4c49-beaa-a65eed0ddd0e',
* identityURL: 'http://192.168.31.61:4100/identity',
* siteJWT: window.__SITE_JWT__ || null // 站点登录后注入
* };
* </script>
* <script async src="/ai-snippet.js"></script>
*
* 有 siteJWT:走 bridge /identity 验签换 LibreDesk widget JWT(生产路径)。
* 无 siteJWT:匿名访客。
*/
(function () {
var cfg = window.YehwangChatConfig || {};
var baseURL = String(cfg.baseURL || 'http://192.168.31.61:9000').replace(/\/+$/, '');
var inboxID = cfg.inboxID || '10fd5207-d235-4c49-beaa-a65eed0ddd0e';
var identityURL = cfg.identityURL || 'http://192.168.31.61:4100/identity';
var state = { loaded: false, userJWT: null };
function loadWidget() {
if (state.loaded) return;
state.loaded = true;
window.LibredeskSettings = { baseURL: baseURL, inboxID: inboxID };
if (state.userJWT) window.LibredeskSettings.userJWT = state.userJWT;
var s = document.createElement('script');
s.async = true;
s.src = baseURL + '/widget.js';
document.head.appendChild(s);
}
function postIdentity(body) {
return fetch(identityURL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
}).then(function (res) {
if (!res.ok) throw new Error('identity HTTP ' + res.status);
return res.json();
});
}
async function resolveUserJWT() {
if (cfg.userJWT) return cfg.userJWT;
if (cfg.siteJWT) {
var out = await postIdentity({ token: cfg.siteJWT });
return out.userJWT;
}
// 仅开发联调:直接传 email(需要 bridge IDENTITY_DEV_MODE=true)
if (cfg.demoEmail) {
var dev = await postIdentity({
email: cfg.demoEmail,
externalUserId: cfg.demoExternalId || cfg.demoEmail,
firstName: cfg.demoFirstName || 'Customer',
lastName: cfg.demoLastName || '',
});
return dev.userJWT;
}
return null;
}
async function boot() {
if (cfg.siteJWT || cfg.demoEmail || cfg.userJWT) {
try {
state.userJWT = await resolveUserJWT();
} catch (e) {
// 身份失败不阻塞 widget,降级为匿名会话
console.warn('[YehwangChat] identity failed, fallback to anonymous:', e.message);
}
}
loadWidget();
}
window.YehwangChat = {
/** 页面登录状态变化后可调用:重新设置 JWT 并重载(简单做法:刷新页面) */
setSiteJWT: function (jwt) { cfg.siteJWT = jwt; },
isLoggedIn: function () { return Boolean(state.userJWT); },
};
boot();
})();
+99
View File
@@ -0,0 +1,99 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>crm-new · LibreDesk Widget 测试页</title>
<style>
body { font-family: -apple-system, "Segoe UI", "PingFang SC", sans-serif; margin: 0; background: #f4f7fb; color: #1f2937; }
.card { max-width: 720px; margin: 48px auto; background: #fff; border-radius: 14px; padding: 28px 32px; box-shadow: 0 10px 30px rgba(0,0,0,.08); }
h1 { font-size: 20px; margin: 0 0 4px; }
.sub { color: #6b7280; font-size: 13px; margin-bottom: 20px; }
label { display: block; font-size: 13px; color: #374151; margin: 14px 0 6px; }
input { width: 100%; box-sizing: border-box; padding: 10px 12px; border: 1px solid #d1d5db; border-radius: 8px; font-size: 14px; }
.row { display: flex; gap: 10px; margin-top: 18px; flex-wrap: wrap; }
button { padding: 10px 16px; border: 0; border-radius: 8px; font-size: 14px; cursor: pointer; }
.primary { background: #1f6feb; color: #fff; }
.ghost { background: #eef2f7; color: #1f2937; }
.badge { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 12px; background: #e0f2fe; color: #0369a1; margin-left: 6px; }
.hint { margin-top: 22px; font-size: 13px; color: #6b7280; border-top: 1px dashed #e5e7eb; padding-top: 16px; line-height: 1.9; }
code { background: #f3f4f6; padding: 1px 5px; border-radius: 4px; font-size: 12px; }
#status { margin-top: 12px; font-size: 13px; color: #065f46; }
</style>
</head>
<body>
<div class="card">
<h1>LibreDesk Widget 测试 <span class="badge" id="modeBadge">匿名</span></h1>
<div class="sub">页面打开后右下角应出现聊天气泡(自动加载,无需点击)· v2</div>
<label>登录用户邮箱(dev 模式)</label>
<input id="email" value="buyer@test-customer.nl" />
<label>站点 JWT(生产路径;留空则用上面的 dev 邮箱)</label>
<input id="siteJwt" placeholder="由 yehwang 站点登录后注入" />
<div class="row">
<button class="primary" onclick="reload('login')">切换为登录身份</button>
<button class="ghost" onclick="reload('anonymous')">切换为匿名访客</button>
<button class="ghost" onclick="location.href=location.pathname">重新加载</button>
</div>
<div id="status"></div>
<div class="hint">
<b>排查</b><br />
· 气泡没出现:打开浏览器控制台看 <code>[YehwangChat]</code> / <code>Failed to initialize Libredesk Widget</code> 报错<br />
· 登录身份:页面会先调 bridge <code>POST /identity</code> 拿 <code>userJWT</code>,再加载 widget<br />
· 聊天窗口打不开:检查 LibreDesk 可信域名里是否有 <code>192.168.31.61:8088</code>(CSP frame-ancestors)
</div>
</div>
<script>
var params = new URLSearchParams(location.search);
var mode = params.get('as') === 'login' ? 'login' : 'anonymous';
var email = params.get('email') || 'buyer@test-customer.nl';
document.getElementById('email').value = email;
document.getElementById('modeBadge').textContent = mode === 'login' ? '登录身份' : '匿名';
function reload(nextMode) {
var q = new URLSearchParams();
q.set('as', nextMode);
if (nextMode === 'login') {
q.set('email', document.getElementById('email').value.trim());
var jwt = document.getElementById('siteJwt').value.trim();
if (jwt) q.set('siteJwt', jwt);
}
location.search = q.toString();
}
function boot() {
var cfg = {
baseURL: 'http://192.168.31.61:9000',
inboxID: '10fd5207-d235-4c49-beaa-a65eed0ddd0e',
identityURL: 'http://192.168.31.61:4100/identity',
};
if (mode === 'login') {
var siteJwt = params.get('siteJwt');
if (siteJwt) cfg.siteJWT = siteJwt;
else {
cfg.demoEmail = email;
cfg.demoFirstName = 'Jan';
cfg.demoLastName = 'Jansen';
}
}
window.YehwangChatConfig = cfg;
var s = document.createElement('script');
s.src = '/ai-snippet.js?v=' + Date.now();
s.onload = function () {
document.getElementById('status').textContent = mode === 'login'
? '已按登录身份加载 widget(右下角气泡)'
: '已按匿名访客加载 widget(右下角气泡)';
};
document.body.appendChild(s);
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot);
else boot();
</script>
</body>
</html>