wuzhen 1 год назад
Родитель
Сommit
9a16535359

+ 32 - 0
backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/InternshipInfoController.java

@@ -5,6 +5,7 @@ import org.dromara.common.core.domain.R;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.web.core.BaseController;
+import org.dromara.common.satoken.utils.LoginHelper;
 import org.dromara.zsjy.domain.vo.InternshipInfoVO;
 import org.dromara.zsjy.domain.bo.InternshipInfoQueryBO;
 import org.dromara.zsjy.domain.bo.InternshipInfoAddBO;
@@ -101,6 +102,37 @@ public class InternshipInfoController extends BaseController {
         return R.ok(internshipInfoService.getInternshipStatistics(queryBO));
     }
 
+    /**
+     * 学生获取自己的实习信息(移动端专用)
+     */
+    @Operation(summary = "学生获取实习信息", description = "学生查看自己的实习信息(移动端)")
+    @GetMapping("/student/info")
+    public R<InternshipInfoVO> getStudentInfo() {
+        log.info("=== 学生获取实习信息 ===");
+        log.info("当前用户ID: {}", LoginHelper.getUserId());
+
+        try {
+            Long currentUserId = LoginHelper.getUserId();
+            if (currentUserId == null) {
+                log.error("用户ID为空,无法获取实习信息");
+                return R.fail("用户未登录");
+            }
+
+            InternshipInfoVO result = internshipInfoService.queryById(currentUserId);
+
+            if (result != null) {
+                log.info("成功获取学生实习信息,学生ID: {}", currentUserId);
+                return R.ok(result);
+            } else {
+                log.info("学生暂无实习信息,学生ID: {}", currentUserId);
+                return R.fail("暂无实习信息");
+            }
+        } catch (Exception e) {
+            log.error("获取学生实习信息异常,用户ID: {},错误信息: {}", LoginHelper.getUserId(), e.getMessage(), e);
+            return R.fail("获取实习信息失败:" + e.getMessage());
+        }
+    }
+
     /**
      * 新增实习信息
      */

+ 2 - 1
backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipDailyController.java

@@ -6,6 +6,7 @@ import org.dromara.common.core.validate.AddGroup;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.web.core.BaseController;
+import org.dromara.zsjy.domain.bo.CheckInGroup;
 import org.dromara.zsjy.domain.bo.ZsjyInternshipDailyAddBo;
 import org.dromara.zsjy.domain.bo.ZsjyInternshipDailyQueryBo;
 import org.dromara.zsjy.domain.vo.ZsjyInternshipDailyVo;
@@ -58,7 +59,7 @@ public class ZsjyInternshipDailyController extends BaseController {
     @Operation(summary = "学生签到打卡", description = "学生进行实习日常签到打卡")
     @SaCheckPermission("zsjy:internshipdaily:checkin")
     @PostMapping("/checkin")
-    public R<Void> checkIn(@Validated(AddGroup.class) @RequestBody ZsjyInternshipDailyAddBo addBo) {
+    public R<Void> checkIn(@Validated(CheckInGroup.class) @RequestBody ZsjyInternshipDailyAddBo addBo) {
         log.info("=== Controller层调试 ===");
         log.info("接口被调用: /zsjy/internship/daily/checkin");
         log.info("签到参数: {}", addBo);

+ 106 - 15
backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipDaily.java

@@ -1,5 +1,7 @@
 package org.dromara.zsjy.domain;
 
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
@@ -9,6 +11,8 @@ import org.dromara.common.mybatis.core.domain.BaseEntity;
 import java.io.Serial;
 import java.math.BigDecimal;
 import java.sql.Time;
+import java.time.LocalDate;
+import java.time.LocalTime;
 import java.util.Date;
 
 /**
@@ -26,74 +30,161 @@ public class ZsjyInternshipDaily extends BaseEntity {
     private static final long serialVersionUID = 1L;
 
     /**
-     * 日常记录ID
+     * 日常记录ID (对应数据库daily_id字段)
      */
-    @TableId(value = "daily_id")
-    private Long dailyId;
+    @TableId(value = "daily_id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    /**
+     * 租户ID
+     */
+    @TableField("tenant_id")
+    private String tenantId;
 
     /**
      * 实习ID
      */
+    @TableField("internship_id")
     private Long internshipId;
 
     /**
      * 学生ID
      */
+    @TableField("student_id")
     private Long studentId;
 
     /**
-     * 打卡日期
+     * 签到日期
      */
-    private Date checkDate;
+    @TableField("check_date")
+    private LocalDate checkDate;
 
     /**
      * 签到时间
      */
-    private Time checkInTime;
+    @TableField("check_in_time")
+    private LocalTime checkInTime;
 
     /**
-     * 签到位置
+     * 签到地点 (对应数据库check_in_location字段)
      */
-    private String checkInLocation;
+    @TableField("check_in_location")
+    private String checkOutLocation;
 
     /**
-     * 纬度
+     * 签到经度 (对应数据库longitude字段)
      */
-    private BigDecimal latitude;
+    @TableField("longitude")
+    private BigDecimal checkOutLongitude;
 
     /**
-     * 经度
+     * 签到纬度 (对应数据库latitude字段)
      */
-    private BigDecimal longitude;
+    @TableField("latitude")
+    private BigDecimal checkOutLatitude;
 
     /**
-     * 日总结
+     * 工作内容/日总结
      */
-    private String dailySummary;
+    @TableField("daily_summary")
+    private String workContent;
 
     /**
      * 图片附件(JSON格式)
      */
+    @TableField("images")
     private String images;
 
     /**
      * 是否异常(0正常 1异常)
      */
+    @TableField("is_exception")
     private String isException;
 
     /**
      * 异常原因
      */
+    @TableField("exception_reason")
     private String exceptionReason;
 
     /**
      * 异常处理状态(0待处理 1已处理)
      */
+    @TableField("exception_status")
     private String exceptionStatus;
 
     /**
-     * 状态(0正常 1停用)
+     * 考勤状态(0=未签到 1=正常 2=迟到 3=早退 4=旷工)
      */
+    @TableField("status")
     private String status;
 
+    /**
+     * 备注
+     */
+    @TableField("remark")
+    private String remark;
+
+    // ==================== 扩展字段(数据库中不存在,通过关联查询获取) ====================
+
+    /**
+     * 学生姓名 (通过student_id关联查询获取)
+     */
+    @TableField(exist = false)
+    private String studentName;
+
+    /**
+     * 学号 (通过student_id关联查询获取)
+     */
+    @TableField(exist = false)
+    private String studentNumber;
+
+    /**
+     * 班级名称 (通过student_id关联查询获取)
+     */
+    @TableField(exist = false)
+    private String className;
+
+    /**
+     * 实习单位名称 (通过internship_id关联查询获取)
+     */
+    @TableField(exist = false)
+    private String companyName;
+
+    /**
+     * 实习岗位 (通过internship_id关联查询获取)
+     */
+    @TableField(exist = false)
+    private String position;
+
+    /**
+     * 签到地点 (业务扩展字段,当前只有签退地点)
+     */
+    @TableField(exist = false)
+    private String checkInLocation;
+
+    /**
+     * 签到经度 (业务扩展字段,当前只有签退经度)
+     */
+    @TableField(exist = false)
+    private BigDecimal checkInLongitude;
+
+    /**
+     * 签到纬度 (业务扩展字段,当前只有签退纬度)
+     */
+    @TableField(exist = false)
+    private BigDecimal checkInLatitude;
+
+    /**
+     * 签退时间 (业务扩展字段,当前只有签到时间)
+     */
+    @TableField(exist = false)
+    private LocalTime checkOutTime;
+
+    /**
+     * 工作时长(小时) (业务计算字段)
+     */
+    @TableField(exist = false)
+    private BigDecimal workDuration;
+
 }

+ 150 - 28
backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipDailyAddBo.java

@@ -10,7 +10,8 @@ import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Size;
 import java.io.Serial;
 import java.math.BigDecimal;
-import java.sql.Time;
+import java.time.LocalDate;
+import java.time.LocalTime;
 import java.util.Date;
 
 /**
@@ -31,53 +32,87 @@ public class ZsjyInternshipDailyAddBo extends BaseEntity {
      * 实习ID
      */
     @Schema(description = "实习ID", requiredMode = Schema.RequiredMode.REQUIRED)
-    @NotNull(message = "实习ID不能为空", groups = {AddGroup.class})
+    @NotNull(message = "实习ID不能为空", groups = {AddGroup.class, CheckInGroup.class})
     private Long internshipId;
 
     /**
      * 学生ID
      */
     @Schema(description = "学生ID", requiredMode = Schema.RequiredMode.REQUIRED)
-    @NotNull(message = "学生ID不能为空", groups = {AddGroup.class})
+    @NotNull(message = "学生ID不能为空", groups = {AddGroup.class, CheckInGroup.class})
     private Long studentId;
 
     /**
-     * 打卡日期
+     * 签到日期
      */
-    @Schema(description = "打卡日期", requiredMode = Schema.RequiredMode.REQUIRED)
-    @NotNull(message = "打卡日期不能为空", groups = {AddGroup.class})
-    private Date checkDate;
+    @Schema(description = "签到日期", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "签到日期不能为空", groups = {AddGroup.class, CheckInGroup.class})
+    private LocalDate checkDate;
 
     /**
      * 签到时间
      */
-    @Schema(description = "签到时间")
-    private Time checkInTime;
+    @Schema(description = "签到时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "签到时间不能为空", groups = {AddGroup.class, CheckInGroup.class})
+    private LocalTime checkInTime;
 
     /**
-     * 签到位置
+     * 签退时间
      */
-    @Schema(description = "签到位置")
-    @Size(max = 200, message = "签到位置长度不能超过200个字符")
+    @Schema(description = "签退时间")
+    private LocalTime checkOutTime;
+
+    /**
+     * 签到地点 (前端传递字段,映射到数据库check_in_location字段)
+     */
+    @Schema(description = "签到地点", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "签到地点不能为空", groups = {CheckInGroup.class})
+    @Size(max = 500, message = "签到地点长度不能超过500个字符")
     private String checkInLocation;
 
     /**
-     * 纬度
+     * 签到纬度 (前端传递字段,映射到数据库latitude字段)
      */
-    @Schema(description = "纬度")
+    @Schema(description = "签到纬度", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "签到纬度不能为空", groups = {CheckInGroup.class})
     private BigDecimal latitude;
 
     /**
-     * 经度
+     * 签到经度 (前端传递字段,映射到数据库longitude字段)
      */
-    @Schema(description = "经度")
+    @Schema(description = "签到经度", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "签到经度不能为空", groups = {CheckInGroup.class})
     private BigDecimal longitude;
 
     /**
-     * 日总结
+     * 签退地点 (对应数据库check_in_location字段,实际存储签退地点)
      */
-    @Schema(description = "日总结")
-    private String dailySummary;
+    @Schema(description = "签退地点", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "签退地点不能为空", groups = {AddGroup.class})
+    @Size(max = 500, message = "签退地点长度不能超过500个字符")
+    private String checkOutLocation;
+
+    /**
+     * 签退纬度 (对应数据库latitude字段)
+     */
+    @Schema(description = "签退纬度", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "签退纬度不能为空", groups = {AddGroup.class})
+    private BigDecimal checkOutLatitude;
+
+    /**
+     * 签退经度 (对应数据库longitude字段)
+     */
+    @Schema(description = "签退经度", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "签退经度不能为空", groups = {AddGroup.class})
+    private BigDecimal checkOutLongitude;
+
+    /**
+     * 工作内容 (对应数据库daily_summary字段)
+     */
+    @Schema(description = "工作内容", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "工作内容不能为空", groups = {AddGroup.class})
+    @Size(max = 1000, message = "工作内容长度不能超过1000个字符")
+    private String workContent;
 
     /**
      * 图片附件(JSON格式)
@@ -95,25 +130,112 @@ public class ZsjyInternshipDailyAddBo extends BaseEntity {
      * 异常原因
      */
     @Schema(description = "异常原因")
-    @Size(max = 500, message = "异常原因长度不能超过500个字符")
     private String exceptionReason;
 
     /**
-     * 异常处理状态(0待处理 1已处理)
+     * 考勤状态(0=未签到 1=正常 2=迟到 3=早退 4=旷工)
      */
-    @Schema(description = "异常处理状态")
-    private String exceptionStatus;
-
-    /**
-     * 状态(0正常 1停用)
-     */
-    @Schema(description = "状态")
+    @Schema(description = "考勤状态")
     private String status;
 
     /**
      * 备注
      */
     @Schema(description = "备注")
+    @Size(max = 500, message = "备注长度不能超过500个字符")
     private String remark;
 
+    // ==================== 扩展字段(用于显示,不存储到数据库) ====================
+
+    /**
+     * 学生姓名(扩展字段,用于显示)
+     */
+    @Schema(description = "学生姓名", hidden = true)
+    private String studentName;
+
+    /**
+     * 学生学号(扩展字段,用于显示)
+     */
+    @Schema(description = "学生学号", hidden = true)
+    private String studentNumber;
+
+    /**
+     * 班级名称(扩展字段,用于显示)
+     */
+    @Schema(description = "班级名称", hidden = true)
+    private String className;
+
+    /**
+     * 企业名称(扩展字段,用于显示)
+     */
+    @Schema(description = "企业名称", hidden = true)
+    private String companyName;
+
+    /**
+     * 实习岗位(扩展字段,用于显示)
+     */
+    @Schema(description = "实习岗位", hidden = true)
+    private String position;
+
+    // ==================== 兼容移动端字段(前端可能使用的别名) ====================
+
+    /**
+     * 日总结(兼容移动端,实际映射为workContent)
+     */
+    @Schema(description = "日总结", hidden = true)
+    private String dailySummary;
+
+    // ==================== 数据转换方法(处理前端字段映射) ====================
+
+    /**
+     * 处理移动端兼容字段映射
+     * 将移动端使用的字段名映射到实际的字段
+     */
+    public void processCompatibleFields() {
+        // 处理经纬度映射(签到时映射到签退字段存储)
+        if (this.latitude != null && this.checkOutLatitude == null) {
+            this.checkOutLatitude = this.latitude;
+        }
+        if (this.longitude != null && this.checkOutLongitude == null) {
+            this.checkOutLongitude = this.longitude;
+        }
+
+        // 处理工作内容映射
+        if (this.dailySummary != null && this.workContent == null) {
+            this.workContent = this.dailySummary;
+        }
+
+        // 处理签到位置映射(签到时映射到签退字段存储)
+        if (this.checkInLocation != null && this.checkOutLocation == null) {
+            this.checkOutLocation = this.checkInLocation;
+        }
+    }
+
+    /**
+     * 获取实际的签退地点(优先使用checkOutLocation,否则使用checkInLocation)
+     */
+    public String getActualCheckOutLocation() {
+        return this.checkOutLocation != null ? this.checkOutLocation : this.checkInLocation;
+    }
+
+    /**
+     * 获取实际的签退经度(优先使用checkOutLongitude,否则使用longitude)
+     */
+    public BigDecimal getActualCheckOutLongitude() {
+        return this.checkOutLongitude != null ? this.checkOutLongitude : this.longitude;
+    }
+
+    /**
+     * 获取实际的签退纬度(优先使用checkOutLatitude,否则使用latitude)
+     */
+    public BigDecimal getActualCheckOutLatitude() {
+        return this.checkOutLatitude != null ? this.checkOutLatitude : this.latitude;
+    }
+
+    /**
+     * 获取实际的工作内容(优先使用workContent,否则使用dailySummary)
+     */
+    public String getActualWorkContent() {
+        return this.workContent != null ? this.workContent : this.dailySummary;
+    }
 }

+ 58 - 16
backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipDailyServiceImpl.java

@@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.satoken.utils.LoginHelper;
@@ -22,8 +23,11 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.sql.Time;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.time.YearMonth;
+import java.time.ZoneId;
 import java.util.*;
 
 /**
@@ -64,32 +68,70 @@ public class ZsjyInternshipDailyServiceImpl implements IZsjyInternshipDailyServi
     @Transactional(rollbackFor = Exception.class)
     public R<Void> checkIn(ZsjyInternshipDailyAddBo addBo) {
         try {
-            // 1. 验证学生当日是否已签到
-            boolean exists = baseMapper.existsByStudentAndDate(addBo.getStudentId(), addBo.getCheckDate());
+            // 1. 验证学生当日是否已签到 - 将LocalDate转换为Date
+            Date checkDateAsDate = Date.from(addBo.getCheckDate().atStartOfDay(ZoneId.systemDefault()).toInstant());
+            boolean exists = baseMapper.existsByStudentAndDate(addBo.getStudentId(), checkDateAsDate);
             if (exists) {
                 return R.fail("今日已签到,请勿重复签到");
             }
 
-            // 2. 构建实体对象
-            ZsjyInternshipDaily entity = MapstructUtils.convert(addBo, ZsjyInternshipDaily.class);
+            // 2. 处理兼容字段映射(移动端可能使用的别名映射到正确的字段)
+            if (addBo.getLatitude() != null && addBo.getCheckOutLatitude() == null) {
+                addBo.setCheckOutLatitude(addBo.getLatitude());
+            }
+            if (addBo.getLongitude() != null && addBo.getCheckOutLongitude() == null) {
+                addBo.setCheckOutLongitude(addBo.getLongitude());
+            }
+            if (addBo.getDailySummary() != null && addBo.getWorkContent() == null) {
+                addBo.setWorkContent(addBo.getDailySummary());
+            }
+            if (addBo.getCheckInLocation() != null && addBo.getCheckOutLocation() == null) {
+                addBo.setCheckOutLocation(addBo.getCheckInLocation());
+            }
 
-            // 3. 设置签到时间为当前时间
+            // 3. 构建实体对象 - 手动创建避免MapStruct转换器错误
+            ZsjyInternshipDaily entity = new ZsjyInternshipDaily();
+
+            // 核心必填字段(对应数据库字段)
+            entity.setInternshipId(addBo.getInternshipId());
+            entity.setStudentId(addBo.getStudentId());
+            entity.setCheckDate(addBo.getCheckDate());
+            entity.setCheckInTime(addBo.getCheckInTime());
+            entity.setCheckOutLocation(addBo.getCheckOutLocation()); // 对应数据库check_in_location字段
+            entity.setCheckOutLatitude(addBo.getCheckOutLatitude()); // 对应数据库latitude字段
+            entity.setCheckOutLongitude(addBo.getCheckOutLongitude()); // 对应数据库longitude字段
+            entity.setWorkContent(addBo.getWorkContent()); // 对应数据库daily_summary字段
+            entity.setImages(addBo.getImages());
+            entity.setIsException(addBo.getIsException());
+            entity.setExceptionReason(addBo.getExceptionReason());
+            entity.setStatus(addBo.getStatus());
+            entity.setRemark(addBo.getRemark());
+
+            // 扩展字段(不存储到数据库,仅用于显示)
+            entity.setStudentName(addBo.getStudentName());
+            entity.setStudentNumber(addBo.getStudentNumber());
+            entity.setClassName(addBo.getClassName());
+            entity.setCompanyName(addBo.getCompanyName());
+            entity.setPosition(addBo.getPosition());
+
+            // 继承BaseEntity的字段会自动填充(create_time, create_by等)
+
+            // 4. 设置签到时间为当前时间(如果没有传入)
             if (ObjectUtil.isNull(entity.getCheckInTime())) {
-                entity.setCheckInTime(Time.valueOf(DateUtil.format(new Date(), "HH:mm:ss")));
+                entity.setCheckInTime(LocalTime.now());
             }
 
-            // 4. 设置默认值
-            if (ObjectUtil.isNull(entity.getIsException())) {
-                entity.setIsException("0"); // 默认正常
-            }
-            if (ObjectUtil.isNull(entity.getExceptionStatus())) {
-                entity.setExceptionStatus("0"); // 默认待处理
-            }
+            // 5. 设置默认状态
             if (ObjectUtil.isNull(entity.getStatus())) {
-                entity.setStatus("0"); // 默认正常
+                entity.setStatus("1"); // 正常状态
+            }
+
+            // 6. 设置默认异常状态
+            if (ObjectUtil.isNull(entity.getIsException())) {
+                entity.setIsException("0"); // 正常
             }
 
-            // 5. 保存签到记录
+            // 7. 保存签到记录
             boolean result = baseMapper.insert(entity) > 0;
             if (!result) {
                 return R.fail("签到失败,请稍后重试");
@@ -100,7 +142,7 @@ public class ZsjyInternshipDailyServiceImpl implements IZsjyInternshipDailyServi
 
         } catch (Exception e) {
             log.error("学生签到失败: {}", e.getMessage(), e);
-            throw new ServiceException("签到失败: " + e.getMessage());
+            return R.fail("签到失败: " + e.getMessage());
         }
     }
 

+ 8 - 0
mobile/src/pages.json

@@ -119,6 +119,14 @@
 				"navigationBarTitleText": "微信JS-SDK测试",
 				"navigationStyle": "custom"
 			}
+		},
+		{
+			"path": "pages/internship/index",
+			"style": {
+				"navigationBarTitleText": "实习打卡",
+				"navigationBarBackgroundColor": "#0051d9",
+				"navigationBarTextStyle": "white"
+			}
 		}
 	],
 	"globalStyle": {

+ 23 - 1
mobile/src/pages/index/index.vue

@@ -154,6 +154,20 @@
                             />
                         </template>
                     </ServiceItem>
+
+                    <ServiceItem
+                        v-if="hasPermi(['student:internship:list'])"
+                        title="实习打卡"
+                        @click="gotoInternshipManage"
+                    >
+                        <template #icon>
+                            <image
+                                src="/static/00116.png"
+                                mode="aspectFit"
+                                style="width: 24px; height: 24px"
+                            />
+                        </template>
+                    </ServiceItem>
                 </GridLayout>
             </Card>
 
@@ -575,7 +589,8 @@ export default {
                 this.hasPermi(['student:psychology:list']) ||
                 this.hasPermi(['student:club:list']) ||
                 this.hasPermi(['student:counsel:list']) ||
-                this.hasPermi(['student:info:edit'])
+                this.hasPermi(['student:info:edit']) ||
+                this.hasPermi(['student:internship:list'])
             );
         },
 
@@ -776,6 +791,13 @@ export default {
             });
         },
 
+        // 跳转到实习管理页面
+        gotoInternshipManage() {
+            uni.navigateTo({
+                url: '/pages/internship/index',
+            });
+        },
+
         // 图片加载成功处理
         handleImageLoad(event) {
             console.log('✅ 轮播图片加载成功:', event.detail);

+ 26 - 0
mobile/src/utils/permission.js

@@ -14,6 +14,10 @@ export const checkPermi = (value) => {
     if (value && value instanceof Array && value.length > 0) {
         const userInfo = userStorage.getUserInfo();
         if (!userInfo || !userInfo.permissions) {
+            console.warn('🚫 权限检查失败: 用户信息或权限列表不存在', {
+                hasUserInfo: !!userInfo,
+                hasPermissions: !!(userInfo && userInfo.permissions)
+            });
             return false;
         }
 
@@ -21,11 +25,33 @@ export const checkPermi = (value) => {
         const permissionDatas = value;
         const all_permission = '*:*:*';
 
+        // 调试日志:输出当前用户权限和检查的权限
+        console.log('🔐 权限检查调试信息:', {
+            检查权限: permissionDatas,
+            用户权限数量: permissions.length,
+            是否包含超级权限: permissions.includes(all_permission),
+            权限匹配结果: permissionDatas.some(perm => permissions.includes(perm))
+        });
+
+        // 特别检查实习相关权限
+        if (permissionDatas.some(perm => perm.includes('internship'))) {
+            console.log('📚 实习权限检查详情:', {
+                检查的实习权限: permissionDatas.filter(perm => perm.includes('internship')),
+                用户拥有的实习权限: permissions.filter(perm => perm.includes('internship')),
+                所有权限前10条: permissions.slice(0, 10)
+            });
+        }
+
         const hasPermission = permissions.some((permission) => {
             return all_permission === permission || permissionDatas.includes(permission);
         });
 
         if (!hasPermission) {
+            console.warn('🚫 权限验证失败:', {
+                需要权限: permissionDatas,
+                用户ID: userInfo.user?.userId,
+                用户名: userInfo.user?.userName
+            });
             return false;
         }
         return true;

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

@@ -125,9 +125,9 @@
       <div class="card-header-vertical">
         <div class="toolbar-title">实习日常管理</div>
         <div class="button-group">
-          <el-button type="primary" icon="Plus" @click="handleAdd" v-hasPermi="['zsjy:internship:daily:add']">新增签到</el-button>
+          <el-button type="primary" icon="Plus" @click="handleAdd" v-hasPermi="['zsjy:internshipdaily:add']">新增签到</el-button>
           <el-button plain icon="Search" @click="showSearch = !showSearch">搜索</el-button>
-          <el-button type="primary" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:internship:daily:export']">导出</el-button>
+          <el-button type="primary" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:internshipdaily:export']">导出</el-button>
         </div>
       </div>
     </el-card>
@@ -578,7 +578,7 @@ const submitAdd = () => {
   proxy.$refs.addFormRef.validate(valid => {
     if (valid) {
       addLoading.value = true;
-      
+
       addInternshipDaily(addForm.value).then(response => {
         proxy.$modal.msgSuccess("新增签到记录成功");
         addDialogVisible.value = false;