#javascript #vue.js #html-table
Вопрос:
Эта проблема кажется мне невыполнимой, но вот она
Итак, у меня есть ассоциативный массив, который можно изменить на основе введенных фильтров (например, столбцы могут исчезать или добавляться, если включены определенные фильтры). И у меня также есть таблица с динамическим «диапазоном строк» (поэтому таблица может быть очень длинной с правой стороны), которая создается на основе этого массива.
{
"countries": {
"25": {
"title": "France",
"cities": {
"8954": {
"title": "Paris",
"languages": { <- here can add another object - "districts"
"16": {
"title": "English",
"quarters": {
"2_2020": {
"title": "% 2020-2021",
"parallels": {
"1": {
"title": "tst1"
},
"2": {
"title": "tst2",
"value": 7.89
},
"3": {
"title": "tst3",
"value": 37.2
},
"4": {
"title": "tst4",
"value": 9.16
},
"5": {
"title": "tst5",
"value": 6.45
}
}
},
"3_2020": {
"title": "% 2019-2020",
"parallels": {
"1": {
"title": "tst1"
},
"2": {
"title": "tst2",
"value": 8.59
},
"3": {
"title": "tst3",
"value": 9.1
},
"4": {
"title": "tst4",
"value": 6.8
},
"5": {
"title": "tst5",
"value": 75.1
}
}
}
}
},
"1000": {
"title": "Spanish",
"quarters": {
"2_2020": {
"title": "% 2020-2021",
"parallels": {
"1": {
"title": "tst1"
},
"2": {
"title": "tst2",
"value": 2.75
},
"3": {
"title": "tst3",
"value": 41.2
},
"4": {
"title": "tst4",
"value": 6.97
},
"5": {
"title": "tst5",
"value": 74.4
}
}
},
"3_2020": {
"title": "% 2019-2020",
"parallels": {
"1": {
"title": "tst1"
},
"2": {
"title": "tst2",
"value": 8.51
},
"3": {
"title": "tst3",
"value": 99.1
},
"4": {
"title": "tst4",
"value": 75.8
},
"5": {
"title": "tst5",
"value": 25.11
}
}
}
}
}
}
}
}
}
}
}
и в идеале этот json выглядит так или (если существуют области объектов) так
Вот мой код vuejs для создания таблицы только для тела, используя другой объект — «район».
<b-tr v-for="(itemCountry, indexCountry) in itemsReal.countries" :key="indexCountry">
<b-tr v-for="(itemCities, indexCities) in itemCountry.cities" :key="indexCities" class="w-25 ">
<b-th class="w-25">{{ itemCities.title }}</b-th>
<b-tr v-for="(itemDistrict, indexDistricts) in itemCities.districts" :key="indexDistricts">
<b-td class="w-25 sticky-sidebar-district">{{ itemDistrict.title }}</b-td>
<b-tr class="language-rows" v-for="(itemLanguages, indexLanguages) in itemDistrict.languages" :key="indexLanguages">
<b-td class="language sticky-sidebar-language-District">{{ itemLanguages.title }}</b-td>
<b-tr class="d-inline-flex" v-for="(itemQuarter, indexQuarter) in itemLanguages.quarters">
<b-td v-for="(currentNumber, indexCurrentNumber) in itemQuarter.testsarr" :key="indexCurrentNumber" class="value-cells">
{{ currentNumber.value }} {{ currentNumber.value === undefined ? 'amp;shy' : null }}
</b-td>
</b-tr>
</b-tr>
</b-tr>
</b-tr>
</b-tr>
Ответ №1:
Это было определенно нелегко!
Прежде всего: vue-bootstrap работает не совсем так, как в вашем примере. Вы не можете просто гнездиться b-tr
и b-td
не использовать области видимости. Возможно, этого не хватает в вашем фрагменте, но это все равно не сработает, потому что
Второе: Вы не можете вложить tr
s в td
s и так далее. Вам нужно работать с rowspan
и. colspan
Таким образом, ваша таблица в обычном HTML (со стилями начальной загрузки) должна выглядеть следующим образом:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" integrity="sha384-KyZXEAg3QhqLMpG8r 8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
<table class="table table-bordered">
<tbody>
<tr>
<td rowspan="2">country</td>
<td rowspan="2">cities</td>
<td rowspan="2">languages</td>
<td colspan="5">2019-2020</td>
<td colspan="5">2020-2021</td>
</tr>
<tr>
<td>tst1</td>
<td>tst2</td>
<td>tst3</td>
<td>tst4</td>
<td>tst5</td>
<td>tst6</td>
<td>tst7</td>
<td>tst8</td>
<td>tst9</td>
<td>tst10</td>
</tr>
<tr>
<th rowspan="4">France</th>
<th rowspan="2">Paris</th>
<td>english</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
</tr>
<tr>
<td>spanish</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
</tr>
<tr>
<th rowspan="2">Lyon</th>
<td>english</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
</tr>
<tr>
<td>spanish</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
</tr>
</tbody>
</table>
Из-за того, как работают HTML-таблицы, не очень легко придумать циклы в vue, но мне удалось заставить это работать. Сначала мне нужно было немного отформатировать ваши данные, поэтому вместо объектов у меня были массивы. Таким образом, вам будет легче получить данные. Я написал для этого вычисляемую функцию на случай, если вы не имеете никакого влияния на форматирование входящих данных. Я добавил несколько комментариев в код. Я надеюсь, что это понятно.
new Vue({
el: "#app",
data() {
return {
countries: {
"25": {
"title": "France",
"cities": {
"8954": {
"title": "Paris",
"languages": {
"16": {
"title": "English",
"quarters": {
"2_2020": {
"title": "% 2020-2021",
"parallels": {
"1": {
"title": "tst1"
},
"2": {
"title": "tst2",
"value": 7.89
},
"3": {
"title": "tst3",
"value": 37.2
},
"4": {
"title": "tst4",
"value": 9.16
},
"5": {
"title": "tst5",
"value": 6.45
}
}
},
"3_2020": {
"title": "% 2019-2020",
"parallels": {
"1": {
"title": "tst1"
},
"2": {
"title": "tst2",
"value": 8.59
},
"3": {
"title": "tst3",
"value": 9.1
},
"4": {
"title": "tst4",
"value": 6.8
},
"5": {
"title": "tst5",
"value": 75.1
}
}
}
}
},
"1000": {
"title": "Spanish",
"quarters": {
"2_2020": {
"title": "% 2020-2021",
"parallels": {
"1": {
"title": "tst1"
},
"2": {
"title": "tst2",
"value": 2.75
},
"3": {
"title": "tst3",
"value": 41.2
},
"4": {
"title": "tst4",
"value": 6.97
},
"5": {
"title": "tst5",
"value": 74.4
}
}
},
"3_2020": {
"title": "% 2019-2020",
"parallels": {
"1": {
"title": "tst1"
},
"2": {
"title": "tst2",
"value": 8.51
},
"3": {
"title": "tst3",
"value": 99.1
},
"4": {
"title": "tst4",
"value": 75.8
},
"5": {
"title": "tst5",
"value": 25.11
}
}
}
}
}
}
}
}
}
}
}
},
computed: {
formattedCountries() {
return Object.keys(this.countries).map(countryKey => {
const country = this.countries[countryKey]
return {
id: countryKey,
title: country.title,
cities: Object.keys(country.cities).map(cityKey => {
const city = country.cities[cityKey]
return {
id: cityKey,
title: city.title,
languages: Object.keys(city.languages).map(languageKey => {
const language = city.languages[languageKey]
return {
id: languageKey,
title: language.title,
values: Object.keys(language.quarters).map(quarterKey => {
const quarter = language.quarters[quarterKey]
return Object.keys(quarter.parallels).map(parallelKey => {
const parallel = quarter.parallels[parallelKey]
return parallel.value ? parallel.value : null
})
}).reduce((a, b) => a.concat(b), [])
}
})
}
})
}
})
}
},
methods: {
calculateCountryRowspan(country) {
return country.cities.reduce((a, b) => {
return a b
}, 0)
}
}
})
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table class="table table-bordered">
<thead>
<tr>
<th rowspan="2">country</th>
<th rowspan="2">cities</th>
<th rowspan="2">languages</th>
<th colspan="5">2019-2020</th>
<th colspan="5">2020-2021</th>
</tr>
<tr>
<th>tst1</th>
<th>tst2</th>
<th>tst3</th>
<th>tst4</th>
<th>tst5</th>
<th>tst6</th>
<th>tst7</th>
<th>tst8</th>
<th>tst9</th>
<th>tst10</th>
</tr>
</thead>
<!-- Loop over coutries -->
<tbody v-for="country in formattedCountries" :key="'country' country.id">
<tr>
<!-- The rowspan of the countries title needs to be the count of its cities times its languages -->
<th :rowspan="calculateCountryRowspan(country)">
{{ country.title }}
</th>
<!-- The countries title is followed its first city and the cities values -->
<template v-if="country.cities.length">
<!-- The rowspan of the city title needs to be the count of its languages -->
<th :rowspan="country.cities[0].languages.length">
{{ country.cities[0].title }}
</th>
<th>
{{ country.cities[0].languages[0].title }}
</th>
<td v-for="(value, valueIndex) in country.cities[0].languages[0].values" :key="'value' valueIndex">
{{ value }}
</td>
</template>
</tr>
<template v-if="country.cities.length">
<!-- If the city has more than one language, add the others as new rows
(languageIndex starts with 1! The loop skips the first language from above) -->
<tr v-for="languageIndex in country.cities[0].languages.length - 1" :key="'city0language' languageIndex">
<th>
{{ country.cities[0].languages[languageIndex].title }}
</th>
<td v-for="(value, valueIndex) in country.cities[0].languages[languageIndex].values" :key="'value' valueIndex">
{{ value }}
</td>
</tr>
</template>
<!-- If there are more than one city, add them linke before
(cityIndex starts with 1! The loop skips the first city from above) -->
<template v-for="cityIndex in country.cities.length - 1">
<tr :key="'city' cityIndex 'row1'">
<th :rowspan="country.cities[cityIndex].languages.length">
{{ country.cities[cityIndex].title }}
</th>
<th>
{{ country.cities[cityIndex].languages[0].title }}
</th>
<td v-for="(value, valueIndex) in country.cities[cityIndex].languages[0].values" :key="'value' valueIndex">
{{ value }}
</td>
</tr>
<tr v-for="languageIndex in country.cities[cityIndex].languages.length - 1" :key="'city' cityIndex 'language' languageIndex">
<th>
{{ country.cities[cityIndex].languages[languageIndex].title }}
</th>
<td v-for="(value, valueIndex) in country.cities[cityIndex].languages[languageIndex].values" :key="'value' valueIndex">
{{ value }}
</td>
</tr>
</template>
</tbody>
</table>
</div>
Комментарии:
1. Этот пример был идеальным для меня, но при добавлении другого столбца все ломается. Я попытался добавить
<template>
и сделать еще один цикл внутри него, но все не так. Не могли бы вы еще немного помочь с этим json , пожалуйста?2. Может быть, сначала попробуйте решить ее с помощью статического HTML. Это могло бы помочь лучше понять структуру. Я также нашел, что это очень сложно, чтобы сделать все правильно.
3. Ах, хорошо, я все сделал правильно, спасибо вам большое за ваш пример. Теперь я сяду и напишу дополнительный ответ здесь