在前端实现树目录,首先我们要想想好数据结构,分单级与多级两种情况如图所示:
json数据
{ "code": "00000", "data": [ { "uniq": "chap2022012113252715", "type": "0", "cname": "米国8", "parent_uniq": "root", "file_type": "", "file_uniq": "", "knowledge_point": "米国", "introduction": "米国", "created_time": "2022-01-21 13:25:27", "children": [] }, { "uniq": "chap2022012113260216", "type": "0", "cname": "米国18", "parent_uniq": "root", "file_type": "", "file_uniq": "", "knowledge_point": "米国", "introduction": "米国", "created_time": "2022-01-21 13:26:02", "children": [] }, { "uniq": "chap2022012113261017", "type": "0", "cname": "乌拉", "parent_uniq": "root", "file_type": "", "file_uniq": "", "knowledge_point": "乌拉", "introduction": "乌拉", "created_time": "2022-01-21 13:26:10", "children": [ { "uniq": "chap2022012113265020", "type": "0", "cname": "2", "parent_uniq": "chap2022012113261017", "file_type": "", "file_uniq": "", "knowledge_point": "28", "introduction": "2", "created_time": "2022-01-21 13:26:50", "children": [ { "uniq": "chap2022012113294121", "type": "0", "cname": "8", "parent_uniq": "chap2022012113265020", "file_type": "", "file_uniq": "", "knowledge_point": "8", "introduction": "8", "created_time": "2022-01-21 13:29:41", "children": [] }, { "uniq": "chap2022012113303222", "type": "0", "cname": "9", "parent_uniq": "chap2022012113265020", "file_type": "", "file_uniq": "", "knowledge_point": "9", "introduction": "9", "created_time": "2022-01-21 13:30:32", "children": [ { "uniq": "chap2022012113323824", "type": "0", "cname": "88", "parent_uniq": "chap2022012113303222", "file_type": "", "file_uniq": "", "knowledge_point": "8", "introduction": "8", "created_time": "2022-01-21 13:32:38", "children": [] } ] }, { "uniq": "chap2022012113304323", "type": "0", "cname": "7", "parent_uniq": "chap2022012113265020", "file_type": "", "file_uniq": "", "knowledge_point": "7", "introduction": "7", "created_time": "2022-01-21 13:30:43", "children": [ { "uniq": "chap2022012113332225", "type": "0", "cname": "6", "parent_uniq": "chap2022012113304323", "file_type": "", "file_uniq": "", "knowledge_point": "6", "introduction": "6", "created_time": "2022-01-21 13:33:22", "children": [ { "uniq": "chap2022012113332826", "type": "0", "cname": "5", "parent_uniq": "chap2022012113332225", "file_type": "", "file_uniq": "", "knowledge_point": "5", "introduction": "5", "created_time": "2022-01-21 13:33:28", "children": [] } ] } ] }, { "uniq": "file2113295507", "type": "1", "cname": "西溪碧桂园.pdf", "parent_uniq": "chap2022012113265020", "file_type": "1", "file_uniq": "0038654742", "knowledge_point": "88", "introduction": "", "created_time": "2022-01-21 13:29:54", "children": [] } ] }, { "uniq": "file2113330308", "type": "1", "cname": "朱飞-评估表-", "parent_uniq": "chap2022012113261017", "file_type": "0", "file_uniq": "0038654743", "knowledge_point": "8", "introduction": "", "created_time": "2022-01-21 13:33:03", "children": [] } ] } ], "msg": "接口调用成功" }
children是我们判断单级还是多级的字段
梳理好业务及数据结构,在动手写代码前我们要考虑代码的逻辑复杂度、维护性、可读性等等,
在树目录这个需求中。1)、我们用vue来开发按照以往开发习惯写一堆Dom结构,在Dom中需要写许多v-if判断,代码可读性不高,维护性不高
2)、如果你用过react或了解果vue3.0或在vue2.0用过jsx,jsx是做复杂业务的首选
总上此需求采用jsx作为子组件完成树目录的业务开发
代码如下:
父级:
<template> <!-- 知识库 --> <div class="box"> <a-row class="simpleImage" v-if="visited"> <a-empty :image="simpleImage" /> </a-row> <a-row class="main_box" v-if="!visited"> <a-col span='7' class="main left_box"> <div class="catalogue">目录</div>
// 树目录
<div class="a_table"> <s-tree ref='s_tree' :dataSource="orgTree" :search="true" @click="handleClick" @add="handleAdd" @defaultTitleClick="defaultTitleClick "> </s-tree> </div> </a-col> </a-row> </div> </template>
<script> import { Empty } from 'ant-design-vue' import { query_knowledge_base_catalogue } from '@/api/factory' import STree from '@/components/ZF/menuTree.jsx' export default { name: 'Knowledge', components: { STree // pdf }, data () { return { visited: false, orgTree: [], openKeys: [] } }, beforeCreate () { this.simpleImage = Empty.PRESENTED_IMAGE_SIMPLE }, created () { }, watch: {}, mounted () { this.base_catalogue() }, methods: { base_catalogue () { const params = {} query_knowledge_base_catalogue(params).then(res => { if (res.code === '00000') { // console.log(res) this.orgTree = res.data this.openKeys.push(res.data[0].uniq) // 默认展示第一项 this.$refs.s_tree.openKeys(this.openKeys) } else { this.$message.error(res.msg) } }) }, // 父级 defaultTitleClick (e) { }, // 单级触发 handleClick (e) { }, // 单级触发 handleAdd (item) { } }, beforeDestroy () { } } </script>
子组件jsx
import { Menu, Tooltip, Input } from 'ant-design-vue' import './menuTree.less' const { Item, ItemGroup, SubMenu } = Menu const { Search } = Input export default { name: 'Tree', props: { dataSource: { type: Array, required: true } // search: { // type: Boolean, // default: false // } }, mounted () { }, data () { return { localOpenKeys: [] } }, methods: {
// 默认项 openKeys (val) { this.localOpenKeys = val }, defaultSelectedKeys (item) { this.$emit('add', item) }, defaultTitleClick (item) { this.$emit('defaultTitleClick', item) }, // 搜索框 // renderSearch () { // return ( // <Search // placeholder="input search text" // style="width: 100%; margin-bottom: 1rem" // /> // ) // }, // 图标 renderIcon (type) { return type && type === '0' ? (<span class='limit img_one'></span>) : (<span class="limit img_two"></span>) }, // 单级 renderMenuItem (item) { return ( <Item key={item.uniq} {...{ on: { click: () => this.defaultSelectedKeys(item) } }}> <span class="father"> { this.renderIcon(item.type) } <Tooltip placement="topLeft"> <template slot="title"> { item.cname } </template> <span class="cname">{ item.cname }</span> </Tooltip> {/* <a class="btn" style="width: 20px;z-index:1300" {...{ on: { click: () => this.handlePlus(item) } }}></a> */} </span> </Item> ) }, // 判断是多级还是单级菜单 renderItem (item) { return item.children.length > 0 ? this.renderSubItem(item, item.uniq) : this.renderMenuItem(item, item.uniq) }, // 多级结构 renderSubItem (item, uniq) { // console.log(item) const childrenItems = item.children.length > 0 && item.children.map(o => { return this.renderItem(o, o.uniq) }) const title = ( <span slot="title" class="father"> { this.renderIcon(item.type) } <Tooltip placement="topLeft"> <template slot="title"> { item.cname } </template> <span class="cname">{ item.cname }</span> </Tooltip> </span> ) return ( <SubMenu key={uniq} {...{ on: { titleClick: () => this.defaultTitleClick(item) } }}> { title } { childrenItems } </SubMenu> ) } }, render () { const { dataSource, search } = this.$props // this.localOpenKeys = openKeys.slice(0) const list = dataSource.map(item => { return this.renderItem(item) }) return ( <div class="tree-wrapper"> {/* { search ? this.renderSearch() : null } 搜索框 */} <Menu mode="inline" class="custom-tree" {...{ on: { click: item => this.$emit('click', item), 'update:openKeys': val => { this.localOpenKeys = val } } }} openKeys={this.localOpenKeys}> { list } </Menu> </div> ) } }
css 注:在使用jsx时图片用img加载不出来时,可以采用背景图片加载,写代码条条道路通罗马,
.img_one { background-image: url(../../assets/img/pdf.png); } .img_two { background-image: url(../../assets/img/file.png); } .limit { display: inline-block; width: 16px; height: 16px; background-repeat: no-repeat; background-size: 16px 16px; margin-right:3px } .father { display: flex; align-items: center; } .cname { display: inline-block; width: 95%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
由此发现jsx在复杂业务场景下为首选