fix(admin): restore Vite admin source files
This commit is contained in:
@@ -0,0 +1,278 @@
|
||||
<template>
|
||||
<div class="aksrt-page-container">
|
||||
<PageHeader
|
||||
title="数据备份"
|
||||
desc="管理设置快照、创建手动备份,并执行恢复或删除。"
|
||||
>
|
||||
<template #actions>
|
||||
<n-space>
|
||||
<n-button quaternary @click="go('/tools/import-export')">导入导出</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
<n-spin :show="loading" style="width: 100%; min-height: 200px;">
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<h3>备份策略</h3>
|
||||
</div>
|
||||
<n-space vertical size="small" style="width: 100%;">
|
||||
<n-space>
|
||||
<n-tag round :type="settings.backup_auto ? 'success' : 'default'">
|
||||
自动备份:{{ settings.backup_auto ? '已开启' : '未开启' }}
|
||||
</n-tag>
|
||||
<n-tag round type="info">频率:{{ frequencyLabel }}</n-tag>
|
||||
<n-tag round type="info">保留:{{ settings.backup_retain }} 份</n-tag>
|
||||
<n-tag round :type="backups.length ? 'success' : 'default'">现有快照:{{ backups.length }} 份</n-tag>
|
||||
</n-space>
|
||||
<n-space align="center" wrap>
|
||||
<span class="aksrt-backup__label">自动备份</span>
|
||||
<n-switch v-model:value="settings.backup_auto" />
|
||||
<span class="aksrt-backup__label">频率</span>
|
||||
<n-select v-model:value="settings.backup_frequency" :options="frequencyOptions" style="width: 140px;" />
|
||||
<span class="aksrt-backup__label">保留</span>
|
||||
<n-input-number v-model:value="settings.backup_retain" :min="3" :max="20" style="width: 140px;" />
|
||||
<n-button type="primary" :loading="savingPolicy" @click="handleSavePolicy">保存策略</n-button>
|
||||
</n-space>
|
||||
</n-space>
|
||||
</n-card>
|
||||
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<h3>手动创建快照</h3>
|
||||
</div>
|
||||
<n-space vertical size="small" style="width: 100%;">
|
||||
<n-input v-model:value="backupLabel" placeholder="可选:输入本次快照备注,如“升级前备份”" />
|
||||
<n-space>
|
||||
<n-button type="primary" :loading="creating" @click="handleCreateBackup">立即创建快照</n-button>
|
||||
<n-button :loading="loading" @click="load">刷新列表</n-button>
|
||||
</n-space>
|
||||
</n-space>
|
||||
</n-card>
|
||||
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<h3>快照列表</h3>
|
||||
</div>
|
||||
<n-empty v-if="!backups.length" description="暂无设置快照" />
|
||||
<n-data-table
|
||||
v-else
|
||||
:columns="columns"
|
||||
:data="backups"
|
||||
:bordered="true"
|
||||
size="small"
|
||||
:single-line="false"
|
||||
:scroll-x="900"
|
||||
/>
|
||||
<p class="aksrt-backup__hint">
|
||||
恢复非当前网站 / 非当前版本的快照可能导致配置异常,恢复前请确认站点环境一致。
|
||||
</p>
|
||||
</n-card>
|
||||
</n-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, h, onMounted, ref } from 'vue'
|
||||
import { NButton, NSpace, NTag } from 'naive-ui'
|
||||
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 creating = ref(false)
|
||||
const savingPolicy = ref(false)
|
||||
const backupLabel = ref('')
|
||||
const backups = ref([])
|
||||
const settings = ref({
|
||||
backup_auto: false,
|
||||
backup_frequency: 'weekly',
|
||||
backup_retain: 7,
|
||||
})
|
||||
|
||||
const frequencyLabelMap = {
|
||||
daily: '每天',
|
||||
weekly: '每周',
|
||||
monthly: '每月',
|
||||
}
|
||||
|
||||
const frequencyOptions = [
|
||||
{ label: '每天', value: 'daily' },
|
||||
{ label: '每周', value: 'weekly' },
|
||||
{ label: '每月', value: 'monthly' },
|
||||
]
|
||||
|
||||
const frequencyLabel = computed(() => frequencyLabelMap[settings.value.backup_frequency] || '每周')
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
key: 'id',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
key: 'type',
|
||||
width: 120,
|
||||
render: (row) => h(NTag, { size: 'small', round: true, type: tagType(row.type) }, () => typeLabel(row.type)),
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
key: 'label',
|
||||
minWidth: 220,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'created_at',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 180,
|
||||
render: (row) => h(NSpace, { size: 'small' }, () => [
|
||||
h(NButton, {
|
||||
size: 'tiny',
|
||||
type: 'primary',
|
||||
quaternary: true,
|
||||
onClick: () => handleRestore(row),
|
||||
}, () => '恢复'),
|
||||
h(NButton, {
|
||||
size: 'tiny',
|
||||
type: 'error',
|
||||
quaternary: true,
|
||||
onClick: () => handleDelete(row),
|
||||
}, () => '删除'),
|
||||
]),
|
||||
},
|
||||
]
|
||||
|
||||
onMounted(load)
|
||||
|
||||
function go(path) {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
function typeLabel(type) {
|
||||
return {
|
||||
manual: '手动备份',
|
||||
scheduled: '定期备份',
|
||||
reset_all: '重置全部',
|
||||
reset_domain: '重置分区',
|
||||
update_theme: '主题更新',
|
||||
}[type] || type
|
||||
}
|
||||
|
||||
function tagType(type) {
|
||||
return {
|
||||
manual: 'info',
|
||||
scheduled: 'success',
|
||||
reset_all: 'warning',
|
||||
reset_domain: 'warning',
|
||||
update_theme: 'error',
|
||||
}[type] || 'default'
|
||||
}
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
const [schemaRes, backupRes] = await Promise.all([
|
||||
api.getSchemaPage('tools', 'backup'),
|
||||
api.listSettingBackups(),
|
||||
])
|
||||
|
||||
const schemaData = schemaRes?.data || {}
|
||||
settings.value = {
|
||||
backup_auto: !!schemaData.backup_auto,
|
||||
backup_frequency: schemaData.backup_frequency || 'weekly',
|
||||
backup_retain: Number(schemaData.backup_retain || 7),
|
||||
}
|
||||
|
||||
backups.value = backupRes?.data?.items || []
|
||||
} catch (error) {
|
||||
message.error(error?.message || '加载备份列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCreateBackup() {
|
||||
creating.value = true
|
||||
try {
|
||||
await api.createSettingBackup({
|
||||
type: 'manual',
|
||||
label: backupLabel.value,
|
||||
})
|
||||
backupLabel.value = ''
|
||||
message.success('设置快照已创建')
|
||||
await load()
|
||||
} catch (error) {
|
||||
message.error(error?.message || '创建快照失败')
|
||||
} finally {
|
||||
creating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSavePolicy() {
|
||||
savingPolicy.value = true
|
||||
try {
|
||||
await api.saveSchemaPage('tools', 'backup', {
|
||||
backup_auto: !!settings.value.backup_auto,
|
||||
backup_frequency: settings.value.backup_frequency || 'weekly',
|
||||
backup_retain: Math.min(20, Math.max(3, Number(settings.value.backup_retain || 7))),
|
||||
})
|
||||
message.success('备份策略已保存')
|
||||
await load()
|
||||
} catch (error) {
|
||||
message.error(error?.message || '保存备份策略失败')
|
||||
} finally {
|
||||
savingPolicy.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRestore(row) {
|
||||
if (!window.confirm(`确认恢复快照「${row.label}」吗?此操作会覆盖当前设置。`)) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await api.restoreSettingBackup(row.id)
|
||||
message.success('设置已恢复')
|
||||
await load()
|
||||
} catch (error) {
|
||||
message.error(error?.message || '恢复快照失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDelete(row) {
|
||||
if (!window.confirm(`确认删除快照「${row.label}」吗?`)) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await api.deleteSettingBackup(row.id)
|
||||
message.success('快照已删除')
|
||||
await load()
|
||||
} catch (error) {
|
||||
message.error(error?.message || '删除快照失败')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.aksrt-backup__hint {
|
||||
margin: 12px 0 0;
|
||||
color: #667085;
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.aksrt-backup__label {
|
||||
color: #344054;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div class="aksrt-page-container">
|
||||
<PageHeader
|
||||
title="邮件回滚"
|
||||
desc="查看 14 天邮箱变更窗口内的账号,并在必要时执行回滚。"
|
||||
>
|
||||
<template #actions>
|
||||
<n-space>
|
||||
<n-button quaternary @click="go('/tools/health')">健康检查</n-button>
|
||||
<n-button quaternary @click="load">刷新列表</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
<n-spin :show="loading" style="width: 100%; min-height: 200px;">
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<h3>待处理账号</h3>
|
||||
<n-tag round type="info">总计 {{ total }}</n-tag>
|
||||
</div>
|
||||
|
||||
<div v-if="items.length" class="aksrt-rollback-list">
|
||||
<div v-for="item in items" :key="item.user_id" class="aksrt-rollback-list__item">
|
||||
<div>
|
||||
<strong>{{ item.display_name || item.login }}</strong>
|
||||
<p style="margin: 6px 0 0; color: #4b5565;">
|
||||
{{ item.current_email }} → {{ item.previous_email }}
|
||||
</p>
|
||||
<p style="margin: 6px 0 0; color: #7a7f8a; font-size: 12px;">
|
||||
剩余窗口:{{ Math.max(0, Math.floor((item.expires_in || 0) / 3600)) }} 小时
|
||||
</p>
|
||||
</div>
|
||||
<n-button size="small" :loading="runningId === item.user_id" @click="handleRollback(item.user_id)">
|
||||
执行回滚
|
||||
</n-button>
|
||||
</div>
|
||||
</div>
|
||||
<n-empty v-else description="当前没有可回滚的邮箱变更记录" />
|
||||
</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 runningId = ref(0)
|
||||
const items = ref([])
|
||||
const total = ref(0)
|
||||
|
||||
function go(path) {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
function extractData(response) {
|
||||
return response?.data ?? response ?? {}
|
||||
}
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = extractData(await api.getEmailRollbackUsers())
|
||||
items.value = Array.isArray(data.items) ? data.items : []
|
||||
total.value = Number(data.total || 0)
|
||||
} catch (error) {
|
||||
message.error(error.message || '加载邮件回滚列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRollback(userId) {
|
||||
runningId.value = userId
|
||||
try {
|
||||
const result = await api.executeEmailRollback(userId)
|
||||
if (result.code === 'success') {
|
||||
message.success(result.message || '回滚成功')
|
||||
await load()
|
||||
} else {
|
||||
message.error(result.message || '回滚失败')
|
||||
}
|
||||
} catch (error) {
|
||||
message.error(error.message || '回滚失败')
|
||||
} finally {
|
||||
runningId.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.aksrt-rollback-list {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.aksrt-rollback-list__item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid #e7ebf3;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.aksrt-rollback-list__item {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div class="aksrt-page-container">
|
||||
<PageHeader
|
||||
title="健康检查"
|
||||
desc="集中查看运行环境、数据库表、邮箱验证迁移和系统诊断状态。"
|
||||
>
|
||||
<template #actions>
|
||||
<n-space>
|
||||
<n-button quaternary @click="go('/tools/import-export')">导入导出</n-button>
|
||||
<n-button quaternary @click="go('/tools/email-rollback')">邮件回滚</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
<n-spin :show="loading" style="width: 100%; min-height: 200px;">
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<h3>邮箱验证状态</h3>
|
||||
</div>
|
||||
<n-space>
|
||||
<n-tag round :type="emailHealth.feature_enabled ? 'success' : 'default'">
|
||||
功能开关:{{ emailHealth.feature_enabled ? '已启用' : '未启用' }}
|
||||
</n-tag>
|
||||
<n-tag round :type="emailHealth.rest_blocking_active ? 'warning' : 'default'">
|
||||
REST 阻断:{{ emailHealth.rest_blocking_active ? '已开启' : '仅记录/关闭' }}
|
||||
</n-tag>
|
||||
<n-tag round type="info">
|
||||
待验证用户:{{ emailHealth.pending_users }}
|
||||
</n-tag>
|
||||
</n-space>
|
||||
</n-card>
|
||||
|
||||
<div class="aksrt-health-grid">
|
||||
<n-card v-for="check in checkItems" :key="check.key" class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<h3>{{ check.label }}</h3>
|
||||
<n-tag round :type="statusToTag(check.status)">{{ check.status }}</n-tag>
|
||||
</div>
|
||||
<p style="margin: 0; color: #4b5565;">{{ check.value }}</p>
|
||||
</n-card>
|
||||
</div>
|
||||
</n-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, 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 healthData = ref({})
|
||||
const emailHealth = ref({
|
||||
feature_enabled: false,
|
||||
pending_users: 0,
|
||||
rest_blocking_active: false,
|
||||
})
|
||||
|
||||
const checkItems = computed(() => {
|
||||
return Object.entries(healthData.value || {}).map(([key, value]) => ({
|
||||
key,
|
||||
...(value || {}),
|
||||
}))
|
||||
})
|
||||
|
||||
function go(path) {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
function extractData(response) {
|
||||
return response?.data ?? response ?? {}
|
||||
}
|
||||
|
||||
function statusToTag(status) {
|
||||
if (status === 'ok') return 'success'
|
||||
if (status === 'warning') return 'warning'
|
||||
if (status === 'error') return 'error'
|
||||
return 'info'
|
||||
}
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
const [healthRes, emailRes] = await Promise.all([
|
||||
api.getHealthCheck(),
|
||||
api.getHealthEmailVerification(),
|
||||
])
|
||||
healthData.value = extractData(healthRes).checks || {}
|
||||
emailHealth.value = extractData(emailRes)
|
||||
} catch (error) {
|
||||
message.error(error.message || '加载健康检查失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.aksrt-health-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<div class="aksrt-page-container">
|
||||
<PageHeader
|
||||
title="导入导出"
|
||||
desc="导出当前设置快照、执行 dry-run 导入校验,并支持按域重置到 schema 默认值。"
|
||||
>
|
||||
<template #actions>
|
||||
<n-space>
|
||||
<n-button quaternary @click="go('/tools/backup')">数据备份</n-button>
|
||||
<n-button quaternary @click="go('/tools/health')">健康检查</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<h3>导出与导入</h3>
|
||||
</div>
|
||||
<n-space style="margin-bottom: 16px;">
|
||||
<n-button :loading="exporting" @click="handleExport">导出当前配置</n-button>
|
||||
<n-button :loading="importing" @click="handleDryRun">Dry Run 校验</n-button>
|
||||
<n-button type="primary" :loading="importing" @click="handleImport">执行导入</n-button>
|
||||
</n-space>
|
||||
<n-input
|
||||
v-model:value="payloadText"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 12, maxRows: 20 }"
|
||||
placeholder="导出的 JSON 会出现在这里,也可以直接粘贴导入。"
|
||||
/>
|
||||
<p class="aksrt-tools-hint">
|
||||
正式导入前会自动创建一份“导入前自动快照”,与“数据备份”页的快照体系共用,便于回滚。
|
||||
</p>
|
||||
</n-card>
|
||||
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<h3>按域重置</h3>
|
||||
</div>
|
||||
<n-space align="center" wrap>
|
||||
<n-select v-model:value="resetDomain" :options="domainOptions" style="width: 220px;" />
|
||||
<n-button :loading="resetting" @click="handleReset">重置所选域</n-button>
|
||||
<n-button type="error" tertiary :loading="resettingAll" @click="handleResetAll">重置全部</n-button>
|
||||
</n-space>
|
||||
<p class="aksrt-tools-hint">仅支持重置 schema 托管的业务域。健康检查、迁移状态和邮件回滚属于运维面板,不参与按域回滚。</p>
|
||||
</n-card>
|
||||
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<h3>最近结果</h3>
|
||||
</div>
|
||||
<pre class="aksrt-tools-output">{{ resultText || '尚未执行操作。' }}</pre>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { 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 exporting = ref(false)
|
||||
const importing = ref(false)
|
||||
const resetting = ref(false)
|
||||
const resettingAll = ref(false)
|
||||
const payloadText = ref('')
|
||||
const resultText = ref('')
|
||||
const resetDomain = ref('global')
|
||||
|
||||
const domainOptions = [
|
||||
{ label: '全局&功能', value: 'global' },
|
||||
{ label: '页面&显示', value: 'page_display' },
|
||||
{ label: '文章&列表', value: 'post_list' },
|
||||
{ label: '用户&互动', value: 'users' },
|
||||
{ label: '支付&付费', value: 'payment' },
|
||||
]
|
||||
|
||||
function go(path) {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
function extractData(response) {
|
||||
return response?.data ?? response ?? {}
|
||||
}
|
||||
|
||||
function parsePayload() {
|
||||
try {
|
||||
return JSON.parse(payloadText.value || '{}')
|
||||
} catch (error) {
|
||||
throw new Error('导入 JSON 格式无效')
|
||||
}
|
||||
}
|
||||
|
||||
async function handleExport() {
|
||||
exporting.value = true
|
||||
try {
|
||||
const data = extractData(await api.exportSettings())
|
||||
payloadText.value = JSON.stringify(data, null, 2)
|
||||
resultText.value = `导出完成\n版本: ${data.version || 'unknown'}\n时间: ${data.exported || 'unknown'}`
|
||||
message.success('导出成功')
|
||||
} catch (error) {
|
||||
message.error(error.message || '导出失败')
|
||||
} finally {
|
||||
exporting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDryRun() {
|
||||
importing.value = true
|
||||
try {
|
||||
const payload = parsePayload()
|
||||
const result = await api.importSettings({ ...payload, dry_run: true })
|
||||
resultText.value = JSON.stringify(result, null, 2)
|
||||
message.success('Dry Run 校验完成')
|
||||
} catch (error) {
|
||||
message.error(error.message || '校验失败')
|
||||
} finally {
|
||||
importing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleImport() {
|
||||
importing.value = true
|
||||
try {
|
||||
const payload = parsePayload()
|
||||
const result = await api.importSettings(payload)
|
||||
resultText.value = JSON.stringify(result, null, 2)
|
||||
message.success('导入完成')
|
||||
} catch (error) {
|
||||
message.error(error.message || '导入失败')
|
||||
} finally {
|
||||
importing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleReset() {
|
||||
resetting.value = true
|
||||
try {
|
||||
const result = await api.resetDomain(resetDomain.value)
|
||||
resultText.value = JSON.stringify(result, null, 2)
|
||||
message.success('域设置已重置')
|
||||
} catch (error) {
|
||||
message.error(error.message || '重置失败')
|
||||
} finally {
|
||||
resetting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleResetAll() {
|
||||
if (!window.confirm('确认重置全部 schema 托管设置吗?系统会先自动创建一份“重置全部前快照”。')) {
|
||||
return
|
||||
}
|
||||
|
||||
resettingAll.value = true
|
||||
try {
|
||||
const result = await api.resetAllSettings()
|
||||
resultText.value = JSON.stringify(result, null, 2)
|
||||
message.success('全部设置已重置')
|
||||
} catch (error) {
|
||||
message.error(error.message || '重置全部失败')
|
||||
} finally {
|
||||
resettingAll.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.aksrt-tools-output {
|
||||
margin: 0;
|
||||
padding: 14px;
|
||||
border-radius: 12px;
|
||||
background: #101828;
|
||||
color: #d0d7e2;
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.aksrt-tools-hint {
|
||||
margin: 12px 0 0;
|
||||
color: #667085;
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user