diff --git a/README.md b/README.md index d461cf8..cd990b2 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,8 @@ See more examples [here](https://github.com/ecomfe/vue-echarts/tree/main/demo). ECharts' universal interface. Modifying this prop will trigger ECharts' `setOption` method. Read more [here →](https://echarts.apache.org/en/option.html) - > 💡 When `update-options` is not specified, `notMerge: false` will be specified by default when the `setOption` method is called if the `option` object is modified directly and the reference remains unchanged; otherwise, if a new reference is bound to `option`, ` notMerge: true` will be specified. + > [!TIP] + > When `update-options` is not specified, `notMerge: false` will be specified by default when the `setOption` method is called if the `option` object is modified directly and the reference remains unchanged; otherwise, if a new reference is bound to `option`, `notMerge: true` will be specified. - `update-options: object` @@ -195,8 +196,7 @@ You can bind events with Vue's `v-on` directive. ``` -> **Note** -> +> [!NOTE] > Only the `.once` event modifier is supported as other modifiers are tightly coupled with the DOM event system. Vue-ECharts support the following events: @@ -337,6 +337,76 @@ export default { > - [`showLoading`](https://echarts.apache.org/en/api.html#echartsInstance.showLoading) / [`hideLoading`](https://echarts.apache.org/en/api.html#echartsInstance.hideLoading): use the `loading` and `loading-options` props instead. > - `setTheme`: use the `theme` prop instead. +### Slots + +Vue-ECharts allows you to define ECharts option's [`tooltip.formatter`](https://echarts.apache.org/en/option.html#tooltip.formatter) and [`toolbox.feature.dataView.optionToContent`](https://echarts.apache.org/en/option.html#toolbox.feature.dataView.optionToContent) callbacks via Vue slots instead of defining them in your `option` object. This simplifies custom HTMLElement rendering using familiar Vue templating. + +**Slot Naming Convention** + +- Slot names begin with `tooltip`/`dataView`, followed by hyphen-separated path segments to the target. +- Each segment corresponds to an `option` property name or an array index (for arrays, use the numeric index). +- The constructed slot name maps directly to the nested callback it overrides. + +**Example mappings**: + +- `tooltip` → `option.tooltip.formatter` +- `tooltip-baseOption` → `option.baseOption.tooltip.formatter` +- `tooltip-xAxis-1` → `option.xAxis[1].tooltip.formatter` +- `tooltip-series-2-data-4` → `option.series[2].data[4].tooltip.formatter` +- `dataView` → `option.toolbox.feature.dataView.optionToContent` +- `dataView-media-1-option` → `option.media[1].option.toolbox.feature.dataView.optionToContent` + +The slot props correspond to the first parameter of the callback function. + +
+Usage + +```vue + +``` + +[Example →](https://vue-echarts.dev/#line) + +
+ +> [!NOTE] +> Slots take precedence over the corresponding callback defined in `props.option`. + ### Static Methods Static methods can be accessed from [`echarts` itself](https://echarts.apache.org/en/api.html#echarts). diff --git a/README.zh-Hans.md b/README.zh-Hans.md index 6fefa2a..dceef5f 100644 --- a/README.zh-Hans.md +++ b/README.zh-Hans.md @@ -155,7 +155,8 @@ app.component('v-chart', VueECharts) ECharts 的万能接口。修改这个 prop 会触发 ECharts 实例的 `setOption` 方法。查看[详情 →](https://echarts.apache.org/zh/option.html) - > 💡 在没有指定 `update-options` 时,如果直接修改 `option` 对象而引用保持不变,`setOption` 方法调用时将默认指定 `notMerge: false`;否则,如果为 `option` 绑定一个新的引用,将指定 `notMerge: true`。 + > [!TIP] + > 在没有指定 `update-options` 时,如果直接修改 `option` 对象而引用保持不变,`setOption` 方法调用时将默认指定 `notMerge: false`;否则,如果为 `option` 绑定一个新的引用,将指定 `notMerge: true`。 - `update-options: object` @@ -195,8 +196,7 @@ app.component('v-chart', VueECharts) ``` -> **Note** -> +> [!NOTE] > 仅支持 `.once` 修饰符,因为其它修饰符都与 DOM 事件机制强耦合。 Vue-ECharts 支持如下事件: @@ -337,6 +337,76 @@ export default { > - [`showLoading`](https://echarts.apache.org/zh/api.html#echartsInstance.showLoading) / [`hideLoading`](https://echarts.apache.org/zh/api.html#echartsInstance.hideLoading):请使用 `loading` 和 `loading-options` prop。 > - `setTheme`:请使用 `theme` prop。 +### 插槽(Slots) + +Vue-ECharts 允许你通过 Vue 插槽来定义 ECharts 配置中的 [`tooltip.formatter`](https://echarts.apache.org/zh/option.html#tooltip.formatter) 和 [`toolbox.feature.dataView.optionToContent`](https://echarts.apache.org/zh/option.html#toolbox.feature.dataView.optionToContent) 回调,而无需在 `option` 对象中定义它们。你可以使用熟悉的 Vue 模板语法来编写自定义提示框或数据视图中的内容。 + +**插槽命名约定** + +- 插槽名称以 `tooltip`/`dataView` 开头,后面跟随用连字符分隔的路径片段,用于定位目标。 +- 每个路径片段对应 `option` 对象的属性名或数组索引(数组索引使用数字形式)。 +- 拼接后的插槽名称直接映射到要覆盖的嵌套回调函数。 + +**示例映射**: + +- `tooltip` → `option.tooltip.formatter` +- `tooltip-baseOption` → `option.baseOption.tooltip.formatter` +- `tooltip-xAxis-1` → `option.xAxis[1].tooltip.formatter` +- `tooltip-series-2-data-4` → `option.series[2].data[4].tooltip.formatter` +- `dataView` → `option.toolbox.feature.dataView.optionToContent` +- `dataView-media-1-option` → `option.media[1].option.toolbox.feature.dataView.optionToContent` + +插槽的 props 对象对应回调函数的第一个参数。 + +
+用法示例 + +```vue + +``` + +[示例 →](https://vue-echarts.dev/#line) + +
+ +> [!NOTE] +> 插槽会优先于 `props.option` 中对应的回调函数。 + ### 静态方法 静态方法请直接通过 [`echarts` 本身](https://echarts.apache.org/zh/api.html#echarts)进行调用。 diff --git a/demo/Demo.vue b/demo/Demo.vue index 9270f02..9e33e98 100644 --- a/demo/Demo.vue +++ b/demo/Demo.vue @@ -8,6 +8,7 @@ import { track } from "@vercel/analytics"; import LogoChart from "./examples/LogoChart.vue"; import BarChart from "./examples/BarChart.vue"; +import LineChart from "./examples/LineChart.vue"; import PieChart from "./examples/PieChart.vue"; import PolarChart from "./examples/PolarChart.vue"; import ScatterChart from "./examples/ScatterChart.vue"; @@ -74,6 +75,7 @@ watch(codeOpen, (open) => {

+ diff --git a/demo/data/line.js b/demo/data/line.js new file mode 100644 index 0000000..35f1601 --- /dev/null +++ b/demo/data/line.js @@ -0,0 +1,56 @@ +export default function getData() { + return { + textStyle: { + fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif', + fontWeight: 300, + }, + legend: { top: 20 }, + tooltip: { + trigger: "axis", + }, + dataset: { + source: [ + ["product", "2012", "2013", "2014", "2015", "2016", "2017"], + ["Milk Tea", 56.5, 82.1, 88.7, 70.1, 53.4, 85.1], + ["Matcha Latte", 51.1, 51.4, 55.1, 53.3, 73.8, 68.7], + ["Cheese Cocoa", 40.1, 62.2, 69.5, 36.4, 45.2, 32.5], + ["Walnut Brownie", 25.2, 37.1, 41.2, 18, 33.9, 49.1], + ], + }, + xAxis: { + type: "category", + triggerEvent: true, + tooltip: { show: true, formatter: "" }, + }, + yAxis: { + triggerEvent: true, + tooltip: { show: true, formatter: "" }, + }, + series: [ + { + type: "line", + smooth: true, + seriesLayoutBy: "row", + emphasis: { focus: "series" }, + }, + { + type: "line", + smooth: true, + seriesLayoutBy: "row", + emphasis: { focus: "series" }, + }, + { + type: "line", + smooth: true, + seriesLayoutBy: "row", + emphasis: { focus: "series" }, + }, + { + type: "line", + smooth: true, + seriesLayoutBy: "row", + emphasis: { focus: "series" }, + }, + ], + }; +} diff --git a/demo/examples/Example.vue b/demo/examples/Example.vue index 88fa11e..3e36779 100644 --- a/demo/examples/Example.vue +++ b/demo/examples/Example.vue @@ -43,7 +43,7 @@ defineProps({ width: fit-content; margin: 2em auto; - .echarts { + > .echarts { width: calc(60vw + 4em); height: 360px; max-width: 720px; diff --git a/demo/examples/LineChart.vue b/demo/examples/LineChart.vue new file mode 100644 index 0000000..30173d4 --- /dev/null +++ b/demo/examples/LineChart.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/package.json b/package.json index 163c7b9..ec7620b 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ ], "peerDependencies": { "echarts": "^6.0.0-beta.1", - "vue": "^3.1.1" + "vue": "^3.3.0" }, "devDependencies": { "@highlightjs/vue-plugin": "^2.1.0", diff --git a/src/ECharts.ts b/src/ECharts.ts index a6b0d88..93739f0 100644 --- a/src/ECharts.ts +++ b/src/ECharts.ts @@ -10,6 +10,7 @@ import { h, nextTick, watchEffect, + toValue, } from "vue"; import { init as initChart } from "echarts/core"; @@ -19,9 +20,10 @@ import { autoresizeProps, useLoading, loadingProps, - type PublicMethods, + useSlotOption, } from "./composables"; -import { isOn, omitOn, toValue } from "./utils"; +import type { PublicMethods, SlotsTypes } from "./composables"; +import { isOn, omitOn } from "./utils"; import { register, TAG_NAME } from "./wc"; import type { PropType, InjectionKey } from "vue"; @@ -64,8 +66,9 @@ export default defineComponent({ ...loadingProps, }, emits: {} as unknown as Emits, + slots: Object as SlotsTypes, inheritAttrs: false, - setup(props, { attrs, expose }) { + setup(props, { attrs, expose, slots }) { const root = shallowRef(); const chart = shallowRef(); const manualOption = shallowRef