1课表模板管理;
2、课表生成; 3、课表的网格视图; 4、导出与打印
This commit is contained in:
Executable
BIN
Binary file not shown.
+33
@@ -1,17 +1,24 @@
|
|||||||
package com.roomroot.web.controller.jwgl;
|
package com.roomroot.web.controller.jwgl;
|
||||||
|
|
||||||
|
import com.roomroot.jwgl.dto.kcb.TimetableGridQuery;
|
||||||
import com.roomroot.jwgl.dto.kcb.TimetableQuery;
|
import com.roomroot.jwgl.dto.kcb.TimetableQuery;
|
||||||
import com.roomroot.jwgl.entity.KCB;
|
import com.roomroot.jwgl.entity.KCB;
|
||||||
import com.roomroot.jwgl.service.KCBService;
|
import com.roomroot.jwgl.service.KCBService;
|
||||||
|
import com.roomroot.jwgl.service.TimetableGridService;
|
||||||
import com.roomroot.jwgl.unit.PageQuery;
|
import com.roomroot.jwgl.unit.PageQuery;
|
||||||
import com.roomroot.jwgl.unit.PageResult;
|
import com.roomroot.jwgl.unit.PageResult;
|
||||||
import com.roomroot.jwgl.unit.Result;
|
import com.roomroot.jwgl.unit.Result;
|
||||||
import com.roomroot.jwgl.vo.kcb.ClassTimetableVO;
|
import com.roomroot.jwgl.vo.kcb.ClassTimetableVO;
|
||||||
import com.roomroot.jwgl.vo.kcb.DailyWeeklyTimetableRangeVO;
|
import com.roomroot.jwgl.vo.kcb.DailyWeeklyTimetableRangeVO;
|
||||||
|
import com.roomroot.jwgl.vo.kcb.TimetableGridVO;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,6 +31,9 @@ public class KCBController {
|
|||||||
@Resource
|
@Resource
|
||||||
private KCBService kcbService;
|
private KCBService kcbService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TimetableGridService timetableGridService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增
|
* 新增
|
||||||
*/
|
*/
|
||||||
@@ -133,4 +143,27 @@ public class KCBController {
|
|||||||
public void exportClassCourses(TimetableQuery cond, HttpServletResponse response) {
|
public void exportClassCourses(TimetableQuery cond, HttpServletResponse response) {
|
||||||
kcbService.exportClassCourses(cond, response);
|
kcbService.exportClassCourses(cond, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 周次横版课表网格(手册一第12章)。
|
||||||
|
* <p>dim=semester|team|teacher|room,id 为对应编号;可传 mbbh 套用课表模板。</p>
|
||||||
|
*/
|
||||||
|
@GetMapping("/grid")
|
||||||
|
public Result<TimetableGridVO> grid(TimetableGridQuery query) {
|
||||||
|
return Result.success(timetableGridService.grid(query));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网格课表矩阵 Excel 导出(每周一个 sheet,节次 × 星期)。
|
||||||
|
*/
|
||||||
|
@GetMapping("/grid/export")
|
||||||
|
public ResponseEntity<byte[]> exportGrid(TimetableGridQuery query) {
|
||||||
|
byte[] bytes = timetableGridService.exportGrid(query);
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
|
||||||
|
String name = "课表_" + query.getDim() + ".xlsx";
|
||||||
|
headers.setContentDispositionFormData("attachment",
|
||||||
|
new String(name.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
|
||||||
|
return ResponseEntity.ok().headers(headers).body(bytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
package com.roomroot.web.controller.jwgl;
|
||||||
|
|
||||||
|
import com.roomroot.jwgl.entity.KCBMBB;
|
||||||
|
import com.roomroot.jwgl.service.KCBMBService;
|
||||||
|
import com.roomroot.jwgl.unit.PageQuery;
|
||||||
|
import com.roomroot.jwgl.unit.PageResult;
|
||||||
|
import com.roomroot.jwgl.unit.Result;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 课表模版管理(手册一第12章;CRUD + 启停 + 下拉选项)
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/kcbmb")
|
||||||
|
public class KCBMBController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private KCBMBService kcbmbService;
|
||||||
|
|
||||||
|
@GetMapping("/list")
|
||||||
|
public Result<PageResult<KCBMBB>> list(PageQuery query, @RequestParam(required = false) String mbmc) {
|
||||||
|
return Result.success(kcbmbService.page(query, mbmc));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
public Result<KCBMBB> get(@RequestParam("bh") String bh) {
|
||||||
|
return Result.success(kcbmbService.get(bh));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 可用模板下拉(课表生成页套用选择) */
|
||||||
|
@GetMapping("/options")
|
||||||
|
public Result<List<KCBMBB>> options() {
|
||||||
|
return Result.success(kcbmbService.options());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/add")
|
||||||
|
public Result<Void> add(@RequestBody KCBMBB tpl) {
|
||||||
|
kcbmbService.add(tpl);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/update")
|
||||||
|
public Result<Void> update(@RequestBody KCBMBB tpl) {
|
||||||
|
kcbmbService.update(tpl);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/delete")
|
||||||
|
public Result<Void> delete(@RequestParam("bh") String bh) {
|
||||||
|
kcbmbService.delete(bh);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 启用/停用模版 */
|
||||||
|
@PostMapping("/toggle")
|
||||||
|
public Result<Void> toggle(@RequestParam("bh") String bh, @RequestParam("enabled") boolean enabled) {
|
||||||
|
kcbmbService.toggle(bh, enabled);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.roomroot.jwgl.dto.kcb;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 课表网格查询参数(手册一第12章 课表生成)。
|
||||||
|
* <p>dim 维度:semester 班次学期 / team 学员队 / teacher 教员 / room 教室。
|
||||||
|
* id 为对应维度主键(xydxqbh / xydbh / jybh / jsbh)。</p>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class TimetableGridQuery {
|
||||||
|
|
||||||
|
/** 维度:semester | team | teacher | room */
|
||||||
|
private String dim;
|
||||||
|
|
||||||
|
/** 维度目标编号 */
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/** 年度(校历/备注取数用;semester 维度自动从班次学期带出) */
|
||||||
|
private Integer nd;
|
||||||
|
|
||||||
|
/** 起始日期 yyyy-MM-dd;空时 semester 取学期范围,其它维度取本周 */
|
||||||
|
private String start;
|
||||||
|
|
||||||
|
/** 结束日期 yyyy-MM-dd */
|
||||||
|
private String end;
|
||||||
|
|
||||||
|
/** 课表模板编号(可选;套用模板显示开关:78节/晚上/夜间/选修/天数) */
|
||||||
|
private String mbbh;
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.roomroot.jwgl.service;
|
package com.roomroot.jwgl.service;
|
||||||
|
|
||||||
|
import com.roomroot.jwgl.entity.JCSJB;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@@ -18,4 +20,10 @@ public interface ClassPeriodService {
|
|||||||
* 上午时段的双节次标签,用于「正课」默认判定。
|
* 上午时段的双节次标签,用于「正课」默认判定。
|
||||||
*/
|
*/
|
||||||
Set<String> mainSlotLabels();
|
Set<String> mainSlotLabels();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单节次时间行(双节次=0),按节次序号升序;表内无数据时返回空,
|
||||||
|
* 调用方回退默认 1–12 节。
|
||||||
|
*/
|
||||||
|
List<JCSJB> singleRows();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.roomroot.jwgl.service;
|
||||||
|
|
||||||
|
import com.roomroot.jwgl.entity.KCBMBB;
|
||||||
|
import com.roomroot.jwgl.unit.PageQuery;
|
||||||
|
import com.roomroot.jwgl.unit.PageResult;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 课表模版管理(手册一第12章:只做 CRUD + 套用,不建新表)。
|
||||||
|
*/
|
||||||
|
public interface KCBMBService {
|
||||||
|
|
||||||
|
PageResult<KCBMBB> page(PageQuery query, String mbmc);
|
||||||
|
|
||||||
|
KCBMBB get(String bh);
|
||||||
|
|
||||||
|
void add(KCBMBB tpl);
|
||||||
|
|
||||||
|
void update(KCBMBB tpl);
|
||||||
|
|
||||||
|
void delete(String bh);
|
||||||
|
|
||||||
|
/** 启用/停用(模版可用开关) */
|
||||||
|
void toggle(String bh, boolean enabled);
|
||||||
|
|
||||||
|
/** 可用模板下拉(只回编号+名称) */
|
||||||
|
List<KCBMBB> options();
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package com.roomroot.jwgl.service;
|
||||||
|
|
||||||
|
import com.roomroot.jwgl.dto.kcb.TimetableGridQuery;
|
||||||
|
import com.roomroot.jwgl.vo.kcb.TimetableGridVO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 课表网格生成(手册一第12章)。
|
||||||
|
* <p>按 班次学期/学员队/教员/教室 四个维度生成「周次 × 星期 × 节次」横版网格。</p>
|
||||||
|
*/
|
||||||
|
public interface TimetableGridService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成课表网格数据。
|
||||||
|
*/
|
||||||
|
TimetableGridVO grid(TimetableGridQuery query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出网格课表为矩阵 Excel(每周一个 sheet)。
|
||||||
|
*/
|
||||||
|
byte[] exportGrid(TimetableGridQuery query);
|
||||||
|
}
|
||||||
+8
@@ -51,6 +51,14 @@ public class ClassPeriodServiceImpl implements ClassPeriodService {
|
|||||||
return main.isEmpty() ? DEFAULT_MAIN_SLOTS : main;
|
return main.isEmpty() ? DEFAULT_MAIN_SLOTS : main;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<JCSJB> singleRows() {
|
||||||
|
List<JCSJB> rows = jcsjbMapper.selectList(new LambdaQueryWrapper<JCSJB>()
|
||||||
|
.eq(JCSJB::getSjc, false));
|
||||||
|
rows.sort(Comparator.comparingInt(r -> PeriodUtil.firstPeriod(r.getJcsy())));
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
private List<JCSJB> loadSlots() {
|
private List<JCSJB> loadSlots() {
|
||||||
List<JCSJB> rows = jcsjbMapper.selectList(new LambdaQueryWrapper<JCSJB>()
|
List<JCSJB> rows = jcsjbMapper.selectList(new LambdaQueryWrapper<JCSJB>()
|
||||||
.eq(JCSJB::getSjc, true));
|
.eq(JCSJB::getSjc, true));
|
||||||
|
|||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
package com.roomroot.jwgl.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.roomroot.jwgl.entity.KCBMBB;
|
||||||
|
import com.roomroot.jwgl.mapper.KCBMBBMapper;
|
||||||
|
import com.roomroot.jwgl.service.KCBMBService;
|
||||||
|
import com.roomroot.jwgl.unit.BusinessException;
|
||||||
|
import com.roomroot.jwgl.unit.PageQuery;
|
||||||
|
import com.roomroot.jwgl.unit.PageResult;
|
||||||
|
import com.roomroot.jwgl.utils.UuidUtil;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class KCBMBServiceImpl implements KCBMBService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private KCBMBBMapper kcbmbbMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<KCBMBB> page(PageQuery query, String mbmc) {
|
||||||
|
Page<KCBMBB> page = new Page<>(query.getPageNum(), query.getPageSize());
|
||||||
|
LambdaQueryWrapper<KCBMBB> qw = new LambdaQueryWrapper<KCBMBB>()
|
||||||
|
.like(mbmc != null && !mbmc.isEmpty(), KCBMBB::getMBMC, mbmc)
|
||||||
|
.orderByDesc(KCBMBB::getGXSJ);
|
||||||
|
Page<KCBMBB> result = kcbmbbMapper.selectPage(page, qw);
|
||||||
|
return new PageResult<>(result.getRecords(), result.getTotal(), query.getPageNum(), query.getPageSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KCBMBB get(String bh) {
|
||||||
|
return kcbmbbMapper.selectById(bh);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(KCBMBB tpl) {
|
||||||
|
if (tpl.getMBMC() == null || tpl.getMBMC().isEmpty()) {
|
||||||
|
throw new BusinessException("模版名称不能为空");
|
||||||
|
}
|
||||||
|
tpl.setBH(UuidUtil.getUUID());
|
||||||
|
tpl.setGXSJ(LocalDateTime.now());
|
||||||
|
if (tpl.getMBKY() == null) {
|
||||||
|
tpl.setMBKY(true);
|
||||||
|
}
|
||||||
|
tpl.setMBCZ(tpl.getMB() != null && !tpl.getMB().isEmpty());
|
||||||
|
kcbmbbMapper.insert(tpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(KCBMBB tpl) {
|
||||||
|
if (tpl.getBH() == null || tpl.getBH().isEmpty()) {
|
||||||
|
throw new BusinessException("模版编号不能为空");
|
||||||
|
}
|
||||||
|
tpl.setGXSJ(LocalDateTime.now());
|
||||||
|
tpl.setMBCZ(tpl.getMB() != null && !tpl.getMB().isEmpty());
|
||||||
|
kcbmbbMapper.updateById(tpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(String bh) {
|
||||||
|
kcbmbbMapper.deleteById(bh);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toggle(String bh, boolean enabled) {
|
||||||
|
KCBMBB tpl = kcbmbbMapper.selectById(bh);
|
||||||
|
if (tpl == null) {
|
||||||
|
throw new BusinessException("模版不存在:" + bh);
|
||||||
|
}
|
||||||
|
tpl.setMBKY(enabled);
|
||||||
|
tpl.setGXSJ(LocalDateTime.now());
|
||||||
|
kcbmbbMapper.updateById(tpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<KCBMBB> options() {
|
||||||
|
return kcbmbbMapper.selectList(new LambdaQueryWrapper<KCBMBB>()
|
||||||
|
.eq(KCBMBB::getMBKY, true)
|
||||||
|
.orderByDesc(KCBMBB::getGXSJ))
|
||||||
|
.stream()
|
||||||
|
.map(t -> {
|
||||||
|
KCBMBB o = new KCBMBB();
|
||||||
|
o.setBH(t.getBH());
|
||||||
|
o.setMBMC(t.getMBMC());
|
||||||
|
return o;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
+617
@@ -0,0 +1,617 @@
|
|||||||
|
package com.roomroot.jwgl.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.roomroot.jwgl.dto.kcb.TimetableGridQuery;
|
||||||
|
import com.roomroot.jwgl.entity.JCSJB;
|
||||||
|
import com.roomroot.jwgl.entity.JQB;
|
||||||
|
import com.roomroot.jwgl.entity.JSB;
|
||||||
|
import com.roomroot.jwgl.entity.JYB;
|
||||||
|
import com.roomroot.jwgl.entity.KB;
|
||||||
|
import com.roomroot.jwgl.entity.KCBMBB;
|
||||||
|
import com.roomroot.jwgl.entity.SSKC;
|
||||||
|
import com.roomroot.jwgl.entity.SSKCB;
|
||||||
|
import com.roomroot.jwgl.entity.SSKCBJS;
|
||||||
|
import com.roomroot.jwgl.entity.SSKCBFZJY;
|
||||||
|
import com.roomroot.jwgl.entity.SSKCBXYD;
|
||||||
|
import com.roomroot.jwgl.entity.XQXLB;
|
||||||
|
import com.roomroot.jwgl.entity.XYDB;
|
||||||
|
import com.roomroot.jwgl.entity.XYDNDXQJBXXB;
|
||||||
|
import com.roomroot.jwgl.mapper.ClassRoomMapper;
|
||||||
|
import com.roomroot.jwgl.mapper.ClassSemesterMapper;
|
||||||
|
import com.roomroot.jwgl.mapper.JQBMapper;
|
||||||
|
import com.roomroot.jwgl.mapper.JYBMapper;
|
||||||
|
import com.roomroot.jwgl.mapper.KBMapper;
|
||||||
|
import com.roomroot.jwgl.mapper.KCBMBBMapper;
|
||||||
|
import com.roomroot.jwgl.mapper.SSKCBJSMapper;
|
||||||
|
import com.roomroot.jwgl.mapper.SSKCBFZJYMapper;
|
||||||
|
import com.roomroot.jwgl.mapper.SSKCBMapper;
|
||||||
|
import com.roomroot.jwgl.mapper.SSKCBXYDMapper;
|
||||||
|
import com.roomroot.jwgl.mapper.SSKCMapper;
|
||||||
|
import com.roomroot.jwgl.mapper.SemesterCalendarMapper;
|
||||||
|
import com.roomroot.jwgl.mapper.XYDBMapper;
|
||||||
|
import com.roomroot.jwgl.service.ClassPeriodService;
|
||||||
|
import com.roomroot.jwgl.service.TimetableGridService;
|
||||||
|
import com.roomroot.jwgl.unit.BusinessException;
|
||||||
|
import com.roomroot.jwgl.utils.PeriodUtil;
|
||||||
|
import com.roomroot.jwgl.vo.kcb.TimetableGridVO;
|
||||||
|
import org.apache.poi.ss.usermodel.CellStyle;
|
||||||
|
import org.apache.poi.ss.usermodel.Font;
|
||||||
|
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||||
|
import org.apache.poi.ss.usermodel.VerticalAlignment;
|
||||||
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.time.DayOfWeek;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 课表网格生成实现。
|
||||||
|
* <p>课次一律从 实施_课程表(SSKCB) 按日期范围取;维度过滤经三张关联子表
|
||||||
|
* (学员队 / 教室 / 辅助教员)在内存归并,避免 N+1 与达梦 GUID 双形态问题。</p>
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class TimetableGridServiceImpl implements TimetableGridService {
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DAY = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
private static final DateTimeFormatter COMPACT = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||||
|
private static final String[] WEEKDAY_NAMES = {"", "一", "二", "三", "四", "五", "六", "日"};
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SSKCBMapper sskcbMapper;
|
||||||
|
@Resource
|
||||||
|
private SSKCBXYDMapper sskcbxydMapper;
|
||||||
|
@Resource
|
||||||
|
private SSKCBJSMapper sskcbjsMapper;
|
||||||
|
@Resource
|
||||||
|
private SSKCBFZJYMapper sskcbfzjyMapper;
|
||||||
|
@Resource
|
||||||
|
private SSKCMapper sskcMapper;
|
||||||
|
@Resource
|
||||||
|
private KBMapper kbMapper;
|
||||||
|
@Resource
|
||||||
|
private XYDBMapper xydbMapper;
|
||||||
|
@Resource
|
||||||
|
private JYBMapper jybMapper;
|
||||||
|
@Resource
|
||||||
|
private ClassRoomMapper classRoomMapper;
|
||||||
|
@Resource
|
||||||
|
private ClassSemesterMapper classSemesterMapper;
|
||||||
|
@Resource
|
||||||
|
private SemesterCalendarMapper semesterCalendarMapper;
|
||||||
|
@Resource
|
||||||
|
private JQBMapper jqbMapper;
|
||||||
|
@Resource
|
||||||
|
private KCBMBBMapper kcbmbbMapper;
|
||||||
|
@Resource
|
||||||
|
private ClassPeriodService classPeriodService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TimetableGridVO grid(TimetableGridQuery query) {
|
||||||
|
if (query == null || query.getDim() == null || query.getId() == null || query.getId().isEmpty()) {
|
||||||
|
throw new BusinessException("课表网格需要维度(dim)与目标编号(id)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 维度目标与日期范围 ----
|
||||||
|
String dim = query.getDim();
|
||||||
|
String targetName = query.getId();
|
||||||
|
String teamFilter = null; // 学员队编号(semester/team 维度)
|
||||||
|
String teacherFilter = null;
|
||||||
|
String roomFilter = null;
|
||||||
|
String semesterBh = null; // 班历取数用(semester 维度)
|
||||||
|
LocalDate start = parseDay(query.getStart());
|
||||||
|
LocalDate end = parseDay(query.getEnd());
|
||||||
|
Integer nd = query.getNd();
|
||||||
|
|
||||||
|
switch (dim) {
|
||||||
|
case "semester": {
|
||||||
|
XYDNDXQJBXXB sem = classSemesterMapper.selectById(query.getId());
|
||||||
|
if (sem == null) {
|
||||||
|
throw new BusinessException("班次学期不存在:" + query.getId());
|
||||||
|
}
|
||||||
|
semesterBh = sem.getBh();
|
||||||
|
teamFilter = sem.getXydbh();
|
||||||
|
nd = sem.getNd();
|
||||||
|
if (start == null) start = sem.getKxrq();
|
||||||
|
if (end == null) end = sem.getJsrq();
|
||||||
|
String xydmc = resolveTeamName(teamFilter);
|
||||||
|
targetName = xydmc + "(" + sem.getNd() + "年度 第" + sem.getXqdc() + "学期)";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "team": {
|
||||||
|
XYDB team = xydbMapper.selectOne(new LambdaQueryWrapper<XYDB>().eq(XYDB::getXydbh, query.getId()));
|
||||||
|
if (team == null) {
|
||||||
|
throw new BusinessException("学员队不存在:" + query.getId());
|
||||||
|
}
|
||||||
|
teamFilter = team.getXydbh();
|
||||||
|
targetName = team.getXydmc() != null ? team.getXydmc() : team.getXydbh();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "teacher": {
|
||||||
|
JYB teacher = jybMapper.selectOne(new LambdaQueryWrapper<JYB>().eq(JYB::getJybh, query.getId()));
|
||||||
|
if (teacher == null) {
|
||||||
|
throw new BusinessException("教员不存在:" + query.getId());
|
||||||
|
}
|
||||||
|
teacherFilter = teacher.getJybh();
|
||||||
|
targetName = (teacher.getJyxm() != null ? teacher.getJyxm() : teacher.getJybh());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "room": {
|
||||||
|
JSB room = classRoomMapper.selectOne(new LambdaQueryWrapper<JSB>()
|
||||||
|
.eq(JSB::getJsbh, query.getId()).eq(JSB::getDelFlag, 0));
|
||||||
|
if (room == null) {
|
||||||
|
throw new BusinessException("教室不存在:" + query.getId());
|
||||||
|
}
|
||||||
|
roomFilter = room.getJsbh();
|
||||||
|
targetName = room.getJsmc() != null ? room.getJsmc() : room.getJsbh();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new BusinessException("不支持的课表维度:" + dim + "(可选 semester/team/teacher/room)");
|
||||||
|
}
|
||||||
|
if (start == null || end == null) {
|
||||||
|
LocalDate monday = LocalDate.now().with(DayOfWeek.MONDAY);
|
||||||
|
if (start == null) start = monday;
|
||||||
|
if (end == null) end = monday.plusDays(6);
|
||||||
|
}
|
||||||
|
if (end.isBefore(start)) {
|
||||||
|
throw new BusinessException("结束日期不能早于开始日期");
|
||||||
|
}
|
||||||
|
if (nd == null) {
|
||||||
|
nd = start.getYear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 模板显示开关 ----
|
||||||
|
KCBMBB tpl = null;
|
||||||
|
if (query.getMbbh() != null && !query.getMbbh().isEmpty()) {
|
||||||
|
tpl = kcbmbbMapper.selectById(query.getMbbh());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 课次与关联 ----
|
||||||
|
List<SSKCB> lessons = sskcbMapper.selectList(new LambdaQueryWrapper<SSKCB>()
|
||||||
|
.ge(SSKCB::getRq, start.atStartOfDay())
|
||||||
|
.lt(SSKCB::getRq, end.plusDays(1).atStartOfDay())
|
||||||
|
.eq(SSKCB::getSczt, 0)
|
||||||
|
.isNotNull(SSKCB::getRq));
|
||||||
|
List<String> lessonBhs = lessons.stream().map(SSKCB::getBh).filter(Objects::nonNull).distinct().collect(Collectors.toList());
|
||||||
|
List<String> courseBhs = lessons.stream().map(SSKCB::getSskcbh).filter(Objects::nonNull).distinct().collect(Collectors.toList());
|
||||||
|
|
||||||
|
Map<String, List<String>> teamsOfLesson = new HashMap<>();
|
||||||
|
Map<String, String> teamShortName = new HashMap<>(); // sskcbbh -> 简称(SSKCBXYD.jc)
|
||||||
|
Map<String, List<String>> roomsOfLesson = new HashMap<>();
|
||||||
|
Map<String, List<String>> teachersOfLesson = new HashMap<>();
|
||||||
|
Map<String, String> mainTeacherOfLesson = new HashMap<>();
|
||||||
|
if (!lessonBhs.isEmpty()) {
|
||||||
|
for (SSKCBXYD l : sskcbxydMapper.selectList(new LambdaQueryWrapper<SSKCBXYD>()
|
||||||
|
.in(SSKCBXYD::getSskcbbh, lessonBhs))) {
|
||||||
|
teamsOfLesson.computeIfAbsent(l.getSskcbbh(), k -> new ArrayList<>()).add(l.getXydbh());
|
||||||
|
if (l.getJc() != null && !l.getJc().isEmpty()) {
|
||||||
|
teamShortName.putIfAbsent(l.getSskcbbh(), l.getJc());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (SSKCBJS l : sskcbjsMapper.selectList(new LambdaQueryWrapper<SSKCBJS>()
|
||||||
|
.in(SSKCBJS::getSskcbbh, lessonBhs))) {
|
||||||
|
roomsOfLesson.computeIfAbsent(l.getSskcbbh(), k -> new ArrayList<>()).add(l.getJsbh());
|
||||||
|
}
|
||||||
|
for (SSKCBFZJY l : sskcbfzjyMapper.selectList(new LambdaQueryWrapper<SSKCBFZJY>()
|
||||||
|
.in(SSKCBFZJY::getSskcbbh, lessonBhs))) {
|
||||||
|
if (l.getFzjybh() == null) continue;
|
||||||
|
teachersOfLesson.computeIfAbsent(l.getSskcbbh(), k -> new ArrayList<>()).add(l.getFzjybh());
|
||||||
|
if (l.getZjy() != null && l.getZjy() == 1) {
|
||||||
|
mainTeacherOfLesson.put(l.getSskcbbh(), l.getFzjybh());
|
||||||
|
} else {
|
||||||
|
mainTeacherOfLesson.putIfAbsent(l.getSskcbbh(), l.getFzjybh());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map<String, SSKC> courses = courseBhs.isEmpty() ? Map.of()
|
||||||
|
: sskcMapper.selectByBhs(courseBhs).stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(SSKC::getBh, c -> c, (a, b) -> a));
|
||||||
|
Set<String> kbhs = courses.values().stream().map(SSKC::getKmbh).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||||
|
Map<String, KB> kbByKbh = kbhs.isEmpty() ? Map.of()
|
||||||
|
: kbMapper.selectBatchIds(kbhs).stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(KB::getKbh, k -> k, (a, b) -> a));
|
||||||
|
Set<String> jybhSet = new HashSet<>();
|
||||||
|
teachersOfLesson.values().forEach(jybhSet::addAll);
|
||||||
|
courses.values().forEach(c -> { if (c.getJybh() != null) jybhSet.add(c.getJybh()); });
|
||||||
|
Map<String, String> teacherNames = jybhSet.isEmpty() ? Map.of()
|
||||||
|
: jybMapper.selectList(new LambdaQueryWrapper<JYB>().in(JYB::getJybh, jybhSet)).stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(JYB::getJybh, j -> j.getJyxm() == null ? j.getJybh() : j.getJyxm(), (a, b) -> a));
|
||||||
|
Set<String> jsbhSet = new HashSet<>();
|
||||||
|
roomsOfLesson.values().forEach(jsbhSet::addAll);
|
||||||
|
Map<String, String> roomNames = jsbhSet.isEmpty() ? Map.of()
|
||||||
|
: classRoomMapper.selectList(new LambdaQueryWrapper<JSB>()
|
||||||
|
.in(JSB::getJsbh, jsbhSet).eq(JSB::getDelFlag, 0)).stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(JSB::getJsbh, j -> j.getJsmc() == null ? j.getJsbh() : j.getJsmc(), (a, b) -> a));
|
||||||
|
Set<String> xydbhSet = new HashSet<>();
|
||||||
|
teamsOfLesson.values().forEach(xydbhSet::addAll);
|
||||||
|
Map<String, String> teamNames = xydbhSet.isEmpty() ? Map.of()
|
||||||
|
: xydbMapper.selectList(new LambdaQueryWrapper<XYDB>().in(XYDB::getXydbh, xydbhSet)).stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(XYDB::getXydbh, t -> t.getXydmc() == null ? t.getXydbh() : t.getXydmc(), (a, b) -> a));
|
||||||
|
|
||||||
|
// ---- 节次行 ----
|
||||||
|
List<TimetableGridVO.Period> periods = buildPeriods(tpl);
|
||||||
|
Set<Integer> periodJcs = periods.stream().map(TimetableGridVO.Period::getJc).collect(Collectors.toSet());
|
||||||
|
Map<Integer, String> periodSd = periods.stream()
|
||||||
|
.collect(Collectors.toMap(TimetableGridVO.Period::getJc, TimetableGridVO.Period::getSd, (a, b) -> a));
|
||||||
|
|
||||||
|
// ---- 历表:不可排格 + 备注栏 ----
|
||||||
|
// 备注栏规则:晚上/夜间/周末 且 备注显示=1 的事件写入备注而非格子。
|
||||||
|
Map<String, String> blockByCell = new HashMap<>(); // date#jc -> 事件名
|
||||||
|
List<TimetableGridVO.Remark> remarks = new ArrayList<>();
|
||||||
|
if (nd != null) {
|
||||||
|
for (XQXLB g : semesterCalendarMapper.selectList(new LambdaQueryWrapper<XQXLB>().eq(XQXLB::getNd, nd))) {
|
||||||
|
applyCalendarRow(g.getJqsj(), g.getCourseClass(), g.getKpk() != null && g.getKpk(),
|
||||||
|
Boolean.TRUE.equals(g.getBzxs()), joinName(g.getJqmc(), g.getBz()),
|
||||||
|
start, end, periodSd, blockByCell, remarks, "校历");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (semesterBh != null) {
|
||||||
|
for (JQB g : jqbMapper.selectList(new LambdaQueryWrapper<JQB>().eq(JQB::getXydxqbh, semesterBh))) {
|
||||||
|
String dateStr = g.getJqsj() == null ? null : intToDay(g.getJqsj());
|
||||||
|
applyCalendarRow(dateStr, g.getCourseClass(), g.getKpk() != null && g.getKpk() == 1,
|
||||||
|
g.getBzxs() != null && g.getBzxs() == 1, joinName(g.getJqmc(), g.getBz()),
|
||||||
|
start, end, periodSd, blockByCell, remarks, "班历");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 组装网格 ----
|
||||||
|
TimetableGridVO vo = new TimetableGridVO();
|
||||||
|
vo.setDim(dim);
|
||||||
|
vo.setTargetName(targetName);
|
||||||
|
vo.setNd(nd);
|
||||||
|
vo.setStartDate(start.format(DAY));
|
||||||
|
vo.setEndDate(end.format(DAY));
|
||||||
|
vo.setPeriods(periods);
|
||||||
|
vo.setRemarks(remarks);
|
||||||
|
|
||||||
|
// 课次按 date#jc 归并(先按维度过滤)
|
||||||
|
Map<String, List<SSKCB>> lessonByCell = new HashMap<>();
|
||||||
|
for (SSKCB l : lessons) {
|
||||||
|
if (!matchDimension(l, dim, teamFilter, teacherFilter, roomFilter, teamsOfLesson, teachersOfLesson, roomsOfLesson, courses)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SSKC course = courses.get(l.getSskcbh());
|
||||||
|
KB kb = course == null ? null : kbByKbh.get(course.getKmbh());
|
||||||
|
if (tpl != null && Boolean.FALSE.equals(tpl.getKBBHXX())
|
||||||
|
&& kb != null && kb.getKclx() != null && kb.getKclx().contains("选修")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (l.getJc() == null || !periodJcs.contains(l.getJc())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
lessonByCell.computeIfAbsent(l.getRq().toLocalDate().format(DAY) + "#" + l.getJc(), k -> new ArrayList<>()).add(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalDate weekStart = start.with(DayOfWeek.MONDAY);
|
||||||
|
int weekNo = 1;
|
||||||
|
while (!weekStart.isAfter(end)) {
|
||||||
|
TimetableGridVO.Week week = new TimetableGridVO.Week();
|
||||||
|
week.setWeekNo(weekNo++);
|
||||||
|
week.setStartDate(weekStart.format(DAY));
|
||||||
|
week.setEndDate(weekStart.plusDays(6).format(DAY));
|
||||||
|
for (int wd = 1; wd <= 7; wd++) {
|
||||||
|
LocalDate date = weekStart.plusDays(wd - 1);
|
||||||
|
TimetableGridVO.Day day = new TimetableGridVO.Day();
|
||||||
|
day.setDate(date.format(DAY));
|
||||||
|
day.setWeekday(wd);
|
||||||
|
if (tpl != null && tpl.getZXXSTS() != null && tpl.getZXXSTS() <= 5 && wd > tpl.getZXXSTS()) {
|
||||||
|
// 模板「纵向显示天数」截断周末列
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (date.isBefore(start) || date.isAfter(end)) {
|
||||||
|
day.setBlocked(true);
|
||||||
|
day.setBlockReason("范围外");
|
||||||
|
week.getDays().add(day);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
boolean dayBlocked = false;
|
||||||
|
String reason = null;
|
||||||
|
for (Integer jc : periodJcs) {
|
||||||
|
String key = date.format(DAY) + "#" + jc;
|
||||||
|
String block = blockByCell.get(key);
|
||||||
|
if (block != null) {
|
||||||
|
dayBlocked = true;
|
||||||
|
reason = block;
|
||||||
|
}
|
||||||
|
List<SSKCB> cellLessons = lessonByCell.get(key);
|
||||||
|
if (cellLessons == null) continue;
|
||||||
|
for (SSKCB l : cellLessons) {
|
||||||
|
day.getCells().computeIfAbsent(String.valueOf(jc), k -> new ArrayList<>())
|
||||||
|
.add(buildCell(l, dim, courses, kbByKbh, teamShortName, teacherNames,
|
||||||
|
roomNames, teamNames, mainTeacherOfLesson, roomsOfLesson, teamsOfLesson));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
day.setBlocked(dayBlocked);
|
||||||
|
day.setBlockReason(reason);
|
||||||
|
week.getDays().add(day);
|
||||||
|
}
|
||||||
|
vo.getWeeks().add(week);
|
||||||
|
}
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] exportGrid(TimetableGridQuery query) {
|
||||||
|
TimetableGridVO vo = grid(query);
|
||||||
|
try (Workbook wb = new XSSFWorkbook(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||||
|
CellStyle head = wb.createCellStyle();
|
||||||
|
Font hf = wb.createFont();
|
||||||
|
hf.setBold(true);
|
||||||
|
head.setFont(hf);
|
||||||
|
head.setAlignment(HorizontalAlignment.CENTER);
|
||||||
|
head.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||||
|
CellStyle center = wb.createCellStyle();
|
||||||
|
center.setAlignment(HorizontalAlignment.CENTER);
|
||||||
|
center.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||||
|
center.setWrapText(true);
|
||||||
|
CellStyle title = wb.createCellStyle();
|
||||||
|
Font tf = wb.createFont();
|
||||||
|
tf.setBold(true);
|
||||||
|
tf.setFontHeightInPoints((short) 14);
|
||||||
|
title.setFont(tf);
|
||||||
|
title.setAlignment(HorizontalAlignment.CENTER);
|
||||||
|
|
||||||
|
for (TimetableGridVO.Week week : vo.getWeeks()) {
|
||||||
|
org.apache.poi.ss.usermodel.Sheet sheet = wb.createSheet("第" + week.getWeekNo() + "周");
|
||||||
|
int r = 0;
|
||||||
|
org.apache.poi.ss.usermodel.Row titleRow = sheet.createRow(r++);
|
||||||
|
org.apache.poi.ss.usermodel.Cell tc = titleRow.createCell(0);
|
||||||
|
tc.setCellValue(vo.getTargetName() + " 课表(" + week.getStartDate() + " ~ " + week.getEndDate() + ")");
|
||||||
|
tc.setCellStyle(title);
|
||||||
|
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(0, 0, 0, 7));
|
||||||
|
|
||||||
|
org.apache.poi.ss.usermodel.Row headRow = sheet.createRow(r++);
|
||||||
|
String[] heads = {"节次", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"};
|
||||||
|
for (int i = 0; i < heads.length; i++) {
|
||||||
|
org.apache.poi.ss.usermodel.Cell c = headRow.createCell(i);
|
||||||
|
c.setCellValue(heads[i]);
|
||||||
|
c.setCellStyle(head);
|
||||||
|
}
|
||||||
|
for (TimetableGridVO.Period p : vo.getPeriods()) {
|
||||||
|
org.apache.poi.ss.usermodel.Row row = sheet.createRow(r++);
|
||||||
|
org.apache.poi.ss.usermodel.Cell pc = row.createCell(0);
|
||||||
|
pc.setCellValue(p.getMc() + (p.getKssj() != null ? "\n" + p.getKssj() + "-" + p.getJssj() : ""));
|
||||||
|
pc.setCellStyle(head);
|
||||||
|
for (TimetableGridVO.Day day : week.getDays()) {
|
||||||
|
org.apache.poi.ss.usermodel.Cell c = row.createCell(day.getWeekday());
|
||||||
|
List<TimetableGridVO.Cell> cells = day.getCells().get(String.valueOf(p.getJc()));
|
||||||
|
if (cells != null && !cells.isEmpty()) {
|
||||||
|
c.setCellValue(cells.stream().map(this::cellText).collect(Collectors.joining("\n")));
|
||||||
|
}
|
||||||
|
c.setCellStyle(center);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!vo.getRemarks().isEmpty()) {
|
||||||
|
r++;
|
||||||
|
org.apache.poi.ss.usermodel.Row rh = sheet.createRow(r++);
|
||||||
|
org.apache.poi.ss.usermodel.Cell rc = rh.createCell(0);
|
||||||
|
rc.setCellValue("备注栏");
|
||||||
|
rc.setCellStyle(head);
|
||||||
|
for (TimetableGridVO.Remark rm : vo.getRemarks()) {
|
||||||
|
org.apache.poi.ss.usermodel.Row rr = sheet.createRow(r++);
|
||||||
|
org.apache.poi.ss.usermodel.Cell cc = rr.createCell(0);
|
||||||
|
cc.setCellValue(rm.getDate() + " " + rm.getScope() + ":" + rm.getText());
|
||||||
|
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(rr.getRowNum(), rr.getRowNum(), 0, 7));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sheet.setColumnWidth(0, 14 * 256);
|
||||||
|
for (int i = 1; i <= 7; i++) {
|
||||||
|
sheet.setColumnWidth(i, 22 * 256);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (vo.getWeeks().isEmpty()) {
|
||||||
|
wb.createSheet("课表").createRow(0).createCell(0).setCellValue("范围内无课表数据");
|
||||||
|
}
|
||||||
|
wb.write(out);
|
||||||
|
return out.toByteArray();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new BusinessException("课表导出失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 内部方法 ====================
|
||||||
|
|
||||||
|
private String cellText(TimetableGridVO.Cell c) {
|
||||||
|
StringBuilder sb = new StringBuilder(c.getKcjc() != null && !c.getKcjc().isEmpty() ? c.getKcjc() : c.getKcmc());
|
||||||
|
List<String> extra = new ArrayList<>();
|
||||||
|
if (c.getJyxm() != null && !c.getJyxm().isEmpty()) extra.add(c.getJyxm());
|
||||||
|
if (c.getJsmc() != null && !c.getJsmc().isEmpty()) extra.add(c.getJsmc());
|
||||||
|
if (c.getXydmc() != null && !c.getXydmc().isEmpty()) extra.add(c.getXydmc());
|
||||||
|
if (!extra.isEmpty()) sb.append("\n(").append(String.join(" ", extra)).append(")");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TimetableGridVO.Cell buildCell(SSKCB l, String dim,
|
||||||
|
Map<String, SSKC> courses, Map<String, KB> kbByKbh,
|
||||||
|
Map<String, String> teamShortName,
|
||||||
|
Map<String, String> teacherNames, Map<String, String> roomNames,
|
||||||
|
Map<String, String> teamNames,
|
||||||
|
Map<String, String> mainTeacherOfLesson,
|
||||||
|
Map<String, List<String>> roomsOfLesson,
|
||||||
|
Map<String, List<String>> teamsOfLesson) {
|
||||||
|
TimetableGridVO.Cell cell = new TimetableGridVO.Cell();
|
||||||
|
cell.setSskcbbh(l.getBh());
|
||||||
|
cell.setSskcbh(l.getSskcbh());
|
||||||
|
SSKC course = courses.get(l.getSskcbh());
|
||||||
|
KB kb = course == null ? null : kbByKbh.get(course.getKmbh());
|
||||||
|
cell.setKcmc(kb != null && kb.getKmc() != null ? kb.getKmc() : l.getJxnr());
|
||||||
|
cell.setKclx(kb == null ? null : kb.getKclx());
|
||||||
|
// 简称优先:课次-学员队关联上的简称 > 课表.简称 > 课程名称
|
||||||
|
String shortName = teamShortName.get(l.getBh());
|
||||||
|
if (shortName == null || shortName.isEmpty()) {
|
||||||
|
shortName = kb != null && kb.getJc() != null && !kb.getJc().isEmpty() ? kb.getJc() : cell.getKcmc();
|
||||||
|
}
|
||||||
|
cell.setKcjc(shortName);
|
||||||
|
String mainJybh = mainTeacherOfLesson.get(l.getBh());
|
||||||
|
if (mainJybh == null && course != null) {
|
||||||
|
mainJybh = course.getJybh();
|
||||||
|
}
|
||||||
|
cell.setJyxm(mainJybh == null ? null : teacherNames.getOrDefault(mainJybh, mainJybh));
|
||||||
|
cell.setJsmc(roomsOfLesson.getOrDefault(l.getBh(), List.of()).stream()
|
||||||
|
.map(b -> roomNames.getOrDefault(b, b)).distinct().collect(Collectors.joining("、")));
|
||||||
|
cell.setXydmc(teamsOfLesson.getOrDefault(l.getBh(), List.of()).stream()
|
||||||
|
.map(b -> teamNames.getOrDefault(b, b)).distinct().collect(Collectors.joining("、")));
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matchDimension(SSKCB l, String dim, String teamFilter, String teacherFilter, String roomFilter,
|
||||||
|
Map<String, List<String>> teamsOfLesson,
|
||||||
|
Map<String, List<String>> teachersOfLesson,
|
||||||
|
Map<String, List<String>> roomsOfLesson,
|
||||||
|
Map<String, SSKC> courses) {
|
||||||
|
switch (dim) {
|
||||||
|
case "semester":
|
||||||
|
case "team":
|
||||||
|
return teamsOfLesson.getOrDefault(l.getBh(), List.of()).contains(teamFilter);
|
||||||
|
case "teacher": {
|
||||||
|
if (teachersOfLesson.getOrDefault(l.getBh(), List.of()).contains(teacherFilter)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
SSKC c = courses.get(l.getSskcbh());
|
||||||
|
return c != null && (teacherFilter.equals(c.getJybh()) || teacherFilter.equals(c.getZrjybh()));
|
||||||
|
}
|
||||||
|
case "room":
|
||||||
|
return roomsOfLesson.getOrDefault(l.getBh(), List.of()).contains(roomFilter);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 历表一行 → 不可排格标记或备注栏条目。
|
||||||
|
* 备注栏规则:晚上/夜间时段或周末 且 备注显示 → remarks;可排课=0 → blockByCell。
|
||||||
|
*/
|
||||||
|
private void applyCalendarRow(String dateStr, String courseClass, boolean schedulable, boolean bzxs,
|
||||||
|
String text, LocalDate start, LocalDate end,
|
||||||
|
Map<Integer, String> periodSd,
|
||||||
|
Map<String, String> blockByCell,
|
||||||
|
List<TimetableGridVO.Remark> remarks, String scope) {
|
||||||
|
if (dateStr == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LocalDate day;
|
||||||
|
try {
|
||||||
|
day = LocalDate.parse(dateStr.substring(0, 10), DAY);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (day.isBefore(start) || day.isAfter(end)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean weekend = day.getDayOfWeek() == DayOfWeek.SATURDAY || day.getDayOfWeek() == DayOfWeek.SUNDAY;
|
||||||
|
List<Integer> jcs = PeriodUtil.parsePeriods(courseClass);
|
||||||
|
boolean evening = jcs.stream().anyMatch(jc -> {
|
||||||
|
String sd = periodSd.getOrDefault(jc, "");
|
||||||
|
return "晚上".equals(sd) || "夜间".equals(sd);
|
||||||
|
});
|
||||||
|
if (bzxs && (weekend || evening) && text != null && !text.isEmpty()) {
|
||||||
|
TimetableGridVO.Remark rm = new TimetableGridVO.Remark();
|
||||||
|
rm.setDate(day.format(DAY));
|
||||||
|
rm.setScope(scope);
|
||||||
|
rm.setText(text);
|
||||||
|
remarks.add(rm);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!schedulable) {
|
||||||
|
for (Integer jc : jcs) {
|
||||||
|
blockByCell.put(day.format(DAY) + "#" + jc, text == null ? "不可排课" : text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 节次行:节次时间表单节次行优先,空表回退 1-12;再套模板显示开关 */
|
||||||
|
private List<TimetableGridVO.Period> buildPeriods(KCBMBB tpl) {
|
||||||
|
List<TimetableGridVO.Period> periods = new ArrayList<>();
|
||||||
|
List<JCSJB> rows = classPeriodService.singleRows();
|
||||||
|
if (rows.isEmpty()) {
|
||||||
|
for (int jc = 1; jc <= 12; jc++) {
|
||||||
|
TimetableGridVO.Period p = new TimetableGridVO.Period();
|
||||||
|
p.setJc(jc);
|
||||||
|
p.setMc("第" + jc + "节");
|
||||||
|
p.setSd(defaultSd(jc));
|
||||||
|
periods.add(p);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (JCSJB row : rows) {
|
||||||
|
for (Integer jc : PeriodUtil.parsePeriods(row.getJcsy())) {
|
||||||
|
TimetableGridVO.Period p = new TimetableGridVO.Period();
|
||||||
|
p.setJc(jc);
|
||||||
|
p.setMc(row.getMc() != null ? row.getMc() : "第" + jc + "节");
|
||||||
|
p.setSd(row.getSd() != null ? row.getSd() : defaultSd(jc));
|
||||||
|
p.setKssj(trimTime(row.getKssj()));
|
||||||
|
p.setJssj(trimTime(row.getJssj()));
|
||||||
|
periods.add(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
periods.sort(Comparator.comparingInt(TimetableGridVO.Period::getJc));
|
||||||
|
}
|
||||||
|
if (tpl != null) {
|
||||||
|
periods = periods.stream().filter(p -> {
|
||||||
|
if (Boolean.FALSE.equals(tpl.getZXXS78J()) && (p.getJc() == 7 || p.getJc() == 8)) return false;
|
||||||
|
if (Boolean.FALSE.equals(tpl.getZXXSWS()) && "晚上".equals(p.getSd())) return false;
|
||||||
|
if (Boolean.FALSE.equals(tpl.getXSYJ()) && "夜间".equals(p.getSd())) return false;
|
||||||
|
return true;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
return periods;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String defaultSd(int jc) {
|
||||||
|
if (jc <= 4) return "上午";
|
||||||
|
if (jc <= 8) return "下午";
|
||||||
|
if (jc <= 10) return "晚上";
|
||||||
|
return "夜间";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String trimTime(String t) {
|
||||||
|
if (t == null) return null;
|
||||||
|
String s = t.trim();
|
||||||
|
return s.length() > 5 ? s.substring(0, 5) : s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String joinName(String mc, String bz) {
|
||||||
|
if (mc == null || mc.isEmpty()) return bz;
|
||||||
|
if (bz == null || bz.isEmpty()) return mc;
|
||||||
|
return mc + "(" + bz + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String intToDay(Integer yyyymmdd) {
|
||||||
|
String s = String.valueOf(yyyymmdd);
|
||||||
|
if (s.length() != 8) return null;
|
||||||
|
return s.substring(0, 4) + "-" + s.substring(4, 6) + "-" + s.substring(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LocalDate parseDay(String s) {
|
||||||
|
if (s == null || s.trim().isEmpty()) return null;
|
||||||
|
try {
|
||||||
|
return LocalDate.parse(s.trim().substring(0, 10), DAY);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveTeamName(String xydbh) {
|
||||||
|
if (xydbh == null) return "";
|
||||||
|
XYDB team = xydbMapper.selectOne(new LambdaQueryWrapper<XYDB>().eq(XYDB::getXydbh, xydbh));
|
||||||
|
return team != null && team.getXydmc() != null ? team.getXydmc() : xydbh;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package com.roomroot.jwgl.vo.kcb;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 周次横版课表网格。
|
||||||
|
* <p>weeks 按周纵向排列;每周内 行=节次、列=星期一~日;
|
||||||
|
* 晚上/夜间/周末且「备注显示」的历表事件进 remarks,不占格子。</p>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class TimetableGridVO {
|
||||||
|
|
||||||
|
/** 维度 */
|
||||||
|
private String dim;
|
||||||
|
|
||||||
|
/** 维度目标显示名(标题用) */
|
||||||
|
private String targetName;
|
||||||
|
|
||||||
|
/** 年度 */
|
||||||
|
private Integer nd;
|
||||||
|
|
||||||
|
private String startDate;
|
||||||
|
private String endDate;
|
||||||
|
|
||||||
|
/** 节次行定义(升序) */
|
||||||
|
private List<Period> periods = new ArrayList<>();
|
||||||
|
|
||||||
|
/** 周块 */
|
||||||
|
private List<Week> weeks = new ArrayList<>();
|
||||||
|
|
||||||
|
/** 备注栏条目(历表「备注显示」且落在晚上/夜间/周末的事件) */
|
||||||
|
private List<Remark> remarks = new ArrayList<>();
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Period {
|
||||||
|
/** 节次序号 */
|
||||||
|
private Integer jc;
|
||||||
|
/** 显示名(如 第1节 / 第1-2节) */
|
||||||
|
private String mc;
|
||||||
|
/** 时段:上午/下午/晚上/夜间 */
|
||||||
|
private String sd;
|
||||||
|
/** 开始时间 HH:mm */
|
||||||
|
private String kssj;
|
||||||
|
/** 结束时间 HH:mm */
|
||||||
|
private String jssj;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Week {
|
||||||
|
private Integer weekNo;
|
||||||
|
private String startDate;
|
||||||
|
private String endDate;
|
||||||
|
/** 固定 7 天(周一~周日) */
|
||||||
|
private List<Day> days = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Day {
|
||||||
|
private String date;
|
||||||
|
/** 1=周一 … 7=周日 */
|
||||||
|
private Integer weekday;
|
||||||
|
/** 校历/班历标记不可排课(该日所有节次或部分节次不可排时为 true) */
|
||||||
|
private Boolean blocked = false;
|
||||||
|
/** 不可排说明(如假期名称) */
|
||||||
|
private String blockReason;
|
||||||
|
/** key=节次序号字符串 */
|
||||||
|
private Map<String, List<Cell>> cells = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Cell {
|
||||||
|
private String sskcbbh;
|
||||||
|
private String sskcbh;
|
||||||
|
/** 课程名称 */
|
||||||
|
private String kcmc;
|
||||||
|
/** 课程简称(格子主显示) */
|
||||||
|
private String kcjc;
|
||||||
|
/** 主讲教员姓名 */
|
||||||
|
private String jyxm;
|
||||||
|
/** 场地名称 */
|
||||||
|
private String jsmc;
|
||||||
|
/** 班次/学员队名(教员、教室维度下展示) */
|
||||||
|
private String xydmc;
|
||||||
|
/** 课程类型(选修过滤用) */
|
||||||
|
private String kclx;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Remark {
|
||||||
|
private String date;
|
||||||
|
/** 来源:校历 / 班历 */
|
||||||
|
private String scope;
|
||||||
|
/** 备注文本(事件名 + 备注) */
|
||||||
|
private String text;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 课表模版管理(KCBMBController /kcbmb,手册一第12章)
|
||||||
|
|
||||||
|
/** 分页查询模版列表 GET /kcbmb/list(pageNum/pageSize/mbmc) */
|
||||||
|
export function listKcbmb(params) {
|
||||||
|
return request({
|
||||||
|
url: '/kcbmb/list',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 模版详情 GET /kcbmb/get?bh= */
|
||||||
|
export function getKcbmb(bh) {
|
||||||
|
return request({
|
||||||
|
url: '/kcbmb/get',
|
||||||
|
method: 'get',
|
||||||
|
params: { bh }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 可用模版下拉 GET /kcbmb/options → [{bh, mbmc}] */
|
||||||
|
export function listKcbmbOptions() {
|
||||||
|
return request({
|
||||||
|
url: '/kcbmb/options',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增模版 POST /kcbmb/add */
|
||||||
|
export function addKcbmb(data) {
|
||||||
|
return request({
|
||||||
|
url: '/kcbmb/add',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新模版 POST /kcbmb/update */
|
||||||
|
export function updateKcbmb(data) {
|
||||||
|
return request({
|
||||||
|
url: '/kcbmb/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除模版 POST /kcbmb/delete?bh= */
|
||||||
|
export function deleteKcbmb(bh) {
|
||||||
|
return request({
|
||||||
|
url: '/kcbmb/delete',
|
||||||
|
method: 'post',
|
||||||
|
params: { bh }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 启用/停用模版 POST /kcbmb/toggle?bh=&enabled= */
|
||||||
|
export function toggleKcbmb(bh, enabled) {
|
||||||
|
return request({
|
||||||
|
url: '/kcbmb/toggle',
|
||||||
|
method: 'post',
|
||||||
|
params: { bh, enabled }
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -87,3 +87,30 @@ export function exportClassCourses(data) {
|
|||||||
responseType: 'blob'
|
responseType: 'blob'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 周次横版课表网格(手册一第12章)
|
||||||
|
* GET /kcb/grid?dim=&id=&nd=&start=&end=&mbbh=
|
||||||
|
* dim: semester 班次学期 | team 学员队 | teacher 教员 | room 教室
|
||||||
|
* 返回 TimetableGridVO:periods / weeks[].days[].cells / remarks
|
||||||
|
*/
|
||||||
|
export function getTimetableGrid(params) {
|
||||||
|
return request({
|
||||||
|
url: '/kcb/grid',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网格课表矩阵 Excel 导出(每周一个 sheet)
|
||||||
|
* GET /kcb/grid/export(参数同 getTimetableGrid)
|
||||||
|
*/
|
||||||
|
export function exportTimetableGrid(params) {
|
||||||
|
return request({
|
||||||
|
url: '/kcb/grid/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,217 @@
|
|||||||
|
<template>
|
||||||
|
<div class="tg-view" v-loading="loading">
|
||||||
|
<template v-if="grid && grid.weeks && grid.weeks.length">
|
||||||
|
<div v-for="week in grid.weeks" :key="week.weekNo" class="tg-week">
|
||||||
|
<div class="tg-week-title">
|
||||||
|
第 {{ week.weekNo }} 周({{ week.startDate }} ~ {{ week.endDate }})
|
||||||
|
</div>
|
||||||
|
<table class="tg-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="tg-col-period">节次</th>
|
||||||
|
<th v-for="d in week.days" :key="d.date" :class="{ 'tg-blocked': d.blocked }">
|
||||||
|
<div>星期{{ weekdayName(d.weekday) }}</div>
|
||||||
|
<div class="tg-date">{{ d.date }}</div>
|
||||||
|
<div v-if="d.blocked && d.blockReason" class="tg-block-reason">{{ d.blockReason }}</div>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="p in grid.periods" :key="p.jc">
|
||||||
|
<td class="tg-period">
|
||||||
|
<div class="tg-period-name">{{ p.mc }}</div>
|
||||||
|
<div v-if="p.kssj" class="tg-period-time">{{ p.kssj }}-{{ p.jssj }}</div>
|
||||||
|
<div class="tg-period-sd">{{ p.sd }}</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
v-for="d in week.days"
|
||||||
|
:key="d.date"
|
||||||
|
class="tg-cell"
|
||||||
|
:class="{ 'tg-blocked': d.blocked }"
|
||||||
|
>
|
||||||
|
<div v-for="(cell, i) in cellsOf(d, p.jc)" :key="i" class="tg-lesson">
|
||||||
|
<div class="tg-course">{{ cell.kcjc || cell.kcmc }}</div>
|
||||||
|
<div v-if="showTeacher && cell.jyxm" class="tg-sub">{{ cell.jyxm }}</div>
|
||||||
|
<div v-if="showRoom && cell.jsmc" class="tg-sub">{{ cell.jsmc }}</div>
|
||||||
|
<div v-if="showTeam && cell.xydmc" class="tg-sub">{{ cell.xydmc }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="grid.remarks && grid.remarks.length" class="tg-remarks">
|
||||||
|
<div class="tg-remarks-title">备注栏</div>
|
||||||
|
<div v-for="(r, i) in grid.remarks" :key="i" class="tg-remark-row">
|
||||||
|
<span class="tg-remark-date">{{ r.date }}</span>
|
||||||
|
<span class="tg-remark-scope">[{{ r.scope }}]</span>
|
||||||
|
<span>{{ r.text }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-empty v-else-if="!loading" description="请选择维度与日期范围后生成课表" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const WEEKDAY_NAMES = ['', '一', '二', '三', '四', '五', '六', '日']
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 周次横版课表网格(手册一第12章)。
|
||||||
|
* 行=节次、列=星期一~日、纵向按周分块;备注栏事件不落格。
|
||||||
|
* 供「课表生成」页与后续角色化页面(教员端/学员端)复用。
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
name: 'TimetableGridView',
|
||||||
|
props: {
|
||||||
|
grid: { type: Object, default: null },
|
||||||
|
loading: { type: Boolean, default: false },
|
||||||
|
showTeacher: { type: Boolean, default: true },
|
||||||
|
showRoom: { type: Boolean, default: true },
|
||||||
|
showTeam: { type: Boolean, default: false }
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
weekdayName(wd) {
|
||||||
|
return WEEKDAY_NAMES[wd] || wd
|
||||||
|
},
|
||||||
|
cellsOf(day, jc) {
|
||||||
|
return (day.cells && day.cells[String(jc)]) || []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.tg-view {
|
||||||
|
.tg-week {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tg-week-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tg-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
table-layout: fixed;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
padding: 6px;
|
||||||
|
vertical-align: top;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background: #f5f7fa;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tg-col-period {
|
||||||
|
width: 96px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tg-date {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #909399;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tg-block-reason {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #f56c6c;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
th.tg-blocked {
|
||||||
|
background: repeating-linear-gradient(45deg, #f5f7fa, #f5f7fa 6px, #eceff2 6px, #eceff2 12px);
|
||||||
|
}
|
||||||
|
|
||||||
|
td.tg-blocked {
|
||||||
|
background: repeating-linear-gradient(45deg, #fafafa, #fafafa 6px, #f0f1f2 6px, #f0f1f2 12px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tg-period {
|
||||||
|
text-align: center;
|
||||||
|
background: #fafbfc;
|
||||||
|
|
||||||
|
.tg-period-name {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tg-period-time {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tg-period-sd {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #c0c4cc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tg-lesson {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
padding: 4px;
|
||||||
|
background: #eef6f2;
|
||||||
|
border-radius: 3px;
|
||||||
|
border-left: 3px solid var(--edu-color-primary, #0b6b55);
|
||||||
|
|
||||||
|
.tg-course {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tg-sub {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tg-remarks {
|
||||||
|
margin-top: 8px;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
.tg-remarks-title {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tg-remark-row {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #606266;
|
||||||
|
line-height: 1.8;
|
||||||
|
|
||||||
|
.tg-remark-date {
|
||||||
|
color: #909399;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tg-remark-scope {
|
||||||
|
color: #e6a23c;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
.tg-view {
|
||||||
|
.tg-week {
|
||||||
|
page-break-inside: avoid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container tg-page">
|
||||||
|
<div class="section-card no-print">
|
||||||
|
<div class="section-header">
|
||||||
|
<div>
|
||||||
|
<div class="section-title">课表生成</div>
|
||||||
|
<div class="section-desc">按 班次学期 / 学员队 / 教员 / 教室 生成周次横版课表,可套用课表模板、导出矩阵 Excel 或直接打印</div>
|
||||||
|
</div>
|
||||||
|
<div class="header-actions">
|
||||||
|
<el-button type="success" size="mini" icon="el-icon-download" :loading="exporting" :disabled="!grid" @click="handleExport">导出 Excel</el-button>
|
||||||
|
<el-button type="primary" size="mini" icon="el-icon-printer" :disabled="!grid" @click="handlePrint">打印</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-form inline class="filter-form" @submit.native.prevent>
|
||||||
|
<el-form-item label="维度">
|
||||||
|
<el-select v-model="dim" style="width: 130px" @change="handleDimChange">
|
||||||
|
<el-option label="班次学期" value="semester" />
|
||||||
|
<el-option label="学员队" value="team" />
|
||||||
|
<el-option label="教员" value="teacher" />
|
||||||
|
<el-option label="教室" value="room" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="dimLabel">
|
||||||
|
<el-select v-model="targetId" :placeholder="'请选择' + dimLabel" clearable filterable :loading="optionsLoading" style="width: 240px">
|
||||||
|
<el-option v-for="o in targetOptions" :key="o.value" :label="o.label" :value="o.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="日期范围">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="默认学期/本周"
|
||||||
|
end-placeholder="默认学期/本周"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
style="width: 250px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="课表模板">
|
||||||
|
<el-select v-model="mbbh" placeholder="默认样式" clearable style="width: 160px">
|
||||||
|
<el-option v-for="t in templateOptions" :key="t.bh" :label="t.mbmc" :value="t.bh" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" size="mini" icon="el-icon-search" :loading="loading" @click="handleGenerate">生成课表</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="格内显示">
|
||||||
|
<el-checkbox v-model="showTeacher">教员</el-checkbox>
|
||||||
|
<el-checkbox v-model="showRoom">场地</el-checkbox>
|
||||||
|
<el-checkbox v-model="showTeam">班次</el-checkbox>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section-card">
|
||||||
|
<div v-if="grid" class="grid-title">
|
||||||
|
{{ grid.targetName }}({{ grid.startDate }} ~ {{ grid.endDate }})
|
||||||
|
<el-tag v-if="mbbh" size="mini" type="warning" class="plan-tag">计划课表 · {{ templateName }}</el-tag>
|
||||||
|
<el-tag v-else size="mini" type="success" class="plan-tag">运行课表</el-tag>
|
||||||
|
</div>
|
||||||
|
<timetable-grid-view
|
||||||
|
:grid="grid"
|
||||||
|
:loading="loading"
|
||||||
|
:show-teacher="showTeacher"
|
||||||
|
:show-room="showRoom"
|
||||||
|
:show-team="showTeam"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import TimetableGridView from '@/components/TimetableGridView'
|
||||||
|
import { getTimetableGrid, exportTimetableGrid } from '@/api/teachBusiness/timetable'
|
||||||
|
import { listKcbmbOptions } from '@/api/teachBusiness/kcbmb'
|
||||||
|
import { allSemester } from '@/api/studentRecords/semester'
|
||||||
|
import { listTeam } from '@/api/studentRecords/team'
|
||||||
|
import { listTeacher } from '@/api/teachOffice/teacher'
|
||||||
|
import { listAllClassroom } from '@/api/teachBusiness/classroom'
|
||||||
|
import { saveAs } from 'file-saver'
|
||||||
|
|
||||||
|
const DIM_LABELS = { semester: '班次学期', team: '学员队', teacher: '教员', room: '教室' }
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TimetableGrid',
|
||||||
|
components: { TimetableGridView },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dim: 'semester',
|
||||||
|
targetId: undefined,
|
||||||
|
targetOptions: [],
|
||||||
|
optionsLoading: false,
|
||||||
|
dateRange: null,
|
||||||
|
mbbh: undefined,
|
||||||
|
templateOptions: [],
|
||||||
|
showTeacher: true,
|
||||||
|
showRoom: true,
|
||||||
|
showTeam: false,
|
||||||
|
loading: false,
|
||||||
|
exporting: false,
|
||||||
|
grid: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
dimLabel() {
|
||||||
|
return DIM_LABELS[this.dim] || '对象'
|
||||||
|
},
|
||||||
|
templateName() {
|
||||||
|
const t = this.templateOptions.find(o => o.bh === this.mbbh)
|
||||||
|
return t ? t.mbmc : ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.loadTemplates()
|
||||||
|
this.loadTargetOptions()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loadTemplates() {
|
||||||
|
listKcbmbOptions().then(response => {
|
||||||
|
this.templateOptions = response.data || []
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
|
handleDimChange() {
|
||||||
|
this.targetId = undefined
|
||||||
|
this.targetOptions = []
|
||||||
|
this.loadTargetOptions()
|
||||||
|
},
|
||||||
|
loadTargetOptions() {
|
||||||
|
this.optionsLoading = true
|
||||||
|
const done = list => {
|
||||||
|
this.targetOptions = list
|
||||||
|
this.optionsLoading = false
|
||||||
|
}
|
||||||
|
const fail = () => {
|
||||||
|
this.targetOptions = []
|
||||||
|
this.optionsLoading = false
|
||||||
|
}
|
||||||
|
if (this.dim === 'semester') {
|
||||||
|
allSemester().then(response => {
|
||||||
|
done((response.data || []).map(s => ({
|
||||||
|
value: s.bh,
|
||||||
|
label: `${s.xydbh}(${s.nd}年度 第${s.xqdc}学期)`
|
||||||
|
})))
|
||||||
|
}).catch(fail)
|
||||||
|
} else if (this.dim === 'team') {
|
||||||
|
listTeam({ pageNum: 1, pageSize: 500 }).then(response => {
|
||||||
|
const page = response.data || {}
|
||||||
|
done((page.records || []).map(t => ({ value: t.xydbh, label: t.xydmc || t.xydbh })))
|
||||||
|
}).catch(fail)
|
||||||
|
} else if (this.dim === 'teacher') {
|
||||||
|
listTeacher({ pageNum: 1, pageSize: 500 }).then(response => {
|
||||||
|
const page = response.data || {}
|
||||||
|
done((page.records || []).map(t => ({ value: t.jybh, label: (t.jyxm || '-') + '(' + t.jybh + ')' })))
|
||||||
|
}).catch(fail)
|
||||||
|
} else {
|
||||||
|
listAllClassroom().then(response => {
|
||||||
|
done((response.data || []).map(r => ({ value: r.jsbh, label: (r.jsmc || '-') + '(' + r.jsbh + ')' })))
|
||||||
|
}).catch(fail)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
buildQuery() {
|
||||||
|
const q = { dim: this.dim, id: this.targetId, mbbh: this.mbbh }
|
||||||
|
if (this.dateRange && this.dateRange.length === 2) {
|
||||||
|
q.start = this.dateRange[0]
|
||||||
|
q.end = this.dateRange[1]
|
||||||
|
}
|
||||||
|
return q
|
||||||
|
},
|
||||||
|
handleGenerate() {
|
||||||
|
if (!this.targetId) {
|
||||||
|
this.$message.warning('请选择' + this.dimLabel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
getTimetableGrid(this.buildQuery()).then(response => {
|
||||||
|
this.grid = response.data
|
||||||
|
}).catch(() => {
|
||||||
|
this.grid = null
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleExport() {
|
||||||
|
this.exporting = true
|
||||||
|
exportTimetableGrid(this.buildQuery()).then(blob => {
|
||||||
|
saveAs(blob, `课表_${this.grid && this.grid.targetName ? this.grid.targetName : this.dim}.xlsx`)
|
||||||
|
}).catch(() => {
|
||||||
|
this.$message.error('导出失败')
|
||||||
|
}).finally(() => {
|
||||||
|
this.exporting = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handlePrint() {
|
||||||
|
window.print()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.tg-page {
|
||||||
|
.section-card {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-desc {
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-form ::v-deep .el-form-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #303133;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
|
||||||
|
.plan-tag {
|
||||||
|
margin-left: 10px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
.tg-page .no-print {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container tpl-page">
|
||||||
|
<div class="section-card">
|
||||||
|
<div class="section-header">
|
||||||
|
<div>
|
||||||
|
<div class="section-title">课表模板管理</div>
|
||||||
|
<div class="section-desc">维护课表输出样式模板;「课表生成」页可选择模板套用其显示开关(78节 / 晚上 / 夜间 / 选修 / 显示天数)</div>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" size="mini" icon="el-icon-plus" @click="handleAdd">新增模板</el-button>
|
||||||
|
</div>
|
||||||
|
<el-form inline class="filter-form" @submit.native.prevent>
|
||||||
|
<el-form-item label="模板名称">
|
||||||
|
<el-input v-model="queryMbmc" placeholder="按名称模糊" clearable style="width: 200px" @keyup.enter.native="handleQuery" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" size="mini" icon="el-icon-search" @click="handleQuery">查询</el-button>
|
||||||
|
<el-button size="mini" icon="el-icon-refresh" @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section-card">
|
||||||
|
<el-table v-loading="loading" :data="tableData" border stripe size="mini">
|
||||||
|
<el-table-column type="index" label="序号" width="55" align="center" />
|
||||||
|
<el-table-column prop="mbmc" label="模板名称" min-width="160" show-overflow-tooltip />
|
||||||
|
<el-table-column label="78节" width="70" align="center">
|
||||||
|
<template slot-scope="scope">{{ boolText(scope.row.zxxs78j) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="晚上" width="70" align="center">
|
||||||
|
<template slot-scope="scope">{{ boolText(scope.row.zxxsws) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="夜间" width="70" align="center">
|
||||||
|
<template slot-scope="scope">{{ boolText(scope.row.xsyj) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="含选修" width="70" align="center">
|
||||||
|
<template slot-scope="scope">{{ boolText(scope.row.kbbhxx) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="zxxsts" label="显示天数" width="80" align="center" />
|
||||||
|
<el-table-column prop="wkzwzf" label="无课站位" width="90" align="center" />
|
||||||
|
<el-table-column label="可用" width="80" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-switch
|
||||||
|
:value="isTrue(scope.row.mbky)"
|
||||||
|
active-text=""
|
||||||
|
@change="v => handleToggle(scope.row, v)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="gxsj" label="更新时间" width="150" align="center">
|
||||||
|
<template slot-scope="scope">{{ fmt(scope.row.gxsj) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="bz" label="备注" min-width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column label="操作" width="130" align="center" fixed="right">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" size="mini" icon="el-icon-edit" @click="handleEdit(scope.row)">编辑</el-button>
|
||||||
|
<el-button type="text" size="mini" icon="el-icon-delete" class="danger-text" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<el-pagination
|
||||||
|
v-if="total > 0"
|
||||||
|
class="pagination"
|
||||||
|
background
|
||||||
|
layout="total, sizes, prev, pager, next"
|
||||||
|
:current-page="pageNum"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 20, 50]"
|
||||||
|
:total="total"
|
||||||
|
@current-change="handlePageChange"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 新增/编辑弹窗 -->
|
||||||
|
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="620px" append-to-body>
|
||||||
|
<el-form ref="form" :model="form" :rules="rules" label-width="130px">
|
||||||
|
<el-form-item label="模板名称" prop="mbmc">
|
||||||
|
<el-input v-model="form.mbmc" placeholder="如:标准周课表" maxlength="50" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="显示 7-8 节">
|
||||||
|
<el-switch v-model="form.zxxs78j" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="显示晚上时段">
|
||||||
|
<el-switch v-model="form.zxxsws" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="显示夜间时段">
|
||||||
|
<el-switch v-model="form.xsyj" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="包含选修课">
|
||||||
|
<el-switch v-model="form.kbbhxx" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="一周显示天数">
|
||||||
|
<el-input-number v-model="form.zxxsts" :min="1" :max="7" />
|
||||||
|
<span class="form-tip">5 = 只显示工作日</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="无课站位字符">
|
||||||
|
<el-input v-model="form.wkzwzf" placeholder="留空则空格,如:—" maxlength="10" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注">
|
||||||
|
<el-input v-model="form.bz" type="textarea" :rows="2" maxlength="200" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer">
|
||||||
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" :loading="saving" @click="handleSave">确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listKcbmb, addKcbmb, updateKcbmb, deleteKcbmb, toggleKcbmb } from '@/api/teachBusiness/kcbmb'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TimetableTemplate',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
saving: false,
|
||||||
|
queryMbmc: '',
|
||||||
|
tableData: [],
|
||||||
|
total: 0,
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
dialogVisible: false,
|
||||||
|
dialogTitle: '新增模板',
|
||||||
|
form: this.emptyForm(),
|
||||||
|
rules: {
|
||||||
|
mbmc: [{ required: true, message: '请输入模板名称', trigger: 'blur' }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.loadList()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
emptyForm() {
|
||||||
|
return {
|
||||||
|
bh: undefined,
|
||||||
|
mbmc: '',
|
||||||
|
zxxs78j: true,
|
||||||
|
zxxsws: true,
|
||||||
|
xsyj: false,
|
||||||
|
kbbhxx: true,
|
||||||
|
zxxsts: 7,
|
||||||
|
wkzwzf: '',
|
||||||
|
bz: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isTrue(v) {
|
||||||
|
return v === true || v === 1 || v === '1' || v === 'true'
|
||||||
|
},
|
||||||
|
boolText(v) {
|
||||||
|
return this.isTrue(v) ? '显示' : '隐藏'
|
||||||
|
},
|
||||||
|
fmt(t) {
|
||||||
|
return (t || '').replace('T', ' ').substring(0, 16)
|
||||||
|
},
|
||||||
|
loadList() {
|
||||||
|
this.loading = true
|
||||||
|
listKcbmb({ pageNum: this.pageNum, pageSize: this.pageSize, mbmc: this.queryMbmc }).then(response => {
|
||||||
|
const page = response.data || {}
|
||||||
|
this.tableData = page.records || []
|
||||||
|
this.total = page.total || 0
|
||||||
|
}).catch(() => {
|
||||||
|
this.tableData = []
|
||||||
|
this.total = 0
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.pageNum = 1
|
||||||
|
this.loadList()
|
||||||
|
},
|
||||||
|
handleReset() {
|
||||||
|
this.queryMbmc = ''
|
||||||
|
this.pageNum = 1
|
||||||
|
this.loadList()
|
||||||
|
},
|
||||||
|
handlePageChange(p) {
|
||||||
|
this.pageNum = p
|
||||||
|
this.loadList()
|
||||||
|
},
|
||||||
|
handleSizeChange(s) {
|
||||||
|
this.pageSize = s
|
||||||
|
this.pageNum = 1
|
||||||
|
this.loadList()
|
||||||
|
},
|
||||||
|
handleAdd() {
|
||||||
|
this.dialogTitle = '新增模板'
|
||||||
|
this.form = this.emptyForm()
|
||||||
|
this.dialogVisible = true
|
||||||
|
},
|
||||||
|
handleEdit(row) {
|
||||||
|
this.dialogTitle = '编辑模板'
|
||||||
|
this.form = {
|
||||||
|
bh: row.bh,
|
||||||
|
mbmc: row.mbmc,
|
||||||
|
zxxs78j: this.isTrue(row.zxxs78j),
|
||||||
|
zxxsws: this.isTrue(row.zxxsws),
|
||||||
|
xsyj: this.isTrue(row.xsyj),
|
||||||
|
kbbhxx: this.isTrue(row.kbbhxx),
|
||||||
|
zxxsts: row.zxxsts || 7,
|
||||||
|
wkzwzf: row.wkzwzf || '',
|
||||||
|
bz: row.bz || ''
|
||||||
|
}
|
||||||
|
this.dialogVisible = true
|
||||||
|
},
|
||||||
|
handleSave() {
|
||||||
|
this.$refs.form.validate(valid => {
|
||||||
|
if (!valid) return
|
||||||
|
this.saving = true
|
||||||
|
const fn = this.form.bh ? updateKcbmb : addKcbmb
|
||||||
|
fn(this.form).then(() => {
|
||||||
|
this.$message.success('保存成功')
|
||||||
|
this.dialogVisible = false
|
||||||
|
this.loadList()
|
||||||
|
}).finally(() => {
|
||||||
|
this.saving = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleToggle(row, enabled) {
|
||||||
|
toggleKcbmb(row.bh, enabled).then(() => {
|
||||||
|
row.mbky = enabled
|
||||||
|
this.$message.success(enabled ? '已启用' : '已停用')
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
|
handleDelete(row) {
|
||||||
|
this.$confirm(`确认删除模板「${row.mbmc}」?`, '提示', { type: 'warning' }).then(() => {
|
||||||
|
return deleteKcbmb(row.bh)
|
||||||
|
}).then(() => {
|
||||||
|
this.$message.success('已删除')
|
||||||
|
this.loadList()
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.tpl-page {
|
||||||
|
.section-card {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-desc {
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-form ::v-deep .el-form-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-tip {
|
||||||
|
margin-left: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.danger-text {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
margin-top: 12px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user