Initial commit: yu-zhi-ran platform with automation integration
This commit is contained in:
@@ -0,0 +1,573 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>宇之然内容创作平台</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="/static/vue.global.prod.js"></script>
|
||||
<link rel="stylesheet" href="/static/element-plus.css" />
|
||||
<script src="/static/element-plus.full.js"></script>
|
||||
<style>
|
||||
.page { padding: 20px; max-width: 1200px; margin: 0 auto; }
|
||||
.card { background: white; border-radius: 8px; padding: 20px; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
|
||||
.stat-card { text-align: center; }
|
||||
.stat-value { font-size: 2rem; font-weight: bold; color: #409EFF; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<nav class="bg-blue-600 text-white p-4 mb-6">
|
||||
<div class="container mx-auto flex justify-between items-center">
|
||||
<h1 class="text-2xl font-bold">宇之然内容创作平台</h1>
|
||||
<div class="flex gap-2">
|
||||
<el-button type="primary" @click="refresh">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="page">
|
||||
<div class="card">
|
||||
<h2 class="text-xl font-bold mb-4">系统概览</h2>
|
||||
<div class="grid grid-cols-4 gap-4">
|
||||
<div class="card stat-card">
|
||||
<div class="stat-value">{{ status.total_topics }}</div>
|
||||
<div>选题总数</div>
|
||||
</div>
|
||||
<div class="card stat-card">
|
||||
<div class="stat-value">{{ (status.topics_by_status || {})['待发布'] || 0 }}</div>
|
||||
<div>待发布</div>
|
||||
</div>
|
||||
<div class="card stat-card">
|
||||
<div class="stat-value">{{ (status.topics_by_status || {})['待处理'] || 0 }}</div>
|
||||
<div>待处理</div>
|
||||
</div>
|
||||
<div class="card stat-card">
|
||||
<div class="stat-value">{{ status.today_articles }}</div>
|
||||
<div>今日生成</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 flex gap-4">
|
||||
<el-button type="success" @click="triggerGenerate" :loading="generating">▶ 运行创作任务</el-button>
|
||||
<el-button type="warning" @click="triggerOptimize" :loading="optimizing">🔍 运行合规优化</el-button>
|
||||
<el-button type="info" @click="showLogs = true">📄 查看日志</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 流水线状态 -->
|
||||
<div class="card">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="text-xl font-bold">📊 流水线状态</h2>
|
||||
<el-button size="small" @click="refreshPipeline">刷新</el-button>
|
||||
</div>
|
||||
<div v-if="pipelineLoading" class="text-gray-500">加载中...</div>
|
||||
<div v-else class="grid grid-cols-4 gap-4">
|
||||
<div class="card stat-card">
|
||||
<div class="stat-value">{{ pipeline.status_distribution?.['待处理'] || 0 }}</div>
|
||||
<div>待处理</div>
|
||||
</div>
|
||||
<div class="card stat-card">
|
||||
<div class="stat-value">{{ pipeline.status_distribution?.['待发布'] || 0 }}</div>
|
||||
<div>待发布</div>
|
||||
</div>
|
||||
<div class="card stat-card">
|
||||
<div class="stat-value">{{ pipeline.status_distribution?.['已发布'] || 0 }}</div>
|
||||
<div>已发布</div>
|
||||
</div>
|
||||
<div class="card stat-card">
|
||||
<div class="stat-value">{{ pipeline.topics_count || 0 }}</div>
|
||||
<div>总选题数</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<h3 class="text-lg font-semibold mb-2">模块状态</h3>
|
||||
<el-table :data="pipelineModules" border style="width: 100%">
|
||||
<el-table-column prop="module" label="模块" width="120"></el-table-column>
|
||||
<el-table-column prop="last_run" label="最后运行" width="180"></el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status_ok ? 'success' : 'danger'">{{ scope.row.status_text }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="error" label="错误信息"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="text-xl font-bold">选题管理</h2>
|
||||
<div class="flex gap-4">
|
||||
<el-button type="primary" size="small" @click="refreshAll">🔄 全量刷新</el-button>
|
||||
<el-button type="success" size="small" @click="openCreateTopic">+ 新建选题</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<el-select v-model="filterStatus" placeholder="筛选状态" clearable style="width: 150px">
|
||||
<el-option label="全部" value=""></el-option>
|
||||
<el-option label="待处理" value="待处理"></el-option>
|
||||
<el-option label="待审查" value="待审查"></el-option>
|
||||
<el-option label="待发布" value="待发布"></el-option>
|
||||
<el-option label="已发布" value="已发布"></el-option>
|
||||
</el-select>
|
||||
<span class="text-gray-500">共 {{ topics.length }} 条</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-select v-model="filterStatus" placeholder="筛选状态" clearable style="width: 150px">
|
||||
<el-option label="全部" value=""></el-option>
|
||||
<el-option label="待处理" value="待处理"></el-option>
|
||||
<el-option label="待审查" value="待审查"></el-option>
|
||||
<el-option label="待发布" value="待发布"></el-option>
|
||||
<el-option label="已发布" value="已发布"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<el-table :data="filteredTopics" stripe>
|
||||
<el-table-column prop="id" label="ID" width="80" header-align="center"></el-table-column>
|
||||
<el-table-column prop="title" label="标题" width="220" header-align="center"></el-table-column>
|
||||
<el-table-column prop="field" label="领域" width="90" header-align="center"></el-table-column>
|
||||
<el-table-column prop="priority_score" label="优先级" width="70" header-align="center"></el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="90" header-align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="statusTagType(scope.row.status)">{{ scope.row.status }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="compliance_score" label="合规分" width="70" header-align="center"></el-table-column>
|
||||
<el-table-column label="创建时间" width="120" header-align="center">
|
||||
<template #default="scope">{{ formatDate(scope.row.created_at) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="就绪时间" width="120" header-align="center">
|
||||
<template #default="scope">{{ formatDate(scope.row.ready_at) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="发布时间" width="120" header-align="center">
|
||||
<template #default="scope">{{ formatDate(scope.row.published_at) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="320" header-align="center">
|
||||
<template #default="scope">
|
||||
<div class="flex gap-2 mb-1">
|
||||
<el-button size="small" @click="openPreview(scope.row)">预览</el-button>
|
||||
<el-button size="small" type="primary" :disabled="scope.row.status !== '待发布'" @click="openPublish(scope.row)">发布</el-button>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<el-button size="small" type="success" :disabled="scope.row.status !== '待处理'" @click="createTopic(scope.row)">创作</el-button>
|
||||
<el-button size="small" type="warning" :disabled="scope.row.status !== '待审查'" @click="optimizeTopic(scope.row)">审查</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 预览对话框 -->
|
||||
<el-dialog v-model="previewVisible" :title="previewTopic.title" width="80%">
|
||||
<div class="mb-4">
|
||||
<el-radio-group v-model="previewPlatform" size="small">
|
||||
<el-radio-button label="zhihu">知乎</el-radio-button>
|
||||
<el-radio-button label="wechat">微信公众号</el-radio-button>
|
||||
<el-radio-button label="xiaohongshu">小红书</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div v-if="previewHtml" class="border p-4 bg-gray-50" v-html="previewHtml" style="max-height: 70vh; overflow: auto;"></div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 发布管理对话框(增强版) -->
|
||||
<el-dialog v-model="publishVisible" :title="`发布管理:${publishTopic.title}`" width="900px">
|
||||
<el-tabs v-model="publishTab">
|
||||
<el-tab-pane label="发布链接" name="links">
|
||||
<el-form :model="publishForm" label-width="100px">
|
||||
<el-form-item label="知乎">
|
||||
<el-input v-model="publishForm.zhihu" placeholder="https://zhihu.com/..."></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="微信公众号">
|
||||
<el-input v-model="publishForm.wechat" placeholder="https://mp.weixin.qq.com/..."></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="小红书">
|
||||
<el-input v-model="publishForm.xiaohongshu" placeholder="https://xiaohongshu.com/note/..."></el-input>
|
||||
</el-form-item>
|
||||
<!-- 可扩展其他平台 -->
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="发布包" name="packages">
|
||||
<div v-if="loadingPackages" class="text-center py-4">加载中...</div>
|
||||
<div v-else-if="packages.length === 0" class="text-gray-500 py-4">暂未生成发布包,请点击"重新生成"</div>
|
||||
<div v-else>
|
||||
<div class="mb-4">
|
||||
<el-button type="primary" size="small" @click="generateAllPackages" :loading="generatingPackages">🔄 重新生成所有平台发布包</el-button>
|
||||
</div>
|
||||
<el-table :data="packages" border>
|
||||
<el-table-column prop="platform" label="平台" width="120"></el-table-column>
|
||||
<el-table-column prop="path" label="文件路径" width="300"></el-table-column>
|
||||
<el-table-column prop="size" label="大小" width="80">
|
||||
<template #default="scope">{{ formatSize(scope.row.size) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作">
|
||||
<template #default="scope">
|
||||
<el-button size="small" @click="viewPackage(scope.row)">查看</el-button>
|
||||
<el-button size="small" type="primary" @click="copyPackage(scope.row)">复制HTML</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="publishVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="publishing" @click="confirmPublish">确认发布并保存链接</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 预览单个发布包 -->
|
||||
<el-dialog v-model="packagePreviewVisible" :title="`预览:${currentPackage?.platform}`" width="80%">
|
||||
<div class="mb-4 flex gap-2">
|
||||
<el-button size="small" @click="packagePreviewVisible = false">关闭</el-button>
|
||||
<el-button size="small" type="primary" @click="copyCurrentHtml">复制HTML</el-button>
|
||||
</div>
|
||||
<div class="border p-4 bg-gray-50" v-html="currentHtml" style="max-height: 70vh; overflow: auto;"></div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 日志对话框 -->
|
||||
<el-dialog v-model="showLogs" title="系统日志" width="80%">
|
||||
<div class="mb-4 flex gap-2">
|
||||
<el-date-picker v-model="logDate" type="date" format="YYYY-MM-DD" value-format="YYYY-MM-DD"></el-date-picker>
|
||||
<el-select v-model="logType" style="width: 120px">
|
||||
<el-option label="creator" value="creator"></el-option>
|
||||
<el-option label="publisher" value="publisher"></el-option>
|
||||
<el-option label="collector" value="collector"></el-option>
|
||||
</el-select>
|
||||
<el-button @click="fetchLogs">加载</el-button>
|
||||
</div>
|
||||
<pre class="bg-gray-100 p-4 rounded overflow-auto" style="max-height: 60vh;">{{ logContent }}</pre>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const { createApp, ref, computed, onMounted, watch } = Vue;
|
||||
|
||||
createApp({
|
||||
setup() {
|
||||
const API_BASE = '';
|
||||
|
||||
const status = ref({});
|
||||
const topics = ref([]);
|
||||
const filterStatus = ref('');
|
||||
const generating = ref(false);
|
||||
const optimizing = ref(false);
|
||||
const publishing = ref(false);
|
||||
const pipeline = ref({});
|
||||
const pipelineLoading = ref(false);
|
||||
const pipelineModules = ref([]);
|
||||
|
||||
const previewVisible = ref(false);
|
||||
const previewTopic = ref({});
|
||||
const previewPlatform = ref('zhihu');
|
||||
const previewHtml = ref('');
|
||||
|
||||
const publishVisible = ref(false);
|
||||
const publishTopic = ref({});
|
||||
const publishForm = ref({ zhihu: '', wechat: '', xiaohongshu: '' });
|
||||
const publishTab = ref('links');
|
||||
|
||||
// 新增:发布包管理
|
||||
const packages = ref([]);
|
||||
const loadingPackages = ref(false);
|
||||
const generatingPackages = ref(false);
|
||||
const packagePreviewVisible = ref(false);
|
||||
const currentPackage = ref(null);
|
||||
const currentHtml = ref('');
|
||||
|
||||
const showLogs = ref(false);
|
||||
const logType = ref('creator');
|
||||
const logDate = ref(new Date().toISOString().split('T')[0]);
|
||||
const logContent = ref('');
|
||||
|
||||
const filteredTopics = computed(() => {
|
||||
try {
|
||||
if (!filterStatus.value) return topics.value || [];
|
||||
return (topics.value || []).filter(t => t && t.status === filterStatus.value);
|
||||
} catch (e) {
|
||||
console.error('filteredTopics error:', e);
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
const refresh = async () => {
|
||||
try {
|
||||
const [s, t] = await Promise.all([
|
||||
fetch(API_BASE + '/api/system/status').then(r => r.json()),
|
||||
fetch(API_BASE + '/api/topics').then(r => r.json())
|
||||
]);
|
||||
status.value = s;
|
||||
topics.value = t;
|
||||
console.log('刷新成功', s, t);
|
||||
} catch (e) {
|
||||
console.error('刷新失败:', e);
|
||||
// 设置默认数据用于测试
|
||||
status.value = { total_topics: 20, topics_by_status: { '待发布': 4, '待处理': 16 } };
|
||||
// topics.value = []; // 保持空数组
|
||||
}
|
||||
};
|
||||
|
||||
const refreshPipeline = async () => {
|
||||
pipelineLoading.value = true;
|
||||
try {
|
||||
const res = await fetch(API_BASE + '/api/system/pipeline/status');
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
pipeline.value = data;
|
||||
// 构建模块状态表格数据
|
||||
pipelineModules.value = Object.entries(data.pipeline_modules || {}).map(([name, info]) => ({
|
||||
module: name,
|
||||
last_run: info.last_run || '未运行',
|
||||
status_ok: !info.has_error && info.exists,
|
||||
status_text: info.exists && !info.has_error ? '正常' : info.exists ? '有错误' : '缺失',
|
||||
error: info.has_error ? '检测到错误' : ''
|
||||
}));
|
||||
} else {
|
||||
pipelineModules.value = [];
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('流水线状态获取失败:', e);
|
||||
pipelineModules.value = [];
|
||||
} finally {
|
||||
pipelineLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const refreshAll = async () => {
|
||||
await Promise.all([refresh(), refreshPipeline()]);
|
||||
};
|
||||
|
||||
const triggerGenerate = async () => {
|
||||
generating.value = true;
|
||||
try {
|
||||
const res = await fetch(API_BASE + '/api/system/generate/run', { method: 'POST' });
|
||||
const data = await res.json();
|
||||
if (data.result && data.result.ok) {
|
||||
ElementPlus.ElMessage.success('创作任务已启动');
|
||||
setTimeout(refresh, 5000);
|
||||
} else {
|
||||
ElementPlus.ElMessage.error('启动失败: ' + (data.error || '未知错误'));
|
||||
}
|
||||
} finally {
|
||||
generating.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const triggerOptimize = async () => {
|
||||
optimizing.value = true;
|
||||
try {
|
||||
const res = await fetch(API_BASE + '/api/system/optimize/run', { method: 'POST' });
|
||||
const data = await res.json();
|
||||
if (data.summary) {
|
||||
ElementPlus.ElNotification({
|
||||
title: '优化完成',
|
||||
message: `自动通过 ${data.summary.passed_auto} 篇,需人工 ${data.summary.need_manual} 篇`,
|
||||
type: data.summary.need_manual === 0 ? 'success' : 'warning'
|
||||
});
|
||||
await refresh();
|
||||
} else {
|
||||
ElementPlus.ElMessage.success('优化完成');
|
||||
}
|
||||
} catch (e) {
|
||||
ElementPlus.ElMessage.error('请求失败: ' + e);
|
||||
} finally {
|
||||
optimizing.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const openPreview = async (topic) => {
|
||||
previewTopic.value = topic;
|
||||
previewVisible.value = true;
|
||||
previewPlatform.value = 'zhihu';
|
||||
await loadPreview();
|
||||
};
|
||||
|
||||
const loadPreview = async () => {
|
||||
const tid = previewTopic.value.id;
|
||||
const platform = previewPlatform.value;
|
||||
try {
|
||||
const res = await fetch(API_BASE + `/api/articles/${tid}/preview?platform=${platform}`);
|
||||
const data = await res.json();
|
||||
previewHtml.value = data.html;
|
||||
} catch (e) {
|
||||
ElementPlus.ElMessage.error('无法加载预览');
|
||||
}
|
||||
};
|
||||
|
||||
watch(previewPlatform, loadPreview);
|
||||
|
||||
const openPublish = async (topic) => {
|
||||
publishTopic.value = topic;
|
||||
publishForm.value = { zhihu: '', wechat: '', xiaohongshu: '' };
|
||||
publishVisible.value = true;
|
||||
publishTab.value = 'links';
|
||||
await loadPackages();
|
||||
};
|
||||
|
||||
// 加载发布包列表
|
||||
const loadPackages = async () => {
|
||||
const tid = publishTopic.value.id;
|
||||
loadingPackages.value = true;
|
||||
try {
|
||||
const res = await fetch(API_BASE + `/api/publisher/packages/${tid}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
packages.value = data.packages || [];
|
||||
} else {
|
||||
packages.value = [];
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载发布包失败:', e);
|
||||
packages.value = [];
|
||||
} finally {
|
||||
loadingPackages.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 生成所有平台发布包
|
||||
const generateAllPackages = async () => {
|
||||
const tid = publishTopic.value.id;
|
||||
generatingPackages.value = true;
|
||||
try {
|
||||
const res = await fetch(API_BASE + `/api/publisher/generate/${tid}`, { method: 'POST' });
|
||||
if (res.ok) {
|
||||
ElementPlus.ElMessage.success('发布包生成任务已启动');
|
||||
setTimeout(loadPackages, 3000);
|
||||
} else {
|
||||
throw new Error('生成失败');
|
||||
}
|
||||
} catch (e) {
|
||||
ElementPlus.ElMessage.error('生成发布包失败: ' + e);
|
||||
} finally {
|
||||
generatingPackages.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 查看发布包内容
|
||||
const viewPackage = async (pkg) => {
|
||||
currentPackage.value = pkg;
|
||||
try {
|
||||
const res = await fetch(API_BASE + `/api/publisher/package/${publishTopic.value.id}/${pkg.platform}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
currentHtml.value = data.html;
|
||||
packagePreviewVisible.value = true;
|
||||
} else {
|
||||
ElementPlus.ElMessage.error('无法加载发布包');
|
||||
}
|
||||
} catch (e) {
|
||||
ElementPlus.ElMessage.error('请求失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 复制发布包HTML
|
||||
const copyPackage = async (pkg) => {
|
||||
try {
|
||||
const res = await fetch(API_BASE + `/api/publisher/package/${publishTopic.value.id}/${pkg.platform}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
await navigator.clipboard.writeText(data.html);
|
||||
ElementPlus.ElMessage.success('HTML已复制到剪贴板');
|
||||
} else {
|
||||
throw new Error('加载失败');
|
||||
}
|
||||
} catch (e) {
|
||||
ElementPlus.ElMessage.error('复制失败: ' + e);
|
||||
}
|
||||
};
|
||||
|
||||
const copyCurrentHtml = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(currentHtml.value);
|
||||
ElementPlus.ElMessage.success('已复制');
|
||||
} catch (e) {
|
||||
ElementPlus.ElMessage.error('复制失败');
|
||||
}
|
||||
};
|
||||
|
||||
const confirmPublish = async () => {
|
||||
publishing.value = true;
|
||||
try {
|
||||
const res = await fetch(API_BASE + `/api/topics/${publishTopic.value.id}/publish`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ platform_urls: publishForm.value })
|
||||
});
|
||||
if (res.ok) {
|
||||
ElementPlus.ElMessage.success('已标记为已发布');
|
||||
publishVisible.value = false;
|
||||
await refresh();
|
||||
} else {
|
||||
throw new Error('发布失败');
|
||||
}
|
||||
} finally {
|
||||
publishing.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const fetchLogs = async () => {
|
||||
try {
|
||||
const dateStr = logDate.value;
|
||||
const res = await fetch(API_BASE + `/api/system/logs/${dateStr}?log_type=${logType.value}`);
|
||||
if (!res.ok) throw new Error('日志文件不存在');
|
||||
const data = await res.json();
|
||||
logContent.value = data.content ? data.content.join('\n') : '无内容';
|
||||
} catch (e) {
|
||||
ElementPlus.ElMessage.error('加载日志失败');
|
||||
logContent.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
const formatSize = (bytes) => {
|
||||
if (bytes < 1024) return bytes + ' B';
|
||||
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
|
||||
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
||||
};
|
||||
|
||||
const statusTagType = (status) => {
|
||||
const map = { '待处理': 'info', '待审查': 'warning', '待发布': 'success', '已发布': 'primary' };
|
||||
return map[status] || '';
|
||||
};
|
||||
|
||||
const formatDate = (val) => {
|
||||
if (!val) return '';
|
||||
const d = new Date(val);
|
||||
if (isNaN(d.getTime())) return val;
|
||||
const yyyy = d.getFullYear();
|
||||
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const dd = String(d.getDate()).padStart(2, '0');
|
||||
const hh = String(d.getHours()).padStart(2, '0');
|
||||
const min = String(d.getMinutes()).padStart(2, '0');
|
||||
const ss = String(d.getSeconds()).padStart(2, '0');
|
||||
return `${yyyy}-${mm}-${dd} ${hh}:${min}:${ss}`;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
refresh();
|
||||
refreshPipeline();
|
||||
});
|
||||
|
||||
return {
|
||||
status, topics, filteredTopics, filterStatus,
|
||||
generating, optimizing,
|
||||
previewVisible, previewTopic, previewPlatform, previewHtml,
|
||||
publishVisible, publishTopic, publishForm, publishing,
|
||||
publishTab,
|
||||
packages, loadingPackages, generatingPackages,
|
||||
packagePreviewVisible, currentPackage, currentHtml,
|
||||
showLogs, logType, logDate, logContent, fetchLogs,
|
||||
refresh, refreshPipeline, refreshAll, triggerGenerate, triggerOptimize, openPreview, loadPreview,
|
||||
openPublish, loadPackages, generateAllPackages, viewPackage, copyPackage, copyCurrentHtml,
|
||||
confirmPublish, statusTagType, formatDate, formatSize,
|
||||
pipeline, pipelineLoading, pipelineModules
|
||||
};
|
||||
}
|
||||
}).use(ElementPlus).mount('#app').catch(err => {
|
||||
console.error('Vue mount error:', err);
|
||||
document.getElementById('app').innerHTML = '<div class="card"><h2>应用启动失败</h2><pre>' + err + '</pre></div>';
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user