♥️作者:小宋1021
🤵♂️个人主页:小宋1021主页
♥️坚持分析平时学习到的项目以及学习到的软件开发知识,和大家一起努力呀!!!
🎈🎈加油! 加油! 加油! 加油
🎈欢迎评论 💬点赞👍🏻 收藏 📂加关注+!
场景:
新发布一个补课班的班级课程,班级上课的科目不一定是一个有可能是多个,这就需要一个字段存储多条数据,主要的修改都在实现类里,下面我们来看怎样实现。
编辑
数据库存储结果:
编辑
存储过程:
数据库:
按照自己的字段需求定义即可,我定义的是varchar
后端:
实体类:
多定义一个subject2[]字段是为了前端传值的时候好拼接,留着以后有用,存储的时候只需要一个String[]类型的subject字段即可。
/** * 科目 */ private String subject; /** * 科目2 */ private String[] subject2;
CourseManageSaveReqVO:
同样是String[]类型的数组,为了存储。
@Schema(description = "科目") private String[] subject;
CourseManageRespVO:
在控制回显的VO里也要回显subject2,前端需要拼接
@Schema(description = "科目") @ExcelProperty("科目") private String subject; @Schema(description = "科目2") @ExcelProperty("科目2") private String[] subject2;
Mapper:
直接继承BaseMapperX方法即可。
/** * 课程管理 Mapper * * @author 平台管理员 */ @Mapper public interface CourseManageMapper extends BaseMapperX<CourseManageDO> { }
Service层:
/** * 课程管理 Service 接口 * * @author 平台管理员 */ public interface CourseManageService { /** * 创建课程管理 * * @param createReqVO 创建信息 * @return 编号 */ Long createCourseManage(@Valid CourseManageSaveReqVO createReqVO); }
CourseManageServiceImpl实现类:
/** * 课程管理 Service 实现类 * * @author 平台管理员 */ @Service @Validated public class CourseManageServiceImpl implements CourseManageService { @Resource private CourseManageMapper courseManageMapper; @Override @LogRecord(type = TEACH_COURSE_MANAGE_TYPE, subType = TEACH_COURSE_MANAGE_SUB_TYPE, bizNo = "{{#teachCourseManage.id}}", success = TEACH_COURSE_MANAGE_CREATE_SUCCESS) public Long createCourseManage(CourseManageSaveReqVO createReqVO) { // 插入 CourseManageDO courseManage = BeanUtils.toBean(createReqVO, CourseManageDO.class); String[] subjects = createReqVO.getSubject(); courseManage.setSubject(String.join(",", subjects)); courseManageMapper.insert(courseManage); // 3. 记录操作日志上下文 LogRecordContext.putVariable("teachCourseManage", courseManage); // 返回 return courseManage.getId(); } @Override @LogRecord(type = TEACH_COURSE_MANAGE_TYPE, subType = TEACH_COURSE_MANAGE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", success = TEACH_COURSE_MANAGE_UPDATE_SUCCESS) public void updateCourseManage(CourseManageSaveReqVO updateReqVO) { // 校验存在 validateCourseManageExists(updateReqVO.getId()); CourseManageDO oldCourseManage = courseManageMapper.selectById(updateReqVO.getId()); // 更新 CourseManageDO updateObj = BeanUtils.toBean(updateReqVO, CourseManageDO.class); courseManageMapper.updateById(updateObj); // 3. 记录操作日志上下文 LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldCourseManage, CourseManageSaveReqVO.class)); LogRecordContext.putVariable("teachCourseManage", oldCourseManage); } }
下面我们来拆解一下新增方法:
CourseManageDO courseManage = BeanUtils.toBean(createReqVO, CourseManageDO.class);
这句话的作用是:
BeanUtils.toBean(createReqVO, CourseManageDO.class); 这行代码是将一个对象(createReqVO)的属性值复制到另一个对象(CourseManageDO的一个实例)的相应属性中。这个过程通常被称为“属性拷贝”或“Bean转换”。
这里的createReqVO是一个请求对象(Request Value Object),比如来自Web请求的表单数据或JSON数据经过反序列化后得到的对象。而CourseManageDO(Data Object)则是一个用于数据库操作或业务逻辑处理的数据对象。
String[] subjects = createReqVO.getSubject();
定义了一个名叫subjects的String类型的数据,来获取createReqVO传过来的subject的值。
courseManage.setSubject(String.join(",", subjects));
给courseManage实体类使用set方法赋值,并使用String.join方法拼接,这就使数据库存储的数据是2,1,0
courseManageMapper.insert(courseManage);
使用BaseMapperX的方法直接插入。
到这存储过程就结束了,要注意的是实体类的类型要保持一致,要是数组类型。
回显过程:
前端:
我这里有一些是测试数据,有些数据没有填写完全,请大家忽略,只关注”科目“即可。
编辑
主要方法:
/** 查询列表 */ const getList = async () => { loading.value = true try { const data = await CourseManageApi.getCourseManagePage(queryParams) list.value = data.list total.value = data.total const subjectList = [] const subjectList2 = [] subjectList.value = getIntDictOptions(DICT_TYPE.SUBJECT) list.value.forEach((item) => { if (item.subject) { item.subject2 = [] subjectList2.value = item.subject.split(',') subjectList.value.forEach(i => { subjectList2.value.forEach(e => { if (Number(e) === i.value) { console.log(item.subject2, typeof (item.subject2), 'asdsad') item.subject2.push(i.label) } }) }) } }) } finally { loading.value = false } }
const subjectList = []
const subjectList2 = []
这里定义了两个数组subjectList 用来接收后端传过来的subject的值,也就是存在数据库里的值,
subjectList2 用来去数据字典里查找匹配再进行回显
subjectList.value = getIntDictOptions(DICT_TYPE.SUBJECT)
把数据库存的值和数据字典里的值匹配再存到subjectList里
getIntDictOptions方法:
export const getIntDictOptions = (dictType: string): NumberDictDataType[] => { // 获得通用的 DictDataType 列表 const dictOptions: DictDataType[] = getDictOptions(dictType) // 转换成 number 类型的 NumberDictDataType 类型 // why 需要特殊转换:避免 IDEA 在 v-for="dict in getIntDictOptions(...)" 时,el-option 的 key 会告警 const dictOption: NumberDictDataType[] = [] dictOptions.forEach((dict: DictDataType) => { dictOption.push({ ...dict, value: parseInt(dict.value + '') }) }) return dictOption }
list.value.forEach((item) => {
if (item.subject) {
item.subject2 = []
subjectList2.value = item.subject.split(',')
subjectList.value.forEach(i => {
subjectList2.value.forEach(e => {
if (Number(e) === i.value) {
item.subject2.push(i.label)
}
})
})
}
})
}
这里用了两个forEach循环,item的箭头函数是vue3特有的语法,目的就是遍历传过来的值再与数据字典里的值一一匹配,匹配成功以后再通过push方法插入到subject2[]这个数组中,通过subject2进行回显
列表项:
<el-table-column label="科目" align="center" prop="subject2" show-overflow-tooltip="true"> <template #default="scope"> <el-tag v-for="(item, index) in scope.row.subject2" style="margin-right: 5px" :key="index" size="small"> {{ item }} </el-tag> </template> </el-table-column>
用el-tag和v-for遍历显示subject2数组中的每一个值,margin-right: 5px是为了每个科目之间有间距,show-overflow-tooltip="true"是隐藏显示, {{ item }}是插值表达式,插入到列表里。
全部代码:
<template> <div> <ContentWrap> <!-- 搜索工作栏 --> <el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form-item label="学员姓名" prop="studentName"> <el-input v-model="queryParams.studentName" placeholder="请输入学员姓名" clearable @keyup.enter="handleQuery" class="!w-240px" /> </el-form-item> <el-form-item label="手机号" prop="studentPhone"> <el-input v-model="queryParams.studentPhone" placeholder="请输入手机号" clearable @keyup.enter="handleQuery" class="!w-240px" /> </el-form-item> <el-form-item label="课程来源" prop="courseSource"> <el-select v-model="queryParams.courseSource" placeholder="请选择课程来源" clearable class="!w-240px"> <el-option v-for="dict in getIntDictOptions(DICT_TYPE.COURSE_SOURCE)" :key="dict.value" :label="dict.label" :value="dict.value" /> </el-select> </el-form-item> <el-form-item label="课程类型" prop="courseType"> <el-select v-model="queryParams.courseType" placeholder="请选择课程类型" clearable class="!w-240px"> <el-option v-for="dict in getIntDictOptions(DICT_TYPE.COURSE_TYPE)" :key="dict.value" :label="dict.label" :value="dict.value" /> </el-select> </el-form-item> <el-form-item label="年级" prop="grade"> <el-select v-model="queryParams.grade" placeholder="请选择年级" clearable class="!w-240px"> <el-option v-for="dict in getIntDictOptions(DICT_TYPE.GRADE)" :key="dict.value" :label="dict.label" :value="dict.value" /> </el-select> </el-form-item> <el-form-item label="科目" prop="subject"> <el-select v-model="queryParams.subject" placeholder="请选择科目" clearable class="!w-240px"> <el-option v-for="dict in getIntDictOptions(DICT_TYPE.SUBJECT)" :key="dict.value" :label="dict.label" :value="dict.value" /> </el-select> </el-form-item> <el-form-item label="学期" prop="term"> <el-select v-model="queryParams.term" placeholder="请选择学期" clearable class="!w-240px"> <el-option v-for="dict in getIntDictOptions(DICT_TYPE.TERM)" :key="dict.value" :label="dict.label" :value="dict.value" /> </el-select> </el-form-item> <el-form-item> <el-button @click="handleQuery"> <Icon icon="ep:search" class="mr-5px" /> 搜索 </el-button> <el-button @click="resetQuery"> <Icon icon="ep:refresh" class="mr-5px" /> 重置 </el-button> <el-button type="primary" plain @click="openForm('create')" v-hasPermi="['teach:course-manage:create']"> <Icon icon="ep:plus" class="mr-5px" /> 新增 </el-button> </el-form-item> </el-form> </ContentWrap> <!-- 列表 --> <ContentWrap> <el-table v-loading="loading" :data="list" :stripe="true"> <el-table-column label="序号" type="index" header-align="center" align="center" width="60px" fixed /> <el-table-column label="课程名称" align="center" prop="courseName" width="150px" /> <el-table-column label="课程来源" align="center" prop="courseSource"> <template #default="scope"> <dict-tag :type="DICT_TYPE.COURSE_SOURCE" :value="scope.row.courseSource" /> </template> </el-table-column> <el-table-column label="课程类型" align="center" prop="courseType"> <template #default="scope"> <dict-tag :type="DICT_TYPE.COURSE_TYPE" :value="scope.row.courseType" /> </template> </el-table-column> <el-table-column label="收费方式" align="center" prop="chargeMethod"> <template #default="scope"> <dict-tag :type="DICT_TYPE.CHARGE_METHOD" :value="scope.row.chargeMethod" /> </template> </el-table-column> <el-table-column label="定价标准" align="center" prop="priceStandard" /> <el-table-column label="课程状态" align="center" prop="courseStatus"> <template #default="scope"> <dict-tag :type="DICT_TYPE.COURSE_STATUS" :value="scope.row.courseStatus" /> </template> </el-table-column> <el-table-column label="年级" align="center" prop="grade" width="150px"> <template #default="scope"> <dict-tag :type="DICT_TYPE.GRADE" :value="scope.row.grade" /> </template> </el-table-column> <el-table-column label="科目" align="center" prop="subject2" show-overflow-tooltip="true"> <template #default="scope"> <el-tag v-for="(item, index) in scope.row.subject2" style="margin-right: 5px" :key="index" size="small"> {{ item }} </el-tag> </template> </el-table-column> <el-table-column label="学期" align="center" prop="term"> <template #default="scope"> <dict-tag :type="DICT_TYPE.TERM" :value="scope.row.term" /> </template> </el-table-column> <el-table-column label="创建人" align="center" prop="creator"> <template #default="scope"> {{ userList.find((user) => user.id === scope.row.creator)?.nickname }} </template> </el-table-column> <el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="180px" /> <el-table-column label="操作" align="center" fixed="right" width="200px"> <template #default="scope"> <el-button link type="primary" @click="openForm('update', scope.row.id)" v-hasPermi="['teach:course-manage:update']"> 编辑 </el-button> <el-button link type="primary" @click="details('update', scope.row.id)" v-hasPermi="['study:plan:delete']"> 详情 </el-button> <el-button link type="primary" @click="openForm('update', scope.row.id)" v-hasPermi="['teach:course-manage:update']"> 启用 </el-button> <el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['teach:course-manage:delete']"> 停用 </el-button> <!-- <el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['teach:course-manage:delete']" > 删除 </el-button> --> </template> </el-table-column> </el-table> <!-- 分页 --> <Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" @pagination="getList" /> </ContentWrap> </div> <!-- 表单弹窗:添加/修改 --> <CourseManageForm ref="formRef" @success="getList" /> <!-- 表单弹窗:详情 --> <el-drawer v-model="drawer" title="详情" :direction="direction" v-if="drawer" size="71%" class="drawer" destory-on-close> <DetailForm ref="detailRef" :detailId="detailId" /> </el-drawer> </template> <script setup lang="ts"> import { getIntDictOptions, DICT_TYPE, getStrDictOptions } from '@/utils/dict' import { dateFormatter } from '@/utils/formatTime' import download from '@/utils/download' import { CourseManageApi, CourseManageVO } from '@/api/teach/coursemanage' import DetailForm from '@/views/teach/coursemanagePlus/Index.vue' import CourseManageForm from './CourseManageForm.vue' import type { DrawerProps } from 'element-plus' import { ref } from 'vue'; import * as UserApi from '@/api/system/user' const userList = ref<UserApi.UserVO[]>([]) /** 课程管理 列表 */ defineOptions({ name: 'CourseManage' }) const message = useMessage() // 消息弹窗 const { t } = useI18n() // 国际化 const direction = ref<DrawerProps['direction']>('rtl') const loading = ref(true) // 列表的加载中 const list = ref<CourseManageVO[]>([]) // 列表的数据 const total = ref(0) // 列表的总页数 const queryParams = reactive({ pageNo: 1, pageSize: 10, courseType: undefined, grade: undefined, subject: undefined, term: undefined, studentName: undefined, studentPhone: undefined, courseSource: undefined, }) const queryFormRef = ref() // 搜索的表单 /** 查询列表 */ const getList = async () => { loading.value = true try { const data = await CourseManageApi.getCourseManagePage(queryParams) list.value = data.list total.value = data.total const subjectList = [] const subjectList2 = [] subjectList.value = getIntDictOptions(DICT_TYPE.SUBJECT) list.value.forEach((item) => { if (item.subject) { item.subject2 = [] subjectList2.value = item.subject.split(',') subjectList.value.forEach(i => { subjectList2.value.forEach(e => { if (Number(e) === i.value) { console.log(item.subject2, typeof (item.subject2), 'asdsad') item.subject2.push(i.label) } }) }) } }) } finally { loading.value = false } } /** 搜索按钮操作 */ const handleQuery = () => { queryParams.pageNo = 1 getList() } /** 查看详情 */ const detailRef = ref() const drawer = ref(false) const detailId = ref() const details = (type: string, id?: number) => { drawer.value = true detailId.value = id } /** 重置按钮操作 */ const resetQuery = () => { queryFormRef.value.resetFields() handleQuery() } /** 添加/修改操作 */ const formRef = ref() const openForm = (type: string, id?: number) => { formRef.value.open(type, id) } /** 删除按钮操作 */ const handleDelete = async (id: number) => { try { // 删除的二次确认 await message.delConfirm() // 发起删除 await CourseManageApi.deleteCourseManage(id) message.success(t('common.delSuccess')) // 刷新列表 await getList() } catch { } } /** 初始化 **/ onMounted(async () => { getList() userList.value = await UserApi.getSimpleUserList() }) </script> <style scoped lang="scss"> .drawer { width: 70% !important; } :deep(.el-table .cell) { overflow: visible; } </style>