VUE-tree組件實現(封裝文件目錄組件,操作目錄-render,多個屬性v-moadal代替方案,增加鉤子函數)

思維:
文件夾以及文件數據來自於兩組數據
父級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'
  })
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章