如何实现一个这样的级联组件

Vue封装组件系列文章

  • 如何实现一个这样的级联组件
  • 如何实现一个这样的级联组件

组件背景

根据产品原型实现一个级联组件,下面看演示图

应用场景很多,如:后台管理系统,旅游系统,广告投放系统,营销系统...等,现在流行VueReactAnagular 三大框架,下面看看怎么使用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县

待选数据组件

这是一个循环嵌套的数据对象,而组件嵌套似乎不能满足产品需求,如果使用数组来代替层级,似乎可以解决数据嵌套的问题

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




复制代码

选择项(子组件)box




复制代码

优化体验

  • 半选功能 在一个大分类的子分类里选择的分类,但是切到别的大类项,虽然结果框里有选择的分类,但是待选的框里还是不能显示子集,需求上线后,客户反应体验不好,所以就研究了复选框的 半选状态,其实改起来很简单,只要在计算属性的加个布尔值显示半选,布尔值就是该分类的data里是否有选中的项check = true

  • 行内文本过长,换行显示优化 因为分类的字数没有限制,做前端其实不能相信用户,同时也不能相信后端返回给的数据,也不能相信产品,在产品没有碰到过字数限制的功能时候产生的问题时,都是期待着用户是个正常的用户的。

    • 文本过长有两种方式解决:
      • 在文本区域设置固定宽度,在超过长度显示... (如果要显示全,只能增加鼠标悬停显示功能了)
      • item 行的高度不使用line-height的参数,用padding 做上下间隔后,让文本自动换行 (这样的问题是,右手边图标的居中问题,字数太多就会加高item项,美观度没那么统一)

经验总结

很多前端新人都接触Vue一年、甚至两年多才会使用像element uiiviewvant开源的UI基础库,但细心的你可能发现,这些只适合参照原型图实现html编码,但业务的层次抽离、逻辑的复用、组件化业务层方面都没有手把手教我们上路。

三大流行框架的核心是快速地组件化开发,而我们只是简单的在路由组件页面堆积UI库的组件吗,显然这不是我们想要的高效开发。一个项目可以大到100多个页面,如果不抽离组件,重复工作量不可预估,效率更是谈不上了。那么如何像作者一样能更深层次使用Vue呢,其实element ui的开源库,每一个组件的实现其实都是很基础的方法实现的,假如你要实现这样的基础库,你就会想办法去看源代码,看着看着你就学会了作者的很多思想,那还会有什么的组件实现不了了?

师傅领进门,修行靠个人,人人都是我们的老师。不知你是否赞成...

以上,欢迎拍砖~


欢迎关注我的开源仓库 GITHUB:xiejunping (Cabber) · GitHub 微信二维码: 扫码添加好友,交个朋友

转载于:https://juejin.im/post/5d3024dc6fb9a07ea420b62f

你可能感兴趣的:(如何实现一个这样的级联组件)