如何实现这样一个布局?

img


要求是这样的(目前的话换行单选多选判断换行展示已经处理完了):

img


但是现在有个问题就是选项的个数问题,选项最多是9个(也就是从AI),最少是34个(判断的选项就是两个,但是若遇到判断就换行了,倒是不用太操心),选项个数不固定每题的宽度就不固定,宽度不固定列数就不固定,列数有影响到了行数,但又要求每列得对齐。
大致就是:
1.选项个数不固定
2.支持横排、竖排的切换
3.每列得对齐
所以想问问各位怎么实现这个需求?
数据:


        list:[{
          title: '1.单选题(8分)',
          idProp: 'questionId',
          code: 'single_choice',
          choices: [
            { identifier: 'A', content: '茂陵刘郎秋风客,夜闻马嘶晓无迹' },
            { identifier: 'B', content: '画栏桂树悬秋香,三十六宫土花碧' },
            { identifier: 'C', content: '魏官牵车指千里,东关酸风射眸子' },
            { identifier: 'D', content: '空将汉月出宫门,忆君清泪如铅水' },
            { identifier: 'E', content: '空将汉月出宫门,忆君清泪如铅水' },
            { identifier: 'F', content: '空将汉月出宫门,忆君清泪如铅水' },
            { identifier: 'G', content: '空将汉月出宫门,忆君清泪如铅水' },
            { identifier: 'H', content: '空将汉月出宫门,忆君清泪如铅水' },
            { identifier: 'I', content: '空将汉月出宫门,忆君清泪如铅水' },
          ],
        },
        {
          title: '2.单选题(8分)',
          idProp: 'questionId',
          code: 'single_choice',
          choices: [
            { identifier: 'A', content: '茂陵刘郎秋风客,夜闻马嘶晓无迹' },
            { identifier: 'B', content: '画栏桂树悬秋香,三十六宫土花碧' },
            { identifier: 'C', content: '魏官牵车指千里,东关酸风射眸子' },
            { identifier: 'D', content: '空将汉月出宫门,忆君清泪如铅水' },
          ],
        },
        {
          title: '3.单选题(8分)',
          idProp: 'questionId',
          code: 'single_choice',
          choices: [
            { identifier: 'A', content: '茂陵刘郎秋风客,夜闻马嘶晓无迹' },
            { identifier: 'B', content: '画栏桂树悬秋香,三十六宫土花碧' },
            { identifier: 'C', content: '魏官牵车指千里,东关酸风射眸子' },
            { identifier: 'D', content: '空将汉月出宫门,忆君清泪如铅水' },
          ],
        },
        {
          title: '4.单选题(8分)',
          idProp: 'questionId',
          code: 'single_choice',
          choices: [
            { identifier: 'A', content: '茂陵刘郎秋风客,夜闻马嘶晓无迹' },
            { identifier: 'B', content: '画栏桂树悬秋香,三十六宫土花碧' },
            { identifier: 'C', content: '魏官牵车指千里,东关酸风射眸子' },
            { identifier: 'D', content: '空将汉月出宫门,忆君清泪如铅水' },
            { identifier: 'E', content: '茂陵刘郎秋风客,夜闻马嘶晓无迹' },
            { identifier: 'F', content: '画栏桂树悬秋香,三十六宫土花碧' },
            { identifier: 'G', content: '魏官牵车指千里,东关酸风射眸子' },
            { identifier: 'H', content: '空将汉月出宫门,忆君清泪如铅水' },
            { identifier: 'I', content: '空将汉月出宫门,忆君清泪如铅水' },
          ],
        },]

vue组件代码:

<!--
 * @Description: 选择题(单选、多选)、判断题
-->
<template>
  <div
    :class="[
      'ac-sweep__choice',
      showBorder && 'is-border',
      isShowBorderBottom && 'is-border-bottom',
    ]"
  >
    <template v-if="Array.isArray(listData)">
      <div v-for="(item, index) in listData" :key="index">
        <ul :class="['ac-sweep__choice-box']">
          <li
            v-for="(_item, _index) in item"
            :key="`${index}_${_index}`"
            class="ac-sweep__choice-each"
          >
            {{ getBeforeChinese(_item.title) }}
            <div class="ac-sweep__choice-item">
              <span
                v-for="(__item, __index) in _item.choices"
                :key="`${index}_${_index}_${__index}`"
                class="ac-sweep__choice-option"
              >
                {{ __item.identifier }}
              </span>
            </div>
          </li>
        </ul>
      </div>
    </template>
    <template v-else>
      <ul class="ac-sweep__choice-box">
        <li>
          {{ getBeforeChinese(list.title) }}
          <div class="ac-sweep__choice-item">
            <span
              v-for="(_item, _index) in list.choices"
              :key="_index"
              class="ac-sweep__choice-option"
            >
              {{ _item.identifier }}
            </span>
          </div>
        </li>
      </ul>
    </template>
  </div>
</template>

<script>
export default {
  name: 'AcSweepChoice',
  components: {},
  props: {
    list: {
      // 此处的类型为什么不统一?原因还是<ac-card>组件中transformTree函数所致,Array类型说明是连续的单选多选判断,Object类型说明这是个单个的单选、多选、判断
      type: [Array, Object],
    },
    // 是否展示边框
    showBorder: {
      type: Boolean,
      default: true,
    },
    // 是否展示下边框
    isShowBorderBottom: {
      type: Boolean,
      default: false,
    },
  },
  computed: {
    layout() {
      return this.$store.getters['AC/layout']
    },
  },
  watch: {},
  mounted() {
    this.convertTree(this.list)
  },
  data() {
    return {
      listData: null,
    }
  },
  methods: {
    getBeforeChinese(str) {
      const chineseReg = /[\u4e00-\u9fa5]/ // 匹配汉字的正则表达式
      if (str) {
        const index = str.search(chineseReg) // 查找第一个汉字的位置
        if (index === -1) {
          return str // 如果没有汉字,则返回原字符串
        } else {
          return str.slice(0, index) // 截取汉字之前的内容
        }
      }
    },

    /**
     * @method convertTree
     * @param questions
     * @Description 此处承接<ac-card>组件中transformTree函数,之前在<ac-card>的transformTree函数中处理了将连续的单选多选判断放到一个数组中,那么下方的函数就是处理是否是连续的单选、多选或是判断了(这里的话如果是单独的单选、多选或是判断,是自身单独一个数组)。
     */
    convertTree(questions) {
      if (Array.isArray(questions)) {
        // 创建空数组用于存储分组后的题目
        const groupedQuestions = []

        // 遍历题目数组
        for (let i = 0; i < questions.length; i++) {
          const currentQuestion = questions[i]

          // 检查当前题目的类型
          if (currentQuestion.code === 'single_choice') {
            // 如果当前题目是单选
            // 检查上一个题目的类型是否与当前题目相同
            if (i === 0 || questions[i - 1].code !== 'single_choice') {
              // 如果上一个题目的类型与当前题目不同,创建新的分组数组
              groupedQuestions.push([])
            }
            // 将当前题目添加到最后一个分组数组中
            groupedQuestions[groupedQuestions.length - 1].push(currentQuestion)
          } else if (currentQuestion.code === 'multiple_choice') {
            // 如果当前题目是多选
            // 检查上一个题目的类型是否与当前题目相同
            if (i === 0 || questions[i - 1].code !== 'multiple_choice') {
              // 如果上一个题目的类型与当前题目不同,创建新的分组数组
              groupedQuestions.push([])
            }
            // 将当前题目添加到最后一个分组数组中
            groupedQuestions[groupedQuestions.length - 1].push(currentQuestion)
          } else if (currentQuestion.code === 'judgment') {
            // 如果当前题目是判断
            // 检查上一个题目的类型是否与当前题目相同
            if (i === 0 || questions[i - 1].code !== 'judgment') {
              // 如果上一个题目的类型与当前题目不同,创建新的分组数组
              groupedQuestions.push([])
            }
            // 将当前题目添加到最后一个分组数组中
            groupedQuestions[groupedQuestions.length - 1].push(currentQuestion)
          }
        }
        this.listData = groupedQuestions
      }
    },
  },
}
</script>

<style scoped lang="scss"></style>

样式代码:

.ac-sweep__choice {
  width:866px;
  padding: 20px 0px;
}
.is-border {
  border: 1px solid #000000;
  border-bottom: none;
}
.is-border-bottom {
  border-bottom: 1px solid #000000;
}
.ac-sweep__choice-box {
  display: grid;
  justify-items: flex-start;
  grid-auto-flow: column;
  // grid-template-columns: repeat(auto-fill, minmax(103px, 318px));
  // grid-template-columns: repeat(auto-fill, minmax(min-content, auto));
  // grid-template-rows: repeat(2, 20px);
  padding: 9px 32px;
  li {
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 12px;
    margin-right: 28px;
    &:last-child {
      margin-right: 0px;
    }
  }
}
.ac-sweep__choice-item {
  display: inline-flex;
  padding-left: 11px;
  .ac-sweep__choice-option {
    position: relative;
    width: 22px;
    height: 12px;
    line-height: 12px;
    margin: 4px 8px 4px 0px;
    text-align: center;
    font-family: PingFangSC-Regular;
    font-size: $--ac-font-size-small;
    &:last-child {
      margin-right: 0px;
    }
  }

  .ac-sweep__choice-option::after,
  .ac-sweep__choice-option::before {
    position: absolute;
    top: 0px;
    width: 5px;
  }

  .ac-sweep__choice-option::before {
    content: '[';
    left: 0;
  }

  .ac-sweep__choice-option::after {
    content: ']';
    right: 0;
  }
}

按理说如何设计排版是美工的活
你们没有专门的美工的话,那么要从需求开始分析
你到底是要搞答题卡还是试卷,答题卡和试卷的排版要求是不同的
反正无非两个思路,一种是不管选项多少个,全按照最多的留,没有的留白
另一种是根据数量自动换行