feat: 教师团队管理接入接口

This commit is contained in:
QDKF 2025-09-23 19:12:00 +08:00
parent 978713b316
commit 17ed40fc23
2 changed files with 210 additions and 51 deletions

View File

@ -1,32 +1,34 @@
<template>
<div class="team-management">
<div class="toolbar">
<n-select v-model:value="selectedDepartment" :options="departmentOptions" placeholder="班级名称"
style="width: 200px" />
<!-- 班级选择器已注释保留布局空间 -->
<div style="width: 200px; height: 34px;"></div>
<NSpace>
<n-button type="primary" @click="showAddModal = true">
<n-button type="primary" @click="handleAddTeacher">
添加老师
</n-button>
<n-input v-model:value="searchKeyword" placeholder="请输入关键词" style="width: 200px" />
<n-button type="primary">
<n-input v-model:value="searchKeyword" placeholder="请输入关键词" style="width: 200px"
@keyup.enter="handleSearch" />
<n-button type="primary" @click="handleSearch">
搜索
</n-button>
<n-button @click="handleClearSearch" v-if="searchKeyword">
清空
</n-button>
</NSpace>
</div>
<n-data-table :columns="columns" :data="data" :pagination="pagination" :loading="loading"
:row-key="(row: TeacherItem) => row.id" striped size="small" />
<n-data-table :columns="columns" :data="filteredData" :pagination="pagination" :loading="loading"
:row-key="(row) => row.id" striped size="small" />
<!-- 添加教师弹窗 -->
<n-modal v-model:show="showAddModal" title="添加老师">
<n-card style="width: 600px" title="添加老师" :bordered="false" size="huge" role="dialog" aria-modal="true">
<n-form ref="formRef" :model="formData" :rules="rules" label-placement="left" label-width="auto"
require-mark-placement="right-hanging">
<n-form-item label="姓名" path="teacherName">
<n-input v-model:value="formData.teacherName" placeholder="请输入老师姓名" />
</n-form-item>
<n-form-item label="工号" path="teacherId">
<n-input v-model:value="formData.teacherId" placeholder="请输入老师工号" />
<n-form-item label="选择教师" path="teacherId">
<n-select v-model:value="formData.teacherId" :options="allTeachers" placeholder="请选择要添加的教师"
filterable clearable />
</n-form-item>
</n-form>
<template #footer>
@ -46,17 +48,19 @@ import {
NDataTable,
NButton,
NSpace,
NSelect,
NInput,
NSelect,
NModal,
NCard,
NForm,
NFormItem,
useMessage,
useDialog,
type FormInst,
type FormRules
} from 'naive-ui'
import type { DataTableColumns } from 'naive-ui'
import { TeachCourseApi } from '@/api/modules/teachCourse'
//
interface TeacherItem {
@ -72,41 +76,44 @@ interface TeacherItem {
//
interface FormData {
teacherName: string
teacherId: string
}
//
interface Props {
courseId: string
}
const props = defineProps<Props>()
const message = useMessage()
const dialog = useDialog()
//
const selectedDepartment = ref('')
const searchKeyword = ref('')
const showAddModal = ref(false)
const formRef = ref<FormInst | null>(null)
const allTeachers = ref<Array<{ label: string, value: string }>>([])
//
const formData = ref<FormData>({
teacherName: '',
teacherId: ''
})
//
const rules: FormRules = {
teacherName: [
{ required: true, message: '请输入教师姓名', trigger: 'blur' }
],
teacherId: [
{ required: true, message: '请输入工号', trigger: 'blur' }
{ required: true, message: '请选择教师', trigger: 'change' }
]
}
//
const departmentOptions = ref([
{ label: '全部班级', value: '' },
{ label: '软件工程1班', value: 'software1' },
{ label: '软件工程2班', value: 'software2' },
{ label: '计算机科学1班', value: 'cs1' }
])
//
// const departmentOptions = ref([
// { label: '', value: '' },
// { label: '1', value: 'software1' },
// { label: '2', value: 'software2' },
// { label: '1', value: 'cs1' }
// ])
//
const columns: DataTableColumns<TeacherItem> = [
@ -151,7 +158,7 @@ const columns: DataTableColumns<TeacherItem> = [
key: 'actions',
width: 100,
align: 'center',
render: (row) => {
render: (row: TeacherItem) => {
return h(
NButton,
{
@ -168,6 +175,7 @@ const columns: DataTableColumns<TeacherItem> = [
//
const data = ref<TeacherItem[]>([])
const filteredData = ref<TeacherItem[]>([])
const loading = ref(false)
//
@ -187,51 +195,192 @@ const pagination = ref({
}
})
//
const handleSearch = () => {
if (!searchKeyword.value.trim()) {
//
filteredData.value = [...data.value]
} else {
//
const keyword = searchKeyword.value.toLowerCase().trim()
filteredData.value = data.value.filter((teacher: TeacherItem) =>
teacher.teacherName.toLowerCase().includes(keyword) ||
teacher.role.toLowerCase().includes(keyword) ||
teacher.department.toLowerCase().includes(keyword) ||
teacher.teachingClass.toLowerCase().includes(keyword)
)
}
//
pagination.value.page = 1
console.log('🔍 搜索完成,关键词:', searchKeyword.value, '结果数量:', filteredData.value.length)
}
//
const handleClearSearch = () => {
searchKeyword.value = ''
filteredData.value = [...data.value]
pagination.value.page = 1
console.log('🧹 搜索已清空,显示所有数据')
}
//
const handleAddTeacher = async () => {
//
await loadAllTeachers()
//
showAddModal.value = true
}
//
const loadAllTeachers = async () => {
try {
console.log('🔍 开始加载所有教师列表...')
const response = await TeachCourseApi.getTeacherList()
console.log('📊 教师列表响应:', response)
if (response.data && response.data.result) {
// ID
const currentTeacherIds = data.value.map(teacher => teacher.id)
console.log('🔍 当前课程已绑定的教师ID:', currentTeacherIds)
//
allTeachers.value = response.data.result
.filter((teacher: any) => !currentTeacherIds.includes(teacher.id))
.map((teacher: any) => ({
label: teacher.realname || teacher.name || '未知教师',
value: teacher.id
}))
console.log('✅ 教师列表加载成功,共', allTeachers.value.length, '位可选教师')
//
if (allTeachers.value.length === 0) {
message.info('所有教师都已绑定到此课程')
}
} else {
console.log('⚠️ 教师列表数据为空')
allTeachers.value = []
}
} catch (error) {
console.error('❌ 加载教师列表失败:', error)
message.error('加载教师列表失败')
allTeachers.value = []
}
}
//
const handleDelete = (row: TeacherItem) => {
message.warning(`移除教师:${row.teacherName}`)
dialog.warning({
title: '确认移除',
content: `确定要移除教师"${row.teacherName}"吗?此操作不可撤销。`,
positiveText: '确定移除',
negativeText: '取消',
onPositiveClick: async () => {
await performDelete(row)
}
})
}
//
const performDelete = async (row: TeacherItem) => {
try {
console.log('🚀 开始移除教师:', row.teacherName, 'ID:', row.id)
// 使API
await TeachCourseApi.unbindTeacher({ courseId: props.courseId, userId: row.id })
message.success(`成功移除教师:${row.teacherName}`)
//
await loadData()
} catch (error) {
console.error('❌ 移除教师失败:', error)
//
const errorMessage = (error as Error).message
if (errorMessage.includes('该教师未绑定到此课程')) {
message.warning('该教师未绑定到此课程')
} else if (errorMessage.includes('无法移除')) {
message.error('无法移除该教师,可能存在依赖关系')
} else {
message.error('移除教师失败: ' + errorMessage)
}
}
}
const handleSubmit = async () => {
try {
await formRef.value?.validate()
//
console.log('🚀 开始添加教师:', formData.value)
// 使API
await TeachCourseApi.addTeacher({ courseId: props.courseId, userId: formData.value.teacherId })
message.success('添加教师成功')
showAddModal.value = false
//
formData.value = {
teacherName: '',
teacherId: ''
}
//
loadData()
await loadData()
} catch (error) {
message.error('请检查表单信息')
console.error('❌ 添加教师失败:', error)
//
const errorMessage = (error as Error).message
if (errorMessage.includes('该教师已绑定到此课程')) {
message.warning('该教师已绑定到此课程,请选择其他教师')
} else {
message.error('添加教师失败: ' + errorMessage)
}
}
}
//
//
const loadData = async () => {
console.log('🔍 TeamManagement 接收到的 courseId:', props.courseId)
if (!props.courseId) {
console.warn('❌ 课程ID不存在无法加载教师数据')
return
}
loading.value = true
try {
await new Promise(resolve => setTimeout(resolve, 500))
console.log('🚀 开始加载课程教师数据课程ID:', props.courseId)
const roles = ['刘桂人']
const departments = ['北京大学经济学理学院']
const mockData: TeacherItem[] = Array.from({ length: 1 }, (_, index) => ({
id: `teacher_${index + 1}`,
teacherName: roles[0],
teacherId: `56566652`,
role: '刘桂人',
studentId: '56566652',
teachingClass: '北京大学经济学理学院',
department: departments[0],
joinTime: '2025.08.02 07:30'
}))
const response = await TeachCourseApi.getTeacherListInCourse(props.courseId)
console.log('📊 API响应:', response)
data.value = mockData
if (response.data && response.data.result) {
data.value = response.data.result.map((teacher: any) => ({
id: teacher.id,
teacherName: teacher.name || '未知教师',
teacherId: teacher.id,
role: teacher.title || '教师',
studentId: teacher.id,
teachingClass: '当前课程',
department: '教学团队',
joinTime: new Date().toLocaleString()
}))
//
filteredData.value = [...data.value]
console.log('✅ 教师数据加载成功,共', data.value.length, '位教师')
} else {
console.log('⚠️ API返回数据为空或失败')
data.value = []
filteredData.value = []
}
} catch (error) {
console.error('加载数据失败:', error)
console.error('❌ 加载课程教师失败:', error)
message.error('加载教师数据失败: ' + (error as Error).message)
//
data.value = []
filteredData.value = []
} finally {
loading.value = false
}

View File

@ -5,7 +5,7 @@
<ClassManagement type="course" />
</n-tab-pane>
<n-tab-pane name="team" tab="教师团队管理">
<TeamManagement />
<TeamManagement :course-id="courseId" />
</n-tab-pane>
<n-tab-pane name="log" tab="操作日志">
<OperationLog />
@ -21,7 +21,8 @@
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ref, computed } from 'vue'
import { useRoute } from 'vue-router'
import { NTabs, NTabPane } from 'naive-ui'
import ClassManagement from '@/components/teacher/ClassManagement.vue'
import TeamManagement from '@/components/teacher/TeamManagement.vue'
@ -29,6 +30,15 @@ import OperationLog from '@/components/teacher/OperationLog.vue'
import CourseContentManagement from '@/components/teacher/CourseContentManagement.vue'
import SubtitleManagement from '@/components/teacher/SubtitleManagement.vue'
const route = useRoute()
// ID
const courseId = computed(() => {
console.log('🔍 路由参数:', route.params)
console.log('🔍 路由路径:', route.path)
return route.params.id as string || ''
})
// tab
const activeTab = ref('class')
</script>