【Vue2】环图报表组件

发布于:2025-08-14 ⋅ 阅读:(17) ⋅ 点赞:(0)

所有文章都是免费查看的,如果有无法查看的情况,烦请联系我修改哈~

序言

        先看效果图:

        主要功能:中间一张上传的图片,从图片任一点位引出一根线,线的尾端是一张报表(也可以是其他需要的图片或功能)

        框架:vue2(2.6.14) + element-ui(2.15.14)+ echarts(5.6.0)+ less(4.3.0)

一、报表组件

        报表组件主要就是展示以及数值传递,功能比较简单,需要注意页面变化时报表的渲染、报表内存的销毁以及穿透操作,其他我就不多赘述,直接上代码:

// WrapCharts

<template>
    <div>
        <div ref="echartsContainer" class="chart-container"></div>
    </div>
</template>

<script>
import * as echarts from "echarts";

export default {
    name: "LineChart",
    props: {
        data: {
            type: Object,
            required: true,
            default: () => {},
        },
    },
    data() {
        return {
            chart: null, // 存储图表实例
        };
    },
    methods: {
        initChart() {
            // 获取容器 DOM 元素
            const chartContainer = this.$refs.echartsContainer;
            if (!chartContainer) {
                console.error("Chart container is not available");
                return;
            }
            // 初始化图表
            this.chart = echarts.init(chartContainer);

            // 设置图表配置项
            const option = {
                grid: {
                    top: "15%", // 上边距(百分比)
                    bottom: "5%", // 下边距(百分比)
                    left: "5%", // 左边距
                    right: "15%", // 右边距
                    containLabel: true, // 确保坐标轴标签不超出 grid
                },
                tooltip: {
                    trigger: "axis",
                    appendToBody: true,
                    confine: false,
                    className: "echart-tooltip-overlay",
                    textStyle: {
                        fontSize: 12, // 设置tooltip字体大小
                    },
                    position: function (pos, params, dom, rect, size) {
                        // 自定义定位逻辑
                        return [pos[0], pos[1] - size.contentSize[1] - 10];
                    },
                },
                xAxis: {
                    type: "category",
                    data: this.data.xAxisData, // 从 props 中获取数据数组,
                    axisLabel: {
                        textStyle: {
                            fontSize: 10, // 设置字体大小
                        },
                    },
                },
                yAxis: {
                    type: "value",
                    max: this.data.maxY,
                    min: this.data.minY,
                    axisLabel: {
                        textStyle: {
                            fontSize: 10, // 设置字体大小
                        },
                    },
                },
                series: [
                    {
                        data: this.data.seriesData, // 从 props 中获取数据数组,
                        type: "line",
                        name: "实际值",
                        markLine: {
                            symbol: "none",
                            data: [
                                {
                                    name: "上限",
                                    yAxis: this.data.upperLimit,
                                    lineStyle: {
                                        type: "dashed",
                                        color: "#ff0000",
                                    },
                                    label: {
                                        show: true,
                                        fontSize: 10,
                                        formatter: "{c}",
                                    },
                                },
                                {
                                    name: "下限",
                                    yAxis: this.data.lowerLimit,
                                    lineStyle: {
                                        type: "dashed",
                                        color: "#ff0000",
                                    },
                                    label: {
                                        show: true,
                                        fontSize: 10,
                                        formatter: "{c}",
                                    },
                                }
                            ],
                        },
                    },
                ],
            };

            // 使用刚定义的配置项和数据显示图表
            option && this.chart.setOption(option);
        },
    },
    mounted() {
        this.$nextTick(() => {
            this.initChart();
        });
    },
    beforeDestroy() {
        // 销毁图表实例,避免内存泄漏
        if (this.chart) {
            this.chart.dispose();
        }
    },
};
</script>

<style scoped lang="less">
.chart-container {
    width: 100%;
    height: 100%;
    overflow: visible !important;
}

.echart-tooltip-overlay {
    z-index: 9999 !important;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2) !important;
    pointer-events: none; /* 允许穿透操作 */
}
</style>

二、核心组件

        主要思路是在图片的上下左右用el的布局隔离出一片区域以展示报表,其中上下是横向展示,满了之后换行展示,左右的则是一直往下展示,所以为了美观上下展示的报表最大数应是可被24整除的正整数(实际根据span确定,代码中我span写死了,实际情况可以用个传入参数来实现可变),左右的数量为了美观不宜超过图片的高度。

        图片的点位我使用了上下百分比来确定位置,与市面上大多数都不相同。所以后面我又增加了一个获取图片百分比点位的组件(后面展示)。

        线条使用canvas绘制,落点根据上下左右分别计算(主要根据报表主件的长宽确定)。展示使用的是直线,曲线部分被我注释了(实际效果一般),如有需要自行取用。

        下面是代码:

// WrapMain

<template>
    <div>
        <el-container class="margin-top-10">
            <el-header>
                <el-row
                    :gutter="20"
                    class="header-row"
                    type="flex"
                    justify="space-between"
                >
                    <el-col
                        v-for="(item, index) in wrapTemp.top"
                        :key="'top' + index"
                        :span="3"
                    >
                        <el-card :ref="'top' + index" class="grid-content">
                            <div slot="header" class="clearfix-header">
                                <span>{{ item.title }}</span>
                            </div>
                            <line-chart
                                :data="item.data"
                                class="grid-chart-wrap"
                            />
                        </el-card>
                    </el-col>
                </el-row>
            </el-header>

            <el-main>
                <el-row :gutter="20" class="header-row">
                    <el-col :span="3" class="flex-col">
                        <el-card
                            v-for="(item, index) in wrapTemp.left"
                            :key="'left' + index"
                            class="grid-content"
                            :class="{ 'margin-top-10': index > 0 }"
                            :ref="'left' + index"
                        >
                            <div slot="header" class="clearfix-header">
                                <span>{{ item.title }}</span>
                            </div>
                            <line-chart
                                :data="item.data"
                                class="grid-chart-wrap"
                            />
                        </el-card>
                    </el-col>

                    <el-col
                        :span="18"
                        v-if="
                            (wrapTemp.top && wrapTemp.top.length > 0) ||
                            wrapTemp.left.length > 0 ||
                            wrapTemp.right.length > 0 ||
                            wrapTemp.bottom.length > 0
                        "
                    >
                        <div class="image-container-wrapper">
                            <div class="image-container" ref="imageContainer">
                                <el-image
                                    :src="wrapTemp.img"
                                    fit="contain"
                                    @load="drawImageAndLines"
                                />
                            </div>
                        </div>
                    </el-col>

                    <el-col :span="3" class="flex-col">
                        <el-card
                            v-for="(item, index) in wrapTemp.right"
                            :key="'right' + index"
                            class="grid-content"
                            :class="{ 'margin-top-10': index > 0 }"
                            :ref="'right' + index"
                        >
                            <div slot="header" class="clearfix-header">
                                <span>{{ item.title }}</span>
                            </div>
                            <line-chart
                                :data="item.data"
                                class="grid-chart-wrap"
                            />
                        </el-card>
                    </el-col>
                </el-row>
            </el-main>

            <el-footer>
                <el-row
                    :gutter="20"
                    class="header-row"
                    type="flex"
                    justify="space-between"
                >
                    <el-col
                        v-for="(item, index) in wrapTemp.bottom"
                        :key="'bottom' + index"
                        :span="3"
                    >
                        <el-card class="grid-content" :ref="'bottom' + index">
                            <div slot="header" class="clearfix-header">
                                <span>{{ item.title }}</span>
                            </div>
                            <line-chart
                                :data="item.data"
                                class="grid-chart-wrap"
                            />
                        </el-card>
                    </el-col>
                </el-row>
            </el-footer>

            <!-- Canvas 用于绘制多条线条 -->
            <canvas ref="lineCanvas" class="line-canvas"></canvas>
        </el-container>
    </div>
</template>

<script>
import LineChart from "./LineChart.vue";

export default {
    components: {
        LineChart,
    },
    props: {
        wrapTemp: {},
    },
    data() {
        return {
            startPoints: [],
            targetDivs: [],
            imageDimensions: null, // 保存图片的宽度和高度
            dialogData: {}, // 用于传递给 chartDialog 的数据
            lineColor: "#000000", // 线条颜色
        };
    },
    methods: {
        // 绘制图片和线条
        drawImageAndLines(event) {
            // 获取图片 DOM 元素
            const imageElement = event.target;
            // 获取图片的宽度和高度
            this.imageDimensions = {
                width: imageElement.naturalWidth,
                height: imageElement.naturalHeight,
            };

            console.log("图片尺寸:", this.imageDimensions.width, this.imageDimensions.height);

            this.drawLines();
        },

        // 绘制线条(核心代码)
        drawLines() {
            if (!this.imageDimensions || !this.imageDimensions.width || !this.imageDimensions.height) {
                console.error("图片尺寸无效,无法绘制线条");
                return;
            }

            const canvas = this.$refs.lineCanvas;
            const ctx = canvas.getContext("2d");

            // 设置 canvas 尺寸为整个页面的大小
            canvas.width = this.$el.clientWidth;
            canvas.height = this.$el.clientHeight;

            // 清空画布
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            // 绘制每条线
            this.startPoints.forEach((startPoint, index) => {
                const targetItem = this.targetDivs[index];
                if (targetItem?.el) {
                    const targetRect = targetItem.el.getBoundingClientRect();
                    const canvasRect = canvas.getBoundingClientRect();

                    // 直接获取方向信息
                    const direction = targetItem.dir;

                    // 根据方向计算终点坐标
                    let endX, endY;
                    switch (direction) {
                        case "top":
                            endX =
                                targetRect.left +
                                targetRect.width / 2 -
                                canvasRect.left; // 相对于 canvas 的 X 坐标
                            endY =
                                targetRect.top +
                                targetRect.height -
                                canvasRect.top; // 相对于 canvas 的 Y 坐标
                            break;
                        case "bottom":
                            endX =
                                targetRect.left +
                                targetRect.width / 2 -
                                canvasRect.left;
                            endY =
                                targetRect.top +
                                targetRect.height / 2 -
                                canvasRect.top;
                            break;
                        case "left":
                            endX =
                                targetRect.left +
                                targetRect.width -
                                canvasRect.left;
                            endY =
                                targetRect.top +
                                targetRect.height -
                                canvasRect.top;
                            break;
                        case "right":
                            endX = targetRect.left - canvasRect.left;
                            endY =
                                targetRect.top +
                                targetRect.height -
                                canvasRect.top;
                            break;
                    }

                    // 假设图片在页面中居中显示,并且大小适应(需要根据实际情况调整)
                    // 这里假设图片的显示区域是 imageContainer 的尺寸
                    const imageContainer = this.$refs.imageContainer;
                    const imageContainerRect =
                        imageContainer.getBoundingClientRect();

                    // 计算图片在 canvas 中的缩放和位置
                    // 这里假设图片按比例缩放以适应 imageContainer,并且居中
                    const imgScaleX =
                        imageContainerRect.width / this.imageDimensions.width;
                    const imgScaleY =
                        imageContainerRect.height / this.imageDimensions.height;
                    const imgScale = Math.min(imgScaleX, imgScaleY); // 保持比例

                    // 图片左上角相对于 canvas 的位置
                    const imgOffsetX =
                        imageContainerRect.left -
                        canvasRect.left +
                        (imageContainerRect.width -
                            this.imageDimensions.width * imgScale) /
                            2;
                    const imgOffsetY =
                        imageContainerRect.top -
                        canvasRect.top +
                        (imageContainerRect.height -
                            this.imageDimensions.height * imgScale) /
                            2;

                    // 计算起点在 canvas 上的坐标
                    const startX =
                        imgOffsetX +
                        startPoint.x * this.imageDimensions.width * imgScale;
                    const startY =
                        imgOffsetY +
                        startPoint.y * this.imageDimensions.height * imgScale;

                    // 计算控制点(在中间位置增加偏移量形成曲线)
                    // const controlX = (startX + endX) / 2 + 40; // 水平偏移量
                    // const controlY = (startY + endY) / 2 - 40; // 垂直偏移量

                    // 绘制起点圆点
                    this.drawStartPoint(ctx, startX, startY);

                    // 绘制线条
                    ctx.beginPath();
                    ctx.moveTo(startX, startY);
                    ctx.lineTo(endX, endY); // 直线
                    // ctx.quadraticCurveTo(controlX, controlY, endX, endY); // 贝塞尔曲线
                    ctx.strokeStyle = this.lineColor;
                    ctx.lineWidth = 1;
                    ctx.stroke();
                }
            });
        },

        // 重新绘制线条
        redrawLines() {
            if (this.$refs.lineCanvas) {
                this.drawLines();
            }
        },

        // 绘制起点圆点
        drawStartPoint(ctx, x, y) {
            ctx.beginPath();
            ctx.arc(x, y, 4, 0, Math.PI * 2); // 半径为4的圆点
            ctx.fillStyle = this.lineColor;
            ctx.fill();
        },

        // 初始化
        init(wrapTemp) {
            this.$nextTick(() => {
                this.startPoints = [
                    ...(wrapTemp.top?.map((t) => t.startPoint) || []),
                    ...(wrapTemp.left?.map((t) => t.startPoint) || []),
                    ...(wrapTemp.right?.map((t) => t.startPoint) || []),
                    ...(wrapTemp.bottom?.map((t) => t.startPoint) || []),
                ];

                // 动态获取卡片DOM引用
                const getCardRefs = (prefix, dir, count) =>
                    Array.from({ length: count }, (_, i) => {
                        const refArray = this.$refs[`${prefix}${i}`];
                        const component = Array.isArray(refArray)
                            ? refArray[0]
                            : refArray;
                        return component?.$el
                            ? { dir, el: component.$el }
                            : null;
                    }).filter(Boolean); // 过滤掉可能为null的引用

                this.targetDivs = [
                    ...getCardRefs("top", "top", wrapTemp.top.length),
                    ...getCardRefs("left", "left", wrapTemp.left.length),
                    ...getCardRefs("right", "right", wrapTemp.right.length),
                    ...getCardRefs(
                        "bottom",
                        "bottom",
                        wrapTemp.bottom.length
                    ),
                ];

                // 确保 startPoints 和 targetDivs 长度一致
                const maxLen = Math.max(
                    this.startPoints.length,
                    this.targetDivs.length
                );
                while (this.startPoints.length < maxLen) {
                    this.startPoints.push({ x: 0.5, y: 0.5 }); // 填充默认起点
                }
                while (this.targetDivs.length < maxLen) {
                    this.targetDivs.push(""); // 填充默认引用
                }

                // 修正startPoints中可能超出图片范围的点
                this.startPoints = this.startPoints.map((point, index) => {
                    // 对于bottom1-bottom4,确保y值不会超出图片底部
                    if (index >= 7) {
                        // bottom1是第7个(索引6),bottom4是第10个(索引9)
                        return {
                            x: Math.min(Math.max(point.x, 0), 1),
                            y: Math.min(Math.max(point.y, 0), 1),
                        };
                    }
                    return point;
                });

                window.addEventListener("resize", this.redrawLines);

                // 初始绘制
                if (
                    this.$refs.imageContainer &&
                    this.$refs.imageContainer.querySelector("img")
                ) {
                    const img = this.$refs.imageContainer.querySelector("img");
                    if (img.complete) {
                        this.drawImageAndLines({ target: img });
                    }
                }
            });
        },

        // 调整 imageContainer 的高度
        adjustImageContainerHeight() {
            if (!this.$refs.imageContainer) return;
            const hasTop =
                this.wrapTemp.top && this.wrapTemp.top.length > 0;
            const elMain = this.$el.querySelector(".el-main");

            // 如果没有 top 数据,让 el-main 的布局不受影响
            if (!hasTop) {
                elMain.style.minHeight = "auto";
                elMain.style.height = "auto";
            } else {
                elMain.style.minHeight = "auto"; // 确保不会被固定高度限制
            }

            // 强制重新计算布局
            this.$nextTick(() => {
                window.dispatchEvent(new Event("resize")); // 触发 resize 事件重新绘制
            });
        },
    },
    mounted() {
        this.init(this.wrapTemp);
        this.adjustImageContainerHeight();
    },
    beforeDestroy() {
        window.removeEventListener("resize", this.redrawLines);
    },
    watch: {
        wrapTemp: {
            handler(newVal) {
                this.init(newVal);
                this.$nextTick(() => {
                    this.adjustImageContainerHeight(); // 数据变化时重新调整
                });
            },
            deep: true,
        },
    },
};
</script>

<style lang="less" scoped>
.image-container {
    display: flex; /* 启用 Flexbox */
    justify-content: center; /* 水平居中 */
    align-items: center; /* 垂直居中 */
    width: 100%; /* 确保宽度占满父容器 */
    height: 100%; /* 确保高度占满父容器 */
    overflow: hidden; /* 防止图片溢出 */
}

.line-canvas {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    pointer-events: none; /* 使 Canvas 元素不响应鼠标事件 */
    z-index: 1; /* 确保 canvas 在其他元素之上 */
}

.bg-purple {
    background: #d3dce6;
}

.margin-top-10 {
    margin-top: 10px;
}

/* 确保 el-container 及其子元素正确布局 */
.el-container {
    position: relative; /* 为绝对定位的 canvas 提供参考 */
}

.el-header,
.el-footer {
    position: relative;
    min-height: auto !important;
    height: auto !important;
    display: block;

    .header-row {
        height: auto;

        .el-col {
            height: auto;

            .grid-content {
                height: 100%;
                min-height: 100px; /* 设置最小高度 */
                z-index: 2;
                position: relative;

                ::v-deep .el-card__body {
                    padding: 0 !important;
                    height: 100%;
                }
            }
        }
    }
}

.el-main {
    position: relative;
    min-height: auto !important;
    height: auto !important;
    display: flex;
    flex-direction: column;

    .header-row {
        height: auto;
        display: flex;

        .el-col {
            height: auto;

            .grid-content {
                z-index: 2;

                ::v-deep .el-card__body {
                    padding: 0 !important;
                    height: 100%;
                }
            }
        }
    }
}

.el-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
}

.image-container-wrapper {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 0;
}

/* 确保图表容器撑满 */
.grid-chart-wrap {
    height: 100px;
    flex: 1;
    overflow: hidden;
}

.flex-col {
    display: flex;
    flex-direction: column; /* 垂直排列 */
    justify-content: space-around; /* 卡片均匀分布 */
    height: 100%; /* 确保高度占满父容器 */
}

::v-deep .el-card__header {
    padding: 15px 8px;
    min-height: 0;
}

.clearfix-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 0;
}
</style>

PS:获取图片的百分比点位组件

        这个没啥好说的,输出格式是:x,y 格式,直接上代码吧:

// ImageMaker

<template>
    <div class="image-marker-container" ref="container">
        <el-image
            ref="imageEl"
            :src="imageUrl"
            fit="contain"
            @load="handleImageLoad"
            @click="handleImageClick"
            class="image-marker-image"
        />

        <div
            v-if="markerPoint"
            :style="getMarkerStyle(markerPoint)"
            class="marker-point"
            @click.stop
        ></div>
    </div>
</template>

<script>
export default {
    name: "ImageMarker",
    props: {
        imageUrl: {
            type: String,
            required: true,
        },
        point: {
            type: String,
            default: null, // 格式: "x,y",其中 x 和 y 在 0-1 之间
        },
    },
    data() {
        return {
            imageDimensions: null,
            markerPoint: null,
        };
    },
    watch: {
        point: {
            immediate: true,
            handler(newPoint) {
                if (newPoint) {
                    const [x, y] = newPoint.split(",").map(Number);
                    if (
                        !isNaN(x) &&
                        !isNaN(y) &&
                        x >= 0 &&
                        x <= 1 &&
                        y >= 0 &&
                        y <= 1
                    ) {
                        this.markerPoint = { x, y };
                    } else {
                        console.warn(
                            "Invalid point format or value. Expected 'x,y' where x and y are between 0 and 1."
                        );
                        this.markerPoint = null;
                    }
                } else {
                    this.markerPoint = null;
                }
            },
        },

        imageUrl() {
            this.imageDimensions = null;
            this.markerPoint = null;
            // 延迟观察,等待新图片加载
            this.$nextTick(() => {
                this.observeImageSize();
            });
        },
    },
    mounted() {
        this.observeImageSize();
    },
    beforeDestroy() {
        if (this.resizeObserver) {
            this.resizeObserver.disconnect();
        }
    },
    methods: {
        observeImageSize() {
            if (this.resizeObserver) {
                this.resizeObserver.disconnect();
            }

            const imgEl = this.$refs.imageEl?.$el?.querySelector("img");
            if (!imgEl) return;

            this.resizeObserver = new ResizeObserver((entries) => {
                for (let entry of entries) {
                    this.imageDimensions = {
                        width: entry.contentRect.width,
                        height: entry.contentRect.height,
                    };
                }
            });

            this.resizeObserver.observe(imgEl);
        },

        handleImageLoad() {
            if (!this.imageDimensions) {
                this.$nextTick(() => {
                    this.observeImageSize(); // 重新观察
                });
            }
        },

        handleImageClick(event) {
            if (!this.imageDimensions) return;

            const imgRect = event.target.getBoundingClientRect();
            const clickX = event.clientX - imgRect.left;
            const clickY = event.clientY - imgRect.top;

            // 计算百分比位置
            const xPercent = clickX / imgRect.width;
            const yPercent = clickY / imgRect.height;

            this.markerPoint = { x: xPercent, y: yPercent };

            // 触发事件,将新点传递给父组件
            this.$emit("point-clicked", `${xPercent},${yPercent}`);
        },

        getMarkerStyle(point) {
            if (!this.imageDimensions || !point) {
                console.log("点位渲染异常!", this.imageDimensions, point);
                return {};
            }
            // 计算标记点在图片上的位置(百分比转像素)
            const x = point.x * this.imageDimensions.width;
            const y = point.y * this.imageDimensions.height;

            return {
                left: `${x}px`,
                top: `${y}px`,
            };
        },
    },
};
</script>

<style scoped lang="less">
.image-marker-container {
    position: relative;
    width: 100%;
    height: 100%;
}

.image-marker-image {
    display: block;
    width: 100%;
    height: auto;
}

.marker-point {
    position: absolute;
    width: 10px;
    height: 10px;
    background-color: red;
    border-radius: 50%;
    transform: translate(-50%, -50%);
    pointer-events: none;
}
</style>

三、测试数据

{
    "img": "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
    "top": [
        {
            "startPoint": { "x": 0.2, "y": 0.2 },
            "title": "间隙top1",
            "data": {
                "seriesData": [100, 120, 110, 80, 70, 100, 110, 100, 120, 111, 80, 70, 100, 110, 100, 120, 110, 80, 70, 100, 111, 100, 120, 110, 80, 70, 100, 110, 100, 120, 111, 80, 70, 100, 110, 100, 120, 110, 80, 70, 100, 111, 100, 120, 110, 80, 70, 100, 110, 100, 120, 110, 80, 70, 100, 110, 100, 120, 110, 80, 70, 100, 110, 100, 120, 110, 80, 70, 100, 110],
                "xAxisData": ["x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70"],
                "upperLimit": 100,
                "lowerLimit": 80
            }
        },
        {
            "startPoint": { "x": 0.3, "y": 0.1 },
            "title": "间隙top2",
            "data": {
                "seriesData": [150, 180, 170, 120, 110, 140, 160],
                "xAxisData": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
                "upperLimit": 200,
                "lowerLimit": 120
            }
        }
    ],
    "left": [
        {
            "startPoint": { "x": 0.1, "y": 0.2 },
            "title": "间隙left1",
            "data": {
                "seriesData": [100, 120, 110, 80, 70, 100, 110],
                "xAxisData": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
                "upperLimit": 100,
                "lowerLimit": 80
            }
        },
        {
            "startPoint": { "x": 0.2, "y": 0.3 },
            "title": "间隙left2",
            "data": {
                "seriesData": [150, 180, 170, 120, 110, 140, 160],
                "xAxisData": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
                "upperLimit": 120,
                "lowerLimit": 30
            }
        }
    ],
    "right": [
        {
            "startPoint": { "x": 0.7, "y": 0.4 },
            "title": "间隙right1",
            "data": {
                "seriesData": [250, 280, 270, 220, 210, 240, 260],
                "xAxisData": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
                "upperLimit": 300,
                "lowerLimit": 220
            }
        }
    ],
    "bottom": [
        {
            "startPoint": { "x": 0.4, "y": 0.6 },
            "title": "间隙bottom1",
            "data": {
                "seriesData": [300, 330, 320, 270, 260, 290, 310],
                "xAxisData": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
                "upperLimit": 350,
                "lowerLimit": 270
            }
        },
        {
            "startPoint": { "x": 0.5, "y": 0.7 },
            "title": "间隙bottom2",
            "data": {
                "seriesData": [350, 380, 370, 320, 310, 340, 360],
                "xAxisData": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
                "upperLimit": 400,
                "lowerLimit": 320
            }
        }
    ]
}

完结撒盐!


网站公告

今日签到

点亮在社区的每一天
去签到