fix: update home page background image and remove unnecessary redirect code chore: update pnpm lock file with new dependencies for auto-animate and svg spinners delete: remove unused images from public directory refactor: modify course and user types for better clarity and structure feat: implement course API with CRUD operations and teacher team management feat: create user authentication page with login functionality and validation feat: add login state management with Pinia for user session handling style: create reusable UI components for cards and tabs chore: implement HTTP utility for API requests with error handling
126 lines
2.9 KiB
Vue
126 lines
2.9 KiB
Vue
<script lang="ts" setup>
|
|
import type { IResponse } from "~/api";
|
|
import { getCourseDetail } from "~/api/course";
|
|
import type { SidebarNavGroup } from "~/components/app/Sidebar.vue";
|
|
import { topbarNavDefaults } from "~/components/app/Topbar.vue";
|
|
import type { ICourse } from "~/types";
|
|
|
|
const {
|
|
fullPath,
|
|
params: { id },
|
|
} = useRoute();
|
|
const router = useRouter();
|
|
|
|
// const course = await getCourseDetail(id as string);
|
|
|
|
const {
|
|
data: course,
|
|
status: courseStatus,
|
|
error: courseError,
|
|
} = await useAsyncData<
|
|
IResponse<{
|
|
data: ICourse;
|
|
}>,
|
|
IResponse
|
|
>(() => getCourseDetail(id as string));
|
|
|
|
useHead({
|
|
title: `${course.value?.data.courseName || '课程不存在'} - 课程管理`,
|
|
});
|
|
|
|
definePageMeta({
|
|
requiresAuth: true,
|
|
});
|
|
|
|
const sideNav: SidebarNavGroup[] = [
|
|
{
|
|
items: [
|
|
{
|
|
title: "课程章节",
|
|
url: `/course/${id}/chapters`,
|
|
icon: "tabler:books",
|
|
},
|
|
{
|
|
title: "教师团队",
|
|
url: `/course/${id}/team`,
|
|
icon: "tabler:users-group",
|
|
},
|
|
{
|
|
title: "学生班级",
|
|
url: `/course/${id}/classes`,
|
|
icon: "tabler:school",
|
|
},
|
|
{
|
|
title: "学生评价",
|
|
url: `/course/${id}/evaluation`,
|
|
icon: "tabler:mood-smile",
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
const topNav = [
|
|
{
|
|
title: "课程管理",
|
|
to: `/course/${id}`,
|
|
icon: "tabler:layout-dashboard",
|
|
},
|
|
...topbarNavDefaults.slice(1),
|
|
];
|
|
|
|
onMounted(() => {
|
|
if (fullPath === `/course/${id}`) {
|
|
router.replace(`/course/${id}/chapters`);
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<AppPageWithSidebar :sidebar-nav="sideNav">
|
|
<template #topbar>
|
|
<AppTopbar :nav="topNav" />
|
|
</template>
|
|
|
|
<template #sidebar="{ sidebarNav }">
|
|
<AppSidebar :nav="sidebarNav">
|
|
<template #extra-header>
|
|
<div class="px-4">
|
|
<div class="flex flex-col items-center gap-1 overflow-hidden">
|
|
<NuxtImg
|
|
:src="course?.data.previewUrl || '/images/bg_home.jpg'"
|
|
alt="课程封面"
|
|
class="w-full aspect-video rounded-md shadow-md"
|
|
/>
|
|
<h1
|
|
class="text-base font-medium drop-shadow-md text-ellipsis line-clamp-1"
|
|
>
|
|
{{ course?.data.courseName || "未知课程" }}
|
|
</h1>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</AppSidebar>
|
|
</template>
|
|
|
|
<ClientOnly>
|
|
<Suspense>
|
|
<div v-if="courseStatus === 'error'">
|
|
<EmptyScreen
|
|
title="课程加载失败"
|
|
:description="courseError?.data?.msg || '请求失败'"
|
|
icon="tabler:exclamation-circle"
|
|
/>
|
|
</div>
|
|
<EmptyScreen
|
|
v-else-if="courseStatus === 'pending'"
|
|
title="加载中..."
|
|
icon="svg-spinners:90-ring-with-bg"
|
|
/>
|
|
<NuxtPage v-else :page-key="fullPath" />
|
|
</Suspense>
|
|
</ClientOnly>
|
|
</AppPageWithSidebar>
|
|
</template>
|
|
|
|
<style scoped></style>
|