diff --git a/package-lock.json b/package-lock.json index 1fcb5c5d..3d1bdc4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "clipboard": "^2.0.11", "crypto-js": "^4.1.1", "dom7": "^4.0.6", + "echarts": "^5.4.2", "framework7": "^8.1.0", "framework7-icons": "^5.0.5", "framework7-vue": "^8.1.0", @@ -30,6 +31,7 @@ "swiper": "^9.3.2", "ua-parser-js": "^1.0.35", "vue": "^3.3.4", + "vue-echarts": "^6.6.0", "vue-i18n": "^9.2.2", "vue-router": "^4.2.2", "vue3-perfect-scrollbar": "^1.6.1", @@ -4177,6 +4179,15 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/echarts": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.4.2.tgz", + "integrity": "sha512-2W3vw3oI2tWJdyAz+b8DuWS0nfXtSDqlDmqgin/lfzbkB01cuMEN66KWBlmur3YMp5nEDEEt5s23pllnAzB4EA==", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.4.3" + } + }, "node_modules/ejs": { "version": "3.1.9", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", @@ -7376,6 +7387,11 @@ "node": ">=0.10.0" } }, + "node_modules/resize-detector": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/resize-detector/-/resize-detector-0.3.0.tgz", + "integrity": "sha512-R/tCuvuOHQ8o2boRP6vgx8hXCCy87H1eY9V5imBYeVNyNVpuL9ciReSccLj2gDcax9+2weXy3bc8Vv+NRXeEvQ==" + }, "node_modules/resolve": { "version": "1.22.2", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", @@ -7942,6 +7958,11 @@ "punycode": "^2.1.0" } }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -8244,6 +8265,51 @@ "@vue/shared": "3.3.4" } }, + "node_modules/vue-echarts": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/vue-echarts/-/vue-echarts-6.6.0.tgz", + "integrity": "sha512-PpXDe1wKKzOJTLM8RM4GJxpRi+9eiC0YUJR7i44/AAa0oDatvNctJcZlfq/bUV4KV+HDsCeQzaB6JocrXEy9LA==", + "hasInstallScript": true, + "dependencies": { + "resize-detector": "^0.3.0", + "vue-demi": "^0.13.2" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.5", + "echarts": "^5.4.1", + "vue": "^2.6.12 || ^3.1.1" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-echarts/node_modules/vue-demi": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz", + "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/vue-eslint-parser": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.0.tgz", @@ -8811,6 +8877,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zrender": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.4.3.tgz", + "integrity": "sha512-DRUM4ZLnoaT0PBVvGBDO9oWIDBKFdAVieNWxWwK0niYzJCMwGchRk21/hsE+RKkIveH3XHCyvXcJDkgLVvfizQ==", + "dependencies": { + "tslib": "2.3.0" + } } } } diff --git a/package.json b/package.json index cb1c2c31..f116e83e 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "clipboard": "^2.0.11", "crypto-js": "^4.1.1", "dom7": "^4.0.6", + "echarts": "^5.4.2", "framework7": "^8.1.0", "framework7-icons": "^5.0.5", "framework7-vue": "^8.1.0", @@ -39,6 +40,7 @@ "swiper": "^9.3.2", "ua-parser-js": "^1.0.35", "vue": "^3.3.4", + "vue-echarts": "^6.6.0", "vue-i18n": "^9.2.2", "vue-router": "^4.2.2", "vue3-perfect-scrollbar": "^1.6.1", diff --git a/src/components/desktop/DateRangeSelectionDialog.vue b/src/components/desktop/DateRangeSelectionDialog.vue new file mode 100644 index 00000000..204f6615 --- /dev/null +++ b/src/components/desktop/DateRangeSelectionDialog.vue @@ -0,0 +1,194 @@ + + + diff --git a/src/components/desktop/ItemIcon.vue b/src/components/desktop/ItemIcon.vue index 87ccd0f1..71668ace 100644 --- a/src/components/desktop/ItemIcon.vue +++ b/src/components/desktop/ItemIcon.vue @@ -27,6 +27,7 @@ export default { 'color', 'defaultColor', 'additionalColorAttr', + 'size', 'hiddenStatus' ], data() { @@ -102,6 +103,10 @@ export default { ret[additionalColorAttr] = color; } + if (this.size) { + ret['font-size'] = this.size; + } + return ret; }, getCategoryIconStyle(color, defaultColor, additionalColorAttr) { @@ -119,6 +124,10 @@ export default { ret[additionalColorAttr] = color; } + if (this.size) { + ret['font-size'] = this.size; + } + return ret; }, getDefaultIconStyle(color, defaultColor, additionalColorAttr) { @@ -136,6 +145,10 @@ export default { ret[additionalColorAttr] = color; } + if (this.size) { + ret['font-size'] = this.size; + } + return ret; } } diff --git a/src/components/desktop/PieChart.vue b/src/components/desktop/PieChart.vue new file mode 100644 index 00000000..4bc9fc58 --- /dev/null +++ b/src/components/desktop/PieChart.vue @@ -0,0 +1,265 @@ + + + + + diff --git a/src/desktop-main.js b/src/desktop-main.js index 0a135327..4a15532e 100644 --- a/src/desktop-main.js +++ b/src/desktop-main.js @@ -8,6 +8,8 @@ import { VApp } from 'vuetify/components/VApp'; import { VAvatar } from 'vuetify/components/VAvatar'; import { VAutocomplete } from 'vuetify/components/VAutocomplete'; import { VBtn } from 'vuetify/components/VBtn'; +import { VBtnGroup } from 'vuetify/components/VBtnGroup'; +import { VBtnToggle } from 'vuetify/components/VBtnToggle'; import { VCard, VCardActions, VCardItem, VCardSubtitle, VCardText, VCardTitle } from 'vuetify/components/VCard'; import { VChip } from 'vuetify/components/VChip'; import { VDialog } from 'vuetify/components/VDialog'; @@ -41,6 +43,15 @@ import { VWindow, VWindowItem } from 'vuetify/components/VWindow'; import { aliases, mdi } from 'vuetify/iconsets/mdi-svg'; import 'vuetify/styles'; +import * as echarts from 'echarts/core'; +import { CanvasRenderer } from 'echarts/renderers'; +import { PieChart } from 'echarts/charts'; +import { + TooltipComponent, + LegendComponent, +} from 'echarts/components'; +import VChart from 'vue-echarts'; + import 'line-awesome/dist/line-awesome/css/line-awesome.css'; import { PerfectScrollbar } from 'vue3-perfect-scrollbar'; @@ -66,6 +77,8 @@ import AmountInput from '@/components/desktop/AmountInput.vue'; import StepsBar from '@/components/desktop/StepsBar.vue'; import ConfirmDialog from '@/components/desktop/ConfirmDialog.vue'; import SnackBar from '@/components/desktop/SnackBar.vue'; +import PieChartComponent from '@/components/desktop/PieChart.vue'; +import DateRangeSelectionDialog from '@/components/desktop/DateRangeSelectionDialog.vue'; import SwitchToMobileDialog from '@/components/desktop/SwitchToMobileDialog.vue'; import '@/styles/desktop/template/base/libs/vuetify/_index.scss'; @@ -88,6 +101,8 @@ const vuetify = createVuetify({ VAvatar, VAutocomplete, VBtn, + VBtnGroup, + VBtnToggle, VCard, VCardActions, VCardItem, @@ -338,11 +353,19 @@ const vuetify = createVuetify({ } }); +echarts.use([ + CanvasRenderer, + PieChart, + TooltipComponent, + LegendComponent +]); + app.use(pinia); app.use(i18n); app.use(vuetify); app.use(router); +app.component('VChart', VChart); app.component('PerfectScrollbar', PerfectScrollbar); app.component('VueDatePicker', VueDatePicker); @@ -353,6 +376,8 @@ app.component('AmountInput', AmountInput); app.component('StepsBar', StepsBar); app.component('ConfirmDialog', ConfirmDialog); app.component('SnackBar', SnackBar); +app.component('PieChart', PieChartComponent); +app.component('DateRangeSelectionDialog', DateRangeSelectionDialog); app.component('SwitchToMobileDialog', SwitchToMobileDialog); app.config.globalProperties.$version = getVersion(); diff --git a/src/styles/desktop/global.scss b/src/styles/desktop/global.scss index 26bf3a4e..3f051056 100644 --- a/src/styles/desktop/global.scss +++ b/src/styles/desktop/global.scss @@ -29,6 +29,14 @@ input[type=number] { } /** custom class **/ +:root { + --default-icon-color: var(--v-theme-on-surface); +} + +:root .dark { + --default-icon-color: var(--v-theme-on-surface); +} + .pin-codes-input { --ebk-pin-code-input-height: 56px; --ebk-pin-code-input-gap: 12px; @@ -83,3 +91,48 @@ input[type=number] { min-width: 16px; height: 1rem; } + +/** Replacing the default style of @vuepic/vue-datepicker **/ +.dp__theme_light { + --dp-primary-color: #c67e48; +} + +.dp__theme_dark { + --dp-primary-color: #c67e48; +} + +/** Fix @vuepic/vue-datepicker style issue **/ +.dp__main.dp__flex_display { + flex-direction: column +} + +.dp__main .dp__preset_range { + white-space: inherit; +} + +.dp__main .dp__menu_inner { + padding-top: 0; + padding-bottom: 0; +} + +.dp__main .dp__menu_inner .dp__month_year_row > button { + width: inherit; +} + +.dp__main .dp__menu_inner .dp__month_year_row > button.dp__button { + width: 100%; +} + +.dp__main .dp__menu_inner .dp__month_year_row .dp__month_year_wrap > button { + line-height: inherit; +} + +.dp__main .dp__calendar .dp__calendar_item { + display: flex; + justify-content: center; + flex: 1; +} + +.dp__main .dp__calendar .dp__calendar_item > .dp__cell_inner { + width: 100%; +} diff --git a/src/views/desktop/statistics/TransactionPage.vue b/src/views/desktop/statistics/TransactionPage.vue index 54eee68a..2a2f78f4 100644 --- a/src/views/desktop/statistics/TransactionPage.vue +++ b/src/views/desktop/statistics/TransactionPage.vue @@ -1,13 +1,660 @@ + + diff --git a/third-patry-dependencies.json b/third-patry-dependencies.json index 4e9a41f9..6095d69c 100644 --- a/third-patry-dependencies.json +++ b/third-patry-dependencies.json @@ -170,6 +170,18 @@ "url": "https://github.com/nolimits4web/skeleton-elements", "licenseUrl": "https://github.com/nolimits4web/skeleton-elements/blob/v4.0.1/LICENSE" }, + { + "name": "Apache ECharts", + "copyright": "Copyright © 2017-2023, The Apache Software Foundation Apache ECharts, ECharts, Apache, the Apache feather, and the Apache ECharts project logo are either registered trademarks or trademarks of the Apache Software Foundation.", + "url": "https://echarts.apache.org/", + "licenseUrl": "https://github.com/apache/echarts/blob/5.4.2/LICENSE" + }, + { + "name": "vue-echarts", + "copyright": "Copyright (c) 2016-present GU Yiling & ECOMFE", + "url": "https://github.com/ecomfe/vue-echarts", + "licenseUrl": "https://github.com/ecomfe/vue-echarts/blob/v6.6.0/LICENSE" + }, { "name": "vue3-perfect-scrollbar", "copyright": "Copyright (c) 2018 Adam", diff --git a/vite.config.js b/vite.config.js index 84862a34..8f569da0 100644 --- a/vite.config.js +++ b/vite.config.js @@ -142,7 +142,7 @@ export default defineConfig(async () => { return 'moment'; } else if (/[\\/]node_modules[\\/](dom7|framework7.*|skeleton-elements|swiper)[\\/]/i.test(id)) { return 'vendor-mobile'; - } else if (/[\\/]node_modules[\\/](vuetify|vue-router|vue3-perfect-scrollbar|@mdi.*)[\\/]/i.test(id)) { + } else if (/[\\/]node_modules[\\/](vuetify|vue-router|vue3-perfect-scrollbar|@mdi.*|echarts|vue-echarts)[\\/]/i.test(id)) { return 'vendor-desktop'; } else if (/[\\/]node_modules[\\/]/i.test(id)) { return 'vendor-common';