This commit is contained in:
zlwu committed 2025-08-24 23:44:53 +08:00
commit 3c62f1f6de
64 files changed
+5288 -4353

No files matched your search

@@ -295,4 +295,13 @@ public class JcbtScholarshipTypeController extends BaseController {
return R.ok(compatibleTypes);
}
/**
* 获取奖学金类型的互斥关系显示文本
*/
@SaCheckPermission("jcbt:scholarshipType:query")
@GetMapping("/{id}/exclusiveRelationDisplay")
public R<String> getExclusiveRelationDisplay(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return R.ok(scholarshipTypeService.getExclusiveRelationDisplay(id));
}
}
@@ -91,4 +91,12 @@ public interface IJcbtScholarshipTypeService {
*/
List<JcbtScholarshipTypeVo> queryOptions();
/**
* 获取奖学金类型的互斥关系显示文本
*
* @param scholarshipTypeId 奖学金类型ID
* @return 互斥关系显示文本
*/
String getExclusiveRelationDisplay(Long scholarshipTypeId);
}
@@ -14,10 +14,12 @@ import org.dromara.jcbt.domain.vo.JcbtScholarshipTypeVo;
import org.dromara.jcbt.domain.JcbtScholarshipType;
import org.dromara.jcbt.mapper.JcbtScholarshipTypeMapper;
import org.dromara.jcbt.service.IJcbtScholarshipTypeService;
import org.dromara.jcbt.service.IJcbtScholarshipCompatibilityService;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.ArrayList;
/**
* 奖学金类型Service业务层处理
@@ -30,6 +32,7 @@ import java.util.Collection;
public class JcbtScholarshipTypeServiceImpl implements IJcbtScholarshipTypeService {
private final JcbtScholarshipTypeMapper baseMapper;
private final IJcbtScholarshipCompatibilityService compatibilityService;
/**
* 查询奖学金类型
@@ -231,4 +234,48 @@ public class JcbtScholarshipTypeServiceImpl implements IJcbtScholarshipTypeServi
JcbtScholarshipType::getTypeCategory, JcbtScholarshipType::getAwardAmount);
return baseMapper.selectVoList(lqw);
}
/**
* 获取奖学金类型的互斥关系显示文本
*/
@Override
public String getExclusiveRelationDisplay(Long scholarshipTypeId) {
// 1. 获取当前奖学金类型信息
JcbtScholarshipTypeVo currentType = queryById(scholarshipTypeId);
if (currentType == null) {
return "互斥(无)";
}
// 2. 如果是EXCLUSIVE类型,显示"互斥(与全部其他奖学金类型)"
if ("EXCLUSIVE".equals(currentType.getCompatibilityType())) {
return "互斥(与全部其他奖学金类型)";
}
// 3. 如果是CUSTOM类型,查询具体的互斥关系
if ("CUSTOM".equals(currentType.getCompatibilityType())) {
List<Long> exclusiveTypeIds = compatibilityService.getExclusiveTypeIds(scholarshipTypeId);
if (exclusiveTypeIds == null || exclusiveTypeIds.isEmpty()) {
return "互斥(无)";
}
// 查询互斥的奖学金类型名称
List<String> exclusiveTypeNames = new ArrayList<>();
for (Long exclusiveTypeId : exclusiveTypeIds) {
JcbtScholarshipTypeVo exclusiveType = queryById(exclusiveTypeId);
if (exclusiveType != null) {
exclusiveTypeNames.add(exclusiveType.getTypeName());
}
}
if (exclusiveTypeNames.isEmpty()) {
return "互斥(无)";
}
return "互斥(与" + String.join("、", exclusiveTypeNames) + ")";
}
// 4. 其他情况(如COMPATIBLE),显示"互斥(无)"
return "互斥(无)";
}
}
@@ -11,7 +11,6 @@ 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.rs.domain.RsTrainingApplication;
import org.dromara.rs.domain.RsTrainingCourse;
import org.dromara.rs.service.IRsTrainingApplicationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
@@ -42,7 +41,7 @@ public class RsTrainingApplicationController extends BaseController {
/**
* 查询培训申请列表
*/
@SaCheckPermission("rs:training:application:list")
@SaCheckPermission("rs:trainingapplication:list")
@GetMapping("/list")
public TableDataInfo<RsTrainingApplication> list(RsTrainingApplication application, PageQuery pageQuery) {
return trainingApplicationService.selectPageRsTrainingApplicationList(application, pageQuery);
@@ -51,7 +50,7 @@ public class RsTrainingApplicationController extends BaseController {
/**
* 导出培训申请列表
*/
@SaCheckPermission("rs:training:application:export")
@SaCheckPermission("rs:trainingapplication:export")
@Log(title = "培训申请", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, RsTrainingApplication application) {
@@ -62,7 +61,7 @@ public class RsTrainingApplicationController extends BaseController {
/**
* 获取培训申请详细信息
*/
@SaCheckPermission("rs:training:application:query")
@SaCheckPermission("rs:trainingapplication:query")
@GetMapping("/{applicationId}")
public R<RsTrainingApplication> getInfo(@PathVariable("applicationId") Long applicationId) {
return R.ok(trainingApplicationService.selectRsTrainingApplicationById(applicationId));
@@ -71,7 +70,7 @@ public class RsTrainingApplicationController extends BaseController {
/**
* 新增培训申请
*/
@SaCheckPermission("rs:training:application:add")
@SaCheckPermission("rs:trainingapplication:add")
@Log(title = "培训申请", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping
@@ -124,7 +123,7 @@ public class RsTrainingApplicationController extends BaseController {
/**
* 修改培训申请
*/
@SaCheckPermission("rs:training:application:edit")
@SaCheckPermission("rs:trainingapplication:edit")
@Log(title = "培训申请", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping
@@ -135,7 +134,7 @@ public class RsTrainingApplicationController extends BaseController {
/**
* 删除培训申请
*/
@SaCheckPermission("rs:training:application:remove")
@SaCheckPermission("rs:trainingapplication:remove")
@Log(title = "培训申请", businessType = BusinessType.DELETE)
@DeleteMapping("/{applicationIds}")
public R<Void> remove(@PathVariable Long[] applicationIds) {
@@ -145,7 +144,7 @@ public class RsTrainingApplicationController extends BaseController {
/**
* 取消培训申请
*/
@SaCheckPermission("rs:training:application:edit")
@SaCheckPermission("rs:trainingapplication:edit")
@Log(title = "培训申请", businessType = BusinessType.UPDATE)
@PutMapping("/cancel/{applicationId}")
public R<Void> cancel(@PathVariable("applicationId") Long applicationId) {
@@ -155,7 +154,7 @@ public class RsTrainingApplicationController extends BaseController {
/**
* 查询我的培训申请列表
*/
@SaCheckPermission("rs:training:application:query")
@SaCheckPermission("rs:trainingapplication:query")
@GetMapping("/my")
public TableDataInfo<RsTrainingApplication> listMyApplications(RsTrainingApplication application, PageQuery pageQuery) {
return trainingApplicationService.selectPageMyApplications(StpUtil.getLoginIdAsLong(), application, pageQuery);
@@ -164,7 +163,7 @@ public class RsTrainingApplicationController extends BaseController {
/**
* 查询指定课程的申请列表
*/
@SaCheckPermission("rs:training:application:list")
@SaCheckPermission("rs:trainingapplication:list")
@GetMapping("/list/{courseId}")
public TableDataInfo<RsTrainingApplication> listByCourse(@PathVariable("courseId") Long courseId, RsTrainingApplication application, PageQuery pageQuery) {
return trainingApplicationService.selectPageApplicationsByCourse(courseId, application, pageQuery);
@@ -227,7 +226,7 @@ public class RsTrainingApplicationController extends BaseController {
/**
* 更新培训申请状态
*/
@SaCheckPermission("rs:training:application:edit")
@SaCheckPermission("rs:trainingapplication:edit")
@Log(title = "培训申请状态", businessType = BusinessType.UPDATE)
@PutMapping("/status/{applicationId}/{status}")
public R<Void> updateStatus(
@@ -240,7 +239,7 @@ public class RsTrainingApplicationController extends BaseController {
/**
* 重新提交被拒绝的培训申请
*/
@SaCheckPermission("rs:training:application:edit")
@SaCheckPermission("rs:trainingapplication:edit")
@Log(title = "重新提交培训申请", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/resubmit/{applicationId}")
@@ -38,7 +38,7 @@ public class RsTrainingApprovalController extends BaseController {
/**
* 查询培训审批列表
*/
@SaCheckPermission("rs:training:approval:list")
@SaCheckPermission("rs:trainingapproval:list")
@GetMapping("/list")
public TableDataInfo<RsTrainingApproval> list(RsTrainingApproval approval, PageQuery pageQuery) {
// 使用带详细信息的查询方法,返回申请人姓名和课程名称
@@ -48,7 +48,7 @@ public class RsTrainingApprovalController extends BaseController {
/**
* 获取培训审批详细信息
*/
@SaCheckPermission("rs:training:approval:query")
@SaCheckPermission("rs:trainingapproval:query")
@GetMapping("/{approvalId}")
public R<RsTrainingApproval> getInfo(@PathVariable("approvalId") Long approvalId) {
return R.ok(trainingApprovalService.selectRsTrainingApprovalById(approvalId));
@@ -57,7 +57,7 @@ public class RsTrainingApprovalController extends BaseController {
/**
* 新增培训审批
*/
@SaCheckPermission("rs:training:approval:add")
@SaCheckPermission("rs:trainingapproval:add")
@Log(title = "培训审批", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping
@@ -68,7 +68,7 @@ public class RsTrainingApprovalController extends BaseController {
/**
* 修改培训审批
*/
@SaCheckPermission("rs:training:approval:edit")
@SaCheckPermission("rs:trainingapproval:edit")
@Log(title = "培训审批", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping
@@ -79,7 +79,7 @@ public class RsTrainingApprovalController extends BaseController {
/**
* 删除培训审批
*/
@SaCheckPermission("rs:training:approval:remove")
@SaCheckPermission("rs:trainingapproval:remove")
@Log(title = "培训审批", businessType = BusinessType.DELETE)
@DeleteMapping("/{approvalIds}")
public R<Void> remove(@PathVariable Long[] approvalIds) {
@@ -89,7 +89,7 @@ public class RsTrainingApprovalController extends BaseController {
/**
* 查询待我审批的列表
*/
@SaCheckPermission("rs:training:approval:process")
@SaCheckPermission("rs:trainingapproval:process")
@GetMapping("/pending")
public TableDataInfo<RsTrainingApproval> listPendingApprovals(RsTrainingApproval approval, PageQuery pageQuery) {
return trainingApprovalService.selectPagePendingApprovals(StpUtil.getLoginIdAsLong(), approval, pageQuery);
@@ -98,7 +98,7 @@ public class RsTrainingApprovalController extends BaseController {
/**
* 处理审批
*/
@SaCheckPermission("rs:training:approval:process")
@SaCheckPermission("rs:trainingapproval:process")
@Log(title = "培训审批", businessType = BusinessType.UPDATE)
@PutMapping("/process/{applicationId}/{status}")
public R<Void> processApproval(@PathVariable("applicationId") Long applicationId, @PathVariable("status") String status, @RequestBody(required = false) Map<String, String> params) {
@@ -109,7 +109,7 @@ public class RsTrainingApprovalController extends BaseController {
/**
* 查询申请审批历史
*/
@SaCheckPermission("rs:training:approval:query")
@SaCheckPermission("rs:trainingapproval:query")
@GetMapping("/history/{applicationId}")
public R<List<RsTrainingApproval>> getApprovalHistory(@PathVariable("applicationId") Long applicationId) {
System.out.println("查询审批历史记录 - applicationId: " + applicationId);
@@ -1,124 +0,0 @@
package org.dromara.rs.controller;
import java.util.List;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
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.rs.domain.RsTrainingCourse;
import org.dromara.rs.service.IRsTrainingCourseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaIgnore;
import jakarta.servlet.http.HttpServletResponse;
/**
* 培训课程控制器
*
* @author weizzzz
* @date 2025-07-16
*/
@RestController
@RequestMapping("/rs/training/course")
public class RsTrainingCourseController extends BaseController {
@Autowired
private IRsTrainingCourseService trainingCourseService;
/**
* 查询培训课程列表
*/
@SaCheckPermission("rs:training:course:list")
@GetMapping("/list")
public TableDataInfo<RsTrainingCourse> list(RsTrainingCourse course, PageQuery pageQuery) {
return trainingCourseService.selectPageRsTrainingCourseList(course, pageQuery);
}
/**
* 导出培训课程列表
*/
@SaCheckPermission("rs:training:course:export")
@Log(title = "培训课程", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, RsTrainingCourse course) {
List<RsTrainingCourse> list = trainingCourseService.selectRsTrainingCourseList(course);
ExcelUtil.exportExcel(list, "培训课程", RsTrainingCourse.class, response);
}
/**
* 获取培训课程详细信息
*/
@SaCheckPermission("rs:training:course:query")
@GetMapping("/{courseId}")
public R<RsTrainingCourse> getInfo(@PathVariable("courseId") Long courseId) {
return R.ok(trainingCourseService.selectRsTrainingCourseById(courseId));
}
/**
* 新增培训课程
*/
@SaCheckPermission("rs:training:course:add")
@Log(title = "培训课程", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping
public R<Void> add(@Validated @RequestBody RsTrainingCourse course) {
return toAjax(trainingCourseService.insertRsTrainingCourse(course));
}
/**
* 修改培训课程
*/
@SaCheckPermission("rs:training:course:edit")
@Log(title = "培训课程", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping
public R<Void> edit(@Validated @RequestBody RsTrainingCourse course) {
return toAjax(trainingCourseService.updateRsTrainingCourse(course));
}
/**
* 删除培训课程
*/
@SaCheckPermission("rs:training:course:remove")
@Log(title = "培训课程", businessType = BusinessType.DELETE)
@DeleteMapping("/{courseIds}")
public R<Void> remove(@PathVariable Long[] courseIds) {
return toAjax(trainingCourseService.deleteRsTrainingCourseByIds(courseIds));
}
/**
* 切换培训课程状态
*/
@SaCheckPermission("rs:training:course:edit")
@Log(title = "培训课程", businessType = BusinessType.UPDATE)
@PutMapping("/toggle/{courseId}")
public R<Void> toggleStatus(@PathVariable("courseId") Long courseId) {
return toAjax(trainingCourseService.toggleCourseStatus(courseId));
}
/**
* 获取可用培训课程列表(不分页)
*/
@SaIgnore
@GetMapping("/available")
public R<List<RsTrainingCourse>> getAvailableCourses() {
RsTrainingCourse course = new RsTrainingCourse();
course.setStatus("0"); // 只查询状态为正常的课程
return R.ok(trainingCourseService.selectRsTrainingCourseList(course));
}
}
@@ -7,6 +7,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.baomidou.mybatisplus.annotation.IdType;
import java.util.Date;
@@ -26,7 +27,7 @@ public class RsTrainingApplication extends BaseEntity {
/**
* 申请ID
*/
@TableId(value = "application_id")
@TableId(value = "application_id", type = IdType.ASSIGN_ID)
private Long applicationId;
/**
@@ -34,6 +35,11 @@ public class RsTrainingApplication extends BaseEntity {
*/
private Long courseId;
/**
* 培训名称
*/
private String courseName;
/**
* 申请人ID
*/
@@ -107,11 +113,6 @@ public class RsTrainingApplication extends BaseEntity {
*/
private Long createDept;
/**
* 自定义课程名称
*/
private String courseName;
/**
* 拒绝时间
*/
@@ -1,92 +0,0 @@
package org.dromara.rs.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
/**
* 培训课程对象 rs_training_course
*
* @author weizzzz
* @date 2025-07-16
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("rs_training_course")
public class RsTrainingCourse extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 课程ID
*/
@TableId(value = "course_id")
private Long courseId;
/**
* 课程名称
*/
private String title;
/**
* 课程类别
*/
private String category;
/**
* 课程描述
*/
private String description;
/**
* 开始时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date startTime;
/**
* 结束时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date endTime;
/**
* 学时数
*/
private Integer duration;
/**
* 培训地点
*/
private String location;
/**
* 最大参与人数
*/
private Integer maxParticipants;
/**
* 当前报名人数
*/
private Integer currentParticipants;
/**
* 状态(0正常 1结束)
*/
private String status;
/**
* 租户ID
*/
private String tenantId;
/** 非数据库字段 **/
@TableField(exist = false)
private Integer applicationCount;
}
@@ -7,6 +7,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.baomidou.mybatisplus.annotation.IdType;
import java.math.BigDecimal;
import java.util.Date;
@@ -27,7 +28,7 @@ public class RsTrainingRecord extends BaseEntity {
/**
* 记录ID
*/
@TableId(value = "record_id")
@TableId(value = "record_id", type = IdType.ASSIGN_ID)
private Long recordId;
private Long applicationId;
@@ -117,6 +118,11 @@ public class RsTrainingRecord extends BaseEntity {
private String tenantId;
private Long createDept;
/**
* 部门ID
*/
private Long deptId;
// 数据库字段 - 直接存储在培训记录表中
private String courseName;
private Integer duration;
@@ -1,14 +0,0 @@
package org.dromara.rs.mapper;
import org.dromara.rs.domain.RsTrainingCourse;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 培训课程Mapper接口
*
* @author weizzzz
* @date 2025-07-16
*/
public interface RsTrainingCourseMapper extends BaseMapperPlus<RsTrainingCourse, RsTrainingCourse> {
}
@@ -1,80 +0,0 @@
package org.dromara.rs.service;
import java.util.List;
import org.dromara.rs.domain.RsTrainingCourse;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
* 培训课程服务接口
*
* @author weizzzz
* @date 2025-07-16
*/
public interface IRsTrainingCourseService {
/**
* 查询培训课程列表
*
* @param course 培训课程
* @param pageQuery 分页参数
* @return 培训课程分页列表
*/
TableDataInfo<RsTrainingCourse> selectPageRsTrainingCourseList(RsTrainingCourse course, PageQuery pageQuery);
/**
* 查询培训课程列表
*
* @param course 培训课程
* @return 培训课程列表
*/
List<RsTrainingCourse> selectRsTrainingCourseList(RsTrainingCourse course);
/**
* 查询培训课程详细信息
*
* @param courseId 培训课程ID
* @return 培训课程
*/
RsTrainingCourse selectRsTrainingCourseById(Long courseId);
/**
* 新增培训课程
*
* @param course 培训课程
* @return 结果
*/
int insertRsTrainingCourse(RsTrainingCourse course);
/**
* 修改培训课程
*
* @param course 培训课程
* @return 结果
*/
int updateRsTrainingCourse(RsTrainingCourse course);
/**
* 批量删除培训课程
*
* @param courseIds 需要删除的培训课程ID数组
* @return 结果
*/
int deleteRsTrainingCourseByIds(Long[] courseIds);
/**
* 删除培训课程信息
*
* @param courseId 培训课程ID
* @return 结果
*/
int deleteRsTrainingCourseById(Long courseId);
/**
* 切换培训课程状态
*
* @param courseId 培训课程ID
* @return 结果
*/
int toggleCourseStatus(Long courseId);
}
@@ -1,30 +1,32 @@
package org.dromara.rs.service.impl;
import java.util.Arrays;
import java.util.List;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.DateUtils;
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;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.dromara.rs.mapper.RsTrainingApplicationMapper;
import org.dromara.rs.mapper.RsTrainingCourseMapper;
import org.dromara.rs.domain.RsTrainingApplication;
import org.dromara.rs.domain.RsTrainingCourse;
import org.dromara.rs.domain.RsTrainingRecord;
import org.dromara.rs.domain.bo.RsTrainingApplicationBo;
import java.util.Arrays;
import org.dromara.rs.mapper.RsTrainingApplicationMapper;
import org.dromara.rs.service.IRsTrainingApplicationService;
import org.dromara.rs.service.IRsTrainingRecordService;
import org.dromara.rs.domain.RsTrainingRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.dromara.common.core.utils.DateUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/**
* 培训申请服务实现类
@@ -33,29 +35,15 @@ import org.springframework.context.ApplicationContextAware;
* @date 2025-07-16
*/
@Service
public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationService, ApplicationContextAware {
@RequiredArgsConstructor
public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationService {
private static final Logger log = LoggerFactory.getLogger(RsTrainingApplicationServiceImpl.class);
@Autowired
private RsTrainingApplicationMapper trainingApplicationMapper;
private final RsTrainingApplicationMapper trainingApplicationMapper;
@Autowired
private RsTrainingCourseMapper trainingCourseMapper;
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* 获取培训记录服务(延迟获取以避免循环依赖)
*/
private IRsTrainingRecordService getRecordService() {
return applicationContext.getBean(IRsTrainingRecordService.class);
}
private IRsTrainingRecordService trainingRecordService;
/**
* 查询培训申请列表
@@ -85,9 +73,9 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
// 使用自定义分页查询获取列表数据
List<RsTrainingApplication> list = trainingApplicationMapper.selectRsTrainingApplicationListPage(application, offset, size);
// 处理自定义培训课程(courseId=0)的情况
// 处理自定义培训课程的情况
for (RsTrainingApplication app : list) {
if (app.getCourseId() != null && app.getCourseId() == 0 && (app.getCourseName() == null || app.getCourseName().isEmpty())) {
if (app.getCourseName() == null || app.getCourseName().isEmpty()) {
app.setCourseName("自定义培训");
}
}
@@ -194,7 +182,6 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int insertRsTrainingApplication(RsTrainingApplication application) {
try {
// 确保applicationId不为空
@@ -212,41 +199,18 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
}
// 处理自定义培训课程的情况(courseId = 0 或 null)
if (application.getCourseId() == null || application.getCourseId() == 0) {
// 对于自定义培训课程,不需要验证课程和更新课程报名人数
application.setCourseId(0L); // 确保courseId为0L而不是null
// 确保courseName不为空
if (application.getCourseName() == null || application.getCourseName().isEmpty()) {
application.setCourseName("自定义培训");
}
application.setCreateTime(DateUtils.getNowDate());
application.setApplyTime(DateUtils.getNowDate());
application.setStatus("0"); // 待审核状态
application.setTenantId("000000"); // 设置默认租户ID
return trainingApplicationMapper.insert(application);
// 确保courseName不为空
if (application.getCourseName() == null || application.getCourseName().isEmpty()) {
application.setCourseName("自定义培训");
}
// 1. 验证课程是否存在并可申请
RsTrainingCourse course = trainingCourseMapper.selectById(application.getCourseId());
if (course == null) {
return 0;
}
// 2. 检查是否已达到最大参与人数
if (course.getCurrentParticipants() >= course.getMaxParticipants()) {
return 0;
}
// 3. 更新课程当前报名人数
course.setCurrentParticipants(course.getCurrentParticipants() + 1);
trainingCourseMapper.updateById(course);
// 4. 保存申请信息
application.setCreateTime(DateUtils.getNowDate());
application.setApplyTime(DateUtils.getNowDate());
Date now = DateUtils.getNowDate();
application.setCreateTime(now);
application.setApplyTime(now);
application.setStatus("0"); // 待审核状态
application.setTenantId("000000"); // 设置默认租户ID
return trainingApplicationMapper.insert(application);
// 以上代码已处理所有情况
} catch (Exception e) {
e.printStackTrace();
throw e;
@@ -272,18 +236,13 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteRsTrainingApplicationByIds(Long[] applicationIds) {
// 查询所有要删除的申请
List<RsTrainingApplication> applications = trainingApplicationMapper.selectBatchIds(Arrays.asList(applicationIds));
// 对每个申请,减少对应课程的当前报名人数
for (RsTrainingApplication app : applications) {
RsTrainingCourse course = trainingCourseMapper.selectById(app.getCourseId());
if (course != null && course.getCurrentParticipants() > 0) {
course.setCurrentParticipants(course.getCurrentParticipants() - 1);
trainingCourseMapper.updateById(course);
}
// 培训课程功能已移除,无需更新课程报名人数
}
return trainingApplicationMapper.deleteBatchIds(Arrays.asList(applicationIds));
@@ -296,17 +255,11 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteRsTrainingApplicationById(Long applicationId) {
// 查询申请
RsTrainingApplication application = trainingApplicationMapper.selectById(applicationId);
if (application != null) {
// 减少课程的当前报名人数
RsTrainingCourse course = trainingCourseMapper.selectById(application.getCourseId());
if (course != null && course.getCurrentParticipants() > 0) {
course.setCurrentParticipants(course.getCurrentParticipants() - 1);
trainingCourseMapper.updateById(course);
}
// 培训课程功能已移除,无需更新课程报名人数
}
return trainingApplicationMapper.deleteById(applicationId);
@@ -319,23 +272,17 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int cancelApplication(Long applicationId) {
// 查询申请
RsTrainingApplication application = trainingApplicationMapper.selectById(applicationId);
if (application != null) {
// 只有待审核状态的申请才能取消
if ("0".equals(application.getStatus())) {
// 更新状态为已取消
application.setStatus("3");
application.setUpdateTime(DateUtils.getNowDate());
// 更新状态为已取消
application.setStatus("3");
application.setUpdateTime(DateUtils.getNowDate());
// 减少课程的当前报名人数
RsTrainingCourse course = trainingCourseMapper.selectById(application.getCourseId());
if (course != null && course.getCurrentParticipants() > 0) {
course.setCurrentParticipants(course.getCurrentParticipants() - 1);
trainingCourseMapper.updateById(course);
}
// 培训课程功能已移除,无需更新课程报名人数
return trainingApplicationMapper.updateById(application);
}
@@ -353,51 +300,28 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
@Override
public int updateApplicationStatus(Long applicationId, String status) {
RsTrainingApplication application = trainingApplicationMapper.selectById(applicationId);
if (application != null) {
application.setStatus(status);
application.setUpdateTime(DateUtils.getNowDate());
// 如果是拒绝,记录被拒绝时间
if ("2".equals(status)) {
application.setRejectTime(DateUtils.getNowDate());
}
if (application != null) {
application.setStatus(status);
Date now = DateUtils.getNowDate();
application.setUpdateTime(now);
// 如果是拒绝,记录被拒绝时间
if ("2".equals(status)) {
application.setRejectTime(now);
}
int result = trainingApplicationMapper.updateById(application);
// 同步到培训记录表:状态为"1已通过""3进行中""4已完成"时自动写入
if ("1".equals(status) || "3".equals(status) || "4".equals(status)) {
log.info("【同步培训记录】触发,applicationId={}, status={}", applicationId, status);
RsTrainingRecord exist = getRecordService().selectByApplicationId(applicationId);
if (exist == null) {
log.info("【同步培训记录】插入新记录,userId={}, courseId={}", application.getUserId(), application.getCourseId());
RsTrainingRecord record = new RsTrainingRecord();
// 设置记录ID(使用时间戳)
record.setRecordId(System.currentTimeMillis());
record.setApplicationId(application.getApplicationId());
record.setCourseId(application.getCourseId());
record.setUserId(application.getUserId());
record.setUserName(application.getUserName());
record.setDepartment(application.getDepartment());
record.setPosition(application.getPosition());
record.setReason(application.getReason());
record.setStatus(application.getStatus());
record.setApplyTime(application.getApplyTime());
record.setCurrentApprover(application.getCurrentApprover());
record.setLocation(application.getLocation());
record.setDuration(application.getDuration());
record.setStartTime(application.getStartTime());
record.setEndTime(application.getEndTime());
record.setCourseName(application.getCourseName());
record.setTenantId(application.getTenantId());
record.setCreateDept(application.getCreateDept());
record.setCreateBy(1L); // 默认系统用户
record.setCreateTime(DateUtils.getNowDate());
// 默认完成状态为进行中
record.setCompletionStatus("0");
getRecordService().insertRsTrainingRecord(record);
} else {
log.info("【同步培训记录】已存在,不重复插入,applicationId={}", applicationId);
// 如果申请通过,自动创建培训记录
if ("1".equals(status)) {
try {
createTrainingRecordFromApplication(application);
} catch (Exception e) {
log.error("创建培训记录失败: applicationId={}, error={}", applicationId, e.getMessage(), e);
// 不影响申请状态更新的结果
}
}
return result;
}
return 0;
@@ -411,7 +335,6 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int resubmitApplication(Long applicationId, RsTrainingApplication applicationData) {
// 查询原申请
RsTrainingApplication existingApp = trainingApplicationMapper.selectById(applicationId);
@@ -424,7 +347,7 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
return 0;
}
log.info("【重新提交申请】原申请状态: {}, 申请ID: {}", existingApp.getStatus(), applicationId);
// 更新申请数据
if (applicationData.getReason() != null) {
@@ -446,13 +369,74 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
existingApp.setCourseName(applicationData.getCourseName());
}
// 重置为待审核状态
existingApp.setStatus("0");
existingApp.setUpdateTime(DateUtils.getNowDate());
existingApp.setApplyTime(DateUtils.getNowDate()); // 更新申请时间
// 重置为待审核状态
existingApp.setStatus("0");
Date now = DateUtils.getNowDate();
existingApp.setUpdateTime(now);
existingApp.setApplyTime(now); // 更新申请时间
log.info("【重新提交申请】更新为待审核状态");
return trainingApplicationMapper.updateById(existingApp);
}
/**
* 根据培训申请创建培训记录
*
* @param application 培训申请
*/
private void createTrainingRecordFromApplication(RsTrainingApplication application) {
try {
// 检查是否已存在培训记录
RsTrainingRecord existingRecord = trainingRecordService.selectByApplicationId(application.getApplicationId());
if (existingRecord != null) {
log.info("培训记录已存在,不重复创建: applicationId={}, recordId={}",
application.getApplicationId(), existingRecord.getRecordId());
return;
}
// 创建新的培训记录
RsTrainingRecord record = new RsTrainingRecord();
record.setApplicationId(application.getApplicationId());
record.setCourseId(application.getCourseId());
record.setUserId(application.getUserId());
record.setUserName(application.getUserName());
record.setDepartment(application.getDepartment());
record.setPosition(application.getPosition());
record.setReason(application.getReason());
// 添加缺失的字段设置
record.setCourseName(application.getCourseName());
record.setDuration(application.getDuration());
record.setLocation(application.getLocation());
record.setStatus("3"); // 3=进行中
record.setApplyTime(application.getApplyTime());
record.setStartTime(application.getStartTime());
record.setEndTime(application.getEndTime());
record.setCompletionStatus("0"); // 0=进行中
// 设置基础字段 - 修复字段缺失问题
Date now = DateUtils.getNowDate();
record.setCreateTime(now);
record.setUpdateTime(now);
// 设置租户和用户信息
record.setTenantId(application.getTenantId() != null ? application.getTenantId() : "000000");
record.setCreateBy(application.getUserId()); // 创建者设为申请人
record.setUpdateBy(application.getUserId()); // 更新者设为申请人
record.setCreateDept(application.getCreateDept() != null ? application.getCreateDept() : 1L); // 使用createDept,如果没有则设为默认值
record.setDeptId(application.getCreateDept() != null ? application.getCreateDept() : 1L); // 设置部门ID
int result = trainingRecordService.insertRsTrainingRecord(record);
if (result > 0) {
log.info("培训记录创建成功: applicationId={}, recordId={}",
application.getApplicationId(), record.getRecordId());
} else {
log.error("培训记录创建失败: applicationId={}", application.getApplicationId());
}
} catch (Exception e) {
log.error("创建培训记录时发生异常: applicationId={}, error={}",
application.getApplicationId(), e.getMessage(), e);
throw e;
}
}
}
@@ -1,138 +0,0 @@
package org.dromara.rs.service.impl;
import java.util.Arrays;
import java.util.List;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.dromara.rs.mapper.RsTrainingCourseMapper;
import org.dromara.rs.domain.RsTrainingCourse;
import org.dromara.rs.service.IRsTrainingCourseService;
/**
* 培训课程服务实现类
*
* @author weizzzz
* @date 2025-07-16
*/
@Service
public class RsTrainingCourseServiceImpl implements IRsTrainingCourseService {
@Autowired
private RsTrainingCourseMapper trainingCourseMapper;
/**
* 查询培训课程列表
*
* @param course 培训课程
* @param pageQuery 分页参数
* @return 培训课程分页列表
*/
@Override
public TableDataInfo<RsTrainingCourse> selectPageRsTrainingCourseList(RsTrainingCourse course, PageQuery pageQuery) {
com.baomidou.mybatisplus.extension.plugins.pagination.Page<RsTrainingCourse> page = trainingCourseMapper.selectPage(pageQuery.build(), buildQueryWrapper(course));
return TableDataInfo.build(page);
}
/**
* 查询培训课程列表
*
* @param course 培训课程
* @return 培训课程列表
*/
@Override
public List<RsTrainingCourse> selectRsTrainingCourseList(RsTrainingCourse course) {
return trainingCourseMapper.selectList(buildQueryWrapper(course));
}
/**
* 查询培训课程详细信息
*
* @param courseId 培训课程ID
* @return 培训课程
*/
@Override
public RsTrainingCourse selectRsTrainingCourseById(Long courseId) {
return trainingCourseMapper.selectById(courseId);
}
/**
* 新增培训课程
*
* @param course 培训课程
* @return 结果
*/
@Override
public int insertRsTrainingCourse(RsTrainingCourse course) {
course.setCreateTime(DateUtils.getNowDate());
return trainingCourseMapper.insert(course);
}
/**
* 修改培训课程
*
* @param course 培训课程
* @return 结果
*/
@Override
public int updateRsTrainingCourse(RsTrainingCourse course) {
course.setUpdateTime(DateUtils.getNowDate());
return trainingCourseMapper.updateById(course);
}
/**
* 批量删除培训课程
*
* @param courseIds 需要删除的培训课程ID数组
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteRsTrainingCourseByIds(Long[] courseIds) {
return trainingCourseMapper.deleteBatchIds(Arrays.asList(courseIds));
}
/**
* 删除培训课程信息
*
* @param courseId 培训课程ID
* @return 结果
*/
@Override
public int deleteRsTrainingCourseById(Long courseId) {
return trainingCourseMapper.deleteById(courseId);
}
/**
* 切换培训课程状态
*
* @param courseId 培训课程ID
* @return 结果
*/
@Override
public int toggleCourseStatus(Long courseId) {
RsTrainingCourse course = trainingCourseMapper.selectById(courseId);
if (course != null) {
// 切换状态:0正常 -> 1停用,或 1停用 -> 0正常
course.setStatus("0".equals(course.getStatus()) ? "1" : "0");
course.setUpdateTime(DateUtils.getNowDate());
return trainingCourseMapper.updateById(course);
}
return 0;
}
/**
* 构建查询条件
*/
private com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<RsTrainingCourse> buildQueryWrapper(RsTrainingCourse course) {
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<RsTrainingCourse> lqw = new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>();
lqw.like(course.getTitle() != null, RsTrainingCourse::getTitle, course.getTitle());
lqw.eq(course.getCategory() != null, RsTrainingCourse::getCategory, course.getCategory());
lqw.eq(course.getStatus() != null, RsTrainingCourse::getStatus, course.getStatus());
return lqw;
}
}
@@ -15,8 +15,7 @@ import org.dromara.rs.domain.RsTrainingRecord;
import org.dromara.rs.service.IRsTrainingRecordService;
import org.dromara.rs.domain.RsTrainingApplication;
import org.dromara.rs.service.IRsTrainingApplicationService;
import org.dromara.rs.domain.RsTrainingCourse;
import org.dromara.rs.service.IRsTrainingCourseService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.lang3.StringUtils;
@@ -37,8 +36,7 @@ public class RsTrainingRecordServiceImpl implements IRsTrainingRecordService, Ap
@Autowired
private RsTrainingRecordMapper trainingRecordMapper;
@Autowired
private IRsTrainingCourseService courseService;
private ApplicationContext applicationContext;
@@ -303,25 +301,11 @@ public class RsTrainingRecordServiceImpl implements IRsTrainingRecordService, Ap
}
}
// 如果申请表中也没有,尝试从课程表获取
if ((StringUtils.isEmpty(record.getCourseName()) || StringUtils.isEmpty(record.getLocation()) || record.getDuration() == null)
&& record.getCourseId() != null) {
RsTrainingCourse course = courseService.selectRsTrainingCourseById(record.getCourseId());
if (course != null) {
if (StringUtils.isEmpty(record.getCourseName())) {
record.setCourseName(course.getTitle());
needUpdate = true;
}
if (StringUtils.isEmpty(record.getLocation())) {
record.setLocation(course.getLocation());
needUpdate = true;
}
if (record.getDuration() == null) {
record.setDuration(course.getDuration());
needUpdate = true;
}
}
// 培训课程功能已移除,不再从课程表获取信息
// 如果记录中仍然缺少信息,设置默认值
if (StringUtils.isEmpty(record.getCourseName())) {
record.setCourseName("自定义培训");
needUpdate = true;
}
// 更新记录
@@ -1,62 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.rs.mapper.RsTrainingCourseMapper">
<resultMap type="RsTrainingCourse" id="RsTrainingCourseResult">
<id property="courseId" column="course_id"/>
<result property="title" column="title"/>
<result property="category" column="category"/>
<result property="description" column="description"/>
<result property="startTime" column="start_time"/>
<result property="endTime" column="end_time"/>
<result property="duration" column="duration"/>
<result property="location" column="location"/>
<result property="maxParticipants" column="max_participants"/>
<result property="currentParticipants" column="current_participants"/>
<result property="status" column="status"/>
<result property="tenantId" column="tenant_id"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="remark" column="remark"/>
<result property="applicationCount" column="application_count"/>
</resultMap>
<sql id="selectRsTrainingCourseVo">
select c.course_id, c.title, c.category, c.description, c.start_time, c.end_time, c.duration,
c.location, c.max_participants, c.current_participants, c.status, c.tenant_id, c.create_by,
c.create_time, c.update_by, c.update_time, c.remark,
(select count(1) from rs_training_application a where a.course_id = c.course_id and a.deleted = '0' and a.tenant_id = c.tenant_id) as application_count
from rs_training_course c
</sql>
<select id="selectRsTrainingCourseList" parameterType="RsTrainingCourse" resultMap="RsTrainingCourseResult">
<include refid="selectRsTrainingCourseVo"/>
<where>
c.deleted = '0'
<if test="title != null and title != ''">
AND c.title like concat('%', #{title}, '%')
</if>
<if test="category != null and category != ''">
AND c.category = #{category}
</if>
<if test="startTime != null">
AND c.start_time = #{startTime}
</if>
<if test="endTime != null">
AND c.end_time = #{endTime}
</if>
<if test="status != null and status != ''">
AND c.status = #{status}
</if>
<if test="tenantId != null and tenantId != ''">
AND c.tenant_id = #{tenantId}
</if>
</where>
order by c.create_time desc
</select>
</mapper>
@@ -36,6 +36,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="courseName" column="course_name"/>
<result property="duration" column="duration"/>
<result property="location" column="location"/>
<result property="deptId" column="dept_id"/>
</resultMap>
<sql id="selectRsTrainingRecordVo">
@@ -43,7 +44,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
r.completion_status, r.completion_time, r.score, r.certificate_path, r.certificate_number, r.attachment_ids,
r.feedback, r.tenant_id, r.create_dept, r.create_by, r.create_time, r.update_by, r.update_time,
r.department, r.position, r.reason, r.status, r.apply_time, r.current_approver, r.start_time, r.end_time,
r.course_name, r.duration, r.location
r.course_name, r.duration, r.location, r.dept_id
from rs_training_record r
</sql>
@@ -51,6 +52,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<include refid="selectRsTrainingRecordVo"/>
<where>
r.deleted = '0'
<if test="applicationId != null">and r.application_id = #{applicationId}</if>
<if test="courseId != null">and r.course_id = #{courseId}</if>
<if test="courseName != null and courseName != ''">and r.course_name like concat('%', #{courseName}, '%')</if>
<if test="userId != null">and r.user_id = #{userId}</if>
@@ -74,12 +76,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
record_id, application_id, course_id, user_id, user_name, completion_status, completion_time, score,
certificate_path, certificate_number, attachment_ids, feedback, location, duration, apply_time,
status, course_name, department, position, reason, current_approver, start_time, end_time,
tenant_id, create_dept, create_by, create_time, deleted
tenant_id, create_dept, create_by, create_time, deleted, dept_id
) VALUES (
#{recordId}, #{applicationId}, #{courseId}, #{userId}, #{userName}, #{completionStatus}, #{completionTime}, #{score},
#{certificatePath}, #{certificateNumber}, #{attachmentIds}, #{feedback}, #{location}, #{duration}, #{applyTime},
#{status}, #{courseName}, #{department}, #{position}, #{reason}, #{currentApprover}, #{startTime}, #{endTime},
#{tenantId}, #{createDept}, #{createBy}, #{createTime}, '0'
#{tenantId}, #{createDept}, #{createBy}, #{createTime}, '0', #{deptId}
)
</insert>
@@ -109,7 +111,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="startTime != null">start_time = #{startTime},</if>
<if test="endTime != null">end_time = #{endTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime}</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="deptId != null">dept_id = #{deptId},</if>
</set>
where record_id = #{recordId}
</update>
@@ -34,7 +34,7 @@ create table rs_training_course (
drop table if exists rs_training_application;
create table rs_training_application (
application_id bigint(20) not null comment '申请ID',
course_id bigint(20) not null comment '课程ID',
course_name varchar(100) not null comment '培训名称',
user_id bigint(20) not null comment '申请人ID',
user_name varchar(50) not null comment '申请人姓名',
department varchar(50) not null comment '所属部门',
@@ -43,7 +43,13 @@ create table rs_training_application (
status char(1) default '0' comment '状态(0待审核 1已通过 2已拒绝 3进行中 4已完成)',
apply_time datetime comment '申请时间',
current_approver varchar(50) comment '当前审批人',
location varchar(200) comment '培训地点',
duration int(11) comment '学时',
start_time datetime comment '开始时间',
end_time datetime comment '结束时间',
tenant_id varchar(20) default '000000' comment '租户编号',
create_dept bigint(20) comment '创建部门',
reject_time datetime comment '拒绝时间',
create_by bigint(20) comment '创建者',
create_time datetime comment '创建时间',
update_by bigint(20) comment '更新者',
@@ -189,6 +189,16 @@ public class ZsjyGraduateInfo extends TenantEntity implements Serializable {
*/
private String workCity;
/**
* 工作地点
*/
private String workLocation;
/**
* 完整地址
*/
private String fullAddress;
/**
* 月薪范围 (1-3000以下 2-3000-5000 3-5000-8000 4-8000-12000 5-12000以上)
*/
@@ -220,6 +220,16 @@ public class ZsjyGraduateInfoBo extends BaseEntity {
*/
private String workCity;
/**
* 工作地点
*/
private String workLocation;
/**
* 完整地址
*/
private String fullAddress;
/**
* 月薪范围 (1-3000以下 2-3000-5000 3-5000-8000 4-8000-12000 5-12000以上)
*/
@@ -43,7 +43,10 @@ public class ZsjyGraduateInfoServiceImpl implements IZsjyGraduateInfoService {
*/
@Override
public ZsjyGraduateInfoVo queryById(Long id) {
return baseMapper.selectVoById(id);
ZsjyGraduateInfoBo bo = new ZsjyGraduateInfoBo();
bo.setId(id);
List<ZsjyGraduateInfoVo> list = baseMapper.selectGraduateInfoVoList(bo);
return list.isEmpty() ? null : list.get(0);
}
/**
@@ -55,8 +58,8 @@ public class ZsjyGraduateInfoServiceImpl implements IZsjyGraduateInfoService {
*/
@Override
public TableDataInfo<ZsjyGraduateInfoVo> queryPageList(ZsjyGraduateInfoBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<ZsjyGraduateInfo> lqw = buildQueryWrapper(bo);
Page<ZsjyGraduateInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
Page<ZsjyGraduateInfoVo> page = pageQuery.build();
IPage<ZsjyGraduateInfoVo> result = baseMapper.selectGraduateInfoPageList(page, bo);
return TableDataInfo.build(result);
}
@@ -68,8 +71,7 @@ public class ZsjyGraduateInfoServiceImpl implements IZsjyGraduateInfoService {
*/
@Override
public List<ZsjyGraduateInfoVo> queryList(ZsjyGraduateInfoBo bo) {
LambdaQueryWrapper<ZsjyGraduateInfo> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
return baseMapper.selectGraduateInfoVoList(bo);
}
/**
@@ -211,6 +213,24 @@ public class ZsjyGraduateInfoServiceImpl implements IZsjyGraduateInfoService {
throw new RuntimeException("升学状态下,升学院校不能为空");
}
}
// 自动设置组合字段
// 设置工作地点(工作省份 + 工作城市)
if (StringUtils.isNotBlank(entity.getWorkProvince()) || StringUtils.isNotBlank(entity.getWorkCity())) {
String workLocation = StringUtils.defaultString(entity.getWorkProvince(), "") +
StringUtils.defaultString(entity.getWorkCity(), "");
entity.setWorkLocation(workLocation);
}
// 设置完整地址(省份 + 城市 + 区县 + 详细地址)
if (StringUtils.isNotBlank(entity.getProvince()) || StringUtils.isNotBlank(entity.getCity()) ||
StringUtils.isNotBlank(entity.getDistrict()) || StringUtils.isNotBlank(entity.getDetailAddress())) {
String fullAddress = StringUtils.defaultString(entity.getProvince(), "") +
StringUtils.defaultString(entity.getCity(), "") +
StringUtils.defaultString(entity.getDistrict(), "") +
StringUtils.defaultString(entity.getDetailAddress(), "");
entity.setFullAddress(fullAddress);
}
}
/**
@@ -25,7 +25,6 @@ import org.dromara.zsjy.service.IZsjyRecruitmentPosterService;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.UUID;
/**
* 招聘海报Service业务层处理
@@ -138,14 +137,23 @@ public class ZsjyRecruitmentPosterServiceImpl implements IZsjyRecruitmentPosterS
throw new ServiceException("企业信息不存在");
}
// 生成海报路径
String posterPath = generatePosterPath("enterprise");
// 检查是否已存在企业海报
LambdaQueryWrapper<ZsjyRecruitmentPoster> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(ZsjyRecruitmentPoster::getEnterpriseId, enterpriseId)
.eq(ZsjyRecruitmentPoster::getPosterType, "1")
.isNull(ZsjyRecruitmentPoster::getPositionId);
ZsjyRecruitmentPoster existingPoster = baseMapper.selectOne(queryWrapper);
// 创建海报记录
if (existingPoster != null) {
// 如果已存在,直接返回现有记录
return baseMapper.selectVoById(existingPoster.getId());
}
// 创建海报记录(不设置posterPath,等待用户上传)
ZsjyRecruitmentPosterBo bo = new ZsjyRecruitmentPosterBo();
bo.setEnterpriseId(enterpriseId);
bo.setPosterPath(posterPath);
bo.setPosterType("1"); // 企业海报
// 注意:不设置posterPath,让用户后续上传图片
// 保存海报记录
insertByBo(bo);
@@ -169,15 +177,23 @@ public class ZsjyRecruitmentPosterServiceImpl implements IZsjyRecruitmentPosterS
throw new ServiceException("岗位信息不存在");
}
// 生成海报路径
String posterPath = generatePosterPath("position");
// 检查是否已存在岗位海报
LambdaQueryWrapper<ZsjyRecruitmentPoster> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(ZsjyRecruitmentPoster::getPositionId, positionId)
.eq(ZsjyRecruitmentPoster::getPosterType, "2");
ZsjyRecruitmentPoster existingPoster = baseMapper.selectOne(queryWrapper);
// 创建海报记录
if (existingPoster != null) {
// 如果已存在,直接返回现有记录
return baseMapper.selectVoById(existingPoster.getId());
}
// 创建海报记录(不设置posterPath,等待用户上传)
ZsjyRecruitmentPosterBo bo = new ZsjyRecruitmentPosterBo();
bo.setEnterpriseId(position.getEnterpriseId());
bo.setPositionId(positionId);
bo.setPosterPath(posterPath);
bo.setPosterType("2"); // 岗位海报
// 注意:不设置posterPath,让用户后续上传图片
// 保存海报记录
insertByBo(bo);
@@ -187,16 +203,5 @@ public class ZsjyRecruitmentPosterServiceImpl implements IZsjyRecruitmentPosterS
return vo;
}
/**
* 生成海报路径
*
* @param type 类型
* @return 海报路径
*/
private String generatePosterPath(String type) {
// 基于类型和UUID生成唯一的海报文件名
String fileName = type + "_" + UUID.randomUUID().toString().replace("-", "") + ".png";
// 海报存储路径
return "/profile/upload/poster/" + fileName;
}
}
@@ -139,6 +139,9 @@
<select id="selectGraduateInfoPageList" resultMap="ZsjyGraduateInfoVoResult">
<include refid="selectZsjyGraduateInfoVo"/>
<if test="bo != null">
<if test="bo.id != null">
and g.id = #{bo.id}
</if>
<if test="bo.studentNo != null and bo.studentNo != ''">
and g.student_no like concat('%', #{bo.studentNo}, '%')
</if>
@@ -239,6 +242,9 @@
<!-- 查询毕业生信息列表(非分页) -->
<select id="selectGraduateInfoVoList" parameterType="org.dromara.zsjy.domain.bo.ZsjyGraduateInfoBo" resultMap="ZsjyGraduateInfoVoResult">
<include refid="selectZsjyGraduateInfoVo"/>
<if test="id != null">
and g.id = #{id}
</if>
<if test="studentNo != null and studentNo != ''">
and g.student_no like concat('%', #{studentNo}, '%')
</if>
+11
View File
@@ -329,3 +329,14 @@ export const getCompatibleTypes = (id: string | number): AxiosPromise<Scholarshi
method: 'get'
});
};
/**
* 获取奖学金类型的互斥关系显示文本
* @param id 奖学金类型ID
*/
export const getExclusiveRelationDisplay = (id: string | number): AxiosPromise<string> => {
return request({
url: `/jcbt/scholarshipType/${id}/exclusiveRelationDisplay`,
method: 'get'
});
};
+3
View File
@@ -107,6 +107,8 @@ export interface RecordQuery {
courseName?: string;
userId?: number | string;
userName?: string;
location?: string;
department?: string;
completionStatus?: string;
params?: Record<string, any>;
}
@@ -119,6 +121,7 @@ export interface RecordVO {
courseName: string;
userId: number | string;
userName: string;
location?: string;
department?: string;
completionStatus: string;
score?: string;
+2 -1
View File
@@ -1,12 +1,13 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { GraduateInfoVO, GraduateInfoForm, GraduateInfoQuery, GraduateStatisticsVO } from '@/api/zsjy/types';
import { TableDataInfo } from '@/api/types';
/**
* 查询毕业生信息列表
* @param query 查询参数
*/
export const listGraduateInfo = (query?: GraduateInfoQuery): AxiosPromise<GraduateInfoVO[]> => {
export const listGraduateInfo = (query?: GraduateInfoQuery): AxiosPromise<TableDataInfo<GraduateInfoVO[]>> => {
return request({
url: '/zsjy/graduate/list',
method: 'get',
+2
View File
@@ -654,6 +654,7 @@ export interface GraduateInfoForm {
city?: string;
district?: string;
detailAddress?: string;
fullAddress?: string;
postalCode?: string;
employmentStatus?: string;
employmentCompany?: string;
@@ -661,6 +662,7 @@ export interface GraduateInfoForm {
jobPosition?: string;
workProvince?: string;
workCity?: string;
workLocation?: string;
salaryRange?: string;
furtherSchool?: string;
furtherMajor?: string;
@@ -87,24 +87,24 @@
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="indicatorName" label="指标名称" min-width="150" align="center" :show-overflow-tooltip="true" />
<el-table-column prop="description" label="指标描述" min-width="180" align="center" :show-overflow-tooltip="true" />
<el-table-column prop="score" label="指标分值" width="80" align="center">
<el-table-column prop="score" label="指标分值" min-width="100" align="center">
<template #default="scope">
<span>{{ scope.row.score || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="自评分数" width="80" align="center">
<el-table-column label="自评分数" min-width="100" align="center">
<template #default="scope">
<span>{{ scope.row.selfScore || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="自评意见" width="180" align="center">
<el-table-column label="自评意见" min-width="180" align="center">
<template #default="scope">
<el-tooltip :content="scope.row.selfComment || '无'" placement="top" :disabled="!scope.row.selfComment">
<span class="text-ellipsis">{{ scope.row.selfComment || '无' }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="上级评分" width="100" align="center">
<el-table-column label="上级评分" min-width="120" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.superiorScore"
@@ -118,7 +118,7 @@
/>
</template>
</el-table-column>
<el-table-column label="审批意见" width="200" align="center">
<el-table-column label="审批意见" min-width="200" align="center">
<template #default="scope">
<el-input
v-model="scope.row.superiorComment"
@@ -204,27 +204,27 @@
<span>特色工作(加20分)</span>
</div>
</template>
<el-table border :data="specialWorkScores" style="width: 100%" class="special-work-table">
<el-table border :data="specialWorkScores" style="width: 100%" max-height="200" class="special-work-table">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="indicatorName" label="特色工作项目" min-width="180" align="center" :show-overflow-tooltip="true" />
<el-table-column label="自评分数" width="80" align="center">
<el-table-column label="自评分数" min-width="100" align="center">
<template #default="scope">
<span>{{ scope.row.score || scope.row.selfScore || 0 }}</span>
<span>{{ scope.row.selfScore || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="自评意见" width="180" align="center">
<el-table-column label="自评意见" min-width="200" align="center">
<template #default="scope">
<el-tooltip :content="scope.row.selfComment || '无'" placement="top" :disabled="!scope.row.selfComment">
<span class="text-ellipsis">{{ scope.row.selfComment || '无' }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="上级评分" width="100" align="center">
<el-table-column label="上级评分" min-width="120" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.superiorScore"
:min="0"
:max="20"
:max="scope.row.score || 20"
:precision="2"
controls-position="right"
style="width: 100%"
@@ -233,7 +233,7 @@
/>
</template>
</el-table-column>
<el-table-column label="审批意见" width="200" align="center">
<el-table-column label="审批意见" min-width="200" align="center">
<template #default="scope">
<el-input
v-model="scope.row.superiorComment"
@@ -6,19 +6,19 @@
<el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" label-width="100px">
<el-row :gutter="20">
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="计划名称" prop="planName">
<el-input v-model="queryParams.planName" placeholder="请输入计划名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="考核方案" prop="schemeName">
<el-select v-model="queryParams.schemeName" placeholder="请选择考核方案" clearable style="width: 100%">
<el-option v-for="item in schemeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="审批状态" prop="auditStatus">
<el-select v-model="queryParams.auditStatus" placeholder="请选择审批状态" clearable style="width: 100%">
<el-option label="待审核" value="0" />
@@ -27,17 +27,21 @@
</el-select>
</el-form-item>
</el-col>
<el-col :lg="6" :md="12" :sm="24">
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="班级名称" prop="className">
<el-input v-model="queryParams.className" placeholder="请输入班级名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="班主任姓名" prop="teacherName">
<el-input v-model="queryParams.teacherName" placeholder="请输入班主任姓名" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :lg="6" :md="24" :sm="24" style="text-align: right">
</el-row>
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -59,21 +63,21 @@
<div class="table-container">
<el-table v-loading="loading" border :data="approvalList" @sort-change="sortChange">
<el-table-column label="计划名称" align="center" prop="planName" :show-overflow-tooltip="true" />
<el-table-column label="考核方案" align="center" prop="schemeName" :show-overflow-tooltip="true" />
<el-table-column label="学年学期" align="center" width="120">
<el-table-column label="计划名称" align="center" prop="planName" min-width="180" :show-overflow-tooltip="true" />
<el-table-column label="考核方案" align="center" prop="schemeName" min-width="180" :show-overflow-tooltip="true" />
<el-table-column label="学年学期" align="center" min-width="140">
<template #default="scope">
<span>{{ scope.row.academicYear }}{{ scope.row.semester === '1' ? '上' : '下' }}学期</span>
<span>{{ scope.row.academicYear }}{{ getSemesterLabel(scope.row.semester) }}</span>
</template>
</el-table-column>
<el-table-column label="班级名称" align="center" prop="className" :show-overflow-tooltip="true" />
<el-table-column label="班主任姓名" align="center" prop="evaluatedUserName" :show-overflow-tooltip="true" />
<el-table-column label="申请时间" align="center" width="180" sortable="custom" prop="submitTime">
<el-table-column label="班级名称" align="center" prop="className" min-width="120" :show-overflow-tooltip="true" />
<el-table-column label="班主任姓名" align="center" prop="evaluatedUserName" min-width="120" :show-overflow-tooltip="true" />
<el-table-column label="申请时间" align="center" min-width="180" sortable="custom" prop="submitTime">
<template #default="scope">
<span>{{ parseTime(scope.row.submitTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="审批状态" align="center" width="120">
<el-table-column label="审批状态" align="center" min-width="120">
<template #default="scope">
<el-tag v-if="!scope.row.auditStatus || scope.row.auditStatus === '0'" type="warning" effect="plain">待审核</el-tag>
<el-tag v-else-if="scope.row.auditStatus === '1'" type="success" effect="plain">审核通过</el-tag>
@@ -120,6 +124,13 @@ import ApprovalDialog from './approvalDialog.vue';
import ViewDialog from './viewDialog.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { student_semester } = toRefs<any>(proxy?.useDict('student_semester'));
// 获取学期标签
const getSemesterLabel = (semester: string) => {
const semesterDict = student_semester.value?.find((item: any) => item.value === semester);
return semesterDict ? semesterDict.label : '';
};
const approvalList = ref<AssessmentApprovalVO[]>([]);
const loading = ref(true);
@@ -248,4 +259,15 @@ onMounted(() => {
background-color: #fff;
margin-top: 0px !important;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.search-btn-container .el-form-item {
margin-bottom: 0 !important;
}
</style>
@@ -85,29 +85,29 @@
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="indicatorName" label="指标名称" min-width="150" align="center" :show-overflow-tooltip="true" />
<el-table-column prop="description" label="指标描述" min-width="180" align="center" :show-overflow-tooltip="true" />
<el-table-column prop="score" label="指标分值" width="80" align="center">
<el-table-column prop="score" label="指标分值" min-width="100" align="center">
<template #default="scope">
<span>{{ scope.row.score || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="自评分数" width="80" align="center">
<el-table-column label="自评分数" min-width="100" align="center">
<template #default="scope">
<span>{{ scope.row.selfScore || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="自评意见" width="180" align="center">
<el-table-column label="自评意见" min-width="200" align="center">
<template #default="scope">
<el-tooltip :content="scope.row.selfComment || '无'" placement="top" :disabled="!scope.row.selfComment">
<span class="text-ellipsis">{{ scope.row.selfComment || '无' }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="上级评分" width="80" align="center">
<el-table-column label="上级评分" min-width="100" align="center">
<template #default="scope">
<span>{{ scope.row.superiorScore || '-' }}</span>
<span>{{ scope.row.superiorScore || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="审批意见" width="180" align="center">
<el-table-column label="审批意见" min-width="200" align="center">
<template #default="scope">
<el-tooltip :content="scope.row.superiorComment || '无'" placement="top" :disabled="!scope.row.superiorComment">
<span class="text-ellipsis">{{ scope.row.superiorComment || '无' }}</span>
@@ -175,24 +175,24 @@
<el-table border :data="specialWorkScores" style="width: 100%" max-height="200" class="special-work-table">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="indicatorName" label="特色工作项目" min-width="180" align="center" :show-overflow-tooltip="true" />
<el-table-column label="自评分数" width="80" align="center">
<el-table-column label="自评分数" min-width="100" align="center">
<template #default="scope">
<span>{{ scope.row.selfScore || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="自评意见" width="180" align="center">
<el-table-column label="自评意见" min-width="200" align="center">
<template #default="scope">
<el-tooltip :content="scope.row.selfComment || '无'" placement="top" :disabled="!scope.row.selfComment">
<span class="text-ellipsis">{{ scope.row.selfComment || '无' }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="上级评分" width="80" align="center">
<el-table-column label="上级评分" min-width="100" align="center">
<template #default="scope">
<span>{{ scope.row.superiorScore || '-' }}</span>
<span>{{ scope.row.superiorScore || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="审批意见" width="180" align="center">
<el-table-column label="审批意见" min-width="200" align="center">
<template #default="scope">
<el-tooltip :content="scope.row.superiorComment || '无'" placement="top" :disabled="!scope.row.superiorComment">
<span class="text-ellipsis">{{ scope.row.superiorComment || '无' }}</span>
@@ -5,24 +5,26 @@
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<el-row :gutter="20">
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="指标名称" prop="indicatorName">
<el-input v-model="queryParams.indicatorName" placeholder="请输入指标名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="指标编码" prop="indicatorCode">
<el-input v-model="queryParams.indicatorCode" placeholder="请输入指标编码" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="指标类型" prop="indicatorType">
<el-select v-model="queryParams.indicatorType" placeholder="请选择指标类型" clearable style="width: 100%">
<el-option v-for="item in indicatorTypeOptions" :key="item.id" :label="item.indicatorTypeName" :value="item.indicatorTypeCode" />
</el-select>
</el-form-item>
</el-col>
<el-col :lg="6" :md="24" :sm="24">
</el-row>
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -54,17 +56,17 @@
<div class="table-container">
<el-table v-loading="loading" border :data="assessmentIndicatorList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="指标编码" align="center" prop="indicatorCode" width="140" show-overflow-tooltip />
<el-table-column label="指标编码" align="center" prop="indicatorCode" min-width="140" show-overflow-tooltip />
<el-table-column label="指标名称" align="center" prop="indicatorName" min-width="180" show-overflow-tooltip />
<el-table-column label="指标类型" align="center" prop="indicatorType" width="120">
<el-table-column label="指标类型" align="center" prop="indicatorType" min-width="150" show-overflow-tooltip>
<template #default="scope">
{{ getIndicatorTypeName(scope.row.indicatorType) }}
</template>
</el-table-column>
<el-table-column label="最高分值" align="center" prop="maxScore" width="100" />
<el-table-column label="分值" align="center" prop="score" width="80" />
<el-table-column label="排序" align="center" prop="sortOrder" width="80" />
<el-table-column label="状态" align="center" prop="status" width="80">
<el-table-column label="最高分值" align="center" prop="maxScore" min-width="100" />
<el-table-column label="分值" align="center" prop="score" min-width="80" />
<el-table-column label="排序" align="center" prop="sortOrder" min-width="80" />
<el-table-column label="状态" align="center" prop="status" min-width="80">
<template #default="scope">
<dict-tag :options="sys_item_enable" :value="scope.row.status" />
</template>
@@ -404,4 +406,15 @@ onMounted(() => {
background-color: #fff;
margin-top: 0px !important;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.search-btn-container .el-form-item {
margin-bottom: 0 !important;
}
</style>
@@ -5,24 +5,26 @@
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<el-row :gutter="20">
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="指标类型编码" prop="indicatorTypeCode">
<el-input v-model="queryParams.indicatorTypeCode" placeholder="请输入指标类型编码" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="指标类型名称" prop="indicatorTypeName">
<el-input v-model="queryParams.indicatorTypeName" placeholder="请输入指标类型名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 100%">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-option v-for="dict in sys_item_enable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :lg="6" :md="24" :sm="24">
</el-row>
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -55,20 +57,20 @@
<div class="table-container">
<el-table v-loading="loading" border :data="assessmentIndicatorTypeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="指标类型编码" align="center" prop="indicatorTypeCode" />
<el-table-column label="指标类型名称" align="center" prop="indicatorTypeName" />
<el-table-column label="状态" align="center" prop="status">
<el-table-column label="指标类型编码" align="center" prop="indicatorTypeCode" min-width="150" show-overflow-tooltip />
<el-table-column label="指标类型名称" align="center" prop="indicatorTypeName" min-width="180" show-overflow-tooltip />
<el-table-column label="状态" align="center" prop="status" min-width="100">
<template #default="scope">
<dict-tag :options="sys_item_enable" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<el-table-column label="备注" align="center" prop="remark" min-width="200" show-overflow-tooltip />
<el-table-column label="创建时间" align="center" prop="createTime" min-width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" width="120" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button
@@ -293,6 +295,17 @@ onMounted(() => {
padding: 8px;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
}
/* 表格卡片布局 */
.table-card {
margin-top: 0;
+45 -23
View File
@@ -5,44 +5,47 @@
<el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" label-width="100px">
<el-row :gutter="20">
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="计划名称" prop="planName">
<el-input v-model="queryParams.planName" placeholder="请输入计划名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="计划编码" prop="planCode">
<el-input v-model="queryParams.planCode" placeholder="请输入计划编码" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="考核方案" prop="schemeId">
<el-select v-model="queryParams.schemeId" placeholder="请选择考核方案" clearable style="width: 100%">
<el-option v-for="item in schemeOptions" :key="item.id" :label="item.schemeName" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :lg="6" :md="12" :sm="24">
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="学年" prop="academicYear">
<el-input v-model="queryParams.academicYear" placeholder="请输入学年" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="学期" prop="semester">
<el-select v-model="queryParams.semester" placeholder="请选择学期" clearable style="width: 100%">
<el-option label="上学期" value="1" />
<el-option label="下学期" value="2" />
<el-option v-for="dict in student_semester" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="计划状态" prop="planStatus">
<el-select v-model="queryParams.planStatus" placeholder="请选择计划状态" clearable style="width: 100%">
<el-option v-for="option in planStatusOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :lg="12" :md="24" :sm="24">
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="创建时间">
<el-date-picker
v-model="dateRange"
@@ -56,7 +59,9 @@
></el-date-picker>
</el-form-item>
</el-col>
<el-col :lg="6" :md="24" :sm="24">
</el-row>
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -87,24 +92,24 @@
<div class="table-container">
<el-table v-loading="loading" border :data="assessmentPlanList" @selection-change="handleSelectionChange" @sort-change="sortChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="计划名称" align="center" prop="planName" :show-overflow-tooltip="true" />
<el-table-column label="计划编码" align="center" prop="planCode" :show-overflow-tooltip="true" />
<el-table-column label="考核方案" align="center" :show-overflow-tooltip="true">
<el-table-column label="计划名称" align="center" prop="planName" min-width="180" :show-overflow-tooltip="true" />
<el-table-column label="计划编码" align="center" prop="planCode" min-width="150" :show-overflow-tooltip="true" />
<el-table-column label="考核方案" align="center" min-width="180" :show-overflow-tooltip="true">
<template #default="scope">
<span>{{ schemeOptions.find((item) => item.id === scope.row.schemeId)?.schemeName || '' }}</span>
</template>
</el-table-column>
<el-table-column label="学年学期" align="center" width="120">
<el-table-column label="学年学期" align="center" min-width="160">
<template #default="scope">
<span>{{ scope.row.academicYear }}{{ scope.row.semester === '1' ? '上' : '下' }}学期</span>
<span>{{ scope.row.academicYear }}{{ getSemesterLabel(scope.row.semester) }}</span>
</template>
</el-table-column>
<el-table-column label="计划时间" align="center" width="180">
<el-table-column label="计划时间" align="center" min-width="200">
<template #default="scope">
<span>{{ parseTime(scope.row.planStartTime, '{y}-{m}-{d}') }} 至 {{ parseTime(scope.row.planEndTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="数据提取时间" align="center" width="180">
<el-table-column label="数据提取时间" align="center" min-width="200">
<template #default="scope">
<span v-if="scope.row.dataExtractStartTime && scope.row.dataExtractEndTime">
{{ parseTime(scope.row.dataExtractStartTime, '{y}-{m}-{d}') }} 至 {{ parseTime(scope.row.dataExtractEndTime, '{y}-{m}-{d}') }}
@@ -112,7 +117,7 @@
<span v-else class="text-gray-400">未设置</span>
</template>
</el-table-column>
<el-table-column label="计划状态" align="center" width="100">
<el-table-column label="计划状态" align="center" min-width="100">
<template #default="scope">
<el-switch
v-model="scope.row.planStatus"
@@ -123,12 +128,12 @@
></el-switch>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<el-table-column label="创建时间" align="center" prop="createTime" min-width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['bzrkh:assessmentPlan:edit']"></el-button>
@@ -168,7 +173,7 @@
<el-option
v-for="item in schemeOptions"
:key="item.id"
:label="`${item.schemeName}(${item.academicYear}${item.semester === '1' ? '上' : '下'}学期)`"
:label="`${item.schemeName}(${item.academicYear}${getSemesterLabel(item.semester)})`"
:value="item.id"
/>
</el-select>
@@ -184,8 +189,7 @@
<el-col :span="12">
<el-form-item label="学期" prop="semester">
<el-select v-model="form.semester" placeholder="请选择学期" style="width: 100%">
<el-option label="上学期" value="1" />
<el-option label="下学期" value="2" />
<el-option v-for="dict in student_semester" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
@@ -286,6 +290,13 @@ import { AssessmentPlanVO, AssessmentPlanQuery, AssessmentPlanForm } from '@/api
import { BmzrAssessmentSchemeVO } from '@/api/bzrkh/assessmentScheme/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { student_semester } = toRefs<any>(proxy?.useDict('student_semester'));
// 获取学期标签
const getSemesterLabel = (semester: string) => {
const semesterDict = student_semester.value?.find((item: any) => item.value === semester);
return semesterDict ? semesterDict.label : '';
};
// 自定义计划状态选项(符合数据库设计:0=禁用 1=启用)
const planStatusOptions = ref([
@@ -534,4 +545,15 @@ onMounted(() => {
background-color: #fff;
margin-top: 0px !important;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.search-btn-container .el-form-item {
margin-bottom: 0 !important;
}
</style>
+25 -15
View File
@@ -5,39 +5,40 @@
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<el-row :gutter="20">
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="方案名称" prop="schemeName">
<el-input v-model="queryParams.schemeName" placeholder="请输入方案名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="方案编码" prop="schemeCode">
<el-input v-model="queryParams.schemeCode" placeholder="请输入方案编码" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="学年" prop="academicYear">
<el-input v-model="queryParams.academicYear" placeholder="请输入学年" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :lg="6" :md="12" :sm="24">
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="学期" prop="semester">
<el-select v-model="queryParams.semester" placeholder="请选择学期" clearable style="width: 100%">
<el-option label="上学期" value="1" />
<el-option label="下学期" value="2" />
<el-option v-for="dict in student_semester" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 100%">
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :lg="18" :md="12" :sm="24">
</el-row>
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -81,8 +82,7 @@
<el-table-column label="学年" align="center" prop="academicYear" width="100" />
<el-table-column label="学期" align="center" prop="semester" width="80">
<template #default="scope">
<el-tag v-if="scope.row.semester === '1'" type="primary">上学期</el-tag>
<el-tag v-else-if="scope.row.semester === '2'" type="success">下学期</el-tag>
<dict-tag :options="student_semester" :value="scope.row.semester" />
</template>
</el-table-column>
<el-table-column label="自评" align="center" prop="selfEvaluationRequired" width="70">
@@ -164,8 +164,7 @@
<el-col :span="12">
<el-form-item label="学期" prop="semester">
<el-select v-model="form.semester" placeholder="请选择学期" style="width: 100%">
<el-option label="上学期" value="1" />
<el-option label="下学期" value="2" />
<el-option v-for="dict in student_semester" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
@@ -234,7 +233,7 @@ import { BmzrAssessmentSchemeVO, BmzrAssessmentSchemeQuery, BmzrAssessmentScheme
import IndicatorManagement from './IndicatorManagement.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable, sys_yesno } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_yesno'));
const { sys_normal_disable, sys_yesno, student_semester } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_yesno', 'student_semester'));
const schemeList = ref<BmzrAssessmentSchemeVO[]>([]);
const buttonLoading = ref(false);
@@ -517,4 +516,15 @@ onMounted(() => {
justify-content: flex-end;
gap: 12px;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.search-btn-container .el-form-item {
margin-bottom: 0 !important;
}
</style>
+38 -26
View File
@@ -10,12 +10,14 @@
<el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" label-width="100px">
<el-row :gutter="20">
<el-col :lg="6" :md="12" :sm="24">
<el-col :span="8">
<el-form-item label="计划名称" prop="planName">
<el-input v-model="queryParams.planName" placeholder="请输入计划名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :lg="6" :md="24" :sm="24">
</el-row>
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -37,20 +39,20 @@
<div class="table-container">
<el-table v-loading="loading" border :data="assessmentPlanList" @sort-change="sortChange">
<el-table-column label="计划名称" align="center" prop="planName" :show-overflow-tooltip="true" />
<el-table-column label="计划编码" align="center" prop="planCode" :show-overflow-tooltip="true" />
<el-table-column label="考核方案" align="center" prop="schemeName" :show-overflow-tooltip="true" />
<el-table-column label="学年学期" align="center" width="120">
<el-table-column label="计划名称" align="center" prop="planName" min-width="180" :show-overflow-tooltip="true" />
<el-table-column label="计划编码" align="center" prop="planCode" min-width="150" :show-overflow-tooltip="true" />
<el-table-column label="考核方案" align="center" prop="schemeName" min-width="180" :show-overflow-tooltip="true" />
<el-table-column label="学年学期" align="center" min-width="160">
<template #default="scope">
<span>{{ scope.row.academicYear }}{{ scope.row.semester === '1' ? '上' : '下' }}学期</span>
<span>{{ scope.row.academicYear }}{{ getSemesterLabel(scope.row.semester) }}</span>
</template>
</el-table-column>
<el-table-column label="计划时间" align="center" width="180">
<el-table-column label="计划时间" align="center" min-width="200">
<template #default="scope">
<span>{{ parseTime(scope.row.planStartTime, '{y}-{m}-{d}') }} 至 {{ parseTime(scope.row.planEndTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="数据提取时间" align="center" width="180">
<el-table-column label="数据提取时间" align="center" min-width="200">
<template #default="scope">
<span v-if="scope.row.dataExtractStartTime && scope.row.dataExtractEndTime">
{{ parseTime(scope.row.dataExtractStartTime, '{y}-{m}-{d}') }} 至 {{ parseTime(scope.row.dataExtractEndTime, '{y}-{m}-{d}') }}
@@ -58,7 +60,7 @@
<span v-else class="text-gray-400">未设置</span>
</template>
</el-table-column>
<el-table-column label="审核状态" align="center" width="120">
<el-table-column label="审核状态" align="center" min-width="120">
<template #default="scope">
<el-tag v-if="!scope.row.submitted" type="info" effect="plain">未提交</el-tag>
<el-tag v-else-if="scope.row.auditStatus === '9'" type="primary" effect="plain">草稿</el-tag>
@@ -68,12 +70,12 @@
<el-tag v-else type="info" effect="plain">未知状态</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<el-table-column label="创建时间" align="center" prop="createTime" min-width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180">
<template #default="scope">
<!-- 审核通过状态:只显示查看考核按钮 -->
<template v-if="scope.row.auditStatus === '1'">
@@ -101,27 +103,18 @@
></el-button>
</el-tooltip>
</template>
<!-- 已提交状态(待审核):显示撤销按钮 -->
<!-- 待审核状态:显示查看考核按钮 -->
<template v-else-if="scope.row.auditStatus === '0'">
<el-tooltip content="撤销提交" placement="top">
<el-tooltip content="查看考核" placement="top">
<el-button
link
type="warning"
icon="RefreshLeft"
type="success"
icon="View"
:disabled="loading || !isDataReady"
@click="handleWithdrawAssessment(scope.row)"
@click="handleViewAssessment(scope.row)"
></el-button>
</el-tooltip>
</template>
<el-tooltip content="查看考核" placement="top">
<el-button
link
type="success"
icon="View"
:disabled="loading || !isDataReady"
@click="handleViewAssessment(scope.row)"
></el-button>
</el-tooltip>
</template>
</template>
</el-table-column>
@@ -170,6 +163,14 @@ import SubmitDialog from './submitDialog.vue';
import ViewDialog from './viewDialog.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { student_semester } = toRefs<any>(proxy?.useDict('student_semester'));
// 获取学期标签
const getSemesterLabel = (semester: string) => {
const semesterDict = student_semester.value?.find((item: any) => item.value === semester);
return semesterDict ? semesterDict.label : '';
};
const userStore = useUserStore();
const teacherClasses = ref<TeacherClassVO[]>([]);
@@ -496,6 +497,17 @@ onMounted(() => {
margin-top: 0px !important;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.search-btn-container .el-form-item {
margin-bottom: 0 !important;
}
/* 标签页样式优化 */
:deep(.el-tabs__header) {
margin-bottom: 20px;
@@ -89,12 +89,12 @@
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="indicatorName" label="指标名称" min-width="180" align="center" :show-overflow-tooltip="true" />
<el-table-column prop="description" label="指标描述" min-width="200" align="center" :show-overflow-tooltip="true" />
<el-table-column prop="score" label="指标分值" width="80" align="center">
<el-table-column prop="score" label="指标分值" min-width="100" align="center">
<template #default="scope">
<span>{{ scope.row.score || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="自评分数" width="130" align="center">
<el-table-column label="自评分数" min-width="130" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.selfScore"
@@ -105,21 +105,19 @@
style="width: 100%"
placeholder="请输入分数"
size="small"
class="score-input"
/>
</template>
</el-table-column>
<el-table-column label="自评意见" width="220" align="center">
<el-table-column label="自评意见" min-width="220" align="center">
<template #default="scope">
<el-input
v-model="scope.row.selfComment"
type="textarea"
:rows="1"
:rows="2"
placeholder="请输入自评意见"
maxlength="200"
show-word-limit
size="small"
class="comment-input"
/>
</template>
</el-table-column>
@@ -344,22 +342,22 @@
<el-tab-pane label="违纪记录" name="discipline">
<el-table :data="studentRecordsData?.disciplineRecords || []" border style="width: 100%" max-height="400" empty-text="暂无违纪记录">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="studentNo" label="学号" width="120" align="center" />
<el-table-column prop="studentName" label="学生姓名" width="100" align="center" />
<el-table-column prop="disciplineTypeName" label="违纪类型" width="120" align="center" />
<el-table-column prop="disciplineDate" label="违纪日期" width="110" align="center">
<el-table-column prop="studentNo" label="学号" min-width="120" align="center" />
<el-table-column prop="studentName" label="学生姓名" min-width="100" align="center" />
<el-table-column prop="disciplineTypeName" label="违纪类型" min-width="120" align="center" />
<el-table-column prop="disciplineDate" label="违纪日期" min-width="110" align="center">
<template #default="scope">
<span>{{ scope.row.disciplineDate?.split(' ')[0] || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="disciplineReason" label="违纪原因" min-width="200" :show-overflow-tooltip="true" />
<el-table-column prop="disciplineContent" label="处分内容" min-width="200" :show-overflow-tooltip="true" />
<el-table-column prop="scoreDeduction" label="扣分" width="80" align="center">
<el-table-column prop="scoreDeduction" label="扣分" min-width="80" align="center">
<template #default="scope">
<el-tag type="danger" size="small">{{ scope.row.scoreDeduction || 0 }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="80" align="center">
<el-table-column prop="status" label="状态" min-width="80" align="center">
<template #default="scope">
<el-tag :type="scope.row.status === '1' ? 'danger' : 'info'" size="small">
{{ scope.row.status === '1' ? '生效' : '已撤销' }}
@@ -373,21 +371,21 @@
<el-tab-pane label="奖励记录" name="reward">
<el-table :data="studentRecordsData?.rewardRecords || []" border style="width: 100%" max-height="400" empty-text="暂无奖励记录">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="studentNo" label="学号" width="120" align="center" />
<el-table-column prop="studentName" label="学生姓名" width="100" align="center" />
<el-table-column prop="rewardTypeName" label="奖励类型" width="120" align="center" />
<el-table-column prop="rewardDate" label="奖励日期" width="110" align="center">
<el-table-column prop="studentNo" label="学号" min-width="120" align="center" />
<el-table-column prop="studentName" label="学生姓名" min-width="100" align="center" />
<el-table-column prop="rewardTypeName" label="奖励类型" min-width="120" align="center" />
<el-table-column prop="rewardDate" label="奖励日期" min-width="110" align="center">
<template #default="scope">
<span>{{ scope.row.rewardDate || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="rewardContent" label="奖励内容" min-width="300" :show-overflow-tooltip="true" />
<el-table-column prop="scoreAddition" label="加分" width="80" align="center">
<el-table-column prop="scoreAddition" label="加分" min-width="80" align="center">
<template #default="scope">
<el-tag type="success" size="small">{{ scope.row.scoreAddition || 0 }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="80" align="center">
<el-table-column prop="status" label="状态" min-width="80" align="center">
<template #default="scope">
<el-tag :type="scope.row.status === '1' ? 'success' : 'info'" size="small">
{{ scope.row.status === '1' ? '生效' : '撤销' }}
@@ -89,37 +89,35 @@
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="indicatorName" label="指标名称" min-width="180" align="center" :show-overflow-tooltip="true" />
<el-table-column prop="description" label="指标描述" min-width="200" align="center" :show-overflow-tooltip="true" />
<el-table-column prop="score" label="指标分值" width="80" align="center">
<el-table-column prop="score" label="指标分值" min-width="100" align="center">
<template #default="scope">
<span>{{ scope.row.score || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="自评分数" width="80" align="center">
<el-table-column label="自评分数" min-width="100" align="center">
<template #default="scope">
<div class="readonly-field">
{{ formatNumber(scope.row.selfScore, 2) }}
</div>
<span>{{ scope.row.selfScore || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="自评意见" width="180" align="center">
<el-table-column label="自评意见" min-width="200" align="center">
<template #default="scope">
<div class="readonly-field comment-field">
{{ scope.row.selfComment || '-' }}
</div>
<el-tooltip :content="scope.row.selfComment || '无'" placement="top" :disabled="!scope.row.selfComment">
<span class="text-ellipsis">{{ scope.row.selfComment || '无' }}</span>
</el-tooltip>
</template>
</el-table-column>
<!-- 审批模式下显示上级评分和审批意见 -->
<template v-if="viewMode === 'approval'">
<el-table-column label="上级评分" width="80" align="center">
<el-table-column label="上级评分" min-width="100" align="center">
<template #default="scope">
<span>{{ scope.row.superiorScore || '-' }}</span>
<span>{{ scope.row.superiorScore || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="审批意见" width="180" align="center">
<el-table-column label="审批意见" min-width="200" align="center">
<template #default="scope">
<div class="readonly-field comment-field">
{{ scope.row.superiorComment || '-' }}
</div>
<el-tooltip :content="scope.row.superiorComment || '无'" placement="top" :disabled="!scope.row.superiorComment">
<span class="text-ellipsis">{{ scope.row.superiorComment || '无' }}</span>
</el-tooltip>
</template>
</el-table-column>
</template>
@@ -230,28 +228,28 @@
<el-table border :data="approvalSpecialWorkScores" style="width: 100%" class="special-work-table">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="indicatorName" label="特色工作项目" min-width="180" align="center" :show-overflow-tooltip="true" />
<el-table-column label="自评分数" width="80" align="center">
<el-table-column label="自评分数" min-width="100" align="center">
<template #default="scope">
<span>{{ scope.row.selfScore || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="自评意见" width="180" align="center">
<el-table-column label="自评意见" min-width="200" align="center">
<template #default="scope">
<div class="readonly-field comment-field">
{{ scope.row.selfComment || '-' }}
</div>
<el-tooltip :content="scope.row.selfComment || '无'" placement="top" :disabled="!scope.row.selfComment">
<span class="text-ellipsis">{{ scope.row.selfComment || '无' }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="上级评分" width="80" align="center">
<el-table-column label="上级评分" min-width="100" align="center">
<template #default="scope">
<span>{{ scope.row.superiorScore || '-' }}</span>
<span>{{ scope.row.superiorScore || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="审批意见" width="180" align="center">
<el-table-column label="审批意见" min-width="200" align="center">
<template #default="scope">
<div class="readonly-field comment-field">
{{ scope.row.superiorComment || '-' }}
</div>
<el-tooltip :content="scope.row.superiorComment || '无'" placement="top" :disabled="!scope.row.superiorComment">
<span class="text-ellipsis">{{ scope.row.superiorComment || '无' }}</span>
</el-tooltip>
</template>
</el-table-column>
</el-table>
@@ -369,22 +367,22 @@
<el-tab-pane label="违纪记录" name="discipline">
<el-table :data="studentRecordsData?.disciplineRecords || []" border style="width: 100%" max-height="400" empty-text="暂无违纪记录">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="studentNo" label="学号" width="120" align="center" />
<el-table-column prop="studentName" label="学生姓名" width="100" align="center" />
<el-table-column prop="disciplineTypeName" label="违纪类型" width="120" align="center" />
<el-table-column prop="disciplineDate" label="违纪日期" width="110" align="center">
<el-table-column prop="studentNo" label="学号" min-width="120" align="center" />
<el-table-column prop="studentName" label="学生姓名" min-width="100" align="center" />
<el-table-column prop="disciplineTypeName" label="违纪类型" min-width="120" align="center" />
<el-table-column prop="disciplineDate" label="违纪日期" min-width="110" align="center">
<template #default="scope">
<span>{{ scope.row.disciplineDate?.split(' ')[0] || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="disciplineReason" label="违纪原因" min-width="200" :show-overflow-tooltip="true" />
<el-table-column prop="disciplineContent" label="处分内容" min-width="200" :show-overflow-tooltip="true" />
<el-table-column prop="scoreDeduction" label="扣分" width="80" align="center">
<el-table-column prop="scoreDeduction" label="扣分" min-width="80" align="center">
<template #default="scope">
<el-tag type="danger" size="small">{{ scope.row.scoreDeduction || 0 }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="80" align="center">
<el-table-column prop="status" label="状态" min-width="80" align="center">
<template #default="scope">
<el-tag :type="scope.row.status === '1' ? 'danger' : 'info'" size="small">
{{ scope.row.status === '1' ? '生效' : '已撤销' }}
@@ -398,21 +396,21 @@
<el-tab-pane label="奖励记录" name="reward">
<el-table :data="studentRecordsData?.rewardRecords || []" border style="width: 100%" max-height="400" empty-text="暂无奖励记录">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="studentNo" label="学号" width="120" align="center" />
<el-table-column prop="studentName" label="学生姓名" width="100" align="center" />
<el-table-column prop="rewardTypeName" label="奖励类型" width="120" align="center" />
<el-table-column prop="rewardDate" label="奖励日期" width="110" align="center">
<el-table-column prop="studentNo" label="学号" min-width="120" align="center" />
<el-table-column prop="studentName" label="学生姓名" min-width="100" align="center" />
<el-table-column prop="rewardTypeName" label="奖励类型" min-width="120" align="center" />
<el-table-column prop="rewardDate" label="奖励日期" min-width="110" align="center">
<template #default="scope">
<span>{{ scope.row.rewardDate || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="rewardContent" label="奖励内容" min-width="300" :show-overflow-tooltip="true" />
<el-table-column prop="scoreAddition" label="加分" width="80" align="center">
<el-table-column prop="scoreAddition" label="加分" min-width="80" align="center">
<template #default="scope">
<el-tag type="success" size="small">{{ scope.row.scoreAddition || 0 }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="80" align="center">
<el-table-column prop="status" label="状态" min-width="80" align="center">
<template #default="scope">
<el-tag :type="scope.row.status === '1' ? 'success' : 'info'" size="small">
{{ scope.row.status === '1' ? '生效' : '撤销' }}
+228 -247
View File
@@ -100,7 +100,9 @@
<el-date-picker v-model="queryParams.year" type="year" placeholder="选择年份" value-format="YYYY" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="18">
</el-row>
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -110,30 +112,31 @@
</el-form>
</el-card>
<!-- 操作按钮区域 -->
<el-card class="button-card">
<el-card shadow="never" class="button-card table-card">
<template #header>
<div class="card-header">
<span class="toolbar-title">项目申报管理</span>
<div class="toolbar-actions">
<el-button type="primary" icon="Plus" @click="handleAdd" v-hasPermi="['cw:application:add']">新增申报</el-button>
<el-button type="success" icon="Edit" :disabled="single" @click="handleUpdate" v-hasPermi="['cw:application:edit']">修改</el-button>
<el-button type="danger" icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['cw:application:remove']">删除</el-button>
<el-button type="info" icon="Upload" @click="handleSubmit" :disabled="multiple" v-hasPermi="['cw:application:submit']"
>提交审批</el-button
>
<el-button type="warning" icon="Download" @click="handleExport" v-hasPermi="['cw:application:export']">导出</el-button>
<right-toolbar :showSearch="showSearch" @update:showSearch="showSearch = $event" @queryTable="getList"></right-toolbar>
</div>
</div>
<el-row ref="toolbarRef" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['cw:application:add']">新增申报</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate" v-hasPermi="['cw:application:edit']">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['cw:application:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Upload" @click="handleSubmit" :disabled="multiple" v-hasPermi="['cw:application:submit']">提交审批</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['cw:application:export']">导出</el-button>
</el-col>
<right-toolbar :showSearch="showSearch" @update:showSearch="showSearch = $event" @queryTable="getList"></right-toolbar>
</el-row>
</template>
</el-card>
<!-- 数据表格区域 -->
<el-card class="table-card">
<div class="table-container">
<el-table v-loading="loading" :data="applicationList" @selection-change="handleSelectionChange" class="scroll-table">
<el-table-column type="selection" width="55" align="center" />
<el-table v-loading="loading" border :data="applicationList" @selection-change="handleSelectionChange" style="width: 100%" :scroll-x="true" class="scroll-table">
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="申报编号" align="center" prop="applicationNo" min-width="140" />
<el-table-column label="项目名称" align="center" prop="projectName" min-width="180" show-overflow-tooltip />
<el-table-column label="项目负责人" align="center" prop="principal" min-width="100" />
@@ -156,41 +159,38 @@
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="280" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" width="280" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<div class="action-buttons">
<el-tooltip content="查看详情" placement="top">
<el-button type="primary" icon="View" circle size="small" @click="handleView(scope.row)" v-hasPermi="['cw:application:query']" />
<el-button link type="info" icon="View" @click="handleView(scope.row)" v-hasPermi="['cw:application:query']" />
</el-tooltip>
<el-tooltip content="编辑" placement="top" v-if="['draft', 'returned'].includes(scope.row.approvalStatus)">
<el-button type="success" icon="Edit" circle size="small" @click="handleUpdate(scope.row)" v-hasPermi="['cw:application:edit']" />
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['cw:application:edit']" />
</el-tooltip>
<el-tooltip content="提交审批" placement="top" v-if="['draft', 'returned'].includes(scope.row.approvalStatus)">
<el-button
link
type="warning"
icon="Upload"
circle
size="small"
@click="handleSubmit(scope.row)"
v-hasPermi="['cw:application:submit']"
/>
</el-tooltip>
<el-tooltip content="撤回" placement="top" v-if="scope.row.approvalStatus === 'submitted'">
<el-button
link
type="info"
icon="RefreshLeft"
circle
size="small"
@click="handleWithdraw(scope.row)"
v-hasPermi="['cw:application:withdraw']"
/>
</el-tooltip>
<el-tooltip content="删除" placement="top" v-if="['draft', 'cancelled', 'returned'].includes(scope.row.approvalStatus)">
<el-button
link
type="danger"
icon="Delete"
circle
size="small"
@click="handleDelete(scope.row)"
v-hasPermi="['cw:application:remove']"
/>
@@ -237,7 +237,7 @@
</div>
</template>
<script>
<script lang="ts">
import {
listProjectApplication,
getProjectApplication,
@@ -553,252 +553,238 @@ export default {
</script>
<style scoped lang="scss">
/* 继承后勤管理模块的样式风格 */
/* 按照满意度调查页面样式重写 */
.page-container {
background-color: #f5f5f5;
min-height: calc(100vh - 84px);
padding: 8px;
}
/* 统计卡片样式 */
.stats-row {
margin-bottom: 20px;
/* 统计卡片样式 */
.stats-row {
margin-bottom: 20px;
}
.stat-card {
border-radius: 12px;
transition: all 0.3s ease;
height: 90px;
cursor: pointer;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.stat-card {
border-radius: 12px;
transition: all 0.3s ease;
height: 90px;
cursor: pointer;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.stat-content {
display: flex;
align-items: center;
padding: 5px 16px;
height: 100%;
box-sizing: border-box;
.stat-icon {
font-size: 28px;
margin-right: 15px;
opacity: 0.9;
}
.stat-info {
flex: 1;
.stat-value {
font-size: 24px;
font-weight: bold;
line-height: 1;
margin-bottom: 4px;
}
.stat-label {
font-size: 14px;
opacity: 0.8;
line-height: 1;
}
}
}
&.stat-card-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
&.stat-card-success {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
color: white;
}
&.stat-card-warning {
background: linear-gradient(135deg, #ff9a56 0%, #ffad56 100%);
color: white;
}
&.stat-card-info {
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
color: #333;
}
}
/* 表格卡片布局 - 参考后勤管理功能 */
.table-card {
margin-top: 0;
}
.table-card :deep(.el-card__body) {
padding: 0px 20px 20px 20px !important;
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
.card-header {
.stat-content {
display: flex;
justify-content: space-between;
align-items: center;
padding: 5px 16px;
height: 100%;
box-sizing: border-box;
.toolbar-title {
font-size: 18px;
font-weight: 600;
color: #1e293b;
display: flex;
align-items: center;
.stat-icon {
font-size: 28px;
margin-right: 15px;
opacity: 0.9;
}
&::before {
content: '';
width: 4px;
height: 20px;
background: linear-gradient(135deg, rgb(93, 135, 255), rgb(73, 190, 255));
border-radius: 2px;
margin-right: 12px;
.stat-info {
flex: 1;
.stat-value {
font-size: 24px;
font-weight: bold;
line-height: 1;
margin-bottom: 4px;
}
}
.toolbar-actions {
display: flex;
gap: 12px;
align-items: center;
}
}
/* 表格主体 */
.table-container {
width: 100%;
margin-bottom: 0;
}
/* 表格样式 - 参考后勤管理功能 */
.scroll-table {
width: 100%;
:deep(.el-table__header-wrapper) {
.el-table__header {
th {
background-color: #f8f9fa;
color: #333;
font-weight: 600;
}
}
}
:deep(.el-table__body-wrapper) {
.el-table__row {
&:hover {
background-color: #f5f7fa;
}
.stat-label {
font-size: 14px;
opacity: 0.8;
line-height: 1;
}
}
}
/* 表格横向滚动条样式优化 - 参考后勤管理功能 */
.scroll-table :deep(.el-table__body-wrapper) {
overflow-x: auto !important;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #409eff #f0f0f0;
&.stat-card-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.scroll-table :deep(.el-table__header-wrapper) {
overflow-x: hidden;
&.stat-card-success {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
color: white;
}
/* Webkit 浏览器的横向滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 12px !important;
width: 12px !important;
&.stat-card-warning {
background: linear-gradient(135deg, #ff9a56 0%, #ffad56 100%);
color: white;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f0f0f0 !important;
border-radius: 6px !important;
border: 1px solid #e4e7ed !important;
&.stat-card-info {
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
color: #333;
}
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #409eff !important;
border-radius: 6px !important;
border: 2px solid #f0f0f0 !important;
min-height: 20px !important;
/* 搜索按钮居中样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #337ecc !important;
}
/* 卡片样式 */
:deep(.el-card) {
border: 1px solid #e4e7ed;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
background: #2b6cb0 !important;
}
/* 表格卡片头部样式 */
.table-card :deep(.el-card__header) {
background-color: #fafafa;
border-bottom: 1px solid #ebeef5;
padding: 18px 20px;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-corner {
background: #f0f0f0;
}
/* 表格卡片内容样式 */
.table-card :deep(.el-card__body) {
padding: 0px 20px 20px 20px !important;
}
/* 固定列样式优化 */
.scroll-table :deep(.el-table__fixed-right) {
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 表格样式 */
.scroll-table {
width: 100%;
.scroll-table :deep(.el-table__fixed-left) {
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 确保固定列在滚动时有正确的背景色 */
.scroll-table :deep(.el-table__fixed-right .el-table__cell),
.scroll-table :deep(.el-table__fixed-left .el-table__cell) {
background-color: #fff;
}
/* 表格行悬停效果在固定列上的同步 */
.scroll-table :deep(.el-table__row:hover .el-table__cell) {
background-color: #f5f7fa !important;
}
/* 操作按钮样式 */
.action-buttons {
display: flex;
gap: 8px;
justify-content: center;
align-items: center;
white-space: nowrap;
.el-button {
width: 27px !important;
height: 27px !important;
padding: 0 !important;
display: flex;
align-items: center;
justify-content: center;
:deep(.el-table__header-wrapper) {
.el-table__header {
th {
background-color: #fafafa;
color: #333;
font-weight: 600;
}
}
}
/* 金额文本样式 */
.amount-text {
font-weight: 600;
color: #e74c3c;
:deep(.el-table__body-wrapper) {
.el-table__row {
&:hover {
background-color: #f5f7fa;
}
}
}
}
/* 分页容器样式 - 参考后勤管理功能 */
.pagination-container {
display: flex;
justify-content: flex-end;
padding: 16px 20px 0 0 !important;
background-color: #fff;
margin-top: 0px !important;
}
/* 表格横向滚动条样式优化 */
.scroll-table :deep(.el-table__body-wrapper) {
overflow-x: auto !important;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #409eff #f0f0f0;
}
/* 对话框样式 */
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.scroll-table :deep(.el-table__header-wrapper) {
overflow-x: hidden;
}
/* Webkit 浏览器的横向滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 12px !important;
width: 12px !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f0f0f0 !important;
border-radius: 6px !important;
border: 1px solid #e4e7ed !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #409eff !important;
border-radius: 6px !important;
border: 2px solid #f0f0f0 !important;
min-height: 20px !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #337ecc !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
background: #2b6cb0 !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-corner {
background: #f0f0f0;
}
/* 固定列样式优化 */
.scroll-table :deep(.el-table__fixed-right) {
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
}
.scroll-table :deep(.el-table__fixed-left) {
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 确保固定列在滚动时有正确的背景色 */
.scroll-table :deep(.el-table__fixed-right .el-table__cell),
.scroll-table :deep(.el-table__fixed-left .el-table__cell) {
background-color: #fff;
}
/* 表格行悬停效果在固定列上的同步 */
.scroll-table :deep(.el-table__row:hover .el-table__cell) {
background-color: #f5f7fa !important;
}
/* 操作按钮样式 */
.action-buttons {
display: flex;
gap: 4px;
justify-content: center;
align-items: center;
white-space: nowrap;
}
/* 金额文本样式 */
.amount-text {
font-weight: 600;
color: #e74c3c;
}
/* 分页容器样式 */
.pagination-container {
display: flex;
justify-content: flex-end;
padding: 16px 20px 0 0 !important;
background-color: #fff;
margin-top: 0px !important;
}
/* 对话框样式 */
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
/* 输入框样式 */
:deep(.el-input .el-input__wrapper) {
border-radius: 4px;
}
:deep(.el-select .el-input__wrapper) {
border-radius: 4px;
}
:deep(.el-date-editor .el-input__wrapper) {
border-radius: 4px;
}
/* 响应式设计 */
@@ -811,11 +797,6 @@ export default {
margin-bottom: 10px;
}
}
.toolbar-actions {
flex-direction: column;
gap: 8px !important;
}
}
}
</style>
+309 -65
View File
@@ -1,68 +1,106 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
<el-form-item label="申报编号" prop="applicationNo">
<el-input v-model="queryParams.applicationNo" placeholder="请输入申报编号" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="申报人" prop="applicantName">
<el-input v-model="queryParams.applicantName" placeholder="请输入申报人" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<div class="p-2 page-container">
<transition :enter-active-class="animate.searchAnimate.enter" :leave-active-class="animate.searchAnimate.leave">
<div ref="searchRef" v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="申报编号" prop="applicationNo">
<el-input v-model="queryParams.applicationNo" placeholder="请输入申报编号" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="申报人" prop="applicantName">
<el-input v-model="queryParams.applicantName" placeholder="请输入申报人" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="审批状态" prop="nodeStatus">
<el-select v-model="queryParams.nodeStatus" placeholder="请选择审批状态" clearable>
<el-option v-for="dict in cw_approval_status" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
</transition>
<!-- 快速操作按钮 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Check" @click="showPendingApprovals">待我审批</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="CircleCheck" @click="showMyApprovals">我已审批</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Download" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-card shadow="never" class="button-card table-card">
<template #header>
<el-row ref="toolbarRef" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Check" @click="showPendingApprovals">待我审批</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="CircleCheck" @click="showMyApprovals">我已审批</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Download" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar :showSearch="showSearch" @update:showSearch="showSearch = $event" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<!-- 数据表格 -->
<el-table v-loading="loading" :data="approvalRecordList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="申报编号" align="center" prop="applicationNo" />
<el-table-column label="项目名称" align="center" prop="projectName" />
<el-table-column label="申报人" align="center" prop="applicantName" />
<el-table-column label="部门" align="center" prop="deptName" />
<el-table-column label="审批节点" align="center" prop="nodeName" />
<el-table-column label="审批状态" align="center" prop="nodeStatus">
<template #default="scope">
<dict-tag :options="cw_approval_status" :value="scope.row.nodeStatus" />
</template>
</el-table-column>
<el-table-column label="审批人" align="center" prop="approverName" />
<el-table-column label="审批意见" align="center" prop="approveOpinion" />
<el-table-column label="审批时间" align="center" prop="endTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="160">
<template #default="scope">
<el-tooltip content="查看详情" placement="top">
<el-button link type="info" icon="View" @click="handleDetail(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="审批" placement="top" v-if="scope.row.nodeStatus === 'waiting'">
<el-button link type="success" icon="Check" @click="handleApprove(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
<div class="table-container">
<el-table
v-loading="loading"
border
:data="approvalRecordList"
@selection-change="handleSelectionChange"
style="width: 100%"
:scroll-x="true"
class="scroll-table"
>
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="申报编号" align="center" prop="applicationNo" width="140" show-overflow-tooltip />
<el-table-column label="项目名称" align="center" prop="projectName" min-width="200" show-overflow-tooltip />
<el-table-column label="申报人" align="center" prop="applicantName" width="120" />
<el-table-column label="部门" align="center" prop="deptName" width="150" />
<el-table-column label="审批节点" align="center" prop="nodeName" width="120" />
<el-table-column label="审批状态" align="center" prop="nodeStatus" width="120">
<template #default="scope">
<dict-tag :options="cw_approval_status" :value="scope.row.nodeStatus" />
</template>
</el-table-column>
<el-table-column label="审批人" align="center" prop="approverName" width="120" />
<el-table-column label="审批意见" align="center" prop="approveOpinion" min-width="200" show-overflow-tooltip />
<el-table-column label="审批时间" align="center" prop="endTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="280" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="查看详情" placement="top">
<el-button link type="info" icon="View" @click="handleDetail(scope.row)" v-hasPermi="['cw:approval:query']"></el-button>
</el-tooltip>
<el-tooltip content="审批" placement="top" v-if="scope.row.nodeStatus === 'waiting'">
<el-button link type="success" icon="Check" @click="handleApprove(scope.row)" v-hasPermi="['cw:approval:process']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination-container">
<pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @update:page="queryParams.pageNum = $event" @update:limit="queryParams.pageSize = $event" @pagination="getList" />
</div>
</el-card>
<!-- 审批对话框 -->
<el-dialog title="审批处理" v-model="approvalOpen" width="500px" append-to-body>
@@ -97,7 +135,7 @@
</div>
</template>
<script>
<script lang="ts">
import { listApprovalRecord, exportApprovalRecord, approveApplication, getMyApprovals, getDepartmentApprovals } from '@/api/cw/approvalRecord';
import { parseTime } from '@/utils/ruoyi';
import ApplicationSummary from '../application/summary.vue';
@@ -129,6 +167,13 @@ export default {
// 查看详情弹出层
viewOpen: false,
currentId: null,
// 动画配置
animate: {
searchAnimate: {
enter: 'animate__animated animate__fadeIn',
leave: 'animate__animated animate__fadeOut'
}
},
// 字典数据
cw_approval_status: [],
// 查询参数
@@ -138,6 +183,7 @@ export default {
applicationNo: '',
projectName: '',
applicantName: '',
nodeStatus: '',
deptId: 'current'
},
// 审批表单
@@ -200,7 +246,7 @@ export default {
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm('queryRef');
this.resetForm('queryFormRef');
this.handleQuery();
},
// 多选框选中数据
@@ -308,4 +354,202 @@ export default {
};
</script>
<style scoped></style>
<style scoped>
/* 页面容器样式 */
.page-container {
padding: 8px;
background-color: #f5f5f5;
min-height: 100vh;
}
/* 搜索按钮容器 - 右对齐 */
.search-btn-container {
display: flex;
justify-content: flex-end;
align-items: center;
}
.search-btn-container .el-form-item {
margin-bottom: 0;
}
/* 表格卡片布局 */
.table-card {
margin-top: 0;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
}
.table-card :deep(.el-card__body) {
padding: 0px 20px 20px 20px !important;
}
/* 表格容器 - 支持横向滚动 */
.table-container {
width: 100%;
margin-bottom: 0;
}
/* 横向滚动表格样式 */
.scroll-table {
width: 100%;
}
/* 表格横向滚动条样式优化 */
.scroll-table :deep(.el-table__body-wrapper) {
overflow-x: auto !important; /* 自动显示横向滚动条 */
overflow-y: auto;
}
.scroll-table :deep(.el-table__header-wrapper) {
overflow-x: hidden;
}
/* 强制显示表格横向滚动条 - 适用于所有浏览器 */
.scroll-table :deep(.el-table__body-wrapper) {
scrollbar-width: thin; /* Firefox */
scrollbar-color: #409eff #f0f0f0; /* Firefox - thumb track */
}
/* Webkit 浏览器的横向滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 12px !important; /* 增加滚动条高度 */
width: 12px !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f0f0f0 !important;
border-radius: 6px !important;
border: 1px solid #e4e7ed !important; /* 添加边框使其更明显 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #409eff !important; /* 使用主题色,更明显 */
border-radius: 6px !important;
border: 2px solid #f0f0f0 !important; /* 添加白色边框 */
min-height: 20px !important; /* 确保最小高度 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #337ecc !important; /* 悬停时更深的蓝色 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
background: #2b6cb0 !important; /* 点击时的颜色 */
}
/* 确保滚动条始终可见的额外设置 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-corner {
background: #f0f0f0;
}
/* 固定列样式优化 */
.scroll-table :deep(.el-table__fixed-right) {
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
}
.scroll-table :deep(.el-table__fixed-left) {
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 确保固定列在滚动时有正确的背景色 */
.scroll-table :deep(.el-table__fixed-right .el-table__cell),
.scroll-table :deep(.el-table__fixed-left .el-table__cell) {
background-color: #fff;
}
/* 表格行悬停效果在固定列上的同步 */
.scroll-table :deep(.el-table__row:hover .el-table__cell) {
background-color: #f5f7fa !important;
}
/* 分页容器样式 */
.pagination-container {
display: flex;
justify-content: flex-end;
padding: 16px 20px 0 0 !important;
background-color: #fff;
margin-top: 0px !important;
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
background-color: #fafafa;
}
/* 表格头部样式 */
.scroll-table :deep(.el-table__header) {
th {
background: #fafafa !important;
color: #303133;
font-weight: 600;
}
}
/* 表格边框样式 */
.scroll-table :deep(.el-table) {
border: 1px solid #e4e7ed;
border-radius: 8px;
overflow: hidden;
}
/* 表格行样式 */
.scroll-table :deep(.el-table__row) {
border-bottom: 1px solid #f0f2f5;
}
.scroll-table :deep(.el-table__row:hover) {
background-color: #f5f7fa !important;
}
/* 操作列按钮样式 */
.scroll-table :deep(.el-button--link) {
padding: 4px 8px;
font-size: 14px;
}
/* Dialog footer样式 */
.dialog-footer {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
width: 100%;
}
/* 动画效果 */
.animate__animated {
animation-duration: 0.3s;
}
.animate__fadeIn {
animation-name: fadeIn;
}
.animate__fadeOut {
animation-name: fadeOut;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeOut {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(-10px);
}
}
</style>
+31 -4
View File
@@ -21,7 +21,9 @@
<el-input v-model="queryParams.applicantName" placeholder="请输入申报人姓名" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
</el-row>
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -81,7 +83,7 @@
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['cw:approval:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
<right-toolbar v-model="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
</el-row>
</template>
@@ -125,7 +127,7 @@
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
<pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 项目详情对话框 -->
@@ -227,7 +229,7 @@
</div>
</template>
<script>
<script lang="ts">
import {
listApprovalRecord,
exportApprovalRecord,
@@ -707,4 +709,29 @@ export default {
.animate__animated {
animation-duration: 0.3s;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
}
/* 表格卡片布局 */
.table-card {
margin-top: 0;
}
.table-card :deep(.el-card__body) {
padding: 0px 20px 20px 20px !important;
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
</style>
+31 -4
View File
@@ -21,7 +21,9 @@
<el-input v-model="queryParams.applicantName" placeholder="请输入申报人姓名" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
</el-row>
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -81,7 +83,7 @@
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['cw:approval:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
<right-toolbar v-model="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
</el-row>
</template>
@@ -125,7 +127,7 @@
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
<pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 项目详情对话框 -->
@@ -202,7 +204,7 @@
</div>
</template>
<script>
<script lang="ts">
import {
getPendingApprovals,
getApprovalHistory,
@@ -615,4 +617,29 @@ export default {
.animate__animated {
animation-duration: 0.3s;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
}
/* 表格卡片布局 */
.table-card {
margin-top: 0;
}
.table-card :deep(.el-card__body) {
padding: 0px 20px 20px 20px !important;
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
</style>
+217 -159
View File
@@ -1,5 +1,5 @@
<template>
<div class="p-2 page-container">
<div class="page-container">
<!-- 统计卡片区域 -->
<div class="mb-10px">
<el-row :gutter="16">
@@ -146,9 +146,9 @@
</el-row>
<!-- 操作按钮行 -->
<el-row>
<el-row :gutter="10" class="mb8">
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-form-item style="margin-bottom: 0;">
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
@@ -159,37 +159,40 @@
</div>
</transition>
<!-- 工具栏 -->
<el-card class="button-card mb-10px" shadow="never">
<div class="card-header-vertical">
<span class="toolbar-title">我的助学金申请</span>
<div class="button-group">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['jcbt:povertyApplication:add']"> 新增申请 </el-button>
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate" v-hasPermi="['jcbt:povertyApplication:edit']">
修改
</el-button>
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['jcbt:povertyApplication:remove']">
删除
</el-button>
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['jcbt:povertyApplication:export']"> 导出 </el-button>
<!-- 表格数据 -->
<el-card shadow="never" class="button-card table-card">
<template #header>
<el-row ref="toolbarRef" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['jcbt:povertyApplication:add']">新增申请</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate" v-hasPermi="['jcbt:povertyApplication:edit']">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['jcbt:povertyApplication:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['jcbt:povertyApplication:export']">导出</el-button>
</el-col>
<right-toolbar :show-search="showSearch" @show-search="showSearch = $event" @query-table="getList"></right-toolbar>
</div>
</div>
</el-card>
<!-- 表格区域 -->
<el-card class="table-card" shadow="never">
</el-row>
</template>
<div class="table-container">
<el-table
v-loading="loading"
border
:data="applicationList"
row-key="id"
class="scroll-table"
@selection-change="handleSelectionChange"
@sort-change="handleSortChange"
class="scroll-table"
max-height="650"
style="width: 100%"
:scroll-x="true"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="申请编号" align="center" prop="applicationNo" min-width="140" />
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="申请编号" align="center" prop="applicationNo" min-width="140" fixed="left" />
<el-table-column label="学号" align="center" prop="studentNo" min-width="120" />
<el-table-column label="学生姓名" align="center" prop="studentName" min-width="100" />
<el-table-column label="班级" align="center" prop="className" min-width="120" />
@@ -217,7 +220,7 @@
<span>{{ parseTime(scope.row.submitTime || scope.row.createTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="220">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="280" fixed="right">
<template #default="scope">
<el-tooltip content="查看详情" placement="top">
<el-button link type="primary" icon="View" @click="handleView(scope.row)"></el-button>
@@ -274,13 +277,13 @@
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination-container">
<pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @pagination="getList" />
</div>
</el-card>
<!-- 分页 -->
<div class="pagination-container">
<pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @pagination="getList" />
</div>
<!-- 添加或修改贫困生认定申请对话框 -->
<el-dialog :title="title" v-model="open" width="800px" append-to-body>
<el-form ref="applicationFormRef" :model="form" :rules="rules" label-width="120px">
@@ -1299,10 +1302,10 @@ onMounted(() => {
});
</script>
<style scoped lang="scss">
/* 继承后勤管理的样式 */
<style scoped>
/* 页面容器样式 */
.page-container {
padding: 20px;
padding: 8px;
}
.mb-10px {
@@ -1311,125 +1314,180 @@ onMounted(() => {
/* 统计卡片样式 */
.stat-card {
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
border: none;
border-radius: 8px;
height: 90px;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}
.stat-content {
display: flex;
align-items: center;
padding: 5px 12px;
height: 100%;
box-sizing: border-box;
.stat-icon {
font-size: 24px;
margin-right: 12px;
opacity: 0.8;
}
.stat-info {
flex: 1;
.stat-value {
font-size: 20px;
font-weight: bold;
line-height: 1;
margin-bottom: 3px;
}
.stat-label {
font-size: 13px;
opacity: 0.7;
line-height: 1;
}
}
}
&.stat-card-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
&.stat-card-success {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
color: white;
}
&.stat-card-warning {
background: linear-gradient(135deg, #ff9a56 0%, #ffad56 100%);
color: white;
}
&.stat-card-info {
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
color: #333;
}
}
/* 搜索卡片样式 */
.search-card {
.search-form {
margin-bottom: 0;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
/* 按钮卡片样式 */
.button-card {
.card-header-vertical {
display: flex;
justify-content: space-between;
align-items: center;
}
.toolbar-title {
font-size: 16px;
font-weight: 600;
color: #303133;
}
.button-group {
display: flex;
align-items: center;
gap: 10px;
}
.stat-card-warning {
background: linear-gradient(135deg, #ff9a56 0%, #ff6b6b 100%);
}
/* 表格卡片样式 */
.stat-card-success {
background: linear-gradient(135deg, #4ecdc4 0%, #44a08d 100%);
}
.stat-card-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.stat-card-info {
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
}
.stat-content {
display: flex;
align-items: center;
padding: 8px 0;
height: 100%;
box-sizing: border-box;
}
.stat-icon {
margin-right: 16px;
font-size: 24px;
color: rgba(255, 255, 255, 0.9);
}
.stat-info {
flex: 1;
}
.stat-value {
font-size: 24px;
font-weight: 700;
color: #fff;
line-height: 1.2;
margin-bottom: 4px;
}
.stat-label {
font-size: 14px;
color: rgba(255, 255, 255, 0.9);
line-height: 1.2;
}
/* 表格卡片布局 */
.table-card {
.table-container {
overflow-x: auto;
}
.scroll-table {
width: 100%;
:deep(.el-table__header-wrapper) {
.el-table__header {
th {
background-color: #f8f9fa;
color: #333;
font-weight: 600;
}
}
}
:deep(.el-table__body-wrapper) {
.el-table__row {
&:hover {
background-color: #f5f7fa;
}
}
}
}
margin-top: 0;
}
/* 操作按钮样式 */
.table-card :deep(.el-card__body) {
padding: 0px 20px 20px 20px !important;
}
/* 表格容器 - 支持横向滚动 */
.table-container {
width: 100%;
margin-bottom: 0;
}
/* 横向滚动表格样式 */
.scroll-table {
width: 100%;
}
/* 表格横向滚动条样式优化 */
.scroll-table :deep(.el-table__body-wrapper) {
overflow-x: auto !important; /* 自动显示横向滚动条 */
overflow-y: auto;
}
.scroll-table :deep(.el-table__header-wrapper) {
overflow-x: hidden;
}
/* 强制显示表格横向滚动条 - 适用于所有浏览器 */
.scroll-table :deep(.el-table__body-wrapper) {
scrollbar-width: thin; /* Firefox */
scrollbar-color: #409eff #f0f0f0; /* Firefox - thumb track */
}
/* Webkit 浏览器的横向滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 12px !important; /* 增加滚动条高度 */
width: 12px !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f0f0f0 !important;
border-radius: 6px !important;
border: 1px solid #e4e7ed !important; /* 添加边框使其更明显 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #409eff !important; /* 使用主题色,更明显 */
border-radius: 6px !important;
border: 2px solid #f0f0f0 !important; /* 添加白色边框 */
min-height: 20px !important; /* 确保最小高度 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #337ecc !important; /* 悬停时更深的蓝色 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
background: #2b6cb0 !important; /* 点击时的颜色 */
}
/* 确保滚动条始终可见的额外设置 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-corner {
background: #f0f0f0;
}
/* 固定列样式优化 */
.scroll-table :deep(.el-table__fixed-right) {
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
}
.scroll-table :deep(.el-table__fixed-left) {
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 确保固定列在滚动时有正确的背景色 */
.scroll-table :deep(.el-table__fixed-right .el-table__cell),
.scroll-table :deep(.el-table__fixed-left .el-table__cell) {
background-color: #fff;
}
/* 表格行悬停效果在固定列上的同步 */
.scroll-table :deep(.el-table__row:hover .el-table__cell) {
background-color: #f5f7fa !important;
}
/* 分页容器样式 */
.pagination-container {
display: flex;
justify-content: flex-end;
padding: 16px 20px 0 0 !important;
background-color: #fff;
margin-top: 0px !important;
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.search-btn-container .el-form-item {
margin-bottom: 0 !important;
}
/* 操作按钮样式 - 参考后勤管理模块 */
.action-buttons {
display: flex;
gap: 8px;
@@ -1439,15 +1497,6 @@ onMounted(() => {
flex-wrap: wrap;
}
/* 分页容器样式 */
.pagination-container {
display: flex;
justify-content: flex-end;
padding: 16px 20px 0 0;
background-color: #fff;
margin-top: 0px;
}
/* 对话框样式 */
.dialog-footer-right {
display: flex;
@@ -1463,15 +1512,24 @@ onMounted(() => {
}
/* 描述列表样式 */
:deep(.el-descriptions) {
.el-descriptions__label {
font-weight: 600;
color: #606266;
background-color: #fafafa;
:deep(.el-descriptions .el-descriptions__label) {
font-weight: 600;
color: #606266;
background-color: #fafafa;
}
:deep(.el-descriptions .el-descriptions__content) {
color: #303133;
}
/* 响应式设计 */
@media (max-width: 768px) {
.page-container {
padding: 10px;
}
.el-descriptions__content {
color: #303133;
.stat-card {
margin-bottom: 10px;
}
}
</style>
+213 -162
View File
@@ -1,5 +1,5 @@
<template>
<div class="p-2 page-container">
<div class="page-container">
<!-- 统计卡片区域 -->
<div class="mb-10px">
<el-row :gutter="16">
@@ -147,46 +147,52 @@
</div>
</transition>
<!-- 工具栏 -->
<el-card class="button-card mb-10px" shadow="never">
<div class="card-header-vertical">
<span class="toolbar-title">助学金申请审核管理</span>
<div class="button-group">
<el-button
type="success"
plain
icon="Check"
:disabled="multiple"
@click="handleBatchApprove"
v-hasPermi="['jcbt:povertyApplication:approve']"
>
批量通过
</el-button>
<el-button type="danger" plain icon="Close" :disabled="multiple" @click="handleBatchReject" v-hasPermi="['jcbt:povertyApplication:reject']">
批量驳回
</el-button>
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['jcbt:povertyApplication:export']"> 导出 </el-button>
<el-button type="info" plain icon="Download" @click="handleExportPending" v-hasPermi="['jcbt:povertyApplication:export']">
导出待审核
</el-button>
<right-toolbar :show-search="showSearch" @show-search="showSearch = $event" @query-table="getList"></right-toolbar>
</div>
</div>
</el-card>
<!-- 表格区域 -->
<el-card class="table-card" shadow="never">
<template #header>
<el-row class="mb-[10px]">
<el-col :span="1.5">
<el-button
type="success"
plain
icon="Check"
:disabled="multiple"
@click="handleBatchApprove"
v-hasPermi="['jcbt:povertyApplication:approve']"
>
批量通过
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Close" :disabled="multiple" @click="handleBatchReject" v-hasPermi="['jcbt:povertyApplication:reject']">
批量驳回
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['jcbt:povertyApplication:export']">导出</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Download" @click="handleExportPending" v-hasPermi="['jcbt:povertyApplication:export']">
导出待审核
</el-button>
</el-col>
<right-toolbar :show-search="showSearch" @show-search="showSearch = $event" @query-table="getList"></right-toolbar>
</el-row>
</template>
<div class="table-container">
<el-table
v-loading="loading"
border
:data="reviewList"
@selection-change="handleSelectionChange"
@sort-change="handleSortChange"
class="scroll-table"
max-height="650"
:scroll-x="true"
style="width: 100%"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="申请编号" align="center" prop="applicationNo" min-width="140" />
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="申请编号" align="center" prop="applicationNo" min-width="140" fixed="left" />
<el-table-column label="学号" align="center" prop="studentNo" min-width="120" />
<el-table-column label="学生姓名" align="center" prop="studentName" min-width="100" />
<el-table-column label="班级" align="center" prop="className" min-width="120" />
@@ -224,7 +230,7 @@
<span>{{ scope.row.auditTime ? parseTime(scope.row.auditTime, '{y}-{m}-{d} {h}:{i}') : '-' }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="280" fixed="right">
<template #default="scope">
<el-tooltip content="查看详情" placement="top">
<el-button link type="primary" icon="View" @click="handleView(scope.row)"></el-button>
@@ -865,10 +871,10 @@ onMounted(() => {
});
</script>
<style scoped lang="scss">
/* 继承后勤管理的样式 */
<style scoped>
/* 页面容器样式 */
.page-container {
padding: 20px;
padding: 8px;
}
.mb-10px {
@@ -880,121 +886,170 @@ onMounted(() => {
border-radius: 8px;
transition: all 0.3s ease;
height: 90px;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}
.stat-content {
display: flex;
align-items: center;
padding: 5px 12px;
height: 100%;
box-sizing: border-box;
.stat-icon {
font-size: 24px;
margin-right: 12px;
opacity: 0.8;
}
.stat-info {
flex: 1;
.stat-value {
font-size: 20px;
font-weight: bold;
line-height: 1;
margin-bottom: 3px;
}
.stat-label {
font-size: 13px;
opacity: 0.7;
line-height: 1;
}
}
}
&.stat-card-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
&.stat-card-success {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
color: white;
}
&.stat-card-warning {
background: linear-gradient(135deg, #ff9a56 0%, #ffad56 100%);
color: white;
}
&.stat-card-danger {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%);
color: white;
}
}
/* 按钮卡片样式 */
.button-card {
.card-header-vertical {
display: flex;
justify-content: space-between;
align-items: center;
}
.toolbar-title {
font-size: 16px;
font-weight: 600;
color: #303133;
}
.button-group {
display: flex;
align-items: center;
gap: 10px;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}
/* 表格卡片样式 */
.stat-card .stat-content {
display: flex;
align-items: center;
padding: 5px 12px;
height: 100%;
box-sizing: border-box;
}
.stat-card .stat-content .stat-icon {
font-size: 24px;
margin-right: 12px;
opacity: 0.8;
}
.stat-card .stat-content .stat-info {
flex: 1;
}
.stat-card .stat-content .stat-info .stat-value {
font-size: 20px;
font-weight: bold;
line-height: 1;
margin-bottom: 3px;
}
.stat-card .stat-content .stat-info .stat-label {
font-size: 13px;
opacity: 0.7;
line-height: 1;
}
.stat-card.stat-card-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.stat-card.stat-card-success {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
color: white;
}
.stat-card.stat-card-warning {
background: linear-gradient(135deg, #ff9a56 0%, #ffad56 100%);
color: white;
}
.stat-card.stat-card-danger {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%);
color: white;
}
/* 表格卡片布局 */
.table-card {
.table-container {
overflow-x: auto;
}
margin-top: 0;
}
.scroll-table {
width: 100%;
.table-card :deep(.el-card__body) {
padding: 0px 20px 20px 20px !important;
}
:deep(.el-table__header-wrapper) {
.el-table__header {
th {
background-color: #f8f9fa;
color: #333;
font-weight: 600;
}
}
}
/* 表格容器 - 支持横向滚动 */
.table-container {
width: 100%;
margin-bottom: 0;
}
:deep(.el-table__body-wrapper) {
.el-table__row {
&:hover {
background-color: #f5f7fa;
}
}
}
}
/* 横向滚动表格样式 */
.scroll-table {
width: 100%;
}
/* 表格横向滚动条样式优化 */
.scroll-table :deep(.el-table__body-wrapper) {
overflow-x: auto !important; /* 自动显示横向滚动条 */
overflow-y: auto;
}
.scroll-table :deep(.el-table__header-wrapper) {
overflow-x: hidden;
}
/* 强制显示表格横向滚动条 - 适用于所有浏览器 */
.scroll-table :deep(.el-table__body-wrapper) {
scrollbar-width: thin; /* Firefox */
scrollbar-color: #409eff #f0f0f0; /* Firefox - thumb track */
}
/* Webkit 浏览器的横向滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 12px !important; /* 增加滚动条高度 */
width: 12px !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f0f0f0 !important;
border-radius: 6px !important;
border: 1px solid #e4e7ed !important; /* 添加边框使其更明显 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #409eff !important; /* 使用主题色,更明显 */
border-radius: 6px !important;
border: 2px solid #f0f0f0 !important; /* 添加白色边框 */
min-height: 20px !important; /* 确保最小高度 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #337ecc !important; /* 悬停时更深的蓝色 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
background: #2b6cb0 !important; /* 点击时的颜色 */
}
/* 确保滚动条始终可见的额外设置 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-corner {
background: #f0f0f0;
}
/* 固定列样式优化 */
.scroll-table :deep(.el-table__fixed-right) {
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
}
.scroll-table :deep(.el-table__fixed-left) {
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 确保固定列在滚动时有正确的背景色 */
.scroll-table :deep(.el-table__fixed-right .el-table__cell),
.scroll-table :deep(.el-table__fixed-left .el-table__cell) {
background-color: #fff;
}
/* 表格行悬停效果在固定列上的同步 */
.scroll-table :deep(.el-table__row:hover .el-table__cell) {
background-color: #f5f7fa !important;
}
/* 分页容器样式 */
.pagination-container {
display: flex;
justify-content: flex-end;
padding: 16px 20px 0 0;
padding: 16px 20px 0 0 !important;
background-color: #fff;
margin-top: 0px;
margin-top: 0px !important;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.search-btn-container .el-form-item {
margin-bottom: 0 !important;
}
/* 对话框样式 */
@@ -1012,35 +1067,31 @@ onMounted(() => {
}
/* 附件预览样式 */
.attachment-preview {
.attachment-item {
text-align: center;
padding: 20px;
border: 1px solid #e4e7ed;
border-radius: 4px;
h4 {
margin-bottom: 15px;
color: #303133;
}
.no-attachment {
padding: 20px;
color: #909399;
}
}
.attachment-preview .attachment-item {
text-align: center;
padding: 20px;
border: 1px solid #e4e7ed;
border-radius: 4px;
}
/* 描述列表样式 */
:deep(.el-descriptions) {
.el-descriptions__label {
font-weight: 600;
color: #606266;
background-color: #fafafa;
}
.attachment-preview .attachment-item h4 {
margin-bottom: 15px;
color: #303133;
}
.el-descriptions__content {
color: #303133;
}
.attachment-preview .attachment-item .no-attachment {
padding: 20px;
color: #909399;
}
/* 描述列表样式优化 */
:deep(.el-descriptions .el-descriptions__label) {
font-weight: 600;
color: #606266;
width: 120px;
}
:deep(.el-descriptions .el-descriptions__content) {
color: #303133;
}
</style>
@@ -1,40 +1,102 @@
<template>
<div class="app-container">
<el-row :gutter="20">
<!-- 查询面板 -->
<el-col :span="24" class="search-container">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="80px">
<el-form-item label="学生姓名" prop="studentName">
<el-input v-model="queryParams.studentName" placeholder="请输入学生姓名" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="学号" prop="studentNo">
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="申请状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择申请状态" clearable>
<el-option label="草稿" value="draft" />
<el-option label="待审核" value="waiting" />
<el-option label="已完成" value="complete" />
<el-option label="已拒绝" value="rejected" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-col>
<div class="page-container">
<!-- 搜索区域 -->
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<!-- 第一行:4个搜索条件 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="学生姓名" prop="studentName">
<el-input v-model="queryParams.studentName" placeholder="请输入学生姓名" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="学号" prop="studentNo">
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="申请状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择申请状态" clearable style="width: 100%">
<el-option label="草稿" value="draft" />
<el-option label="待审核" value="waiting" />
<el-option label="已完成" value="complete" />
<el-option label="已拒绝" value="rejected" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="申请类型" prop="applicationType">
<el-select v-model="queryParams.applicationType" placeholder="请选择申请类型" clearable style="width: 100%">
<el-option label="教师推荐" value="RECOMMEND" />
<el-option label="学生申请" value="STUDENT" />
<el-option label="自动评定" value="AUTO" />
<el-option label="直接指定" value="DIRECT" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 工具栏 -->
<el-col :span="24">
<el-row :gutter="10" class="mb8">
<!-- 第二行:2个搜索条件 + 操作按钮 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="奖学金类型" prop="scholarshipTypeId">
<el-select v-model="queryParams.scholarshipTypeId" placeholder="请选择奖学金类型" clearable style="width: 100%">
<el-option
v-for="type in scholarshipTypeList"
:key="type.id"
:label="type.typeName"
:value="type.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="申请时间">
<el-date-picker
v-model="daterangeCreateTime"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="handleQuery"
style="width: 100%"
></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="10" class="mb8" >
<el-col :span="24" class="search-btn-container">
<el-form-item style="margin-bottom: 0;">
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
</transition>
<!-- 表格数据 -->
<el-card shadow="never" class="button-card table-card">
<template #header>
<el-row ref="toolbarRef" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['jcbt:scholarshipApplication:add']">班主任批量申请</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['jcbt:scholarshipApplication:edit']"
>修改</el-button
>
<el-button
type="success"
plain
icon="Edit"
:disabled="single"
@click="handleUpdate()"
v-hasPermi="['jcbt:scholarshipApplication:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
@@ -44,17 +106,25 @@
:disabled="multiple"
@click="handleDelete()"
v-hasPermi="['jcbt:scholarshipApplication:remove']"
>删除</el-button
>
>删除</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</el-col>
<!-- 数据表格 -->
<el-col :span="24">
<el-table v-loading="loading" :data="applicationList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
</template>
<div class="table-container">
<el-table
ref="scholarshipApplicationTableRef"
v-loading="loading"
border
:data="applicationList"
row-key="id"
class="scroll-table"
@selection-change="handleSelectionChange"
style="width: 100%"
:scroll-x="true"
>
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="学号" align="center" prop="studentNo" min-width="120" />
<el-table-column label="学生姓名" align="center" prop="studentName" min-width="100" />
<el-table-column label="学院" align="center" prop="deptName" min-width="120" />
@@ -86,7 +156,7 @@
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="160">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="160" fixed="right">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button
@@ -109,10 +179,13 @@
</template>
</el-table-column>
</el-table>
</div>
</el-card>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-col>
</el-row>
<!-- 分页 -->
<div class="pagination-container">
<pagination v-show="total > 0" :page="queryParams.pageNum" :limit="queryParams.pageSize" :total="total" @pagination="getList" />
</div>
<!-- 班主任批量申请奖学金对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="1000px" append-to-body>
@@ -1079,6 +1152,124 @@ onMounted(() => {
</script>
<style scoped>
/* 页面容器样式 */
.page-container {
padding: 8px;
}
/* 表格卡片布局 */
.table-card {
margin-top: 0;
}
.table-card :deep(.el-card__body) {
padding: 0px 20px 20px 20px !important;
}
/* 表格容器 - 支持横向滚动 */
.table-container {
width: 100%;
margin-bottom: 0;
}
/* 横向滚动表格样式 */
.scroll-table {
width: 100%;
}
/* 表格横向滚动条样式优化 */
.scroll-table :deep(.el-table__body-wrapper) {
overflow-x: auto !important; /* 自动显示横向滚动条 */
overflow-y: auto;
}
.scroll-table :deep(.el-table__header-wrapper) {
overflow-x: hidden;
}
/* 强制显示表格横向滚动条 - 适用于所有浏览器 */
.scroll-table :deep(.el-table__body-wrapper) {
scrollbar-width: thin; /* Firefox */
scrollbar-color: #409eff #f0f0f0; /* Firefox - thumb track */
}
/* Webkit 浏览器的横向滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 12px !important; /* 增加滚动条高度 */
width: 12px !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f0f0f0 !important;
border-radius: 6px !important;
border: 1px solid #e4e7ed !important; /* 添加边框使其更明显 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #409eff !important; /* 使用主题色,更明显 */
border-radius: 6px !important;
border: 2px solid #f0f0f0 !important; /* 添加白色边框 */
min-height: 20px !important; /* 确保最小高度 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #337ecc !important; /* 悬停时更深的蓝色 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
background: #2b6cb0 !important; /* 点击时的颜色 */
}
/* 确保滚动条始终可见的额外设置 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-corner {
background: #f0f0f0;
}
/* 固定列样式优化 */
.scroll-table :deep(.el-table__fixed-right) {
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
}
.scroll-table :deep(.el-table__fixed-left) {
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 确保固定列在滚动时有正确的背景色 */
.scroll-table :deep(.el-table__fixed-right .el-table__cell),
.scroll-table :deep(.el-table__fixed-left .el-table__cell) {
background-color: #fff;
}
/* 表格行悬停效果在固定列上的同步 */
.scroll-table :deep(.el-table__row:hover .el-table__cell) {
background-color: #f5f7fa !important;
}
/* 分页容器样式 */
.pagination-container {
display: flex;
justify-content: flex-end;
padding: 16px 20px 0 0 !important;
background-color: #fff;
margin-top: 0px !important;
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.search-btn-container .el-form-item {
margin-bottom: 0 !important;
}
.search-container {
background: #fff;
padding: 20px;
+317 -123
View File
@@ -1,5 +1,5 @@
<template>
<div class="p-2 page-container">
<div class="page-container">
<!-- 搜索区域 -->
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
@@ -38,7 +38,7 @@
</el-col>
</el-row>
<!-- 第二行:3个搜索条件 + 操作按钮 -->
<!-- 第二行:3个搜索条件 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="批次类型" prop="batchType">
@@ -73,8 +73,10 @@
></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item>
</el-row>
<el-row :gutter="10" class="mb8">
<el-col :span="24" class="search-btn-container">
<el-form-item style="margin-bottom: 0;">
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
@@ -146,131 +148,158 @@
<!-- 表格区域 -->
<el-card shadow="never" class="button-card table-card">
<template #header>
<el-row :gutter="10" class="mb8">
<el-row ref="toolbarRef" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['jcbt:scholarshipBatch:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['jcbt:scholarshipBatch:edit']">修改</el-button>
<el-button
type="success"
plain
icon="Edit"
:disabled="single"
@click="handleUpdate()"
v-hasPermi="['jcbt:scholarshipBatch:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['jcbt:scholarshipBatch:remove']">删除</el-button>
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete()"
v-hasPermi="['jcbt:scholarshipBatch:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="Download" @click="handleExport" v-hasPermi="['jcbt:scholarshipBatch:export']">导出</el-button>
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['jcbt:scholarshipBatch:export']"
>导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="batchList" @selection-change="handleSelectionChange" @row-dblclick="handleView">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="批次编码" align="center" prop="batchCode" min-width="120" />
<el-table-column label="批次名称" align="center" prop="batchName" min-width="150" show-overflow-tooltip />
<el-table-column label="学年" align="center" prop="academicYear" width="120" />
<el-table-column label="学期" align="center" prop="semester" width="80">
<template #default="scope">
<span v-if="scope.row.semester === 1">第一学期</span>
<span v-else-if="scope.row.semester === 2">第二学期</span>
<span v-else-if="scope.row.semester === 3">全年</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="批次类型" align="center" prop="batchType" width="100">
<template #default="scope">
<el-tag v-if="scope.row.batchType === 'REGULAR'" type="primary">常规批次</el-tag>
<el-tag v-else-if="scope.row.batchType === 'SPECIAL'" type="warning">专项批次</el-tag>
<el-tag v-else-if="scope.row.batchType === 'SUPPLEMENTARY'" type="info">补充批次</el-tag>
<el-tag v-else type="info">未知</el-tag>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status" width="100">
<template #default="scope">
<el-tag v-if="scope.row.status === 'DRAFT'" type="info">草稿</el-tag>
<el-tag v-else-if="scope.row.status === 'ACTIVE'" type="success">进行中</el-tag>
<el-tag v-else-if="scope.row.status === 'COMPLETED'" type="primary">已完成</el-tag>
<el-tag v-else-if="scope.row.status === 'CANCELLED'" type="danger">已取消</el-tag>
<el-tag v-else type="info">未知</el-tag>
</template>
</el-table-column>
<el-table-column label="申请时间" align="center" width="200">
<template #default="scope">
<div v-if="scope.row.applyStartTime && scope.row.applyEndTime">
<div>{{ parseTime(scope.row.applyStartTime, '{y}-{m}-{d}') }}</div>
<div style="color: #909399; font-size: 12px">至 {{ parseTime(scope.row.applyEndTime, '{y}-{m}-{d}') }}</div>
</div>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="审核时间" align="center" width="200">
<template #default="scope">
<div v-if="scope.row.reviewStartTime && scope.row.reviewEndTime">
<div>{{ parseTime(scope.row.reviewStartTime, '{y}-{m}-{d}') }}</div>
<div style="color: #909399; font-size: 12px">至 {{ parseTime(scope.row.reviewEndTime, '{y}-{m}-{d}') }}</div>
</div>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="280">
<template #default="scope">
<el-tooltip content="查看" placement="top">
<el-button
link
type="primary"
icon="View"
size="default"
style="font-size: 16px; margin-right: 8px; color: blue"
@click="handleView(scope.row)"
v-hasPermi="['jcbt:scholarshipBatch:query']"
></el-button>
</el-tooltip>
<el-tooltip content="修改" placement="top">
<el-button
link
type="primary"
icon="Edit"
size="default"
style="font-size: 16px; margin-right: 8px"
@click="handleUpdate(scope.row)"
v-hasPermi="['jcbt:scholarshipBatch:edit']"
></el-button>
</el-tooltip>
<el-tooltip content="状态变更" placement="top">
<el-button
link
type="warning"
icon="Switch"
size="default"
style="font-size: 16px; margin-right: 8px"
@click="handleStatusChange(scope.row)"
v-hasPermi="['jcbt:scholarshipBatch:edit']"
></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button
link
type="danger"
icon="Delete"
size="default"
style="font-size: 16px"
@click="handleDelete(scope.row)"
v-hasPermi="['jcbt:scholarshipBatch:remove']"
></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
<div class="table-container">
<el-table
ref="batchTableRef"
v-loading="loading"
border
:data="batchList"
row-key="id"
class="scroll-table"
@selection-change="handleSelectionChange"
@row-dblclick="handleView"
style="width: 100%"
:scroll-x="true"
>
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="批次编码" align="center" prop="batchCode" min-width="120" />
<el-table-column label="批次名称" align="center" prop="batchName" min-width="150" show-overflow-tooltip />
<el-table-column label="学年" align="center" prop="academicYear" width="120" />
<el-table-column label="学期" align="center" prop="semester" width="80">
<template #default="scope">
<span v-if="scope.row.semester === 1">第一学期</span>
<span v-else-if="scope.row.semester === 2">第二学期</span>
<span v-else-if="scope.row.semester === 3">全年</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="批次类型" align="center" prop="batchType" width="100">
<template #default="scope">
<el-tag v-if="scope.row.batchType === 'REGULAR'" type="primary">常规批次</el-tag>
<el-tag v-else-if="scope.row.batchType === 'SPECIAL'" type="warning">专项批次</el-tag>
<el-tag v-else-if="scope.row.batchType === 'SUPPLEMENTARY'" type="info">补充批次</el-tag>
<el-tag v-else type="info">未知</el-tag>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status" width="100">
<template #default="scope">
<el-tag v-if="scope.row.status === 'DRAFT'" type="info">草稿</el-tag>
<el-tag v-else-if="scope.row.status === 'ACTIVE'" type="success">进行中</el-tag>
<el-tag v-else-if="scope.row.status === 'COMPLETED'" type="primary">已完成</el-tag>
<el-tag v-else-if="scope.row.status === 'CANCELLED'" type="danger">已取消</el-tag>
<el-tag v-else type="info">未知</el-tag>
</template>
</el-table-column>
<el-table-column label="申请时间" align="center" width="200">
<template #default="scope">
<div v-if="scope.row.applyStartTime && scope.row.applyEndTime">
<div>{{ parseTime(scope.row.applyStartTime, '{y}-{m}-{d}') }}</div>
<div style="color: #909399; font-size: 12px">至 {{ parseTime(scope.row.applyEndTime, '{y}-{m}-{d}') }}</div>
</div>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="审核时间" align="center" width="200">
<template #default="scope">
<div v-if="scope.row.reviewStartTime && scope.row.reviewEndTime">
<div>{{ parseTime(scope.row.reviewStartTime, '{y}-{m}-{d}') }}</div>
<div style="color: #909399; font-size: 12px">至 {{ parseTime(scope.row.reviewEndTime, '{y}-{m}-{d}') }}</div>
</div>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="280" fixed="right">
<template #default="scope">
<el-tooltip content="查看" placement="top">
<el-button
link
type="info"
icon="View"
@click="handleView(scope.row)"
v-hasPermi="['jcbt:scholarshipBatch:query']"
></el-button>
</el-tooltip>
<el-tooltip content="修改" placement="top">
<el-button
link
type="primary"
icon="Edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['jcbt:scholarshipBatch:edit']"
></el-button>
</el-tooltip>
<el-tooltip content="状态变更" placement="top">
<el-button
link
type="warning"
icon="Switch"
@click="handleStatusChange(scope.row)"
v-hasPermi="['jcbt:scholarshipBatch:edit']"
></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button
link
type="danger"
icon="Delete"
@click="handleDelete(scope.row)"
v-hasPermi="['jcbt:scholarshipBatch:remove']"
></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
</el-card>
<!-- 分页 -->
<div class="pagination-container">
<pagination v-show="total > 0" :page="queryParams.pageNum" :limit="queryParams.pageSize" :total="total" @pagination="getList" />
</div>
<!-- 添加或修改批次对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="1200px" append-to-body>
<el-form ref="batchFormRef" :model="form" :rules="rules" label-width="120px">
@@ -518,6 +547,10 @@
</template>
<script setup name="ScholarshipBatch" lang="ts">
import { reactive, ref, onMounted, getCurrentInstance, toRefs } from 'vue';
import type { ComponentInternalInstance } from 'vue';
import type { FormInstance, TableInstance } from 'element-plus';
import { parseTime } from '@/utils/ruoyi';
import {
listScholarshipBatch,
getScholarshipBatch,
@@ -533,8 +566,19 @@ import {
} from '@/api/jcbt/scholarshipBatch';
import { listScholarshipType, type ScholarshipTypeVO } from '@/api/jcbt/scholarshipType';
const { proxy } = getCurrentInstance()!;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
// 类型定义
interface DialogOption {
visible: boolean;
title: string;
}
interface PageData<T, U> {
form: T;
queryParams: U;
rules: any;
}
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const batchList = ref<ScholarshipBatchVO[]>([]);
const loading = ref(true);
@@ -545,8 +589,10 @@ const multiple = ref(true);
const total = ref(0);
const buttonLoading = ref(false);
const queryFormRef = ref<ElFormInstance>();
const batchFormRef = ref<ElFormInstance>();
const queryFormRef = ref<FormInstance>();
const batchFormRef = ref<FormInstance>();
const toolbarRef = ref<HTMLElement>();
const batchTableRef = ref<TableInstance>();
const dialog = reactive<DialogOption>({
visible: false,
@@ -612,7 +658,7 @@ const data = reactive<PageData<ScholarshipBatchForm, ScholarshipBatchQuery>>({
const { queryParams, form, rules } = toRefs(data);
// 时间范围
const daterangeCreateTime = ref<[DateModelType, DateModelType]>(['', '']);
const daterangeCreateTime = ref<[string, string]>(['', '']);
// 状态变更表单
const statusForm = ref({
@@ -867,12 +913,165 @@ onMounted(() => {
</script>
<style scoped>
/* 页面容器样式 */
.page-container {
padding: 8px;
}
/* 表格卡片布局 */
.table-card {
margin-top: 0;
}
.table-card :deep(.el-card__body) {
padding: 0px 20px 20px 20px !important;
}
/* 表格容器 - 支持横向滚动 */
.table-container {
width: 100%;
margin-bottom: 0;
}
/* 横向滚动表格样式 */
.scroll-table {
width: 100%;
}
/* 表格横向滚动条样式优化 */
.scroll-table :deep(.el-table__body-wrapper) {
overflow-x: auto !important; /* 自动显示横向滚动条 */
overflow-y: auto;
}
.scroll-table :deep(.el-table__header-wrapper) {
overflow-x: hidden;
}
/* 强制显示表格横向滚动条 - 适用于所有浏览器 */
.scroll-table :deep(.el-table__body-wrapper) {
scrollbar-width: thin; /* Firefox */
scrollbar-color: #409eff #f0f0f0; /* Firefox - thumb track */
}
/* Webkit 浏览器的横向滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 12px !important; /* 增加滚动条高度 */
width: 12px !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f0f0f0 !important;
border-radius: 6px !important;
border: 1px solid #e4e7ed !important; /* 添加边框使其更明显 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #409eff !important; /* 使用主题色,更明显 */
border-radius: 6px !important;
border: 2px solid #f0f0f0 !important; /* 添加白色边框 */
min-height: 20px !important; /* 确保最小高度 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #337ecc !important; /* 悬停时更深的蓝色 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
background: #2b6cb0 !important; /* 点击时的颜色 */
}
/* 确保滚动条始终可见的额外设置 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-corner {
background: #f0f0f0;
}
/* 固定列样式优化 */
.scroll-table :deep(.el-table__fixed-right) {
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
}
.scroll-table :deep(.el-table__fixed-left) {
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 确保固定列在滚动时有正确的背景色 */
.scroll-table :deep(.el-table__fixed-right .el-table__cell),
.scroll-table :deep(.el-table__fixed-left .el-table__cell) {
background-color: #fff;
}
/* 表格行悬停效果在固定列上的同步 */
.scroll-table :deep(.el-table__row:hover .el-table__cell) {
background-color: #f5f7fa !important;
}
/* 分页容器样式 */
.pagination-container {
display: flex;
justify-content: flex-end;
padding: 16px 20px 0 0 !important;
background-color: #fff;
margin-top: 0px !important;
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
/* 对话框样式 */
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.dialog-form {
.el-form-item {
margin-bottom: 18px;
}
.form-tip {
font-size: 12px;
color: #909399;
margin-top: 4px;
line-height: 1.4;
}
}
/* 描述列表样式优化 */
:deep(.el-descriptions) {
.el-descriptions__label {
font-weight: 600;
color: #606266;
width: 120px;
}
.el-descriptions__content {
color: #303133;
}
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
}
/* 统计卡片样式 */
.form-help-text {
font-size: 12px;
color: #909399;
margin-top: 4px;
line-height: 1.4;
}
.stats-row {
margin-bottom: 16px;
}
@@ -958,11 +1157,6 @@ onMounted(() => {
color: #909399;
}
.pagination-container {
margin-top: 16px;
text-align: center;
}
.description-cell {
max-width: 200px;
overflow: hidden;
+269 -369
View File
@@ -1,252 +1,206 @@
<template>
<div class="page-container">
<!-- 统计卡片区域 -->
<el-row :gutter="20" class="stats-row">
<el-col :span="6">
<div class="stat-card stat-card-primary">
<div class="stat-content">
<div class="stat-icon">
<el-icon><TrendCharts /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ totalQuota }}</div>
<div class="stat-label">总名额</div>
</div>
</div>
</div>
</el-col>
<el-col :span="6">
<div class="stat-card stat-card-success">
<div class="stat-content">
<div class="stat-icon">
<el-icon><Check /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ usedQuota }}</div>
<div class="stat-label">已使用</div>
</div>
</div>
</div>
</el-col>
<el-col :span="6">
<div class="stat-card stat-card-warning">
<div class="stat-content">
<div class="stat-icon">
<el-icon><Opportunity /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ availableQuota }}</div>
<div class="stat-label">可用名额</div>
</div>
</div>
</div>
</el-col>
<el-col :span="6">
<div class="stat-card stat-card-info">
<div class="stat-content">
<div class="stat-icon">
<el-icon><DataBoard /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ usageRate }}%</div>
<div class="stat-label">使用率</div>
</div>
</div>
</div>
</el-col>
</el-row>
<!-- 搜索区域 -->
<el-card class="button-card" v-show="showSearch">
<template #header>
<div class="card-header">
<span class="toolbar-title">名额分配查询</span>
</div>
</template>
<el-form :model="queryParams" ref="queryRef" label-width="100px">
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="奖学金批次" prop="batchId">
<el-select v-model="queryParams.batchId" placeholder="请选择奖学金批次" clearable style="width: 100%" @change="handleBatchChange">
<el-option v-for="batch in batchList" :key="batch.id" :label="batch.batchName" :value="batch.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="奖学金类型" prop="scholarshipTypeId">
<el-select v-model="queryParams.scholarshipTypeId" placeholder="请选择奖学金类型" clearable style="width: 100%">
<el-option v-for="type in scholarshipTypeList" :key="type.id" :label="type.name" :value="type.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="分配级别" prop="allocationLevel">
<el-select v-model="queryParams.allocationLevel" placeholder="请选择分配级别" clearable style="width: 100%">
<el-option label="全校" value="UNIVERSITY" />
<el-option label="学院" value="COLLEGE" />
<el-option label="专业" value="MAJOR" />
<el-option label="班级" value="CLASS" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 100%">
<el-option label="激活" value="ACTIVE" />
<el-option label="停用" value="INACTIVE" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="目标部门" prop="targetDeptId">
<el-tree-select
v-model="queryParams.targetDeptId"
:data="deptOptions"
:props="{ value: 'id', label: 'label', children: 'children' }"
value-key="id"
placeholder="请选择目标部门"
check-strictly
clearable
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="锁定状态" prop="isLocked">
<el-select v-model="queryParams.isLocked" placeholder="请选择锁定状态" clearable style="width: 100%">
<el-option label="未锁定" :value="0" />
<el-option label="已锁定" :value="1" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryRef" :model="queryParams" label-width="100px">
<!-- 第一行:4个搜索条件 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="奖学金批次" prop="batchId">
<el-select v-model="queryParams.batchId" placeholder="请选择奖学金批次" clearable style="width: 100%" @change="handleBatchChange">
<el-option v-for="batch in batchList" :key="batch.id" :label="batch.batchName" :value="batch.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="奖学金类型" prop="scholarshipTypeId">
<el-select v-model="queryParams.scholarshipTypeId" placeholder="请选择奖学金类型" clearable style="width: 100%">
<el-option v-for="type in scholarshipTypeList" :key="type.id" :label="type.name" :value="type.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="分配级别" prop="allocationLevel">
<el-select v-model="queryParams.allocationLevel" placeholder="请选择分配级别" clearable style="width: 100%">
<el-option label="全校" value="UNIVERSITY" />
<el-option label="学院" value="COLLEGE" />
<el-option label="专业" value="MAJOR" />
<el-option label="班级" value="CLASS" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 100%">
<el-option label="激活" value="ACTIVE" />
<el-option label="停用" value="INACTIVE" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 操作按钮区域 -->
<el-card class="button-card">
<!-- 第二行:2个搜索条件 + 操作按钮 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="目标部门" prop="targetDeptId">
<el-tree-select
v-model="queryParams.targetDeptId"
:data="deptOptions"
:props="{ value: 'id', label: 'label', children: 'children' }"
value-key="id"
placeholder="请选择目标部门"
check-strictly
clearable
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="锁定状态" prop="isLocked">
<el-select v-model="queryParams.isLocked" placeholder="请选择锁定状态" clearable style="width: 100%">
<el-option label="未锁定" :value="0" />
<el-option label="已锁定" :value="1" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="10" class="mb8">
<el-col :span="24" class="search-btn-container">
<el-form-item style="margin-bottom: 0;">
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
</transition>
<!-- 表格数据 -->
<el-card shadow="never" class="button-card table-card">
<template #header>
<div class="card-header">
<span class="toolbar-title">名额分配管理</span>
<div class="toolbar-actions">
<el-row ref="toolbarRef" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['jcbt:scholarshipQuota:add']">新增</el-button>
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['jcbt:scholarshipQuota:edit']"
>修改</el-button
>
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['jcbt:scholarshipQuota:remove']"
>删除</el-button
>
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['jcbt:scholarshipQuota:export']">导出</el-button>
<el-button type="info" plain icon="Operation" @click="handleBatchAllocate" v-hasPermi="['jcbt:scholarshipQuota:add']">批量分配</el-button>
<el-button type="primary" plain icon="Refresh" @click="handleAutoAllocate" v-hasPermi="['jcbt:scholarshipQuota:edit']" v-if="false"
>自动分配</el-button
>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</div>
</div>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="Edit"
:disabled="single"
@click="handleUpdate()"
v-hasPermi="['jcbt:scholarshipQuota:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete()"
v-hasPermi="['jcbt:scholarshipQuota:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['jcbt:scholarshipQuota:export']"
>导出</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="Operation"
@click="handleBatchAllocate"
v-hasPermi="['jcbt:scholarshipQuota:add']"
>批量分配</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
</el-card>
<!-- 数据表格 -->
<el-card class="table-card">
<div class="table-container">
<el-table
ref="scholarshipQuotaTableRef"
v-loading="loading"
border
:data="scholarshipQuotaList"
@selection-change="handleSelectionChange"
class="scroll-table"
row-key="id"
class="scroll-table"
@selection-change="handleSelectionChange"
style="width: 100%"
:scroll-x="true"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="奖学金批次" align="center" prop="batchName" width="150" />
<el-table-column label="奖学金类型" align="center" prop="scholarshipTypeName" width="150" />
<el-table-column label="分配级别" align="center" prop="allocationLevelName" width="100" />
<el-table-column label="目标部门" align="center" prop="targetDeptName" width="150" />
<el-table-column label="总名额" align="center" prop="totalQuota" width="80" />
<el-table-column label="已使用" align="center" prop="usedQuota" width="80">
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="奖学金批次" prop="batchName" min-width="150" show-overflow-tooltip />
<el-table-column label="奖学金类型" prop="scholarshipTypeName" min-width="150" show-overflow-tooltip />
<el-table-column label="分配级别" prop="allocationLevelName" width="100" align="center" />
<el-table-column label="目标部门" prop="targetDeptName" min-width="150" show-overflow-tooltip />
<el-table-column label="总名额" prop="totalQuota" width="80" align="center" />
<el-table-column label="已使用" prop="usedQuota" width="80" align="center">
<template #default="scope">
<span :class="{ 'text-danger': scope.row.usedQuota > scope.row.totalQuota }">
{{ scope.row.usedQuota || 0 }}
</span>
</template>
</el-table-column>
<el-table-column label="可用名额" align="center" prop="availableQuota" width="80">
<el-table-column label="可用名额" prop="availableQuota" width="90" align="center">
<template #default="scope">
<span :class="{ 'text-success': scope.row.availableQuota > 0, 'text-danger': scope.row.availableQuota <= 0 }">
{{ scope.row.availableQuota || 0 }}
</span>
</template>
</el-table-column>
<el-table-column label="预留名额" align="center" prop="reservedQuota" width="80" />
<el-table-column label="分配依据" align="center" prop="allocationBasis" min-width="150" show-overflow-tooltip />
<el-table-column label="状态" align="center" prop="statusName" width="80" />
<el-table-column label="锁定状态" align="center" prop="isLocked" width="100">
<el-table-column label="预留名额" prop="reservedQuota" width="90" align="center" />
<el-table-column label="分配依据" prop="allocationBasis" min-width="150" show-overflow-tooltip />
<el-table-column label="状态" prop="statusName" width="80" align="center" />
<el-table-column label="锁定状态" prop="isLocked" width="100" align="center">
<template #default="scope">
<el-tag :type="scope.row.isLocked === 1 ? 'danger' : 'success'">
{{ scope.row.isLocked === 1 ? '已锁定' : '未锁定' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="160" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200">
<el-table-column label="创建时间" prop="createTime" width="180" align="center">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button
link
type="primary"
icon="Edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['jcbt:scholarshipQuota:edit']"
:disabled="scope.row.isLocked === 1"
></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button
link
type="danger"
icon="Delete"
@click="handleDelete(scope.row)"
v-hasPermi="['jcbt:scholarshipQuota:remove']"
:disabled="scope.row.isLocked === 1"
></el-button>
</el-tooltip>
<el-tooltip content="锁定/解锁" placement="top">
<el-button
link
:type="scope.row.isLocked === 1 ? 'warning' : 'info'"
:icon="scope.row.isLocked === 1 ? 'Unlock' : 'Lock'"
@click="handleLock(scope.row)"
v-hasPermi="['jcbt:scholarshipQuota:edit']"
></el-button>
</el-tooltip>
<el-tooltip content="调整名额" placement="top">
<el-button
link
type="success"
icon="Switch"
@click="handleAdjust(scope.row)"
v-hasPermi="['jcbt:scholarshipQuota:edit']"
:disabled="scope.row.isLocked === 1"
></el-button>
</el-tooltip>
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="280" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['jcbt:scholarshipQuota:edit']" :disabled="scope.row.isLocked === 1"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['jcbt:scholarshipQuota:remove']" :disabled="scope.row.isLocked === 1"></el-button>
</el-tooltip>
<el-tooltip content="锁定/解锁" placement="top">
<el-button link :type="scope.row.isLocked === 1 ? 'warning' : 'info'" :icon="scope.row.isLocked === 1 ? 'Unlock' : 'Lock'" @click="handleLock(scope.row)" v-hasPermi="['jcbt:scholarshipQuota:edit']"></el-button>
</el-tooltip>
<el-tooltip content="调整名额" placement="top">
<el-button link type="success" icon="Switch" @click="handleAdjust(scope.row)" v-hasPermi="['jcbt:scholarshipQuota:edit']" :disabled="scope.row.isLocked === 1"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 分页 -->
<div class="pagination-container">
<pagination v-show="total > 0" :page="queryParams.pageNum" :limit="queryParams.pageSize" :total="total" @pagination="getList" />
</div>
<!-- 添加或修改名额分配对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="600px" append-to-body>
<el-form ref="scholarshipQuotaFormRef" :model="form" :rules="rules" label-width="120px">
@@ -475,7 +429,10 @@
</template>
<script setup name="ScholarshipQuota" lang="ts">
import { ref, reactive, onMounted, computed } from 'vue';
import { ref, reactive, onMounted, computed, getCurrentInstance, toRefs } from 'vue';
import type { ComponentInternalInstance } from 'vue';
import type { FormRules, FormInstance, TableInstance } from 'element-plus';
import { parseTime } from '@/utils/ruoyi';
import {
listScholarshipQuota,
getScholarshipQuota,
@@ -495,16 +452,22 @@ import { getScholarshipTypesByBatch } from '@/api/jcbt/scholarshipApplication';
import { deptTreeSelect } from '@/api/system/user';
import type { ScholarshipQuotaVO, ScholarshipQuotaForm, ScholarshipQuotaQuery, BatchAllocateQuota } from '@/api/jcbt/scholarshipQuota';
const { proxy } = getCurrentInstance() as any;
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
interface DialogOption {
visible: boolean;
title: string;
}
// 表单ref定义
const queryRef = ref();
const scholarshipQuotaFormRef = ref();
const batchAllocateFormRef = ref();
const adjustFormRef = ref();
const queryRef = ref<FormInstance>();
const scholarshipQuotaFormRef = ref<FormInstance>();
const batchAllocateFormRef = ref<FormInstance>();
const adjustFormRef = ref<FormInstance>();
const scholarshipQuotaTableRef = ref<TableInstance>();
// 字典数据
const { allocation_level, quota_status } = toRefs<any>(proxy.useDict('allocation_level', 'quota_status'));
// const { allocation_level, quota_status } = toRefs<any>(proxy.useDict('allocation_level', 'quota_status'));
// 遮罩层
const loading = ref(true);
@@ -1007,80 +970,13 @@ onMounted(() => {
});
</script>
<style scoped lang="scss">
/* 继承后勤管理模块的样式风格 */
<style scoped>
/* 页面容器样式 */
.page-container {
padding: 8px;
}
/* 统计卡片样式 */
.stats-row {
margin-bottom: 20px;
}
.stat-card {
border-radius: 12px;
transition: all 0.3s ease;
height: 90px;
cursor: pointer;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.stat-content {
display: flex;
align-items: center;
padding: 5px 16px;
height: 100%;
box-sizing: border-box;
.stat-icon {
font-size: 28px;
margin-right: 15px;
opacity: 0.9;
}
.stat-info {
flex: 1;
.stat-value {
font-size: 24px;
font-weight: bold;
line-height: 1;
margin-bottom: 4px;
}
.stat-label {
font-size: 14px;
opacity: 0.8;
line-height: 1;
}
}
}
&.stat-card-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
&.stat-card-success {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
color: white;
}
&.stat-card-warning {
background: linear-gradient(135deg, #ff9a56 0%, #ffad56 100%);
color: white;
}
&.stat-card-info {
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
color: #333;
}
}
/* 表格卡片布局 - 参考后勤管理功能 */
/* 表格卡片布局 */
.table-card {
margin-top: 0;
}
@@ -1089,105 +985,100 @@ onMounted(() => {
padding: 0px 20px 20px 20px !important;
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
/* 表格容器 - 支持横向滚动 */
.table-container {
width: 100%;
margin-bottom: 0;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
/* 横向滚动表格样式 */
.scroll-table {
width: 100%;
}
.toolbar-title {
font-size: 18px;
font-weight: 600;
color: #1e293b;
display: flex;
align-items: center;
/* 表格横向滚动条样式优化 */
.scroll-table :deep(.el-table__body-wrapper) {
overflow-x: auto !important; /* 自动显示横向滚动条 */
overflow-y: auto;
}
&::before {
content: '';
width: 4px;
height: 20px;
background: linear-gradient(135deg, rgb(93, 135, 255), rgb(73, 190, 255));
border-radius: 2px;
margin-right: 12px;
}
}
.scroll-table :deep(.el-table__header-wrapper) {
overflow-x: hidden;
}
.toolbar-actions {
display: flex;
gap: 12px;
align-items: center;
}
}
/* 强制显示表格横向滚动条 - 适用于所有浏览器 */
.scroll-table :deep(.el-table__body-wrapper) {
scrollbar-width: thin; /* Firefox */
scrollbar-color: #409eff #f0f0f0; /* Firefox - thumb track */
}
/* 表格主体 */
.table-container {
width: 100%;
margin-bottom: 0;
}
/* 表格样式 - 参考后勤管理功能 */
.scroll-table {
width: 100%;
:deep(.el-table__header-wrapper) {
.el-table__header {
th {
background-color: #f8f9fa;
color: #333;
font-weight: 600;
}
}
}
:deep(.el-table__body-wrapper) {
.el-table__row {
&:hover {
background-color: #f5f7fa;
}
}
}
}
/* 表格横向滚动条样式优化 - 参考后勤管理功能 */
.scroll-table :deep(.el-table__body-wrapper) {
overflow-x: auto !important;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #409eff #f0f0f0;
}
.scroll-table :deep(.el-table__header-wrapper) {
overflow-x: hidden;
}
/* Webkit 浏览器的横向滚动条样式 */
/* Webkit 浏览器的横向滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 12px !important;
width: 12px !important;
height: 12px !important; /* 增加滚动条高度 */
width: 12px !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f0f0f0 !important;
border-radius: 6px !important;
border: 1px solid #e4e7ed !important;
background: #f0f0f0 !important;
border-radius: 6px !important;
border: 1px solid #e4e7ed !important; /* 添加边框使其更明显 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #409eff !important;
border-radius: 6px !important;
border: 2px solid #f0f0f0 !important;
min-height: 20px !important;
background: #409eff !important; /* 使用主题色,更明显 */
border-radius: 6px !important;
border: 2px solid #f0f0f0 !important; /* 添加白色边框 */
min-height: 20px !important; /* 确保最小高度 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #337ecc !important;
background: #337ecc !important; /* 悬停时更深的蓝色 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
background: #2b6cb0 !important; /* 点击时的颜色 */
}
/* 确保滚动条始终可见的额外设置 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-corner {
background: #f0f0f0;
}
/* 固定列样式优化 */
.scroll-table :deep(.el-table__fixed-right) {
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
}
.scroll-table :deep(.el-table__fixed-left) {
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 确保固定列在滚动时有正确的背景色 */
.scroll-table :deep(.el-table__fixed-right .el-table__cell),
.scroll-table :deep(.el-table__fixed-left .el-table__cell) {
background-color: #fff;
}
/* 表格行悬停效果在固定列上的同步 */
.scroll-table :deep(.el-table__row:hover .el-table__cell) {
background-color: #f5f7fa !important;
}
/* 分页容器样式 */
.pagination-container {
display: flex;
justify-content: flex-end;
padding: 16px 20px 0 0 !important;
background-color: #fff;
margin-top: 0px !important;
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
/* 文本颜色样式 */
.text-danger {
color: #f56c6c;
}
@@ -1195,4 +1086,13 @@ onMounted(() => {
.text-success {
color: #67c23a;
}
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
}
</style>
+220 -268
View File
@@ -1,5 +1,5 @@
<template>
<div class="p-2 page-container">
<div class="page-container">
<!-- 审核统计 -->
<div class="mb-[10px]">
<el-row :gutter="16" class="stats-row">
@@ -94,7 +94,7 @@
</el-col>
</el-row>
<!-- 第二行:4个搜索条件 + 操作按钮 -->
<!-- 第二行:4个搜索条件 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="奖学金类型" prop="scholarshipTypeId">
@@ -137,9 +137,9 @@
</el-row>
<!-- 操作按钮行 -->
<el-row>
<el-row :gutter="10" class="mb8">
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-form-item style="margin-bottom: 0;">
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
@@ -151,21 +151,22 @@
</transition>
<!-- 操作按钮 -->
<el-card shadow="never" class="button-card">
<div class="card-header">
<div class="toolbar-title">审核记录管理</div>
<div class="toolbar-actions">
<el-button type="success" plain icon="Check" :disabled="multiple" @click="handleBatchApprove" v-hasPermi="['jcbt:review:batchApprove']">
批量通过
</el-button>
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['jcbt:review:export']"> 导出 </el-button>
<right-toolbar :showSearch="showSearch" @update:showSearch="showSearch = $event" @queryTable="getList"></right-toolbar>
</div>
</div>
</el-card>
<el-card shadow="never" class="button-card table-card">
<template #header>
<el-row ref="toolbarRef" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="success" plain icon="Check" :disabled="multiple" @click="handleBatchApprove" v-hasPermi="['jcbt:review:batchApprove']">
批量通过
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['jcbt:review:export']"> 导出 </el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<!-- 表格数据 -->
<el-card shadow="never" class="table-card">
<!-- 表格数据 -->
<div class="table-container">
<el-table
ref="reviewRef"
@@ -173,21 +174,23 @@
:data="reviewList"
@selection-change="handleSelectionChange"
class="scroll-table"
:scroll="{ x: 'max-content' }"
border
:scroll-x="true"
>
<el-table-column type="selection" width="50" align="center" fixed="left" />
<el-table-column label="申请编号" align="center" prop="applicationNo" width="180" fixed="left">
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="申请编号" align="center" prop="applicationNo" min-width="180" fixed="left">
<template #default="scope">
<el-button link type="primary" @click="handleView(scope.row)">
{{ scope.row.applicationNo }}
</el-button>
</template>
</el-table-column>
<el-table-column label="学号" align="center" prop="studentNo" width="120" />
<el-table-column label="学生姓名" align="center" prop="studentName" width="120" />
<el-table-column label="班级" align="center" prop="className" width="120" />
<el-table-column label="奖学金类型" align="center" prop="scholarshipTypeName" width="150" />
<el-table-column label="申请类型" align="center" prop="applicationType" width="120">
<el-table-column label="学号" align="center" prop="studentNo" min-width="120" />
<el-table-column label="学生姓名" align="center" prop="studentName" min-width="120" />
<el-table-column label="班级" align="center" prop="className" min-width="120" />
<el-table-column label="奖学金类型" align="center" prop="scholarshipTypeName" min-width="150" />
<el-table-column label="申请类型" align="center" prop="applicationType" min-width="120">
<template #default="scope">
<el-tag v-if="scope.row.applicationType === 'RECOMMEND'" type="primary">教师推荐</el-tag>
<el-tag v-else-if="scope.row.applicationType === 'STUDENT'" type="success">学生申请</el-tag>
@@ -196,7 +199,7 @@
<el-tag v-else type="info">{{ scope.row.applicationType }}</el-tag>
</template>
</el-table-column>
<el-table-column label="申请状态" align="center" prop="status" width="120">
<el-table-column label="申请状态" align="center" prop="status" min-width="120">
<template #default="scope">
<el-tag v-if="scope.row.status === 'DRAFT'" type="info">草稿</el-tag>
<el-tag v-else-if="scope.row.status === 'SUBMITTED'" type="primary">已提交</el-tag>
@@ -208,17 +211,17 @@
</template>
</el-table-column>
<el-table-column label="提交时间" align="center" prop="submitTime" width="160">
<el-table-column label="提交时间" align="center" prop="submitTime" min-width="160">
<template #default="scope">
<span>{{ parseTime(scope.row.submitTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="160">
<el-table-column label="创建时间" align="center" prop="createTime" min-width="160">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="220" fixed="right">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" min-width="220" fixed="right">
<template #default="scope">
<!-- 查看按钮,所有记录都可以查看 -->
<el-tooltip content="查看" placement="top">
@@ -227,7 +230,6 @@
type="info"
icon="View"
size="default"
style="font-size: 16px; margin-right: 8px"
@click="handleView(scope.row)"
v-hasPermi="['jcbt:scholarshipApplication:query']"
></el-button>
@@ -241,7 +243,6 @@
type="success"
icon="Check"
size="default"
style="font-size: 16px; margin-right: 8px"
@click="handleApprove(scope.row)"
v-hasPermi="['jcbt:scholarshipApplication:audit']"
></el-button>
@@ -252,7 +253,6 @@
type="danger"
icon="Close"
size="default"
style="font-size: 16px; margin-right: 8px"
@click="handleReject(scope.row)"
v-hasPermi="['jcbt:scholarshipApplication:audit']"
></el-button>
@@ -263,7 +263,6 @@
type="warning"
icon="Document"
size="default"
style="font-size: 16px"
@click="handleReview(scope.row)"
v-hasPermi="['jcbt:scholarshipApplication:audit']"
></el-button>
@@ -278,7 +277,6 @@
type="primary"
icon="Edit"
size="default"
style="font-size: 16px"
@click="handleUpdate(scope.row)"
v-hasPermi="['jcbt:scholarshipApplication:edit']"
></el-button>
@@ -288,20 +286,21 @@
</el-table-column>
</el-table>
</div>
<div class="pagination-container">
<pagination
v-show="total > 0"
:total="total"
:page="queryParams.pageNum"
:limit="queryParams.pageSize"
@update:page="queryParams.pageNum = $event"
@update:limit="queryParams.pageSize = $event"
@pagination="getList"
/>
</div>
</el-card>
<!-- 分页 -->
<div class="pagination-container">
<pagination
v-show="total > 0"
:total="total"
:page="queryParams.pageNum"
:limit="queryParams.pageSize"
@update:page="queryParams.pageNum = $event"
@update:limit="queryParams.pageSize = $event"
@pagination="getList"
/>
</div>
<!-- 审核对话框 -->
<el-drawer ref="drawerRef" v-model="reviewOpen" title="奖学金申请审核" direction="rtl" size="50%" :before-close="handleReviewClose">
<template #default>
@@ -407,7 +406,7 @@
</div>
</template>
<script setup name="ScholarshipReview">
<script setup name="ScholarshipReview" lang="ts">
import { listScholarshipApplication, getScholarshipApplication, updateScholarshipApplication } from '@/api/jcbt/scholarshipApplication';
import { listScholarshipType } from '@/api/jcbt/scholarshipType';
import { listScholarshipBatch } from '@/api/jcbt/scholarshipBatch';
@@ -783,274 +782,227 @@ onMounted(() => {
});
</script>
<style scoped lang="scss">
/* 继承后勤管理模块的样式风格 */
<style scoped>
/* 页面容器样式 */
.page-container {
padding: 8px;
}
/* 统计卡片样式 */
.stats-row {
margin-bottom: 20px;
}
/* 表格卡片布局 */
.table-card {
margin-top: 0;
}
.stat-card {
border-radius: 12px;
transition: all 0.3s ease;
height: 90px;
cursor: pointer;
.table-card :deep(.el-card__body) {
padding: 0px 20px 20px 20px !important;
}
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
/* 表格容器 - 支持横向滚动 */
.table-container {
width: 100%;
margin-bottom: 0;
}
.stat-content {
display: flex;
align-items: center;
padding: 5px 16px;
height: 100%;
box-sizing: border-box;
/* 横向滚动表格样式 */
.scroll-table {
width: 100%;
}
.stat-icon {
font-size: 28px;
margin-right: 15px;
opacity: 0.9;
}
/* 表格横向滚动条样式优化 */
.scroll-table :deep(.el-table__body-wrapper) {
overflow-x: auto !important; /* 自动显示横向滚动条 */
overflow-y: auto;
}
.stat-info {
flex: 1;
.scroll-table :deep(.el-table__header-wrapper) {
overflow-x: hidden;
}
.stat-value {
font-size: 24px;
font-weight: bold;
line-height: 1;
margin-bottom: 4px;
}
/* 强制显示表格横向滚动条 - 适用于所有浏览器 */
.scroll-table :deep(.el-table__body-wrapper) {
scrollbar-width: thin; /* Firefox */
scrollbar-color: #409eff #f0f0f0; /* Firefox - thumb track */
}
.stat-label {
font-size: 14px;
opacity: 0.8;
line-height: 1;
}
}
}
/* Webkit 浏览器的横向滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 12px !important; /* 增加滚动条高度 */
width: 12px !important;
}
&.stat-card-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f0f0f0 !important;
border-radius: 6px !important;
border: 1px solid #e4e7ed !important; /* 添加边框使其更明显 */
}
&.stat-card-success {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
color: white;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #409eff !important; /* 使用主题色,更明显 */
border-radius: 6px !important;
border: 2px solid #f0f0f0 !important; /* 添加白色边框 */
min-height: 20px !important; /* 确保最小高度 */
}
&.stat-card-warning {
background: linear-gradient(135deg, #ff9a56 0%, #ffad56 100%);
color: white;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #337ecc !important; /* 悬停时更深的蓝色 */
}
&.stat-card-info {
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
color: #333;
}
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
background: #2b6cb0 !important; /* 点击时的颜色 */
}
/* 搜索区域样式 */
.search-btn-container {
text-align: right;
}
/* 确保滚动条始终可见的额外设置 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-corner {
background: #f0f0f0;
}
/* 表格卡片布局 - 参考后勤管理功能 */
.button-card {
margin-bottom: 10px;
}
/* 固定列样式优化 */
.scroll-table :deep(.el-table__fixed-right) {
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
}
.table-card {
margin-top: 0;
}
.scroll-table :deep(.el-table__fixed-left) {
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
.table-card :deep(.el-card__body) {
padding: 0px 20px 20px 20px !important;
}
/* 确保固定列在滚动时有正确的背景色 */
.scroll-table :deep(.el-table__fixed-right .el-table__cell),
.scroll-table :deep(.el-table__fixed-left .el-table__cell) {
background-color: #fff;
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
/* 表格行悬停效果在固定列上的同步 */
.scroll-table :deep(.el-table__row:hover .el-table__cell) {
background-color: #f5f7fa !important;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
/* 分页容器样式 */
.pagination-container {
display: flex;
justify-content: flex-end;
padding: 16px 20px 0 0 !important;
background-color: #fff;
margin-top: 0px !important;
}
.toolbar-title {
font-size: 18px;
font-weight: 600;
color: #1e293b;
display: flex;
align-items: center;
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
&::before {
content: '';
width: 4px;
height: 20px;
background: linear-gradient(135deg, rgb(93, 135, 255), rgb(73, 190, 255));
border-radius: 2px;
margin-right: 12px;
}
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.toolbar-actions {
display: flex;
gap: 12px;
align-items: center;
}
}
.search-btn-container .el-form-item {
margin-bottom: 0 !important;
}
/* 表格主体 */
.table-container {
width: 100%;
margin-bottom: 0;
}
/* 统计卡片样式 */
.stats-row {
margin-bottom: 16px;
}
/* 表格样式 - 参考后勤管理功能 */
.scroll-table {
width: 100%;
.stat-card {
cursor: pointer;
transition: all 0.3s ease;
border: none;
border-radius: 8px;
}
:deep(.el-table__header-wrapper) {
.el-table__header {
th {
background-color: #f8f9fa;
color: #333;
font-weight: 600;
}
}
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
:deep(.el-table__body-wrapper) {
.el-table__row {
&:hover {
background-color: #f5f7fa;
}
}
}
}
.stat-card-warning {
background: linear-gradient(135deg, #ff9a56 0%, #ff6b6b 100%);
}
/* 表格横向滚动条样式优化 - 参考后勤管理功能 */
.scroll-table :deep(.el-table__body-wrapper) {
overflow-x: auto !important;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #409eff #f0f0f0;
}
.stat-card-success {
background: linear-gradient(135deg, #4ecdc4 0%, #44a08d 100%);
}
.scroll-table :deep(.el-table__header-wrapper) {
overflow-x: hidden;
}
.stat-card-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
/* Webkit 浏览器的横向滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 12px !important;
width: 12px !important;
}
.stat-card-info {
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f0f0f0 !important;
border-radius: 6px !important;
border: 1px solid #e4e7ed !important;
}
.stat-content {
display: flex;
align-items: center;
padding: 8px 0;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #409eff !important;
border-radius: 6px !important;
border: 2px solid #f0f0f0 !important;
min-height: 20px !important;
}
.stat-icon {
margin-right: 16px;
font-size: 24px;
color: rgba(255, 255, 255, 0.9);
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #337ecc !important;
}
.stat-info {
flex: 1;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
background: #2b6cb0 !important;
}
.stat-value {
font-size: 24px;
font-weight: 700;
color: #fff;
line-height: 1.2;
margin-bottom: 4px;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-corner {
background: #f0f0f0;
}
.stat-label {
font-size: 14px;
color: rgba(255, 255, 255, 0.9);
line-height: 1.2;
}
/* 固定列样式优化 */
.scroll-table :deep(.el-table__fixed-right) {
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 操作按钮样式 - 参考后勤管理模块 */
.scroll-table :deep(.el-table__fixed-left) {
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 金额文本样式 */
.amount-text {
font-weight: 600;
color: #e74c3c;
}
/* 确保固定列在滚动时有正确的背景色 */
.scroll-table :deep(.el-table__fixed-right .el-table__cell),
.scroll-table :deep(.el-table__fixed-left .el-table__cell) {
background-color: #fff;
}
/* 对话框样式 */
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
/* 表格行悬停效果在固定列上的同步 */
.scroll-table :deep(.el-table__row:hover .el-table__cell) {
background-color: #f5f7fa !important;
}
/* 操作按钮样式 - 参考后勤管理模块 */
/* 金额文本样式 */
.amount-text {
font-weight: 600;
color: #e74c3c;
}
/* 分页容器样式 - 参考后勤管理功能 */
.pagination-container {
display: flex;
justify-content: flex-end;
padding: 16px 20px 0 0 !important;
background-color: #fff;
margin-top: 0px !important;
}
/* 对话框样式 */
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
/* 抽屉内容样式 */
.drawer-content {
padding: 20px;
}
/* 抽屉内容样式 */
.drawer-content {
padding: 20px;
}
/* 响应式设计 */
@media (max-width: 768px) {
.page-container {
padding: 10px;
}
.stats-row {
.el-col {
margin-bottom: 10px;
}
}
.stats-row .el-col {
margin-bottom: 10px;
}
.card-header {
flex-direction: column;
align-items: flex-start;
gap: 10px;
.card-header {
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
.toolbar-actions {
width: 100%;
justify-content: flex-start;
}
}
.card-header .toolbar-actions {
width: 100%;
justify-content: flex-start;
}
}
</style>
+218 -296
View File
@@ -1,5 +1,5 @@
<template>
<div class="p-2 page-container">
<div class="page-container">
<!-- 搜索区域 -->
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
@@ -68,8 +68,10 @@
></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item>
</el-row>
<el-row :gutter="10" class="mb8" >
<el-col :span="24" class="search-btn-container">
<el-form-item style="margin-bottom: 0;">
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
@@ -80,100 +82,61 @@
</div>
</transition>
<!-- 统计卡片区域 -->
<div class="mb-[10px]">
<el-row :gutter="16" class="stats-row">
<el-col :span="6">
<el-card shadow="hover" class="stat-card stat-card-primary">
<div class="stat-content">
<div class="stat-icon">
<el-icon><Trophy /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ statistics.totalTypes }}</div>
<div class="stat-label">奖学金类型总数</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover" class="stat-card stat-card-success">
<div class="stat-content">
<div class="stat-icon">
<el-icon><CircleCheck /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ statistics.activeTypes }}</div>
<div class="stat-label">启用状态</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover" class="stat-card stat-card-warning">
<div class="stat-content">
<div class="stat-icon">
<el-icon><Money /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ statistics.totalAmount }}</div>
<div class="stat-label">总奖励金额(万元)</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover" class="stat-card stat-card-info">
<div class="stat-content">
<div class="stat-icon">
<el-icon><Star /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ statistics.avgAmount }}</div>
<div class="stat-label">平均奖励金额(元)</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
<!-- 操作工具栏 -->
<el-card class="button-card">
<div class="card-header">
<div class="toolbar-title">
<el-icon><Trophy /></el-icon>
奖学金类型管理
</div>
<div class="button-group">
<el-button v-hasPermi="['jcbt:scholarshipType:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
<el-button v-hasPermi="['jcbt:scholarshipType:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
>修改</el-button
>
<el-button v-hasPermi="['jcbt:scholarshipType:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"
>删除</el-button
>
<el-button v-hasPermi="['jcbt:scholarshipType:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</div>
</div>
</el-card>
<!-- 表格数据 -->
<el-card class="table-card">
<el-card shadow="never" class="button-card table-card">
<template #header>
<el-row ref="toolbarRef" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['jcbt:scholarshipType:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="Edit"
:disabled="single"
@click="handleUpdate()"
v-hasPermi="['jcbt:scholarshipType:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete()"
v-hasPermi="['jcbt:scholarshipType:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['jcbt:scholarshipType:export']"
>导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<div class="table-container">
<el-table
ref="scholarshipTypeTableRef"
v-loading="loading"
border
:data="scholarshipTypeList"
row-key="id"
class="scroll-table"
@selection-change="handleSelectionChange"
style="width: 100%"
:scroll-x="true"
>
<el-table-column type="selection" width="50" align="center" />
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="奖学金名称" prop="typeName" min-width="150" show-overflow-tooltip />
<el-table-column label="类别" prop="typeCategory" width="120" align="center">
<el-table-column label="奖学金名称" prop="typeName" min-width="200" show-overflow-tooltip />
<el-table-column label="类别" prop="typeCategory" width="140" align="center">
<template #default="scope">
<el-tag :type="getCategoryTagType(scope.row.typeCategory)">
{{ getCategoryText(scope.row.typeCategory) }}
@@ -187,38 +150,45 @@
</el-tag>
</template>
</el-table-column>
<el-table-column label="奖励金额" prop="awardAmount" width="120" align="center">
<el-table-column label="奖励金额" prop="awardAmount" width="130" align="center">
<template #default="scope">
<span class="amount-text">¥{{ parseFloat(scope.row.awardAmount || '0').toLocaleString() }}</span>
</template>
</el-table-column>
<el-table-column label="兼得类型" prop="compatibilityType" width="120" align="center">
<el-table-column label="兼得类型" prop="compatibilityType" width="200" align="center">
<template #default="scope">
<el-tag :type="getCompatibilityTagType(scope.row.compatibilityType)">
{{ getCompatibilityText(scope.row.compatibilityType) }}
</el-tag>
<div>
<el-tag :type="getCompatibilityTagType(scope.row.compatibilityType)">
{{ getCompatibilityText(scope.row.compatibilityType) }}
</el-tag>
<div v-if="scope.row.compatibilityType === 'CUSTOM'" class="compatibility-detail">
<span class="exclusive-relation-text">{{ getExclusiveRelationText(scope.row.id) }}</span>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="排序" prop="sortOrder" width="80" align="center" />
<el-table-column label="状态" prop="status" width="80" align="center">
<el-table-column label="状态" prop="status" width="100" align="center">
<template #default="scope">
<el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0" @change="handleStatusChange(scope.row)" />
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createTime" width="160" align="center" />
<el-table-column label="操作" width="180" align="center" fixed="right" class-name="small-padding fixed-width">
<el-table-column label="创建时间" prop="createTime" width="180" align="center">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="280" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<div class="action-buttons">
<el-tooltip content="查看" placement="top">
<el-button v-hasPermi="['jcbt:scholarshipType:query']" type="primary" icon="View" @click="handleView(scope.row)" />
<el-button link type="info" icon="View" @click="handleView(scope.row)" v-hasPermi="['jcbt:scholarshipType:query']"></el-button>
</el-tooltip>
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['jcbt:scholarshipType:edit']" type="success" icon="Edit" @click="handleUpdate(scope.row)" />
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['jcbt:scholarshipType:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['jcbt:scholarshipType:remove']" type="danger" icon="Delete" @click="handleDelete(scope.row)" />
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['jcbt:scholarshipType:remove']"></el-button>
</el-tooltip>
</div>
</template>
</el-table-column>
</el-table>
@@ -227,7 +197,7 @@
<!-- 分页 -->
<div class="pagination-container">
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
<pagination v-show="total > 0" :page="queryParams.pageNum" :limit="queryParams.pageSize" :total="total" @pagination="getList" />
</div>
<!-- 添加或修改奖学金类型对话框 -->
@@ -496,6 +466,7 @@
import { reactive, ref, onMounted, getCurrentInstance } from 'vue';
import type { ComponentInternalInstance } from 'vue';
import type { FormRules, FormInstance, TableInstance } from 'element-plus';
import { parseTime } from '@/utils/ruoyi';
import {
listScholarshipType,
getScholarshipType,
@@ -507,6 +478,7 @@ import {
checkScholarshipTypeNameUnique,
getCompatibilityConfig,
saveCompatibilityConfig,
getExclusiveRelationDisplay,
type ScholarshipTypeVO,
type ScholarshipTypeForm,
type ScholarshipTypeQuery,
@@ -560,6 +532,9 @@ const availableScholarshipTypes = ref<ScholarshipTypeVO[]>([]);
// 当前奖学金的兼容性配置(用于编辑时显示原来的配置)
const currentCompatibilityConfig = ref<CompatibilityConfig | null>(null);
// 互斥关系显示文本缓存
const exclusiveRelationCache = ref<Map<string | number, string>>(new Map());
const dataRules = reactive<FormRules>({
typeCode: [
{ required: true, message: '奖学金类型编码不能为空', trigger: 'blur' },
@@ -660,6 +635,44 @@ const getCompatibilityTagType = (value: string) => {
return typeMap[value] || 'warning'; // 未知类型用橙色警告标签
};
/** 获取互斥关系显示文本 */
const getExclusiveRelationText = (scholarshipTypeId: string | number): string => {
if (!scholarshipTypeId) return '';
// 先从缓存中获取
const cached = exclusiveRelationCache.value.get(scholarshipTypeId);
if (cached !== undefined) {
return cached;
}
// 异步获取并缓存
getExclusiveRelationDisplay(scholarshipTypeId).then(response => {
exclusiveRelationCache.value.set(scholarshipTypeId, response.data);
}).catch(error => {
console.warn('获取互斥关系显示文本失败:', error);
exclusiveRelationCache.value.set(scholarshipTypeId, '互斥(无)');
});
return '加载中...';
};
/** 预加载互斥关系显示文本 */
const preloadExclusiveRelations = async () => {
const customTypes = scholarshipTypeList.value.filter(item => item.compatibilityType === 'CUSTOM');
for (const type of customTypes) {
if (type.id && !exclusiveRelationCache.value.has(type.id)) {
try {
const response = await getExclusiveRelationDisplay(type.id);
exclusiveRelationCache.value.set(type.id, response.data);
} catch (error) {
console.warn(`获取奖学金类型 ${type.id} 的互斥关系失败:`, error);
exclusiveRelationCache.value.set(type.id, '互斥(无)');
}
}
}
};
/** 查询奖学金类型列表 */
const getList = async () => {
loading.value = true;
@@ -693,6 +706,9 @@ const getList = async () => {
scholarshipTypeList.value = response.rows;
total.value = response.total;
// 预加载CUSTOM类型的互斥关系显示文本
preloadExclusiveRelations();
// 计算统计数据
calculateStatistics();
} finally {
@@ -722,6 +738,8 @@ const resetQuery = () => {
daterangeCreateTime.value = ['', ''];
queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1;
// 清空缓存
exclusiveRelationCache.value.clear();
handleQuery();
};
@@ -803,6 +821,8 @@ const handleDelete = async (row?: ScholarshipTypeVO) => {
try {
await proxy?.$modal.confirm(`是否确认删除奖学金类型"${typeNames.join('、')}"?`);
await delScholarshipType(typeIds);
// 清空缓存以便重新加载
exclusiveRelationCache.value.clear();
await getList();
proxy?.$modal.msgSuccess('删除成功');
} catch (error) {
@@ -824,6 +844,8 @@ const handleStatusChange = async (row: ScholarshipTypeVO) => {
await proxy?.$modal.confirm(`确认要${text}"${row.typeName}"吗?`);
await changeScholarshipTypeStatus(row.id!, row.status!);
proxy?.$modal.msgSuccess(`${text}成功`);
// 清空缓存以便重新加载
exclusiveRelationCache.value.clear();
await getList();
} catch (error) {
if (error !== 'cancel') {
@@ -858,6 +880,8 @@ const dialogSubmit = () => {
}
dialogOpen.value = false;
// 清空缓存以便重新加载
exclusiveRelationCache.value.clear();
await getList();
} catch (error) {
proxy?.$modal.msgError(form.value.id ? '修改失败' : '新增失败');
@@ -936,80 +960,13 @@ onMounted(() => {
});
</script>
<style scoped lang="scss">
/* 继承后勤管理模块的样式风格 */
<style scoped>
/* 页面容器样式 */
.page-container {
padding: 8px;
}
/* 统计卡片样式 */
.stats-row {
margin-bottom: 20px;
}
.stat-card {
border-radius: 12px;
transition: all 0.3s ease;
height: 90px;
cursor: pointer;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.stat-content {
display: flex;
align-items: center;
padding: 5px 16px;
height: 100%;
box-sizing: border-box;
.stat-icon {
font-size: 28px;
margin-right: 15px;
opacity: 0.9;
}
.stat-info {
flex: 1;
.stat-value {
font-size: 24px;
font-weight: bold;
line-height: 1;
margin-bottom: 4px;
}
.stat-label {
font-size: 14px;
opacity: 0.8;
line-height: 1;
}
}
}
&.stat-card-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
&.stat-card-success {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
color: white;
}
&.stat-card-warning {
background: linear-gradient(135deg, #ff9a56 0%, #ffad56 100%);
color: white;
}
&.stat-card-info {
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
color: #333;
}
}
/* 表格卡片布局 - 参考后勤管理功能 */
/* 表格卡片布局 */
.table-card {
margin-top: 0;
}
@@ -1018,100 +975,86 @@ onMounted(() => {
padding: 0px 20px 20px 20px !important;
}
/* 操作工具栏样式 */
.button-card {
margin-bottom: 20px;
/* 表格容器 - 支持横向滚动 */
.table-container {
width: 100%;
margin-bottom: 0;
}
:deep(.el-card__body) {
padding: 20px;
}
}
/* 横向滚动表格样式 */
.scroll-table {
width: 100%;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
/* 表格横向滚动条样式优化 */
.scroll-table :deep(.el-table__body-wrapper) {
overflow-x: auto !important; /* 自动显示横向滚动条 */
overflow-y: auto;
}
.toolbar-title {
font-size: 18px;
font-weight: bold;
color: #303133;
display: flex;
align-items: center;
gap: 8px;
}
.scroll-table :deep(.el-table__header-wrapper) {
overflow-x: hidden;
}
.button-group {
display: flex;
align-items: center;
gap: 10px;
}
}
/* 强制显示表格横向滚动条 - 适用于所有浏览器 */
.scroll-table :deep(.el-table__body-wrapper) {
scrollbar-width: thin; /* Firefox */
scrollbar-color: #409eff #f0f0f0; /* Firefox - thumb track */
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
/* 滚动表格样式 */
.table-container {
overflow-x: auto;
}
.scroll-table {
width: 100%;
min-width: 1200px;
}
/* 表格滚动条美化 */
/* Webkit 浏览器的横向滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 8px;
height: 12px !important; /* 增加滚动条高度 */
width: 12px !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
background: #f0f0f0 !important;
border-radius: 6px !important;
border: 1px solid #e4e7ed !important; /* 添加边框使其更明显 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 4px;
transition: background 0.3s ease;
background: #409eff !important; /* 使用主题色,更明显 */
border-radius: 6px !important;
border: 2px solid #f0f0f0 !important; /* 添加白色边框 */
min-height: 20px !important; /* 确保最小高度 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #337ecc !important;
background: #337ecc !important; /* 悬停时更深的蓝色 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
background: #2b6cb0 !important;
}
background: #2b6cb0 !important; /* 点击时的颜色 */
}
/* 操作按钮样式 */
.action-buttons {
display: flex;
gap: 8px;
justify-content: center;
align-items: center;
white-space: nowrap;
/* 确保滚动条始终可见的额外设置 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-corner {
background: #f0f0f0;
}
.el-button {
width: 27px !important;
height: 27px !important;
padding: 0 !important;
display: flex;
align-items: center;
justify-content: center;
}
}
/* 固定列样式优化 */
.scroll-table :deep(.el-table__fixed-right) {
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 金额文本样式 */
.amount-text {
font-weight: 600;
color: #e74c3c;
}
.scroll-table :deep(.el-table__fixed-left) {
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 分页容器样式 - 参考后勤管理功能 */
/* 确保固定列在滚动时有正确的背景色 */
.scroll-table :deep(.el-table__fixed-right .el-table__cell),
.scroll-table :deep(.el-table__fixed-left .el-table__cell) {
background-color: #fff;
}
/* 表格行悬停效果在固定列上的同步 */
.scroll-table :deep(.el-table__row:hover .el-table__cell) {
background-color: #f5f7fa !important;
}
/* 分页容器样式 */
.pagination-container {
display: flex;
justify-content: flex-end;
@@ -1120,6 +1063,17 @@ onMounted(() => {
margin-top: 0px !important;
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
/* 金额文本样式 */
.amount-text {
font-weight: 600;
color: #e74c3c;
}
/* 对话框样式 */
.dialog-footer {
display: flex;
@@ -1137,59 +1091,6 @@ onMounted(() => {
color: #909399;
margin-top: 4px;
line-height: 1.4;
}
}
}
/* 响应式设计 */
@media (max-width: 768px) {
.page-container {
padding: 10px;
.stats-row {
.el-col {
margin-bottom: 10px;
}
}
.stat-card {
height: 80px;
.stat-content {
padding: 5px 12px;
.stat-icon {
font-size: 24px;
margin-right: 10px;
}
.stat-info {
.stat-value {
font-size: 20px;
}
.stat-label {
font-size: 12px;
}
}
}
}
.card-header {
flex-direction: column;
gap: 15px;
align-items: flex-start;
.button-group {
width: 100%;
justify-content: flex-end;
flex-wrap: wrap;
}
}
.scroll-table {
min-width: 800px;
}
}
}
@@ -1205,4 +1106,25 @@ onMounted(() => {
color: #303133;
}
}
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
}
/* 兼容性详情样式 */
.compatibility-detail {
margin-top: 4px;
font-size: 12px;
}
.exclusive-relation-text {
color: #909399;
line-height: 1.2;
word-break: break-word;
}
</style>
+14 -8
View File
@@ -60,7 +60,7 @@
<el-button icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['rs:job:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="Download" @click="handleExport" v-hasPermi="['rs:job:export']">导出</el-button>
<el-button icon="Download" @click="handleExport" v-hasPermi="['rs:job:export']" style="display: none">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
@@ -91,7 +91,7 @@
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="240">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="280">
<template #default="scope">
<el-tooltip content="查看" placement="top">
<el-button
@@ -113,6 +113,17 @@
@click="handleUpdate(scope.row)"
></el-button>
</el-tooltip>
<el-tooltip content="查看简历记录" placement="top">
<el-button
link
type="info"
icon="Document"
size="default"
style="font-size: 16px; margin-right: 8px"
@click="handleApplications(scope.row)"
v-if="scope.row.jobId"
></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button
link
@@ -216,7 +227,7 @@
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button icon="Download" @click="handleExportApplications" v-hasPermi="['rs:application:export']">导出</el-button>
<el-button icon="Download" @click="handleExportApplications" v-hasPermi="['rs:application:export']" style="display: none">导出</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="Printer" @click="handlePrintApplications">打印</el-button>
@@ -386,7 +397,6 @@
</el-descriptions>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleApplications(viewForm)" v-if="viewForm.jobId">查看简历记录</el-button>
<el-button @click="viewOpen = false">关 闭</el-button>
</div>
</template>
@@ -1686,10 +1696,6 @@ function handleExportResume() {
<p><strong>应聘职位:</strong>${selectedRecord.jobTitle || '未提供'}</p>
<p><strong>联系电话:</strong>${selectedRecord.contactPhone || '未提供'}</p>
<p><strong>联系邮箱:</strong>${selectedRecord.contactEmail || '未提供'}</p>
<p><strong>申请状态:</strong>${
selectedRecord.status === '0' ? '待处理' : selectedRecord.status === '1' ? '已通过' : selectedRecord.status === '2' ? '已拒绝' : '未知'
}</p>
<p><strong>申请时间:</strong>${parseTime(selectedRecord.applyTime)}</p>
</div>
<div style="margin-bottom: 30px;">
+9 -3
View File
@@ -76,7 +76,7 @@
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button icon="Download" @click="handleExportPending" v-hasPermi="['rs:overseas:export']">导出</el-button>
<el-button icon="Download" @click="handleExportPending" v-hasPermi="['rs:overseas:export']" style="display: none">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showPendingSearch" @queryTable="getPendingList"></right-toolbar>
</el-row>
@@ -219,7 +219,7 @@
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button icon="Download" @click="handleExportRecord" v-hasPermi="['rs:overseas:export']">导出</el-button>
<el-button icon="Download" @click="handleExportRecord" v-hasPermi="['rs:overseas:export']" style="display: none">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showRecordSearch" @queryTable="getRecordList"></right-toolbar>
</el-row>
@@ -854,7 +854,13 @@ const handleRecordDateRangeChange = (dates: [string, string] | null) => {
<style scoped>
.search-btn-container {
text-align: center;
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.search-btn-container :deep(.el-form-item) {
margin-bottom: 0 !important;
}
.button-card {
+8 -2
View File
@@ -67,7 +67,7 @@
<el-button type="primary" plain icon="Plus" @click="handleAddApplication" v-hasPermi="['rs:overseas:add']">新增申请</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="Download" @click="handleExportApplication" v-hasPermi="['rs:overseas:export']">导出</el-button>
<el-button icon="Download" @click="handleExportApplication" v-hasPermi="['rs:overseas:export']" style="display: none">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showApplicationSearch" @queryTable="getApplicationList"></right-toolbar>
</el-row>
@@ -1055,7 +1055,13 @@ const handleViewRecord = (row: OverseasRecordVO) => {
<style scoped>
.search-btn-container {
text-align: center;
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.search-btn-container :deep(.el-form-item) {
margin-bottom: 0 !important;
}
.button-card {
+8 -3
View File
@@ -64,7 +64,7 @@
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['rs:overseas:export']">导出</el-button>
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['rs:overseas:export']" style="display: none">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
@@ -678,8 +678,13 @@ onMounted(() => {
/* 搜索按钮容器 */
.search-btn-container {
text-align: left;
padding-top: 10px;
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.search-btn-container :deep(.el-form-item) {
margin-bottom: 0 !important;
}
/* 表格卡片样式 */
+12 -6
View File
@@ -344,9 +344,9 @@ onMounted(() => {
min-height: 100vh;
.header-banner {
background-color: #409eff;
background: #004e48;
color: white;
padding: 40px 0;
padding: 50px 0;
text-align: center;
.banner-content {
@@ -355,14 +355,20 @@ onMounted(() => {
padding: 0 20px;
.apply-title {
font-size: 2rem;
margin-bottom: 10px;
font-size: 2.5rem;
color: #ffffff;
margin-bottom: 15px;
font-weight: 600;
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.4);
}
.apply-subtitle {
font-size: 1rem;
opacity: 0.8;
font-size: 1.3rem;
color: #ffffff;
opacity: 0.92;
margin: 0;
font-weight: 300;
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.4);
}
}
}
+230 -96
View File
@@ -375,50 +375,71 @@ onMounted(() => {
<style scoped lang="scss">
.recruitment-portal {
min-height: 100vh;
background: #f8f9fa;
background: #f5f5f5;
.header-banner {
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
// 纯色背景,不要图案
background: #004e48;
position: relative;
color: white;
padding: 60px 0;
padding: 50px 0;
text-align: center;
.banner-content {
margin: 0 auto;
padding: 0 30px;
position: relative;
z-index: 1;
.portal-title {
font-size: 3.5rem;
font-weight: 700;
margin-bottom: 20px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
font-size: 3.2rem;
font-weight: 600;
margin-bottom: 15px;
text-shadow: 2px 2px 6px rgba(0, 0, 0, 0.5);
color: #ffffff;
letter-spacing: 3px;
}
.portal-subtitle {
font-size: 1.5rem;
opacity: 0.9;
font-size: 1.3rem;
color: #ffffff;
opacity: 0.92;
margin: 0;
font-weight: 300;
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.4);
}
}
}
.breadcrumb-container {
background: white;
padding: 15px 0;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
border-bottom: 1px solid #e8ecf0;
padding: 12px 0;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
border-bottom: 1px solid #e0e0e0;
.el-breadcrumb {
margin: 0 auto;
padding: 0 30px;
:deep(.el-breadcrumb__item) {
.el-breadcrumb__inner {
color: #666;
font-weight: 400;
}
&:last-child .el-breadcrumb__inner {
color: #2d5a3d;
font-weight: 500;
}
}
}
}
.portal-container {
margin: 0;
padding: 30px;
padding: 25px;
display: flex;
gap: 25px;
gap: 20px;
min-height: calc(100vh - 200px);
}
@@ -427,51 +448,101 @@ onMounted(() => {
flex-shrink: 0;
.nav-section {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
border: 1px solid #e8ecf0;
// 参考官网左侧导航的样式 - 整体深绿色背景
background: #004e48;
border-radius: 0;
padding: 0;
margin-bottom: 15px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border: none;
overflow: visible;
.nav-title {
font-size: 1.2rem;
font-weight: 600;
margin-bottom: 20px;
color: #2c3e50;
border-bottom: 2px solid #e3f2fd;
padding-bottom: 10px;
margin: 0;
color: #ffffff;
// 参考官网"学工在线"的深绿色背景
background: #004e48;
padding: 18px 20px;
text-align: center;
position: relative;
letter-spacing: 1px;
}
.nav-list {
list-style: none;
padding: 0;
margin: 0;
background: #004e48;
.nav-item {
display: flex;
align-items: center;
padding: 12px 15px;
margin-bottom: 8px;
border-radius: 8px;
padding: 15px 20px;
margin: 0;
cursor: pointer;
transition: all 0.3s ease;
color: #555;
color: #ffffff;
font-weight: 500;
font-size: 1rem;
position: relative;
border: none;
// 默认状态 - 普通导航项
background: #004e48;
&:hover {
background: #f8f9fa;
color: #667eea;
background: #006157;
}
&.active {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
// 参考官网"学生活动"的样式 - 更深的绿色和箭头形状
background: #003a37;
position: relative;
z-index: 2;
// 创建右箭头效果
&::after {
content: '';
position: absolute;
right: -15px;
top: 50%;
transform: translateY(-50%);
width: 0;
height: 0;
border-left: 15px solid #003a37;
border-top: 15px solid transparent;
border-bottom: 15px solid transparent;
z-index: 3;
}
// 箭头阴影效果
&::before {
content: '';
position: absolute;
right: -17px;
top: 50%;
transform: translateY(-50%);
width: 0;
height: 0;
border-left: 17px solid rgba(0, 0, 0, 0.15);
border-top: 17px solid transparent;
border-bottom: 17px solid transparent;
z-index: 1;
}
}
.el-icon {
margin-right: 10px;
font-size: 1.1rem;
color: #ffffff;
transition: color 0.2s ease;
}
span {
font-size: 1rem;
letter-spacing: 0.5px;
}
}
}
@@ -480,11 +551,25 @@ onMounted(() => {
.main-content {
flex: 1;
min-width: 0; // 确保flex子项能够收缩
/* 搜索区域样式已删除 */
min-width: 0;
.job-list-section {
.el-card {
border-radius: 8px;
border: 1px solid #e8e8e8;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
:deep(.el-card__header) {
background: #fafafa;
border-bottom: 1px solid #e8e8e8;
padding: 16px 20px;
}
:deep(.el-card__body) {
padding: 20px;
}
}
.list-header {
display: flex;
justify-content: space-between;
@@ -497,96 +582,106 @@ onMounted(() => {
h3 {
margin: 0;
color: #2c3e50;
color: #2d5a3d;
font-weight: 600;
font-size: 1.2rem;
}
.job-count {
color: #666;
font-size: 0.9rem;
background: #e8f5e8;
padding: 4px 8px;
border-radius: 12px;
font-weight: 500;
}
}
.view-options {
:deep(.el-radio-group) {
.el-radio-button__inner {
background: #fff;
border-color: #2d5a3d;
color: #2d5a3d;
&:hover {
color: #2d5a3d;
}
}
.el-radio-button__original-radio:checked + .el-radio-button__inner {
background: #2d5a3d;
border-color: #2d5a3d;
color: #fff;
}
}
}
}
.list-view {
:deep(.el-table) {
.el-table__header {
background: #fafafa;
th {
background: #fafafa !important;
color: #333;
font-weight: 500;
}
}
.el-table__row {
&:hover {
background: #f8f9fa;
}
}
}
.job-title-link {
font-weight: 600;
font-weight: 500;
color: #2d5a3d;
text-decoration: none;
&:hover {
color: #4a7c59;
text-decoration: underline;
}
}
.deadline-passed {
color: #f56c6c;
font-weight: 600;
}
.deadline-warning {
color: #e6a23c;
font-weight: 600;
}
}
.card-view {
.job-card-col {
margin-bottom: 15px;
}
.job-card {
height: 100%;
cursor: pointer;
border: 1px solid #e8e8e8;
border-radius: 8px;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
&:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
transform: translateY(-3px);
box-shadow: 0 6px 20px rgba(45, 90, 61, 0.15);
border-color: #2d5a3d;
}
.job-card-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 15px;
.job-card-title {
margin: 0;
font-size: 1.1rem;
color: #2d5a3d;
font-weight: 600;
color: #2c3e50;
flex: 1;
margin-right: 10px;
}
}
.job-card-info {
margin-bottom: 15px;
flex: 1;
.info-item {
display: flex;
align-items: center;
margin-bottom: 8px;
color: #666;
font-size: 0.9rem;
.el-icon {
margin-right: 8px;
color: #999;
}
}
}
.job-card-footer {
display: flex;
justify-content: space-between;
align-items: center;
.publish-time {
font-size: 0.8rem;
color: #999;
.el-button {
background: #2d5a3d;
border-color: #2d5a3d;
&:hover {
background: #4a7c59;
border-color: #4a7c59;
}
&:disabled {
background: #ccc;
border-color: #ccc;
}
}
}
}
@@ -594,12 +689,28 @@ onMounted(() => {
}
}
// 对话框样式优化
:deep(.el-dialog) {
border-radius: 8px;
.el-dialog__header {
background: #fafafa;
border-bottom: 1px solid #e8e8e8;
.el-dialog__title {
color: #2d5a3d;
font-weight: 600;
}
}
}
.job-detail {
.detail-header {
.job-basic-info {
h3 {
margin-bottom: 15px;
color: #2c3e50;
color: #2d5a3d;
font-weight: 600;
}
.job-meta {
@@ -612,6 +723,14 @@ onMounted(() => {
color: #666;
font-size: 0.9rem;
}
.el-tag {
&.el-tag--success {
background: #e8f5e8;
color: #2d5a3d;
border-color: #2d5a3d;
}
}
}
}
}
@@ -621,9 +740,12 @@ onMounted(() => {
margin-bottom: 25px;
h4 {
color: #2c3e50;
color: #2d5a3d;
margin-bottom: 15px;
font-size: 1.1rem;
font-weight: 600;
border-left: 4px solid #2d5a3d;
padding-left: 12px;
}
.content-text {
@@ -644,9 +766,21 @@ onMounted(() => {
.dialog-footer {
text-align: right;
padding-top: 15px;
border-top: 1px solid #e8e8e8;
.el-button {
margin-left: 10px;
&.el-button--primary {
background: #2d5a3d;
border-color: #2d5a3d;
&:hover {
background: #4a7c59;
border-color: #4a7c59;
}
}
}
}
}
+268 -86
View File
@@ -57,12 +57,12 @@
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAddApplication" v-hasPermi="['rs:training:application:add']"
<el-button type="primary" plain icon="Plus" @click="handleAddApplication" v-hasPermi="['rs:trainingapplication:add']"
>申请培训</el-button
>
</el-col>
<el-col :span="1.5">
<el-button icon="Download" @click="handleExportTrainingApplication" v-hasPermi="['rs:training:application:export']">导出</el-button>
<el-button icon="Download" @click="handleExportTrainingApplication" v-hasPermi="['rs:trainingapplication:export']" style="display: none">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showApplicationSearch" @queryTable="getApplicationList"></right-toolbar>
</el-row>
@@ -95,7 +95,7 @@
type="primary"
icon="View"
@click="handleViewApplicant(scope.row)"
v-hasPermi="['rs:training:application:query']"
v-hasPermi="['rs:trainingapplication:query']"
></el-button>
</el-tooltip>
<!-- 待审核状态:显示撤回按钮 -->
@@ -105,7 +105,7 @@
type="warning"
icon="RefreshLeft"
@click="handleCancelApplication(scope.row)"
v-hasPermi="['rs:training:application:cancel']"
v-hasPermi="['rs:trainingapplication:cancel']"
></el-button>
</el-tooltip>
<!-- 已拒绝状态:显示重新提交按钮(需要修改并重新提交权限) -->
@@ -115,7 +115,7 @@
type="success"
icon="RefreshRight"
@click="handleUpdateAndResubmitApplication(scope.row)"
v-hasPermi="['rs:training:application:edit', 'rs:training:application:submit']"
v-hasPermi="['rs:trainingapplication:edit', 'rs:trainingapplication:submit']"
></el-button>
</el-tooltip>
<!-- 已通过和已拒绝状态:显示审批历史 -->
@@ -128,8 +128,8 @@
link
type="danger"
icon="Delete"
@click="handleDeleteApplication(scope.row)"
v-hasPermi="['rs:training:application:remove']"
@click="handleDeleteApplication(scope.row)"
v-hasPermi="['rs:trainingapplication:remove']"
></el-button>
</el-tooltip>
</template>
@@ -161,16 +161,13 @@
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="学员姓名" prop="userName">
<el-input v-model="recordQueryParams.userName" placeholder="请输入学员姓名" clearable @keyup.enter="handleRecordQuery" />
<el-form-item label="培训地点" prop="location">
<el-input v-model="recordQueryParams.location" placeholder="请输入培训地点" clearable @keyup.enter="handleRecordQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="完成状态" prop="completionStatus">
<el-select v-model="recordQueryParams.completionStatus" placeholder="请选择完成状态" clearable style="width: 100%">
<el-option label="未完成" value="0" />
<el-option label="已完成" value="1" />
</el-select>
<el-form-item label="部门" prop="department">
<el-input v-model="recordQueryParams.department" placeholder="请输入部门" clearable style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
@@ -193,7 +190,7 @@
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button icon="Download" @click="handleExportRecord" v-hasPermi="['rs:training:record:export']">导出</el-button>
<el-button icon="Download" @click="handleExportRecord" v-hasPermi="['rs:training:record:export']" style="display: none">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showRecordSearch" @queryTable="getRecordList"></right-toolbar>
</el-row>
@@ -210,21 +207,17 @@
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="课程名称" align="center" prop="courseName" min-width="150" :show-overflow-tooltip="true" />
<el-table-column label="培训地点" align="center" prop="location" min-width="110" :show-overflow-tooltip="true" />
<el-table-column label="学时" align="center" prop="duration" width="80" />
<el-table-column label="申请时间" align="center" prop="applyTime" width="160">
<template #default="scope">
<span>{{ parseTime(scope.row.applyTime) }}</span>
</template>
</el-table-column>
<el-table-column label="申请状态" align="center" prop="status" width="100">
<el-table-column label="部门" align="center" prop="department" min-width="110" :show-overflow-tooltip="true" />
<el-table-column label="完成状态" align="center" prop="completionStatus" width="100">
<template #default="scope">
<el-tag
:type="scope.row.status === '0' ? 'warning' : scope.row.status === '1' ? 'success' : scope.row.status === '2' ? 'danger' : 'info'"
:type="scope.row.completionStatus === '0' ? 'warning' : scope.row.completionStatus === '1' ? 'success' : 'info'"
>
{{ getStatusLabel(scope.row.status) }}
{{ scope.row.completionStatus === '0' ? '未完成' : scope.row.completionStatus === '1' ? '已完成' : '未知' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="学时" align="center" prop="duration" width="80" />
<el-table-column label="完成时间" align="center" prop="completionTime" width="160">
<template #default="scope">
<span>{{ parseTime(scope.row.completionTime) || '未完成' }}</span>
@@ -355,7 +348,7 @@
type="primary"
@click="handleViewApplications(courseDetail)"
v-if="courseDetail.courseId"
v-hasPermi="['rs:training:application:list']"
v-hasPermi="['rs:trainingapplication:list']"
>查看申请记录</el-button
>
<el-button @click="courseDetailDialogVisible = false">关 闭</el-button>
@@ -456,7 +449,12 @@
<el-input-number v-model="applicationForm.duration" :min="0" :max="1000" :step="0.5" style="width: 200px" />
</el-form-item>
<el-form-item label="所属部门" prop="department">
<el-input v-model="applicationForm.department" placeholder="请输入所属部门" :disabled="true" />
<el-input
v-model="applicationForm.department"
placeholder="请输入所属部门"
readonly
class="readonly-input"
/>
</el-form-item>
<el-form-item label="培训时间" required>
<el-col :span="11">
@@ -771,7 +769,8 @@ import {
deleteTrainingApplication,
getTrainingApprovalHistory,
resubmitTrainingApplication,
updateRecordAttachments
updateRecordAttachments,
listAllApprovalRecords
} from '@/api/rs/training';
import { parseTime } from '@/utils/ruoyi';
import request from '@/utils/request';
@@ -787,12 +786,15 @@ import type {
ApplicationVO,
ApplicationForm
} from '@/api/rs/training';
import { listDept } from '@/api/system/dept';
import { listDept, getDept } from '@/api/system/dept';
import type { DeptVO } from '@/api/system/dept/types';
import { getFilePreviewUrl } from '@/api/system/file';
import { getToken } from '@/utils/auth';
import { getInfo as getUserInfo } from '@/api/login';
import { useUserStore } from '@/store/modules/user';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const userStore = useUserStore();
// 环境变量
const baseApi = import.meta.env.VITE_APP_BASE_API;
@@ -976,17 +978,19 @@ const applicationDetail = reactive<any>({
const approvalHistoryOpen = ref(false);
const approvalHistoryList = ref<any[]>([]);
// 培训记录相关
// 培训记录相关
const recordLoading = ref(false);
const recordList = ref<RecordVO[]>([]);
const recordList = ref<RecordVO[]>([]);
const recordTotal = ref(0);
const showRecordSearch = ref(true);
const recordQueryParams = reactive<RecordQuery>({
const recordQueryParams = reactive<RecordQuery>({
pageNum: 1,
pageSize: 10,
pageSize: 20,
courseId: undefined as string | number | undefined,
courseName: '',
userId: undefined as string | number | undefined,
userName: '',
department: '',
completionStatus: '',
params: {}
});
@@ -1325,17 +1329,72 @@ function getApplicationList() {
}
/** 新增培训申请 */
function handleAddApplication() {
async function handleAddApplication() {
resetApplicationForm();
applicationDialogVisible.value = true;
applicationDialogTitle.value = '申请培训';
// 加载课程选项
if (courseOptions.value.length === 0) {
if (courseOptions.value && courseOptions.value.length === 0) {
loadCourseOptions();
}
// 模拟用户部门信息
applicationForm.department = '计算机科学学院';
// 自动获取登录用户的部门信息
try {
// 确保用户信息已加载
if (!userStore.userId) {
await userStore.getInfo();
}
console.log('开始自动填充用户部门信息:', {
userId: userStore.userId,
cachedDeptName: userStore.deptName
});
// 重新获取用户信息以确保获取到deptId
await userStore.getInfo();
const userInfo = await getUserInfo();
const userDeptId = userInfo.data.user.deptId;
console.log('用户信息详情:', {
userId: userStore.userId,
deptId: userDeptId,
cachedDeptName: userStore.deptName
});
// 如果有deptId,从数据库sys_dept表实时获取部门名称
if (userDeptId) {
console.log('开始从数据库sys_dept表获取部门信息 - deptId:', userDeptId);
try {
const deptResponse = await getDept(userDeptId);
if (deptResponse.code === 200 && deptResponse.data) {
const realDeptName = deptResponse.data.deptName;
applicationForm.department = realDeptName;
console.log('培训申请部门自动匹配成功:', {
userId: userStore.userId,
userDeptId: userDeptId,
deptNameFromDatabase: realDeptName,
关联逻辑: 'sys_user.dept_id -> sys_dept.dept_name'
});
} else {
console.warn('从数据库获取部门信息失败,使用缓存的部门名称');
applicationForm.department = userStore.deptName || '';
}
} catch (deptError) {
console.error('查询部门信息API调用失败:', deptError);
applicationForm.department = userStore.deptName || '';
}
} else {
console.warn('用户deptId不存在,使用缓存的部门名称');
applicationForm.department = userStore.deptName || '';
}
} catch (error) {
console.error('获取用户信息失败:', error);
// 如果获取失败,使用空字符串,用户可以手动输入
applicationForm.department = '';
}
}
/** 重置培训申请表单 */
@@ -1722,8 +1781,12 @@ function handleTrainingSubTabClick() {
// 加载课程选项
function loadCourseOptions() {
return listTrainingCourse({ pageNum: 1, pageSize: 100, status: '0' }).then((response) => {
courseOptions.value = response.rows;
return response.rows;
courseOptions.value = response.rows || [];
return courseOptions.value;
}).catch((error) => {
console.error('加载课程选项失败:', error);
courseOptions.value = [];
throw error;
});
}
@@ -1755,10 +1818,19 @@ function remoteSearchUser(query: string) {
/** 获取培训记录列表 */
function getRecordList() {
recordLoading.value = true;
listTrainingRecord(recordQueryParams).then(response => {
// 设置当前用户ID,只查询当前用户的培训记录
const queryParams = {
...recordQueryParams,
userId: userStore.userId // 设置当前用户ID
};
// 调用培训记录接口,获取培训记录数据
listTrainingRecord(queryParams).then(response => {
recordList.value = response.rows;
recordTotal.value = response.total;
recordLoading.value = false;
}).catch(() => {
recordLoading.value = false;
});
}
@@ -1768,13 +1840,14 @@ function handleAddRecord() {
recordDialogVisible.value = true;
recordDialogTitle.value = '添加培训记录';
// 加载课程选项
if (courseOptions.value.length === 0) {
if (!courseOptions.value || courseOptions.value.length === 0) {
loadCourseOptions();
}
}
/** 重置培训记录表单 */
function resetRecordForm() {
console.log('resetRecordForm 开始执行');
recordForm.recordId = undefined;
recordForm.courseId = '';
recordForm.userId = '';
@@ -1787,8 +1860,18 @@ function resetRecordForm() {
recordForm.attachmentIds = '';
recordForm.feedback = '';
recordForm.remark = '';
recordCertificateFileList.value = [];
console.log('开始重置附件列表,当前 recordCertificateFileList.value:', recordCertificateFileList.value);
if (recordCertificateFileList.value) {
recordCertificateFileList.value = [];
} else {
console.warn('recordCertificateFileList.value 是 undefined,创建新数组');
recordCertificateFileList.value = [];
}
console.log('开始重置表单字段');
recordFormRef.value?.resetFields();
console.log('resetRecordForm 执行完成');
}
/** 取消培训记录对话框 */
@@ -1808,6 +1891,10 @@ function submitRecordForm() {
console.log('证书路径:', recordForm.certificatePath);
console.log('证书文件名:', recordForm.certificateFileName);
// 在提交前确保attachmentIds字段是最新的
updateRecordFormAttachmentIds();
console.log('提交前的attachmentIds:', recordForm.attachmentIds);
if (recordForm.recordId) {
// 修改记录时,保存证书信息到本地存储
if (recordForm.certificatePath) {
@@ -1873,51 +1960,108 @@ async function handleUpdateRecord(row: RecordVO) {
console.log('=== 修改培训记录开始 ===');
console.log('row数据:', row);
resetRecordForm();
// 加载课程选项
if (courseOptions.value.length === 0) {
loadCourseOptions();
}
try {
const response = await getTrainingRecord(row.recordId);
const data = response.data || row;
Object.assign(recordForm, data);
// 确保课程名称正确显示
if (!recordForm.courseName && recordForm.courseId) {
const course = courseOptions.value.find((c) => c.courseId === recordForm.courseId);
if (course) {
recordForm.courseName = course.title;
}
// 立即显示对话框,避免用户等待
recordDialogVisible.value = true;
recordDialogTitle.value = '修改培训记录';
// 重置表单
try {
console.log('开始重置表单...');
resetRecordForm();
console.log('表单重置完成');
} catch (error) {
console.error('重置表单时出错:', error);
throw error;
}
} catch (error) {
console.log('使用模拟数据,原因:', error);
// 模拟数据,实际应使用API返回的数据
Object.assign(recordForm, {
recordId: row.recordId,
courseId: row.courseId,
courseName: row.courseName,
userId: row.userId,
completionStatus: row.completionStatus,
score: row.score,
duration: row.duration,
completionTime: row.completionTime,
certificatePath: row.certificatePath,
certificateFileName: row.certificateFileName,
remark: row.remark
// 先设置基础数据,确保对话框内容可见
try {
console.log('开始设置基础数据...');
Object.assign(recordForm, {
recordId: row.recordId,
courseId: row.courseId,
courseName: row.courseName,
userId: row.userId,
userName: row.userName,
completionStatus: row.completionStatus || '0',
score: row.score || '',
duration: row.duration || '',
completionTime: row.completionTime || '',
certificatePath: row.certificatePath || '',
certificateNumber: row.certificateNumber || '',
certificateFileName: row.certificateFileName || '',
attachmentIds: (row as any).attachmentIds || '',
feedback: row.feedback || '',
remark: row.remark || ''
});
console.log('基础数据设置完成,attachmentIds:', recordForm.attachmentIds);
} catch (error) {
console.error('设置基础数据时出错:', error);
throw error;
}
console.log('设置基础数据后的recordForm:', recordForm);
console.log('当前courseOptions.value:', courseOptions.value);
// 异步加载课程选项(如果还没有加载)
try {
console.log('开始处理课程选项,当前courseOptions:', courseOptions.value);
if (!courseOptions.value || courseOptions.value.length === 0) {
console.log('需要加载课程选项');
loadCourseOptions().then(() => {
console.log('课程选项加载完成,开始匹配课程名称');
// 确保课程名称正确显示
if (!recordForm.courseName && recordForm.courseId && courseOptions.value) {
const course = courseOptions.value.find((c) => c.courseId === recordForm.courseId);
if (course) {
recordForm.courseName = course.title;
}
}
}).catch(error => {
console.warn('加载课程选项失败:', error);
});
} else {
console.log('使用现有课程选项');
// 确保课程名称正确显示
if (!recordForm.courseName && recordForm.courseId && courseOptions.value) {
const course = courseOptions.value.find((c) => c.courseId === recordForm.courseId);
if (course) {
recordForm.courseName = course.title;
}
}
}
console.log('课程选项处理完成');
} catch (error) {
console.error('处理课程选项时出错:', error);
throw error;
}
// 异步获取详细数据(可选)
try {
const response = await getTrainingRecord(row.recordId);
if (response.data) {
// 合并API返回的数据,保留已设置的基础数据
Object.assign(recordForm, response.data);
console.log('API返回的详细数据:', response.data);
}
} catch (error) {
console.log('获取详细数据失败,使用基础数据:', error);
// 使用基础数据继续,不影响对话框显示
}
// 异步处理证书附件数据
handleRecordAttachmentData().catch(error => {
console.warn('处理附件数据失败:', error);
// 不影响对话框显示
});
console.log('=== 修改培训记录完成 ===');
} catch (error) {
console.error('修改培训记录过程中发生错误:', error);
// 即使出错也要显示对话框,让用户看到基础数据
proxy?.$modal.msgError('加载培训记录数据时发生错误,请重试');
}
console.log('设置后的recordForm:', recordForm);
// 处理证书附件数据
await handleRecordAttachmentData();
recordDialogVisible.value = true;
recordDialogTitle.value = '修改培训记录';
console.log('=== 修改培训记录完成 ===');
}
/** 查看培训记录详情 */
@@ -1960,7 +2104,7 @@ function handleDeleteRecord(row?: RecordVO) {
if (row) {
confirmMessage = `是否确认删除学员"${row.userName}"的培训记录?`;
} else {
const selectedItems = recordList.value.filter((item) => recordIds.value.includes(item.recordId as number | string));
const selectedItems = recordList.value.filter((item) => recordIds.value.includes(item.applicationId as number | string));
if (selectedItems.length === 1) {
confirmMessage = `是否确认删除学员"${selectedItems[0].userName}"的培训记录?`;
} else {
@@ -2090,7 +2234,7 @@ function handleRecordCertificateSuccess(response: any, file: any) {
file.url = response.data.url;
file.ossId = response.data.ossId;
proxy?.$modal.msgSuccess('证书上传成功!');
proxy?.$modal.msgSuccess('证书上传成功!');
console.log('证书上传成功:', response.data);
// 更新文件状态为成功
@@ -2099,6 +2243,9 @@ function handleRecordCertificateSuccess(response: any, file: any) {
// 自动保存附件ID到后端
saveRecordAttachments();
// 同时更新recordForm中的attachmentIds字段,确保提交时包含附件信息
updateRecordFormAttachmentIds();
}
}
@@ -2147,6 +2294,25 @@ async function saveRecordAttachments() {
}
}
/** 更新recordForm中的attachmentIds字段 */
function updateRecordFormAttachmentIds() {
if (!recordForm.recordId) {
console.warn('记录ID为空,无法更新附件ID');
return;
}
// 提取所有成功上传的ossId
const attachmentOssIds = recordCertificateFileList.value
.filter(file => file.status === 'success' && file.ossId)
.map(file => file.ossId);
// 转换为逗号分隔的字符串并更新recordForm
const attachmentIds = attachmentOssIds.join(',');
recordForm.attachmentIds = attachmentIds;
console.log('更新recordForm.attachmentIds:', attachmentIds);
}
/** 处理培训记录附件数据 */
async function handleRecordAttachmentData() {
try {
@@ -2761,6 +2927,9 @@ function handleRemoveCertificate(file: any) {
// 自动保存附件ID到后端
saveRecordAttachments();
// 同时更新recordForm中的attachmentIds字段
updateRecordFormAttachmentIds();
proxy?.$modal.msgSuccess('证书文件已删除');
}
@@ -2774,8 +2943,8 @@ function handleRecordQuery() {
function resetRecordQuery() {
recordQueryForm.value?.resetFields();
recordQueryParams.courseName = '';
recordQueryParams.userName = '';
recordQueryParams.completionStatus = '';
recordQueryParams.location = '';
recordQueryParams.department = '';
handleRecordQuery();
}
@@ -3115,6 +3284,19 @@ onMounted(() => {
color: #909399 !important;
line-height: 1.4 !important;
}
/* 只读输入框样式 */
.readonly-input :deep(.el-input__inner) {
background-color: #f5f7fa !important;
color: #606266 !important;
border-color: #dcdfe6 !important;
cursor: not-allowed !important;
}
.readonly-input :deep(.el-input__inner:focus) {
border-color: #dcdfe6 !important;
box-shadow: none !important;
}
</style>
<style scoped>
+32 -8
View File
@@ -57,7 +57,7 @@
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button icon="Download" @click="handleExportTrainingApplication" v-hasPermi="['rs:trainingApproval:export']">导出</el-button>
<el-button icon="Download" @click="handleExportTrainingApplication" v-hasPermi="['rs:trainingapproval:export']" style="display: none">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showApplicationSearch" @queryTable="getApplicationList"></right-toolbar>
</el-row>
@@ -120,7 +120,7 @@
</el-tab-pane>
<!-- 审批记录选项卡 -->
<el-tab-pane label="审批记录" name="approvalRecord" v-hasPermi="['rs:trainingApproval:query']">
<el-tab-pane label="审批记录" name="approvalRecord" v-hasPermi="['rs:trainingapproval:query']">
<!-- 搜索区域 -->
<transition :enter-active-class="animate.searchAnimate.enter" :leave-active-class="animate.searchAnimate.leave">
<div v-show="showApprovalRecordSearch" class="mb-[10px]">
@@ -175,7 +175,7 @@
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button icon="Download" @click="handleExportApprovalRecord" v-hasPermi="['rs:trainingApproval:export']">导出</el-button>
<el-button icon="Download" @click="handleExportApprovalRecord" v-hasPermi="['rs:trainingapproval:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showApprovalRecordSearch" @queryTable="getApprovalRecordList"></right-toolbar>
</el-row>
@@ -351,12 +351,12 @@
<el-row>
<el-col :span="12">
<el-form-item label="申请时间">
<el-input v-model="approvalRecordDetail.applyTime" readonly />
<el-input :value="parseTime(approvalRecordDetail.applyTime)" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="审批时间">
<el-input v-model="approvalRecordDetail.approvalTime" readonly />
<el-input :value="parseTime(approvalRecordDetail.approvalTime)" readonly />
</el-form-item>
</el-col>
</el-row>
@@ -822,8 +822,26 @@ const handleApprovalRecordSelectionChange = (selection: any[]) => {
// 查看审批记录详情
const handleViewApprovalRecord = async (row: any) => {
try {
const response = await getTrainingApplication(row.applicationId);
Object.assign(approvalRecordDetail, response.data);
// 获取原始申请数据,以获取完整的申请信息(申请时间、部门、申请理由等)
const applicationResponse = await getTrainingApplication(row.applicationId);
const applicationData = applicationResponse.data;
// 合并申请数据和审批数据
Object.assign(approvalRecordDetail, {
applicationId: row.applicationId,
courseId: applicationData.courseId,
courseName: applicationData.courseName,
userId: applicationData.userId,
userName: applicationData.userName,
department: applicationData.department, // 从申请数据获取部门
status: row.status, // 使用审批记录的状态
applyTime: applicationData.applyTime, // 从申请数据获取申请时间
approvalTime: row.approvalTime, // 从审批记录获取审批时间
reason: applicationData.reason, // 从申请数据获取申请理由
remark: applicationData.remark,
adminRemark: row.opinion, // 从审批记录获取审批意见
certificatePath: applicationData.certificatePath
});
// 获取审批历史记录
const historyResponse = await getTrainingApprovalHistory(row.applicationId);
@@ -1007,7 +1025,13 @@ onMounted(() => {
}
.search-btn-container {
text-align: right;
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.search-btn-container :deep(.el-form-item) {
margin-bottom: 0 !important;
}
.button-card {
+347 -232
View File
@@ -2,125 +2,133 @@
<div class="page-container">
<!-- 搜索区域 -->
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<el-card v-show="showSearch" class="search-card">
<el-form :model="queryParams" ref="queryFormRef" label-width="80px">
<el-row :gutter="20">
<!-- 第一行:4个条件 -->
<el-col :span="6">
<el-form-item label="姓名" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入姓名" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="身份证号" prop="idCard">
<el-input v-model="queryParams.idCard" placeholder="请输入身份证号" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="准考证号" prop="examNumber">
<el-input v-model="queryParams.examNumber" placeholder="请输入准考证号" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="性别" prop="gender">
<el-select v-model="queryParams.gender" placeholder="请选择性别" clearable class="w-100">
<el-option v-for="dict in zsjy_gender" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<!-- 第一行:4个搜索条件 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="姓名" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入姓名" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="身份证号" prop="idCard">
<el-input v-model="queryParams.idCard" placeholder="请输入身份证号" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="准考证号" prop="examNumber">
<el-input v-model="queryParams.examNumber" placeholder="请输入准考证号" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="性别" prop="gender">
<el-select v-model="queryParams.gender" placeholder="请选择性别" clearable style="width: 100%">
<el-option v-for="dict in zsjy_gender" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<!-- 第二行:4个条件 -->
<el-col :span="6">
<el-form-item label="所属学院" prop="collegeId">
<el-select v-model="queryParams.collegeId" placeholder="请选择学院" clearable class="w-100" @change="onSearchCollegeChange">
<el-option v-for="college in collegeOptions" :key="college.deptId" :label="college.deptName" :value="college.deptCategory" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="报考专业" prop="appliedMajor">
<el-select v-model="queryParams.appliedMajor" placeholder="请选择报考专业" clearable class="w-100">
<el-option v-for="major in searchMajorOptions" :key="major.value" :label="major.label" :value="major.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="联系电话" prop="phone1">
<el-input v-model="queryParams.phone1" placeholder="请输入联系电话" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="中考地区" prop="examRegion">
<el-input v-model="queryParams.examRegion" placeholder="请输入中考地区" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
</el-row>
<!-- 第二行:4个搜索条件 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="所属学院" prop="collegeId">
<el-select v-model="queryParams.collegeId" placeholder="请选择学院" clearable style="width: 100%" @change="onSearchCollegeChange">
<el-option v-for="college in collegeOptions" :key="college.deptId" :label="college.deptName" :value="college.deptCategory" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="报考专业" prop="appliedMajor">
<el-select v-model="queryParams.appliedMajor" placeholder="请选择报考专业" clearable style="width: 100%">
<el-option v-for="major in searchMajorOptions" :key="major.value" :label="major.label" :value="major.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="联系电话" prop="phone1">
<el-input v-model="queryParams.phone1" placeholder="请输入联系电话" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="中考地区" prop="examRegion">
<el-input v-model="queryParams.examRegion" placeholder="请输入中考地区" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<!-- 第三行:1个条件 + 操作按钮 -->
<el-col :span="6">
<el-form-item label="报名状态" prop="enrollmentStatus">
<el-select v-model="queryParams.enrollmentStatus" placeholder="请选择报名状态" clearable class="w-100">
<el-option :value="ENROLLMENT_STATUS.PENDING" label="待审核" />
<el-option :value="ENROLLMENT_STATUS.AUDITING" label="信息审核中" />
<el-option :value="ENROLLMENT_STATUS.PROCESSING" label="录取处理中" />
<el-option :value="ENROLLMENT_STATUS.CONFIRMING" label="待最终确认" />
<el-option :value="ENROLLMENT_STATUS.COMPLETED" label="录取完成" />
<el-option :value="ENROLLMENT_STATUS.REJECTED" label="已驳回" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="18" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<!-- 第三行:1个搜索条件 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="报名状态" prop="enrollmentStatus">
<el-select v-model="queryParams.enrollmentStatus" placeholder="请选择报名状态" clearable style="width: 100%">
<el-option :value="ENROLLMENT_STATUS.PENDING" label="待审核" />
<el-option :value="ENROLLMENT_STATUS.AUDITING" label="信息审核中" />
<el-option :value="ENROLLMENT_STATUS.PROCESSING" label="录取处理中" />
<el-option :value="ENROLLMENT_STATUS.CONFIRMING" label="待最终确认" />
<el-option :value="ENROLLMENT_STATUS.COMPLETED" label="录取完成" />
<el-option :value="ENROLLMENT_STATUS.REJECTED" label="已驳回" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 操作按钮行 -->
<el-row :gutter="10" class="mb8">
<el-col :span="24" class="search-btn-container">
<el-form-item style="margin-bottom: 0;">
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
</transition>
<!-- 操作按钮区域 -->
<el-card class="button-card">
<!-- 表格数据 -->
<el-card shadow="never" class="button-card table-card">
<template #header>
<div class="card-header-vertical">
<span class="toolbar-title">招生信息管理</span>
<div class="search-toggle">
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</div>
</div>
<el-row ref="toolbarRef" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['zsjy:enrollment:info:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['zsjy:enrollment:info:edit']">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['zsjy:enrollment:info:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:enrollment:info:export']">导出</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Check" :disabled="multiple" @click="handleBatchAudit()" v-hasPermi="['zsjy:enrollment:info:edit']">批量审核</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<div class="button-group">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['zsjy:enrollment:info:add']">新增</el-button>
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['zsjy:enrollment:info:edit']"
>修改</el-button
>
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['zsjy:enrollment:info:remove']"
>删除</el-button
>
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:enrollment:info:export']">导出</el-button>
<el-button type="success" plain icon="Check" :disabled="multiple" @click="handleBatchAudit()" v-hasPermi="['zsjy:enrollment:info:edit']"
>批量审核</el-button
>
</div>
</el-card>
<!-- 表格区域 -->
<el-card class="table-card">
<div class="table-container">
<el-table v-loading="loading" :data="enrollmentInfoList" class="scroll-table" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="主键ID" align="center" prop="id" v-if="false" />
<el-table-column label="姓名" align="center" prop="name" :show-overflow-tooltip="true" />
<el-table
ref="enrollmentTableRef"
v-loading="loading"
border
:data="enrollmentInfoList"
row-key="id"
class="scroll-table"
@selection-change="handleSelectionChange"
style="width: 100%"
:scroll-x="true"
>
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="姓名" align="center" prop="name" :show-overflow-tooltip="true" min-width="100" />
<el-table-column label="身份证号" align="center" prop="idCard" :show-overflow-tooltip="true" width="180" />
<el-table-column label="性别" align="center" prop="gender">
<el-table-column label="性别" align="center" prop="gender" width="80">
<template #default="scope">
<!-- 直接显示性别文字 -->
<span class="gender-text-direct">
{{ scope.row.gender === 'M' ? '男' : scope.row.gender === 'F' ? '女' : getGenderDisplay(scope.row.gender, scope.row.idCard) }}
</span>
@@ -128,7 +136,7 @@
</el-table-column>
<el-table-column label="联系电话" align="center" prop="phone1" :show-overflow-tooltip="true" width="130" />
<el-table-column label="准考证号" align="center" prop="examNumber" :show-overflow-tooltip="true" width="130" />
<el-table-column label="中考地区" align="center" prop="examRegion" :show-overflow-tooltip="true" />
<el-table-column label="中考地区" align="center" prop="examRegion" :show-overflow-tooltip="true" min-width="120" />
<el-table-column label="中考成绩" align="center" prop="totalScore" width="100" />
<el-table-column label="报考专业" align="center" prop="appliedMajor" :show-overflow-tooltip="true" width="120" />
<el-table-column label="所属学院" align="center" prop="collegeName" :show-overflow-tooltip="true" width="120" />
@@ -142,39 +150,31 @@
<span>{{ parseTime(scope.row.applicationTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="220" fixed="right">
<el-table-column label="操作" align="center" width="280" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<div class="action-buttons">
<el-tooltip content="查看" placement="top">
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['zsjy:enrollment:info:query']"></el-button>
</el-tooltip>
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['zsjy:enrollment:info:edit']"></el-button>
</el-tooltip>
<el-tooltip content="审核" placement="top">
<el-button link type="success" icon="Check" @click="handleAudit(scope.row)" v-hasPermi="['zsjy:enrollment:info:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button
link
type="danger"
icon="Delete"
@click="handleDelete(scope.row)"
v-hasPermi="['zsjy:enrollment:info:remove']"
></el-button>
</el-tooltip>
</div>
<el-tooltip content="查看" placement="top">
<el-button link type="info" icon="View" @click="handleView(scope.row)" v-hasPermi="['zsjy:enrollment:info:query']"></el-button>
</el-tooltip>
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['zsjy:enrollment:info:edit']"></el-button>
</el-tooltip>
<el-tooltip content="审核" placement="top">
<el-button link type="success" icon="Check" @click="handleAudit(scope.row)" v-hasPermi="['zsjy:enrollment:info:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['zsjy:enrollment:info:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination-container">
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</div>
</el-card>
<!-- 分页 -->
<div class="pagination-container">
<pagination v-show="total > 0" :page="queryParams.pageNum" :limit="queryParams.pageSize" :total="total" @pagination="getList" />
</div>
<!-- 添加或修改招生报名信息对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="1000px" append-to-body>
<el-form ref="enrollmentInfoFormRef" :model="form" :rules="rules" label-width="120px">
@@ -892,6 +892,7 @@ const handleBatchCurrentChange = (val: number) => {
const queryFormRef = ref<FormInstance>();
const enrollmentInfoFormRef = ref<FormInstance>();
const enrollmentTableRef = ref();
const dialog = reactive<DialogOption>({
visible: false,
@@ -1594,6 +1595,33 @@ const handleExport = () => {
);
};
/** 批量更新按钮操作 */
const handleBatchUpdate = async () => {
if (ids.value.length === 0) {
proxy?.$modal.msgError('请选择要修改的招生报名信息');
return;
}
if (ids.value.length > 1) {
proxy?.$modal.msgError('批量修改功能暂不支持,请选择单条记录进行修改');
return;
}
// 如果只选择了一条记录,调用单个修改方法
const row = enrollmentInfoList.value.find(item => item.id === ids.value[0]);
if (row) {
handleUpdate(row);
}
};
/** 批量删除按钮操作 */
const handleBatchDelete = async () => {
if (ids.value.length === 0) {
proxy?.$modal.msgError('请选择要删除的招生报名信息');
return;
}
// 调用现有的删除方法,它已经支持批量删除
handleDelete();
};
/** 批量审核按钮操作 */
const handleBatchAudit = async () => {
if (ids.value.length === 0) {
@@ -1920,77 +1948,157 @@ onMounted(() => {
</script>
<style scoped>
/* 页面容器样式 */
.page-container {
padding: 20px;
background-color: #f5f7fa;
}
.search-card {
margin-bottom: 20px;
padding: 20px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.search-btn-container {
text-align: right;
}
.button-card {
margin-bottom: 20px;
padding: 20px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.card-header-vertical {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.toolbar-title {
font-size: 18px;
font-weight: bold;
color: #303133;
}
.button-group {
display: flex;
align-items: center;
gap: 10px;
padding-top: 5px;
}
.search-toggle {
display: flex;
align-items: center;
padding: 8px;
}
/* 表格卡片布局 */
.table-card {
margin-bottom: 20px;
padding: 20px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
margin-top: 0;
}
.table-card :deep(.el-card__body) {
padding: 0px 20px 20px 20px !important;
}
/* 表格容器 - 支持横向滚动 */
.table-container {
overflow-x: auto;
width: 100%;
margin-bottom: 0;
}
/* 横向滚动表格样式 */
.scroll-table {
width: 100%;
}
.pagination-container {
text-align: right;
padding-top: 20px;
/* 表格横向滚动条样式优化 */
.scroll-table :deep(.el-table__body-wrapper) {
overflow-x: auto !important; /* 自动显示横向滚动条 */
overflow-y: auto;
}
.scroll-table :deep(.el-table__header-wrapper) {
overflow-x: hidden;
}
/* 强制显示表格横向滚动条 - 适用于所有浏览器 */
.scroll-table :deep(.el-table__body-wrapper) {
scrollbar-width: thin; /* Firefox */
scrollbar-color: #409eff #f0f0f0; /* Firefox - thumb track */
}
/* Webkit 浏览器的横向滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 12px !important; /* 增加滚动条高度 */
width: 12px !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f0f0f0 !important;
border-radius: 6px !important;
border: 1px solid #e4e7ed !important; /* 添加边框使其更明显 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #409eff !important; /* 使用主题色,更明显 */
border-radius: 6px !important;
border: 2px solid #f0f0f0 !important; /* 添加白色边框 */
min-height: 20px !important; /* 确保最小高度 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #337ecc !important; /* 悬停时更深的蓝色 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
background: #2b6cb0 !important; /* 点击时的颜色 */
}
/* 确保滚动条始终可见的额外设置 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-corner {
background: #f0f0f0;
}
/* 固定列样式优化 */
.scroll-table :deep(.el-table__fixed-right) {
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
}
.scroll-table :deep(.el-table__fixed-left) {
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 确保固定列在滚动时有正确的背景色 */
.scroll-table :deep(.el-table__fixed-right .el-table__cell),
.scroll-table :deep(.el-table__fixed-left .el-table__cell) {
background-color: #fff;
}
/* 表格行悬停效果在固定列上的同步 */
.scroll-table :deep(.el-table__row:hover .el-table__cell) {
background-color: #f5f7fa !important;
}
/* 分页容器样式 */
.pagination-container {
display: flex;
justify-content: flex-end;
padding: 16px 20px 0 0 !important;
background-color: #fff;
margin-top: 0px !important;
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
/* 对话框样式 */
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.dialog-form {
.el-form-item {
margin-bottom: 18px;
}
.form-tip {
font-size: 12px;
color: #909399;
margin-top: 4px;
line-height: 1.4;
}
}
/* 描述列表样式优化 */
:deep(.el-descriptions) {
.el-descriptions__label {
font-weight: 600;
color: #606266;
width: 120px;
}
.el-descriptions__content {
color: #303133;
}
}
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
}
/* 表单区域样式 */
.form-section {
margin-bottom: 20px;
}
@@ -2004,6 +2112,7 @@ onMounted(() => {
border-bottom: 2px solid #409eff;
}
/* 查看文本样式 */
.view-text {
color: #606266;
font-size: 14px;
@@ -2015,16 +2124,43 @@ onMounted(() => {
line-height: 1.5;
}
.w-100 {
width: 100% !important;
/* 性别文本样式 */
.gender-text-direct {
font-size: 14px;
color: #303133;
font-weight: 500;
}
.action-buttons {
display: flex;
justify-content: center;
gap: 10px;
/* 住宿文本样式 */
.boarding-text {
font-size: 14px;
color: #303133;
}
/* 学院和专业名称样式 */
.college-name {
font-size: 14px;
color: #303133;
font-weight: 500;
}
.college-code {
font-size: 14px;
color: #303133;
}
.major-name {
font-size: 14px;
color: #303133;
font-weight: 500;
}
.major-code {
font-size: 14px;
color: #303133;
}
/* 学生信息区域样式 */
.student-info-section {
margin-bottom: 20px;
padding: 20px;
@@ -2041,6 +2177,7 @@ onMounted(() => {
border-bottom: 2px solid #409eff;
}
/* 批量审核相关样式 */
.batch-audit-info {
margin-bottom: 20px;
}
@@ -2069,6 +2206,17 @@ onMounted(() => {
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
/* 其他辅助样式 */
.w-100 {
width: 100% !important;
}
.action-buttons {
display: flex;
justify-content: center;
gap: 10px;
}
.selected-gender {
margin-top: 5px;
font-size: 12px;
@@ -2095,37 +2243,4 @@ onMounted(() => {
font-size: 14px;
color: #606266;
}
.gender-text-direct {
font-size: 14px;
color: #303133;
font-weight: 500;
}
.boarding-text {
font-size: 14px;
color: #303133;
}
.college-name {
font-size: 14px;
color: #303133;
font-weight: 500;
}
.college-code {
font-size: 14px;
color: #303133;
}
.major-name {
font-size: 14px;
color: #303133;
font-weight: 500;
}
.major-code {
font-size: 14px;
color: #303133;
}
</style>
+9
View File
@@ -504,4 +504,13 @@ onMounted(() => {
color: #303133;
}
}
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
}
</style>
+81 -182
View File
@@ -65,13 +65,7 @@
<!-- 第三行:搜索条件 + 操作按钮 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="审核状态" prop="auditStatus">
<el-select v-model="queryParams.auditStatus" placeholder="请选择审核状态" clearable style="width: 100%">
<el-option v-for="dict in zsjy_graduate_audit_status" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<!-- 审核状态搜索条件已隐藏 -->
<el-col :span="6">
<el-form-item label="手机号码" prop="phoneNumber">
<el-input v-model="queryParams.phoneNumber" placeholder="请输入手机号码" clearable @keyup.enter="handleQuery" />
@@ -184,19 +178,7 @@
</div>
</el-card>
</el-col>
<el-col :span="3">
<el-card shadow="hover" class="stat-card stat-card-warning">
<div class="stat-content">
<div class="stat-icon">
<el-icon><DocumentChecked /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ pendingAuditCount || 0 }}</div>
<div class="stat-label">待审核</div>
</div>
</div>
</el-card>
</el-col>
<!-- 待审核统计卡片已隐藏 -->
<el-col :span="3">
<el-card shadow="hover" class="stat-card stat-card-primary" @click="handleStatistics">
<div class="stat-content">
@@ -229,12 +211,7 @@
<el-col :span="1.5">
<el-button icon="Download" @click="handleExport" v-hasPermi="['zsjy:graduate:export']">导出</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="Check" :disabled="multiple" @click="handleBatchAudit('1')" v-hasPermi="['zsjy:graduate:audit']">批量通过</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="Close" :disabled="multiple" @click="handleBatchAudit('2')" v-hasPermi="['zsjy:graduate:audit']">批量拒绝</el-button>
</el-col>
<!-- 批量审核按钮已隐藏 -->
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
@@ -274,19 +251,8 @@
<el-table-column label="工作地点" align="center" prop="workLocation" width="140" show-overflow-tooltip />
<el-table-column label="薪资范围" align="center" prop="salaryRangeName" width="120" />
<el-table-column label="升学院校" align="center" prop="furtherSchool" width="160" show-overflow-tooltip />
<el-table-column label="审核状态" align="center" prop="auditStatus" width="100">
<template #default="scope">
<el-tag :type="getAuditStatusTagType(scope.row.auditStatus)" size="small">
{{ scope.row.auditStatusName }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="审核时间" align="center" prop="auditTime" width="160">
<template #default="scope">
<span v-if="scope.row.auditTime">{{ proxy.parseTime(scope.row.auditTime, '{y}-{m}-{d} {h}:{i}') }}</span>
<span v-else>-</span>
</template>
</el-table-column>
<!-- 审核状态列已隐藏 -->
<!-- 审核时间列已隐藏 -->
<el-table-column label="创建时间" align="center" prop="createTime" width="160">
<template #default="scope">
<span>{{ proxy.parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}') }}</span>
@@ -300,9 +266,7 @@
<el-tooltip content="编辑" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['zsjy:graduate:edit']"></el-button>
</el-tooltip>
<el-tooltip content="审核" placement="top" v-if="scope.row.auditStatus === '0'">
<el-button link type="success" icon="Check" @click="handleAudit(scope.row)" v-hasPermi="['zsjy:graduate:audit']"></el-button>
</el-tooltip>
<!-- 审核按钮已隐藏 -->
<el-tooltip content="删除" placement="top">
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['zsjy:graduate:remove']"></el-button>
</el-tooltip>
@@ -483,39 +447,20 @@
</div>
</el-card>
<!-- 审核信息 -->
<el-card class="detail-card" shadow="never">
<!-- 审核信息已隐藏 -->
<!-- 备注信息 -->
<el-card class="detail-card" shadow="never" v-if="detailData.remark">
<template #header>
<div class="card-header">
<div class="card-title">
<el-icon><DocumentChecked /></el-icon>
<span>审核信息</span>
<el-icon><Document /></el-icon>
<span>备注信息</span>
</div>
</div>
</template>
<div class="detail-grid">
<div class="detail-item">
<label>审核状态:</label>
<div class="detail-value">
<el-tag :type="getAuditStatusTagType(detailData.auditStatus)" size="small">
{{ detailData.auditStatusName }}
</el-tag>
</div>
</div>
<div class="detail-item">
<label>审核人:</label>
<div class="detail-value">{{ detailData.auditByName || '-' }}</div>
</div>
<div class="detail-item">
<label>审核时间:</label>
<div class="detail-value">{{ detailData.auditTime ? proxy.parseTime(detailData.auditTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}</div>
</div>
<div class="detail-item full-width">
<label>审核意见:</label>
<div class="detail-value description">{{ detailData.auditRemark || '-' }}</div>
</div>
<div class="detail-item full-width">
<label>备注信息:</label>
<label>备注:</label>
<div class="detail-value description">{{ detailData.remark || '-' }}</div>
</div>
</div>
@@ -523,26 +468,7 @@
</div>
</el-dialog>
<!-- 审核弹窗 -->
<el-dialog title="审核毕业生信息" v-model="auditVisible" width="500px" :before-close="closeAudit">
<el-form ref="auditFormRef" :model="auditForm" label-width="100px">
<el-form-item label="审核状态" prop="auditStatus" required>
<el-radio-group v-model="auditForm.auditStatus">
<el-radio value="1">通过</el-radio>
<el-radio value="2">拒绝</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="审核意见" prop="auditRemark">
<el-input v-model="auditForm.auditRemark" type="textarea" :rows="4" placeholder="请输入审核意见(选填)" maxlength="500" show-word-limit />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeAudit">取消</el-button>
<el-button type="primary" @click="submitAudit">确定</el-button>
</div>
</template>
</el-dialog>
<!-- 审核弹窗已隐藏 -->
</div>
</template>
@@ -553,18 +479,17 @@ import {
getGraduateInfo,
delGraduateInfo,
exportGraduateInfo,
auditGraduateInfo,
batchAuditGraduateInfo,
getOverallStatistics,
getPendingAuditCount
getOverallStatistics
} from '@/api/zsjy/graduateInfo';
import { GraduateInfoVO, GraduateInfoQuery, GraduateStatisticsVO } from '@/api/zsjy/types';
const { proxy } = getCurrentInstance() as any;
const { sys_user_sex, zsjy_graduate_employment_status, zsjy_graduate_audit_status } = proxy.useDict(
const { sys_user_sex, zsjy_education_level, zsjy_graduate_employment_status, zsjy_graduate_company_type, zsjy_graduate_salary_range } = proxy.useDict(
'sys_user_sex',
'zsjy_education_level',
'zsjy_graduate_employment_status',
'zsjy_graduate_audit_status'
'zsjy_graduate_company_type',
'zsjy_graduate_salary_range'
);
// 响应式数据
@@ -576,15 +501,8 @@ const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const statistics = ref<GraduateStatisticsVO>({});
const pendingAuditCount = ref(0);
const detailVisible = ref(false);
const detailData = ref<GraduateInfoVO>();
const auditVisible = ref(false);
const auditForm = reactive({
id: '',
auditStatus: '1',
auditRemark: ''
});
const daterangeCreateTime = ref<[string, string]>(['', '']);
// 查询参数
@@ -599,7 +517,6 @@ const queryParams = reactive<GraduateInfoQuery>({
collegeName: '',
majorName: '',
employmentStatus: '',
auditStatus: '',
phoneNumber: '',
params: {}
});
@@ -616,26 +533,46 @@ const getList = async () => {
try {
const response = await listGraduateInfo(queryParams);
console.log('API响应完整结构:', response); // 调试日志
console.log('响应类型:', typeof response); // 调试日志
// 检查响应数据格式并设置列表数据
if (response && response.rows !== undefined) {
// 处理 TableDataInfo 格式的数据 (直接从response获取)
graduateInfoList.value = response.rows || [];
total.value = response.total || 0;
} else if (response && response.data && response.data.rows !== undefined) {
// 处理嵌套在data中的 TableDataInfo 格式
graduateInfoList.value = response.data.rows || [];
total.value = response.data.total || 0;
} else if (response && Array.isArray(response)) {
// 处理直接返回的数组格式
graduateInfoList.value = response;
total.value = response.length;
} else if (response && response.data && Array.isArray(response.data)) {
// 处理嵌套的数组格式
graduateInfoList.value = response.data;
total.value = response.data.length;
// 根据request.ts的响应拦截器,response直接是TableDataInfo格式
if (response && typeof response === 'object') {
// 使用类型断言处理不同的响应格式
const responseData = response as any;
// 检查是否是TableDataInfo格式(直接有rows和total)
if (responseData.rows !== undefined) {
console.log('响应数据是TableDataInfo格式');
graduateInfoList.value = responseData.rows || [];
total.value = responseData.total || 0;
}
// 如果是R<TableDataInfo>格式(有data字段)
else if (responseData.data && responseData.data.rows !== undefined) {
console.log('响应数据是R<TableDataInfo>格式');
graduateInfoList.value = responseData.data.rows || [];
total.value = responseData.data.total || 0;
}
// 如果直接是数组
else if (Array.isArray(responseData)) {
console.log('响应数据是直接数组格式');
graduateInfoList.value = responseData;
total.value = responseData.length;
}
// 其他格式
else {
console.error('未知的响应数据格式:', responseData);
console.error('响应对象的属性:', Object.keys(responseData)); // 调试日志
graduateInfoList.value = [];
total.value = 0;
}
console.log('设置列表数据:', graduateInfoList.value.length, '条记录'); // 调试日志
if (graduateInfoList.value.length > 0) {
console.log('第一条数据示例:', graduateInfoList.value[0]);
console.log('第一条数据的employmentCompany:', graduateInfoList.value[0]?.employmentCompany);
}
} else {
console.error('未知的响应数据格式:', response);
console.error('响应数据格式异常:', response);
graduateInfoList.value = [];
total.value = 0;
}
@@ -652,11 +589,31 @@ const getList = async () => {
/** 获取统计数据 */
const getStatistics = async () => {
try {
const [statsResponse, pendingResponse] = await Promise.all([getOverallStatistics(), getPendingAuditCount()]);
statistics.value = statsResponse.data;
pendingAuditCount.value = pendingResponse.data;
const statsResponse = await getOverallStatistics();
console.log('统计数据响应:', statsResponse); // 调试日志
// 根据后端Controller,统计接口返回R<GraduateStatisticsVO>格式
if (statsResponse && typeof statsResponse === 'object') {
// 使用类型断言处理响应格式
const statsData = statsResponse as any;
// 如果响应有data字段(R格式)
if (statsData.data !== undefined) {
console.log('统计数据是R格式');
statistics.value = statsData.data || {};
}
// 如果响应直接是统计对象
else {
console.log('统计数据是直接对象格式');
statistics.value = statsData;
}
} else {
statistics.value = {};
}
console.log('设置统计数据:', statistics.value); // 调试日志
} catch (error) {
console.error('获取统计数据失败:', error);
statistics.value = {};
}
};
@@ -709,53 +666,7 @@ const closeDetail = () => {
detailData.value = undefined;
};
/** 审核按钮操作 */
const handleAudit = (row: GraduateInfoVO) => {
auditForm.id = row.id as string;
auditForm.auditStatus = '1';
auditForm.auditRemark = '';
auditVisible.value = true;
};
/** 提交审核 */
const submitAudit = async () => {
try {
await auditGraduateInfo(auditForm.id, auditForm.auditStatus, auditForm.auditRemark);
proxy.$modal.msgSuccess('审核成功');
closeAudit();
getList();
getStatistics();
} catch (error) {
proxy.$modal.msgError('审核失败');
}
};
/** 关闭审核弹窗 */
const closeAudit = () => {
auditVisible.value = false;
auditForm.id = '';
auditForm.auditStatus = '1';
auditForm.auditRemark = '';
};
/** 批量审核操作 */
const handleBatchAudit = async (auditStatus: string) => {
if (ids.value.length === 0) {
proxy.$modal.msgWarning('请选择要审核的数据');
return;
}
const statusText = auditStatus === '1' ? '通过' : '拒绝';
try {
await proxy.$modal.confirm(`确认要批量${statusText}选中的毕业生信息吗?`);
await batchAuditGraduateInfo(ids.value, auditStatus);
proxy.$modal.msgSuccess(`批量${statusText}成功`);
getList();
getStatistics();
} catch (error) {
// 用户取消操作
}
};
/** 审核相关功能已隐藏 */
/** 删除按钮操作 */
const handleDelete = async (row?: GraduateInfoVO) => {
@@ -809,19 +720,7 @@ const getEmploymentStatusTagType = (status: string) => {
}
};
/** 获取审核状态标签类型 */
const getAuditStatusTagType = (status: string) => {
switch (status) {
case '0':
return 'warning';
case '1':
return 'success';
case '2':
return 'danger';
default:
return '';
}
};
/** 审核状态标签类型函数已移除 */
// 页面初始化
onMounted(() => {
+163 -97
View File
@@ -1,5 +1,5 @@
<template>
<div class="p-2 page-container">
<div class="page-container">
<!-- 搜索区域 -->
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
@@ -34,7 +34,7 @@
</el-col>
</el-row>
<!-- 第二行:日期筛选 + 操作按钮 -->
<!-- 第二行:日期筛选 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="签到日期">
@@ -50,8 +50,12 @@
></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12" class="search-btn-container">
<el-form-item>
</el-row>
<!-- 第三行:搜索按钮 -->
<el-row :gutter="10" class="mb8">
<el-col :span="24" class="search-btn-container">
<el-form-item style="margin-bottom: 0;">
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
@@ -120,35 +124,38 @@
</el-row>
</div>
<!-- 操作工具栏 -->
<el-card shadow="never" class="button-card">
<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: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:internshipdaily:export']">导出</el-button>
</div>
</div>
</el-card>
<!-- 表格数据 -->
<el-card shadow="never" class="button-card table-card">
<template #header>
<el-row ref="toolbarRef" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['zsjy:internshipdaily:add']">新增签到</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:internshipdaily:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<!-- 数据表格 -->
<el-card shadow="never" class="table-card">
<div class="table-container">
<el-table
ref="internshipDailyTableRef"
v-loading="loading"
border
:data="internshipDailyList"
row-key="id"
class="scroll-table"
@selection-change="handleSelectionChange"
style="width: 100%"
:scroll-x="true"
>
<el-table-column type="selection" width="50" align="center" />
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="学生信息" min-width="180" align="left">
<template #default="scope">
<div class=":liststudent-info">
<div class="student-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> -->
@@ -176,27 +183,35 @@
<el-table-column label="签到地点" prop="checkInLocation" min-width="150" align="left" show-overflow-tooltip />
<el-table-column label="操作" width="120" align="center" class-name="small-padding fixed-width">
<el-table-column label="操作" width="160" align="center" class-name="small-padding fixed-width" fixed="right">
<template #default="scope">
<div class="action-buttons">
<el-tooltip content="查看详情" placement="top">
<el-button link type="primary" icon="View" @click="handleView(scope.row)" />
</el-tooltip>
<el-tooltip content="查看日历" placement="top">
<el-button link type="success" icon="Calendar" @click="handleCalendar(scope.row)" />
</el-tooltip>
</div>
<el-tooltip content="查看详情" placement="top">
<el-button
link
type="info"
icon="View"
@click="handleView(scope.row)"
></el-button>
</el-tooltip>
<el-tooltip content="查看日历" placement="top">
<el-button
link
type="success"
icon="Calendar"
@click="handleCalendar(scope.row)"
></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页组件 -->
<div class="pagination-container">
<pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @pagination="getList" />
</div>
</el-card>
<!-- 分页 -->
<div class="pagination-container">
<pagination v-show="total > 0" :page="queryParams.pageNum" :limit="queryParams.pageSize" :total="total" @pagination="getList" />
</div>
<!-- 详情对话框 -->
<el-dialog title="考勤详情" v-model="viewDialogVisible" width="600px" append-to-body>
<div v-if="currentRecord" class="detail-container">
@@ -615,47 +630,114 @@ onMounted(() => {
</script>
<style scoped lang="scss">
// 继承后勤管理的样式
/* 页面容器 */
.page-container {
.search-btn-container {
text-align: right;
padding: 8px;
}
/* 搜索按钮容器 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
}
.button-card {
margin-bottom: 20px;
/* 表格卡片 */
.button-card {
:deep(.el-card__header) {
border-bottom: none !important;
padding: 18px 20px 0 20px;
}
}
.card-header-vertical {
display: flex;
justify-content: space-between;
align-items: center;
/* 表格容器和滚动样式 */
.table-container {
overflow-x: auto;
overflow-y: hidden;
.scroll-table {
min-width: 100%;
/* 滚动条样式 */
:deep(.el-table__body-wrapper) {
&::-webkit-scrollbar {
height: 8px;
}
&::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
&::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 4px;
&:hover {
background: #a8a8a8;
}
}
/* Firefox 滚动条 */
scrollbar-width: thin;
scrollbar-color: #c1c1c1 #f1f1f1;
}
.toolbar-title {
font-size: 18px;
font-weight: bold;
color: #303133;
/* 固定列阴影效果 */
:deep(.el-table__fixed) {
&::before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: -1px;
width: 30px;
pointer-events: none;
transition: box-shadow 0.3s;
}
}
.button-group {
display: flex;
align-items: center;
gap: 10px;
:deep(.el-table__fixed-right) {
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: -1px;
width: 30px;
pointer-events: none;
transition: box-shadow 0.3s;
}
}
/* 表格行悬停效果 */
:deep(.el-table__row) {
&:hover {
background-color: #f5f7fa;
.el-table__fixed .el-table__cell {
background-color: #f5f7fa;
}
.el-table__fixed-right .el-table__cell {
background-color: #f5f7fa;
}
}
}
}
}
.table-card {
.table-container {
overflow-x: auto;
}
.pagination-container {
display: flex;
justify-content: flex-end;
padding: 16px 20px 0 0 !important;
background-color: #fff;
margin-top: 0px !important;
}
}
/* 分页容器 */
.pagination-container {
display: flex;
justify-content: flex-end;
padding: 16px 20px 0 0 !important;
background-color: #fff;
margin-top: 0px !important;
}
/* 统计卡片样式 */
@@ -721,27 +803,6 @@ onMounted(() => {
}
}
/* 表格样式 */
.scroll-table {
:deep(.el-table__header-wrapper) {
.el-table__header {
th {
background-color: #f8f9fa;
color: #333;
font-weight: 600;
}
}
}
:deep(.el-table__body-wrapper) {
.el-table__row {
&:hover {
background-color: #f5f7fa;
}
}
}
}
/* 学生信息样式 */
.student-info {
.student-name {
@@ -772,14 +833,6 @@ onMounted(() => {
color: #909399;
}
/* 操作按钮样式 */
.action-buttons {
display: flex;
gap: 8px;
justify-content: center;
align-items: center;
}
/* 对话框样式 */
.dialog-footer-right {
display: flex;
@@ -831,15 +884,28 @@ onMounted(() => {
}
}
/* 描述列表样式优化 */
:deep(.el-descriptions) {
.el-descriptions__label {
font-weight: 600;
color: #606266;
width: 120px;
}
.el-descriptions__content {
color: #303133;
}
}
/* 按钮栏样式 */
.mb8 {
margin-bottom: 8px;
}
/* 响应式设计 */
@media (max-width: 768px) {
.stat-card {
margin-bottom: 10px;
}
.action-buttons {
flex-direction: column;
gap: 4px;
}
}
</style>
+308 -168
View File
@@ -1,9 +1,11 @@
<template>
<div class="p-2 page-container">
<div class="page-container">
<!-- 搜索区域 -->
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" label-width="120px">
<!-- 第一行:4个搜索条件 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="学生姓名" prop="studentName">
@@ -30,13 +32,14 @@
</el-form-item>
</el-col>
</el-row>
<!-- 第二行:2个搜索条件 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="实习公司" prop="companyName">
<el-input v-model="queryParams.companyName" placeholder="请输入实习公司名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-col :span="6">
<el-form-item label="实习开始时间">
<el-date-picker
v-model="dateRange"
@@ -50,10 +53,10 @@
</el-form-item>
</el-col>
</el-row>
<el-row>
<!-- 搜索按钮行 -->
<el-row :gutter="10" class="mb8">
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-form-item style="margin-bottom: 0;">
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
@@ -115,82 +118,95 @@
</el-row>
</el-card>
<!-- 表格数据 -->
<el-card shadow="never" class="button-card table-card">
<template #header>
<el-row :gutter="10" class="mb8">
<el-row ref="toolbarRef" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button icon="Download" @click="handleExport" v-hasPermi="['zsjy:internship:export']">导出</el-button>
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:internship:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="internshipList" @row-dblclick="handleView">
<el-table-column label="学号" align="center" prop="studentNo" width="120" />
<el-table-column label="姓名" align="center" prop="studentName" width="100" />
<el-table-column label="性别" align="center" prop="gender" width="80">
<template #default="scope">
<dict-tag :options="sys_user_sex" :value="scope.row.gender" />
</template>
</el-table-column>
<el-table-column label="班级" align="center" prop="className" width="180" />
<el-table-column label="班主任" align="center" prop="classTeacherName" width="100" />
<el-table-column label="实习状态" align="center" prop="internshipStatus" width="100">
<template #default="scope">
<span v-if="scope.row.internshipStatus === 'null' || !scope.row.internshipStatus" class="status-tag not-started">未实习</span>
<dict-tag v-else :options="internship_status" :value="scope.row.internshipStatus" />
</template>
</el-table-column>
<el-table-column label="实习公司" align="center" prop="companyName" min-width="150" show-overflow-tooltip />
<el-table-column label="实习岗位" align="center" prop="internshipPosition" width="120" show-overflow-tooltip />
<el-table-column label="实习开始时间" align="center" prop="internshipStartDate" width="120">
<template #default="scope">
<span v-if="scope.row.internshipStartDate">{{ parseTime(scope.row.internshipStartDate, '{y}-{m}-{d}') }}</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="实习地点" align="center" prop="companyAddress" width="120" show-overflow-tooltip />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200">
<template #default="scope">
<el-tooltip content="查看详情" placement="top">
<el-button
link
type="primary"
icon="View"
size="default"
@click="handleView(scope.row)"
v-hasPermi="['zsjy:internship:query']"
></el-button>
</el-tooltip>
<el-tooltip content="新增实习信息" placement="top" v-if="!scope.row.internshipId">
<el-button
link
type="success"
icon="Plus"
size="default"
@click="handleAdd(scope.row)"
v-hasPermi="['zsjy:internship:add']"
></el-button>
</el-tooltip>
<el-tooltip content="编辑实习信息" placement="top" v-if="scope.row.internshipId">
<el-button
link
type="warning"
icon="Edit"
size="default"
@click="handleEdit(scope.row)"
v-hasPermi="['zsjy:internship:edit']"
></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
<div class="table-container">
<el-table
ref="internshipTableRef"
v-loading="loading"
border
:data="internshipList"
row-key="studentId"
class="scroll-table"
@row-dblclick="handleView"
style="width: 100%"
:scroll-x="true"
>
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="学号" align="center" prop="studentNo" width="120" />
<el-table-column label="姓名" align="center" prop="studentName" width="100" />
<el-table-column label="性别" align="center" prop="gender" width="80">
<template #default="scope">
<dict-tag :options="sys_user_sex" :value="scope.row.gender" />
</template>
</el-table-column>
<el-table-column label="班级" align="center" prop="className" width="180" />
<el-table-column label="班主任" align="center" prop="classTeacherName" width="100" />
<el-table-column label="实习状态" align="center" prop="internshipStatus" width="100">
<template #default="scope">
<span v-if="scope.row.internshipStatus === 'null' || !scope.row.internshipStatus" class="status-tag not-started">未实习</span>
<dict-tag v-else :options="internship_status" :value="scope.row.internshipStatus" />
</template>
</el-table-column>
<el-table-column label="实习公司" align="center" prop="companyName" min-width="150" show-overflow-tooltip />
<el-table-column label="实习岗位" align="center" prop="internshipPosition" width="120" show-overflow-tooltip />
<el-table-column label="实习开始时间" align="center" prop="internshipStartDate" width="120">
<template #default="scope">
<span v-if="scope.row.internshipStartDate">{{ parseTime(scope.row.internshipStartDate, '{y}-{m}-{d}') }}</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="实习地点" align="center" prop="companyAddress" width="120" show-overflow-tooltip />
<el-table-column label="操作" width="280" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="查看详情" placement="top">
<el-button
link
type="info"
icon="View"
@click="handleView(scope.row)"
v-hasPermi="['zsjy:internship:query']"
></el-button>
</el-tooltip>
<el-tooltip content="新增实习信息" placement="top" v-if="!scope.row.internshipId">
<el-button
link
type="success"
icon="Plus"
@click="handleAdd(scope.row)"
v-hasPermi="['zsjy:internship:add']"
></el-button>
</el-tooltip>
<el-tooltip content="编辑实习信息" placement="top" v-if="scope.row.internshipId">
<el-button
link
type="primary"
icon="Edit"
@click="handleEdit(scope.row)"
v-hasPermi="['zsjy:internship:edit']"
></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
</el-card>
<!-- 分页 -->
<div class="pagination-container">
<pagination v-show="total > 0" :page="queryParams.pageNum" :limit="queryParams.pageSize" :total="total" @pagination="getList" />
</div>
<!-- 学生实习详情对话框 -->
<el-dialog title="学生实习详情" v-model="dialog.visible" width="900px" append-to-body>
<div v-if="studentDetail" class="student-detail">
@@ -460,110 +476,234 @@ const cancel = () => {
})();
</script>
<style lang="scss" scoped>
<style scoped>
/* 页面容器样式 */
.page-container {
.search-btn-container {
text-align: right;
margin-top: 10px;
padding: 8px;
}
/* 表格卡片布局 */
.table-card {
margin-top: 0;
}
.table-card :deep(.el-card__body) {
padding: 0px 20px 20px 20px !important;
}
/* 表格容器 - 支持横向滚动 */
.table-container {
width: 100%;
margin-bottom: 0;
}
/* 横向滚动表格样式 */
.scroll-table {
width: 100%;
}
/* 表格横向滚动条样式优化 */
.scroll-table :deep(.el-table__body-wrapper) {
overflow-x: auto !important; /* 自动显示横向滚动条 */
overflow-y: auto;
}
.scroll-table :deep(.el-table__header-wrapper) {
overflow-x: hidden;
}
/* 强制显示表格横向滚动条 - 适用于所有浏览器 */
.scroll-table :deep(.el-table__body-wrapper) {
scrollbar-width: thin; /* Firefox */
scrollbar-color: #409eff #f0f0f0; /* Firefox - thumb track */
}
/* Webkit 浏览器的横向滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 12px !important; /* 增加滚动条高度 */
width: 12px !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f0f0f0 !important;
border-radius: 6px !important;
border: 1px solid #e4e7ed !important; /* 添加边框使其更明显 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #409eff !important; /* 使用主题色,更明显 */
border-radius: 6px !important;
border: 2px solid #f0f0f0 !important; /* 添加白色边框 */
min-height: 20px !important; /* 确保最小高度 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #337ecc !important; /* 悬停时更深的蓝色 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
background: #2b6cb0 !important; /* 点击时的颜色 */
}
/* 确保滚动条始终可见的额外设置 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-corner {
background: #f0f0f0;
}
/* 固定列样式优化 */
.scroll-table :deep(.el-table__fixed-right) {
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
}
.scroll-table :deep(.el-table__fixed-left) {
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 确保固定列在滚动时有正确的背景色 */
.scroll-table :deep(.el-table__fixed-right .el-table__cell),
.scroll-table :deep(.el-table__fixed-left .el-table__cell) {
background-color: #fff;
}
/* 表格行悬停效果在固定列上的同步 */
.scroll-table :deep(.el-table__row:hover .el-table__cell) {
background-color: #f5f7fa !important;
}
/* 分页容器样式 */
.pagination-container {
display: flex;
justify-content: flex-end;
padding: 16px 20px 0 0 !important;
background-color: #fff;
margin-top: 0px !important;
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
/* 对话框样式 */
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.dialog-form {
.el-form-item {
margin-bottom: 18px;
}
.statistics-row {
.statistics-card {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
color: white;
position: relative;
overflow: hidden;
height: 100px; /* 设置固定高度,确保所有卡片高度一致 */
.statistics-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center; /* 内容垂直居中 */
.statistics-number {
font-size: 28px;
font-weight: bold;
margin-bottom: 5px;
}
.statistics-label {
font-size: 14px;
opacity: 0.9;
}
.statistics-rate {
font-size: 12px;
opacity: 0.8;
margin-top: 2px;
}
.statistics-description {
font-size: 12px;
opacity: 0.8;
margin-top: 2px;
}
}
.statistics-icon {
font-size: 40px;
opacity: 0.8;
}
&.total-students {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
&.not-internship {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
&.internshiping {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
&.completed {
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
}
}
}
.button-card {
.mb8 {
margin-bottom: 8px;
}
}
.table-card {
.pagination-container {
display: flex;
justify-content: center;
margin-top: 20px;
}
}
.student-detail {
.mt-4 {
margin-top: 20px;
}
}
.status-tag {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
.form-tip {
font-size: 12px;
font-weight: 500;
color: #909399;
margin-top: 4px;
line-height: 1.4;
}
}
&.not-started {
background-color: #f56c6c;
color: white;
/* 描述列表样式优化 */
:deep(.el-descriptions) {
.el-descriptions__label {
font-weight: 600;
color: #606266;
width: 120px;
}
.el-descriptions__content {
color: #303133;
}
}
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
}
/* 统计卡片样式 */
.statistics-row {
.statistics-card {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
color: white;
position: relative;
overflow: hidden;
height: 100px; /* 设置固定高度,确保所有卡片高度一致 */
.statistics-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center; /* 内容垂直居中 */
.statistics-number {
font-size: 28px;
font-weight: bold;
margin-bottom: 5px;
}
.statistics-label {
font-size: 14px;
opacity: 0.9;
}
.statistics-description {
font-size: 12px;
opacity: 0.8;
margin-top: 2px;
}
}
.statistics-icon {
font-size: 40px;
opacity: 0.8;
}
&.total-students {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
&.not-internship {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
&.internshiping {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
&.completed {
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
}
}
}
/* 状态标签样式 */
.status-tag {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
&.not-started {
background-color: #f56c6c;
color: white;
}
}
/* 按钮栏样式 */
.mb8 {
margin-bottom: 8px;
}
</style>
+234 -103
View File
@@ -34,8 +34,10 @@
<el-input-number v-model="queryParams.professionalSkills" :min="1" :max="10" placeholder="请输入专业技能评分" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item>
</el-row>
<el-row :gutter="10" class="mb8">
<el-col :span="24" class="search-btn-container">
<el-form-item style="margin-bottom: 0;">
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
@@ -46,95 +48,119 @@
</div>
</transition>
<!-- 操作按钮区域 -->
<el-card shadow="never">
<!-- 表格数据 -->
<el-card shadow="never" class="button-card table-card">
<template #header>
<el-row :gutter="10" class="mb8">
<el-row ref="toolbarRef" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['zsjy:internshipmonthly:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['zsjy:internshipmonthly:edit']"
>修改</el-button
>
<el-button
type="success"
plain
icon="Edit"
:disabled="single"
@click="handleUpdate()"
v-hasPermi="['zsjy:internshipmonthly:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['zsjy:internshipmonthly:remove']"
>删除</el-button
>
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete()"
v-hasPermi="['zsjy:internshipmonthly:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:internshipmonthly:export']">导出</el-button>
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['zsjy:internshipmonthly:export']"
>导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<!-- 数据表格 -->
<el-table v-loading="loading" :data="monthlyList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="总结月份" align="center" prop="summaryMonth" width="120" />
<el-table-column label="工作内容" align="center" prop="workContent" :show-overflow-tooltip="true" min-width="200" />
<el-table-column label="综合评分" align="center" width="120">
<template #default="scope">
<el-tooltip content="专业/沟通/团队/学习/态度" placement="top">
<el-tag :type="getScoreTagType(getAverageScore(scope.row))"> {{ getAverageScore(scope.row).toFixed(1) }}分 </el-tag>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="字数统计" align="center" width="100">
<template #default="scope"> {{ scope.row.wordCount || 0 }}字 </template>
</el-table-column>
<el-table-column label="审核状态" align="center" prop="reviewStatus" width="100">
<template #default="scope">
<dict-tag :options="internship_monthly_status" :value="scope.row.reviewStatus" />
</template>
</el-table-column>
<el-table-column label="是否逾期" align="center" prop="isOverdue" width="100">
<template #default="scope">
<el-tag v-if="scope.row.isOverdue === 1" type="danger">逾期</el-tag>
<el-tag v-else type="success">正常</el-tag>
</template>
</el-table-column>
<el-table-column label="提交时间" align="center" prop="submitTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.submitTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200" fixed="right">
<template #default="scope">
<!-- 查看详情 -->
<el-tooltip content="查看详情" placement="top">
<el-button link type="primary" icon="View" @click="handleDetail(scope.row)" v-hasPermi="['zsjy:internshipmonthly:list']"></el-button>
</el-tooltip>
<div class="table-container">
<el-table
ref="monthlyTableRef"
v-loading="loading"
border
:data="monthlyList"
row-key="id"
class="scroll-table"
@selection-change="handleSelectionChange"
style="width: 100%"
:scroll-x="true"
>
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="总结月份" prop="summaryMonth" width="120" align="center" />
<el-table-column label="工作内容" prop="workContent" min-width="200" show-overflow-tooltip />
<el-table-column label="综合评分" width="120" align="center">
<template #default="scope">
<el-tooltip content="专业/沟通/团队/学习/态度" placement="top">
<el-tag :type="getScoreTagType(getAverageScore(scope.row))"> {{ getAverageScore(scope.row).toFixed(1) }}分 </el-tag>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="字数统计" width="100" align="center">
<template #default="scope"> {{ scope.row.wordCount || 0 }}字 </template>
</el-table-column>
<el-table-column label="审核状态" prop="reviewStatus" width="100" align="center">
<template #default="scope">
<dict-tag :options="internship_monthly_status" :value="scope.row.reviewStatus" />
</template>
</el-table-column>
<el-table-column label="是否逾期" prop="isOverdue" width="100" align="center">
<template #default="scope">
<el-tag v-if="scope.row.isOverdue === 1" type="danger">逾期</el-tag>
<el-tag v-else type="success">正常</el-tag>
</template>
</el-table-column>
<el-table-column label="提交时间" prop="submitTime" width="180" align="center">
<template #default="scope">
<span>{{ parseTime(scope.row.submitTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createTime" width="180" align="center">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="280" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="查看" placement="top">
<el-button link type="info" icon="View" @click="handleDetail(scope.row)" v-hasPermi="['zsjy:internshipmonthly:list']"></el-button>
</el-tooltip>
<el-tooltip content="修改" placement="top" v-if="scope.row.reviewStatus === 3">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['zsjy:internshipmonthly:edit']"></el-button>
</el-tooltip>
<el-tooltip content="提交" placement="top" v-if="scope.row.reviewStatus === 3">
<el-button link type="success" icon="Check" @click="handleSubmit(scope.row)" v-hasPermi="['zsjy:internshipmonthly:submit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top" v-if="scope.row.reviewStatus === 3">
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['zsjy:internshipmonthly:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
<!-- 编辑(只有草稿状态才能编辑) -->
<el-tooltip content="编辑" placement="top" v-if="scope.row.reviewStatus === 3">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['zsjy:internshipmonthly:edit']"></el-button>
</el-tooltip>
<!-- 提交(只有草稿状态才能提交) -->
<el-tooltip content="提交" placement="top" v-if="scope.row.reviewStatus === 3">
<el-button link type="success" icon="Check" @click="handleSubmit(scope.row)" v-hasPermi="['zsjy:internshipmonthly:submit']"></el-button>
</el-tooltip>
<!-- 删除(只有草稿状态才能删除) -->
<el-tooltip content="删除" placement="top" v-if="scope.row.reviewStatus === 3">
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['zsjy:internshipmonthly:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 分页 -->
<div class="pagination-container">
<pagination v-show="total > 0" :page="queryParams.pageNum" :limit="queryParams.pageSize" :total="total" @pagination="getList" />
</div>
<!-- 添加或修改月度总结对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="1000px" append-to-body>
<MonthlyForm ref="monthlyFormRef" @success="getList" />
@@ -148,6 +174,9 @@
</template>
<script setup name="InternshipMonthly" lang="ts">
import { reactive, ref, toRefs, getCurrentInstance, nextTick, onMounted, type ComponentInternalInstance } from 'vue';
// import type { ElFormInstance, ComponentRef, DialogOption, PageData } from '@/types';
// import { parseTime } from '@/utils/ruoyi';
import {
listInternshipMonthly,
getInternshipMonthly,
@@ -169,12 +198,12 @@ const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const dialog = reactive<DialogOption>({
const dialog = reactive({
visible: false,
title: ''
});
const detailDialog = reactive<DialogOption>({
const detailDialog = reactive({
visible: false,
title: ''
});
@@ -186,7 +215,7 @@ const initFormData: any = {
reviewStatus: undefined,
professionalSkills: undefined
};
const data = reactive<PageData<any, any>>({
const data = reactive({
form: {},
queryParams: { ...initFormData },
rules: {}
@@ -194,9 +223,9 @@ const data = reactive<PageData<any, any>>({
const { queryParams, form, rules } = toRefs(data);
const queryFormRef = ref<ElFormInstance>();
const monthlyFormRef = ref<ComponentRef<typeof MonthlyForm>>();
const monthlyDetailRef = ref<ComponentRef<typeof MonthlyDetail>>();
const queryFormRef = ref();
const monthlyFormRef = ref();
const monthlyDetailRef = ref();
/** 查询月度总结列表 */
const getList = async () => {
@@ -307,44 +336,101 @@ const getScoreTagType = (score: number) => {
return 'danger';
};
/** 时间格式化函数 */
const parseTime = (time: any, pattern: string) => {
if (!time) return '';
const date = new Date(time);
const format = {
y: date.getFullYear(),
m: String(date.getMonth() + 1).padStart(2, '0'),
d: String(date.getDate()).padStart(2, '0'),
h: String(date.getHours()).padStart(2, '0'),
i: String(date.getMinutes()).padStart(2, '0'),
s: String(date.getSeconds()).padStart(2, '0')
};
return pattern.replace(/{([ymdhis])+}/g, (result, key) => {
return format[key as keyof typeof format] || result;
});
};
onMounted(() => {
getList();
});
</script>
<style scoped>
/* 操作按钮样式优化 - 参考后勤管理页面 */
:deep(.el-table__fixed-right) {
/* 页面容器样式 */
.page-container {
padding: 20px;
background-color: #f4f4f5;
min-height: calc(100vh - 84px);
}
/* 表格容器样式 */
.table-container {
overflow-x: auto;
position: relative;
}
/* 表格滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper) {
scrollbar-width: thin; /* Firefox */
scrollbar-color: #409eff #f0f0f0; /* Firefox - thumb track */
}
/* Webkit 浏览器的横向滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 12px !important; /* 增加滚动条高度 */
width: 12px !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f0f0f0 !important;
border-radius: 6px !important;
border: 1px solid #e4e7ed !important; /* 添加边框使其更明显 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #409eff !important; /* 使用主题色,更明显 */
border-radius: 6px !important;
border: 2px solid #f0f0f0 !important; /* 添加白色边框 */
min-height: 20px !important; /* 确保最小高度 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #337ecc !important; /* 悬停时更深的蓝色 */
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
background: #2b6cb0 !important; /* 点击时的颜色 */
}
/* 确保滚动条始终可见的额外设置 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-corner {
background: #f0f0f0;
}
/* 固定列样式优化 */
.scroll-table :deep(.el-table__fixed-right) {
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
}
:deep(.el-table__fixed-right .el-table__cell) {
.scroll-table :deep(.el-table__fixed-left) {
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 确保固定列在滚动时有正确的背景色 */
.scroll-table :deep(.el-table__fixed-right .el-table__cell),
.scroll-table :deep(.el-table__fixed-left .el-table__cell) {
background-color: #fff;
}
:deep(.el-table__row:hover .el-table__cell) {
/* 表格行悬停效果在固定列上的同步 */
.scroll-table :deep(.el-table__row:hover .el-table__cell) {
background-color: #f5f7fa !important;
}
/* 操作列按钮间距优化 */
.small-padding .el-button + .el-button {
margin-left: 8px;
}
/* 确保操作按钮在固定列中居中对齐 */
.small-padding {
text-align: center;
}
.small-padding .el-tooltip {
display: inline-block;
margin-right: 8px;
}
.small-padding .el-tooltip:last-child {
margin-right: 0;
}
/* 分页容器样式 */
.pagination-container {
display: flex;
@@ -353,4 +439,49 @@ onMounted(() => {
background-color: #fff;
margin-top: 0px !important;
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.search-btn-container .el-form-item {
margin-bottom: 0 !important;
}
/* 对话框样式 */
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.dialog-form .el-form-item {
margin-bottom: 18px;
}
.dialog-form .form-tip {
font-size: 12px;
color: #909399;
margin-top: 4px;
line-height: 1.4;
}
/* 描述列表样式优化 */
:deep(.el-descriptions .el-descriptions__label) {
font-weight: 600;
color: #606266;
width: 120px;
}
:deep(.el-descriptions .el-descriptions__content) {
color: #303133;
}
</style>
+218 -302
View File
@@ -1,91 +1,25 @@
<template>
<div class="app-container">
<!-- 统计卡片 -->
<el-row :gutter="16" class="stats-row">
<el-col :xl="6" :lg="12" :md="12" :sm="12" :xs="24">
<div class="stat-card total">
<div class="stat-icon">
<el-icon><User /></el-icon>
</div>
<div class="stat-content">
<div class="stat-number">{{ statistics.total }}</div>
<div class="stat-label">走访总数</div>
</div>
</div>
</el-col>
<el-col :xl="6" :lg="12" :md="12" :sm="12" :xs="24">
<div class="stat-card month">
<div class="stat-icon">
<el-icon><Calendar /></el-icon>
</div>
<div class="stat-content">
<div class="stat-number">{{ statistics.monthCount }}</div>
<div class="stat-label">本月走访</div>
</div>
</div>
</el-col>
<el-col :xl="6" :lg="12" :md="12" :sm="12" :xs="24">
<div class="stat-card completed">
<div class="stat-icon">
<el-icon><CircleCheck /></el-icon>
</div>
<div class="stat-content">
<div class="stat-number">{{ statistics.completedCount }}</div>
<div class="stat-label">已完成</div>
</div>
</div>
</el-col>
<el-col :xl="6" :lg="12" :md="12" :sm="12" :xs="24">
<div class="stat-card pending">
<div class="stat-icon">
<el-icon><Clock /></el-icon>
</div>
<div class="stat-content">
<div class="stat-number">{{ statistics.pendingCount }}</div>
<div class="stat-label">待走访</div>
</div>
</div>
</el-col>
</el-row>
<div class="page-container">
<!-- 搜索区域 -->
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<!-- 搜索条件 -->
<!-- 第一行:4个搜索条件 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="学生信息" prop="studentName">
<el-input
v-model="queryParams.studentName"
placeholder="请输入学生姓名或学号"
clearable
style="width: 100%"
@keyup.enter="handleQuery"
/>
<el-input v-model="queryParams.studentName" placeholder="请输入学生姓名或学号" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="实习单位" prop="companyName">
<el-input
v-model="queryParams.companyName"
placeholder="请输入实习单位名称"
clearable
style="width: 100%"
@keyup.enter="handleQuery"
/>
<el-input v-model="queryParams.companyName" placeholder="请输入实习单位名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="走访人" prop="visitorName">
<el-input
v-model="queryParams.visitorName"
placeholder="请输入走访人姓名"
clearable
style="width: 100%"
@keyup.enter="handleQuery"
/>
<el-input v-model="queryParams.visitorName" placeholder="请输入走访人姓名" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
@@ -97,30 +31,31 @@
</el-col>
</el-row>
<!-- 第二行:日期范围 + 操作按钮 -->
<!-- 第二行:1个搜索条件 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="走访日期" prop="visitDateRange">
<el-form-item label="走访日期">
<el-date-picker
v-model="queryParams.visitDateRange"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="至"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
@change="handleQuery"
style="width: 100%"
/>
></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<div class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-row>
<el-button type="info" plain icon="Search" @click="showSearch = !showSearch"> {{ showSearch ? '隐藏' : '显示' }}搜索 </el-button>
</el-form-item>
</div>
<!-- 第三行:搜索按钮 -->
<el-row :gutter="10" class="mb8">
<el-col :span="24" class="search-btn-container">
<el-form-item style="margin-bottom: 0;">
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
@@ -128,37 +63,59 @@
</div>
</transition>
<!-- 操作按钮栏 -->
<el-card shadow="hover" class="button-card">
<!-- 表格数据 -->
<el-card shadow="never" class="button-card table-card">
<template #header>
<div class="card-header-vertical">
<div class="toolbar-title">班主任走访列表</div>
<div class="button-group">
<el-row ref="toolbarRef" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['zsjy:internshipVisit:add']">新增走访</el-button>
<el-button type="success" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:internshipVisit:export']">导出</el-button>
<el-button type="info" plain icon="Search" @click="showSearch = !showSearch"> {{ showSearch ? '隐藏' : '显示' }}搜索 </el-button>
<el-tooltip content="刷新" placement="top">
<el-button icon="Refresh" circle @click="getList" />
</el-tooltip>
</div>
</div>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="Edit"
:disabled="single"
@click="handleBatchUpdate"
v-hasPermi="['zsjy:internshipVisit:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleBatchDelete"
v-hasPermi="['zsjy:internshipVisit:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['zsjy:internshipVisit:export']"
>导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
</el-card>
<!-- 数据表格 -->
<el-card shadow="hover" class="table-card">
<div class="table-container">
<el-table
ref="visitTableRef"
v-loading="loading"
border
:data="visitList"
row-key="visitId"
border
stripe
highlight-current-row
class="scroll-table"
@selection-change="handleSelectionChange"
style="width: 100%"
:scroll-x="true"
>
<el-table-column type="selection" width="50" align="center" />
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="学生信息" min-width="150" align="left">
<template #default="scope">
<div class="student-info">
@@ -168,53 +125,45 @@
</div>
</template>
</el-table-column>
<el-table-column label="实习单位" prop="companyName" min-width="150" align="left" show-overflow-tooltip />
<el-table-column label="走访人" prop="visitorName" min-width="100" align="center" />
<el-table-column label="走访日期" prop="visitDate" min-width="120" align="center">
<template #default="scope">
{{ parseTime(scope.row.visitDate, '{y}-{m}-{d}') }}
</template>
</el-table-column>
<el-table-column label="走访状态" prop="status" min-width="100" align="center">
<template #default="scope">
<dict-tag :options="visitStatusOptions" :value="scope.row.status || ''" />
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createTime" min-width="160" align="center">
<template #default="scope">
{{ parseTime(scope.row.createTime) }}
</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width">
<el-table-column label="操作" width="180" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<div class="action-buttons">
<el-tooltip content="查看详情" placement="top">
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['zsjy:internshipVisit:query']" />
</el-tooltip>
<el-tooltip content="编辑" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['zsjy:internshipVisit:edit']" />
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['zsjy:internshipVisit:remove']" />
</el-tooltip>
</div>
<el-tooltip content="查看" placement="top">
<el-button link type="info" icon="View" @click="handleView(scope.row)" v-hasPermi="['zsjy:internshipVisit:query']"></el-button>
</el-tooltip>
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['zsjy:internshipVisit:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['zsjy:internshipVisit:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页组件 -->
<div class="pagination-container">
<pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @pagination="getList" />
</div>
</el-card>
<!-- 分页 -->
<div class="pagination-container">
<pagination v-show="total > 0" :page="queryParams.pageNum" :limit="queryParams.pageSize" :total="total" @pagination="getList" />
</div>
<!-- 详情对话框 -->
<el-dialog title="走访详情" v-model="viewDialogVisible" width="800px" append-to-body>
<div v-if="currentRecord" class="detail-container">
@@ -386,13 +335,7 @@ const viewDialogVisible = ref(false);
const formDialogVisible = ref(false);
const currentRecord = ref(null);
// 统计数据
const statistics = ref({
total: 0,
monthCount: 0,
completedCount: 0,
pendingCount: 0
});
// 字典数据
const visitStatusOptions = ref([]);
@@ -495,8 +438,6 @@ function getList() {
}
loading.value = false;
// 计算统计数据
calculateStatistics();
})
.catch((error) => {
console.error('获取走访记录列表失败:', error);
@@ -508,18 +449,7 @@ function getList() {
});
}
/** 计算统计数据 */
function calculateStatistics() {
const now = new Date();
const currentMonth = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0');
statistics.value = {
total: visitList.value.length,
monthCount: visitList.value.filter((item) => item.visitDate && item.visitDate.startsWith(currentMonth)).length,
completedCount: visitList.value.filter((item) => item.status === '2').length, // 2-已审核
pendingCount: visitList.value.filter((item) => item.status === '0').length // 0-草稿
};
}
/** 搜索按钮操作 */
function handleQuery() {
@@ -605,6 +535,34 @@ function submitForm() {
});
}
/** 批量修改按钮操作 */
function handleBatchUpdate() {
if (ids.value.length === 1) {
// 单个修改,直接调用修改方法
const visitId = ids.value[0];
const row = visitList.value.find(item => item.visitId === visitId);
if (row) {
handleUpdate(row);
}
} else {
ElMessage.warning('请选择一条记录进行修改');
}
}
/** 批量删除按钮操作 */
function handleBatchDelete() {
const visitIds = ids.value;
ElMessageBox.confirm('是否确认删除选中的' + visitIds.length + '条走访记录?')
.then(function () {
return delInternshipVisit(visitIds);
})
.then(() => {
getList();
ElMessage.success('删除成功');
})
.catch(() => {});
}
/** 删除按钮操作 */
function handleDelete(row) {
const visitIds = row.visitId || ids.value;
@@ -819,177 +777,124 @@ onMounted(() => {
</script>
<style scoped>
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
align-items: center;
height: 100%;
/* 页面容器样式 */
.page-container {
padding: 8px;
}
/* 按钮卡片样式 */
.button-card {
margin-bottom: 16px;
/* 表格卡片布局 */
.table-card {
margin-top: 0;
}
.form-item-tip {
font-size: 12px;
color: #909399;
line-height: 1.4;
margin-top: 4px;
.table-card :deep(.el-card__body) {
padding: 0px 20px 20px 20px !important;
}
.card-header-vertical {
display: flex;
justify-content: space-between;
align-items: center;
/* 表格容器 - 支持横向滚动 */
.table-container {
width: 100%;
margin-bottom: 0;
}
/* 横向滚动表格样式 */
.scroll-table {
width: 100%;
}
.toolbar-title {
font-size: 16px;
font-weight: 600;
color: #303133;
/* 表格横向滚动条样式优化 */
.scroll-table :deep(.el-table__body-wrapper) {
overflow-x: auto !important; /* 自动显示横向滚动条 */
overflow-y: auto;
}
.button-group {
display: flex;
gap: 8px;
align-items: center;
.scroll-table :deep(.el-table__header-wrapper) {
overflow-x: hidden;
}
.table-card {
margin-bottom: 16px;
/* 强制显示表格横向滚动条 - 适用于所有浏览器 */
.scroll-table :deep(.el-table__body-wrapper) {
scrollbar-width: thin; /* Firefox */
scrollbar-color: #409eff #f0f0f0; /* Firefox - thumb track */
}
/* 统计卡片样式 */
.stats-row {
margin-bottom: 20px;
/* Webkit 浏览器的横向滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 12px !important; /* 增加滚动条高度 */
width: 12px !important;
}
.stat-card {
background: white;
border-radius: 8px;
padding: 20px;
display: flex;
align-items: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
margin-bottom: 16px;
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f0f0f0 !important;
border-radius: 6px !important;
border: 1px solid #e4e7ed !important; /* 添加边框使其更明显 */
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #409eff !important; /* 使用主题色,更明显 */
border-radius: 6px !important;
border: 2px solid #f0f0f0 !important; /* 添加白色边框 */
min-height: 20px !important; /* 确保最小高度 */
}
.stat-icon {
width: 60px;
height: 60px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16px;
font-size: 24px;
color: white;
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #337ecc !important; /* 悬停时更深的蓝色 */
}
.stat-card.total .stat-icon {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
background: #2b6cb0 !important; /* 点击时的颜色 */
}
.stat-card.month .stat-icon {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
/* 确保滚动条始终可见的额外设置 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-corner {
background: #f0f0f0;
}
.stat-card.completed .stat-icon {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
/* 固定列样式优化 */
.scroll-table :deep(.el-table__fixed-right) {
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
}
.stat-card.pending .stat-icon {
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
.scroll-table :deep(.el-table__fixed-left) {
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
.stat-content {
flex: 1;
/* 确保固定列在滚动时有正确的背景色 */
.scroll-table :deep(.el-table__fixed-right .el-table__cell),
.scroll-table :deep(.el-table__fixed-left .el-table__cell) {
background-color: #fff;
}
.stat-number {
font-size: 28px;
font-weight: bold;
color: #303133;
margin-bottom: 4px;
}
.stat-label {
font-size: 14px;
color: #909399;
}
/* 主卡片样式 */
.main-card {
margin-top: 20px;
}
.main-card .el-card__body {
padding: 20px;
}
/* 搜索容器样式 */
.search-container {
background: #f8f9fa;
padding: 16px;
border-radius: 6px;
margin-bottom: 16px;
}
/* 工具栏样式 */
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding: 0 4px;
}
.toolbar-left {
display: flex;
gap: 12px;
}
.toolbar-right {
display: flex;
gap: 8px;
}
/* 表格容器样式 */
.table-container {
border-radius: 6px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.el-table {
border-radius: 6px;
}
.el-table th {
background-color: #f5f7fa;
color: #606266;
font-weight: 600;
}
.el-table .el-table__row:hover {
background-color: #f5f7fa;
/* 表格行悬停效果在固定列上的同步 */
.scroll-table :deep(.el-table__row:hover .el-table__cell) {
background-color: #f5f7fa !important;
}
/* 分页容器样式 */
.pagination-container {
display: flex;
justify-content: center;
margin-top: 20px;
padding: 16px 0;
justify-content: flex-end;
padding: 16px 20px 0 0 !important;
background-color: #fff;
margin-top: 0px !important;
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
}
/* 学生信息样式 */
@@ -1008,12 +913,12 @@ onMounted(() => {
}
}
/* 操作按钮样式 */
.action-buttons {
display: flex;
gap: 8px;
justify-content: center;
align-items: center;
/* 表单提示样式 */
.form-item-tip {
font-size: 12px;
color: #909399;
line-height: 1.4;
margin-top: 4px;
}
/* 对话框样式 */
@@ -1023,6 +928,32 @@ onMounted(() => {
gap: 10px;
}
.dialog-form {
.el-form-item {
margin-bottom: 18px;
}
.form-tip {
font-size: 12px;
color: #909399;
margin-top: 4px;
line-height: 1.4;
}
}
/* 描述列表样式优化 */
:deep(.el-descriptions) {
.el-descriptions__label {
font-weight: 600;
color: #606266;
width: 120px;
}
.el-descriptions__content {
color: #303133;
}
}
/* 详情容器样式 */
.detail-container {
padding: 8px 0;
@@ -1030,21 +961,6 @@ onMounted(() => {
/* 响应式设计 */
@media (max-width: 768px) {
.stat-card {
margin-bottom: 10px;
}
.toolbar {
flex-direction: column;
gap: 12px;
align-items: stretch;
}
.toolbar-left,
.toolbar-right {
justify-content: center;
}
.action-buttons {
flex-direction: column;
gap: 4px;
+233 -229
View File
@@ -1,15 +1,13 @@
<template>
<div class="p-2 page-container">
<!-- 搜索区域 -->
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<div ref="searchRef" v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<!-- 搜索条件 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="所属企业" prop="enterpriseId">
<el-select v-model="queryParams.enterpriseId" placeholder="请选择所属企业" clearable style="width: 100%" @change="handleQuery">
<el-select v-model="queryParams.enterpriseId" placeholder="请选择所属企业" clearable>
<el-option v-for="item in enterpriseOptions" :key="item.id" :label="item.enterpriseName" :value="item.id" />
</el-select>
</el-form-item>
@@ -26,14 +24,46 @@
</el-col>
<el-col :span="6">
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 100%">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 操作按钮行 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="是否永久" prop="isPermanent">
<el-select v-model="queryParams.isPermanent" placeholder="请选择是否永久" clearable>
<el-option label="是" :value="1" />
<el-option label="否" :value="0" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="开始时间" prop="startDate">
<el-date-picker
clearable
v-model="queryParams.startDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择开始时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="结束时间" prop="endDate">
<el-date-picker
clearable
v-model="queryParams.endDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择结束时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
@@ -47,39 +77,42 @@
</div>
</transition>
<!-- 操作按钮栏 -->
<el-card shadow="hover" class="button-card">
<el-card shadow="never" class="button-card table-card">
<template #header>
<div class="card-header-vertical">
<div class="toolbar-title">招聘岗位列表</div>
<div class="button-group">
<el-row ref="toolbarRef" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['zsjy:position:add']">新增</el-button>
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate" v-hasPermi="['zsjy:position:edit']">修改</el-button>
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['zsjy:position:remove']"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['zsjy:position:edit']">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['zsjy:position:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:position:export']">导出</el-button>
<el-button type="info" plain icon="Fold" @click="toggleSearch">
<span v-if="showSearch">隐藏</span>
<span v-else>展开</span>
搜索
</el-button>
</div>
</div>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
</el-card>
<!-- 表格区域 -->
<el-card shadow="hover" class="table-card">
<div class="table-container">
<el-table v-loading="loading" :data="jobPositionList" @selection-change="handleSelectionChange" class="scroll-table" stripe border>
<el-table
v-loading="loading"
border
:data="jobPositionList"
@selection-change="handleSelectionChange"
style="width: 100%"
:scroll-x="true"
class="scroll-table"
>
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="企业名称" align="center" prop="enterpriseName" min-width="150" show-overflow-tooltip />
<el-table-column label="所属企业" align="center" prop="enterpriseName" min-width="150" show-overflow-tooltip />
<el-table-column label="岗位名称" align="center" prop="positionName" min-width="150" show-overflow-tooltip />
<el-table-column label="工作地址" align="center" prop="workAddress" min-width="180" show-overflow-tooltip />
<el-table-column label="工作时间" align="center" prop="workTime" min-width="120" show-overflow-tooltip />
<el-table-column label="薪资" align="center" prop="salary" min-width="120" />
<el-table-column label="有效期" align="center" min-width="180">
<el-table-column label="工作地址" align="center" prop="workAddress" min-width="200" show-overflow-tooltip />
<el-table-column label="工作时间" align="center" prop="workTime" width="180" show-overflow-tooltip />
<el-table-column label="薪资" align="center" prop="salary" width="150" show-overflow-tooltip />
<el-table-column label="有效期" align="center" width="200">
<template #default="scope">
<span v-if="scope.row.isPermanent === 1">永久有效</span>
<span v-else>{{ scope.row.startDate }} ~ {{ scope.row.endDate }}</span>
@@ -91,54 +124,28 @@
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="160" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
<el-table-column label="操作" align="center" width="280" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="查看" placement="top">
<el-button
link
type="primary"
icon="View"
size="default"
style="font-size: 16px; margin-right: 8px; color: blue"
@click="handleDetail(scope.row)"
v-hasPermi="['zsjy:position:query']"
></el-button>
<el-button link type="info" icon="View" @click="handleDetail(scope.row)" v-hasPermi="['zsjy:position:query']"></el-button>
</el-tooltip>
<el-tooltip content="修改" placement="top">
<el-button
link
type="primary"
icon="Edit"
size="default"
style="font-size: 16px; margin-right: 8px"
@click="handleUpdate(scope.row)"
v-hasPermi="['zsjy:position:edit']"
></el-button>
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['zsjy:position:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button
link
type="danger"
icon="Delete"
size="default"
style="font-size: 16px"
@click="handleDelete(scope.row)"
v-hasPermi="['zsjy:position:remove']"
></el-button>
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['zsjy:position:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination-container">
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</div>
</el-card>
<!-- 新增/修改对话框 -->
<el-dialog :title="title" v-model="open" width="800px" append-to-body>
<!-- 添加或修改招聘岗位对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body>
<el-form ref="jobPositionFormRef" :model="form" :rules="rules" label-width="100px">
<el-row :gutter="20">
<el-col :span="12">
@@ -217,7 +224,7 @@
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</template>
@@ -253,58 +260,30 @@
</template>
<script setup name="JobPosition" lang="ts">
import { getCurrentInstance, ComponentInternalInstance, ref, reactive, toRefs, onMounted } from 'vue';
import { FormInstance } from 'element-plus';
import {
listJobPosition,
getJobPosition,
delJobPosition,
addJobPosition,
updateJobPosition,
exportJobPosition,
listEnterpriseOptions
} from '@/api/zsjy/jobPosition';
import { listJobPosition, getJobPosition, delJobPosition, addJobPosition, updateJobPosition, exportJobPosition, listEnterpriseOptions } from '@/api/zsjy/jobPosition';
import { JobPositionVO, JobPositionForm, JobPositionQuery } from '@/api/zsjy/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// 字典数据
const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
// 表格数据
const jobPositionList = ref<JobPositionVO[]>([]);
const total = ref(0);
const loading = ref(false);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
// 表单相关
const open = ref(false);
const title = ref('');
const jobPositionFormRef = ref<FormInstance>();
const queryFormRef = ref<FormInstance>();
const queryFormRef = ref<ElFormInstance>();
const jobPositionFormRef = ref<ElFormInstance>();
// 企业选项
const enterpriseOptions = ref<any[]>([]);
// 日期范围
const dateRange = ref<[string, string]>([]);
// 查询参数
const queryParams = ref<JobPositionQuery>({
pageNum: 1,
pageSize: 10,
enterpriseId: undefined,
positionName: undefined,
workAddress: undefined,
status: undefined,
isPermanent: undefined
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
// 表单参数
const form = ref<JobPositionForm>({
const initFormData: JobPositionForm = {
id: undefined,
enterpriseId: '',
positionName: '',
@@ -317,17 +296,37 @@ const form = ref<JobPositionForm>({
isPermanent: 0,
status: '0',
remark: ''
};
const data = reactive<PageData<JobPositionForm, JobPositionQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
enterpriseId: undefined,
positionName: undefined,
workAddress: undefined,
status: undefined,
isPermanent: undefined,
params: {}
},
rules: {
enterpriseId: [{ required: true, message: '请选择所属企业', trigger: 'change' }],
positionName: [{ required: true, message: '岗位名称不能为空', trigger: 'blur' }],
workAddress: [{ required: true, message: '工作地址不能为空', trigger: 'blur' }],
workTime: [{ required: true, message: '工作时间不能为空', trigger: 'blur' }],
salary: [{ required: true, message: '薪资不能为空', trigger: 'blur' }],
status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
}
});
// 表单校验规则
const rules = {
enterpriseId: [{ required: true, message: '请选择所属企业', trigger: 'change' }],
positionName: [{ required: true, message: '岗位名称不能为空', trigger: 'blur' }],
workAddress: [{ required: true, message: '工作地址不能为空', trigger: 'blur' }],
workTime: [{ required: true, message: '工作时间不能为空', trigger: 'blur' }],
salary: [{ required: true, message: '薪资不能为空', trigger: 'blur' }],
status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
};
const { queryParams, form, rules } = toRefs(data);
// 企业选项
const enterpriseOptions = ref<any[]>([]);
// 日期范围
const dateRange = ref<[string, string] | []>([]);
// 详情数据
const detailOpen = ref(false);
@@ -360,11 +359,6 @@ const resetQuery = () => {
handleQuery();
};
/** 切换搜索显示 */
const toggleSearch = () => {
showSearch.value = !showSearch.value;
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: JobPositionVO[]) => {
ids.value = selection.map((item) => item.id);
@@ -374,22 +368,22 @@ const handleSelectionChange = (selection: JobPositionVO[]) => {
/** 新增按钮操作 */
const handleAdd = () => {
resetForm();
open.value = true;
title.value = '添加招聘岗位';
reset();
dialog.visible = true;
dialog.title = '添加招聘岗位';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: JobPositionVO) => {
resetForm();
reset();
const id = row?.id || ids.value[0];
const res = await getJobPosition(id);
Object.assign(form.value, res.data);
if (!form.value.isPermanent) {
dateRange.value = [form.value.startDate, form.value.endDate];
}
open.value = true;
title.value = '修改招聘岗位';
dialog.visible = true;
dialog.title = '修改招聘岗位';
};
/** 详情按钮操作 */
@@ -412,6 +406,8 @@ const handlePermanentChange = (val: number) => {
const submitForm = () => {
jobPositionFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
// 处理日期范围
if (form.value.isPermanent === 0 && dateRange.value && dateRange.value.length === 2) {
form.value.startDate = dateRange.value[0];
@@ -419,14 +415,13 @@ const submitForm = () => {
}
if (form.value.id) {
await updateJobPosition(form.value);
proxy?.$modal.msgSuccess('修改成功');
await updateJobPosition(form.value).finally(() => (buttonLoading.value = false));
} else {
await addJobPosition(form.value);
proxy?.$modal.msgSuccess('新增成功');
await addJobPosition(form.value).finally(() => (buttonLoading.value = false));
}
open.value = false;
getList();
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
}
});
};
@@ -434,27 +429,31 @@ const submitForm = () => {
/** 删除按钮操作 */
const handleDelete = async (row?: JobPositionVO) => {
const jobIds = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除选中的招聘岗位?');
await proxy?.$modal.confirm('是否确认删除选中的招聘岗位?').finally(() => (loading.value = false));
await delJobPosition(jobIds);
proxy?.$modal.msgSuccess('删除成功');
getList();
await getList();
};
/** 导出按钮操作 */
const handleExport = async () => {
await proxy?.$modal.confirm('是否确认导出所有招聘岗位数据?');
await exportJobPosition(queryParams.value);
proxy?.$modal.msgSuccess('导出成功');
const handleExport = () => {
proxy?.download(
'zsjy/position/export',
{
...queryParams.value
},
`jobPosition_${new Date().getTime()}.xlsx`
);
};
/** 取消按钮 */
const cancel = () => {
open.value = false;
resetForm();
reset();
dialog.visible = false;
};
/** 表单重置 */
const resetForm = () => {
const reset = () => {
form.value = {
id: undefined,
enterpriseId: '',
@@ -480,102 +479,107 @@ onMounted(() => {
});
</script>
<style scoped lang="scss">
// 继承后勤管理的样式
<style scoped>
.page-container {
.search-btn-container {
text-align: right;
}
.button-card {
margin-bottom: 20px;
padding: 20px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.card-header-vertical {
display: flex;
justify-content: space-between;
align-items: center;
}
.toolbar-title {
font-size: 18px;
font-weight: bold;
color: #303133;
}
.button-group {
display: flex;
align-items: center;
gap: 10px;
}
.table-card {
margin-bottom: 20px;
padding: 20px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.table-container {
overflow-x: auto;
}
.scroll-table {
width: 100%;
}
.pagination-container {
text-align: right;
padding-top: 20px;
}
.dialog-footer {
text-align: right;
}
// 表格适应性样式
:deep(.el-table) {
.el-table__cell {
padding: 8px 0;
}
}
// 响应式表格
:deep(.el-table__body-wrapper) {
overflow-x: auto;
}
// 表格列宽自适应
.el-table {
width: 100%;
table-layout: auto;
}
background-color: #f5f5f5;
min-height: 100vh;
}
// 操作按钮样式
.action-buttons {
display: flex;
gap: 8px;
justify-content: center;
align-items: center;
white-space: nowrap;
.search-btn-container {
text-align: center;
margin-top: 10px;
}
// 描述列表样式优化
:deep(.el-descriptions) {
.el-descriptions__label {
font-weight: 600;
color: #606266;
width: 120px;
}
.button-card {
margin-top: 10px;
}
.el-descriptions__content {
color: #303133;
}
.table-card {
background-color: #fff;
}
.table-container {
overflow-x: auto;
}
.scroll-table {
min-width: 100%;
}
.pagination-container {
margin-top: 20px;
text-align: right;
}
.dialog-footer {
text-align: center;
}
:deep(.el-card__header) {
padding: 15px 20px;
border-bottom: 1px solid #ebeef5;
background-color: #fafafa;
}
:deep(.el-table) {
border-radius: 4px;
}
:deep(.el-table th) {
background-color: #fafafa;
color: #606266;
font-weight: 500;
}
:deep(.el-button--plain) {
border-color: #dcdfe6;
color: #606266;
}
:deep(.el-button--plain:hover) {
border-color: #409eff;
color: #409eff;
}
:deep(.el-button--primary.is-plain) {
border-color: #409eff;
color: #409eff;
}
:deep(.el-button--success.is-plain) {
border-color: #67c23a;
color: #67c23a;
}
:deep(.el-button--danger.is-plain) {
border-color: #f56c6c;
color: #f56c6c;
}
:deep(.el-button--warning.is-plain) {
border-color: #e6a23c;
color: #e6a23c;
}
:deep(.el-form-item__label) {
color: #606266;
font-weight: 500;
}
:deep(.el-input__wrapper) {
border-radius: 4px;
}
:deep(.el-select .el-input__wrapper) {
border-radius: 4px;
}
:deep(.el-date-editor .el-input__wrapper) {
border-radius: 4px;
}
.exclusive-relation-text {
color: #909399;
line-height: 1.2;
word-break: break-word;
}
</style>
+223 -177
View File
@@ -2,10 +2,9 @@
<div class="p-2 page-container">
<!-- 搜索区域 -->
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<div ref="searchRef" v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<!-- 搜索条件 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="所属企业" prop="enterpriseId">
@@ -40,7 +39,9 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
</el-row>
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -52,32 +53,35 @@
</div>
</transition>
<!-- 操作按钮栏 -->
<el-card shadow="hover" class="button-card">
<el-card shadow="never" class="button-card table-card">
<template #header>
<div class="card-header-vertical">
<div class="toolbar-title">招聘海报列表</div>
<div class="button-group">
<el-row ref="toolbarRef" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['zsjy:poster:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate" v-hasPermi="['zsjy:poster:edit']">修改</el-button>
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['zsjy:poster:remove']"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['zsjy:poster:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:poster:export']">导出</el-button>
<el-button type="info" plain icon="Fold" @click="toggleSearch">
<span v-if="showSearch">隐藏</span>
<span v-else>展开</span>
搜索
</el-button>
</div>
</div>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
</el-card>
<!-- 表格区域 -->
<el-card shadow="hover" class="table-card">
<div class="table-container">
<el-table v-loading="loading" :data="posterList" @selection-change="handleSelectionChange" class="scroll-table" stripe border>
<el-table
v-loading="loading"
border
:data="posterList"
@selection-change="handleSelectionChange"
style="width: 100%"
:scroll-x="true"
class="scroll-table"
>
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="企业名称" align="center" prop="enterpriseName" min-width="150" show-overflow-tooltip />
<el-table-column label="海报类型" align="center" prop="posterType" width="120">
@@ -92,76 +96,45 @@
</el-table-column>
<el-table-column label="海报预览" align="center" width="120">
<template #default="scope">
<el-image
<!-- 使用 ImagePreview 组件,支持 OSS ID 格式 -->
<image-preview
v-if="scope.row.posterPath"
:src="scope.row.posterPath"
:preview-src-list="[scope.row.posterPath]"
:preview-teleported="true"
fit="cover"
style="width: 80px; height: 80px; cursor: pointer"
>
<template #error>
<div
style="
width: 80px;
height: 80px;
display: flex;
align-items: center;
justify-content: center;
background: #f5f7fa;
color: #909399;
font-size: 12px;
"
>
加载失败
</div>
</template>
</el-image>
<span v-else>-</span>
:width="80"
:height="80"
:preview="true"
style="cursor: pointer"
/>
<div v-else style="width: 80px; height: 80px; background: #fafafa; display: flex; align-items: center; justify-content: center; font-size: 12px; color: #ccc; border: 1px dashed #d9d9d9; border-radius: 4px;">
<div style="text-align: center;">
<el-icon><Picture /></el-icon>
<div>未上传</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="海报状态" align="center" width="100">
<template #default="scope">
<el-tag v-if="scope.row.posterPath" type="success" size="small">已上传</el-tag>
<el-tag v-else type="warning" size="small">待上传</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="160" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
<el-table-column label="操作" align="center" width="280" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="预览" placement="top">
<el-button
link
type="primary"
icon="View"
size="default"
style="font-size: 16px; margin-right: 8px; color: blue"
@click="handlePreview(scope.row)"
v-hasPermi="['zsjy:poster:query']"
></el-button>
<el-button link type="info" icon="View" @click="handlePreview(scope.row)" v-hasPermi="['zsjy:poster:query']"></el-button>
</el-tooltip>
<el-tooltip content="修改" placement="top">
<el-button
link
type="primary"
icon="Edit"
size="default"
style="font-size: 16px; margin-right: 8px"
@click="handleUpdate(scope.row)"
v-hasPermi="['zsjy:poster:edit']"
></el-button>
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['zsjy:poster:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button
link
type="danger"
icon="Delete"
size="default"
style="font-size: 16px"
@click="handleDelete(scope.row)"
v-hasPermi="['zsjy:poster:remove']"
></el-button>
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['zsjy:poster:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination-container">
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</div>
@@ -209,12 +182,44 @@
<!-- 预览对话框 -->
<el-dialog title="海报预览" v-model="previewOpen" width="900px" append-to-body>
<PosterPreview
v-if="previewOpen && previewData.posterPath"
:poster-data="previewData"
:enterprise-detail="enterpriseDetail"
:position-detail="positionDetail"
/>
<div v-if="previewOpen && previewData.posterPath" class="poster-preview-container">
<!-- 海报图片预览 -->
<div class="poster-image-section">
<image-preview
:src="previewData.posterPath"
:width="400"
:height="600"
:preview="true"
style="margin: 0 auto;"
/>
</div>
<!-- 海报详情信息 -->
<div class="poster-info-section">
<el-descriptions title="海报详情" :column="2" border>
<el-descriptions-item label="海报类型">
<dict-tag :options="zsjy_poster_type" :value="previewData.posterType" />
</el-descriptions-item>
<el-descriptions-item label="所属企业">
{{ enterpriseDetail.enterpriseName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="关联岗位" v-if="previewData.posterType === '2'">
{{ positionDetail.positionName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="创建时间">
{{ previewData.createTime || '-' }}
</el-descriptions-item>
</el-descriptions>
</div>
</div>
<!-- 无海报图片提示 -->
<div v-else-if="previewOpen && !previewData.posterPath" class="no-poster-tip">
<el-empty description="暂无海报图片,请先上传海报模板">
<el-button type="primary" @click="handleUpdate(previewData)">立即上传</el-button>
</el-empty>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="previewOpen = false">关 闭</el-button>
@@ -225,8 +230,9 @@
</template>
<script setup name="RecruitmentPoster" lang="ts">
import { getCurrentInstance, ComponentInternalInstance, ref, reactive, toRefs } from 'vue';
import { getCurrentInstance, ComponentInternalInstance, ref, reactive, toRefs, onMounted } from 'vue';
import { FormInstance } from 'element-plus';
import { Picture } from '@element-plus/icons-vue';
import {
listRecruitmentPoster,
getRecruitmentPoster,
@@ -240,7 +246,6 @@ import { listEnterpriseOptions } from '@/api/zsjy/jobPosition';
import { getEnterprise } from '@/api/zsjy/enterprise';
import { getJobPosition } from '@/api/zsjy/jobPosition';
import { RecruitmentPosterVO, RecruitmentPosterForm, RecruitmentPosterQuery } from '@/api/zsjy/types';
import PosterPreview from '@/components/PosterPreview/index.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -349,11 +354,6 @@ const resetQuery = () => {
handleQuery();
};
/** 切换搜索显示 */
const toggleSearch = () => {
showSearch.value = !showSearch.value;
};
/** 企业选择改变 */
const handleEnterpriseChange = (val: number | string) => {
queryParams.value.positionId = undefined;
@@ -506,102 +506,148 @@ onMounted(() => {
});
</script>
<style scoped lang="scss">
// 继承后勤管理的样式
<style scoped>
.page-container {
.search-btn-container {
text-align: right;
}
.button-card {
margin-bottom: 20px;
padding: 20px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.card-header-vertical {
display: flex;
justify-content: space-between;
align-items: center;
}
.toolbar-title {
font-size: 18px;
font-weight: bold;
color: #303133;
}
.button-group {
display: flex;
align-items: center;
gap: 10px;
}
.table-card {
margin-bottom: 20px;
padding: 20px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.table-container {
overflow-x: auto;
}
.scroll-table {
width: 100%;
}
.pagination-container {
text-align: right;
padding-top: 20px;
}
.dialog-footer {
text-align: right;
}
// 表格适应性样式
:deep(.el-table) {
.el-table__cell {
padding: 8px 0;
}
}
// 响应式表格
:deep(.el-table__body-wrapper) {
overflow-x: auto;
}
// 表格列宽自适应
.el-table {
width: 100%;
table-layout: auto;
}
background-color: #f5f5f5;
min-height: 100vh;
}
// 操作按钮样式
.action-buttons {
.search-btn-container {
text-align: center;
margin-top: 10px;
}
.button-card {
margin-top: 10px;
}
.table-card {
background-color: #fff;
}
.table-container {
overflow-x: auto;
}
.scroll-table {
min-width: 100%;
}
.pagination-container {
margin-top: 20px;
text-align: right;
}
.dialog-footer {
text-align: center;
}
:deep(.el-card__header) {
padding: 15px 20px;
border-bottom: 1px solid #ebeef5;
background-color: #fafafa;
}
:deep(.el-table) {
border-radius: 4px;
}
:deep(.el-table th) {
background-color: #fafafa;
color: #606266;
font-weight: 500;
}
:deep(.el-button--plain) {
border-color: #dcdfe6;
color: #606266;
}
:deep(.el-button--plain:hover) {
border-color: #409eff;
color: #409eff;
}
:deep(.el-button--primary.is-plain) {
border-color: #409eff;
color: #409eff;
}
:deep(.el-button--success.is-plain) {
border-color: #67c23a;
color: #67c23a;
}
:deep(.el-button--danger.is-plain) {
border-color: #f56c6c;
color: #f56c6c;
}
:deep(.el-button--warning.is-plain) {
border-color: #e6a23c;
color: #e6a23c;
}
:deep(.el-form-item__label) {
color: #606266;
font-weight: 500;
}
:deep(.el-input__wrapper) {
border-radius: 4px;
}
:deep(.el-select .el-input__wrapper) {
border-radius: 4px;
}
:deep(.el-date-editor .el-input__wrapper) {
border-radius: 4px;
}
/* 海报预览样式 */
.poster-preview-container {
display: flex;
gap: 8px;
justify-content: center;
flex-direction: column;
align-items: center;
white-space: nowrap;
gap: 20px;
}
// 描述列表样式优化
:deep(.el-descriptions) {
.el-descriptions__label {
font-weight: 600;
color: #606266;
width: 120px;
}
.poster-image-section {
display: flex;
justify-content: center;
padding: 20px;
background: #f5f7fa;
border-radius: 8px;
}
.el-descriptions__content {
color: #303133;
.poster-info-section {
width: 100%;
max-width: 600px;
}
.no-poster-tip {
padding: 40px;
text-align: center;
}
/* 描述列表样式优化 */
:deep(.el-descriptions) .el-descriptions__label {
font-weight: 600;
color: #606266;
width: 120px;
}
:deep(.el-descriptions) .el-descriptions__content {
color: #303133;
}
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
}
</style>