话说UI库提供的功能是越来越强了,比如这个 element 的 Cascader 实在是太强大了,拿过来准备好数据就可以直接使用了。
那么为啥还要封装一下呢?一个是为了封装表单控件统一风格,这样可以统一管理,
另一个就是想小改一下数据结构。
Cascader 默认的数据结构是前端非常喜欢的“套娃”式结构,这个嘛前端正常,但是对于后端来说就很头痛了,因为要转换一下才行,这个不多说了,以免引发争吵,这里是想解决问题,而不是推脱踢皮球。
后端的数据库里的数据都是平列的,兄弟结构,给出多个单独的数据是比较方便的,给出套娃的数据大概要用递归的方式来修改一下。
那么能不能不改数据结构直接使用呢?当然是可以的,Cascader 提供了一种 动态加载 的方式,我们可以利用这个回调函数来实现。
默认的数据结构
const options = [ { value: 'zhinan', label: '指南', children: [{ value: 'shejiyuanze', label: '设计原则', children: [{ value: 'yizhi', label: '一致' }, { value: 'fankui', label: '反馈' }, { value: 'xiaolv', label: '效率' }, { value: 'kekong', label: '可控' }] }, { value: 'daohang', label: '导航', children: [{ value: 'cexiangdaohang', label: '侧向导航' }, { value: 'dingbudaohang', label: '顶部导航' }] }] }, { value: 'zujian', label: '组件', 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: 'NavMenu 导航菜单' }, { 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 折叠面板' }] }] } ] 复制代码
这是官网给出来的实例数据,截取了两个大分类,这个,说实话,没感觉怎么清晰了,要说清晰呢,还得靠编辑器带来的辅助功能,才看的清晰,比如这样:
把 children 折叠起来之后,看起来才清晰一些。
后端常见的数据结构
const foo = { levelName: ['foo1', 'foo2', 'foo3'], foo1: [ { value: 'zhinan', label: '指南1' }, { value: 'zujian', label: '组件1' }, { value: 'ziyuan', label: '资源1' } ], foo2: [ { pId: 'zhinan', value: 'shejiyuanze', label: '设计原则' }, { pId: 'zhinan', value: 'daohang', label: '导航', leaf: true }, { pId: 'zujian', value: 'basic', label: 'basic', leaf: true }, { pId: 'zujian', value: 'form', label: 'form', leaf: true } ], foo3: [ { pId: 'shejiyuanze', value: 'yizhi', label: '一致', leaf: true }, { pId: 'shejiyuanze', value: 'fankui', label: '反馈', leaf: true } ] } 复制代码
好吧,好像也不太好看,我们不讨论这个了,还是看看代码如何实现吧。
对了,foo1 表示第一级的数据,foo2是第二级的数据,foo3是第三级的数据,名称的等级关系由levelName来决定,所以叫啥名字是无所谓的。
使用 lazyLoad 直接支持后端数据
先看看代码
lazyLoad (node, resolve) { // 响应函数 console.log('node', node) const { level } = node // 判断级数是否超过数组下标 if (levelName.length >= level) { // 找到子数据- console.log(levelName[level]) const key = levelName[level] const newNode = data[key].filter((item) => item.pId === node.value) resolve(newNode) // 交给组件 } else { resolve([{ value: '22', label: '选项11', leaf: true }]) } } 复制代码
根据选择的level,加载对应的数据,然后用filter查找pId === 选中值的数据,然后返回给 cascader 即可。
好吧,上面的代码比较粗糙,好像else部分没有被执行,另外还需要数据设置 leaf属性,这个也是比较麻烦的,后面再改进。
总之这样处理一下 Cascader 就可以直接使用后端的兄弟级别的数据了。
你看,我们谁都没有转变数据格式,他不香吗?
而且这个函数也可以封装起来,统一使用,不必每次都写。
不足
当然也有不足的地方, Cascader 还提供了一个 “可搜索” 的功能,这个功能也是很厉害的,只是这个是在套娃的数据里面去查询,也就是说我上面写得那个代码废废了。
找了半天找到了 filter-method,结果空欢喜一场,这个只能控制选不选中,但是似乎不能增加新节点。
所以如果一定要用 搜索功能的话,大概只有转类型这种方式了,或者干脆用另一种方式来实现。
其他的功能还没有测试。。。