目录
1.对于这种有层次的逐级查看方式,便采用了Cascader 级联选择器组件。
2.查看其源代码可以知道前端大概需要的数据结构(父子级嵌套)
3.由于后台的代码结构可以满足,但是他的字段不一定是value、label这种
最近碰到一个需求:要做一个省市区页面展示,网上百度一番之后发现地图组件element-china-area-data很不错,能大大简化前后端工作。但是领导说地区数据源不好控制,于是便自己做了一个自定义的。由此便记录一下。(我们是全栈哈,所以前后端都会讲到)。
前端(vue3+Element Plus):
1.对于这种有层次的逐级查看方式,便采用了Cascader 级联选择器组件。
2.查看其源代码可以知道前端大概需要的数据结构(父子级嵌套)
<template>
<el-cascader :options="options" clearable />
</template>
<script lang="ts" setup>
const options = [
{
value: 'guide',
label: 'Guide',
children: [
{
value: 'disciplines',
label: 'Disciplines',
children: [
{
value: 'consistency',
label: 'Consistency',
},
{
value: 'feedback',
label: 'Feedback',
},
{
value: 'efficiency',
label: 'Efficiency',
},
{
value: 'controllability',
label: 'Controllability',
},
],
},
{
value: 'navigation',
label: 'Navigation',
children: [
{
value: 'side nav',
label: 'Side Navigation',
},
{
value: 'top nav',
label: 'Top Navigation',
},
],
},
],
},
{
value: 'component',
label: 'Component',
children: [
{
value: 'basic',
label: 'Basic',
children: [
{
value: 'layout',
label: 'Layout',
},
{
value: 'color',
label: 'Color',
},
{
value: 'typography',
label: 'Typography',
},
{
value: 'icon',
label: 'Icon',
},
{
value: 'button',
label: 'Button',
},
],
},
{
value: 'form',
label: 'Form',
children: [
{
value: 'radio',
label: 'Radio',
},
{
value: 'checkbox',
label: 'Checkbox',
},
{
value: 'input',
label: 'Input',
},
{
value: 'input-number',
label: 'InputNumber',
},
{
value: 'select',
label: 'Select',
},
{
value: 'cascader',
label: 'Cascader',
},
{
value: 'switch',
label: 'Switch',
},
{
value: 'slider',
label: 'Slider',
},
{
value: 'time-picker',
label: 'TimePicker',
},
{
value: 'date-picker',
label: 'DatePicker',
},
{
value: 'datetime-picker',
label: 'DateTimePicker',
},
{
value: 'upload',
label: 'Upload',
},
{
value: 'rate',
label: 'Rate',
},
{
value: 'form',
label: 'Form',
},
],
},
{
value: 'data',
label: 'Data',
children: [
{
value: 'table',
label: 'Table',
},
{
value: 'tag',
label: 'Tag',
},
{
value: 'progress',
label: 'Progress',
},
{
value: 'tree',
label: 'Tree',
},
{
value: 'pagination',
label: 'Pagination',
},
{
value: 'badge',
label: 'Badge',
},
],
},
{
value: 'notice',
label: 'Notice',
children: [
{
value: 'alert',
label: 'Alert',
},
{
value: 'loading',
label: 'Loading',
},
{
value: 'message',
label: 'Message',
},
{
value: 'message-box',
label: 'MessageBox',
},
{
value: 'notification',
label: 'Notification',
},
],
},
{
value: 'navigation',
label: 'Navigation',
children: [
{
value: 'menu',
label: 'Menu',
},
{
value: 'tabs',
label: 'Tabs',
},
{
value: 'breadcrumb',
label: 'Breadcrumb',
},
{
value: 'dropdown',
label: 'Dropdown',
},
{
value: 'steps',
label: 'Steps',
},
],
},
{
value: 'others',
label: 'Others',
children: [
{
value: 'dialog',
label: 'Dialog',
},
{
value: 'tooltip',
label: 'Tooltip',
},
{
value: 'popover',
label: 'Popover',
},
{
value: 'card',
label: 'Card',
},
{
value: 'carousel',
label: 'Carousel',
},
{
value: 'collapse',
label: 'Collapse',
},
],
},
],
},
{
value: 'resource',
label: 'Resource',
children: [
{
value: 'axure',
label: 'Axure Components',
},
{
value: 'sketch',
label: 'Sketch Templates',
},
{
value: 'docs',
label: 'Design Documentation',
},
],
},
]
</script>
3.由于后台的代码结构可以满足,但是他的字段不一定是value、label这种
答:通过属性props来转换字段
看代码:
<el-form-item label="地区" prop="id">
<el-cascader :options="areaDictDataList" clearable :props="optionProps" v-model="detailAddressOptions" />
</el-form-item>
const optionProps = ref({
checkStrictly: true,
value: "id",
label: "name",
children: "child"
})
4.调用后端接口
/** 查询地区数据 */
function getRegionDictData() {
queryRegionData(queryParams.value).then(response => {
regionDictDataList.value = response.data;
});
}
5.有同学可能会遇到编辑,地区没法回显呀?
答:好吧,不瞒你说,我也遇到了。。。
言归正传,当我们选中了省市区之后呢,我们传给后端的其实是一个数组字段,编辑回显拿到的后端的字段也是数组字段,我没有回显的原因是因为数据类型不一样,前端是整形数组,后端是字符数组,统一之后就正常回显啦。网上那些方法我试了对我没有用
贴一下图让大家瞅瞅
后端(java+mybatisplus+mysql):
1.数据库
CREATE TABLE `ces_diqu` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增长id',
`parent_id` int(10) unsigned DEFAULT NULL COMMENT '父级id',
`type` mediumint(8) unsigned DEFAULT NULL COMMENT '区域级别:1省,2:市,3:区',
`name` varchar(50) NOT NULL COMMENT '地区名',
`create_by` varchar(20) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(20) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3370 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='地区表';
2.CesDiquMapper.xml写法,
1.特别注意里面的resultMap写法,有几层嵌套就有有几个collection
2.getCustomSqlSegment相当于where条件,这里我加了sr1.type=1(在第五步接口实现类有提)
<?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="com.ruoyi.system.mapper.CesDiquMapper">
<resultMap type="com.ruoyi.system.domain.CesDiqu" id="CesDiquResult">
<result property="id" column="id"/>
<result property="parentId" column="parent_id"/>
<result property="type" column="type"/>
<result property="name" column="name"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<collection property="child" ofType="com.ruoyi.system.domain.CesDiqu">
<id column="id_2" property="id"/>
<result column="parent_id_2" property="parentId"/>
<result column="name_2" property="name"/>
<collection property="child" ofType="com.ruoyi.system.domain.CesDiqu">
<id column="id_3" property="id"/>
<result column="parent_id_3" property="parentId"/>
<result column="name_3" property="name"/>
</collection>
</collection>
</resultMap>
<select id="queryAllDictData" resultMap="CesDiquResult">
SELECT sr1.id,
sr1.parent_id,
sr1.name,
sr2.id as id_2,
sr2.parent_id as parent_id_2,
sr2.name as name_2,
sr3.id as id_3,
sr3.parent_id as parent_id_3,
sr3.name as name_3
FROM ces_diqu sr1
left join
ces_diqu sr2 on sr1.id = sr2.parent_id
left join ces_diqu sr3
on sr2.id = sr3.parent_id
${ew.getCustomSqlSegment};
</select>
</mapper>
注意:这里用的右连接,内连接会丢数据
3.实体类(注意里面的嵌套子集)
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* @Author: 菜鸟
* @Date: 2024/06/22/21:08
* @Description: 地区对象 ces_diqu
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("ces_diqu")
public class CesDiqu extends BaseEntity {
private static final long serialVersionUID=1L;
/**
* 自增长id
*/
@TableId(value = "id")
private Long id;
/**
* 父级id
*/
private Integer parentId;
/**
* 区域级别:1省,2:市,3:区
*/
private Integer type;
/**
* 地区名
*/
private String name;
/**
* 子集地区数据
*/
@TableField(exist = false)
private List<CesDiqu> child;
}
4.mapper写法
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @Author: 菜鸟
* @Date: 2024/06/22/21:12
* @Description: 地区Mapper接口
*/
public interface CesDiquMapper extends BaseMapperPlus<CesDiquMapper, CesDiqu, CesDiquVo> {
/**
* 查询地区数据
*
* @return 省市区数据
*/
List<CesDiqu> queryAllDictData(@Param(Constants.WRAPPER) Wrapper<CesDiqu> queryWrapper);
}
5.接口实现类
6.返回数据结构
剩下的的后台代码就不贴了,重点都在上面了
看看效果吧
总结
1.上述方式合适数据量小的,数据量大了就会有性能问题,此时就需要动态加载,与之对应的后端接口也得调整,这部分优化我会放到下一篇去讲。
2.后端的难点主要在于父子级数据的查询,主要是连表查询。不熟的同学可以多看看连表
其实我对连表查询也很生疏。。。
3.有同学可能会问,你地区数据哪来的?
答:爬虫搞来的
4.我的前端就入门水平,前端代码轻喷
5.有不足的地方欢迎大家一起讨论!