This commit is contained in:
zlwu committed 2025-08-22 08:18:04 +08:00
commit c43680f0aa
6 files changed
+267 -197

No files matched your search

@@ -324,7 +324,6 @@ public class JcbtWorkStudyApplicationController extends BaseController {
* 获取当前用户学生信息
*/
@Operation(summary = "获取当前用户学生信息")
@SaCheckPermission("jcbt:workStudyApplication:query")
@GetMapping("/currentStudentInfo")
public R<XgStudentMsgVo> getCurrentStudentInfo() {
try {
+10
View File
@@ -131,3 +131,13 @@ export const updateApplication = (data) => {
data: data
});
};
/**
* 获取学生已申请岗位状态列表
*/
export const getStudentApplicationStatusList = () => {
return request({
url: '/jcbt/workStudyApplication/student/applicationStatus',
method: 'GET'
});
};
+26 -105
View File
@@ -113,19 +113,7 @@
/>
</u-form-item>
<u-form-item
label="年级"
prop="grade"
>
<view @click="showGradePicker = true">
<u-input
v-model="gradeText"
placeholder="请选择年级"
readonly
suffix-icon="arrow-down"
/>
</view>
</u-form-item>
</view>
<!-- 申请信息 -->
@@ -137,14 +125,15 @@
prop="familyFinancialStatus"
:required="true"
>
<view @click="showFinancialPicker = true">
<u-input
v-model="financialText"
placeholder="请选择家庭经济情况"
readonly
suffix-icon="arrow-down"
<u-radio-group v-model="formData.familyFinancialStatus">
<u-radio
v-for="option in familyStatusOptions"
:key="option.value"
:name="option.value"
:label="option.label"
style="margin-right: 20rpx; margin-bottom: 15rpx; font-size: 28rpx;"
/>
</view>
</u-radio-group>
</u-form-item>
<u-form-item
@@ -241,23 +230,9 @@
</u-button>
</view>
<!-- 年级选择器 -->
<u-picker
:show="showGradePicker"
:columns="gradeOptions"
keyName="label"
@confirm="onGradeConfirm"
@cancel="showGradePicker = false"
/>
<!-- 家庭经济情况选择器 -->
<u-picker
:show="showFinancialPicker"
:columns="financialOptions"
keyName="label"
@confirm="onFinancialConfirm"
@cancel="showFinancialPicker = false"
/>
<!-- 加载状态 -->
<u-loading-page :loading="loading" loading-text="加载中..." />
@@ -293,7 +268,7 @@ export default {
collegeName: '',
majorName: '',
className: '',
grade: '',
familyFinancialStatus: '',
plannedWorkHours: '',
applicationReason: '',
@@ -302,17 +277,13 @@ export default {
remark: ''
},
// 选择器显示状态
showGradePicker: false,
showFinancialPicker: false,
// 选择器文本
gradeText: '',
financialText: '',
// 选择器选项
gradeOptions: [[]],
financialOptions: [[]],
// 家庭经济情况选项(与web端保持一致)
familyStatusOptions: [
{ value: '特困', label: '特困' },
{ value: '贫困', label: '贫困' },
{ value: '一般', label: '一般' },
{ value: '良好', label: '良好' }
],
// 加载状态
loading: false,
@@ -348,8 +319,7 @@ export default {
// 并行加载数据
const promises = [
this.loadPositionData(),
this.loadStudentInfo(),
this.loadApplicationOptions()
this.loadStudentInfo()
];
await Promise.all(promises);
@@ -396,20 +366,6 @@ export default {
}
},
// 加载申请选项
async loadApplicationOptions() {
const response = await getApplicationOptions();
if (response.code === 200) {
const options = response.data;
// 设置年级选项
this.gradeOptions = [options.gradeOptions || []];
// 设置家庭经济情况选项
this.financialOptions = [options.familyFinancialStatusOptions || []];
}
},
// 加载申请详情(编辑模式)
async loadApplicationDetail() {
const response = await getApplicationDetail(this.applicationId);
@@ -423,16 +379,16 @@ export default {
}
});
// 设置选择器文本
this.updatePickerTexts();
}
},
// 填充学生基本信息
fillStudentInfo() {
if (this.studentInfo) {
this.formData.studentName = this.studentInfo.studentName || '';
this.formData.studentNumber = this.studentInfo.studentNumber || '';
// 修改字段映射以匹配后端返回的字段名
this.formData.studentName = this.studentInfo.name || ''; // 修正:name
this.formData.studentNumber = this.studentInfo.studentNo || ''; // 修正:studentNo
this.formData.collegeName = this.studentInfo.collegeName || '';
this.formData.majorName = this.studentInfo.majorName || '';
this.formData.className = this.studentInfo.className || '';
@@ -440,47 +396,12 @@ export default {
// 如果是创建模式,填充其他基本信息
if (this.mode === 'create') {
this.formData.idCard = this.studentInfo.idCard || '';
this.formData.contactPhone = this.studentInfo.contactPhone || '';
this.formData.grade = this.studentInfo.grade || '';
this.formData.contactPhone = this.studentInfo.phone || ''; // 修正:phone
}
this.updatePickerTexts();
}
},
// 更新选择器文本显示
updatePickerTexts() {
// 更新年级文本
if (this.formData.grade && this.gradeOptions[0].length > 0) {
const gradeOption = this.gradeOptions[0].find(item => item.value === this.formData.grade);
this.gradeText = gradeOption ? gradeOption.label : '';
}
// 更新家庭经济情况文本
if (this.formData.familyFinancialStatus && this.financialOptions[0].length > 0) {
const financialOption = this.financialOptions[0].find(item => item.value === this.formData.familyFinancialStatus);
this.financialText = financialOption ? financialOption.label : '';
}
},
// 年级选择确认
onGradeConfirm(e) {
const index = e.indexs[0];
const selected = this.gradeOptions[0][index];
this.formData.grade = selected.value;
this.gradeText = selected.label;
this.showGradePicker = false;
},
// 家庭经济情况选择确认
onFinancialConfirm(e) {
const index = e.indexs[0];
const selected = this.financialOptions[0][index];
this.formData.familyFinancialStatus = selected.value;
this.financialText = selected.label;
this.showFinancialPicker = false;
},
// 表单验证
validateForm() {
const requiredFields = [
+206 -56
View File
@@ -142,6 +142,11 @@
<u-icon name="calendar" size="28" color="#666" />
<text class="info-text">有效期:{{ item.startDate }} 至 {{ item.endDate }}</text>
</view>
<!-- 申请状态信息 -->
<view class="info-row" v-if="getPositionApplicationStatus(item)">
<u-icon name="checkmark-circle" size="28" color="#1890ff" />
<text class="info-text" style="color: #1890ff;">我的申请:{{ getPositionApplicationStatus(item) }}</text>
</view>
</view>
<!-- 操作按钮 -->
@@ -286,7 +291,8 @@ import {
getAvailablePositions,
getMyApplications,
cancelApplication,
getCurrentStudentInfo
getCurrentStudentInfo,
getStudentApplicationStatusList
} from '@/api/workStudy.js';
export default {
@@ -354,7 +360,10 @@ export default {
refreshing: false,
// 用户信息
studentInfo: null
studentInfo: null,
// 学生申请状态列表(用于判断申请状态)
studentApplications: []
};
},
@@ -396,6 +405,8 @@ export default {
try {
// 获取学生信息
await this.loadStudentInfo();
// 获取学生申请状态列表
await this.loadStudentApplications();
// 加载岗位列表
await this.loadPositionList(true);
} catch (error) {
@@ -419,6 +430,18 @@ export default {
}
},
// 加载学生申请状态列表
async loadStudentApplications() {
try {
const response = await getStudentApplicationStatusList();
if (response.code === 200) {
this.studentApplications = response.data || [];
}
} catch (error) {
console.error('获取申请状态失败:', error);
}
},
// 加载岗位列表
async loadPositionList(reset = false) {
if (this.loading) return;
@@ -532,16 +555,21 @@ export default {
},
// 刷新数据
refreshData() {
async refreshData() {
this.refreshing = true;
if (this.currentTab === 0) {
this.loadPositionList(true).finally(() => {
this.refreshing = false;
});
} else {
this.loadMyApplications(true).finally(() => {
this.refreshing = false;
});
try {
// 同时刷新学生申请状态
await this.loadStudentApplications();
if (this.currentTab === 0) {
await this.loadPositionList(true);
} else {
await this.loadMyApplications(true);
}
} catch (error) {
console.error('刷新数据失败:', error);
} finally {
this.refreshing = false;
}
},
@@ -564,23 +592,45 @@ export default {
});
},
// 申请岗位
handleApply(event, item) {
// 如果只传了一个参数,说明是从卡片点击调用的,第一个参数就是item
if (!item && typeof event === 'object' && event.id) {
item = event;
// 申请岗位(参考web端实现)
handleApply(event, position) {
// 如果只传了一个参数,说明是从卡片点击调用的,第一个参数就是position
if (!position && typeof event === 'object' && event.id) {
position = event;
}
if (!this.canApply(item)) {
const application = this.studentApplications.find(app => app.positionId === position.id);
// 如果已有申请且状态为已提交或审核中,跳转到申请管理页面
if (application && ['submitted', 'college_reviewing', 'final_reviewing'].includes(application.status)) {
uni.switchTab({
url: '/pages/workStudy/index'
});
// 切换到"我的申请"标签页
this.currentTab = 1;
this.onTabChange(1);
return;
}
// 如果已录用,不允许操作
if (application && application.status === 'approved') {
uni.showToast({
title: this.getApplyButtonText(item),
title: '您已被该岗位录用',
icon: 'none'
});
return;
}
if (!this.canApply(position)) {
uni.showToast({
title: '该岗位当前不可申请',
icon: 'none'
});
return;
}
uni.navigateTo({
url: `/pages/workStudy/apply?positionId=${item.id}`
url: `/pages/workStudy/apply?positionId=${position.id}`
});
},
@@ -661,53 +711,140 @@ export default {
return typeMap[status] || 'default';
},
// 判断是否可以申请
canApply(item) {
return item.status === 'RECRUITING' &&
(item.currentCount || 0) < (item.requiredCount || 0);
// 判断是否可以申请(完全参考web端实现)
canApply(position) {
const application = this.studentApplications.find(app => app.positionId === position.id);
// 如果已录用,禁用按钮
if (application && application.status === 'approved') {
return false;
}
// 检查岗位状态
if (!['PUBLISHED', 'RECRUITING'].includes(position.status || '')) {
return false;
}
// 检查是否还有空缺
const current = position.currentCount || 0;
const required = position.requiredCount || 0;
if (current >= required) {
return false;
}
// 检查有效期
if (position.endDate) {
const endDate = new Date(position.endDate);
if (endDate < new Date()) {
return false;
}
}
return true;
},
// 获取申请按钮文字
getApplyButtonText(item) {
if (item.status !== 'RECRUITING') {
return '暂不可申请';
// 获取申请按钮文字(参考web端实现8种状态)
getApplyButtonText(position) {
const application = this.studentApplications.find(app => app.positionId === position.id);
// 如果已录用
if (application && application.status === 'approved') {
return '申请已通过';
}
if ((item.currentCount || 0) >= (item.requiredCount || 0)) {
return '已满员';
// 如果有申请且状态为审核中
if (application && ['submitted', 'college_reviewing', 'final_reviewing'].includes(application.status)) {
return '申请审核中';
}
return '申请岗位';
// 如果被拒绝
if (application && application.status === 'rejected') {
return '重新申请';
}
// 检查岗位状态
if (!['PUBLISHED', 'RECRUITING'].includes(position.status || '')) {
if (position.status === 'EXPIRED') {
return '岗位已过期';
} else if (position.status === 'SUSPENDED') {
return '岗位已暂停';
} else if (position.status === 'CANCELLED') {
return '岗位已取消';
} else {
return '暂不可申请';
}
}
// 检查是否还有空缺
const current = position.currentCount || 0;
const required = position.requiredCount || 0;
if (current >= required) {
return '岗位已满员';
}
// 检查有效期
if (position.endDate) {
const endDate = new Date(position.endDate);
if (endDate < new Date()) {
return '岗位已过期';
}
}
// 默认可申请
return '申请此岗位';
},
// 获取申请状态文字
// 获取申请状态文字(与web端保持一致)
getApplicationStatusText(item) {
const statusMap = {
'DRAFT': '草稿',
'SUBMITTED': '已提交',
'COLLEGE_REVIEWING': '学院审核中',
'COLLEGE_APPROVED': '学院审核通过',
'COLLEGE_REJECTED': '学院审核未通过',
'FINAL_REVIEWING': '学生处审核中',
'FINAL_APPROVED': '审核通过',
'FINAL_REJECTED': '审核未通过',
'CANCELLED': '已撤销'
};
return statusMap[item.status] || '未知状态';
if (!item) return '未申请';
// 支持两种格式:web端的小写格式和移动端的大写格式
const status = item.status ? item.status.toLowerCase() : '';
switch (status) {
case 'submitted':
case 'SUBMITTED':
return '已提交';
case 'college_reviewing':
case 'COLLEGE_REVIEWING':
return '学院审核中';
case 'final_reviewing':
case 'FINAL_REVIEWING':
return '学生处审核中';
case 'approved':
case 'FINAL_APPROVED':
return '审核通过';
case 'rejected':
case 'FINAL_REJECTED':
return '审核不通过';
case 'cancelled':
case 'CANCELLED':
return '已撤销';
case 'draft':
case 'DRAFT':
return '草稿';
default:
return '未知状态';
}
},
// 获取申请状态类型
// 获取申请状态类型(与web端保持一致)
getApplicationStatusType(item) {
if (!item) return 'default';
// 支持两种格式:web端的小写格式和移动端的大写格式
const status = item.status ? item.status.toLowerCase() : '';
const typeMap = {
'DRAFT': 'info',
'SUBMITTED': 'warning',
'COLLEGE_REVIEWING': 'warning',
'COLLEGE_APPROVED': 'success',
'COLLEGE_REJECTED': 'error',
'FINAL_REVIEWING': 'warning',
'FINAL_APPROVED': 'success',
'FINAL_REJECTED': 'error',
'CANCELLED': 'info'
'draft': 'info',
'submitted': 'warning',
'college_reviewing': 'warning',
'final_reviewing': 'warning',
'approved': 'success',
'rejected': 'error',
'cancelled': 'info'
};
return typeMap[item.status] || 'default';
return typeMap[status] || 'default';
},
// 获取审核状态文字
@@ -720,9 +857,22 @@ export default {
return statusMap[status] || '未知';
},
// 判断是否可以撤销申请
// 判断是否可以撤销申请(与web端保持一致)
canCancelApplication(item) {
return ['DRAFT', 'SUBMITTED', 'COLLEGE_REVIEWING'].includes(item.status);
if (!item) return false;
// 支持两种格式:web端的小写格式和移动端的大写格式
const status = item.status ? item.status.toLowerCase() : '';
return ['draft', 'submitted', 'college_reviewing'].includes(status) ||
['DRAFT', 'SUBMITTED', 'COLLEGE_REVIEWING'].includes(item.status);
},
// 获取岗位的申请状态(用于在岗位卡片中显示)
getPositionApplicationStatus(position) {
const application = this.studentApplications.find(app => app.positionId === position.id);
if (!application) return null;
return this.getApplicationStatusText(application);
}
}
};
@@ -232,8 +232,6 @@ const initFormData = async () => {
formData.positionId = props.position.id as number;
formData.positionName = props.position.positionName || '';
formData.positionType = '勤工俭学'; // 默认类型
// 使用学院名称作为工作地点
formData.workLocation = props.position.collegeName || '待确定';
}
try {
@@ -156,8 +156,7 @@
<!-- 岗位详情对话框 -->
<el-dialog v-model="detailDialogVisible" title="岗位详情" width="80%" :close-on-click-modal="false" class="position-detail-dialog">
<!-- <PositionDetail v-if="detailDialogVisible && selectedPositionId" :position-id="selectedPositionId" @apply="handleApplyFromDetail" /> -->
<div style="padding: 20px; text-align: center;">岗位详情页面开发中...</div>
<PositionDetail v-if="detailDialogVisible && selectedPositionId" :position-id="selectedPositionId" @apply="handleApplyFromDetail" />
<template #footer>
<el-button @click="detailDialogVisible = false">关闭</el-button>
</template>
@@ -165,17 +164,12 @@
<!-- 申请表单对话框 -->
<el-dialog v-model="applyDialogVisible" title="申请岗位" width="70%" :close-on-click-modal="false" class="apply-dialog">
<!-- <ApplicationForm
<ApplicationForm
v-if="applyDialogVisible && selectedPosition"
:position="selectedPosition"
@success="handleApplySuccess"
@cancel="applyDialogVisible = false"
/> -->
<div style="padding: 20px; text-align: center;">申请表单页面开发中...</div>
<template #footer>
<el-button @click="applyDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleApplySuccess">确认申请</el-button>
</template>
/>
</el-dialog>
</div>
</template>
@@ -187,8 +181,8 @@ import { useRouter } from 'vue-router';
import { listVisiblePositions } from '@/api/jcbt/workStudyPosition';
import type { WorkStudyPositionVO } from '@/api/jcbt/workStudyPosition';
import { getStudentApplicationStatusList } from '@/api/jcbt/workStudyApplication';
// import PositionDetail from './detail.vue';
// import ApplicationForm from './form.vue';
import PositionDetail from './detail.vue';
import ApplicationForm from './form.vue';
const router = useRouter();
@@ -557,13 +551,11 @@ onMounted(() => {
margin-bottom: 20px;
}
:deep(.el-table__header) {
th {
background: linear-gradient(135deg, #fafbff 0%, #f8fafc 100%);
color: #303133;
font-weight: 600;
border-color: rgba(93, 135, 255, 0.1);
}
:deep(.el-table__header th) {
background: linear-gradient(135deg, #fafbff 0%, #f8fafc 100%);
color: #303133;
font-weight: 600;
border-color: rgba(93, 135, 255, 0.1);
}
:deep(.el-table__row:hover) {
@@ -579,10 +571,10 @@ onMounted(() => {
.number-display {
font-weight: 600;
color: rgb(93, 135, 255);
}
&.text-success {
color: rgb(19, 222, 185);
}
.number-display.text-success {
color: rgb(19, 222, 185);
}
.salary-info {
@@ -591,10 +583,10 @@ onMounted(() => {
gap: 4px;
color: rgb(255, 174, 31);
font-weight: 500;
}
.el-icon {
color: rgb(255, 174, 31);
}
.salary-info .el-icon {
color: rgb(255, 174, 31);
}
.date-range {
@@ -602,17 +594,17 @@ onMounted(() => {
flex-direction: column;
gap: 2px;
font-size: 12px;
}
.date-item {
color: #606266;
font-family: monospace;
}
.date-range .date-item {
color: #606266;
font-family: monospace;
}
.date-separator {
color: #909399;
text-align: center;
font-size: 10px;
}
.date-range .date-separator {
color: #909399;
text-align: center;
font-size: 10px;
}
.action-buttons {