221 lines
5.6 KiB
Vue
221 lines
5.6 KiB
Vue
<script lang="ts" setup>
|
||
import { toTypedSchema } from '@vee-validate/zod'
|
||
import * as z from 'zod'
|
||
import { useForm } from 'vee-validate'
|
||
import { toast } from 'vue-sonner'
|
||
import {
|
||
createCourseChatper,
|
||
deleteCourseChatper,
|
||
deleteCourseSection,
|
||
deleteResource,
|
||
getCourseChatpers,
|
||
} from '~/api/course'
|
||
|
||
definePageMeta({
|
||
requiresAuth: true,
|
||
})
|
||
|
||
const {
|
||
params: { id: courseId },
|
||
} = useRoute()
|
||
|
||
const { data: chapters, refresh: refreshChapters } = useAsyncData(() =>
|
||
getCourseChatpers(parseInt(courseId as string)),
|
||
)
|
||
|
||
const createChatperDialogOpen = ref(false)
|
||
|
||
const createChatperSchema = toTypedSchema(
|
||
z.object({
|
||
title: z.string().min(2, '章节名称至少2个字符').max(32, '最大长度32个字符'),
|
||
courseId: z.number().min(1, '课程ID不能为空'),
|
||
}),
|
||
)
|
||
|
||
const createChatperForm = useForm({
|
||
validationSchema: createChatperSchema,
|
||
initialValues: {
|
||
title: '',
|
||
courseId: Number(courseId),
|
||
},
|
||
})
|
||
|
||
const onCreateChapterSubmit = createChatperForm.handleSubmit((values) => {
|
||
toast.promise(createCourseChatper(values), {
|
||
loading: '正在创建章节...',
|
||
success: () => {
|
||
refreshChapters()
|
||
createChatperForm.resetForm()
|
||
createChatperDialogOpen.value = false
|
||
return '创建章节成功'
|
||
},
|
||
error: () => {
|
||
return '创建章节失败'
|
||
},
|
||
})
|
||
})
|
||
|
||
const onDeleteChatper = (chapterId: number) => {
|
||
toast.promise(deleteCourseChatper(chapterId), {
|
||
loading: '正在删除章节...',
|
||
success: () => {
|
||
refreshChapters()
|
||
return '删除章节成功'
|
||
},
|
||
error: () => {
|
||
return '删除章节失败'
|
||
},
|
||
})
|
||
}
|
||
|
||
const onDeleteSection = (sectionId: number) => {
|
||
toast.promise(deleteCourseSection(sectionId), {
|
||
loading: '正在删除小节...',
|
||
success: () => {
|
||
refreshChapters()
|
||
return '删除小节成功'
|
||
},
|
||
error: () => {
|
||
return '删除小节失败'
|
||
},
|
||
})
|
||
}
|
||
|
||
const onDeleteResource = (resourceId: number) => {
|
||
toast.promise(deleteResource(resourceId), {
|
||
loading: '正在删除资源...',
|
||
success: () => {
|
||
refreshChapters()
|
||
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">
|
||
课程章节管理
|
||
</h1>
|
||
<div class="flex items-center gap-4">
|
||
<Tooltip :delay-duration="0">
|
||
<TooltipTrigger>
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
class="flex items-center gap-2 text-muted-foreground"
|
||
>
|
||
<div class="w-2 h-2 rounded-full bg-emerald-500" />
|
||
<span>训练完成</span>
|
||
</Button>
|
||
</TooltipTrigger>
|
||
<TooltipContent>点击进行召回测试</TooltipContent>
|
||
</Tooltip>
|
||
|
||
<Dialog v-model:open="createChatperDialogOpen">
|
||
<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-chapter-form"
|
||
autocomplete="off"
|
||
class="space-y-2"
|
||
@submit="onCreateChapterSubmit"
|
||
>
|
||
<FormField
|
||
v-slot="{ componentField }"
|
||
name="title"
|
||
>
|
||
<FormItem v-auto-animate>
|
||
<FormLabel>章节名称</FormLabel>
|
||
<FormControl>
|
||
<Input
|
||
type="text"
|
||
placeholder="请输入章节名称"
|
||
v-bind="componentField"
|
||
/>
|
||
</FormControl>
|
||
<FormMessage />
|
||
</FormItem>
|
||
</FormField>
|
||
<input
|
||
type="hidden"
|
||
name="courseId"
|
||
/>
|
||
</form>
|
||
|
||
<DialogFooter>
|
||
<Button
|
||
type="submit"
|
||
form="create-chapter-form"
|
||
>
|
||
创建
|
||
</Button>
|
||
</DialogFooter>
|
||
</DialogContent>
|
||
</Dialog>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
v-if="chapters?.rows && chapters.rows.length > 0"
|
||
class="flex flex-col gap-8"
|
||
>
|
||
<!-- chatpter -->
|
||
<CourseChapter
|
||
v-for="chapter in chapters?.rows"
|
||
:key="chapter.id"
|
||
:tag="`${chapter.id}`"
|
||
:chapter="chapter"
|
||
@refresh="refreshChapters"
|
||
@delete-chapter="onDeleteChatper"
|
||
@delete-section="onDeleteSection"
|
||
@delete-resource="onDeleteResource"
|
||
/>
|
||
</div>
|
||
<EmptyScreen
|
||
v-else
|
||
title="暂无章节"
|
||
icon="fluent-color:document-add-24"
|
||
>
|
||
<div class="flex flex-col gap-2">
|
||
<p class="text-sm text-muted-foreground">
|
||
课程章节列表为空,先创建章节吧
|
||
</p>
|
||
<Button
|
||
variant="outline"
|
||
size="sm"
|
||
@click="createChatperDialogOpen = true"
|
||
>
|
||
<Icon
|
||
name="tabler:plus"
|
||
size="16px"
|
||
/>
|
||
<span>添加章节</span>
|
||
</Button>
|
||
</div>
|
||
</EmptyScreen>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped></style>
|