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> <template>
<div class="team-management"> <div class="team-management">
<div class="toolbar"> <div class="toolbar">
<n-select v-model:value="selectedDepartment" :options="departmentOptions" placeholder="班级名称" <!-- 班级选择器已注释保留布局空间 -->
style="width: 200px" /> <div style="width: 200px; height: 34px;"></div>
<NSpace> <NSpace>
<n-button type="primary" @click="showAddModal = true"> <n-button type="primary" @click="handleAddTeacher">
添加老师 添加老师
</n-button> </n-button>
<n-input v-model:value="searchKeyword" placeholder="请输入关键词" style="width: 200px" /> <n-input v-model:value="searchKeyword" placeholder="请输入关键词" style="width: 200px"
<n-button type="primary"> @keyup.enter="handleSearch" />
<n-button type="primary" @click="handleSearch">
搜索 搜索
</n-button> </n-button>
<n-button @click="handleClearSearch" v-if="searchKeyword">
清空
</n-button>
</NSpace> </NSpace>
</div> </div>
<n-data-table :columns="columns" :data="data" :pagination="pagination" :loading="loading" <n-data-table :columns="columns" :data="filteredData" :pagination="pagination" :loading="loading"
:row-key="(row: TeacherItem) => row.id" striped size="small" /> :row-key="(row) => row.id" striped size="small" />
<!-- 添加教师弹窗 --> <!-- 添加教师弹窗 -->
<n-modal v-model:show="showAddModal" title="添加老师"> <n-modal v-model:show="showAddModal" title="添加老师">
<n-card style="width: 600px" title="添加老师" :bordered="false" size="huge" role="dialog" aria-modal="true"> <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" <n-form ref="formRef" :model="formData" :rules="rules" label-placement="left" label-width="auto"
require-mark-placement="right-hanging"> require-mark-placement="right-hanging">
<n-form-item label="姓名" path="teacherName"> <n-form-item label="选择教师" path="teacherId">
<n-input v-model:value="formData.teacherName" placeholder="请输入老师姓名" /> <n-select v-model:value="formData.teacherId" :options="allTeachers" placeholder="请选择要添加的教师"
</n-form-item> filterable clearable />
<n-form-item label="工号" path="teacherId">
<n-input v-model:value="formData.teacherId" placeholder="请输入老师工号" />
</n-form-item> </n-form-item>
</n-form> </n-form>
<template #footer> <template #footer>
@ -46,17 +48,19 @@ import {
NDataTable, NDataTable,
NButton, NButton,
NSpace, NSpace,
NSelect,
NInput, NInput,
NSelect,
NModal, NModal,
NCard, NCard,
NForm, NForm,
NFormItem, NFormItem,
useMessage, useMessage,
useDialog,
type FormInst, type FormInst,
type FormRules type FormRules
} from 'naive-ui' } from 'naive-ui'
import type { DataTableColumns } from 'naive-ui' import type { DataTableColumns } from 'naive-ui'
import { TeachCourseApi } from '@/api/modules/teachCourse'
// //
interface TeacherItem { interface TeacherItem {
@ -72,41 +76,44 @@ interface TeacherItem {
// //
interface FormData { interface FormData {
teacherName: string
teacherId: string teacherId: string
} }
//
interface Props {
courseId: string
}
const props = defineProps<Props>()
const message = useMessage() const message = useMessage()
const dialog = useDialog()
// //
const selectedDepartment = ref('')
const searchKeyword = ref('') const searchKeyword = ref('')
const showAddModal = ref(false) const showAddModal = ref(false)
const formRef = ref<FormInst | null>(null) const formRef = ref<FormInst | null>(null)
const allTeachers = ref<Array<{ label: string, value: string }>>([])
// //
const formData = ref<FormData>({ const formData = ref<FormData>({
teacherName: '',
teacherId: '' teacherId: ''
}) })
// //
const rules: FormRules = { const rules: FormRules = {
teacherName: [
{ required: true, message: '请输入教师姓名', trigger: 'blur' }
],
teacherId: [ teacherId: [
{ required: true, message: '请输入工号', trigger: 'blur' } { required: true, message: '请选择教师', trigger: 'change' }
] ]
} }
// //
const departmentOptions = ref([ // const departmentOptions = ref([
{ label: '全部班级', value: '' }, // { label: '', value: '' },
{ label: '软件工程1班', value: 'software1' }, // { label: '1', value: 'software1' },
{ label: '软件工程2班', value: 'software2' }, // { label: '2', value: 'software2' },
{ label: '计算机科学1班', value: 'cs1' } // { label: '1', value: 'cs1' }
]) // ])
// //
const columns: DataTableColumns<TeacherItem> = [ const columns: DataTableColumns<TeacherItem> = [
@ -151,7 +158,7 @@ const columns: DataTableColumns<TeacherItem> = [
key: 'actions', key: 'actions',
width: 100, width: 100,
align: 'center', align: 'center',
render: (row) => { render: (row: TeacherItem) => {
return h( return h(
NButton, NButton,
{ {
@ -168,6 +175,7 @@ const columns: DataTableColumns<TeacherItem> = [
// //
const data = ref<TeacherItem[]>([]) const data = ref<TeacherItem[]>([])
const filteredData = ref<TeacherItem[]>([])
const loading = ref(false) 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) => { 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 () => { const handleSubmit = async () => {
try { try {
await formRef.value?.validate() await formRef.value?.validate()
//
console.log('🚀 开始添加教师:', formData.value)
// 使API
await TeachCourseApi.addTeacher({ courseId: props.courseId, userId: formData.value.teacherId })
message.success('添加教师成功') message.success('添加教师成功')
showAddModal.value = false showAddModal.value = false
// //
formData.value = { formData.value = {
teacherName: '',
teacherId: '' teacherId: ''
} }
// //
loadData() await loadData()
} catch (error) { } 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 () => { const loadData = async () => {
console.log('🔍 TeamManagement 接收到的 courseId:', props.courseId)
if (!props.courseId) {
console.warn('❌ 课程ID不存在无法加载教师数据')
return
}
loading.value = true loading.value = true
try { try {
await new Promise(resolve => setTimeout(resolve, 500)) console.log('🚀 开始加载课程教师数据课程ID:', props.courseId)
const roles = ['刘桂人'] const response = await TeachCourseApi.getTeacherListInCourse(props.courseId)
const departments = ['北京大学经济学理学院'] console.log('📊 API响应:', response)
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'
}))
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) { } catch (error) {
console.error('加载数据失败:', error) console.error('❌ 加载课程教师失败:', error)
message.error('加载教师数据失败: ' + (error as Error).message)
//
data.value = []
filteredData.value = []
} finally { } finally {
loading.value = false loading.value = false
} }

View File

@ -5,7 +5,7 @@
<ClassManagement type="course" /> <ClassManagement type="course" />
</n-tab-pane> </n-tab-pane>
<n-tab-pane name="team" tab="教师团队管理"> <n-tab-pane name="team" tab="教师团队管理">
<TeamManagement /> <TeamManagement :course-id="courseId" />
</n-tab-pane> </n-tab-pane>
<n-tab-pane name="log" tab="操作日志"> <n-tab-pane name="log" tab="操作日志">
<OperationLog /> <OperationLog />
@ -21,7 +21,8 @@
</template> </template>
<script setup lang="ts"> <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 { NTabs, NTabPane } from 'naive-ui'
import ClassManagement from '@/components/teacher/ClassManagement.vue' import ClassManagement from '@/components/teacher/ClassManagement.vue'
import TeamManagement from '@/components/teacher/TeamManagement.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 CourseContentManagement from '@/components/teacher/CourseContentManagement.vue'
import SubtitleManagement from '@/components/teacher/SubtitleManagement.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 // tab
const activeTab = ref('class') const activeTab = ref('class')
</script> </script>