zlwu 1 рік тому
батько
коміт
6d2a23942a
20 змінених файлів з 4191 додано та 321 видалено
  1. 13 0
      backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/controller/JcbtWorkStudyApplicationController.java
  2. 57 3
      backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/controller/JcbtWorkStudyPositionController.java
  3. 3 0
      backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/mapper/JcbtWorkStudyApplicationMapper.java
  4. 6 0
      backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/service/IJcbtWorkStudyApplicationService.java
  5. 70 7
      backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/service/impl/JcbtWorkStudyApplicationServiceImpl.java
  6. 6 0
      backend/ruoyi-modules/ruoyi-jcbt/src/main/resources/mapper/jcbt/JcbtWorkStudyApplicationMapper.xml
  7. 133 0
      mobile/src/api/workStudy.js
  8. 304 256
      mobile/src/pages.json
  9. 22 0
      mobile/src/pages/index/index.vue
  10. 624 0
      mobile/src/pages/workStudy/applicationDetail.vue
  11. 743 0
      mobile/src/pages/workStudy/apply.vue
  12. 632 0
      mobile/src/pages/workStudy/detail.vue
  13. 852 0
      mobile/src/pages/workStudy/index.vue
  14. 494 45
      mobile/src/pages/xskh/khsq/khsq.vue
  15. 8 0
      web/src/api/jcbt/workStudyApplication.js
  16. 10 0
      web/src/api/jcbt/workStudyApplication.ts
  17. 10 0
      web/src/api/jcbt/workStudyPosition.ts
  18. 189 9
      web/src/views/jcbt/workstudy/Application/index.vue
  19. 14 0
      web/src/views/jcbt/workstudy/Position/index.vue
  20. 1 1
      web/src/views/zsjy/internship/daily/index.vue

+ 13 - 0
backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/controller/JcbtWorkStudyApplicationController.java

@@ -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);
+
+    }
+
 }

+ 57 - 3
backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/controller/JcbtWorkStudyPositionController.java

@@ -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;
+        }
     }
 }

+ 3 - 0
backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/mapper/JcbtWorkStudyApplicationMapper.java

@@ -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);
+
 }

+ 6 - 0
backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/service/IJcbtWorkStudyApplicationService.java

@@ -164,4 +164,10 @@ public interface IJcbtWorkStudyApplicationService {
      */
     Map<String, Object> getPositionApplicationStatistics(Long positionId);
 
+    /**
+     * 获取学生已申请岗位列表
+     *
+     * @return 学生已申请岗位状态列表 (包含岗位ID和申请状态)
+     */
+    List<Map<String, Object>> getStudentApplicationStatus();
 }

+ 70 - 7
backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/service/impl/JcbtWorkStudyApplicationServiceImpl.java

@@ -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";
+        }
+    }
 }

+ 6 - 0
backend/ruoyi-modules/ruoyi-jcbt/src/main/resources/mapper/jcbt/JcbtWorkStudyApplicationMapper.xml

@@ -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 - 0
mobile/src/api/workStudy.js

@@ -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
mobile/src/pages.json

@@ -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 - 0
mobile/src/pages/index/index.vue

@@ -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({

+ 624 - 0
mobile/src/pages/workStudy/applicationDetail.vue

@@ -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 - 0
mobile/src/pages/workStudy/apply.vue

@@ -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 - 0
mobile/src/pages/workStudy/detail.vue

@@ -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 - 0
mobile/src/pages/workStudy/index.vue

@@ -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>

+ 494 - 45
mobile/src/pages/xskh/khsq/khsq.vue

@@ -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>
+
+        <!-- 加载状态 -->
+        <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 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="content">
-            <view class="notice-card">
-                <u-icon name="info-circle" size="20" color="#1890ff" />
-                <text class="notice-text">考核申请功能正在开发中...</text>
+                    <!-- 卡片内容 -->
+                    <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 class="form-section">
-                <!-- 申请表单内容将在这里添加 -->
+            <!-- 分页加载 -->
+            <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;
+}
+
+.schedule-list {
+    display: flex;
+    flex-direction: column;
+    gap: 20rpx;
 }
 
-.content {
-    padding: 16px;
+.schedule-card {
+    background-color: #fff;
+    border-radius: 16rpx;
+    box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
+    overflow: hidden;
+    margin-bottom: 20rpx;
 }
 
-.notice-card {
-    background: #ffffff;
-    border-radius: 12px;
-    padding: 16px;
-    margin-bottom: 16px;
-    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+.card-header {
     display: flex;
+    justify-content: space-between;
     align-items: center;
-    gap: 12px;
+    padding: 24rpx;
+    border-bottom: 1px solid #f0f0f0;
 }
 
-.notice-text {
-    color: #666666;
-    font-size: 14px;
+.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 - 0
web/src/api/jcbt/workStudyApplication.js

@@ -114,3 +114,11 @@ export function getApplicationStatistics(positionId) {
     method: 'get'
   });
 }
+
+// 获取学生申请状态列表
+export function getStudentApplicationStatusList() {
+  return request({
+    url: '/jcbt/workStudyApplication/student/applicationStatus',
+    method: 'get'
+  });
+}

+ 10 - 0
web/src/api/jcbt/workStudyApplication.ts

@@ -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 - 0
web/src/api/jcbt/workStudyPosition.ts

@@ -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'
+  });
+}
+
 /**
  * 取消岗位
  */

+ 189 - 9
web/src/views/jcbt/workstudy/Application/index.vue

@@ -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>
 

+ 14 - 0
web/src/views/jcbt/workstudy/Position/index.vue

@@ -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);

+ 1 - 1
web/src/views/zsjy/internship/daily/index.vue

@@ -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> -->