SpringCloud+Vue3一个字段多个存储以及回显

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云原生数据库 PolarDB 分布式版,标准版 2核8GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: SpringCloud+Vue3一个字段多个存储以及回显


♥️作者:小宋1021

🤵‍♂️个人主页:小宋1021主页

♥️坚持分析平时学习到的项目以及学习到的软件开发知识,和大家一起努力呀!!!

🎈🎈加油! 加油! 加油! 加油

🎈欢迎评论 💬点赞👍🏻 收藏 📂加关注+!



场景:

新发布一个补课班的班级课程,班级上课的科目不一定是一个有可能是多个,这就需要一个字段存储多条数据,主要的修改都在实现类里,下面我们来看怎样实现。

image.gif 编辑

数据库存储结果:

image.gif 编辑

存储过程:

数据库:

按照自己的字段需求定义即可,我定义的是varchar

后端:

实体类:

多定义一个subject2[]字段是为了前端传值的时候好拼接,留着以后有用,存储的时候只需要一个String[]类型的subject字段即可。

/**
     * 科目
     */
    private String subject;
    /**
     * 科目2
     */
    private String[] subject2;

image.gif

CourseManageSaveReqVO:

同样是String[]类型的数组,为了存储。

@Schema(description = "科目")
    private String[] subject;

image.gif

CourseManageRespVO:

在控制回显的VO里也要回显subject2,前端需要拼接

@Schema(description = "科目")
    @ExcelProperty("科目")
    private String subject;
    @Schema(description = "科目2")
    @ExcelProperty("科目2")
    private String[] subject2;

image.gif

Mapper:

直接继承BaseMapperX方法即可。

/**
 * 课程管理 Mapper
 *
 * @author 平台管理员
 */
@Mapper
public interface CourseManageMapper extends BaseMapperX<CourseManageDO> {
}

image.gif

Service层:

/**
 * 课程管理 Service 接口
 *
 * @author 平台管理员
 */
public interface CourseManageService {
    /**
     * 创建课程管理
     *
     * @param createReqVO 创建信息
     * @return 编号
     */
    Long createCourseManage(@Valid CourseManageSaveReqVO createReqVO);
}

image.gif

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);
    }
}

image.gif

下面我们来拆解一下新增方法:

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的方法直接插入。

到这存储过程就结束了,要注意的是实体类的类型要保持一致,要是数组类型。

回显过程:

前端:

我这里有一些是测试数据,有些数据没有填写完全,请大家忽略,只关注”科目“即可。

image.gif 编辑

主要方法:

/** 查询列表 */
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
  }
}

image.gif

   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
}

image.gif

 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>

image.gif

用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>

image.gif


目录
相关文章
|
存储 SpringCloudAlibaba Nacos
SpringCloud Alibaba核心组件Nacos【服务多级存储模型&配置集群】第2章(上)
SpringCloud Alibaba核心组件Nacos【服务多级存储模型&配置集群】第2章
SpringCloud Alibaba核心组件Nacos【服务多级存储模型&配置集群】第2章(上)
|
1月前
|
XML 前端开发 数据格式
echarts柱图前后端代码SpringCloud+Vue3
echarts柱图前后端代码SpringCloud+Vue3
52 1
|
6月前
|
存储 Nacos 数据安全/隐私保护
【SpringCloud】Nacos的安装、Nacos注册、Nacos服务多级存储模型
【SpringCloud】Nacos的安装、Nacos注册、Nacos服务多级存储模型
83 1
|
3月前
Vue3+SpringCloud实现图片上传和回显
Vue3+SpringCloud实现图片上传和回显
55 1
|
3月前
|
XML 前端开发 数据库
SpringCloud+Vue3主子表插入数据
SpringCloud+Vue3主子表插入数据
49 0
|
3月前
|
XML 前端开发 数据库
SpringCloud+Vue3主子表插入数据
SpringCloud+Vue3主子表插入数据
30 0
|
存储 SpringCloudAlibaba JavaScript
SpringCloud Alibaba核心组件Nacos【服务多级存储模型&配置集群】第2章(下)
SpringCloud Alibaba核心组件Nacos【服务多级存储模型&配置集群】第2章
|
存储 消息中间件 缓存
从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(十三)rocketmq 篇(3): 消息读写队列,消息存储,消息发送,消息消费关联流程和原理
从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(十三)rocketmq 篇(3): 消息读写队列,消息存储,消息发送,消息消费关联流程和原理
从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(十三)rocketmq 篇(3): 消息读写队列,消息存储,消息发送,消息消费关联流程和原理
|
存储 SpringCloudAlibaba JavaScript
SpringCloud Alibaba核心组件Nacos【服务多级存储模型&配置集群】第2章
本章主要诠释:服务集群属性和配置,服务分级存储是什么,什么是集群,为什么要引物服务分级,解决什么问题
SpringCloud Alibaba核心组件Nacos【服务多级存储模型&配置集群】第2章
|
存储 SpringCloudAlibaba Dubbo
SpringCloud Alibaba实战(3:存储设计与基础架构设计)
SpringCloud Alibaba实战(3:存储设计与基础架构设计)
303 0
SpringCloud Alibaba实战(3:存储设计与基础架构设计)