Vue3组件(15)封装一下联动下拉

话说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 折叠面板'
      }]
    }]
  }
]

这是官网给出来的实例数据,截取了两个大分类,这个,说实话,没感觉怎么清晰了,要说清晰呢,还得靠编辑器带来的辅助功能,才看的清晰,比如这样:


030联动数据1.png

把 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,结果空欢喜一场,这个只能控制选不选中,但是似乎不能增加新节点。

所以如果一定要用 搜索功能的话,大概只有转类型这种方式了,或者干脆用另一种方式来实现。

其他的功能还没有测试。。。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容