美文网首页
封装Tree组件-高级实现

封装Tree组件-高级实现

作者: 幻影翔 | 来源:发表于2020-01-01 23:30 被阅读0次

    实现功能

    • 封装文件目录组件
    • 操作目录
    • 多个属性 v-model 替代方案
    • 增加钩子函数

    实现效果

    实现文件目录的修改和删除


    tree组件封装

    多个属性 v-model 替代方案

    父组件采用 .sync 修饰,子组件使用 $emit修改

    // 父组件使用 sync 
    :folder-list.sync="folderList"
    :file-list.sync="fileList"
    
    // 子组件 $emit 提交更新父组件内容
    this.$emit('update:folderList', list);
    this.$emit('update:fileList', list);
    

    钩子函数

    操作前等待后台操作的返回再修改前台

    // 定义beforeDelete的属性
    :beforeDelete="beforeDelete"
    
    methods: {
                // 删除的钩子 模拟后台删除的动作
                beforeDelete () {
                    return new Promise((resolve,reject) => {
                        setTimeout(() => {
                            let error = new Error('error');
                            if (!error) {
                                resolve();
                            } else reject(error);
                        },2000);
                    })
                }
            },
    

    完整代码

    • 视图 folder-tree.vue
    <template>
        <div class="folder-wrapper">
            <folder-tree
                :folder-list.sync="folderList"
                :file-list.sync="fileList"
                :folder-drop="folderDrop"
                :file-drop="fileDrop"
                :beforeDelete="beforeDelete"
            ></folder-tree>
        </div>
    </template>
    <script>
        import { getFolderList, getFileList } from "@/api/data";
        import FolderTree from '_c/folder-tree';
    
        export default {
            components: {
                FolderTree
            },
            data () {
                return {
                    folderList: [],
                    fileList: [],
                    folderDrop: [
                        {
                            name: 'rename',
                            title: '重命名'
                        },
                        {
                            name: 'delete',
                            title: '删除文件夹'
                        }
                    ],
                    fileDrop: [
                        {
                            name: 'rename',
                            title: '重命名'
                        },
                        {
                            name: 'delete',
                            title: '删除文件'
                        }
                    ]
                }
            },
            methods: {
                // 删除的钩子 模拟后台删除的动作
                beforeDelete () {
                    return new Promise((resolve,reject) => {
                        setTimeout(() => {
                            let error = new Error('error');
                            if (!error) {
                                resolve();
                            } else reject(error);
                        },2000);
                    })
                }
            },
            mounted () {
                // 同时发送两个请求
                Promise.all([getFolderList(), getFileList()]).then(res => {
                    this.folderList = res[0];
                    this.fileList = res[1];
                })
            }
        }
    </script>
    <style lang="less">
        .folder-wrapper{
            width: 300px;
        }
    </style>
    
    • 封装的组件 folder-tree.vue
    <template>
        <Tree :data="folderTree" :render="renderFunc"></Tree>
    </template>
    <script>
        import { putFileInFolder, transformFolderToTree, expandSpecifiedFolder } from "@/lib/util";
        import clonedeep from 'clonedeep'
        export default {
            name: 'FolderTree',
            data () {
                return {
                    currentRenamingId: '',
                    currentRenamingContent: '',
                    folderTree: [],
                    renderFunc: (h, { root, node, data }) => {
                        const dropList = data.type === 'folder' ? this.folderDrop : this.fileDrop;
                        const dropdownRender = dropList.map(item => {
                            return (<dropdown-item name={ item.name }>{ item.title }</dropdown-item>)
                        });
                        const isRenaming = this.currentRenamingId === `${data.type || 'file' }_${data.id}`;
                        /*jsx表达式*/
                        return (
                            <div class="tree-item">
                                { data.type === 'folder' ? <icon type="ios-folder" color="#2d8cf0" style="margin-right:10px;"/> : ''}
                                {
                                    isRenaming
                                        ? <span>
                                        <i-input value= { data.title } on-input={ this.handleInput } class="tree-rename-input"></i-input>
                                        <i-button size="small" type="text" on-click={ this.saveRename.bind(this,data ) }><icon type="md-checkmark" /></i-button>
                                        <i-button size="small" type="text" ><icon type="md-close"/></i-button>
                                    </span>
                                        : <span>{ data.title }</span>
                                }
                                {
                                    dropList && !isRenaming? <dropdown placement="right-start" on-on-click={ this.handleDropDownClick.bind(this,data) }>
                                        <i-button size="small" type="text" >
                                            <icon type="md-more" size={12} />
                                        </i-button>
                                        <dropdown-menu slot="list">
                                            { dropdownRender }
                                        </dropdown-menu>
                                    </dropdown> : ''
                                }
                            </div>
                        )
                    }
                }
            },
            // 定义组件的属性
            props: {
                folderList: {
                    type: Array,
                    default: () => []
                },
                fileList: {
                    type: Array,
                    default: () => []
                },
                folderDrop: Array,
                fileDrop: Array,
                beforeDelete: Function
            },
            // 监听树结构中值的变化
            watch: {
                folderList () {
                    this.transData();
                },
                fileList () {
                    this.transData();
                }
            },
            methods: {
                // 将扁平数据转换为树状
                transData () {
                    this.folderTree = transformFolderToTree(putFileInFolder(this.folderList, this.fileList));
                },
                isFolder (type) {
                    return type === 'folder';
                },
                // 删除操作
                handleDelete (data) {
                    const isFolder = this.isFolder(data.type);
                    const folderId = data.folder_id;
                    let updateListName = isFolder ? 'folderList' : 'fileList';
                    let list = isFolder ? clonedeep(this.folderList) : clonedeep(this.fileList);
                    list = list.filter(item => item.id !== data.id);
                    this.$emit(`update:${updateListName}`,list);
                    this.$nextTick(() => {
                        expandSpecifiedFolder(this.folderTree,folderId);
                    });
                },
                // 下拉菜单事件
                handleDropDownClick (data,name) {
                    if(name === 'rename') {
                        this.currentRenamingId = `${data.type || 'file'}_${data.id}`;
                    } else if( name === 'delete') {
                        this.$Modal.confirm({
                            title: '提示',
                            content: `您确认要删除${this.isFolder(data.type) ? '文件夹' : '文件'}《${data.title}》 吗?`,
                            onOk: () => {
                                this.beforeDelete ? this.beforeDelete().then(() => {
                                    this.handleDelete(data);
                                }).catch(() => {
                                    this.$Message.error('删除失败');
                                }) : this.handleDelete(data);
                            }
                        })
                    }
    
                },
                // 输入值进行替换
                handleInput (value) {
                    this.currentRenamingContent = value;
                },
                // 更新当前目录
                updateList (list, id) {
                    let i = -1;
                    let len = list.length;
                    while (++i < len ) {
                        let item = list[i];
                        if(item.id === id) {
                            item.name = this.currentRenamingContent;
                            list.splice(i, 1, item);
                            break;
                        }
                    }
                    return list;
                },
                // 保存更新后的名称
                saveRename (data) {
                    let id = data.id;
                    const type = data.type;
                    if (type === 'folder') {
                        const list = this.updateList(clonedeep(this.folderList), id);
                        // 更新父组件,有多个属性时 需要添加在父组件 .sync
                        this.$emit('update:folderList', list);
                    } else {
                        const list = this.updateList(clonedeep(this.fileList), id);
                        this.$emit('update:fileList', list);
                    }
                    this.currentRenamingId = '';
                }
            },
            mounted () {
                // 一开始有数据执行
                this.transData();
            }
        }
    </script>
    <style lang="less">
        .tree-item{
            display: inline-block;
            width: ~"calc(100%-50px)";
            height: 30px;
            line-height: 30px;
            & > .ivu-dropdown{
                float: right;
            }
            .tree-rename-input{
                width: ~"calc(100%-80px)"
            }
        }
    </style>
    
    • 展开判断的工具类 lib/util.js
    import clonedeep from 'clonedeep'
    
    export const expandSpecifiedFolder = (folderTree, id) => {
        return folderTree.map(item => {
            if (item.type === 'folder') {
                if (item.id === id) {
                    item.expand = true;
                } else {
                    if (item.children && item.children.length) {
                        item.children = expandSpecifiedFolder(item.children, id);
                        // 判断数组中是否expand,有即为true
                        if (item.children.some(child => {
                            return child.expand === true;
                        }))  {
                            item.expand = true;
                        } else {
                            item.expand =false;
                        }
                    }
                }
            }
            return item;
        })
    };
    

    相关文章

      网友评论

          本文标题:封装Tree组件-高级实现

          本文链接:https://www.haomeiwen.com/subject/dvmioctx.html