IntelliClass_FE/pages/course/[id]/team.vue

231 lines
6.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts" setup>
import { toast } from "vue-sonner";
import { userSearch } from "~/api";
import {
addTeacherToCourse,
deleteTeacherTeamRecord,
getCourseDetail,
getTeacherTeamByCourse,
} from "~/api/course";
import type { FetchError } from "~/types";
definePageMeta({
requiresAuth: true,
});
const {
params: { id: courseId },
} = useRoute();
const loginState = useLoginState();
const course = await getCourseDetail(courseId as string);
const { data: teacherTeam, refresh: refreshTeacherTeam } = useAsyncData(() =>
getTeacherTeamByCourse(parseInt(courseId as string))
);
const searchKeyword = ref("");
const {
data: searchResults,
refresh: refreshSearch,
clear: clearSearch,
} = useAsyncData(
() =>
userSearch({
searchType: "teacher",
keyword: searchKeyword.value,
}),
{
immediate: false,
}
);
// watch searchKeyword and refresh search results, with debounce
const triggerSearch = useDebounceFn(() => {
if (searchKeyword.value.length > 0) {
refreshSearch();
} else {
clearSearch();
}
}, 500);
watch(searchKeyword, (newValue) => {
if (newValue.length > 0) {
triggerSearch();
}
});
const isInTeam = (userId: number) => {
return teacherTeam?.value?.data?.some((item) => item.teacherId === userId);
};
const onAddTeacherToCourse = (teacherId: number) => {
toast.promise(
addTeacherToCourse({
courseId: parseInt(courseId as string),
teacherId,
}),
{
loading: "正在添加教师...",
success: () => {
refreshTeacherTeam();
return "添加教师成功";
},
error: (error: FetchError) => {
if (error.statusCode === 409) {
return "该教师已在团队中";
}
return `添加教师失败:${error.message}`;
},
}
);
};
const onDeleteTeacher = (recordId: number) => {
toast.promise(deleteTeacherTeamRecord(recordId), {
loading: "正在移出教师...",
success: () => {
refreshTeacherTeam();
return "移出教师成功";
},
error: (error: FetchError) => {
return `移出教师失败:${error.message}`;
},
});
};
</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">
<Popover>
<PopoverTrigger as-child>
<Button
variant="secondary"
size="sm"
class="flex items-center gap-1"
>
<Icon name="tabler:plus" size="16px" />
<span>添加教师</span>
</Button>
</PopoverTrigger>
<PopoverContent class="w-96" :align="'end'">
<div class="flex flex-col gap-4">
<FormField v-slot="{ componentField }" name="keyword">
<FormItem>
<FormLabel>搜索教师</FormLabel>
<FormControl>
<Input
v-bind="componentField"
v-model="searchKeyword"
type="text"
placeholder="搜索工号/姓名/手机号"
/>
</FormControl>
<FormDescription>
<p class="text-xs">
搜索教师工号/姓名/手机号,然后添加到团队中
</p>
</FormDescription>
<FormMessage />
</FormItem>
</FormField>
<hr />
<div class="flex flex-col gap-2">
<p class="text-sm text-muted-foreground">搜索结果</p>
<div
v-if="searchResults?.data && searchResults.data.length > 0"
class="flex flex-col gap-2"
>
<div
v-for="user in searchResults.data"
:key="user.userId"
class="flex justify-between items-center gap-2"
>
<div class="flex items-center gap-4">
<Avatar class="w-12 h-12 text-base">
<AvatarImage
:src="user.avatar || ''"
:alt="user.userName"
/>
<AvatarFallback class="rounded-lg">
{{ user.userName.slice(0, 2).toUpperCase() }}
</AvatarFallback>
</Avatar>
<div class="flex flex-col gap-1">
<h1
class="text-sm font-medium text-ellipsis line-clamp-1"
>
{{ user.userName || "未知教师" }}
</h1>
<p class="text-xs text-muted-foreground/80">
工号:{{ user.employeeId || "未知" }}
</p>
<p class="text-xs text-muted-foreground/80">
{{ user.collegeName || "未知学院" }}
</p>
</div>
</div>
<Button
variant="secondary"
size="sm"
class="flex items-center gap-1"
:disabled="isInTeam(user.id!)"
@click="onAddTeacherToCourse(user.id!)"
>
<Icon
v-if="!isInTeam(user.id!)"
name="tabler:plus"
size="16px"
/>
<span>
{{ isInTeam(user.id!) ? "已在团队" : "添加" }}
</span>
</Button>
</div>
</div>
<EmptyScreen
v-else
title="没有搜索结果"
description="没有找到符合条件的教师"
icon="fluent-color:people-list-24"
/>
</div>
</div>
</PopoverContent>
</Popover>
</div>
</div>
<div
v-if="teacherTeam?.data && teacherTeam.data.length > 0"
class="grid gap-6 grid-cols-2 sm:grid-cols-3 2xl:grid-cols-5"
>
<CourseTeamMember
v-for="member in teacherTeam.data"
:key="member.teacherId"
:is-current-user="loginState.user?.userId === member.teacherId"
:member
@delete="onDeleteTeacher"
/>
</div>
<EmptyScreen
v-else
title="暂无团队成员"
description="请添加教师作为团队成员"
icon="fluent-color:people-list-24"
/>
</div>
</template>
<style scoped></style>