wuzhen update 招生就业相关修改

This commit is contained in:
“wuzhen” committed 2026-06-22 14:50:19 +08:00
1 parent 80cdde97a1
commit 6546732bb2
29 files changed
+1389 -287

No files matched your search

+30 -53
View File
@@ -39,7 +39,7 @@
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="出生日期" prop="birthDate">
<el-form-item label="出生日期" prop="birthDate" required>
<el-date-picker v-model="applyForm.birthDate" type="date" placeholder="选择出生日期" value-format="YYYY-MM-DD" style="width: 100%" />
</el-form-item>
</el-col>
@@ -49,33 +49,33 @@
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="备用电话" prop="phone2">
<el-form-item label="备用电话" prop="phone2" required>
<el-input v-model="applyForm.phone2" placeholder="请输入备用电话" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="民族" prop="nation">
<el-form-item label="民族" prop="nation" required>
<el-input v-model="applyForm.nation" placeholder="请输入民族" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="是否住宿" prop="isBoarding">
<el-form-item label="是否住宿" prop="isBoarding" required>
<el-select v-model="applyForm.isBoarding" placeholder="请选择是否住宿" style="width: 100%">
<el-option v-for="dict in sys_yes_no" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="健康状况" prop="healthStatus">
<el-form-item label="健康状况" prop="healthStatus" required>
<el-input v-model="applyForm.healthStatus" placeholder="请输入健康状况" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="家庭住址" prop="homeAddress">
<el-form-item label="家庭住址" prop="homeAddress" required>
<el-input v-model="applyForm.homeAddress" placeholder="请输入详细的家庭住址" />
</el-form-item>
</el-col>
@@ -90,63 +90,63 @@
</h3>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="毕业中学" prop="middleSchool">
<el-form-item label="毕业中学" prop="middleSchool" required>
<el-input v-model="applyForm.middleSchool" placeholder="请输入毕业中学全称" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="准考证号" prop="examNumber">
<el-form-item label="准考证号" prop="examNumber" required>
<el-input v-model="applyForm.examNumber" placeholder="请输入准考证号" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="中考地区" prop="examRegion">
<el-form-item label="中考地区" prop="examRegion" required>
<el-input v-model="applyForm.examRegion" placeholder="请输入中考地区" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="中考满分" prop="totalScoreFull">
<el-form-item label="中考满分" prop="totalScoreFull" required>
<el-input-number v-model="applyForm.totalScoreFull" placeholder="请输入中考满分" :min="100" :max="1000" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="中考成绩" prop="totalScore">
<el-form-item label="中考成绩" prop="totalScore" required>
<el-input-number v-model="applyForm.totalScore" placeholder="请输入中考成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="语文成绩" prop="chineseScore">
<el-form-item label="语文成绩" prop="chineseScore" required>
<el-input-number v-model="applyForm.chineseScore" placeholder="请输入语文成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="数学成绩" prop="mathScore">
<el-form-item label="数学成绩" prop="mathScore" required>
<el-input-number v-model="applyForm.mathScore" placeholder="请输入数学成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="英语成绩" prop="englishScore">
<el-form-item label="英语成绩" prop="englishScore" required>
<el-input-number v-model="applyForm.englishScore" placeholder="请输入英语成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="科学成绩" prop="scienceScore">
<el-form-item label="科学成绩" prop="scienceScore" required>
<el-input-number v-model="applyForm.scienceScore" placeholder="请输入科学成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="社会成绩" prop="socialScore">
<el-form-item label="社会成绩" prop="socialScore" required>
<el-input-number v-model="applyForm.socialScore" placeholder="请输入社会成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="体育成绩" prop="peScore">
<el-form-item label="体育成绩" prop="peScore" required>
<el-input-number v-model="applyForm.peScore" placeholder="请输入体育成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
@@ -168,7 +168,7 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学院代码" prop="collegeId">
<el-form-item label="学院代码" prop="collegeId" required>
<el-input v-model="applyForm.collegeId" placeholder="学院代码将自动填入" readonly />
</el-form-item>
</el-col>
@@ -182,16 +182,16 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="专业代码" prop="majorCode">
<el-form-item label="专业代码" prop="majorCode" required>
<el-input v-model="applyForm.majorCode" placeholder="专业代码将自动填入" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="校服尺码" prop="uniformSize">
<el-form-item label="校服尺码" prop="uniformSize" required>
<el-select v-model="applyForm.uniformSize" placeholder="请选择校服尺码" style="width: 100%">
<el-option v-for="dict in zsjy_uniform_size" :key="dict.value" :label="dict.label" :value="dict.value" />
<el-option v-for="dict in uniformSizeOptions" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
@@ -282,11 +282,13 @@
</template>
<script setup name="EnrollmentApply" lang="ts">
import { reactive, ref, toRefs, getCurrentInstance, ComponentInternalInstance, onMounted, nextTick } from 'vue';
import { reactive, ref, toRefs, getCurrentInstance, ComponentInternalInstance, onMounted, nextTick, computed } from 'vue';
import { ElForm } from 'element-plus';
import { submitEnrollmentApplication, queryMyEnrollmentStatus } from '@/api/zsjy/enrollment';
import type { EnrollmentInfoForm } from '@/api/zsjy/enrollment/types';
import { listColleges, listMajorsByCollegeId } from '@/api/xg/xsjcxx/classMsg/college';
import { listOpenColleges, listOpenMajorsByCollegeId } from '@/api/zsjy/enrollmentMajor';
import { enrollmentFormRules } from './enrollmentFormRules';
import { resolveUniformSizeOptions } from './uniformSizeOptions';
import { User, School, Star, Document, Check, Refresh } from '@element-plus/icons-vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -294,6 +296,8 @@ const { zsjy_gender, sys_yes_no, zsjy_major, zsjy_uniform_size } = toRefs<any>(
(proxy as any)?.useDict('zsjy_gender', 'sys_yes_no', 'zsjy_major', 'zsjy_uniform_size')
);
const uniformSizeOptions = computed(() => resolveUniformSizeOptions(zsjy_uniform_size.value));
const applyFormRef = ref<InstanceType<typeof ElForm>>();
const submitLoading = ref(false);
const queryLoading = ref(false);
@@ -343,33 +347,7 @@ const queryForm = reactive({
examNumber: ''
});
const rules = {
name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
idCard: [
{ required: true, message: '身份证号不能为空', trigger: 'blur' },
{
pattern: /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
message: '身份证号格式不正确',
trigger: 'blur'
}
],
gender: [{ required: true, message: '性别不能为空', trigger: 'change' }],
phone1: [
{ required: true, message: '联系电话不能为空', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '手机号码格式不正确', trigger: 'blur' }
],
phone2: [{ pattern: /^$|^1[3-9]\d{9}$/, message: '手机号码格式不正确', trigger: 'blur' }],
collegeName: [{ required: true, message: '所属学院不能为空', trigger: 'change' }],
appliedMajor: [{ required: true, message: '报考专业不能为空', trigger: 'change' }],
totalScoreFull: [{ type: 'number', min: 100, max: 1000, message: '中考满分应在100-1000之间', trigger: 'blur' }],
totalScore: [{ type: 'number', min: 0, message: '中考成绩不能小于0', trigger: 'blur' }],
chineseScore: [{ type: 'number', min: 0, message: '语文成绩不能小于0', trigger: 'blur' }],
mathScore: [{ type: 'number', min: 0, message: '数学成绩不能小于0', trigger: 'blur' }],
englishScore: [{ type: 'number', min: 0, message: '英语成绩不能小于0', trigger: 'blur' }],
scienceScore: [{ type: 'number', min: 0, message: '科学成绩不能小于0', trigger: 'blur' }],
socialScore: [{ type: 'number', min: 0, message: '社会成绩不能小于0', trigger: 'blur' }],
peScore: [{ type: 'number', min: 0, message: '体育成绩不能小于0', trigger: 'blur' }]
};
const rules = enrollmentFormRules;
/** 身份证号变化处理 */
const onIdCardChange = () => {
@@ -438,7 +416,7 @@ const onCollegeChange = async (selectedCollegeId: number) => {
/** 根据学院ID加载专业 */
const loadMajorsByCollegeId = async (collegeId: number) => {
try {
const response = await listMajorsByCollegeId(collegeId);
const response = await listOpenMajorsByCollegeId(collegeId);
if (response.data) {
majorOptions.value = response.data.map((major: any) => ({
value: major.deptId.toString(),
@@ -566,7 +544,7 @@ const queryStatus = async () => {
/** 获取学院选项 */
const getCollegeOptions = async () => {
try {
const response = await listColleges();
const response = await listOpenColleges();
if (response.data) {
collegeOptions.value = response.data;
console.log('学院数据加载成功:', collegeOptions.value);
@@ -579,7 +557,6 @@ const getCollegeOptions = async () => {
onMounted(() => {
getCollegeOptions();
getMajorOptions();
});
</script>
@@ -0,0 +1,58 @@
import type { FormRules } from 'element-plus';
const idCardPattern = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
const phonePattern = /^1[3-9]\d{9}$/;
const requiredText = (label: string, trigger: 'blur' | 'change' = 'blur') => [
{ required: true, message: `${label}不能为空`, trigger }
];
const requiredNumber = (label: string, min: number, max?: number) => {
const rules: FormRules[string] = [
{ required: true, message: `${label}不能为空`, trigger: 'blur' },
{ type: 'number', min, message: `${label}不能小于${min}`, trigger: 'blur' }
];
if (max !== undefined) {
rules.push({ type: 'number', max, message: `${label}不能大于${max}`, trigger: 'blur' });
}
return rules;
};
/** 招生报名信息表单校验(特长爱好、获奖情况除外) */
export const enrollmentFormRules: FormRules = {
name: requiredText('姓名'),
idCard: [
{ required: true, message: '身份证号不能为空', trigger: 'blur' },
{ pattern: idCardPattern, message: '身份证号格式不正确', trigger: 'blur' }
],
gender: requiredText('性别', 'change'),
birthDate: requiredText('出生日期', 'change'),
phone1: [
{ required: true, message: '联系电话不能为空', trigger: 'blur' },
{ pattern: phonePattern, message: '手机号码格式不正确', trigger: 'blur' }
],
phone2: [
{ required: true, message: '备用电话不能为空', trigger: 'blur' },
{ pattern: phonePattern, message: '手机号码格式不正确', trigger: 'blur' }
],
nation: requiredText('民族'),
isBoarding: requiredText('是否住宿', 'change'),
healthStatus: requiredText('健康状况'),
homeAddress: requiredText('家庭住址'),
middleSchool: requiredText('毕业中学'),
examNumber: requiredText('准考证号'),
examRegion: requiredText('中考地区'),
totalScoreFull: requiredNumber('中考满分', 100, 1000),
totalScore: requiredNumber('中考成绩', 0),
chineseScore: requiredNumber('语文成绩', 0),
mathScore: requiredNumber('数学成绩', 0),
englishScore: requiredNumber('英语成绩', 0),
scienceScore: requiredNumber('科学成绩', 0),
socialScore: requiredNumber('社会成绩', 0),
peScore: requiredNumber('体育成绩', 0),
collegeName: requiredText('所属学院', 'change'),
collegeId: requiredText('学院代码'),
appliedMajor: requiredText('报考专业', 'change'),
majorCode: requiredText('专业代码'),
uniformSize: requiredText('校服尺码', 'change')
};
@@ -0,0 +1,147 @@
export interface EnrollmentPrintField {
label: string;
value?: string | number | null;
/** 占满一行(如地址、特长等) */
fullWidth?: boolean;
}
export interface EnrollmentPrintSection {
title: string;
fields: EnrollmentPrintField[];
}
const formatValue = (value?: string | number | null) => {
if (value === undefined || value === null || value === '') {
return '-';
}
return String(value);
};
const escapeHtml = (text: string) =>
text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
const buildSectionHtml = (section: EnrollmentPrintSection) => {
const fieldsHtml = section.fields
.map((field) => {
const cssClass = field.fullWidth ? 'field full' : 'field';
return `<div class="${cssClass}"><span class="label">${escapeHtml(field.label)}:</span><span class="value">${escapeHtml(formatValue(field.value))}</span></div>`;
})
.join('');
return `<section class="section"><h2>${escapeHtml(section.title)}</h2><div class="fields">${fieldsHtml}</div></section>`;
};
/** 打印招生报名信息 */
export const printEnrollmentInfo = (documentTitle: string, sections: EnrollmentPrintSection[], printTime?: string) => {
const printWindow = window.open('', '_blank');
if (!printWindow) {
throw new Error('无法打开打印窗口,请检查浏览器是否拦截弹窗');
}
const sectionsHtml = sections.map(buildSectionHtml).join('');
const timeText = printTime || new Date().toLocaleString('zh-CN');
const html = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<title>${escapeHtml(documentTitle)}</title>
<style>
* { box-sizing: border-box; }
body {
margin: 0;
padding: 24px 32px;
font-family: "Microsoft YaHei", "PingFang SC", Arial, sans-serif;
color: #303133;
font-size: 14px;
line-height: 1.6;
}
.print-header {
text-align: center;
margin-bottom: 24px;
padding-bottom: 12px;
border-bottom: 2px solid #409eff;
}
.print-title {
margin: 0 0 8px;
font-size: 22px;
font-weight: 700;
}
.print-meta {
margin: 0;
color: #909399;
font-size: 13px;
}
.section {
margin-bottom: 20px;
page-break-inside: avoid;
}
.section h2 {
margin: 0 0 12px;
padding-left: 10px;
font-size: 16px;
border-left: 4px solid #409eff;
}
.fields {
display: flex;
flex-wrap: wrap;
border: 1px solid #ebeef5;
border-radius: 4px;
overflow: hidden;
}
.field {
width: 33.33%;
display: flex;
padding: 10px 12px;
border-bottom: 1px solid #ebeef5;
border-right: 1px solid #ebeef5;
min-height: 42px;
}
.field:nth-child(3n) {
border-right: none;
}
.field.full {
width: 100%;
border-right: none;
}
.label {
flex: 0 0 auto;
color: #606266;
font-weight: 600;
white-space: nowrap;
}
.value {
flex: 1;
color: #303133;
word-break: break-all;
}
@media print {
body { padding: 12px 16px; }
@page { margin: 12mm; }
}
</style>
</head>
<body>
<div class="print-header">
<h1 class="print-title">${escapeHtml(documentTitle)}</h1>
<p class="print-meta">打印时间:${escapeHtml(timeText)}</p>
</div>
${sectionsHtml}
<script>
window.onload = function () {
window.print();
window.onafterprint = function () { window.close(); };
};
<\/script>
</body>
</html>`;
printWindow.document.open();
printWindow.document.write(html);
printWindow.document.close();
};
+140 -149
View File
@@ -189,12 +189,12 @@
<h3 class="form-section-title">基本信息</h3>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="姓名" prop="name">
<el-form-item label="姓名" prop="name" required>
<el-input v-model="form.name" placeholder="请输入姓名" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="身份证号" prop="idCard">
<el-form-item label="身份证号" prop="idCard" required>
<el-input
v-model="form.idCard"
placeholder="请输入身份证号"
@@ -205,7 +205,7 @@
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="性别" prop="gender">
<el-form-item label="性别" prop="gender" required>
<el-select
v-model="form.gender"
placeholder="请选择性别"
@@ -220,43 +220,43 @@
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="出生日期" prop="birthDate">
<el-form-item label="出生日期" prop="birthDate" required>
<el-date-picker v-model="form.birthDate" type="date" placeholder="选择出生日期" value-format="YYYY-MM-DD" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="联系电话" prop="phone1">
<el-form-item label="联系电话" prop="phone1" required>
<el-input v-model="form.phone1" placeholder="请输入联系电话" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="备用电话" prop="phone2">
<el-form-item label="备用电话" prop="phone2" required>
<el-input v-model="form.phone2" placeholder="请输入备用电话" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="民族" prop="nation">
<el-form-item label="民族" prop="nation" required>
<el-input v-model="form.nation" placeholder="请输入民族" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="是否住宿" prop="isBoarding">
<el-form-item label="是否住宿" prop="isBoarding" required>
<el-select v-model="form.isBoarding" placeholder="请选择是否住宿" style="width: 100%">
<el-option v-for="dict in sys_yes_no" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="健康状况" prop="healthStatus">
<el-form-item label="健康状况" prop="healthStatus" required>
<el-input v-model="form.healthStatus" placeholder="请输入健康状况" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="家庭住址" prop="homeAddress">
<el-form-item label="家庭住址" prop="homeAddress" required>
<el-input v-model="form.homeAddress" placeholder="请输入家庭住址" />
</el-form-item>
</el-col>
@@ -268,63 +268,63 @@
<h3 class="form-section-title">学业信息</h3>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="毕业中学" prop="middleSchool">
<el-form-item label="毕业中学" prop="middleSchool" required>
<el-input v-model="form.middleSchool" placeholder="请输入毕业中学" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="准考证号" prop="examNumber">
<el-form-item label="准考证号" prop="examNumber" required>
<el-input v-model="form.examNumber" placeholder="请输入准考证号" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="中考地区" prop="examRegion">
<el-form-item label="中考地区" prop="examRegion" required>
<el-input v-model="form.examRegion" placeholder="请输入中考地区" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="中考满分" prop="totalScoreFull">
<el-form-item label="中考满分" prop="totalScoreFull" required>
<el-input-number v-model="form.totalScoreFull" placeholder="请输入中考满分" :min="100" :max="1000" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="中考成绩" prop="totalScore">
<el-form-item label="中考成绩" prop="totalScore" required>
<el-input-number v-model="form.totalScore" placeholder="请输入中考成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="语文成绩" prop="chineseScore">
<el-form-item label="语文成绩" prop="chineseScore" required>
<el-input-number v-model="form.chineseScore" placeholder="请输入语文成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="数学成绩" prop="mathScore">
<el-form-item label="数学成绩" prop="mathScore" required>
<el-input-number v-model="form.mathScore" placeholder="请输入数学成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="英语成绩" prop="englishScore">
<el-form-item label="英语成绩" prop="englishScore" required>
<el-input-number v-model="form.englishScore" placeholder="请输入英语成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="科学成绩" prop="scienceScore">
<el-form-item label="科学成绩" prop="scienceScore" required>
<el-input-number v-model="form.scienceScore" placeholder="请输入科学成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="社会成绩" prop="socialScore">
<el-form-item label="社会成绩" prop="socialScore" required>
<el-input-number v-model="form.socialScore" placeholder="请输入社会成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="体育成绩" prop="peScore">
<el-form-item label="体育成绩" prop="peScore" required>
<el-input-number v-model="form.peScore" placeholder="请输入体育成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
@@ -336,7 +336,7 @@
<h3 class="form-section-title">报考信息</h3>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="所属学院" prop="collegeName">
<el-form-item label="所属学院" prop="collegeName" required>
<el-select
v-model="(form as EnrollmentInfoFormExtended).selectCollegeId"
placeholder="请选择所属学院"
@@ -348,30 +348,30 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学院代码" prop="collegeId">
<el-form-item label="学院代码" prop="collegeId" required>
<el-input v-model="form.collegeId" placeholder="学院代码将自动填入" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="报考专业" prop="appliedMajor">
<el-form-item label="报考专业" prop="appliedMajor" required>
<el-select v-model="form.appliedMajor" placeholder="请选择报考专业" style="width: 100%" @change="onMajorChange">
<el-option v-for="major in majorOptions" :key="major.value" :label="major.label" :value="major.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="专业代码" prop="majorCode">
<el-form-item label="专业代码" prop="majorCode" required>
<el-input v-model="form.majorCode" placeholder="专业代码将自动填入" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="校服尺码" prop="uniformSize">
<el-form-item label="校服尺码" prop="uniformSize" required>
<el-select v-model="form.uniformSize" placeholder="请选择校服尺码" style="width: 100%">
<el-option v-for="dict in zsjy_uniform_size" :key="dict.value" :label="dict.label" :value="dict.value" />
<el-option v-for="dict in uniformSizeOptions" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
@@ -605,6 +605,7 @@
<template #footer>
<div class="dialog-footer">
<el-button @click="cancelView">关 闭</el-button>
<el-button type="success" @click="printViewForm">打 印</el-button>
<el-button type="primary" @click="showRawData">查看原始数据</el-button>
</div>
</template>
@@ -824,7 +825,10 @@ import { createProcessFromEnrollment } from '@/api/zsjy/enrollmentProcess';
import { EnrollmentInfoVO, EnrollmentInfoQuery, EnrollmentInfoForm } from '@/api/zsjy/enrollment/types';
import { ComponentInternalInstance, getCurrentInstance, reactive, ref, toRefs, onMounted, watch, computed, nextTick } from 'vue';
import { FormInstance } from 'element-plus';
import { listDept } from '@/api/system/dept';
import { listOpenColleges, listOpenMajorsByCollegeId } from '@/api/zsjy/enrollmentMajor';
import { enrollmentFormRules } from './enrollmentFormRules';
import { getUniformSizeLabel as resolveUniformSizeLabel, resolveUniformSizeOptions } from './uniformSizeOptions';
import { printEnrollmentInfo } from './enrollmentPrint';
import { DeptVO } from '@/api/system/dept/types';
// 状态常量
@@ -1040,39 +1044,16 @@ const data = reactive<PageData<EnrollmentInfoForm, EnrollmentInfoQuery>>({
majorCode: undefined,
phone1: undefined,
examRegion: undefined,
enrollmentStatus: ENROLLMENT_STATUS.AUDITING, // 默认只查询"信息审核中"状态的学生
enrollmentStatus: undefined,
params: {}
},
rules: {
name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
idCard: [
{ required: true, message: '身份证号不能为空', trigger: 'blur' },
{
pattern: /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
message: '身份证号格式不正确',
trigger: 'blur'
}
],
gender: [{ required: true, message: '性别不能为空', trigger: 'change' }],
phone1: [
{ required: true, message: '联系电话不能为空', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '手机号码格式不正确', trigger: 'blur' }
],
phone2: [{ pattern: /^$|^1[3-9]\d{9}$/, message: '手机号码格式不正确', trigger: 'blur' }],
appliedMajor: [{ required: true, message: '报考专业不能为空', trigger: 'change' }],
totalScoreFull: [{ type: 'number', min: 100, max: 1000, message: '中考满分应在100-1000之间', trigger: 'blur' }],
totalScore: [{ type: 'number', min: 0, message: '中考成绩不能小于0', trigger: 'blur' }],
chineseScore: [{ type: 'number', min: 0, message: '语文成绩不能小于0', trigger: 'blur' }],
mathScore: [{ type: 'number', min: 0, message: '数学成绩不能小于0', trigger: 'blur' }],
englishScore: [{ type: 'number', min: 0, message: '英语成绩不能小于0', trigger: 'blur' }],
scienceScore: [{ type: 'number', min: 0, message: '科学成绩不能小于0', trigger: 'blur' }],
socialScore: [{ type: 'number', min: 0, message: '社会成绩不能小于0', trigger: 'blur' }],
peScore: [{ type: 'number', min: 0, message: '体育成绩不能小于0', trigger: 'blur' }]
}
rules: enrollmentFormRules
});
const { queryParams, form, rules } = toRefs(data);
const uniformSizeOptions = computed(() => resolveUniformSizeOptions(zsjy_uniform_size.value));
/** 身份证号变化处理 */
const onIdCardChange = () => {
const idCard = form.value.idCard;
@@ -1267,12 +1248,7 @@ const getCollegeNameEnhanced = (student: any) => {
};
/** 获取校服尺码显示名称 */
const getUniformSizeLabel = (value: string | undefined) => {
if (!value) return '-';
// 使用字典数据查找校服尺码
const size = zsjy_uniform_size.value?.find((item: any) => item.value === value);
return size ? size.label : value;
};
const getUniformSizeLabel = (value: string | undefined) => resolveUniformSizeLabel(value, zsjy_uniform_size.value);
/** 获取是否住宿显示文本 */
const getBoardingDisplay = (value: any) => {
@@ -1360,8 +1336,6 @@ const handleQuery = () => {
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
// 确保重置后状态过滤条件依然生效
queryParams.value.enrollmentStatus = ENROLLMENT_STATUS.AUDITING;
handleQuery();
};
@@ -1733,6 +1707,82 @@ const cancelView = () => {
viewDialog.visible = false;
};
/** 打印查看的报名信息 */
const printViewForm = () => {
const form = viewForm;
const display = (value: unknown) => (value === undefined || value === null || value === '' ? '-' : String(value));
const genderText =
form.gender === 'M' ? '男' : form.gender === 'F' ? '女' : getGenderDisplay(form.gender, form.idCard);
const statusText = form.enrollmentStatus
? proxy?.selectDictLabel(zsjy_enrollment_status.value, form.enrollmentStatus) || form.enrollmentStatus
: '-';
const applicationTimeText = form.applicationTime
? proxy?.parseTime(form.applicationTime, '{y}-{m}-{d} {h}:{i}:{s}')
: '-';
try {
printEnrollmentInfo(
`招生报名信息${form.name ? ' - ' + form.name : ''}`,
[
{
title: '基本信息',
fields: [
{ label: '姓名', value: display(form.name) },
{ label: '身份证号', value: display(form.idCard) },
{ label: '性别', value: genderText },
{ label: '出生日期', value: display(form.birthDate) },
{ label: '联系电话', value: display(form.phone1) },
{ label: '备用电话', value: display(form.phone2) },
{ label: '民族', value: display(form.nation) },
{ label: '是否住宿', value: getBoardingDisplay(form.isBoarding) },
{ label: '健康状况', value: display(form.healthStatus) },
{ label: '家庭住址', value: display(form.homeAddress), fullWidth: true }
]
},
{
title: '学业信息',
fields: [
{ label: '毕业中学', value: display(form.middleSchool) },
{ label: '准考证号', value: display(form.examNumber) },
{ label: '中考地区', value: display(form.examRegion) },
{ label: '中考满分', value: display(form.totalScoreFull) },
{ label: '中考成绩', value: display(form.totalScore) },
{ label: '语文成绩', value: display(form.chineseScore) },
{ label: '数学成绩', value: display(form.mathScore) },
{ label: '英语成绩', value: display(form.englishScore) },
{ label: '科学成绩', value: display(form.scienceScore) },
{ label: '社会成绩', value: display(form.socialScore) },
{ label: '体育成绩', value: display(form.peScore) }
]
},
{
title: '报考信息',
fields: [
{ label: '所属学院', value: display(form.collegeName) },
{ label: '学院代码', value: display(form.collegeId) },
{ label: '报考专业', value: display(form.appliedMajor) },
{ label: '专业代码', value: display(form.majorCode) },
{ label: '校服尺码', value: getUniformSizeLabel(form.uniformSize) },
{ label: '报名状态', value: statusText },
{ label: '报名时间', value: applicationTimeText }
]
},
{
title: '其他信息',
fields: [
{ label: '特长爱好', value: display(form.specialties), fullWidth: true },
{ label: '获奖情况', value: display(form.awards), fullWidth: true }
]
}
],
proxy?.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')
);
} catch (error) {
console.error('打印报名信息失败:', error);
proxy?.$modal.msgError('无法打开打印窗口,请检查浏览器是否拦截弹窗');
}
};
/** 显示原始数据 */
const showRawData = () => {
// 显示所有表单数据
@@ -1833,93 +1883,40 @@ const collegeOptions = ref<DeptVO[]>([]);
const majorOptionsByCollege = ref<{ [key: string]: DeptVO[] }>({});
// 添加获取部门数据的方法
/** 获取学院和专业数据 */
/** 获取学院和专业数据(仅开放报名的专业) */
const getCollegeAndMajorData = async () => {
try {
console.log('开始加载学院和专业数据...');
// 强制刷新字典数据
console.log('加载学院字典数据...');
try {
const dictResponse = await proxy?.getDicts('zsjy_college');
console.log('学院字典加载结果:', dictResponse);
} catch (dictError) {
console.error('加载字典数据出错:', dictError);
}
// 获取所有部门数据
console.log('加载部门数据...');
const res = await listDept();
console.log('开始加载开放报名的学院和专业数据...');
const res = await listOpenColleges();
if (res.code === 200 && res.data) {
// 找出学院 - 类别编码以xy开头的部门
const colleges = res.data.filter((dept: DeptVO) => {
return dept.deptCategory && dept.deptCategory.startsWith('xy');
});
const colleges = res.data.map((item: any) => ({
deptId: item.deptId,
deptName: item.deptName,
deptCategory: item.deptCategory,
parentId: item.parentId
})) as DeptVO[];
collegeOptions.value = colleges;
console.log(
'获取到的学院数据:',
colleges.map((c) => ({
deptId: c.deptId,
deptName: c.deptName,
deptCategory: c.deptCategory
}))
majorOptionsByCollege.value = {};
await Promise.all(
colleges.map(async (college) => {
const majorRes = await listOpenMajorsByCollegeId(college.deptId);
const majors = (majorRes.data || []).map((major: any) => ({
deptId: major.deptId,
deptName: major.deptName,
deptCategory: major.deptCategory,
parentId: college.deptId
})) as DeptVO[];
majorOptionsByCollege.value[college.deptId] = majors;
})
);
// 如果没有找到学院数据,尝试其他可能的过滤条件
if (colleges.length === 0) {
console.log('未找到xy开头的学院,尝试其他可能的部门...');
// 尝试查找所有非叶子节点部门
const possibleColleges = res.data.filter((dept: DeptVO) => {
return !dept.isLeaf;
});
if (possibleColleges.length > 0) {
collegeOptions.value = possibleColleges;
console.log(
'找到可能的学院:',
possibleColleges.map((c) => ({
deptId: c.deptId,
deptName: c.deptName,
deptCategory: c.deptCategory
}))
);
}
}
// 找出专业 - 类别编码以zy开头的部门
const allMajors = res.data.filter((dept: DeptVO) => {
return dept.deptCategory && dept.deptCategory.startsWith('zy');
});
console.log('获取到的所有专业数据:', allMajors.length);
// 按照上下级关系组织专业
colleges.forEach((college: DeptVO) => {
// 找出该学院下的所有专业
const majors = allMajors.filter(
(dept: DeptVO) =>
// 使用上下级关系或其他关联关系确定专业归属哪个学院
dept.parentId === college.deptId
);
majorOptionsByCollege.value[college.deptId] = majors;
if (majors.length > 0) {
console.log(`学院 ${college.deptName} 的专业数量: ${majors.length}`);
}
});
// 如果当前有学生信息且缺少学院名称,尝试找到匹配
if (viewForm.collegeId && !viewForm.collegeName) {
// 查找学院名称
const college = collegeOptions.value.find((c) => c.deptCategory === viewForm.collegeId);
if (college) {
console.log(`根据学院代码 ${viewForm.collegeId} 找到学院名称: ${college.deptName}`);
viewForm.collegeName = college.deptName;
} else {
console.warn(`未找到匹配学院代码 ${viewForm.collegeId} 的学院名称`);
}
}
@@ -1927,20 +1924,14 @@ const getCollegeAndMajorData = async () => {
success: true,
collegeCount: collegeOptions.value.length
};
} else {
console.error('部门数据返回异常:', res);
return {
success: false,
error: '部门数据返回异常'
};
}
console.error('开放学院数据返回异常:', res);
return { success: false, error: '开放学院数据返回异常' };
} catch (error) {
console.error('获取部门数据出错:', error);
console.error('获取开放学院和专业数据出错:', error);
proxy?.$modal.msgError('获取学院和专业数据失败');
return {
success: false,
error: error
};
return { success: false, error };
}
};
@@ -0,0 +1,28 @@
/** 招生报名校服尺码标准选项(与移动端兜底数据一致) */
export const DEFAULT_UNIFORM_SIZE_OPTIONS: DictDataOption[] = [
{ value: '150/84A', label: '150/84A(身高150cm)' },
{ value: '155/86A', label: '155/86A(身高155cm)' },
{ value: '160/88A', label: '160/88A(身高160cm)' },
{ value: '165/90A', label: '165/90A(身高165cm)' },
{ value: '170/92A', label: '170/92A(身高170cm)' },
{ value: '175/94A', label: '175/94A(身高175cm)' },
{ value: '180/96A', label: '180/96A(身高180cm)' },
{ value: '185/98A', label: '185/98A(身高185cm)' },
{ value: '190/100A', label: '190/100A(身高190cm)' }
];
/** 优先使用字典数据,字典为空时使用标准尺码兜底 */
export const resolveUniformSizeOptions = (dictOptions?: DictDataOption[] | null): DictDataOption[] => {
if (dictOptions && dictOptions.length > 0) {
return dictOptions;
}
return DEFAULT_UNIFORM_SIZE_OPTIONS;
};
/** 获取校服尺码显示名称 */
export const getUniformSizeLabel = (value: string | undefined, dictOptions?: DictDataOption[] | null): string => {
if (!value) return '-';
const options = resolveUniformSizeOptions(dictOptions);
const size = options.find((item) => item.value === value);
return size ? size.label : value;
};
@@ -0,0 +1,227 @@
<template>
<div class="p-2 page-container">
<div class="flex h-full">
<div class="w-72 mr-4">
<el-card shadow="never" class="h-full">
<template #header>
<div class="flex items-center justify-between">
<span class="text-base font-medium">二级学院</span>
<el-button type="primary" link icon="Plus" @click="goAddMajor">新增专业</el-button>
</div>
</template>
<el-tree
v-loading="collegeLoading"
:data="collegeTreeData"
:props="treeProps"
node-key="deptId"
highlight-current
:current-node-key="activeCollegeId"
@node-click="handleCollegeClick"
>
<template #default="{ node, data }">
<span class="text-sm">{{ node.label }}</span>
<span v-if="data.deptCategory" class="text-xs text-gray-400 ml-2">({{ data.deptCategory }})</span>
</template>
</el-tree>
</el-card>
</div>
<div class="flex-1">
<el-card shadow="never">
<template #header>
<div class="flex items-center justify-between">
<span class="text-base font-medium">{{ activeCollegeName || '招生报名专业管理' }}</span>
<el-button type="primary" :loading="saving" @click="handleSave" v-hasPermi="['zsjy:enrollmentMajor:save']">
保存本学院配置
</el-button>
</div>
</template>
<div v-loading="loading" class="major-config-panel">
<el-empty v-if="!activeCollegeId" description="请在左侧选择学院" />
<template v-else>
<div class="toolbar">
<el-checkbox v-model="checkAll" :indeterminate="isIndeterminate" @change="handleCheckAllChange">全选</el-checkbox>
<span class="selected-count">已选 {{ checkedMajorIds.length }} / {{ majorList.length }}</span>
</div>
<el-empty v-if="majorList.length === 0" description="该学院暂无专业,请前往专业信息维护" />
<el-checkbox-group v-else v-model="checkedMajorIds" class="major-checkbox-group" @change="handleCheckedChange">
<el-checkbox v-for="item in majorList" :key="item.majorDeptId" :label="String(item.majorDeptId)" class="major-checkbox-item">
{{ item.majorName }}
<span class="major-code">({{ item.majorCode }})</span>
</el-checkbox>
</el-checkbox-group>
</template>
</div>
</el-card>
</div>
</div>
</div>
</template>
<script setup lang="ts" name="EnrollmentMajor">
import { ref, computed, onMounted, getCurrentInstance } from 'vue';
import router from '@/router';
import {
listEnrollmentMajorColleges,
listEnrollmentMajorConfig,
saveEnrollmentMajorConfig,
EnrollmentDeptVO
} from '@/api/zsjy/enrollmentMajor';
const { proxy } = getCurrentInstance() as any;
const collegeList = ref<EnrollmentDeptVO[]>([]);
const majorList = ref<EnrollmentDeptVO[]>([]);
const activeCollegeId = ref('');
const checkedMajorIds = ref<string[]>([]);
const collegeLoading = ref(false);
const loading = ref(false);
const saving = ref(false);
const checkAll = ref(false);
const isIndeterminate = ref(false);
const treeProps = {
label: 'deptName',
children: 'children'
};
const collegeTreeData = computed(() => collegeList.value);
const activeCollegeName = computed(() => {
const college = collegeList.value.find((item) => String(item.deptId) === activeCollegeId.value);
return college?.deptName || '';
});
const syncCheckAllState = () => {
const total = majorList.value.length;
const checked = checkedMajorIds.value.length;
checkAll.value = total > 0 && checked === total;
isIndeterminate.value = checked > 0 && checked < total;
};
const loadColleges = async () => {
collegeLoading.value = true;
try {
const res = await listEnrollmentMajorColleges();
collegeList.value = res.data || [];
if (collegeList.value.length === 0) {
proxy?.$modal.msgWarning('未加载到学院数据,请确认后端已更新并分配菜单权限');
return;
}
activeCollegeId.value = String(collegeList.value[0].deptId);
await loadMajorConfig(activeCollegeId.value);
} catch (error) {
console.error('加载学院失败', error);
proxy?.$modal.msgError('加载学院数据失败,请确认后端服务已重启');
} finally {
collegeLoading.value = false;
}
};
const loadMajorConfig = async (collegeDeptId: string) => {
if (!collegeDeptId) return;
loading.value = true;
try {
const res = await listEnrollmentMajorConfig(collegeDeptId);
majorList.value = res.data || [];
checkedMajorIds.value = majorList.value
.filter((item) => item.selected === true || item.selected === 1)
.map((item) => String(item.majorDeptId));
syncCheckAllState();
} catch (error) {
console.error('加载专业配置失败', error);
proxy?.$modal.msgError('加载专业配置失败');
majorList.value = [];
checkedMajorIds.value = [];
} finally {
loading.value = false;
}
};
const handleCollegeClick = (data: EnrollmentDeptVO) => {
activeCollegeId.value = String(data.deptId);
loadMajorConfig(activeCollegeId.value);
};
const handleCheckAllChange = (val: boolean) => {
checkedMajorIds.value = val ? majorList.value.map((item) => String(item.majorDeptId)) : [];
isIndeterminate.value = false;
};
const handleCheckedChange = () => {
syncCheckAllState();
};
const handleSave = async () => {
if (!activeCollegeId.value) return;
saving.value = true;
try {
await saveEnrollmentMajorConfig({
collegeDeptId: activeCollegeId.value,
majorDeptIds: checkedMajorIds.value
});
proxy?.$modal.msgSuccess('保存成功');
await loadMajorConfig(activeCollegeId.value);
} catch (error) {
console.error('保存失败', error);
} finally {
saving.value = false;
}
};
const resolveMajorManagePath = () => {
const matched = router.getRoutes().find((route) => {
const component = route.meta?.component as string | undefined;
return (component && component.includes('majormsg/index')) || route.path.endsWith('/majormsg');
});
return matched?.path || '/student/classmsg/majormsg';
};
const goAddMajor = () => {
const path = resolveMajorManagePath();
router.push(path).catch(() => {
proxy?.$modal.msgError('未找到专业信息页面,请联系管理员配置菜单');
});
};
onMounted(() => {
loadColleges();
});
</script>
<style scoped>
.major-config-panel {
min-height: 360px;
}
.toolbar {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 16px;
}
.selected-count {
color: #909399;
font-size: 13px;
}
.major-checkbox-group {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 8px 16px;
}
.major-checkbox-item {
margin-right: 0;
}
.major-code {
color: #909399;
font-size: 12px;
margin-left: 4px;
}
</style>
@@ -170,7 +170,6 @@
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
import {
listEnrollmentProcess,
exportEnrollmentProcess,
EnrollmentProcessQuery,
EnrollmentProcessVO,
getEnrollmentProcess
@@ -498,9 +497,14 @@ const resetQuery = () => {
handleQuery();
};
// 处理导出
// 处理导出(新生录取名册格式)
const handleExport = () => {
exportEnrollmentProcess(queryParams);
const year = new Date().getFullYear();
proxy?.download(
'/zsjy/enrollment/process/exportRegister',
{ ...queryParams },
`${year}年新生录取名册_${new Date().getTime()}.xlsx`
);
};
// 查看详情