VUE-tree组件实现(封装文件目录组件,操作目录-render,多个属性v-moadal代替方案,增加钩子函数)

2019-11-06 admin

思维: 文件夹以及文件数据来自于两组数据 父级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'
  })
}

[转载]原文链接:https://segmentfault.com/a/1190000020915338

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-78859.html

文章标题:VUE-tree组件实现(封装文件目录组件,操作目录-render,多个属性v-moadal代替方案,增加钩子函数)

相关文章
JavaScript编辑器推荐
主流编辑器有SublimeText,Notepad++,webstorm等,是使用最广泛的编辑器,但也有一些JavaScript编辑器提供有着各自的特性和功能,适应不同人的需求,以下是几款优秀的编辑器,相信你一定能找到自己喜欢的。 1. W...
2015-11-12
js性能优化 如何更快速加载你的JavaScript页面
确保代码尽量简洁 不要什么都依赖JavaScript。不要编写重复性的脚本。要把JavaScript当作糖果工具,只是起到美化作用。别给你的网站添加大量的JavaScript代码。只有必要的时候用一下。只有确实能改善用户体验的时候用一下。 ...
2015-11-12
10个强大的纯CSS3动画案例分享
我们的网页外观主要由CSS控制,编写CSS代码可以任意改变我们的网页布局以及网页内容的样式。CSS3的出现,更是可以让网页增添了不少动画元素,让我们的网页变得更加生动有趣,并且更易于交互。本文分享了10个非常炫酷的CSS3动画案例,希望大家...
2015-11-16
2015年JavaScript或“亲库而远框架”
2014年过去了,作为一个JavaScript开发者很难满怀信心的去“挽回”一个特定的库或技术,即便是强大的Angular,似乎也因为最近的一些事情而动摇。 2014年10月的ng-europe会议上,Angular开发者团队透露了一个关于...
2015-11-12
Android中Okhttp3实现上传多张图片同时传递参数
之前上传图片都是直接将图片转化为io流传给服务器,没有用框架传图片。 最近做项目,打算换个方法上传图片。 Android发展到现在,Okhttp显得越来越重要,所以,这次我选择用Okhttp上传图片。 Okhttp目前已经更新到Okhttp...
2017-03-17
v-charts | 饿了么团队开源的基于 Vue 和 ECharts 的图表工具
在使用echarts生成图表时,经常需要做繁琐的数据类型转化、修改复杂的配置项,v-charts的出现正是为了解决这个 痛点。基于Vue2.0和echarts封装的v-charts图表组件,只需要统一提供一种对前后端都友好的数据格式 设置简...
2018-05-24
JavaScript实现PC手机端和嵌入式滑动拼图验证码三种效果
PC和手机端网站滑动拼图验证码效果源码,同时包涵了弹出式Demo,使用ajax形式提交二次验证码所需的验证结果值,嵌入式Demo,使用表单形式提交二次验证所需的验证结果值,移动端手动实现弹出式Demo三种效果 首先要确认前端使用页面,比如...
2017-03-17
Angular2-primeNG文件上传模块FileUpload使用详解
近期在学习使用Angular2做小项目,期间用到很多primeNG的模块。 本系列将结合实战总结angular2-primeNG各个模块的使用经验。 文件上传模块FileUploadModule 首先要在使用该组件的模块内导入文件上传模块 ...
2017-03-09
Vue.js组件tab实现选项卡切换
本文实例为大家分享了vue插件tab选项卡的具体代码,供大家参考,具体内容如下 效果图: 代码如下: &lt;!DOCTYPE html&gt; &lt;html lang=&quot;en&quot;&gt; &lt;head&gt; ...
2017-03-13
Vue获取DOM元素样式和样式更改示例
在 vue 中用 document 获取 dom 节点进行节点样式更改的时候有可能会出现 ‘style’ is not definde的错误,这时候可以在 mounted 里用 $refs 来获取样式,并进行更改: &lt;template...
2017-03-13
回到顶部