Skip to content
This repository was archived by the owner on Jan 7, 2026. It is now read-only.

Commit bef61c1

Browse files
committed
feat: add category meta
1 parent 5e59e13 commit bef61c1

File tree

9 files changed

+108
-9
lines changed

9 files changed

+108
-9
lines changed

app/app.config.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export default defineAppConfig({
2+
videoCategories: [
3+
{ label: '生活', value: 'life' },
4+
{ label: '科技', value: 'tech' },
5+
{ label: '娱乐', value: 'entertainment' },
6+
{ label: '音乐', value: 'music' },
7+
{ label: '教育', value: 'education' },
8+
],
9+
});

app/pages/index.vue

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
<h3 class="video-title">{{ video.title }}</h3>
2424
<div class="video-meta">
2525
<span class="uploader">{{ video.uploader }}</span>
26+
<el-tag
27+
size="small"
28+
type="info"
29+
class="category-tag"
30+
disable-transitions
31+
>{{ getCategoryLabel(video.category) }}</el-tag
32+
>
2633
</div>
2734
</div>
2835
</el-card>
@@ -34,6 +41,13 @@
3441
<script setup lang="ts">
3542
const { data: videos } = await useFetch('/api/videos');
3643
44+
const appConfig = useAppConfig();
45+
const videoCategories = appConfig.videoCategories;
46+
47+
const getCategoryLabel = (value?: string) => {
48+
return videoCategories?.find((c) => c.value === value)?.label || value;
49+
};
50+
3751
const formatDuration = (seconds: number) => {
3852
const m = Math.floor(seconds / 60);
3953
const s = Math.floor(seconds % 60);
@@ -93,12 +107,19 @@ const formatDuration = (seconds: number) => {
93107
height: 2.8em;
94108
overflow: hidden;
95109
display: -webkit-box;
96-
-webkit-line-clamp: 2;
110+
line-clamp: 2;
97111
-webkit-box-orient: vertical;
98112
}
99113
.video-meta {
100114
margin-top: 8px;
101115
font-size: 13px;
102116
color: #909399;
117+
display: flex;
118+
justify-content: space-between;
119+
align-items: center;
120+
gap: 8px;
121+
}
122+
.category-tag {
123+
font-size: 10px;
103124
}
104125
</style>

app/pages/upload.vue

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,21 @@
88
<el-form-item label="视频标题">
99
<el-input v-model="title" placeholder="请输入视频标题" required />
1010
</el-form-item>
11+
<el-form-item label="视频分类">
12+
<el-select
13+
v-model="category"
14+
placeholder="请选择视频分类"
15+
style="width: 100%"
16+
required
17+
>
18+
<el-option
19+
v-for="item in videoCategories"
20+
:key="item.value"
21+
:label="item.label"
22+
:value="item.value"
23+
/>
24+
</el-select>
25+
</el-form-item>
1126
<el-form-item label="视频文件">
1227
<div class="file-input-wrapper">
1328
<input
@@ -69,15 +84,19 @@
6984
import { Picture, VideoCamera } from '@element-plus/icons-vue';
7085
7186
const title = ref('');
87+
const category = ref('');
7288
const videoFile = ref<File | null>(null);
7389
const thumbFile = ref<File | null>(null);
7490
const loading = ref(false);
7591
const token = useCookie('token');
7692
93+
const appConfig = useAppConfig();
94+
const videoCategories = appConfig.videoCategories;
95+
7796
definePageMeta({
7897
middleware: () => {
7998
if (!token.value) {
80-
if (process.client) {
99+
if (import.meta.client) {
81100
ElMessage.warning('请先登录');
82101
}
83102
return navigateTo('/login');
@@ -101,6 +120,7 @@ const handleUpload = async () => {
101120
102121
const formData = new FormData();
103122
formData.append('title', title.value);
123+
formData.append('category', category.value);
104124
formData.append('video', videoFile.value);
105125
formData.append('thumbnail', thumbFile.value);
106126
@@ -114,7 +134,7 @@ const handleUpload = async () => {
114134
});
115135
ElMessage.success('上传成功');
116136
navigateTo('/');
117-
} catch (error: any) {
137+
} catch (error) {
118138
ElMessage.error(error.data?.statusMessage || '上传失败');
119139
} finally {
120140
loading.value = false;

app/pages/video/[id].vue

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,20 @@
1111
<el-avatar :size="40" icon="UserFilled" />
1212
<div class="uploader-info">
1313
<div class="uploader-name">{{ video.uploader }}</div>
14-
<div class="video-meta">发布于最近</div>
14+
<div class="video-meta">
15+
<span
16+
>发布于
17+
{{
18+
new Date(video.created_at).toLocaleString('zh-CN', {
19+
dateStyle: 'short',
20+
timeStyle: 'medium',
21+
})
22+
}}</span
23+
>
24+
<el-tag size="small" type="info" class="category-tag">{{
25+
getCategoryLabel(video.category)
26+
}}</el-tag>
27+
</div>
1528
</div>
1629
</div>
1730
</el-card>
@@ -21,6 +34,22 @@
2134
<script setup lang="ts">
2235
const route = useRoute();
2336
const { data: video } = await useFetch(`/api/videos/${route.params.id}`);
37+
38+
const appConfig = useAppConfig();
39+
const videoCategories = appConfig.videoCategories;
40+
41+
const getCategoryLabel = (value?: string) => {
42+
return videoCategories?.find((c) => c.value === value)?.label || value;
43+
};
44+
45+
useHead(() => ({
46+
meta: [
47+
{
48+
name: 'adflux-page-category',
49+
content: getCategoryLabel(video.value?.category),
50+
},
51+
],
52+
}));
2453
</script>
2554

2655
<style scoped>
@@ -62,5 +91,11 @@ const { data: video } = await useFetch(`/api/videos/${route.params.id}`);
6291
font-size: 12px;
6392
color: #909399;
6493
margin-top: 4px;
94+
display: flex;
95+
align-items: center;
96+
gap: 8px;
97+
}
98+
.category-tag {
99+
font-size: 10px;
65100
}
66101
</style>

app/types/app-config.d.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* eslint-disable no-unused-vars */
2+
export {};
3+
4+
declare module 'nuxt/schema' {
5+
interface AppConfigInput {
6+
videoCategories?: { label: string; value: string }[];
7+
}
8+
interface AppConfig {
9+
videoCategories: { label: string; value: string }[];
10+
}
11+
}

server/api/upload.post.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ export default defineEventHandler(async (event) => {
3939
const videoFile = Array.isArray(files.video) ? files.video[0] : files.video;
4040
const thumbnailFile = Array.isArray(files.thumbnail) ? files.thumbnail[0] : files.thumbnail;
4141
const title = Array.isArray(fields.title) ? fields.title[0] : fields.title;
42+
const category = Array.isArray(fields.category) ? fields.category[0] : fields.category;
4243

43-
if (!videoFile || !thumbnailFile || !title) {
44-
throw createError({ statusCode: 400, statusMessage: '缺少视频、封面或标题' });
44+
if (!videoFile || !thumbnailFile || !title || !category) {
45+
throw createError({ statusCode: 400, statusMessage: '缺少视频、封面、标题或分类' });
4546
}
4647

4748
const videoExt = path.extname(videoFile.originalFilename || '.mp4');
@@ -84,8 +85,8 @@ export default defineEventHandler(async (event) => {
8485
const thumbUrl = `/uploads/${thumbFilename}`;
8586

8687
const result = await sql<{ id: number }[]>`
87-
INSERT INTO videos (title, url, thumbnail_url, duration, uploader_id)
88-
VALUES (${title}, ${videoUrl}, ${thumbUrl}, ${duration}, ${user.id})
88+
INSERT INTO videos (title, url, thumbnail_url, duration, category, uploader_id)
89+
VALUES (${title}, ${videoUrl}, ${thumbUrl}, ${duration}, ${category}, ${user.id})
8990
RETURNING id
9091
`;
9192

server/api/videos/index.get.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export default defineEventHandler(async () => {
22
// Randomize order
33
const videos = await sql<VideoWithUploader[]>`
4-
SELECT v.id, v.title, v.thumbnail_url, v.duration, u.username as uploader
4+
SELECT v.id, v.title, v.thumbnail_url, v.duration, v.category, u.username as uploader
55
FROM videos v
66
JOIN users u ON v.uploader_id = u.id
77
ORDER BY RANDOM()

server/plugins/setup.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export default defineNitroPlugin(async () => {
2222
url TEXT NOT NULL,
2323
thumbnail_url TEXT NOT NULL,
2424
duration FLOAT NOT NULL,
25+
category VARCHAR(50),
2526
uploader_id INTEGER REFERENCES users(id),
2627
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
2728
);

server/utils/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface Video {
1111
url: string;
1212
thumbnail_url: string;
1313
duration: number;
14+
category: string;
1415
uploader_id: number;
1516
created_at: Date;
1617
}

0 commit comments

Comments
 (0)