redesign: site-wide UI/UX overhaul per web-interface-guidelines
- unified design system: focus-visible rings, reduced-motion, color-scheme, theme persistence, tightened emerald radius tokens (checkbox circle bug) - redesigned all pages: Home/Login/Signup/404, dashboard shell, Overview hero (time-based gradient), list pages with empty states, form modals, detail pages - role-scoped navigation: console menu vs admin console with cross-switch entries, Chinese route titles and breadcrumbs - theme switcher (light/dark/auto) via composables/theme, home + dashboard - state-driven dropdowns (touch-safe logout), menu divider pointer-events fix - btn-primary unified to black-on-white / white-on-black per theme - update REFACTOR_PLAN.md (increments 2-6)
This commit is contained in:
@@ -1,150 +1,122 @@
|
||||
<template>
|
||||
<div class="min-h-screen bg-base-100 p-3 md:p-6">
|
||||
<div class="space-y-6">
|
||||
<BreadcrumbHeader />
|
||||
|
||||
<div class="card shadow-xl overflow-hidden transition-all duration-300 hover:shadow-2xl mb-6" >
|
||||
<div :class="['card-body text-white', getGradientClass()]">
|
||||
<div class="flex flex-col sm:flex-row items-center gap-4 sm:gap-6">
|
||||
<div class="avatar">
|
||||
<div class="w-16 h-16 sm:w-20 sm:h-20 rounded-full ring ring-white ring-offset-base-100 ring-offset-2"
|
||||
v-if="!user?.avatar_url">
|
||||
<div
|
||||
class="bg-white text-primary-content font-bold flex items-center justify-center w-full h-full">
|
||||
<span class="text-2xl sm:text-3xl">{{ user?.username?.[0]?.toUpperCase() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-16 h-16 sm:w-20 sm:h-20 rounded-full ring ring-white ring-offset-base-100 ring-offset-2"
|
||||
v-else>
|
||||
<img :src="user?.avatar_url" :alt="user?.name" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center sm:text-left">
|
||||
<h1 class="text-2xl sm:text-4xl font-extrabold tracking-tight">
|
||||
<span class="mr-2">👋 </span>{{ getTimeOfDay() }},{{ user?.name || user?.username }}
|
||||
</h1>
|
||||
<p class="text-white/80 text-base sm:text-lg mt-1 font-medium">欢迎回到您的个人仪表盘</p>
|
||||
<!-- Welcome hero(随时间段切换配色) -->
|
||||
<section
|
||||
class="relative overflow-hidden rounded-2xl bg-gradient-to-br text-white shadow-sm"
|
||||
:class="getTimeTheme()">
|
||||
<div class="flex flex-col items-center gap-4 p-6 text-center sm:flex-row sm:p-8 sm:text-left">
|
||||
<div class="placeholder avatar">
|
||||
<div
|
||||
class="flex w-16 items-center justify-center rounded-full bg-white/15 ring-2 ring-white/25 sm:w-20">
|
||||
<span v-if="!user?.avatar_url" class="text-2xl font-bold sm:text-3xl">{{ user?.username?.[0]?.toUpperCase() }}</span>
|
||||
<img v-else :src="user?.avatar_url" :alt="user?.name || user?.username || 'Avatar'"
|
||||
class="h-full w-full object-cover" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 md:gap-6 lg:gap-8">
|
||||
<div class="card bg-base-100 shadow-xl hover:shadow-2xl transition-all duration-300 border border-base-200">
|
||||
<div class="card-body p-4 md:p-6">
|
||||
<h2 class="card-title text-lg md:text-xl font-bold flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 md:h-6 md:w-6 text-base-content"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||
</svg>
|
||||
基本信息
|
||||
<div>
|
||||
<h2 class="text-xl font-bold tracking-tight text-balance sm:text-2xl">
|
||||
<span class="mr-1.5" aria-hidden="true">👋</span>{{ getTimeOfDay() }},{{ user?.name || user?.username }}
|
||||
</h2>
|
||||
<div class="divider my-1"></div>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-semibold text-base-content/70 w-20">用户名</span>
|
||||
<span class="badge">{{ user?.username }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-semibold text-base-content/70 w-20">显示名称</span>
|
||||
<span class="badge">{{ user?.name || '-' }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-semibold text-base-content/70 w-20">邮箱</span>
|
||||
<span class="badge truncate max-w-full">{{ user?.email || '-' }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-semibold text-base-content/70 w-20">角色</span>
|
||||
<span class="badge" :class="(user?.role || 0) > 0 ? 'badge-warning' : 'badge-ghost'">{{
|
||||
getRoleName(user?.role || 0) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-white/80">欢迎回到您的个人仪表盘</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="card bg-base-100 shadow-xl hover:shadow-2xl transition-all duration-300 border border-base-200">
|
||||
<div class="card-body p-4 md:p-6">
|
||||
<h2 class="card-title text-lg md:text-xl font-bold flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 md:h-6 md:w-6 text-base-content"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
|
||||
</svg>
|
||||
账户状态
|
||||
</h2>
|
||||
<div class="divider my-1"></div>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-semibold text-base-content/70 w-20">状态</span>
|
||||
<span :class="[
|
||||
'badge badge-outline',
|
||||
user?.active ? 'badge-success' : 'badge-error'
|
||||
]">
|
||||
{{ user?.active ? 'Active' : 'Inactive' }}
|
||||
</span>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<!-- 基本信息 -->
|
||||
<article class="card border border-base-300/60 bg-base-100 shadow-sm">
|
||||
<div class="card-body p-5">
|
||||
<h3 class="card-title pb-1 text-base font-semibold">基本信息</h3>
|
||||
<dl class="divide-y divide-base-300/40 text-sm">
|
||||
<div class="flex items-center justify-between gap-3 py-2.5">
|
||||
<dt class="shrink-0 text-base-content/60">用户名</dt>
|
||||
<dd class="truncate font-medium">{{ user?.username }}</dd>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-semibold text-base-content/70 w-20">时区</span>
|
||||
<span class="badge ">{{ user?.timezone || 'UTC' }}</span>
|
||||
<div class="flex items-center justify-between gap-3 py-2.5">
|
||||
<dt class="shrink-0 text-base-content/60">显示名称</dt>
|
||||
<dd class="truncate font-medium">{{ user?.name || '—' }}</dd>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-semibold text-base-content/70 w-20">语言</span>
|
||||
<span class="badge">{{ user?.language || 'en' }}</span>
|
||||
<div class="flex items-center justify-between gap-3 py-2.5">
|
||||
<dt class="shrink-0 text-base-content/60">邮箱</dt>
|
||||
<dd class="truncate font-medium">{{ user?.email || '—' }}</dd>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-semibold text-base-content/70 w-20">最后活动</span>
|
||||
<span class="badge">{{ getLastActive() }}</span>
|
||||
<div class="flex items-center justify-between gap-3 py-2.5">
|
||||
<dt class="shrink-0 text-base-content/60">角色</dt>
|
||||
<dd>
|
||||
<span class="badge badge-sm"
|
||||
:class="(user?.role || 0) > 0 ? 'badge-warning badge-soft' : 'badge-ghost'">{{
|
||||
getRoleName(user?.role || 0) }}</span>
|
||||
</dd>
|
||||
</div>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<div class="card bg-base-100 shadow-xl hover:shadow-2xl transition-all duration-300 border border-base-200">
|
||||
<div class="card-body p-4 md:p-6">
|
||||
<h2 class="card-title text-lg md:text-xl font-bold flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 md:h-6 md:w-6 text-base-content"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z" />
|
||||
</svg>
|
||||
配额信息
|
||||
</h2>
|
||||
<div class="divider my-1"></div>
|
||||
<div class="space-y-4">
|
||||
<div v-if="user?.unlimited_quota">
|
||||
<div class="flex flex-wrap justify-between mb-2">
|
||||
<span class="font-semibold text-base-content/70">使用量</span>
|
||||
<span class="badge badge-lg text-success">无限制</span>
|
||||
</div>
|
||||
<!-- 账户状态 -->
|
||||
<article class="card border border-base-300/60 bg-base-100 shadow-sm">
|
||||
<div class="card-body p-5">
|
||||
<h3 class="card-title pb-1 text-base font-semibold">账户状态</h3>
|
||||
<dl class="divide-y divide-base-300/40 text-sm">
|
||||
<div class="flex items-center justify-between gap-3 py-2.5">
|
||||
<dt class="shrink-0 text-base-content/60">状态</dt>
|
||||
<dd>
|
||||
<span class="badge badge-sm"
|
||||
:class="user?.active ? 'badge-success badge-soft' : 'badge-error badge-soft'">
|
||||
{{ user?.active ? 'Active' : 'Inactive' }}
|
||||
</span>
|
||||
</dd>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="flex flex-wrap justify-between mb-2">
|
||||
<span class="font-semibold text-base-content/70">使用量</span>
|
||||
<span :class="getQuotaColor">
|
||||
{{ formatQuota(user?.used_quota || 0, user?.quota||0) }}
|
||||
<div class="flex items-center justify-between gap-3 py-2.5">
|
||||
<dt class="shrink-0 text-base-content/60">时区</dt>
|
||||
<dd class="font-medium">{{ user?.timezone || 'UTC' }}</dd>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-3 py-2.5">
|
||||
<dt class="shrink-0 text-base-content/60">语言</dt>
|
||||
<dd class="font-medium uppercase">{{ user?.language || 'en' }}</dd>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-3 py-2.5">
|
||||
<dt class="shrink-0 text-base-content/60">最后活动</dt>
|
||||
<dd class="font-medium tabular-nums">{{ getLastActive() }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- 配额信息 -->
|
||||
<article class="card border border-base-300/60 bg-base-100 shadow-sm">
|
||||
<div class="card-body p-5">
|
||||
<h3 class="card-title pb-1 text-base font-semibold">配额信息</h3>
|
||||
<div class="flex-1 space-y-4 pt-1">
|
||||
<div v-if="user?.unlimited_quota" class="flex items-center justify-between text-sm">
|
||||
<span class="text-base-content/60">使用量</span>
|
||||
<span class="badge badge-success badge-soft">无限制</span>
|
||||
</div>
|
||||
<div v-else class="space-y-2 text-sm">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<span class="text-base-content/60">使用量</span>
|
||||
<span class="font-medium tabular-nums" :class="getQuotaColor">
|
||||
{{ formatQuota(user?.used_quota || 0, user?.quota || 0) }}
|
||||
</span>
|
||||
</div>
|
||||
<progress class="progress w-full"
|
||||
:class="getQuotaColorClass"
|
||||
:value="quotaPercentage"
|
||||
max="100"></progress>
|
||||
<progress class="progress w-full" :class="getQuotaColorClass" :value="quotaPercentage"
|
||||
max="100" :aria-label="`Quota used ${quotaPercentage.toFixed(1)}%`"></progress>
|
||||
</div>
|
||||
|
||||
<div class="stats stats-vertical shadow w-full bg-base-100">
|
||||
<div class="stat p-2 md:p-4">
|
||||
<div class="stat-title text-xs md:text-sm">创建时间</div>
|
||||
<div class="stat-value text-sm md:text-base">{{ formatDateTime(user?.created_at) }}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3 border-t border-base-300/40 pt-3">
|
||||
<div>
|
||||
<p class="text-xs text-base-content/50">创建时间</p>
|
||||
<p class="mt-0.5 text-sm font-medium tabular-nums">{{ formatDateTime(user?.created_at) }}</p>
|
||||
</div>
|
||||
<div class="stat p-2 md:p-4">
|
||||
<div class="stat-title text-xs md:text-sm">更新时间</div>
|
||||
<div class="stat-value text-sm md:text-base">{{ formatDateTime(user?.updated_at) }}
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs text-base-content/50">更新时间</p>
|
||||
<p class="mt-0.5 text-sm font-medium tabular-nums">{{ formatDateTime(user?.updated_at) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -169,11 +141,21 @@ onMounted(async () => {
|
||||
|
||||
const getTimeOfDay = (): string => {
|
||||
const hour = new Date().getHours();
|
||||
if (hour < 6) return '夜深了';
|
||||
if (hour < 12) return '早上好';
|
||||
if (hour < 18) return '下午好';
|
||||
return '晚上好';
|
||||
};
|
||||
|
||||
// 横幅随时间段切换配色:白色文字位于渐变左侧深色端,保证对比度
|
||||
const getTimeTheme = (): string => {
|
||||
const hour = new Date().getHours();
|
||||
if (hour < 6) return 'from-slate-900 via-slate-800 to-indigo-950'; // 深夜
|
||||
if (hour < 12) return 'from-sky-600 via-sky-500 to-amber-400'; // 早晨
|
||||
if (hour < 18) return 'from-blue-600 via-sky-500 to-cyan-400'; // 白天
|
||||
return 'from-slate-900 via-indigo-950 to-slate-800'; // 夜晚
|
||||
};
|
||||
|
||||
const formatQuota = (used: number, total: number): string => {
|
||||
if (total === 0) return '无限制';
|
||||
|
||||
@@ -215,15 +197,6 @@ function formatDateTime(unixTimestamp?: number): string {
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
// 获取背景渐变类
|
||||
const getGradientClass = (): string => {
|
||||
const hour = new Date().getHours();
|
||||
if (hour < 6) return 'bg-gradient-to-r from-[#e0f2f1] to-[#1a1a1a] bg-opacity-50 backdrop-blur-lg'; // 深夜到黎明:柔和的薄荷绿渐变到微黑
|
||||
if (hour < 12) return 'bg-gradient-to-r from-[#8cc7f1] to-[#cf6f26] bg-opacity-50 backdrop-blur-lg'; // 早晨:温暖的杏仁色渐变到深灰
|
||||
if (hour < 18) return 'bg-gradient-to-r from-[#e3f2fd] to-[#ad4212] bg-opacity-50 backdrop-blur-lg'; // 下午:清新的天空蓝渐变到近黑
|
||||
return 'bg-gradient-to-r from-[#000000] to-[#434343] bg-opacity-50 backdrop-blur-lg'; // 夜晚:纯黑到深灰
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 计算配额百分比
|
||||
@@ -269,23 +242,3 @@ const getLastActive = (): string => {
|
||||
return '刚刚';
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 添加一些过渡动画 */
|
||||
.card {
|
||||
transform-origin: center;
|
||||
backface-visibility: hidden;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
/* 确保响应式设计中文本不会溢出 */
|
||||
.badge {
|
||||
white-space: normal;
|
||||
height: auto;
|
||||
min-height: 1.6rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user