Files
wordpress-theme-aksrt/assets/admin/src/pages/tools/ToolsUpdate.vue
T

199 lines
6.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="aksrt-page-container">
<PageHeader
title="更新中心"
desc="查看版本状态、检测更新并浏览更新日志与教程入口。"
>
<template #actions>
<n-space>
<n-button quaternary :loading="checking" @click="handleCheck">检测更新</n-button>
<n-button quaternary @click="go('/docs')">文档中心</n-button>
</n-space>
</template>
</PageHeader>
<n-spin :show="loading" style="width: 100%; min-height: 220px;">
<n-card class="aksrt-section-card">
<div class="aksrt-section-header">
<h3>版本状态</h3>
</div>
<n-space>
<n-tag round type="info">当前版本:{{ updateInfo.current_version || '0.0.0' }}</n-tag>
<n-tag round :type="updateInfo.update_available ? 'warning' : 'success'">
{{ updateInfo.update_available ? `发现新版本 ${updateInfo.remote_version}` : '已是最新版本' }}
</n-tag>
<n-tag round type="default">上次检测:{{ formatDate(updateInfo.last_check) }}</n-tag>
</n-space>
</n-card>
<n-card class="aksrt-section-card" v-if="updateInfo.update_available">
<div class="aksrt-section-header">
<h3>可用更新</h3>
</div>
<n-alert type="warning" style="margin-bottom: 12px;">
检测到新版本 {{ updateInfo.remote_version }},当前版本为 {{ updateInfo.current_version }}。
</n-alert>
<n-space>
<n-button v-if="updateInfo.download_url" type="primary" tag="a" :href="updateInfo.download_url" target="_blank">
下载更新
</n-button>
<n-button @click="handleDismiss" :loading="dismissing">忽略此版本</n-button>
</n-space>
<div v-if="updateInfo.changelog" class="aksrt-update__changelog">
<h4>更新日志</h4>
<pre>{{ updateInfo.changelog }}</pre>
</div>
</n-card>
<n-card class="aksrt-section-card">
<div class="aksrt-section-header">
<h3>常用教程入口</h3>
</div>
<div class="aksrt-update__grid">
<a v-for="item in guides" :key="item.key" class="aksrt-update__guide" :href="item.url" target="_blank" rel="noreferrer">
<strong>{{ item.title }}</strong>
<span>{{ item.desc }}</span>
</a>
</div>
</n-card>
</n-spin>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { useMessage } from 'naive-ui'
import api from '@/api'
import PageHeader from '@/components/PageHeader.vue'
const router = useRouter()
const message = useMessage()
const loading = ref(true)
const checking = ref(false)
const dismissing = ref(false)
const updateInfo = ref({
current_version: '0.0.0',
remote_version: '',
update_available: false,
download_url: '',
changelog: '',
last_check: 0,
})
const guides = [
{ key: 'rewrite', title: '伪静态配置', desc: '检查固定链接与重写规则', url: '/wp-admin/options-permalink.php' },
{ key: 'oauth', title: '第三方登录', desc: '检查 GitHub / Gitee / QQ / Google 登录', url: '#/users/oauth' },
{ key: 'backup', title: '设置快照', desc: '创建与恢复站点配置快照', url: '#/tools/backup' },
{ key: 'health', title: '健康检查', desc: '查看 PHP / WP / 数据表状态', url: '#/tools/health' },
{ key: 'meilisearch', title: 'Meilisearch', desc: '配置搜索索引与同步策略', url: '#/global/search' },
{ key: 'i18n', title: '多语言翻译', desc: '检查语言切换与术语配置', url: '#/global/i18n' },
{ key: 'cdn', title: '静态资源与 CDN', desc: '核对前端资源地址与缓存策略', url: '#/global/common' },
{ key: 'ip-geo', title: 'IP 归属地', desc: '验证 IP 查询服务与密钥配置', url: '#/extensions/ip' },
{ key: 'payment', title: '支付与会员', desc: '核对网关、会员与充值配置', url: '#/payment/gateway' },
{ key: 'credits', title: '积分与签到', desc: '检查积分规则、签到与推广奖励', url: '#/users/credits' },
{ key: 'badge', title: '等级与徽章', desc: '维护等级、认证与徽章体系', url: '#/users/badge' },
]
onMounted(load)
function go(path) {
router.push(path)
}
function formatDate(value) {
if (!value) return '未检测'
const timestamp = Number(value)
if (!Number.isNaN(timestamp) && timestamp > 0) {
return new Date(timestamp * 1000).toLocaleString()
}
return String(value)
}
async function load() {
loading.value = true
try {
const res = await api.getUpdateInfo()
updateInfo.value = res?.data || {}
} catch (error) {
message.error(error?.message || '加载更新状态失败')
} finally {
loading.value = false
}
}
async function handleCheck() {
checking.value = true
try {
const res = await api.detectUpdate()
updateInfo.value = res?.data || {}
message.success('更新检测完成')
} catch (error) {
message.error(error?.message || '检测更新失败')
} finally {
checking.value = false
}
}
async function handleDismiss() {
if (!updateInfo.value.remote_version) return
dismissing.value = true
try {
await api.dismissUpdate(updateInfo.value.remote_version)
message.success('已忽略此版本')
await load()
} catch (error) {
message.error(error?.message || '忽略版本失败')
} finally {
dismissing.value = false
}
}
</script>
<style scoped>
.aksrt-update__changelog {
margin-top: 16px;
}
.aksrt-update__changelog pre {
margin: 8px 0 0;
padding: 12px 14px;
border-radius: 12px;
background: #101828;
color: #d0d7e2;
font-size: 12px;
line-height: 1.7;
white-space: pre-wrap;
word-break: break-word;
}
.aksrt-update__grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 12px;
}
.aksrt-update__guide {
display: flex;
flex-direction: column;
gap: 4px;
padding: 14px;
border: 1px solid var(--aksrt-color-border);
border-radius: 12px;
text-decoration: none;
color: inherit;
background: #fafafa;
}
.aksrt-update__guide:hover {
border-color: var(--aksrt-color-primary);
background: #f5faff;
}
.aksrt-update__guide span {
font-size: 12px;
color: #667085;
}
</style>