diff --git a/src/components/custom/count-to.vue b/src/components/custom/count-to.vue
new file mode 100644
index 000000000..7d8dcc950
--- /dev/null
+++ b/src/components/custom/count-to.vue
@@ -0,0 +1,75 @@
+
+
+
+ {{ value }}
+
+
+
diff --git a/src/hooks/chart/use-echarts.ts b/src/hooks/chart/use-echarts.ts
index b74bfd1cc..f05913300 100644
--- a/src/hooks/chart/use-echarts.ts
+++ b/src/hooks/chart/use-echarts.ts
@@ -1,5 +1,4 @@
-import { effectScope, nextTick, onScopeDispose, ref, watch } from 'vue';
-import type { ComputedRef, Ref } from 'vue';
+import { computed, effectScope, nextTick, onScopeDispose, ref, watch } from 'vue';
import * as echarts from 'echarts/core';
import { BarChart, GaugeChart, LineChart, PictorialBarChart, PieChart, RadarChart, ScatterChart } from 'echarts/charts';
import type {
@@ -31,6 +30,7 @@ import type {
import { LabelLayout, UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
import { useElementSize } from '@vueuse/core';
+import { useThemeStore } from '@/store/modules/theme';
export type ECOption = echarts.ComposeOption<
| BarSeriesOption
@@ -70,58 +70,95 @@ echarts.use([
interface ChartHooks {
onRender?: (chart: echarts.ECharts) => void | Promise;
+ onUpdated?: (chart: echarts.ECharts) => void | Promise;
onDestroy?: (chart: echarts.ECharts) => void | Promise;
}
/**
* use echarts
*
- * @param options echarts options
+ * @param optionsFactory echarts options factory function
* @param darkMode dark mode
*/
-export function useEcharts(options: ECOption, darkMode: Ref | ComputedRef, hooks?: ChartHooks) {
+export function useEcharts(
+ optionsFactory: () => T,
+ hooks: ChartHooks = {
+ onRender(chart) {
+ chart.showLoading();
+ },
+ onUpdated(chart) {
+ chart.hideLoading();
+ }
+ }
+) {
const scope = effectScope();
- const domRef = ref(null);
+ const themeStore = useThemeStore();
+ const darkMode = computed(() => themeStore.darkMode);
+ const domRef = ref(null);
const initialSize = { width: 0, height: 0 };
const { width, height } = useElementSize(domRef, initialSize);
let chart: echarts.ECharts | null = null;
+ const chartOptions: T = optionsFactory();
+ /**
+ * whether can render chart
+ *
+ * when domRef is ready and initialSize is valid
+ */
function canRender() {
- return initialSize.width > 0 && initialSize.height > 0;
+ return domRef.value && initialSize.width > 0 && initialSize.height > 0;
}
+ /** is chart rendered */
function isRendered() {
return Boolean(domRef.value && chart);
}
- function setOptions(opts: ECOption) {
+ /**
+ * update chart options
+ *
+ * @param callback callback function
+ */
+ async function updateOptions(callback: (opts: T, optsFactory: () => T) => ECOption = () => chartOptions) {
+ if (!isRendered()) return;
+
+ const updatedOpts = callback(chartOptions, optionsFactory);
+
+ Object.assign(chartOptions, updatedOpts);
+
if (isRendered()) {
chart?.clear();
- chart?.setOption({ ...opts, backgroundColor: 'transparent' });
}
+
+ chart?.setOption({ ...updatedOpts, backgroundColor: 'transparent' });
+
+ await hooks?.onUpdated?.(chart!);
}
+ /** render chart */
async function render() {
- if (domRef.value) {
+ if (!isRendered()) {
const chartTheme = darkMode.value ? 'dark' : 'light';
await nextTick();
chart = echarts.init(domRef.value, chartTheme);
- setOptions(options);
+ chart.setOption({ ...chartOptions, backgroundColor: 'transparent' });
await hooks?.onRender?.(chart);
}
}
+ /** resize chart */
function resize() {
chart?.resize();
}
+ /** destroy chart */
async function destroy() {
if (!chart) return;
@@ -130,16 +167,18 @@ export function useEcharts(options: ECOption, darkMode: Ref | ComputedR
chart = null;
}
+ /** change chart theme */
async function changeTheme() {
await destroy();
await render();
+ await hooks?.onUpdated?.(chart!);
}
/**
* render chart by size
*
- * @param w
- * @param h
+ * @param w width
+ * @param h height
*/
async function renderChartBySize(w: number, h: number) {
initialSize.width = w;
@@ -152,14 +191,13 @@ export function useEcharts(options: ECOption, darkMode: Ref | ComputedR
return;
}
- // render chart
- if (!isRendered()) {
- await render();
- return;
+ // resize chart
+ if (isRendered()) {
+ resize();
}
- // resize chart
- resize();
+ // render chart
+ await render();
}
scope.run(() => {
@@ -179,6 +217,6 @@ export function useEcharts(options: ECOption, darkMode: Ref | ComputedR
return {
domRef,
- setOptions
+ updateOptions
};
}
diff --git a/src/locales/langs/en-us.ts b/src/locales/langs/en-us.ts
index 2a83b11bb..b8117628a 100644
--- a/src/locales/langs/en-us.ts
+++ b/src/locales/langs/en-us.ts
@@ -184,6 +184,18 @@ const local: App.I18n.Schema = {
},
prdDep: 'Production Dependency',
devDep: 'Development Dependency'
+ },
+ home: {
+ downloadCount: 'Download Count',
+ registerCount: 'Register Count',
+ schedule: 'Work and rest Schedule',
+ study: 'Study',
+ work: 'Work',
+ rest: 'Rest',
+ entertainment: 'Entertainment',
+ visit: 'Visit Count',
+ amount: 'Amount',
+ trade: 'Trade Count'
}
},
form: {
diff --git a/src/locales/langs/zh-cn.ts b/src/locales/langs/zh-cn.ts
index 70c9637bf..f8eae156d 100644
--- a/src/locales/langs/zh-cn.ts
+++ b/src/locales/langs/zh-cn.ts
@@ -184,6 +184,18 @@ const local: App.I18n.Schema = {
},
prdDep: '生产依赖',
devDep: '开发依赖'
+ },
+ home: {
+ downloadCount: '下载量',
+ registerCount: '注册量',
+ schedule: '作息安排',
+ study: '学习',
+ work: '工作',
+ rest: '休息',
+ entertainment: '娱乐',
+ visit: '访问量',
+ amount: '成交额',
+ trade: '成交量'
}
},
form: {
diff --git a/src/typings/app.d.ts b/src/typings/app.d.ts
index e132a8256..d949792ba 100644
--- a/src/typings/app.d.ts
+++ b/src/typings/app.d.ts
@@ -369,6 +369,18 @@ declare namespace App {
prdDep: string;
devDep: string;
};
+ home: {
+ downloadCount: string;
+ registerCount: string;
+ schedule: string;
+ study: string;
+ work: string;
+ rest: string;
+ entertainment: string;
+ visit: string;
+ amount: string;
+ trade: string;
+ };
};
form: {
userName: FormMsg;
diff --git a/src/typings/components.d.ts b/src/typings/components.d.ts
index df75b79f1..d63e270a3 100644
--- a/src/typings/components.d.ts
+++ b/src/typings/components.d.ts
@@ -10,6 +10,7 @@ declare module 'vue' {
AppProvider: typeof import('./../components/common/app-provider.vue')['default']
BetterScroll: typeof import('./../components/custom/better-scroll.vue')['default']
ButtonIcon: typeof import('./../components/custom/button-icon.vue')['default']
+ CountTo: typeof import('./../components/custom/count-to.vue')['default']
DarkModeContainer: typeof import('./../components/common/dark-mode-container.vue')['default']
ExceptionBase: typeof import('./../components/common/exception-base.vue')['default']
FullScreen: typeof import('./../components/common/full-screen.vue')['default']
@@ -35,6 +36,9 @@ declare module 'vue' {
NDropdown: typeof import('naive-ui')['NDropdown']
NForm: typeof import('naive-ui')['NForm']
NFormItem: typeof import('naive-ui')['NFormItem']
+ NGi: typeof import('naive-ui')['NGi']
+ NGrid: typeof import('naive-ui')['NGrid']
+ NGridItem: typeof import('naive-ui')['NGridItem']
NInput: typeof import('naive-ui')['NInput']
NInputNumber: typeof import('naive-ui')['NInputNumber']
NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
diff --git a/src/views/home/components/gradient-bg.vue b/src/views/home/components/gradient-bg.vue
new file mode 100644
index 000000000..a5840846f
--- /dev/null
+++ b/src/views/home/components/gradient-bg.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/views/home/index.vue b/src/views/home/index.vue
index e830d7f46..f7d70e29b 100644
--- a/src/views/home/index.vue
+++ b/src/views/home/index.vue
@@ -1,9 +1,317 @@
-
+
-
-
-
+
+
+
+
+
+
Dashboard
+
Overview Of Lasted Month
+
+
+
+
Current Month Earnings
+
+
+
+
Current Month Sales
+
Last Month Summary
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+