const { createApp, ref, computed, onMounted } = Vue; // 1. 全局渲染器 - 纯手写的高鲁棒性解析器,不再依赖 marked const globalRenderMarkdown = (text) => { if (!text) return ''; // 0. 预处理:去除 AI 可能返回的代码块标记 let cleanText = text.trim(); // 去除开头的 ```markdown 或 ``` cleanText = cleanText.replace(/^```(markdown)?\s*/i, ''); // 去除结尾的 ``` cleanText = cleanText.replace(/```\s*$/, ''); // 1. 预处理:按行分割 const lines = cleanText.split('\n'); let html = ''; let inList = false; for (let i = 0; i < lines.length; i++) { let line = lines[i]; // 关键:去除行首尾空白,解决缩进导致的解析失败 let trimmed = line.trim(); // 空行处理 if (!trimmed) { if (inList) { html += '\n'; inList = false; } continue; // 忽略空行,或者可以加
} // 2. 内联格式处理(加粗、代码、链接) // 加粗 **text** -> text trimmed = trimmed.replace(/\*\*(.*?)\*\*/g, '$1'); // 代码 `text` -> text trimmed = trimmed.replace(/`([^`]+)`/g, '$1'); // 3. 块级元素处理 // 标题 ### Title if (trimmed.startsWith('###')) { if (inList) { html += '\n'; inList = false; } html += `

${trimmed.replace(/^###\s*/, '')}

`; } else if (trimmed.startsWith('##')) { if (inList) { html += '\n'; inList = false; } html += `

${trimmed.replace(/^##\s*/, '')}

`; } else if (trimmed.startsWith('#')) { if (inList) { html += '\n'; inList = false; } html += `

${trimmed.replace(/^#\s*/, '')}

`; } // 列表 - Item 或 * Item else if (trimmed.startsWith('- ') || trimmed.startsWith('* ')) { if (!inList) { html += '\n'; } return html; }; createApp({ setup() { const apiBase = ref(window.location.origin || "http://localhost:8000"); // Douyin State const shareText = ref(''); const douyinLoading = ref(false); const douyinRecords = ref([]); let douyinTimer = null; // Douyin Summary State const showSummaryDialog = ref(false); const summaryLoading = ref(false); const summaryText = ref(''); // 使用全局渲染器 const renderedSummary = computed(() => { if (!summaryText.value) return ''; const html = globalRenderMarkdown(summaryText.value); return html; }); // 这里的配置仍然保留,作为双重保险 if (typeof marked !== 'undefined') { const m = (typeof marked.marked === 'function') ? marked.marked : marked; if (m.setOptions) { m.setOptions({ gfm: true, breaks: true, mangle: false, headerIds: false }); } } // Methods const startParsing = async () => { if (!shareText.value.trim()) return; douyinLoading.value = true; try { const response = await axios.post(apiBase.value + '/api/parse', { text: shareText.value }); if (response.data.id || (response.data.ids && response.data.ids.length > 0)) { shareText.value = ''; fetchDouyinRecords(); if (typeof ElementPlus !== 'undefined') { const count = response.data.ids ? response.data.ids.length : 1; ElementPlus.ElMessage.success(`成功提交 ${count} 个解析任务`); } } } catch (error) { console.error('Error:', error); if (typeof ElementPlus !== 'undefined') ElementPlus.ElMessage.error('解析请求失败'); } finally { douyinLoading.value = false; } }; const fetchDouyinRecords = async (isManual) => { try { const response = await axios.get(apiBase.value + '/api/records'); const newRecords = response.data; douyinRecords.value = newRecords.map(newRec => { const oldRec = douyinRecords.value.find(r => r.id === newRec.id); return { ...newRec, expanded: oldRec ? oldRec.expanded : false, showOriginal: oldRec ? oldRec.showOriginal : false }; }); if (isManual === true || (isManual && isManual.type === 'click')) { if (typeof ElementPlus !== 'undefined') ElementPlus.ElMessage.success('列表已刷新'); } } catch (error) { console.error('Error fetching records:', error); if (isManual === true || (isManual && isManual.type === 'click')) { if (typeof ElementPlus !== 'undefined') ElementPlus.ElMessage.error('刷新失败'); } } }; const deleteRecord = async (id) => { if (!confirm('确定要删除这条记录吗?')) return; try { await axios.delete(apiBase.value + `/api/records/${id}`); fetchDouyinRecords(); if (typeof ElementPlus !== 'undefined') ElementPlus.ElMessage.success('删除成功'); } catch (error) { console.error('Error deleting:', error); if (typeof ElementPlus !== 'undefined') ElementPlus.ElMessage.error('删除失败'); } }; const openSummaryDialog = () => { showSummaryDialog.value = true; if (!summaryText.value) { fetchDouyinSummary(); } }; const handleSummaryClose = () => { showSummaryDialog.value = false; }; const fetchDouyinSummary = async () => { if (summaryLoading.value) return; summaryText.value = ""; summaryLoading.value = true; try { const response = await fetch(apiBase.value + "/api/douyin/summary", { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ids: []}) }); if (!response.body) return; const reader = response.body.getReader(); const decoder = new TextDecoder(); while (true) { const {done, value} = await reader.read(); if (done) break; const chunk = decoder.decode(value, {stream: true}); summaryText.value += chunk; } } catch (e) { console.error(e); summaryText.value += "\n(总结过程出错: " + e.message + ")"; } finally { summaryLoading.value = false; } }; const statusColor = (status) => { switch(status) { case 'COMPLETED': return '#10b981'; case 'FAILED': return '#ef4444'; case 'PROCESSING': return '#3b82f6'; default: return '#9ca3af'; } }; const statusType = (status) => { switch(status) { case 'COMPLETED': return 'success'; case 'FAILED': return 'danger'; case 'PROCESSING': return 'primary'; default: return 'info'; } }; const formatDate = (dateStr) => { if (!dateStr) return ''; return new Date(dateStr).toLocaleString(); }; onMounted(() => { fetchDouyinRecords(); if (douyinTimer) clearInterval(douyinTimer); douyinTimer = setInterval(() => { fetchDouyinRecords(); }, 3000); }); return { shareText, douyinLoading, douyinRecords, startParsing, fetchDouyinRecords, deleteRecord, statusColor, statusType, formatDate, showSummaryDialog, summaryLoading, summaryText, renderedSummary, openSummaryDialog, handleSummaryClose, fetchDouyinSummary }; } }).use(ElementPlus).mount("#app");