8、几行代码实现一个树组件【BUI App开发】

这多是最简单的一棵树了.html

树菜单效果图预览

file

分析交互

这棵树的交互像已有的哪一个组件? 折叠菜单? 点击显示,点击隐藏. 只是折叠菜单只有一层, 树有无限层. ajax

折叠菜单效果预览数组

file

BUI的组件都是只定义了交互, 其它给予了比较大的想象空间. 好比: 折叠面板, 新闻详情的查看更多dom

折叠面板效果异步

file

新闻详情的查看更多效果组件化

file

树的实现

若是折叠菜单进行多层嵌套, 理论上就能够实现树的效果, 再把图标进行修改, 就是咱们要的了. 测试

折叠面板结构ui

<dl id="uiAccordion" class="bui-accoridon">
    <dt>菜单1</dt>
    <dd>内容1</dd>
    <dt>菜单2</dt>
    <dd>内容2</dd>
</dl>

结构进行嵌套.this

<dl id="uiAccordion" class="bui-accoridon">
    <dt>菜单1</dt>
    <dd>
        <dl class="bui-accoridon">
            <dt>菜单1-1</dt>
            <dd>内容1</dd>
            <dt>菜单1-2</dt>
            <dd>内容2</dd>
        </dl>
    </dd>
    <dt>菜单2</dt>
    <dd>内容2</dd>
</dl>
// 初始化代码无需修改
var uiAccordion = bui.accordion({
    id: "#uiAccordion"
})

通过测试是可行的, 那这个再把它组件化, 组件化的目的是为了更简单方便的复用.url

组件化

这里结合store进行数据绑定. 修改后的样式结构.

新建html文件 pages/components/tree/index.html

<style>
    .bui-tree .bui-accordion{
        border-top: 0;
        border-bottom: 0;
        padding-left: .2rem;
    }
    .bui-tree .bui-accordion dd{
        padding-left: .3rem;
    }
    .bui-tree .bui-accordion dt:last-child{
        border-bottom: 0;
    }
    .bui-tree .bui-accordion dd{
        border-top: 0;
    }
    .bui-tree .bui-accordion dd:last-child{
        border-bottom: 0;
    }
    .bui-tree .bui-btn .icon-nosubmenu ,
    .bui-tree .bui-btn .icon-accordion {
        margin-right: .2rem;
    }
    .bui-tree .bui-btn > .icon-accordion:before{
        content:"\e61e"
    }
    .bui-tree .active > .icon-accordion:before{
        content:"\e620"
    }
</style>
<dl class="bui-accordion bui-tree" b-template="tree.tplTrees(tree.trees)">
    <!-- 保留原有的结构注释, 便于模板理解 
        <dt class="bui-btn bui-box">
        <div class="span1">折叠菜单</div>
        <i class="icon-accordion"></i>
    </dt>
    <dd>
        折叠菜单内容
    </dd> -->
</dl>

新建一个js文件 pages/components/tree/index.js

// 组件化必须加上 loader.define
loader.define(function(require, exports, module) {

    // 假设树的数据层级结构是这样, 如下是模拟数据
    var data = [
        {id: 1, name: "办公管理", pid: 0 ,
            children:[
                { id: 2, name: "请假申请", pid: 1,
                    children:[
                        { id: 4, name: "请假记录", pid: 2 },
                    ],
                },
                { id: 3, name: "出差申请", pid: 1},
            ]
        },
        {id: 5, name: "系统设置", pid: 0 ,
            children:[
                { id: 6, name: "权限管理", pid: 5,
                    children:[
                        { id: 7, name: "用户角色", pid: 6 },
                        { id: 8, name: "菜单设置", pid: 6 },
                    ]
                },
            ]
        },
    ]

    // 初始化数据行为存储, 接收外部参数的 data数组, 若是没有则展现默认数据, mounted里面只是初始化了一次accordion, 但本来的id,换成了 `#${module.id} .bui-accordion` , module.id是外部component标签的id, 没有会自动生成. 

    // 注意: module.props 是1.6.3的用法,能够使用 var props = bui.history.getPageParams(module.id) 代替.
    var bs = bui.store({
        el: `#${module.id}`,
        scope: "tree",
        data: {
           trees: module.props && module.props.data || data,
        },
        methods: {},
        templates: {
            tplTrees: function (data,pclass) {
                var html="";

                data.forEach((item, index) => {
                    let hasChildren=item&&item.children&&item.children.length;
                    // 是否有下一级图标
                    let hasChildrenIcon=hasChildren? `<i class="icon-accordion"></i>`:`<i class="icon-nosubmenu">&#xe620;</i>`;

                    // 没有下一级展现向右的箭头
                    let hasChildrenNext=hasChildren? ``:`<i class="icon-listright"></i>`;

                    // 不一样层级加上父级样式
                    let level= (pclass||"bui-tree-level") + `-${index}`;

                    html+=`<dt class="bui-btn bui-box ${level}">
                                ${hasChildrenIcon}
                                <div class="span1">${item.name}</div>
                            </dt>`

                    // 若是有子集菜单继续递归生成
                    if (hasChildren) {
                        html+=`<dd class="${level}"><dl class="bui-accordion">`;
                        html+=this.tplTrees(item.children,level);
                        html+=`</dl></dd>`;
                    }

                });
                
                return html;
            }
        },
        mounted: function () {
            // 初始化折叠插件
            var uiAccordion = bui.accordion({
                id: `#${module.id} .bui-accordion`,
                callback: function (e) {
                    // 点击的时候触发了一次click事件, 若是外部有订阅这个事件,就会被触发. 
                    loader.trigger(`${module.id}:click`,e)
                }
            });
        }
    })

    return bs;
})

组件的预览

bui的组件是能够实时预览的, 直接打开 index.html#pages/components/tree/index 就能够预览当前组件效果, 加上?data=xxx能够传组件须要的参数进行模拟, 没有则使用默认数据;

file

组件的使用

<div class="bui-page bui-box-vertical">
    <header>
        <div class="bui-bar">
            <div class="bui-bar-left">
                <a class="bui-btn btn-back"><i class="icon-back"></i></a>
            </div>
            <div class="bui-bar-main">树菜单</div>
            <div class="bui-bar-right">
            </div>
        </div>
    </header>
    <main>
        /* 这类组件的数据通常是经过异步请求后而来, 因此不能直接写在属性上 */
        <component id="tree" name="pages/components/tree/index" delay="true"></component>
    </main>
</div>
// 模拟请求
bui.ajax({
    url:""
}).then(function(result){
    // 假设树的数据在 result.data
    // 加载树组件
    loader.delay({
        id:"tree",
        param: {
            data:result.data
        }
    })
})

监听树点击事件

loader.on("tree:click",function(e){
    // e 为 loader.trigger 传过来的参数. 能够经过 e.target 知道当前点击的是哪一个dom
    console.log(e.target);
})

更多使用, 请查看组件章节.

最终效果:

file