mirror of
				https://github.com/soybeanjs/soybean-admin.git
				synced 2025-11-04 15:53:43 +08:00 
			
		
		
		
	feat(projects): 引入echarts替换antvG2plot
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -28,3 +28,5 @@ stats.html
 | 
			
		||||
*.njsproj
 | 
			
		||||
*.sln
 | 
			
		||||
*.sw?
 | 
			
		||||
 | 
			
		||||
/src/typings/components.d.ts
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,7 @@ Soybean Admin  是一个基于 Vue3、Vite、TypeScript、Naive UI 的免费中
 | 
			
		||||
 | 
			
		||||
## 开发计划
 | 
			
		||||
 | 
			
		||||
- [ ] 引入ECharts替换AntV G2Plot
 | 
			
		||||
- [x] 引入ECharts替换AntV G2Plot
 | 
			
		||||
- [ ] 多页签:同一页面支持多个Tab(应用场景:不同query参数的详情页面在不同tab展示)
 | 
			
		||||
- [ ] 最近功能的有关文档更新
 | 
			
		||||
- [ ] 性能优化(优化递归函数)
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,6 @@
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@antv/g2plot": "^2.4.16",
 | 
			
		||||
    "@better-scroll/core": "^2.4.2",
 | 
			
		||||
    "@soybeanjs/vue-admin-layout": "^1.0.3",
 | 
			
		||||
    "@soybeanjs/vue-admin-tab": "^1.0.1",
 | 
			
		||||
@@ -36,6 +35,7 @@
 | 
			
		||||
    "colord": "^2.9.2",
 | 
			
		||||
    "crypto-js": "^4.1.1",
 | 
			
		||||
    "dayjs": "^1.11.1",
 | 
			
		||||
    "echarts": "^5.3.2",
 | 
			
		||||
    "form-data": "^4.0.0",
 | 
			
		||||
    "lodash-es": "^4.17.21",
 | 
			
		||||
    "naive-ui": "^2.28.2",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										622
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										622
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,5 +1,8 @@
 | 
			
		||||
import useEcharts from './useEcharts';
 | 
			
		||||
import useCountDown from './useCountDown';
 | 
			
		||||
import useSmsCode from './useSmsCode';
 | 
			
		||||
import useImageVerify from './useImageVerify';
 | 
			
		||||
 | 
			
		||||
export { useCountDown, useSmsCode, useImageVerify };
 | 
			
		||||
export { useEcharts, useCountDown, useSmsCode, useImageVerify };
 | 
			
		||||
 | 
			
		||||
export * from './useEcharts';
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										136
									
								
								src/hooks/business/useEcharts.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/hooks/business/useEcharts.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
			
		||||
import { ref, watch, nextTick, onUnmounted, computed } from 'vue';
 | 
			
		||||
import type { Ref, ComputedRef } from 'vue';
 | 
			
		||||
import * as echarts from 'echarts/core';
 | 
			
		||||
import { BarChart, LineChart, PieChart } from 'echarts/charts';
 | 
			
		||||
import type { BarSeriesOption, LineSeriesOption, PieSeriesOption } from 'echarts/charts';
 | 
			
		||||
import {
 | 
			
		||||
  TitleComponent,
 | 
			
		||||
  LegendComponent,
 | 
			
		||||
  TooltipComponent,
 | 
			
		||||
  GridComponent,
 | 
			
		||||
  DatasetComponent,
 | 
			
		||||
  TransformComponent,
 | 
			
		||||
  ToolboxComponent
 | 
			
		||||
} from 'echarts/components';
 | 
			
		||||
import type {
 | 
			
		||||
  TitleComponentOption,
 | 
			
		||||
  LegendComponentOption,
 | 
			
		||||
  TooltipComponentOption,
 | 
			
		||||
  GridComponentOption,
 | 
			
		||||
  ToolboxComponentOption,
 | 
			
		||||
  DatasetComponentOption
 | 
			
		||||
} from 'echarts/components';
 | 
			
		||||
import { LabelLayout, UniversalTransition } from 'echarts/features';
 | 
			
		||||
import { CanvasRenderer } from 'echarts/renderers';
 | 
			
		||||
import { useElementSize } from '@vueuse/core';
 | 
			
		||||
 | 
			
		||||
export type ECOption = echarts.ComposeOption<
 | 
			
		||||
  | BarSeriesOption
 | 
			
		||||
  | LineSeriesOption
 | 
			
		||||
  | PieSeriesOption
 | 
			
		||||
  | TitleComponentOption
 | 
			
		||||
  | LegendComponentOption
 | 
			
		||||
  | TooltipComponentOption
 | 
			
		||||
  | GridComponentOption
 | 
			
		||||
  | ToolboxComponentOption
 | 
			
		||||
  | DatasetComponentOption
 | 
			
		||||
>;
 | 
			
		||||
 | 
			
		||||
echarts.use([
 | 
			
		||||
  TitleComponent,
 | 
			
		||||
  LegendComponent,
 | 
			
		||||
  TooltipComponent,
 | 
			
		||||
  GridComponent,
 | 
			
		||||
  DatasetComponent,
 | 
			
		||||
  TransformComponent,
 | 
			
		||||
  ToolboxComponent,
 | 
			
		||||
  BarChart,
 | 
			
		||||
  LineChart,
 | 
			
		||||
  PieChart,
 | 
			
		||||
  LabelLayout,
 | 
			
		||||
  UniversalTransition,
 | 
			
		||||
  CanvasRenderer
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Echarts hooks函数
 | 
			
		||||
 * @param options - 图表配置
 | 
			
		||||
 * @param renderFun - 图表函数(例如:图表监听函数)
 | 
			
		||||
 */
 | 
			
		||||
export default function useEcharts(
 | 
			
		||||
  options: Ref<ECOption> | ComputedRef<ECOption>,
 | 
			
		||||
  darkMode?: ComputedRef<boolean>,
 | 
			
		||||
  renderFun?: (chartInstance: echarts.ECharts) => void
 | 
			
		||||
) {
 | 
			
		||||
  let chart: echarts.ECharts | null = null;
 | 
			
		||||
  const domRef = ref<HTMLElement>();
 | 
			
		||||
  const initialSize = { width: 0, height: 0 };
 | 
			
		||||
  const { width, height } = useElementSize(domRef, initialSize);
 | 
			
		||||
 | 
			
		||||
  function canRender() {
 | 
			
		||||
    return initialSize.width > 0 && initialSize.height > 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function isRendered() {
 | 
			
		||||
    return Boolean(domRef.value && chart);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function update(updateOptions: ECOption) {
 | 
			
		||||
    if (isRendered()) {
 | 
			
		||||
      chart!.setOption({ ...updateOptions, backgroundColor: 'transparent' });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async function render() {
 | 
			
		||||
    if (domRef.value) {
 | 
			
		||||
      const theme = darkMode?.value ? 'dark' : 'light';
 | 
			
		||||
      await nextTick();
 | 
			
		||||
      chart = echarts.init(domRef.value, theme);
 | 
			
		||||
      if (renderFun) {
 | 
			
		||||
        renderFun(chart);
 | 
			
		||||
      }
 | 
			
		||||
      update(options.value);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function resize() {
 | 
			
		||||
    chart?.resize();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function destroy() {
 | 
			
		||||
    chart?.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function updateTheme() {
 | 
			
		||||
    destroy();
 | 
			
		||||
    render();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  watch([width, height], ([newWidth, newHeight]) => {
 | 
			
		||||
    initialSize.width = newWidth;
 | 
			
		||||
    initialSize.height = newHeight;
 | 
			
		||||
    if (canRender()) {
 | 
			
		||||
      if (!isRendered()) {
 | 
			
		||||
        render();
 | 
			
		||||
      } else {
 | 
			
		||||
        resize();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  watch(options, newValue => {
 | 
			
		||||
    update(newValue);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  watch(darkMode || computed(() => false), () => {
 | 
			
		||||
    updateTheme();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  onUnmounted(() => {
 | 
			
		||||
    destroy();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    domRef
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										105
									
								
								src/typings/components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										105
									
								
								src/typings/components.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -1,105 +0,0 @@
 | 
			
		||||
// generated by unplugin-vue-components
 | 
			
		||||
// We suggest you to commit this file into source control
 | 
			
		||||
// Read more: https://github.com/vuejs/vue-next/pull/3399
 | 
			
		||||
import '@vue/runtime-core'
 | 
			
		||||
 | 
			
		||||
declare module '@vue/runtime-core' {
 | 
			
		||||
  export interface GlobalComponents {
 | 
			
		||||
    BetterScroll: typeof import('./../components/custom/BetterScroll.vue')['default']
 | 
			
		||||
    CountTo: typeof import('./../components/custom/CountTo.vue')['default']
 | 
			
		||||
    DarkModeContainer: typeof import('./../components/common/DarkModeContainer.vue')['default']
 | 
			
		||||
    DarkModeSwitch: typeof import('./../components/common/DarkModeSwitch.vue')['default']
 | 
			
		||||
    GithubLink: typeof import('./../components/custom/GithubLink.vue')['default']
 | 
			
		||||
    HoverContainer: typeof import('./../components/common/HoverContainer.vue')['default']
 | 
			
		||||
    IconAntDesignCloseOutlined: typeof import('~icons/ant-design/close-outlined')['default']
 | 
			
		||||
    IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
 | 
			
		||||
    IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
 | 
			
		||||
    IconCustomActivity: typeof import('~icons/custom/activity')['default']
 | 
			
		||||
    IconCustomAvatar: typeof import('~icons/custom/avatar')['default']
 | 
			
		||||
    IconCustomBanner: typeof import('~icons/custom/banner')['default']
 | 
			
		||||
    IconCustomCast: typeof import('~icons/custom/cast')['default']
 | 
			
		||||
    IconCustomEmptyData: typeof import('~icons/custom/empty-data')['default']
 | 
			
		||||
    IconCustomLogo: typeof import('~icons/custom/logo')['default']
 | 
			
		||||
    IconCustomLogoFill: typeof import('~icons/custom/logo-fill')['default']
 | 
			
		||||
    IconCustomNetworkError: typeof import('~icons/custom/network-error')['default']
 | 
			
		||||
    IconCustomNoPermission: typeof import('~icons/custom/no-permission')['default']
 | 
			
		||||
    IconCustomNotFound: typeof import('~icons/custom/not-found')['default']
 | 
			
		||||
    IconCustomServiceError: typeof import('~icons/custom/service-error')['default']
 | 
			
		||||
    IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
 | 
			
		||||
    IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
 | 
			
		||||
    IconIcBaselineDoNotDisturb: typeof import('~icons/ic/baseline-do-not-disturb')['default']
 | 
			
		||||
    IconIcOutlineCheck: typeof import('~icons/ic/outline-check')['default']
 | 
			
		||||
    IconIcRoundHdrAuto: typeof import('~icons/ic/round-hdr-auto')['default']
 | 
			
		||||
    IconLineMdMenuFoldLeft: typeof import('~icons/line-md/menu-fold-left')['default']
 | 
			
		||||
    IconLineMdMenuUnfoldLeft: typeof import('~icons/line-md/menu-unfold-left')['default']
 | 
			
		||||
    IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
 | 
			
		||||
    IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
 | 
			
		||||
    IconMdiClose: typeof import('~icons/mdi/close')['default']
 | 
			
		||||
    IconMdiGithub: typeof import('~icons/mdi/github')['default']
 | 
			
		||||
    IconMdiMoonWaningCrescent: typeof import('~icons/mdi/moon-waning-crescent')['default']
 | 
			
		||||
    IconMdiPin: typeof import('~icons/mdi/pin')['default']
 | 
			
		||||
    IconMdiPinOff: typeof import('~icons/mdi/pin-off')['default']
 | 
			
		||||
    IconMdiRefresh: typeof import('~icons/mdi/refresh')['default']
 | 
			
		||||
    IconMdiWechat: typeof import('~icons/mdi/wechat')['default']
 | 
			
		||||
    IconMdiWhiteBalanceSunny: typeof import('~icons/mdi/white-balance-sunny')['default']
 | 
			
		||||
    IconPhCaretDoubleLeftBold: typeof import('~icons/ph/caret-double-left-bold')['default']
 | 
			
		||||
    IconPhCaretDoubleRightBold: typeof import('~icons/ph/caret-double-right-bold')['default']
 | 
			
		||||
    IconSelect: typeof import('./../components/custom/IconSelect.vue')['default']
 | 
			
		||||
    IconUilSearch: typeof import('~icons/uil/search')['default']
 | 
			
		||||
    ImageVerify: typeof import('./../components/custom/ImageVerify.vue')['default']
 | 
			
		||||
    LoadingEmptyWrapper: typeof import('./../components/business/LoadingEmptyWrapper.vue')['default']
 | 
			
		||||
    LoginAgreement: typeof import('./../components/business/LoginAgreement.vue')['default']
 | 
			
		||||
    NaiveProvider: typeof import('./../components/common/NaiveProvider.vue')['default']
 | 
			
		||||
    NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
 | 
			
		||||
    NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
 | 
			
		||||
    NButton: typeof import('naive-ui')['NButton']
 | 
			
		||||
    NCard: typeof import('naive-ui')['NCard']
 | 
			
		||||
    NCheckbox: typeof import('naive-ui')['NCheckbox']
 | 
			
		||||
    NColorPicker: typeof import('naive-ui')['NColorPicker']
 | 
			
		||||
    NConfigProvider: typeof import('naive-ui')['NConfigProvider']
 | 
			
		||||
    NDataTable: typeof import('naive-ui')['NDataTable']
 | 
			
		||||
    NDescriptions: typeof import('naive-ui')['NDescriptions']
 | 
			
		||||
    NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem']
 | 
			
		||||
    NDialogProvider: typeof import('naive-ui')['NDialogProvider']
 | 
			
		||||
    NDivider: typeof import('naive-ui')['NDivider']
 | 
			
		||||
    NDrawer: typeof import('naive-ui')['NDrawer']
 | 
			
		||||
    NDrawerContent: typeof import('naive-ui')['NDrawerContent']
 | 
			
		||||
    NDropdown: typeof import('naive-ui')['NDropdown']
 | 
			
		||||
    NEmpty: typeof import('naive-ui')['NEmpty']
 | 
			
		||||
    NForm: typeof import('naive-ui')['NForm']
 | 
			
		||||
    NFormItem: typeof import('naive-ui')['NFormItem']
 | 
			
		||||
    NGradientText: typeof import('naive-ui')['NGradientText']
 | 
			
		||||
    NGrid: typeof import('naive-ui')['NGrid']
 | 
			
		||||
    NGridItem: typeof import('naive-ui')['NGridItem']
 | 
			
		||||
    NInput: typeof import('naive-ui')['NInput']
 | 
			
		||||
    NInputGroup: typeof import('naive-ui')['NInputGroup']
 | 
			
		||||
    NInputNumber: typeof import('naive-ui')['NInputNumber']
 | 
			
		||||
    NList: typeof import('naive-ui')['NList']
 | 
			
		||||
    NListItem: typeof import('naive-ui')['NListItem']
 | 
			
		||||
    NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
 | 
			
		||||
    NMenu: typeof import('naive-ui')['NMenu']
 | 
			
		||||
    NMessageProvider: typeof import('naive-ui')['NMessageProvider']
 | 
			
		||||
    NModal: typeof import('naive-ui')['NModal']
 | 
			
		||||
    NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
 | 
			
		||||
    NPopover: typeof import('naive-ui')['NPopover']
 | 
			
		||||
    NScrollbar: typeof import('naive-ui')['NScrollbar']
 | 
			
		||||
    NSelect: typeof import('naive-ui')['NSelect']
 | 
			
		||||
    NSpace: typeof import('naive-ui')['NSpace']
 | 
			
		||||
    NSpin: typeof import('naive-ui')['NSpin']
 | 
			
		||||
    NStatistic: typeof import('naive-ui')['NStatistic']
 | 
			
		||||
    NSwitch: typeof import('naive-ui')['NSwitch']
 | 
			
		||||
    NTabPane: typeof import('naive-ui')['NTabPane']
 | 
			
		||||
    NTabs: typeof import('naive-ui')['NTabs']
 | 
			
		||||
    NTag: typeof import('naive-ui')['NTag']
 | 
			
		||||
    NThing: typeof import('naive-ui')['NThing']
 | 
			
		||||
    NTimeline: typeof import('naive-ui')['NTimeline']
 | 
			
		||||
    NTimelineItem: typeof import('naive-ui')['NTimelineItem']
 | 
			
		||||
    NTooltip: typeof import('naive-ui')['NTooltip']
 | 
			
		||||
    RouterLink: typeof import('vue-router')['RouterLink']
 | 
			
		||||
    RouterView: typeof import('vue-router')['RouterView']
 | 
			
		||||
    SystemLogo: typeof import('./../components/common/SystemLogo.vue')['default']
 | 
			
		||||
    WebSiteLink: typeof import('./../components/custom/WebSiteLink.vue')['default']
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export {}
 | 
			
		||||
@@ -1,152 +0,0 @@
 | 
			
		||||
[
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/1",
 | 
			
		||||
		"type": "下载量",
 | 
			
		||||
		"value": 4623
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/1",
 | 
			
		||||
		"type": "注册数",
 | 
			
		||||
		"value": 2208
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/2",
 | 
			
		||||
		"type": "下载量",
 | 
			
		||||
		"value": 6145
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/2",
 | 
			
		||||
		"type": "注册数",
 | 
			
		||||
		"value": 2016
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/3",
 | 
			
		||||
		"type": "下载量",
 | 
			
		||||
		"value": 508
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/3",
 | 
			
		||||
		"type": "注册数",
 | 
			
		||||
		"value": 2916
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/4",
 | 
			
		||||
		"type": "下载量",
 | 
			
		||||
		"value": 6268
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/4",
 | 
			
		||||
		"type": "注册数",
 | 
			
		||||
		"value": 4512
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/5",
 | 
			
		||||
		"type": "下载量",
 | 
			
		||||
		"value": 6411
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/5",
 | 
			
		||||
		"type": "注册数",
 | 
			
		||||
		"value": 8281
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/6",
 | 
			
		||||
		"type": "下载量",
 | 
			
		||||
		"value": 1890
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/6",
 | 
			
		||||
		"type": "注册数",
 | 
			
		||||
		"value": 2008
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/7",
 | 
			
		||||
		"type": "下载量",
 | 
			
		||||
		"value": 4251
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/7",
 | 
			
		||||
		"type": "注册数",
 | 
			
		||||
		"value": 1963
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/8",
 | 
			
		||||
		"type": "下载量",
 | 
			
		||||
		"value": 2978
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/8",
 | 
			
		||||
		"type": "注册数",
 | 
			
		||||
		"value": 2367
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/9",
 | 
			
		||||
		"type": "下载量",
 | 
			
		||||
		"value": 3880
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/9",
 | 
			
		||||
		"type": "注册数",
 | 
			
		||||
		"value": 2956
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/10",
 | 
			
		||||
		"type": "下载量",
 | 
			
		||||
		"value": 3606
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/10",
 | 
			
		||||
		"type": "注册数",
 | 
			
		||||
		"value": 678
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/11",
 | 
			
		||||
		"type": "下载量",
 | 
			
		||||
		"value": 4311
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/11",
 | 
			
		||||
		"type": "注册数",
 | 
			
		||||
		"value": 3188
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/12",
 | 
			
		||||
		"type": "下载量",
 | 
			
		||||
		"value": 4116
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/12",
 | 
			
		||||
		"type": "注册数",
 | 
			
		||||
		"value": 3491
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/13",
 | 
			
		||||
		"type": "下载量",
 | 
			
		||||
		"value": 6419
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/13",
 | 
			
		||||
		"type": "注册数",
 | 
			
		||||
		"value": 2852
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/14",
 | 
			
		||||
		"type": "下载量",
 | 
			
		||||
		"value": 1643
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/14",
 | 
			
		||||
		"type": "注册数",
 | 
			
		||||
		"value": 4788
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/15",
 | 
			
		||||
		"type": "下载量",
 | 
			
		||||
		"value": 445
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"date": "2021/10/15",
 | 
			
		||||
		"type": "注册数",
 | 
			
		||||
		"value": 4319
 | 
			
		||||
	}
 | 
			
		||||
]
 | 
			
		||||
@@ -31,99 +31,159 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref, onMounted } from 'vue';
 | 
			
		||||
import { Line, Pie } from '@antv/g2plot';
 | 
			
		||||
import data from './data.json';
 | 
			
		||||
import { ref, computed } from 'vue';
 | 
			
		||||
import { useThemeStore } from '@/store';
 | 
			
		||||
import { useEcharts, type ECOption } from '@/hooks';
 | 
			
		||||
 | 
			
		||||
const lineRef = ref<HTMLElement>();
 | 
			
		||||
const line = ref<Line>();
 | 
			
		||||
const pieRef = ref<HTMLElement>();
 | 
			
		||||
const pie = ref<Pie>();
 | 
			
		||||
const theme = useThemeStore();
 | 
			
		||||
 | 
			
		||||
function renderLineChart() {
 | 
			
		||||
  if (!lineRef.value) return;
 | 
			
		||||
  line.value = new Line(lineRef.value, {
 | 
			
		||||
    data,
 | 
			
		||||
    autoFit: true,
 | 
			
		||||
    xField: 'date',
 | 
			
		||||
    yField: 'value',
 | 
			
		||||
    seriesField: 'type',
 | 
			
		||||
    lineStyle: {
 | 
			
		||||
      lineWidth: 4
 | 
			
		||||
    },
 | 
			
		||||
    area: {
 | 
			
		||||
      style: {
 | 
			
		||||
        fill: 'l(270) 0:#ffffff 0.5:#7ec2f3 1:#1890ff'
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    smooth: true,
 | 
			
		||||
    animation: {
 | 
			
		||||
      appear: {
 | 
			
		||||
        animation: 'wave-in',
 | 
			
		||||
        duration: 2000
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  line.value.render();
 | 
			
		||||
}
 | 
			
		||||
function renderPieChart() {
 | 
			
		||||
  if (!pieRef.value) return;
 | 
			
		||||
  const pieData = [
 | 
			
		||||
    { type: '学习', value: 20 },
 | 
			
		||||
    { type: '娱乐', value: 10 },
 | 
			
		||||
    { type: '工作', value: 30 },
 | 
			
		||||
    { type: '休息', value: 40 }
 | 
			
		||||
  ];
 | 
			
		||||
  pie.value = new Pie(pieRef.value, {
 | 
			
		||||
    appendPadding: 10,
 | 
			
		||||
    data: pieData,
 | 
			
		||||
    angleField: 'value',
 | 
			
		||||
    colorField: 'type',
 | 
			
		||||
    radius: 0.8,
 | 
			
		||||
    innerRadius: 0.65,
 | 
			
		||||
    meta: {
 | 
			
		||||
      value: {
 | 
			
		||||
        formatter: v => `${v}%`
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    label: {
 | 
			
		||||
      type: 'inner',
 | 
			
		||||
      autoRotate: false,
 | 
			
		||||
      formatter: ({ percent }) => `${(percent * 100).toFixed(0)}%`
 | 
			
		||||
    },
 | 
			
		||||
    statistic: undefined,
 | 
			
		||||
    pieStyle: {
 | 
			
		||||
      radius: [20]
 | 
			
		||||
    },
 | 
			
		||||
    color: ['#025DF4', '#DB6BCF', '#2498D1', '#FF745A', '#007E99', '#FFA8A8', '#2391FF'],
 | 
			
		||||
    legend: {
 | 
			
		||||
      position: 'bottom'
 | 
			
		||||
    },
 | 
			
		||||
    interactions: [
 | 
			
		||||
      { type: 'element-selected' },
 | 
			
		||||
      { type: 'element-active' },
 | 
			
		||||
      {
 | 
			
		||||
        type: 'pie-statistic-active',
 | 
			
		||||
        cfg: {
 | 
			
		||||
          start: [
 | 
			
		||||
            { trigger: 'element:mouseenter', action: 'pie-statistic:change' },
 | 
			
		||||
            { trigger: 'legend-item:mouseenter', action: 'pie-statistic:change' }
 | 
			
		||||
          ],
 | 
			
		||||
          end: [
 | 
			
		||||
            { trigger: 'element:mouseleave', action: 'pie-statistic:reset' },
 | 
			
		||||
            { trigger: 'legend-item:mouseleave', action: 'pie-statistic:reset' }
 | 
			
		||||
          ]
 | 
			
		||||
const darkMode = computed(() => theme.darkMode);
 | 
			
		||||
 | 
			
		||||
const { domRef: lineRef } = useEcharts(
 | 
			
		||||
  ref<ECOption>({
 | 
			
		||||
    tooltip: {
 | 
			
		||||
      trigger: 'axis',
 | 
			
		||||
      axisPointer: {
 | 
			
		||||
        type: 'cross',
 | 
			
		||||
        label: {
 | 
			
		||||
          backgroundColor: '#6a7985'
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    legend: {
 | 
			
		||||
      data: ['下载量', '注册数']
 | 
			
		||||
    },
 | 
			
		||||
    grid: {
 | 
			
		||||
      left: '3%',
 | 
			
		||||
      right: '4%',
 | 
			
		||||
      bottom: '3%',
 | 
			
		||||
      containLabel: true
 | 
			
		||||
    },
 | 
			
		||||
    xAxis: [
 | 
			
		||||
      {
 | 
			
		||||
        type: 'category',
 | 
			
		||||
        boundaryGap: false,
 | 
			
		||||
        data: ['06:00', '08:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00', '24:00']
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    yAxis: [
 | 
			
		||||
      {
 | 
			
		||||
        type: 'value'
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    series: [
 | 
			
		||||
      {
 | 
			
		||||
        color: '#8e9dff',
 | 
			
		||||
        name: '下载量',
 | 
			
		||||
        type: 'line',
 | 
			
		||||
        smooth: true,
 | 
			
		||||
        stack: 'Total',
 | 
			
		||||
        areaStyle: {
 | 
			
		||||
          color: {
 | 
			
		||||
            type: 'linear',
 | 
			
		||||
            x: 0,
 | 
			
		||||
            y: 0,
 | 
			
		||||
            x2: 0,
 | 
			
		||||
            y2: 1,
 | 
			
		||||
            colorStops: [
 | 
			
		||||
              {
 | 
			
		||||
                offset: 0.25,
 | 
			
		||||
                color: '#8e9dff'
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                offset: 1,
 | 
			
		||||
                color: '#fff'
 | 
			
		||||
              }
 | 
			
		||||
            ]
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        emphasis: {
 | 
			
		||||
          focus: 'series'
 | 
			
		||||
        },
 | 
			
		||||
        data: [4623, 6145, 6268, 6411, 1890, 4251, 2978, 3880, 3606, 4311]
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        color: '#26deca',
 | 
			
		||||
        name: '注册数',
 | 
			
		||||
        type: 'line',
 | 
			
		||||
        smooth: true,
 | 
			
		||||
        stack: 'Total',
 | 
			
		||||
        areaStyle: {
 | 
			
		||||
          color: {
 | 
			
		||||
            type: 'linear',
 | 
			
		||||
            x: 0,
 | 
			
		||||
            y: 0,
 | 
			
		||||
            x2: 0,
 | 
			
		||||
            y2: 1,
 | 
			
		||||
            colorStops: [
 | 
			
		||||
              {
 | 
			
		||||
                offset: 0.25,
 | 
			
		||||
                color: '#26deca'
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                offset: 1,
 | 
			
		||||
                color: '#fff'
 | 
			
		||||
              }
 | 
			
		||||
            ]
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        emphasis: {
 | 
			
		||||
          focus: 'series'
 | 
			
		||||
        },
 | 
			
		||||
        data: [2208, 2016, 2916, 4512, 8281, 2008, 1963, 2367, 2956, 678]
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  });
 | 
			
		||||
  }),
 | 
			
		||||
  darkMode
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
  pie.value.render();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  renderLineChart();
 | 
			
		||||
  renderPieChart();
 | 
			
		||||
});
 | 
			
		||||
const { domRef: pieRef } = useEcharts(
 | 
			
		||||
  ref<ECOption>({
 | 
			
		||||
    tooltip: {
 | 
			
		||||
      trigger: 'item'
 | 
			
		||||
    },
 | 
			
		||||
    legend: {
 | 
			
		||||
      bottom: '1%',
 | 
			
		||||
      left: 'center',
 | 
			
		||||
      itemStyle: {
 | 
			
		||||
        borderWidth: 0
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    series: [
 | 
			
		||||
      {
 | 
			
		||||
        color: ['#5da8ff', '#8e9dff', '#fedc69', '#26deca'],
 | 
			
		||||
        name: '时间安排',
 | 
			
		||||
        type: 'pie',
 | 
			
		||||
        radius: ['45%', '75%'],
 | 
			
		||||
        avoidLabelOverlap: false,
 | 
			
		||||
        itemStyle: {
 | 
			
		||||
          borderRadius: 10,
 | 
			
		||||
          borderColor: '#fff',
 | 
			
		||||
          borderWidth: 1
 | 
			
		||||
        },
 | 
			
		||||
        label: {
 | 
			
		||||
          show: false,
 | 
			
		||||
          position: 'center'
 | 
			
		||||
        },
 | 
			
		||||
        emphasis: {
 | 
			
		||||
          label: {
 | 
			
		||||
            show: true,
 | 
			
		||||
            fontSize: '12'
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        labelLine: {
 | 
			
		||||
          show: false
 | 
			
		||||
        },
 | 
			
		||||
        data: [
 | 
			
		||||
          { value: 20, name: '学习' },
 | 
			
		||||
          { value: 10, name: '娱乐' },
 | 
			
		||||
          { value: 30, name: '工作' },
 | 
			
		||||
          { value: 40, name: '休息' }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  }),
 | 
			
		||||
  darkMode
 | 
			
		||||
);
 | 
			
		||||
</script>
 | 
			
		||||
<style></style>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user