const { createApp, ref, computed, onMounted } = Vue; 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(''); // Simple Markdown Parser (Zero Dependency) const simpleMarkdown = (text) => { if (!text) return ''; let lines = text.split('\n'); let html = ''; let inList = false; // Helper: Parse inline styles const parseInline = (str) => { return str .replace(/\*\*(.*?)\*\*/g, '$1') // Bold .replace(/`(.*?)`/g, '$1'); // Code }; for (let line of lines) { let trimmed = line.trim(); if (!trimmed) continue; // Headers if (trimmed.startsWith('### ')) { if (inList) { html += ''; inList = false; } html += `

${parseInline(trimmed.substring(4))}

`; } // Lists else if (trimmed.startsWith('- ') || /^\d+\./.test(trimmed)) { if (!inList) { html += ''; inList = false; } html += `

${parseInline(trimmed)}

`; } } if (inList) html += ''; return html; }; const renderedSummary = computed(() => { if (!summaryText.value) return ''; try { return simpleMarkdown(summaryText.value); } catch (e) { console.error("Simple markdown error:", e); return summaryText.value; } }); // 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");