思維:
文件夾以及文件數據來自於兩組數據
父級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'
})
}