1015 lines
35 KiB
JavaScript
1015 lines
35 KiB
JavaScript
let currentQA = null;
|
||
let selectedQuestions = [];
|
||
let selectedAnswers = [];
|
||
let selectedQAs = new Set(); // 用于批量操作
|
||
let groupList = []; // 存储群组列表
|
||
|
||
// 加载群组列表
|
||
async function loadGroupList() {
|
||
try {
|
||
const response = await fetch('api/groups');
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
groupList = result.groups || [];
|
||
|
||
// 更新过滤区域的群组下拉框
|
||
const groupFilter = document.getElementById('groupFilter');
|
||
if (groupFilter) {
|
||
updateGroupSelect(groupFilter);
|
||
}
|
||
|
||
// 更新模态框中的群组下拉框
|
||
const groupIdSelect = document.getElementById('groupId');
|
||
if (groupIdSelect) {
|
||
updateGroupSelect(groupIdSelect);
|
||
}
|
||
} else {
|
||
console.error('加载群组列表失败:', result.error);
|
||
}
|
||
} catch (error) {
|
||
console.error('加载群组列表时出错:', error);
|
||
}
|
||
}
|
||
|
||
// 更新群组下拉框
|
||
function updateGroupSelect(selectElement) {
|
||
if (!selectElement) return;
|
||
|
||
// 保存当前选中的值
|
||
const currentValue = selectElement.value;
|
||
|
||
// 清空下拉框,保留第一个选项
|
||
selectElement.innerHTML = '<option value="">' + (selectElement.id === 'groupFilter' ? '全部群组' : '选择群组(可选)') + '</option>';
|
||
|
||
// 添加群组选项
|
||
groupList.forEach(group => {
|
||
const option = document.createElement('option');
|
||
option.value = group.id;
|
||
option.textContent = group.name ? `${group.name} (${group.id})` : group.id;
|
||
selectElement.appendChild(option);
|
||
});
|
||
|
||
// 恢复之前选中的值
|
||
if (currentValue) {
|
||
selectElement.value = currentValue;
|
||
}
|
||
}
|
||
|
||
// 页面加载时获取QA列表
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
loadQAList();
|
||
loadGroupList();
|
||
updateSearchHistory();
|
||
|
||
// 添加点击外部关闭搜索建议的事件
|
||
document.addEventListener('click', function(e) {
|
||
const suggestionsContainer = document.getElementById('searchSuggestions');
|
||
const searchInput = document.getElementById('searchInput');
|
||
|
||
if (suggestionsContainer && searchInput &&
|
||
!suggestionsContainer.contains(e.target) &&
|
||
e.target !== searchInput) {
|
||
suggestionsContainer.style.display = 'none';
|
||
}
|
||
});
|
||
|
||
// 添加回车键搜索功能
|
||
const searchInput = document.getElementById('searchInput');
|
||
if (searchInput) {
|
||
searchInput.addEventListener('keypress', function(e) {
|
||
if (e.key === 'Enter') {
|
||
advancedSearch();
|
||
}
|
||
});
|
||
}
|
||
});
|
||
|
||
// 加载QA列表
|
||
async function loadQAList() {
|
||
try {
|
||
// 获取搜索参数
|
||
const searchInput = document.getElementById('searchInput');
|
||
const groupFilter = document.getElementById('groupFilter');
|
||
const startDateFilter = document.getElementById('startDateFilter');
|
||
const endDateFilter = document.getElementById('endDateFilter');
|
||
|
||
const keyword = searchInput ? searchInput.value.trim() : '';
|
||
const groupId = groupFilter ? groupFilter.value.trim() : '';
|
||
const startTime = startDateFilter ? startDateFilter.value : '';
|
||
const endTime = endDateFilter ? endDateFilter.value : '';
|
||
|
||
// 构建查询URL
|
||
let url = 'api/qa';
|
||
const params = new URLSearchParams();
|
||
|
||
if (keyword) params.append('keyword', keyword);
|
||
if (groupId) params.append('group_id', groupId);
|
||
if (startTime) params.append('start_time', startTime);
|
||
if (endTime) params.append('end_time', endTime);
|
||
|
||
if (params.toString()) {
|
||
url += '?' + params.toString();
|
||
}
|
||
|
||
const response = await fetch(url);
|
||
const result = await response.json();
|
||
if (result.success) {
|
||
// 根据后端API返回的数据结构,data字段包含qa_list
|
||
const qaList = result.qa_list || [];
|
||
displayQAList(qaList);
|
||
|
||
// 更新统计信息
|
||
updateStats(result.total || qaList.length);
|
||
} else {
|
||
alert('加载QA列表失败: ' + result.error);
|
||
}
|
||
} catch (error) {
|
||
// 修复错误:确保error.message存在,避免"Cannot read properties of undefined"错误
|
||
const errorMessage = error?.message || error?.toString() || '未知错误';
|
||
alert('加载QA列表时出错: ' + errorMessage);
|
||
}
|
||
}
|
||
|
||
// 更新统计信息
|
||
function updateStats(total) {
|
||
const statsElement = document.getElementById('statsInfo');
|
||
if (statsElement) {
|
||
statsElement.textContent = `共找到 ${total} 条QA记录`;
|
||
}
|
||
}
|
||
|
||
// 显示QA列表
|
||
async function displayQAList(qaList) {
|
||
const qaListContainer = document.getElementById('qaList');
|
||
|
||
if (qaList.length === 0) {
|
||
qaListContainer.innerHTML = `
|
||
<div class="empty-state">
|
||
<div>📝</div>
|
||
<h3>暂无QA记录</h3>
|
||
<p>点击"创建新QA"按钮开始创建第一个问答记录</p>
|
||
</div>
|
||
`;
|
||
// 隐藏批量操作按钮
|
||
const batchActions = document.getElementById('batchActions');
|
||
if (batchActions) batchActions.style.display = 'none';
|
||
return;
|
||
}
|
||
|
||
// 显示批量操作按钮
|
||
const batchActions = document.getElementById('batchActions');
|
||
if (batchActions) batchActions.style.display = 'block';
|
||
|
||
// 获取所有消息的详细内容
|
||
let allMessageIds = [];
|
||
qaList.forEach(qa => {
|
||
// 解析JSON格式的question和answer字段
|
||
let questionIds = [];
|
||
let answerIds = [];
|
||
|
||
try {
|
||
questionIds = typeof qa.question === 'string' ? JSON.parse(qa.question) : qa.question || [];
|
||
answerIds = typeof qa.answer === 'string' ? JSON.parse(qa.answer) : qa.answer || [];
|
||
} catch (e) {
|
||
console.error('解析QA数据时出错:', e);
|
||
questionIds = [];
|
||
answerIds = [];
|
||
}
|
||
|
||
allMessageIds = [...allMessageIds, ...questionIds, ...answerIds];
|
||
});
|
||
|
||
let messageMap = {};
|
||
if (allMessageIds.length > 0) {
|
||
try {
|
||
const response = await fetch('api/messages_by_ids', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({message_ids: allMessageIds})
|
||
});
|
||
const result = await response.json();
|
||
if (result.success) {
|
||
result.messages.forEach(msg => {
|
||
messageMap[msg.id] = msg;
|
||
});
|
||
}
|
||
} catch (error) {
|
||
console.error('获取消息详情时出错:', error);
|
||
}
|
||
}
|
||
|
||
qaListContainer.innerHTML = qaList.map(qa => {
|
||
// 解析JSON格式的question和answer字段
|
||
let questionIds = [];
|
||
let answerIds = [];
|
||
|
||
try {
|
||
questionIds = typeof qa.question === 'string' ? JSON.parse(qa.question) : qa.question || [];
|
||
answerIds = typeof qa.answer === 'string' ? JSON.parse(qa.answer) : qa.answer || [];
|
||
} catch (e) {
|
||
console.error('解析QA数据时出错:', e);
|
||
questionIds = [];
|
||
answerIds = [];
|
||
}
|
||
|
||
// 获取第一个问题的内容作为显示
|
||
let questionContent = '暂无问题内容';
|
||
if (questionIds.length > 0) {
|
||
const firstQuestionId = questionIds[0];
|
||
const message = messageMap[firstQuestionId];
|
||
if (message && message.content) {
|
||
questionContent = message.content;
|
||
} else {
|
||
questionContent = `问题消息ID: ${firstQuestionId}`;
|
||
}
|
||
}
|
||
|
||
// 截取问题内容,超过30个字用省略号
|
||
if (questionContent.length > 30) {
|
||
questionContent = questionContent.substring(0, 30) + '...';
|
||
}
|
||
|
||
const isSelected = selectedQAs.has(qa.id);
|
||
|
||
return `
|
||
<div class="qa-item ${isSelected ? 'selected' : ''}" data-qa-id="${qa.id}">
|
||
<div class="qa-header">
|
||
<div class="qa-checkbox">
|
||
<input type="checkbox" class="qa-select" ${isSelected ? 'checked' : ''} onchange="toggleQASelection(${qa.id}, this.checked)">
|
||
</div>
|
||
<div class="qa-id">QA #${qa.id}</div>
|
||
<div class="qa-group">${qa.groupId || '无群组'}</div>
|
||
</div>
|
||
|
||
<div class="qa-content">
|
||
<div class="qa-question">
|
||
<strong>问题:</strong> ${questionContent}
|
||
</div>
|
||
<div class="qa-stats">
|
||
<span class="stat">问题: ${questionIds.length}条</span>
|
||
<span class="stat">答案: ${answerIds.length}条</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="qa-actions">
|
||
<button class="btn btn-primary" onclick="previewQA(${qa.id})">预览</button>
|
||
<button class="btn" onclick="editQA(${qa.id})">编辑</button>
|
||
<button class="btn btn-danger" onclick="deleteQA(${qa.id})">删除</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}).join('');
|
||
|
||
// 更新批量操作按钮状态
|
||
updateBatchActions();
|
||
}
|
||
|
||
// 搜索消息
|
||
async function searchMessages() {
|
||
const keyword = document.getElementById('searchInput').value.trim();
|
||
if (!keyword) {
|
||
alert('请输入搜索关键词');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 显示搜索中状态
|
||
const qaListContainer = document.getElementById('qaList');
|
||
qaListContainer.innerHTML = '<div class="loading">搜索中...</div>';
|
||
|
||
const response = await fetch(`api/search_messages?keyword=${encodeURIComponent(keyword)}`);
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
displaySearchResults(result.messages);
|
||
// 保存搜索历史
|
||
saveSearchHistory(keyword);
|
||
} else {
|
||
alert('搜索失败: ' + result.error);
|
||
}
|
||
} catch (error) {
|
||
// 修复错误:确保error.message存在,避免"Cannot read properties of undefined"错误
|
||
const errorMessage = error?.message || error?.toString() || '未知错误';
|
||
alert('搜索时出错: ' + errorMessage);
|
||
}
|
||
}
|
||
|
||
// 高级搜索
|
||
async function advancedSearch() {
|
||
const keyword = document.getElementById('searchInput').value.trim();
|
||
const groupFilter = document.getElementById('groupFilter').value.trim();
|
||
const startDate = document.getElementById('startDateFilter').value;
|
||
const endDate = document.getElementById('endDateFilter').value;
|
||
|
||
if (!keyword && !groupFilter && !startDate && !endDate) {
|
||
alert('请输入至少一个搜索条件');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 显示搜索中状态
|
||
const qaListContainer = document.getElementById('qaList');
|
||
qaListContainer.innerHTML = '<div class="loading">搜索中...</div>';
|
||
|
||
// 构建查询参数
|
||
const params = new URLSearchParams();
|
||
if (keyword) params.append('keyword', keyword);
|
||
if (groupFilter) params.append('group_id', groupFilter);
|
||
if (startDate) params.append('start_time', startDate);
|
||
if (endDate) params.append('end_time', endDate);
|
||
|
||
const response = await fetch(`api/qa?${params.toString()}`);
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
displayQAList(result.qa_list || []);
|
||
// 保存搜索历史
|
||
if (keyword) saveSearchHistory(keyword);
|
||
} else {
|
||
alert('搜索失败: ' + result.error);
|
||
}
|
||
} catch (error) {
|
||
const errorMessage = error?.message || error?.toString() || '未知错误';
|
||
alert('搜索时出错: ' + errorMessage);
|
||
}
|
||
}
|
||
|
||
// 保存搜索历史
|
||
function saveSearchHistory(keyword) {
|
||
let searchHistory = JSON.parse(localStorage.getItem('qa_search_history') || '[]');
|
||
|
||
// 移除重复项
|
||
searchHistory = searchHistory.filter(item => item !== keyword);
|
||
|
||
// 添加到开头
|
||
searchHistory.unshift(keyword);
|
||
|
||
// 限制历史记录数量
|
||
if (searchHistory.length > 10) {
|
||
searchHistory = searchHistory.slice(0, 10);
|
||
}
|
||
|
||
localStorage.setItem('qa_search_history', JSON.stringify(searchHistory));
|
||
|
||
// 更新搜索历史显示
|
||
updateSearchHistory();
|
||
}
|
||
|
||
// 更新搜索历史显示
|
||
function updateSearchHistory() {
|
||
const searchHistory = JSON.parse(localStorage.getItem('qa_search_history') || '[]');
|
||
const historyContainer = document.getElementById('searchHistory');
|
||
|
||
if (!historyContainer) return;
|
||
|
||
if (searchHistory.length === 0) {
|
||
historyContainer.style.display = 'none';
|
||
return;
|
||
}
|
||
|
||
historyContainer.style.display = 'block';
|
||
historyContainer.innerHTML = `
|
||
<div class="search-history-header">
|
||
<span>搜索历史</span>
|
||
<button class="btn-clear-history" onclick="clearSearchHistory()">清空</button>
|
||
</div>
|
||
<div class="search-history-items">
|
||
${searchHistory.map(item => `
|
||
<div class="search-history-item" onclick="fillSearchInput('${item.replace(/'/g, "\\'")}')">
|
||
${item}
|
||
</div>
|
||
`).join('')}
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
// 填充搜索输入框
|
||
function fillSearchInput(keyword) {
|
||
document.getElementById('searchInput').value = keyword;
|
||
}
|
||
|
||
// 清空搜索历史
|
||
function clearSearchHistory() {
|
||
if (confirm('确定要清空搜索历史吗?')) {
|
||
localStorage.removeItem('qa_search_history');
|
||
updateSearchHistory();
|
||
}
|
||
}
|
||
|
||
// 智能搜索建议
|
||
async function showSearchSuggestions() {
|
||
const keyword = document.getElementById('searchInput').value.trim();
|
||
const suggestionsContainer = document.getElementById('searchSuggestions');
|
||
|
||
if (!keyword) {
|
||
suggestionsContainer.style.display = 'none';
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 调用后端API获取搜索建议
|
||
const response = await fetch(`api/search_suggestions?keyword=${encodeURIComponent(keyword)}`);
|
||
const result = await response.json();
|
||
|
||
if (result.success && result.suggestions.length > 0) {
|
||
suggestionsContainer.innerHTML = result.suggestions.map(item => `
|
||
<div class="search-suggestion-item" onclick="selectSearchSuggestion('${item.replace(/'/g, "\\'")}')">
|
||
${item}
|
||
</div>
|
||
`).join('');
|
||
suggestionsContainer.style.display = 'block';
|
||
} else {
|
||
suggestionsContainer.style.display = 'none';
|
||
}
|
||
} catch (error) {
|
||
suggestionsContainer.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
// 选择搜索建议
|
||
function selectSearchSuggestion(suggestion) {
|
||
document.getElementById('searchInput').value = suggestion;
|
||
document.getElementById('searchSuggestions').style.display = 'none';
|
||
searchMessages();
|
||
}
|
||
|
||
// 显示搜索结果
|
||
function displaySearchResults(messages) {
|
||
const qaListContainer = document.getElementById('qaList');
|
||
const keyword = document.getElementById('searchInput').value.trim();
|
||
|
||
if (messages.length === 0) {
|
||
qaListContainer.innerHTML = `
|
||
<div class="empty-state">
|
||
<div>🔍</div>
|
||
<h3>未找到相关消息</h3>
|
||
<p>请尝试其他搜索关键词或调整搜索条件</p>
|
||
</div>
|
||
`;
|
||
return;
|
||
}
|
||
|
||
qaListContainer.innerHTML = `
|
||
<div class="search-results-header">
|
||
<h3>搜索结果 (${messages.length} 条)</h3>
|
||
<div class="search-summary">
|
||
搜索关键词: "${keyword}"
|
||
</div>
|
||
</div>
|
||
${messages.map(msg => `
|
||
<div class="qa-item search-result-item">
|
||
<div class="qa-header">
|
||
<div class="qa-id">消息ID: ${msg.id}</div>
|
||
<div class="qa-group">${msg.formatted_time || '未知时间'}</div>
|
||
</div>
|
||
|
||
<div class="qa-content">
|
||
<div class="message-item">
|
||
<strong>类型:</strong> ${msg.type} |
|
||
<strong>发送者:</strong> ${msg.sender_name || '未知'} |
|
||
<strong>接收者:</strong> ${msg.receiver || '未知'}
|
||
</div>
|
||
<div class="message-item" style="margin-top: 10px;">
|
||
<strong>内容:</strong>
|
||
<div class="message-content">
|
||
${msg.highlighted_content ? msg.highlighted_content : (msg.display_content || '无内容')}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="qa-actions">
|
||
<button class="btn btn-primary" onclick="addToQuestion('${msg.id}')">设为问题</button>
|
||
<button class="btn btn-success" onclick="addToAnswer('${msg.id}')">设为答案</button>
|
||
<button class="btn btn-info" onclick="viewMessageDetail('${msg.id}')">查看详情</button>
|
||
</div>
|
||
</div>
|
||
`).join('')}
|
||
`;
|
||
}
|
||
|
||
// 查看消息详情
|
||
function viewMessageDetail(messageId) {
|
||
// 这里可以打开一个模态框显示消息的完整详情
|
||
alert(`查看消息详情: ${messageId}\n此功能可以进一步扩展显示完整消息内容`);
|
||
}
|
||
|
||
// 打开创建模态框
|
||
function openCreateModal() {
|
||
currentQA = null;
|
||
selectedQuestions = [];
|
||
selectedAnswers = [];
|
||
|
||
document.getElementById('modalTitle').textContent = '创建QA记录';
|
||
document.getElementById('groupId').value = '';
|
||
updateSelectedMessages();
|
||
|
||
document.getElementById('qaModal').style.display = 'block';
|
||
}
|
||
|
||
// 编辑QA
|
||
async function editQA(qaId) {
|
||
try {
|
||
const response = await fetch(`api/qa/${qaId}`);
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
currentQA = result.qa;
|
||
selectedQuestions = [...currentQA.question_list];
|
||
selectedAnswers = [...currentQA.answer_list];
|
||
|
||
document.getElementById('modalTitle').textContent = '编辑QA记录';
|
||
document.getElementById('groupId').value = currentQA.groupId || '';
|
||
updateSelectedMessages();
|
||
|
||
document.getElementById('qaModal').style.display = 'block';
|
||
} else {
|
||
alert('加载QA详情失败: ' + result.error);
|
||
}
|
||
} catch (error) {
|
||
// 修复错误:确保error.message存在,避免"Cannot read properties of undefined"错误
|
||
const errorMessage = error?.message || error?.toString() || '未知错误';
|
||
alert('加载QA详情时出错: ' + errorMessage);
|
||
}
|
||
}
|
||
|
||
// 清空过滤器
|
||
function clearFilters() {
|
||
document.getElementById('searchInput').value = '';
|
||
document.getElementById('groupFilter').value = '';
|
||
document.getElementById('startDateFilter').value = '';
|
||
document.getElementById('endDateFilter').value = '';
|
||
loadQAList();
|
||
}
|
||
|
||
// 切换QA选择状态
|
||
function toggleQASelection(qaId, isSelected) {
|
||
if (isSelected) {
|
||
selectedQAs.add(qaId);
|
||
} else {
|
||
selectedQAs.delete(qaId);
|
||
}
|
||
|
||
// 更新UI
|
||
const qaItem = document.querySelector(`[data-qa-id="${qaId}"]`);
|
||
if (qaItem) {
|
||
if (isSelected) {
|
||
qaItem.classList.add('selected');
|
||
} else {
|
||
qaItem.classList.remove('selected');
|
||
}
|
||
}
|
||
|
||
updateBatchActions();
|
||
}
|
||
|
||
// 全选/取消全选
|
||
function toggleSelectAll() {
|
||
const selectAllCheckbox = document.getElementById('selectAll');
|
||
const qaItems = document.querySelectorAll('.qa-item');
|
||
|
||
if (selectAllCheckbox.checked) {
|
||
// 全选
|
||
qaItems.forEach(item => {
|
||
const qaId = parseInt(item.getAttribute('data-qa-id'));
|
||
selectedQAs.add(qaId);
|
||
item.classList.add('selected');
|
||
const checkbox = item.querySelector('.qa-select');
|
||
if (checkbox) checkbox.checked = true;
|
||
});
|
||
} else {
|
||
// 取消全选
|
||
selectedQAs.clear();
|
||
qaItems.forEach(item => {
|
||
item.classList.remove('selected');
|
||
const checkbox = item.querySelector('.qa-select');
|
||
if (checkbox) checkbox.checked = false;
|
||
});
|
||
}
|
||
|
||
updateBatchActions();
|
||
}
|
||
|
||
// 更新批量操作按钮状态
|
||
function updateBatchActions() {
|
||
const selectedCount = selectedQAs.size;
|
||
const batchInfo = document.getElementById('batchInfo');
|
||
const batchDeleteBtn = document.getElementById('batchDeleteBtn');
|
||
|
||
if (batchInfo) {
|
||
batchInfo.textContent = `已选择 ${selectedCount} 条记录`;
|
||
}
|
||
|
||
if (batchDeleteBtn) {
|
||
batchDeleteBtn.disabled = selectedCount === 0;
|
||
}
|
||
}
|
||
|
||
// 批量删除QA记录
|
||
async function batchDeleteQA() {
|
||
const selectedCount = selectedQAs.size;
|
||
if (selectedCount === 0) {
|
||
alert('请先选择要删除的QA记录');
|
||
return;
|
||
}
|
||
|
||
if (!confirm(`确定要删除选中的 ${selectedCount} 条QA记录吗?此操作不可恢复。`)) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch('api/qa/batch', {
|
||
method: 'DELETE',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({qa_ids: Array.from(selectedQAs)})
|
||
});
|
||
|
||
if (response.status === 404) {
|
||
// 404状态码表示删除成功
|
||
alert('批量删除成功');
|
||
selectedQAs.clear();
|
||
loadQAList();
|
||
} else if (response.status === 500) {
|
||
// 500状态码表示删除失败
|
||
const result = await response.json();
|
||
alert('批量删除失败: ' + (result.error || '服务器内部错误'));
|
||
} else {
|
||
// 其他状态码处理
|
||
const result = await response.json();
|
||
if (result.success) {
|
||
alert(result.data.message);
|
||
selectedQAs.clear();
|
||
loadQAList();
|
||
} else {
|
||
alert('批量删除失败: ' + result.error);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
// 修复错误:确保error.message存在,避免"Cannot read properties of undefined"错误
|
||
const errorMessage = error?.message || error?.toString() || '未知错误';
|
||
alert('批量删除时出错: ' + errorMessage);
|
||
}
|
||
}
|
||
|
||
// 搜索问题消息
|
||
async function searchQuestionMessages() {
|
||
const keyword = document.getElementById('questionSearch').value.trim();
|
||
if (!keyword) {
|
||
document.getElementById('questionResults').innerHTML = '';
|
||
return;
|
||
}
|
||
const groupIdSelect = document.getElementById('groupId');
|
||
const groupId = groupIdSelect.value;
|
||
try {
|
||
const url = `api/search_messages?keyword=${encodeURIComponent(keyword)}&limit=20${groupId ? `&group_id=${groupId}` : ''}`;
|
||
const response = await fetch(url);
|
||
const result = await response.json();
|
||
if (result.success) {
|
||
displayQuestionResults(result.messages);
|
||
}
|
||
} catch (error) {
|
||
console.error('搜索问题消息时出错:', error);
|
||
}
|
||
}
|
||
|
||
// 添加回车键搜索功能
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const searchInput = document.getElementById('searchInput');
|
||
if (searchInput) {
|
||
searchInput.addEventListener('keypress', function(e) {
|
||
if (e.key === 'Enter') {
|
||
loadQAList();
|
||
}
|
||
});
|
||
}
|
||
|
||
const groupFilter = document.getElementById('groupFilter');
|
||
if (groupFilter) {
|
||
groupFilter.addEventListener('keypress', function(e) {
|
||
if (e.key === 'Enter') {
|
||
loadQAList();
|
||
}
|
||
});
|
||
}
|
||
});
|
||
|
||
// 显示问题搜索结果
|
||
function displayQuestionResults(messages) {
|
||
const resultsContainer = document.getElementById('questionResults');
|
||
console.log(messages);
|
||
resultsContainer.innerHTML = messages.map(msg => `
|
||
<div class="search-result-item" onclick="selectQuestion('${msg.id}', '${msg.content.replace(/'/g, "\\'").substring(0, 100)}')">
|
||
<div><strong>ID:</strong> ${msg.id}</div>
|
||
<div><strong>内容:</strong> ${msg.content ? msg.content.substring(0, 100) + (msg.content.length > 100 ? '...' : '') : '无内容'}</div>
|
||
<div><strong>时间:</strong> ${msg.formatted_time || '未知'}</div>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
// 搜索答案消息
|
||
async function searchAnswerMessages() {
|
||
const keyword = document.getElementById('answerSearch').value.trim();
|
||
if (!keyword) {
|
||
document.getElementById('answerResults').innerHTML = '';
|
||
return;
|
||
}
|
||
const groupIdSelect = document.getElementById('groupId');
|
||
const groupId = groupIdSelect.value;
|
||
|
||
try {
|
||
const url = `api/search_messages?keyword=${encodeURIComponent(keyword)}&limit=20${groupId ? `&group_id=${groupId}` : ''}`;
|
||
const response = await fetch(url);
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
displayAnswerResults(result.messages);
|
||
}
|
||
} catch (error) {
|
||
console.error('搜索答案消息时出错:', error);
|
||
}
|
||
}
|
||
|
||
// 显示答案搜索结果
|
||
function displayAnswerResults(messages) {
|
||
const resultsContainer = document.getElementById('answerResults');
|
||
|
||
resultsContainer.innerHTML = messages.map(msg => `
|
||
<div class="search-result-item" onclick="selectAnswer('${msg.id}', '${msg.content.replace(/'/g, "\\'").substring(0, 100)}')">
|
||
<div><strong>ID:</strong> ${msg.id}</div>
|
||
<div><strong>内容:</strong> ${msg.content ? msg.content.substring(0, 100) + (msg.content.length > 100 ? '...' : '') : '无内容'}</div>
|
||
<div><strong>时间:</strong> ${msg.formatted_time || '未知'}</div>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
// 选择问题消息
|
||
function selectQuestion(messageId, content) {
|
||
if (!selectedQuestions.includes(messageId)) {
|
||
selectedQuestions.push(messageId);
|
||
updateSelectedMessages();
|
||
}
|
||
}
|
||
|
||
// 选择答案消息
|
||
function selectAnswer(messageId, content) {
|
||
if (!selectedAnswers.includes(messageId)) {
|
||
selectedAnswers.push(messageId);
|
||
updateSelectedMessages();
|
||
}
|
||
}
|
||
|
||
// 从搜索结果添加为问题
|
||
function addToQuestion(messageId) {
|
||
if (!selectedQuestions.includes(messageId)) {
|
||
selectedQuestions.push(messageId);
|
||
alert('已添加到问题列表');
|
||
}
|
||
}
|
||
|
||
// 从搜索结果添加为答案
|
||
function addToAnswer(messageId) {
|
||
if (!selectedAnswers.includes(messageId)) {
|
||
selectedAnswers.push(messageId);
|
||
alert('已添加到答案列表');
|
||
}
|
||
}
|
||
|
||
// 更新已选择的消息显示
|
||
function updateSelectedMessages() {
|
||
document.getElementById('selectedQuestions').innerHTML = selectedQuestions.map(id => `
|
||
<div class="selected-message">
|
||
<span>消息ID: ${id}</span>
|
||
<button class="btn btn-danger" onclick="removeQuestion('${id}')" style="padding: 2px 8px; font-size: 12px;">移除</button>
|
||
</div>
|
||
`).join('');
|
||
|
||
document.getElementById('selectedAnswers').innerHTML = selectedAnswers.map(id => `
|
||
<div class="selected-message">
|
||
<span>消息ID: ${id}</span>
|
||
<button class="btn btn-danger" onclick="removeAnswer('${id}')" style="padding: 2px 8px; font-size: 12px;">移除</button>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
// 移除问题消息
|
||
function removeQuestion(messageId) {
|
||
selectedQuestions = selectedQuestions.filter(id => id !== messageId);
|
||
updateSelectedMessages();
|
||
}
|
||
|
||
// 移除答案消息
|
||
function removeAnswer(messageId) {
|
||
selectedAnswers = selectedAnswers.filter(id => id !== messageId);
|
||
updateSelectedMessages();
|
||
}
|
||
|
||
// 保存QA记录
|
||
async function saveQA() {
|
||
const groupIdSelect = document.getElementById('groupId');
|
||
const groupId = groupIdSelect.value;
|
||
|
||
if (selectedQuestions.length === 0) {
|
||
alert('请至少选择一个问题消息');
|
||
return;
|
||
}
|
||
|
||
if (selectedAnswers.length === 0) {
|
||
alert('请至少选择一个答案消息');
|
||
return;
|
||
}
|
||
|
||
const qaData = {
|
||
groupId: groupId,
|
||
question: selectedQuestions,
|
||
answer: selectedAnswers
|
||
};
|
||
|
||
try {
|
||
const url = currentQA ? `api/qa/${currentQA.id}` : '/api/qa';
|
||
const method = currentQA ? 'PUT' : 'POST';
|
||
|
||
const response = await fetch(url, {
|
||
method: method,
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(qaData)
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
alert(currentQA ? 'QA记录更新成功' : 'QA记录创建成功');
|
||
closeModal();
|
||
loadQAList();
|
||
} else {
|
||
alert('保存失败: ' + result.error);
|
||
}
|
||
} catch (error) {
|
||
// 修复错误:确保error.message存在,避免"Cannot read properties of undefined"错误
|
||
const errorMessage = error?.message || error?.toString() || '未知错误';
|
||
alert('保存时出错: ' + errorMessage);
|
||
}
|
||
}
|
||
|
||
// 预览QA
|
||
async function previewQA(qaId) {
|
||
try {
|
||
const response = await fetch(`api/qa/${qaId}`);
|
||
const result = await response.json();
|
||
if (result.success) {
|
||
displayPreview(result.qa);
|
||
} else {
|
||
alert('加载QA详情失败: ' + result.error);
|
||
}
|
||
} catch (error) {
|
||
// 修复错误:确保error.message存在,避免"Cannot read properties of undefined"错误
|
||
const errorMessage = error?.message || error?.toString() || '未知错误';
|
||
alert('加载QA详情时出错: ' + errorMessage);
|
||
}
|
||
}
|
||
|
||
// 显示预览
|
||
async function displayPreview(qa) {
|
||
const previewContent = document.getElementById('previewContent');
|
||
// 获取所有消息的详细内容
|
||
let allMessages = [];
|
||
const allMessageIds = [...qa.question_list, ...qa.answer_list];
|
||
|
||
if (allMessageIds.length > 0) {
|
||
try {
|
||
const response = await fetch('api/messages_by_ids', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({message_ids: allMessageIds})
|
||
});
|
||
const result = await response.json();
|
||
if (result.success) {
|
||
allMessages = result.messages;
|
||
}
|
||
} catch (error) {
|
||
console.error('获取消息详情时出错:', error);
|
||
}
|
||
}
|
||
|
||
// 创建消息映射
|
||
const messageMap = {};
|
||
allMessages.forEach(msg => {
|
||
messageMap[msg.id] = msg;
|
||
});
|
||
|
||
// 按时间排序所有消息
|
||
const sortedMessages = allMessages.sort((a, b) => {
|
||
return new Date(a.time) - new Date(b.time);
|
||
});
|
||
|
||
previewContent.innerHTML = `
|
||
<div class="chat-preview">
|
||
<div class="chat-header">
|
||
<h3>QA #${qa.id} - 聊天记录预览</h3>
|
||
<div class="chat-info">群组: ${qa.groupId || '无群组'} | 消息数量: ${sortedMessages.length}</div>
|
||
</div>
|
||
|
||
<div class="chat-container">
|
||
${sortedMessages.map(msg => {
|
||
|
||
const isQuestion = qa.question_list.includes(msg.id);
|
||
const isAnswer = qa.answer_list.includes(msg.id);
|
||
const messageType = isQuestion ? 'question' : (isAnswer ? 'answer' : 'other');
|
||
|
||
return `
|
||
<div class="chat-message ${messageType}">
|
||
<div class="message-avatar">
|
||
${messageType === 'question' ? '❓' : (messageType === 'answer' ? '💬' : '📝')}
|
||
</div>
|
||
<div class="message-content">
|
||
<div class="message-header">
|
||
<span class="sender">${msg.sender_name || msg.sender}</span>
|
||
<span class="time">${msg.formatted_time || '未知时间'}</span>
|
||
<span class="message-type-badge ${messageType}">${messageType === 'question' ? '问题' : (messageType === 'answer' ? '答案' : '其他')}</span>
|
||
</div>
|
||
<div class="message-text">${msg.display_content || '无内容'}</div>
|
||
<div class="message-meta">
|
||
<span class="message-id">消息ID: ${msg.id}</span>
|
||
<span class="message-type">类型: ${msg.type || '未知'}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}).join('')}
|
||
</div>
|
||
|
||
<div class="chat-summary">
|
||
<div class="summary-item">
|
||
<span class="summary-label">问题数量:</span>
|
||
<span class="summary-value">${qa.question_list.length}</span>
|
||
</div>
|
||
<div class="summary-item">
|
||
<span class="summary-label">答案数量:</span>
|
||
<span class="summary-value">${qa.answer_list.length}</span>
|
||
</div>
|
||
<div class="summary-item">
|
||
<span class="summary-label">总消息数:</span>
|
||
<span class="summary-value">${sortedMessages.length}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
document.getElementById('previewModal').style.display = 'block';
|
||
}
|
||
|
||
// 删除QA
|
||
async function deleteQA(qaId) {
|
||
if (!confirm('确定要删除这个QA记录吗?此操作不可恢复。')) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch(`api/qa/${qaId}`, {
|
||
method: 'DELETE'
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
alert('QA记录删除成功');
|
||
loadQAList();
|
||
} else {
|
||
alert('删除失败: ' + result.error);
|
||
}
|
||
} catch (error) {
|
||
// 修复错误:确保error.message存在,避免"Cannot read properties of undefined"错误
|
||
const errorMessage = error?.message || error?.toString() || '未知错误';
|
||
alert('删除时出错: ' + errorMessage);
|
||
}
|
||
}
|
||
|
||
// 关闭模态框
|
||
function closeModal() {
|
||
document.getElementById('qaModal').style.display = 'none';
|
||
}
|
||
|
||
// 关闭预览模态框
|
||
function closePreviewModal() {
|
||
document.getElementById('previewModal').style.display = 'none';
|
||
}
|
||
|
||
// 点击模态框外部关闭
|
||
window.onclick = function (event) {
|
||
const modal = document.getElementById('qaModal');
|
||
const previewModal = document.getElementById('previewModal');
|
||
|
||
if (event.target === modal) {
|
||
modal.style.display = 'none';
|
||
}
|
||
if (event.target === previewModal) {
|
||
previewModal.style.display = 'none';
|
||
}
|
||
} |