Add platform config website_url, admin tab, fix writer DB config fallback, add trigger endpoints

This commit is contained in:
Yuzhiran Dev
2026-05-20 09:38:56 +08:00
parent 55e5f3d166
commit 498165440f
84 changed files with 1988 additions and 242 deletions
+26 -11
View File
@@ -8,6 +8,9 @@
<link rel="stylesheet" href="theme-modern.css">
<style>
.selected-count { color: var(--color-text-secondary); font-size: var(--font-size-body); margin-left: auto; }
.preview-dialog-custom .el-dialog__body { overflow-y: auto; max-height: calc(90vh - 120px); padding: 16px 20px; }
.preview-dialog-custom .preview-body { max-height: none !important; min-height: 300px; overflow: visible; }
.preview-dialog-custom .preview-body img { max-width: 100%; }
.topic-card-list { display: none; }
@media (max-width: 768px) {
.el-table { display: none; }
@@ -50,7 +53,7 @@
<el-button size="default" :type="filterStatus === 'ready' ? 'primary' : ''" @click="filterStatus = 'ready'; fetchTopics()">待发布 ({{ statusStats.ready }})</el-button>
<el-button size="default" :type="filterStatus === 'published' ? 'primary' : ''" @click="filterStatus = 'published'; fetchTopics()">已发布 ({{ statusStats.published }})</el-button>
</div>
<el-table :data="paginatedTopics" stripe v-loading="loadingTable" @selection-change="selectedTopicIds = $event.map(item => item.id)">
<el-table ref="topicTable" :data="paginatedTopics" stripe v-loading="loadingTable" @selection-change="selectedTopicIds = $event.map(item => item.id)">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="id" label="ID" width="70" fixed></el-table-column>
<el-table-column prop="title" label="标题" min-width="200"></el-table-column>
@@ -160,7 +163,7 @@
</div>
</div>
<div style="width:100%;">
<div v-if="!editing" class="preview-body" style="min-height:300px; height:68vh; border:1px solid #ebeef5; border-radius:8px; background:#fff; padding:20px; overflow-y:auto; width:100%; line-height:1.8;" v-html="currentPreviewBody"></div>
<div v-if="!editing" class="preview-body" style="border:1px solid #ebeef5; border-radius:8px; background:#fff; padding:20px; width:100%; line-height:1.8;" v-html="currentPreviewBody"></div>
<div v-else ref="editor" contenteditable="true" style="min-height:300px; height:68vh; border:1px solid #409eff; border-radius:8px; background:#fff; padding:20px; overflow-y:auto; width:100%; line-height:1.8; outline:none; box-shadow:0 0 0 2px rgba(64,158,255,0.2);" @input="editContent = $event.target.innerHTML"></div>
</div>
</div>
@@ -237,11 +240,13 @@ const TopicsApp = {
currentPreviewBody() {
const html = this.platformContents[this.previewPlatform];
if (!html) return '';
if (html.length < 50) return '<div style="padding:40px;text-align:center;color:#909399;"><p style="font-size:36px;margin:0 0 12px;">📝</p><p>该平台内容暂未生成或内容为空</p><p style="font-size:13px;margin-top:8px;">可点击上方「批量创作」重新生成,或在编辑模式下手动添加内容</p></div>';
try {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const body = doc.body;
if (!body) return html;
if (body.innerHTML.length < 30) return '<div style="padding:40px;text-align:center;color:#909399;"><p style="font-size:36px;margin:0 0 12px;">📝</p><p>内容为空,请编辑或重新生成</p></div>';
body.querySelectorAll('script, nav, .header, .tags, footer, .interaction').forEach(el => el.remove());
return body.innerHTML;
} catch (e) { console.error('解析 HTML 失败:', e); return html; }
@@ -340,8 +345,18 @@ const TopicsApp = {
},
platformName(platform) { return { zhihu: '知乎', wechat: '微信公众号', xiaohongshu: '小红书' }[platform] || platform; },
copyContent(platform) {
if (!this.previewTopic || !this.previewTopic.content) { this.$message.warning('暂无内容可复制'); return; }
navigator.clipboard.writeText(`标题:${this.previewTopic.title}\n\n内容:\n${this.previewTopic.content}`)
const html = this.platformContents[platform];
if (!html) { this.$message.warning('暂无内容可复制'); return; }
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const titleEl = doc.querySelector('h1');
const title = titleEl ? titleEl.textContent.trim() : (this.previewTopic?.title || '');
const body = doc.body;
if (body) {
body.querySelectorAll('script, nav, .header, .tags, footer, .interaction').forEach(el => el.remove());
}
const text = body ? body.textContent.trim() : html.replace(/<[^>]+>/g, '').trim();
navigator.clipboard.writeText(`标题:${title}\n\n内容:\n${text}`)
.then(() => this.$message.success(`已复制内容,请前往${platform}粘贴发布`))
.catch(() => this.$message.error('复制失败,请手动复制'));
},
@@ -433,13 +448,12 @@ const TopicsApp = {
}
},
toggleSelectAll() {
const visible = this.filteredTopics.map(t => t.id);
const allChecked = visible.every(id => this.selectedTopicIds.includes(id));
if (allChecked) {
this.selectedTopicIds = this.selectedTopicIds.filter(id => !visible.includes(id));
} else {
const existing = new Set(this.selectedTopicIds);
for (const id of visible) { if (!existing.has(id)) { this.selectedTopicIds.push(id); } }
const table = this.$refs.topicTable;
if (!table) return;
const visible = this.filteredTopics;
const allChecked = visible.every(t => this.selectedTopicIds.includes(t.id));
for (const row of visible) {
table.toggleRowSelection(row, !allChecked);
}
},
formatDate(dateStr) {
@@ -467,5 +481,6 @@ if (window.installIcons) { window.installIcons(app); }
if (window.installUniNav) { window.installUniNav(app); }
app.mount('#app');
</script>
<script src="ai-assistant.js"></script>
</body>
</html>