思惟:
文件夾以及文件數據來自於兩組數據
父級folder-tree中:
在template中:node
<folder-tree :folder-list.sync="folderList" :file-list.sync="fileList" :folder-drop="folderDrop" :file-drop="fileDrop" :beforDelete="beforeDelete" />
在script中:ios
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:axios
<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中api
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' }) }