mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-29 14:46:41 +08:00
feat: add vchart hook
This commit is contained in:
parent
2486d9d296
commit
0e105f32e4
@ -59,6 +59,7 @@
|
||||
"@sa/materials": "workspace:*",
|
||||
"@sa/utils": "workspace:*",
|
||||
"@visactor/vchart": "1.12.8",
|
||||
"@visactor/vchart-theme": "1.12.1",
|
||||
"@visactor/vue-vtable": "1.10.1",
|
||||
"@vueuse/components": "^11.1.0",
|
||||
"@vueuse/core": "11.1.0",
|
||||
|
@ -44,6 +44,9 @@ importers:
|
||||
'@visactor/vchart':
|
||||
specifier: 1.12.8
|
||||
version: 1.12.8
|
||||
'@visactor/vchart-theme':
|
||||
specifier: 1.12.1
|
||||
version: 1.12.1(@visactor/vchart@1.12.8)
|
||||
'@visactor/vue-vtable':
|
||||
specifier: 1.10.1
|
||||
version: 1.10.1
|
||||
@ -2614,6 +2617,14 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@visactor/vchart-theme@1.12.1(@visactor/vchart@1.12.8):
|
||||
resolution: {integrity: sha512-8994bN0+dnr/hrWH5nIBCFCl29MI7T2O5Q8/ZHDJKtgtt9zjzern11wAmP1sUPooZwT59feveGB3iWEonLCVuQ==}
|
||||
peerDependencies:
|
||||
'@visactor/vchart': '>=1.10.4'
|
||||
dependencies:
|
||||
'@visactor/vchart': 1.12.8
|
||||
dev: false
|
||||
|
||||
/@visactor/vchart@1.12.8:
|
||||
resolution: {integrity: sha512-QG8KY/KMcr78AL1DPOMNBVi1pDK2AyRlaP7uQueu0vgBmqCbJfFNsAy7RUSNvufw3cDk85wfBqmsckqOiEeiow==}
|
||||
dependencies:
|
||||
|
@ -0,0 +1,157 @@
|
||||
import { computed, effectScope, onScopeDispose, ref, watch } from 'vue';
|
||||
import VChart, { registerLiquidChart } from '@visactor/vchart';
|
||||
import type { ISpec } from '@visactor/vchart';
|
||||
import light from '@visactor/vchart-theme/public/light.json';
|
||||
import dark from '@visactor/vchart-theme/public/dark.json';
|
||||
import { useElementSize } from '@vueuse/core';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
registerLiquidChart();
|
||||
|
||||
// register the theme
|
||||
VChart.ThemeManager.registerTheme('light', light as any);
|
||||
VChart.ThemeManager.registerTheme('dark', dark as any);
|
||||
|
||||
interface ChartHooks {
|
||||
onRender?: (chart: VChart) => void | Promise<void>;
|
||||
onUpdated?: (chart: VChart) => void | Promise<void>;
|
||||
onDestroy?: (chart: VChart) => void | Promise<void>;
|
||||
}
|
||||
|
||||
export function useVChart<T extends ISpec>(specFactory: () => T, hooks: ChartHooks = {}) {
|
||||
const scope = effectScope();
|
||||
const themeStore = useThemeStore();
|
||||
const darkMode = computed(() => themeStore.darkMode);
|
||||
|
||||
const domRef = ref<HTMLElement | null>(null);
|
||||
const initialSize = { width: 0, height: 0 };
|
||||
const { width, height } = useElementSize(domRef, initialSize);
|
||||
|
||||
let chart: VChart | null = null;
|
||||
const spec: T = specFactory();
|
||||
|
||||
const { onRender, onUpdated, onDestroy } = hooks;
|
||||
|
||||
/**
|
||||
* whether can render chart
|
||||
*
|
||||
* when domRef is ready and initialSize is valid
|
||||
*/
|
||||
function canRender() {
|
||||
return domRef.value && initialSize.width > 0 && initialSize.height > 0;
|
||||
}
|
||||
|
||||
/** is chart rendered */
|
||||
function isRendered() {
|
||||
return Boolean(domRef.value && chart);
|
||||
}
|
||||
|
||||
/**
|
||||
* update chart spec
|
||||
*
|
||||
* @param callback callback function
|
||||
*/
|
||||
async function updateSpec(callback: (opts: T, optsFactory: () => T) => ISpec = () => spec) {
|
||||
if (!isRendered()) return;
|
||||
|
||||
const updatedOpts = callback(spec, specFactory);
|
||||
|
||||
Object.assign(spec, updatedOpts);
|
||||
|
||||
if (isRendered()) {
|
||||
chart?.release();
|
||||
}
|
||||
|
||||
chart?.updateSpec({ ...updatedOpts }, true);
|
||||
|
||||
await onUpdated?.(chart!);
|
||||
}
|
||||
|
||||
function setSpec(newSpec: T) {
|
||||
chart?.updateSpec(newSpec);
|
||||
}
|
||||
|
||||
/** render chart */
|
||||
async function render() {
|
||||
if (!isRendered()) {
|
||||
// apply the theme
|
||||
if (darkMode.value) {
|
||||
VChart.ThemeManager.setCurrentTheme('dark');
|
||||
} else {
|
||||
VChart.ThemeManager.setCurrentTheme('light');
|
||||
}
|
||||
|
||||
chart = new VChart(spec, { dom: domRef.value as HTMLElement });
|
||||
chart.renderSync();
|
||||
|
||||
await onRender?.(chart);
|
||||
}
|
||||
}
|
||||
|
||||
/** resize chart */
|
||||
function resize() {
|
||||
// chart?.resize();
|
||||
}
|
||||
|
||||
/** destroy chart */
|
||||
async function destroy() {
|
||||
if (!chart) return;
|
||||
|
||||
await onDestroy?.(chart);
|
||||
chart?.release();
|
||||
chart = null;
|
||||
}
|
||||
|
||||
/** change chart theme */
|
||||
async function changeTheme() {
|
||||
await destroy();
|
||||
await render();
|
||||
await onUpdated?.(chart!);
|
||||
}
|
||||
|
||||
/**
|
||||
* render chart by size
|
||||
*
|
||||
* @param w width
|
||||
* @param h height
|
||||
*/
|
||||
async function renderChartBySize(w: number, h: number) {
|
||||
initialSize.width = w;
|
||||
initialSize.height = h;
|
||||
|
||||
// size is abnormal, destroy chart
|
||||
if (!canRender()) {
|
||||
await destroy();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// resize chart
|
||||
if (isRendered()) {
|
||||
resize();
|
||||
}
|
||||
|
||||
// render chart
|
||||
await render();
|
||||
}
|
||||
|
||||
scope.run(() => {
|
||||
watch([width, height], ([newWidth, newHeight]) => {
|
||||
renderChartBySize(newWidth, newHeight);
|
||||
});
|
||||
|
||||
watch(darkMode, () => {
|
||||
changeTheme();
|
||||
});
|
||||
});
|
||||
|
||||
onScopeDispose(() => {
|
||||
destroy();
|
||||
scope.stop();
|
||||
});
|
||||
|
||||
return {
|
||||
domRef,
|
||||
updateSpec,
|
||||
setSpec
|
||||
};
|
||||
}
|
861
src/views/plugin/charts/vchart/data.ts
Normal file
861
src/views/plugin/charts/vchart/data.ts
Normal file
@ -0,0 +1,861 @@
|
||||
export const shapeWordCloudSpec = {
|
||||
type: 'wordCloud',
|
||||
maskShape: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/log.jpeg',
|
||||
nameField: 'challenge_name',
|
||||
valueField: 'sum_count',
|
||||
seriesField: 'challenge_name',
|
||||
data: [
|
||||
{
|
||||
name: 'data',
|
||||
values: [
|
||||
{
|
||||
challenge_name: '刘浩存',
|
||||
sum_count: 957
|
||||
},
|
||||
{
|
||||
challenge_name: '刘昊然',
|
||||
sum_count: 942
|
||||
},
|
||||
{
|
||||
challenge_name: '喜欢',
|
||||
sum_count: 842
|
||||
},
|
||||
{
|
||||
challenge_name: '真的',
|
||||
sum_count: 828
|
||||
},
|
||||
{
|
||||
challenge_name: '四海',
|
||||
sum_count: 665
|
||||
},
|
||||
{
|
||||
challenge_name: '好看',
|
||||
sum_count: 627
|
||||
},
|
||||
{
|
||||
challenge_name: '评论',
|
||||
sum_count: 574
|
||||
},
|
||||
{
|
||||
challenge_name: '好像',
|
||||
sum_count: 564
|
||||
},
|
||||
{
|
||||
challenge_name: '沈腾',
|
||||
sum_count: 554
|
||||
},
|
||||
{
|
||||
challenge_name: '不像',
|
||||
sum_count: 540
|
||||
},
|
||||
{
|
||||
challenge_name: '多少钱',
|
||||
sum_count: 513
|
||||
},
|
||||
{
|
||||
challenge_name: '韩寒',
|
||||
sum_count: 513
|
||||
},
|
||||
{
|
||||
challenge_name: '不知道',
|
||||
sum_count: 499
|
||||
},
|
||||
{
|
||||
challenge_name: '感觉',
|
||||
sum_count: 499
|
||||
},
|
||||
{
|
||||
challenge_name: '尹正',
|
||||
sum_count: 495
|
||||
},
|
||||
{
|
||||
challenge_name: '不看',
|
||||
sum_count: 487
|
||||
},
|
||||
{
|
||||
challenge_name: '奥特之父',
|
||||
sum_count: 484
|
||||
},
|
||||
{
|
||||
challenge_name: '阿姨',
|
||||
sum_count: 482
|
||||
},
|
||||
{
|
||||
challenge_name: '支持',
|
||||
sum_count: 482
|
||||
},
|
||||
{
|
||||
challenge_name: '父母',
|
||||
sum_count: 479
|
||||
},
|
||||
{
|
||||
challenge_name: '一条',
|
||||
sum_count: 462
|
||||
},
|
||||
{
|
||||
challenge_name: '女主',
|
||||
sum_count: 456
|
||||
},
|
||||
{
|
||||
challenge_name: '确实',
|
||||
sum_count: 456
|
||||
},
|
||||
{
|
||||
challenge_name: '票房',
|
||||
sum_count: 456
|
||||
},
|
||||
{
|
||||
challenge_name: '无语',
|
||||
sum_count: 443
|
||||
},
|
||||
{
|
||||
challenge_name: '干干净净',
|
||||
sum_count: 443
|
||||
},
|
||||
{
|
||||
challenge_name: '为啥',
|
||||
sum_count: 426
|
||||
},
|
||||
{
|
||||
challenge_name: '爱情',
|
||||
sum_count: 425
|
||||
},
|
||||
{
|
||||
challenge_name: '喜剧',
|
||||
sum_count: 422
|
||||
},
|
||||
{
|
||||
challenge_name: '春节',
|
||||
sum_count: 414
|
||||
},
|
||||
{
|
||||
challenge_name: '剧情',
|
||||
sum_count: 414
|
||||
},
|
||||
{
|
||||
challenge_name: '人生',
|
||||
sum_count: 409
|
||||
},
|
||||
{
|
||||
challenge_name: '风格',
|
||||
sum_count: 408
|
||||
},
|
||||
{
|
||||
challenge_name: '演员',
|
||||
sum_count: 403
|
||||
},
|
||||
{
|
||||
challenge_name: '成长',
|
||||
sum_count: 403
|
||||
},
|
||||
{
|
||||
challenge_name: '玩意',
|
||||
sum_count: 402
|
||||
},
|
||||
{
|
||||
challenge_name: '文学',
|
||||
sum_count: 397
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const circularProgressTickSpec = {
|
||||
type: 'circularProgress',
|
||||
data: [
|
||||
{
|
||||
id: 'id0',
|
||||
values: [
|
||||
{
|
||||
type: 'Tradition Industries',
|
||||
value: 0.795,
|
||||
text: '79.5%'
|
||||
},
|
||||
{
|
||||
type: 'Business Companies',
|
||||
value: 0.5,
|
||||
text: '50%'
|
||||
},
|
||||
{
|
||||
type: 'Customer-facing Companies',
|
||||
value: 0.25,
|
||||
text: '25%'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
color: ['rgb(255, 222, 0)', 'rgb(171, 205, 5)', 'rgb(0, 154, 68)'],
|
||||
valueField: 'value',
|
||||
categoryField: 'type',
|
||||
seriesField: 'type',
|
||||
radius: 0.8,
|
||||
innerRadius: 0.4,
|
||||
tickMask: {
|
||||
visible: true,
|
||||
angle: 10,
|
||||
offsetAngle: 0,
|
||||
forceAlign: true,
|
||||
style: {
|
||||
cornerRadius: 15
|
||||
}
|
||||
},
|
||||
axes: [
|
||||
{
|
||||
visible: false,
|
||||
type: 'linear',
|
||||
orient: 'angle'
|
||||
},
|
||||
{
|
||||
visible: false,
|
||||
type: 'band',
|
||||
orient: 'radius'
|
||||
}
|
||||
],
|
||||
indicator: {
|
||||
visible: true,
|
||||
trigger: 'hover',
|
||||
title: {
|
||||
visible: true,
|
||||
field: 'type',
|
||||
autoLimit: true,
|
||||
style: {
|
||||
fontSize: 20,
|
||||
fill: 'black'
|
||||
}
|
||||
},
|
||||
content: [
|
||||
{
|
||||
visible: true,
|
||||
field: 'text',
|
||||
style: {
|
||||
fontSize: 16,
|
||||
fill: 'gray'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
legends: {
|
||||
visible: true,
|
||||
orient: 'bottom',
|
||||
title: {
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const liquidChartSmartInvertSpec = {
|
||||
type: 'liquid',
|
||||
valueField: 'value',
|
||||
data: {
|
||||
id: 'data',
|
||||
values: [
|
||||
{
|
||||
value: 0.8
|
||||
}
|
||||
]
|
||||
},
|
||||
maskShape: 'drop', // 水滴
|
||||
// maskShape: 'circle',
|
||||
// maskShape: 'star',
|
||||
indicatorSmartInvert: true,
|
||||
indicator: {
|
||||
visible: true,
|
||||
title: {
|
||||
visible: true,
|
||||
style: {
|
||||
text: '进度'
|
||||
}
|
||||
},
|
||||
content: [
|
||||
{
|
||||
visible: true,
|
||||
style: {
|
||||
fill: 'black',
|
||||
text: '80%'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
liquidBackground: {
|
||||
style: {
|
||||
fill: 'blue'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const goldenMedals: Record<number, any[]> = {
|
||||
2000: [
|
||||
{ country: 'USA', value: 37 },
|
||||
{ country: 'Russia', value: 32 },
|
||||
{ country: 'China', value: 28 },
|
||||
{ country: 'Australia', value: 16 },
|
||||
{ country: 'Germany', value: 13 },
|
||||
{ country: 'France', value: 13 },
|
||||
{ country: 'Italy', value: 13 },
|
||||
{ country: 'Netherlands', value: 12 },
|
||||
{ country: 'Cuba', value: 11 },
|
||||
{ country: 'U.K.', value: 11 }
|
||||
],
|
||||
2004: [
|
||||
{ country: 'USA', value: 36 },
|
||||
{ country: 'China', value: 32 },
|
||||
{ country: 'Russia', value: 28 },
|
||||
{ country: 'Australia', value: 17 },
|
||||
{ country: 'Japan', value: 16 },
|
||||
{ country: 'Germany', value: 13 },
|
||||
{ country: 'France', value: 11 },
|
||||
{ country: 'Italy', value: 10 },
|
||||
{ country: 'South Korea', value: 9 },
|
||||
{ country: 'U.K.', value: 9 }
|
||||
],
|
||||
2008: [
|
||||
{ country: 'China', value: 48 },
|
||||
{ country: 'USA', value: 36 },
|
||||
{ country: 'Russia', value: 24 },
|
||||
{ country: 'U.K.', value: 19 },
|
||||
{ country: 'Germany', value: 16 },
|
||||
{ country: 'Australia', value: 14 },
|
||||
{ country: 'South Korea', value: 13 },
|
||||
{ country: 'Japan', value: 9 },
|
||||
{ country: 'Italy', value: 8 },
|
||||
{ country: 'France', value: 7 }
|
||||
],
|
||||
2012: [
|
||||
{ country: 'USA', value: 46 },
|
||||
{ country: 'China', value: 39 },
|
||||
{ country: 'U.K.', value: 29 },
|
||||
{ country: 'Russia', value: 19 },
|
||||
{ country: 'South Korea', value: 13 },
|
||||
{ country: 'Germany', value: 11 },
|
||||
{ country: 'France', value: 11 },
|
||||
{ country: 'Australia', value: 8 },
|
||||
{ country: 'Italy', value: 8 },
|
||||
{ country: 'Hungary', value: 8 }
|
||||
],
|
||||
2016: [
|
||||
{ country: 'USA', value: 46 },
|
||||
{ country: 'U.K.', value: 27 },
|
||||
{ country: 'China', value: 26 },
|
||||
{ country: 'Russia', value: 19 },
|
||||
{ country: 'Germany', value: 17 },
|
||||
{ country: 'Japan', value: 12 },
|
||||
{ country: 'France', value: 10 },
|
||||
{ country: 'South Korea', value: 9 },
|
||||
{ country: 'Italy', value: 8 },
|
||||
{ country: 'Australia', value: 8 }
|
||||
],
|
||||
2020: [
|
||||
{ country: 'USA', value: 39 },
|
||||
{ country: 'China', value: 38 },
|
||||
{ country: 'Japan', value: 27 },
|
||||
{ country: 'U.K.', value: 22 },
|
||||
{ country: 'Russian Olympic Committee', value: 20 },
|
||||
{ country: 'Australia', value: 17 },
|
||||
{ country: 'Netherlands', value: 10 },
|
||||
{ country: 'France', value: 10 },
|
||||
{ country: 'Germany', value: 10 },
|
||||
{ country: 'Italy', value: 10 }
|
||||
]
|
||||
};
|
||||
|
||||
const colors = {
|
||||
China: '#d62728',
|
||||
USA: '#1664FF',
|
||||
Russia: '#B2CFFF',
|
||||
'U.K.': '#1AC6FF',
|
||||
Australia: '#94EFFF',
|
||||
Japan: '#FF8A00',
|
||||
Cuba: '#FFCE7A',
|
||||
Germany: '#3CC780',
|
||||
France: '#B9EDCD',
|
||||
Italy: '#7442D4',
|
||||
'South Korea': '#DDC5FA',
|
||||
'Russian Olympic Committee': '#B2CFFF',
|
||||
Netherlands: '#FFC400',
|
||||
Hungary: '#FAE878'
|
||||
};
|
||||
|
||||
const dataSpecs = Object.keys(goldenMedals).map(year => {
|
||||
return {
|
||||
data: [
|
||||
{
|
||||
id: 'id',
|
||||
values: (goldenMedals[year as unknown as number] as any)
|
||||
.sort((a: any, b: any) => b.value - a.value)
|
||||
.map((v: any) => {
|
||||
return { ...v, fill: (colors as any)[v.country] };
|
||||
})
|
||||
},
|
||||
{
|
||||
id: 'year',
|
||||
values: [{ year }]
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
const duration = 1000;
|
||||
const exchangeDuration = 600;
|
||||
|
||||
export const rankingBarSpec = {
|
||||
type: 'bar',
|
||||
padding: {
|
||||
top: 12,
|
||||
right: 100,
|
||||
bottom: 12
|
||||
},
|
||||
data: dataSpecs[0].data,
|
||||
direction: 'horizontal',
|
||||
yField: 'country',
|
||||
xField: 'value',
|
||||
seriesField: 'country',
|
||||
bar: {
|
||||
style: {
|
||||
fill: (datum: any) => datum.fill
|
||||
}
|
||||
},
|
||||
axes: [
|
||||
{
|
||||
animation: true,
|
||||
orient: 'bottom',
|
||||
type: 'linear',
|
||||
visible: true,
|
||||
max: 50,
|
||||
grid: {
|
||||
visible: true
|
||||
}
|
||||
},
|
||||
{
|
||||
animation: true,
|
||||
id: 'axis-left',
|
||||
orient: 'left',
|
||||
width: 130,
|
||||
tick: { visible: false },
|
||||
label: { visible: true },
|
||||
type: 'band'
|
||||
}
|
||||
],
|
||||
title: {
|
||||
visible: true,
|
||||
text: 'Top 10 Olympic Gold Medals by Country Since 2000'
|
||||
},
|
||||
animationUpdate: {
|
||||
bar: [
|
||||
{
|
||||
type: 'update',
|
||||
options: { excludeChannels: ['y'] },
|
||||
easing: 'linear',
|
||||
duration
|
||||
},
|
||||
{
|
||||
channel: ['y'],
|
||||
easing: 'circInOut',
|
||||
duration: exchangeDuration
|
||||
}
|
||||
],
|
||||
axis: {
|
||||
duration: exchangeDuration,
|
||||
easing: 'circInOut'
|
||||
}
|
||||
},
|
||||
animationEnter: {
|
||||
bar: [
|
||||
{
|
||||
type: 'moveIn',
|
||||
duration: exchangeDuration,
|
||||
easing: 'circInOut',
|
||||
options: {
|
||||
direction: 'y',
|
||||
orient: 'negative'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
animationExit: {
|
||||
bar: [
|
||||
{
|
||||
type: 'fadeOut',
|
||||
duration: exchangeDuration
|
||||
}
|
||||
]
|
||||
},
|
||||
customMark: [
|
||||
{
|
||||
type: 'text',
|
||||
dataId: 'year',
|
||||
style: {
|
||||
textBaseline: 'bottom',
|
||||
fontSize: 200,
|
||||
textAlign: 'right',
|
||||
fontFamily: 'PingFang SC',
|
||||
fontWeight: 600,
|
||||
text: (datum: any) => datum.year,
|
||||
x: (_datum: any, ctx: any) => {
|
||||
return ctx.vchart.getChart().getCanvasRect()?.width - 50;
|
||||
},
|
||||
y: (_datum: any, ctx: any) => {
|
||||
return ctx.vchart.getChart().getCanvasRect()?.height - 50;
|
||||
},
|
||||
fill: 'grey',
|
||||
fillOpacity: 0.5
|
||||
}
|
||||
}
|
||||
],
|
||||
player: {
|
||||
type: 'continuous',
|
||||
orient: 'bottom',
|
||||
auto: true,
|
||||
loop: true,
|
||||
dx: 80,
|
||||
position: 'middle',
|
||||
interval: duration,
|
||||
specs: dataSpecs,
|
||||
slider: {
|
||||
railStyle: {
|
||||
height: 6
|
||||
}
|
||||
},
|
||||
controller: {
|
||||
backward: {
|
||||
style: {
|
||||
size: 12
|
||||
}
|
||||
},
|
||||
forward: {
|
||||
style: {
|
||||
size: 12
|
||||
}
|
||||
},
|
||||
start: {
|
||||
order: 1,
|
||||
position: 'end'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const stackedDashAreaSpec = {
|
||||
type: 'area',
|
||||
data: {
|
||||
values: [
|
||||
{ month: 'Jan', country: 'Africa', value: 4229 },
|
||||
{ month: 'Jan', country: 'EU', value: 4376 },
|
||||
{ month: 'Jan', country: 'China', value: 3054 },
|
||||
{ month: 'Jan', country: 'USA', value: 12814 },
|
||||
{ month: 'Feb', country: 'Africa', value: 3932 },
|
||||
{ month: 'Feb', country: 'EU', value: 3987 },
|
||||
{ month: 'Feb', country: 'China', value: 5067 },
|
||||
{ month: 'Feb', country: 'USA', value: 13012 },
|
||||
{ month: 'Mar', country: 'Africa', value: 5221 },
|
||||
{ month: 'Mar', country: 'EU', value: 3574 },
|
||||
{ month: 'Mar', country: 'China', value: 7004 },
|
||||
{ month: 'Mar', country: 'USA', value: 11624 },
|
||||
{ month: 'Apr', country: 'Africa', value: 9256 },
|
||||
{ month: 'Apr', country: 'EU', value: 4376 },
|
||||
{ month: 'Apr', country: 'China', value: 9054 },
|
||||
{ month: 'Apr', country: 'USA', value: 8814 },
|
||||
{ month: 'May', country: 'Africa', value: 3308 },
|
||||
{ month: 'May', country: 'EU', value: 4572 },
|
||||
{ month: 'May', country: 'China', value: 12043 },
|
||||
{ month: 'May', country: 'USA', value: 12998 },
|
||||
{ month: 'Jun', country: 'Africa', value: 5432 },
|
||||
{ month: 'Jun', country: 'EU', value: 3417 },
|
||||
{ month: 'Jun', country: 'China', value: 15067 },
|
||||
{ month: 'Jun', country: 'USA', value: 12321 },
|
||||
{ month: 'Jul', country: 'Africa', value: 13701 },
|
||||
{ month: 'Jul', country: 'EU', value: 5231 },
|
||||
{ month: 'Jul', country: 'China', value: 10119 },
|
||||
{ month: 'Jul', country: 'USA', value: 10342 },
|
||||
{ month: 'Aug', country: 'Africa', value: 4008, forecast: true },
|
||||
{ month: 'Aug', country: 'EU', value: 4572, forecast: true },
|
||||
{ month: 'Aug', country: 'China', value: 12043, forecast: true },
|
||||
{ month: 'Aug', country: 'USA', value: 22998, forecast: true },
|
||||
{ month: 'Sept', country: 'Africa', value: 18712, forecast: true },
|
||||
{ month: 'Sept', country: 'EU', value: 6134, forecast: true },
|
||||
{ month: 'Sept', country: 'China', value: 10419, forecast: true },
|
||||
{ month: 'Sept', country: 'USA', value: 11261, forecast: true }
|
||||
]
|
||||
},
|
||||
stack: true,
|
||||
xField: 'month',
|
||||
yField: 'value',
|
||||
seriesField: 'country',
|
||||
point: {
|
||||
style: {
|
||||
size: 0
|
||||
},
|
||||
state: {
|
||||
dimension_hover: {
|
||||
size: 10,
|
||||
outerBorder: {
|
||||
distance: 0,
|
||||
lineWidth: 6,
|
||||
strokeOpacity: 0.2
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
line: {
|
||||
style: {
|
||||
// Configure the lineDash attribute based on the forecast field value of the data
|
||||
lineDash: (data: any) => {
|
||||
if (data.forecast) {
|
||||
return [5, 5];
|
||||
}
|
||||
return [0];
|
||||
}
|
||||
}
|
||||
},
|
||||
area: {
|
||||
style: {
|
||||
fillOpacity: 0.5,
|
||||
textureColor: '#fff',
|
||||
textureSize: 14,
|
||||
// Configure the texture attribute based on the forecast field value of the data
|
||||
texture: (data: any) => {
|
||||
if (data.forecast) {
|
||||
return 'bias-rl';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
legends: [{ visible: true, position: 'middle', orient: 'bottom' }],
|
||||
crosshair: {
|
||||
xField: {
|
||||
visible: true,
|
||||
line: {
|
||||
type: 'line'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const barMarkPointSpec = {
|
||||
type: 'bar',
|
||||
height: 300,
|
||||
data: [
|
||||
{
|
||||
id: 'barData',
|
||||
values: [
|
||||
{ time: '10:20', cost: 2 },
|
||||
{ time: '10:30', cost: 1 },
|
||||
{ time: '10:40', cost: 1 },
|
||||
{ time: '10:50', cost: 2 },
|
||||
{ time: '11:00', cost: 2 },
|
||||
{ time: '11:10', cost: 2 },
|
||||
{ time: '11:20', cost: 1 },
|
||||
{ time: '11:30', cost: 1 },
|
||||
{ time: '11:40', cost: 2 },
|
||||
{ time: '11:50', cost: 1 }
|
||||
]
|
||||
}
|
||||
],
|
||||
xField: 'time',
|
||||
yField: 'cost',
|
||||
crosshair: {
|
||||
xField: {
|
||||
visible: true,
|
||||
line: {
|
||||
type: 'rect',
|
||||
style: {
|
||||
fill: 'rgb(85,208,93)',
|
||||
fillOpacity: 0.1
|
||||
}
|
||||
},
|
||||
bindingAxesIndex: [1],
|
||||
defaultSelect: {
|
||||
axisIndex: 1,
|
||||
datum: '10:20'
|
||||
}
|
||||
}
|
||||
},
|
||||
label: {
|
||||
visible: true,
|
||||
animation: false,
|
||||
formatMethod: (datum: any) => `${datum}分钟`,
|
||||
style: {
|
||||
fill: 'rgb(155,155,155)'
|
||||
}
|
||||
},
|
||||
bar: {
|
||||
style: {
|
||||
fill: 'rgb(85,208,93)',
|
||||
cornerRadius: [4, 4, 0, 0],
|
||||
width: 30
|
||||
}
|
||||
},
|
||||
markPoint: [
|
||||
{
|
||||
coordinate: {
|
||||
time: '10:20',
|
||||
cost: 2
|
||||
},
|
||||
itemContent: {
|
||||
type: 'text',
|
||||
autoRotate: false,
|
||||
offsetY: -10,
|
||||
text: {
|
||||
dy: 14,
|
||||
text: '2分钟',
|
||||
style: {
|
||||
fill: 'white',
|
||||
fontSize: 14
|
||||
},
|
||||
labelBackground: {
|
||||
padding: [5, 10, 5, 10],
|
||||
style: {
|
||||
fill: '#000',
|
||||
cornerRadius: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
itemLine: {
|
||||
endSymbol: {
|
||||
visible: true,
|
||||
style: {
|
||||
angle: Math.PI,
|
||||
scaleY: 0.4,
|
||||
fill: '#000',
|
||||
dy: 4,
|
||||
stroke: '#000'
|
||||
}
|
||||
},
|
||||
startSymbol: { visible: false },
|
||||
line: {
|
||||
style: {
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
animationUpdate: false,
|
||||
axes: [
|
||||
{
|
||||
orient: 'left',
|
||||
max: 10,
|
||||
label: { visible: false },
|
||||
grid: {
|
||||
style: { lineDash: [4, 4] }
|
||||
}
|
||||
},
|
||||
{
|
||||
orient: 'bottom',
|
||||
label: {
|
||||
formatMethod: (datum: any) => {
|
||||
return datum === '10:20' ? '当前' : datum;
|
||||
},
|
||||
style: (datum: any) => {
|
||||
return {
|
||||
fontSize: datum === '10:20' ? 14 : 12,
|
||||
fill: datum === '10:20' ? 'black' : 'grey'
|
||||
};
|
||||
}
|
||||
},
|
||||
paddingOuter: 0.5,
|
||||
paddingInner: 0,
|
||||
grid: {
|
||||
visible: true,
|
||||
alignWithLabel: false,
|
||||
style: { lineDash: [4, 4] }
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const histogramDifferentBinSpec = {
|
||||
type: 'histogram',
|
||||
xField: 'from',
|
||||
x2Field: 'to',
|
||||
yField: 'profit',
|
||||
seriesField: 'type',
|
||||
bar: {
|
||||
style: {
|
||||
stroke: 'white',
|
||||
lineWidth: 1
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text: 'Profit',
|
||||
textStyle: {
|
||||
align: 'center',
|
||||
height: 50,
|
||||
lineWidth: 3,
|
||||
fill: '#333',
|
||||
fontSize: 25,
|
||||
fontFamily: 'Times New Roman'
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
visible: true,
|
||||
mark: {
|
||||
title: {
|
||||
key: 'title',
|
||||
value: 'profit'
|
||||
},
|
||||
content: [
|
||||
{
|
||||
key: (datum: any) => `${datum.from}~${datum.to}`,
|
||||
value: (datum: any) => datum.profit
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
axes: [
|
||||
{
|
||||
orient: 'bottom',
|
||||
nice: false
|
||||
}
|
||||
],
|
||||
data: [
|
||||
{
|
||||
name: 'data1',
|
||||
values: [
|
||||
{
|
||||
from: 0,
|
||||
to: 10,
|
||||
profit: 2,
|
||||
type: 'A'
|
||||
},
|
||||
{
|
||||
from: 10,
|
||||
to: 16,
|
||||
profit: 3,
|
||||
type: 'B'
|
||||
},
|
||||
{
|
||||
from: 16,
|
||||
to: 18,
|
||||
profit: 15,
|
||||
type: 'C'
|
||||
},
|
||||
{
|
||||
from: 18,
|
||||
to: 26,
|
||||
profit: 12,
|
||||
type: 'D'
|
||||
},
|
||||
{
|
||||
from: 26,
|
||||
to: 32,
|
||||
profit: 22,
|
||||
type: 'E'
|
||||
},
|
||||
{
|
||||
from: 32,
|
||||
to: 56,
|
||||
profit: 7,
|
||||
type: 'F'
|
||||
},
|
||||
{
|
||||
from: 56,
|
||||
to: 62,
|
||||
profit: 17,
|
||||
type: 'G'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
@ -1,61 +1,36 @@
|
||||
<script setup lang="ts">
|
||||
import { onUnmounted } from 'vue';
|
||||
// import { useEcharts } from '@/hooks/common/echarts';
|
||||
// import {
|
||||
// barOptions,
|
||||
// gaugeOptions,
|
||||
// getPictorialBarOption,
|
||||
// getScatterOption,
|
||||
// lineOptions,
|
||||
// pieOptions,
|
||||
// radarOptions
|
||||
// } from './data';
|
||||
import type {
|
||||
IAreaChartSpec,
|
||||
IBarChartSpec,
|
||||
ICircularProgressChartSpec,
|
||||
IHistogramChartSpec,
|
||||
ILiquidChartSpec
|
||||
} from '@visactor/vchart';
|
||||
import { useVChart } from '@/hooks/common/vchart';
|
||||
import {
|
||||
barMarkPointSpec,
|
||||
circularProgressTickSpec,
|
||||
histogramDifferentBinSpec,
|
||||
liquidChartSmartInvertSpec,
|
||||
rankingBarSpec,
|
||||
shapeWordCloudSpec,
|
||||
stackedDashAreaSpec
|
||||
} from './data';
|
||||
|
||||
// const { domRef: pieRef } = useEcharts(() => pieOptions, { onRender() {} });
|
||||
// const { domRef: lineRef } = useEcharts(() => lineOptions, { onRender() {} });
|
||||
// const { domRef: barRef } = useEcharts(() => barOptions, { onRender() {} });
|
||||
// const { domRef: pictorialBarRef } = useEcharts(() => getPictorialBarOption(), { onRender() {} });
|
||||
// const { domRef: radarRef } = useEcharts(() => radarOptions, { onRender() {} });
|
||||
// const { domRef: scatterRef } = useEcharts(() => getScatterOption(), { onRender() {} });
|
||||
// const { domRef: gaugeRef, setOptions: setGaugeOptions } = useEcharts(() => gaugeOptions, { onRender() {} });
|
||||
|
||||
// let intervalId: NodeJS.Timeout;
|
||||
|
||||
// function initGaugeChart() {
|
||||
// intervalId = setInterval(() => {
|
||||
// const date = new Date();
|
||||
// const second = date.getSeconds();
|
||||
// const minute = date.getMinutes() + second / 60;
|
||||
// const hour = (date.getHours() % 12) + minute / 60;
|
||||
|
||||
// setGaugeOptions({
|
||||
// animationDurationUpdate: 300,
|
||||
// series: [
|
||||
// {
|
||||
// name: 'hour',
|
||||
// animation: hour !== 0,
|
||||
// data: [{ value: hour }]
|
||||
// },
|
||||
// {
|
||||
// name: 'minute',
|
||||
// animation: minute !== 0,
|
||||
// data: [{ value: minute }]
|
||||
// },
|
||||
// {
|
||||
// animation: second !== 0,
|
||||
// name: 'second',
|
||||
// data: [{ value: second }]
|
||||
// }
|
||||
// ]
|
||||
// });
|
||||
// }, 1000);
|
||||
// }
|
||||
|
||||
// function clearGaugeChart() {
|
||||
// clearInterval(intervalId);
|
||||
// }
|
||||
|
||||
// initGaugeChart();
|
||||
const { domRef: stackedDashAreaRef } = useVChart(() => stackedDashAreaSpec as IAreaChartSpec, { onRender() {} });
|
||||
const { domRef: barMarkPointRef } = useVChart(() => barMarkPointSpec as IBarChartSpec, { onRender() {} });
|
||||
const { domRef: histogramDifferentBinRef } = useVChart(() => histogramDifferentBinSpec as IHistogramChartSpec, {
|
||||
onRender() {}
|
||||
});
|
||||
const { domRef: rankingBarRef } = useVChart(() => rankingBarSpec as IBarChartSpec, { onRender() {} });
|
||||
const { domRef: shapeWordCloudRef } = useVChart(() => shapeWordCloudSpec, { onRender() {} });
|
||||
const { domRef: circularProgressTickRef } = useVChart(() => circularProgressTickSpec as ICircularProgressChartSpec, {
|
||||
onRender() {}
|
||||
});
|
||||
const { domRef: liquidChartSmartInvertRef } = useVChart(() => liquidChartSmartInvertSpec as ILiquidChartSpec, {
|
||||
onRender() {}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
// clearGaugeChart();
|
||||
@ -64,7 +39,27 @@ onUnmounted(() => {
|
||||
|
||||
<template>
|
||||
<NSpace vertical :size="16">
|
||||
<div>vchart</div>
|
||||
<NCard title="List Chart" :bordered="false" class="h-full card-wrapper">
|
||||
<div ref="stackedDashAreaRef" class="h-400px" />
|
||||
</NCard>
|
||||
<NCard title="List Chart" :bordered="false" class="h-full card-wrapper">
|
||||
<div ref="barMarkPointRef" class="h-400px" />
|
||||
</NCard>
|
||||
<NCard title="List Chart" :bordered="false" class="h-full card-wrapper">
|
||||
<div ref="histogramDifferentBinRef" class="h-400px" />
|
||||
</NCard>
|
||||
<NCard title="List Chart" :bordered="false" class="h-full card-wrapper">
|
||||
<div ref="rankingBarRef" class="h-400px" />
|
||||
</NCard>
|
||||
<NCard title="List Chart" :bordered="false" class="h-full card-wrapper">
|
||||
<div ref="circularProgressTickRef" class="h-400px" />
|
||||
</NCard>
|
||||
<NCard title="List Chart" :bordered="false" class="h-full card-wrapper">
|
||||
<div ref="liquidChartSmartInvertRef" class="h-400px" />
|
||||
</NCard>
|
||||
<NCard title="List Chart" :bordered="false" class="h-full card-wrapper">
|
||||
<div ref="shapeWordCloudRef" class="h-400px" />
|
||||
</NCard>
|
||||
</NSpace>
|
||||
</template>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user