Image

AI 智能助手

就绪
输入内容超过5000字符,请精简内容后再使用AI功能
AI响应将显示在这里...

AI 智能助手

就绪
输入内容超过5000字符,请精简内容后再使用AI功能
AI响应将显示在这里...
`; const blob = new Blob([content], { type: 'text/html;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${note.title.replace(/[<>:"/\\|?*]/g, '_')}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); showNotification('笔记已导出为HTML文件', 'success'); } // 更新字数统计 function updateWordCount() { const text = editor.textContent || ''; // 中文字数统计(一个汉字算一个字) const chineseChars = text.match(/[\u4e00-\u9fa5]/g) || []; // 非空格字符统计 const otherChars = text.match(/\S/g) || []; // 总字数 = 汉字数 + 其他非空格字符数 const totalChars = chineseChars.length + otherChars.length - chineseChars.length; wordCount.textContent = `${totalChars} 字`; // 检查是否超过5000字符限制 if (totalChars > 5000) { charLimitWarning.classList.add('show'); if (window.innerWidth <= 768) { mobileCharLimitWarning.classList.add('show'); } } else { charLimitWarning.classList.remove('show'); if (window.innerWidth <= 768) { mobileCharLimitWarning.classList.remove('show'); } } } // 检查字符限制 function checkCharLimit() { const text = editor.textContent || ''; // 中文字数统计(一个汉字算一个字) const chineseChars = text.match(/[\u4e00-\u9fa5]/g) || []; // 非空格字符统计 const otherChars = text.match(/\S/g) || []; // 总字数 = 汉字数 + 其他非空格字符数 const totalChars = chineseChars.length + otherChars.length - chineseChars.length; return totalChars <= 5000; } // 停止AI生成 async function stopAIGeneration() { if (isGenerating && currentStreamReader) { try { await currentStreamReader.cancel(); currentStreamReader = null; isGenerating = false; // 更新发送按钮状态 aiSendBtn.innerHTML = '发送'; aiSendBtn.classList.remove('stop-generating'); aiSendBtn.classList.add('generating'); aiSendBtn.disabled = false; // 更新移动端发送按钮状态 mobileAiSendBtn.innerHTML = '发送'; mobileAiSendBtn.classList.remove('stop-generating'); mobileAiSendBtn.classList.add('generating'); mobileAiSendBtn.disabled = false; // 更新状态 aiStatus.textContent = '已停止'; aiStatus.style.color = 'var(--warning-color)'; mobileAiStatus.textContent = '已停止'; mobileAiStatus.style.color = 'var(--warning-color)'; showNotification('AI生成已停止', 'info'); } catch (error) { console.error('停止AI生成时出错:', error); } } } // AI功能 - 真实API调用(流式输出) async function callVolcengineAPI(prompt, functionType, targetLanguage = '英文', isMobile = false) { // 检查字符限制 if (!checkCharLimit()) { showNotification('编辑器内容超过5000字符,请精简内容后再使用AI功能', 'error'); return; } // 如果正在生成,先停止 if (isGenerating) { await stopAIGeneration(); return; } // 获取编辑器内容 const editorContent = getEditorTextContent(3000); const editorHTML = editor.innerHTML; // 更新状态 currentFunctionType = functionType; isGenerating = true; if (isMobile) { mobileAiStatus.textContent = '处理中...'; mobileAiStatus.style.color = 'var(--warning-color)'; mobileAiResponseDefault.style.display = 'none'; mobileAiResponseContent.innerHTML = ''; // 更新移动端发送按钮为停止按钮 mobileAiSendBtn.innerHTML = '停止生成'; mobileAiSendBtn.classList.remove('generating'); mobileAiSendBtn.classList.add('stop-generating'); mobileAiSendBtn.disabled = false; } else { aiStatus.textContent = '处理中...'; aiStatus.style.color = 'var(--warning-color)'; aiResponseDefault.style.display = 'none'; aiResponseContent.innerHTML = ''; // 更新发送按钮为停止按钮 aiSendBtn.innerHTML = '停止生成'; aiSendBtn.classList.remove('generating'); aiSendBtn.classList.add('stop-generating'); aiSendBtn.disabled = false; } // 显示流式输出容器 const responseId = 'response_' + Date.now(); let responseContainer; if (isMobile) { responseContainer = document.createElement('div'); responseContainer.className = 'ai-response-content'; responseContainer.id = 'mobile_' + responseId; mobileAiResponseContent.appendChild(responseContainer); } else { responseContainer = document.createElement('div'); responseContainer.className = 'ai-response-content'; responseContainer.id = responseId; aiResponseContent.appendChild(responseContainer); } // 创建操作按钮容器 const actionButtons = document.createElement('div'); actionButtons.className = 'ai-message-actions'; actionButtons.id = 'aiActions_' + responseId; actionButtons.style.display = 'none'; // 初始隐藏,流式输出完成后再显示 if (isMobile) { mobileAiResponseContent.appendChild(actionButtons); } else { aiResponseContent.appendChild(actionButtons); } let fullResponse = ''; try { // 构建请求数据 const requestData = { prompt: prompt, editor_content: editorContent, editor_html: editorHTML, // 发送HTML格式以便AI了解原文结构 function_type: functionType, target_language: targetLanguage, stream: true, // 启用流式输出 temperature: 0.7, max_tokens: 2000 }; // 调用PHP后端代理(流式输出) const response = await fetch('', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' }, body: JSON.stringify(requestData) }); if (!response.ok) { throw new Error(`HTTP错误: ${response.status}`); } // 读取流式响应 const reader = response.body.getReader(); currentStreamReader = reader; const decoder = new TextDecoder(); while (isGenerating) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value); const lines = chunk.split('\n'); for (const line of lines) { if (line.startsWith('data: ')) { const data = line.substring(6); if (data === '[DONE]') { // 流式输出完成 isGenerating = false; currentStreamReader = null; if (isMobile) { mobileAiStatus.textContent = `${functionType}完成`; mobileAiStatus.style.color = 'var(--success-color)'; // 恢复移动端发送按钮 mobileAiSendBtn.innerHTML = '发送'; mobileAiSendBtn.classList.remove('stop-generating'); mobileAiSendBtn.classList.remove('generating'); } else { aiStatus.textContent = `${functionType}完成`; aiStatus.style.color = 'var(--success-color)'; // 恢复发送按钮 aiSendBtn.innerHTML = '发送'; aiSendBtn.classList.remove('stop-generating'); aiSendBtn.classList.remove('generating'); } // 显示操作按钮 actionButtons.style.display = 'flex'; actionButtons.innerHTML = ` `; break; } try { const jsonData = JSON.parse(data); if (jsonData.error) { throw new Error(jsonData.error); } if (jsonData.content) { fullResponse += jsonData.content; // 直接显示Markdown格式的内容,但应用自定义的HTML样式 const formattedContent = formatAIResponse(fullResponse); responseContainer.innerHTML = formattedContent; // 滚动到底部 if (isMobile) { mobileAiResponse.scrollTop = mobileAiResponse.scrollHeight; } else { aiResponse.scrollTop = aiResponse.scrollHeight; } } } catch (e) { console.error('解析JSON出错:', e); } } } } } catch (error) { console.error('API调用出错:', error); responseContainer.innerHTML = `

抱歉,处理${functionType}请求时出现错误: ${error.message}

`; showNotification(`${functionType}功能暂时不可用,请稍后重试`, 'error'); // 重置状态 isGenerating = false; currentStreamReader = null; if (isMobile) { mobileAiStatus.textContent = '就绪'; mobileAiStatus.style.color = ''; // 恢复移动端发送按钮 mobileAiSendBtn.innerHTML = '发送'; mobileAiSendBtn.classList.remove('stop-generating'); mobileAiSendBtn.classList.remove('generating'); } else { aiStatus.textContent = '就绪'; aiStatus.style.color = ''; // 恢复发送按钮 aiSendBtn.innerHTML = '发送'; aiSendBtn.classList.remove('stop-generating'); aiSendBtn.classList.remove('generating'); } } } // 格式化AI响应为HTML格式 - 修复数字序号显示问题 function formatAIResponse(markdownText) { // 首先清理多余的代码块标记和HTML标签 let html = markdownText; // 移除常见的多余字符模式 html = html.replace(/```html\s*/g, ''); html = html.replace(/```\s*/g, ''); html = html.replace(/`\s*`\s*`/g, ''); html = html.replace(/</g, '<'); html = html.replace(/>/g, '>'); html = html.replace(/&/g, '&'); html = html.replace(/"/g, '"'); html = html.replace(/'/g, "'"); // 处理标题 html = html.replace(/^# (.*$)/gm, '

$1

'); html = html.replace(/^## (.*$)/gm, '

$2

'); html = html.replace(/^### (.*$)/gm, '

$3

'); html = html.replace(/^#### (.*$)/gm, '

$4

'); html = html.replace(/^##### (.*$)/gm, '
$5
'); html = html.replace(/^###### (.*$)/gm, '
$6
'); // 处理加粗 html = html.replace(/\*\*(.*?)\*\*/g, '$1'); html = html.replace(/__(.*?)__/g, '$1'); // 处理斜体 html = html.replace(/\*(.*?)\*/g, '$1'); html = html.replace(/_(.*?)_/g, '$1'); // 处理删除线 html = html.replace(/~~(.*?)~~/g, '$1'); // 处理块引用 html = html.replace(/^>\s*(.*$)/gm, '

$1

'); // 处理行内代码 html = html.replace(/`([^`\n]+)`/g, '$1'); // 处理代码块 const codeBlockRegex = /```(?:\w+)?\n([\s\S]*?)```/g; html = html.replace(codeBlockRegex, '
$1
'); // 处理链接 html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1'); // 处理图片 html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '$1'); // 处理段落 - 先将所有换行转换为段落 const lines = html.split('\n'); let result = []; let inList = false; let listType = ''; // 'ul' 或 'ol' let listItems = []; for (let line of lines) { line = line.trim(); if (!line) { // 空行,结束当前列表 if (inList && listItems.length > 0) { result.push(`<${listType}>`); result.push(listItems.join('')); result.push(``); inList = false; listType = ''; listItems = []; } continue; } // 检查列表项 const unorderedMatch = line.match(/^[-*+]\s+(.*)$/); const orderedMatch = line.match(/^(\d+)\.\s+(.*)$/); if (orderedMatch) { // 有序列表项 const content = orderedMatch[2]; if (!inList || listType !== 'ol') { // 如果之前有列表,先结束 if (inList && listItems.length > 0) { result.push(`<${listType}>`); result.push(listItems.join('')); result.push(``); listItems = []; } inList = true; listType = 'ol'; } listItems.push(`
  • ${content}
  • `); } else if (unorderedMatch) { // 无序列表项 const content = unorderedMatch[1]; if (!inList || listType !== 'ul') { // 如果之前有列表,先结束 if (inList && listItems.length > 0) { result.push(`<${listType}>`); result.push(listItems.join('')); result.push(``); listItems = []; } inList = true; listType = 'ul'; } listItems.push(`
  • ${content}
  • `); } else { // 非列表项,结束当前列表 if (inList && listItems.length > 0) { result.push(`<${listType}>`); result.push(listItems.join('')); result.push(``); inList = false; listType = ''; listItems = []; } // 普通段落 result.push(`

    ${line}

    `); } } // 处理最后可能存在的列表 if (inList && listItems.length > 0) { result.push(`<${listType}>`); result.push(listItems.join('')); result.push(``); } html = result.join('\n'); // 清理空的段落 html = html.replace(/

    <\/p>/g, ''); html = html.replace(/

    \s*<\/p>/g, ''); return html; } // 显示AI应用确认弹窗 window.showApplyConfirm = function(responseId, isMobile = false) { pendingApplyResponseId = responseId; applyConfirmModal.style.display = 'flex'; // 如果是移动端,关闭AI助手 if (isMobile && window.innerWidth <= 768) { closeMobileAI(); } }; // 应用AI内容到编辑器 - 应用所有内容 function applyAIContentDirect(responseId) { let responseContainer; if (window.innerWidth <= 768) { responseContainer = document.getElementById('mobile_' + responseId); } else { responseContainer = document.getElementById(responseId); } if (!responseContainer) return; // 获取HTML内容 let htmlContent = responseContainer.innerHTML; if (!htmlContent.trim()) { showNotification('没有内容可以应用', 'warning'); return; } // 修复应用后的数字序号左边距问题 // 确保有序列表有正确的左边距 htmlContent = htmlContent.replace(/

      /g, '
        '); htmlContent = htmlContent.replace(/
      1. /g, '
      2. '); // 保存当前内容到历史记录 saveToHistory(); // 应用所有内容到编辑器 editor.innerHTML = htmlContent; // 保存笔记 saveNote(); showNotification('AI内容已应用到编辑器', 'success'); }; // 复制AI内容 window.copyAIContent = function(responseId, isMobile = false) { let responseContainer; if (isMobile) { responseContainer = document.getElementById('mobile_' + responseId); } else { responseContainer = document.getElementById(responseId); } if (!responseContainer) return; const textContent = responseContainer.textContent; if (!textContent.trim()) { showNotification('没有内容可以复制', 'warning'); return; } navigator.clipboard.writeText(textContent).then(() => { showNotification('内容已复制到剪贴板', 'success'); }).catch(err => { console.error('复制失败:', err); // 备用方法 const textArea = document.createElement('textarea'); textArea.value = textContent; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); showNotification('内容已复制到剪贴板', 'success'); }); }; // 保存AI响应为HTML文件 window.saveAIResponse = function(responseId, isMobile = false) { let responseContainer; if (isMobile) { responseContainer = document.getElementById('mobile_' + responseId); } else { responseContainer = document.getElementById(responseId); } if (!responseContainer) return; const textContent = responseContainer.textContent; if (!textContent.trim()) { showNotification('没有内容可以保存', 'warning'); return; } const htmlContent = responseContainer.innerHTML; const now = new Date(); const timestamp = now.toISOString().replace(/[:.]/g, '-').slice(0, 19); const fullHtml = ` AI响应保存 - ${timestamp}

        AI智能助手响应保存

        七豆AI记事本 - 智能在线记事本

        保存时间: ${now.toLocaleString('zh-CN')}
        来源: 智能在线记事本 - AI助手
        ${htmlContent}
        `; const blob = new Blob([fullHtml], { type: 'text/html;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `AI响应_${timestamp}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); showNotification('AI响应已保存为HTML文件', 'success'); }; // 获取编辑器文本内容 function getEditorTextContent(maxLength = 3000) { const tempDiv = document.createElement('div'); tempDiv.innerHTML = editor.innerHTML; let text = tempDiv.textContent || ''; // 限制长度 if (text.length > maxLength) { text = text.substring(0, maxLength) + '...(内容过长,已截断)'; } return text; } // 插入视频 - 修复视频插入问题 function insertVideo() { let videoSrc = videoUrl.value.trim(); let videoWidthVal = parseInt(videoWidth.value) || 640; let videoHeightVal = parseInt(videoHeight.value) || 360; const controls = videoControls.checked; if (!videoSrc) { showNotification('请输入视频URL', 'warning'); return; } // 创建视频元素 const video = document.createElement('video'); video.src = videoSrc; video.width = videoWidthVal; video.height = videoHeightVal; video.controls = controls; video.className = 'editor-video'; video.style.maxWidth = '100%'; video.style.height = 'auto'; // 设置视频属性 video.setAttribute('preload', 'metadata'); video.setAttribute('playsinline', ''); // 如果是YouTube链接,转换为嵌入格式 if (videoSrc.includes('youtube.com') || videoSrc.includes('youtu.be')) { let videoId = ''; // 提取YouTube视频ID if (videoSrc.includes('youtube.com/watch?v=')) { videoId = videoSrc.split('v=')[1]; const ampersandPosition = videoId.indexOf('&'); if (ampersandPosition !== -1) { videoId = videoId.substring(0, ampersandPosition); } } else if (videoSrc.includes('youtu.be/')) { videoId = videoSrc.split('youtu.be/')[1]; } if (videoId) { // 创建YouTube iframe嵌入 const iframe = document.createElement('iframe'); iframe.src = `https://www.youtube.com/embed/${videoId}`; iframe.width = videoWidthVal; iframe.height = videoHeightVal; iframe.frameBorder = '0'; iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture'; iframe.allowFullscreen = true; iframe.className = 'editor-video'; iframe.style.borderRadius = '8px'; // 保存到历史记录 saveToHistory(); // 插入视频到编辑器 insertAtCursor(iframe); videoModal.style.display = 'none'; showNotification('YouTube视频已插入', 'success'); // 保存笔记 saveNote(); return; } } // 保存到历史记录 saveToHistory(); // 插入视频到编辑器 insertAtCursor(video); videoModal.style.display = 'none'; videoUrl.value = ''; videoWidth.value = '640'; videoHeight.value = '360'; uploadedVideoUrl = null; // 保存笔记 saveNote(); showNotification('视频已插入到记事本中', 'success'); } // 生成分享链接 - 自动保存到服务器 async function generateShareLink() { if (!currentNoteId) { showNotification('请先打开一个笔记', 'warning'); return; } const note = notes.find(n => n.id === currentNoteId); if (!note) return; // 生成唯一的分享ID const shareId = 'share_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); const now = new Date(); const createdAt = now.toLocaleString('zh-CN'); // 保存到服务器 try { const response = await fetch('', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-Requested-With': 'XMLHttpRequest' }, body: new URLSearchParams({ action: 'save_share_content', share_id: shareId, share_type: selectedShareType, title: note.title, content: editor.innerHTML, created_at: createdAt }) }); const data = await response.json(); if (data.error) { showNotification('分享失败: ' + data.error, 'error'); return; } // 显示分享链接 shareLink.textContent = data.share_url; shareModal.style.display = 'flex'; showNotification('内容已保存到服务器', 'success'); } catch (error) { console.error('保存分享失败:', error); showNotification('分享失败,请稍后重试', 'error'); } } // 复制分享链接 function copyShareLink() { const linkText = shareLink.textContent; if (!linkText.trim()) { showNotification('没有可复制的链接', 'warning'); return; } navigator.clipboard.writeText(linkText).then(() => { showNotification('分享链接已复制到剪贴板', 'success'); }).catch(err => { console.error('复制失败:', err); // 备用方法 const textArea = document.createElement('textarea'); textArea.value = linkText; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); showNotification('分享链接已复制到剪贴板', 'success'); }); } // 处理分享链接加载 function loadSharedNote() { const urlParams = new URLSearchParams(window.location.search); const shareId = urlParams.get('share'); if (shareId) { // 从服务器获取分享内容 fetch(`?action=get_share_content&share_id=${shareId}`) .then(response => response.json()) .then(data => { if (data.error) { showNotification(data.error, 'error'); return; } if (data.success && data.content) { // 显示分享内容 document.body.innerHTML = data.content; showNotification('分享内容已加载', 'info'); } }) .catch(error => { console.error('加载分享失败:', error); showNotification('加载分享失败,链接可能已过期', 'error'); }); } } // 视频上传处理 function handleVideoUpload(file) { if (!file.type.startsWith('video/')) { showNotification('请上传视频文件', 'error'); return; } if (file.size > 50 * 1024 * 1024) { showNotification('视频大小不能超过50MB', 'error'); return; } const formData = new FormData(); formData.append('video_upload', file); // 显示上传进度条 uploadProgress.classList.add('show'); // 模拟上传进度 let progress = 0; const progressInterval = setInterval(() => { progress += 5; progressFill.style.width = progress + '%'; if (progress >= 90) { clearInterval(progressInterval); } }, 300); // 上传到服务器 fetch('', { method: 'POST', headers: { 'X-Requested-With': 'XMLHttpRequest' }, body: formData }) .then(response => response.json()) .then(data => { clearInterval(progressInterval); progressFill.style.width = '100%'; setTimeout(() => { uploadProgress.classList.remove('show'); progressFill.style.width = '0%'; }, 500); if (data.error) { showNotification(data.error, 'error'); return; } // 保存上传的视频URL uploadedVideoUrl = data.video_url; // 将视频URL自动填写到输入框 videoUrl.value = uploadedVideoUrl; showNotification('视频上传成功,URL已自动填写', 'success'); }) .catch(error => { console.error('视频上传失败:', error); clearInterval(progressInterval); uploadProgress.classList.remove('show'); progressFill.style.width = '0%'; showNotification('视频上传失败,请重试', 'error'); }); } // 导入文本文件 - 支持Word文档格式导入(保持格式) async function importTextFile(file) { if (!file) return; const fileExtension = file.name.split('.').pop().toLowerCase(); const reader = new FileReader(); reader.onload = async function(e) { let content = ''; // 根据文件类型处理内容 switch(fileExtension) { case 'txt': case 'rtf': // 纯文本和RTF文件,直接使用 content = e.target.result; // 转换换行符为HTML段落 const paragraphs = content.split(/\n\s*\n/); content = paragraphs.map(p => { const lines = p.split('\n').filter(line => line.trim()); return lines.map(line => `

        ${line}

        `).join(''); }).join(''); break; case 'csv': // CSV文件,修复乱码问题 try { // 尝试多种编码 let csvContent = e.target.result; // 先尝试UTF-8 let decoder = new TextDecoder('utf-8'); let uint8Array = new Uint8Array(e.target.result); csvContent = decoder.decode(uint8Array); // 如果解码后包含乱码,尝试GBK/GB2312 if (csvContent.includes('�') || /[\u0000-\u001F\u007F-\u009F]/.test(csvContent)) { try { // 尝试GB18030(包含GBK和GB2312) decoder = new TextDecoder('gb18030'); csvContent = decoder.decode(uint8Array); } catch (gbError) { // 如果GB18030失败,尝试GBK try { decoder = new TextDecoder('gbk'); csvContent = decoder.decode(uint8Array); } catch (gbkError) { // 最后尝试UTF-8 with BOM decoder = new TextDecoder('utf-8'); csvContent = decoder.decode(uint8Array); } } } // 分割CSV行,处理逗号分隔 const lines = csvContent.split('\n').filter(line => line.trim()); if (lines.length === 0) { showNotification('CSV文件内容为空', 'warning'); return; } let tableHtml = ''; // 处理CSV行 for (let i = 0; i < lines.length; i++) { const line = lines[i]; // 简单的CSV解析,处理引号包裹的字段 const cells = []; let currentCell = ''; let inQuotes = false; for (let j = 0; j < line.length; j++) { const char = line[j]; if (char === '"') { inQuotes = !inQuotes; } else if (char === ',' && !inQuotes) { cells.push(currentCell.trim()); currentCell = ''; } else { currentCell += char; } } cells.push(currentCell.trim()); if (cells.length > 0 && cells[0]) { tableHtml += ''; cells.forEach(cell => { // 清理引号 let cellContent = cell.replace(/^"(.*)"$/, '$1').replace(/""/g, '"'); if (i === 0) { tableHtml += ``; } else { tableHtml += ``; } }); tableHtml += ''; } } tableHtml += '
        ${cellContent}${cellContent}
        '; content = tableHtml; showNotification('CSV文件已导入', 'success'); } catch (csvError) { console.error('CSV处理失败:', csvError); showNotification('CSV文件处理失败,请确保文件格式正确', 'error'); return; } break; case 'doc': case 'docx': // Word文档,使用Mammoth.js提取内容并保持格式 try { const arrayBuffer = e.target.result; // 使用Mammoth.js转换Word文档为HTML,并保持基本格式 const result = await mammoth.convertToHtml({arrayBuffer: arrayBuffer}, { styleMap: [ "p[style-name='Heading 1'] => h1:fresh", "p[style-name='Heading 2'] => h2:fresh", "p[style-name='Heading 3'] => h3:fresh", "p[style-name='Heading 4'] => h4:fresh", "p[style-name='Heading 5'] => h5:fresh", "p[style-name='Heading 6'] => h6:fresh", "r[style-name='Strong'] => strong", "r[style-name='Emphasis'] => em", "r[style-name='Underline'] => u", "p[style-name='List Paragraph'] => ul > li:fresh", "p[style-name='List Bullet'] => ul > li:fresh", "p[style-name='List Number'] => ol > li:fresh" ], includeEmbeddedStyleMap: true, includeDefaultStyleMap: true }); content = result.value; // 清理和优化HTML content = content.replace(/

        \s*<\/p>/g, ''); content = content.replace(/

         <\/p>/g, ''); content = content.replace(/\s*/g, '

        '); // 修复Word文档导入顺序问题 - 保持原有顺序 // 将内容按段落分割,然后按正确顺序组合 const tempDiv = document.createElement('div'); tempDiv.innerHTML = content; // 收集所有子节点 const nodes = []; let currentNode = tempDiv.firstChild; while (currentNode) { nodes.push(currentNode); currentNode = currentNode.nextSibling; } // 按原始顺序重建内容 content = ''; nodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { content += node.outerHTML; } else if (node.nodeType === Node.TEXT_NODE && node.textContent.trim()) { content += `

        ${node.textContent}

        `; } }); showNotification('Word文档内容已导入并保持格式', 'success'); } catch (error) { console.error('Word文档处理失败:', error); showNotification('Word文档处理失败,请尝试复制粘贴内容', 'error'); return; } break; default: showNotification('不支持的文件格式,请使用.txt, .doc, .docx, .rtf或.csv文件', 'error'); return; } // 将内容插入到编辑器 if (content.trim()) { // 保存到历史记录 saveToHistory(); const selection = window.getSelection(); if (selection.rangeCount > 0) { const range = selection.getRangeAt(0); range.deleteContents(); // 创建临时div来解析HTML const tempDiv = document.createElement('div'); tempDiv.innerHTML = content; // 插入所有子节点(按顺序) const fragment = document.createDocumentFragment(); while (tempDiv.firstChild) { fragment.appendChild(tempDiv.firstChild); } range.insertNode(fragment); // 将光标移动到插入的内容之后 range.setStartAfter(range.endContainer); range.setEndAfter(range.endContainer); selection.removeAllRanges(); selection.addRange(range); } else { // 如果没有选择范围,直接添加到编辑器末尾 editor.innerHTML += content; } // 聚焦编辑器 editor.focus(); // 更新字数 updateWordCount(); // 保存笔记 saveNote(); showNotification('文件已导入到记事本', 'success'); } else { showNotification('文件内容为空或无法提取', 'warning'); } }; reader.onerror = function() { showNotification('文件读取失败,请重试', 'error'); }; // 读取文件 if (fileExtension === 'txt' || fileExtension === 'rtf') { reader.readAsText(file, 'UTF-8'); } else if (fileExtension === 'csv') { // 对于CSV文件,读取为ArrayBuffer以支持多种编码 reader.readAsArrayBuffer(file); } else if (fileExtension === 'doc' || fileExtension === 'docx') { reader.readAsArrayBuffer(file); } else { // 对于其他格式,尝试读取为文本 reader.readAsText(file, 'UTF-8'); } } // AI功能快捷按钮 async function aiSummarize() { // 检查字符限制 if (!checkCharLimit()) { showNotification('编辑器内容超过5000字符,请精简内容后再使用AI功能', 'error'); return; } const text = getEditorTextContent(3000); if (!text.trim()) { showNotification('请先在左侧编辑器中输入一些文本以便总结', 'warning'); return; } const isMobile = window.innerWidth <= 768; await callVolcengineAPI('', '总结', '英文', isMobile); } async function aiImprove() { // 检查字符限制 if (!checkCharLimit()) { showNotification('编辑器内容超过5000字符,请精简内容后再使用AI功能', 'error'); return; } const text = getEditorTextContent(2000); if (!text.trim()) { showNotification('请先在左侧编辑器中输入一些文本以便润色', 'warning'); return; } const isMobile = window.innerWidth <= 768; await callVolcengineAPI('', '润色', '英文', isMobile); } async function aiTranslate() { // 检查字符限制 if (!checkCharLimit()) { showNotification('编辑器内容超过5000字符,请精简内容后再使用AI功能', 'error'); return; } const text = getEditorTextContent(1500); if (!text.trim()) { showNotification('请先在左侧编辑器中输入一些文本以便翻译', 'warning'); return; } // 显示翻译模态框 translateModal.style.display = 'flex'; selectedLanguage = '英文'; customLanguage.value = ''; // 重置语言选择 languageOptions.forEach(opt => opt.classList.remove('selected')); languageOptions[0].classList.add('selected'); } async function aiCorrect() { // 检查字符限制 if (!checkCharLimit()) { showNotification('编辑器内容超过5000字符,请精简内容后再使用AI功能', 'error'); return; } const text = getEditorTextContent(2500); if (!text.trim()) { showNotification('请先在左侧编辑器中输入一些文本以便纠错', 'warning'); return; } const isMobile = window.innerWidth <= 768; await callVolcengineAPI('', '纠错', '英文', isMobile); } async function sendAIPrompt(isMobile = false) { // 检查字符限制 if (!checkCharLimit()) { showNotification('编辑器内容超过5000字符,请精简内容后再使用AI功能', 'error'); return; } // 如果正在生成,点击发送按钮变为停止生成 if (isGenerating) { await stopAIGeneration(); return; } const prompt = isMobile ? mobileAiPrompt.value.trim() : aiPrompt.value.trim(); if (!prompt) { showNotification('请输入指令', 'warning'); return; } if (!getEditorTextContent(100).trim()) { showNotification('请先在左侧编辑器中输入一些文本', 'warning'); return; } await callVolcengineAPI(prompt, '自定义', '英文', isMobile); // 清空输入框 if (isMobile) { mobileAiPrompt.value = ''; } else { aiPrompt.value = ''; } } // 开始翻译 async function startTranslation() { // 检查字符限制 if (!checkCharLimit()) { showNotification('编辑器内容超过5000字符,请精简内容后再使用AI功能', 'error'); return; } const text = getEditorTextContent(1500); if (!text.trim()) { showNotification('请先在左侧编辑器中输入一些文本以便翻译', 'warning'); return; } // 获取目标语言 let targetLang = selectedLanguage; if (customLanguage.value.trim()) { targetLang = customLanguage.value.trim(); } // 关闭模态框 translateModal.style.display = 'none'; // 调用翻译API const isMobile = window.innerWidth <= 768; await callVolcengineAPI('', '翻译', targetLang, isMobile); } // 图片上传处理 function handleImageUpload(file) { if (!file.type.match('image.*')) { showNotification('请上传图片文件 (JPG, PNG, GIF)', 'error'); return; } if (file.size > 5 * 1024 * 1024) { showNotification('图片大小不能超过5MB', 'error'); return; } const reader = new FileReader(); reader.onload = function(e) { const img = document.createElement('img'); img.src = e.target.result; img.style.maxWidth = '100%'; img.style.height = 'auto'; img.style.borderRadius = '8px'; img.style.margin = '15px 0'; img.style.boxShadow = '0 4px 12px rgba(0,0,0,0.1)'; img.style.display = 'block'; // 保存到历史记录 saveToHistory(); // 插入到编辑器光标位置 insertAtCursor(img); showNotification('图片已插入到记事本中', 'success'); saveNote(); // 自动保存 }; reader.onerror = function() { showNotification('图片读取失败,请重试', 'error'); }; reader.readAsDataURL(file); } // 插入链接 function insertLink() { const url = linkUrl.value.trim(); const text = linkText.value.trim() || url; if (!url) { showNotification('请输入链接URL', 'warning'); return; } // 确保URL有协议前缀 const fullUrl = url.startsWith('http') ? url : 'https://' + url; const a = document.createElement('a'); a.href = fullUrl; a.textContent = text; a.target = '_blank'; a.rel = 'noopener noreferrer'; a.style.color = 'var(--primary-color)'; a.style.textDecoration = 'none'; a.style.borderBottom = '1px solid var(--primary-light)'; a.addEventListener('mouseover', () => { a.style.textDecoration = 'underline'; }); a.addEventListener('mouseout', () => { a.style.textDecoration = 'none'; }); // 保存到历史记录 saveToHistory(); insertAtCursor(a); linkModal.style.display = 'none'; linkUrl.value = ''; linkText.value = ''; showNotification('链接已插入', 'success'); saveNote(); // 自动保存 } // 在光标位置插入内容 function insertAtCursor(node) { const selection = window.getSelection(); if (selection.rangeCount > 0) { const range = selection.getRangeAt(0); range.deleteContents(); range.insertNode(node); // 将光标移动到插入的内容之后 range.setStartAfter(node); range.setEndAfter(node); selection.removeAllRanges(); selection.addRange(range); // 聚焦编辑器 editor.focus(); } else { // 如果没有选择范围,直接添加到编辑器末尾 editor.appendChild(node); editor.focus(); } } // 执行编辑器命令 function execCommand(command, value = null) { // 保存到历史记录 saveToHistory(); document.execCommand(command, false, value); editor.focus(); updateWordCount(); } // 显示通知(从中间顶部弹出) function showNotification(message, type = 'success') { // 移除之前的通知 const existingNotification = document.querySelector('.notification'); if (existingNotification) { existingNotification.remove(); } // 创建新通知 const notification = document.createElement('div'); notification.className = `notification ${type}`; notification.innerHTML = ` ${message} `; document.body.appendChild(notification); // 3秒后自动移除 setTimeout(() => { if (notification.parentNode) { notification.style.animation = 'slideDown 0.3s ease reverse'; setTimeout(() => notification.remove(), 300); } }, 3000); } // 设置事件监听器 function setupEventListeners() { // 编辑器点击事件 - 修复:第一次访问的用户点击任意位置清空编辑框 editor.addEventListener('click', () => { if (isFirstTimeUser) { // 保存到历史记录 saveToHistory(); // 清空编辑器 editor.innerHTML = ''; updateWordCount(); // 标记用户已经完成第一次点击 isFirstTimeUser = false; localStorage.setItem('firstTimeVisit', 'false'); showNotification('可以开始编辑了', 'info'); } }); // 编辑器事件 editor.addEventListener('input', updateWordCount); editor.addEventListener('keydown', (e) => { // Ctrl+S 保存 if ((e.ctrlKey || e.metaKey) && e.key === 's') { e.preventDefault(); saveNote(); } // Ctrl+N 新建笔记 if ((e.ctrlKey || e.metaKey) && e.key === 'n') { e.preventDefault(); newNote(); } // Ctrl+Z 撤销 if ((e.ctrlKey || e.metaKey) && e.key === 'z') { e.preventDefault(); undo(); } // Ctrl+Y 或 Ctrl+Shift+Z 恢复 if ((e.ctrlKey || e.metaKey) && (e.key === 'y' || (e.shiftKey && e.key === 'z'))) { e.preventDefault(); redo(); } // Ctrl+B 加粗 if ((e.ctrlKey || e.metaKey) && e.key === 'b') { e.preventDefault(); execCommand('bold'); } // Ctrl+I 斜体 if ((e.ctrlKey || e.metaKey) && e.key === 'i') { e.preventDefault(); execCommand('italic'); } // Ctrl+U 下划线 if ((e.ctrlKey || e.metaKey) && e.key === 'u') { e.preventDefault(); execCommand('underline'); } }); // 工具按钮 toolButtons.forEach(button => { button.addEventListener('click', () => { const command = button.getAttribute('data-command'); const value = button.getAttribute('data-value'); execCommand(command, value); // 添加视觉反馈 button.classList.add('active'); setTimeout(() => button.classList.remove('active'), 300); }); }); // 撤销和恢复按钮 undoBtn.addEventListener('click', undo); redoBtn.addEventListener('click', redo); // 导入文件按钮 importFileBtn.addEventListener('click', () => { importFileInput.click(); }); importFileInput.addEventListener('change', (e) => { if (e.target.files.length > 0) { importTextFile(e.target.files[0]); // 清空输入值,以便可以再次选择同一文件 e.target.value = ''; } }); // 字体大小和字体选择 fontSizeSelect.addEventListener('change', (e) => { if (e.target.value) { execCommand('fontSize', e.target.value); e.target.value = ''; } }); fontFamilySelect.addEventListener('change', (e) => { if (e.target.value) { execCommand('fontName', e.target.value); e.target.value = ''; } }); // 插入视频按钮 insertVideoBtn.addEventListener('click', () => { // 重置视频模态框 uploadedVideoUrl = null; videoUrl.value = ''; videoModal.style.display = 'flex'; }); // 视频上传区域点击事件 videoUploadArea.addEventListener('click', () => { videoFileInput.click(); }); // 视频文件选择事件 videoFileInput.addEventListener('change', (e) => { if (e.target.files.length > 0) { handleVideoUpload(e.target.files[0]); } }); // 图片上传按钮 insertImageBtnToolbar.addEventListener('click', () => { // 创建文件输入元素 const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = 'image/*'; fileInput.style.display = 'none'; fileInput.addEventListener('change', (e) => { if (e.target.files.length > 0) { handleImageUpload(e.target.files[0]); } document.body.removeChild(fileInput); }); document.body.appendChild(fileInput); fileInput.click(); }); // 链接插入按钮 insertLinkBtnToolbar.addEventListener('click', () => { linkModal.style.display = 'flex'; linkUrl.focus(); }); // 分享按钮 shareBtn.addEventListener('click', () => { // 自动生成分享链接 generateShareLink(); }); // 视频模态框事件 cancelVideo.addEventListener('click', () => { videoModal.style.display = 'none'; }); insertVideoBtnModal.addEventListener('click', insertVideo); // 分享模态框事件 cancelShare.addEventListener('click', () => { shareModal.style.display = 'none'; }); copyShareLinkBtn.addEventListener('click', copyShareLink); // 主控制按钮 newNoteBtn.addEventListener('click', newNote); myNotesBtn.addEventListener('click', () => { loadNotes(); notesModal.style.display = 'flex'; }); saveNoteBtn.addEventListener('click', saveNote); exportBtn.addEventListener('click', exportNote); clearBtn.addEventListener('click', clearEditor); // AI功能按钮 summarizeBtn.addEventListener('click', aiSummarize); improveBtn.addEventListener('click', aiImprove); translateBtn.addEventListener('click', aiTranslate); correctBtn.addEventListener('click', aiCorrect); aiSendBtn.addEventListener('click', () => sendAIPrompt(false)); // AI自定义提示输入框 aiPrompt.addEventListener('keydown', (e) => { if (e.key === 'Enter' && e.ctrlKey) { e.preventDefault(); sendAIPrompt(false); } }); // 模态框事件 cancelNewNote.addEventListener('click', () => { newNoteModal.style.display = 'none'; }); createNoteBtn.addEventListener('click', createNote); closeNotesModal.addEventListener('click', () => { notesModal.style.display = 'none'; }); cancelLink.addEventListener('click', () => { linkModal.style.display = 'none'; }); insertLinkBtn.addEventListener('click', insertLink); // 翻译模态框事件 cancelTranslate.addEventListener('click', () => { translateModal.style.display = 'none'; }); confirmTranslate.addEventListener('click', startTranslation); // 点击模态框外部关闭 window.addEventListener('click', (e) => { if (e.target === newNoteModal) { newNoteModal.style.display = 'none'; } if (e.target === notesModal) { notesModal.style.display = 'none'; } if (e.target === linkModal) { linkModal.style.display = 'none'; } if (e.target === videoModal) { videoModal.style.display = 'none'; } if (e.target === shareModal) { shareModal.style.display = 'none'; } if (e.target === translateModal) { translateModal.style.display = 'none'; } if (e.target === applyConfirmModal) { applyConfirmModal.style.display = 'none'; } }); // 模态框内按Enter键提交 noteTitle.addEventListener('keypress', (e) => { if (e.key === 'Enter') { createNoteBtn.click(); } }); linkUrl.addEventListener('keypress', (e) => { if (e.key === 'Enter') { insertLinkBtn.click(); } }); videoUrl.addEventListener('keypress', (e) => { if (e.key === 'Enter') { insertVideoBtnModal.click(); } }); customLanguage.addEventListener('keypress', (e) => { if (e.key === 'Enter') { confirmTranslate.click(); } }); // 键盘快捷键 document.addEventListener('keydown', (e) => { // Esc 关闭模态框 if (e.key === 'Escape') { newNoteModal.style.display = 'none'; notesModal.style.display = 'none'; linkModal.style.display = 'none'; videoModal.style.display = 'none'; shareModal.style.display = 'none'; translateModal.style.display = 'none'; applyConfirmModal.style.display = 'none'; // Esc 关闭移动端AI助手 if (window.innerWidth <= 768 && isMobileAiOpen) { closeMobileAI(); } } // Esc 停止AI生成 if (e.key === 'Escape' && isGenerating) { e.preventDefault(); stopAIGeneration(); } }); // 窗口大小变化时更新UI window.addEventListener('resize', () => { updateWordCount(); // 移动端隐藏分割线 if (window.innerWidth <= 768) { resizer.style.display = 'none'; document.querySelector('.editor-section').style.flex = '1'; document.querySelector('.ai-section').style.flex = '1'; } else { resizer.style.display = 'flex'; document.querySelector('.editor-section').style.flex = editorWidth; document.querySelector('.ai-section').style.flex = 100 - editorWidth; } }); } // 初始化应用 document.addEventListener('DOMContentLoaded', () => { init(); // 检查是否有分享链接 loadSharedNote(); });