提交代码

This commit is contained in:
zlwu committed 2025-08-22 00:53:38 +08:00
commit 6d2a23942a
20 files changed
+4194 -324

No files matched your search

@@ -347,4 +347,17 @@ public class JcbtWorkStudyApplicationController extends BaseController {
}
}
/**
* 获取学生已申请岗位列表
*/
@Operation(summary = "获取学生已申请岗位列表")
@GetMapping("/student/applicationStatus")
@SaCheckPermission("jcbt:workStudyApplication:query")
public R<List<Map<String, Object>>> getStudentApplicationStatus() {
List<Map<String, Object>> statusList = workStudyApplicationService.getStudentApplicationStatus();
return R.ok(statusList);
}
}
@@ -416,10 +416,64 @@ public class JcbtWorkStudyPositionController extends BaseController {
/**
* 获取当前用户所在学院ID
* TODO: 实现获取当前用户学院ID的逻辑
* 通过查询学生信息或用户部门信息获取
*/
private Long getCurrentUserCollegeId() {
// 临时返回固定值,实际应该从用户信息中获取
return 1L;
try {
// 获取当前登录用户ID
Long userId = LoginHelper.getUserId();
if (userId == null) {
log.warn("用户未登录");
return null;
}
// 首先尝试通过学生信息获取学院ID
try {
XgStudentMsgVo studentInfo = xgStudentMsgService.queryByUserId(userId);
if (studentInfo != null && studentInfo.getCollegeId() != null) {
log.debug("通过学生信息获取学院ID: {}", studentInfo.getCollegeId());
return Long.parseLong(studentInfo.getCollegeId());
}
} catch (Exception e) {
log.debug("通过学生信息获取学院ID失败,可能用户不是学生: {}", e.getMessage());
}
// 如果不是学生用户,通过用户部门信息获取学院ID
try {
Long deptId = LoginHelper.getDeptId();
if (deptId != null) {
log.debug("通过部门信息获取学院ID: {}", deptId);
return deptId;
}
} catch (Exception e) {
log.debug("通过部门信息获取学院ID失败: {}", e.getMessage());
}
// 尝试通过部门类别获取学院信息
try {
String deptCategory = LoginHelper.getDeptCategory();
String deptName = LoginHelper.getDeptName();
log.debug("用户部门信息 - 部门名称: {}, 部门类别: {}", deptName, deptCategory);
// 如果部门类别是学院类型,则使用部门ID作为学院ID
if ("college".equals(deptCategory) || "学院".equals(deptCategory)) {
Long deptId = LoginHelper.getDeptId();
if (deptId != null) {
log.info("根据部门类别确认为学院用户,学院ID: {}", deptId);
return deptId;
}
}
} catch (Exception e) {
log.debug("通过部门类别获取学院ID失败: {}", e.getMessage());
}
log.warn("无法获取用户 {} 的学院信息,用户ID: {}, 部门ID: {}, 部门名称: {}",
userId, userId, LoginHelper.getDeptId(), LoginHelper.getDeptName());
return null;
} catch (Exception e) {
log.error("获取当前用户学院ID失败", e);
return null;
}
}
}
@@ -5,6 +5,7 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.jcbt.domain.JcbtWorkStudyApplication;
import org.dromara.jcbt.domain.vo.JcbtWorkStudyApplicationVo;
import java.util.List;
import java.util.Map;
/**
@@ -24,4 +25,6 @@ public interface JcbtWorkStudyApplicationMapper extends BaseMapperPlus<JcbtWorkS
*/
Map<String, Object> selectApplicationStatus(@Param("positionId") Long positionId, @Param("userId") Long userId);
List<Map<String, Object>> selectApplicationStatusByStudentId(@Param("studentId") Long studentId);
}
@@ -164,4 +164,10 @@ public interface IJcbtWorkStudyApplicationService {
*/
Map<String, Object> getPositionApplicationStatistics(Long positionId);
/**
* 获取学生已申请岗位列表
*
* @return 学生已申请岗位状态列表 (包含岗位ID和申请状态)
*/
List<Map<String, Object>> getStudentApplicationStatus();
}
@@ -28,6 +28,9 @@ import org.dromara.xg.domain.vo.XgStudentMsgVo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
@@ -338,25 +341,35 @@ public class JcbtWorkStudyApplicationServiceImpl implements IJcbtWorkStudyApplic
// 更新总状态 - 关键修改:确保状态正确更新
String oldStatus = application.getStatus();
if ("APPROVED".equals(reviewBo.getReviewResult())) {
// 标准化审核结果参数,兼容前端传递的小写值
String reviewResult = reviewBo.getReviewResult();
if (reviewResult != null) {
reviewResult = reviewResult.toUpperCase();
}
if ("APPROVED".equals(reviewResult)) {
application.setStatus(WorkStudyApplicationStatusEnum.COLLEGE_APPROVED.getCode());
log.info("🔍 [学院审核] 审核通过,状态更新: {} -> {}", oldStatus, WorkStudyApplicationStatusEnum.COLLEGE_APPROVED.getCode());
} else if ("REJECTED".equals(reviewBo.getReviewResult())) {
} else if ("REJECTED".equals(reviewResult)) {
application.setStatus(WorkStudyApplicationStatusEnum.COLLEGE_REJECTED.getCode());
log.info("🔍 [学院审核] 审核拒绝,状态更新: {} -> {}", oldStatus, WorkStudyApplicationStatusEnum.COLLEGE_REJECTED.getCode());
} else {
throw new ServiceException("审核结果参数错误,只支持approved或rejected");
}
// 使用updateById确保更新
boolean result = baseMapper.updateById(application) > 0;
log.info("🔍 [学院审核] 数据库更新结果: {}", result ? "成功" : "失败");
if (result) {
if (result) {
// 验证更新后的状态
JcbtWorkStudyApplication updatedApplication = baseMapper.selectById(reviewBo.getApplicationId());
log.info("🔍 [学院审核] 更新后验证状态: {}, 审核状态: {}", updatedApplication.getStatus(), updatedApplication.getCollegeReviewStatus());
// 确保状态更新成功
if ("APPROVED".equals(reviewBo.getReviewResult()) && !WorkStudyApplicationStatusEnum.COLLEGE_APPROVED.getCode().equals(updatedApplication.getStatus())) {
String normalizedResult = reviewBo.getReviewResult() != null ? reviewBo.getReviewResult().toUpperCase() : "";
if ("APPROVED".equals(normalizedResult) && !WorkStudyApplicationStatusEnum.COLLEGE_APPROVED.getCode().equals(updatedApplication.getStatus())) {
log.error("🔍 [学院审核] 状态更新失败!期望: {}, 实际: {}", WorkStudyApplicationStatusEnum.COLLEGE_APPROVED.getCode(), updatedApplication.getStatus());
throw new ServiceException("状态更新失败,请重试");
}
@@ -388,8 +401,13 @@ public class JcbtWorkStudyApplicationServiceImpl implements IJcbtWorkStudyApplic
application.setFinalReviewerName(currentUserName);
application.setFinalReviewComment(reviewBo.getReviewComment());
// 更新总状态
if ("APPROVED".equals(reviewBo.getReviewResult())) {
// 更新总状态 - 标准化审核结果参数,兼容前端传递的小写值
String reviewResult = reviewBo.getReviewResult();
if (reviewResult != null) {
reviewResult = reviewResult.toUpperCase();
}
if ("APPROVED".equals(reviewResult)) {
application.setStatus(WorkStudyApplicationStatusEnum.FINAL_APPROVED.getCode());
// 更新岗位当前申请通过人数
@@ -402,8 +420,10 @@ public class JcbtWorkStudyApplicationServiceImpl implements IJcbtWorkStudyApplic
}
positionMapper.updateById(position);
}
} else if ("REJECTED".equals(reviewBo.getReviewResult())) {
} else if ("REJECTED".equals(reviewResult)) {
application.setStatus(WorkStudyApplicationStatusEnum.FINAL_REJECTED.getCode());
} else {
throw new ServiceException("审核结果参数错误,只支持approved或rejected");
}
return baseMapper.updateById(application) > 0;
@@ -529,4 +549,47 @@ public class JcbtWorkStudyApplicationServiceImpl implements IJcbtWorkStudyApplic
return statistics;
}
/**
* 获取学生已申请岗位状态列表
*/
@Override
public List<Map<String, Object>> getStudentApplicationStatus() {
Long studentId = LoginHelper.getUserId();
List<Map<String, Object>> resultList = baseMapper.selectApplicationStatusByStudentId(studentId);
// 转换数据结构并进行状态值转换
List<Map<String, Object>> statusList = new ArrayList<>();
for (Map<String, Object> item : resultList) {
Map<String, Object> result = new HashMap<>();
result.put("positionId", item.get("position_id"));
result.put("status", convertStatusToFrontend((String) item.get("status")));
statusList.add(result);
}
return statusList;
}
/**
* 将后端状态转换为前端状态
*/
private String convertStatusToFrontend(String backendStatus) {
if (backendStatus == null) {
return "submitted";
}
switch (backendStatus) {
case "SUBMITTED":
return "submitted";
case "COLLEGE_APPROVED":
return "college_reviewing";
case "FINAL_APPROVED":
return "approved";
case "COLLEGE_REJECTED":
case "FINAL_REJECTED":
return "rejected";
case "CANCELLED":
return "cancelled";
default:
return "submitted";
}
}
}
@@ -133,4 +133,10 @@
AND deleted = 0
</select>
<select id="selectApplicationStatusByStudentId" resultType="java.util.Map">
SELECT position_id, status
FROM jcbt_work_study_application
WHERE student_id = #{studentId} AND deleted = 0
</select>
</mapper>
+133
View File
@@ -0,0 +1,133 @@
// 勤工俭学API接口
import { request } from '@/common/request.js';
/**
* 学生查看可申请的岗位列表
* @param {Object} params 查询参数
*/
export const getAvailablePositions = (params = {}) => {
return request({
url: '/jcbt/workStudyPosition/visibleList',
method: 'GET',
params: {
...params,
excludeApplied: true // 排除已申请的岗位
}
});
};
/**
* 获取岗位详情及申请状态
* @param {String|Number} positionId 岗位ID
*/
export const getPositionWithApplicationStatus = (positionId) => {
return request({
url: `/jcbt/workStudyPosition/${positionId}/withApplicationStatus`,
method: 'GET'
});
};
/**
* 查看我的申请列表
* @param {Object} params 查询参数
*/
export const getMyApplications = (params = {}) => {
return request({
url: '/jcbt/workStudyApplication/myList',
method: 'GET',
params: params
});
};
/**
* 新增申请
* @param {Object} data 申请数据
*/
export const addApplication = (data) => {
return request({
url: '/jcbt/workStudyApplication',
method: 'POST',
data: data
});
};
/**
* 提交申请
* @param {String|Number} applicationId 申请ID
*/
export const submitApplication = (applicationId) => {
return request({
url: `/jcbt/workStudyApplication/submit/${applicationId}`,
method: 'POST'
});
};
/**
* 撤销申请
* @param {String|Number} applicationId 申请ID
*/
export const cancelApplication = (applicationId) => {
return request({
url: `/jcbt/workStudyApplication/cancel/${applicationId}`,
method: 'POST'
});
};
/**
* 获取当前学生信息
*/
export const getCurrentStudentInfo = () => {
return request({
url: '/jcbt/workStudyApplication/currentStudentInfo',
method: 'GET'
});
};
/**
* 获取申请选项数据
*/
export const getApplicationOptions = () => {
return request({
url: '/jcbt/workStudyApplication/options',
method: 'GET'
});
};
/**
* 检查重复申请
* @param {String} studentNumber 学号
* @param {Number} positionId 岗位ID
*/
export const checkDuplicateApplication = (studentNumber, positionId) => {
return request({
url: '/jcbt/workStudyApplication/checkDuplicate',
method: 'GET',
params: {
studentNumber: studentNumber,
positionId: positionId
}
});
};
/**
* 查询申请详情
* @param {String|Number} applicationId 申请ID
*/
export const getApplicationDetail = (applicationId) => {
return request({
url: `/jcbt/workStudyApplication/detail/${applicationId}`,
method: 'GET'
});
};
/**
* 修改申请
* @param {Object} data 申请数据
*/
export const updateApplication = (data) => {
return request({
url: '/jcbt/workStudyApplication',
method: 'PUT',
data: data
});
};
+304 -256
View File
@@ -1,257 +1,305 @@
{
"easycom": {
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
},
"pages": [
{
"path": "pages/login/index",
"style": {
"navigationBarTitleText": "登录",
"navigationStyle": "custom"
}
},
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "杭州第一技师学院智慧校园",
"navigationStyle": "custom"
}
},
{
"path": "pages/task/myDocument",
"style": {
"navigationBarTitleText": "我发起的",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/task/allTaskWaiting",
"style": {
"navigationBarTitleText": "我的待办",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/task/taskWaiting",
"style": {
"navigationBarTitleText": "我的待办",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/task/taskFinish",
"style": {
"navigationBarTitleText": "我的已办",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/task/taskCopyList",
"style": {
"navigationBarTitleText": "抄送我的",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xg/xsjcxx/xsjcxx/index",
"style": {
"navigationBarTitleText": "学生个人信息",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xg/xsjcxx/xsws/index",
"style": {
"navigationBarTitleText": "学生信息完善",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xg/xsjcxx/xsws/stuMsgDialog",
"style": {
"navigationBarTitleText": "信息完善",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xg/xsjcxx/xsws/stuMsgDetailDialog",
"style": {
"navigationBarTitleText": "提交历史",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xg/xsjcxx/xsqj/leave/index",
"style": {
"navigationBarTitleText": "学生请假",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xg/xsjcxx/xsqj/leaveEdit/index",
"style": {
"navigationBarTitleText": "请假申请",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xg/xsjcxx/xsqj/cancelLeaveEdit/index",
"style": {
"navigationBarTitleText": "学生销假",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/wechat/js-sdk-test",
"style": {
"navigationBarTitleText": "微信JS-SDK测试",
"navigationStyle": "custom"
}
},
{
"path": "pages/internship/index",
"style": {
"navigationBarTitleText": "实习打卡",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/visit/index",
"style": {
"navigationBarTitleText": "班主任走访",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/visit/add",
"style": {
"navigationBarTitleText": "新增走访",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/visit/detail",
"style": {
"navigationBarTitleText": "走访详情",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/visit/edit",
"style": {
"navigationBarTitleText": "编辑走访",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xgss/sswj/grsswj",
"style": {
"navigationBarTitleText": "宿舍违纪",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xgss/sswj/sswjDetail",
"style": {
"navigationBarTitleText": "违纪详情",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xgss/wsjc/wsjc",
"style": {
"navigationBarTitleText": "宿舍卫生检查",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xgss/wsjc/wsjcAdd",
"style": {
"navigationBarTitleText": "新增宿舍卫生检查",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xgss/wsjc/wsjcDetail",
"style": {
"navigationBarTitleText": "宿舍卫生检查详情",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xgss/xssswsjc/xssswsjc",
"style": {
"navigationBarTitleText": "宿舍卫生检查",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xgss/xssswsjc/xssswsjcDetail",
"style": {
"navigationBarTitleText": "宿舍卫生检查详情",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/enrollment/index",
"style": {
"navigationBarTitleText": "新生报名登记",
"navigationStyle": "custom"
}
},
{
"path": "pages/xskh/wjjl/xswjjl",
"style": {
"navigationBarTitleText": "违纪记录(学生)",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xskh/wjjl/revokeApplication",
"style": {
"navigationBarTitleText": "撤销申请",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xskh/khsq/khsq",
"style": {
"navigationBarTitleText": "考核申请",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "智慧校园",
"navigationBarBackgroundColor": "#0051d9",
"backgroundColor": "#F8F8F8"
}
{
"easycom": {
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
},
"pages": [
{
"path": "pages/login/index",
"style": {
"navigationBarTitleText": "登录",
"navigationStyle": "custom"
}
},
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "杭州第一技师学院智慧校园",
"navigationStyle": "custom"
}
},
{
"path": "pages/task/myDocument",
"style": {
"navigationBarTitleText": "我发起的",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/task/allTaskWaiting",
"style": {
"navigationBarTitleText": "我的待办",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/task/taskWaiting",
"style": {
"navigationBarTitleText": "我的待办",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/task/taskFinish",
"style": {
"navigationBarTitleText": "我的已办",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/task/taskCopyList",
"style": {
"navigationBarTitleText": "抄送我的",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xg/xsjcxx/xsjcxx/index",
"style": {
"navigationBarTitleText": "学生个人信息",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xg/xsjcxx/xsws/index",
"style": {
"navigationBarTitleText": "学生信息完善",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xg/xsjcxx/xsws/stuMsgDialog",
"style": {
"navigationBarTitleText": "信息完善",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xg/xsjcxx/xsws/stuMsgDetailDialog",
"style": {
"navigationBarTitleText": "提交历史",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xg/xsjcxx/xsqj/leave/index",
"style": {
"navigationBarTitleText": "学生请假",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xg/xsjcxx/xsqj/leaveEdit/index",
"style": {
"navigationBarTitleText": "请假申请",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xg/xsjcxx/xsqj/cancelLeaveEdit/index",
"style": {
"navigationBarTitleText": "学生销假",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/wechat/js-sdk-test",
"style": {
"navigationBarTitleText": "微信JS-SDK测试",
"navigationStyle": "custom"
}
},
{
"path": "pages/internship/index",
"style": {
"navigationBarTitleText": "实习打卡",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/visit/index",
"style": {
"navigationBarTitleText": "班主任走访",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/visit/add",
"style": {
"navigationBarTitleText": "新增走访",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/visit/detail",
"style": {
"navigationBarTitleText": "走访详情",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/visit/edit",
"style": {
"navigationBarTitleText": "编辑走访",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xgss/sswj/grsswj",
"style": {
"navigationBarTitleText": "宿舍违纪",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xgss/sswj/sswjDetail",
"style": {
"navigationBarTitleText": "违纪详情",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xgss/wsjc/wsjc",
"style": {
"navigationBarTitleText": "宿舍卫生检查",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xgss/wsjc/wsjcAdd",
"style": {
"navigationBarTitleText": "新增宿舍卫生检查",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xgss/wsjc/wsjcDetail",
"style": {
"navigationBarTitleText": "宿舍卫生检查详情",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xgss/xssswsjc/xssswsjc",
"style": {
"navigationBarTitleText": "宿舍卫生检查",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xgss/xssswsjc/xssswsjcDetail",
"style": {
"navigationBarTitleText": "宿舍卫生检查详情",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/enrollment/index",
"style": {
"navigationBarTitleText": "新生报名登记",
"navigationStyle": "custom"
}
},
{
"path": "pages/xskh/wjjl/xswjjl",
"style": {
"navigationBarTitleText": "违纪记录(学生)",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xskh/wjjl/revokeApplication",
"style": {
"navigationBarTitleText": "撤销申请",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xskh/khsq/khsq",
"style": {
"navigationBarTitleText": "考核申请",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xskh/khsq/form",
"style": {
"navigationBarTitleText": "考核申请表单",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xskh/khsq/indicatorDataDialog",
"style": {
"navigationBarTitleText": "学生数据详情",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/workStudy/index",
"style": {
"navigationBarTitleText": "勤工俭学",
"navigationBarBackgroundColor": "#1890ff",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/workStudy/detail",
"style": {
"navigationBarTitleText": "岗位详情",
"navigationBarBackgroundColor": "#1890ff",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/workStudy/apply",
"style": {
"navigationBarTitleText": "申请岗位",
"navigationBarBackgroundColor": "#1890ff",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/workStudy/applicationDetail",
"style": {
"navigationBarTitleText": "申请详情",
"navigationBarBackgroundColor": "#1890ff",
"navigationBarTextStyle": "white"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "智慧校园",
"navigationBarBackgroundColor": "#0051d9",
"backgroundColor": "#F8F8F8"
}
}
+22
View File
@@ -221,6 +221,20 @@
</template>
</ServiceItem>
<ServiceItem
v-if="hasPermi(['student:workstudy:list'])"
title="勤工俭学"
@click="gotoWorkStudy"
>
<template #icon>
<image
src="/static/00127.png"
mode="aspectFit"
style="width: 24px; height: 24px"
/>
</template>
</ServiceItem>
<ServiceItem
v-if="hasPermi(['student:violation:list'])"
title="违纪记录(学生)"
@@ -674,6 +688,7 @@ export default {
this.hasPermi(['student:dorm:violation:list']) ||
this.hasPermi(['student:visit:list']) ||
this.hasPermi(['student:dorm:hygiene:list']) ||
this.hasPermi(['student:workstudy:list']) ||
this.hasPermi(['student:violation:list']) ||
this.hasPermi(['student:assessment:apply'])
);
@@ -910,6 +925,13 @@ export default {
});
},
// 跳转到勤工俭学页面
gotoWorkStudy() {
uni.navigateTo({
url: '/pages/workStudy/index',
});
},
// 跳转到学生违纪记录页面
gotoStudentViolation() {
uni.navigateTo({
@@ -0,0 +1,624 @@
<template>
<view class="detail-container">
<!-- 申请状态卡片 -->
<view class="status-card" v-if="applicationData">
<view class="card-title">申请状态</view>
<view class="status-info">
<view class="main-status">
<u-tag
:text="getApplicationStatusText(applicationData.status)"
:type="getApplicationStatusType(applicationData.status)"
size="large"
shape="circle"
/>
</view>
<view class="status-detail">
<view class="status-item">
<text class="status-label">申请时间:</text>
<text class="status-value">{{ applicationData.createTime }}</text>
</view>
<view class="status-item" v-if="applicationData.updateTime !== applicationData.createTime">
<text class="status-label">更新时间:</text>
<text class="status-value">{{ applicationData.updateTime }}</text>
</view>
</view>
</view>
</view>
<!-- 岗位信息 -->
<view class="info-card" v-if="applicationData">
<view class="card-title">申请岗位</view>
<view class="position-info">
<view class="position-name">{{ applicationData.positionName }}</view>
<view class="position-detail">
<view class="detail-item">
<u-icon name="list" size="28" color="#666" />
<text>岗位类型:{{ applicationData.positionType }}</text>
</view>
<view class="detail-item">
<u-icon name="map" size="28" color="#666" />
<text>工作地点:{{ applicationData.workLocation }}</text>
</view>
</view>
</view>
</view>
<!-- 基本信息 -->
<view class="info-card" v-if="applicationData">
<view class="card-title">基本信息</view>
<view class="info-list">
<view class="info-item">
<text class="info-label">学生姓名:</text>
<text class="info-value">{{ applicationData.studentName }}</text>
</view>
<view class="info-item">
<text class="info-label">学号:</text>
<text class="info-value">{{ applicationData.studentNumber }}</text>
</view>
<view class="info-item">
<text class="info-label">身份证号:</text>
<text class="info-value">{{ formatIdCard(applicationData.idCard) }}</text>
</view>
<view class="info-item">
<text class="info-label">联系电话:</text>
<text class="info-value contact-phone" @click="callPhone">{{ applicationData.contactPhone }}</text>
</view>
<view class="info-item">
<text class="info-label">学院:</text>
<text class="info-value">{{ applicationData.collegeName }}</text>
</view>
<view class="info-item">
<text class="info-label">专业:</text>
<text class="info-value">{{ applicationData.majorName }}</text>
</view>
<view class="info-item">
<text class="info-label">班级:</text>
<text class="info-value">{{ applicationData.className }}</text>
</view>
<view class="info-item" v-if="applicationData.grade">
<text class="info-label">年级:</text>
<text class="info-value">{{ applicationData.grade }}</text>
</view>
</view>
</view>
<!-- 申请信息 -->
<view class="info-card" v-if="applicationData">
<view class="card-title">申请信息</view>
<view class="info-list">
<view class="info-item">
<text class="info-label">家庭经济情况:</text>
<text class="info-value">{{ applicationData.familyFinancialStatus }}</text>
</view>
<view class="info-item" v-if="applicationData.plannedWorkHours">
<text class="info-label">计划工作时长:</text>
<text class="info-value">{{ applicationData.plannedWorkHours }}小时/周</text>
</view>
</view>
</view>
<!-- 申请理由 -->
<view class="content-card" v-if="applicationData && applicationData.applicationReason">
<view class="card-title">申请理由</view>
<view class="content-text">{{ applicationData.applicationReason }}</view>
</view>
<!-- 个人能力及经验 -->
<view class="content-card" v-if="applicationData && applicationData.personalAbility">
<view class="card-title">个人能力及经验</view>
<view class="content-text">{{ applicationData.personalAbility }}</view>
</view>
<!-- 时间安排 -->
<view class="content-card" v-if="applicationData && applicationData.timeArrangement">
<view class="card-title">时间安排</view>
<view class="content-text">{{ applicationData.timeArrangement }}</view>
</view>
<!-- 备注信息 -->
<view class="content-card" v-if="applicationData && applicationData.remark">
<view class="card-title">备注信息</view>
<view class="content-text">{{ applicationData.remark }}</view>
</view>
<!-- 审核记录 -->
<view class="review-card" v-if="hasReviewRecord">
<view class="card-title">审核记录</view>
<!-- 学院审核 -->
<view class="review-item" v-if="applicationData.collegeReviewStatus">
<view class="review-header">
<view class="review-title">学院审核</view>
<u-tag
:text="getReviewStatusText(applicationData.collegeReviewStatus)"
:type="getReviewStatusType(applicationData.collegeReviewStatus)"
size="mini"
shape="circle"
/>
</view>
<view class="review-info">
<view class="review-detail" v-if="applicationData.collegeReviewTime">
<text class="detail-label">审核时间:</text>
<text class="detail-value">{{ applicationData.collegeReviewTime }}</text>
</view>
<view class="review-detail" v-if="applicationData.collegeReviewerName">
<text class="detail-label">审核人员:</text>
<text class="detail-value">{{ applicationData.collegeReviewerName }}</text>
</view>
<view class="review-comment" v-if="applicationData.collegeReviewComment">
<text class="comment-label">审核意见:</text>
<text class="comment-text">{{ applicationData.collegeReviewComment }}</text>
</view>
</view>
</view>
<!-- 学生处审核 -->
<view class="review-item" v-if="applicationData.finalReviewStatus">
<view class="review-header">
<view class="review-title">学生处审核</view>
<u-tag
:text="getReviewStatusText(applicationData.finalReviewStatus)"
:type="getReviewStatusType(applicationData.finalReviewStatus)"
size="mini"
shape="circle"
/>
</view>
<view class="review-info">
<view class="review-detail" v-if="applicationData.finalReviewTime">
<text class="detail-label">审核时间:</text>
<text class="detail-value">{{ applicationData.finalReviewTime }}</text>
</view>
<view class="review-detail" v-if="applicationData.finalReviewerName">
<text class="detail-label">审核人员:</text>
<text class="detail-value">{{ applicationData.finalReviewerName }}</text>
</view>
<view class="review-comment" v-if="applicationData.finalReviewComment">
<text class="comment-label">审核意见:</text>
<text class="comment-text">{{ applicationData.finalReviewComment }}</text>
</view>
</view>
</view>
</view>
<!-- 底部操作按钮 -->
<view class="bottom-actions" v-if="showActions">
<u-button
type="warning"
size="large"
@click="editApplication"
v-if="canEdit"
style="margin-right: 20rpx;"
>
编辑申请
</u-button>
<u-button
type="error"
size="large"
@click="cancelApplication"
v-if="canCancel"
:loading="canceling"
>
撤销申请
</u-button>
</view>
<!-- 加载状态 -->
<u-loading-page :loading="loading" loading-text="加载中..." />
</view>
</template>
<script>
import {
getApplicationDetail,
cancelApplication
} from '@/api/workStudy.js';
export default {
name: 'ApplicationDetail',
data() {
return {
applicationId: '',
applicationData: null,
loading: false,
canceling: false
};
},
computed: {
// 是否有审核记录
hasReviewRecord() {
return this.applicationData &&
(this.applicationData.collegeReviewStatus || this.applicationData.finalReviewStatus);
},
// 是否显示操作按钮
showActions() {
return this.canEdit || this.canCancel;
},
// 是否可以编辑
canEdit() {
return this.applicationData &&
['DRAFT', 'COLLEGE_REJECTED'].includes(this.applicationData.status);
},
// 是否可以撤销
canCancel() {
return this.applicationData &&
['DRAFT', 'SUBMITTED', 'COLLEGE_REVIEWING'].includes(this.applicationData.status);
}
},
onLoad(options) {
if (options.id) {
this.applicationId = options.id;
this.loadApplicationDetail();
} else {
uni.showToast({
title: '申请ID不能为空',
icon: 'none'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
}
},
methods: {
// 加载申请详情
async loadApplicationDetail() {
this.loading = true;
try {
const response = await getApplicationDetail(this.applicationId);
if (response.code === 200) {
this.applicationData = response.data;
} else {
throw new Error(response.msg || '获取申请详情失败');
}
} catch (error) {
console.error('加载申请详情失败:', error);
uni.showToast({
title: error.message || '加载失败',
icon: 'none'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
} finally {
this.loading = false;
}
},
// 编辑申请
editApplication() {
if (!this.applicationData) return;
uni.navigateTo({
url: `/pages/workStudy/apply?positionId=${this.applicationData.positionId}&applicationId=${this.applicationData.id}&mode=edit`
});
},
// 撤销申请
async cancelApplication() {
try {
const { confirm } = await uni.showModal({
title: '确认撤销',
content: '确定要撤销此申请吗?',
showCancel: true
});
if (!confirm) return; // 用户取消
this.canceling = true;
const response = await cancelApplication(this.applicationData.id);
if (response.code === 200) {
uni.showToast({
title: '撤销成功',
icon: 'success'
});
// 重新加载数据
setTimeout(() => {
this.loadApplicationDetail();
}, 1000);
} else {
throw new Error(response.msg || '撤销失败');
}
} catch (error) {
console.error('撤销申请失败:', error);
uni.showToast({
title: error.message || '撤销失败',
icon: 'none'
});
} finally {
this.canceling = false;
}
},
// 拨打电话
callPhone() {
if (this.applicationData && this.applicationData.contactPhone) {
uni.makePhoneCall({
phoneNumber: this.applicationData.contactPhone,
fail: (error) => {
console.error('拨打电话失败:', error);
uni.showToast({
title: '拨打电话失败',
icon: 'none'
});
}
});
}
},
// 格式化身份证号(中间位隐藏)
formatIdCard(idCard) {
if (!idCard || idCard.length < 10) return idCard;
return idCard.substring(0, 6) + '********' + idCard.substring(idCard.length - 4);
},
// 获取申请状态文字
getApplicationStatusText(status) {
const statusMap = {
'DRAFT': '草稿',
'SUBMITTED': '已提交',
'COLLEGE_REVIEWING': '学院审核中',
'COLLEGE_APPROVED': '学院审核通过',
'COLLEGE_REJECTED': '学院审核未通过',
'FINAL_REVIEWING': '学生处审核中',
'FINAL_APPROVED': '审核通过',
'FINAL_REJECTED': '审核未通过',
'CANCELLED': '已撤销'
};
return statusMap[status] || '未知状态';
},
// 获取申请状态类型
getApplicationStatusType(status) {
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'
};
return typeMap[status] || 'default';
},
// 获取审核状态文字
getReviewStatusText(status) {
const statusMap = {
'PENDING': '待审核',
'APPROVED': '通过',
'REJECTED': '未通过'
};
return statusMap[status] || '未知';
},
// 获取审核状态类型
getReviewStatusType(status) {
const typeMap = {
'PENDING': 'warning',
'APPROVED': 'success',
'REJECTED': 'error'
};
return typeMap[status] || 'default';
}
}
};
</script>
<style scoped>
.detail-container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 120rpx; /* 为底部按钮留空间 */
}
.status-card, .info-card, .content-card, .review-card {
background-color: #fff;
margin: 20rpx;
border-radius: 12rpx;
padding: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.card-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 24rpx;
padding-bottom: 16rpx;
border-bottom: 1rpx solid #f0f0f0;
}
/* 状态卡片 */
.status-info {
display: flex;
flex-direction: column;
align-items: center;
}
.main-status {
margin-bottom: 20rpx;
}
.status-detail {
width: 100%;
}
.status-item {
display: flex;
justify-content: space-between;
margin-bottom: 12rpx;
}
.status-label {
font-size: 28rpx;
color: #666;
}
.status-value {
font-size: 28rpx;
color: #333;
}
/* 岗位信息 */
.position-name {
font-size: 30rpx;
font-weight: 500;
color: #1890ff;
margin-bottom: 16rpx;
}
.position-detail {
display: flex;
flex-direction: column;
gap: 12rpx;
}
.detail-item {
display: flex;
align-items: center;
}
.detail-item text {
font-size: 26rpx;
color: #666;
margin-left: 12rpx;
}
/* 信息列表 */
.info-list {
margin-top: 12rpx;
}
.info-item {
display: flex;
align-items: flex-start;
margin-bottom: 20rpx;
padding-bottom: 16rpx;
border-bottom: 1rpx solid #f5f5f5;
}
.info-item:last-child {
margin-bottom: 0;
border-bottom: none;
}
.info-label {
font-size: 28rpx;
color: #666;
min-width: 160rpx;
flex-shrink: 0;
}
.info-value {
font-size: 28rpx;
color: #333;
flex: 1;
line-height: 1.5;
}
.contact-phone {
color: #1890ff;
text-decoration: underline;
}
/* 内容卡片 */
.content-text {
font-size: 28rpx;
color: #666;
line-height: 1.8;
white-space: pre-wrap;
}
/* 审核记录 */
.review-item {
margin-bottom: 30rpx;
padding: 24rpx;
background-color: #fafafa;
border-radius: 8rpx;
}
.review-item:last-child {
margin-bottom: 0;
}
.review-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.review-title {
font-size: 30rpx;
font-weight: 500;
color: #333;
}
.review-info {
margin-top: 12rpx;
}
.review-detail {
display: flex;
margin-bottom: 12rpx;
}
.detail-label {
font-size: 26rpx;
color: #666;
min-width: 120rpx;
}
.detail-value {
font-size: 26rpx;
color: #333;
}
.review-comment {
margin-top: 16rpx;
padding-top: 16rpx;
border-top: 1rpx solid #e8e8e8;
}
.comment-label {
font-size: 26rpx;
color: #666;
display: block;
margin-bottom: 8rpx;
}
.comment-text {
font-size: 26rpx;
color: #333;
line-height: 1.6;
}
/* 底部操作 */
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 20rpx 30rpx;
border-top: 1rpx solid #eee;
display: flex;
z-index: 100;
}
.bottom-actions .u-button {
flex: 1;
}
</style>
+743
View File
@@ -0,0 +1,743 @@
<template>
<view class="apply-container">
<!-- 岗位信息卡片 -->
<view class="position-card" v-if="positionData">
<view class="card-title">申请岗位</view>
<view class="position-info">
<view class="position-name">{{ positionData.positionName }}</view>
<view class="position-detail">
<text>工作时间:{{ positionData.workTime }}</text>
<text>联系老师:{{ positionData.contactTeacher }}</text>
</view>
</view>
</view>
<!-- 申请表单 -->
<view class="form-container">
<u-form
:model="formData"
ref="form"
label-width="140"
label-position="left"
>
<!-- 基本信息 -->
<view class="form-section">
<view class="section-title">基本信息</view>
<u-form-item
label="学生姓名"
prop="studentName"
:required="true"
>
<u-input
v-model="formData.studentName"
placeholder="请输入学生姓名"
:disabled="true"
/>
</u-form-item>
<u-form-item
label="学号"
prop="studentNumber"
:required="true"
>
<u-input
v-model="formData.studentNumber"
placeholder="请输入学号"
:disabled="true"
/>
</u-form-item>
<u-form-item
label="身份证号"
prop="idCard"
:required="true"
>
<u-input
v-model="formData.idCard"
placeholder="请输入身份证号"
maxlength="18"
/>
</u-form-item>
<u-form-item
label="联系电话"
prop="contactPhone"
:required="true"
>
<u-input
v-model="formData.contactPhone"
placeholder="请输入联系电话"
maxlength="11"
/>
</u-form-item>
</view>
<!-- 学籍信息 -->
<view class="form-section">
<view class="section-title">学籍信息</view>
<u-form-item
label="学院名称"
prop="collegeName"
:required="true"
>
<u-input
v-model="formData.collegeName"
placeholder="学院名称"
:disabled="true"
/>
</u-form-item>
<u-form-item
label="专业名称"
prop="majorName"
:required="true"
>
<u-input
v-model="formData.majorName"
placeholder="专业名称"
:disabled="true"
/>
</u-form-item>
<u-form-item
label="班级名称"
prop="className"
:required="true"
>
<u-input
v-model="formData.className"
placeholder="班级名称"
:disabled="true"
/>
</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>
<!-- 申请信息 -->
<view class="form-section">
<view class="section-title">申请信息</view>
<u-form-item
label="家庭经济情况"
prop="familyFinancialStatus"
:required="true"
>
<view @click="showFinancialPicker = true">
<u-input
v-model="financialText"
placeholder="请选择家庭经济情况"
readonly
suffix-icon="arrow-down"
/>
</view>
</u-form-item>
<u-form-item
label="计划工作时长"
prop="plannedWorkHours"
>
<u-input
v-model="formData.plannedWorkHours"
placeholder="请输入计划工作时长(小时/周)"
type="number"
/>
</u-form-item>
<u-form-item
label="申请理由"
prop="applicationReason"
:required="true"
>
<u-textarea
v-model="formData.applicationReason"
placeholder="请详细说明申请此岗位的理由"
:maxlength="500"
:show-word-limit="true"
height="200"
count
/>
</u-form-item>
<u-form-item
label="个人能力及经验"
prop="personalAbility"
:required="true"
>
<u-textarea
v-model="formData.personalAbility"
placeholder="请描述与岗位相关的个人能力和经验"
:maxlength="500"
:show-word-limit="true"
height="200"
count
/>
</u-form-item>
<u-form-item
label="时间安排"
prop="timeArrangement"
:required="true"
>
<u-textarea
v-model="formData.timeArrangement"
placeholder="请说明您的时间安排,确保能够胜任此岗位工作"
:maxlength="300"
:show-word-limit="true"
height="150"
count
/>
</u-form-item>
<u-form-item
label="备注"
prop="remark"
>
<u-textarea
v-model="formData.remark"
placeholder="其他需要说明的情况(选填)"
:maxlength="200"
:show-word-limit="true"
height="120"
count
/>
</u-form-item>
</view>
</u-form>
</view>
<!-- 底部操作按钮 -->
<view class="bottom-actions">
<u-button
type="default"
size="large"
@click="saveDraft"
:loading="saving"
style="margin-right: 20rpx;"
>
保存草稿
</u-button>
<u-button
type="primary"
size="large"
@click="submitApplication"
:loading="submitting"
>
提交申请
</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="加载中..." />
</view>
</template>
<script>
import {
getPositionWithApplicationStatus,
getCurrentStudentInfo,
getApplicationOptions,
addApplication,
updateApplication,
getApplicationDetail
} from '@/api/workStudy.js';
export default {
name: 'WorkStudyApply',
data() {
return {
positionId: '',
applicationId: '',
mode: 'create', // create 或 edit
positionData: null,
studentInfo: null,
// 表单数据
formData: {
studentName: '',
studentNumber: '',
idCard: '',
contactPhone: '',
collegeName: '',
majorName: '',
className: '',
grade: '',
familyFinancialStatus: '',
plannedWorkHours: '',
applicationReason: '',
personalAbility: '',
timeArrangement: '',
remark: ''
},
// 选择器显示状态
showGradePicker: false,
showFinancialPicker: false,
// 选择器文本
gradeText: '',
financialText: '',
// 选择器选项
gradeOptions: [[]],
financialOptions: [[]],
// 加载状态
loading: false,
saving: false,
submitting: false
};
},
onLoad(options) {
if (options.positionId) {
this.positionId = options.positionId;
this.applicationId = options.applicationId || '';
this.mode = options.mode || 'create';
this.initData();
} else {
uni.showToast({
title: '岗位ID不能为空',
icon: 'none'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
}
},
methods: {
// 初始化数据
async initData() {
this.loading = true;
try {
// 并行加载数据
const promises = [
this.loadPositionData(),
this.loadStudentInfo(),
this.loadApplicationOptions()
];
await Promise.all(promises);
// 如果是编辑模式,加载申请详情
if (this.mode === 'edit' && this.applicationId) {
await this.loadApplicationDetail();
}
// 填充学生基本信息
this.fillStudentInfo();
} catch (error) {
console.error('初始化数据失败:', error);
uni.showToast({
title: '加载数据失败',
icon: 'none'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
} finally {
this.loading = false;
}
},
// 加载岗位数据
async loadPositionData() {
const response = await getPositionWithApplicationStatus(this.positionId);
if (response.code === 200) {
this.positionData = response.data.position;
} else {
throw new Error(response.msg || '获取岗位信息失败');
}
},
// 加载学生信息
async loadStudentInfo() {
const response = await getCurrentStudentInfo();
if (response.code === 200) {
this.studentInfo = response.data;
} else {
throw new Error(response.msg || '获取学生信息失败');
}
},
// 加载申请选项
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);
if (response.code === 200) {
const data = response.data;
// 填充表单数据
Object.keys(this.formData).forEach(key => {
if (data[key] !== undefined) {
this.formData[key] = data[key];
}
});
// 设置选择器文本
this.updatePickerTexts();
}
},
// 填充学生基本信息
fillStudentInfo() {
if (this.studentInfo) {
this.formData.studentName = this.studentInfo.studentName || '';
this.formData.studentNumber = this.studentInfo.studentNumber || '';
this.formData.collegeName = this.studentInfo.collegeName || '';
this.formData.majorName = this.studentInfo.majorName || '';
this.formData.className = this.studentInfo.className || '';
// 如果是创建模式,填充其他基本信息
if (this.mode === 'create') {
this.formData.idCard = this.studentInfo.idCard || '';
this.formData.contactPhone = this.studentInfo.contactPhone || '';
this.formData.grade = this.studentInfo.grade || '';
}
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 = [
{ field: 'studentName', name: '学生姓名' },
{ field: 'studentNumber', name: '学号' },
{ field: 'idCard', name: '身份证号' },
{ field: 'contactPhone', name: '联系电话' },
{ field: 'collegeName', name: '学院名称' },
{ field: 'majorName', name: '专业名称' },
{ field: 'className', name: '班级名称' },
{ field: 'familyFinancialStatus', name: '家庭经济情况' },
{ field: 'applicationReason', name: '申请理由' },
{ field: 'personalAbility', name: '个人能力及经验' },
{ field: 'timeArrangement', name: '时间安排' }
];
for (const item of requiredFields) {
if (!this.formData[item.field] || this.formData[item.field].trim() === '') {
uni.showToast({
title: `请填写${item.name}`,
icon: 'none'
});
return false;
}
}
// 验证身份证号格式
if (this.formData.idCard && !/^[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]$/.test(this.formData.idCard)) {
uni.showToast({
title: '身份证号格式不正确',
icon: 'none'
});
return false;
}
// 验证手机号格式
if (this.formData.contactPhone && !/^1[3-9]\d{9}$/.test(this.formData.contactPhone)) {
uni.showToast({
title: '手机号格式不正确',
icon: 'none'
});
return false;
}
return true;
},
// 保存草稿
async saveDraft(autoNavigate = true) {
if (!this.validateForm()) return false;
this.saving = true;
try {
const requestData = {
...this.formData,
positionId: this.positionId,
positionName: this.positionData.positionName,
positionType: this.positionData.positionType || ''
};
let response;
if (this.mode === 'edit' && this.applicationId) {
requestData.id = this.applicationId;
response = await updateApplication(requestData);
} else {
response = await addApplication(requestData);
}
if (response.code === 200) {
uni.showToast({
title: '保存成功',
icon: 'success'
});
if (autoNavigate) {
setTimeout(() => {
uni.navigateBack();
}, 1000);
}
return true;
} else {
throw new Error(response.msg || '保存失败');
}
} catch (error) {
console.error('保存草稿失败:', error);
uni.showToast({
title: error.message || '保存失败',
icon: 'none'
});
return false;
} finally {
this.saving = false;
}
},
// 提交申请
async submitApplication() {
if (!this.validateForm()) return;
try {
const { confirm } = await uni.showModal({
title: '确认提交',
content: '确定要提交申请吗?提交后将无法修改。',
showCancel: true
});
if (!confirm) return; // 用户取消
this.submitting = true;
// 先保存申请(不自动跳转)
const saveSuccess = await this.saveDraft(false);
if (saveSuccess) {
// 这里可以添加提交申请的逻辑
// 如果后端有单独的提交接口,可以调用
uni.showToast({
title: '提交成功',
icon: 'success'
});
setTimeout(() => {
uni.navigateBack();
}, 1000);
}
} catch (error) {
console.error('提交申请失败:', error);
uni.showToast({
title: error.message || '提交失败',
icon: 'none'
});
} finally {
this.submitting = false;
}
}
}
};
</script>
<style scoped>
.apply-container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 120rpx; /* 为底部按钮留空间 */
}
.position-card {
background-color: #fff;
margin: 20rpx;
border-radius: 12rpx;
padding: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.card-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
padding-bottom: 16rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.position-info {
margin-top: 12rpx;
}
.position-name {
font-size: 30rpx;
font-weight: 500;
color: #1890ff;
margin-bottom: 12rpx;
}
.position-detail {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.position-detail text {
font-size: 26rpx;
color: #666;
}
.form-container {
background-color: #fff;
margin: 20rpx;
border-radius: 12rpx;
padding: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.form-section {
margin-bottom: 40rpx;
}
.form-section:last-child {
margin-bottom: 0;
}
.section-title {
font-size: 30rpx;
font-weight: bold;
color: #333;
margin-bottom: 30rpx;
padding-bottom: 12rpx;
border-bottom: 2rpx solid #1890ff;
position: relative;
}
.section-title::after {
content: '';
position: absolute;
bottom: -2rpx;
left: 0;
width: 60rpx;
height: 2rpx;
background-color: #1890ff;
}
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 20rpx 30rpx;
border-top: 1rpx solid #eee;
display: flex;
z-index: 100;
}
.bottom-actions .u-button {
flex: 1;
}
/* 覆盖uview样式 */
:deep(.u-form-item) {
margin-bottom: 30rpx;
}
:deep(.u-form-item__body__left__content__label) {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
:deep(.u-input__content__field-wrapper__field) {
font-size: 28rpx;
}
:deep(.u-textarea__count) {
font-size: 24rpx;
color: #999;
}
</style>
+632
View File
@@ -0,0 +1,632 @@
<template>
<view class="detail-container">
<!-- 岗位基本信息 -->
<view class="info-card" v-if="positionData">
<view class="card-header">
<view class="position-title">
<view class="position-name">{{ positionData.positionName }}</view>
<view class="position-status">
<u-tag
:text="getStatusText(positionData.status)"
:type="getStatusType(positionData.status)"
size="mini"
shape="circle"
/>
</view>
</view>
<view class="publish-info">
<text class="publish-scope">{{ positionData.positionScopeName }}</text>
<text class="create-time">发布时间:{{ positionData.createTime }}</text>
</view>
</view>
<!-- 核心信息 -->
<view class="core-info">
<view class="info-item">
<view class="info-label">
<u-icon name="account" size="32" color="#1890ff" />
<text>招录情况</text>
</view>
<view class="info-content">
<view class="recruitment-info">
<text class="recruitment-text">需求 {{ positionData.requiredCount }} 人</text>
<text class="recruitment-text current">已招录 {{ positionData.currentCount || 0 }} 人</text>
</view>
<u-line-progress
:percent="getRecruitmentProgress()"
:active-color="getProgressColor()"
height="12"
:show-percent="false"
/>
</view>
</view>
<view class="info-item">
<view class="info-label">
<u-icon name="clock" size="32" color="#1890ff" />
<text>工作时间</text>
</view>
<view class="info-content">
<text class="info-value">{{ positionData.workTime }}</text>
</view>
</view>
<view class="info-item">
<view class="info-label">
<u-icon name="calendar" size="32" color="#1890ff" />
<text>有效期限</text>
</view>
<view class="info-content">
<text class="info-value">{{ positionData.startDate }} 至 {{ positionData.endDate }}</text>
</view>
</view>
<view class="info-item">
<view class="info-label">
<u-icon name="account" size="32" color="#1890ff" />
<text>联系老师</text>
</view>
<view class="info-content">
<text class="info-value">{{ positionData.contactTeacher }}</text>
</view>
</view>
<view class="info-item">
<view class="info-label">
<u-icon name="phone" size="32" color="#1890ff" />
<text>联系电话</text>
</view>
<view class="info-content">
<text class="info-value contact-phone" @click="callPhone">{{ positionData.contactPhone }}</text>
</view>
</view>
<view class="info-item" v-if="positionData.salaryInfo">
<view class="info-label">
<u-icon name="rmb-circle" size="32" color="#1890ff" />
<text>薪资情况</text>
</view>
<view class="info-content">
<text class="info-value salary">{{ positionData.salaryInfo }}</text>
</view>
</view>
</view>
</view>
<!-- 岗位要求 -->
<view class="content-card" v-if="positionData && positionData.positionRequirements">
<view class="card-title">
<u-icon name="file-text" size="32" color="#1890ff" />
<text>岗位要求</text>
</view>
<view class="content-text">{{ positionData.positionRequirements }}</view>
</view>
<!-- 工作内容 -->
<view class="content-card" v-if="positionData && positionData.workContent">
<view class="card-title">
<u-icon name="list" size="32" color="#1890ff" />
<text>工作内容</text>
</view>
<view class="content-text">{{ positionData.workContent }}</view>
</view>
<!-- 申请状态 -->
<view class="status-card" v-if="applicationStatus">
<view class="card-title">
<u-icon name="info-circle" size="32" color="#1890ff" />
<text>我的申请状态</text>
</view>
<view class="status-content">
<view class="status-item">
<text class="status-label">申请状态:</text>
<u-tag
:text="applicationStatus.applicationStatusText"
:type="getApplicationStatusType(applicationStatus.applicationStatus)"
size="mini"
shape="circle"
/>
</view>
<view class="status-item" v-if="applicationStatus.lastApplicationTime">
<text class="status-label">申请时间:</text>
<text class="status-value">{{ applicationStatus.lastApplicationTime }}</text>
</view>
<view class="status-actions" v-if="applicationStatus.canEdit || applicationStatus.canCancel">
<u-button
type="warning"
size="small"
@click="editApplication"
v-if="applicationStatus.canEdit"
>
编辑申请
</u-button>
<u-button
type="error"
size="small"
@click="cancelApplication"
v-if="applicationStatus.canCancel"
style="margin-left: 20rpx;"
>
撤销申请
</u-button>
</view>
</view>
</view>
<!-- 备注信息 -->
<view class="content-card" v-if="positionData && positionData.remark">
<view class="card-title">
<u-icon name="chat" size="32" color="#1890ff" />
<text>备注信息</text>
</view>
<view class="content-text">{{ positionData.remark }}</view>
</view>
<!-- 底部操作按钮 -->
<view class="bottom-actions">
<u-button
type="primary"
size="large"
@click="handleApply"
:disabled="!canApply()"
:loading="submitting"
>
{{ getApplyButtonText() }}
</u-button>
</view>
<!-- 加载状态 -->
<u-loading-page :loading="loading" loading-text="加载中..." />
</view>
</template>
<script>
import {
getPositionWithApplicationStatus,
cancelApplication
} from '@/api/workStudy.js';
export default {
name: 'WorkStudyDetail',
data() {
return {
positionId: '',
positionData: null,
applicationStatus: null,
loading: false,
submitting: false
};
},
onLoad(options) {
if (options.id) {
this.positionId = options.id;
this.loadPositionDetail();
} else {
uni.showToast({
title: '岗位ID不能为空',
icon: 'none'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
}
},
methods: {
// 加载岗位详情
async loadPositionDetail() {
this.loading = true;
try {
const response = await getPositionWithApplicationStatus(this.positionId);
if (response.code === 200) {
this.positionData = response.data.position;
this.applicationStatus = response.data.applicationStatus;
} else {
throw new Error(response.msg || '获取岗位详情失败');
}
} catch (error) {
console.error('加载岗位详情失败:', error);
uni.showToast({
title: error.message || '加载失败',
icon: 'none'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
} finally {
this.loading = false;
}
},
// 申请岗位
handleApply() {
if (!this.canApply()) {
uni.showToast({
title: this.getApplyButtonText(),
icon: 'none'
});
return;
}
// 如果已有申请且可以编辑,则编辑申请
if (this.applicationStatus && this.applicationStatus.hasApplication && this.applicationStatus.canEdit) {
this.editApplication();
return;
}
// 否则新建申请
uni.navigateTo({
url: `/pages/workStudy/apply?positionId=${this.positionId}`
});
},
// 编辑申请
editApplication() {
if (this.applicationStatus && this.applicationStatus.applicationId) {
uni.navigateTo({
url: `/pages/workStudy/apply?positionId=${this.positionId}&applicationId=${this.applicationStatus.applicationId}&mode=edit`
});
}
},
// 撤销申请
async cancelApplication() {
if (!this.applicationStatus || !this.applicationStatus.applicationId) {
return;
}
try {
const { confirm } = await uni.showModal({
title: '确认撤销',
content: '确定要撤销此申请吗?',
showCancel: true
});
if (!confirm) return; // 用户取消
this.submitting = true;
const response = await cancelApplication(this.applicationStatus.applicationId);
if (response.code === 200) {
uni.showToast({
title: '撤销成功',
icon: 'success'
});
// 重新加载数据
setTimeout(() => {
this.loadPositionDetail();
}, 1000);
} else {
throw new Error(response.msg || '撤销失败');
}
} catch (error) {
console.error('撤销申请失败:', error);
uni.showToast({
title: error.message || '撤销失败',
icon: 'none'
});
} finally {
this.submitting = false;
}
},
// 拨打电话
callPhone() {
if (this.positionData && this.positionData.contactPhone) {
uni.makePhoneCall({
phoneNumber: this.positionData.contactPhone,
fail: (error) => {
console.error('拨打电话失败:', error);
uni.showToast({
title: '拨打电话失败',
icon: 'none'
});
}
});
}
},
// 获取状态文字
getStatusText(status) {
const statusMap = {
'DRAFT': '草稿',
'PUBLISHED': '已发布',
'RECRUITING': '招录中',
'FULL': '已满员',
'EXPIRED': '已过期',
'SUSPENDED': '已暂停',
'CANCELLED': '已取消'
};
return statusMap[status] || '未知状态';
},
// 获取状态类型
getStatusType(status) {
const typeMap = {
'DRAFT': 'info',
'PUBLISHED': 'warning',
'RECRUITING': 'success',
'FULL': 'primary',
'EXPIRED': 'error',
'SUSPENDED': 'warning',
'CANCELLED': 'error'
};
return typeMap[status] || 'default';
},
// 获取招录进度
getRecruitmentProgress() {
if (!this.positionData || !this.positionData.requiredCount) return 0;
const current = this.positionData.currentCount || 0;
const required = this.positionData.requiredCount;
return Math.min(Math.round((current / required) * 100), 100);
},
// 获取进度条颜色
getProgressColor() {
const progress = this.getRecruitmentProgress();
if (progress >= 100) return '#f5222d';
if (progress >= 80) return '#fa8c16';
if (progress >= 50) return '#1890ff';
return '#52c41a';
},
// 判断是否可以申请
canApply() {
if (!this.positionData) return false;
// 岗位状态必须是招录中
if (this.positionData.status !== 'RECRUITING') return false;
// 如果已有申请,判断是否可以重新申请
if (this.applicationStatus && this.applicationStatus.hasApplication) {
// 如果申请被拒绝,可以重新申请
return this.applicationStatus.applicationStatus === 'FINAL_REJECTED' ||
this.applicationStatus.applicationStatus === 'COLLEGE_REJECTED';
}
// 检查是否已满员
const current = this.positionData.currentCount || 0;
const required = this.positionData.requiredCount || 0;
return current < required;
},
// 获取申请按钮文字
getApplyButtonText() {
if (!this.positionData) return '加载中...';
if (this.positionData.status !== 'RECRUITING') {
return '岗位暂不可申请';
}
// 检查是否已满员
const current = this.positionData.currentCount || 0;
const required = this.positionData.requiredCount || 0;
if (current >= required) {
return '岗位已满员';
}
// 如果有申请记录
if (this.applicationStatus && this.applicationStatus.hasApplication) {
const status = this.applicationStatus.applicationStatus;
if (status === 'DRAFT') {
return '完善申请信息';
} else if (status === 'SUBMITTED' || status === 'COLLEGE_REVIEWING' || status === 'FINAL_REVIEWING') {
return '申请审核中';
} else if (status === 'FINAL_APPROVED') {
return '申请已通过';
} else if (status === 'FINAL_REJECTED' || status === 'COLLEGE_REJECTED') {
return '重新申请';
} else if (status === 'CANCELLED') {
return '重新申请';
}
return '查看申请状态';
}
return '申请此岗位';
},
// 获取申请状态类型
getApplicationStatusType(status) {
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'
};
return typeMap[status] || 'default';
}
}
};
</script>
<style scoped>
.detail-container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 120rpx; /* 为底部按钮留空间 */
}
.info-card, .content-card, .status-card {
background-color: #fff;
margin: 20rpx;
border-radius: 12rpx;
padding: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.card-header {
margin-bottom: 30rpx;
}
.position-title {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 12rpx;
}
.position-name {
font-size: 36rpx;
font-weight: bold;
color: #333;
flex: 1;
line-height: 1.3;
}
.position-status {
margin-left: 20rpx;
}
.publish-info {
display: flex;
justify-content: space-between;
align-items: center;
}
.publish-scope {
font-size: 28rpx;
color: #1890ff;
padding: 8rpx 16rpx;
background-color: #e6f7ff;
border-radius: 20rpx;
}
.create-time {
font-size: 24rpx;
color: #999;
}
.core-info {
margin-top: 30rpx;
}
.info-item {
margin-bottom: 30rpx;
}
.info-item:last-child {
margin-bottom: 0;
}
.info-label {
display: flex;
align-items: center;
margin-bottom: 12rpx;
}
.info-label text {
font-size: 28rpx;
font-weight: 500;
color: #333;
margin-left: 12rpx;
}
.info-content {
margin-left: 44rpx;
}
.info-value {
font-size: 28rpx;
color: #666;
line-height: 1.5;
}
.contact-phone {
color: #1890ff;
text-decoration: underline;
}
.salary {
color: #f5222d;
font-weight: 500;
}
.recruitment-info {
display: flex;
justify-content: space-between;
margin-bottom: 12rpx;
}
.recruitment-text {
font-size: 28rpx;
color: #666;
}
.recruitment-text.current {
color: #1890ff;
font-weight: 500;
}
.card-title {
display: flex;
align-items: center;
margin-bottom: 20rpx;
padding-bottom: 16rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.card-title text {
font-size: 32rpx;
font-weight: 500;
color: #333;
margin-left: 12rpx;
}
.content-text {
font-size: 28rpx;
color: #666;
line-height: 1.8;
white-space: pre-wrap;
}
.status-content {
margin-top: 20rpx;
}
.status-item {
display: flex;
align-items: center;
margin-bottom: 16rpx;
}
.status-label {
font-size: 28rpx;
color: #666;
margin-right: 12rpx;
}
.status-value {
font-size: 28rpx;
color: #333;
}
.status-actions {
margin-top: 20rpx;
display: flex;
justify-content: flex-end;
}
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 20rpx 30rpx;
border-top: 1rpx solid #eee;
z-index: 100;
}
</style>
+852
View File
@@ -0,0 +1,852 @@
<template>
<view class="work-study-container">
<!-- 搜索栏 -->
<view class="search-bar">
<u-search
v-model="searchKeyword"
placeholder="搜索岗位名称或联系老师"
:show-action="false"
@search="handleSearch"
@custom="handleSearch"
bg-color="#f6f6f6"
height="72"
shape="round"
/>
</view>
<!-- 标签页 -->
<view class="tabs-container">
<u-tabs
:list="tabsList"
:current="currentTab"
@change="onTabChange"
active-color="#1890ff"
inactive-color="#999"
bar-width="40"
bar-height="4"
font-size="30"
/>
</view>
<!-- 岗位列表 -->
<view class="list-container" v-if="currentTab === 0">
<!-- 筛选条件 -->
<view class="filter-bar" v-if="showFilter">
<view class="filter-item">
<view @click="showScopePicker = true">
<u-input
v-model="scopeText"
placeholder="选择发布范围"
readonly
border="surround"
suffix-icon="arrow-down"
/>
</view>
</view>
<view class="filter-item">
<view @click="showStatusPicker = true">
<u-input
v-model="statusText"
placeholder="选择岗位状态"
readonly
border="surround"
suffix-icon="arrow-down"
/>
</view>
</view>
<view class="filter-actions">
<u-button
type="primary"
size="small"
@click="handleFilter"
:loading="loading"
>
筛选
</u-button>
<u-button
type="default"
size="small"
@click="resetFilter"
style="margin-left: 10rpx;"
>
重置
</u-button>
</view>
</view>
<!-- 筛选按钮 -->
<view class="action-bar">
<u-button
type="default"
size="small"
:icon="showFilter ? 'arrow-up' : 'arrow-down'"
@click="showFilter = !showFilter"
>
{{ showFilter ? '收起筛选' : '展开筛选' }}
</u-button>
<u-button
type="primary"
size="small"
icon="reload"
@click="refreshData"
:loading="refreshing"
>
刷新
</u-button>
</view>
<!-- 岗位卡片列表 -->
<view class="list-content" v-if="positionList.length > 0">
<view
class="position-card"
v-for="item in positionList"
:key="item.id"
@click="handleViewDetail(item)"
>
<!-- 卡片头部 -->
<view class="card-header">
<view class="position-info">
<view class="position-name">{{ item.positionName }}</view>
<view class="position-type">{{ item.positionScopeName || '未设置' }}</view>
</view>
<view class="status-badge">
<u-tag
:text="item.statusName || '未知'"
:type="getStatusType(item.status)"
size="mini"
shape="circle"
/>
</view>
</view>
<!-- 岗位信息 -->
<view class="card-content">
<view class="info-row">
<u-icon name="account" size="28" color="#666" />
<text class="info-text">需求人数:{{ item.requiredCount }}人</text>
<text class="info-text current-count">已招录:{{ item.currentCount || 0 }}人</text>
</view>
<view class="info-row">
<u-icon name="clock" size="28" color="#666" />
<text class="info-text">工作时间:{{ item.workTime }}</text>
</view>
<view class="info-row">
<u-icon name="map" size="28" color="#666" />
<text class="info-text">联系老师:{{ item.contactTeacher }}</text>
</view>
<view class="info-row">
<u-icon name="phone" size="28" color="#666" />
<text class="info-text">联系电话:{{ item.contactPhone }}</text>
</view>
<view class="info-row">
<u-icon name="calendar" size="28" color="#666" />
<text class="info-text">有效期:{{ item.startDate }} 至 {{ item.endDate }}</text>
</view>
</view>
<!-- 操作按钮 -->
<view class="card-actions">
<u-button
type="default"
size="small"
@click.stop="handleViewDetail($event, item)"
>
查看详情
</u-button>
<u-button
type="primary"
size="small"
@click.stop="handleApply($event, item)"
:disabled="!canApply(item)"
style="margin-left: 20rpx;"
>
{{ getApplyButtonText(item) }}
</u-button>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-else-if="!loading">
<u-empty
text="暂无岗位信息"
icon="search"
:show="true"
/>
</view>
<!-- 加载更多 -->
<view class="load-more" v-if="hasMore && positionList.length > 0">
<u-loadmore
:status="loadMoreStatus"
:load-text="loadText"
@loadmore="loadMore"
/>
</view>
</view>
<!-- 我的申请列表 -->
<view class="list-container" v-if="currentTab === 1">
<view class="list-content" v-if="applicationList.length > 0">
<view
class="application-card"
v-for="item in applicationList"
:key="item.id"
@click="handleViewApplication(item)"
>
<!-- 申请信息 -->
<view class="card-header">
<view class="application-info">
<view class="position-name">{{ item.positionName }}</view>
<view class="apply-time">申请时间:{{ item.createTime }}</view>
</view>
<view class="status-badge">
<u-tag
:text="getApplicationStatusText(item)"
:type="getApplicationStatusType(item)"
size="mini"
shape="circle"
/>
</view>
</view>
<!-- 申请状态 -->
<view class="card-content">
<view class="info-row">
<u-icon name="file-text" size="28" color="#666" />
<text class="info-text">申请状态:{{ getApplicationStatusText(item) }}</text>
</view>
<view class="info-row" v-if="item.collegeReviewStatus">
<u-icon name="checkmark-circle" size="28" color="#666" />
<text class="info-text">学院审核:{{ getReviewStatusText(item.collegeReviewStatus) }}</text>
</view>
<view class="info-row" v-if="item.finalReviewStatus">
<u-icon name="checkmark-circle" size="28" color="#666" />
<text class="info-text">学生处审核:{{ getReviewStatusText(item.finalReviewStatus) }}</text>
</view>
</view>
<!-- 操作按钮 -->
<view class="card-actions">
<u-button
type="default"
size="small"
@click.stop="handleViewApplication($event, item)"
>
查看详情
</u-button>
<u-button
type="warning"
size="small"
@click.stop="handleCancelApplication($event, item)"
v-if="canCancelApplication(item)"
style="margin-left: 20rpx;"
>
撤销申请
</u-button>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-else-if="!loading">
<u-empty
text="暂无申请记录"
icon="file-text"
:show="true"
/>
</view>
</view>
<!-- 发布范围选择器 -->
<u-picker
:show="showScopePicker"
:columns="scopeOptions"
keyName="label"
@confirm="onScopeConfirm"
@cancel="showScopePicker = false"
/>
<!-- 状态选择器 -->
<u-picker
:show="showStatusPicker"
:columns="statusOptions"
keyName="label"
@confirm="onStatusConfirm"
@cancel="showStatusPicker = false"
/>
<!-- 加载状态 -->
<u-loading-page :loading="loading" loading-text="加载中..." />
</view>
</template>
<script>
import {
getAvailablePositions,
getMyApplications,
cancelApplication,
getCurrentStudentInfo
} from '@/api/workStudy.js';
export default {
name: 'WorkStudyIndex',
data() {
return {
// 搜索关键词
searchKeyword: '',
// 标签页
currentTab: 0, // 确保是数字类型
tabsList: [
{ name: '可申请岗位' },
{ name: '我的申请' }
],
// 筛选条件
showFilter: false,
showScopePicker: false,
showStatusPicker: false,
scopeText: '',
statusText: '',
// 选择器选项
scopeOptions: [
[
{ label: '全部', value: '' },
{ label: '本学院', value: 'COLLEGE' },
{ label: '全校', value: 'UNIVERSITY' }
]
],
statusOptions: [
[
{ label: '全部', value: '' },
{ label: '已发布', value: 'PUBLISHED' },
{ label: '招录中', value: 'RECRUITING' },
{ label: '已暂停', value: 'SUSPENDED' }
]
],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
positionName: '',
positionScope: '',
status: ''
},
// 列表数据
positionList: [],
applicationList: [],
// 分页状态
hasMore: true,
loadMoreStatus: 'loadmore',
loadText: {
loadmore: '点击或上拉加载更多',
loading: '正在加载...',
nomore: '没有更多了'
},
// 加载状态
loading: false,
refreshing: false,
// 用户信息
studentInfo: null
};
},
onLoad() {
// 确保currentTab是数字类型
this.currentTab = Number(this.currentTab);
this.initData();
},
onReady() {
// 页面渲染完成后再次确保数据类型正确
this.$nextTick(() => {
this.currentTab = Number(this.currentTab);
});
},
onShow() {
// 如果是从申请页面返回,刷新数据
if (this.currentTab === 1) {
this.loadMyApplications(true);
}
},
// 下拉刷新
onPullDownRefresh() {
this.refreshData();
},
// 上拉加载更多
onReachBottom() {
if (this.currentTab === 0 && this.hasMore && !this.loading) {
this.loadMore();
}
},
methods: {
// 初始化数据
async initData() {
try {
// 获取学生信息
await this.loadStudentInfo();
// 加载岗位列表
await this.loadPositionList(true);
} catch (error) {
console.error('初始化数据失败:', error);
uni.showToast({
title: '加载数据失败',
icon: 'none'
});
}
},
// 获取学生信息
async loadStudentInfo() {
try {
const response = await getCurrentStudentInfo();
if (response.code === 200) {
this.studentInfo = response.data;
}
} catch (error) {
console.error('获取学生信息失败:', error);
}
},
// 加载岗位列表
async loadPositionList(reset = false) {
if (this.loading) return;
if (reset) {
this.queryParams.pageNum = 1;
this.positionList = [];
this.hasMore = true;
}
this.loading = true;
this.loadMoreStatus = 'loading';
try {
const response = await getAvailablePositions(this.queryParams);
if (response.code === 200) {
const newList = response.rows || [];
if (reset) {
this.positionList = newList;
} else {
this.positionList = [...this.positionList, ...newList];
}
// 检查是否还有更多数据
if (newList.length < this.queryParams.pageSize) {
this.hasMore = false;
this.loadMoreStatus = 'nomore';
} else {
this.loadMoreStatus = 'loadmore';
}
} else {
throw new Error(response.msg || '获取岗位列表失败');
}
} catch (error) {
console.error('加载岗位列表失败:', error);
uni.showToast({
title: error.message || '加载失败',
icon: 'none'
});
this.loadMoreStatus = 'loadmore';
} finally {
this.loading = false;
uni.stopPullDownRefresh();
}
},
// 加载我的申请列表
async loadMyApplications(reset = false) {
if (this.loading) return;
this.loading = true;
try {
const response = await getMyApplications();
if (response.code === 200) {
this.applicationList = response.data || [];
} else {
throw new Error(response.msg || '获取申请列表失败');
}
} catch (error) {
console.error('加载申请列表失败:', error);
uni.showToast({
title: error.message || '加载失败',
icon: 'none'
});
} finally {
this.loading = false;
uni.stopPullDownRefresh();
}
},
// 标签页切换
onTabChange(index) {
// 确保index是数字类型
const tabIndex = typeof index === 'object' ? index.index : index;
this.currentTab = Number(tabIndex);
// 延迟执行,确保DOM渲染完成
this.$nextTick(() => {
if (this.currentTab === 0) {
this.loadPositionList(true);
} else if (this.currentTab === 1) {
this.loadMyApplications(true);
}
});
},
// 搜索
handleSearch() {
this.queryParams.positionName = this.searchKeyword;
this.loadPositionList(true);
},
// 筛选
handleFilter() {
this.loadPositionList(true);
},
// 重置筛选
resetFilter() {
this.queryParams.positionScope = '';
this.queryParams.status = '';
this.scopeText = '';
this.statusText = '';
this.searchKeyword = '';
this.queryParams.positionName = '';
this.loadPositionList(true);
},
// 刷新数据
refreshData() {
this.refreshing = true;
if (this.currentTab === 0) {
this.loadPositionList(true).finally(() => {
this.refreshing = false;
});
} else {
this.loadMyApplications(true).finally(() => {
this.refreshing = false;
});
}
},
// 加载更多
loadMore() {
if (!this.hasMore || this.loading) return;
this.queryParams.pageNum++;
this.loadPositionList(false);
},
// 查看岗位详情
handleViewDetail(event, item) {
// 如果只传了一个参数,说明是从卡片点击调用的,第一个参数就是item
if (!item && typeof event === 'object' && event.id) {
item = event;
}
uni.navigateTo({
url: `/pages/workStudy/detail?id=${item.id}`
});
},
// 申请岗位
handleApply(event, item) {
// 如果只传了一个参数,说明是从卡片点击调用的,第一个参数就是item
if (!item && typeof event === 'object' && event.id) {
item = event;
}
if (!this.canApply(item)) {
uni.showToast({
title: this.getApplyButtonText(item),
icon: 'none'
});
return;
}
uni.navigateTo({
url: `/pages/workStudy/apply?positionId=${item.id}`
});
},
// 查看申请详情
handleViewApplication(event, item) {
// 如果只传了一个参数,说明是从卡片点击调用的,第一个参数就是item
if (!item && typeof event === 'object' && event.id) {
item = event;
}
uni.navigateTo({
url: `/pages/workStudy/applicationDetail?id=${item.id}`
});
},
// 撤销申请
async handleCancelApplication(event, item) {
// 如果只传了一个参数,说明是从卡片点击调用的,第一个参数就是item
if (!item && typeof event === 'object' && event.id) {
item = event;
}
try {
const { confirm } = await uni.showModal({
title: '确认撤销',
content: '确定要撤销此申请吗?',
showCancel: true
});
if (!confirm) return; // 用户取消
const response = await cancelApplication(item.id);
if (response.code === 200) {
uni.showToast({
title: '撤销成功',
icon: 'success'
});
this.loadMyApplications(true);
} else {
throw new Error(response.msg || '撤销失败');
}
} catch (error) {
console.error('撤销申请失败:', error);
uni.showToast({
title: error.message || '撤销失败',
icon: 'none'
});
}
},
// 发布范围选择确认
onScopeConfirm(e) {
const index = e.indexs[0];
const selected = this.scopeOptions[0][index];
this.queryParams.positionScope = selected.value;
this.scopeText = selected.label;
this.showScopePicker = false;
},
// 状态选择确认
onStatusConfirm(e) {
const index = e.indexs[0];
const selected = this.statusOptions[0][index];
this.queryParams.status = selected.value;
this.statusText = selected.label;
this.showStatusPicker = false;
},
// 获取状态类型
getStatusType(status) {
const typeMap = {
'DRAFT': 'info',
'PUBLISHED': 'warning',
'RECRUITING': 'success',
'FULL': 'primary',
'EXPIRED': 'error',
'SUSPENDED': 'warning',
'CANCELLED': 'error'
};
return typeMap[status] || 'default';
},
// 判断是否可以申请
canApply(item) {
return item.status === 'RECRUITING' &&
(item.currentCount || 0) < (item.requiredCount || 0);
},
// 获取申请按钮文字
getApplyButtonText(item) {
if (item.status !== 'RECRUITING') {
return '暂不可申请';
}
if ((item.currentCount || 0) >= (item.requiredCount || 0)) {
return '已满员';
}
return '申请岗位';
},
// 获取申请状态文字
getApplicationStatusText(item) {
const statusMap = {
'DRAFT': '草稿',
'SUBMITTED': '已提交',
'COLLEGE_REVIEWING': '学院审核中',
'COLLEGE_APPROVED': '学院审核通过',
'COLLEGE_REJECTED': '学院审核未通过',
'FINAL_REVIEWING': '学生处审核中',
'FINAL_APPROVED': '审核通过',
'FINAL_REJECTED': '审核未通过',
'CANCELLED': '已撤销'
};
return statusMap[item.status] || '未知状态';
},
// 获取申请状态类型
getApplicationStatusType(item) {
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'
};
return typeMap[item.status] || 'default';
},
// 获取审核状态文字
getReviewStatusText(status) {
const statusMap = {
'PENDING': '待审核',
'APPROVED': '通过',
'REJECTED': '未通过'
};
return statusMap[status] || '未知';
},
// 判断是否可以撤销申请
canCancelApplication(item) {
return ['DRAFT', 'SUBMITTED', 'COLLEGE_REVIEWING'].includes(item.status);
}
}
};
</script>
<style scoped>
.work-study-container {
min-height: 100vh;
background-color: #f5f5f5;
}
.search-bar {
padding: 20rpx;
background-color: #fff;
border-bottom: 1rpx solid #eee;
}
.tabs-container {
background-color: #fff;
border-bottom: 1rpx solid #eee;
}
.filter-bar {
background-color: #fff;
padding: 20rpx;
border-bottom: 1rpx solid #eee;
}
.filter-item {
margin-bottom: 20rpx;
}
.filter-actions {
display: flex;
justify-content: flex-end;
margin-top: 20rpx;
}
.action-bar {
display: flex;
justify-content: space-between;
padding: 20rpx;
background-color: #fff;
border-bottom: 1rpx solid #eee;
}
.list-container {
flex: 1;
}
.list-content {
padding: 20rpx;
}
.position-card, .application-card {
background-color: #fff;
border-radius: 12rpx;
margin-bottom: 20rpx;
padding: 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 20rpx;
}
.position-info, .application-info {
flex: 1;
}
.position-name {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.position-type, .apply-time {
font-size: 24rpx;
color: #999;
}
.status-badge {
margin-left: 20rpx;
}
.card-content {
margin-bottom: 20rpx;
}
.info-row {
display: flex;
align-items: center;
margin-bottom: 12rpx;
}
.info-text {
font-size: 28rpx;
color: #666;
margin-left: 12rpx;
flex: 1;
}
.current-count {
margin-left: 20rpx;
color: #1890ff;
}
.card-actions {
display: flex;
justify-content: flex-end;
border-top: 1rpx solid #eee;
padding-top: 20rpx;
}
.empty-state {
padding: 80rpx 20rpx;
text-align: center;
}
.load-more {
padding: 20rpx;
}
</style>
+497 -48
View File
@@ -1,87 +1,536 @@
<template>
<view class="container">
<view class="page-header">
<text class="page-title">考核申请</text>
</view>
<view class="page-container">
<!-- 顶部导航 -->
<u-navbar title="考核申请" :border-bottom="false" bg-color="#3c9cff" title-color="#fff">
<view slot="left" @click="goBack">
<u-icon name="arrow-left" color="#fff" size="22"></u-icon>
</view>
</u-navbar>
<view class="content">
<view class="notice-card">
<u-icon name="info-circle" size="20" color="#1890ff" />
<text class="notice-text">考核申请功能正在开发中...</text>
<!-- 加载状态 -->
<u-loading-page :loading="loading" loading-text="加载中..."></u-loading-page>
<!-- 内容区域 -->
<view v-if="!loading" class="content-wrapper">
<!-- 空状态 -->
<view v-if="assessmentScheduleList.length === 0" class="empty-container">
<u-empty text="暂无可申请的考核计划" mode="data"></u-empty>
</view>
<!-- 这里可以添加考核申请的具体内容 -->
<view class="form-section">
<!-- 申请表单内容将在这里添加 -->
<!-- 考核计划列表 -->
<view v-else class="schedule-list">
<view
v-for="(item, index) in assessmentScheduleList"
:key="item.id || index"
class="schedule-card"
>
<!-- 卡片头部 -->
<view class="card-header">
<view class="schedule-info">
<text class="schedule-name">{{ item.scheduleName || '暂无' }}</text>
<view class="schedule-meta">
<text class="academic-info">
{{ item.academicYear || '暂无' }}
{{ getSemesterText(item.semester) }}
</text>
<u-tag
:text="getStatusText(item.status)"
:type="getStatusTagType(item.status)"
size="mini"
></u-tag>
</view>
</view>
</view>
<!-- 卡片内容 -->
<view class="card-content">
<view class="schedule-description">
<text class="description-text">
{{ item.description || '暂无说明' }}
</text>
</view>
<view class="time-info">
<view class="time-item">
<text class="time-label">开始时间:</text>
<text class="time-value">{{ formatTime(item.startTime) }}</text>
</view>
<view class="time-item">
<text class="time-label">结束时间:</text>
<text class="time-value">{{ formatTime(item.endTime) }}</text>
</view>
</view>
<view class="plan-info">
<text class="plan-name">考核方案: {{ item.planName || '暂无' }}</text>
</view>
</view>
<!-- 卡片操作按钮 -->
<view class="card-actions">
<view class="action-btn info-btn" @click="handleViewSchedule(item)">
<u-icon name="eye" size="20" color="#3c9cff"></u-icon>
<text class="btn-text">查看计划</text>
</view>
<view
v-if="canSubmitApplication(item)"
class="action-btn success-btn"
@click="handleSubmitApplication(item)"
>
<u-icon name="edit-pen" size="20" color="#5ac725"></u-icon>
<text class="btn-text">提交申请</text>
</view>
<view class="action-btn warning-btn" @click="handleViewApplication(item)">
<u-icon name="file-text" size="20" color="#ff9900"></u-icon>
<text class="btn-text">查看申请</text>
</view>
</view>
</view>
</view>
<!-- 分页加载 -->
<view v-if="hasMore && assessmentScheduleList.length > 0" class="load-more">
<u-loadmore :status="loadStatus" @loadmore="loadMore"></u-loadmore>
</view>
</view>
</view>
</template>
<script>
import { request } from '@/common/request.js';
import { getDictLabel, getDictTagType, getDict } from '@/common/dict.js';
export default {
name: 'AssessmentApply',
data() {
return {
// 页面数据
loading: false,
assessmentScheduleList: [],
total: 0,
hasMore: true,
loadStatus: 'loadmore',
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
statusList: ['2', '3'], // 只查询进行中和已结束的考核计划
},
// 字典数据
assessmentScheduleStatus: [],
studentSemester: [],
};
},
onLoad(options) {
// 页面加载时的处理
async onLoad(options) {
console.log('考核申请页面加载');
await this.loadDictData();
await this.getList();
},
onShow() {
// 从其他页面返回时刷新列表
if (this.assessmentScheduleList.length > 0) {
this.handleSuccess();
}
},
methods: {
// 页面方法
// 返回上一页
goBack() {
uni.navigateBack();
},
// 加载字典数据
async loadDictData() {
try {
this.assessmentScheduleStatus = await getDict('assessment_schedule_status');
this.studentSemester = await getDict('student_semester');
} catch (error) {
console.error('加载字典数据失败:', error);
}
},
// 获取考核计划列表
async getList(loadMore = false) {
if (!loadMore) {
this.loading = true;
}
try {
const res = await request({
url: '/assessment/schedule/listByStatus',
method: 'GET',
params: this.queryParams,
});
if (res && res.code === 200) {
const rows = res.rows || [];
if (loadMore) {
this.assessmentScheduleList = this.assessmentScheduleList.concat(rows);
} else {
this.assessmentScheduleList = rows;
}
this.total = res.total || 0;
if (rows.length < this.queryParams.pageSize) {
this.hasMore = false;
this.loadStatus = 'nomore';
} else {
this.hasMore = true;
this.loadStatus = 'loadmore';
}
} else {
this.handleRequestError(res?.msg || '获取数据失败', loadMore);
}
} catch (error) {
console.error('获取数据失败:', error);
this.handleRequestError('获取数据失败', loadMore);
} finally {
if (!loadMore) {
this.loading = false;
}
}
},
// 错误处理
handleRequestError(message, isLoadMore = false) {
if (!isLoadMore) {
this.assessmentScheduleList = [];
this.total = 0;
}
this.hasMore = false;
this.loadStatus = 'nomore';
uni.showToast({
title: message,
icon: 'none',
});
},
// 加载更多
async loadMore() {
if (!this.hasMore || this.loadStatus !== 'loadmore') {
return;
}
this.loadStatus = 'loading';
this.queryParams.pageNum += 1;
try {
await this.getList(true);
} catch (error) {
console.error('加载更多数据失败:', error);
this.queryParams.pageNum -= 1;
this.loadStatus = 'loadmore';
}
},
// 查看考核计划信息
handleViewSchedule(row) {
const message = `考核计划:${row.scheduleName}\n说明:${row.description}\n时间:${row.startTime} 至 ${row.endTime}`;
uni.showModal({
title: '考核计划信息',
content: message,
showCancel: false,
});
},
// 判断是否可以提交申请
canSubmitApplication(row) {
const now = new Date();
const startTime = new Date(row.startTime);
const endTime = new Date(row.endTime);
return row.status === '2' && now >= startTime && now <= endTime;
},
// 提交考核申请
async handleSubmitApplication(row) {
try {
// 先获取当前用户的学生信息
const response = await request({
url: '/studentMsg/studentMsg/personalInfo',
method: 'get',
});
if (response.code === 200 && response.data) {
const studentInfo = response.data;
// 检查用户是否已经填写过申请
const existingApplication = await request({
url: `/assessment/application/student/${studentInfo.id}/schedule/${row.id}`,
method: 'get',
});
console.log('existingApplication:', existingApplication);
if (existingApplication.code === 200 && existingApplication.data) {
// 用户已经填写过申请,跳转到编辑页面
uni.navigateTo({
url: `/pages/xskh/khsq/form?id=${existingApplication.data.id}&title=编辑考核申请&scheduleId=${row.id}`,
});
} else {
// 用户没有填写过申请,跳转到新增页面
uni.navigateTo({
url: `/pages/xskh/khsq/form?title=提交考核申请&scheduleId=${row.id}`,
});
}
} else {
// 获取学生信息失败,按新增模式处理
uni.navigateTo({
url: `/pages/xskh/khsq/form?title=提交考核申请&scheduleId=${row.id}`,
});
}
} catch (error) {
console.error('检查用户申请记录失败:', error);
// 检查失败时,按新增模式处理
uni.navigateTo({
url: `/pages/xskh/khsq/form?title=提交考核申请&scheduleId=${row.id}`,
});
}
},
// 查看考核申请
async handleViewApplication(row) {
try {
// 先获取当前用户的学生信息
const response = await request({
url: '/studentMsg/studentMsg/personalInfo',
method: 'get',
});
if (response.code === 200 && response.data) {
const studentInfo = response.data;
// 检查用户是否已经填写过申请
const existingApplication = await request({
url: `/assessment/application/student/${studentInfo.id}/schedule/${row.id}`,
method: 'get',
});
if (existingApplication.code === 200 && existingApplication.data) {
// 用户已经填写过申请,跳转到查看页面
uni.navigateTo({
url: `/pages/xskh/khsq/form?id=${existingApplication.data.id}&title=查看考核申请&scheduleId=${row.id}&viewMode=true`,
});
} else {
// 用户没有填写过申请,显示提示信息
uni.showToast({
title: '您还没有提交过该考核计划的申请',
icon: 'none',
});
}
} else {
uni.showToast({
title: '获取用户信息失败,无法查看申请',
icon: 'none',
});
}
} catch (error) {
console.error('检查用户申请记录失败:', error);
uni.showToast({
title: '检查申请记录失败,请稍后重试',
icon: 'none',
});
}
},
// 提交成功回调
handleSuccess() {
this.queryParams.pageNum = 1;
this.hasMore = true;
this.loadStatus = 'loadmore';
this.getList();
},
// 时间格式化
formatTime(timeStr) {
if (!timeStr) return '-';
try {
const date = new Date(timeStr);
if (isNaN(date.getTime())) return timeStr;
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
} catch (error) {
console.error('时间格式化失败:', error);
return timeStr;
}
},
// 获取学期文本
getSemesterText(semester) {
return getDictLabel(this.studentSemester, semester) || '暂无';
},
// 获取状态文本
getStatusText(status) {
return getDictLabel(this.assessmentScheduleStatus, status) || '未知状态';
},
// 获取状态标签类型
getStatusTagType(status) {
const fallbackMap = {
1: 'info', // 待开始
2: 'success', // 进行中
3: 'warning', // 已结束
4: 'error', // 已取消
};
return getDictTagType(this.assessmentScheduleStatus, status, fallbackMap);
},
},
};
</script>
<style lang="scss" scoped>
.container {
.page-container {
min-height: 100vh;
background-color: #f5f5f5;
background-color: #f5f6fa;
}
.page-header {
background: linear-gradient(135deg, #007aff, #0051d9);
padding: calc(var(--status-bar-height) + 16px) 16px 16px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
.content-wrapper {
padding: 20rpx;
}
.page-title {
font-size: 20px;
font-weight: 600;
color: #ffffff;
text-align: center;
display: block;
.empty-container {
padding: 100rpx 0;
}
.content {
padding: 16px;
}
.notice-card {
background: #ffffff;
border-radius: 12px;
padding: 16px;
margin-bottom: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
.schedule-list {
display: flex;
align-items: center;
gap: 12px;
flex-direction: column;
gap: 20rpx;
}
.notice-text {
color: #666666;
font-size: 14px;
.schedule-card {
background-color: #fff;
border-radius: 16rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
overflow: hidden;
margin-bottom: 20rpx;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx;
border-bottom: 1px solid #f0f0f0;
}
.schedule-info {
flex: 1;
}
.form-section {
background: #ffffff;
border-radius: 12px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
min-height: 200px;
.schedule-name {
font-size: 32rpx;
font-weight: bold;
color: #303133;
display: block;
margin-bottom: 12rpx;
}
.schedule-meta {
display: flex;
align-items: center;
gap: 16rpx;
}
.academic-info {
font-size: 26rpx;
color: #606266;
}
.card-content {
padding: 24rpx;
}
.schedule-description {
margin-bottom: 20rpx;
}
.description-text {
font-size: 28rpx;
color: #606266;
line-height: 1.5;
}
.time-info {
margin-bottom: 16rpx;
}
.time-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8rpx 0;
}
.time-label {
font-size: 26rpx;
color: #909399;
min-width: 140rpx;
}
.time-value {
font-size: 26rpx;
color: #303133;
}
.plan-info {
padding-top: 8rpx;
border-top: 1px solid #f0f0f0;
}
.plan-name {
font-size: 26rpx;
color: #606266;
}
.card-actions {
display: flex;
justify-content: space-around;
padding: 20rpx;
border-top: 1px solid #f0f0f0;
gap: 16rpx;
}
.action-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 16rpx 20rpx;
border-radius: 12rpx;
min-width: 120rpx;
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
opacity: 0.8;
}
}
.btn-text {
font-size: 24rpx;
margin-top: 8rpx;
font-weight: 500;
}
.info-btn .btn-text {
color: #3c9cff;
}
.success-btn .btn-text {
color: #5ac725;
}
.warning-btn .btn-text {
color: #ff9900;
}
.load-more {
padding: 20rpx 0;
}
</style>
+8
View File
@@ -114,3 +114,11 @@ export function getApplicationStatistics(positionId) {
method: 'get'
});
}
// 获取学生申请状态列表
export function getStudentApplicationStatusList() {
return request({
url: '/jcbt/workStudyApplication/student/applicationStatus',
method: 'get'
});
}
+10
View File
@@ -447,3 +447,13 @@ export function getCurrentStudentInfo(): AxiosPromise<any> {
method: 'get'
});
}
/**
* 获取学生已申请岗位状态列表
*/
export function getStudentApplicationStatusList(): AxiosPromise<any[]> {
return request({
url: '/jcbt/workStudyApplication/student/applicationStatus',
method: 'get'
});
}
+10
View File
@@ -244,6 +244,16 @@ export function resumePosition(id: string | number) {
});
}
/**
* 开启岗位招录
*/
export function startRecruiting(id: string | number) {
return request({
url: `/jcbt/workStudyPosition/startRecruiting/${id}`,
method: 'put'
});
}
/**
* 取消岗位
*/
@@ -112,12 +112,27 @@
</template>
</el-table-column>
<el-table-column label="操作" width="160" align="center" fixed="right">
<el-table-column label="申请状态" width="120" align="center">
<template #default="{ row }">
<el-tag :type="getApplicationStatusType(row)" size="small">
{{ getApplicationStatusText(row) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center" fixed="right">
<template #default="{ row }">
<div class="action-buttons">
<el-button type="primary" icon="View" size="small" @click="handleViewDetail(row)" title="查看详情"> 查看 </el-button>
<el-button type="success" icon="Plus" size="small" @click="handleApply(row)" :disabled="!canApply(row)" title="申请岗位">
申请
<el-button
:type="getApplyButtonType(row)"
:icon="getApplyButtonIcon(row)"
size="small"
@click="handleApply(row)"
:disabled="!canApply(row)"
:title="getApplyButtonTitle(row)"
>
{{ getApplyButtonText(row) }}
</el-button>
</div>
</template>
@@ -141,7 +156,8 @@
<!-- 岗位详情对话框 -->
<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" />
<!-- <PositionDetail v-if="detailDialogVisible && selectedPositionId" :position-id="selectedPositionId" @apply="handleApplyFromDetail" /> -->
<div style="padding: 20px; text-align: center;">岗位详情页面开发中...</div>
<template #footer>
<el-button @click="detailDialogVisible = false">关闭</el-button>
</template>
@@ -149,12 +165,17 @@
<!-- 申请表单对话框 -->
<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>
@@ -162,10 +183,14 @@
<script setup lang="ts">
import { ref, reactive, onMounted, computed } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { useRouter } from 'vue-router';
import { listVisiblePositions } from '@/api/jcbt/workStudyPosition';
import type { WorkStudyPositionVO } from '@/api/jcbt/workStudyPosition';
import PositionDetail from './detail.vue';
import ApplicationForm from './form.vue';
import { getStudentApplicationStatusList } from '@/api/jcbt/workStudyApplication';
// import PositionDetail from './detail.vue';
// import ApplicationForm from './form.vue';
const router = useRouter();
// 组件引用
const queryFormRef = ref();
@@ -179,6 +204,7 @@ const detailDialogVisible = ref(false);
const applyDialogVisible = ref(false);
const selectedPositionId = ref<number>();
const selectedPosition = ref<WorkStudyPositionVO>();
const studentApplications = ref<any[]>([]);
// 查询参数
const queryParams = reactive({
@@ -210,6 +236,13 @@ const getStatusType = (status: string) => {
/** 判断是否可以申请 */
const canApply = (position: WorkStudyPositionVO) => {
const application = studentApplications.value.find((app) => app.positionId === position.id);
// 如果已录用,禁用按钮
if (application && application.status === 'approved') {
return false;
}
// 检查岗位状态
if (!['PUBLISHED', 'RECRUITING'].includes(position.status || '')) {
return false;
@@ -282,6 +315,20 @@ const handleViewDetail = (position: WorkStudyPositionVO) => {
/** 申请岗位 */
const handleApply = (position: WorkStudyPositionVO) => {
const application = studentApplications.value.find((app) => app.positionId === position.id);
// 如果已有申请且状态为已提交或审核中,跳转到申请管理页面
if (application && ['submitted', 'college_reviewing', 'final_reviewing'].includes(application.status)) {
router.push('/jcbt/workstudy/my-applications');
return;
}
// 如果已录用,不允许操作
if (application && application.status === 'approved') {
ElMessage.info('您已被该岗位录用');
return;
}
if (!canApply(position)) {
ElMessage.warning('该岗位当前不可申请');
return;
@@ -298,17 +345,150 @@ const handleApplyFromDetail = (position: WorkStudyPositionVO) => {
applyDialogVisible.value = true;
};
/** 加载学生申请状态列表 */
const loadStudentApplications = async () => {
try {
const response = await getStudentApplicationStatusList();
if (response.code === 200) {
studentApplications.value = response.data || [];
}
} catch (error) {
console.error('获取申请状态失败:', error);
}
};
/** 获取申请状态文本 */
const getApplicationStatusText = (row: WorkStudyPositionVO) => {
const application = studentApplications.value.find((app) => app.positionId === row.id);
if (!application) return '未申请';
switch (application.status) {
case 'submitted':
return '已提交';
case 'college_reviewing':
return '学院审核中';
case 'final_reviewing':
return '学生处审核中';
case 'approved':
return '审核通过';
case 'rejected':
return '审核不通过';
default:
return '未知状态';
}
};
/** 获取申请状态标签类型 */
const getApplicationStatusType = (row: WorkStudyPositionVO) => {
const application = studentApplications.value.find((app) => app.positionId === row.id);
if (!application) return 'info';
switch (application.status) {
case 'submitted':
return 'primary';
case 'college_reviewing':
case 'final_reviewing':
return 'warning';
case 'approved':
return 'success';
case 'rejected':
return 'danger';
default:
return 'info';
}
};
/** 获取申请按钮文本 */
const getApplyButtonText = (row: WorkStudyPositionVO) => {
const application = studentApplications.value.find((app) => app.positionId === row.id);
if (!application) return '申请';
switch (application.status) {
case 'submitted':
case 'college_reviewing':
case 'final_reviewing':
return '查看申请';
case 'approved':
return '已录用';
case 'rejected':
return '重新申请';
default:
return '申请';
}
};
/** 获取申请按钮类型 */
const getApplyButtonType = (row: WorkStudyPositionVO) => {
const application = studentApplications.value.find((app) => app.positionId === row.id);
if (!application) return 'success';
switch (application.status) {
case 'submitted':
case 'college_reviewing':
case 'final_reviewing':
return 'primary';
case 'approved':
return 'info';
case 'rejected':
return 'warning';
default:
return 'success';
}
};
/** 获取申请按钮图标 */
const getApplyButtonIcon = (row: WorkStudyPositionVO) => {
const application = studentApplications.value.find((app) => app.positionId === row.id);
if (!application) return 'Plus';
switch (application.status) {
case 'submitted':
case 'college_reviewing':
case 'final_reviewing':
return 'View';
case 'approved':
return 'Check';
case 'rejected':
return 'RefreshRight';
default:
return 'Plus';
}
};
/** 获取申请按钮提示文本 */
const getApplyButtonTitle = (row: WorkStudyPositionVO) => {
const application = studentApplications.value.find((app) => app.positionId === row.id);
if (!application) return '申请岗位';
switch (application.status) {
case 'submitted':
return '查看申请详情';
case 'college_reviewing':
return '学院审核中,查看申请详情';
case 'final_reviewing':
return '学生处审核中,查看申请详情';
case 'approved':
return '已被录用';
case 'rejected':
return '重新申请该岗位';
default:
return '申请岗位';
}
};
/** 申请成功回调 */
const handleApplySuccess = () => {
applyDialogVisible.value = false;
ElMessage.success('申请提交成功!');
// 刷新列表
// 刷新列表和申请状态
getPositionList();
loadStudentApplications();
};
// 生命周期
onMounted(() => {
getPositionList();
loadStudentApplications();
});
</script>
@@ -228,6 +228,13 @@
>
<el-icon><Upload /></el-icon>发布岗位
</el-dropdown-item>
<el-dropdown-item
v-if="hasPermission('jcbt:workStudyPosition:startRecruiting')"
command="startRecruiting"
:disabled="getRowStatus(scope.row) !== 'PUBLISHED'"
>
<el-icon><VideoPlay /></el-icon>开启招录
</el-dropdown-item>
<el-dropdown-item
v-if="hasPermission('jcbt:workStudyPosition:suspend')"
command="suspend"
@@ -288,6 +295,7 @@ import {
exportWorkStudyPosition,
getPositionStatistics,
publishPosition,
startRecruiting,
suspendPosition,
resumePosition,
cancelPosition
@@ -518,6 +526,12 @@ async function handleCommand(command, row) {
proxy?.$modal.msgSuccess('发布成功');
operationSuccess = true;
break;
case 'startRecruiting':
await proxy?.$modal.confirm('确认开启此岗位的招录?开启后学生可以申请此岗位。');
await startRecruiting(row.id);
proxy?.$modal.msgSuccess('开启招录成功');
operationSuccess = true;
break;
case 'suspend':
await proxy?.$modal.confirm('确认暂停此岗位的招录?暂停后学生无法申请此岗位。');
await suspendPosition(row.id);
@@ -148,7 +148,7 @@
<el-table-column label="学生信息" min-width="180" align="left">
<template #default="scope">
<div class="student-info">
<div class=":liststudent-info">
<div class="student-name">{{ scope.row.studentName }}</div>
<div class="student-number">学号:{{ scope.row.studentNumber }}</div>
<!-- <div class="student-class">班级:{{ scope.row.className }}</div> -->