vue3 怎么更新 echarts?

需求,我的 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"

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"

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"

}

]


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

但是突然可以跑起来了?

所以是可以运行了

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

而且会有警告

vue3 怎么更新 echarts?


界面样式:

vue3 怎么更新 echarts?

我的需求是,在这个 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

watchwatchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:
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()


回答:

你的watch写的有问题,https://cn.vuejs.org/api/reactivity-core.html#watch,watch最少也得两个参数,你的才一个,第一个参数应该是被监听的数据。另外echarts是操作dom的,所以你需要在dom更新后再去更新echarts,变成代码就是用nextTick包起来


回答:

我最后改成下面这样解决了

<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"

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"

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, shallowRef } from "vue";

import * as echarts from "echarts";

import { useRouter, useRoute } from "vue-router";

const chartRefText = ref(null);

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',

});

const myChartText = shallowRef(null);

onMounted(async () => {

if (myChartText.value === null) {

myChartText.value = 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.value.setOption(option);

} catch (error) {

console.error(error);

}

}

});

watch(async () => {

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.value.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.value.getOption() || {}; // Ensure legend is defined

const { selected } = legend || {};

const newSelected = {};

Object.keys(selected || {}).forEach((key) => {

newSelected[key] = !selected[key];

});

myChartText.value.setOption({

legend: {

selected: newSelected,

},

});

};

</script>

<style scoped>

.chart-container {

width: 100%;

max-width: 1440px;

margin: 0 auto;

}

.chart {

width: 100%;

height: 400px;

}

</style>

以上是 vue3 怎么更新 echarts? 的全部内容, 来源链接: utcz.com/p/934712.html

回到顶部