feat(update): 下载页展示历史版本 + 控制台新增下载页链接入口

- 下载页支持展开查看/下载全部曾发布过的历史版本 APK,不再只显示最新版本
- 版本管理页工具栏新增"复制对外下载页链接"按钮,方便管理员查找和分享对外下载页地址

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
这个提交包含在:
XuqmGroup 2026-07-04 02:13:16 +08:00
父节点 e346043b53
当前提交 24ac4cbc08
共有 3 个文件被更改,包括 102 次插入0 次删除

查看文件

@ -241,6 +241,15 @@ export interface UnifiedReleaseManifest {
rnBundles: UnifiedRnUploadItem[] rnBundles: UnifiedRnUploadItem[]
} }
export interface DownloadPageHistoryItem {
versionName: string
versionCode: number
downloadUrl?: string
changeLog?: string
publishedAt: string
current: boolean
}
export interface DownloadPageAppInfo { export interface DownloadPageAppInfo {
versionName: string versionName: string
versionCode: number versionCode: number
@ -249,6 +258,7 @@ export interface DownloadPageAppInfo {
storeUrl?: string storeUrl?: string
storeLinks?: Record<string, string> storeLinks?: Record<string, string>
publishedAt: string publishedAt: string
history?: DownloadPageHistoryItem[]
} }
export interface DownloadPageInfo { export interface DownloadPageInfo {

查看文件

@ -51,6 +51,22 @@
rel="noopener" rel="noopener"
>{{ link.label }}</a> >{{ link.label }}</a>
</div> </div>
<div v-if="section.key === 'android' && olderHistory.length" class="history">
<button class="history-toggle" @click="historyOpen = !historyOpen">
历史版本{{ olderHistory.length }}{{ historyOpen ? '收起' : '展开' }}
</button>
<div v-if="historyOpen" class="history-list">
<div v-for="item in olderHistory" :key="item.versionCode" class="history-item">
<div class="history-item-info">
<span class="version-name">{{ item.versionName }}</span>
<span class="version-code">{{ item.versionCode }}</span>
<span v-if="item.publishedAt" class="history-date">{{ formatDate(item.publishedAt) }}</span>
</div>
<a v-if="item.downloadUrl" class="btn btn-secondary btn-small" :href="item.downloadUrl">下载</a>
</div>
</div>
</div>
</template> </template>
<div v-else class="empty">当前应用无可用安装包</div> <div v-else class="empty">当前应用无可用安装包</div>
@ -145,6 +161,20 @@ const otherStoreLinks = computed(() => {
})) }))
}) })
const historyOpen = ref(false)
const olderHistory = computed(() => {
const history = data.value?.android?.history
if (!history) return []
return history.filter((item) => !item.current)
})
function formatDate(iso: string): string {
const d = new Date(iso)
if (Number.isNaN(d.getTime())) return ''
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
}
onMounted(async () => { onMounted(async () => {
if (!appKey) { if (!appKey) {
loadError.value = '缺少应用标识' loadError.value = '缺少应用标识'
@ -258,4 +288,51 @@ onMounted(async () => {
color: #999; color: #999;
font-size: 14px; font-size: 14px;
} }
.history {
margin-top: 16px;
border-top: 1px solid #eee;
padding-top: 12px;
}
.history-toggle {
background: none;
border: none;
color: #409eff;
font-size: 13px;
cursor: pointer;
padding: 0;
}
.history-list {
margin-top: 10px;
display: flex;
flex-direction: column;
gap: 10px;
}
.history-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
font-size: 13px;
}
.history-item-info {
display: flex;
flex-direction: column;
gap: 2px;
}
.history-date {
color: #999;
font-size: 12px;
}
.btn-small {
padding: 6px 14px;
font-size: 13px;
flex-shrink: 0;
}
</style> </style>

查看文件

@ -47,6 +47,7 @@
</el-radio-group> </el-radio-group>
<el-button type="primary" @click="openUploadAppDialog" :disabled="!platformPackageName">上传新版本</el-button> <el-button type="primary" @click="openUploadAppDialog" :disabled="!platformPackageName">上传新版本</el-button>
<el-button @click="loadAppVersions" :loading="loadingApp">刷新</el-button> <el-button @click="loadAppVersions" :loading="loadingApp">刷新</el-button>
<el-button @click="copyDownloadPageLink">复制对外下载页链接</el-button>
</div> </div>
<el-alert <el-alert
v-if="!platformPackageName" v-if="!platformPackageName"
@ -1033,6 +1034,20 @@ const showActivationDialog = ref(false)
const activationReason = ref('') const activationReason = ref('')
const submittingActivation = ref(false) const submittingActivation = ref(false)
async function copyDownloadPageLink() {
if (!appKey.value) {
ElMessage.warning('缺少应用标识')
return
}
const url = `${window.location.origin}/download/${appKey.value}`
try {
await navigator.clipboard.writeText(url)
ElMessage.success(`已复制对外下载页链接:${url}`)
} catch {
ElMessage.info(url)
}
}
async function checkServiceEnabled() { async function checkServiceEnabled() {
checkingService.value = true checkingService.value = true
serviceEnabled.value = null serviceEnabled.value = null