- 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.
123 lines
3.4 KiB
Vue
123 lines
3.4 KiB
Vue
<script lang="ts" setup>
|
|
import { toast } from "vue-sonner";
|
|
import { addResourceToSection } from "~/api/course";
|
|
import type { FetchError, ICourseSection, IResource } from "~/types";
|
|
|
|
const props = defineProps<{
|
|
tag?: string;
|
|
section: ICourseSection;
|
|
}>();
|
|
|
|
const emit = defineEmits<{
|
|
refresh: [];
|
|
"delete-section": [sectionId: number];
|
|
"delete-resource": [resourceId: number];
|
|
}>();
|
|
|
|
const isUploadOpen = ref(false);
|
|
|
|
const handleDeleteSection = () => {
|
|
if (props.section.resources.length > 0) {
|
|
const confirmDelete = confirm(
|
|
"该小节下有资源,删除后将无法恢复,是否继续?"
|
|
);
|
|
if (!confirmDelete) return;
|
|
}
|
|
emit("delete-section", props.section.id);
|
|
};
|
|
|
|
const onCreateResource = (resource: IResource) => {
|
|
toast.promise(
|
|
addResourceToSection({
|
|
sectionId: props.section.id,
|
|
resourceId: resource.id,
|
|
}),
|
|
{
|
|
loading: "添加资源中...",
|
|
success: () => {
|
|
isUploadOpen.value = false;
|
|
return "添加资源成功";
|
|
},
|
|
error: (error: FetchError) => {
|
|
return `添加资源失败: ${error.message}`;
|
|
},
|
|
finally: () => {
|
|
emit("refresh");
|
|
},
|
|
}
|
|
);
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="rounded-md border border-muted overflow-hidden">
|
|
<div
|
|
class="w-full px-4 py-2.5 bg-muted dark:bg-muted/50 flex justify-between items-center group/section"
|
|
>
|
|
<div class="flex items-center gap-2">
|
|
<div class="w-10 flex justify-center">
|
|
<Badge variant="outline" class="text-xs bg-background">
|
|
<span>
|
|
{{
|
|
tag || section.resources.length > 0
|
|
? section.resources.length
|
|
: 0
|
|
}}
|
|
</span>
|
|
</Badge>
|
|
</div>
|
|
<h2 class="inline-flex items-center gap-2 text-sm font-medium">
|
|
<!-- <span>1.1</span> -->
|
|
<span class="text-ellipsis line-clamp-1">{{ section.title }}</span>
|
|
</h2>
|
|
</div>
|
|
<div
|
|
class="flex items-center gap-2 mr-8 opacity-0 group-hover/section:opacity-100"
|
|
>
|
|
<Button
|
|
variant="link"
|
|
size="xs"
|
|
class="flex items-center gap-1 text-muted-foreground"
|
|
@click="isUploadOpen = true"
|
|
>
|
|
<Icon name="tabler:plus" size="16px" />
|
|
<span>添加资源</span>
|
|
</Button>
|
|
<Button
|
|
variant="link"
|
|
size="xs"
|
|
class="flex items-center gap-1 text-red-500"
|
|
@click="handleDeleteSection"
|
|
>
|
|
<Icon name="tabler:trash" size="16px" />
|
|
<span>删除</span>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<div v-if="section.resources.length > 0" class="flex flex-col gap-2 py-2">
|
|
<!-- Resource -->
|
|
<CourseResource
|
|
v-for="resource in section.resources"
|
|
:key="resource.id"
|
|
:resource="resource"
|
|
@refresh="emit('refresh')"
|
|
@delete-resource="emit('delete-resource', resource.id)"
|
|
/>
|
|
</div>
|
|
<!-- <div
|
|
v-else
|
|
class="py-4 flex items-center justify-center gap-2 text-muted-foreground"
|
|
>
|
|
<Icon name="tabler:circle-minus" size="16px" />
|
|
<span class="text-sm">该小节下暂无资源</span>
|
|
</div> -->
|
|
<ResourceUploader
|
|
v-model="isUploadOpen"
|
|
@refresh="emit('refresh')"
|
|
@on-create="onCreateResource"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped></style>
|