This commit is contained in:
吴红兵
2026-03-07 12:35:45 +08:00
parent 271710e870
commit b997b3ba48
423 changed files with 79612 additions and 91574 deletions

View File

@@ -1,443 +1,428 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 搜索表单 -->
<el-form :model="queryForm" inline ref="searchFormRef">
<el-form-item label="招生计划" prop="groupId">
<el-select v-model="queryForm.groupId" filterable clearable placeholder="请选择招生计划">
<el-option
v-for="item in planList"
:key="item.id"
:label="item.groupName"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="唯一号/姓名/身份证号" prop="search">
<el-input v-model="queryForm.search" clearable placeholder="唯一号/姓名/身份证号/学校名称" />
</el-form-item>
<el-form-item label="异动审核" prop="isMajorChange">
<el-select v-model="queryForm.isMajorChange" filterable clearable placeholder="请选择异动审核">
<el-option
v-for="item in majorChangeList"
: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="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 搜索表单 -->
<el-form :model="queryForm" inline ref="searchFormRef">
<el-form-item label="招生计划" prop="groupId">
<el-select v-model="queryForm.groupId" filterable clearable placeholder="请选择招生计划">
<el-option v-for="item in planList" :key="item.id" :label="item.groupName" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="唯一号/姓名/身份证号" prop="search">
<el-input v-model="queryForm.search" clearable placeholder="唯一号/姓名/身份证号/学校名称" />
</el-form-item>
<el-form-item label="异动审核" prop="isMajorChange">
<el-select v-model="queryForm.isMajorChange" filterable clearable placeholder="请选择异动审核">
<el-option v-for="item in majorChangeList" :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>
<!-- 表格 -->
<el-table
ref="tableRef"
:data="state.dataList"
v-loading="state.loading"
border
stripe
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="type" label="异动类型" width="100" align="center" show-overflow-tooltip>
<template #default="scope">
{{ getTypeLabel(scope.row.type) }}
</template>
</el-table-column>
<el-table-column prop="name" label="姓名[唯一号]" min-width="160" align="center" show-overflow-tooltip />
<el-table-column prop="majorChangeInfo" label="专业变更情况" min-width="180" align="center" show-overflow-tooltip>
<template #default="scope">
<DetailPopover
v-if="scope.row.newMajorInfo || scope.row.oldMajorInfo"
title="专业变更详情"
:title-icon="InfoFilled"
:width="320"
:items="(() => {
const items = []
if (scope.row.newMajorInfo) {
items.push({ label: '新专业', content: scope.row.newMajorInfo, contentClass: 'new-major' })
}
if (scope.row.oldMajorInfo) {
items.push({ label: '旧专业', content: scope.row.oldMajorInfo })
}
return items
})()">
<template #reference>
<span class="major-change-link">
{{ scope.row.newMajorInfo || '查看详情' }}
<el-icon class="title-icon"><InfoFilled /></el-icon>
</span>
</template>
</DetailPopover>
<span v-else class="empty-text">-</span>
</template>
</el-table-column>
<el-table-column label="代办费变更情况" min-width="130" align="center" show-overflow-tooltip>
<template #default="scope">
<span class="new-fee">{{ scope.row.newAgencyFee }}</span> / <span class="old-fee">{{ scope.row.oldAgencyFee }}</span>
</template>
</el-table-column>
<el-table-column label="学费变更情况" min-width="140" align="center" show-overflow-tooltip>
<template #default="scope">
<span class="new-fee">{{ scope.row.newFeeTuition }}</span> / <span class="old-fee">{{ scope.row.oldFeeTuition }}</span>
</template>
</el-table-column>
<el-table-column label="分数变更情况" min-width="130" align="center">
<template #default="scope">
<span class="new-fee">{{ scope.row.newCorrectedScore }}</span> / <span class="old-fee">{{ scope.row.oldCorrectedScore }}</span>
</template>
</el-table-column>
<el-table-column prop="createBy" label="异动发起人" min-width="120" align="center" show-overflow-tooltip />
<el-table-column prop="createTime" label="异动时间" min-width="180" align="center" show-overflow-tooltip />
<el-table-column prop="isMajorChange" label="异动审核" min-width="110" align="center" show-overflow-tooltip>
<template #default="scope">
<template v-if="getStatusConfig(auditStateOptions, scope.row.isMajorChange)">
<!-- 有备注信息时使用 popover 包裹 ClickableTag -->
<DetailPopover
v-if="scope.row.remarks"
title="异动审核详情"
:width="300"
:items="[
{
label: '审核状态',
layout: 'horizontal',
content: getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.label
},
{
label: '备注信息',
content: scope.row.remarks,
contentClass: 'reason-content'
}
]">
<template #reference>
<ClickableTag
width="80"
:type="getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.type || 'info'"
:left-icon="getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.icon">
{{ getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.label }}
</ClickableTag>
</template>
<template #content-0>
<ClickableTag
:type="getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.type || 'info'"
:left-icon="getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.icon"
:right-icon="null">
{{ getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.label }}
</ClickableTag>
</template>
<template #content-1>
<div class="reason-content">
<el-icon class="reason-icon"><Warning /></el-icon>
<span>{{ scope.row.remarks }}</span>
</div>
</template>
</DetailPopover>
<!-- 没有备注信息时直接显示 ClickableTag -->
<ClickableTag
v-else
width="80"
:type="getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.type || 'info'"
:left-icon="getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.icon"
:right-icon="null">
{{ getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.label }}
</ClickableTag>
</template>
<!-- 无状态 -->
<span v-else class="empty-text">-</span>
</template>
</el-table-column>
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="scope">
<el-button
v-if="hasAuth('recruit_recruitstudentsignupturnover_edit') && scope.row.isMajorChange == '1'"
type="primary"
link
icon="EditPen"
@click="majorchange(scope.row.id, scope.row.groupId)"
>
审核
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 表格 -->
<el-table
ref="tableRef"
:data="state.dataList"
v-loading="state.loading"
border
stripe
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="type" label="异动类型" width="100" align="center" show-overflow-tooltip>
<template #default="scope">
{{ getTypeLabel(scope.row.type) }}
</template>
</el-table-column>
<el-table-column prop="name" label="姓名[唯一号]" min-width="160" align="center" show-overflow-tooltip />
<el-table-column prop="majorChangeInfo" label="专业变更情况" min-width="180" align="center" show-overflow-tooltip>
<template #default="scope">
<DetailPopover
v-if="scope.row.newMajorInfo || scope.row.oldMajorInfo"
title="专业变更详情"
:title-icon="InfoFilled"
:width="320"
:items="
(() => {
const items = [];
if (scope.row.newMajorInfo) {
items.push({ label: '新专业', content: scope.row.newMajorInfo, contentClass: 'new-major' });
}
if (scope.row.oldMajorInfo) {
items.push({ label: '旧专业', content: scope.row.oldMajorInfo });
}
return items;
})()
"
>
<template #reference>
<span class="major-change-link">
{{ scope.row.newMajorInfo || '查看详情' }}
<el-icon class="title-icon"><InfoFilled /></el-icon>
</span>
</template>
</DetailPopover>
<span v-else class="empty-text">-</span>
</template>
</el-table-column>
<el-table-column label="代办费变更情况" min-width="130" align="center" show-overflow-tooltip>
<template #default="scope">
<span class="new-fee">{{ scope.row.newAgencyFee }}</span> / <span class="old-fee">{{ scope.row.oldAgencyFee }}</span>
</template>
</el-table-column>
<el-table-column label="学费变更情况" min-width="140" align="center" show-overflow-tooltip>
<template #default="scope">
<span class="new-fee">{{ scope.row.newFeeTuition }}</span> / <span class="old-fee">{{ scope.row.oldFeeTuition }}</span>
</template>
</el-table-column>
<el-table-column label="分数变更情况" min-width="130" align="center">
<template #default="scope">
<span class="new-fee">{{ scope.row.newCorrectedScore }}</span> / <span class="old-fee">{{ scope.row.oldCorrectedScore }}</span>
</template>
</el-table-column>
<el-table-column prop="createBy" label="异动发起人" min-width="120" align="center" show-overflow-tooltip />
<el-table-column prop="createTime" label="异动时间" min-width="180" align="center" show-overflow-tooltip />
<el-table-column prop="isMajorChange" label="异动审核" min-width="110" align="center" show-overflow-tooltip>
<template #default="scope">
<template v-if="getStatusConfig(auditStateOptions, scope.row.isMajorChange)">
<!-- 有备注信息时使用 popover 包裹 ClickableTag -->
<DetailPopover
v-if="scope.row.remarks"
title="异动审核详情"
:width="300"
:items="[
{
label: '审核状态',
layout: 'horizontal',
content: getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.label,
},
{
label: '备注信息',
content: scope.row.remarks,
contentClass: 'reason-content',
},
]"
>
<template #reference>
<ClickableTag
width="80"
:type="getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.type || 'info'"
:left-icon="getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.icon"
>
{{ getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.label }}
</ClickableTag>
</template>
<template #content-0>
<ClickableTag
:type="getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.type || 'info'"
:left-icon="getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.icon"
:right-icon="null"
>
{{ getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.label }}
</ClickableTag>
</template>
<template #content-1>
<div class="reason-content">
<el-icon class="reason-icon"><Warning /></el-icon>
<span>{{ scope.row.remarks }}</span>
</div>
</template>
</DetailPopover>
<!-- 分页 -->
<pagination
v-bind="state.pagination"
@current-change="currentChangeHandle"
@size-change="sizeChangeHandle"
/>
<!-- 没有备注信息时直接显示 ClickableTag -->
<ClickableTag
v-else
width="80"
:type="getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.type || 'info'"
:left-icon="getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.icon"
:right-icon="null"
>
{{ getStatusConfig(auditStateOptions, scope.row.isMajorChange)?.label }}
</ClickableTag>
</template>
<!-- 异动审核弹窗 -->
<el-dialog v-model="majorChangeVisible" title="异动审核" width="600px">
<el-form :model="exarmForm" ref="exarmFormRef" label-width="100px" :rules="dataRule">
<el-form-item label="审核结果" prop="isMajorChange">
<el-select v-model="exarmForm.isMajorChange" filterable clearable placeholder="请选择审核结果">
<el-option
v-for="item in isMajorChangeList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="审核意见" prop="remarks">
<el-input
type="textarea"
placeholder="请输入审核意见"
:autosize="{ minRows: 2, maxRows: 4 }"
style="width: 100%"
v-model="exarmForm.remarks"
maxlength="20"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancelPlace">取消</el-button>
<el-button type="primary" @click="update(false)">保存</el-button>
<el-button type="warning" @click="update(true)" v-auth="'recruit_signup_change_force'">强制保存</el-button>
</div>
</template>
</el-dialog>
</div>
</div>
<!-- 无状态 -->
<span v-else class="empty-text">-</span>
</template>
</el-table-column>
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="scope">
<el-button
v-if="hasAuth('recruit_recruitstudentsignupturnover_edit') && scope.row.isMajorChange == '1'"
type="primary"
link
icon="EditPen"
@click="majorchange(scope.row.id, scope.row.groupId)"
>
审核
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination v-bind="state.pagination" @current-change="currentChangeHandle" @size-change="sizeChangeHandle" />
<!-- 异动审核弹窗 -->
<el-dialog v-model="majorChangeVisible" title="异动审核" width="600px">
<el-form :model="exarmForm" ref="exarmFormRef" label-width="100px" :rules="dataRule">
<el-form-item label="审核结果" prop="isMajorChange">
<el-select v-model="exarmForm.isMajorChange" filterable clearable placeholder="请选择审核结果">
<el-option v-for="item in isMajorChangeList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="审核意见" prop="remarks">
<el-input
type="textarea"
placeholder="请输入审核意见"
:autosize="{ minRows: 2, maxRows: 4 }"
style="width: 100%"
v-model="exarmForm.remarks"
maxlength="20"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancelPlace">取消</el-button>
<el-button type="primary" @click="update(false)">保存</el-button>
<el-button type="warning" @click="update(true)" v-auth="'recruit_signup_change_force'">强制保存</el-button>
</div>
</template>
</el-dialog>
</div>
</div>
</template>
<script setup lang="ts" name="recruitstudentsignupturnover">
import { ref, reactive, computed, onMounted } from 'vue'
import { useAuth } from '/@/hooks/auth'
import { BasicTableProps, useTable } from '/@/hooks/table'
import { useMessage } from '/@/hooks/message'
import { fetchList, putObj } from '/@/api/recruit/recruitstudentsignupturnover'
import { getList } from '/@/api/recruit/recruitstudentplangroup'
import ClickableTag from '/@/components/ClickableTag/index.vue'
import DetailPopover from '/@/components/DetailPopover/index.vue'
import { Warning, InfoFilled } from '@element-plus/icons-vue'
import { TURNOVER_AUDIT_STATUS_LIST, getStatusConfig } from '/@/config/global'
import { getDicts } from '/@/api/admin/dict'
import { ref, reactive, computed, onMounted } from 'vue';
import { useAuth } from '/@/hooks/auth';
import { BasicTableProps, useTable } from '/@/hooks/table';
import { useMessage } from '/@/hooks/message';
import { fetchList, putObj } from '/@/api/recruit/recruitstudentsignupturnover';
import { getList } from '/@/api/recruit/recruitstudentplangroup';
import ClickableTag from '/@/components/ClickableTag/index.vue';
import DetailPopover from '/@/components/DetailPopover/index.vue';
import { Warning, InfoFilled } from '@element-plus/icons-vue';
import { TURNOVER_AUDIT_STATUS_LIST, getStatusConfig } from '/@/config/global';
import { getDicts } from '/@/api/admin/dict';
const { hasAuth } = useAuth()
const { hasAuth } = useAuth();
// 消息提示 hooks
const message = useMessage()
const message = useMessage();
// 表格引用
const tableRef = ref()
const searchFormRef = ref()
const exarmFormRef = ref()
const tableRef = ref();
const searchFormRef = ref();
const exarmFormRef = ref();
// 弹窗状态
const majorChangeVisible = ref(false)
const majorChangeVisible = ref(false);
// 数据
const planList = ref<any[]>([])
const planList = ref<any[]>([]);
// 使用字典 recruit_change_type
const typeList = ref<any[]>([])
const typeList = ref<any[]>([]);
// 审核状态选项配置(用于 ClickableTag 组件和检索条件)
const auditStateOptions = ref<any[]>(TURNOVER_AUDIT_STATUS_LIST)
const auditStateOptions = ref<any[]>(TURNOVER_AUDIT_STATUS_LIST);
// 从 auditStateOptions 派生检索条件列表(只包含 label 和 value
const majorChangeList = computed(() => {
return auditStateOptions.value.map(item => ({
label: item.label,
value: item.value
}))
})
return auditStateOptions.value.map((item) => ({
label: item.label,
value: item.value,
}));
});
// 审核弹窗中的选项(只包含通过和驳回)
const isMajorChangeList = computed(() => {
return auditStateOptions.value
.filter(item => item.value !== '1')
.map(item => ({
label: item.label,
value: item.value
}))
})
return auditStateOptions.value
.filter((item) => item.value !== '1')
.map((item) => ({
label: item.label,
value: item.value,
}));
});
// 查询表单
const queryForm = reactive({
groupId: '',
search: '',
isMajorChange: ''
})
groupId: '',
search: '',
isMajorChange: '',
});
// 审核表单
const exarmForm = reactive({
id: '',
isMajorChange: '',
examRemark: '',
groupId: '',
remarks: '',
force:false
})
id: '',
isMajorChange: '',
examRemark: '',
groupId: '',
remarks: '',
force: false,
});
// 表单验证规则
const dataRule = {
isMajorChange: [
{ required: true, message: '请选择审核结果', trigger: 'change' }
]
}
isMajorChange: [{ required: true, message: '请选择审核结果', trigger: 'change' }],
};
// 获取异动类型标签
const getTypeLabel = (type: string) => {
const item = typeList.value.find((it: any) => String(it.value) === String(type))
return item ? item.label : ''
}
const item = typeList.value.find((it: any) => String(it.value) === String(type));
return item ? item.label : '';
};
// 表格状态
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: queryForm,
pageList: async (params: any) => {
const response = await fetchList(params)
return {
data: {
records: response.data.records,
total: response.data.total
}
}
},
createdIsNeed: false
})
queryForm: queryForm,
pageList: async (params: any) => {
const response = await fetchList(params);
return {
data: {
records: response.data.records,
total: response.data.total,
},
};
},
createdIsNeed: false,
});
// 使用 table hook
const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state)
const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state);
// 初始化
const init = async () => {
try {
const data = await getList()
planList.value = data.data || []
if (planList.value.length > 0) {
queryForm.groupId = planList.value[0].id
}
const typeData = await getDicts('recruit_change_type')
typeList.value = typeData.data || []
getDataList()
} catch (error) {
// eslint-disable-next-line no-console
console.log('初始化失败', error)
}
}
try {
const data = await getList();
planList.value = data.data || [];
if (planList.value.length > 0) {
queryForm.groupId = planList.value[0].id;
}
const typeData = await getDicts('recruit_change_type');
typeList.value = typeData.data || [];
getDataList();
} catch (error) {
// eslint-disable-next-line no-console
console.log('初始化失败', error);
}
};
// 打开审核弹窗
const majorchange = (id: string, groupId: string) => {
exarmForm.id = id
exarmForm.groupId = groupId
exarmForm.isMajorChange = ''
exarmForm.remarks = ''
majorChangeVisible.value = true
}
exarmForm.id = id;
exarmForm.groupId = groupId;
exarmForm.isMajorChange = '';
exarmForm.remarks = '';
majorChangeVisible.value = true;
};
// 取消审核
const cancelPlace = () => {
majorChangeVisible.value = false
}
majorChangeVisible.value = false;
};
// 保存审核
const update = async (force:any) => {
try {
const valid = await exarmFormRef.value?.validate().catch(() => {})
if (!valid) return
exarmForm.force=force
await putObj(exarmForm)
message.success('审核成功')
majorChangeVisible.value = false
getDataList()
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
}
}
const update = async (force: any) => {
try {
const valid = await exarmFormRef.value?.validate().catch(() => {});
if (!valid) return;
exarmForm.force = force;
await putObj(exarmForm);
message.success('审核成功');
majorChangeVisible.value = false;
getDataList();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
};
// 重置查询
const resetQuery = () => {
searchFormRef.value?.resetFields()
queryForm.groupId = ''
queryForm.search = ''
queryForm.isMajorChange = ''
if (planList.value.length > 0) {
queryForm.groupId = planList.value[0].id
}
getDataList()
}
searchFormRef.value?.resetFields();
queryForm.groupId = '';
queryForm.search = '';
queryForm.isMajorChange = '';
if (planList.value.length > 0) {
queryForm.groupId = planList.value[0].id;
}
getDataList();
};
onMounted(() => {
init()
})
init();
});
</script>
<style lang="scss" scoped>
.new-fee {
color: var(--el-color-primary);
font-weight: 600;
font-size: 14px;
color: var(--el-color-primary);
font-weight: 600;
font-size: 14px;
}
.old-fee {
text-decoration: line-through;
color: #606266;
font-size: 13px;
margin-left: 4px;
text-decoration: line-through;
color: #606266;
font-size: 13px;
margin-left: 4px;
}
.empty-text {
color: #909399;
color: #909399;
}
// 备注内容样式(用于 DetailPopover 的插槽内容)
.reason-content {
display: flex;
align-items: flex-start;
gap: 8px;
padding: 12px;
background-color: #fef0f0;
border-radius: 4px;
border-left: 3px solid #f56c6c;
.reason-icon {
color: #f56c6c;
font-size: 16px;
flex-shrink: 0;
margin-top: 1px;
}
span {
color: #f56c6c;
line-height: 1.6;
word-break: break-all;
flex: 1;
}
display: flex;
align-items: flex-start;
gap: 8px;
padding: 12px;
background-color: #fef0f0;
border-radius: 4px;
border-left: 3px solid #f56c6c;
.reason-icon {
color: #f56c6c;
font-size: 16px;
flex-shrink: 0;
margin-top: 1px;
}
span {
color: #f56c6c;
line-height: 1.6;
word-break: break-all;
flex: 1;
}
}
// 专业变更链接样式
.major-change-link {
display: inline-flex;
align-items: center;
gap: 4px;
color: var(--el-color-primary);
text-decoration: underline;
cursor: pointer;
font-weight: 500;
&:hover {
color: var(--el-color-primary-light-3);
}
.title-icon {
color: var(--el-color-primary);
}
display: inline-flex;
align-items: center;
gap: 4px;
color: var(--el-color-primary);
text-decoration: underline;
cursor: pointer;
font-weight: 500;
&:hover {
color: var(--el-color-primary-light-3);
}
.title-icon {
color: var(--el-color-primary);
}
}
// 新专业样式(用于 DetailPopover 的内容类)
.new-major {
color: var(--el-color-primary);
font-weight: 500;
color: var(--el-color-primary);
font-weight: 500;
}
</style>