123 lines
3.3 KiB
Vue
123 lines
3.3 KiB
Vue
<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>
|