在vue中把一个下拉框分成多个组并可以选择多个选项
通常一个字段如果有多个选择,我们会使用下拉框来表现数据,如支付类型(支付宝,微信,银行卡);但是有时候普通的下拉框满足不了业务需求,如我想选择多种支付方式,甚至如果支付方式又延伸为线上和线下两大类,线上为支付宝等等,线下为当面交易等等,这样页面的数据展示效果和页面与后台存值取值交互就变得更麻烦。但是vue已经为我们安排好了一切~本篇博客就以上述场景作为例子来写一个demo。 ...
通常一个字段如果有多个选择,我们会使用下拉框来表现数据,如支付类型(支付宝,微信,银行卡);但是有时候普通的下拉框满足不了业务需求,如我想选择多种支付方式,甚至如果支付方式又延伸为线上和线下两大类,线上为支付宝等等,线下为当面交易等等,这样页面的数据展示效果和页面与后台存值取值交互就变得更麻烦。但是vue已经为我们安排好了一切~
本篇博客就以上述场景作为例子来写一个demo。
效果图
建表以及初始数据SQL
-- 创建用户信息表
CREATE TABLE `user_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_name` varchar(255) NULL DEFAULT NULL COMMENT '用户名称',
`remark` text NULL COMMENT '备注',
`create_datetime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) COMMENT = '用户信息表';
-- 创建支付方式表
CREATE TABLE `pay_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`pay_name` varchar(255) NULL DEFAULT NULL COMMENT '支付方式名称',
`parent_id` bigint(20) NULL DEFAULT NULL COMMENT '父级标签id',
`remark` text NULL COMMENT '备注',
`create_datetime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) COMMENT = '支付方式表';
-- 创建用户支付方式关联表
CREATE TABLE `user_pay_rel` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_id` bigint(20) NULL DEFAULT NULL COMMENT '用户id',
`pay_info_id` bigint(20) NULL DEFAULT NULL COMMENT '支付方式id',
`remark` text NULL COMMENT '备注',
`create_datetime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) COMMENT = '用户支付方式关联表';
-- 插入初始数据
insert into user_info (user_name,remark) value ('斗苦故事','梦想成为全栈。');
insert into pay_info (pay_name,parent_id,remark) value ('线上',0,'线上支付方式(父)'),('支付宝',1,'线上支付方式(子)'),('微信',1,'线上支付方式(子)'),('网银支付',1,'线上支付方式(子)');
insert into pay_info (pay_name,parent_id,remark) value ('线下',0,'线下支付方式(父)'),('银行转账',5,'线下支付方式(子)'),('当面交易',5,'线下支付方式(子)');
insert into user_pay_rel (user_id,pay_info_id,remark) value (1,2,'使用支付宝支付'),(1,3,'使用微信支付'),(1,6,'到银行转账支付');
实体类 JavaBean
除了三张表各对应一个基础实体类外,还要额外创建三个扩展实体类
注:为了代码规范以及未来可扩展性,一般页面接收和返回的数据都会封装成一个扩展实体类
先分析一下下拉框多分组需要哪样的数据格式(对应上面的支付方式表的数据)
object : [
{
parentLabelName:'线上', -> payName
parentLabelValue:'1', -> id
child: [ childLabelName -> payName ; childLabelValue -> id
{childLabelName:'支付宝',childLabelValue:'2'},
{childLabelName:'微信',childLabelValue:'3'},
{childLabelName:'网银支付',childLabelValue:'4'}
]
},
{
parentLabelName:'线下', -> payName
parentLabelValue:'5', -> id
child: [ childLabelName -> payName ; childLabelValue -> id
{childLabelName:'银行转账',childLabelValue:'6'},
{childLabelName:'当面交易',childLabelValue:'7'}
]
},
]
- object:第一个扩展对象,里面封装着父级对象
- parentLabel:第二个扩展对象,里面封装着父级属性和子级对象
- child:第三个扩展对象,里面封装着子级属性
- 一共三层,最外面一层 object 就是返回到页面的主要对象,里面包含着父级集合(线上对象和线下对象),每一个父级对象中又包含一个子级集合(线上->支付宝/微信/网银支付 线下->银行转账/当面交易)
实体类 Java 代码
- 三个基础实体类
// UserInfo.java
private static final long serialVersionUID = 1L;
// 主键
private Long id;
// 用户名称
private String userName;
// 备注
private String remark;
// 创建时间
private Date createDatetime;
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
public String getUserName() {return userName;}
public void setUserName(String userName) {this.userName = userName;}
public String getRemark() {return remark;}
public void setRemark(String remark) {this.remark = remark;}
public Date getCreateDatetime() {return createDatetime;}
public void setCreateDatetime(Date createDatetime) {this.createDatetime = createDatetime;}
// PayInfo.java
private static final long serialVersionUID = 1L;
// 主键
private Long id;
// 支付方式名称
private String payName;
// 父级标签id
private Long parentId;
// 备注
private String remark;
// 创建时间
private Date createDatetime;
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
public String getPayName() {return payName;}
public void setPayName(String payName) {this.payName = payName;}
public Long getParentId() {return parentId;}
public void setParentId(Long parentId) {this.parentId = parentId;}
public String getRemark() {return remark;}
public void setRemark(String remark) {this.remark = remark;}
public Date getCreateDatetime() {return createDatetime;}
public void setCreateDatetime(Date createDatetime) {this.createDatetime = createDatetime;}
// UserPayRel.java
private static final long serialVersionUID = 1L;
// 主键
private Long id;
// 用户id
private Long userId;
// 支付方式id
private Long payInfoId;
// 备注
private String remark;
// 创建时间
private Date createDatetime;
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
public Long getUserId() {return userId;}
public void setUserId(Long userId) {this.userId = userId;}
public Long getPayInfoId() {return payInfoId;}
public void setPayInfoId(Long payInfoId) {this.payInfoId = payInfoId;}
public String getRemark() {return remark;}
public void setRemark(String remark) {this.remark = remark;}
public Date getCreateDatetime() {return createDatetime;}
public void setCreateDatetime(Date createDatetime) {this.createDatetime = createDatetime;}
- 第三个扩展对象,里面封装着子级属性
// PayChildDTO.java
private static final long serialVersionUID = 1L;
// 主键
private Long id;
// 支付方式名称
private String payName;
// 父级标签id
private Long parentId;
// 备注
private String remark;
// 创建时间
private Date createDatetime;
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
public String getPayName() {return payName;}
public void setPayName(String payName) {this.payName = payName;}
public Long getParentId() {return parentId;}
public void setParentId(Long parentId) {this.parentId = parentId;}
public String getRemark() {return remark;}
public void setRemark(String remark) {this.remark = remark;}
public Date getCreateDatetime() {return createDatetime;}
public void setCreateDatetime(Date createDatetime) {this.createDatetime = createDatetime;}
- 第二个扩展对象,里面封装着父级属性和子级集合对象
// PayParentDTO.java
private static final long serialVersionUID = 1L;
// 主键
private Long id;
// 支付方式名称
private String payName;
// 父级标签id
private Long parentId;
// 备注
private String remark;
// 创建时间
private Date createDatetime;
// 子级支付方式集合
private List<PayChildDTO> options;
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
public String getPayName() {return payName;}
public void setPayName(String payName) {this.payName = payName;}
public Long getParentId() {return parentId;}
public void setParentId(Long parentId) {this.parentId = parentId;}
public String getRemark() {return remark;}
public void setRemark(String remark) {this.remark = remark;}
public Date getCreateDatetime() {return createDatetime;}
public void setCreateDatetime(Date createDatetime) {this.createDatetime = createDatetime;}
public List<PayChildDTO> getOptions() {return options;}
public void setOptions(List<PayChildDTO> options) {this.options = options;}
- 第一个扩展对象,里面封装着父级集合对象
// PayResponseDTO.java
private static final long serialVersionUID = 1L;
// 父级支付方式集合
private List<PayParentDTO> options;
public List<PayParentDTO> getOptions() {return options;}
public void setOptions(List<PayParentDTO> options) {this.options = options;}
页面 vue 代码
payInfo.vue
1. <template>
<template>
<el-select @change="changeFun" multiple filterable v-model="relList" placeholder="请选择">
<el-option-group v-for="(group,index) in allPayInfo" :key="group.id" :label="group.payName">
<el-option v-for="(item,index) in group.options" :key="item.id" :label="item.payName" :value="item.id"></el-option>
</el-option-group>
</el-select>
</template>
- <el-select>:当选项过多时,使用下拉菜单展示并选择内容
- <el-select> -> @change:选中值发生变化时触发
- <el-select> -> multiple:是否能够多选
- <el-select> -> filterable:是否可搜索
- <el-select> -> v-model:下拉框绑定的值,值的格式为 String 类型的集合,取 <el-option> 中 :value 的值
- <el-select> -> placeholder:占位符
- <el-option-group>:下拉框分组
- <el-option-group> -> v-for:遍历父级集合
- <el-option-group> -> :key:分组唯一 key
- <el-option-group> -> :label:分组的组名
- <el-option>:下拉框的选择项
- <el-option> -> v-for:遍历父级对象中的子级集合
- <el-option> -> :key:下拉选择项的唯一 key
- <el-option> -> :label:选项的标签,若不设置则默认与 value 相同
- <el-option> -> :value:选项的值。每选择一次值后, <el-select> -> v-model 绑定的集合中就会添加所选择的值
2. <script>
<script>
import { getAllPayInfoApi , getUserPayRelByUserIdApi } from "@/api/payInfo/payInfo";
export default {
name: "payInfo",
data () {
return {
allPayInfo:[],
relList:[]
}
},
created () {
// 获取所有支付方式
this.getAllPayInfo();
// 根据用户id查询用户支付方式关联信息
this.getUserPayRelByUserId();
},
mounted () {},
methods : {
// 获取所有支付方式
getAllPayInfo(){
getAllPayInfoApi().then(res => {
this.allPayInfo = res.data.data.options;
console.log(this.allPayInfo);
});
},
// 根据用户id查询用户支付方式关联信息
getUserPayRelByUserId(){
getUserPayRelByUserIdApi(1).then(res => {
this.relList = res.data.data;
console.log(this.relList);
});
},
// 选中值发生变化时触发
changeFun() {
console.log(this.relList);
},
}
}
</script>
- import:导入两个查询请求接口
- name:全局ID
- data -> allPayInfo / relList:页面上绑定的两个集合(所有支付方式 / 用户支付方式关联信息)
- created:页面创建后触发的方法
- methods:自定义的方法
payInfo.js
// 获取所有支付方式
export function getAllPayInfoApi() {
return request({
url: `127.0.0.1:8080/payInfo/getAllPayInfo`,
method: 'post'
})
}
// 根据用户id查询用户支付方式关联信息
export function getUserPayRelByUserIdApi(id) {
return request({
url: `127.0.0.1:8080/userPayRel/getUserPayRelByUserId?userId=` + id,
method: 'post'
})
}
- 这两个接口就是调用的 Java 后台 controller,请求方式都是 post
- 第二个方法入参在 vue 页面的方法调用处,给的值是固定值 1,当然这里只是因为测试所以这样写,运用到项目中的时候可以动态传参进去
前端完整代码如下
// payInfo.vue
<template>
<div class="app-container">
<el-form>
<el-form-item label="支付方式:">
<template>
<el-select @change="changeFun" multiple filterable v-model="relList" placeholder="请选择">
<el-option-group v-for="(group,index) in allPayInfo" :key="group.id" :label="group.payName">
<el-option v-for="(item,index) in group.options" :key="item.id" :label="item.payName" :value="item.id"></el-option>
</el-option-group>
</el-select>
</template>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { getAllPayInfoApi , getUserPayRelByUserIdApi } from "@/api/payInfo/payInfo";
export default {
name: "payInfo",
data () {
return {
allPayInfo:[],
relList:[]
}
},
created () {
// 获取所有支付方式
this.getAllPayInfo();
// 根据用户id查询用户支付方式关联信息
this.getUserPayRelByUserId();
},
mounted () {},
methods : {
// 获取所有支付方式
getAllPayInfo(){
getAllPayInfoApi().then(res => {
this.allPayInfo = res.data.data.options;
console.log(this.allPayInfo);
});
},
// 根据用户id查询用户支付方式关联信息
getUserPayRelByUserId(){
getUserPayRelByUserIdApi(1).then(res => {
this.relList = res.data.data;
console.log(this.relList);
});
},
// 选中值发生变化时触发
changeFun() {
console.log(this.relList);
},
}
}
</script>
<style lang="scss">
</style>
// payInfo.js
import request from '@/这里写你项目中具体路径'
// 获取所有支付方式
export function getAllPayInfoApi() {
return request({
url: `127.0.0.1:8080/payInfo/getAllPayInfo`,
method: 'post'
})
}
// 根据用户id查询用户支付方式关联信息
export function getUserPayRelByUserIdApi(id) {
return request({
url: `127.0.0.1:8080/userPayRel/getUserPayRelByUserId?userId=` + id,
method: 'post'
})
}
后台 Java 逻辑代码
两个接口按照 url 请求到 Java 后台 controller,以及查询所有支付方式和根据 id 查询关联表数据 这两个单表查询操作就不详细写了,这里只把关键代码粘贴出来说明一下
1. 获取所有支付方式
- 方法返回的数据类型是 PayResponseDTO
- 业务逻辑代码如下
/*
* @ClassName PayInfoService
* @Desc TODO 获取所有支付方式
* @Date 2019/1/9 11:35
* @Version 1.0
*/
public PayResponseDTO getAllPayInfo(){
// 返回结果对象
PayResponseDTO result = new PayResponseDTO();
// 1. 查询出所有支付方式集合
// 注:当前 demo 数据量不大,如果正式环境中数据量比较大,建议此步操作从 redis 中取
List<PayInfo> allPayInfo = payInfoMapper.getAllPayInfo();
// 如果存在支付方式
if(allPayInfo.size()>0){
// 2. 放入父级
List<PayParentDTO> payParentDTOS = new ArrayList<>();
allPayInfo.forEach(
payInfo -> {
if(payInfo.getParentId()==0){
// 复制父级属性值
PayParentDTO payParentDTO = new PayParentDTO();
BeanUtils.copyProperties(payInfo,payParentDTO);
// 存入父级中
payParentDTOS.add(payParentDTO);
}
}
);
// 如果存在父级集合
if(payParentDTOS.size()>0){
// 3. 遍历父级,把父级下的所有子级放入当前遍历父级的子级集合中
payParentDTOS.forEach(
parentForEach -> {
// 每个父级 new 一个子级集合
List<PayChildDTO> payChildDTOS = new ArrayList<>();
// 遍历所有支付方式
allPayInfo.forEach(
allPayForEach -> {
if(parentForEach.getId()==allPayForEach.getParentId()){
// 复制子级属性值
PayChildDTO payChildDTO = new PayChildDTO();
BeanUtils.copyProperties(allPayForEach,payChildDTO);
payChildDTOS.add(payChildDTO);
}
}
);
// 每一个父级遍历结束后都存入子级
parentForEach.setOptions(payChildDTOS);
}
);
// 4. 父级遍历结束,父级集合放入响应对象中
result.setOptions(payParentDTOS);
}
}
// 5. 返回结果
return result;
}
- payInfoMapper.getAllPayInfo() 就是一个查询 pay_info 全部数据的操作(select 全部字段 from pay_info)
2. 根据用户 id 查询用户支付方式关联信息
- 方法返回的数据类型是 List<String>
- 业务逻辑代码如下
/*
* @ClassName UserPayRelService
* @Desc TODO 根据用户id查询用户支付方式关联信息
* @Date 2019/1/9 13:45
* @Version 1.0
*/
public List<String> getUserPayRelByUserId(Long userId){
// 返回集合对象
List<String> result = new ArrayList<>();
// 根据用户id查询用户支付方式关联信息
List<UserPayRel> userPayRelByUserId = userPayRelMapper.getUserPayRelByUserId(userId);
// 非空判断
if(userPayRelByUserId.size()>0){
// 遍历所有关联关系,把支付方式id存入返回集合对象中
userPayRelByUserId.forEach(userPayRel -> {
result.add(userPayRel.getPayInfoId().toString());
});
}
return result;
}
- userPayRelMapper.getUserPayRelByUserId(userId) 方法是根据 user_id 到 user_pay_rel 表中查询出关联数据操作(select 全部字段 from user_pay_rel where user_id = #{userId} )
到此此篇博客已经结束了
有不对的地方或有疑问的地方欢迎留言与我交流,探讨
欢迎来访我的vue专栏总篇博客
希望能够帮助到你
over
更多推荐
所有评论(0)