change pie chart

This commit is contained in:
MaysWind
2021-01-30 23:31:04 +08:00
parent d443f2f6dd
commit 063dcde458
6 changed files with 225 additions and 219 deletions
+2 -47
View File
@@ -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=="
}
}
}
}
}
-2
View File
@@ -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",
+166
View File
@@ -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>
+11
View File
@@ -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
View File
@@ -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));
+42 -160
View File
@@ -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>