#独家
vue3 怎么更新 echarts?

2023-08-06 0 2,256

需求,我的 vue3 中的 data=ref([]) 更新之后,我希望 echarts 的图标也跟着一起更新

但是目前不会,我该怎么修改

<template>
    <top-bar></top-bar>

    <div class="container">
        <a-page-header style="border: 1px solid rgb(235, 237, 240)" title="匹配结果入库" sub-title="查看匹配结果入库情况"
            @back="navigateToRoot">
            <a-row class="content">
                <div style="flex: 1">
                    <br />

                    <div>
                        在该页面,可以查看以下信息:
                        <br />
                        <br />
                        <ul>
                            <li>指定公司维度的产出</li>
                            <li>指定平台维度的产出</li>
                            <li>指定母本的产出</li>
                        </ul>
                    </div>
                </div>
            </a-row>
        </a-page-header>
    </div>


    <div class="container">
        <div class="container-item">
            <a-form :model="formState" :label-col="labelCol" :wrapper-col="wrapperCol">


                <a-form-item label="meta_uuid">
                    <a-input v-model:value="formState.meta_uuid" placeholder="根据 meta_uuid 筛选,可以不填" />
                </a-form-item>

                <a-form-item label="company_id">
                    <a-form-item name="input-number" no-style>
                        <a-input-number v-model:value="formState['company_id']" placeholder="根据 company_id 筛选,可以不填" />
                    </a-form-item>
                    <span class="ant-form-text">根据 company_id 筛选,可以不填</span><a href="/company_list" rel="external nofollow" 
                        target="_blank">查看公司列表</a>
                </a-form-item>

                <a-form-item label="track_source_id">
                    <a-form-item name="input-number" no-style>
                        <a-input-number v-model:value="formState['track_source_id']" placeholder="请输入 track_source_id" />
                    </a-form-item>
                    <span class="ant-form-text">指定 track_source_id <a href="/search_engine_health_check" rel="external nofollow" 
                            target="_blank">[详情]</a></span>
                </a-form-item>

                <a-form-item label="last_day">
                    <a-form-item name="input-number" no-style>
                        <a-input-number v-model:value="formState['last_day']" placeholder="查看最近多少天" />
                    </a-form-item>
                    <span class="ant-form-text"> 查看最近多少天</span>
                </a-form-item>

                <a-form-item label="选择资源类型">
                    <a-form-item name="input-number" no-style>
                        <!-- <a-input-number v-model:value="formState['source_type']" placeholder="根据 source_type 筛选,可以不填" /> -->
                        <a-radio-group v-model:value="formState.source_type">
                            <a-radio value="image">image</a-radio>
                            <a-radio value="text">text</a-radio>
                        </a-radio-group>
                    </a-form-item>
                    <!-- <span class="ant-form-text">选择资源类型:image 或者 text</span> -->
                </a-form-item>

                <a-form-item label="match 状态">
                    <a-form-item name="input-number" no-style>
                        <!-- <a-input-number v-model:value="formState['classify']" placeholder="根据 source_type 筛选,可以不填" /> -->

                        <a-radio-group v-model:value="formState.classify">
                            <a-radio value="all">所有</a-radio>
                            <a-radio value="unclassify">疑似侵权</a-radio>
                            <a-radio value="infringement">确认侵权</a-radio>
                        </a-radio-group>


                    </a-form-item>
                    <!-- <span class="ant-form-text">选择全部还是疑似侵权</span> -->
                </a-form-item>

                <a-form-item :wrapper-col="{ span: 14, offset: 4 }">
                    <a-button type="primary" :loading="searchLoading" @click="sendRequest">
                        提交
                    </a-button>
                    <span style="margin-left: 25px"></span>
                    <a-button @click="clearForm">清空</a-button>
                </a-form-item>
            </a-form>
        </div>
    </div>

    <div class="container">
        <div class="container-item">
            <a-typography-title :level="5"> 每个入库情况:
            </a-typography-title>

            <div ref="chartRefText" class="chart"></div>

            <button @click="toggleLegend">一键反选图例</button>
        </div>
    </div>
</template>
  
<script setup>
import axios from "axios";
import { ref, onMounted, reactive, watch } from "vue";
import * as echarts from "echarts";
import { useRouter, useRoute } from "vue-router";

const chartRefText = ref(null);
let myChartText;

const data = ref([]);
const searchLoading = ref(false);

const labelCol = {
    style: {
        width: "150px",
    },
};
const wrapperCol = {
    span: 14,
};

const route = useRoute();



const formState = reactive({
    keyword_task_id: route.query.keyword_task_id || null,
    meta_uuid: route.query.meta_uuid || null,
    company_id: route.query.company_id || null,
    last_day: route.query.last_day || 7,
    track_source_id: route.query.track_source_id || null,
    classify: route.query.classify || 'all',
    source_type: route.query.source_type || 'image',
});

onMounted(async () => {
    myChartText = echarts.init(chartRefText.value);
    try {
        const xAxisData = data.value.map(item => item.source_date);
        const yAxisData = data.value.map(item => item.source_count);


        const option = {
            xAxis: {
                type: 'category',
                data: xAxisData
            },
            yAxis: {
                type: 'value'
            },
            series: [{
                type: 'line',
                data: yAxisData
            }]
        };
        myChartText.setOption(option);
    } catch (error) {
        console.error(error);
    }
});

watch(async () => {
    myChartText = echarts.init(chartRefText.value);
    try {
        const xAxisData = data.value.map(item => item.source_date);
        const yAxisData = data.value.map(item => item.source_count);


        const option = {
            xAxis: {
                type: 'category',
                data: xAxisData
            },
            yAxis: {
                type: 'value'
            },
            series: [{
                type: 'line',
                data: yAxisData
            }]
        };


        myChartText.setOption(option);
    } catch (error) {
        console.error(error);
    }
});

const clearForm = () => {
    for (const key in formState) {
        if (Reflect.has(formState, key)) {
            formState[key] = null;
        }
    }
    formState["limit"] = 20;

    const urlParams = new URLSearchParams();
    for (const key in formState) {
        if (Reflect.has(formState, key)) {
            if (formState[key] === "") {
                formState[key] = null;
            }
            if (formState[key] !== null) {
                urlParams.set(key, formState[key]);
            }
        }
    }
    window.history.replaceState(null, null, `?${urlParams.toString()}`);
};

const sendRequest = () => {
    const urlParams = new URLSearchParams();
    for (const key in formState) {
        if (Reflect.has(formState, key)) {
            if (formState[key] === "") {
                formState[key] = null;
            }
            if (formState[key] !== null) {
                urlParams.set(key, formState[key]);
            }
        }
    }

    window.history.replaceState(null, null, `?${urlParams.toString()}`);

    searchLoading.value = true;

    const queryParams = {};

    for (const key in formState) {
        if (Reflect.has(formState, key)) {
            if (formState[key] !== null) {
                queryParams[key] = formState[key];
            }
        }
    }

    const url =
        "http://xxxxx.cn/match/count";
    axios
        .get(url, {
            params: queryParams,
        })
        .then((response) => {
            data.value = response.data;
        })
        .catch((error) => {
            console.error(error);
        })
        .finally(() => {
            searchLoading.value = false;
        });
};

const toggleLegend = () => {
    const { legend } = myChartText.getOption() || {}; // Ensure legend is defined
    const { selected } = legend || {};
    const newSelected = {};
    Object.keys(selected || {}).forEach((key) => {
        newSelected[key] = !selected[key];
    });
    myChartText.setOption({
        legend: {
            selected: newSelected,
        },
    });
};
</script>
  
<style scoped>
.chart-container {
    width: 100%;
    max-width: 1440px;
    margin: 0 auto;
}

.chart {
    width: 100%;
    height: 400px;
}
</style>

接口返回的数据结构类似:

[
    {
        "source_count": 2986,
        "source_date": "2023-07-27"
    },
    {
        "source_count": 1947,
        "source_date": "2023-07-28"
    },
    {
        "source_count": 8124,
        "source_date": "2023-07-29"
    },
    {
        "source_count": 4600,
        "source_date": "2023-07-30"
    },
    {
        "source_count": 262235,
        "source_date": "2023-07-31"
    },
    {
        "source_count": 890030,
        "source_date": "2023-08-01"
    },
    {
        "source_count": 178823,
        "source_date": "2023-08-02"
    },
    {
        "source_count": 3574,
        "source_date": "2023-08-03"
    }
]

我重新描述一下我的需求,因为上面的那段代码,本来是跑不起来的

但是突然可以跑起来了?

所以是可以运行了

但是我觉得实现方式非常的不优雅

而且会有警告

我的需求是,在这个 form 组件里面,选择好了一堆参数之后,点击「提交」

然后 axios 请求后端接口,然后接口返回数据

然后会数据结构是这样的:

[
    {
        "source_count": 2986,
        "source_date": "2023-07-27"
    },
    {
        "source_count": 1947,
        "source_date": "2023-07-28"
    },
    {
        "source_count": 8124,
        "source_date": "2023-07-29"
    },
    {
        "source_count": 4600,
        "source_date": "2023-07-30"
    },
    {
        "source_count": 262235,
        "source_date": "2023-07-31"
    },
    {
        "source_count": 890030,
        "source_date": "2023-08-01"
    },
    {
        "source_count": 178823,
        "source_date": "2023-08-02"
    },
    {
        "source_count": 3574,
        "source_date": "2023-08-03"
    }
]

然后我要用 echarts 把这个 list[dict] 的数据渲染成折线图


但是落实到具体编码的时候,我遇到了问题:

  • 但我在 onMounted 里面定义 myChartText = echarts.init(chartRefText.value); 的时候,点击「提交」之后,echarts 是不会刷新的
  • 然后我又在 watch 里面重新定义了 myChartText = echarts.init(chartRefText.value); ,这样功能实现是没有问题,但是会有警告 [ECharts] There is a chart instance already initialized on the dom., 我感觉不优雅

所以,我应该怎么办?

是定义一个全局变量 myChartText = echarts.init(chartRefText.value);,然后在 onMounted 或者 watch 里面使用全局变量 myChartText 吗?

watch 用法有误,需要在第一个参数手动指定被侦听的数据,或者可以直接改为使用 watchEffect,可以无需手动指定被侦听数据。示例如下:

// 使用watch时指定侦听data
watch(data, () => {

});

// 使用watchEffect时无需指定侦听data
watchEffect(() => {
  // 直接访问即可,watchEffect会自动跟踪变化
  const xAxisData = data.value.map(item => item.source_date);

});

watch vs. watchEffect

watch 和 watchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:
watch 只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。watch 会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。
watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确。


至于你补充的警告,在 watch 中不要每次都调用 echarts.init 方法,可以判断如果未初始化的情况再去调用。另外,在 watch 中执行初始化及更新的操作即可,无需在 onMounted 中提前执行一次。示例如下:

const element = ref(null);
const instance = shallowRef(null);

// 这里改用 watchPostEffect 以防止 element.value 为 null
watchPostEffect(() => {
  if(instance.value == null) {
    instance.value = echarts.init(element.value);
  }

  const option = {
    // ...
  };

  instance.value.setOption(option);
});

关于 watchPostEffect 的用法可查看文档 Vue watchPostEffect()

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

1. JK下载官网所有资源来源于开发团队,加入会员即可下载使用!如有问题请联系右下角在线客服!
2. JK下载官方保障所有软件都通过人工亲测,为每位会员用户提供安全可靠的应用软件、游戏资源下载及程序开发服务。
3. JK开发团队针对会员诉求,历经多年拥有现今开发成果, 每款应用程序上线前都经过人工测试无误后提供安装使用,只为会员提供安全原创的应用。
4. PC/移动端应用下载后如遇安装使用问题请联系右下角在线客服或提交工单,一对一指导解决疑难。

JK软件下载官网 技术分享 vue3 怎么更新 echarts? https://www.jkxiazai.com/2416.html

JK软件应用商店是经过官方安全认证,保障正版软件平台

相关资源

官方客服团队

为您解决烦忧 - 24小时在线 专业服务