Files
school-developer/src/views/recruit/newstucheckin/index.vue
guochunsi c6da6e286f a
2026-01-30 16:29:15 +08:00

519 lines
15 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="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 搜索表单 -->
<el-form :model="queryForm" inline ref="searchFormRef">
<el-form-item label="关键词">
<el-input
v-model="queryForm.searchTotal"
clearable
placeholder="请输入姓名/身份证号/家庭联系人"
/>
</el-form-item>
<el-form-item label="学院" prop="deptCode">
<el-select
v-model="queryForm.deptCode"
filterable
clearable
placeholder="请选择学院">
<el-option
v-for="item in deptList"
:key="item.deptCode"
:label="item.deptName"
:value="item.deptCode"
/>
</el-select>
</el-form-item>
<el-form-item label="入学年份" prop="grade">
<el-select
v-model="queryForm.grade"
filterable
clearable
placeholder="请选择入学年份">
<el-option
v-for="item in planList"
:key="item.id"
:label="item.year"
:value="item.year"
/>
</el-select>
</el-form-item>
<el-form-item label="班级" prop="classCode">
<el-select
v-model="queryForm.classCode"
filterable
clearable
placeholder="请选择班级">
<el-option
v-for="item in classList"
:key="item.classCode"
:label="item.classNo"
:value="item.classCode"
/>
</el-select>
</el-form-item>
<el-form-item label="报到状态" prop="checkInStatus">
<el-select
v-model="queryForm.checkInStatus"
filterable
clearable
placeholder="请选择报到状态">
<el-option
v-for="item in checkInStatusData"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="住宿申请" prop="isDormApply">
<el-select
v-model="queryForm.isDormApply"
filterable
clearable
placeholder="请选择住宿申请">
<el-option
v-for="item in dormApplyStatusList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
<el-button icon="Refresh" class="ml10" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作按钮 -->
<div class="mb15">
<el-button
v-if="hasAuth('recruit_newstucheckin_output')"
type="warning"
plain
icon="Download"
@click="handleExportOut"
:loading="exportLoading"
>
导出
</el-button>
</div>
<!-- 表格 -->
<el-table
ref="tableRef"
:data="state.dataList"
v-loading="state.loading"
border
stripe
row-key="id"
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="deptCode" label="学院" align="center" show-overflow-tooltip />
<el-table-column prop="classCode" label="班级" align="center" width="80" show-overflow-tooltip />
<el-table-column label="姓名/学号" align="center" min-width="150" show-overflow-tooltip>
<template #default="scope">
<TeacherNameNo :name="scope.row.name" :no="scope.row.stuNo" />
</template>
</el-table-column>
<el-table-column prop="gender" label="性别" align="center" width="80">
<template #default="scope">
<GenderTag :sex="scope.row.gender" />
</template>
</el-table-column>
<el-table-column prop="idNumber" label="身份证号" align="center" show-overflow-tooltip />
<el-table-column prop="checkInStatus" label="报到状态" align="center" show-overflow-tooltip>
<template #default="scope">
<ClickableTag
v-if="getCheckInStatusConfigLocal(scope.row.checkInStatus)"
:type="getCheckInStatusConfigLocal(scope.row.checkInStatus)?.type || 'info'"
:left-icon="getCheckInStatusConfigLocal(scope.row.checkInStatus)?.icon"
:right-icon="null">
{{ getCheckInStatusConfigLocal(scope.row.checkInStatus)?.label }}
</ClickableTag>
<span v-else class="empty-text">-</span>
</template>
</el-table-column>
<el-table-column prop="isRoom" label="是否住宿" align="center" width="120">
<template #default="scope">
<template v-if="scope.row.isRoom == '1'">
<DetailPopover
title="住宿信息"
:title-icon="InfoFilled"
:width="320"
:items="[
{
label: '住宿申请',
layout: 'horizontal'
},
{
label: '宿舍号',
layout: 'horizontal'
},
{
label: '床位号',
layout: 'horizontal'
}
]">
<template #reference>
<ClickableTag
type="success"
>
{{ getStatusConfig(yes_no_type, scope.row.isRoom)?.label }}
</ClickableTag>
</template>
<!-- 住宿申请状态 -->
<template #content-0>
<div class="dorm-apply-content">
<ClickableTag
v-if="getStatusConfig(dormApplyStatusList, scope.row.isDormApply)"
:type="scope.row.isDormApply == '1'?'success':'danger'"
:left-icon="scope.row.isDormApply == '1'?'CircleCheck':'CircleClose'"
:right-icon="null">
{{getStatusConfig(dormApplyStatusList, scope.row.isDormApply)?.label}}
</ClickableTag>
<span v-else class="empty-text">-</span>
</div>
</template>
<!-- 宿舍号 -->
<template #content-1>
<div class="dorm-room-content">
<span :class="scope.row.roomNo ? 'room-text' : 'empty-text'">
{{ scope.row.roomNo || '-' }}
</span>
</div>
</template>
<!-- 床位号 -->
<template #content-2>
<el-tag v-if="scope.row.bedNo" size="small" type="sucess" effect="dark">
{{ scope.row.bedNo }}
</el-tag>
</template>
</DetailPopover>
</template>
<span v-else>{{ getStatusConfig(yes_no_type, scope.row.isRoom)?.label }}</span>
</template>
</el-table-column>
<el-table-column prop="degreeOfEducation" label="文化程度" align="center" show-overflow-tooltip >
<template #default="scope">
<span>{{ getStatusConfig(eduList, scope.row.degreeOfEducation)?.label }}</span>
</template>
</el-table-column>
<el-table-column prop="residenceDetail" label="居住地址" align="center" show-overflow-tooltip />
<el-table-column prop="parentName" label="家庭联系人" width="100" align="center" show-overflow-tooltip />
<el-table-column label="家长电话1" align="center" prop="parentTelOne" show-overflow-tooltip></el-table-column>
<el-table-column label="家长电话2" align="center" prop="parentTelTwo" show-overflow-tooltip></el-table-column>
<el-table-column prop="remarks" label="备注" align="center" show-overflow-tooltip />
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="scope">
<el-button
v-if="hasAuth('recruit_newstucheckin_edit')"
type="primary"
link
icon="EditPen"
@click="handleCheckIn(scope.row)"
>
报到
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
v-bind="state.pagination"
@current-change="currentChangeHandle"
@size-change="sizeChangeHandle"
/>
<!-- 报到弹窗组件 -->
<stu-check-in ref="stuCheckInRef" @reload="refreshChange"></stu-check-in>
</div>
</div>
</template>
<script setup lang="ts" name="newstucheckin">
import { ref, reactive, onMounted, defineAsyncComponent } from 'vue'
import { useAuth } from '/@/hooks/auth'
import { BasicTableProps, useTable } from '/@/hooks/table'
import { useMessage } from '/@/hooks/message'
import { fetchList } from '/@/api/recruit/newstucheckin'
import { getDictsByTypes } from '/@/api/admin/dict'
import { useDict } from '/@/hooks/dict'
import request from '/@/utils/request'
import { getStatusConfig, getCheckInStatusConfig, DORM_APPLY_STATUS_LIST } from '/@/config/global'
import { getDeptList, getClassListByRole } from '/@/api/basic/basicclass'
import { getList } from '/@/api/recruit/recruitstudentplangroup'
import DetailPopover from '/@/components/DetailPopover/index.vue'
import ClickableTag from '/@/components/ClickableTag/index.vue'
import { InfoFilled, CircleCheck, CircleClose, DocumentChecked, Warning, Clock } from '@element-plus/icons-vue'
const StuCheckIn = defineAsyncComponent(() => import('./stu-check-in.vue'))
const TeacherNameNo = defineAsyncComponent(() => import('/@/components/TeacherNameNo/index.vue'))
const GenderTag = defineAsyncComponent(() => import('/@/components/GenderTag/index.vue'))
const { hasAuth } = useAuth()
// 是否住宿字典
const { yes_no_type } = useDict('yes_no_type')
// 住宿申请状态列表
const dormApplyStatusList = DORM_APPLY_STATUS_LIST
// 消息提示 hooks
const message = useMessage()
// 文化程度字典数据
const eduList = ref<any[]>([])
// 表格引用
const tableRef = ref()
const searchFormRef = ref()
const stuCheckInRef = ref()
// 导出加载状态
const exportLoading = ref(false)
// 数据列表
const deptList = ref<any[]>([])
const planList = ref<any[]>([])
const classList = ref<any[]>([])
// 查询表单
const queryForm = reactive({
deptCode: '',
grade: '',
classCode: '',
checkInStatus: '',
isDormApply: '',
searchTotal: ''
})
// 报到状态字典数据
const checkInStatusData = ref<any[]>([])
// 表格状态
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: queryForm,
pageList: async (params: any) => {
const response = await fetchList({
...params,
deptCode: queryForm.deptCode,
grade: queryForm.grade,
classCode: queryForm.classCode,
checkInStatus: queryForm.checkInStatus,
isDormApply: queryForm.isDormApply,
searchTotal: queryForm.searchTotal
})
return {
data: {
records: response.data.records,
total: response.data.total
}
}
}
})
// 使用 table hook
const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state)
// 获取报到状态配置(用于 ClickableTag
const getCheckInStatusConfigLocal = (value: string) => {
return getCheckInStatusConfig(checkInStatusData.value, value, {
CircleCheck,
CircleClose,
DocumentChecked,
Warning,
Clock
})
}
// 重置查询
const resetQuery = () => {
searchFormRef.value?.resetFields()
queryForm.deptCode = ''
queryForm.grade = ''
queryForm.classCode = ''
queryForm.checkInStatus = ''
queryForm.isDormApply = ''
queryForm.searchTotal = ''
getDataList()
}
// 刷新回调
const refreshChange = () => {
getDataList()
}
// 打开报到窗口
const handleCheckIn = (row: any) => {
if (stuCheckInRef.value) {
stuCheckInRef.value.init(row, {
currentPage: state.pagination?.current || 1,
pageSize: state.pagination?.size || 10,
total: state.pagination?.total || 0
})
}
}
// 导出
const handleExportOut = async () => {
exportLoading.value = true
try {
const res = await request({
method: 'post',
url: '/recruit/newstucheckin/exportData',
data: {
...queryForm,
current: state.pagination?.current || 1,
size: state.pagination?.size || 10
},
responseType: 'blob',
headers: {
'Content-Type': 'application/json'
}
})
const blob = new Blob([res.data])
const fileName = '新生报到导出表.xls'
const elink = document.createElement('a')
elink.download = fileName
elink.style.display = 'none'
elink.href = URL.createObjectURL(blob)
document.body.appendChild(elink)
elink.click()
URL.revokeObjectURL(elink.href)
document.body.removeChild(elink)
message.success('导出成功')
} catch (error: any) {
// console.log(error)
} finally {
exportLoading.value = false
}
}
// 查询报到状态字典
const getDictsData = async () => {
try {
const data = await getDictsByTypes(['check_in_status','finance_student_source'])
checkInStatusData.value = data.data.check_in_status || []
eduList.value = data.data.finance_student_source || []
} catch (error) {
// 获取报到状态字典失败
}
}
// 初始化数据
const init = async () => {
try {
// 获取学院列表
const deptData = await getDeptList()
deptList.value = deptData.data || []
// 获取入学年份列表(招生计划)
const planData = await getList()
planList.value = planData.data || []
// 获取班级列表
const classData = await getClassListByRole()
classList.value = classData.data || []
// 获取字典数据
await getDictsData()
getDataList()
} catch (error) {
// console.log(error)
}
}
onMounted(() => {
init()
})
</script>
<style lang="scss" scoped>
.parent-tel {
display: flex;
align-items: center;
// justify-content: center;
gap: 4px;
.tel-item {
color: #303133;
}
.tel-separator {
color: #909399;
margin: 0 2px;
}
}
.empty-text {
color: #909399;
}
.dorm-tag {
display: inline-flex;
align-items: center;
gap: 4px;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.info-icon {
font-size: 12px;
opacity: 0.8;
}
}
// 住宿申请状态内容
.dorm-apply-content {
display: flex;
align-items: center;
}
// 宿舍号内容
.dorm-room-content {
display: flex;
align-items: center;
gap: 6px;
.room-icon {
color: var(--el-color-primary);
font-size: 16px;
}
.room-text {
color: var(--el-color-primary);
font-weight: 600;
font-size: 15px;
}
}
// 床位号内容
.dorm-bed-content {
display: flex;
align-items: center;
gap: 6px;
.bed-icon {
color: var(--el-color-primary);
font-size: 16px;
}
.bed-text {
color: var(--el-color-primary);
font-weight: 600;
font-size: 15px;
}
}
</style>