feat(ui): adopt Tailwind v4 design system and archive change
- Add tailwindcss v4 + @tailwindcss/vite to mall, shop-admin, admin - Add @vmall/shared/theme.css tokens with html[data-accent] presets - Add @vmall/ui kit (VBtn/VBadge/VField/VInput/VCard/VPanel/VTable/VPage, VAccentSwatch, useAccent) as a Nuxt module - Convert all three apps to kit + utilities; delete ui.css/mall.css and every <style scoped>; consoles get accent presets, mall locked to red - Fix VCard boolean prop default (padding) and PDP/store stale useAsyncData keys on param navigation - Archive adopt-tailwind-design-system; new frontend-ui capability spec
This commit is contained in:
+32
-100
@@ -49,110 +49,42 @@ const cards = computed(() => [
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<p class="eyebrow">{{ $t("common.appName") }} · Admin</p>
|
||||
<h1 class="page-title">{{ $t("admin.dashboardTitle") }}</h1>
|
||||
</div>
|
||||
<button class="btn sm" type="button" :disabled="loading" @click="loadDashboard">
|
||||
{{ $t("admin.refresh") }}
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="loading" class="muted">{{ $t("common.loading") }}</p>
|
||||
<p v-else-if="errorMessage" class="error-text" role="alert">{{ errorMessage }}</p>
|
||||
<VPage :title="$t('admin.dashboardTitle')">
|
||||
<template #actions>
|
||||
<VBtn size="sm" :disabled="loading" @click="loadDashboard">{{ $t("admin.refresh") }}</VBtn>
|
||||
</template>
|
||||
<p class="mb-4 text-xs font-bold uppercase tracking-[0.08em] text-primary">{{ $t("common.appName") }} · Admin</p>
|
||||
<p v-if="loading" class="text-sm text-muted">{{ $t("common.loading") }}</p>
|
||||
<p v-else-if="errorMessage" class="my-2 text-sm text-danger" role="alert">{{ errorMessage }}</p>
|
||||
<template v-else>
|
||||
<div class="stat-grid">
|
||||
<NuxtLink v-for="card in cards" :key="card.link" :to="card.link" class="stat-card">
|
||||
<span class="stat-label">{{ $t(card.label) }}</span>
|
||||
<strong class="stat-value" :class="`tone-${card.tone}`">{{ card.value }}</strong>
|
||||
<span class="stat-link">{{ $t("admin.open") }} →</span>
|
||||
<div class="grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
|
||||
<NuxtLink
|
||||
v-for="card in cards"
|
||||
:key="card.link"
|
||||
:to="card.link"
|
||||
class="flex flex-col gap-2 rounded-lg border border-border bg-surface p-5 shadow-sm transition-transform hover:-translate-y-0.5 hover:border-primary"
|
||||
>
|
||||
<span class="text-[13px] text-muted">{{ $t(card.label) }}</span>
|
||||
<strong
|
||||
class="text-[32px] leading-none"
|
||||
:class="card.tone === 'green' ? 'text-success' : card.tone === 'orange' ? 'text-warning' : 'text-primary'"
|
||||
>{{ card.value }}</strong>
|
||||
<span class="text-xs text-primary">{{ $t("admin.open") }} →</span>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div class="grid shortcuts">
|
||||
<NuxtLink class="card shortcut" to="/users">
|
||||
<strong>{{ $t("admin.userManagement") }}</strong>
|
||||
<span class="muted">{{ $t("nav.users") }}</span>
|
||||
</NuxtLink>
|
||||
<NuxtLink class="card shortcut" to="/shops">
|
||||
<strong>{{ $t("admin.shopManagement") }}</strong>
|
||||
<span class="muted">{{ $t("nav.shops") }}</span>
|
||||
</NuxtLink>
|
||||
<NuxtLink class="card shortcut" to="/orders">
|
||||
<strong>{{ $t("admin.orderManagement") }}</strong>
|
||||
<span class="muted">{{ $t("nav.orders") }}</span>
|
||||
</NuxtLink>
|
||||
<NuxtLink class="card shortcut" to="/currencies">
|
||||
<strong>{{ $t("admin.currencyManagement") }}</strong>
|
||||
<span class="muted">{{ $t("nav.currencies") }}</span>
|
||||
<div class="mt-6 grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
|
||||
<NuxtLink v-for="shortcut in [
|
||||
{ to: '/users', title: 'admin.userManagement', label: 'nav.users' },
|
||||
{ to: '/shops', title: 'admin.shopManagement', label: 'nav.shops' },
|
||||
{ to: '/orders', title: 'admin.orderManagement', label: 'nav.orders' },
|
||||
{ to: '/currencies', title: 'admin.currencyManagement', label: 'nav.currencies' },
|
||||
]" :key="shortcut.to" :to="shortcut.to" class="block no-underline hover:no-underline">
|
||||
<VCard>
|
||||
<strong class="block">{{ $t(shortcut.title) }}</strong>
|
||||
<span class="text-sm text-muted">{{ $t(shortcut.label) }}</span>
|
||||
</VCard>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</VPage>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.eyebrow {
|
||||
color: var(--primary);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
margin: 0 0 6px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.stat-grid {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
.stat-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 20px;
|
||||
text-decoration: none;
|
||||
transition: transform 150ms ease, border-color 150ms ease;
|
||||
}
|
||||
.stat-card:hover {
|
||||
border-color: var(--primary);
|
||||
text-decoration: none;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.stat-label {
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
.stat-value {
|
||||
font-size: 32px;
|
||||
line-height: 1;
|
||||
}
|
||||
.tone-blue { color: var(--primary); }
|
||||
.tone-green { color: var(--success); }
|
||||
.tone-orange { color: var(--warning); }
|
||||
.tone-purple { color: #7257b8; }
|
||||
.stat-link {
|
||||
color: var(--primary);
|
||||
font-size: 12px;
|
||||
}
|
||||
.shortcuts {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
margin-top: 24px;
|
||||
}
|
||||
.shortcut {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
.shortcut:hover { text-decoration: none; }
|
||||
@media (max-width: 900px) {
|
||||
.stat-grid, .shortcuts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.stat-grid, .shortcuts { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user