feat(update): 下载页展示历史版本 + 控制台新增下载页链接入口
- 下载页支持展开查看/下载全部曾发布过的历史版本 APK,不再只显示最新版本 - 版本管理页工具栏新增"复制对外下载页链接"按钮,方便管理员查找和分享对外下载页地址 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
这个提交包含在:
父节点
e346043b53
当前提交
24ac4cbc08
@ -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
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户