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('');
// 降级用的简易 Markdown 解析器
const simpleMarkdown = (text) => {
if (!text) return '';
let lines = text.split('\n');
let html = '';
let inList = false;
const parseInline = (str) => {
return str
.replace(/\*\*(.*?)\*\*/g, '$1')
.replace(/`(.*?)`/g, '$1');
};
for (let line of lines) {
let trimmed = line.trim();
if (!trimmed) {
if (inList) { html += ''; inList = false; }
html += '
';
continue;
}
if (trimmed.startsWith('### ')) {
if (inList) { html += ''; inList = false; }
html += `
${parseInline(trimmed.substring(4))}
`;
} else if (trimmed.startsWith('- ') || /^\d+\./.test(trimmed)) {
if (!inList) { html += ''; inList = true; }
let content = trimmed.replace(/^(- |\d+\. )/, '');
html += `- ${parseInline(content)}
`;
} else {
if (inList) { html += '
'; inList = false; }
html += `${parseInline(trimmed)}
`;
}
}
if (inList) html += '';
return html;
};
// Configure Marked
if (typeof marked !== 'undefined') {
marked.use({
gfm: true,
breaks: true
});
}
// 增强的 Markdown & LaTeX 解析器
const renderMarkdownAndLatex = (text) => {
if (!text) return '';
try {
// 1. 处理 LaTeX (简单替换,先处理 $$ 再处理 $)
let processedText = text;
// 处理块级 LaTeX: $$ ... $$
processedText = processedText.replace(/\$\$\s*([\s\S]*?)\s*\$\$/g, (match, formula) => {
try {
if (typeof katex !== 'undefined') {
return '' + katex.renderToString(formula, { displayMode: true, throwOnError: false }) + '
';
}
return match;
} catch (e) {
return match;
}
});
// 处理行内 LaTeX: $ ... $
processedText = processedText.replace(/\$([^\$\n]+?)\$/g, (match, formula) => {
try {
if (typeof katex !== 'undefined') {
return katex.renderToString(formula, { displayMode: false, throwOnError: false });
}
return match;
} catch (e) {
return match;
}
});
// 2. 使用 marked 解析 Markdown
if (typeof marked !== 'undefined') {
return marked.parse(processedText);
} else {
// 降级使用之前的 simpleMarkdown
return simpleMarkdown(processedText);
}
} catch (e) {
console.error('Markdown/LaTeX rendering error:', e);
return text;
}
};
const renderedSummary = computed(() => {
if (!summaryText.value) return '';
return renderMarkdownAndLatex(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");