This commit is contained in:
mei 2024-12-13 21:40:52 +08:00
parent f78a466c39
commit f727b212df
11 changed files with 101 additions and 87 deletions

30
Dockerfile Normal file
View File

@ -0,0 +1,30 @@
# Stage 1: Install dependencies
FROM node:18-alpine AS builder
WORKDIR /app
RUN sed -i 's#https\?://dl-cdn.alpinelinux.org/alpine#https://mirrors.tuna.tsinghua.edu.cn/alpine#g' /etc/apk/repositories
RUN apk update
RUN npm config set registry https://registry.npmmirror.com
COPY package*.json ./
RUN npm ci
# Stage 2: Build the applicatio
COPY . .
RUN npm run build
# Stage 3: Production image
FROM node:18-alpine AS runner
RUN mkdir -p /app/data && chmod 755 /app/data
WORKDIR /app
RUN sed -i 's#https\?://dl-cdn.alpinelinux.org/alpine#https://mirrors.tuna.tsinghua.edu.cn/alpine#g' /etc/apk/repositories
RUN apk update
ENV NODE_ENV Production
COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/data/shorturl.db ./data
VOLUME /app/data
# Run App
CMD ["node", "server.js"]

View File

@ -1,17 +1,11 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import yaml from 'yaml'; import yaml from 'yaml';
// 定义活动项的接口
interface ActivityParticipant {
name: string;
}
interface Activity { interface Activity {
title: string; title: string;
coverImage: string; coverImage: string;
description: string; description: string;
date: string; date: string;
participants: ActivityParticipant[];
organizer: string; organizer: string;
} }

View File

@ -1,11 +1,12 @@
import './globals.css' import './globals.css'
import { Inter } from 'next/font/google' import { Inter } from 'next/font/google'
import Navbar from '@/components/Navbar' import Navbar from '@/components/Navbar'
import Footer from '@/components/footer'
const inter = Inter({ subsets: ['latin'] }) const inter = Inter({ subsets: ['latin'] })
export const metadata = { export const metadata = {
title: '影视星河摄影社团', title: '影视星河',
description: '影视星河摄影社团官方网站', description: '影视星河摄影社团官方网站',
} }
@ -19,6 +20,7 @@ export default function RootLayout({
<body className={inter.className}> <body className={inter.className}>
<Navbar /> <Navbar />
{children} {children}
<Footer />
</body> </body>
</html> </html>
) )

View File

@ -36,7 +36,7 @@ export default function Home() {
playsInline playsInline
className="absolute top-0 left-0 w-full h-full object-cover" className="absolute top-0 left-0 w-full h-full object-cover"
> >
<source src="https://c.mmeiblog.cn/d/share/newbili/影视星河片头.mp4" type="video/mp4" /> <source src="https://c.mmeiblog.cn/d/share/newbili/%E5%BD%B1%E8%A7%86%E6%98%9F%E6%B2%B3%E7%89%87%E5%A4%B4.mp4" type="video/mp4" />
</video> </video>
<div className="absolute top-0 left-0 w-full h-full bg-black opacity-50"></div> <div className="absolute top-0 left-0 w-full h-full bg-black opacity-50"></div>
@ -67,13 +67,7 @@ export default function Home() {
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8"> <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{filteredActivities.map((activity, index) => ( {filteredActivities.map((activity, index) => (
<ActivityCard <ActivityCard key={index} activity={activity} />
key={index}
activity={{
...activity,
participants: activity.participants.map(participant => participant.name)
}}
/>
))} ))}
</div> </div>
</div> </div>

View File

@ -2,7 +2,6 @@
import Image from 'next/image' import Image from 'next/image'
import { useState } from 'react' import { useState } from 'react'
import { ChevronDown, ChevronUp } from 'lucide-react'
interface ActivityProps { interface ActivityProps {
activity: { activity: {
@ -10,13 +9,12 @@ interface ActivityProps {
coverImage: string coverImage: string
description: string description: string
date: string date: string
participants: string[]
organizer: string organizer: string
} }
} }
const ActivityCard: React.FC<ActivityProps> = ({ activity }) => { const ActivityCard: React.FC<ActivityProps> = ({ activity }) => {
const [isExpanded, setIsExpanded] = useState(false) const [] = useState(false)
return ( return (
<div className="bg-white shadow-lg rounded-lg overflow-hidden"> <div className="bg-white shadow-lg rounded-lg overflow-hidden">
@ -33,45 +31,6 @@ const ActivityCard: React.FC<ActivityProps> = ({ activity }) => {
<p className="text-gray-600 mb-2">{activity.description}</p> <p className="text-gray-600 mb-2">{activity.description}</p>
<p className="text-sm text-gray-500 mb-2">{activity.date}</p> <p className="text-sm text-gray-500 mb-2">{activity.date}</p>
<p className="text-sm text-gray-500 mb-2">{activity.organizer}</p> <p className="text-sm text-gray-500 mb-2">{activity.organizer}</p>
<div className="mt-4">
<p className="text-sm font-semibold mb-1"></p>
{activity.participants.length <= 3 ? (
<ul className="list-disc list-inside">
{activity.participants.map((participant, index) => (
<li key={index} className="text-sm text-gray-600">{participant}</li>
))}
</ul>
) : (
<div>
<ul className="list-disc list-inside">
{activity.participants.slice(0, 3).map((participant, index) => (
<li key={index} className="text-sm text-gray-600">{participant}</li>
))}
</ul>
{isExpanded && (
<ul className="list-disc list-inside mt-2">
{activity.participants.slice(3).map((participant, index) => (
<li key={index + 3} className="text-sm text-gray-600">{participant}</li>
))}
</ul>
)}
<button
className="mt-2 text-sm text-blue-600 hover:text-blue-800 flex items-center"
onClick={() => setIsExpanded(!isExpanded)}
>
{isExpanded ? (
<>
<ChevronUp className="ml-1 w-4 h-4" />
</>
) : (
<>
({activity.participants.length - 3}) <ChevronDown className="ml-1 w-4 h-4" />
</>
)}
</button>
</div>
)}
</div>
</div> </div>
</div> </div>
) )

View File

@ -115,7 +115,7 @@ export default function Device() {
return ( return (
<div className="container mx-auto bg-white rounded-lg shadow-lg p-6 space-y-8"> <div className="container mx-auto bg-white rounded-lg shadow-lg p-6 space-y-8">
<h1 className="text-3xl font-bold text-center text-indigo-700 mb-8"></h1> <h1 className="text-3xl font-bold text-center text-indigo-700 mb-8"></h1>
<div className="flex justify-center space-x-4 mb-8"> <div className="flex justify-center space-x-4 mb-8">
<label className="bg-blue-500 text-white px-4 py-2 rounded-full cursor-pointer hover:bg-blue-600 transition duration-300"> <label className="bg-blue-500 text-white px-4 py-2 rounded-full cursor-pointer hover:bg-blue-600 transition duration-300">

50
components/footer.tsx Normal file
View File

@ -0,0 +1,50 @@
"use client";
const Footer = () => {
const currentYear = new Date().getFullYear();
return (
<footer className="footer">
<div className="container">
<p>
© {currentYear}
</p>
<p>
:
<a href="http://www.beian.miit.gov.cn" target="_blank" rel="noopener noreferrer">
ICP备12345678号-9
</a>
</p>
{/* 如果有公安备案,可以添加如下行 */}
{/* <p>
<a href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=1234567890123456"
target="_blank" rel="noopener noreferrer">
<img src="/path/to/police/beian.png" alt="公安备案图标"/>
1234567890123456
</a>
</p> */}
</div>
<style jsx>{`
.footer {
background-color: #f8f9fa;
padding: 1rem 0;
text-align: center;
color: #6c757d;
}
.container {
margin: 0 auto;
max-width: 1200px;
}
.footer a {
color: #007bff;
text-decoration: none;
}
.footer a:hover {
text-decoration: underline;
}
`}</style>
</footer>
);
};
export default Footer;

View File

@ -1,47 +1,26 @@
activities: activities:
- title: 月度摄影漫步 - title: 月度摄影漫步
coverImage: /images/photo-walk.jpg coverImage: https://api.mmeiblog.cn/files/img/832582a1.jpg
description: 加入我们的月度城市摄影漫步。捕捉城市景观和街头场景。 description: 加入我们的月度城市摄影漫步。捕捉城市景观和街头场景。
date: 2023-07-15 date: 2023-07-15
participants:
- 张三
- 李四
- 王五
- 赵六
- 钱七
organizer: 刘老师 organizer: 刘老师
- title: 人像摄影工作坊 - title: 人像摄影工作坊
coverImage: /images/portrait-workshop.jpg coverImage: https://api.mmeiblog.cn/files/img/01J95FFQDMJ1HSMQK7RDAD8S6A.jpg
description: 在专业摄影师的指导下,学习人像摄影的艺术。 description: 在专业摄影师的指导下,学习人像摄影的艺术。
date: 2023-07-22 date: 2023-07-22
participants:
- 陈一
- 周二
- 吴三
organizer: 黄教授 organizer: 黄教授
- title: 自然摄影探险 - title: 自然摄影探险
coverImage: /images/nature-expedition.jpg coverImage: https://api.mmeiblog.cn/files/img/1718019633883.jpg
description: 探索大自然,在周末的自然摄影探险中捕捉壮丽的风景。 description: 探索大自然,在周末的自然摄影探险中捕捉壮丽的风景。
date: 2023-08-05 date: 2023-08-05
participants:
- 郑八
- 孙九
- 杨十
- 朱十一
- 秦十二
- 许十三
organizer: 林导师 organizer: 林导师
- title: 照片编辑大师班 - title: 照片编辑大师班
coverImage: /images/editing-masterclass.jpg coverImage: /images/editing-masterclass.jpg
description: 通过我们的照片编辑大师班,使用行业标准软件提升您的后期处理技能。 description: 通过我们的照片编辑大师班,使用行业标准软件提升您的后期处理技能。
date: 2023-08-12 date: 2023-08-12
participants:
- 冯一
- 蒋二
- 沈三
organizer: 马老师 organizer: 马老师
showcase: showcase:

11
next.config.js Normal file
View File

@ -0,0 +1,11 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
domains: ['api.mmeiblog.cn'],
},
output: 'standalone',
}
module.exports = nextConfig

View File

@ -1,4 +0,0 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default nextConfig;

View File

@ -3,7 +3,6 @@ export interface Activity {
coverImage: string coverImage: string
description: string description: string
date: string date: string
participants: string[]
organizer: string organizer: string
} }