В последнее время я работаю над системой управления.Мне нужно дерево каталогов в левой части страницы для облегчения работы с файлами.Я не хочу строить колесо с нуля, поэтому я рассматриваю использование дерева iview или После исследования я обнаружил, что дерево iview все еще немного ограничено, без функции перетаскивания и перемещения, нет функции ленивой загрузки подкаталогов и т. д., и элемент больше соответствует нашим потребностям, хотя есть много ям...
lazy & load
существует<el-tree>
Добавление атрибута lazy к дереву может превратить дерево в дерево с отложенной загрузкой, то есть маленькая стрелка раскрывающегося списка слева отображается по умолчанию.Нажатие каждой маленькой стрелки может вызвать операцию загрузки, которая может реализовать операцию динамическое получение узлов под деревом.
Здесь возникает первая проблема: как получить путь, соответствующий каждому узлу?
Нам нужно отправить запрос в фон в соответствии с путем, где находится каждый узел.Путь к узлу - это путь к ресурсу, который мы запросили.Конечно, способ получить этот путь состоит в том, чтобы конкатенировать строки, получить объект узла из текущий узел, и в зависимости от того, есть ли у него родитель, вставьте его родителя в наш массив currentPath, после каждого нажатия нам нужно установить текущий узел в его родителя
- Как получить путь, соответствующий каждому узлу
# 获取当前文件所在路径
getCurrentPath (node) {
if (node && node.data && node.data.name) {
let nodeParent = node.parent
this.currentPath = [node.data.name]
while (nodeParent && nodeParent.data && nodeParent.data.name
&& typeof nodeParent.data === 'object') {
this.currentPath.unshift(nodeParent.data.name)
nodeParent = nodeParent.parent
}
}
}
props
Когда мы создаем файл, нам не нужноlazy load
Маленькая стрелка, сгенерированная при создании файла, потому что файл не может быть создан под файлом, поэтому вам нужно выполнить некоторую настройку, передать тип в el-tree при создании файла и сказать ему, что я хочу создать файл , не надо мне это рендерить Маленькая стрелочка, как ее настроить? На самом деле в официальном документе element есть конкретные примеры этой проблемы.
Второй вопрос: как выборочно рендерить
lazy load
Сгенерированные маленькие стрелки?
<el-tree
:props="props1"
:load="loadNode1"
lazy>
</el-tree>
<script>
export default {
data() {
return {
props1: {
label: 'name',
children: 'zones',
isLeaf: 'leaf'
},
};
},
methods: {
loadNode1(node, resolve) {
if (node.level === 0) {
return resolve([{ name: 'region' }]);
}
if (node.level > 1) return resolve([]);
setTimeout(() => {
const data = [{
name: 'leaf',
leaf: true
}, {
name: 'zone'
}];
resolve(data);
}, 500);
}
}
};
</script>
renderContent
renderContent будет отслеживать значение атрибута в данных, чтобы решить, следует ли отображать и отображать соответствующее представление.Если есть необходимость оценить значение атрибута определенных данных, значение атрибута необходимо инициализировать.
Например: Содержимое рендеринга определяется в соответствии с data.type узла node.В начале нужно присвоить data.type начальное значение.Если не присвоить, то нельзя отслеживать изменения (я просто поставил data.type='edit' напрямую, потому что тип не был инициализирован в начале. , а затем представление не было обновлено...)
Третий вопрос: почему data.type изменился, а представление не обновилось?
# 重命名编辑框
if (data.type === 'edit') {
return h('input', {
attrs: {
id: 'treeInput',
value: this.currentNodeData.name
},
on: {
blur: (e) => {
this.updateCurrentNode(e.target.value || data.name)
},
keyup: (e) => {
if (e.keyCode === 13 || e.keyCode === 27) {
e.target.blur()
}
}
}
})
}
# 新建编辑框
if (data.type === 'input') {
return h('input', {
attrs: {
id: 'treeInput'
},
on: {
blur: (e) => {
this.createNewNode(e.target.value)
},
keyup: (e) => {
if (e.keyCode === 13 || e.keyCode === 27) {
e.target.blur()
}
}
}
})
}
Здесь есть еще одна небольшая проблема:on-blur
а такжеon-keyup
я писалthis.createNewNode(e.target.value)
, но срабатывал дваждыcreate
операция, это былоkeyup
В то же время поле ввода потеряет фокус, поэтому оно срабатывает.blur
, поэтому используйтеe.target.blur()
Вместо оригинального метода письма унифицированное использованиеblur
для запускаcreate
операция
Проблемы с отображением в браузере
Четвертый вопрос: зачем вам setTimeout?
Процесс нашей операции с деревом часто требует использования setTimeout(fn, 0), например:
tryToCreateNode (type) {
this.$refs.tree.append({
id: 'treeInput',
type: 'create'
}, this.currentNodeData.id)
this.currentNode.expanded = true
this.createNewWay = 'append'
setTimeout(() => {
this.$el.querySelector('#treeInput').focus()
}, 0)
}
Это связано с тем, что когда мы выполняем операцию добавления, запускается перестановка и перерисовка браузера, и дерево dom необходимо перестроить. Этот процесс занимает много времени. Если мы выполним его сразу послеthis.$el.querySelector('#treeInput').focus()
, В это время дерево dom не имеет элемента treeInput, setTimeout поместит операцию querySelector в очередь задач и дождется, пока основной процесс завершит построение дерева dom, а затем выполнит операцию в setTimeout. , мы можем получить наш treeInput
Рекурсивно получить данные дерева каталогов из бэкэнда
Пятый вопрос: как сделать рендеринг дерева каталогов без ленивой загрузки?
Поскольку мы храним файлы в серверной части этого проекта, это файловая система, точно так же, как и то, что мы видим локально, файлы и папки хранятся слой за слоем, поэтому наш клиентский интерфейс также должен отправлять запросы слой за слоем. , чтобы получить файл соответствующего уровня, необходимо рекурсивно считать, чтобы каждый раз сохранять данные, полученные в объекте treeData, например, данные первого уровняtreeData[0]
,treeData[1]
, данные второго слояtreeData[0].children
, treeData[1].children
так далее
Как реализовать эту рекурсивную функцию?
-
Получить слой данных
-
Передать данные типа папки, полученные предыдущим слоем, в рекурсивную функцию
-
Когда количество файлов на этом уровне равно 0 или все файлы получены, завершаем рекурсию
# 递归获取目录树数据
async getTreeDataRecursively (path) {
# getDirByPath是我们自己定义的获取对应目录文件的函数
let dataList = await this.getDirByPath(path)
if (dataList && dataList.length >= 0) {
if (!dataList || dataList.length === 0 || dataList.every(el => el.type === 'file')) {
return dataList
} else {
for (let i = 0; i < dataList.length; i++) {
let path = dataList[i].id
if (dataList[i].isDir) {
this.$set(dataList[i], 'children', await this.getTreeDataRecursively(path))
}
}
return dataList
}
}
}
В Vue, если вы напрямую передаете заданиеmyObj.name = 'aaa'
Таким образом, добавление определенного свойства объекта не приведет к обновлению представления.$set
добавить, тем самым инициировав обновление, подробнее см.официальная документация
Вставить дочерний узел в дерево каталогов
Как вставить рекурсивно полученные данные в соответствующий узел?
В последнем выпуске мы обращаемся к каждому из следующих приобретений узла подузла, если объект содержит все дочерние узлы по определенному пути, объект необходимо вставить в соответствующую структуру каталогов, например:
--- a
--- aa
--- aaa
--- aaa1
--- aaaa
мы проходимgetTreeDataRecursively('/a/aa')
понятно/a/aa
Следующая структура каталогов:aaa
а такжеaaa1.children(aaaa)
, то теперь мы хотим вставить его в соответствующий путь/a/aa
Далее, как этого добиться?
Первоначальная идея состоит в том, чтобы сравнить идентификатор каждого узла через путь, потому что мы присваиваем путь идентификатору каждого узла в рекурсивной функции выше, если id = '/a/aa', тогда мы будем DataList вставляется ниже , в целом идея не проблема, но шаг изменения данных соответствующего уровня treeData застрял и не может быть реализован...
Поскольку изменить структуру данных treeData невозможно, я просмотрел официальную документацию по дереву элементов и, наконец, нашел решение...
Мы можем официально предоставить этот метод, здесьkey
нашid
(/a/aa
), а такжеvalue
это список данных
напиши в конце
Это та яма, с которой мы столкнулись в этом огромном компоненте.Конечно, там еще много чего не записано.Эта статья лишь несколько важных моментов для записи.Это также удобно для студентов,которые столкнулись с такой же проблемой. Это может дать небольшую идею, это тоже большая честь~