前端grid布局

目录

1.说明

2.grid布局的常用属性的说明及使用

3.使用示例

4.总结


1.说明

前端使用grid布局,生成一个X乘Y的矩阵。

2.grid布局的常用属性的说明及使用

html部分

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>4x5 Grid Example</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="grid-container">
        <div class="grid-item">1</div>
        <div class="grid-item">2</div>
        <div class="grid-item">3</div>
        <div class="grid-item">4</div>
        <div class="grid-item">5</div>
        <div class="grid-item">6</div>
        <div class="grid-item">7</div>
        <div class="grid-item">8</div>
        <div class="grid-item">9</div>
        <div class="grid-item">10</div>
        <div class="grid-item">11</div>
        <div class="grid-item">12</div>
        <div class="grid-item">13</div>
        <div class="grid-item">14</div>
        <div class="grid-item">15</div>
        <div class="grid-item">16</div>
    </div>
</body>
</html>

 css部分 

body {
    font-family: Arial, sans-serif;
}

.grid-container {
    display: grid;
    grid-template-columns: repeat(5, 1fr); /* 5 列 */
    grid-template-rows: repeat(4, 100px);   /* 4 行,每行高度为 100px */
    gap: 10px;                               /* 网格项之间的间距 */
    padding: 10px;
}

.grid-item {
    background-color: #4CAF50; /* 背景颜色 */
    color: white;               /* 字体颜色 */
    display: flex;              /* 使用 flexbox 居中内容 */
    justify-content: center;    /* 水平居中 */
    align-items: center;        /* 垂直居中 */
    border: 1px solid #fff;     /* 边框 */
}

说明:

首先设置外面大div的css:

① 设置布局模式为grid布局

display: grid;

②设置每行展示的列数及列宽

grid-template-columns: repeat(5, 1fr); /* 5 列,每列占据相等的宽度 */
grid-template-columns: 100px 200px 150px; /* 三列,分别为 100px, 200px, 150px */
grid-template-columns: 50% 30% 20%; /* 三列,分别占据容器宽度的 50%, 30%, 20% */
grid-template-columns: auto auto; /* 两列,宽度根据内容自动变化 */
grid-template-columns: 1fr 2fr; /* 第一列占据 1 份,第二列占据 2 份,比例为 1:2 */
grid-template-columns: 100px 1fr 2fr; /* 第一列固定 100px,第二列与第三列按比例分配剩余空间 */
grid-template-columns: repeat(3, 100px); /* 创建 3 列,每列宽度为 100px */
grid-template-columns: 100px repeat(2, 1fr); /* 第一列 100px,后面两列平分剩余空间 */
grid-template-columns: 100px 200px 1fr; /* 第一列 100px,第二列 200px,第三列自适应剩余空间 */
grid-template-columns: repeat(2, 150px) 1fr; /* 前两列固定为 150px,最后一列自适应剩余空间 */

 上面几种设置列数及列宽的方法中,如果设置列宽自适应时,当大div的宽度较大,每行的小div较少时,会导致div之间的间距较大。可以采用下面的方式将列宽固定


grid-template-columns: repeat(3, minmax(100px, 100px));
grid-template-columns: repeat(3, 100px);

上面的设置方式代表,显示3列,每列的宽度固定为100px。

额外说明一下repeat

repeat(count, size)

  • count: 指定重复的次数,可以是一个整数(如 3),也可以是一个关键字(如 auto-fillauto-fit)。
  • size: 定义每次重复的大小,可以是固定的长度单位(例如 pxemrem)、灵活的单位(例如 fr),或者使用其他函数(如 minmax())来定义最小和最大值。

③设置显示的行数及行高,和列的设置一致

    grid-template-rows: repeat(4, 100px);   /* 4 行,每行高度为 100px */

④设置列间距及行间距

gap: 10px;       /* 列之间的间距及行之间的间距 */
row-gap: 20px; /* 设置行间距为 20px */
column-gap: 15px; /* 设置列间距为 15px */

3.使用示例

<template>
  <a-modal :visible="drawerVisible" width="50%"
           unmountOnClose :mask-closable="false" :closable="false" :esc-to-close="false" :draggable="true">
    <template #title>
      <span> {{ type === "1" ? "Add Experiment Layout Template" : "Edit Experiment Layout Template" }} </span>
    </template>
    <template #footer>
      <a-button type="primary" @click="handleCancel">Cancel</a-button>
      <a-button type="primary" @click="handleNext">Next</a-button>
      <a-button type="primary" @click="handleOk" :loading="okLoading">Finish</a-button>
    </template>
    <a-form ref="formRef" :model="insertForm" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }"
            label-align="left" :rules="rules">
      <a-row :gutter="20">
        <a-col :span="20">
          <a-form-item field="templateName" label="模板名称">
            <a-input v-model.trim="insertForm.templateName" placeholder="请填写模板名称" :max-length="80" allow-clear
                     show-word-limit/>
          </a-form-item>
          <a-form-item field="numEntries" label="数量">
            <a-input-number v-model="insertForm.numEntries" placeholder="请填写数量" allow-clear :min="1" :step="1"
                            :max-length="4" show-word-limit
                            :precision="0"/>
          </a-form-item>
          <a-form-item field="plotsWide" label="地块长度">
            <a-input-number v-model="insertForm.plotsWide" placeholder="请填写地块长度" allow-clear :min="1"
                            :max="insertForm.numEntries ? insertForm.numEntries : 999" :step="1" :max-length="3"
                            :precision="0" show-word-limit/>
          </a-form-item>
          <a-form-item field="sizeRestriction" label="大小限制">
            <a-select v-model="insertForm.sizeRestriction" placeholder="请填写大小限制" :options="sizeRestrict" allow-clear
                      allow-search/>
          </a-form-item>
          <a-form-item field="mapPlacement" label="种植顺序">
            <a-select v-model="insertForm.mapPlacement" placeholder="请填写种植顺序" :options="mapPlacement" allow-clear
                      allow-search/>
          </a-form-item>
          <a-form-item field="enabled" label="是否可用">
            <a-switch v-model="insertForm.enabled" :checked-value="1" :unchecked-value="0"/>
          </a-form-item>
        </a-col>
      </a-row>
    </a-form>
  </a-modal>
  <a-modal :visible="templateVisible" width="80%"
           :mask-closable="false" :closable="false" :esc-to-close="false" :draggable="true">
    <template #title>
      <span> {{ type === "1" ? "Add Experiment Layout Template" : "Edit Experiment Layout Template" }} </span>
    </template>
    <template #footer>
      <a-button type="primary" @click="handleCancelMap">Cancel</a-button>
      <a-button type="primary" @click="handlePrev">Prev</a-button>
      <a-button type="primary" @click="handleOk" :loading="okLoading">Finish</a-button>
    </template>
    <div style="display: flex;">
      <a-spin :loading="mapLoading" style="width: 85%">
        <div class="container" id="parentDiv" ref="divRef" :style="setGridTemplateColumns()">
          <div class="input-wrapper" v-for="(item,index) in map" :id="'div' + index">
            <!--            <span>{{ item.entryNum }}</span>-->
            <a-input :id="'input' + index" class="input" :style="setDefaultColor(item.regionNum)"
                     v-model="item.checkNum" :disabled="item.disable === '1'" :max-length="5"
                     @focus="inputClick(index)" @change="changeVal(index)"/>
          </div>
        </div>
      </a-spin>
      <div style="margin-left: 10px">
        <a-table :data="data" :pagination="false" row-key="region" :row-selection="rowSelection"
                 v-model:selectedKeys="selectedKeys">
          <template #columns>
            <a-table-column title="Region" data-index="region"></a-table-column>
            <a-table-column title="Count">
              <template #cell="{ record }">
                <div :style="`background-color:${record.color}`">{{ record.count }}</div>
              </template>
            </a-table-column>
          </template>
        </a-table>
        <div style="margin-top: 15px">
          <a-button @click="setColor">Assign Region</a-button>
        </div>
      </div>
    </div>
    <div style="margin-top: 10px">
      <span>Total Entries:&nbsp;{{ insertForm.numEntries }}&nbsp;&nbsp;</span>
      <span>#Checks:&nbsp;{{ checksNum }} ({{ checkRatio }}%)&nbsp;&nbsp;</span>
      <span>Non-Checks:&nbsp;{{ noChecksNum }}</span>
    </div>
    <div style="margin-top: 10px">
      <a-space :size="5">
        <a-button type="primary" @click="handlePattern">Pattern</a-button>
        <a-button type="primary" @click="handleClearChecks">Clear All Checks</a-button>
        <a-button type="primary" @click="handleClearRegions">Clear All Regions</a-button>
      </a-space>
    </div>
  </a-modal>
  <a-modal :visible="patternVisible" width="40%" @ok="patternOk" @cancel="patternCancel"
           :mask-closable="false" :closable="false" :esc-to-close="false" :draggable="true">
    <template #title>
      <span> Check Pattern </span>
    </template>
    <a-form ref="patternFormRef" :model="patternData" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }"
            label-align="left" :rules="patternRules">
      <a-row :gutter="20">
        <a-col :span="20">
          <a-form-item field="xVal" label="X Increment">
            <a-input-number v-model.trim="patternData.xVal" :min="1" :step="1" :max="insertForm.plotsWide - 1"
                            :precision="0" allow-clear/>
          </a-form-item>
          <a-form-item field="yVal" label="Y Increment">
            <a-input-number v-model="patternData.yVal" :min="1" :step="1" :max="rows-2" :precision="0" allow-clear/>
          </a-form-item>
          <a-form-item field="checkOption" label="check Options">
            <a-radio-group v-model="patternData.checkOption" direction="vertical" :options="checkNumberOption">
            </a-radio-group>
          </a-form-item>
          <a-form-item field="setVal" label="Set val" v-if="patternData.checkOption === 1 ">
            <a-input-number v-model.trim="patternData.setVal" :min="1" :step="1" :max="99999" :precision="0"
                            allow-clear/>
          </a-form-item>
          <a-form-item field="minVal" label="Minimum" v-if="patternData.checkOption === 2 ">
            <a-input-number v-model.trim="patternData.minVal" :min="1" :step="1" :max="99999" :precision="0"
                            allow-clear/>
          </a-form-item>
          <a-form-item field="maxVal" label="Maximum" v-if="patternData.checkOption === 2 ">
            <a-input-number v-model.trim="patternData.maxVal" :min="patternData.minVal" :step="1" :max="99999"
                            :precision="0" allow-clear/>
          </a-form-item>
        </a-col>
      </a-row>
    </a-form>
  </a-modal>
</template>

<script lang="ts" setup>
import {addEdit, getEditData, getMapInfo} from '@/api/dictionaries/expLayoutTemplate'
import {reactive, ref, computed} from "vue";
import {FormInstance} from '@arco-design/web-vue/es/form';
import {Message} from '@arco-design/web-vue';

const formRef = ref<FormInstance>();
const patternFormRef = ref()
// 父组件传递的参数(所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递)
const props = defineProps({
  type: String,
});
const emits = defineEmits(['handleQuery']);
// 确认按钮的加载状态
const okLoading = ref(false);

const drawerVisible = ref(false);
const templateVisible = ref(false)
const mapLoading = ref(false)
const patternVisible = ref(false)

const insertFormData = () => {
  return {
    layoutTemplateId: "",
    templateName: "",
    numEntries: undefined,
    plotsWide: undefined,
    mapPlacement: 2,
    sizeRestriction: 1,
    enabled: 1,
  };
};

const checkNumberOption = [
  {label: 'Set all to val', value: 1},
  {label: 'Randomize Check Numbers', value: 2},
];

const patternFormData = () => {
  return {
    xVal: 1,
    yVal: 1,
    checkOption: 1,
    setVal: 1,
    minVal: 1,
    maxVal: 1,
  };
}
const insertForm = ref(insertFormData());

const patternData = ref(patternFormData())

const mapPlacement = reactive([{
  "value": 1, "label": 'Left to Right'
}, {"value": 2, "label": 'Serpentine'}])

const sizeRestrict = reactive([{
  "value": 0, "label": 'None'
}, {
  "value": 1, "label": 'Multiple of Plots Wide'
}, {
  "value": 2, "label": 'Exact'
}])


const patternRules = {
  xVal: [
    {
      required: true,
      message: '内容不能为空',
    },
  ],
  yVal: [
    {
      required: true,
      message: '内容不能为空',
    },
  ],
  checkOption: [
    {
      required: true,
      message: '内容不能为空',
    },
  ],
  setVal: [
    {
      required: true,
      message: '内容不能为空',
    },
  ],
  minVal: [
    {
      required: true,
      message: '内容不能为空',
    },
  ],
  maxVal: [
    {
      required: true,
      message: '内容不能为空',
    },
  ],
}

const rules = {
  templateName: [
    {
      required: true,
      message: '模板名称不能为空',
    },
  ],
  numEntries: [
    {
      required: true,
      message: '数量不能为空',
    },
  ],
  plotsWide: [
    {
      required: true,
      message: '地块长度不能为空',
    },
  ],
  mapPlacement: [
    {
      required: true,
      message: '种植顺序不能为空',
    },
  ],
  sizeRestriction: [
    {
      required: true,
      message: '大小限制不能为空',
    },
  ],
  enabled: [
    {
      required: true,
      message: '是否可用不能为空',
    },
  ],
}

const checksNum = ref(0)
const noChecksNum = ref(0)


// 打开弹窗
const handleType = ref("")
const openDialog = async (row: any, type: any) => {
  handleType.value = type
  if (type == "2") {
    // 查询修改的数据
    const res = await getEditData({"layoutTemplateId": row.layoutTemplateId})
    insertForm.value = res;
  }
  drawerVisible.value = true;
};
// 导出方法在父组件中进行使用
defineExpose({openDialog});

// 表单提交
const handleOk = async () => {
  okLoading.value = true;
  // 需要使用await,方法执行完成之后才能判断返回结果
  const validRes = await formRef.value?.validate();
  if (Number(insertForm.value.numEntries) < Number(insertForm.value.plotsWide)) {
    Message.error({content: 'The number of entries cannot be less than the plots wide', position: 'top'});
    return
  }
  if (!validRes) {
    try {
      let formData = insertForm.value;
      const mapInfo = map.value.filter((item: { entryNum: number; checkNum: string; regionNum: string; }) => item.entryNum && (item.checkNum || item.regionNum != 'None'))
      let mapList = []
      for (let ele of mapInfo) {
        let info = {...ele}
        if (info.regionNum == 'None') {
          info.regionNum = '0'
        }
        mapList.push(info)
      }
      await addEdit({"template": formData, "map": mapList});
      drawerVisible.value = false;
      templateVisible.value = false;
      insertForm.value = insertFormData()
      checksNum.value = 0
      noChecksNum.value = 0
      // setTimeout(() => map.value = [], 50)
      Message.success({content: '操作成功', position: 'top'});
      // 调用父组件方法进行画面刷新
      emits('handleQuery');
    } catch (e) {
      console.log("执行失败," + JSON.stringify(e))
    } finally {
      okLoading.value = false;
    }
  } else {
    okLoading.value = false;
  }
};
// 取消
const handleCancel = () => {
  insertForm.value = insertFormData();
  drawerVisible.value = false;
  // 调用父组件方法进行画面刷新
  // emits('handleQuery');
}

const originalData = ref([] as any)
const handleNext = async () => {
  map.value = []
  const validRes = await formRef.value?.validate();
  // entries不能小于plotwide
  if (Number(insertForm.value.numEntries) < Number(insertForm.value.plotsWide)) {
    Message.error({content: 'The number of entries cannot be less than the plots wide', position: 'top'});
    return
  }
  if (!validRes) {
    templateVisible.value = true
    drawerVisible.value = false
    // 设置颜色板
    setColorData()
    mapLoading.value = true
    try {
      if (handleType.value == '1') {
        // 设置check信息
        noChecksNum.value = insertForm.value.numEntries!
        checksNum.value = 0
        // 设置颜色数据
        for (let eleColor of data.value) {
          if (eleColor.region === 'None') {
            eleColor.count = Number(insertForm.value.numEntries)
          } else {
            eleColor.count = 0
          }
        }
        // 获取地图数据
        const res = await getMapInfo({"template": insertForm.value})
        // 设置地图数据
        map.value = res.mapList.flatMap((item: any) => item)
        // 保留原始地图数据
        originalData.value = res.mapList
        // 设置地图显示的行数
        rows = res.rowNum
      } else {
        // 查询地图信息
        const res = await getMapInfo({"template": insertForm.value})
        // 设置地图数据
        map.value = res.mapList.flatMap((item: any) => item)
        // 保留原始地图数据
        originalData.value = res.mapList
        // 设置地图显示的行数
        rows = res.rowNum
        // 设置颜色信息
        let colorList = res.mapColorList.filter((item: { regionNum: number; }) => item.regionNum != 0)
        data.value[0].count = insertForm.value.numEntries!
        for (let ele of colorList) {
          let color = data.value.find((item: { region: string; }) => item.region == ele.regionNum)
          color!.count++
          data.value[0].count--
        }
        // 设置check信息
        checksNum.value = res.checkNum
        noChecksNum.value = insertForm.value.numEntries! - res.checkNum
      }
    } catch (e) {
      console.log(e)
    } finally {
      mapLoading.value = false
    }

  }
}
let isMouseDown = false;
let startIndex = 0;

const mouseDownEvent = (index: number) => {
  console.log("mouseDown:" + index)
  isMouseDown = true
  startIndex = index
  const divInfo = 'div' + index; // 动态生成 ID
  const divElement = document.getElementById(divInfo); // 通过 ID 获取元素
  divElement!.style.border = '2px solid'
}

const mouseOverEvent = (index: number) => {
  console.log("mouseOver:" + index)
  if (isMouseDown) {
    const currentIndex = index;

    // 计算选中区域的起点和终点
    const [start, end] = startIndex < currentIndex ?
        [startIndex, currentIndex] : [currentIndex, startIndex];

    // 清除之前的选中状态

    // 选中区域
    for (let i = start; i <= end; i++) {
      const rowStart = Math.floor(startIndex / (insertForm.value.plotsWide! + 1));
      const colStart = startIndex % (insertForm.value.plotsWide! + 1);
      const rowEnd = Math.floor(currentIndex / (insertForm.value.plotsWide! + 1));
      const colEnd = currentIndex % (insertForm.value.plotsWide! + 1);

      for (let r = Math.min(rowStart, rowEnd); r <= Math.max(rowStart, rowEnd); r++) {
        for (let c = Math.min(colStart, colEnd); c <= Math.max(colStart, colEnd); c++) {
          const selectedIndex = r * (insertForm.value.plotsWide! + 1) + c; // 计算该行列在 gridItems 中的索引
          if (selectedIndex >= 0) {
            const divInfo = 'div' + selectedIndex; // 动态生成 ID
            const divElement = document.getElementById(divInfo); // 通过 ID 获取元素
            divElement!.style.border = '2px solid'
          }
        }
      }
    }
  }
}

const mouseUpEvent = (index: number) => {
  console.log("mouseUp:" + index)
  isMouseDown = false; // 鼠标抬起时重置状态
}

const handlePrev = () => {
  drawerVisible.value = true
  templateVisible.value = false
  checksNum.value = 0
  noChecksNum.value = 0
  // setTimeout(() => map.value = [], 50)
}

const handleCancelMap = () => {
  templateVisible.value = false
  insertForm.value = insertFormData();
  checksNum.value = 0
  noChecksNum.value = 0
  // setTimeout(() => map.value = [], 50)
}

const map = ref([] as any)

const selectedKeys = ref([] as any);

const rowSelection = reactive({
  type: 'radio'
});

const columns = [
  {
    title: 'Region',
    dataIndex: 'region',
  },
  {
    title: 'Count',
    dataIndex: 'count',
  },
];
const data = ref([] as any)

const setColorData = () => {
  data.value = [
    {
      region: 'None',
      count: 0,
      color: '#ffffff',
    }, {
      region: '1',
      count: 0,
      color: '#ffc0cb',
    }, {
      region: '2',
      count: 0,
      color: '#add8e6',
    }, {
      region: '3',
      count: 0,
      color: '#90ee90',
    }, {
      region: '4',
      count: 0,
      color: '#ffff00',
    }, {
      region: '5',
      count: 0,
      color: '#f5f5dc',
    }, {
      region: '6',
      count: 0,
      color: '#00ffff',
    }, {
      region: '7',
      count: 0,
      color: '#ffa07a',
    }, {
      region: '8',
      count: 0,
      color: '#eee8aa',
    }, {
      region: '9',
      count: 0,
      color: '#ff0000',
    }, {
      region: '10',
      count: 0,
      color: '#0000ff',
    }, {
      region: '11',
      count: 0,
      color: '#008000',
    }, {
      region: '12',
      count: 0,
      color: '#ffa500',
    }]
}

const titleColorList = [
  {
    region: '21',
    color: '#ececec',
  }, {
    region: '22',
    color: '#d3d3d3',
  }
]
// 设置颜色时,获取选择数据的下标
const selIndex = ref("")
const oldData = ref({} as any)
const inputClick = (index: string) => {
  // 设置div的选中效果
  // 将之前选择的div的样式还原
  if (selIndex.value) {
    const oldDiv = 'div' + selIndex.value; // 动态生成 ID
    const oldEle = document.getElementById(oldDiv); // 通过 ID 获取元素
    oldEle!.style.border = '1px solid #ccc'
  }
  // 对选中的div进行样式设置
  const divInfo = 'div' + index; // 动态生成 ID
  const divElement = document.getElementById(divInfo); // 通过 ID 获取元素
  divElement!.style.border = '2px solid'
  selIndex.value = index
  // 获取修改之前的值
  oldData.value = {...map.value[selIndex.value]}
}

const changeVal = (index: string) => {
  // 如果旧值为空,新值为空,则不更新check信息;
  // 如果旧值为空,新增不为空,则更新check信息;
  // 如果旧值不为空,新值为空,则更新check信息;
  // 如果旧值不为空,新值不为空,则不更新check信息
  // 获取新值
  const newData = map.value[index]
  // 数值校验
  const regex = /^[1-9]\d*$/;
  if (newData.checkNum && !regex.test(newData.checkNum)) {
    Message.error({content: 'checkNo必须是正整数', position: 'top'});
    newData.checkNum = ''
    return
  }
  if (!oldData.value.checkNum && newData.checkNum) {
    checksNum.value++
    noChecksNum.value--
  } else if (oldData.value.checkNum && !newData.checkNum) {
    checksNum.value--
    noChecksNum.value++
  }
}

// 选择颜色后,调用设置方法
const setColor = () => {
  // 校验
  if (!selIndex.value) {
    Message.error({content: '请选择要设置颜色的区域', position: 'top'});
    return
  }
  if (selectedKeys.value.length === 0) {
    Message.error({content: '请选择要设置的颜色', position: 'top'});
    return
  }
  // 获取选择的div元素
  const inputId = 'input' + selIndex.value; // 动态生成 ID
  const inputElement = document.getElementById(inputId); // 通过 ID 获取元素

  // 获取原来的颜色
  let selItem = map.value[selIndex.value]
  if (selItem.regionNum != selectedKeys.value[0]) {
    // 原颜色数量减1
    let oldColor = data.value.find((item: { region: string; }) => item.region == selItem.regionNum)
    oldColor!.count--
    // 新颜色数量+1
    const newColor = data.value.find((item: { region: string; }) => item.region == selectedKeys.value[0])
    newColor!.count++
    // 对选中的div进行颜色设置
    inputElement!.style.backgroundColor = newColor!.color
    // 将新颜色进行保存
    selItem.regionNum = selectedKeys.value[0]
  }
  // 设置完成之后清空
  selectedKeys.value = []
}
// 分割数组
const splitArray = (arr: any, chunkSize: number) => {
  const result = [];
  for (let i = 0; i < arr.length; i += chunkSize) {
    result.push(arr.slice(i, i + chunkSize));
  }
  return result;
}

//设置地图数据
let rows = 0;
const setInsData = (formData: any) => {
  // 将数据按照小区数进行分割,如果最后一个集合的长度小于小区数,则补充X直至等于小区数。
  // 如果种植规则是left to right,则直接返回,如果是龙摆尾则对偶数行进行顺序反转
  let dataList = []
  for (let i = 1; i <= formData.numEntries; i++) {
    dataList.push({'entryNum': i, 'checkNum': '', 'regionNum': 'None', 'disable': '0'})
  }
  let splitList = splitArray(dataList, formData.plotsWide)
  let splitLength = splitList.length
  // 补充地图
  let len = splitList[splitLength - 1].length
  if (len != formData.plotsWide) {
    for (let i = len; i < formData.plotsWide; i++) {
      splitList[splitLength - 1].push({'entryNum': '', 'checkNum': 'X', 'regionNum': '22', 'disable': '1'})
    }
  }
  // 设置走向
  if (formData.mapPlacement == '2') {
    for (let i = 0; i < splitList.length; i++) {
      if ((i + 1) % 2 == 0) {
        splitList[i].reverse()
      }
      splitList[i].unshift({'entryNum': '', 'checkNum': String(i + 1), 'regionNum': '21', 'disable': '1'})
    }
  } else if (formData.mapPlacement == '1') {
    for (let i = 0; i < splitList.length; i++) {
      splitList[i].unshift({'entryNum': '', 'checkNum': String(i + 1), 'regionNum': '21', 'disable': '1'})
    }
  }
  let titleArr = []
  for (let i = 0; i <= formData.plotsWide; i++) {
    titleArr.push({'entryNum': '', 'checkNum': i == 0 ? '' : String(i), 'regionNum': '21', 'disable': '1'})
  }
  splitList.push(titleArr)
  splitList.reverse()
  rows = splitList.length
  return splitList.flatMap(item => item)
}

const setDefaultColor = (regionNum: string) => {
  const color = data.value.find((item: { region: string; }) => item.region == regionNum)
  if (color) {
    return {backgroundColor: color!.color};
  } else {
    // const titleColor = titleColorList.find(item => item.region == regionNum)
    return {backgroundColor: '#ececec'};
  }
}

const checkRatio = computed(() => {
  return (checksNum.value / Number(insertForm.value.numEntries)).toFixed(3)
})

// 页面加载时,设置页面中每行显示的位数
const setGridTemplateColumns = () => {
  return {
    display: 'grid',
    gridTemplateColumns: `repeat(${insertForm.value.plotsWide! + 1}, minmax(50px, 50px))`,
    gridTemplateRows: `repeat(${rows}, minmax(70px, 70px))`,
    // gridRowGap: '1px'
  };
}

// 斜对角放置
const handlePattern = () => {
  // 必须选择起始位置
  if (!selIndex.value) {
    Message.error({content: '请选择起始位置', position: 'top'});
    return
  }
  patternVisible.value = true
}

// 清空checks
const handleClearChecks = async () => {
  mapLoading.value = true;
  try {
    map.value.forEach((item: { entryNum: any; checkNum: string; }) => {
      if (item.entryNum) {
        item.checkNum = ''
      }
    })
    // 设置check信息
    checksNum.value = 0
    noChecksNum.value = 0
    // 清空颜色信息
    await handleClearRegions()
  } finally {
    mapLoading.value = false
  }

}

// 清空所有的颜色
const handleClearRegions = () => {
  mapLoading.value = true;
  try {
    map.value.forEach((item: { entryNum: any; regionNum: string; }) => {
      if (item.entryNum) {
        item.regionNum = 'None'
      }
    })
    // 清空颜色信息
    setColorData()
    data.value[0].count = insertForm.value.numEntries!
  } finally {
    mapLoading.value = false
  }
}

// 设置对角放置
const patternOk = async () => {
  // 表单校验处理
  const validateRes = await patternFormRef.value.validate()
  if (validateRes) {
    return
  }
  patternVisible.value = false
  mapLoading.value = true;
  try {
    // 根据起始位置查找entry no
    const selData = map.value[selIndex.value]
    if (patternData.value.checkOption === 1) {
      selData.checkNum = patternData.value.setVal.toString()
    } else {
      selData.checkNum = getRandomInteger(patternData.value.minVal, patternData.value.maxVal).toString()
    }
    // 行下标
    let selRowIndex: number = 0
    // 列下标
    let selColIndex: number = 0
    for (let i = 0; i < originalData.value.length; i++) {
      const index = originalData.value[i].findIndex((item: { entryNum: any; }) => item.entryNum == selData.entryNum)
      if (index >= 0) {
        selRowIndex = i
        selColIndex = index
        break
      }
    }
    if (selRowIndex == 0) {
      return
    }
    for (let i = selRowIndex - patternData.value.yVal; i >= 1; i = i - patternData.value.yVal) {
      selColIndex = selColIndex + patternData.value.xVal
      if (selColIndex > insertForm.value.plotsWide!) {
        break
      }
      let next = originalData.value[i][selColIndex]
      let data = map.value.find((x: { entryNum: number; }) => x.entryNum == next.entryNum)
      if (patternData.value.checkOption === 1) {
        data.checkNum = patternData.value.setVal.toString()
      } else {
        data.checkNum = getRandomInteger(patternData.value.minVal, patternData.value.maxVal).toString()
      }
    }
    const checks = map.value.filter((x: {
        entryNum: any; checkNum: any;
    }) => x.entryNum && x.checkNum).length
    checksNum.value = checks
    noChecksNum.value = insertForm.value.numEntries! - checks
  } finally {
    mapLoading.value = false;
  }
}

// 生成随机数
const getRandomInteger = (min: number, max: number) => {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

const patternCancel = () => {
  patternVisible.value = false
  patternData.value = patternFormData()
}


// 组件完成初始渲染并创建 DOM 节点后运行
// onMounted(async () => {
//   await getLocationInfo()
// })

</script>
<style scoped>
.container {
  padding: 0px 20px 20px 0px; /* 整体内边距 */
  width: 100%;
  height: 620px;
  overflow: auto;
}

.input-wrapper {
  width: 50px;
  height: 70px;
  padding: 0px 0px 0px 0px; /* 内边距 */
  border: 1px solid #ccc; /* 边框 */
  /*margin-bottom: 1px;*/
  /*margin-top: 1px;*/
  /*border-radius: 5px; !* 圆角 *!*/
}

.input {
  width: 100%; /* 输入框占满父容器 */
  height: 100%;
  border: 1px; /* 移除默认边框 */
  font-weight: bold;
  /*outline: none; !* 移除聚焦时的轮廓 *!*/
}

:deep .arco-input-wrapper {
  padding-right: 1px;
  padding-left: 1px
}
</style>

4.总结

注意列数及行数的设置

注意列宽及行高的设置

注意行间距及列间距的设置

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Web前端中的Grid布局是一种用于创建网页布局的强大且灵活的CSS布局系统。它通过将网页分成行和列的网格,使得元素可以更容易地放置和对齐。Grid布局可以通过设置不同的属性来定义网格的行数、列数和大小。在Grid布局中,可以使用grid-template-areas属性来放置元素。通过在网格容器中使用grid-template-areas属性,可以为每个元素指定一个区域,并且根据这些区域来放置元素。 另外,Grid布局还可以通过grid-row-start和grid-row-end属性来定义元素在网格中的行位置,grid-column-start和grid-column-end属性来定义元素在网格中的列位置。通过设置这些属性的值,可以将元素放置在特定的行和列中。 例如,可以使用grid-column属性设置元素跨越多列,grid-row属性设置元素跨越多行。 这些属性的值可以是具体的行和列编号,也可以是span关键字加上具体的跨度值。通过组合这些属性,可以灵活地定义元素在网格中的位置和大小。 总的来说,Web前端中的Grid布局是一种强大的布局系统,可以通过设置不同的属性和值来定义元素在网格中的位置和大小,从而实现灵活的网页布局。 <span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [前端Grid布局](https://blog.csdn.net/weixin_46372074/article/details/123462030)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值