Наступление на управление деревом пользовательского интерфейса элемента дневника ямы

Node.js задняя часть внешний интерфейс

В последнее время я работаю над системой управления.Мне нужно дерево каталогов в левой части страницы для облегчения работы с файлами.Я не хочу строить колесо с нуля, поэтому я рассматриваю использование дерева 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это список данных

напиши в конце

Это та яма, с которой мы столкнулись в этом огромном компоненте.Конечно, там еще много чего не записано.Эта статья лишь несколько важных моментов для записи.Это также удобно для студентов,которые столкнулись с такой же проблемой. Это может дать небольшую идею, это тоже большая честь~