Files
yphsalumni.org/app/error.vue
xiaomai 6de61c24b2 style(theme): expand color palettes and refine dark mode styles
- Expand CSS variables for primary and secondary colors to include full 50-950 scales in `main.css`.
- Update components to reference specific color shades (e.g., `primary-400`, `secondary-200`) instead of generic variables.
- Add dark mode background and text colors to Events, Hall of Fame, and Index sections.
- Adjust image aspect ratio in the Events component.
2025-11-28 17:27:34 +08:00

272 lines
8.9 KiB
Vue
Raw Permalink 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.
<script setup lang="ts">
import type { NuxtError } from "#app";
// 定义扩展错误类型
interface ExtendedError extends NuxtError {
url?: string;
}
const props = defineProps<{ error?: ExtendedError }>();
const router = useRouter();
const route = useRoute();
const status = computed(() => props.error?.statusCode ?? 500);
const statusMessage = computed(() => props.error?.statusMessage ?? "");
const title = computed(() => {
switch (status.value) {
case 404:
return "页面未找到";
case 403:
return "禁止访问";
case 500:
return "服务器内部错误";
default:
return `${status.value} 错误`;
}
});
useHead({ title: title.value });
const friendlyMessage = computed(() => {
if (status.value === 404)
return "抱歉,我们找不到您要访问的页面。它可能已被移动、删除或从未存在过。";
if (status.value === 403)
return "抱歉,您没有权限访问此页面。请检查您的账户权限或联系管理员。";
if (status.value === 500)
return "服务器出了点问题,我们正在努力修复。请稍后再试。";
return props.error?.message ?? "发生了未知错误,我们的技术团队已经收到通知。";
});
const goHome = () => router.push("/");
const goBack = () => {
if (import.meta.client && window.history.length > 1) router.back();
else router.push("/");
};
const reloadPage = () => {
if (import.meta.client) window.location.reload();
};
const contactSupport = () => {
if (!import.meta.client) return;
const subject = encodeURIComponent(
`网站错误反馈 — ${title.value}${route.fullPath}`
);
const body = encodeURIComponent(`错误信息:
Status: ${status.value} ${statusMessage.value}
Path: ${route.fullPath}
请描述您在做什么以及复现步骤:`);
// 请替换为真实的支持邮箱或工单链接
window.location.href = `mailto:hello@example.com?subject=${subject}&body=${body}`;
};
const isDev = import.meta.dev;
// 格式化错误信息
const formattedDebugInfo = computed(() => {
if (!props.error) return isDev ? "无错误对象传入。" : "错误详情已隐藏。";
if (isDev) {
try {
return {
status: props.error.statusCode,
message: props.error.message || props.error.statusMessage,
url: props.error.url,
stack: props.error.stack,
};
} catch (e) {
return { error: String(props.error) };
}
}
return {
status: props.error.statusCode || "N/A",
message: props.error.message || props.error.statusMessage || "N/A",
};
});
// 错误图标
const errorIcon = computed(() => {
switch (status.value) {
case 404:
return "mdi:magnify-close";
case 403:
return "mdi:lock-alert";
case 500:
return "mdi:server-off";
default:
return "mdi:alert-circle";
}
});
// 错误颜色
const errorColor = computed(() => {
switch (status.value) {
case 404:
return "#fb9e3a"; // 使用主题色
case 403:
return "#e74c3c";
case 500:
return "#c0392b";
default:
return "#fb9e3a"; // 使用主题色
}
});
</script>
<template>
<main
class="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900 p-4"
>
<div
class="max-w-3xl w-full bg-white dark:bg-gray-800 shadow-lg rounded-lg overflow-hidden"
>
<!-- 顶部状态栏 -->
<div class="h-2 w-full" :style="{ backgroundColor: errorColor }"></div>
<div class="p-8 md:p-10">
<div class="flex flex-col md:flex-row items-center gap-8">
<!-- 错误代码和图标 -->
<div class="flex-none text-center">
<div class="relative">
<div
class="w-32 h-32 rounded-full flex items-center justify-center mx-auto mb-4"
:style="{ backgroundColor: errorColor + '20' }"
>
<div class="text-4xl" :style="{ color: errorColor }">
<span v-if="status === 404">404</span>
<span v-else-if="status === 403">403</span>
<span v-else-if="status === 500">500</span>
<span v-else>{{ status }}</span>
</div>
</div>
</div>
<div
class="text-sm text-gray-500 dark:text-gray-400 font-medium mt-2"
>
{{ statusMessage || title }}
</div>
</div>
<!-- 错误信息和操作 -->
<div class="flex-1">
<h1
class="text-2xl font-bold text-gray-900 dark:text-gray-100 mb-3"
>
{{ title }}
</h1>
<p class="text-gray-600 dark:text-gray-300 leading-relaxed mb-6">
{{ friendlyMessage }}
</p>
<!-- 操作按钮组 -->
<div class="flex flex-wrap gap-3 mb-6">
<button
@click="goHome"
class="px-5 py-2.5 rounded-md text-white font-medium transition-colors flex items-center gap-2"
style="background-color: var(--color-primary-400)"
>
<span>返回首页</span>
</button>
<button
@click="goBack"
class="px-5 py-2.5 rounded-md border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 font-medium hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
>
返回上页
</button>
<button
@click="reloadPage"
class="px-5 py-2.5 rounded-md border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 font-medium hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
>
重新加载
</button>
</div>
<!-- 报告问题按钮 -->
<div class="mb-6">
<button
@click="contactSupport"
class="px-5 py-2.5 rounded-md bg-gray-800 dark:bg-gray-700 text-white font-medium hover:bg-gray-700 dark:hover:bg-gray-600 transition-colors flex items-center gap-2"
>
<span>报告问题</span>
</button>
</div>
<!-- 提示信息 -->
<div class="text-sm text-gray-500 dark:text-gray-400 mb-6">
如果这是由链接或书签导致的请尝试返回或访问主页
</div>
<!-- 错误详情 -->
<details
class="border border-gray-200 dark:border-gray-700 rounded-md overflow-hidden"
>
<summary
class="cursor-pointer p-3 bg-gray-50 dark:bg-gray-900 font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
>
错误详情{{ isDev ? "开发模式" : "精简信息" }}
</summary>
<div
class="p-4 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 overflow-auto max-h-64"
>
<div v-if="typeof formattedDebugInfo === 'object'">
<div v-if="isDev" class="space-y-4">
<div v-if="formattedDebugInfo.status">
<strong>状态码:</strong> {{ formattedDebugInfo.status }}
</div>
<div v-if="formattedDebugInfo.message">
<strong>消息:</strong> {{ formattedDebugInfo.message }}
</div>
<div v-if="formattedDebugInfo.url">
<strong>URL:</strong> {{ formattedDebugInfo.url }}
</div>
<div v-if="formattedDebugInfo.stack" class="mt-3">
<strong>堆栈跟踪:</strong>
<pre
class="mt-2 text-xs bg-gray-50 dark:bg-gray-900 p-3 rounded overflow-x-auto"
>{{ formattedDebugInfo.stack }}</pre
>
</div>
</div>
<div v-else>
<div v-if="formattedDebugInfo.status">
<strong>状态码:</strong> {{ formattedDebugInfo.status }}
</div>
<div v-if="formattedDebugInfo.message">
<strong>消息:</strong> {{ formattedDebugInfo.message }}
</div>
</div>
</div>
<div v-else>
{{ formattedDebugInfo }}
</div>
</div>
</details>
</div>
</div>
</div>
</div>
</main>
</template>
<style scoped>
/* 自定义样式补充 */
details[open] summary {
border-bottom: 1px solid #e5e7eb;
}
/* 暗色模式适配 */
@media (prefers-color-scheme: dark) {
details[open] summary {
border-bottom-color: #4b5563;
}
}
/* 响应式调整 */
@media (max-width: 640px) {
.text-4xl {
font-size: 2rem;
}
}
</style>