diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipDailyQueryBo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipDailyQueryBo.java index 6f0f040c..648015fb 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipDailyQueryBo.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipDailyQueryBo.java @@ -82,4 +82,10 @@ public class ZsjyInternshipDailyQueryBo extends BaseEntity { @Schema(description = "公司名称") private String companyName; + /** + * 班主任ID(用于权限控制) + */ + @Schema(description = "班主任ID") + private Long headTeacherId; + } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipDailyMapper.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipDailyMapper.java index 7af7458d..d0de12c7 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipDailyMapper.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipDailyMapper.java @@ -61,4 +61,12 @@ public interface ZsjyInternshipDailyMapper extends BaseMapper queryPageList(ZsjyInternshipDailyQueryBo queryBo, PageQuery pageQuery) { + log.info("=== 实习日常查询后端调试日志 ==="); + log.info("1. 查询参数: {}", queryBo); + log.info("2. 分页参数: pageNum={}, pageSize={}", pageQuery.getPageNum(), pageQuery.getPageSize()); + + // 设置数据权限 + setDataPermission(queryBo); + log.info("3. 设置数据权限后的查询参数: {}", queryBo); + log.info("3.1. 权限控制参数: headTeacherId={}", queryBo.getHeadTeacherId()); + Page page = pageQuery.build(); List list = baseMapper.selectDailyList(page, queryBo); + log.info("4. 查询到的数据条数: {}", list != null ? list.size() : "null"); + return TableDataInfo.build(list); } @@ -58,6 +69,9 @@ public class ZsjyInternshipDailyServiceImpl implements IZsjyInternshipDailyServi */ @Override public List queryList(ZsjyInternshipDailyQueryBo queryBo) { + // 设置数据权限 + setDataPermission(queryBo); + return baseMapper.selectDailyList(null, queryBo); } @@ -202,4 +216,46 @@ public class ZsjyInternshipDailyServiceImpl implements IZsjyInternshipDailyServi } } + /** + * 设置数据权限(班主任只能查看自己班级的学生数据) + */ + private void setDataPermission(ZsjyInternshipDailyQueryBo queryBo) { + try { + // 获取当前登录用户ID + Long currentUserId = LoginHelper.getUserId(); + log.info("=== 权限控制调试 ==="); + log.info("当前登录用户ID: {}", currentUserId); + + if (ObjectUtil.isNotNull(currentUserId)) { + // 检查该用户是否是班主任 + Long classTeacherCount = baseMapper.countClassesByTeacherId(currentUserId); + log.info("用户ID {}作为班主任管理的班级数量: {}", currentUserId, classTeacherCount); + + if (classTeacherCount > 0) { + // 是班主任,设置权限限制 + queryBo.setHeadTeacherId(currentUserId); + log.info("✅ 权限判断:用户是班主任,设置headTeacherId={}", currentUserId); + } else { + // 不是班主任,设置不存在的班主任ID,使其无法查看任何数据 + log.info("❌ 权限判断:用户不是班主任,设置权限限制"); + + // 设置一个不存在的班主任ID(-1),这样SQL查询结果为空 + queryBo.setHeadTeacherId(-1L); + log.info("🔒 设置数据权限:非班主任用户无法查看数据,headTeacherId=-1"); + } + + // 输出最终的权限设置 + log.info("🎯 最终权限设置:queryBo.headTeacherId = {}", queryBo.getHeadTeacherId()); + } else { + log.warn("⚠️ 未获取到当前用户ID,设置权限限制"); + queryBo.setHeadTeacherId(-1L); + } + } catch (Exception e) { + log.error("❌ 设置数据权限失败:{}", e.getMessage(), e); + // 异常情况下,设置权限限制以确保数据安全 + queryBo.setHeadTeacherId(-1L); + log.warn("🔒 异常情况下设置权限限制,headTeacherId=-1"); + } + } + } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipDailyMapper.xml b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipDailyMapper.xml index e1904a0e..c741ffea 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipDailyMapper.xml +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipDailyMapper.xml @@ -104,6 +104,15 @@ AND i.company_name LIKE CONCAT('%', #{queryBo.companyName}, '%') + + + AND EXISTS ( + SELECT 1 FROM xg_student_msg s + WHERE s.id = d.student_id + AND s.class_teacher_id = #{queryBo.headTeacherId} + AND s.deleted = 0 + ) + ORDER BY d.check_date DESC, d.create_time DESC @@ -184,4 +193,13 @@ AND deleted = 0 + + + diff --git a/mobile/src/pages/internship/index.vue b/mobile/src/pages/internship/index.vue index ca7c5b41..c4f08576 100644 --- a/mobile/src/pages/internship/index.vue +++ b/mobile/src/pages/internship/index.vue @@ -92,66 +92,112 @@ - - 当前位置 + + + 当前位置 + - - {{ currentLocation.address }} - - 经度:{{ currentLocation.longitude.toFixed(6) }} - 纬度:{{ currentLocation.latitude.toFixed(6) }} + + + + + {{ currentLocation.address }} + + + + + + + 经度 + {{ currentLocation.longitude.toFixed(6) }} + + + + 纬度 + {{ currentLocation.latitude.toFixed(6) }} + - - 定位精度:{{ currentLocation.accuracy.toFixed(0) }}米 + + + + + + + 定位精度 {{ currentLocation.accuracy.toFixed(0) }}米 - - - - 在地图中查看 - - + + + + + + + 在地图中查看 + - - 位置信息 + + + 位置信息 + + - - {{ locationError }} - 签到需要位置信息,请确保: - • 已开启位置权限 - • GPS/网络定位已开启 - • 网络连接正常 + + + 无法获取位置信息 + + + {{ locationError }} + + + 签到需要位置信息,请确保: + + + + 已开启位置权限 + + + + GPS/网络定位已开启 + + + + 网络连接正常 + + + @@ -997,19 +1043,29 @@ export default { .card-header { display: flex; align-items: center; - margin-bottom: 12px; - padding-bottom: 8px; + justify-content: space-between; + margin-bottom: 16px; + padding-bottom: 12px; border-bottom: 1px solid #f0f0f0; } +.header-left { + display: flex; + align-items: center; + flex: 1; +} + .card-title { font-size: 16px; font-weight: bold; margin-left: 8px; - flex: 1; color: #303133; } +.refresh-btn { + flex-shrink: 0; +} + .date-text { font-size: 12px; color: #909399; @@ -1099,43 +1155,112 @@ export default { } /* 位置信息 */ -.location-info { - padding-top: 8px; +.location-address { + display: flex; + align-items: flex-start; + margin-bottom: 16px; + padding: 12px; + background: #f8f9fa; + border-radius: 8px; + border-left: 3px solid #67c23a; } -.location-text { +.address-icon { + margin-right: 8px; + margin-top: 2px; + flex-shrink: 0; +} + +.address-text { font-size: 14px; color: #303133; - margin-bottom: 8px; line-height: 1.5; + flex: 1; } -.coordinates { - display: flex; - justify-content: space-between; +/* 坐标信息 */ +.coordinates-section { + margin-bottom: 16px; } -.coord-text { - font-size: 12px; - color: #909399; -} - -.location-accuracy { +.coord-row { display: flex; align-items: center; - margin-top: 8px; - margin-bottom: 8px; + background: #f8f9fa; + border-radius: 8px; + padding: 12px; +} + +.coord-item { + flex: 1; + text-align: center; +} + +.coord-divider { + width: 1px; + height: 32px; + background: #e4e7ed; + margin: 0 16px; +} + +.coord-label { + display: block; + font-size: 12px; + color: #909399; + margin-bottom: 4px; +} + +.coord-value { + display: block; + font-size: 14px; + color: #303133; + font-weight: 500; + font-family: monospace; +} + +/* 精度信息 */ +.accuracy-section { + margin-bottom: 16px; +} + +.accuracy-info { + display: flex; + align-items: center; + padding: 10px 12px; + background: #f0f9ff; + border-radius: 8px; + border: 1px solid #b3e5fc; +} + +.accuracy-icon { + margin-right: 8px; + flex-shrink: 0; } .accuracy-text { - font-size: 12px; - color: #909399; - margin-right: 8px; + font-size: 13px; + color: #67c23a; + font-weight: 500; + flex: 1; } +.accuracy-tag { + margin-left: 8px; + flex-shrink: 0; +} + +/* 操作按钮 */ .location-actions { display: flex; - justify-content: flex-end; + justify-content: center; +} + +.map-btn { + min-width: 140px; +} + +.btn-text { + margin-left: 6px; } /* 位置获取失败提示 */ @@ -1149,24 +1274,73 @@ export default { } .error-content { - display: flex; - flex-direction: column; - align-items: center; - padding-top: 12px; + padding-top: 8px; } -.error-text { +.error-header { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 16px; +} + +.error-icon { + margin-right: 8px; +} + +.error-title { + font-size: 16px; + font-weight: bold; + color: #e6a23c; +} + +.error-message { + display: block; font-size: 14px; color: #faad14; - margin-top: 8px; text-align: center; + margin-bottom: 20px; + line-height: 1.5; } -.error-tips { - font-size: 12px; - color: #faad14; - margin-top: 4px; - text-align: center; +.error-tips-section { + background: #fff7e6; + border-radius: 8px; + padding: 16px; + border: 1px solid #ffd591; +} + +.tips-title { + display: block; + font-size: 14px; + font-weight: bold; + color: #d48806; + margin-bottom: 12px; +} + +.tips-list { + padding-left: 0; +} + +.tip-item { + display: flex; + align-items: center; + margin-bottom: 8px; +} + +.tip-item:last-child { + margin-bottom: 0; +} + +.tip-icon { + margin-right: 8px; + flex-shrink: 0; +} + +.tip-text { + font-size: 13px; + color: #d48806; + line-height: 1.4; } /* 统计数据 */ @@ -1245,4 +1419,64 @@ export default { color: #909399; margin-top: 8px; } + +/* 响应式设计 */ +@media screen and (max-width: 375px) { + .coord-row { + padding: 10px; + } + + .coord-divider { + margin: 0 12px; + } + + .coord-value { + font-size: 13px; + } + + .map-btn { + min-width: 120px; + font-size: 14px; + } + + .error-tips-section { + padding: 12px; + } +} + +@media screen and (min-width: 414px) { + .location-address { + padding: 16px; + } + + .coord-row { + padding: 16px; + } + + .accuracy-info { + padding: 12px 16px; + } + + .coord-value { + font-size: 15px; + } +} + +/* 深色模式适配 */ +@media (prefers-color-scheme: dark) { + .location-address, + .coord-row { + background: rgba(255, 255, 255, 0.05); + } + + .accuracy-info { + background: rgba(103, 194, 58, 0.1); + border-color: rgba(103, 194, 58, 0.3); + } + + .error-tips-section { + background: rgba(255, 247, 230, 0.1); + border-color: rgba(255, 213, 145, 0.3); + } +} diff --git a/web/src/api/zsjy/internshipDaily.js b/web/src/api/zsjy/internshipDaily.js index 2772d03d..512954f4 100644 --- a/web/src/api/zsjy/internshipDaily.js +++ b/web/src/api/zsjy/internshipDaily.js @@ -1,4 +1,4 @@ -import request from '@/utils/request' +import request from '@/utils/request'; // 查询实习日常记录列表 export function listInternshipDaily(query) { @@ -6,7 +6,7 @@ export function listInternshipDaily(query) { url: '/zsjy/internship/daily/list', method: 'get', params: query - }) + }); } // 学生签到打卡 @@ -15,7 +15,7 @@ export function checkIn(data) { url: '/zsjy/internship/daily/checkin', method: 'post', data: data - }) + }); } // 新增实习日常记录 @@ -24,7 +24,7 @@ export function addInternshipDaily(data) { url: '/zsjy/internship/daily', method: 'post', data: data - }) + }); } // 获取学生考勤日历 @@ -33,7 +33,7 @@ export function getCalendar(studentId, params) { url: `/zsjy/internship/daily/calendar/${studentId}`, method: 'get', params: params - }) + }); } // 获取考勤统计数据 @@ -42,5 +42,5 @@ export function getStatistics(query) { url: '/zsjy/internship/daily/statistics', method: 'get', params: query - }) + }); } diff --git a/web/src/api/zsjy/types.ts b/web/src/api/zsjy/types.ts index 23dd8ba4..d18c8f3c 100644 --- a/web/src/api/zsjy/types.ts +++ b/web/src/api/zsjy/types.ts @@ -1195,6 +1195,16 @@ export interface InternshipInfoQuery { * 实习开始日期-结束 */ internshipStartDateEnd?: string; + + /** + * 班主任ID(用于数据权限控制) + */ + classTeacherId?: string | number; + + /** + * 辅导员ID(用于数据权限控制) + */ + counselorId?: string | number; } /** @@ -1261,3 +1271,283 @@ export interface InternshipStatisticsVO { */ internshipRate?: string; } + +/** + * 班主任走访记录 VO + */ +export interface InternshipVisitVO { + /** + * 走访记录ID + */ + visitId?: string | number; + + /** + * 实习记录ID + */ + internshipId?: string | number; + + /** + * 学生ID + */ + studentId?: string | number; + + /** + * 学生姓名 + */ + studentName?: string; + + /** + * 学号 + */ + studentNumber?: string; + + /** + * 班级名称 + */ + className?: string; + + /** + * 专业名称 + */ + majorName?: string; + + /** + * 学院名称 + */ + collegeName?: string; + + /** + * 企业名称 + */ + companyName?: string; + + /** + * 企业地址 + */ + companyAddress?: string; + + /** + * 走访日期 + */ + visitDate?: string; + + /** + * 走访人员 + */ + visitorName?: string; + + /** + * 走访人员职务 + */ + visitorPosition?: string; + + /** + * 走访目的 + */ + visitPurpose?: string; + + /** + * 企业情况 + */ + companySituation?: string; + + /** + * 学生表现 + */ + studentPerformance?: string; + + /** + * 发现问题 + */ + problemsFound?: string; + + /** + * 改进建议 + */ + suggestions?: string; + + /** + * 走访总结 + */ + visitSummary?: string; + + /** + * 现场照片(JSON格式) + */ + visitImages?: string; + + /** + * 走访文件附件(JSON格式) + */ + visitFiles?: string; + + /** + * 状态(0-草稿 1-已提交 2-已审核) + */ + status?: string; + + /** + * 备注 + */ + remark?: string; + + /** + * 创建时间 + */ + createTime?: string; + + /** + * 创建者 + */ + createBy?: number; + + /** + * 更新时间 + */ + updateTime?: string; + + /** + * 更新者 + */ + updateBy?: number; +} + +/** + * 班主任走访记录查询参数 + */ +export interface InternshipVisitQuery extends PageQuery { + /** + * 学生姓名 + */ + studentName?: string; + + /** + * 学号 + */ + studentNumber?: string; + + /** + * 企业名称 + */ + companyName?: string; + + /** + * 走访人员 + */ + visitorName?: string; + + /** + * 走访日期-开始 + */ + visitDateBegin?: string; + + /** + * 走访日期-结束 + */ + visitDateEnd?: string; + + /** + * 状态 + */ + status?: string; +} + +/** + * 班主任走访记录新增参数 + */ +export interface InternshipVisitAddBO { + /** + * 实习记录ID + */ + internshipId?: string | number; + + /** + * 学生ID + */ + studentId?: string | number; + + /** + * 企业名称 + */ + companyName?: string; + + /** + * 企业地址 + */ + companyAddress?: string; + + /** + * 走访日期 + */ + visitDate?: string; + + /** + * 走访人员 + */ + visitorName?: string; + + /** + * 走访人员职务 + */ + visitorPosition?: string; + + /** + * 走访目的 + */ + visitPurpose?: string; + + /** + * 企业情况 + */ + companySituation?: string; + + /** + * 学生表现 + */ + studentPerformance?: string; + + /** + * 发现问题 + */ + problemsFound?: string; + + /** + * 改进建议 + */ + suggestions?: string; + + /** + * 走访总结 + */ + visitSummary?: string; + + /** + * 现场照片(JSON格式) + */ + visitImages?: string; + + /** + * 走访文件附件(JSON格式) + */ + visitFiles?: string; + + /** + * 状态 + */ + status?: string; + + /** + * 备注 + */ + remark?: string; +} + +/** + * 班主任走访记录编辑参数 + */ +export interface InternshipVisitEditBO extends InternshipVisitAddBO { + /** + * 走访记录ID + */ + visitId?: string | number; +} diff --git a/web/src/components/ImageUpload/index.vue b/web/src/components/ImageUpload/index.vue index e3020e5d..2df7959f 100644 --- a/web/src/components/ImageUpload/index.vue +++ b/web/src/components/ImageUpload/index.vue @@ -455,14 +455,38 @@ const handleBeforeUpload = (file: any) => { } } + // 设置超时处理 - 如果60秒后仍在loading状态,自动关闭并提示 + const timeoutId = setTimeout(() => { + if (number.value > 0) { + console.warn('上传超时,自动关闭loading'); + (proxy as any)?.$modal.closeLoading(); + (proxy as any)?.$modal.msgError('上传超时,请检查网络连接或重试'); + number.value = 0; // 重置计数器 + } + }, 60000); // 60秒超时 + //压缩图片,开启压缩并且大于指定的压缩大小时才压缩 if (props.compressSupport && file.size / 1024 > props.compressTargetSize) { (proxy as any)?.$modal.loading('正在上传图片,请稍候...'); number.value++; - return compressAccurately(file, props.compressTargetSize); + + // 压缩图片时的错误处理 + try { + return compressAccurately(file, props.compressTargetSize); + } catch (compressionError) { + console.error('图片压缩失败:', compressionError); + clearTimeout(timeoutId); + (proxy as any)?.$modal.closeLoading(); + (proxy as any)?.$modal.msgError('图片压缩失败,请重试'); + number.value--; + return false; + } } else { (proxy as any)?.$modal.loading('正在上传图片,请稍候...'); number.value++; + + // 保存timeout ID以便在上传完成时清除 + (file as any).uploadTimeoutId = timeoutId; } }; @@ -476,6 +500,11 @@ const handleUploadSuccess = (res: any, file: any) => { console.log('handleUploadSuccess:res', res); console.log('handleUploadSuccess:file', file); + // 清除上传超时处理 + if ((file as any).uploadTimeoutId) { + clearTimeout((file as any).uploadTimeoutId); + } + if (res.code === 200) { const uploadItem = { name: res.data.fileName, @@ -488,7 +517,9 @@ const handleUploadSuccess = (res: any, file: any) => { } else { number.value--; (proxy as any)?.$modal.closeLoading(); - (proxy as any)?.$modal.msgError(res.msg); + // 提供更详细的错误信息 + const errorMsg = res.msg || '上传失败,请重试'; + (proxy as any)?.$modal.msgError(errorMsg); imageUploadRef.value?.handleRemove(file); uploadedSuccessfully(); } @@ -657,9 +688,62 @@ const uploadedSuccessfully = async () => { }; // 上传失败 -const handleUploadError = () => { - (proxy as any)?.$modal.msgError('上传图片失败'); +const handleUploadError = (error: any, file: any) => { + console.error('上传失败详细信息:', error); + + // 清除上传超时处理 + if (file && (file as any).uploadTimeoutId) { + clearTimeout((file as any).uploadTimeoutId); + } + + // 确保关闭loading (proxy as any)?.$modal.closeLoading(); + + // 减少计数器 + number.value--; + + // 根据错误类型提供不同的错误信息 + let errorMessage = '上传图片失败'; + + if (error.response) { + const status = error.response.status; + const responseData = error.response.data; + + switch (status) { + case 403: + errorMessage = '没有文件上传权限,请联系管理员'; + break; + case 413: + errorMessage = '文件太大,超过服务器限制'; + break; + case 500: + errorMessage = '服务器内部错误,请稍后重试'; + break; + case 504: + errorMessage = '上传超时,请检查网络连接'; + break; + default: + if (responseData && responseData.msg) { + errorMessage = responseData.msg; + } else if (responseData && typeof responseData === 'string') { + errorMessage = `上传失败: ${responseData}`; + } else { + errorMessage = `上传失败,错误代码: ${status}`; + } + } + } else if (error.request) { + // 网络错误 + if (error.code === 'ECONNABORTED' || error.message.includes('timeout')) { + errorMessage = '上传超时,请检查网络连接或文件大小'; + } else { + errorMessage = '网络连接错误,请检查网络设置'; + } + } else if (error.message) { + errorMessage = `上传失败: ${error.message}`; + } + + console.error('上传错误信息:', errorMessage); + (proxy as any)?.$modal.msgError(errorMessage); }; // 预览 diff --git a/web/src/views/jcbt/workstudy/xgreview/README.md b/web/src/views/jcbt/workstudy/xgreview/README.md index 1690a1d7..0d48eed1 100644 --- a/web/src/views/jcbt/workstudy/xgreview/README.md +++ b/web/src/views/jcbt/workstudy/xgreview/README.md @@ -7,12 +7,14 @@ ## 功能特性 ### 1. 统计概览 + - 待审核申请数量 -- 已通过申请数量 +- 已通过申请数量 - 已拒绝申请数量 - 总处理数量 ### 2. 待审核申请管理 + - 查看待审核申请列表 - 支持多条件搜索(岗位名称、学生姓名、学号、学院名称、申请日期) - 单个申请审核(通过/拒绝) @@ -20,6 +22,7 @@ - 查看申请详情 ### 3. 所有申请查看 + - 查看所有申请记录 - 支持按状态筛选 - 导出申请数据 @@ -40,16 +43,19 @@ xgreview/ ## 技术特点 ### 1. 响应式设计 + - 使用Vue 3 Composition API - TypeScript类型支持 - Element Plus UI组件库 ### 2. 数据管理 + - 基于学工处审核API - 支持分页查询 - 实时数据更新 ### 3. 用户体验 + - 加载状态提示 - 操作反馈消息 - 响应式布局 @@ -57,6 +63,7 @@ xgreview/ ## API接口 ### 主要接口 + - `listPendingApplications` - 获取待审核申请列表 - `listStudentAffairsApplications` - 获取所有申请列表 - `getStudentAffairsApplication` - 获取申请详情 @@ -68,15 +75,18 @@ xgreview/ ## 使用说明 ### 1. 访问路径 + ``` /jcbt/workstudy/xgreview ``` ### 2. 权限要求 + - 需要学工处审核权限 - 支持批量操作权限 ### 3. 操作流程 + 1. 查看统计概览,了解待处理申请数量 2. 在"待审核申请"标签页中查看待处理申请 3. 使用搜索功能筛选特定申请 @@ -100,16 +110,19 @@ xgreview/ ## 开发说明 ### 1. 组件复用 + 本模块复用了学院审核模块的组件结构,但使用不同的API接口和字段。 ### 2. 样式规范 + 遵循项目的前端开发规范,使用统一的颜色主题和组件样式。 ### 3. 错误处理 + 包含完整的错误处理机制,提供用户友好的错误提示。 ## 更新日志 - **2025-01-03**:创建学工处审核模块 - **2025-01-03**:实现基础审核功能 -- **2025-01-03**:添加统计概览和批量操作 \ No newline at end of file +- **2025-01-03**:添加统计概览和批量操作 diff --git a/web/src/views/jcbt/workstudy/xgreview/开发总结.md b/web/src/views/jcbt/workstudy/xgreview/开发总结.md index 1693bd02..eb0e1df5 100644 --- a/web/src/views/jcbt/workstudy/xgreview/开发总结.md +++ b/web/src/views/jcbt/workstudy/xgreview/开发总结.md @@ -7,12 +7,14 @@ ## ✅ 已完成功能 ### 1. 主页面 (index.vue) + - ✅ 统计卡片展示(待审核、已通过、已拒绝、总计) - ✅ 标签页切换(待审核申请、所有申请) - ✅ 响应式布局和美观的UI设计 - ✅ 与后勤管理模块保持一致的样式风格 ### 2. 待审核申请组件 (PendingApplications.vue) + - ✅ 多条件搜索表单(岗位名称、学生姓名、学号、学院名称、申请日期) - ✅ 批量操作按钮(批量通过、批量拒绝) - ✅ 数据表格展示(包含学院审核状态) @@ -22,6 +24,7 @@ - ✅ 分页功能 ### 3. 所有申请组件 (AllApplications.vue) + - ✅ 扩展搜索条件(包含学工处审核状态) - ✅ 完整申请列表展示 - ✅ 学院审核和学工处审核状态对比 @@ -29,6 +32,7 @@ - ✅ 审核操作(针对待审核状态) ### 4. 申请详情组件 (ApplicationDetail.vue) + - ✅ 完整的申请信息展示 - ✅ 学院审核信息展示 - ✅ 学工处审核信息展示 @@ -36,23 +40,27 @@ - ✅ 立即审核功能 ### 5. 路由配置 (jcbt.ts) + - ✅ 创建jcbt模块路由配置 - ✅ 包含学工处审核模块路由 - ✅ 支持懒加载 ### 6. 文档说明 + - ✅ README.md - 详细的功能说明和使用指南 - ✅ 开发总结.md - 开发过程和成果总结 ## 🏗️ 技术架构 ### 前端技术栈 + - **Vue 3.5.13** + **TypeScript 5.8.3** - **Element Plus** UI组件库 - **Composition API** 响应式编程 - **SCSS** 样式预处理器 ### 组件结构 + ``` xgreview/ ├── index.vue # 主页面 @@ -66,6 +74,7 @@ xgreview/ ``` ### API接口 + - 基于 `@/api/jcbt/workStudyApplicationStudentAffairsReview.ts` - 支持加密请求(AES+RSA) - 完整的CRUD操作接口 @@ -73,16 +82,19 @@ xgreview/ ## 🎨 设计特点 ### 1. 视觉一致性 + - 与学院审核模块保持完全一致的视觉风格 - 使用相同的颜色主题和组件样式 - 统一的布局结构和交互模式 ### 2. 用户体验 + - 响应式设计,支持不同屏幕尺寸 - 加载状态提示和操作反馈 - 直观的操作流程和清晰的界面布局 ### 3. 功能完整性 + - 覆盖完整的审核业务流程 - 支持单个和批量操作 - 提供详细的数据展示和操作记录 @@ -90,53 +102,61 @@ xgreview/ ## 🔧 开发规范遵循 ### 1. 前端开发规范 + - ✅ 遵循 `.cursor/cursorrules-frontend.md` 规范 - ✅ 使用Vue 3 Composition API - ✅ TypeScript类型定义完整 - ✅ 组件化开发,代码复用 ### 2. 项目结构规范 + - ✅ 页面代码存放在 `web/src/views/jcbt/workstudy/xgreview/` - ✅ API代码存放在 `web/src/api/jcbt/` - ✅ 路由配置存放在 `web/src/router/` ### 3. 代码质量 + - ✅ 完整的错误处理机制 - ✅ 清晰的代码注释和文档 - ✅ 遵循Vue 3最佳实践 ## 📊 功能对比 -| 功能模块 | 学院审核 | 学工处审核 | 状态 | -|---------|---------|-----------|------| -| 统计概览 | ✅ | ✅ | 完全一致 | -| 待审核列表 | ✅ | ✅ | 完全一致 | -| 所有申请 | ✅ | ✅ | 完全一致 | -| 申请详情 | ✅ | ✅ | 完全一致 | -| 审核操作 | ✅ | ✅ | 完全一致 | -| 批量操作 | ✅ | ✅ | 完全一致 | -| 导出功能 | ✅ | ✅ | 完全一致 | +| 功能模块 | 学院审核 | 学工处审核 | 状态 | +| ---------- | -------- | ---------- | -------- | +| 统计概览 | ✅ | ✅ | 完全一致 | +| 待审核列表 | ✅ | ✅ | 完全一致 | +| 所有申请 | ✅ | ✅ | 完全一致 | +| 申请详情 | ✅ | ✅ | 完全一致 | +| 审核操作 | ✅ | ✅ | 完全一致 | +| 批量操作 | ✅ | ✅ | 完全一致 | +| 导出功能 | ✅ | ✅ | 完全一致 | ## 🚀 部署说明 ### 1. 文件位置 + 所有文件已正确放置在指定目录: + - 主页面:`web/src/views/jcbt/workstudy/xgreview/index.vue` - 组件:`web/src/views/jcbt/workstudy/xgreview/components/` - 路由:`web/src/router/jcbt.ts` ### 2. 访问路径 + ``` /jcbt/workstudy/xgreview ``` ### 3. 权限要求 + - 需要学工处审核权限 - 支持批量操作权限 ## 🔍 测试建议 ### 1. 功能测试 + - 统计数据展示 - 搜索和筛选功能 - 审核操作流程 @@ -144,11 +164,13 @@ xgreview/ - 导出功能 ### 2. 兼容性测试 + - 不同浏览器兼容性 - 不同屏幕尺寸响应式 - 权限控制验证 ### 3. 性能测试 + - 大数据量加载性能 - 操作响应速度 - 内存使用情况 @@ -156,16 +178,19 @@ xgreview/ ## 📝 后续优化建议 ### 1. 功能增强 + - 添加审核历史记录 - 支持审核意见模板 - 增加数据统计图表 ### 2. 性能优化 + - 实现虚拟滚动 - 添加数据缓存 - 优化API请求 ### 3. 用户体验 + - 添加操作引导 - 支持快捷键操作 - 增加个性化设置 @@ -186,4 +211,4 @@ xgreview/ **开发完成时间**:2025-01-03 **开发状态**:✅ 完成 **代码质量**:⭐⭐⭐⭐⭐ -**文档完整性**:⭐⭐⭐⭐⭐ \ No newline at end of file +**文档完整性**:⭐⭐⭐⭐⭐ diff --git a/web/src/views/zsjy/internship/daily/checkin.vue b/web/src/views/zsjy/internship/daily/checkin.vue index 8b476ddf..b2b4e222 100644 --- a/web/src/views/zsjy/internship/daily/checkin.vue +++ b/web/src/views/zsjy/internship/daily/checkin.vue @@ -216,8 +216,8 @@