You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
126 lines
4.9 KiB
126 lines
4.9 KiB
<script setup lang="ts">
|
|
import type { NuxtError } from "#app";
|
|
|
|
const props = defineProps<{
|
|
error: NuxtError;
|
|
}>();
|
|
|
|
const statusCode = computed(() => props.error?.statusCode ?? 500);
|
|
const statusMessage = computed(() => {
|
|
if (props.error?.statusMessage) {
|
|
return props.error.statusMessage;
|
|
}
|
|
return statusCode.value >= 500 ? "服务暂时不可用,请稍后重试" : "请求失败,请检查后重试";
|
|
});
|
|
const errorTitle = computed(() => {
|
|
if (statusCode.value === 401) return "未登录或会话已失效";
|
|
if (statusCode.value === 403) return "当前账号无访问权限";
|
|
if (statusCode.value === 404) return "页面不存在";
|
|
if (statusCode.value >= 500) return "服务开小差了";
|
|
return "发生了一点异常";
|
|
});
|
|
const tips = computed(() => {
|
|
if (statusCode.value === 401) return "请重新登录后再继续操作。";
|
|
if (statusCode.value === 403) return "如需访问此页面,请联系管理员开通权限。";
|
|
if (statusCode.value === 404) return "你访问的地址可能已变更或被移除。";
|
|
if (statusCode.value >= 500) return "我们正在处理该问题,请稍后再试。";
|
|
return "你可以先返回首页,或点击重试。";
|
|
});
|
|
const levelTag = computed(() => {
|
|
if (statusCode.value >= 500) return "P1 / 高优先级";
|
|
if (statusCode.value === 401 || statusCode.value === 403) return "P2 / 权限类";
|
|
if (statusCode.value === 404) return "P3 / 资源类";
|
|
return "P3 / 一般异常";
|
|
});
|
|
const actionHints = computed(() => {
|
|
if (statusCode.value === 401) {
|
|
return ["确认登录状态是否过期", "重新登录后刷新页面", "检查接口鉴权中间件配置"];
|
|
}
|
|
if (statusCode.value === 403) {
|
|
return ["确认账号角色与权限策略", "检查后端权限拦截规则", "联系管理员开通访问权限"];
|
|
}
|
|
if (statusCode.value === 404) {
|
|
return ["确认访问路径是否正确", "检查路由或接口是否已下线", "核对部署环境与 baseURL 配置"];
|
|
}
|
|
if (statusCode.value >= 500) {
|
|
return ["查看服务端日志定位堆栈", "检查依赖服务和数据库连接", "确认最新变更是否引入回归"];
|
|
}
|
|
return ["查看浏览器控制台错误信息", "检查网络请求返回内容", "重试并记录复现路径"];
|
|
});
|
|
|
|
const handleBackHome = () => clearError({ redirect: "/" });
|
|
const handleRetry = async () => {
|
|
if (statusCode.value === 401 || statusCode.value === 403) {
|
|
await clearError({ redirect: "/login" });
|
|
return;
|
|
}
|
|
|
|
if (import.meta.client) {
|
|
window.location.reload();
|
|
return;
|
|
}
|
|
|
|
await clearError({ redirect: "/" });
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<main class="min-h-screen bg-default text-default">
|
|
<section class="mx-auto w-full max-w-5xl px-4 py-10 sm:px-6">
|
|
<header class="mb-4 flex items-center justify-between rounded-xl border border-default bg-elevated px-4 py-3">
|
|
<div class="flex items-center gap-2">
|
|
<span class="size-2 rounded-full bg-error" />
|
|
<p class="text-sm font-semibold text-toned">运行异常面板</p>
|
|
</div>
|
|
<span class="rounded-md border border-default px-2 py-1 text-xs text-muted">
|
|
{{ levelTag }}
|
|
</span>
|
|
</header>
|
|
|
|
<div class="grid gap-4 lg:grid-cols-[1.35fr,1fr]">
|
|
<article class="rounded-xl border border-default bg-elevated p-6 sm:p-7">
|
|
<p class="mb-1 text-xs tracking-wide text-muted">状态码</p>
|
|
<p class="mb-3 font-mono text-5xl font-semibold leading-none text-error sm:text-6xl">
|
|
{{ statusCode }}
|
|
</p>
|
|
<h1 class="mb-2 text-3xl font-semibold tracking-tight">
|
|
{{ errorTitle }}
|
|
</h1>
|
|
<p class="mb-2 text-toned">
|
|
{{ statusMessage }}
|
|
</p>
|
|
<p class="mb-6 text-sm text-muted">
|
|
{{ tips }}
|
|
</p>
|
|
|
|
<div class="flex flex-wrap gap-3">
|
|
<UButton color="error" variant="solid" size="md" @click="handleRetry">
|
|
立即重试
|
|
</UButton>
|
|
<UButton color="neutral" variant="outline" size="md" @click="handleBackHome">
|
|
返回首页
|
|
</UButton>
|
|
</div>
|
|
</article>
|
|
|
|
<aside class="rounded-xl border border-default bg-elevated p-6 sm:p-7">
|
|
<p class="mb-3 text-xs tracking-wide text-muted">建议排查项</p>
|
|
<ul class="mb-5 space-y-2 text-sm text-toned">
|
|
<li v-for="hint in actionHints" :key="hint" class="rounded-lg border border-default bg-default px-3 py-2">
|
|
{{ hint }}
|
|
</li>
|
|
</ul>
|
|
|
|
<details v-if="error?.message" class="rounded-lg border border-default bg-default p-3 mt-2">
|
|
<summary class="cursor-pointer select-none text-sm font-medium text-toned">
|
|
查看错误详情
|
|
</summary>
|
|
<p class="mt-2 break-words text-xs text-muted">
|
|
{{ error.message }}
|
|
</p>
|
|
</details>
|
|
</aside>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
</template>
|
|
|