feat(admin): add initial admin dashboard structure

This commit introduces the foundational structure for the new admin dashboard.

- Utilizes @nuxt/ui to build the dashboard layout, including a collapsible sidebar and navigation.
- Adds initial pages for the dashboard, news, events, and hall of fame management.
- Implements a composable `useDashboardSidebarLinks` for managing sidebar navigation.
- Refactors the default layout by integrating the header and footer directly.
- Swaps the primary and secondary theme colors across the application.
This commit is contained in:
xiaomai
2025-10-08 08:49:08 +08:00
parent b05faddfc0
commit 6f181d3f22
19 changed files with 371 additions and 85 deletions

View File

@@ -1,39 +0,0 @@
<template>
<!-- 页脚 -->
<footer class="bg-gray-900 text-gray-300 py-6">
<div class="max-w-6xl mx-auto px-4 flex flex-col md:flex-row justify-between items-center">
<!-- 左侧版权信息 -->
<div class="text-center md:text-left">
<p>© 2025 永平中学校友会. 保留所有权利.</p>
</div>
<div class="">
<p class="mt-1">
Powered by:
<a href="https://tootaio.com" target="_blank" class="font-semibold hover:underline" style="color: #e24545;">
Tootaio Studio
</a>
<span class="mt-1 text-sm text-gray-400">2018 级毕业学长麦祖奕</span>
</p>
</div>
<!-- 右侧社交链接 -->
<div class="flex space-x-4 mt-3 md:mt-0">
<a href="#">
<Icon name="mdi-facebook" />
</a>
<a href="#">
<Icon name="mdi-instagram" />
</a>
<a href="#">
<Icon name="mdi-gmail" />
</a>
</div>
</div>
</footer>
</template>
<script lang="ts" setup>
</script>
<style></style>

View File

@@ -1,25 +0,0 @@
<template>
<!-- 导航栏 -->
<header class="bg-white shadow-md sticky top-0 z-50">
<div class="max-w-6xl mx-auto px-4 py-3 flex justify-between items-center">
<div>
<img class="inline w-16" src="/Logo.svg" alt="YPHS Alumni">
<h1 class="inline text-xl font-bold text-gray-900">
<a href="/" class="ml-4 hover:text-secondary">永平中学校友会</a>
</h1>
</div>
<nav class="space-x-6 hidden md:flex items-center">
<a href="#news" class="hover:text-secondary">新闻</a>
<a href="#events" class="hover:text-secondary">活动</a>
<a href="#donate" class="hover:text-secondary">捐赠未开放</a>
<a href="#about" class="hover:text-secondary">关于</a>
<a href="/join-us"
class="inline-flex items-center gap-2 bg-secondary text-white px-4 py-2 rounded-xl shadow hover:opacity-90">
加入
<Icon name="mdi:account-plus" class="w-5 h-5" />
</a>
</nav>
</div>
</header>
</template>

View File

@@ -0,0 +1,52 @@
<template>
<UModal v-model:open="open" title="撰写新闻" description="在数据库中添加一篇新闻">
<UButton label="撰写新闻" icon="mdi:newspaper-plus" />
<template #body>
<UForm :schema="newsSchema" :state="newsState" @submit="onSubmit">
<UFormField label="新闻标题" name="title">
<UInput v-model="newsState.title" placeholder="请输入新闻标题" class="w-full" />
</UFormField>
<UFormField label="新闻内容" name="contents">
<UTextarea v-model="newsState.content" class="w-full" />
</UFormField>
这里应该是要有一个 Markdown 编辑界面
<div class="flex justify-end gap-2">
<UButton label="Cancel" color="neutral" variant="subtle" @click="open = false" />
<UButton label="Create" color="primary" variant="solid" type="submit" />
</div>
</UForm>
</template>
</UModal>
</template>
<script lang="ts" setup>
import type { FormSubmitEvent } from '@nuxt/ui';
import * as z from 'zod'
const open = ref(false);
const newsSchema = z.object({
title: z.string().min(2, '标题太短了容易产生歧义,请写长一些').max(15, '标题太长了影响阅读体验,请简短一些'),
content: z.string()
})
type NewsSchema = z.output<typeof newsSchema>
const newsState = reactive<Partial<NewsSchema>>({
title: undefined,
content: undefined
})
const toast = useToast();
const onSubmit = async (event: FormSubmitEvent<NewsSchema>) => {
toast.add({
title: "成功",
description: `${event.data.title} 新闻稿已经撰写完毕`,
color: 'success'
})
open.value = false
}
</script>
<style></style>

View File

@@ -1,10 +1,10 @@
<template>
<div>
<!-- 捐赠模块 -->
<section id="donate" class="py-16 text-center bg-[var(--color-primary)]">
<section id="donate" class="py-16 text-center bg-secondary">
<h3 class="text-2xl font-bold text-gray-900 mb-4">支持与捐赠功能未开放</h3>
<p class="max-w-2xl mx-auto text-gray-700 mb-6">您的捐赠将用于奖学金校园建设及校友活动发展感谢您对母校的支持</p>
<a href="#" class="bg-[var(--color-secondary)] text-white px-8 py-3 rounded-xl shadow hover:opacity-90">立即捐赠</a>
<a href="#" class="bg-primary text-white px-8 py-3 rounded-xl shadow hover:opacity-90">立即捐赠</a>
</section>
</div>
</template>

View File

@@ -11,7 +11,7 @@
<h4 class="font-semibold text-lg mb-2">{{ event.title }}</h4>
<p class="text-sm text-gray-600 mb-1">日期{{ useChineseDateFormat(event.date) }}</p>
<p class="text-sm text-gray-600 mb-4">地点{{ event.location }}</p>
<a :href="event.path" class="bg-secondary text-white px-5 py-2 rounded-lg hover:opacity-90">阅读详情</a>
<a :href="event.path" class="bg-primary text-white px-5 py-2 rounded-lg hover:opacity-90">阅读详情</a>
</div>
</div>
</div>

View File

@@ -5,7 +5,7 @@
<h3 class="text-2xl font-bold text-center text-gray-900 mb-6">名人堂</h3>
<div class="grid md:grid-cols-4 gap-6">
<div v-for="person in persons" :key="person.id" class="flex flex-col items-center cursor-pointer transition hover:scale-105 hover:drop-shadow-2xl hover:-translate-y-1" @click="jumpToPersonIntro(person.path)">
<img :src="person.photo" :alt="person.name" class="w-40 rounded-full border-secondary border-4" />
<img :src="person.photo" :alt="person.name" class="w-40 rounded-full border-primary border-4" />
<h4 class="text-lg font-bold">{{ person.name }}</h4>
<p class="text-sm text-gray-500">{{ person.title }}</p>
</div>

View File

@@ -1,7 +1,7 @@
<template>
<div>
<!-- Hero Banner -->
<section class="relative bg-primary py-32 md:py-48 lg:py-64 text-center bg-cover bg-center"
<section class="relative bg-secondary py-32 md:py-48 lg:py-64 text-center bg-cover bg-center"
style="background-image: url('/hero-image.jpg');">
<!-- 遮罩 -->
<div class="absolute inset-0 bg-black/40"></div>
@@ -15,7 +15,7 @@
马来西亚柔佛永平中学校友会官方网站
</p>
<div class="mt-6 space-x-4">
<a href="/join-us" class="bg-secondary text-white px-6 py-3 rounded-xl shadow hover:opacity-90">
<a href="/join-us" class="bg-primary text-white px-6 py-3 rounded-xl shadow hover:opacity-90">
立即加入我们
</a>
<!-- <a href="#donate"

View File

@@ -5,11 +5,11 @@
<h2 class="text-2xl font-bold text-gray-900 mb-6">最新新闻与公告</h2>
<div class="grid md:grid-cols-3 gap-6">
<article v-for="n in news" :key="n.id" @click="jumpToNewsDetail(n.stem)"
class="bg-primary/10 rounded-xl shadow cursor-pointer transition transform hover:-translate-y-1 hover:scale-105 hover:shadow-xl duration-300 ease-in-out">
class="bg-secondary/10 rounded-xl shadow cursor-pointer transition transform hover:-translate-y-1 hover:scale-105 hover:shadow-xl duration-300 ease-in-out">
<img class="rounded-xl" :src="n.cover" :alt="n.title">
<div class="p-5">
<h3 class="font-semibold mb-2">{{ n.title }}</h3>
<div class="px-1 w-max bg-primary/25 border-primary border-2 rounded-xl text-secondary text-sm mb-2">{{
<div class="px-1 w-max bg-secondary/25 border-secondary border-2 rounded-xl text-primary text-sm mb-2">{{
useChineseDateFormat(n.date) }}</div>
<p class="text-sm text-gray-600">{{ n.description }}</p>
</div>