更新了update门户站点界面和部分功能

This commit is contained in:
QWQLwToo
2026-06-26 14:30:09 +08:00
parent 57f4d94d0a
commit cd2fd435a2
20 changed files with 1128 additions and 168 deletions
@@ -9,9 +9,9 @@ defineProps<{ ctx: any }>();
<thead><tr><th>类型</th><th>目标</th><th>信息</th><th>IP</th><th>时间</th></tr></thead>
<tbody>
<tr v-for="item in ctx.auditLogs" :key="item.id">
<td>{{ item.type }}</td>
<td><span class="badge neutral">{{ ctx.auditTypeLabel(item.type) }}</span></td>
<td>{{ item.target }}</td>
<td>{{ item.message }}</td>
<td>{{ ctx.auditMessage(item) }}</td>
<td>{{ item.ip || "-" }}</td>
<td>{{ item.createdAt }}</td>
</tr>
@@ -12,7 +12,10 @@ defineProps<{ ctx: any }>();
<td class="mono">{{ item.id || item.sourceId }}</td>
<td>{{ item.category || item.categoryId }}</td>
<td>{{ item.proxyMode }}</td>
<td><span :class="['badge', ctx.statusTone(ctx.endpointStatus(item))]">{{ ctx.endpointStatus(item) }}</span></td>
<td>
<span :class="['badge', ctx.statusTone(ctx.endpointStatus(item))]">{{ ctx.labelStatus(ctx.endpointStatus(item)) }}</span>
<span v-if="ctx.endpointStatus(item) === 'redirected' || item.health?.meta?.redirected" class="badge warn">重定向接口</span>
</td>
<td>{{ item.cacheSeconds || 0 }}s</td>
<td class="hash">{{ item.urlTemplate || item.apiUrl }}</td>
<td><button class="btn ghost compact" @click="ctx.copyEndpointToSource(item)">编辑</button></td>
@@ -1,30 +1,102 @@
<script setup lang="ts">
import { CheckCircle2, Save } from "lucide-vue-next";
import { CheckCircle2, Plus, Save, Trash2 } from "lucide-vue-next";
defineProps<{ ctx: any }>();
</script>
<template>
<section class="split wide-split">
<section class="panel editor-panel">
<div class="section-head">
<section class="panel page-stack">
<div class="section-head">
<div>
<h2>{{ ctx.activeLegacyLabel }}</h2>
<div class="button-row">
<button class="btn ghost" @click="ctx.validateLegacy(ctx.activeLegacyName)"><CheckCircle2 :size="16" />校验</button>
<button class="btn primary" @click="ctx.saveLegacy(ctx.activeLegacyName)"><Save :size="16" />保存发布</button>
</div>
<p class="muted">以当前兼容 JSON 为基板表单保存会合并进原 JSON未知字段保留</p>
</div>
<div class="button-row">
<button class="btn ghost" @click="ctx.validateLegacy(ctx.activeLegacyName)"><CheckCircle2 :size="16" />校验</button>
<button class="btn primary" @click="ctx.saveLegacy(ctx.activeLegacyName)"><Save :size="16" />保存发布</button>
</div>
</div>
<div class="tabs">
<button :class="{ active: ctx.legacyDrafts[ctx.activeLegacyName].tab === 'form' }" @click="ctx.legacyDrafts[ctx.activeLegacyName].tab = 'form'">可视化表单</button>
<button :class="{ active: ctx.legacyDrafts[ctx.activeLegacyName].tab === 'raw' }" @click="ctx.legacyDrafts[ctx.activeLegacyName].tab = 'raw'">Raw JSON</button>
<button :class="{ active: ctx.legacyDrafts[ctx.activeLegacyName].tab === 'preview' }" @click="ctx.legacyDrafts[ctx.activeLegacyName].tab = 'preview'">预览</button>
<button :class="{ active: ctx.legacyDrafts[ctx.activeLegacyName].tab === 'history' }" @click="ctx.legacyDrafts[ctx.activeLegacyName].tab = 'history'">历史版本</button>
</div>
<section v-if="ctx.legacyDrafts[ctx.activeLegacyName].tab === 'form' && ctx.activeLegacyName === 'update-info'" class="form-grid">
<label>版本号<input v-model="ctx.legacyDrafts['update-info'].form.app_version" /></label>
<label>标题<input v-model="ctx.legacyDrafts['update-info'].form.title" /></label>
<label class="wide">公告<textarea v-model="ctx.legacyDrafts['update-info'].form.message" rows="3"></textarea></label>
<label class="wide">下载地址<input v-model="ctx.legacyDrafts['update-info'].form.download_url" /></label>
<label> SHA256<input v-model="ctx.legacyDrafts['update-info'].form.package_sha256" /></label>
<label>包大小<input v-model="ctx.legacyDrafts['update-info'].form.package_size" type="number" /></label>
<label class="wide">发布说明<textarea v-model="ctx.legacyDrafts['update-info'].form.release_notes" rows="4"></textarea></label>
<label class="wide">发布说明 Markdown<textarea v-model="ctx.legacyDrafts['update-info'].form.release_notes_md" rows="5"></textarea></label>
<label class="wide">更新说明 JSON<textarea v-model="ctx.legacyDrafts['update-info'].form.update_notes" class="code-editor mini-editor"></textarea></label>
<label class="wide">上次更新说明 JSON<textarea v-model="ctx.legacyDrafts['update-info'].form.last_update_notes" class="code-editor mini-editor"></textarea></label>
<div class="wide button-row">
<button class="btn ghost" @click="ctx.addUpdateMirror"><Plus :size="16" />新增镜像字段到底稿</button>
<button class="btn" @click="ctx.updateLegacyRawFromForm('update-info')">生成预览 JSON</button>
</div>
</section>
<section v-else-if="ctx.legacyDrafts[ctx.activeLegacyName].tab === 'form'" class="page-stack">
<div class="form-grid">
<label>布局版本<input v-model="ctx.legacyDrafts['media-types'].form.layout_version" /></label>
<label>更新时间<input v-model="ctx.legacyDrafts['media-types'].form.last_updated" /></label>
<label class="wide">UI 配置 JSON<textarea v-model="ctx.legacyDrafts['media-types'].form.ui_config" class="code-editor mini-editor"></textarea></label>
</div>
<div class="button-row">
<button class="btn ghost" @click="ctx.addMediaCategory('media-types')"><Plus :size="16" />新增分类</button>
<button class="btn" @click="ctx.updateLegacyRawFromForm('media-types')">生成预览 JSON</button>
</div>
<section v-for="(cat, cIndex) in ctx.legacyDrafts['media-types'].form.categories" :key="cIndex" class="nested-card">
<div class="section-head">
<h3>分类 {{ cIndex + 1 }}</h3>
<button class="btn ghost compact" @click="ctx.removeItem(ctx.legacyDrafts['media-types'].form.categories, cIndex)"><Trash2 :size="14" />删除</button>
</div>
<div class="form-grid">
<label>ID<input v-model="cat.id" /></label>
<label>名称<input v-model="cat.name" /></label>
<label class="checkbox"><input v-model="cat.enabled" type="checkbox" />启用分类</label>
</div>
<div class="button-row">
<button class="btn ghost compact" @click="ctx.addMediaSubcategory(cat)"><Plus :size="14" />新增子接口</button>
</div>
<section v-for="(sub, sIndex) in cat.subcategories" :key="sIndex" class="nested-card inner">
<div class="section-head">
<h3>{{ sub.name || "子接口" }}</h3>
<button class="btn ghost compact" @click="ctx.removeItem(cat.subcategories, sIndex)"><Trash2 :size="14" />删除</button>
</div>
<div class="form-grid">
<label>ID<input v-model="sub.id" /></label>
<label>名称<input v-model="sub.name" /></label>
<label class="wide">接口 URL<input v-model="sub.api_url" /></label>
<label>缩略图<input v-model="sub.thumbnail_url" /></label>
<label>刷新间隔<input v-model.number="sub.refresh_interval" type="number" /></label>
<label>格式<input v-model="sub.supported_formats" placeholder="json, xml" /></label>
<label class="checkbox"><input v-model="sub.downloadable" type="checkbox" />可下载</label>
<label class="wide">描述<textarea v-model="sub.description" rows="2"></textarea></label>
</div>
</section>
</section>
</section>
<section v-else-if="ctx.legacyDrafts[ctx.activeLegacyName].tab === 'raw'" class="page-stack">
<textarea v-model="ctx.legacyDrafts[ctx.activeLegacyName].raw" class="code-editor"></textarea>
<label>保存备注<input v-model="ctx.legacyDrafts[ctx.activeLegacyName].note" /></label>
</section>
<aside class="panel page-stack">
<h2>预览与历史</h2>
<pre class="json-preview">{{ ctx.pretty(ctx.legacyDrafts[ctx.activeLegacyName].preview) }}</pre>
<div class="revision-list">
<button v-for="revision in ctx.legacyDocuments[ctx.activeLegacyName]?.revisions || []" :key="revision.id" @click="ctx.restoreLegacy(ctx.activeLegacyName, revision.id)">
#{{ revision.id }} {{ revision.createdAt }}<small>{{ revision.note || "无备注" }}</small>
</button>
</div>
</aside>
<section v-else-if="ctx.legacyDrafts[ctx.activeLegacyName].tab === 'preview'">
<pre class="json-preview tall">{{ ctx.pretty(ctx.legacyDrafts[ctx.activeLegacyName].preview) }}</pre>
</section>
<section v-else class="revision-list">
<button v-for="revision in ctx.legacyDocuments[ctx.activeLegacyName]?.revisions || []" :key="revision.id" @click="ctx.restoreLegacy(ctx.activeLegacyName, revision.id)">
#{{ revision.id }} {{ revision.createdAt }}<small>{{ revision.note || "无备注" }}</small>
</button>
<div v-if="(ctx.legacyDocuments[ctx.activeLegacyName]?.revisions || []).length === 0" class="empty-state compact">暂无历史版本</div>
</section>
</section>
</template>
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { CheckCircle2, Save } from "lucide-vue-next";
import { CheckCircle2, Save, UploadCloud } from "lucide-vue-next";
defineProps<{ ctx: any }>();
</script>
@@ -9,8 +9,24 @@ defineProps<{ ctx: any }>();
<section class="panel page-stack">
<div class="section-head">
<h2>发布包</h2>
<a href="/update-info.json" target="_blank">查看旧版 update-info.json</a>
<span class="badge">{{ ctx.releasePackages.length }} 个文件</span>
</div>
<section class="nested-card upload-card">
<div class="section-head">
<h3>上传最新版本包</h3>
<span class="badge neutral">保存到下载目录</span>
</div>
<div class="form-grid">
<label class="wide">安装包<input type="file" accept=".exe,.msix,.appinstaller,.msi,.zip,.7z" @change="ctx.onPackageSelected" /></label>
<label>版本号<input v-model="ctx.uploadDraft.version" placeholder="2.0.6.31" /></label>
<label>平台<select v-model="ctx.uploadDraft.platform"><option value="windows">Windows</option><option value="linux">Linux</option></select></label>
<label>架构<select v-model="ctx.uploadDraft.arch"><option value="x64">x64</option><option value="x86">x86</option><option value="arm64">arm64</option></select></label>
<label>通道<select v-model="ctx.uploadDraft.channel"><option value="stable">stable</option><option value="beta">beta</option></select></label>
<label class="wide">发布说明<textarea v-model="ctx.uploadDraft.notes" rows="3"></textarea></label>
<label class="checkbox wide"><input v-model="ctx.uploadDraft.updateManifest" type="checkbox" />上传后同步更新兼容 update-info.json</label>
</div>
<button class="btn primary" @click="ctx.uploadPackage"><UploadCloud :size="16" />上传发布包</button>
</section>
<table>
<thead><tr><th>文件</th><th>版本</th><th>平台</th><th>大小</th><th>SHA256</th></tr></thead>
<tbody>