Vue封装组件系列文章
- 如何实现一个这样的级联组件
- 如何实现一个这样的级联组件
组件背景
根据产品原型实现一个级联组件,下面看演示图
应用场景很多,如:后台管理系统,旅游系统,广告投放系统,营销系统...等,现在流行
Vue
,React
,Anagular
三大框架,下面看看怎么使用Vue
实现
实现逻辑
产品经理的评审功能需求如下
- 根据大分类到子分类层级选择,无层级限制(根据UI的横板宽度,适合做多级,但深度很深的场景并不多)
- 每个层级支持全选,根据子级可以推导全选项选中,并对其父级执行选中操作
- 已选层级可显示出结果列表,可对其结果操作,并有快速清空结果功能
- 分类名称字数并不做限制,待选区域分类名称应在该项中居中显示,长度过长换行显示
- 结果选项结构简化,每项固定一行,过长在尾部出现
...
代表过长,鼠标移上时显示全部内容
思路
Vue.js 的核心包括一套“响应式系统”。
"响应式",开发思路跟Jquery的开发思路完全不同。
“响应式”,是指当数据改变后,Vue 会通知到使用该数据的代码。例如,视图渲染中使用了数据,数据改变后,视图也会自动更新。
根据地区数据 JSON
可以看出其结构
[
{
"value": "中国",
"key": 1156,
"id": 1156,
"children": [
{
"value": "北京市",
"id": 10000,
"key": 10000,
"children": []
},
{
"value": "河北省",
"key": 200107,
"id": 200107,
"children": [
{
"value": "石家庄",
"key": 20010701,
"id": 20010701
},
{
"value": "唐山市",
"key": 20010702,
"id": 20010702,
"children": [
{
"value": "路南区",
"key": 2001070201,
"id": 2001070201,
"children": []
}
]
}
]
}
]
复制代码
- 中国
- 直辖市
- xx省
- xx市
- xx区
- xx市
- xx县
- xx市
待选数据组件
这是一个循环嵌套的数据对象,而组件嵌套似乎不能满足产品需求,如果使用数组来代替层级,似乎可以解决数据嵌套的问题
array => level 1 -> level 2 -> level 3 -> level 4
level 1 => current, children => level 2 (array) level 2 => current, children => level 3 (array) ...
每个
level
都是一个整体,
- 有标题
title
- 有全选 计算data中是否都选中
select
- 子集的集合数据
data
- 有当前选中
current
- 标记当期层级 数组的索引
level
首先定义个空的数组代表组件
const array = []
把数据处理成数组格式就能展开这个组件,那怎么处理数据呢 初始化组件时不是所有都显示,必须让用户选择当前一个顶级大类
拿到所有顶级大类,并构建第一个元素
- title = 省级
- data = 顶级大类
- current = 空
- level = 1
- select = false
array.push({title, select, data, current, level})
在选择顶级大类时,给这个数组增加其一个子集元素
array.push({title, select, data, current, level})
...
依次类推
结果选择器
获取组件的选择结果, 可以过滤数据的check 属性得到, 可使用Vue的计算属性得知随时的结果
结果选择框可以直接绑定已选的计算组件,可构建结果UI
组件构想
- 主组件
- 布局组件
- 选择项
主组件 Selecter
用来负责组件框架, 左右分栏, 左边是选择区域, 右边是结果区域 这个是组件引用层,统一对外提供导入props 数据
和 导出的 emit 事件
组件需要做到完全配置化,内部所以参数需要被抽象
- 选择区 更具层级平均分配空间,所有在横向固定空间中,不能做过多的层级,太窄了没法显示 因为需要循环显示其层级,抽离层级为布局组件,布局组件由
标题
和滚动的选择区域
组成
<Row>
<Col :span="col" v-for="(box, idx) in resource" :key="idx">
<select-item :title="box.title">
<select-box v-model="box.current" :data="box.data" :level="box.level" @on-child="pushChild" @on-select="selectAll" />
select-item>
Col>
Row>
复制代码
- 结果区 在有选择时才显示,有标题栏显示,结果区可统计结果个数,选择项使用Tag标签,支持快速删除,建立纵向滚动条 可使用布局组件 与选择区保持风格统一,
<Col span="7" offset="1">
<select-item v-if="resultLen && transfer" title="已选" clear @on-clear="$emit('on-clear', {list: data})">
<div v-for="item in result" :key="item.id" class="c-pop-tip">
<Tag :name="item.value" closable class="c-tag-item" @on-close="handleClose">{{item.value}}Tag>
div>
select-item>
Col>
复制代码
布局组件 item
要兼容选择区与结果区使用,所以统计个数得有开关控制, 边框,颜色 UI 控制
抽象 清空按钮UI 抽象 统计个数UI
选择项 box(子组件)
最关键的组件就算这个了
逻辑
双向绑定
v-model 绑定数据的好处是: 数据在内部发生了改变,而在原始端同样改变了,只要使用就可以了, 当然在使用上也有些不方便的地方, props导入的数据,通过什么props 属性接收呢, value
...
props: {
value: {
type: Array
}
}
...
复制代码
在组件内部是不能Set 改变的,只能通过事件传到父组件中来 通过什么方法名来传呢, input
(初级很多人不知道)this.$emit('input', val)
原始数据构建选择层级组件
在初始化过程中,构建第一层级组件的 title
data
current
level
假使省市json 数据为 cityJson
构建第一层级的data
const data = this.cityJson.map(ret => {
delete ret.children
return ret
})
复制代码
当用户选择层级的 item
时触发 动作新增层级数据 当用户选中层级的 item
时触发 动作新增层级数据 选中该层级下所有数据
全选
清空
删除
代码
贴上所有源代码,难免里面有些引用的文件,如果不能直接使用,请不要喷,因为这篇文章不是送个伸手党的,是你有一定的基础,想提升一下技能的你
主组件 Selecter
{{item.value}}
复制代码
布局组件 item
{{title}}
清空全部
复制代码
选择项(子组件)box
全选
{{item.value}}
{{item.value}}
复制代码
优化体验
-
半选功能 在一个大分类的子分类里选择的分类,但是切到别的大类项,虽然结果框里有选择的分类,但是待选的框里还是不能显示子集,需求上线后,客户反应体验不好,所以就研究了复选框的
半选
状态,其实改起来很简单,只要在计算属性的加个布尔值显示半选,布尔值就是该分类的data
里是否有选中的项check = true
-
行内文本过长,换行显示优化 因为分类的字数没有限制,做前端其实不能相信用户,同时也不能相信后端返回给的数据,也不能相信产品,在产品没有碰到过字数限制的功能时候产生的问题时,都是期待着用户是个正常的用户的。
- 文本过长有两种方式解决:
- 在文本区域设置固定宽度,在超过长度显示... (如果要显示全,只能增加鼠标悬停显示功能了)
- 在
item
行的高度不使用line-height
的参数,用padding
做上下间隔后,让文本自动换行 (这样的问题是,右手边图标的居中问题,字数太多就会加高item
项,美观度没那么统一)
- 文本过长有两种方式解决:
经验总结
很多前端新人都接触Vue一年、甚至两年多才会使用像element ui
、iview
、vant
开源的UI基础库,但细心的你可能发现,这些只适合参照原型图实现html编码,但业务的层次抽离、逻辑的复用、组件化业务层方面都没有手把手教我们上路。
三大流行框架的核心是快速地组件化开发,而我们只是简单的在路由组件页面堆积UI库的组件吗,显然这不是我们想要的高效开发。一个项目可以大到100多个页面,如果不抽离组件,重复工作量不可预估,效率更是谈不上了。那么如何像作者一样能更深层次使用Vue呢,其实element ui的开源库,每一个组件的实现其实都是很基础的方法实现的,假如你要实现这样的基础库,你就会想办法去看源代码,看着看着你就学会了作者的很多思想,那还会有什么的组件实现不了了?
师傅领进门,修行靠个人,人人都是我们的老师。不知你是否赞成...
以上,欢迎拍砖~
欢迎关注我的开源仓库 GITHUB:xiejunping (Cabber) · GitHub 微信二维码: 扫码添加好友,交个朋友