change pie chart
This commit is contained in:
Generated
+2
-47
@@ -4623,22 +4623,6 @@
|
||||
"safer-buffer": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"echarts": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.0.1.tgz",
|
||||
"integrity": "sha512-JYn22Dolt2esY2jEzUsw1OxbobuW67oGjIoTjZO3rW89SWkfJ4kbrmC2OW9JjsBrD1rdkmaWBuZZ2HgmThyxJw==",
|
||||
"requires": {
|
||||
"tslib": "2.0.3",
|
||||
"zrender": "5.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
@@ -7074,7 +7058,8 @@
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.defaultsdeep": {
|
||||
"version": "4.6.1",
|
||||
@@ -9376,11 +9361,6 @@
|
||||
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
|
||||
"dev": true
|
||||
},
|
||||
"resize-detector": {
|
||||
"version": "0.1.10",
|
||||
"resolved": "https://registry.npmjs.org/resize-detector/-/resize-detector-0.1.10.tgz",
|
||||
"integrity": "sha512-iLcXC8A6Fb0DfA+TRiywrK/0A22bFqkhntjMJMEzXDA4XkcEkfwpNbv7W8iewUiD0xYIaeiXOfiEehTqGKsUFw=="
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.17.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
|
||||
@@ -11116,16 +11096,6 @@
|
||||
"clipboard": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"vue-echarts": {
|
||||
"version": "5.0.0-beta.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-echarts/-/vue-echarts-5.0.0-beta.0.tgz",
|
||||
"integrity": "sha512-QZFKGXDAYFQo+F20REpzcdLx79nsl4kOorJRpN+08aYq4YiIlmtWss1Lxadm7Fo+NYyWm8nnT+h4xHv3uqWIDQ==",
|
||||
"requires": {
|
||||
"core-js": "^3.4.4",
|
||||
"lodash": "^4.17.15",
|
||||
"resize-detector": "^0.1.10"
|
||||
}
|
||||
},
|
||||
"vue-eslint-parser": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.1.1.tgz",
|
||||
@@ -12184,21 +12154,6 @@
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"zrender": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.0.3.tgz",
|
||||
"integrity": "sha512-TVcN2IMdo7je3GEq/E4CER4AGBe/n50/izILdupppyHf/hVHuiXCRliqdu8+32Z1OmGg6RfKt5qQlkX+bOtU0g==",
|
||||
"requires": {
|
||||
"tslib": "2.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
"cbor-js": "^0.1.0",
|
||||
"core-js": "^3.6.5",
|
||||
"crypto-js": "^4.0.0",
|
||||
"echarts": "^5.0.1",
|
||||
"framework7": "^5.7.14",
|
||||
"framework7-icons": "^3.0.1",
|
||||
"framework7-vue": "^5.7.14",
|
||||
@@ -22,7 +21,6 @@
|
||||
"ua-parser-js": "^0.7.22",
|
||||
"vue": "^2.6.12",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
"vue-echarts": "^5.0.0-beta.0",
|
||||
"vue-i18n": "^8.22.1",
|
||||
"vue-moment": "^4.1.0",
|
||||
"vue-pincode-input": "^0.4.0",
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<div class="pie-chart-container">
|
||||
<svg class="pie-chart" :viewBox="`${-diameter} ${-diameter} ${diameter * 2} ${diameter * 2}`">
|
||||
<circle class="pie-chart-background" cx="0" cy="0" :r="diameter"></circle>
|
||||
<circle class="pie-chart-item"
|
||||
v-for="(item, idx) in validItems" :key="idx"
|
||||
fill="transparent"
|
||||
cx="0" cy="0"
|
||||
:r="diameter / 2"
|
||||
:stroke="item.color | defaultIconColor('var(--default-icon-color)')"
|
||||
:stroke-width="diameter"
|
||||
:stroke-dasharray="item | itemStrokeDash(circumference)"
|
||||
:stroke-dashoffset="item | itemDashOffset(validItems, circumference, firstValidItemDashOffset)">
|
||||
</circle>
|
||||
<circle class="pie-chart-text-background"
|
||||
cx="0" cy="0"
|
||||
:style="{ '--pie-chart-text-background': centerTextBackground ? centerTextBackground : 'var(--f7-theme-color)' }"
|
||||
:r="diameter / 2.5"
|
||||
v-if="showCenterText"/>
|
||||
<g class="pie-chart-text-group" v-if="showCenterText">
|
||||
<slot></slot>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: [
|
||||
'items',
|
||||
'nameField',
|
||||
'valueField',
|
||||
'colorField',
|
||||
'minValidPercent',
|
||||
'showCenterText',
|
||||
'centerTextBackground',
|
||||
],
|
||||
data: function () {
|
||||
const diameter = 100;
|
||||
|
||||
return {
|
||||
diameter: diameter,
|
||||
circumference: diameter * Math.PI
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
validItems: function () {
|
||||
let totalValidValue = 0;
|
||||
|
||||
for (let i = 0; i < this.items.length; i++) {
|
||||
const item = this.items[i];
|
||||
|
||||
if (item[this.valueField] && item[this.valueField] > 0) {
|
||||
totalValidValue += item[this.valueField];
|
||||
}
|
||||
}
|
||||
|
||||
const validItems = [];
|
||||
|
||||
for (let i = 0; i < this.items.length; i++) {
|
||||
const item = this.items[i];
|
||||
|
||||
if (item[this.valueField] && item[this.valueField] > 0 &&
|
||||
(!this.minValidPercent || item[this.valueField] / totalValidValue > this.minValidPercent)) {
|
||||
validItems.push({
|
||||
name: item[this.nameField],
|
||||
value: item[this.valueField],
|
||||
percent: item[this.valueField] / totalValidValue,
|
||||
color: item[this.colorField] ? item[this.colorField] : 'c8c8c8'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return validItems;
|
||||
},
|
||||
totalValidValue: function () {
|
||||
let totalValidValue = 0;
|
||||
|
||||
for (let i = 0; i < this.validItems.length; i++) {
|
||||
totalValidValue += this.validItems[i].value;
|
||||
}
|
||||
|
||||
return totalValidValue;
|
||||
},
|
||||
firstValidItemDashOffset: function () {
|
||||
if (this.totalValidValue <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.validItems.length; i++) {
|
||||
const item = this.validItems[i];
|
||||
|
||||
if (item.percent > 0) {
|
||||
return -this.circumference * (1 - item.percent) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
itemStrokeDash(item, circumference) {
|
||||
const length = item.percent * circumference;
|
||||
return `${length} ${circumference - length}`;
|
||||
},
|
||||
itemDashOffset(item, items, circumference, offset) {
|
||||
let allPreviousPercent = 0;
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const curItem = items[i];
|
||||
|
||||
if (curItem === item) {
|
||||
break;
|
||||
}
|
||||
|
||||
allPreviousPercent += curItem.percent;
|
||||
}
|
||||
|
||||
if (offset) {
|
||||
offset += circumference / 4;
|
||||
} else {
|
||||
offset = circumference / 4;
|
||||
}
|
||||
|
||||
if (allPreviousPercent <= 0) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
const allPreviousLength = allPreviousPercent * circumference;
|
||||
return circumference - allPreviousLength + offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pie-chart-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.pie-chart {
|
||||
margin: 24px;
|
||||
}
|
||||
|
||||
.pie-chart-background {
|
||||
fill: #f0f0f0;
|
||||
}
|
||||
|
||||
.theme-dark .pie-chart-background {
|
||||
fill: #181818;
|
||||
}
|
||||
|
||||
.pie-chart-text-background {
|
||||
--pie-chart-text-background: var(--f7-theme-color);
|
||||
fill: var(--pie-chart-text-background);
|
||||
}
|
||||
|
||||
.pie-chart-text-group {
|
||||
font-size: 0.7em;
|
||||
-moz-transform: translateY(-1em);
|
||||
-ms-transform: translateY(-1em);
|
||||
-webkit-transform: translateY(-1em);
|
||||
transform: translateY(-1em);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,11 @@
|
||||
import colorConstants from '../consts/color.js';
|
||||
|
||||
export default function (color, defaultColor) {
|
||||
if (color && color !== colorConstants.defaultColor) {
|
||||
color = '#' + color;
|
||||
} else {
|
||||
color = defaultColor;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
+4
-10
@@ -2,7 +2,6 @@ import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
|
||||
import VueI18n from 'vue-i18n';
|
||||
import ECharts from 'vue-echarts';
|
||||
import PincodeInput from 'vue-pincode-input';
|
||||
import VueMoment from 'vue-moment';
|
||||
import VueClipboard from 'vue-clipboard2';
|
||||
@@ -46,14 +45,6 @@ import 'framework7-icons';
|
||||
|
||||
import 'line-awesome/dist/line-awesome/css/line-awesome.css';
|
||||
|
||||
import 'echarts/lib/chart/line';
|
||||
import 'echarts/lib/chart/pie';
|
||||
import 'echarts/lib/chart/bar';
|
||||
import 'echarts/lib/component/title';
|
||||
import 'echarts/lib/component/legend';
|
||||
import 'echarts/lib/component/grid';
|
||||
import 'echarts/lib/component/tooltip';
|
||||
|
||||
import { getAllLanguages, getLanguage, getDefaultLanguage, getI18nOptions, getLocalizedError, getLocalizedErrorParameters } from './lib/i18n.js';
|
||||
import api from './consts/api.js';
|
||||
import datetime from './consts/datetime.js';
|
||||
@@ -79,6 +70,7 @@ import itemFieldContentFilter from './filters/itemFieldContent.js';
|
||||
import currencyFilter from './filters/currency.js';
|
||||
import iconFilter from './filters/icon.js';
|
||||
import iconStyleFilter from './filters/iconStyle.js';
|
||||
import defaultIconColorFilter from './filters/defaultIconColor.js';
|
||||
import accountIconFilter from './filters/accountIcon.js';
|
||||
import accountIconStyleFilter from './filters/accountIconStyle.js';
|
||||
import categoryIconFilter from './filters/categoryIcon.js';
|
||||
@@ -86,6 +78,7 @@ import categoryIconStyleFilter from './filters/categoryIconStyle.js';
|
||||
import tokenDeviceFilter from './filters/tokenDevice.js';
|
||||
import tokenIconFilter from './filters/tokenIcon.js';
|
||||
|
||||
import PieChart from "./components/mobile/PieChart.vue";
|
||||
import PasswordInputSheet from "./components/mobile/PasswordInputSheet.vue";
|
||||
import PasscodeInputSheet from "./components/mobile/PasscodeInputSheet.vue";
|
||||
import PinCodeInputSheet from "./components/mobile/PinCodeInputSheet.vue";
|
||||
@@ -136,8 +129,8 @@ Vue.use(VueI18n);
|
||||
Vue.use(VueMoment, { moment });
|
||||
Vue.use(VueClipboard);
|
||||
|
||||
Vue.component('v-chart', ECharts);
|
||||
Vue.component('PincodeInput', PincodeInput);
|
||||
Vue.component('PieChart', PieChart);
|
||||
Vue.component('PasswordInputSheet', PasswordInputSheet);
|
||||
Vue.component('PasscodeInputSheet', PasscodeInputSheet);
|
||||
Vue.component('PinCodeInputSheet', PinCodeInputSheet);
|
||||
@@ -296,6 +289,7 @@ Vue.filter('itemFieldContent', (value, fieldName, defaultValue, translate) => it
|
||||
Vue.filter('currency', (value, currencyCode) => currencyFilter({ i18n }, value, currencyCode));
|
||||
Vue.filter('icon', (value, iconType) => iconFilter(value, iconType));
|
||||
Vue.filter('iconStyle', (value, iconType, defaultColor) => iconStyleFilter(value, iconType, defaultColor));
|
||||
Vue.filter('defaultIconColor', (value, defaultColor) => defaultIconColorFilter(value, defaultColor));
|
||||
Vue.filter('accountIcon', (value) => accountIconFilter(value));
|
||||
Vue.filter('accountIconStyle', (value, defaultColor) => accountIconStyleFilter(value, defaultColor));
|
||||
Vue.filter('categoryIcon', (value) => categoryIconFilter(value));
|
||||
|
||||
@@ -22,8 +22,30 @@
|
||||
</f7-popover>
|
||||
|
||||
<f7-card v-if="query.chartType === $constants.statistics.allChartTypes.Pie">
|
||||
<f7-card-content class="no-safe-areas chart-container" :padding="false">
|
||||
<v-chart :options="chartData" v-if="chartData" />
|
||||
<f7-card-content class="pie-chart-container" :padding="false">
|
||||
<pie-chart
|
||||
:items="[{value: 60, color: '7c7c7f'}, {value: 20, color: 'a5a5aa'}, {value: 20, color: 'c5c5c9'}]"
|
||||
:show-center-text="true"
|
||||
value-field="value"
|
||||
color-field="color"
|
||||
center-text-background="#cccccc"
|
||||
v-if="loading"
|
||||
></pie-chart>
|
||||
<pie-chart
|
||||
:items="statisticsData.items"
|
||||
:min-valid-percent="0.0001"
|
||||
:show-center-text="true"
|
||||
name-field="name"
|
||||
value-field="totalAmount"
|
||||
color-field="color"
|
||||
v-else-if="!loading"
|
||||
>
|
||||
<text class="statistics-pie-chart-total-amount-title" v-if="query.chartDataType === $constants.statistics.allChartDataTypes.ExpenseByAccount || query.chartDataType === $constants.statistics.allChartDataTypes.ExpenseByPrimaryCategory || query.chartDataType === $constants.statistics.allChartDataTypes.ExpenseBySecondaryCategory">{{ $t('Total Expense') }}</text>
|
||||
<text class="statistics-pie-chart-total-amount-title" v-if="query.chartDataType === $constants.statistics.allChartDataTypes.IncomeByAccount || query.chartDataType === $constants.statistics.allChartDataTypes.IncomeByPrimaryCategory || query.chartDataType === $constants.statistics.allChartDataTypes.IncomeBySecondaryCategory">{{ $t('Total Income') }}</text>
|
||||
<text class="statistics-pie-chart-total-amount-value">
|
||||
{{ statisticsData.totalAmount | currency(defaultCurrency) }}
|
||||
</text>
|
||||
</pie-chart>
|
||||
</f7-card-content>
|
||||
</f7-card>
|
||||
|
||||
@@ -361,97 +383,6 @@ export default {
|
||||
totalAmount: allAmount,
|
||||
items: allStatisticsItems
|
||||
};
|
||||
},
|
||||
chartData() {
|
||||
const self = this;
|
||||
|
||||
if (!self.$store.state.transactionStatistics ||
|
||||
!self.$store.state.transactionStatistics.items ||
|
||||
!self.$store.state.transactionStatistics.items.length) {
|
||||
return self.skeletonChart();
|
||||
}
|
||||
|
||||
const allData = [];
|
||||
|
||||
for (let i = 0; i < this.statisticsData.items.length; i++) {
|
||||
const data = this.statisticsData.items[i];
|
||||
|
||||
allData.push({
|
||||
name: data.name,
|
||||
value: data.totalAmount / 100,
|
||||
itemStyle: {
|
||||
color: `#${data.color}`
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const chartData = {
|
||||
label: {
|
||||
show: true,
|
||||
overflow: 'truncate',
|
||||
align: 'left',
|
||||
formatter: params => {
|
||||
return `${params.name} ${self.$options.filters.currency(params.value * 100, self.defaultCurrency)}`;
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: allData,
|
||||
}
|
||||
],
|
||||
animation: false
|
||||
};
|
||||
|
||||
if (this.query.chartType === this.$constants.statistics.allChartTypes.Bar) {
|
||||
return this.$utilities.copyObjectTo({
|
||||
grid: {
|
||||
left: 30,
|
||||
top: 30,
|
||||
right: 50,
|
||||
bottom: 50
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
type: 'bar'
|
||||
}]
|
||||
}, chartData);
|
||||
} else {
|
||||
return this.$utilities.copyObjectTo({
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
series: [{
|
||||
type: 'pie',
|
||||
label: {
|
||||
position: 'inside'
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}]
|
||||
}, chartData);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -608,64 +539,6 @@ export default {
|
||||
const displayEndTime = this.$utilities.formatUnixTime(query.endTime, this.$t(startTimeYear !== endTimeYear ? 'format.date.short' : 'format.date.shortMonthDay'));
|
||||
|
||||
return `${displayStartTime} ~ ${displayEndTime}`;
|
||||
},
|
||||
skeletonChart() {
|
||||
const skeletonChartData = {
|
||||
series: [{
|
||||
data: [{
|
||||
value: 20,
|
||||
itemStyle: {
|
||||
color: '#7c7c7f'
|
||||
}
|
||||
},{
|
||||
value: 20,
|
||||
itemStyle: {
|
||||
color: '#a5a5aa'
|
||||
}
|
||||
},{
|
||||
value: 60,
|
||||
itemStyle: {
|
||||
color: '#c5c5c9'
|
||||
}
|
||||
}]
|
||||
}],
|
||||
animation: false
|
||||
};
|
||||
|
||||
if (this.query.chartType === this.$constants.statistics.allChartTypes.Bar) {
|
||||
return this.$utilities.copyObjectTo({
|
||||
grid: {
|
||||
left: 30,
|
||||
top: 30,
|
||||
right: 50,
|
||||
bottom: 50
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category'
|
||||
},
|
||||
series: [{
|
||||
type: 'bar'
|
||||
}]
|
||||
}, skeletonChartData);
|
||||
} else {
|
||||
return this.$utilities.copyObjectTo({
|
||||
series: [{
|
||||
type: 'pie',
|
||||
label: {
|
||||
position: 'inside'
|
||||
}
|
||||
}]
|
||||
}, skeletonChartData);
|
||||
}
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
@@ -704,6 +577,24 @@ export default {
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.statistics-pie-chart-total-amount-title {
|
||||
fill: #f8f8f8;
|
||||
text-anchor: middle;
|
||||
-moz-transform: translateY(0.35em);
|
||||
-ms-transform: translateY(0.35em);
|
||||
-webkit-transform: translateY(0.35em);
|
||||
transform: translateY(0.35em);
|
||||
}
|
||||
|
||||
.statistics-pie-chart-total-amount-value {
|
||||
fill: #fff;
|
||||
text-anchor: middle;
|
||||
-moz-transform: translateY(2em);
|
||||
-ms-transform: translateY(2em);
|
||||
-webkit-transform: translateY(2em);
|
||||
transform: translateY(2em);
|
||||
}
|
||||
|
||||
.statistics-list-item-overview {
|
||||
padding-top: 12px;
|
||||
margin-bottom: 6px;
|
||||
@@ -751,13 +642,4 @@ export default {
|
||||
.theme-dark .statistics-percent-line .progressbar {
|
||||
--f7-progressbar-bg-color: #161616;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.chart-container .echarts {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user