- Expanded CourseResourceType to include "resource" and "temp". - Renamed ICourseResource to IResource and updated its properties for consistency. - Introduced ICreateResource type for resource creation. - Modified ICourseSection and ICourseChapter interfaces to use the new IResource type and updated property names for camelCase. - Implemented uploadFile function in file API for handling file uploads. - Created ResourceUploader component for uploading resources with validation and feedback. - Developed Card component for displaying course class details and managing student enrollment. - Added AlertDialog components for consistent alert dialog UI. - Enhanced table components for better data presentation and management. - Implemented preview page for displaying various resource types based on file extension.
168 lines
4.6 KiB
Vue
168 lines
4.6 KiB
Vue
<script lang="ts" setup>
|
||
import { toTypedSchema } from "@vee-validate/zod";
|
||
import { useForm } from "vee-validate";
|
||
import { toast } from "vue-sonner";
|
||
import { z } from "zod";
|
||
import {
|
||
createClass,
|
||
getClassListByCourse,
|
||
getCourseDetail,
|
||
} from "~/api/course";
|
||
|
||
definePageMeta({
|
||
requiresAuth: true,
|
||
});
|
||
|
||
const {
|
||
params: { id: courseId },
|
||
} = useRoute();
|
||
|
||
// const loginState = useLoginState();
|
||
const course = await getCourseDetail(courseId as string);
|
||
|
||
const { data: classes, refresh: refreshClasses } = useAsyncData(() =>
|
||
getClassListByCourse(parseInt(courseId as string))
|
||
);
|
||
|
||
const createClassDialogOpen = ref(false);
|
||
|
||
const createClassSchema = toTypedSchema(
|
||
z.object({
|
||
className: z
|
||
.string()
|
||
.min(2, "班级名称至少2个字符")
|
||
.max(32, "最大长度32个字符"),
|
||
notes: z.string().max(200, "班级介绍最大长度200个字符"),
|
||
courseId: z.number().min(1, "课程ID不能为空"),
|
||
})
|
||
);
|
||
|
||
const createClassForm = useForm({
|
||
validationSchema: createClassSchema,
|
||
initialValues: {
|
||
className: "",
|
||
notes: "",
|
||
courseId: Number(courseId),
|
||
},
|
||
});
|
||
|
||
const onCreateClassSubmit = createClassForm.handleSubmit((values) => {
|
||
toast.promise(createClass(values), {
|
||
loading: "正在创建班级...",
|
||
success: () => {
|
||
createClassForm.resetForm();
|
||
createClassDialogOpen.value = false;
|
||
return "创建班级成功";
|
||
},
|
||
error: () => {
|
||
return "创建班级失败";
|
||
},
|
||
finally: () => {
|
||
refreshClasses();
|
||
},
|
||
});
|
||
});
|
||
|
||
// const onDeleteClass = (classId: number) => {
|
||
// toast.promise(deleteCourseClass(classId), {
|
||
// loading: "正在删除班级...",
|
||
// success: () => {
|
||
// refreshClasses();
|
||
// return "删除班级成功";
|
||
// },
|
||
// error: () => {
|
||
// return "删除班级失败";
|
||
// },
|
||
// });
|
||
// };
|
||
</script>
|
||
|
||
<template>
|
||
<div class="flex flex-col gap-4 px-4 py-2">
|
||
<div class="flex justify-between items-start">
|
||
<h1 class="text-xl font-medium">
|
||
课程班级管理
|
||
<span class="block text-sm text-muted-foreground">
|
||
课程负责人:{{ course.data.teacherName || "未知" }}
|
||
</span>
|
||
</h1>
|
||
<div class="flex items-center gap-4">
|
||
<Dialog v-model:open="createClassDialogOpen">
|
||
<DialogTrigger as-child>
|
||
<Button
|
||
variant="secondary"
|
||
size="sm"
|
||
class="flex items-center gap-1"
|
||
>
|
||
<Icon name="tabler:plus" size="16px" />
|
||
<span>创建班级</span>
|
||
</Button>
|
||
</DialogTrigger>
|
||
<DialogContent class="sm:max-w-[425px]">
|
||
<DialogHeader>
|
||
<DialogTitle>创建班级</DialogTitle>
|
||
</DialogHeader>
|
||
|
||
<form
|
||
id="create-class-form"
|
||
autocomplete="off"
|
||
class="space-y-2"
|
||
@submit="onCreateClassSubmit"
|
||
>
|
||
<FormField v-slot="{ componentField }" name="className">
|
||
<FormItem v-auto-animate>
|
||
<FormLabel>班级名称</FormLabel>
|
||
<FormControl>
|
||
<Input
|
||
type="text"
|
||
placeholder="请输入班级名称"
|
||
v-bind="componentField"
|
||
/>
|
||
</FormControl>
|
||
<FormMessage />
|
||
</FormItem>
|
||
</FormField>
|
||
<FormField v-slot="{ componentField }" name="notes">
|
||
<FormItem v-auto-animate>
|
||
<FormLabel>班级介绍</FormLabel>
|
||
<FormControl>
|
||
<Textarea
|
||
placeholder="请输入班级介绍"
|
||
v-bind="componentField"
|
||
/>
|
||
</FormControl>
|
||
<FormMessage />
|
||
</FormItem>
|
||
</FormField>
|
||
<input type="hidden" name="courseId" />
|
||
</form>
|
||
|
||
<DialogFooter>
|
||
<Button type="submit" form="create-class-form">创建</Button>
|
||
</DialogFooter>
|
||
</DialogContent>
|
||
</Dialog>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
v-if="classes?.data && classes.data.length > 0"
|
||
class="grid gap-6 grid-cols-1 sm:grid-cols-2 2xl:grid-cols-4"
|
||
>
|
||
<CourseClassCard
|
||
v-for="classItem in classes.data"
|
||
:key="classItem.classId"
|
||
:class-item="classItem"
|
||
/>
|
||
</div>
|
||
<EmptyScreen
|
||
v-else
|
||
title="暂无班级"
|
||
description="课程下没有班级,请创建新的班级"
|
||
icon="fluent-color:people-list-24"
|
||
/>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped></style>
|