edu/admin/src/views/teacher/teacher copy/index.vue

340 lines
13 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.

<template>
<div class="index-lists">
<el-card class="!border-none" shadow="never">
<el-form ref="formRef" class="mb-[-16px]" :model="queryParams" :inline="true">
<el-form-item label="教师工号" prop="teacherCode">
<el-input class="w-[280px]" v-model="queryParams.teacherCode" />
</el-form-item>
<el-form-item label="教师姓名" prop="name">
<el-input class="w-[280px]" v-model="queryParams.name" />
</el-form-item>
<el-form-item label="邀请码" prop="invitationCode">
<el-input class="w-[280px]" v-model="queryParams.invitationCode" />
</el-form-item>
<el-form-item label="所属学院" prop="collegeName">
<el-input class="w-[280px]" v-model="queryParams.collegeName" />
</el-form-item>
<el-form-item label="教师状态" prop="teacherStatus">
<el-select v-model="queryParams.teacherStatus" class="w-[280px]" clearable>
<el-option label="在职" value="1" />
<el-option label="休假" value="2" />
<el-option label="离职" value="3" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="resetPage">查询</el-button>
<el-button @click="resetParams">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="!border-none mt-4" shadow="never">
<div>
<el-button v-perms="['teacher:add']" type="primary" @click="handleAdd()">
<template #icon>
<icon name="el-icon-Plus" />
</template>
新增
</el-button>
</div>
<el-table class="mt-4" size="large" v-loading="pager.loading" :data="pager.lists">
<el-table-column label="教师工号" prop="teacherCode" min-width="100" />
<el-table-column label="姓名" prop="name" min-width="100" />
<el-table-column label="所属学院" prop="collegeName" min-width="100" />
<el-table-column label="联系电话" prop="contactPhone" min-width="100" />
<el-table-column label="联系邮箱" prop="contactEmail" min-width="100" />
<el-table-column label="邀请码" prop="invitationCode" min-width="160" />
<el-table-column label="二维码" prop="qrcodeUrl" min-width="120">
<template #default="{ row }">
<el-button
v-if="row.qrcodeUrl"
type="primary"
link
@click="handleShowQrCode(row)"
>
查看
</el-button>
<span v-else class="text-info">未生成</span>
</template>
</el-table-column>
<el-table-column
label="教师状态"
prop="teacherStatus"
min-width="100"
:formatter="formatTeacherStatus"
/>
<el-table-column label="创建时间" prop="createTime" min-width="100" />
<el-table-column label="修改时间" prop="updateTime" min-width="100" />
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }">
<el-button
v-perms="['teacher/course/allCourseList']"
type="primary"
link
@click="handleCourse(row)"
>
添加课程
</el-button>
<el-button
v-perms="['teacher/generateInvitationCode']"
type="primary"
link
@click="handleGenerateQrCode(row)"
>
生成二维码
</el-button>
<el-button
v-perms="['teacher/edit']"
type="primary"
link
@click="handleEdit(row)"
>
编辑
</el-button>
<el-button
v-perms="['teacher/del']"
type="danger"
link
@click="handleDelete(row.teacherId)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<div class="flex justify-end mt-4">
<pagination v-model="pager" @change="getLists" />
</div>
</el-card>
<edit-popup v-if="showEdit" ref="editRef" @success="getLists" @close="showEdit = false" />
<course-popup
v-if="showCourse"
ref="courseRef"
@success="getLists"
@close="showCourse = false"
/>
<el-dialog v-model="qrDialog.visible" title="教师二维码" width="420px">
<div v-if="qrDialog.loading" class="py-6 text-center text-info">加载中...</div>
<div v-else class="flex flex-col items-center gap-3">
<el-image
v-if="qrDialog.imageDisplayUrl"
:src="qrDialog.imageDisplayUrl"
fit="contain"
style="width: 280px; height: 280px"
/>
<div v-else-if="qrDialog.qrcodeUrl" class="py-6 text-center text-info">
二维码加载失败
</div>
<div class="w-full">
<div class="text-sm text-info mb-1">邀请码</div>
<div class="flex items-center gap-2">
<el-input v-model="qrDialog.invitationCode" disabled placeholder="暂无" />
<el-button
v-copy="qrDialog.invitationCode"
:disabled="!qrDialog.invitationCode"
>
复制
</el-button>
</div>
</div>
<div class="w-full">
<div class="text-sm text-info mb-1">二维码图片地址</div>
<div class="flex items-center gap-2">
<el-input v-model="qrDialog.qrcodeUrl" disabled placeholder="暂无" />
<el-button v-copy="qrDialog.qrcodeUrl" :disabled="!qrDialog.qrcodeUrl">
复制
</el-button>
</div>
</div>
</div>
<template #footer>
<el-button @click="qrDialog.visible = false">关闭</el-button>
<el-button
type="primary"
:disabled="!qrDialog.qrcodeUrl"
@click="downloadQrCodeImage"
>
下载二维码
</el-button>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="teacher">
import {
teacherDelete,
teacherLists,
teacherGenerateInvitationCode,
teacherGetQrCodeUrl,
teacherGetQrCodeImage
} from '@/api/teacher'
import { usePaging } from '@/hooks/usePaging'
import feedback from '@/utils/feedback'
import { snakeToCamel } from '@/utils/format'
import CoursePopup from './course.vue'
import EditPopup from './edit.vue'
const courseRef = shallowRef<InstanceType<typeof CoursePopup>>()
const editRef = shallowRef<InstanceType<typeof EditPopup>>()
const showEdit = ref(false)
const showCourse = ref(false)
const qrDialog = reactive({
visible: false,
loading: false,
teacherId: 0,
invitationCode: '',
qrcodeUrl: '',
/** 用于 el-image 展示的 blob URL带鉴权拉取避免直接填 qrcodeUrl 导致加载失败 */
imageDisplayUrl: ''
})
const queryParams = reactive({
adminId: '',
teacherCode: '',
name: '',
invitationCode: '',
collegeId: '',
collegeName: '',
teacherStatus: ''
})
const { pager, getLists, resetPage, resetParams } = usePaging({
fetchFun: async (params: any) => {
const response = await teacherLists(params)
return {
...response,
lists: response.lists.map((item: any) => snakeToCamel(item))
}
},
params: queryParams
})
const formatTeacherStatus = (row: any, column: any, cellValue: any) => {
const statusMap: Record<string | number, string> = {
'1': '在职',
'2': '休假',
'3': '离职'
}
return statusMap[String(cellValue)] || cellValue
}
const handleAdd = async () => {
showEdit.value = true
await nextTick()
editRef.value?.open('add')
}
const handleEdit = async (data: any) => {
showEdit.value = true
await nextTick()
editRef.value?.open('edit')
editRef.value?.getDetail(data)
}
const handleCourse = async (data: any) => {
showCourse.value = true
await nextTick()
courseRef.value?.open()
courseRef.value?.setFormData(data)
courseRef.value?.fetchTeacherCourses(data)
}
const handleDelete = async (id: number) => {
await feedback.confirm('确定要删除?')
await teacherDelete({ id: id })
feedback.msgSuccess('删除成功')
getLists()
}
/** 释放二维码展示用的 blob URL避免内存泄漏 */
const revokeQrDisplayUrl = () => {
if (qrDialog.imageDisplayUrl) {
window.URL.revokeObjectURL(qrDialog.imageDisplayUrl)
qrDialog.imageDisplayUrl = ''
}
}
/** 通过鉴权接口拉取二维码图片并生成用于展示的 blob URL */
const loadQrCodeImageForDisplay = async () => {
revokeQrDisplayUrl()
if (!qrDialog.teacherId) return
try {
const res: any = await teacherGetQrCodeImage({ id: qrDialog.teacherId })
const blob = res?.data
if (blob && blob instanceof Blob) {
qrDialog.imageDisplayUrl = window.URL.createObjectURL(blob)
}
} catch (_) {
// 拉取失败时 imageDisplayUrl 为空,模板会显示「二维码加载失败」
}
}
const downloadQrCodeImage = async () => {
if (!qrDialog.teacherId) return
try {
const res: any = await teacherGetQrCodeImage({ id: qrDialog.teacherId })
const blob = res?.data
if (!blob || !(blob instanceof Blob)) throw new Error('下载失败')
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = `teacher_${qrDialog.teacherId || 'qrcode'}.png`
document.body.appendChild(a)
a.click()
a.remove()
window.URL.revokeObjectURL(url)
} catch (e: any) {
feedback.msgError(e?.message || '下载失败')
}
}
/** 统一的「打开弹窗并展示二维码」逻辑,查看与生成都走这里 */
const openQrCodeDialog = async (payload: {
teacherId: number
invitationCode?: string
qrcodeUrl?: string
}) => {
qrDialog.visible = true
qrDialog.loading = true
qrDialog.teacherId = payload.teacherId
qrDialog.invitationCode = payload.invitationCode ?? ''
qrDialog.qrcodeUrl = payload.qrcodeUrl ?? ''
try {
if (!qrDialog.qrcodeUrl) {
const url: any = await teacherGetQrCodeUrl({ id: payload.teacherId })
qrDialog.qrcodeUrl = url || ''
}
await loadQrCodeImageForDisplay()
} finally {
qrDialog.loading = false
}
}
const handleShowQrCode = (row: any) => {
openQrCodeDialog({
teacherId: row.teacherId,
invitationCode: row.invitationCode ?? row.invitation_code ?? '',
qrcodeUrl: row.qrcodeUrl ?? row.qrcode_url ?? ''
})
}
const handleGenerateQrCode = async (row: any) => {
await feedback.confirm('确定为该教师生成新的邀请码和二维码?生成后会覆盖原有邀请码。')
await teacherGenerateInvitationCode({ id: row.teacherId })
await getLists()
const updatedRow = pager.lists.find((item: any) => item.teacherId === row.teacherId)
if (updatedRow) handleShowQrCode(updatedRow)
feedback.msgSuccess('生成成功')
}
watch(
() => qrDialog.visible,
(visible) => {
if (!visible) revokeQrDisplayUrl()
}
)
getLists()
</script>