Если количество уровней вложенности невелико и известно заранее, то... Для обработки каждого уровня вложенности используется отдельный
<template v-for
, ячейки с rowspan'ами создаются в зависимости от равенства индексов элементов вложенных массивов нулю, значения rowspan'ов - длины (суммы длин) вложенных массивов.
Например:
const rowspan = item => item.backlinks.reduce((acc, n) => acc + n.recipients.length, 0);
<tbody>
<template v-for="item in data">
<template v-for="(backlink, iBacklink) in item.backlinks">
<template v-for="(recipient, iRecipient) in backlink.recipients">
<tr>
<template v-if="!iBacklink && !iRecipient">
<td :rowspan="rowspan(item)">{{ item.name }}</td>
<td :rowspan="rowspan(item)">{{ item['domain score'] }}</td>
</template>
<template v-if="!iRecipient">
<td :rowspan="backlink.recipients.length">{{ backlink.donor }}</td>
<td :rowspan="backlink.recipients.length">{{ backlink['page score'] }}</td>
</template>
<td>{{ recipient.url }}</td>
<td>{{ recipient.image }}</td>
</tr>
</template>
</template>
</template>
</tbody>
Более универсальное решение - собрать из вложенных данных плоские, дополнив их значениями rowspan. Понадобится массив ключей и рекурсивная функция, принимающая массив данных, массив ключей, и индекс, начиная с которого надо обрабатывать ключи. Бежим по массиву данных, вложенный цикл по ключам - если значение, соответствующее текущему ключу, не является массивом, то оно помещается в массив данных строки, в противном случае следует рекурсивный вызов, после которого элементам массива данных строки добавляется значение rowspan, равное размеру результата рекурсивного вызова:
const keys = ref([
'name', 'domain score', 'backlinks',
'donor', 'page score', 'recipients',
'url', 'image',
]);
function createTableData(arr, keys, iKey = 0) {
return arr.flatMap(n => {
const row = [];
const innerRows = [];
for (let i = iKey; i < keys.length; i++) {
const val = n[keys[i]];
if (Array.isArray(val)) {
innerRows.push(...createTableData(val, keys, i + 1));
row.forEach(cell => cell.rowspan = innerRows.length);
row.push(...innerRows.shift());
break;
} else {
row.push({ text: val });
}
}
return [ row, ...innerRows ];
});
}
Преобразование вложенных данных в плоские можно оформить в виде вычисляемого свойства:
const tableData = computed(() => {
return createTableData(props.data, props.keys);
});
Ну а построить таблицу на основе плоского массива -
дело тривиальное:
<tbody>
<tr v-for="row in tableData">
<td
v-for="cell in row"
v-text="cell.text"
:rowspan="cell.rowspan"
></td>
</tr>
</tbody>