258 lines
8.1 KiB
Vue
258 lines
8.1 KiB
Vue
<script lang="ts" setup>
|
|
import { toast } from "vue-sonner";
|
|
import { toTypedSchema } from "@vee-validate/zod";
|
|
import * as z from "zod";
|
|
import type { ICourse } from "~/types";
|
|
|
|
definePageMeta({
|
|
layout: "no-sidebar",
|
|
});
|
|
|
|
useHead({
|
|
title: "课程中心",
|
|
});
|
|
|
|
const courseList = ref<ICourse[]>([
|
|
{
|
|
id: "1",
|
|
title: "传感器应用技术",
|
|
description: "学习传感器的基本原理及其应用。",
|
|
thumbnail_url:
|
|
"https://static-xsh.oss-cn-chengdu.aliyuncs.com/file/2024-08-05/9a8b2ed8d66340d43a4b840f58597f13.png",
|
|
school_name: "电子科技大学",
|
|
teacher_name: "张三",
|
|
semester: "2023-2024-1",
|
|
is_published: true,
|
|
created_at: 1690000000,
|
|
updated_at: 1690000000,
|
|
},
|
|
{
|
|
id: "2",
|
|
title: "人工智能导论",
|
|
description: "探索人工智能的基本概念和应用。",
|
|
thumbnail_url:
|
|
"https://static-xsh.oss-cn-chengdu.aliyuncs.com/file/2024-08-05/9a8b2ed8d66340d43a4b840f58597f13.png",
|
|
school_name: "清华大学",
|
|
teacher_name: "李四",
|
|
semester: "2023-2024-2",
|
|
is_published: false,
|
|
created_at: 1691000000,
|
|
updated_at: 1691000000,
|
|
},
|
|
]);
|
|
|
|
/**
|
|
* 生成学期列表
|
|
* @param years - 后推年数
|
|
* @returns 学期列表
|
|
*/
|
|
const getSemesters = (years: number) => {
|
|
const currentYear = new Date().getFullYear() - 1;
|
|
const semesters = [];
|
|
for (let i = 0; i < years + 1; i++) {
|
|
const year = currentYear + i;
|
|
semesters.push(`${year}-${year + 1}-1`, `${year}-${year + 1}-2`);
|
|
}
|
|
return semesters;
|
|
};
|
|
|
|
const courseFormSchema = toTypedSchema(
|
|
z.object({
|
|
courseName: z.string().min(4).max(32),
|
|
schoolName: z.string().min(4).max(32),
|
|
teacherName: z.string().min(2).max(12),
|
|
semester: z.enum([...getSemesters(3)] as [string, ...string[]]),
|
|
})
|
|
);
|
|
|
|
const folderFormSchema = toTypedSchema(
|
|
z.object({
|
|
folderName: z.string().min(2).max(32),
|
|
})
|
|
);
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const onCourseSubmit = (values: any) => {
|
|
toast("submit data:", {
|
|
description: JSON.stringify(values, null, 2),
|
|
});
|
|
};
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const onFolderSubmit = (values: any) => {
|
|
toast("submit data:", {
|
|
description: JSON.stringify(values, null, 2),
|
|
});
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="container mx-auto flex flex-col gap-8">
|
|
<div class="flex justify-between items-center">
|
|
<div class="flex items-center gap-2">
|
|
<Form
|
|
v-slot="{ handleSubmit }"
|
|
as=""
|
|
keep-values
|
|
:validation-schema="courseFormSchema"
|
|
>
|
|
<Dialog>
|
|
<DialogTrigger as-child>
|
|
<Button variant="secondary" size="sm">
|
|
<Icon name="tabler:plus" size="16px" />
|
|
新建课程
|
|
</Button>
|
|
</DialogTrigger>
|
|
<DialogContent class="sm:max-w-[425px]">
|
|
<DialogHeader>
|
|
<DialogTitle>创建课程</DialogTitle>
|
|
<DialogDescription>
|
|
课程创建后,您可以在课程中添加章节等内容。
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<form
|
|
id="createCourseForm"
|
|
autocomplete="off"
|
|
class="space-y-2"
|
|
@submit="handleSubmit($event, onCourseSubmit)"
|
|
>
|
|
<FormField v-slot="{ componentField }" name="courseName">
|
|
<FormItem>
|
|
<FormLabel>课程名称</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
type="text"
|
|
placeholder="请输入课程名称"
|
|
v-bind="componentField"
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
</FormField>
|
|
<FormField v-slot="{ componentField }" name="schoolName">
|
|
<FormItem>
|
|
<FormLabel>学校名称</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
type="text"
|
|
placeholder="请输入院校名称"
|
|
v-bind="componentField"
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
</FormField>
|
|
<FormField v-slot="{ componentField }" name="teacherName">
|
|
<FormItem>
|
|
<FormLabel>教师名称</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
type="text"
|
|
placeholder="请输入教师名称"
|
|
v-bind="componentField"
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
</FormField>
|
|
<FormField v-slot="{ componentField }" name="semester">
|
|
<FormItem>
|
|
<FormLabel>学期</FormLabel>
|
|
<FormControl>
|
|
<Select v-bind="componentField">
|
|
<FormControl>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="请选择学期" />
|
|
</SelectTrigger>
|
|
</FormControl>
|
|
<SelectContent>
|
|
<SelectGroup>
|
|
<SelectItem
|
|
v-for="semester in getSemesters(3)"
|
|
:key="semester"
|
|
:value="semester"
|
|
>
|
|
{{ semester }}
|
|
</SelectItem>
|
|
</SelectGroup>
|
|
</SelectContent>
|
|
</Select>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
</FormField>
|
|
</form>
|
|
|
|
<DialogFooter>
|
|
<Button type="submit" form="createCourseForm">创建</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</Form>
|
|
|
|
<Form
|
|
v-slot="{ handleSubmit }"
|
|
as=""
|
|
keep-values
|
|
:validation-schema="folderFormSchema"
|
|
>
|
|
<Dialog>
|
|
<DialogTrigger as-child>
|
|
<Button variant="secondary" size="sm">
|
|
<Icon name="tabler:folder-plus" size="16px" />
|
|
新建文件夹
|
|
</Button>
|
|
</DialogTrigger>
|
|
<DialogContent class="sm:max-w-[425px]">
|
|
<DialogHeader>
|
|
<DialogTitle>创建文件夹</DialogTitle>
|
|
<DialogDescription>
|
|
可以将多门课程收纳在文件夹中
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<form
|
|
id="createCourseForm"
|
|
autocomplete="off"
|
|
class="space-y-2"
|
|
@submit="handleSubmit($event, onFolderSubmit)"
|
|
>
|
|
<FormField v-slot="{ componentField }" name="folderName">
|
|
<FormItem>
|
|
<FormLabel>文件夹名称</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
type="text"
|
|
placeholder="请输入文件夹名称"
|
|
v-bind="componentField"
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
</FormField>
|
|
</form>
|
|
|
|
<DialogFooter>
|
|
<Button type="submit" form="createCourseForm">创建</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</Form>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<Button variant="secondary" size="sm">删除课程</Button>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-5 gap-8">
|
|
<CourseCard
|
|
v-for="course in courseList"
|
|
:key="course.id"
|
|
:data="course"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped></style>
|