Commit 0f71878b by qlintonger xeno

feat: 添加其他图以及数字动画

parent 72139ecf
......@@ -10,6 +10,7 @@
"dependencies": {
"@tailwindcss/vite": "^4.1.3",
"echarts": "^5.6.0",
"gsap": "^3.13.0",
"lodash": "^4.17.21",
"v-scale-screen": "^2.3.0",
"vue": "^3.4.31",
......@@ -1794,6 +1795,12 @@
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"license": "ISC"
},
"node_modules/gsap": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/gsap/-/gsap-3.13.0.tgz",
"integrity": "sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw==",
"license": "Standard 'no charge' license: https://gsap.com/standard-license."
},
"node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
......
......@@ -11,6 +11,7 @@
"dependencies": {
"@tailwindcss/vite": "^4.1.3",
"echarts": "^5.6.0",
"gsap": "^3.13.0",
"lodash": "^4.17.21",
"v-scale-screen": "^2.3.0",
"vue": "^3.4.31",
......
......@@ -7,17 +7,124 @@ const sampleForLineCharts = {
data: [120, 200, 150, 80, 70, 110, 130],
};
const sampleForPieCharts = {
type: ChartCompType.EChartsPieCenterText,
radius: ["60%", "90%"],
title: "示例图表",
const sampleForRadarCharts = {
type: ChartCompType.SimpleRadar,
indicators: [
{ name: 'Sales', max: 6500 },
{ name: 'Administration', max: 16000 },
{ name: 'Information Technology', max: 30000 },
{ name: 'Customer Support', max: 38000 },
{ name: 'Development', max: 52000 },
{ name: 'Marketing', max: 25000 }
],
data: [
{
value: [4200, 3000, 20000, 35000, 50000, 18000],
name: 'Allocated Budget'
},
{
value: [5000, 14000, 28000, 26000, 42000, 21000],
name: 'Actual Spending'
}
]
}
const sampleForVerLineCharts = {
type: ChartCompType.SimpleLine,
category: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
data: [
{ name: "类型一", color: "#ffaacc", value: 100 },
{ name: "类型二", color: "#abc001", value: 200 },
{ name: "类型三", color: "#cad13c", value: 50 },
{ name: "测试1", data: [120, 200, 150, 80, 70, 110, 130] },
{ name: "测试2", data: [100, 100, 110, 10, 10, 100, 100] },
],
};
const sampleKLine = {
type: ChartCompType.DIY,
options: {
xAxis: {
data: ['2017-10-24', '2017-10-25', '2017-10-26', '2017-10-27']
},
yAxis: {},
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
appendTo: 'body'
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
top: "5%",
},
series: [
{
type: 'candlestick',
data: [
[20, 34, 10, 38],
[40, 35, 30, 50],
[31, 38, 33, 44],
[38, 15, 5, 42]
]
}
]
}
}
const sampleScatter = {
type: ChartCompType.DIY,
options: {
xAxis: {},
yAxis: {},
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
appendTo: 'body'
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
top: "5%",
},
series: [
{
symbolSize: 20,
data: [
[10.0, 8.04],
[8.07, 6.95],
[13.0, 7.58],
[9.05, 8.81],
[11.0, 8.33],
[14.0, 7.66],
[13.4, 6.81],
[10.0, 6.33],
[14.0, 8.96],
[12.5, 6.82],
[9.15, 7.2],
[11.5, 7.2],
[3.03, 4.23],
[12.2, 7.83],
[2.02, 4.47],
[1.05, 3.33],
[4.05, 4.96],
[6.03, 7.24],
[12.0, 6.26],
[12.0, 8.84],
[7.08, 5.82],
[5.02, 5.68]
],
type: 'scatter'
}
]
}
}
const bottomCenterTextCharts = {
title: "奇特上下排位图表",
columns: 4,
......@@ -77,8 +184,8 @@ const indexMapper: Record<string, any> = {
chartData: [sampleForLineCharts, sampleForLineCharts],
},
0: {
title: "简单混排图",
chartData: [sampleForLineCharts, sampleForPieCharts],
title: "简单折线图",
chartData: [sampleForVerLineCharts, sampleForVerLineCharts],
},
5: {
title: "啊哈哈表格呢",
......@@ -106,12 +213,12 @@ const indexMapper: Record<string, any> = {
],
},
1: {
title: "简单混排图",
chartData: [sampleForLineCharts, sampleForPieCharts],
title: "随意设置图",
chartData: [sampleKLine, sampleScatter],
},
6: {
title: "简单混排图",
chartData: [sampleForLineCharts, sampleForPieCharts],
chartData: [sampleForLineCharts, sampleForRadarCharts],
},
};
......
<template>
<template v-if="ps.type === ChartCompType.EChartsBar">
<template
v-if="
ps.type === ChartCompType.EChartsBar ||
ps.type === ChartCompType.SimpleLine ||
ps.type === ChartCompType.SimpleRadar ||
ps.type === ChartCompType.DIY
"
>
<global-echarts
v-bind="$attrs"
:height="ps.item.chartWidth"
......@@ -61,10 +68,20 @@
:style="{
fontSize: mapWidth(32) + 'px',
}"
v-if="!isNumber(ps.item.title)"
>
{{ ps.item.title }}
</div>
<div
:style="{
fontSize: mapWidth(32) + 'px',
}"
v-else
v-number-animation="ps.item.title"
>
0
</div>
<div
:style="{
marginTop: mapHeight(16) + 'px',
fontSize: mapWidth(14) + 'px',
......@@ -89,15 +106,14 @@
</template>
<script setup lang="ts">
import { ECBasicOption } from "echarts/types/dist/shared";
import { ChartCompType } from "../const/chartCompType.ts";
import {ECBasicOption} from "echarts/types/dist/shared";
import {ChartCompType} from "../const/chartCompType";
import GlobalEcharts from "./global-echarts.vue";
import {
GeneralChartOptions,
SimpleTableChart,
} from "../types/ScreenChart.typing.ts";
import { mapWidth, mapHeight } from "../funcs/mapGEOStyle.ts";
import { computed } from "vue";
import {GeneralChartOptions, SimpleTableChart,} from "../types/ScreenChart.typing";
import {mapHeight, mapWidth} from "../funcs/mapGEOStyle";
import {computed} from "vue";
import {isNumber} from "lodash";
import {vNumberAnimation} from "../directive/number-animate.ts";
interface Props {
options?: ECBasicOption;
......
......@@ -51,6 +51,9 @@ onMounted(() => {
const el = chartRef.value;
const inst = getCurrentInstance();
if (el) {
if (ps.options!.series?.find((item: any) => item.type === "radar")) {
console.log('the ps -radar', ps.options)
}
nextTick(() => {
chartInstance = echarts.init(el);
setOptions(ps.options);
......
<script lang="ts" setup>
import { ScreenChartProps } from "./types/ScreenChart.typing.ts";
import { ScreenChartProps } from "./types/ScreenChart.typing";
import {
getAllGridSize,
gridItemStyle,
mapContainerStyle,
} from "./funcs/mapStyle.ts";
import { mapToEchartsOptions } from "./funcs/mapToEchartsOptions.ts";
} from "./funcs/mapStyle";
import { mapToEchartsOptions } from "./funcs/mapToEchartsOptions";
import { computed, onMounted, onUnmounted } from "vue";
import ChartsWrapper from "./Components/charts-wrapper.vue";
import {
......@@ -13,7 +13,7 @@ import {
isAllMounted,
registerGraph,
} from "./funcs/procedureFunc.ts";
import { onNew, onRemoved } from "./funcs/hooksFunc.ts";
import { onNew, onRemoved } from "./funcs/hooksFunc";
import { mapHeight } from "./funcs/mapGEOStyle";
const props = withDefaults(defineProps<ScreenChartProps>(), {
......
......@@ -4,4 +4,7 @@ export enum ChartCompType {
EchartsPieBottomCenterText,
BottomCenterText,
SimpleTable,
SimpleLine,
SimpleRadar,
DIY
}
export const singleContainerClass = "overflow-auto grid justify-between";
export const baseEchartOptions = (item: any) => ({
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
top: "5%",
},
xAxis: {
type: "category",
data: item.category,
},
yAxis: {
type: "value",
boundaryGap: [0, 0.01],
},
});
import {Directive, DirectiveBinding, ref} from 'vue';
import gsap from 'gsap';
interface NumberAnimationOptions {
value?: number;
decimals?: number;
ease?: string;
formatter?: (value: number) => string;
}
type NumberAnimationBinding = number | NumberAnimationOptions;
export const vNumberAnimation: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding<NumberAnimationBinding>) {
const target = ref(0);
const duration = parseFloat(binding.arg as string) || 1;
const options = typeof binding.value === 'number'
? { value: binding.value }
: binding.value || {};
el.textContent = target.value.toString();
// @ts-ignore
el._animation = gsap.to(target, {
value: options.value || parseFloat(el.dataset.value || '0'),
duration,
ease: options.ease || 'power1.out',
onUpdate: () => {
el.textContent = options.formatter
? options.formatter(target.value)
: target.value.toFixed(options.decimals || 0);
}
});
},
updated(el: HTMLElement, binding: DirectiveBinding<NumberAnimationBinding>) {
// @ts-ignore
if (el._animation) {
// @ts-ignore
el._animation.kill();
}
// @ts-ignore
const target = ref(parseFloat(el.textContent));
const duration = parseFloat(binding.arg as string) || 1;
const options = typeof binding.value === 'number'
? { value: binding.value }
: binding.value || {};
// @ts-ignore
el._animation = gsap.to(target, {
value: options.value || parseFloat(el.dataset.value || '0'),
duration,
ease: options.ease || 'power1.out',
onUpdate: () => {
el.textContent = options.formatter
? options.formatter(target.value)
: target.value.toFixed(options.decimals || 0);
}
});
},
unmounted(el: HTMLElement) {
// @ts-ignore
if (el._animation) {
// @ts-ignore
el._animation.kill();
}
}
};
\ No newline at end of file
import {
BarChartOptions,
DIYChart,
GeneralChartOptions,
LineChartOptions,
PieCharOptions,
SimpleRadarChart
} from "../types/ScreenChart.typing.ts";
import { ChartCompType } from "../const/chartCompType.ts";
import {ChartCompType} from "../const/chartCompType.ts";
import {baseEchartOptions} from "../const/staticConsts.ts";
export function mapToEchartsOptions(item: GeneralChartOptions) {
if (item.type === ChartCompType.EChartsBar) {
let series: any[];
// 简单类型数据
item = <BarChartOptions>item;
if (item.data.every((a) => typeof a === "number")) {
series = [{ data: item.data, type: "bar" }];
} else {
series = item.data.map((a) => {
if (item.type === ChartCompType.DIY) {
item = <DIYChart>item;
return item.options;
}
if (item.type === ChartCompType.SimpleLine) {
let series: any[];
item = <LineChartOptions>item;
if (item.data.every((a) => typeof a === "number")) {
series = [{data: item.data, type: "line"}];
} else {
series = item.data.map((a) => {
return {
type: "line",
name: a.name,
data: a.data,
};
});
}
return {
type: "bar",
name: a.name,
data: a.data,
...baseEchartOptions(item),
series,
};
});
}
return {
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
top: "5%",
},
xAxis: {
type: "category",
data: item.category,
},
yAxis: {
type: "value",
boundaryGap: [0, 0.01],
},
series,
};
}
if (
item.type === ChartCompType.EChartsPieCenterText ||
item.type === ChartCompType.EchartsPieBottomCenterText
) {
item = <PieCharOptions>item;
const total = item.data.reduce((a, b) => a + b.value, 0);
return {
grid: {
left: 0,
right: 0,
bottom: 0,
containLabel: true,
top: 0,
},
series: [
{
radius: item.radius || undefined,
type: "pie",
center: ["50%", "50%"],
avoidLabelOverlap: false,
emphasis: {
scaleSize: 3,
},
label: {
show: false,
},
labelLine: {
show: false,
},
data: item.data.map(function (item) {
return {
value: item.value,
name: item.name,
itemStyle: {
color: item.color,
},
};
}),
},
],
toolbox: false,
legend: item.legend || undefined,
tooltip: {
trigger: "item",
formatter: function (param: any) {
return `${param.name}${
total ? ((param.value * 100) / total).toFixed(2) : 100
}% ${param.value}`;
},
showDelay: 0,
hideDelay: 0,
extraCssText: "pointer-events: none;",
position: "top",
appendTo: "body",
},
};
}
if (item.type === ChartCompType.EChartsBar) {
let series: any[];
// 简单类型数据
item = <BarChartOptions>item;
if (item.data.every((a) => typeof a === "number")) {
series = [{data: item.data, type: "bar"}];
} else {
series = item.data.map((a) => {
return {
type: "bar",
name: a.name,
data: a.data,
};
});
}
return {
...baseEchartOptions(item),
series,
};
}
if (
item.type === ChartCompType.EChartsPieCenterText ||
item.type === ChartCompType.EchartsPieBottomCenterText
) {
item = <PieCharOptions>item;
const total = item.data.reduce((a, b) => a + b.value, 0);
return {
grid: {
left: 0,
right: 0,
bottom: 0,
containLabel: true,
top: 0,
},
series: [
{
radius: item.radius || undefined,
type: "pie",
center: ["50%", "50%"],
avoidLabelOverlap: false,
emphasis: {
scaleSize: 3,
},
label: {
show: false,
},
labelLine: {
show: false,
},
data: item.data.map(function (item) {
return {
value: item.value,
name: item.name,
itemStyle: {
color: item.color,
},
};
}),
},
],
toolbox: false,
legend: item.legend || undefined,
tooltip: {
trigger: "item",
formatter: function (param: any) {
return `${param.name}${
total ? ((param.value * 100) / total).toFixed(2) : 100
}% ${param.value}`;
},
showDelay: 0,
hideDelay: 0,
extraCssText: "pointer-events: none;",
position: "top",
appendTo: "body",
},
};
}
if (item.type === ChartCompType.SimpleRadar) {
item = <SimpleRadarChart>item;
return {
...baseEchartOptions(item),
xAxis: null,
yAxis: null,
tooltip: {
trigger: 'item',
appendTo: 'body'
},
radar: {
indicator: item.indicators || [],
},
legend: item.legend || undefined,
series: [
{
type: 'radar',
data: item.data,
areaStyle: {
color: 'rgb(188, 217, 251)' // 设置填充颜色
},
symbol: 'none', // 不显示圆点
symbolSize: 0 // 圆点大小设置为0
}
]
}
}
}
......@@ -7,8 +7,11 @@ export type BarChartOptions = {
type: ChartCompType;
chartWidth?: number | string;
chartHeight?: number | string;
options?: any
};
export type LineChartOptions = BarChartOptions;
export type PieChartOptionsBase = {
type: ChartCompType;
radius: Array<string>;
......@@ -16,6 +19,7 @@ export type PieChartOptionsBase = {
chartWidth?: number | string;
chartHeight?: number | string;
legend?: any;
options?: any
};
export type PieCharOptions = PieChartOptionsBase & {
......@@ -26,7 +30,7 @@ export type PieCharOptionsForBottomCenterText = PieCharOptions & {
bottomText: string;
};
export type BottonCenterText = {
export type ButtonCenterText = {
title: string;
bottomTex: string;
type: ChartCompType;
......@@ -36,14 +40,34 @@ export type SimpleTableChart = {
type: ChartCompType;
columns: Array<{ title: string; key: string }>;
data: Array<Record<string, any>>;
options?: any
};
export type SimpleRadarChart = {
type: ChartCompType;
data: Array<{
name: string;
value: number[];
}>;
legend?: any;
indicators: Array<{ name: string; max: number }>;
options?: any
};
export type DIYChart = {
type: ChartCompType.DIY,
options: any,
chartWidth?: number | string;
chartHeight?: number | string;
}
export type GeneralChartOptions =
| BarChartOptions
| PieCharOptions
| PieCharOptionsForBottomCenterText
| BottonCenterText
| SimpleTableChart;
| ButtonCenterText
| SimpleTableChart
| LineChartOptions | SimpleRadarChart | DIYChart;
export type ChartOptionSet = Array<GeneralChartOptions>;
......
import {createApp} from 'vue'
import './style.css'
import App from './App.vue'
import {injectScreenChart} from "./lib/Table2Chart/plugin.inject.ts";
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
import { injectScreenChart } from "./lib/Table2Chart/plugin.inject.ts";
createApp(App).use(injectScreenChart).mount('#app')
createApp(App).use(injectScreenChart).mount("#app");
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment