代码生成:完善 vue2 代码生成单元测试

This commit is contained in:
puhui999 2023-11-21 16:22:16 +08:00
parent c50d27f5cb
commit 3d2022e31f
107 changed files with 8625 additions and 0 deletions

View File

@ -0,0 +1,73 @@
[ {
"contentPath" : "java/InfraStudentPageReqVO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java"
}, {
"contentPath" : "java/InfraStudentRespVO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java"
}, {
"contentPath" : "java/InfraStudentSaveReqVO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java"
}, {
"contentPath" : "java/InfraStudentController",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java"
}, {
"contentPath" : "java/InfraStudentDO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java"
}, {
"contentPath" : "java/InfraStudentContactDO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java"
}, {
"contentPath" : "java/InfraStudentTeacherDO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java"
}, {
"contentPath" : "java/InfraStudentMapper",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java"
}, {
"contentPath" : "java/InfraStudentContactMapper",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java"
}, {
"contentPath" : "java/InfraStudentTeacherMapper",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java"
}, {
"contentPath" : "xml/InfraStudentMapper",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml"
}, {
"contentPath" : "java/InfraStudentServiceImpl",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java"
}, {
"contentPath" : "java/InfraStudentService",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java"
}, {
"contentPath" : "java/InfraStudentServiceImplTest",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java"
}, {
"contentPath" : "java/ErrorCodeConstants_手动操作",
"filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
}, {
"contentPath" : "sql/sql",
"filePath" : "sql/sql.sql"
}, {
"contentPath" : "sql/h2",
"filePath" : "sql/h2.sql"
}, {
"contentPath" : "vue/index",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue"
}, {
"contentPath" : "js/student",
"filePath" : "yudao-ui-admin-vue2/src/api/infra/student.js"
}, {
"contentPath" : "vue/StudentForm",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue"
}, {
"contentPath" : "vue/StudentContactForm",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue"
}, {
"contentPath" : "vue/StudentTeacherForm",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue"
}, {
"contentPath" : "vue/StudentContactList",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactList.vue"
}, {
"contentPath" : "vue/StudentTeacherList",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherList.vue"
} ]

View File

@ -0,0 +1,6 @@
// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意请给“TODO 补充编号”设置一个错误码编号!!!
// ========== 学生 TODO 补充编号 ==========
ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在");
ErrorCode STUDENT_CONTACT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生联系人不存在");
ErrorCode STUDENT_TEACHER_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任不存在");
ErrorCode STUDENT_TEACHER_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任已存在");

View File

@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 学生联系人 DO
*
* @author 芋道源码
*/
@TableName("infra_student_contact")
@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class InfraStudentContactDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 学生编号
*/
private Long studentId;
/**
* 名字
*/
private String name;
/**
* 简介
*/
private String description;
/**
* 出生日期
*/
private LocalDateTime birthday;
/**
* 性别
*
* 枚举 {@link TODO system_user_sex 对应的类}
*/
private Integer sex;
/**
* 是否有效
*
* 枚举 {@link TODO infra_boolean_string 对应的类}
*/
private Boolean enabled;
/**
* 头像
*/
private String avatar;
/**
* 附件
*/
private String video;
/**
* 备注
*/
private String memo;
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 学生联系人 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> {
default PageResult<InfraStudentContactDO> selectPage(PageParam reqVO, Long studentId) {
return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentContactDO>()
.eq(InfraStudentContactDO::getStudentId, studentId)
.orderByDesc(InfraStudentContactDO::getId));
}
default int deleteByStudentId(Long studentId) {
return delete(InfraStudentContactDO::getStudentId, studentId);
}
}

View File

@ -0,0 +1,183 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;
@Tag(name = "管理后台 - 学生")
@RestController
@RequestMapping("/infra/student")
@Validated
public class InfraStudentController {
@Resource
private InfraStudentService studentService;
@PostMapping("/create")
@Operation(summary = "创建学生")
@PreAuthorize("@ss.hasPermission('infra:student:create')")
public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {
return success(studentService.createStudent(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新学生")
@PreAuthorize("@ss.hasPermission('infra:student:update')")
public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {
studentService.updateStudent(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除学生")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:student:delete')")
public CommonResult<Boolean> deleteStudent(@RequestParam("id") Long id) {
studentService.deleteStudent(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得学生")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:student:query')")
public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) {
InfraStudentDO student = studentService.getStudent(id);
return success(BeanUtils.toBean(student, InfraStudentRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得学生分页")
@PreAuthorize("@ss.hasPermission('infra:student:query')")
public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出学生 Excel")
@PreAuthorize("@ss.hasPermission('infra:student:export')")
@OperateLog(type = EXPORT)
public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class,
BeanUtils.toBean(list, InfraStudentRespVO.class));
}
// ==================== 子表(学生联系人) ====================
@GetMapping("/student-contact/page")
@Operation(summary = "获得学生联系人分页")
@Parameter(name = "studentId", description = "学生编号")
@PreAuthorize("@ss.hasPermission('infra:student:query')")
public CommonResult<PageResult<InfraStudentContactDO>> getStudentContactPage(PageParam pageReqVO,
@RequestParam("studentId") Long studentId) {
return success(studentService.getStudentContactPage(pageReqVO, studentId));
}
@PostMapping("/student-contact/create")
@Operation(summary = "创建学生联系人")
@PreAuthorize("@ss.hasPermission('infra:student:create')")
public CommonResult<Long> createStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) {
return success(studentService.createStudentContact(studentContact));
}
@PutMapping("/student-contact/update")
@Operation(summary = "更新学生联系人")
@PreAuthorize("@ss.hasPermission('infra:student:update')")
public CommonResult<Boolean> updateStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) {
studentService.updateStudentContact(studentContact);
return success(true);
}
@DeleteMapping("/student-contact/delete")
@Parameter(name = "id", description = "编号", required = true)
@Operation(summary = "删除学生联系人")
@PreAuthorize("@ss.hasPermission('infra:student:delete')")
public CommonResult<Boolean> deleteStudentContact(@RequestParam("id") Long id) {
studentService.deleteStudentContact(id);
return success(true);
}
@GetMapping("/student-contact/get")
@Operation(summary = "获得学生联系人")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:student:query')")
public CommonResult<InfraStudentContactDO> getStudentContact(@RequestParam("id") Long id) {
return success(studentService.getStudentContact(id));
}
// ==================== 子表(学生班主任) ====================
@GetMapping("/student-teacher/page")
@Operation(summary = "获得学生班主任分页")
@Parameter(name = "studentId", description = "学生编号")
@PreAuthorize("@ss.hasPermission('infra:student:query')")
public CommonResult<PageResult<InfraStudentTeacherDO>> getStudentTeacherPage(PageParam pageReqVO,
@RequestParam("studentId") Long studentId) {
return success(studentService.getStudentTeacherPage(pageReqVO, studentId));
}
@PostMapping("/student-teacher/create")
@Operation(summary = "创建学生班主任")
@PreAuthorize("@ss.hasPermission('infra:student:create')")
public CommonResult<Long> createStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) {
return success(studentService.createStudentTeacher(studentTeacher));
}
@PutMapping("/student-teacher/update")
@Operation(summary = "更新学生班主任")
@PreAuthorize("@ss.hasPermission('infra:student:update')")
public CommonResult<Boolean> updateStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) {
studentService.updateStudentTeacher(studentTeacher);
return success(true);
}
@DeleteMapping("/student-teacher/delete")
@Parameter(name = "id", description = "编号", required = true)
@Operation(summary = "删除学生班主任")
@PreAuthorize("@ss.hasPermission('infra:student:delete')")
public CommonResult<Boolean> deleteStudentTeacher(@RequestParam("id") Long id) {
studentService.deleteStudentTeacher(id);
return success(true);
}
@GetMapping("/student-teacher/get")
@Operation(summary = "获得学生班主任")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:student:query')")
public CommonResult<InfraStudentTeacherDO> getStudentTeacher(@RequestParam("id") Long id) {
return success(studentService.getStudentTeacher(id));
}
}

View File

@ -0,0 +1,67 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 学生 DO
*
* @author 芋道源码
*/
@TableName("infra_student")
@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class InfraStudentDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 名字
*/
private String name;
/**
* 简介
*/
private String description;
/**
* 出生日期
*/
private LocalDateTime birthday;
/**
* 性别
*
* 枚举 {@link TODO system_user_sex 对应的类}
*/
private Integer sex;
/**
* 是否有效
*
* 枚举 {@link TODO infra_boolean_string 对应的类}
*/
private Boolean enabled;
/**
* 头像
*/
private String avatar;
/**
* 附件
*/
private String video;
/**
* 备注
*/
private String memo;
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
/**
* 学生 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {
default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()
.likeIfPresent(InfraStudentDO::getName, reqVO.getName())
.eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())
.eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())
.eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())
.betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(InfraStudentDO::getId));
}
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 学生分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class InfraStudentPageReqVO extends PageParam {
@Schema(description = "名字", example = "芋头")
private String name;
@Schema(description = "出生日期")
private LocalDateTime birthday;
@Schema(description = "性别", example = "1")
private Integer sex;
@Schema(description = "是否有效", example = "true")
private Boolean enabled;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,60 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
@Schema(description = "管理后台 - 学生 Response VO")
@Data
@ExcelIgnoreUnannotated
public class InfraStudentRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@ExcelProperty("编号")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
@ExcelProperty("名字")
private String name;
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
@ExcelProperty("简介")
private String description;
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("出生日期")
private LocalDateTime birthday;
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty(value = "性别", converter = DictConvert.class)
@DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private Integer sex;
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@ExcelProperty(value = "是否有效", converter = DictConvert.class)
@DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private Boolean enabled;
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
@ExcelProperty("头像")
private String avatar;
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
@ExcelProperty("附件")
private String video;
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
@ExcelProperty("备注")
private String memo;
@Schema(description = "创建时间")
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
@Schema(description = "管理后台 - 学生新增/修改 Request VO")
@Data
public class InfraStudentSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
@NotEmpty(message = "名字不能为空")
private String name;
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
@NotEmpty(message = "简介不能为空")
private String description;
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "出生日期不能为空")
private LocalDateTime birthday;
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "性别不能为空")
private Integer sex;
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否有效不能为空")
private Boolean enabled;
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
@NotEmpty(message = "头像不能为空")
private String avatar;
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
@NotEmpty(message = "附件不能为空")
private String video;
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
@NotEmpty(message = "备注不能为空")
private String memo;
}

View File

@ -0,0 +1,139 @@
package cn.iocoder.yudao.module.infra.service.demo;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
/**
* 学生 Service 接口
*
* @author 芋道源码
*/
public interface InfraStudentService {
/**
* 创建学生
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);
/**
* 更新学生
*
* @param updateReqVO 更新信息
*/
void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);
/**
* 删除学生
*
* @param id 编号
*/
void deleteStudent(Long id);
/**
* 获得学生
*
* @param id 编号
* @return 学生
*/
InfraStudentDO getStudent(Long id);
/**
* 获得学生分页
*
* @param pageReqVO 分页查询
* @return 学生分页
*/
PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);
// ==================== 子表(学生联系人) ====================
/**
* 获得学生联系人分页
*
* @param pageReqVO 分页查询
* @param studentId 学生编号
* @return 学生联系人分页
*/
PageResult<InfraStudentContactDO> getStudentContactPage(PageParam pageReqVO, Long studentId);
/**
* 创建学生联系人
*
* @param studentContact 创建信息
* @return 编号
*/
Long createStudentContact(@Valid InfraStudentContactDO studentContact);
/**
* 更新学生联系人
*
* @param studentContact 更新信息
*/
void updateStudentContact(@Valid InfraStudentContactDO studentContact);
/**
* 删除学生联系人
*
* @param id 编号
*/
void deleteStudentContact(Long id);
/**
* 获得学生联系人
*
* @param id 编号
* @return 学生联系人
*/
InfraStudentContactDO getStudentContact(Long id);
// ==================== 子表(学生班主任) ====================
/**
* 获得学生班主任分页
*
* @param pageReqVO 分页查询
* @param studentId 学生编号
* @return 学生班主任分页
*/
PageResult<InfraStudentTeacherDO> getStudentTeacherPage(PageParam pageReqVO, Long studentId);
/**
* 创建学生班主任
*
* @param studentTeacher 创建信息
* @return 编号
*/
Long createStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher);
/**
* 更新学生班主任
*
* @param studentTeacher 更新信息
*/
void updateStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher);
/**
* 删除学生班主任
*
* @param id 编号
*/
void deleteStudentTeacher(Long id);
/**
* 获得学生班主任
*
* @param id 编号
* @return 学生班主任
*/
InfraStudentTeacherDO getStudentTeacher(Long id);
}

View File

@ -0,0 +1,180 @@
package cn.iocoder.yudao.module.infra.service.demo;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
/**
* 学生 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class InfraStudentServiceImpl implements InfraStudentService {
@Resource
private InfraStudentMapper studentMapper;
@Resource
private InfraStudentContactMapper studentContactMapper;
@Resource
private InfraStudentTeacherMapper studentTeacherMapper;
@Override
public Long createStudent(InfraStudentSaveReqVO createReqVO) {
// 插入
InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);
studentMapper.insert(student);
// 返回
return student.getId();
}
@Override
public void updateStudent(InfraStudentSaveReqVO updateReqVO) {
// 校验存在
validateStudentExists(updateReqVO.getId());
// 更新
InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);
studentMapper.updateById(updateObj);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteStudent(Long id) {
// 校验存在
validateStudentExists(id);
// 删除
studentMapper.deleteById(id);
// 删除子表
deleteStudentContactByStudentId(id);
deleteStudentTeacherByStudentId(id);
}
private void validateStudentExists(Long id) {
if (studentMapper.selectById(id) == null) {
throw exception(STUDENT_NOT_EXISTS);
}
}
@Override
public InfraStudentDO getStudent(Long id) {
return studentMapper.selectById(id);
}
@Override
public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {
return studentMapper.selectPage(pageReqVO);
}
// ==================== 子表(学生联系人) ====================
@Override
public PageResult<InfraStudentContactDO> getStudentContactPage(PageParam pageReqVO, Long studentId) {
return studentContactMapper.selectPage(pageReqVO, studentId);
}
@Override
public Long createStudentContact(InfraStudentContactDO studentContact) {
studentContactMapper.insert(studentContact);
return studentContact.getId();
}
@Override
public void updateStudentContact(InfraStudentContactDO studentContact) {
// 校验存在
validateStudentContactExists(studentContact.getId());
// 更新
studentContactMapper.updateById(studentContact);
}
@Override
public void deleteStudentContact(Long id) {
// 校验存在
validateStudentContactExists(id);
// 删除
studentContactMapper.deleteById(id);
}
@Override
public InfraStudentContactDO getStudentContact(Long id) {
return studentContactMapper.selectById(id);
}
private void validateStudentContactExists(Long id) {
if (studentContactMapper.selectById(id) == null) {
throw exception(STUDENT_CONTACT_NOT_EXISTS);
}
}
private void deleteStudentContactByStudentId(Long studentId) {
studentContactMapper.deleteByStudentId(studentId);
}
// ==================== 子表(学生班主任) ====================
@Override
public PageResult<InfraStudentTeacherDO> getStudentTeacherPage(PageParam pageReqVO, Long studentId) {
return studentTeacherMapper.selectPage(pageReqVO, studentId);
}
@Override
public Long createStudentTeacher(InfraStudentTeacherDO studentTeacher) {
// 校验是否已经存在
if (studentTeacherMapper.selectByStudentId(studentTeacher.getStudentId()) != null) {
throw exception(STUDENT_TEACHER_EXISTS);
}
// 插入
studentTeacherMapper.insert(studentTeacher);
return studentTeacher.getId();
}
@Override
public void updateStudentTeacher(InfraStudentTeacherDO studentTeacher) {
// 校验存在
validateStudentTeacherExists(studentTeacher.getId());
// 更新
studentTeacherMapper.updateById(studentTeacher);
}
@Override
public void deleteStudentTeacher(Long id) {
// 校验存在
validateStudentTeacherExists(id);
// 删除
studentTeacherMapper.deleteById(id);
}
@Override
public InfraStudentTeacherDO getStudentTeacher(Long id) {
return studentTeacherMapper.selectById(id);
}
private void validateStudentTeacherExists(Long id) {
if (studentTeacherMapper.selectById(id) == null) {
throw exception(STUDENT_TEACHER_NOT_EXISTS);
}
}
private void deleteStudentTeacherByStudentId(Long studentId) {
studentTeacherMapper.deleteByStudentId(studentId);
}
}

View File

@ -0,0 +1,146 @@
package cn.iocoder.yudao.module.infra.service.demo;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import javax.annotation.Resource;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import javax.annotation.Resource;
import org.springframework.context.annotation.Import;
import java.util.*;
import java.time.LocalDateTime;
import static cn.hutool.core.util.RandomUtil.*;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* {@link InfraStudentServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(InfraStudentServiceImpl.class)
public class InfraStudentServiceImplTest extends BaseDbUnitTest {
@Resource
private InfraStudentServiceImpl studentService;
@Resource
private InfraStudentMapper studentMapper;
@Test
public void testCreateStudent_success() {
// 准备参数
InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);
// 调用
Long studentId = studentService.createStudent(createReqVO);
// 断言
assertNotNull(studentId);
// 校验记录的属性是否正确
InfraStudentDO student = studentMapper.selectById(studentId);
assertPojoEquals(createReqVO, student, "id");
}
@Test
public void testUpdateStudent_success() {
// mock 数据
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
// 准备参数
InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {
o.setId(dbStudent.getId()); // 设置更新的 ID
});
// 调用
studentService.updateStudent(updateReqVO);
// 校验是否更新正确
InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的
assertPojoEquals(updateReqVO, student);
}
@Test
public void testUpdateStudent_notExists() {
// 准备参数
InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);
}
@Test
public void testDeleteStudent_success() {
// mock 数据
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbStudent.getId();
// 调用
studentService.deleteStudent(id);
// 校验数据不存在了
assertNull(studentMapper.selectById(id));
}
@Test
public void testDeleteStudent_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);
}
@Test
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
public void testGetStudentPage() {
// mock 数据
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到
o.setName(null);
o.setBirthday(null);
o.setSex(null);
o.setEnabled(null);
o.setCreateTime(null);
});
studentMapper.insert(dbStudent);
// 测试 name 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));
// 测试 birthday 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));
// 测试 sex 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));
// 测试 enabled 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));
// 测试 createTime 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));
// 准备参数
InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();
reqVO.setName(null);
reqVO.setBirthday(null);
reqVO.setSex(null);
reqVO.setEnabled(null);
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
// 调用
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbStudent, pageResult.getList().get(0));
}
}

View File

@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 学生班主任 DO
*
* @author 芋道源码
*/
@TableName("infra_student_teacher")
@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class InfraStudentTeacherDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 学生编号
*/
private Long studentId;
/**
* 名字
*/
private String name;
/**
* 简介
*/
private String description;
/**
* 出生日期
*/
private LocalDateTime birthday;
/**
* 性别
*
* 枚举 {@link TODO system_user_sex 对应的类}
*/
private Integer sex;
/**
* 是否有效
*
* 枚举 {@link TODO infra_boolean_string 对应的类}
*/
private Boolean enabled;
/**
* 头像
*/
private String avatar;
/**
* 附件
*/
private String video;
/**
* 备注
*/
private String memo;
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 学生班主任 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface InfraStudentTeacherMapper extends BaseMapperX<InfraStudentTeacherDO> {
default PageResult<InfraStudentTeacherDO> selectPage(PageParam reqVO, Long studentId) {
return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentTeacherDO>()
.eq(InfraStudentTeacherDO::getStudentId, studentId)
.orderByDesc(InfraStudentTeacherDO::getId));
}
default int deleteByStudentId(Long studentId) {
return delete(InfraStudentTeacherDO::getStudentId, studentId);
}
}

View File

@ -0,0 +1,141 @@
import request from '@/utils/request'
// 创建学生
export function createStudent(data) {
return request({
url: '/infra/student/create',
method: 'post',
data: data
})
}
// 更新学生
export function updateStudent(data) {
return request({
url: '/infra/student/update',
method: 'put',
data: data
})
}
// 删除学生
export function deleteStudent(id) {
return request({
url: '/infra/student/delete?id=' + id,
method: 'delete'
})
}
// 获得学生
export function getStudent(id) {
return request({
url: '/infra/student/get?id=' + id,
method: 'get'
})
}
// 获得学生分页
export function getStudentPage(params) {
return request({
url: '/infra/student/page',
method: 'get',
params
})
}
// 导出学生 Excel
export function exportStudentExcel(params) {
return request({
url: '/infra/student/export-excel',
method: 'get',
params,
responseType: 'blob'
})
}
// ==================== 子表(学生联系人) ====================
// 获得学生联系人分页
export function getStudentContactPage(params) {
return request({
url: '/infra/student/student-contact/page',
method: 'get',
params
})
}
// 新增学生联系人
export function createStudentContact(data) {
return request({
url: `/infra/student/student-contact/create`,
method: 'post',
data
})
}
// 修改学生联系人
export function updateStudentContact(data) {
return request({
url: `/infra/student/student-contact/update`,
method: 'post',
data
})
}
// 删除学生联系人
export function deleteStudentContact(id) {
return request({
url: `/infra/student/student-contact/delete?id=` + id,
method: 'delete'
})
}
// 获得学生联系人
export function getStudentContact(id) {
return request({
url: `/infra/student/student-contact/get?id=` + id,
method: 'get'
})
}
// ==================== 子表(学生班主任) ====================
// 获得学生班主任分页
export function getStudentTeacherPage(params) {
return request({
url: '/infra/student/student-teacher/page',
method: 'get',
params
})
}
// 新增学生班主任
export function createStudentTeacher(data) {
return request({
url: `/infra/student/student-teacher/create`,
method: 'post',
data
})
}
// 修改学生班主任
export function updateStudentTeacher(data) {
return request({
url: `/infra/student/student-teacher/update`,
method: 'post',
data
})
}
// 删除学生班主任
export function deleteStudentTeacher(id) {
return request({
url: `/infra/student/student-teacher/delete?id=` + id,
method: 'delete'
})
}
// 获得学生班主任
export function getStudentTeacher(id) {
return request({
url: `/infra/student/student-teacher/get?id=` + id,
method: 'get'
})
}

View File

@ -0,0 +1,17 @@
-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
CREATE TABLE IF NOT EXISTS "infra_student" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar NOT NULL,
"description" varchar NOT NULL,
"birthday" varchar NOT NULL,
"sex" int NOT NULL,
"enabled" bit NOT NULL,
"avatar" varchar NOT NULL,
"video" varchar NOT NULL,
"memo" varchar NOT NULL,
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY ("id")
) COMMENT '学生表';
-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
DELETE FROM "infra_student";

View File

@ -0,0 +1,55 @@
-- 菜单 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status, component_name
)
VALUES (
'学生管理', '', 2, 0, 888,
'student', '', 'infra/demo/index', 0, 'InfraStudent'
);
-- 按钮父菜单ID
-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
SELECT @parentId := LAST_INSERT_ID();
-- 按钮 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生查询', 'infra:student:query', 3, 1, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生创建', 'infra:student:create', 3, 2, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生更新', 'infra:student:update', 3, 3, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生删除', 'infra:student:delete', 3, 4, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生导出', 'infra:student:export', 3, 5, @parentId,
'', '', '', 0
);

View File

@ -0,0 +1,159 @@
<template>
<div class="app-container">
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
<el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
<el-form-item label="名字" prop="name">
<el-input v-model="formData.name" placeholder="请输入名字" />
</el-form-item>
<el-form-item label="简介" prop="description">
<el-input v-model="formData.description" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="出生日期" prop="birthday">
<el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-select v-model="formData.sex" placeholder="请选择性别">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
</el-select>
</el-form-item>
<el-form-item label="是否有效" prop="enabled">
<el-radio-group v-model="formData.enabled">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="头像" prop="avatar">
<ImageUpload v-model="formData.avatar"/>
</el-form-item>
<el-form-item label="附件" prop="video">
<FileUpload v-model="formData.video"/>
</el-form-item>
<el-form-item label="备注" prop="memo">
<editor v-model="formData.memo" :min-height="192"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo'
import ImageUpload from '@/components/ImageUpload';
import FileUpload from '@/components/FileUpload';
import Editor from '@/components/Editor';
export default {
name: "StudentContactForm",
components: {
ImageUpload,
FileUpload,
Editor,
},
data() {
return {
// 弹出层标题
dialogTitle: "",
// 是否显示弹出层
dialogVisible: false,
// 表单的加载中1修改时的数据加载2提交的按钮禁用
formLoading: false,
// 表单参数
formData: {
id: undefined,
studentId: undefined,
name: undefined,
description: undefined,
birthday: undefined,
sex: undefined,
enabled: undefined,
avatar: undefined,
video: undefined,
memo: undefined,
},
// 表单校验
formRules: {
studentId: [{ required: true, message: "学生编号不能为空", trigger: "blur" }],
name: [{ required: true, message: "名字不能为空", trigger: "blur" }],
description: [{ required: true, message: "简介不能为空", trigger: "blur" }],
birthday: [{ required: true, message: "出生日期不能为空", trigger: "blur" }],
sex: [{ required: true, message: "性别不能为空", trigger: "change" }],
enabled: [{ required: true, message: "是否有效不能为空", trigger: "blur" }],
avatar: [{ required: true, message: "头像不能为空", trigger: "blur" }],
memo: [{ required: true, message: "备注不能为空", trigger: "blur" }],
},
};
},
methods: {
/** 打开弹窗 */
open(id, studentId) {
this.dialogVisible = true;
this.reset();
const that = this;
this.formData.studentId = studentId;
// 修改时,设置数据
if (id) {
this.formLoading = true;
try {
StudentApi.getStudentContact(id).then(res=>{
that.formData = res.data;
that.dialogTitle = "修改学生联系人";
})
} finally {
this.formLoading = false;
}
}
this.dialogTitle = "新增学生联系人";
},
/** 提交按钮 */
submitForm() {
this.formLoading = true;
try {
let data = this.formData;
this.$refs["formRef"].validate(valid => {
if (!valid) {
return;
}
// 修改的提交
if (data.id) {
StudentApi.updateStudentContact(data).then(response => {
this.$modal.msgSuccess("修改成功");
this.dialogVisible = false;
this.$emit('success');
});
return;
}
// 添加的提交
StudentApi.createStudentContact(data).then(response => {
this.$modal.msgSuccess("新增成功");
this.dialogVisible = false;
this.$emit('success');
});
});
}finally {
this.formLoading = false
}
},
/** 表单重置 */
reset() {
this.formData = {
id: undefined,
studentId: undefined,
name: undefined,
description: undefined,
birthday: undefined,
sex: undefined,
enabled: undefined,
avatar: undefined,
video: undefined,
memo: undefined,
};
this.resetForm("formRef");
},
}
};
</script>

View File

@ -0,0 +1,134 @@
<template>
<div class="app-container">
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
v-hasPermi="['infra:student:create']">新增</el-button>
</el-col>
</el-row>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="名字" align="center" prop="name" />
<el-table-column label="简介" align="center" prop="description" />
<el-table-column label="出生日期" align="center" prop="birthday" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.birthday) }}</span>
</template>
</el-table-column>
<el-table-column label="性别" align="center" prop="sex">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
</template>
</el-table-column>
<el-table-column label="是否有效" align="center" prop="enabled">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" />
</template>
</el-table-column>
<el-table-column label="头像" align="center" prop="avatar" />
<el-table-column label="附件" align="center" prop="video" />
<el-table-column label="备注" align="center" prop="memo" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
v-hasPermi="['infra:student:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['infra:student:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<StudentContactForm ref="formRef" @success="getList" />
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo'
import StudentContactForm from './StudentContactForm.vue'
export default {
name: "StudentContactList",
components: {
StudentContactForm
},
props:[
'studentId'
],// 学生编号(主表的关联字段)
data() {
return {
// 遮罩层
loading: true,
// 列表的数据
list: [],
// 列表的总页数
total: 0,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
studentId: undefined
}
};
},
watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
studentId:{
handler(val) {
this.queryParams.studentId = val;
if (val){
this.handleQuery();
}
},
immediate: true
}
},
methods: {
/** 查询列表 */
getList() {
try {
this.loading = true;
const that = this;
StudentApi.getStudentContactPage(this.queryParams).then(response => {
that.list = response.data.list;
that.total = response.data.total;
});
} finally {
this.loading = false;
}
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 添加/修改操作 */
openForm(id) {
if (!this.studentId) {
that.$modal.msgError('请选择一个学生');
return;
}
this.$refs["formRef"].open(id, this.studentId);
},
/** 删除按钮操作 */
handleDelete(row) {
const that = this;
try {
const id = row.id;
this.$modal.confirm('是否确认删除学生编号为"' + id + '"的数据项?').then(()=>{
return StudentApi.deleteStudentContact(id);
}).then(() => {
that.getList();
that.$modal.msgSuccess("删除成功");
}).catch(() => {});
} catch {}
},
}
};
</script>

View File

@ -0,0 +1,164 @@
<template>
<div class="app-container">
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
<el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
<el-form-item label="名字" prop="name">
<el-input v-model="formData.name" placeholder="请输入名字" />
</el-form-item>
<el-form-item label="简介" prop="description">
<el-input v-model="formData.description" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="出生日期" prop="birthday">
<el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-select v-model="formData.sex" placeholder="请选择性别">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
</el-select>
</el-form-item>
<el-form-item label="是否有效" prop="enabled">
<el-radio-group v-model="formData.enabled">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="头像">
<ImageUpload v-model="formData.avatar"/>
</el-form-item>
<el-form-item label="附件">
<FileUpload v-model="formData.video"/>
</el-form-item>
<el-form-item label="备注">
<Editor v-model="formData.memo" :min-height="192"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo'
import ImageUpload from '@/components/ImageUpload';
import FileUpload from '@/components/FileUpload';
import Editor from '@/components/Editor';
export default {
name: "StudentForm",
components: {
ImageUpload,
FileUpload,
Editor,
},
data() {
return {
// 弹出层标题
dialogTitle: "",
// 是否显示弹出层
dialogVisible: false,
// 表单的加载中1修改时的数据加载2提交的按钮禁用
formLoading: false,
// 表单参数
formData: {
id: undefined,
name: undefined,
description: undefined,
birthday: undefined,
sex: undefined,
enabled: undefined,
avatar: undefined,
video: undefined,
memo: undefined,
},
// 表单校验
formRules: {
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],
birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],
sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],
enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],
avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],
video: [{ required: true, message: '附件不能为空', trigger: 'blur' }],
memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],
},
};
},
methods: {
/** 打开弹窗 */
open(id) {
this.dialogVisible = true;
this.reset();
const that = this;
// 修改时,设置数据
if (id) {
this.formLoading = true;
try {
StudentApi.getStudent(id).then(res=>{
that.formData = res.data;
that.title = "修改学生";
})
} finally {
this.formLoading = false;
}
}
this.title = "新增学生";
},
/** 提交按钮 */
submitForm() {
this.formLoading = true;
try {
const that = this;
let data = this.formData;
let validate = false;
// 校验主表
this.getRef("formRef").validate(valid => {
validate = valid;
});
// 所有表单校验通过后方可提交
if (!validate) {
return;
}
// 修改的提交
if (data.id) {
StudentApi.updateStudent(data).then(response => {
that.$modal.msgSuccess("修改成功");
that.dialogVisible = false;
that.$emit('success');
});
return;
}
// 添加的提交
StudentApi.createStudent(data).then(response => {
that.$modal.msgSuccess("新增成功");
that.dialogVisible = false;
that.$emit('success');
});
}finally {
this.formLoading = false;
}
},
getRef(refName){
return this.$refs[refName];
},
/** 表单重置 */
reset() {
this.formData = {
id: undefined,
name: undefined,
description: undefined,
birthday: undefined,
sex: undefined,
enabled: undefined,
avatar: undefined,
video: undefined,
memo: undefined,
};
this.resetForm("formRef");
},
}
};
</script>

View File

@ -0,0 +1,159 @@
<template>
<div class="app-container">
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
<el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
<el-form-item label="名字" prop="name">
<el-input v-model="formData.name" placeholder="请输入名字" />
</el-form-item>
<el-form-item label="简介" prop="description">
<el-input v-model="formData.description" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="出生日期" prop="birthday">
<el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-select v-model="formData.sex" placeholder="请选择性别">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
</el-select>
</el-form-item>
<el-form-item label="是否有效" prop="enabled">
<el-radio-group v-model="formData.enabled">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="头像" prop="avatar">
<ImageUpload v-model="formData.avatar"/>
</el-form-item>
<el-form-item label="附件" prop="video">
<FileUpload v-model="formData.video"/>
</el-form-item>
<el-form-item label="备注" prop="memo">
<editor v-model="formData.memo" :min-height="192"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo'
import ImageUpload from '@/components/ImageUpload';
import FileUpload from '@/components/FileUpload';
import Editor from '@/components/Editor';
export default {
name: "StudentTeacherForm",
components: {
ImageUpload,
FileUpload,
Editor,
},
data() {
return {
// 弹出层标题
dialogTitle: "",
// 是否显示弹出层
dialogVisible: false,
// 表单的加载中1修改时的数据加载2提交的按钮禁用
formLoading: false,
// 表单参数
formData: {
id: undefined,
studentId: undefined,
name: undefined,
description: undefined,
birthday: undefined,
sex: undefined,
enabled: undefined,
avatar: undefined,
video: undefined,
memo: undefined,
},
// 表单校验
formRules: {
studentId: [{ required: true, message: "学生编号不能为空", trigger: "blur" }],
name: [{ required: true, message: "名字不能为空", trigger: "blur" }],
description: [{ required: true, message: "简介不能为空", trigger: "blur" }],
birthday: [{ required: true, message: "出生日期不能为空", trigger: "blur" }],
sex: [{ required: true, message: "性别不能为空", trigger: "change" }],
enabled: [{ required: true, message: "是否有效不能为空", trigger: "blur" }],
avatar: [{ required: true, message: "头像不能为空", trigger: "blur" }],
memo: [{ required: true, message: "备注不能为空", trigger: "blur" }],
},
};
},
methods: {
/** 打开弹窗 */
open(id, studentId) {
this.dialogVisible = true;
this.reset();
const that = this;
this.formData.studentId = studentId;
// 修改时,设置数据
if (id) {
this.formLoading = true;
try {
StudentApi.getStudentTeacher(id).then(res=>{
that.formData = res.data;
that.dialogTitle = "修改学生班主任";
})
} finally {
this.formLoading = false;
}
}
this.dialogTitle = "新增学生班主任";
},
/** 提交按钮 */
submitForm() {
this.formLoading = true;
try {
let data = this.formData;
this.$refs["formRef"].validate(valid => {
if (!valid) {
return;
}
// 修改的提交
if (data.id) {
StudentApi.updateStudentTeacher(data).then(response => {
this.$modal.msgSuccess("修改成功");
this.dialogVisible = false;
this.$emit('success');
});
return;
}
// 添加的提交
StudentApi.createStudentTeacher(data).then(response => {
this.$modal.msgSuccess("新增成功");
this.dialogVisible = false;
this.$emit('success');
});
});
}finally {
this.formLoading = false
}
},
/** 表单重置 */
reset() {
this.formData = {
id: undefined,
studentId: undefined,
name: undefined,
description: undefined,
birthday: undefined,
sex: undefined,
enabled: undefined,
avatar: undefined,
video: undefined,
memo: undefined,
};
this.resetForm("formRef");
},
}
};
</script>

View File

@ -0,0 +1,134 @@
<template>
<div class="app-container">
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
v-hasPermi="['infra:student:create']">新增</el-button>
</el-col>
</el-row>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="名字" align="center" prop="name" />
<el-table-column label="简介" align="center" prop="description" />
<el-table-column label="出生日期" align="center" prop="birthday" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.birthday) }}</span>
</template>
</el-table-column>
<el-table-column label="性别" align="center" prop="sex">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
</template>
</el-table-column>
<el-table-column label="是否有效" align="center" prop="enabled">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" />
</template>
</el-table-column>
<el-table-column label="头像" align="center" prop="avatar" />
<el-table-column label="附件" align="center" prop="video" />
<el-table-column label="备注" align="center" prop="memo" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
v-hasPermi="['infra:student:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['infra:student:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<StudentTeacherForm ref="formRef" @success="getList" />
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo'
import StudentTeacherForm from './StudentTeacherForm.vue'
export default {
name: "StudentTeacherList",
components: {
StudentTeacherForm
},
props:[
'studentId'
],// 学生编号(主表的关联字段)
data() {
return {
// 遮罩层
loading: true,
// 列表的数据
list: [],
// 列表的总页数
total: 0,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
studentId: undefined
}
};
},
watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
studentId:{
handler(val) {
this.queryParams.studentId = val;
if (val){
this.handleQuery();
}
},
immediate: true
}
},
methods: {
/** 查询列表 */
getList() {
try {
this.loading = true;
const that = this;
StudentApi.getStudentTeacherPage(this.queryParams).then(response => {
that.list = response.data.list;
that.total = response.data.total;
});
} finally {
this.loading = false;
}
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 添加/修改操作 */
openForm(id) {
if (!this.studentId) {
that.$modal.msgError('请选择一个学生');
return;
}
this.$refs["formRef"].open(id, this.studentId);
},
/** 删除按钮操作 */
handleDelete(row) {
const that = this;
try {
const id = row.id;
this.$modal.confirm('是否确认删除学生编号为"' + id + '"的数据项?').then(()=>{
return StudentApi.deleteStudentTeacher(id);
}).then(() => {
that.getList();
that.$modal.msgSuccess("删除成功");
}).catch(() => {});
} catch {}
},
}
};
</script>

View File

@ -0,0 +1,241 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="名字" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入名字" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="出生日期" prop="birthday">
<el-date-picker clearable v-model="queryParams.birthday" type="date" value-format="yyyy-MM-dd" placeholder="选择出生日期" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-select v-model="queryParams.sex" placeholder="请选择性别" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="是否有效" prop="enabled">
<el-select v-model="queryParams.enabled" placeholder="请选择是否有效" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
v-hasPermi="['infra:student:create']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
v-hasPermi="['infra:student:export']">导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table
v-loading="loading"
:data="list"
:stripe="true"
:highlight-current-row="true"
:show-overflow-tooltip="true"
@current-change="handleCurrentChange"
>
<el-table-column label="编号" align="center" prop="id">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.id" />
</template>
</el-table-column>
<el-table-column label="名字" align="center" prop="name">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.name" />
</template>
</el-table-column>
<el-table-column label="简介" align="center" prop="description">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.description" />
</template>
</el-table-column>
<el-table-column label="出生日期" align="center" prop="birthday" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.birthday) }}</span>
</template>
</el-table-column>
<el-table-column label="性别" align="center" prop="sex">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
</template>
</el-table-column>
<el-table-column label="是否有效" align="center" prop="enabled">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" />
</template>
</el-table-column>
<el-table-column label="头像" align="center" prop="avatar">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.avatar" />
</template>
</el-table-column>
<el-table-column label="附件" align="center" prop="video">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.video" />
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="memo">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.memo" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
v-hasPermi="['infra:student:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['infra:student:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<StudentForm ref="formRef" @success="getList" />
<!-- 子表的列表 -->
<el-tabs v-model="subTabsName">
<el-tab-pane label="学生联系人" name="studentContact">
<StudentContactList v-if="currentRow.id" :student-id="currentRow.id" />
</el-tab-pane>
<el-tab-pane label="学生班主任" name="studentTeacher">
<StudentTeacherList v-if="currentRow.id" :student-id="currentRow.id" />
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo';
import StudentForm from './StudentForm.vue';
import StudentContactList from './components/StudentContactList.vue'
import StudentTeacherList from './components/StudentTeacherList.vue'
export default {
name: "Student",
components: {
StudentForm,
StudentContactList,
StudentTeacherList,
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 学生列表
list: [],
// 是否展开,默认全部展开
isExpandAll: true,
// 重新渲染表格状态
refreshTable: true,
// 选中行
currentRow: {},
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
birthday: null,
sex: null,
enabled: null,
createTime: [],
},
/** 子表的列表 */
subTabsName: 'studentContact'
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
try {
this.loading = true;
StudentApi.getStudentPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
});
} finally {
this.loading = false;
}
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 添加/修改操作 */
openForm(id) {
this.$refs["formRef"].open(id);
},
/** 删除按钮操作 */
handleDelete(row) {
const that = this;
try {
const id = row.id;
this.$modal.confirm('是否确认删除学生编号为"' + id + '"的数据项?').then(()=>{
return StudentApi.deleteStudent(id);
}).then(() => {
that.getList();
that.$modal.msgSuccess("删除成功");
}).catch(() => {});
} catch {}
},
/** 导出按钮操作 */
handleExport() {
const that = this;
try {
this.$modal.confirm('是否确认导出所有学生数据项?').then(() => {
that.exportLoading = true;
return StudentApi.exportStudentExcel(params);
}).then(response => {
that.$download.excel(response, '学生.xls');
});
} catch {
} finally {
that.exportLoading = false;
}
},
/** 选中行操作 */
handleCurrentChange(row) {
this.currentRow = row;
/** 子表的列表 */
this.subTabsName = 'studentContact';
},
}
};
</script>

View File

@ -0,0 +1,12 @@
<?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="cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -0,0 +1,73 @@
[ {
"contentPath" : "java/InfraStudentPageReqVO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java"
}, {
"contentPath" : "java/InfraStudentRespVO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java"
}, {
"contentPath" : "java/InfraStudentSaveReqVO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java"
}, {
"contentPath" : "java/InfraStudentController",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java"
}, {
"contentPath" : "java/InfraStudentDO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java"
}, {
"contentPath" : "java/InfraStudentContactDO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java"
}, {
"contentPath" : "java/InfraStudentTeacherDO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java"
}, {
"contentPath" : "java/InfraStudentMapper",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java"
}, {
"contentPath" : "java/InfraStudentContactMapper",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java"
}, {
"contentPath" : "java/InfraStudentTeacherMapper",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java"
}, {
"contentPath" : "xml/InfraStudentMapper",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml"
}, {
"contentPath" : "java/InfraStudentServiceImpl",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java"
}, {
"contentPath" : "java/InfraStudentService",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java"
}, {
"contentPath" : "java/InfraStudentServiceImplTest",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java"
}, {
"contentPath" : "java/ErrorCodeConstants_手动操作",
"filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
}, {
"contentPath" : "sql/sql",
"filePath" : "sql/sql.sql"
}, {
"contentPath" : "sql/h2",
"filePath" : "sql/h2.sql"
}, {
"contentPath" : "vue/index",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue"
}, {
"contentPath" : "js/student",
"filePath" : "yudao-ui-admin-vue2/src/api/infra/student.js"
}, {
"contentPath" : "vue/StudentForm",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue"
}, {
"contentPath" : "vue/StudentContactForm",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue"
}, {
"contentPath" : "vue/StudentTeacherForm",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue"
}, {
"contentPath" : "vue/StudentContactList",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactList.vue"
}, {
"contentPath" : "vue/StudentTeacherList",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherList.vue"
} ]

View File

@ -0,0 +1,3 @@
// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意请给“TODO 补充编号”设置一个错误码编号!!!
// ========== 学生 TODO 补充编号 ==========
ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在");

View File

@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 学生联系人 DO
*
* @author 芋道源码
*/
@TableName("infra_student_contact")
@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class InfraStudentContactDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 学生编号
*/
private Long studentId;
/**
* 名字
*/
private String name;
/**
* 简介
*/
private String description;
/**
* 出生日期
*/
private LocalDateTime birthday;
/**
* 性别
*
* 枚举 {@link TODO system_user_sex 对应的类}
*/
private Integer sex;
/**
* 是否有效
*
* 枚举 {@link TODO infra_boolean_string 对应的类}
*/
private Boolean enabled;
/**
* 头像
*/
private String avatar;
/**
* 附件
*/
private String video;
/**
* 备注
*/
private String memo;
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 学生联系人 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> {
default List<InfraStudentContactDO> selectListByStudentId(Long studentId) {
return selectList(InfraStudentContactDO::getStudentId, studentId);
}
default int deleteByStudentId(Long studentId) {
return delete(InfraStudentContactDO::getStudentId, studentId);
}
}

View File

@ -0,0 +1,117 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;
@Tag(name = "管理后台 - 学生")
@RestController
@RequestMapping("/infra/student")
@Validated
public class InfraStudentController {
@Resource
private InfraStudentService studentService;
@PostMapping("/create")
@Operation(summary = "创建学生")
@PreAuthorize("@ss.hasPermission('infra:student:create')")
public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {
return success(studentService.createStudent(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新学生")
@PreAuthorize("@ss.hasPermission('infra:student:update')")
public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {
studentService.updateStudent(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除学生")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:student:delete')")
public CommonResult<Boolean> deleteStudent(@RequestParam("id") Long id) {
studentService.deleteStudent(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得学生")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:student:query')")
public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) {
InfraStudentDO student = studentService.getStudent(id);
return success(BeanUtils.toBean(student, InfraStudentRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得学生分页")
@PreAuthorize("@ss.hasPermission('infra:student:query')")
public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出学生 Excel")
@PreAuthorize("@ss.hasPermission('infra:student:export')")
@OperateLog(type = EXPORT)
public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class,
BeanUtils.toBean(list, InfraStudentRespVO.class));
}
// ==================== 子表(学生联系人) ====================
@GetMapping("/student-contact/list-by-student-id")
@Operation(summary = "获得学生联系人列表")
@Parameter(name = "studentId", description = "学生编号")
@PreAuthorize("@ss.hasPermission('infra:student:query')")
public CommonResult<List<InfraStudentContactDO>> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) {
return success(studentService.getStudentContactListByStudentId(studentId));
}
// ==================== 子表(学生班主任) ====================
@GetMapping("/student-teacher/get-by-student-id")
@Operation(summary = "获得学生班主任")
@Parameter(name = "studentId", description = "学生编号")
@PreAuthorize("@ss.hasPermission('infra:student:query')")
public CommonResult<InfraStudentTeacherDO> getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) {
return success(studentService.getStudentTeacherByStudentId(studentId));
}
}

View File

@ -0,0 +1,67 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 学生 DO
*
* @author 芋道源码
*/
@TableName("infra_student")
@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class InfraStudentDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 名字
*/
private String name;
/**
* 简介
*/
private String description;
/**
* 出生日期
*/
private LocalDateTime birthday;
/**
* 性别
*
* 枚举 {@link TODO system_user_sex 对应的类}
*/
private Integer sex;
/**
* 是否有效
*
* 枚举 {@link TODO infra_boolean_string 对应的类}
*/
private Boolean enabled;
/**
* 头像
*/
private String avatar;
/**
* 附件
*/
private String video;
/**
* 备注
*/
private String memo;
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
/**
* 学生 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {
default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()
.likeIfPresent(InfraStudentDO::getName, reqVO.getName())
.eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())
.eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())
.eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())
.betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(InfraStudentDO::getId));
}
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 学生分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class InfraStudentPageReqVO extends PageParam {
@Schema(description = "名字", example = "芋头")
private String name;
@Schema(description = "出生日期")
private LocalDateTime birthday;
@Schema(description = "性别", example = "1")
private Integer sex;
@Schema(description = "是否有效", example = "true")
private Boolean enabled;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,60 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
@Schema(description = "管理后台 - 学生 Response VO")
@Data
@ExcelIgnoreUnannotated
public class InfraStudentRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@ExcelProperty("编号")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
@ExcelProperty("名字")
private String name;
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
@ExcelProperty("简介")
private String description;
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("出生日期")
private LocalDateTime birthday;
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty(value = "性别", converter = DictConvert.class)
@DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private Integer sex;
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@ExcelProperty(value = "是否有效", converter = DictConvert.class)
@DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private Boolean enabled;
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
@ExcelProperty("头像")
private String avatar;
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
@ExcelProperty("附件")
private String video;
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
@ExcelProperty("备注")
private String memo;
@Schema(description = "创建时间")
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,58 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
@Schema(description = "管理后台 - 学生新增/修改 Request VO")
@Data
public class InfraStudentSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
@NotEmpty(message = "名字不能为空")
private String name;
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
@NotEmpty(message = "简介不能为空")
private String description;
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "出生日期不能为空")
private LocalDateTime birthday;
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "性别不能为空")
private Integer sex;
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否有效不能为空")
private Boolean enabled;
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
@NotEmpty(message = "头像不能为空")
private String avatar;
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
@NotEmpty(message = "附件不能为空")
private String video;
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
@NotEmpty(message = "备注不能为空")
private String memo;
@Schema(description = "学生联系人列表")
private List<InfraStudentContactDO> studentContacts;
@Schema(description = "学生班主任")
private InfraStudentTeacherDO studentTeacher;
}

View File

@ -0,0 +1,77 @@
package cn.iocoder.yudao.module.infra.service.demo;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
/**
* 学生 Service 接口
*
* @author 芋道源码
*/
public interface InfraStudentService {
/**
* 创建学生
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);
/**
* 更新学生
*
* @param updateReqVO 更新信息
*/
void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);
/**
* 删除学生
*
* @param id 编号
*/
void deleteStudent(Long id);
/**
* 获得学生
*
* @param id 编号
* @return 学生
*/
InfraStudentDO getStudent(Long id);
/**
* 获得学生分页
*
* @param pageReqVO 分页查询
* @return 学生分页
*/
PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);
// ==================== 子表(学生联系人) ====================
/**
* 获得学生联系人列表
*
* @param studentId 学生编号
* @return 学生联系人列表
*/
List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId);
// ==================== 子表(学生班主任) ====================
/**
* 获得学生班主任
*
* @param studentId 学生编号
* @return 学生班主任
*/
InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId);
}

View File

@ -0,0 +1,147 @@
package cn.iocoder.yudao.module.infra.service.demo;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
/**
* 学生 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class InfraStudentServiceImpl implements InfraStudentService {
@Resource
private InfraStudentMapper studentMapper;
@Resource
private InfraStudentContactMapper studentContactMapper;
@Resource
private InfraStudentTeacherMapper studentTeacherMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createStudent(InfraStudentSaveReqVO createReqVO) {
// 插入
InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);
studentMapper.insert(student);
// 插入子表
createStudentContactList(student.getId(), createReqVO.getStudentContacts());
createStudentTeacher(student.getId(), createReqVO.getStudentTeacher());
// 返回
return student.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateStudent(InfraStudentSaveReqVO updateReqVO) {
// 校验存在
validateStudentExists(updateReqVO.getId());
// 更新
InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);
studentMapper.updateById(updateObj);
// 更新子表
updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts());
updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteStudent(Long id) {
// 校验存在
validateStudentExists(id);
// 删除
studentMapper.deleteById(id);
// 删除子表
deleteStudentContactByStudentId(id);
deleteStudentTeacherByStudentId(id);
}
private void validateStudentExists(Long id) {
if (studentMapper.selectById(id) == null) {
throw exception(STUDENT_NOT_EXISTS);
}
}
@Override
public InfraStudentDO getStudent(Long id) {
return studentMapper.selectById(id);
}
@Override
public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {
return studentMapper.selectPage(pageReqVO);
}
// ==================== 子表(学生联系人) ====================
@Override
public List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId) {
return studentContactMapper.selectListByStudentId(studentId);
}
private void createStudentContactList(Long studentId, List<InfraStudentContactDO> list) {
list.forEach(o -> o.setStudentId(studentId));
studentContactMapper.insertBatch(list);
}
private void updateStudentContactList(Long studentId, List<InfraStudentContactDO> list) {
deleteStudentContactByStudentId(studentId);
list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下1id 冲突2updateTime 不更新
createStudentContactList(studentId, list);
}
private void deleteStudentContactByStudentId(Long studentId) {
studentContactMapper.deleteByStudentId(studentId);
}
// ==================== 子表(学生班主任) ====================
@Override
public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) {
return studentTeacherMapper.selectByStudentId(studentId);
}
private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {
if (studentTeacher == null) {
return;
}
studentTeacher.setStudentId(studentId);
studentTeacherMapper.insert(studentTeacher);
}
private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {
if (studentTeacher == null) {
return;
}
studentTeacher.setStudentId(studentId);
studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下updateTime 不更新
studentTeacherMapper.insertOrUpdate(studentTeacher);
}
private void deleteStudentTeacherByStudentId(Long studentId) {
studentTeacherMapper.deleteByStudentId(studentId);
}
}

View File

@ -0,0 +1,146 @@
package cn.iocoder.yudao.module.infra.service.demo;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import javax.annotation.Resource;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import javax.annotation.Resource;
import org.springframework.context.annotation.Import;
import java.util.*;
import java.time.LocalDateTime;
import static cn.hutool.core.util.RandomUtil.*;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* {@link InfraStudentServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(InfraStudentServiceImpl.class)
public class InfraStudentServiceImplTest extends BaseDbUnitTest {
@Resource
private InfraStudentServiceImpl studentService;
@Resource
private InfraStudentMapper studentMapper;
@Test
public void testCreateStudent_success() {
// 准备参数
InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);
// 调用
Long studentId = studentService.createStudent(createReqVO);
// 断言
assertNotNull(studentId);
// 校验记录的属性是否正确
InfraStudentDO student = studentMapper.selectById(studentId);
assertPojoEquals(createReqVO, student, "id");
}
@Test
public void testUpdateStudent_success() {
// mock 数据
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
// 准备参数
InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {
o.setId(dbStudent.getId()); // 设置更新的 ID
});
// 调用
studentService.updateStudent(updateReqVO);
// 校验是否更新正确
InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的
assertPojoEquals(updateReqVO, student);
}
@Test
public void testUpdateStudent_notExists() {
// 准备参数
InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);
}
@Test
public void testDeleteStudent_success() {
// mock 数据
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbStudent.getId();
// 调用
studentService.deleteStudent(id);
// 校验数据不存在了
assertNull(studentMapper.selectById(id));
}
@Test
public void testDeleteStudent_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);
}
@Test
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
public void testGetStudentPage() {
// mock 数据
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到
o.setName(null);
o.setBirthday(null);
o.setSex(null);
o.setEnabled(null);
o.setCreateTime(null);
});
studentMapper.insert(dbStudent);
// 测试 name 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));
// 测试 birthday 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));
// 测试 sex 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));
// 测试 enabled 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));
// 测试 createTime 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));
// 准备参数
InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();
reqVO.setName(null);
reqVO.setBirthday(null);
reqVO.setSex(null);
reqVO.setEnabled(null);
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
// 调用
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbStudent, pageResult.getList().get(0));
}
}

View File

@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 学生班主任 DO
*
* @author 芋道源码
*/
@TableName("infra_student_teacher")
@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class InfraStudentTeacherDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 学生编号
*/
private Long studentId;
/**
* 名字
*/
private String name;
/**
* 简介
*/
private String description;
/**
* 出生日期
*/
private LocalDateTime birthday;
/**
* 性别
*
* 枚举 {@link TODO system_user_sex 对应的类}
*/
private Integer sex;
/**
* 是否有效
*
* 枚举 {@link TODO infra_boolean_string 对应的类}
*/
private Boolean enabled;
/**
* 头像
*/
private String avatar;
/**
* 附件
*/
private String video;
/**
* 备注
*/
private String memo;
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 学生班主任 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface InfraStudentTeacherMapper extends BaseMapperX<InfraStudentTeacherDO> {
default InfraStudentTeacherDO selectByStudentId(Long studentId) {
return selectOne(InfraStudentTeacherDO::getStudentId, studentId);
}
default int deleteByStudentId(Long studentId) {
return delete(InfraStudentTeacherDO::getStudentId, studentId);
}
}

View File

@ -0,0 +1,74 @@
import request from '@/utils/request'
// 创建学生
export function createStudent(data) {
return request({
url: '/infra/student/create',
method: 'post',
data: data
})
}
// 更新学生
export function updateStudent(data) {
return request({
url: '/infra/student/update',
method: 'put',
data: data
})
}
// 删除学生
export function deleteStudent(id) {
return request({
url: '/infra/student/delete?id=' + id,
method: 'delete'
})
}
// 获得学生
export function getStudent(id) {
return request({
url: '/infra/student/get?id=' + id,
method: 'get'
})
}
// 获得学生分页
export function getStudentPage(params) {
return request({
url: '/infra/student/page',
method: 'get',
params
})
}
// 导出学生 Excel
export function exportStudentExcel(params) {
return request({
url: '/infra/student/export-excel',
method: 'get',
params,
responseType: 'blob'
})
}
// ==================== 子表(学生联系人) ====================
// 获得学生联系人列表
export function getStudentContactListByStudentId(studentId) {
return request({
url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId,
method: 'get'
})
}
// ==================== 子表(学生班主任) ====================
// 获得学生班主任
export function getStudentTeacherByStudentId(studentId) {
return request({
url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId,
method: 'get'
})
}

View File

@ -0,0 +1,17 @@
-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
CREATE TABLE IF NOT EXISTS "infra_student" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar NOT NULL,
"description" varchar NOT NULL,
"birthday" varchar NOT NULL,
"sex" int NOT NULL,
"enabled" bit NOT NULL,
"avatar" varchar NOT NULL,
"video" varchar NOT NULL,
"memo" varchar NOT NULL,
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY ("id")
) COMMENT '学生表';
-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
DELETE FROM "infra_student";

View File

@ -0,0 +1,55 @@
-- 菜单 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status, component_name
)
VALUES (
'学生管理', '', 2, 0, 888,
'student', '', 'infra/demo/index', 0, 'InfraStudent'
);
-- 按钮父菜单ID
-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
SELECT @parentId := LAST_INSERT_ID();
-- 按钮 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生查询', 'infra:student:query', 3, 1, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生创建', 'infra:student:create', 3, 2, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生更新', 'infra:student:update', 3, 3, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生删除', 'infra:student:delete', 3, 4, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生导出', 'infra:student:export', 3, 5, @parentId,
'', '', '', 0
);

View File

@ -0,0 +1,176 @@
<template>
<div class="app-container">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
v-loading="formLoading"
label-width="0px"
:inline-message="true"
>
<el-table :data="formData" class="-mt-10px">
<el-table-column label="序号" type="index" width="100" />
<el-table-column label="名字" min-width="150">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.name`" :rules="formRules.name" class="mb-0px!">
<el-input v-model="row.name" placeholder="请输入名字" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="简介" min-width="200">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.description`" :rules="formRules.description" class="mb-0px!">
<el-input v-model="row.description" type="textarea" placeholder="请输入简介" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="出生日期" min-width="150">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.birthday`" :rules="formRules.birthday" class="mb-0px!">
<el-date-picker clearable v-model="row.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="性别" min-width="150">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.sex`" :rules="formRules.sex" class="mb-0px!">
<el-select v-model="row.sex" placeholder="请选择性别">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="是否有效" min-width="150">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.enabled`" :rules="formRules.enabled" class="mb-0px!">
<el-radio-group v-model="row.enabled">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="头像" min-width="200">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.avatar`" :rules="formRules.avatar" class="mb-0px!">
<ImageUpload v-model="row.avatar"/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="附件" min-width="200">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.video`" :rules="formRules.video" class="mb-0px!">
<FileUpload v-model="row.video"/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="备注" min-width="400">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.memo`" :rules="formRules.memo" class="mb-0px!">
<Editor v-model="row.memo" :min-height="192"/>
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="60">
<template v-slot="{ $index }">
<el-link @click="handleDelete($index)">—</el-link>
</template>
</el-table-column>
</el-table>
</el-form>
<el-row justify="center" class="mt-3">
<el-button @click="handleAdd" round>+ 添加学生联系人</el-button>
</el-row>
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo'
import ImageUpload from '@/components/ImageUpload';
import FileUpload from '@/components/FileUpload';
import Editor from '@/components/Editor';
export default {
name: "StudentContactForm",
components: {
ImageUpload,
FileUpload,
Editor,
},
props:[
'studentId'
],// 学生编号(主表的关联字段)
data() {
return {
// 表单的加载中1修改时的数据加载2提交的按钮禁用
formLoading: false,
// 表单参数
formData: [],
// 表单校验
formRules: {
studentId: [{ required: true, message: "学生编号不能为空", trigger: "blur" }],
name: [{ required: true, message: "名字不能为空", trigger: "blur" }],
description: [{ required: true, message: "简介不能为空", trigger: "blur" }],
birthday: [{ required: true, message: "出生日期不能为空", trigger: "blur" }],
sex: [{ required: true, message: "性别不能为空", trigger: "change" }],
enabled: [{ required: true, message: "是否有效不能为空", trigger: "blur" }],
avatar: [{ required: true, message: "头像不能为空", trigger: "blur" }],
memo: [{ required: true, message: "备注不能为空", trigger: "blur" }],
},
};
},
watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
studentId:{
handler(val) {
// 1. 重置表单
this.formData = []
// 2. val 非空,则加载数据
if (!val) {
return;
}
try {
this.formLoading = true;
const that = this;
StudentApi.getStudentContactListByStudentId(val).then(res=>{
that.formData = res.data;
})
} finally {
this.formLoading = false;
}
},
immediate: true
}
},
methods: {
/** 新增按钮操作 */
handleAdd() {
const row = {
id: undefined,
studentId: undefined,
name: undefined,
description: undefined,
birthday: undefined,
sex: undefined,
enabled: undefined,
avatar: undefined,
video: undefined,
memo: undefined,
}
row.studentId = this.studentId
this.formData.push(row)
},
/** 删除按钮操作 */
handleDelete(index) {
this.formData.splice(index, 1)
},
/** 表单校验 */
validate(){
return this.$refs["formRef"].validate()
},
/** 表单值 */
getData(){
return this.formData
}
}
};
</script>

View File

@ -0,0 +1,91 @@
<template>
<div class="app-container">
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="名字" align="center" prop="name" />
<el-table-column label="简介" align="center" prop="description" />
<el-table-column label="出生日期" align="center" prop="birthday" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.birthday) }}</span>
</template>
</el-table-column>
<el-table-column label="性别" align="center" prop="sex">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
</template>
</el-table-column>
<el-table-column label="是否有效" align="center" prop="enabled">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" />
</template>
</el-table-column>
<el-table-column label="头像" align="center" prop="avatar" />
<el-table-column label="附件" align="center" prop="video" />
<el-table-column label="备注" align="center" prop="memo" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
v-hasPermi="['infra:student:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['infra:student:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo'
export default {
name: "StudentContactList",
props:[
'studentId'
],// 学生编号(主表的关联字段)
data() {
return {
// 遮罩层
loading: true,
// 列表的数据
list: [],
};
},
created() {
this.getList();
},
watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
studentId:{
handler(val) {
this.queryParams.studentId = val;
if (val){
this.handleQuery();
}
},
immediate: true
}
},
methods: {
/** 查询列表 */
getList() {
try {
this.loading = true;
const that = this;
StudentApi.getStudentContactListByStudentId(this.studentId).then(response=>{
that.list = response.data;
})
} finally {
this.loading = false;
}
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
}
};
</script>

View File

@ -0,0 +1,221 @@
<template>
<div class="app-container">
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
<el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
<el-form-item label="名字" prop="name">
<el-input v-model="formData.name" placeholder="请输入名字" />
</el-form-item>
<el-form-item label="简介" prop="description">
<el-input v-model="formData.description" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="出生日期" prop="birthday">
<el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-select v-model="formData.sex" placeholder="请选择性别">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
</el-select>
</el-form-item>
<el-form-item label="是否有效" prop="enabled">
<el-radio-group v-model="formData.enabled">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="头像">
<ImageUpload v-model="formData.avatar"/>
</el-form-item>
<el-form-item label="附件">
<FileUpload v-model="formData.video"/>
</el-form-item>
<el-form-item label="备注">
<Editor v-model="formData.memo" :min-height="192"/>
</el-form-item>
</el-form>
<!-- 子表的表单 -->
<el-tabs v-model="subTabsName">
<el-tab-pane label="学生联系人" name="studentContact">
<StudentContactForm ref="studentContactFormRef" :student-id="formData.id" />
</el-tab-pane>
<el-tab-pane label="学生班主任" name="studentTeacher">
<StudentTeacherForm ref="studentTeacherFormRef" :student-id="formData.id" />
</el-tab-pane>
</el-tabs>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo'
import ImageUpload from '@/components/ImageUpload';
import FileUpload from '@/components/FileUpload';
import Editor from '@/components/Editor';
import StudentContactForm from './components/StudentContactForm.vue'
import StudentTeacherForm from './components/StudentTeacherForm.vue'
export default {
name: "StudentForm",
components: {
ImageUpload,
FileUpload,
Editor,
StudentContactForm,
StudentTeacherForm,
},
data() {
return {
// 弹出层标题
dialogTitle: "",
// 是否显示弹出层
dialogVisible: false,
// 表单的加载中1修改时的数据加载2提交的按钮禁用
formLoading: false,
// 表单参数
formData: {
id: undefined,
name: undefined,
description: undefined,
birthday: undefined,
sex: undefined,
enabled: undefined,
avatar: undefined,
video: undefined,
memo: undefined,
},
// 表单校验
formRules: {
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],
birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],
sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],
enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],
avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],
video: [{ required: true, message: '附件不能为空', trigger: 'blur' }],
memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],
},
/** 子表的表单 */
subTabsName: 'studentContact'
};
},
methods: {
/** 打开弹窗 */
open(id) {
this.dialogVisible = true;
this.reset();
const that = this;
// 修改时,设置数据
if (id) {
this.formLoading = true;
try {
StudentApi.getStudent(id).then(res=>{
that.formData = res.data;
that.title = "修改学生";
})
} finally {
this.formLoading = false;
}
}
this.title = "新增学生";
},
/** 提交按钮 */
submitForm() {
this.formLoading = true;
try {
const that = this;
let data = this.formData;
let validate = false;
// 校验主表
this.getRef("formRef").validate(valid => {
validate = valid;
});
// 校验子表
this.validateSubFrom01().then(() => {
// 全部校验通过-拼接子表的数据
// 拼接子表的数据
data.studentContacts = that.getRef('studentContactFormRef').getData();
data.studentTeacher = that.getRef('studentTeacherFormRef').getData();
}).catch((err) => {
validate = false;
that.subTabsName = err.replace("FormRef", ""); // 定位到没有校验通过的子表单
})
// 所有表单校验通过后方可提交
if (!validate) {
return;
}
// 修改的提交
if (data.id) {
StudentApi.updateStudent(data).then(response => {
that.$modal.msgSuccess("修改成功");
that.dialogVisible = false;
that.$emit('success');
});
return;
}
// 添加的提交
StudentApi.createStudent(data).then(response => {
that.$modal.msgSuccess("新增成功");
that.dialogVisible = false;
that.$emit('success');
});
}finally {
this.formLoading = false;
}
},
getRef(refName){
return this.$refs[refName];
},
/** 校验子表单 */
validateSubFrom(item) {
return new Promise((resolve, reject) => {
this.getRef(item).validate()
.then(() => {
resolve();
})
.catch(() => {
reject(item);
})
})
},
/** 校验所有子表单 */
validateSubFrom01() {
// 需要校验的表单 ref
const validFormRefArr = [
"studentContactFormRef",
"studentTeacherFormRef",
];
const validArr = []; // 校验
for (const item of validFormRefArr) {
validArr.push(this.validateSubFrom(item));
}
return new Promise((resolve, reject) => {
// 校验所有
Promise.all(validArr).then(() => {
resolve();
}).catch((err) => {
reject(err);
})
})
},
/** 表单重置 */
reset() {
this.formData = {
id: undefined,
name: undefined,
description: undefined,
birthday: undefined,
sex: undefined,
enabled: undefined,
avatar: undefined,
video: undefined,
memo: undefined,
};
this.resetForm("formRef");
},
}
};
</script>

View File

@ -0,0 +1,126 @@
<template>
<div class="app-container">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="名字" prop="name">
<el-input v-model="formData.name" placeholder="请输入名字" />
</el-form-item>
<el-form-item label="简介" prop="description">
<el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />
</el-form-item>
<el-form-item label="出生日期" prop="birthday">
<el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-select v-model="formData.sex" placeholder="请选择性别">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
</el-select>
</el-form-item>
<el-form-item label="是否有效" prop="enabled">
<el-radio-group v-model="formData.enabled">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="头像">
<ImageUpload v-model="formData.avatar"/>
</el-form-item>
<el-form-item label="附件">
<FileUpload v-model="formData.video"/>
</el-form-item>
<el-form-item label="备注">
<Editor v-model="formData.memo" :min-height="192"/>
</el-form-item>
</el-form>
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo'
import ImageUpload from '@/components/ImageUpload';
import FileUpload from '@/components/FileUpload';
import Editor from '@/components/Editor';
export default {
name: "StudentTeacherForm",
components: {
ImageUpload,
FileUpload,
Editor,
},
props:[
'studentId'
],// 学生编号(主表的关联字段)
data() {
return {
// 表单的加载中1修改时的数据加载2提交的按钮禁用
formLoading: false,
// 表单参数
formData: [],
// 表单校验
formRules: {
studentId: [{ required: true, message: "学生编号不能为空", trigger: "blur" }],
name: [{ required: true, message: "名字不能为空", trigger: "blur" }],
description: [{ required: true, message: "简介不能为空", trigger: "blur" }],
birthday: [{ required: true, message: "出生日期不能为空", trigger: "blur" }],
sex: [{ required: true, message: "性别不能为空", trigger: "change" }],
enabled: [{ required: true, message: "是否有效不能为空", trigger: "blur" }],
avatar: [{ required: true, message: "头像不能为空", trigger: "blur" }],
memo: [{ required: true, message: "备注不能为空", trigger: "blur" }],
},
};
},
watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
studentId:{
handler(val) {
// 1. 重置表单
this.formData = {
id: undefined,
studentId: undefined,
name: undefined,
description: undefined,
birthday: undefined,
sex: undefined,
enabled: undefined,
avatar: undefined,
video: undefined,
memo: undefined,
}
// 2. val 非空,则加载数据
if (!val) {
return;
}
try {
this.formLoading = true;
const that = this;
StudentApi.getStudentTeacherByStudentId(val).then(res=>{
const data = res.data;
if (!data) {
return
}
that.formData = data;
})
} finally {
this.formLoading = false;
}
},
immediate: true
}
},
methods: {
/** 表单校验 */
validate(){
return this.$refs["formRef"].validate()
},
/** 表单值 */
getData(){
return this.formData
}
}
};
</script>

View File

@ -0,0 +1,95 @@
<template>
<div class="app-container">
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="名字" align="center" prop="name" />
<el-table-column label="简介" align="center" prop="description" />
<el-table-column label="出生日期" align="center" prop="birthday" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.birthday) }}</span>
</template>
</el-table-column>
<el-table-column label="性别" align="center" prop="sex">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
</template>
</el-table-column>
<el-table-column label="是否有效" align="center" prop="enabled">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" />
</template>
</el-table-column>
<el-table-column label="头像" align="center" prop="avatar" />
<el-table-column label="附件" align="center" prop="video" />
<el-table-column label="备注" align="center" prop="memo" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
v-hasPermi="['infra:student:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['infra:student:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo'
export default {
name: "StudentTeacherList",
props:[
'studentId'
],// 学生编号(主表的关联字段)
data() {
return {
// 遮罩层
loading: true,
// 列表的数据
list: [],
};
},
created() {
this.getList();
},
watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
studentId:{
handler(val) {
this.queryParams.studentId = val;
if (val){
this.handleQuery();
}
},
immediate: true
}
},
methods: {
/** 查询列表 */
getList() {
try {
this.loading = true;
const that = this;
StudentApi.getStudentTeacherByStudentId(this.studentId).then(response=>{
const data = response.data;
if (!data) {
return
}
that.list.push(data)
})
} finally {
this.loading = false;
}
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
}
};
</script>

View File

@ -0,0 +1,230 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="名字" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入名字" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="出生日期" prop="birthday">
<el-date-picker clearable v-model="queryParams.birthday" type="date" value-format="yyyy-MM-dd" placeholder="选择出生日期" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-select v-model="queryParams.sex" placeholder="请选择性别" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="是否有效" prop="enabled">
<el-select v-model="queryParams.enabled" placeholder="请选择是否有效" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
v-hasPermi="['infra:student:create']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
v-hasPermi="['infra:student:export']">导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<!-- 子表的列表 -->
<el-table-column type="expand">
<template #default="scope">
<el-tabs value="studentContact">
<el-tab-pane label="学生联系人" name="studentContact">
<StudentContactList :student-id="scope.row.id" />
</el-tab-pane>
<el-tab-pane label="学生班主任" name="studentTeacher">
<StudentTeacherList :student-id="scope.row.id" />
</el-tab-pane>
</el-tabs>
</template>
</el-table-column>
<el-table-column label="编号" align="center" prop="id">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.id" />
</template>
</el-table-column>
<el-table-column label="名字" align="center" prop="name">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.name" />
</template>
</el-table-column>
<el-table-column label="简介" align="center" prop="description">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.description" />
</template>
</el-table-column>
<el-table-column label="出生日期" align="center" prop="birthday" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.birthday) }}</span>
</template>
</el-table-column>
<el-table-column label="性别" align="center" prop="sex">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
</template>
</el-table-column>
<el-table-column label="是否有效" align="center" prop="enabled">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" />
</template>
</el-table-column>
<el-table-column label="头像" align="center" prop="avatar">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.avatar" />
</template>
</el-table-column>
<el-table-column label="附件" align="center" prop="video">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.video" />
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="memo">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.memo" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
v-hasPermi="['infra:student:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['infra:student:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<StudentForm ref="formRef" @success="getList" />
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo';
import StudentForm from './StudentForm.vue';
import StudentContactList from './components/StudentContactList.vue'
import StudentTeacherList from './components/StudentTeacherList.vue'
export default {
name: "Student",
components: {
StudentForm,
StudentContactList,
StudentTeacherList,
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 学生列表
list: [],
// 是否展开,默认全部展开
isExpandAll: true,
// 重新渲染表格状态
refreshTable: true,
// 选中行
currentRow: {},
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
birthday: null,
sex: null,
enabled: null,
createTime: [],
},
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
try {
this.loading = true;
StudentApi.getStudentPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
});
} finally {
this.loading = false;
}
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 添加/修改操作 */
openForm(id) {
this.$refs["formRef"].open(id);
},
/** 删除按钮操作 */
handleDelete(row) {
const that = this;
try {
const id = row.id;
this.$modal.confirm('是否确认删除学生编号为"' + id + '"的数据项?').then(()=>{
return StudentApi.deleteStudent(id);
}).then(() => {
that.getList();
that.$modal.msgSuccess("删除成功");
}).catch(() => {});
} catch {}
},
/** 导出按钮操作 */
handleExport() {
const that = this;
try {
this.$modal.confirm('是否确认导出所有学生数据项?').then(() => {
that.exportLoading = true;
return StudentApi.exportStudentExcel(params);
}).then(response => {
that.$download.excel(response, '学生.xls');
});
} catch {
} finally {
that.exportLoading = false;
}
},
}
};
</script>

View File

@ -0,0 +1,12 @@
<?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="cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -0,0 +1,67 @@
[ {
"contentPath" : "java/InfraStudentPageReqVO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java"
}, {
"contentPath" : "java/InfraStudentRespVO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java"
}, {
"contentPath" : "java/InfraStudentSaveReqVO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java"
}, {
"contentPath" : "java/InfraStudentController",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java"
}, {
"contentPath" : "java/InfraStudentDO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java"
}, {
"contentPath" : "java/InfraStudentContactDO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java"
}, {
"contentPath" : "java/InfraStudentTeacherDO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java"
}, {
"contentPath" : "java/InfraStudentMapper",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java"
}, {
"contentPath" : "java/InfraStudentContactMapper",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java"
}, {
"contentPath" : "java/InfraStudentTeacherMapper",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java"
}, {
"contentPath" : "xml/InfraStudentMapper",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml"
}, {
"contentPath" : "java/InfraStudentServiceImpl",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java"
}, {
"contentPath" : "java/InfraStudentService",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java"
}, {
"contentPath" : "java/InfraStudentServiceImplTest",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java"
}, {
"contentPath" : "java/ErrorCodeConstants_手动操作",
"filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
}, {
"contentPath" : "sql/sql",
"filePath" : "sql/sql.sql"
}, {
"contentPath" : "sql/h2",
"filePath" : "sql/h2.sql"
}, {
"contentPath" : "vue/index",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue"
}, {
"contentPath" : "js/student",
"filePath" : "yudao-ui-admin-vue2/src/api/infra/student.js"
}, {
"contentPath" : "vue/StudentForm",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue"
}, {
"contentPath" : "vue/StudentContactForm",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue"
}, {
"contentPath" : "vue/StudentTeacherForm",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue"
} ]

View File

@ -0,0 +1,3 @@
// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意请给“TODO 补充编号”设置一个错误码编号!!!
// ========== 学生 TODO 补充编号 ==========
ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在");

View File

@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 学生联系人 DO
*
* @author 芋道源码
*/
@TableName("infra_student_contact")
@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class InfraStudentContactDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 学生编号
*/
private Long studentId;
/**
* 名字
*/
private String name;
/**
* 简介
*/
private String description;
/**
* 出生日期
*/
private LocalDateTime birthday;
/**
* 性别
*
* 枚举 {@link TODO system_user_sex 对应的类}
*/
private Integer sex;
/**
* 是否有效
*
* 枚举 {@link TODO infra_boolean_string 对应的类}
*/
private Boolean enabled;
/**
* 头像
*/
private String avatar;
/**
* 附件
*/
private String video;
/**
* 备注
*/
private String memo;
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 学生联系人 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> {
default List<InfraStudentContactDO> selectListByStudentId(Long studentId) {
return selectList(InfraStudentContactDO::getStudentId, studentId);
}
default int deleteByStudentId(Long studentId) {
return delete(InfraStudentContactDO::getStudentId, studentId);
}
}

View File

@ -0,0 +1,117 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;
@Tag(name = "管理后台 - 学生")
@RestController
@RequestMapping("/infra/student")
@Validated
public class InfraStudentController {
@Resource
private InfraStudentService studentService;
@PostMapping("/create")
@Operation(summary = "创建学生")
@PreAuthorize("@ss.hasPermission('infra:student:create')")
public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {
return success(studentService.createStudent(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新学生")
@PreAuthorize("@ss.hasPermission('infra:student:update')")
public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {
studentService.updateStudent(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除学生")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:student:delete')")
public CommonResult<Boolean> deleteStudent(@RequestParam("id") Long id) {
studentService.deleteStudent(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得学生")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:student:query')")
public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) {
InfraStudentDO student = studentService.getStudent(id);
return success(BeanUtils.toBean(student, InfraStudentRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得学生分页")
@PreAuthorize("@ss.hasPermission('infra:student:query')")
public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出学生 Excel")
@PreAuthorize("@ss.hasPermission('infra:student:export')")
@OperateLog(type = EXPORT)
public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class,
BeanUtils.toBean(list, InfraStudentRespVO.class));
}
// ==================== 子表(学生联系人) ====================
@GetMapping("/student-contact/list-by-student-id")
@Operation(summary = "获得学生联系人列表")
@Parameter(name = "studentId", description = "学生编号")
@PreAuthorize("@ss.hasPermission('infra:student:query')")
public CommonResult<List<InfraStudentContactDO>> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) {
return success(studentService.getStudentContactListByStudentId(studentId));
}
// ==================== 子表(学生班主任) ====================
@GetMapping("/student-teacher/get-by-student-id")
@Operation(summary = "获得学生班主任")
@Parameter(name = "studentId", description = "学生编号")
@PreAuthorize("@ss.hasPermission('infra:student:query')")
public CommonResult<InfraStudentTeacherDO> getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) {
return success(studentService.getStudentTeacherByStudentId(studentId));
}
}

View File

@ -0,0 +1,67 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 学生 DO
*
* @author 芋道源码
*/
@TableName("infra_student")
@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class InfraStudentDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 名字
*/
private String name;
/**
* 简介
*/
private String description;
/**
* 出生日期
*/
private LocalDateTime birthday;
/**
* 性别
*
* 枚举 {@link TODO system_user_sex 对应的类}
*/
private Integer sex;
/**
* 是否有效
*
* 枚举 {@link TODO infra_boolean_string 对应的类}
*/
private Boolean enabled;
/**
* 头像
*/
private String avatar;
/**
* 附件
*/
private String video;
/**
* 备注
*/
private String memo;
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
/**
* 学生 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {
default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()
.likeIfPresent(InfraStudentDO::getName, reqVO.getName())
.eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())
.eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())
.eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())
.betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(InfraStudentDO::getId));
}
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 学生分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class InfraStudentPageReqVO extends PageParam {
@Schema(description = "名字", example = "芋头")
private String name;
@Schema(description = "出生日期")
private LocalDateTime birthday;
@Schema(description = "性别", example = "1")
private Integer sex;
@Schema(description = "是否有效", example = "true")
private Boolean enabled;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,60 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
@Schema(description = "管理后台 - 学生 Response VO")
@Data
@ExcelIgnoreUnannotated
public class InfraStudentRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@ExcelProperty("编号")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
@ExcelProperty("名字")
private String name;
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
@ExcelProperty("简介")
private String description;
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("出生日期")
private LocalDateTime birthday;
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty(value = "性别", converter = DictConvert.class)
@DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private Integer sex;
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@ExcelProperty(value = "是否有效", converter = DictConvert.class)
@DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private Boolean enabled;
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
@ExcelProperty("头像")
private String avatar;
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
@ExcelProperty("附件")
private String video;
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
@ExcelProperty("备注")
private String memo;
@Schema(description = "创建时间")
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,58 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
@Schema(description = "管理后台 - 学生新增/修改 Request VO")
@Data
public class InfraStudentSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
@NotEmpty(message = "名字不能为空")
private String name;
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
@NotEmpty(message = "简介不能为空")
private String description;
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "出生日期不能为空")
private LocalDateTime birthday;
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "性别不能为空")
private Integer sex;
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否有效不能为空")
private Boolean enabled;
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
@NotEmpty(message = "头像不能为空")
private String avatar;
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
@NotEmpty(message = "附件不能为空")
private String video;
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
@NotEmpty(message = "备注不能为空")
private String memo;
@Schema(description = "学生联系人列表")
private List<InfraStudentContactDO> studentContacts;
@Schema(description = "学生班主任")
private InfraStudentTeacherDO studentTeacher;
}

View File

@ -0,0 +1,77 @@
package cn.iocoder.yudao.module.infra.service.demo;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
/**
* 学生 Service 接口
*
* @author 芋道源码
*/
public interface InfraStudentService {
/**
* 创建学生
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);
/**
* 更新学生
*
* @param updateReqVO 更新信息
*/
void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);
/**
* 删除学生
*
* @param id 编号
*/
void deleteStudent(Long id);
/**
* 获得学生
*
* @param id 编号
* @return 学生
*/
InfraStudentDO getStudent(Long id);
/**
* 获得学生分页
*
* @param pageReqVO 分页查询
* @return 学生分页
*/
PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);
// ==================== 子表(学生联系人) ====================
/**
* 获得学生联系人列表
*
* @param studentId 学生编号
* @return 学生联系人列表
*/
List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId);
// ==================== 子表(学生班主任) ====================
/**
* 获得学生班主任
*
* @param studentId 学生编号
* @return 学生班主任
*/
InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId);
}

View File

@ -0,0 +1,147 @@
package cn.iocoder.yudao.module.infra.service.demo;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
/**
* 学生 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class InfraStudentServiceImpl implements InfraStudentService {
@Resource
private InfraStudentMapper studentMapper;
@Resource
private InfraStudentContactMapper studentContactMapper;
@Resource
private InfraStudentTeacherMapper studentTeacherMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createStudent(InfraStudentSaveReqVO createReqVO) {
// 插入
InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);
studentMapper.insert(student);
// 插入子表
createStudentContactList(student.getId(), createReqVO.getStudentContacts());
createStudentTeacher(student.getId(), createReqVO.getStudentTeacher());
// 返回
return student.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateStudent(InfraStudentSaveReqVO updateReqVO) {
// 校验存在
validateStudentExists(updateReqVO.getId());
// 更新
InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);
studentMapper.updateById(updateObj);
// 更新子表
updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts());
updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteStudent(Long id) {
// 校验存在
validateStudentExists(id);
// 删除
studentMapper.deleteById(id);
// 删除子表
deleteStudentContactByStudentId(id);
deleteStudentTeacherByStudentId(id);
}
private void validateStudentExists(Long id) {
if (studentMapper.selectById(id) == null) {
throw exception(STUDENT_NOT_EXISTS);
}
}
@Override
public InfraStudentDO getStudent(Long id) {
return studentMapper.selectById(id);
}
@Override
public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {
return studentMapper.selectPage(pageReqVO);
}
// ==================== 子表(学生联系人) ====================
@Override
public List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId) {
return studentContactMapper.selectListByStudentId(studentId);
}
private void createStudentContactList(Long studentId, List<InfraStudentContactDO> list) {
list.forEach(o -> o.setStudentId(studentId));
studentContactMapper.insertBatch(list);
}
private void updateStudentContactList(Long studentId, List<InfraStudentContactDO> list) {
deleteStudentContactByStudentId(studentId);
list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下1id 冲突2updateTime 不更新
createStudentContactList(studentId, list);
}
private void deleteStudentContactByStudentId(Long studentId) {
studentContactMapper.deleteByStudentId(studentId);
}
// ==================== 子表(学生班主任) ====================
@Override
public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) {
return studentTeacherMapper.selectByStudentId(studentId);
}
private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {
if (studentTeacher == null) {
return;
}
studentTeacher.setStudentId(studentId);
studentTeacherMapper.insert(studentTeacher);
}
private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {
if (studentTeacher == null) {
return;
}
studentTeacher.setStudentId(studentId);
studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下updateTime 不更新
studentTeacherMapper.insertOrUpdate(studentTeacher);
}
private void deleteStudentTeacherByStudentId(Long studentId) {
studentTeacherMapper.deleteByStudentId(studentId);
}
}

View File

@ -0,0 +1,146 @@
package cn.iocoder.yudao.module.infra.service.demo;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import javax.annotation.Resource;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import javax.annotation.Resource;
import org.springframework.context.annotation.Import;
import java.util.*;
import java.time.LocalDateTime;
import static cn.hutool.core.util.RandomUtil.*;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* {@link InfraStudentServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(InfraStudentServiceImpl.class)
public class InfraStudentServiceImplTest extends BaseDbUnitTest {
@Resource
private InfraStudentServiceImpl studentService;
@Resource
private InfraStudentMapper studentMapper;
@Test
public void testCreateStudent_success() {
// 准备参数
InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);
// 调用
Long studentId = studentService.createStudent(createReqVO);
// 断言
assertNotNull(studentId);
// 校验记录的属性是否正确
InfraStudentDO student = studentMapper.selectById(studentId);
assertPojoEquals(createReqVO, student, "id");
}
@Test
public void testUpdateStudent_success() {
// mock 数据
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
// 准备参数
InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {
o.setId(dbStudent.getId()); // 设置更新的 ID
});
// 调用
studentService.updateStudent(updateReqVO);
// 校验是否更新正确
InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的
assertPojoEquals(updateReqVO, student);
}
@Test
public void testUpdateStudent_notExists() {
// 准备参数
InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);
}
@Test
public void testDeleteStudent_success() {
// mock 数据
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbStudent.getId();
// 调用
studentService.deleteStudent(id);
// 校验数据不存在了
assertNull(studentMapper.selectById(id));
}
@Test
public void testDeleteStudent_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);
}
@Test
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
public void testGetStudentPage() {
// mock 数据
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到
o.setName(null);
o.setBirthday(null);
o.setSex(null);
o.setEnabled(null);
o.setCreateTime(null);
});
studentMapper.insert(dbStudent);
// 测试 name 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));
// 测试 birthday 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));
// 测试 sex 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));
// 测试 enabled 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));
// 测试 createTime 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));
// 准备参数
InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();
reqVO.setName(null);
reqVO.setBirthday(null);
reqVO.setSex(null);
reqVO.setEnabled(null);
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
// 调用
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbStudent, pageResult.getList().get(0));
}
}

View File

@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 学生班主任 DO
*
* @author 芋道源码
*/
@TableName("infra_student_teacher")
@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class InfraStudentTeacherDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 学生编号
*/
private Long studentId;
/**
* 名字
*/
private String name;
/**
* 简介
*/
private String description;
/**
* 出生日期
*/
private LocalDateTime birthday;
/**
* 性别
*
* 枚举 {@link TODO system_user_sex 对应的类}
*/
private Integer sex;
/**
* 是否有效
*
* 枚举 {@link TODO infra_boolean_string 对应的类}
*/
private Boolean enabled;
/**
* 头像
*/
private String avatar;
/**
* 附件
*/
private String video;
/**
* 备注
*/
private String memo;
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 学生班主任 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface InfraStudentTeacherMapper extends BaseMapperX<InfraStudentTeacherDO> {
default InfraStudentTeacherDO selectByStudentId(Long studentId) {
return selectOne(InfraStudentTeacherDO::getStudentId, studentId);
}
default int deleteByStudentId(Long studentId) {
return delete(InfraStudentTeacherDO::getStudentId, studentId);
}
}

View File

@ -0,0 +1,74 @@
import request from '@/utils/request'
// 创建学生
export function createStudent(data) {
return request({
url: '/infra/student/create',
method: 'post',
data: data
})
}
// 更新学生
export function updateStudent(data) {
return request({
url: '/infra/student/update',
method: 'put',
data: data
})
}
// 删除学生
export function deleteStudent(id) {
return request({
url: '/infra/student/delete?id=' + id,
method: 'delete'
})
}
// 获得学生
export function getStudent(id) {
return request({
url: '/infra/student/get?id=' + id,
method: 'get'
})
}
// 获得学生分页
export function getStudentPage(params) {
return request({
url: '/infra/student/page',
method: 'get',
params
})
}
// 导出学生 Excel
export function exportStudentExcel(params) {
return request({
url: '/infra/student/export-excel',
method: 'get',
params,
responseType: 'blob'
})
}
// ==================== 子表(学生联系人) ====================
// 获得学生联系人列表
export function getStudentContactListByStudentId(studentId) {
return request({
url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId,
method: 'get'
})
}
// ==================== 子表(学生班主任) ====================
// 获得学生班主任
export function getStudentTeacherByStudentId(studentId) {
return request({
url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId,
method: 'get'
})
}

View File

@ -0,0 +1,17 @@
-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
CREATE TABLE IF NOT EXISTS "infra_student" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar NOT NULL,
"description" varchar NOT NULL,
"birthday" varchar NOT NULL,
"sex" int NOT NULL,
"enabled" bit NOT NULL,
"avatar" varchar NOT NULL,
"video" varchar NOT NULL,
"memo" varchar NOT NULL,
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY ("id")
) COMMENT '学生表';
-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
DELETE FROM "infra_student";

View File

@ -0,0 +1,55 @@
-- 菜单 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status, component_name
)
VALUES (
'学生管理', '', 2, 0, 888,
'student', '', 'infra/demo/index', 0, 'InfraStudent'
);
-- 按钮父菜单ID
-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
SELECT @parentId := LAST_INSERT_ID();
-- 按钮 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生查询', 'infra:student:query', 3, 1, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生创建', 'infra:student:create', 3, 2, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生更新', 'infra:student:update', 3, 3, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生删除', 'infra:student:delete', 3, 4, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生导出', 'infra:student:export', 3, 5, @parentId,
'', '', '', 0
);

View File

@ -0,0 +1,176 @@
<template>
<div class="app-container">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
v-loading="formLoading"
label-width="0px"
:inline-message="true"
>
<el-table :data="formData" class="-mt-10px">
<el-table-column label="序号" type="index" width="100" />
<el-table-column label="名字" min-width="150">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.name`" :rules="formRules.name" class="mb-0px!">
<el-input v-model="row.name" placeholder="请输入名字" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="简介" min-width="200">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.description`" :rules="formRules.description" class="mb-0px!">
<el-input v-model="row.description" type="textarea" placeholder="请输入简介" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="出生日期" min-width="150">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.birthday`" :rules="formRules.birthday" class="mb-0px!">
<el-date-picker clearable v-model="row.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="性别" min-width="150">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.sex`" :rules="formRules.sex" class="mb-0px!">
<el-select v-model="row.sex" placeholder="请选择性别">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="是否有效" min-width="150">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.enabled`" :rules="formRules.enabled" class="mb-0px!">
<el-radio-group v-model="row.enabled">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="头像" min-width="200">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.avatar`" :rules="formRules.avatar" class="mb-0px!">
<ImageUpload v-model="row.avatar"/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="附件" min-width="200">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.video`" :rules="formRules.video" class="mb-0px!">
<FileUpload v-model="row.video"/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="备注" min-width="400">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.memo`" :rules="formRules.memo" class="mb-0px!">
<Editor v-model="row.memo" :min-height="192"/>
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="60">
<template v-slot="{ $index }">
<el-link @click="handleDelete($index)">—</el-link>
</template>
</el-table-column>
</el-table>
</el-form>
<el-row justify="center" class="mt-3">
<el-button @click="handleAdd" round>+ 添加学生联系人</el-button>
</el-row>
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo'
import ImageUpload from '@/components/ImageUpload';
import FileUpload from '@/components/FileUpload';
import Editor from '@/components/Editor';
export default {
name: "StudentContactForm",
components: {
ImageUpload,
FileUpload,
Editor,
},
props:[
'studentId'
],// 学生编号(主表的关联字段)
data() {
return {
// 表单的加载中1修改时的数据加载2提交的按钮禁用
formLoading: false,
// 表单参数
formData: [],
// 表单校验
formRules: {
studentId: [{ required: true, message: "学生编号不能为空", trigger: "blur" }],
name: [{ required: true, message: "名字不能为空", trigger: "blur" }],
description: [{ required: true, message: "简介不能为空", trigger: "blur" }],
birthday: [{ required: true, message: "出生日期不能为空", trigger: "blur" }],
sex: [{ required: true, message: "性别不能为空", trigger: "change" }],
enabled: [{ required: true, message: "是否有效不能为空", trigger: "blur" }],
avatar: [{ required: true, message: "头像不能为空", trigger: "blur" }],
memo: [{ required: true, message: "备注不能为空", trigger: "blur" }],
},
};
},
watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
studentId:{
handler(val) {
// 1. 重置表单
this.formData = []
// 2. val 非空,则加载数据
if (!val) {
return;
}
try {
this.formLoading = true;
const that = this;
StudentApi.getStudentContactListByStudentId(val).then(res=>{
that.formData = res.data;
})
} finally {
this.formLoading = false;
}
},
immediate: true
}
},
methods: {
/** 新增按钮操作 */
handleAdd() {
const row = {
id: undefined,
studentId: undefined,
name: undefined,
description: undefined,
birthday: undefined,
sex: undefined,
enabled: undefined,
avatar: undefined,
video: undefined,
memo: undefined,
}
row.studentId = this.studentId
this.formData.push(row)
},
/** 删除按钮操作 */
handleDelete(index) {
this.formData.splice(index, 1)
},
/** 表单校验 */
validate(){
return this.$refs["formRef"].validate()
},
/** 表单值 */
getData(){
return this.formData
}
}
};
</script>

View File

@ -0,0 +1,221 @@
<template>
<div class="app-container">
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
<el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
<el-form-item label="名字" prop="name">
<el-input v-model="formData.name" placeholder="请输入名字" />
</el-form-item>
<el-form-item label="简介" prop="description">
<el-input v-model="formData.description" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="出生日期" prop="birthday">
<el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-select v-model="formData.sex" placeholder="请选择性别">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
</el-select>
</el-form-item>
<el-form-item label="是否有效" prop="enabled">
<el-radio-group v-model="formData.enabled">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="头像">
<ImageUpload v-model="formData.avatar"/>
</el-form-item>
<el-form-item label="附件">
<FileUpload v-model="formData.video"/>
</el-form-item>
<el-form-item label="备注">
<Editor v-model="formData.memo" :min-height="192"/>
</el-form-item>
</el-form>
<!-- 子表的表单 -->
<el-tabs v-model="subTabsName">
<el-tab-pane label="学生联系人" name="studentContact">
<StudentContactForm ref="studentContactFormRef" :student-id="formData.id" />
</el-tab-pane>
<el-tab-pane label="学生班主任" name="studentTeacher">
<StudentTeacherForm ref="studentTeacherFormRef" :student-id="formData.id" />
</el-tab-pane>
</el-tabs>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo'
import ImageUpload from '@/components/ImageUpload';
import FileUpload from '@/components/FileUpload';
import Editor from '@/components/Editor';
import StudentContactForm from './components/StudentContactForm.vue'
import StudentTeacherForm from './components/StudentTeacherForm.vue'
export default {
name: "StudentForm",
components: {
ImageUpload,
FileUpload,
Editor,
StudentContactForm,
StudentTeacherForm,
},
data() {
return {
// 弹出层标题
dialogTitle: "",
// 是否显示弹出层
dialogVisible: false,
// 表单的加载中1修改时的数据加载2提交的按钮禁用
formLoading: false,
// 表单参数
formData: {
id: undefined,
name: undefined,
description: undefined,
birthday: undefined,
sex: undefined,
enabled: undefined,
avatar: undefined,
video: undefined,
memo: undefined,
},
// 表单校验
formRules: {
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],
birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],
sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],
enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],
avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],
video: [{ required: true, message: '附件不能为空', trigger: 'blur' }],
memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],
},
/** 子表的表单 */
subTabsName: 'studentContact'
};
},
methods: {
/** 打开弹窗 */
open(id) {
this.dialogVisible = true;
this.reset();
const that = this;
// 修改时,设置数据
if (id) {
this.formLoading = true;
try {
StudentApi.getStudent(id).then(res=>{
that.formData = res.data;
that.title = "修改学生";
})
} finally {
this.formLoading = false;
}
}
this.title = "新增学生";
},
/** 提交按钮 */
submitForm() {
this.formLoading = true;
try {
const that = this;
let data = this.formData;
let validate = false;
// 校验主表
this.getRef("formRef").validate(valid => {
validate = valid;
});
// 校验子表
this.validateSubFrom01().then(() => {
// 全部校验通过-拼接子表的数据
// 拼接子表的数据
data.studentContacts = that.getRef('studentContactFormRef').getData();
data.studentTeacher = that.getRef('studentTeacherFormRef').getData();
}).catch((err) => {
validate = false;
that.subTabsName = err.replace("FormRef", ""); // 定位到没有校验通过的子表单
})
// 所有表单校验通过后方可提交
if (!validate) {
return;
}
// 修改的提交
if (data.id) {
StudentApi.updateStudent(data).then(response => {
that.$modal.msgSuccess("修改成功");
that.dialogVisible = false;
that.$emit('success');
});
return;
}
// 添加的提交
StudentApi.createStudent(data).then(response => {
that.$modal.msgSuccess("新增成功");
that.dialogVisible = false;
that.$emit('success');
});
}finally {
this.formLoading = false;
}
},
getRef(refName){
return this.$refs[refName];
},
/** 校验子表单 */
validateSubFrom(item) {
return new Promise((resolve, reject) => {
this.getRef(item).validate()
.then(() => {
resolve();
})
.catch(() => {
reject(item);
})
})
},
/** 校验所有子表单 */
validateSubFrom01() {
// 需要校验的表单 ref
const validFormRefArr = [
"studentContactFormRef",
"studentTeacherFormRef",
];
const validArr = []; // 校验
for (const item of validFormRefArr) {
validArr.push(this.validateSubFrom(item));
}
return new Promise((resolve, reject) => {
// 校验所有
Promise.all(validArr).then(() => {
resolve();
}).catch((err) => {
reject(err);
})
})
},
/** 表单重置 */
reset() {
this.formData = {
id: undefined,
name: undefined,
description: undefined,
birthday: undefined,
sex: undefined,
enabled: undefined,
avatar: undefined,
video: undefined,
memo: undefined,
};
this.resetForm("formRef");
},
}
};
</script>

View File

@ -0,0 +1,126 @@
<template>
<div class="app-container">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="名字" prop="name">
<el-input v-model="formData.name" placeholder="请输入名字" />
</el-form-item>
<el-form-item label="简介" prop="description">
<el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />
</el-form-item>
<el-form-item label="出生日期" prop="birthday">
<el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-select v-model="formData.sex" placeholder="请选择性别">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
</el-select>
</el-form-item>
<el-form-item label="是否有效" prop="enabled">
<el-radio-group v-model="formData.enabled">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="头像">
<ImageUpload v-model="formData.avatar"/>
</el-form-item>
<el-form-item label="附件">
<FileUpload v-model="formData.video"/>
</el-form-item>
<el-form-item label="备注">
<Editor v-model="formData.memo" :min-height="192"/>
</el-form-item>
</el-form>
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo'
import ImageUpload from '@/components/ImageUpload';
import FileUpload from '@/components/FileUpload';
import Editor from '@/components/Editor';
export default {
name: "StudentTeacherForm",
components: {
ImageUpload,
FileUpload,
Editor,
},
props:[
'studentId'
],// 学生编号(主表的关联字段)
data() {
return {
// 表单的加载中1修改时的数据加载2提交的按钮禁用
formLoading: false,
// 表单参数
formData: [],
// 表单校验
formRules: {
studentId: [{ required: true, message: "学生编号不能为空", trigger: "blur" }],
name: [{ required: true, message: "名字不能为空", trigger: "blur" }],
description: [{ required: true, message: "简介不能为空", trigger: "blur" }],
birthday: [{ required: true, message: "出生日期不能为空", trigger: "blur" }],
sex: [{ required: true, message: "性别不能为空", trigger: "change" }],
enabled: [{ required: true, message: "是否有效不能为空", trigger: "blur" }],
avatar: [{ required: true, message: "头像不能为空", trigger: "blur" }],
memo: [{ required: true, message: "备注不能为空", trigger: "blur" }],
},
};
},
watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
studentId:{
handler(val) {
// 1. 重置表单
this.formData = {
id: undefined,
studentId: undefined,
name: undefined,
description: undefined,
birthday: undefined,
sex: undefined,
enabled: undefined,
avatar: undefined,
video: undefined,
memo: undefined,
}
// 2. val 非空,则加载数据
if (!val) {
return;
}
try {
this.formLoading = true;
const that = this;
StudentApi.getStudentTeacherByStudentId(val).then(res=>{
const data = res.data;
if (!data) {
return
}
that.formData = data;
})
} finally {
this.formLoading = false;
}
},
immediate: true
}
},
methods: {
/** 表单校验 */
validate(){
return this.$refs["formRef"].validate()
},
/** 表单值 */
getData(){
return this.formData
}
}
};
</script>

View File

@ -0,0 +1,213 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="名字" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入名字" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="出生日期" prop="birthday">
<el-date-picker clearable v-model="queryParams.birthday" type="date" value-format="yyyy-MM-dd" placeholder="选择出生日期" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-select v-model="queryParams.sex" placeholder="请选择性别" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="是否有效" prop="enabled">
<el-select v-model="queryParams.enabled" placeholder="请选择是否有效" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
v-hasPermi="['infra:student:create']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
v-hasPermi="['infra:student:export']">导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="编号" align="center" prop="id">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.id" />
</template>
</el-table-column>
<el-table-column label="名字" align="center" prop="name">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.name" />
</template>
</el-table-column>
<el-table-column label="简介" align="center" prop="description">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.description" />
</template>
</el-table-column>
<el-table-column label="出生日期" align="center" prop="birthday" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.birthday) }}</span>
</template>
</el-table-column>
<el-table-column label="性别" align="center" prop="sex">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
</template>
</el-table-column>
<el-table-column label="是否有效" align="center" prop="enabled">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" />
</template>
</el-table-column>
<el-table-column label="头像" align="center" prop="avatar">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.avatar" />
</template>
</el-table-column>
<el-table-column label="附件" align="center" prop="video">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.video" />
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="memo">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.memo" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
v-hasPermi="['infra:student:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['infra:student:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<StudentForm ref="formRef" @success="getList" />
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo';
import StudentForm from './StudentForm.vue';
export default {
name: "Student",
components: {
StudentForm,
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 学生列表
list: [],
// 是否展开,默认全部展开
isExpandAll: true,
// 重新渲染表格状态
refreshTable: true,
// 选中行
currentRow: {},
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
birthday: null,
sex: null,
enabled: null,
createTime: [],
},
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
try {
this.loading = true;
StudentApi.getStudentPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
});
} finally {
this.loading = false;
}
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 添加/修改操作 */
openForm(id) {
this.$refs["formRef"].open(id);
},
/** 删除按钮操作 */
handleDelete(row) {
const that = this;
try {
const id = row.id;
this.$modal.confirm('是否确认删除学生编号为"' + id + '"的数据项?').then(()=>{
return StudentApi.deleteStudent(id);
}).then(() => {
that.getList();
that.$modal.msgSuccess("删除成功");
}).catch(() => {});
} catch {}
},
/** 导出按钮操作 */
handleExport() {
const that = this;
try {
this.$modal.confirm('是否确认导出所有学生数据项?').then(() => {
that.exportLoading = true;
return StudentApi.exportStudentExcel(params);
}).then(response => {
that.$download.excel(response, '学生.xls');
});
} catch {
} finally {
that.exportLoading = false;
}
},
}
};
</script>

View File

@ -0,0 +1,12 @@
<?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="cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -0,0 +1,49 @@
[ {
"contentPath" : "java/InfraStudentPageReqVO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java"
}, {
"contentPath" : "java/InfraStudentRespVO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java"
}, {
"contentPath" : "java/InfraStudentSaveReqVO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java"
}, {
"contentPath" : "java/InfraStudentController",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java"
}, {
"contentPath" : "java/InfraStudentDO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java"
}, {
"contentPath" : "java/InfraStudentMapper",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java"
}, {
"contentPath" : "xml/InfraStudentMapper",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml"
}, {
"contentPath" : "java/InfraStudentServiceImpl",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java"
}, {
"contentPath" : "java/InfraStudentService",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java"
}, {
"contentPath" : "java/InfraStudentServiceImplTest",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java"
}, {
"contentPath" : "java/ErrorCodeConstants_手动操作",
"filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
}, {
"contentPath" : "sql/sql",
"filePath" : "sql/sql.sql"
}, {
"contentPath" : "sql/h2",
"filePath" : "sql/h2.sql"
}, {
"contentPath" : "vue/index",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue"
}, {
"contentPath" : "js/student",
"filePath" : "yudao-ui-admin-vue2/src/api/infra/student.js"
}, {
"contentPath" : "vue/StudentForm",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue"
} ]

View File

@ -0,0 +1,3 @@
// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意请给“TODO 补充编号”设置一个错误码编号!!!
// ========== 学生 TODO 补充编号 ==========
ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在");

View File

@ -0,0 +1,95 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;
@Tag(name = "管理后台 - 学生")
@RestController
@RequestMapping("/infra/student")
@Validated
public class InfraStudentController {
@Resource
private InfraStudentService studentService;
@PostMapping("/create")
@Operation(summary = "创建学生")
@PreAuthorize("@ss.hasPermission('infra:student:create')")
public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {
return success(studentService.createStudent(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新学生")
@PreAuthorize("@ss.hasPermission('infra:student:update')")
public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {
studentService.updateStudent(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除学生")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:student:delete')")
public CommonResult<Boolean> deleteStudent(@RequestParam("id") Long id) {
studentService.deleteStudent(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得学生")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:student:query')")
public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) {
InfraStudentDO student = studentService.getStudent(id);
return success(BeanUtils.toBean(student, InfraStudentRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得学生分页")
@PreAuthorize("@ss.hasPermission('infra:student:query')")
public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出学生 Excel")
@PreAuthorize("@ss.hasPermission('infra:student:export')")
@OperateLog(type = EXPORT)
public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class,
BeanUtils.toBean(list, InfraStudentRespVO.class));
}
}

View File

@ -0,0 +1,67 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 学生 DO
*
* @author 芋道源码
*/
@TableName("infra_student")
@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class InfraStudentDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 名字
*/
private String name;
/**
* 简介
*/
private String description;
/**
* 出生日期
*/
private LocalDateTime birthday;
/**
* 性别
*
* 枚举 {@link TODO system_user_sex 对应的类}
*/
private Integer sex;
/**
* 是否有效
*
* 枚举 {@link TODO infra_boolean_string 对应的类}
*/
private Boolean enabled;
/**
* 头像
*/
private String avatar;
/**
* 附件
*/
private String video;
/**
* 备注
*/
private String memo;
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
/**
* 学生 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {
default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()
.likeIfPresent(InfraStudentDO::getName, reqVO.getName())
.eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())
.eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())
.eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())
.betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(InfraStudentDO::getId));
}
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 学生分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class InfraStudentPageReqVO extends PageParam {
@Schema(description = "名字", example = "芋头")
private String name;
@Schema(description = "出生日期")
private LocalDateTime birthday;
@Schema(description = "性别", example = "1")
private Integer sex;
@Schema(description = "是否有效", example = "true")
private Boolean enabled;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,60 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
@Schema(description = "管理后台 - 学生 Response VO")
@Data
@ExcelIgnoreUnannotated
public class InfraStudentRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@ExcelProperty("编号")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
@ExcelProperty("名字")
private String name;
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
@ExcelProperty("简介")
private String description;
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("出生日期")
private LocalDateTime birthday;
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty(value = "性别", converter = DictConvert.class)
@DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private Integer sex;
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@ExcelProperty(value = "是否有效", converter = DictConvert.class)
@DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private Boolean enabled;
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
@ExcelProperty("头像")
private String avatar;
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
@ExcelProperty("附件")
private String video;
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
@ExcelProperty("备注")
private String memo;
@Schema(description = "创建时间")
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,50 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 学生新增/修改 Request VO")
@Data
public class InfraStudentSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
@NotEmpty(message = "名字不能为空")
private String name;
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
@NotEmpty(message = "简介不能为空")
private String description;
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "出生日期不能为空")
private LocalDateTime birthday;
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "性别不能为空")
private Integer sex;
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否有效不能为空")
private Boolean enabled;
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
@NotEmpty(message = "头像不能为空")
private String avatar;
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
@NotEmpty(message = "附件不能为空")
private String video;
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
@NotEmpty(message = "备注不能为空")
private String memo;
}

View File

@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.infra.service.demo;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
/**
* 学生 Service 接口
*
* @author 芋道源码
*/
public interface InfraStudentService {
/**
* 创建学生
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);
/**
* 更新学生
*
* @param updateReqVO 更新信息
*/
void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);
/**
* 删除学生
*
* @param id 编号
*/
void deleteStudent(Long id);
/**
* 获得学生
*
* @param id 编号
* @return 学生
*/
InfraStudentDO getStudent(Long id);
/**
* 获得学生分页
*
* @param pageReqVO 分页查询
* @return 学生分页
*/
PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);
}

View File

@ -0,0 +1,74 @@
package cn.iocoder.yudao.module.infra.service.demo;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
/**
* 学生 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class InfraStudentServiceImpl implements InfraStudentService {
@Resource
private InfraStudentMapper studentMapper;
@Override
public Long createStudent(InfraStudentSaveReqVO createReqVO) {
// 插入
InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);
studentMapper.insert(student);
// 返回
return student.getId();
}
@Override
public void updateStudent(InfraStudentSaveReqVO updateReqVO) {
// 校验存在
validateStudentExists(updateReqVO.getId());
// 更新
InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);
studentMapper.updateById(updateObj);
}
@Override
public void deleteStudent(Long id) {
// 校验存在
validateStudentExists(id);
// 删除
studentMapper.deleteById(id);
}
private void validateStudentExists(Long id) {
if (studentMapper.selectById(id) == null) {
throw exception(STUDENT_NOT_EXISTS);
}
}
@Override
public InfraStudentDO getStudent(Long id) {
return studentMapper.selectById(id);
}
@Override
public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {
return studentMapper.selectPage(pageReqVO);
}
}

View File

@ -0,0 +1,146 @@
package cn.iocoder.yudao.module.infra.service.demo;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import javax.annotation.Resource;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import javax.annotation.Resource;
import org.springframework.context.annotation.Import;
import java.util.*;
import java.time.LocalDateTime;
import static cn.hutool.core.util.RandomUtil.*;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* {@link InfraStudentServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(InfraStudentServiceImpl.class)
public class InfraStudentServiceImplTest extends BaseDbUnitTest {
@Resource
private InfraStudentServiceImpl studentService;
@Resource
private InfraStudentMapper studentMapper;
@Test
public void testCreateStudent_success() {
// 准备参数
InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);
// 调用
Long studentId = studentService.createStudent(createReqVO);
// 断言
assertNotNull(studentId);
// 校验记录的属性是否正确
InfraStudentDO student = studentMapper.selectById(studentId);
assertPojoEquals(createReqVO, student, "id");
}
@Test
public void testUpdateStudent_success() {
// mock 数据
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
// 准备参数
InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {
o.setId(dbStudent.getId()); // 设置更新的 ID
});
// 调用
studentService.updateStudent(updateReqVO);
// 校验是否更新正确
InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的
assertPojoEquals(updateReqVO, student);
}
@Test
public void testUpdateStudent_notExists() {
// 准备参数
InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);
}
@Test
public void testDeleteStudent_success() {
// mock 数据
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbStudent.getId();
// 调用
studentService.deleteStudent(id);
// 校验数据不存在了
assertNull(studentMapper.selectById(id));
}
@Test
public void testDeleteStudent_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);
}
@Test
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
public void testGetStudentPage() {
// mock 数据
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到
o.setName(null);
o.setBirthday(null);
o.setSex(null);
o.setEnabled(null);
o.setCreateTime(null);
});
studentMapper.insert(dbStudent);
// 测试 name 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));
// 测试 birthday 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));
// 测试 sex 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));
// 测试 enabled 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));
// 测试 createTime 不匹配
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));
// 准备参数
InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();
reqVO.setName(null);
reqVO.setBirthday(null);
reqVO.setSex(null);
reqVO.setEnabled(null);
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
// 调用
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbStudent, pageResult.getList().get(0));
}
}

View File

@ -0,0 +1,53 @@
import request from '@/utils/request'
// 创建学生
export function createStudent(data) {
return request({
url: '/infra/student/create',
method: 'post',
data: data
})
}
// 更新学生
export function updateStudent(data) {
return request({
url: '/infra/student/update',
method: 'put',
data: data
})
}
// 删除学生
export function deleteStudent(id) {
return request({
url: '/infra/student/delete?id=' + id,
method: 'delete'
})
}
// 获得学生
export function getStudent(id) {
return request({
url: '/infra/student/get?id=' + id,
method: 'get'
})
}
// 获得学生分页
export function getStudentPage(params) {
return request({
url: '/infra/student/page',
method: 'get',
params
})
}
// 导出学生 Excel
export function exportStudentExcel(params) {
return request({
url: '/infra/student/export-excel',
method: 'get',
params,
responseType: 'blob'
})
}

View File

@ -0,0 +1,17 @@
-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
CREATE TABLE IF NOT EXISTS "infra_student" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar NOT NULL,
"description" varchar NOT NULL,
"birthday" varchar NOT NULL,
"sex" int NOT NULL,
"enabled" bit NOT NULL,
"avatar" varchar NOT NULL,
"video" varchar NOT NULL,
"memo" varchar NOT NULL,
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY ("id")
) COMMENT '学生表';
-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
DELETE FROM "infra_student";

View File

@ -0,0 +1,55 @@
-- 菜单 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status, component_name
)
VALUES (
'学生管理', '', 2, 0, 888,
'student', '', 'infra/demo/index', 0, 'InfraStudent'
);
-- 按钮父菜单ID
-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
SELECT @parentId := LAST_INSERT_ID();
-- 按钮 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生查询', 'infra:student:query', 3, 1, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生创建', 'infra:student:create', 3, 2, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生更新', 'infra:student:update', 3, 3, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生删除', 'infra:student:delete', 3, 4, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'学生导出', 'infra:student:export', 3, 5, @parentId,
'', '', '', 0
);

View File

@ -0,0 +1,164 @@
<template>
<div class="app-container">
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
<el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
<el-form-item label="名字" prop="name">
<el-input v-model="formData.name" placeholder="请输入名字" />
</el-form-item>
<el-form-item label="简介" prop="description">
<el-input v-model="formData.description" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="出生日期" prop="birthday">
<el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-select v-model="formData.sex" placeholder="请选择性别">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
</el-select>
</el-form-item>
<el-form-item label="是否有效" prop="enabled">
<el-radio-group v-model="formData.enabled">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="头像">
<ImageUpload v-model="formData.avatar"/>
</el-form-item>
<el-form-item label="附件">
<FileUpload v-model="formData.video"/>
</el-form-item>
<el-form-item label="备注">
<Editor v-model="formData.memo" :min-height="192"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo'
import ImageUpload from '@/components/ImageUpload';
import FileUpload from '@/components/FileUpload';
import Editor from '@/components/Editor';
export default {
name: "StudentForm",
components: {
ImageUpload,
FileUpload,
Editor,
},
data() {
return {
// 弹出层标题
dialogTitle: "",
// 是否显示弹出层
dialogVisible: false,
// 表单的加载中1修改时的数据加载2提交的按钮禁用
formLoading: false,
// 表单参数
formData: {
id: undefined,
name: undefined,
description: undefined,
birthday: undefined,
sex: undefined,
enabled: undefined,
avatar: undefined,
video: undefined,
memo: undefined,
},
// 表单校验
formRules: {
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],
birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],
sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],
enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],
avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],
video: [{ required: true, message: '附件不能为空', trigger: 'blur' }],
memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],
},
};
},
methods: {
/** 打开弹窗 */
open(id) {
this.dialogVisible = true;
this.reset();
const that = this;
// 修改时,设置数据
if (id) {
this.formLoading = true;
try {
StudentApi.getStudent(id).then(res=>{
that.formData = res.data;
that.title = "修改学生";
})
} finally {
this.formLoading = false;
}
}
this.title = "新增学生";
},
/** 提交按钮 */
submitForm() {
this.formLoading = true;
try {
const that = this;
let data = this.formData;
let validate = false;
// 校验主表
this.getRef("formRef").validate(valid => {
validate = valid;
});
// 所有表单校验通过后方可提交
if (!validate) {
return;
}
// 修改的提交
if (data.id) {
StudentApi.updateStudent(data).then(response => {
that.$modal.msgSuccess("修改成功");
that.dialogVisible = false;
that.$emit('success');
});
return;
}
// 添加的提交
StudentApi.createStudent(data).then(response => {
that.$modal.msgSuccess("新增成功");
that.dialogVisible = false;
that.$emit('success');
});
}finally {
this.formLoading = false;
}
},
getRef(refName){
return this.$refs[refName];
},
/** 表单重置 */
reset() {
this.formData = {
id: undefined,
name: undefined,
description: undefined,
birthday: undefined,
sex: undefined,
enabled: undefined,
avatar: undefined,
video: undefined,
memo: undefined,
};
this.resetForm("formRef");
},
}
};
</script>

View File

@ -0,0 +1,213 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="名字" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入名字" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="出生日期" prop="birthday">
<el-date-picker clearable v-model="queryParams.birthday" type="date" value-format="yyyy-MM-dd" placeholder="选择出生日期" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-select v-model="queryParams.sex" placeholder="请选择性别" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="是否有效" prop="enabled">
<el-select v-model="queryParams.enabled" placeholder="请选择是否有效" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
v-hasPermi="['infra:student:create']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
v-hasPermi="['infra:student:export']">导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="编号" align="center" prop="id">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.id" />
</template>
</el-table-column>
<el-table-column label="名字" align="center" prop="name">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.name" />
</template>
</el-table-column>
<el-table-column label="简介" align="center" prop="description">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.description" />
</template>
</el-table-column>
<el-table-column label="出生日期" align="center" prop="birthday" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.birthday) }}</span>
</template>
</el-table-column>
<el-table-column label="性别" align="center" prop="sex">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
</template>
</el-table-column>
<el-table-column label="是否有效" align="center" prop="enabled">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" />
</template>
</el-table-column>
<el-table-column label="头像" align="center" prop="avatar">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.avatar" />
</template>
</el-table-column>
<el-table-column label="附件" align="center" prop="video">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.video" />
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="memo">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.memo" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
v-hasPermi="['infra:student:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['infra:student:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<StudentForm ref="formRef" @success="getList" />
</div>
</template>
<script>
import * as StudentApi from '@/api/infra/demo';
import StudentForm from './StudentForm.vue';
export default {
name: "Student",
components: {
StudentForm,
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 学生列表
list: [],
// 是否展开,默认全部展开
isExpandAll: true,
// 重新渲染表格状态
refreshTable: true,
// 选中行
currentRow: {},
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
birthday: null,
sex: null,
enabled: null,
createTime: [],
},
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
try {
this.loading = true;
StudentApi.getStudentPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
});
} finally {
this.loading = false;
}
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 添加/修改操作 */
openForm(id) {
this.$refs["formRef"].open(id);
},
/** 删除按钮操作 */
handleDelete(row) {
const that = this;
try {
const id = row.id;
this.$modal.confirm('是否确认删除学生编号为"' + id + '"的数据项?').then(()=>{
return StudentApi.deleteStudent(id);
}).then(() => {
that.getList();
that.$modal.msgSuccess("删除成功");
}).catch(() => {});
} catch {}
},
/** 导出按钮操作 */
handleExport() {
const that = this;
try {
this.$modal.confirm('是否确认导出所有学生数据项?').then(() => {
that.exportLoading = true;
return StudentApi.exportStudentExcel(params);
}).then(response => {
that.$download.excel(response, '学生.xls');
});
} catch {
} finally {
that.exportLoading = false;
}
},
}
};
</script>

View File

@ -0,0 +1,12 @@
<?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="cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -0,0 +1,49 @@
[ {
"contentPath" : "java/InfraCategoryListReqVO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryListReqVO.java"
}, {
"contentPath" : "java/InfraCategoryRespVO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryRespVO.java"
}, {
"contentPath" : "java/InfraCategorySaveReqVO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategorySaveReqVO.java"
}, {
"contentPath" : "java/InfraCategoryController",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraCategoryController.java"
}, {
"contentPath" : "java/InfraCategoryDO",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraCategoryDO.java"
}, {
"contentPath" : "java/InfraCategoryMapper",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraCategoryMapper.java"
}, {
"contentPath" : "xml/InfraCategoryMapper",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraCategoryMapper.xml"
}, {
"contentPath" : "java/InfraCategoryServiceImpl",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImpl.java"
}, {
"contentPath" : "java/InfraCategoryService",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryService.java"
}, {
"contentPath" : "java/InfraCategoryServiceImplTest",
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImplTest.java"
}, {
"contentPath" : "java/ErrorCodeConstants_手动操作",
"filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
}, {
"contentPath" : "sql/sql",
"filePath" : "sql/sql.sql"
}, {
"contentPath" : "sql/h2",
"filePath" : "sql/h2.sql"
}, {
"contentPath" : "vue/index",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue"
}, {
"contentPath" : "js/category",
"filePath" : "yudao-ui-admin-vue2/src/api/infra/category.js"
}, {
"contentPath" : "vue/CategoryForm",
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/CategoryForm.vue"
} ]

View File

@ -0,0 +1,8 @@
// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意请给“TODO 补充编号”设置一个错误码编号!!!
// ========== 分类 TODO 补充编号 ==========
ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(TODO 补充编号, "分类不存在");
ErrorCode CATEGORY_EXITS_CHILDREN = new ErrorCode(TODO 补充编号, "存在存在子分类,无法删除");
ErrorCode CATEGORY_PARENT_NOT_EXITS = new ErrorCode(TODO 补充编号,"父级分类不存在");
ErrorCode CATEGORY_PARENT_ERROR = new ErrorCode(TODO 补充编号, "不能设置自己为父分类");
ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(TODO 补充编号, "已经存在该名字的分类");
ErrorCode CATEGORY_PARENT_IS_CHILD = new ErrorCode(TODO 补充编号, "不能设置自己的子InfraCategory为父InfraCategory");

View File

@ -0,0 +1,94 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;
import cn.iocoder.yudao.module.infra.service.demo.InfraCategoryService;
@Tag(name = "管理后台 - 分类")
@RestController
@RequestMapping("/infra/category")
@Validated
public class InfraCategoryController {
@Resource
private InfraCategoryService categoryService;
@PostMapping("/create")
@Operation(summary = "创建分类")
@PreAuthorize("@ss.hasPermission('infra:category:create')")
public CommonResult<Long> createCategory(@Valid @RequestBody InfraCategorySaveReqVO createReqVO) {
return success(categoryService.createCategory(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新分类")
@PreAuthorize("@ss.hasPermission('infra:category:update')")
public CommonResult<Boolean> updateCategory(@Valid @RequestBody InfraCategorySaveReqVO updateReqVO) {
categoryService.updateCategory(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除分类")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:category:delete')")
public CommonResult<Boolean> deleteCategory(@RequestParam("id") Long id) {
categoryService.deleteCategory(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得分类")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:category:query')")
public CommonResult<InfraCategoryRespVO> getCategory(@RequestParam("id") Long id) {
InfraCategoryDO category = categoryService.getCategory(id);
return success(BeanUtils.toBean(category, InfraCategoryRespVO.class));
}
@GetMapping("/list")
@Operation(summary = "获得分类列表")
@PreAuthorize("@ss.hasPermission('infra:category:query')")
public CommonResult<List<InfraCategoryRespVO>> getCategoryList(@Valid InfraCategoryListReqVO listReqVO) {
List<InfraCategoryDO> list = categoryService.getCategoryList(listReqVO);
return success(BeanUtils.toBean(list, InfraCategoryRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出分类 Excel")
@PreAuthorize("@ss.hasPermission('infra:category:export')")
@OperateLog(type = EXPORT)
public void exportCategoryExcel(@Valid InfraCategoryListReqVO listReqVO,
HttpServletResponse response) throws IOException {
List<InfraCategoryDO> list = categoryService.getCategoryList(listReqVO);
// 导出 Excel
ExcelUtils.write(response, "分类.xls", "数据", InfraCategoryRespVO.class,
BeanUtils.toBean(list, InfraCategoryRespVO.class));
}
}

View File

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
import lombok.*;
import java.util.*;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 分类 DO
*
* @author 芋道源码
*/
@TableName("infra_category")
@KeySequence("infra_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class InfraCategoryDO extends BaseDO {
public static final Long PARENT_ID_ROOT = 0L;
/**
* 编号
*/
@TableId
private Long id;
/**
* 名字
*/
private String name;
/**
* 父编号
*/
private Long parentId;
}

View File

@ -0,0 +1,15 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
@Schema(description = "管理后台 - 分类列表 Request VO")
@Data
public class InfraCategoryListReqVO {
@Schema(description = "名字", example = "芋头")
private String name;
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
/**
* 分类 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface InfraCategoryMapper extends BaseMapperX<InfraCategoryDO> {
default List<InfraCategoryDO> selectList(InfraCategoryListReqVO reqVO) {
return selectList(new LambdaQueryWrapperX<InfraCategoryDO>()
.likeIfPresent(InfraCategoryDO::getName, reqVO.getName())
.orderByDesc(InfraCategoryDO::getId));
}
default InfraCategoryDO selectByParentIdAndName(Long parentId, String name) {
return selectOne(InfraCategoryDO::getParentId, parentId, InfraCategoryDO::getName, name);
}
default Long selectCountByParentId(Long parentId) {
return selectCount(InfraCategoryDO::getParentId, parentId);
}
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.util.*;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 分类 Response VO")
@Data
@ExcelIgnoreUnannotated
public class InfraCategoryRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@ExcelProperty("编号")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
@ExcelProperty("名字")
private String name;
@Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
@ExcelProperty("父编号")
private Long parentId;
}

View File

@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
import java.util.*;
@Schema(description = "管理后台 - 分类新增/修改 Request VO")
@Data
public class InfraCategorySaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
@NotEmpty(message = "名字不能为空")
private String name;
@Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
@NotNull(message = "父编号不能为空")
private Long parentId;
}

View File

@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.infra.service.demo;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
/**
* 分类 Service 接口
*
* @author 芋道源码
*/
public interface InfraCategoryService {
/**
* 创建分类
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createCategory(@Valid InfraCategorySaveReqVO createReqVO);
/**
* 更新分类
*
* @param updateReqVO 更新信息
*/
void updateCategory(@Valid InfraCategorySaveReqVO updateReqVO);
/**
* 删除分类
*
* @param id 编号
*/
void deleteCategory(Long id);
/**
* 获得分类
*
* @param id 编号
* @return 分类
*/
InfraCategoryDO getCategory(Long id);
/**
* 获得分类列表
*
* @param listReqVO 查询条件
* @return 分类列表
*/
List<InfraCategoryDO> getCategoryList(InfraCategoryListReqVO listReqVO);
}

View File

@ -0,0 +1,136 @@
package cn.iocoder.yudao.module.infra.service.demo;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
/**
* 分类 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class InfraCategoryServiceImpl implements InfraCategoryService {
@Resource
private InfraCategoryMapper categoryMapper;
@Override
public Long createCategory(InfraCategorySaveReqVO createReqVO) {
// 校验父编号的有效性
validateParentCategory(null, createReqVO.getParentId());
// 校验名字的唯一性
validateCategoryNameUnique(null, createReqVO.getParentId(), createReqVO.getName());
// 插入
InfraCategoryDO category = BeanUtils.toBean(createReqVO, InfraCategoryDO.class);
categoryMapper.insert(category);
// 返回
return category.getId();
}
@Override
public void updateCategory(InfraCategorySaveReqVO updateReqVO) {
// 校验存在
validateCategoryExists(updateReqVO.getId());
// 校验父编号的有效性
validateParentCategory(updateReqVO.getId(), updateReqVO.getParentId());
// 校验名字的唯一性
validateCategoryNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());
// 更新
InfraCategoryDO updateObj = BeanUtils.toBean(updateReqVO, InfraCategoryDO.class);
categoryMapper.updateById(updateObj);
}
@Override
public void deleteCategory(Long id) {
// 校验存在
validateCategoryExists(id);
// 校验是否有子分类
if (categoryMapper.selectCountByParentId(id) > 0) {
throw exception(CATEGORY_EXITS_CHILDREN);
}
// 删除
categoryMapper.deleteById(id);
}
private void validateCategoryExists(Long id) {
if (categoryMapper.selectById(id) == null) {
throw exception(CATEGORY_NOT_EXISTS);
}
}
private void validateParentCategory(Long id, Long parentId) {
if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) {
return;
}
// 1. 不能设置自己为父分类
if (Objects.equals(id, parentId)) {
throw exception(CATEGORY_PARENT_ERROR);
}
// 2. 父分类不存在
CategoryDO parentCategory = categoryMapper.selectById(parentId);
if (parentCategory == null) {
throw exception(CATEGORY_PARENT_NOT_EXITS);
}
// 3. 递归校验父分类,如果父分类是自己的子分类,则报错,避免形成环路
if (id == null) { // id 为空,说明新增,不需要考虑环路
return;
}
for (int i = 0; i < Short.MAX_VALUE; i++) {
// 3.1 校验环路
parentId = parentCategory.getParentId();
if (Objects.equals(id, parentId)) {
throw exception(CATEGORY_PARENT_IS_CHILD);
}
// 3.2 继续递归下一级父分类
if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) {
break;
}
parentCategory = categoryMapper.selectById(parentId);
if (parentCategory == null) {
break;
}
}
}
private void validateCategoryNameUnique(Long id, Long parentId, String name) {
CategoryDO category = categoryMapper.selectByParentIdAndName(parentId, name);
if (category == null) {
return;
}
// 如果 id 为空,说明不用比较是否为相同 id 的分类
if (id == null) {
throw exception(CATEGORY_NAME_DUPLICATE);
}
if (!Objects.equals(category.getId(), id)) {
throw exception(CATEGORY_NAME_DUPLICATE);
}
}
@Override
public InfraCategoryDO getCategory(Long id) {
return categoryMapper.selectById(id);
}
@Override
public List<InfraCategoryDO> getCategoryList(InfraCategoryListReqVO listReqVO) {
return categoryMapper.selectList(listReqVO);
}
}

Some files were not shown because too many files have changed in this diff Show More