Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
zgsgs committed Nov 28, 2022
2 parents 4d94e04 + 7553a32 commit 1a76657
Show file tree
Hide file tree
Showing 16 changed files with 439 additions and 11 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"pnpm",
"soybeanjs",
"swiper",
"Uncapitalize",
"unocss",
"unplugin",
"vditor",
Expand Down
2 changes: 2 additions & 0 deletions packages/admin/src/components/business/VChart/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './type'
export * as VChart from './v-charts.vue'
72 changes: 72 additions & 0 deletions packages/admin/src/components/business/VChart/options/bar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { BarECOption } from '../type'
const options: BarECOption = {
legend: {},
tooltip: {},
xAxis: {
type: 'category',
axisLine: {
lineStyle: {
// type: "dashed",
color: '#C8D0D7',
},
},
axisTick: {
show: false,
},
axisLabel: {
color: '#7D8292',
},
},
yAxis: {
type: 'value',
min: 'dataMin',
alignTicks: true,
splitLine: {
show: true,
lineStyle: {
color: '#C8D0D7',
type: 'dashed',
},
},
axisLine: {
lineStyle: {
color: '#7D8292',
},
},
},
grid: {
left: 60,
bottom: '8%',
top: '20%',
},
series: [
{
type: 'bar',
barWidth: 20,
itemStyle: {
color: {
type: 'linear',
x: 0,
x2: 0,
y: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: '#62A5FF', // 0% 处的颜色
},
{
offset: 1,
color: '#3365FF', // 100% 处的颜色
},
],
},
},
// label: {
// show: true,
// position: "top"
// }
},
],
}
export default options
Empty file.
Empty file.
95 changes: 95 additions & 0 deletions packages/admin/src/components/business/VChart/type.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@

/*
* @Description:
* @Version: 2.0
* @Autor: GC
* @Date: 2022-03-02 10:21:33
* @LastEditors: GC
* @LastEditTime: 2022-06-02 17:45:48
*/
// import * as echarts from 'echarts/core';
import * as echarts from 'echarts'
import { XAXisComponentOption, YAXisComponentOption } from 'echarts';

import { ECElementEvent, SelectChangedPayload, HighlightPayload, } from 'echarts/types/src/util/types'

import {
TitleComponentOption,
TooltipComponentOption,
GridComponentOption,
DatasetComponentOption,
AriaComponentOption,
AxisPointerComponentOption,
LegendComponentOption,
} from 'echarts/components';// 组件
import {
// 系列类型的定义后缀都为 SeriesOption
BarSeriesOption,
LineSeriesOption,
PieSeriesOption,
FunnelSeriesOption,
GaugeSeriesOption
} from 'echarts/charts';

type Options = LineECOption | BarECOption | PieECOption | FunnelOption

type BaseOptionType = XAXisComponentOption | YAXisComponentOption | TitleComponentOption | TooltipComponentOption | LegendComponentOption | GridComponentOption

type BaseOption = echarts.ComposeOption<BaseOptionType>

type LineECOption = echarts.ComposeOption<LineSeriesOption | BaseOptionType>

type BarECOption = echarts.ComposeOption<BarSeriesOption | BaseOptionType>

type PieECOption = echarts.ComposeOption<PieSeriesOption | BaseOptionType>

type FunnelOption = echarts.ComposeOption<FunnelSeriesOption | BaseOptionType>

type GaugeECOption = echarts.ComposeOption<GaugeSeriesOption | GridComponentOption>

type EChartsOption = echarts.EChartsOption;

type ChartType = 'bar' | 'line' | 'pie' | 'gauge'

// echarts事件
declare namespace ChartsEvents {
// 鼠标事件类型
type MouseEventType = 'click' | 'dblclick' | 'mousedown' | 'mousemove' | 'mouseup' | 'mouseover' | 'mouseout' | 'globalout' | 'contextmenu' // 鼠标事件类型
type MouseEvents = {
[key in Exclude<MouseEventType,'globalout'|'contextmenu'> as `chart${Capitalize<key>}`] :ECElementEvent
}
// 其他的事件类型极参数
interface Events extends MouseEvents {
globalout:ECElementEvent,
contextmenu:ECElementEvent,
selectchanged: SelectChangedPayload;
highlight: HighlightPayload;
legendselected: { // 图例选中后的事件
type: 'legendselected',
// 选中的图例名称
name: string
// 所有图例的选中状态表
selected: {
[name: string]: boolean
}
};
// ... 其他类型的事件在这里定义
}


// echarts所有的事件类型
type EventType = keyof Events
}

export {
BaseOption,
ChartType,
LineECOption,
BarECOption,
Options,
PieECOption,
FunnelOption,
GaugeECOption,
EChartsOption,
ChartsEvents
}
137 changes: 137 additions & 0 deletions packages/admin/src/components/business/VChart/useCharts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import * as echarts from 'echarts/core'
import type { Ref, ShallowRef } from 'vue'

import {
DatasetComponent,
GridComponent,
LegendComponent,
TitleComponent,
TooltipComponent,
TransformComponent,
} from 'echarts/components'

import { BarChart, GaugeChart, LineChart, PieChart } from 'echarts/charts'

import { LabelLayout, UniversalTransition } from 'echarts/features'
import { CanvasRenderer } from 'echarts/renderers'
import type { ChartType } from './type'

const optionsModules = import.meta.glob<{ default: echarts.EChartsCoreOption }>('./options/**.ts')

interface ChartHookOption {
type?: Ref<ChartType>
el: ShallowRef<HTMLElement>
}

type timeIdType = undefined | ReturnType<typeof setTimeout>

/**
* 视口变化时echart图表自适应调整
*/
class ChartsResize {
#charts = new Set<echarts.ECharts>() // 缓存已经创建的图表实例
#timeId: timeIdType = undefined
constructor() {
window.addEventListener('resize', this.handleResize.bind(this)) // 视口变化时调整图表
}

getCharts() {
return [...this.#charts]
}

handleResize() {
clearTimeout(this.#timeId)
this.#timeId = setTimeout(() => {
this.#charts.forEach((chart) => {
chart.resize()
})
}, 500)
}

add(chart: echarts.ECharts) {
this.#charts.add(chart)
}

remove(chart: echarts.ECharts) {
this.#charts.delete(chart)
}

removeListener() {
window.removeEventListener('resize', this.handleResize)
}
}

export const chartsResize = new ChartsResize()

export const useCharts = ({ type, el }: ChartHookOption) => {
echarts.use([
BarChart,
LineChart,
BarChart,
PieChart,
GaugeChart,
TitleComponent,
LegendComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
LabelLayout,
UniversalTransition,
CanvasRenderer,
])
const charts = shallowRef<echarts.ECharts>()

let options!: echarts.EChartsCoreOption
const getOptions = async () => {
const typeName = type?.value || 'bar'
const moduleKey = `./options/${typeName}.ts`
const { default: defaultOption } = await optionsModules[moduleKey]()
return defaultOption
}

const setOptions = (opt: echarts.EChartsCoreOption) => {
if (!charts.value)
return
charts.value.setOption(opt)
}

/**
* 初始化事件,按需绑定事件
*/
const attrs = useAttrs()
const initEvent = () => {
Object.keys(attrs).forEach((attrKey) => {
if (attrKey.startsWith('on')) {
const cb = attrs[attrKey]
attrKey = attrKey.replace(/^on(Chart)?/, '').replace(/^\S/, s => s.toLowerCase()) // onClickChart -> ClickChart -> clickChart
typeof cb === 'function' && charts.value?.on(attrKey, cb as () => void)
}
})
}

const initChart = async () => {
charts.value = echarts.init(el.value)
options = await getOptions()
charts.value.setOption(options)
chartsResize.add(charts.value) // 将图表实例添加到缓存中
initEvent() // 添加事件支持
}

onBeforeUnmount(() => {
if (!charts.value)
return
chartsResize.remove(charts.value) // 移除缓存
})

return {
charts,
setOptions,
initChart,
initEvent,
}
}

export const chartsOptions = <T extends echarts.EChartsCoreOption>(option: T) => shallowReactive<T>(option)

export * from './type.d'
68 changes: 68 additions & 0 deletions packages/admin/src/components/business/VChart/v-charts.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<script lang="ts" setup>
import type { PropType } from 'vue'
import type * as echarts from 'echarts/core'
import type { ChartType, ChartsEvents } from './useCharts'
import { useCharts } from './useCharts'
/**
* echarts事件类型
* 截至目前,vue3类型声明参数必须是以下内容之一,暂不支持外部引入类型参数
* 1. 类型字面量
* 2. 在同一文件中的接口或类型字面量的引用
* 文档中有说明:https://cn.vuejs.org/api/sfc-script-setup.html#typescript-only-features
*/
interface EventEmitsType {
<T extends ChartsEvents.EventType>(e: `${T}`, event: ChartsEvents.Events[Uncapitalize<T>]): void
}
const props = defineProps({
type: {
type: String as PropType<ChartType>,
default: 'bar',
},
option: {
type: Object as PropType<echarts.EChartsCoreOption>,
default: () => ({}),
},
})
// 定义事件,提供ts支持,在组件使用时可获得友好提示
defineEmits<EventEmitsType>()
defineOptions({
name: 'VCharts',
})
const { type, option } = toRefs(props)
const chartRef = shallowRef()
const { charts, setOptions, initChart } = useCharts({ type, el: chartRef })
onMounted(async () => {
await initChart()
setOptions(option.value)
})
watch(
option,
() => {
setOptions(option.value)
},
{
deep: true,
},
)
defineExpose({
$charts: charts,
})
</script>

<template>
<div ref="chartRef" class="v-charts" />
</template>

<style lang="scss" scoped>
.v-charts {
width: 100%;
height: 100%;
min-height: 200px;
}
</style>
9 changes: 4 additions & 5 deletions packages/admin/src/hooks/business/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import useCountDown from './useCountDown'
import useSmsCode from './useSmsCode'
import useImageVerify from './useImageVerify'

export { useCountDown, useSmsCode, useImageVerify }
export * from './useCountDown'
export * from './useSmsCode'
export * from './useImageVerify'
export * from './useStaticDeviceByUserObject'

0 comments on commit 1a76657

Please sign in to comment.