一次 grid 布局导致的echarts resize失效的记录

1. 问题背景:

页面使用grid布局分成三列,前两列分别占1fr宽度,第三列固定450px宽度。开始只写了第一列的内容,页面缩放时图表可以正常resize,但是在开始写第二列图表时,缩放页面发现第一列宽度正常缩小,但是第二列的宽度不会自动缩小,要手动刷新页面才可以。

页面主要布局结构如下:

<div class="grid gap-10px grid-cols-3">
  <!-- 第一列内容 -->
  <div class="">
    // ... 原有内容 ...
  </div>

  <!-- 第二列内容 -->
  <div class="">
    // ... 原有内容 ...
  </div>

  <!-- 第三列保持固定宽度 -->
  <div class=""></div>
</div>

页面缩小后的效果图:(中间列宽度没有按预期缩小,左侧列被挤压)
image

2. 修复问题:

<div class="grid gap-10px grid-cols-[minmax(0,1fr)_minmax(0,1fr)_450px]">
  <!-- 第一列 -->
  <div class="">
    // ... 原有内容 ...
  </div>

  <!-- 第二列 -->
  <div class="">
    // ... 原有内容 ...
  </div>

  <!-- 第三列保持固定宽度 -->
  <div class=""></div>
</div>

修改后的效果图:(一、二列宽度一致且正常缩小,三列固定宽度不变)
image

使用 minmax(0,1fr) 替代简单的 1fr,防止内容溢出导致的布局问题。

3. 完整代码(仅用于参考)

点此可查看完整内容代码
<template>
  <div class="h-full p-20px box-border">
    <div class="flex items-center mb-20px">
      <el-button-group class="mr-20px">
        <el-button
          plain
          v-for="item in dateList"
          :key="item.value"
          :class="{
            'active-btn': currentDate === item.value,
          }"
          @click="dateChange(item.value)"
        >
          {{ item.label }}
        </el-button>
      </el-button-group>
      <div class="w-260px">
        <el-date-picker
          v-model="dateSelectValue"
          type="daterange"
          range-separator="至"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
        />
      </div>
    </div>
    <div class="grid gap-10px grid-cols-[1fr_1fr_450px]">
      <div class="">
        <div class="card">
          <div class="card-title">工单统计</div>
          <ul class="card-list flex flex-wrap">
            <li v-for="item in orderList" :key="item.name">
              <img :src="item.icon" alt="" class="h-40px mr-10px" />
              <div class="flex flex-col">
                <div>{{ item.name }}</div>
                <div>
                  <span class="font-bold text-20px">{{ item.value }}</span> 张
                </div>
              </div>
            </li>
          </ul>
        </div>
        <div class="card">
          <div class="card-title">报告任务统计</div>
          <ul class="card-list grid grid-cols-2">
            <li v-for="item in reportList" :key="item.name">
              <img :src="item.icon" alt="" class="h-40px mr-10px" />
              <div class="flex flex-col">
                <div>{{ item.name }}</div>
                <div>
                  <span class="font-bold text-20px">{{ item.value }}</span> 个
                </div>
              </div>
            </li>
          </ul>
        </div>
        <div class="card">
          <div class="card-title">告警统计</div>
          <ul class="card-list grid grid-cols-2">
            <li v-for="item in alarmList" :key="item.name">
              <img :src="item.icon" alt="" class="h-40px mr-10px" />
              <div class="flex flex-col">
                <div>{{ item.name }}</div>
                <div>
                  <span class="font-bold text-20px">{{ item.value }}</span> 个
                </div>
              </div>
            </li>
          </ul>
        </div>
        <div class="card">
          <div class="card-title">任务统计</div>
          <div ref="taskChart" class="h-300px"></div>
        </div>
      </div>
      <div class="">
        <div class="card">
          <div class="card-title">工单状态分布</div>
          <div ref="orderStatusChart" class="h-300px"></div>
        </div>
        <div class="card">
          <div class="card-title">报告任务状态分布</div>
          <div ref="reportStatusChart" class="h-300px"></div>
        </div>
        <div class="card">
          <div class="card-title">告警等级分布</div>
          <div ref="alarmLevelChart" class="h-300px"></div>
        </div>
      </div>
      <div class=""></div>
    </div>
  </div>
</template>

<script setup>
import { ref, getCurrentInstance, reactive, onMounted, nextTick } from "vue";

import Header from "@/components/Header.vue";
import { useUserStore } from "@/stores/userStore";
const userStore = useUserStore();
import useVueRouter from "@/hooks/useRouter";
const { goBack, jumpPage } = useVueRouter();

import useChart from "@/utils/useChart";

const {
  proxy: { $api },
} = getCurrentInstance();

const dateList = ref([
  {
    label: "今天",
    value: "1",
  },
  {
    label: "近一周",
    value: "2",
  },
  {
    label: "近一个月",
    value: "3",
  },
]);
const currentDate = ref("1");
const dateSelectValue = ref("");

const dateChange = (val) => {
  currentDate.value = val;
};

const allIcon = new URL("@/assets/images/icon1.png", import.meta.url).href;
const finishIcon = new URL("@/assets/images/icon2.png", import.meta.url).href;
const waitIcon = new URL("@/assets/images/icon3.png", import.meta.url).href;
const processIcon = new URL("@/assets/images/icon4.png", import.meta.url).href;
const overtimeIcon = new URL("@/assets/images/icon5.png", import.meta.url).href;
const orderList = ref([
  {
    icon: allIcon,
    name: "累计工单",
    value: 310,
  },
  {
    icon: finishIcon,
    name: "已处理工单",
    value: 270,
  },
  {
    icon: waitIcon,
    name: "待处理",
    value: 40,
  },
  {
    icon: processIcon,
    name: "处理中",
    value: 4,
  },
  {
    icon: overtimeIcon,
    name: "超时工单",
    value: 40,
  },
]);
const reportList = ref([
  {
    icon: allIcon,
    name: "累计历史报告任务",
    value: 314,
  },
  {
    icon: finishIcon,
    name: "已处理任务报告",
    value: 40,
  },
  {
    icon: processIcon,
    name: "处理中任务报告",
    value: 270,
  },
  {
    icon: waitIcon,
    name: "待处理任务报告",
    value: 4,
  },
]);
const alarmList = ref([
  {
    icon: allIcon,
    name: "累计历史告警",
    value: 88,
  },
  {
    icon: overtimeIcon,
    name: "高风险告警",
    value: 12,
  },
  {
    icon: processIcon,
    name: "中风险告警",
    value: 23,
  },
  {
    icon: finishIcon,
    name: "低风险告警",
    value: 53,
  },
]);

const taskChart = ref(null);
const { setOption: setTaskOption } = useChart(taskChart, true, false);

const orderStatusChart = ref(null);
const { setOption: setOrderStatusOption } = useChart(
  orderStatusChart,
  true,
  false
);

const reportStatusChart = ref(null);
const { setOption: setReportStatusOption } = useChart(
  reportStatusChart,
  true,
  false
);

const alarmLevelChart = ref(null);
const { setOption: setAlarmLevelOption } = useChart(
  alarmLevelChart,
  true,
  false
);

onMounted(() => {
  initTaskChart();
  initOrderStatusChart();
  initReportStatusChart();
  initAlarmLevelChart();
  // window.addEventListener("resize", () => {
  //   taskChart.value?.resize();
  //   orderStatusChart.value?.resize();
  //   reportStatusChart.value?.resize();
  //   alarmLevelChart.value?.resize();
  // });
});
const initTaskChart = () => {
  const option = {
    tooltip: {
      trigger: "axis",
      axisPointer: {
        // Use axis to trigger tooltip
        type: "shadow", // 'shadow' as default; can also be 'line' or 'shadow'
      },
    },
    legend: {},
    grid: {
      left: "3%",
      right: "4%",
      bottom: "3%",
      containLabel: true,
    },
    xAxis: {
      type: "value",
    },
    yAxis: {
      type: "category",
      data: ["巡检", "配置", "变更", "发布", "事件"],
    },
    series: [
      {
        name: "执行中",
        type: "bar",
        stack: "total",
        label: {
          show: true,
        },
        emphasis: {
          focus: "series",
        },
        itemStyle: {
          color: "#5299fb",
        },
        data: [23, 31, 67, 24, 56],
      },
      {
        name: "已完成",
        type: "bar",
        stack: "total",
        label: {
          show: true,
        },
        emphasis: {
          focus: "series",
        },
        itemStyle: {
          color: "#31de7a",
        },
        data: [17, 69, 45, 46, 74],
      },
    ],
  };
  setTaskOption(option);
};
const initOrderStatusChart = () => {
  const option = {
    tooltip: {
      trigger: "item",
    },
    series: [
      {
        name: "状态",
        type: "pie",
        radius: ["40%", "70%"],
        avoidLabelOverlap: false,
        color: ["#ff9900", "#5299fb", "#31de7a"],
        label: {
          show: true,
          position: "outside",
          formatter: "{a|{b},{c}\n{d}%}",
          rich: {
            a: {
              align: "left",
              lineHeight: 20,
            },
          },
        },
        // labelLine: {
        //   normal: {
        //     length: 15,
        //     length2: 30,
        //     lineStyle: {
        //       width: 1,
        //     },
        //   },
        // },
        data: [
          { value: 40, name: "待处理" },
          { value: 23, name: "处理中" },
          { value: 270, name: "已处理" },
        ],
      },
    ],
  };
  setOrderStatusOption(option);
};
const initReportStatusChart = () => {
  const option = {
    title: [
      {
        text: "报告任务",
        x: "50%",
        y: "43%",
        textAlign: "center",
        textStyle: {
          fontSize: "14",
          fontWeight: "normal",
          color: "#7F8D9D",
          textAlign: "center",
        },
      },
      {
        text: "310条",
        left: "50%",
        top: "52%",
        textAlign: "center",
        textStyle: {
          fontSize: "18",
          color: "#5c5a68",
          textAlign: "center",
        },
      },
    ],
    tooltip: {
      trigger: "item",
    },
    legend: {
      top: "0",
      left: "60%",
    },
    series: [
      {
        name: "状态",
        type: "pie",
        radius: ["40%", "51%"],
        avoidLabelOverlap: false,
        padAngle: 5,
        itemStyle: {
          borderRadius: 10,
        },
        color: ["#5299fb", "#31de7a"],
        label: {
          show: true,
          position: "outside",
          formatter: "{a|{b},{c}}",
          rich: {
            a: {
              align: "left",
              lineHeight: 20,
            },
          },
        },
        itemStyle: {
          borderRadius: 100,
          shadowColor: "#e8effa",
          shadowBlur: 0,
          shadowOffsetY: 0,
          shadowOffsetX: 0,
          borderColor: "#e8effa",
          borderWidth: 4,
        },
        data: [
          { value: 270, name: "处理中" },
          { value: 40, name: "已处理" },
        ],
      },
    ],
  };
  setReportStatusOption(option);
};
const initAlarmLevelChart = () => {
  const option = {
    tooltip: {
      trigger: "item",
    },
    series: [
      {
        name: "等级",
        type: "pie",
        // radius: ["40%", "70%"],
        avoidLabelOverlap: false,
        color: ["#f05a28", "#ff9900", "#5299fb"],
        label: {
          show: true,
          position: "outside",
          formatter: "{a|{b},{c}\n{d}%}",
          rich: {
            a: {
              align: "left",
              lineHeight: 20,
            },
          },
        },
        data: [
          { value: 12, name: "高风险" },
          { value: 23, name: "中风险" },
          { value: 53, name: "低风险" },
        ],
      },
    ],
  };
  setAlarmLevelOption(option);
};
</script>

<style scoped>
.el-button,
:deep(.el-input__wrapper) {
  background-color: transparent;
}
:deep(.el-date-editor.el-input),
:deep(.el-date-editor.el-input__wrapper) {
  width: 260px;
}
.active-btn {
  border-color: #409eff !important;
  color: #409eff !important;
  z-index: 1;
}
.card {
  margin-bottom: 20px;
  padding: 10px;
  box-sizing: border-box;
  background-color: #fbfbfb;
}
.card-title {
  position: relative;
  margin-bottom: 10px;
  padding-left: 15px;
  font-size: 16px;
  font-weight: bold;
}
.card-title::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 4px;
  height: 100%;
  background-color: #409eff;
}
.card-list li {
  display: flex;
  align-items: center;
  min-width: 180px;
  max-width: 200px;
  margin-bottom: 20px;
  margin-right: 10px;
  padding: 10px;
  border-radius: 8px;
  background-image: url("../../assets/images/card-bg.svg");
  background-size: cover;
  color: #3d5063;
}
</style>

posted @ 2025-06-25 14:51  Li_pk  阅读(81)  评论(0)    收藏  举报