519 lines
15 KiB
Vue
519 lines
15 KiB
Vue
<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>
|