index.vue 7.09 KB
<script lang="ts" setup>
  import { Button, PageHeader } from 'ant-design-vue';
  import { GridItem, GridLayout, Layout } from 'vue3-grid-layout';
  import { nextTick, ref } from 'vue';
  import BaseDashboard from '../cpns/Dashboard/BaseDashboard.vue';
  import WidgetWrapper from '../cpns/WidgetWrapper/WidgetWrapper.vue';
  import BaseWidgetHeader from '../cpns/WidgetHeader/BaseWidgetHeader.vue';
  const handleBack = () => {};

  interface ChartAttr {
    id: string | number;
    width: number;
    height: number;
  }

  interface ChartSetting extends Layout {
    chart: ChartAttr[];
  }

  const widgetEl = new Map<string, Fn>();

  const id = '296Charts';
  // GridItem.
  const layout = ref<ChartSetting[]>([
    {
      x: 0,
      y: 0,
      w: 6,
      h: 6,
      i: id,
      static: false,
      chart: [
        { id: 'a', width: 100, height: 100 },
        { id: 'b', width: 100, height: 100 },
        { id: 'c', width: 100, height: 100 },
        { id: 'd', width: 100, height: 100 },
        { id: 'e', width: 100, height: 100 },
        { id: 'f', width: 100, height: 100 },
        { id: 'g', width: 100, height: 100 },
        { id: 'h', width: 100, height: 100 },
        { id: 'i', width: 100, height: 100 },
      ],
    },
    {
      x: 0,
      y: 0,
      w: 6,
      h: 6,
      i: 'sdasdf',
      static: false,
      chart: [
        { id: 'j', width: 100, height: 100 },
        { id: 'k', width: 100, height: 100 },
        { id: 'l', width: 100, height: 100 },
        { id: 'm', width: 100, height: 100 },
        { id: 'n', width: 100, height: 100 },
        { id: 'o', width: 100, height: 100 },
        { id: 'p', width: 100, height: 100 },
        { id: 'q', width: 100, height: 100 },
        { id: 'r', width: 100, height: 100 },
      ],
    },
  ]);
  const draggable = ref(true);
  const resizable = ref(true);

  const GirdLayoutColNum = 24;

  const GridLayoutMargin = 10;

  function updateSize(i: string, newH: number, newW: number, newHPx: number, newWPx: number) {
    newWPx = Number(newWPx);
    newHPx = Number(newHPx);

    const data = layout.value.find((item) => item.i === i)!;
    const border = 2;
    const length = data.chart.length || 0;

    const row = Math.floor(Math.pow(length, 0.5));
    const col = Math.floor(length / row);
    let width = Math.floor(100 / col);
    let height = Math.floor(100 / row);

    const WHRatio = newWPx / newHPx;
    const HWRatio = newHPx / newWPx;

    if (WHRatio > 1.6) {
      width = Math.floor(100 / length);
      height = 100;
    }

    if (HWRatio > 1.6) {
      height = Math.floor(100 / length);
      width = 100;
    }

    data.chart = data?.chart.map((item) => {
      return {
        ...item,
        width,
        height,
      };
    });
    nextTick(() => {
      const updateFn = widgetEl.get(i);
      if (updateFn) updateFn();
    });
  }

  const itemResized = (i: string, newH: number, newW: number, newHPx: number, newWPx: number) => {
    updateSize(i, newH, newW, newHPx, newWPx);
  };

  const itemContainerResized = (
    i: string,
    newH: number,
    newW: number,
    newHPx: number,
    newWPx: number
  ) => {
    updateSize(i, newH, newW, newHPx, newWPx);
  };

  const updateCharts = (i: string) => {
    nextTick(() => {
      const updateFn = widgetEl.get(i);
      if (updateFn) updateFn();
    });
  };

  const setComponentRef = (el: Element, record: ChartSetting) => {
    if (widgetEl.has(record.i)) widgetEl.delete(record.i);
    if (el && (el as unknown as { update: Fn }).update)
      widgetEl.set(record.i, (el as unknown as { update: Fn }).update);
  };
</script>

<template>
  <!-- <PageWrapper title="水电表看板" @back="handleBack" content="已创建组件: 3个">
    <template #extra>
      <Button type="primary">创建组件</Button>
    </template>

  </PageWrapper> -->
  <section class="bg-light-50 flex flex-col overflow-hidden h-full w-full">
    <PageHeader title="水电表看板" @back="handleBack">
      <template #extra>
        <Button type="primary">创建组件</Button>
      </template>
      <div>已创建组件: 3个</div>
    </PageHeader>
    <section class="flex-1">
      <GridLayout
        v-model:layout="layout"
        :col-num="GirdLayoutColNum"
        :row-height="30"
        :margin="[GridLayoutMargin, GridLayoutMargin]"
        :is-draggable="draggable"
        :is-resizable="resizable"
        :vertical-compact="true"
        :use-css-transforms="true"
        style="width: 100%"
      >
        <GridItem
          v-for="item in layout"
          :key="item.i"
          :static="item.static"
          :x="item.x"
          :y="item.y"
          :w="item.w"
          :h="item.h"
          :i="item.i"
          style="display: flex; flex-wrap: wrap"
          class="grid-item-layout"
          @resized="itemResized"
          @resize="itemResized"
          @moved="updateCharts"
          @container-resized="itemContainerResized"
        >
          <WidgetWrapper
            :key="item.i"
            :ref="(el) => setComponentRef(el, item)"
            :data-source="item.chart"
          >
            <template #header>
              <!-- <div>header</div> -->
              <BaseWidgetHeader />
            </template>
            <template #controls="{ record, add }">
              <!-- <ToggleSwitch /> -->
              <!-- <SlidingSwitch @change="handleChange" /> -->
              <!-- <LightBulbSwitch @change="handleChange" /> -->
              <!-- <div :id="getControlsWidgetId(record.id)" class="widget-charts" /> -->
              <BaseDashboard :data-source="record" :add="add" />
            </template>
            <template #value="{ record }">
              <span>{{ record.id }}</span>
            </template>
            <template #label="{ record }">
              <span>{{ record.id }}</span>
            </template>
          </WidgetWrapper>
        </GridItem>
      </GridLayout>
    </section>
  </section>
</template>

<style>
  .vue-grid-item:not(.vue-grid-placeholder) {
    background: #fcfcfc;
    border: 1px solid black;
  }
  .vue-grid-item .resizing {
    opacity: 0.9;
  }
  .vue-grid-item .static {
    background: #cce;
  }
  .vue-grid-item .text {
    font-size: 24px;
    text-align: center;
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
    height: 100%;
    width: 100%;
  }
  .vue-grid-item .no-drag {
    height: 100%;
    width: 100%;
  }
  .vue-grid-item .minMax {
    font-size: 12px;
  }
  .vue-grid-item .add {
    cursor: pointer;
  }
  .vue-draggable-handle {
    position: absolute;
    width: 20px;
    height: 20px;
    top: 0;
    left: 0;
    background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><circle cx='5' cy='5' r='5' fill='#999999'/></svg>")
      no-repeat;
    background-position: bottom right;
    padding: 0 8px 8px 0;
    background-repeat: no-repeat;
    background-origin: content-box;
    box-sizing: border-box;
    cursor: pointer;
  }

  .container {
    display: grid;
    grid-template-columns: 3;
    grid-row: 3;
  }

  .grid-item-layout {
    overflow: hidden;
    border: 1px solid #eee !important;
    background-color: #fcfcfc !important;
  }
</style>