предисловие
Эта статья написана днем, ветерок и солнце, это очень удобно 🏖.
Тогда без лишних слов перейдем сразу к теме.Сегодня мы в основном объяснимМасштабируемые столбцы, фиксированные столбцы, многоуровневые заголовкиИ несколько общих вопросов о формах✍️, полных галантереи 😯.
Напоминание: эта статья является продолжением предыдущейТабличный компонент понять?Она была написана после этой статьи, поэтому советую сначала прочитать предыдущую статью👀. Конечно, можно смотреть и прямо вниз, потому что здесь основная идея — рассказать об идее, а кода не так много 😁.
масштабируемый столбец
Масштабируемые столбцы, как следует из названия, мы можем перетаскивать заголовокborder
Чтобы добиться размера ширины столбца, посмотрите на следующую картинку и вы все поймете👇:Смысл вышеприведенной картинки должен быть вполне ясен, а теперь кратко рассмотрим конкретные шаги реализации👇:
первый шаг:
Каждый из наших заголовковth
добавить еще одинdiv
, что является красной частью на рисунке выше, а затем абсолютно расположен вth
справа.
Шаг 2:
в заголовкеheader
и телоbody
Добавить один на том же уровнеdiv
Чтобы представить пунктирную линию на приведенном выше рисунке, она по умолчанию скрыта, как и на следующем рисунке:
третий шаг:
Следите за событиями мыши в красной части: при нажатии мыши отображается пунктирная линия, и пунктирная линия изменяется в реальном времени.left
значение, когда мышь поднята, пунктирная линия скрывается, а ширина столбца после перетаскивания вычисляется, а затем изменяетсяcolumns
соответствующий столбецwidth
То есть ширина столбцов заголовка и тела будет меняться синхронно.
Конечно, не забудьте отвязать, когда мышь поднята.mousemove
а такжеmouseup
события, это хорошая привычка.
Вот как реализован масштабируемый столбец.
фиксированный столбец
Далее давайте посмотрим, как реализованы фиксированные столбцы. Во-первых, это дизайн API, о котором должно быть легко думать, мы находимся вcolumns
Добавьте столбцы, которые необходимо зафиксировать внутриfixed
свойство, его значение имеет два варианта (left
илиright
), следующим образом:
columns: [
{
title: '姓名',
key: 'name',
width: 100,
fixed: 'left'
}
...
]
Чтобы упростить задачу, здесь мы рассматриваем только фиксацию левой колонки, потому что фиксация правой колонки такая же.
Вот предложение, объясняющее принцип 😁: нужно визуализировать еще одну таблицу и абсолютно расположить ее на исходной таблице. Следующая картинка должна помочь вам понять👇:По сути вышеприведенная картинка уже является ядром реализации, так что поговорим непосредственно о конкретном методе реализации😎:
Шаг 1: Обработайте табличные данные
Поскольку мы хотим поместить столбцы, которые необходимо зафиксировать, слева, в начале нам нужно иметь дело с табличными данными.fixed="left"
Атрибуты перечислены первыми.
Шаг 2: визуализируйте две таблицы
Следующим шагом является нормальная визуализация двух таблиц (A и B. Фактически, таблица A и таблица B абсолютно одинаковы, за исключением того, что таблица B имеет некоторые дополнительные свойства, такие как абсолютное позиционирование в левом верхнем углу и фиксированная ширина ( ширинаfixed="left"
Сумма ширины столбцов атрибута), скрытое переполнение и т.д. Конкретная структура кода показана на следующем рисунке:Кроме того, для формы Аfixed
часть, которую мы можем установитьvisibility: hidden
, потому что его не нужно отображать, а Element так и пишется; аналогично для не-fixed
Часть также может быть установленаvisibility: hidden
.
В это время у меня вдруг возник вопрос 🤔, т.е.Почему он должен быть установлен наvisibility: hidden
не установленоdisplay: none
Шерстяная ткань?display: none
Разве нельзя было бы сделать меньше dom? установлен вvisibility
Каково значение ?Это хороший вопрос, и я предлагаю вам подумать над ним. . . Смотрите дальше вниз 😁.
С сомнениями я взглянул на структуру dom iView и Ant Design и обнаружил, что iView также используетvisibility: hidden
чтобы с этим справиться, а Ant Design не рендерит напрямую, что очень странно! Поэтому я поставил iView и Element ofvisibility: hidden
заменитьdisplay: none
Попробовал и обнаружил, что вроде нормально, отображение таблицы тоже правильное, проблем нет, так почему? На самом деле основная причина в том, чтоvisibility: hidden
заменитьdisplay: none
вызоветсмещение линииЭта проблема.
Что это значит? То есть, если мы установимdisplay: none
, высота строки в таблице A не фиксирована, но в это время таблица B не отображает содержимое тела таблицы в таблице A, поэтому таблица B не может синхронизировать высоту в таблице A; и если мы зададим ее какvisibility: hidden
Если это так, то таблица A и таблица B на самом деле содержат все данные, но они не видны визуально, так что высоту их строк можно оставить точно такой же, хотя это приведет к избыточным элементам dom.
Так почему же Ant Design это делает? На самом деле у Ant Design тоже есть эта проблема.Хотя он не отображает избыточный dom, он заранее рассчитает высоту строки таблицы A, а затем синхронно установит высоту строки фиксированного столбца.Кроме того, когда размер таблицы и изменение ширины столбца Также для синхронизации. Это два разных решения, давайте попробуем сами🙌.
Конечно, здесь мы используем схему Element и iView.
Шаг 3: Синхронизированная прокрутка
Что не так с приведенной выше реализацией 🤔?Наиболее очевидная проблема заключается в том, что форма A и форма B разделены.Поэтому, когда одна из таблиц прокручивается, другая таблица не будет реагировать соответствующим образом. На самом деле, заголовок и тело каждой таблицы также разделены, так что теперь нам нужно прокручивать синхронно: когда мы прокручиваем тело таблицы А по горизонтали, нам нужно прокручивать часть заголовка таблицы А синхронно; когда мы прокрутка по вертикали При прокрутке тела формы A нам необходимо синхронно прокручивать тело формы B.
Вот тело таблицы в форме A (A__body
) прокрутив в качестве примера, кратко опишите конкретный метод:A__body
При боковой прокрутке: получитьA__body
изscrollLeft
value, а затем синхронизировать значение с заголовком таблицы A;A__body
При вертикальной прокрутке: получитьA__body
изscrollTop
value, а затем синхронизируйте значение с телом таблицы B.
Конечно, прокрутка — это высокочастотное действие, поэтому мы можем сделать обработку против тряски, также можно попробовать использоватьtransform
заменитьscrollTop
а такжеscrollLeft
проматывать.
Шаг 4: Синхронизируйте наведение
Как и на третьем шаге, когда мыhover
Каждая строка также должна быть синхронизирована, как прокрутка, то естьhover
Синхронизация стилей.
Так же мы используемhover
Возьмите в качестве примера тело формы А🌰, следите за строкойmouseenter
а такжеmouseleave
Событие, когда мышь перемещается в строку, вы можете получить информацию о строке, а затем синхронизироваться с соответствующей строкой фиксированного столбца; аналогичноhover
Когда речь идет о фиксированных столбцах, их также следует синхронизировать с таблицей A, которая здесь повторяться не будет.
многоуровневый заголовок
заголовок
Во-первых, это дизайн API, мы надеемсяcolumns
добавитьchildren
Этого можно добиться, например, следующим образом (кстати, посмотрите, как выглядит многоуровневый заголовок):
columns: [
{
title: '日期',
key: 'date',
width: 200
},
{
title: '配送信息',
children: [
{
title: '姓名',
key: 'name',
width: 200
},
{
title: '地址',
key: 'addr'
}
]
}
]
Из приведенного выше рисунка видно, что многоуровневый заголовок на самом деле рендерится построчно, и каждая строка может быть разделена на столбцы и столбцы для рендеринга, что на самом деле представляет собой двумерный массив, дваv-for
Цикл, давайте разрежем код iView, чтобы увидеть общую структуру:Итак, первое, что мы должны сделать, этоcolumns
отформатируйте его так, чтобы он стал
Это выглядит так (двумерный массив):Каждый из узлов данных дастlevel
,rowspan
а такжеcolspan
, последние два используются для объединения ячеек, напримерcolumns
дата первого элемента, егоcolspan
должно быть такchildren
общее количество, если нетchildren
равно 1; егоrowspan
Должно быть равно максимальному количеству слоев - текущее количество слоев + 1, если естьchildren
1.
Здесь может быть небольшая загвоздка, так что нужно остановиться и подумать🤔, но на самом деле суть в преобразовании структур данных, поэтому особого акцента на том, как его преобразовать, нет, так что давайте попробуем себя 😊.
тело
Рендеринг тела прост, но мы также должныcolumns
Выполним некоторую обработку, посмотрим, как выглядит отсортированный столбец:На самом деле новыйcolumns
просто пройди старыйcolumns
,имеютchildren
Продолжайте движение, чтобы получить подстолбцы, нетchildren
Просто выньте столбец напрямую, аналогично получению столбца всех листовых узлов, этот новый столбец заменит исходный.columns
Для рендеринга, поскольку это преобразование структуры данных проще, я разместил код:
const getAllColumns = (cols, forTableHead = false) => {
const columns = deepCopy(cols);
const result = [];
columns.forEach((column) => {
if (column.children) {
if (forTableHead) result.push(column);
result.push.apply(result, getAllColumns(column.children, forTableHead));
} else {
result.push(column);
}
});
return result;
};
На самом деле, это все еще проблема преобразования данных.Вы можете взглянуть на следующие две картинки, чтобы углубить свое понимание👇: На данный момент мы закончили реализацию масштабируемых колонок, фиксированных колонок и многоуровневых заголовков, ха-ха 😁😁😁.
Дополнение к вопросу
1, ширина столбца непостоянна
На самом деле ширина столбцов нашей таблицы и тела таблицы на самом деле несовместимы, например, когда тело таблицы имеет полосу прокрутки, а заголовок - нет, поэтому будет разрыв в ширине полосы прокрутки. время обычная практика заключается в прокрутке по вертикали Когда полоса существует, добавьте столбец в конце заголовка, чтобы компенсировать эту разницу в ширине, обычно мы называем этоgutter
. что этоgutter
Каково значение ширины? По сути, это ширина полосы прокрутки, как рассчитать ширину полосы прокрутки? Вероятно, принцип состоит в том, чтобы создатьdiv
, затем используйте (нетoverflow: scroll
атрибутdiv
ширина) - (даoverflow: scroll
изdiv
width), которая является шириной полосы прокрутки.
2. Вывих
В реальной разработке при использовании Element, если таблица неуместна, не зная почему, вы можете попытаться решить эту проблему следующими способами:
this.$nextTick(() => {
this.$refs['table'] && this.$refs['table'].doLayout()
})
3. Катон
Если стол заполненtooltip
а такжеinput
Он застрянет, ведь обработчиков событий и dom-структур больше, поэтому такой ситуации следует избегать. Например, он отображается только тогда, когда строка находится в состоянии редактирования.input
;tooltip
Перейдите на рендеринг в реальном времени, не добавляйте все в началеtooltip
.
4. ключевое значение
v-for
не использовать, когдаindex
связыватьkey
ценность, потому чтоindex
Он может измениться, поэтому он ненадежен, попробуйте использоватьid
связывать.
5. Рендеринг больших данных
Для тысяч строк рендеринга данных нормально, если есть зависание.В настоящее время, если данные вашей таблицы используются только для чистого отображения, вы можете добавить их в компонентfunctional: true
Сделайте его функциональным компонентом, что уменьшит накладные расходы на рендеринг. Другой - виртуальный список.Хотя существуют тысячи табличных данных, в видимой области содержится ограниченное количество данных, а область действия настолько велика.Нам всегда нужно визуализировать только видимую область и часть данных вокруг верхней и нижние части видимой области. , остальные рендерить не надо, а потом вычислять текущий интервал данных для отображения по количеству скролла при скролле.Общая идея такова, конечно, проще сказать чем сделано 😂.
резюме
Хо-хо, пока мы наконец закончили говорить об общих функциях табличного компонента, честно говоря, это действительно хлопотно, но я надеюсь, что это может помочь вам, хе-хе😄!