首页 > 解决方案 > 如何从具有 5 深度 VueJS 的关联数组动态创建行跨表

问题描述

这个问题对我来说似乎不可能,但在这里

所以,我有一个关联数组,可以根据输入的过滤器进行更改(例如,如果某些过滤器打开,列可以消失或添加)。而且我还有一个动态“rowspan”的表(所以右边的表可能很长),它是基于这个数组创建的。

 {
  "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 ? '&shy' : null }}
          </b-td>
        </b-tr>
      </b-tr>
    </b-tr>
  </b-tr>
</b-tr>

标签: javascriptvue.jshtml-table

解决方案


那绝对不是一件容易的事!

首先: vue-bootstrap 不像你的例子那样工作。您不能简单地嵌套b-tr而不b-td使用范围。也许那是你的片段中的缺失,但无论如何它都不起作用,因为

第二:你不能在str中嵌套std等等。您需要使用rowspancolspan。因此,您的纯 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>


推荐阅读