.*?<\/div>/gs;
let tableParts = [];
let nonTableParts = [];
let lastIndex = 0;
let match;
// 分离表格和非表格部分
while ((match = tablePattern.exec(html)) !== null) {
nonTableParts.push(html.substring(lastIndex, match.index));
tableParts.push(match[0]);
lastIndex = match.index + match[0].length;
}
nonTableParts.push(html.substring(lastIndex));
// 只处理非表格部分
for (let i = 0; i < nonTableParts.length; i++) {
let part = nonTableParts[i];
// 应用所有特殊标记替换
for (const marker of specialMarkers) {
part = part.replace(marker.pattern, marker.replacement);
}
nonTableParts[i] = part;
}
// 重新组合文档
let result = '';
for (let i = 0; i < nonTableParts.length; i++) {
result += nonTableParts[i];
if (i < tableParts.length) {
result += tableParts[i];
}
}
return result;
}
/**
* 生成分享部分HTML
* @param {string} analysisId - 分析ID
* @returns {string} HTML代码
*/
function generateShareSectionHTML(analysisId) {
return `
`;
}
/**
* 显示分享选项
* @param {string} analysisId - 分析ID
*/
function showShareOptions(analysisId) {
if (!analysisId) {
console.error('分析ID为空,无法继续分享操作');
alert('无法生成分享链接,缺少必要的分析ID');
return;
}
console.log('准备显示分享模态窗口,分析ID:', analysisId);
// 创建模态框
const modalHtml = `
`;
// 删除可能已存在的模态框
const existingModal = document.getElementById('shareModal');
if (existingModal) {
console.log('删除已存在的分享模态窗口');
existingModal.remove();
}
// 添加模态框到页面
document.body.insertAdjacentHTML('beforeend', modalHtml);
console.log('分享模态窗口HTML已添加到页面');
// 获取模态框元素
const modal = document.getElementById('shareModal');
if (!modal) {
console.error('无法获取分享模态窗口元素');
alert('无法显示分享窗口,请刷新页面后重试');
return;
}
// 确保Bootstrap已加载
if (typeof bootstrap === 'undefined') {
console.error('Bootstrap未加载,无法初始化模态窗口');
// 尝试动态加载Bootstrap
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js';
script.onload = function() {
console.log('Bootstrap已动态加载');
initializeShareModal(modal, analysisId);
};
script.onerror = function() {
console.error('无法加载Bootstrap');
alert('无法加载必要的组件,请检查网络连接后重试');
};
document.head.appendChild(script);
return;
}
// 初始化模态窗口
initializeShareModal(modal, analysisId);
}
/**
* 初始化分享模态窗口
* @param {HTMLElement} modal - 模态窗口元素
* @param {string} analysisId - 分析ID
*/
function initializeShareModal(modal, analysisId) {
try {
console.log('初始化Bootstrap模态窗口');
// 确保QRCode库已加载
if (typeof QRCode === 'undefined') {
console.log('QRCode库未加载,正在加载...');
const qrScript = document.createElement('script');
qrScript.src = 'https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js';
document.head.appendChild(qrScript);
}
const modalInstance = new bootstrap.Modal(modal);
modalInstance.show();
console.log('分享模态窗口已显示');
// 绑定事件
initShareModalEvents(analysisId);
} catch (error) {
console.error('模态窗口初始化失败:', error);
alert('显示分享窗口失败,请刷新页面后重试');
}
}
/**
* 初始化分享模态框事件
* @param {string} analysisId - 分析ID
*/
function initShareModalEvents(analysisId) {
console.log('初始化分享模态窗口事件,分析ID:', analysisId);
// 隐私选项卡事件
document.querySelectorAll('.privacy-card').forEach(card => {
card.addEventListener('click', function() {
// 移除其他卡片的active类
document.querySelectorAll('.privacy-card').forEach(c => c.classList.remove('active'));
// 添加当前卡片的active类
this.classList.add('active');
// 选中当前卡片的单选按钮
const radio = this.querySelector('.privacy-card-radio');
if (radio) radio.checked = true;
});
});
// 生成链接按钮事件
const generateBtn = document.getElementById('generateLinkBtn');
if (generateBtn) {
generateBtn.addEventListener('click', function() {
// 获取选中的分享模式
const selectedOption = document.querySelector('.privacy-card-radio:checked');
if (!selectedOption) {
alert('请选择分享方式');
return;
}
const privacy = selectedOption.value;
// 显示加载状态
this.innerHTML = '
生成中...';
this.disabled = true;
// 格式化分析ID
let formattedId = analysisId;
if (analysisId.length === 32 && !analysisId.includes('-')) {
formattedId = `${analysisId.substring(0, 8)}-${analysisId.substring(8, 12)}-${analysisId.substring(12, 16)}-${analysisId.substring(16, 20)}-${analysisId.substring(20, 32)}`;
}
console.log('调用API生成分享链接,分析ID:', formattedId, '分享模式:', privacy);
console.log('请求URL:', '/api/share/' + formattedId);
// 调用后端API生成分享链接
fetch('/api/share/' + formattedId, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({
mode: privacy
})
})
.then(response => {
if (!response.ok) {
throw new Error('网络响应不正常');
}
return response.json();
})
.then(data => {
console.log('分享链接生成结果:', data);
if (data.success) {
// 更新链接输入框
const shareUrlInput = document.getElementById('shareUrlInput');
if (shareUrlInput) shareUrlInput.value = data.url;
// 切换显示步骤
document.getElementById('shareStep1').style.display = 'none';
document.getElementById('shareStep2').style.display = 'block';
// 更新隐私指示器
if (privacy === 'private') {
document.getElementById('privateIndicator').style.display = 'inline';
document.getElementById('publicIndicator').style.display = 'none';
} else {
document.getElementById('privateIndicator').style.display = 'none';
document.getElementById('publicIndicator').style.display = 'inline';
}
// 生成二维码
setTimeout(() => {
try {
const qrcodeContainer = document.getElementById('qrcodeContainer');
if (qrcodeContainer) {
qrcodeContainer.innerHTML = '';
if (typeof QRCode !== 'undefined') {
console.log('生成二维码,URL:', data.url);
new QRCode(qrcodeContainer, {
text: data.url,
width: 150,
height: 150,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H
});
} else {
console.error('QRCode库未加载');
qrcodeContainer.innerHTML = '
二维码库未加载,请刷新页面重试
';
}
}
} catch(e) {
console.error('生成二维码失败:', e);
}
}, 300);
} else {
alert(data.error || '生成分享链接失败');
}
})
.catch(error => {
console.error('请求分享链接失败:', error);
alert('生成分享链接失败,请稍后再试');
})
.finally(() => {
// 恢复按钮状态
this.innerHTML = '
生成分享链接';
this.disabled = false;
});
});
}
// 返回按钮事件
const backBtn = document.getElementById('backToOptionsBtn');
if (backBtn) {
backBtn.addEventListener('click', function() {
document.getElementById('shareStep1').style.display = 'block';
document.getElementById('shareStep2').style.display = 'none';
});
}
// 复制链接按钮事件
const copyBtn = document.getElementById('copyLinkBtn');
if (copyBtn) {
copyBtn.addEventListener('click', function() {
const shareUrlInput = document.getElementById('shareUrlInput');
if (shareUrlInput && shareUrlInput.value) {
shareUrlInput.select();
try {
// 尝试使用现代API
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(shareUrlInput.value)
.then(() => {
// 更新按钮状态
const originalText = this.innerHTML;
this.innerHTML = '
已复制';
setTimeout(() => {
this.innerHTML = originalText;
}, 2000);
// 显示成功提示
showCopySuccessToast();
})
.catch(err => {
console.error('复制失败:', err);
// 回退到传统方法
document.execCommand('copy');
// 更新按钮状态
const originalText = this.innerHTML;
this.innerHTML = '
已复制';
setTimeout(() => {
this.innerHTML = originalText;
}, 2000);
showCopySuccessToast();
});
} else {
// 回退到传统方法
document.execCommand('copy');
// 更新按钮状态
const originalText = this.innerHTML;
this.innerHTML = '
已复制';
setTimeout(() => {
this.innerHTML = originalText;
}, 2000);
// 显示成功提示
showCopySuccessToast();
}
} catch (err) {
console.error('复制失败:', err);
alert('复制失败,请手动复制链接');
}
} else {
alert('没有可供复制的链接');
}
});
}
// 下载二维码按钮事件
const downloadBtn = document.getElementById('downloadQRBtn');
if (downloadBtn) {
downloadBtn.addEventListener('click', function() {
const qrcodeContainer = document.getElementById('qrcodeContainer');
if (qrcodeContainer) {
const canvas = qrcodeContainer.querySelector('canvas');
if (canvas) {
try {
const imgData = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.href = imgData;
link.download = '命理分析分享二维码.png';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} catch (error) {
console.error('下载二维码失败:', error);
alert('下载二维码失败,请重试');
}
} else {
alert('二维码尚未生成,请先生成分享链接');
}
}
});
}
}
/**
* 显示复制成功提示
*/
function showCopySuccessToast() {
// 创建提示元素
const toast = document.createElement('div');
toast.className = 'copy-success-alert';
toast.innerHTML = `
链接已复制到剪贴板
`;
// 添加到页面
document.body.appendChild(toast);
// 显示动画
setTimeout(() => {
toast.style.transform = 'translateY(0)';
toast.style.opacity = '1';
// 2秒后隐藏
setTimeout(() => {
toast.style.transform = 'translateY(-100px)';
toast.style.opacity = '0';
// 动画结束后移除
setTimeout(() => {
if (document.body.contains(toast)) {
document.body.removeChild(toast);
}
}, 300);
}, 2000);
}, 10);
}
/**
* 请求AI分析并处理结果
* @param {Object} baziData - 八字数据
* @param {string} analysisId - 分析ID(UUID格式)
* @param {boolean} useStream - 是否使用流式响应
* @param {function} progressCallback - 进度回调函数,用于实时更新内容
* @returns {Promise} 请求处理Promise
*/
function requestAIAnalysis(baziData, analysisId, useStream = true, progressCallback = null, modelType = 'basic') {
return new Promise((resolve, reject) => {
// 查找或创建结果容器
let resultDiv = document.getElementById('result');
// 如果找不到#result容器,尝试寻找其他可用容器
if (!resultDiv) {
console.log('找不到#result容器,尝试寻找替代容器');
// 查找顺序:.result-container > #ai > body
resultDiv = document.querySelector('.result-container');
if (!resultDiv) {
resultDiv = document.getElementById('ai');
if (resultDiv) {
// 添加类以便后续使用
resultDiv.classList.add('result-container');
} else {
// 最后的选择:使用body
resultDiv = document.body;
console.log('未找到专用容器,使用body作为容器');
}
}
}
const aiLoadingDiv = document.createElement('div');
aiLoadingDiv.className = 'ai-loading';
// 流式响应时使用不同的加载显示
if (useStream) {
aiLoadingDiv.innerHTML = `
正在生成AI深度分析,正文将逐步显示...
`;
} else {
aiLoadingDiv.innerHTML = `
正在请求AI深度分析,这可能需要一些时间...
`;
}
// 查找合适的位置插入加载提示
const analysisCard = resultDiv.querySelector('.unified-analysis-card');
if (analysisCard) {
analysisCard.parentNode.insertBefore(aiLoadingDiv, analysisCard.nextSibling);
} else {
resultDiv.appendChild(aiLoadingDiv);
}
// 构建请求参数
const requestData = {
stream: useStream,
model_type: modelType // 添加模型类型参数
};
if (analysisId) {
console.log(`[调试] 准备发送AI分析请求,使用分析ID: ${analysisId} (类型: ${typeof analysisId}), 模型类型: ${modelType}`);
requestData.analysis_id = analysisId;
} else if (baziData) {
console.log(`[调试] 准备发送AI分析请求,使用八字数据, 模型类型: ${modelType}`);
requestData.bazi_data = baziData;
} else {
console.error('[错误] 无法获取分析数据,缺少分析ID和八字数据');
aiLoadingDiv.innerHTML = '
无法获取分析数据
';
reject('无法获取分析数据');
return;
}
// 打印完整的请求数据,辅助调试
console.log('[调试] 完整的AI分析请求数据:', JSON.stringify(requestData));
// 创建AI分析结果容器
const aiResultDiv = document.createElement('div');
aiResultDiv.className = 'ai-analysis-card';
aiResultDiv.innerHTML = `
AI深度命理分析
使用模型:${modelType === 'deep' ? '深度分析模型' : '基础分析模型'}
此分析由AI根据传统命理理论生成,仅供参考
`;
// 插入到适当位置
if (analysisCard) {
analysisCard.parentNode.insertBefore(aiResultDiv, aiLoadingDiv.nextSibling);
} else {
resultDiv.appendChild(aiResultDiv);
}
// 获取内容容器
const aiContentDiv = aiResultDiv.querySelector('.ai-analysis-content');
// 使用流式响应
if (useStream) {
// 初始化EventSource接收流式响应
let contentBuffer = '';
let actualContent = ''; // 保存解析后的实际内容
let xhr = new XMLHttpRequest();
xhr.open('POST', '/ai_analysis');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.responseType = 'text';
// 处理分块响应
xhr.onprogress = function() {
// 获取新内容
const newContent = xhr.responseText.substring(contentBuffer.length);
contentBuffer += newContent;
try {
// 处理新的SSE事件
const lines = newContent.split('\n\n');
lines.forEach(line => {
if (line.trim().startsWith('data:')) {
const jsonStr = line.replace('data:', '').trim();
if (!jsonStr) return; // 跳过空数据
const data = JSON.parse(jsonStr);
console.log('解析流数据:', data);
// 处理块
if (data.chunk) {
// 只提取实际内容,不要存储整个JSON
if (typeof data.chunk === 'string') {
// 累积实际内容
actualContent += data.chunk;
// 调用进度回调函数(如果有)
if (typeof progressCallback === 'function') {
progressCallback(data.chunk);
}
// 将解析后的内容显示在页面上
aiContentDiv.innerHTML = convertMarkdownToHTML(actualContent);
} else {
console.warn('接收到非字符串chunk数据:', data.chunk);
}
// 滚动到底部
aiContentDiv.scrollTop = aiContentDiv.scrollHeight;
}
// 处理完成事件
if (data.done) {
console.log('流式响应完成');
aiLoadingDiv.remove();
// 确保最后一次完整显示
aiContentDiv.innerHTML = convertMarkdownToHTML(actualContent);
// 如果提供了重定向URL,处理重定向
if (data.redirect) {
alert('AI分析已完成,即将跳转到详情页面查看');
setTimeout(() => {
window.location.href = data.redirect;
}, 1000);
}
resolve(actualContent);
}
// 处理错误
if (data.error) {
console.error('流式响应错误:', data.error);
aiLoadingDiv.innerHTML = `
${data.error}
`;
reject(data.error);
}
}
});
} catch (e) {
console.error('解析流式数据时出错:', e);
}
};
// 处理请求完成
xhr.onload = function() {
if (xhr.status === 200) {
// 移除加载提示
aiLoadingDiv.remove();
console.log('流式请求完成');
resolve(actualContent);
} else {
console.error('AI分析请求失败:', xhr.status, xhr.statusText);
aiLoadingDiv.innerHTML = `
请求失败: ${xhr.statusText}
`;
reject(`请求失败: ${xhr.statusText}`);
}
};
// 处理请求错误
xhr.onerror = function(e) {
console.error('AI分析请求网络错误:', e);
aiLoadingDiv.innerHTML = '
网络错误,请稍后再试
';
reject('网络错误,请稍后再试');
};
// 发送请求
xhr.send(JSON.stringify(requestData));
} else {
// 非流式响应处理
fetch('/ai_analysis', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify(requestData)
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP错误 ${response.status}`);
}
return response.json();
})
.then(data => {
// 移除加载提示
aiLoadingDiv.remove();
if (data.success) {
// 显示AI分析结果
aiContentDiv.innerHTML = convertMarkdownToHTML(data.result);
// 如果提供了重定向URL,处理重定向
if (data.redirect) {
alert('AI分析已完成,即将跳转到详情页面查看');
setTimeout(() => {
window.location.href = data.redirect;
}, 1000);
}
resolve(data.result);
} else {
throw new Error(data.error || '获取AI分析失败');
}
})
.catch(error => {
console.error('AI分析请求失败:', error);
aiLoadingDiv.innerHTML = `
${error.message}
`;
reject(error);
});
}
});
}
/**
* 保存分析结果到数据库(在请求AI分析前)
* @param {Object} data - 分析数据
* @returns {Promise
} 返回分析ID(UUID格式)或null
*/
function saveAnalysisFirst(data) {
const resultDiv = document.getElementById('result');
// 创建保存中提示
const savingNotice = document.createElement('div');
savingNotice.className = 'saving-notice';
savingNotice.innerHTML = '正在保存分析结果...';
resultDiv.appendChild(savingNotice);
// 构造保存数据
const saveData = {
name: data.name || '',
gender: data.gender || '',
birth_date: data.birth_date || '',
birth_time: data.birth_time || '',
birth_place: data.birth_place || '',
analysis_data: JSON.stringify(data)
};
console.log('[调试] 发送保存分析请求:', saveData);
// 发送保存请求
return fetch('/save_analysis', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify(saveData)
})
.then(response => response.json())
.then(result => {
savingNotice.remove();
if (result.success) {
console.log(`[调试] 分析结果已保存,ID: ${result.analysis_id}`);
// 返回生成的分析ID(UUID格式)
return result.analysis_id;
} else {
console.error('[错误] 保存分析结果失败:', result.error);
throw new Error(result.error || '保存分析结果失败');
}
})
.catch(error => {
savingNotice.remove();
console.error('[错误] 保存分析请求错误:', error);
// 显示错误提示
const errorNotice = document.createElement('div');
errorNotice.className = 'error-message';
errorNotice.innerHTML = `${error.message || '保存分析结果失败'}`;
resultDiv.appendChild(errorNotice);
// 3秒后移除错误提示
setTimeout(() => errorNotice.remove(), 3000);
return null;
});
}
/**
* 添加"请求AI分析"按钮
* @param {Object} data - 八字分析数据
* @param {HTMLElement} container - 结果容器元素
*/
function addAIAnalysisButton(data, container) {
// 创建一个容器,包含选择模型类型和按钮
const aiControlsContainer = document.createElement('div');
aiControlsContainer.className = 'ai-controls-container mt-4 text-center';
// 添加模型选择选项
const modelSelectionDiv = document.createElement('div');
modelSelectionDiv.className = 'model-selection mb-4';
modelSelectionDiv.innerHTML = `
选择分析模型
`;
aiControlsContainer.appendChild(modelSelectionDiv);
// 创建按钮
const aiButton = document.createElement('button');
aiButton.className = 'btn btn-primary ai-analysis-button';
aiButton.innerHTML = '获取AI深度分析';
aiControlsContainer.appendChild(aiButton);
// 获取并显示模型使用情况
fetch('/api/model_usage')
.then(response => response.json())
.then(data => {
if (data.success) {
// 使用通用的更新UI函数
updateModelSelectionUI(data.usage, aiControlsContainer);
} else {
console.error('获取模型使用情况失败:', data.error);
}
})
.catch(error => {
console.error('获取模型使用情况请求失败:', error);
});
// 添加点击事件处理
aiButton.addEventListener('click', function() {
// 获取选择的模型类型
let selectedModelType = 'basic'; // 默认使用基础模型
const modelTypeRadios = document.querySelectorAll('input[name="modelType"]');
modelTypeRadios.forEach(radio => {
if (radio.checked) {
selectedModelType = radio.value;
}
});
console.log(`选择的模型类型: ${selectedModelType}`);
// 禁用按钮防止重复点击
this.disabled = true;
this.innerHTML = ' AI分析中';
// 请求AI分析
// 检查是否有分析ID,如果没有,需要先保存分析结果
if (data.analysis_id) {
// 如果已有分析ID,直接使用
console.log(`[调试] 使用已有分析ID进行AI分析: ${data.analysis_id}, 模型类型: ${selectedModelType}`);
requestAIAnalysis(data, data.analysis_id, true, null, selectedModelType)
.finally(() => {
// 移除按钮
aiControlsContainer.remove();
});
} else {
// 如果没有分析ID,先保存分析结果,然后使用返回的分析ID
console.log('[调试] 保存分析并获取UUID...');
saveAnalysisFirst(data)
.then(analysisId => {
if (analysisId) {
console.log(`[调试] 获取到分析ID: ${analysisId},开始AI分析,模型类型: ${selectedModelType}`);
// 保存ID到data对象,以便后续使用
data.analysis_id = analysisId;
// 调用AI分析,传递ID和模型类型
return requestAIAnalysis(data, analysisId, true, null, selectedModelType);
} else {
throw new Error('无法获取分析ID');
}
})
.catch(error => {
console.error('[错误] AI分析请求失败:', error);
// 恢复按钮状态,允许重试
this.disabled = false;
this.innerHTML = '重新尝试AI分析';
})
.finally(() => {
if (!this.disabled) return; // 如果按钮已恢复,不要移除
// 移除按钮
aiControlsContainer.remove();
});
}
});
// 添加到结果区域
const resultDiv = container || document.getElementById('result');
const lastCard = resultDiv.querySelector('.result-card:last-child, .unified-analysis-card');
if (lastCard) {
lastCard.parentNode.insertBefore(aiControlsContainer, lastCard.nextSibling);
} else {
resultDiv.appendChild(aiControlsContainer);
}
return aiButton;
}
/**
* 更新模型选择UI中的剩余次数显示
* @param {Object} usage - 使用情况数据
* @param {Element} container - 可选容器,如果指定则仅更新此容器内的元素
*/
function updateModelSelectionUI(usage, container = document) {
// 处理model-option中的usage-count标签
const basicUsageCountElements = container.querySelectorAll('.model-option:has(#basicModel) .usage-count, .model-option:has(#streamBasicModel) .usage-count');
const deepUsageCountElements = container.querySelectorAll('.model-option:has(#deepModel) .usage-count, .model-option:has(#streamDeepModel) .usage-count');
// 更新基础模型剩余次数
if (basicUsageCountElements.length > 0) {
const basicRemaining = usage.basic.remaining;
basicUsageCountElements.forEach(el => {
el.textContent = basicRemaining;
// 获取父元素model-option
const modelOption = el.closest('.model-option');
if (modelOption) {
// 清除现有的警告/危险样式
modelOption.classList.remove('warning', 'danger');
// 添加相应样式
if (basicRemaining === 0) {
modelOption.classList.add('danger');
// 禁用单选按钮
const radio = modelOption.querySelector('input[type="radio"]');
if (radio) radio.disabled = true;
} else if (basicRemaining === 1) {
modelOption.classList.add('warning');
}
}
});
}
// 更新深度模型剩余次数
if (deepUsageCountElements.length > 0) {
const deepRemaining = usage.deep.remaining;
deepUsageCountElements.forEach(el => {
el.textContent = deepRemaining;
// 获取父元素model-option
const modelOption = el.closest('.model-option');
if (modelOption) {
// 清除现有的警告/危险样式
modelOption.classList.remove('warning', 'danger');
// 添加相应样式
if (deepRemaining === 0) {
modelOption.classList.add('danger');
// 禁用单选按钮
const radio = modelOption.querySelector('input[type="radio"]');
if (radio) radio.disabled = true;
// 如果深度模型不可用,确保选择基础模型
const basicRadio = container.querySelector('#basicModel, #streamBasicModel');
if (basicRadio && !basicRadio.disabled) {
basicRadio.checked = true;
}
} else if (deepRemaining === 1) {
modelOption.classList.add('warning');
}
}
});
}
// 更新model-usage-badge
const basicBadges = container.querySelectorAll('#basicModelUsage, #streamBasicModelUsage');
const deepBadges = container.querySelectorAll('#deepModelUsage, #streamDeepModelUsage');
// 更新基础模型badge
if (basicBadges.length > 0) {
const basicRemaining = usage.basic.remaining;
basicBadges.forEach(badge => {
badge.textContent = `剩余${basicRemaining}次`;
// 清除现有样式
badge.classList.remove('warning', 'danger');
// 添加相应样式
if (basicRemaining === 0) {
badge.classList.add('danger');
} else if (basicRemaining === 1) {
badge.classList.add('warning');
}
});
}
// 更新深度模型badge
if (deepBadges.length > 0) {
const deepRemaining = usage.deep.remaining;
deepBadges.forEach(badge => {
badge.textContent = `剩余${deepRemaining}次`;
// 清除现有样式
badge.classList.remove('warning', 'danger');
// 添加相应样式
if (deepRemaining === 0) {
badge.classList.add('danger');
} else if (deepRemaining === 1) {
badge.classList.add('warning');
}
});
}
// 更新分析按钮状态
const analysisButtons = container.querySelectorAll('.ai-analysis-button');
if (analysisButtons.length > 0 && usage.basic.remaining === 0 && usage.deep.remaining === 0) {
analysisButtons.forEach(btn => {
btn.disabled = true;
btn.innerHTML = '今日分析次数已用完';
btn.classList.add('disabled');
});
}
}
/**
* 请求AI分析数据并处理流式响应
* @param {string} responseId - 响应ID
*/
function requestStreamAIAnalysis(responseId) {
if (!responseId) {
showToast('错误', '无效的响应ID', 'error');
return;
}
// 检查是否已经有分析按钮,避免重复添加
if (document.querySelector('.ai-analysis-btn')) {
console.log('AI分析按钮已存在,不重复添加');
return;
}
// 创建一个容器,包含模型选择选项和按钮
const aiControlsContainer = document.createElement('div');
aiControlsContainer.className = 'ai-controls-container mt-3 text-center';
// 添加模型选择选项
const modelSelectionDiv = document.createElement('div');
modelSelectionDiv.className = 'model-selection mb-4';
modelSelectionDiv.innerHTML = `
选择分析模型
`;
// 添加AI分析按钮
const analysisBtn = document.createElement('button');
analysisBtn.className = 'btn btn-primary ai-analysis-button';
analysisBtn.innerHTML = '开始AI分析';
// 添加元素到容器
aiControlsContainer.appendChild(modelSelectionDiv);
aiControlsContainer.appendChild(analysisBtn);
// 将整个容器添加到页面
const resultContainer = document.querySelector('.result-container');
if (!resultContainer) {
console.log('未找到.result-container元素,尝试查找并创建替代容器');
const aiTab = document.getElementById('ai');
if (aiTab) {
aiTab.classList.add('result-container');
aiTab.appendChild(aiControlsContainer);
} else {
// 如果连#ai都没找到,尝试使用#result或body
const fallbackContainer = document.getElementById('result') || document.body;
console.log('使用备用容器:', fallbackContainer.tagName);
fallbackContainer.appendChild(aiControlsContainer);
}
} else {
resultContainer.appendChild(aiControlsContainer);
}
// 分析按钮点击事件
analysisBtn.onclick = function() {
// 获取选择的模型类型
let selectedModelType = 'basic'; // 默认使用基础模型
const modelTypeRadios = document.querySelectorAll('input[name="streamModelType"]');
modelTypeRadios.forEach(radio => {
if (radio.checked) {
selectedModelType = radio.value;
}
});
console.log(`选择的模型类型: ${selectedModelType}`);
// 显示分析中的加载提示
showLoadingModal('AI正在深度分析中...', '这可能需要10-20秒,请耐心等待');
// 创建并显示分析结果容器
const analysisContainer = document.createElement('div');
// 使用正确的requestAIAnalysis函数,调用/ai_analysis接口
// 参数: baziData=null, analysisId=分析ID, useStream=true, progressCallback=null, modelType=选择的模型类型
requestAIAnalysis(null, responseId, true, null, selectedModelType)
.then(() => {
hideLoadingModal();
console.log('AI分析完成');
})
.catch(error => {
hideLoadingModal();
console.error('AI分析请求失败:', error);
showToast('错误', error.message || '请求失败', 'error');
});
};
// 获取并显示模型使用情况
fetch('/api/model_usage')
.then(response => response.json())
.then(data => {
if (data.success) {
// 使用通用的更新UI函数
updateModelSelectionUI(data.usage, aiControlsContainer);
} else {
console.error('获取模型使用情况失败:', data.error);
}
})
.catch(error => {
console.error('获取模型使用情况请求失败:', error);
});
return analysisBtn;
}
/**
* 添加AI分析内容样式
*/
function addAIAnalysisStyles() {
// 添加样式
const styles = `
.ai-list {
padding-left: 1.5em;
margin-top: 0.6em;
margin-bottom: 1em;
}
.ai-list-item {
margin-bottom: 0.7em;
position: relative;
}
.ai-list-item:last-child {
margin-bottom: 0;
}
.markdown-hr {
border: 0;
height: 1px;
background-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0));
margin: 1em 0;
}
.section-divider {
border: 0;
height: 1px;
background-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0));
margin: 1.2em 0 0.7em 0;
}
.section-with-divider {
margin-top: 1em;
}
.h4-small-margin {
margin-top: 0.5em;
margin-bottom: 0.8em;
font-weight: 600;
color: #333;
}
h4.section-title {
font-weight: 600;
color: #444;
margin-top: 1.2em;
margin-bottom: 0.8em;
}
h4.small-margin {
margin-top: 0.7em;
}
h3.section-title {
font-weight: 700;
color: #333;
margin-top: 1.5em;
margin-bottom: 1em;
}
.markdown-content p + .section-with-divider {
margin-top: 1.5em;
}
.markdown-content ul + .section-with-divider,
.markdown-content ol + .section-with-divider {
margin-top: 1.2em;
}
.markdown-content .section-with-divider + p {
margin-top: 0.6em;
}
/* 确保第一行标题正确显示 */
.markdown-content > div:first-child {
margin-top: 0 !important;
}
.markdown-content > .section-with-divider:first-child hr.section-divider {
display: none; /* 第一个分隔线隐藏,避免页面顶部有多余的间隔 */
}
`;
// 检查是否已存在样式元素
let styleElement = document.getElementById('ai-analysis-styles');
if (!styleElement) {
styleElement = document.createElement('style');
styleElement.id = 'ai-analysis-styles';
document.head.appendChild(styleElement);
}
styleElement.textContent = styles;
}
// 初始化时添加样式
document.addEventListener('DOMContentLoaded', addAIAnalysisStyles);
function setupAIAnalysisRequest() {
const requestAiButton = document.getElementById('requestAiAnalysis');
if (requestAiButton) {
// 确保分析区域左对齐
const aiContainer = document.querySelector('#ai');
if (aiContainer) {
const centerElements = aiContainer.querySelectorAll('.text-center');
centerElements.forEach(el => {
if (!el.querySelector('button')) { // 保留按钮的居中
el.classList.remove('text-center');
el.style.textAlign = 'left';
}
});
}
// 页面加载时确保流式内容区域左对齐
const streamingContent = document.getElementById('streamingContent');
if (streamingContent) {
streamingContent.style.textAlign = 'left';
if (streamingContent.parentElement) {
streamingContent.parentElement.style.textAlign = 'left';
}
}
requestAiButton.addEventListener('click', function() {
// ... existing code ...
// 自定义流处理函数,将收到的内容实时显示
const customProgressHandler = (chunk) => {
if (chunk && streamingContent) {
// 累积分析文本
analysisText += chunk;
// 处理文本,如果有第一行是标题,删除它
let processedText = analysisText;
let lines = processedText.split('\n');
// 优化处理第一行标题的逻辑,确保能正确处理任何级别的标题(包括###)
if (lines.length > 1) {
const firstLine = lines[0].trim();
// 处理所有级别的Markdown标题和空行
if (firstLine.match(/^#{1,6}\s+/) || firstLine === '') {
console.log('删除标题行:', firstLine);
processedText = lines.slice(1).join('\n');
}
}
// 实时更新显示,确保使用统一的markdown转换函数
streamingContent.innerHTML = convertMarkdownToHTML(processedText);
// 确保样式一致性
if (typeof addAIAnalysisStyles === 'function') {
addAIAnalysisStyles();
}
// 确保流式内容是左对齐的
streamingContent.style.textAlign = 'left';
if (streamingContent.parentElement) {
streamingContent.parentElement.style.textAlign = 'left';
}
// 移除可能导致居中的容器样式
const containers = document.querySelectorAll('.text-center');
containers.forEach(container => {
if (container.contains(streamingContent)) {
container.classList.remove('text-center');
}
});
// 自动滚动到底部
streamingContent.scrollTop = streamingContent.scrollHeight;
}
};
// ... existing code ...
});
}
}
/**
* 初始化工具提示
*/
function initializeTooltips() {
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
}
/**
* 初始化复制按钮
*/
function initializeCopyButtons() {
document.querySelectorAll('.copy-btn').forEach(button => {
button.addEventListener('click', function() {
const input = this.previousElementSibling;
if (input) {
input.select();
document.execCommand('copy');
// 更新按钮文本
const originalText = this.innerHTML;
this.innerHTML = ' 已复制';
setTimeout(() => {
this.innerHTML = originalText;
}, 2000);
}
});
});
}
// ... existing code ...
/**
* 初始化性别选择器
*/
function initializeGenderSelector() {
console.log('开始初始化性别选择器');
const genderOptions = document.querySelectorAll('.gender-option');
const genderInput = document.getElementById('gender');
const genderFeedback = document.getElementById('gender-feedback');
if (!genderOptions.length) {
console.error('未找到性别选项元素');
return;
}
if (!genderInput) {
console.error('未找到性别输入框元素');
return;
}
// 隐藏性别验证反馈
if (genderFeedback) {
genderFeedback.style.display = 'none';
}
console.log(`找到 ${genderOptions.length} 个性别选项`);
genderOptions.forEach(option => {
option.addEventListener('click', function(e) {
e.preventDefault();
const selectedGender = this.dataset.gender;
console.log('点击性别选项:', selectedGender);
// 移除其他选项的选中状态
genderOptions.forEach(opt => {
opt.classList.remove('selected', 'active');
});
// 添加当前选项的选中状态
this.classList.add('selected', 'active');
// 更新隐藏输入框的值
genderInput.value = selectedGender;
console.log('更新性别值为:', genderInput.value);
// 隐藏验证反馈
if (genderFeedback) {
genderFeedback.style.opacity = '0';
setTimeout(() => {
genderFeedback.style.display = 'none';
genderFeedback.style.opacity = '1';
}, 300);
}
// 移除动画效果
genderOptions.forEach(opt => {
opt.style.animation = '';
});
// 触发change事件以触发表单验证
const event = new Event('change', {
bubbles: true,
cancelable: true,
});
genderInput.dispatchEvent(event);
});
});
}
// 在DOMContentLoaded事件中调用初始化函数
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM加载完成,开始初始化表单');
initializeGenderSelector();
initializeDateSelectors();
// 监听表单提交事件
const fortuneForm = document.getElementById('fortuneForm');
if (fortuneForm) {
fortuneForm.addEventListener('submit', function(e) {
e.preventDefault();
handleFortuneFormSubmit(this);
});
}
});
// ... existing code ...