♥️作者:小宋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的方法直接插入。
到这存储过程就结束了,要注意的是实体类的类型要保持一致,要是数组类型。
前端:
<el-form-item label="科目" prop="subject">
<el-select v-model="formData.subject"
multiple
placeholder="请选择科目">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.SUBJECT)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
const formData = ref({
id: undefined,
courseName: undefined,
courseType: undefined,
chargeMethod: undefined,
priceStandard: undefined,
courseStatus: undefined,
grade: undefined,
subject: [],
term: undefined,
studentName: undefined,
studentPhone: undefined,
courseSource: undefined,
courseApplicationScope: undefined,
remark: undefined,
deduceClassRules: undefined,
operaName: undefined,
operaTime: undefined,
operaType: undefined,
operaExplain: undefined,
coursePrice: undefined,
courseStudyTime: undefined,
discount: undefined,
giftCourseNumber: undefined,
})
回显过程:
前端:
我这里有一些是测试数据,有些数据没有填写完全,请大家忽略,只关注”科目“即可。
主要方法:
/** 查询列表 */
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>