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

<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>