乐趣区

VUEtree组件实现封装文件目录组件操作目录render多个属性vmoadal代替方案增加钩子函数

思维:
文件夹以及文件数据来自于两组数据
父级 folder-tree 中:
在 template 中:

<folder-tree
  :folder-list.sync="folderList"
  :file-list.sync="fileList"
  :folder-drop="folderDrop"
  :file-drop="fileDrop"
  :beforDelete="beforeDelete"
/>

在 script 中:

 import {getFolderList, getFileList} from '@/api/data'
   import {putFileInFolder, transferFolderToTree} from '@/lib/util'
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(() => {console.log(222)
      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]
})
}
}

子组件中:
template:

<Tree :data="folderTree" :render="renderFunc"></Tree>

script:

import {putFileInFolder, transferFolderToTree, expandSpecifiedFolder} from '@/lib/util'
import clonedeep from 'clonedeep'
export default {
name: 'FolderTree',
data () {
return {folderTree: [],
  currentRenameingId: '',
  currentRenameingContent: '',
  renderFunc: (h, { root, node, data}) => {
    const dropList = data.type === 'folder' ? this.folderDrop : this.fileDrop
    const dropdownRender = dropList.map(item => {return (<dropdownItem name={item.name}>{item.title}</dropdownItem>)
    })
    const isRenaming = this.currentRenameingId === `${data.type || 'file'}_${data.id}`
    return (
      <div class='tree-item'>
        {data.type === 'folder' ? <icon type="ios-folder" class='folder-icon' /> : ''}
        {
          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" class="tree-item-button">
              <icon type="md-more" size={12} />
            </i-button>
            <dropdownMenu slot="list">
              {dropdownRender}
            </dropdownMenu>
          </dropdown> : ''
        }
  </div>
  )
  }

}
 },
 props: {
folderList: {
  type: Array,
  default: () => {}
},
fileList: {
  type: Array,
  default: () => {}
},
// folderDrop: {
//   type: Array,s
//   default: () => {}
// }
// 此处如果是空数组,说明这里不需要下拉菜单,这里不用设置默认值;// 这里就不是直接判断数组是否为空,而是直接判断这里 folderDrop 是否为 undefined,如果为 undefined 说明这里没有传入值
folderDrop: Array,
fileDrop: Array,
beforeDelete: Function
},
watch: {folderList () {this.transData()
},
fileList () {this.transData()
}
 },
methods: {transData () {this.folderTree = transferFolderToTree(putFileInFolder(this.folderList, this.fileList))
},
isFolder (type) {return type === 'folder'},
handleDelete (data) {
  const folderId = data.folder_id
  const isFolder = this.isFolder(data.type)
  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, this.folderTree, folderId)
  })
},
handleDropdownClick (data, name) {if (name === 'rename') {this.currentRenameingId = `${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.currentRenameingContent = value},
updateList (list, id) {
  let i = -1
  let len = list.length
  while (++i < len) {let folderItem = list[i]
    if (folderItem.id === id) {
      folderItem.name = this.currentRenameingContent
      list.splice(i, 1, folderItem)
      break
    }
  }
  return list
},
saveRename (data) {
  const id = data.id
  const folderId = data.folder_id
  const type = data.type
  if (type === 'folder') {const list = this.updateList(clonedeep(this.folderList), id)
    this.$emit('update:folderList', list)
    this.$nextTick(() => {expandSpecifiedFolder(this, this.folderTree, folderId)
    })
  } else {const list = this.updateList(this.fileList, id)
    this.$emit('update:fileList', list)
    this.$nextTick(() => {expandSpecifiedFolder(this, this.folderTree, folderId)
    })
  }
  this.currentRenameingId = ''
},
delete () {//}
},
mounted () {this.transData()
 }
}

在 @/lib/util 中

import clonedeep from 'clonedeep'
export const putFileInFolder = (folderList, fileList) => {const folderListCloned = clonedeep(folderList)
      const fileListCloned = clonedeep(fileList)
      return folderListCloned.map(folderItem => {
    const folderId = folderItem.id
      let index = fileListCloned.length
      while (--index >= 0) {const fileItem = fileListCloned[index]
          if (fileItem.folder_id === folderId) {const file = fileListCloned.splice(index, 1)[0]
         file.title = file.name
        if (folderItem.children) folderItem.children.push((file))
            else folderItem.children = [file]
          }
      }
     folderItem.type = 'folder'
     return folderItem
     })
}
// 只对文件夹进行处理
export const transferFolderToTree = folderList => {if (!folderList.length) return []
      const folderListCloned = clonedeep(folderList)
      const handle = id => {let arr = []
    folderListCloned.forEach(folder => {if (folder.folder_id === id) {const children = handle(folder.id)
    if (folder.children) folder.children = [].concat(folder.children, children)
   else folder.children = children
    folder.title = folder.name
    arr.push(folder)
      }
    })
    return arr
     }
      return handle(0)
  }
// 根据目录中 id 展开指定的文件夹;folderTree 代表展开文件夹树状列表,id 是展开文件的 id
export const expandSpecifiedFolder = (vm, folderTree, id) => {
return folderTree.map(item => {if (item.type === 'folder') {if (item.id === id) {
    // item.expand = true
    vm.$set(item, 'expand', true)
  } else {if (item.children && item.children.length) {item.children = expandSpecifiedFolder(vm, item.children, id)
      if (item.children.some(child => {return child.expand === true})) {
        // item.expand = true
        vm.$set(item, 'expand', true)
      } else {
        // item.expand = false
        vm.$set(item, 'expand', false)
      }
    }
  }
}
return item
})
}

在 MOCK 中

import {doCustomTimes} from '@/lib/tools'
import Mock from 'mockjs'
export const getFileList = () => {
      const template = {
       'name|5': '@cword',
    'creat_time': '@datetime',
    'folder_id|1-5': 0,
    'id|+1': 10000
      }
      let arr = []
      doCustomTimes(10, () => {arr.push(Mock.mock(template))
     })
    return arr
}

export const getFolderList = () => {
      const template1 = {
    'name|1': '@word',
    'creat_time': '@datetime',
       'folder_id': 0,
       'id|+1': 1
      }
      const template2 = {
    'name|1': '@word',
    'creat_time': '@datetime',
    'folder_id|+1': 1,
    'id|+1': 4
      }
      let arr = []
      doCustomTimes(3, () => {arr.push(Mock.mock(template1))
      })
      doCustomTimes(2, () => {arr.push(Mock.mock(template2))
      })
      return arr
}

在 lib/tools 中

// 与业务无关的工具函数
export const doCustomTimes = (times, callback) => {
  let i = -1
  while (++i < times) {callback()
      }
}

在 api/data 中

export const getFolderList = () => {
  return axios.request({
url: '/getFolderList',
method: 'get'
  })
}
export const getFileList = () => {
return axios.request({
url: '/getFileList',
method: 'get'
  })
}
退出移动版