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"
}
]
我重新描述一下我的需求,因为上面的那段代码,本来是跑不起来的
但是突然可以跑起来了?
所以是可以运行了
但是我觉得实现方式非常的不优雅
而且会有警告
界面样式:
我的需求是,在这个 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时指定侦听datawatch(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()
回答:
你的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