Quasar实现单列标题高度变化时所有列卡片同步下移
解决Jeopardy游戏列标题换行后卡片对齐问题
我用Vue3 + Quasar 2.11.6开发Jeopardy游戏,布局是q-cards组成的网格,每列顶部是可编辑的q-input作为主题标题。现在遇到的问题是:当某列标题文本过长换行时,只有该列的卡片会向下偏移,其他列的卡片位置保持不变。我需要所有列的卡片都同步下移,对齐到高度最高的列标题下方的位置。
当前代码:
<template> <div v-for="category in categories" :key=category.id class="column no-wrap"> <div class="row flex justify-center text-h4"> <q-input borderless type="textarea" autogrow v-model="category.title" :input-style="{ fontSize: '35px', width: '270px', lineHeight: '35px'}"/> </div> <div v-for="question in category.questions" :key="question.id" class="row no-wrap"> <q-card class="bg-teal-3 q-ma-sm"> </q-card> </div> </div> </template>
解决方案1:使用Quasar Grid组件(推荐)
Quasar的q-grid组件原生支持行列对齐,能自动让同一行的所有元素高度匹配,完美解决卡片偏移问题。
修改后的模板代码:
<template> <!-- 外层网格容器,列数等于分类数量,设置列间距 --> <q-grid :cols="categories.length" :col-gap="16" class="q-pa-md"> <!-- 标题行:每个分类标题占一个网格项 --> <q-grid-item v-for="category in categories" :key="category.id" class="flex justify-center" > <q-input borderless type="textarea" autogrow v-model="category.title" :input-style="{ fontSize: '35px', width: '270px', lineHeight: '35px'}" /> </q-grid-item> <!-- 问题行:按最大问题数循环,每行的每个分类对应一个卡片 --> <template v-for="(_, rowIndex) in maxQuestionCount" :key="rowIndex" > <q-grid-item v-for="category in categories" :key="`${category.id}-${rowIndex}`" class="flex justify-center" > <q-card class="bg-teal-3 q-ma-sm" style="width: 270px; height: 120px;"> <!-- 卡片内容区域 --> </q-card> </q-grid-item> </template> </q-grid> </template> <script setup> import { computed } from 'vue'; // 假设categories是你的分类数据源 const categories = /* 你的分类数据 */; // 计算所有分类中最大的问题数量,确保行数覆盖所有分类 const maxQuestionCount = computed(() => { return Math.max(...categories.map(cat => cat.questions.length), 0); }); </script>
- 核心逻辑:
q-grid会将同一行的所有网格项高度统一为该行最高元素的高度,无论哪个标题换行变高,整行高度都会自适应,后续问题行自然对齐。
解决方案2:CSS Grid自定义布局
如果更倾向原生CSS方案,用CSS Grid也能实现相同效果:
<template> <div class="jeopardy-grid" :style="{ '--cols': categories.length }"> <!-- 标题行 --> <div v-for="category in categories" :key="category.id" class="category-title" > <q-input borderless type="textarea" autogrow v-model="category.title" :input-style="{ fontSize: '35px', width: '270px', lineHeight: '35px'}" /> </div> <!-- 问题行 --> <template v-for="(_, rowIndex) in maxQuestionCount" :key="rowIndex" > <div v-for="category in categories" :key="`${category.id}-${rowIndex}`" class="question-card" > <q-card class="bg-teal-3 q-ma-sm" style="width: 270px; height: 120px;"> <!-- 卡片内容区域 --> </q-card> </div> </template> </div> </template> <script setup> import { computed } from 'vue'; const categories = /* 你的分类数据 */; const maxQuestionCount = computed(() => { return Math.max(...categories.map(cat => cat.questions.length), 0); }); </script> <style scoped> .jeopardy-grid { display: grid; /* 根据分类数量动态设置列数 */ grid-template-columns: repeat(var(--cols), 1fr); gap: 16px; padding: 24px; } .category-title { display: flex; justify-content: center; } .question-card { display: flex; justify-content: center; } </style>
- 核心逻辑:外层容器通过CSS Grid划分行列,所有标题和卡片按行列顺序排列,同一行的元素自动继承行高,实现全局对齐。
原代码问题分析
原代码中每个分类是独立的column容器,各容器内部的标题和卡片仅在自身垂直流中排列,容器之间没有行对齐约束。因此某列标题高度变化时,只会影响自身容器内的卡片位置,其他列不受影响。而网格布局将整个页面划分为统一的行列结构,强制同一行的元素高度一致,从根本上解决了对齐问题。
内容的提问来源于stack exchange,提问作者Frederik Kliemt
相关产品推荐
相关产品推荐

