feat: add search_image and product_list content types with image upload
Some checks failed
Lock Threads / action (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot EE docker images / merge (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot CE docker images / merge (push) Has been cancelled
Run Chatwoot CE spec / lint-backend (push) Has been cancelled
Run Chatwoot CE spec / lint-frontend (push) Has been cancelled
Run Chatwoot CE spec / frontend-tests (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (0, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (1, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (10, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (11, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (12, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (13, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (14, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (15, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (2, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (3, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (4, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (5, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (6, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (7, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (8, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (9, 16) (push) Has been cancelled
Run Linux nightly installer / nightly (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Some checks failed
Lock Threads / action (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot EE docker images / merge (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot CE docker images / merge (push) Has been cancelled
Run Chatwoot CE spec / lint-backend (push) Has been cancelled
Run Chatwoot CE spec / lint-frontend (push) Has been cancelled
Run Chatwoot CE spec / frontend-tests (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (0, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (1, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (10, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (11, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (12, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (13, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (14, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (15, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (2, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (3, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (4, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (5, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (6, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (7, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (8, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (9, 16) (push) Has been cancelled
Run Linux nightly installer / nightly (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
## 新增功能
### 1. 新增消息类型
- 添加 search_image (17) 和 product_list (18) content_type
- 支持图片搜索和商品列表消息展示
### 2. 图片上传功能
- 添加 ImageUploadButton 组件,支持图片上传到 Mall API
- 上传后发送 search_image 类型消息,图片 URL 存储在 content_attributes
- 支持图片文件验证(类型、大小)
### 3. Webhook 推送优化
- 修改 webhook_listener.rb,允许 search_image 类型即使 content 为空也推送 webhook
- 解决 search_image 消息不触发 webhook 的问题
### 4. 前端组件
- 新增 SearchImage.vue 组件(widget 和 dashboard)
- 新增 ProductList.vue、Logistics.vue、OrderDetail.vue、OrderList.vue 组件
- 更新 Message.vue 路由逻辑支持新的 content_type
- 更新 UserMessage.vue 支持 search_image 消息显示
### 5. API 层修改
- widget/messages_controller.rb: 允许 content_attributes 参数
- widget/base_controller.rb: 使用前端传入的 content_attributes
- widget/conversation API: 支持 contentAttributes 参数传递
- conversation actions 和 helpers: 完整的 content_attributes 数据流
### 6. Widget 测试页面优化
- 重写 /widget_tests 页面,支持游客/用户模式切换
- 登录流程:使用 reset() + setUser(),不刷新页面
- 退出流程:使用 reset() + 刷新页面
- 添加详细的日志输出和状态显示
## 技术细节
### Message Model
```ruby
enum content_type: {
# ...existing types...
search_image: 17,
product_list: 18
}
```
### Webhook Listener
```ruby
# Allow search_image webhook even if content is blank
return if message.content.blank? && message.content_type != 'search_image'
```
### Widget Upload Flow
```
用户选择图片
→ 上传到 Mall API
→ 获取图片 URL
→ 发送消息: { content: '', content_type: 'search_image', content_attributes: { url: '...' } }
```
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
json.id @message.id
|
||||
json.content @message.content
|
||||
json.content_type @message.content_type
|
||||
json.inbox_id @message.inbox_id
|
||||
json.conversation_id @message.conversation.display_id
|
||||
json.message_type @message.message_type_before_type_cast
|
||||
|
||||
@@ -1,18 +1,222 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
padding: 20px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.test-panel {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.test-panel h1 {
|
||||
margin-top: 0;
|
||||
color: #333;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.status-card {
|
||||
background: #f8f9fa;
|
||||
border-left: 4px solid #28a745;
|
||||
padding: 15px;
|
||||
margin: 20px 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.status-card.visitor {
|
||||
border-left-color: #ffc107;
|
||||
}
|
||||
|
||||
.status-card h3 {
|
||||
margin-top: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.status-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.status-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.status-label {
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.status-value {
|
||||
color: #6c757d;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 12px 24px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #218838;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #5a6268;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #c82333;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background: #ffc107;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.btn-warning:hover {
|
||||
background: #e0a800;
|
||||
}
|
||||
|
||||
.log-section {
|
||||
margin-top: 30px;
|
||||
background: #1e1e1e;
|
||||
color: #d4d4d4;
|
||||
padding: 15px;
|
||||
border-radius: 6px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 12px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.log-section h3 {
|
||||
color: #4ec9b0;
|
||||
margin-top: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div class="test-panel">
|
||||
<h1>🧪 Chatwoot Widget 测试页面</h1>
|
||||
<p style="color: #666; margin-bottom: 20px;">
|
||||
<strong>测试流程:</strong><br>
|
||||
1. 页面加载时以游客模式启动(不做任何处理)<br>
|
||||
2. 点击"模拟用户登录":使用 reset() 清除聊天 + setUser() 设置用户(不刷新页面)<br>
|
||||
3. 点击"用户退出":使用 reset() 清除内容并刷新页面
|
||||
</p>
|
||||
|
||||
<div id="statusCard" class="status-card visitor">
|
||||
<h3 id="statusTitle">👤 当前状态:游客模式</h3>
|
||||
<div class="status-item">
|
||||
<span class="status-label">User ID:</span>
|
||||
<span class="status-value" id="displayUserId">无</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="status-label">Token:</span>
|
||||
<span class="status-value" id="displayToken">不存在</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="status-label">登录状态:</span>
|
||||
<span class="status-value" id="displayLoginStatus">未登录</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<button class="btn btn-primary" id="loginBtn" onclick="simulateLogin()">🔐 模拟用户登录</button>
|
||||
<button class="btn btn-danger hidden" id="logoutBtn" onclick="simulateLogout()">🚪 用户退出</button>
|
||||
<button class="btn btn-secondary" onclick="refreshPage()">🔄 刷新页面</button>
|
||||
<button class="btn btn-secondary" onclick="openWidget()">💬 打开聊天</button>
|
||||
<button class="btn btn-secondary" onclick="clearAllData()">🗑️ 清除所有数据</button>
|
||||
<button class="btn btn-warning hidden" id="cancelRefreshBtn" onclick="cancelRefresh()">❌ 取消刷新</button>
|
||||
</div>
|
||||
|
||||
<div id="refreshNotice" class="hidden" style="margin-top: 15px; padding: 10px; background: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;">
|
||||
<strong>⏳ 页面将在 <span id="countdown">2</span> 秒后自动刷新...</strong>
|
||||
</div>
|
||||
|
||||
<div class="log-section">
|
||||
<h3>📋 控制台日志</h3>
|
||||
<div id="logOutput">等待操作...</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<%
|
||||
# 使用动态的 user identifier,生成对应的 hash
|
||||
user_id = '123'
|
||||
user_hash = OpenSSL::HMAC.hexdigest(
|
||||
'sha256',
|
||||
@web_widget.hmac_token,
|
||||
user_id.to_s
|
||||
)
|
||||
# 测试用的用户信息
|
||||
# 检查 cookie 中是否有 token
|
||||
token_present = cookies[:token].present?
|
||||
|
||||
if token_present
|
||||
test_user_id = '211845'
|
||||
test_user_hash = OpenSSL::HMAC.hexdigest(
|
||||
'sha256',
|
||||
@web_widget.hmac_token,
|
||||
test_user_id.to_s
|
||||
)
|
||||
else
|
||||
test_user_id = nil
|
||||
test_user_hash = nil
|
||||
end
|
||||
%>
|
||||
|
||||
<script>
|
||||
// 测试用的 JWT Token
|
||||
const TEST_TOKEN = 'eyJ0eXAiOiJqd3QifQ.eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvOiIsImV4cCI6MTc3MjAwNTk1MSwiaWF0IjoxNzY5NDEzOTUxLCJuYmYiOjE3Njk0MTM5NTEsInVzZXJJZCI6MjExODQ1LCJ0eXBlIjoyLCJ0ZW5hbnRJZCI6MiwidWlkIjoyMTE4NDUsInMiOiI4VG5wd2QiLCJqdGkiOiI5MjFlYzhhMDE2Yjk3MTA5OTI1MjgxMjUzZWQ1MzBkYSJ9.ql7ianUz3Scn_y0JbMXjeq56BVhjpQqt2_vrdq0kUL4';
|
||||
|
||||
// 测试用户信息
|
||||
const TEST_USER = {
|
||||
userId: '<%= test_user_id %>',
|
||||
userHash: '<%= test_user_hash %>',
|
||||
email: '211845@example.com',
|
||||
name: '测试用户 211845'
|
||||
};
|
||||
|
||||
// 当前状态
|
||||
let isLoggedIn = false;
|
||||
let currentUser = null;
|
||||
let refreshTimer = null;
|
||||
let countdownTimer = null;
|
||||
let hadUserIdentifier = false; // 记录之前是否有过用户标识
|
||||
|
||||
// Helper function to get cookie value by name
|
||||
function getCookie(name) {
|
||||
const value = `; ${document.cookie}`;
|
||||
@@ -21,22 +225,64 @@ function getCookie(name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Helper function to set cookie
|
||||
function setCookie(name, value, days = 7) {
|
||||
const expires = new Date(Date.now() + days * 864e5).toUTCString();
|
||||
document.cookie = name + '=' + value + '; expires=' + expires + '; path=/';
|
||||
}
|
||||
|
||||
// Helper function to delete cookie
|
||||
function deleteCookie(name) {
|
||||
document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/';
|
||||
}
|
||||
|
||||
// 日志输出
|
||||
function addLog(message) {
|
||||
const logOutput = document.getElementById('logOutput');
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
logOutput.innerHTML += `<div>[${timestamp}] ${message}</div>`;
|
||||
logOutput.scrollTop = logOutput.scrollHeight;
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
// 更新状态显示
|
||||
function updateStatusDisplay() {
|
||||
const token = getCookie('token');
|
||||
const userId = currentUser ? currentUser.userId : null;
|
||||
|
||||
document.getElementById('displayUserId').textContent = userId || '无';
|
||||
document.getElementById('displayToken').textContent = token ? '存在' : '不存在';
|
||||
document.getElementById('displayLoginStatus').textContent = isLoggedIn ? '已登录' : '未登录';
|
||||
|
||||
const statusCard = document.getElementById('statusCard');
|
||||
const statusTitle = document.getElementById('statusTitle');
|
||||
const loginBtn = document.getElementById('loginBtn');
|
||||
const logoutBtn = document.getElementById('logoutBtn');
|
||||
|
||||
if (isLoggedIn) {
|
||||
statusCard.className = 'status-card';
|
||||
statusTitle.textContent = '✅ 当前状态:已登录';
|
||||
loginBtn.classList.add('hidden');
|
||||
logoutBtn.classList.remove('hidden');
|
||||
} else {
|
||||
statusCard.className = 'status-card visitor';
|
||||
statusTitle.textContent = '👤 当前状态:游客模式';
|
||||
loginBtn.classList.remove('hidden');
|
||||
logoutBtn.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化 Chatwoot Widget
|
||||
window.chatwootSettings = {
|
||||
hideMessageBubble: false,
|
||||
// showUnreadMessagesDialog: false,
|
||||
// baseDomain: '.loca.lt',
|
||||
position: '<%= @widget_position %>',
|
||||
locale: 'zh_CN',
|
||||
useBrowserLanguage: false,
|
||||
type: '<%= @widget_type %>',
|
||||
// showPopoutButton: true,
|
||||
widgetStyle: '<%= @widget_style %>',
|
||||
darkMode: '<%= @dark_mode %>',
|
||||
};
|
||||
|
||||
// User ID for identification (simple string, not JWT token)
|
||||
const userId = '<%= user_id %>';
|
||||
|
||||
(function(d,t) {
|
||||
var BASE_URL = '';
|
||||
var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
|
||||
@@ -44,10 +290,20 @@ const userId = '<%= user_id %>';
|
||||
g.async = true;
|
||||
s.parentNode.insertBefore(g,s);
|
||||
g.onload=function(){
|
||||
// Get token from cookie (for custom attributes only)
|
||||
const token = getCookie('token');
|
||||
addLog('✅ Chatwoot SDK 加载完成');
|
||||
|
||||
// Initialize config with simple userId as userIdentifier
|
||||
// 检查当前登录状态(仅用于显示)
|
||||
const token = getCookie('token');
|
||||
addLog('🔍 检查登录状态...');
|
||||
addLog(' - Token 存在: ' + (token ? '是' : '否'));
|
||||
addLog(' - userId (服务器): ' + ('<%= test_user_id %>' || '无'));
|
||||
|
||||
// 无论是否有 token,都以游客模式启动
|
||||
// 用户信息通过点击登录按钮来设置
|
||||
addLog('👤 游客模式启动(不做任何处理)');
|
||||
hadUserIdentifier = false;
|
||||
|
||||
// 游客模式:不清除任何数据,保留现有会话
|
||||
const widgetConfig = {
|
||||
websiteToken: '<%= @web_widget.website_token %>',
|
||||
baseUrl: BASE_URL,
|
||||
@@ -55,68 +311,220 @@ const userId = '<%= user_id %>';
|
||||
useBrowserLanguage: false
|
||||
};
|
||||
|
||||
// Add userIdentifier (use simple userId, not JWT token)
|
||||
if (userId) {
|
||||
widgetConfig.userIdentifier = userId;
|
||||
}
|
||||
addLog('📝 初始化 widgetConfig (游客模式):');
|
||||
addLog(' - websiteToken: ' + widgetConfig.websiteToken);
|
||||
addLog(' - userIdentifier: (未设置)');
|
||||
|
||||
window.chatwootSDK.run(widgetConfig);
|
||||
|
||||
console.log('✅ Chatwoot Widget 已加载');
|
||||
console.log('User ID:', userId);
|
||||
console.log('Token from cookie:', token || 'Not set');
|
||||
|
||||
// Wait for widget to load, then set user attributes
|
||||
setTimeout(function() {
|
||||
if (userId && window.$chatwoot && window.$chatwoot.setUser) {
|
||||
window.$chatwoot.setUser(userId, {
|
||||
identifier_hash: '<%= user_hash %>',
|
||||
email: 'user@example.com',
|
||||
name: 'Token User',
|
||||
phone_number: '',
|
||||
custom_attributes: token ? {
|
||||
jwt_token: token,
|
||||
mall_token: token
|
||||
} : {}
|
||||
});
|
||||
console.log('✅ 已通过 setUser 设置用户属性,userId:', userId);
|
||||
} else if (token && window.$chatwoot && window.$chatwoot.setCustomAttributes) {
|
||||
// Fallback: use setCustomAttributes
|
||||
window.$chatwoot.setCustomAttributes({
|
||||
jwt_token: token,
|
||||
mall_token: token
|
||||
});
|
||||
console.log('✅ 已通过 setCustomAttributes 设置用户属性');
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
addLog('✅ 游客模式已启动');
|
||||
updateStatusDisplay();
|
||||
};
|
||||
})(document,"script");
|
||||
|
||||
window.addEventListener('chatwoot:ready', function() {
|
||||
console.log('chatwoot:ready', window.$chatwoot);
|
||||
})
|
||||
// 模拟用户登录
|
||||
function simulateLogin() {
|
||||
addLog('🔐 开始模拟用户登录...');
|
||||
|
||||
window.addEventListener('chatwoot:error', function(e) {
|
||||
console.log('chatwoot:error', e.detail)
|
||||
// 设置 token cookie
|
||||
setCookie('token', TEST_TOKEN);
|
||||
|
||||
// 验证 token 是否设置成功
|
||||
const tokenCheck = getCookie('token');
|
||||
if (tokenCheck) {
|
||||
addLog('✅ Token 已设置到 cookie');
|
||||
|
||||
// 设置当前用户状态
|
||||
isLoggedIn = true;
|
||||
currentUser = TEST_USER;
|
||||
|
||||
addLog('🔄 使用 reset() 清除之前的聊天内容...');
|
||||
if (window.$chatwoot && window.$chatwoot.reset) {
|
||||
window.$chatwoot.reset();
|
||||
addLog('✅ 已清除聊天内容和会话数据');
|
||||
} else {
|
||||
addLog('⚠️ Chatwoot reset() 方法不可用');
|
||||
}
|
||||
|
||||
// 等待 reset 完成后设置用户信息
|
||||
setTimeout(function() {
|
||||
addLog('👤 设置用户信息...');
|
||||
if (window.$chatwoot && window.$chatwoot.setUser) {
|
||||
window.$chatwoot.setUser(currentUser.userId, {
|
||||
identifier_hash: currentUser.userHash,
|
||||
email: currentUser.email,
|
||||
name: currentUser.name,
|
||||
phone_number: '',
|
||||
custom_attributes: {
|
||||
jwt_token: tokenCheck,
|
||||
mall_token: tokenCheck,
|
||||
login_status: 'logged_in'
|
||||
}
|
||||
});
|
||||
addLog('✅ 用户信息已设置: ' + currentUser.name);
|
||||
addLog('✅ 登录成功(无需刷新页面)');
|
||||
} else {
|
||||
addLog('⚠️ window.$chatwoot 未就绪');
|
||||
}
|
||||
updateStatusDisplay();
|
||||
}, 500);
|
||||
} else {
|
||||
addLog('❌ Token 设置失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟用户退出
|
||||
function simulateLogout() {
|
||||
addLog('🚪 开始模拟用户退出...');
|
||||
|
||||
// 删除 token cookie
|
||||
deleteCookie('token');
|
||||
isLoggedIn = false;
|
||||
currentUser = null;
|
||||
|
||||
addLog('✅ Token 已从 cookie 删除');
|
||||
|
||||
// 使用 Chatwoot 官方的 reset() 方法清除用户和会话数据
|
||||
if (window.$chatwoot && window.$chatwoot.reset) {
|
||||
window.$chatwoot.reset();
|
||||
addLog('✅ 已调用 Chatwoot reset() - 清除用户和会话数据');
|
||||
} else {
|
||||
addLog('⚠️ Chatwoot reset() 方法不可用');
|
||||
}
|
||||
|
||||
// 清除 localStorage 缓存
|
||||
try {
|
||||
const keysToRemove = [];
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i);
|
||||
if (key && (key.includes('chatwoot') || key.includes('cw_') || key.includes('widget'))) {
|
||||
keysToRemove.push(key);
|
||||
}
|
||||
}
|
||||
keysToRemove.forEach(key => localStorage.removeItem(key));
|
||||
addLog('🧹 已清除 ' + keysToRemove.length + ' 个 localStorage 缓存项');
|
||||
} catch (e) {
|
||||
addLog('⚠️ 清除缓存时出错: ' + e.message);
|
||||
}
|
||||
|
||||
addLog('⏳ 即将刷新页面以完成退出...');
|
||||
|
||||
// 显示刷新倒计时
|
||||
document.getElementById('refreshNotice').classList.remove('hidden');
|
||||
document.getElementById('cancelRefreshBtn').classList.remove('hidden');
|
||||
document.getElementById('countdown').textContent = '2';
|
||||
|
||||
let countdown = 2;
|
||||
countdownTimer = setInterval(() => {
|
||||
countdown--;
|
||||
document.getElementById('countdown').textContent = countdown;
|
||||
if (countdown <= 0) {
|
||||
clearInterval(countdownTimer);
|
||||
addLog('🔄 正在刷新页面...');
|
||||
location.reload();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
refreshTimer = setTimeout(() => {
|
||||
location.reload();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// 取消刷新
|
||||
function cancelRefresh() {
|
||||
if (refreshTimer) {
|
||||
clearTimeout(refreshTimer);
|
||||
refreshTimer = null;
|
||||
}
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer);
|
||||
countdownTimer = null;
|
||||
}
|
||||
|
||||
document.getElementById('refreshNotice').classList.add('hidden');
|
||||
document.getElementById('cancelRefreshBtn').classList.add('hidden');
|
||||
|
||||
addLog('✅ 已取消自动刷新');
|
||||
addLog('⚠️ 注意:Chatwoot Widget 仍保持登录状态,需要手动刷新页面才能完全恢复游客模式');
|
||||
}
|
||||
|
||||
// 清除所有数据(包括会话)
|
||||
function clearAllData() {
|
||||
addLog('🗑️ 开始清除所有数据...');
|
||||
|
||||
// 使用 Chatwoot reset() 方法清除会话
|
||||
if (window.$chatwoot && window.$chatwoot.reset) {
|
||||
window.$chatwoot.reset();
|
||||
addLog('✅ 已调用 Chatwoot reset() - 清除会话和用户数据');
|
||||
}
|
||||
|
||||
// 删除 token cookie
|
||||
deleteCookie('token');
|
||||
addLog('✅ Token 已删除');
|
||||
|
||||
// 清除 localStorage
|
||||
try {
|
||||
const keysToRemove = [];
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i);
|
||||
if (key && (key.includes('chatwoot') || key.includes('cw_') || key.includes('widget'))) {
|
||||
keysToRemove.push(key);
|
||||
}
|
||||
}
|
||||
keysToRemove.forEach(key => localStorage.removeItem(key));
|
||||
addLog('🧹 已清除 ' + keysToRemove.length + ' 个 localStorage 项');
|
||||
} catch (e) {
|
||||
addLog('⚠️ 清除 localStorage 时出错: ' + e.message);
|
||||
}
|
||||
|
||||
isLoggedIn = false;
|
||||
currentUser = null;
|
||||
hadUserIdentifier = false;
|
||||
|
||||
updateStatusDisplay();
|
||||
addLog('✅ 所有数据已清除');
|
||||
|
||||
// 刷新页面
|
||||
addLog('⏳ 即将刷新页面...');
|
||||
setTimeout(() => {
|
||||
location.reload();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// 刷新页面
|
||||
function refreshPage() {
|
||||
addLog('🔄 正在刷新页面...');
|
||||
setTimeout(() => {
|
||||
location.reload();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// 打开聊天窗口
|
||||
function openWidget() {
|
||||
if (window.$chatwoot && window.$chatwoot.toggle) {
|
||||
window.$chatwoot.toggle('open');
|
||||
addLog('💬 聊天窗口已打开');
|
||||
} else {
|
||||
addLog('⚠️ Chatwoot 未就绪');
|
||||
}
|
||||
}
|
||||
|
||||
// 事件监听
|
||||
window.addEventListener('chatwoot:ready', function() {
|
||||
addLog('✅ Chatwoot Widget 已就绪');
|
||||
})
|
||||
|
||||
window.addEventListener('chatwoot:on-message', function(e) {
|
||||
console.log('chatwoot:on-message', e.detail)
|
||||
})
|
||||
|
||||
window.addEventListener('chatwoot:postback', function(e) {
|
||||
console.log('chatwoot:postback', e.detail)
|
||||
addLog('📨 收到消息');
|
||||
})
|
||||
|
||||
window.addEventListener('chatwoot:opened', function() {
|
||||
console.log('chatwoot:opened')
|
||||
addLog('🔓 Widget 已打开');
|
||||
})
|
||||
|
||||
window.addEventListener('chatwoot:closed', function() {
|
||||
console.log('chatwoot:closed')
|
||||
addLog('🔒 Widget 已关闭');
|
||||
})
|
||||
|
||||
window.addEventListener('chatwoot:on-start-conversation', function(e) {
|
||||
console.log('chatwoot:on-start-conversation', e.detail)
|
||||
})
|
||||
// 初始化
|
||||
addLog('🚀 测试页面已加载');
|
||||
</script>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
timezone: '<%= @web_widget.inbox.timezone %>',
|
||||
allowMessagesAfterResolved: <%= @web_widget.inbox.allow_messages_after_resolved %>,
|
||||
disableBranding: <%= @web_widget.inbox.account.feature_enabled?('disable_branding') %>,
|
||||
mallUrl: '<%= ENV.fetch('MALL_URL', 'https://apicn.qa1.gaia888.com') %>',
|
||||
}
|
||||
window.chatwootPubsubToken = '<%= @contact_inbox.pubsub_token %>'
|
||||
window.authToken = '<%= @token %>'
|
||||
|
||||
Reference in New Issue
Block a user