mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-19 17:24:26 +08:00
chart data source supports expense and income by primary category
This commit is contained in:
@@ -14,7 +14,7 @@ const allChartDataTypes = {
|
|||||||
IncomeBySecondaryCategory: 5
|
IncomeBySecondaryCategory: 5
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultChartDataType = allChartDataTypes.ExpenseBySecondaryCategory;
|
const defaultChartDataType = allChartDataTypes.ExpenseByPrimaryCategory;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
allChartTypes: allChartTypes,
|
allChartTypes: allChartTypes,
|
||||||
|
|||||||
+11
-1
@@ -771,8 +771,18 @@ const stores = {
|
|||||||
item.category = state.allTransactionCategoriesMap[item.categoryId];
|
item.category = state.allTransactionCategoriesMap[item.categoryId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.category && item.category.parentId !== '0') {
|
||||||
|
item.primaryCategory = state.allTransactionCategoriesMap[item.category.parentId];
|
||||||
|
} else {
|
||||||
|
item.primaryCategory = item.category;
|
||||||
|
}
|
||||||
|
|
||||||
if (item.account && item.account.currency !== defaultCurrency) {
|
if (item.account && item.account.currency !== defaultCurrency) {
|
||||||
item.amountInDefaultCurrency = getExchangedAmount(state)(item.amount, item.account.currency, defaultCurrency);
|
const amount = getExchangedAmount(state)(item.amount, item.account.currency, defaultCurrency);
|
||||||
|
|
||||||
|
if (utils.isNumber(amount)) {
|
||||||
|
item.amountInDefaultCurrency = Math.floor(amount);
|
||||||
|
}
|
||||||
} else if (item.account && item.account.currency === defaultCurrency) {
|
} else if (item.account && item.account.currency === defaultCurrency) {
|
||||||
item.amountInDefaultCurrency = item.amount;
|
item.amountInDefaultCurrency = item.amount;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -2,12 +2,25 @@
|
|||||||
<f7-page>
|
<f7-page>
|
||||||
<f7-navbar>
|
<f7-navbar>
|
||||||
<f7-nav-left :back-link="$t('Back')"></f7-nav-left>
|
<f7-nav-left :back-link="$t('Back')"></f7-nav-left>
|
||||||
<f7-nav-title :title="$t('Statistics')"></f7-nav-title>
|
<f7-nav-title>
|
||||||
<f7-nav-right>
|
<f7-link popover-open=".chart-data-type-popover-menu">
|
||||||
<f7-link icon-f7="ellipsis" @click="showMoreActionSheet = true"></f7-link>
|
<span>{{ query.chartDataType | chartDataTypeName(allChartDataTypes) | localized }}</span>
|
||||||
</f7-nav-right>
|
<f7-icon size="14px" :f7="showChartDataTypePopover ? 'arrowtriangle_up_fill' : 'arrowtriangle_down_fill'"></f7-icon>
|
||||||
|
</f7-link>
|
||||||
|
</f7-nav-title>
|
||||||
</f7-navbar>
|
</f7-navbar>
|
||||||
|
|
||||||
|
<f7-popover class="chart-data-type-popover-menu" :opened="showChartDataTypePopover"
|
||||||
|
@popover:open="showChartDataTypePopover = true" @popover:close="showChartDataTypePopover = false">
|
||||||
|
<f7-list>
|
||||||
|
<f7-list-item
|
||||||
|
v-for="dataType in allChartDataTypes" :key="dataType.type"
|
||||||
|
:title="$t(dataType.name)" @click="setChartDataType(dataType.type)">
|
||||||
|
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.chartDataType === dataType.type"></f7-icon>
|
||||||
|
</f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
</f7-popover>
|
||||||
|
|
||||||
<f7-card>
|
<f7-card>
|
||||||
<f7-card-content class="no-safe-areas chart-container" :padding="false">
|
<f7-card-content class="no-safe-areas chart-container" :padding="false">
|
||||||
<v-chart :options="chartData" v-if="chartData" />
|
<v-chart :options="chartData" v-if="chartData" />
|
||||||
@@ -31,22 +44,6 @@
|
|||||||
<span :class="{ 'tabbar-item-changed': query.chartType === $constants.statistics.allChartTypes.Bar }">{{ $t('Bar Chart') }}</span>
|
<span :class="{ 'tabbar-item-changed': query.chartType === $constants.statistics.allChartTypes.Bar }">{{ $t('Bar Chart') }}</span>
|
||||||
</f7-link>
|
</f7-link>
|
||||||
</f7-toolbar>
|
</f7-toolbar>
|
||||||
|
|
||||||
<f7-actions close-by-outside-click close-on-escape :opened="showMoreActionSheet" @actions:closed="showMoreActionSheet = false">
|
|
||||||
<f7-actions-group>
|
|
||||||
<f7-actions-label>{{ $t('Expense Chart') }}</f7-actions-label>
|
|
||||||
<f7-actions-button @click="setChartDataType($constants.statistics.allChartDataTypes.ExpenseByAccount)">{{ $t('Expense By Account') }}</f7-actions-button>
|
|
||||||
<f7-actions-button @click="setChartDataType($constants.statistics.allChartDataTypes.ExpenseBySecondaryCategory)">{{ $t('Expense By Secondary Category') }}</f7-actions-button>
|
|
||||||
</f7-actions-group>
|
|
||||||
<f7-actions-group>
|
|
||||||
<f7-actions-label>{{ $t('Income Chart') }}</f7-actions-label>
|
|
||||||
<f7-actions-button @click="setChartDataType($constants.statistics.allChartDataTypes.IncomeByAccount)">{{ $t('Income By Account') }}</f7-actions-button>
|
|
||||||
<f7-actions-button @click="setChartDataType($constants.statistics.allChartDataTypes.IncomeBySecondaryCategory)">{{ $t('Income By Secondary Category') }}</f7-actions-button>
|
|
||||||
</f7-actions-group>
|
|
||||||
<f7-actions-group>
|
|
||||||
<f7-actions-button bold close>{{ $t('Cancel') }}</f7-actions-button>
|
|
||||||
</f7-actions-group>
|
|
||||||
</f7-actions>
|
|
||||||
</f7-page>
|
</f7-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -55,7 +52,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: true,
|
loading: true,
|
||||||
showMoreActionSheet: false
|
showChartDataTypePopover: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -73,22 +70,44 @@ export default {
|
|||||||
query() {
|
query() {
|
||||||
return this.$store.state.transactionStatisticsFilter;
|
return this.$store.state.transactionStatisticsFilter;
|
||||||
},
|
},
|
||||||
chartData() {
|
allChartDataTypes() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
type: this.$constants.statistics.allChartDataTypes.ExpenseByAccount,
|
||||||
|
name: 'Expense By Account'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: this.$constants.statistics.allChartDataTypes.ExpenseByPrimaryCategory,
|
||||||
|
name: 'Expense By Primary Category'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: this.$constants.statistics.allChartDataTypes.ExpenseBySecondaryCategory,
|
||||||
|
name: 'Expense By Secondary Category'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: this.$constants.statistics.allChartDataTypes.IncomeByAccount,
|
||||||
|
name: 'Income By Account'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: this.$constants.statistics.allChartDataTypes.IncomeByPrimaryCategory,
|
||||||
|
name: 'Income By Primary Category'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: this.$constants.statistics.allChartDataTypes.IncomeBySecondaryCategory,
|
||||||
|
name: 'Income By Secondary Category'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
statisticsData() {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
if (!self.$store.state.transactionStatistics ||
|
|
||||||
!self.$store.state.transactionStatistics.items ||
|
|
||||||
!self.$store.state.transactionStatistics.items.length) {
|
|
||||||
return self.skeletonChart();
|
|
||||||
}
|
|
||||||
|
|
||||||
const combinedData = {};
|
const combinedData = {};
|
||||||
const allData = [];
|
|
||||||
|
let allAmount = 0;
|
||||||
|
|
||||||
for (let i = 0; i < self.$store.state.transactionStatistics.items.length; i++) {
|
for (let i = 0; i < self.$store.state.transactionStatistics.items.length; i++) {
|
||||||
const item = self.$store.state.transactionStatistics.items[i];
|
const item = self.$store.state.transactionStatistics.items[i];
|
||||||
|
|
||||||
if (!item.account || !item.category) {
|
if (!item.account || !item.primaryCategory || !item.category) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,59 +130,102 @@ export default {
|
|||||||
if (self.query.chartDataType === self.$constants.statistics.allChartDataTypes.ExpenseByAccount ||
|
if (self.query.chartDataType === self.$constants.statistics.allChartDataTypes.ExpenseByAccount ||
|
||||||
self.query.chartDataType === self.$constants.statistics.allChartDataTypes.IncomeByAccount) {
|
self.query.chartDataType === self.$constants.statistics.allChartDataTypes.IncomeByAccount) {
|
||||||
if (self.$utilities.isNumber(item.amountInDefaultCurrency)) {
|
if (self.$utilities.isNumber(item.amountInDefaultCurrency)) {
|
||||||
let data = combinedData[item.account.name];
|
let data = combinedData[item.account.id];
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
data.totalAmount += item.amountInDefaultCurrency;
|
data.totalAmount += item.amountInDefaultCurrency;
|
||||||
} else {
|
} else {
|
||||||
data = {
|
data = {
|
||||||
totalAmount: item.amountInDefaultCurrency,
|
name: item.account.name,
|
||||||
color: item.account.color || self.$constants.colors.defaultAccountColor
|
icon: item.account.icon || self.$constants.icons.defaultAccountIcon.icon,
|
||||||
|
color: item.account.color || self.$constants.colors.defaultAccountColor,
|
||||||
|
totalAmount: item.amountInDefaultCurrency
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
combinedData[item.account.name] = data;
|
allAmount += item.amountInDefaultCurrency;
|
||||||
|
combinedData[item.account.id] = data;
|
||||||
|
}
|
||||||
|
} else if (self.query.chartDataType === self.$constants.statistics.allChartDataTypes.ExpenseByPrimaryCategory ||
|
||||||
|
self.query.chartDataType === self.$constants.statistics.allChartDataTypes.IncomeByPrimaryCategory) {
|
||||||
|
if (self.$utilities.isNumber(item.amountInDefaultCurrency)) {
|
||||||
|
let data = combinedData[item.primaryCategory.id];
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
data.totalAmount += item.amountInDefaultCurrency;
|
||||||
|
} else {
|
||||||
|
data = {
|
||||||
|
name: item.primaryCategory.name,
|
||||||
|
icon: item.account.icon || self.$constants.icons.defaultCategoryIcon.icon,
|
||||||
|
color: item.primaryCategory.color || self.$constants.colors.defaultCategoryColor,
|
||||||
|
totalAmount: item.amountInDefaultCurrency
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allAmount += item.amountInDefaultCurrency;
|
||||||
|
combinedData[item.primaryCategory.id] = data;
|
||||||
}
|
}
|
||||||
} else if (self.query.chartDataType === self.$constants.statistics.allChartDataTypes.ExpenseBySecondaryCategory ||
|
} else if (self.query.chartDataType === self.$constants.statistics.allChartDataTypes.ExpenseBySecondaryCategory ||
|
||||||
self.query.chartDataType === self.$constants.statistics.allChartDataTypes.IncomeBySecondaryCategory) {
|
self.query.chartDataType === self.$constants.statistics.allChartDataTypes.IncomeBySecondaryCategory) {
|
||||||
if (self.$utilities.isNumber(item.amountInDefaultCurrency)) {
|
if (self.$utilities.isNumber(item.amountInDefaultCurrency)) {
|
||||||
let data = combinedData[item.category.name];
|
let data = combinedData[item.category.id];
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
data.totalAmount += item.amountInDefaultCurrency;
|
data.totalAmount += item.amountInDefaultCurrency;
|
||||||
} else {
|
} else {
|
||||||
data = {
|
data = {
|
||||||
totalAmount: item.amountInDefaultCurrency,
|
name: item.category.name,
|
||||||
color: item.category.color || self.$constants.colors.defaultCategoryColor
|
icon: item.account.icon || self.$constants.icons.defaultCategoryIcon.icon,
|
||||||
|
color: item.category.color || self.$constants.colors.defaultCategoryColor,
|
||||||
|
totalAmount: item.amountInDefaultCurrency
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
combinedData[item.category.name] = data;
|
allAmount += item.amountInDefaultCurrency;
|
||||||
|
combinedData[item.category.id] = data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let legendName in combinedData) {
|
const allStatisticsData = [];
|
||||||
if (!Object.prototype.hasOwnProperty.call(combinedData, legendName)) {
|
|
||||||
|
for (let id in combinedData) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(combinedData, id)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalAmount = Math.floor(combinedData[legendName].totalAmount) / 100;
|
const data = combinedData[id];
|
||||||
|
data.percent = data.totalAmount * 100 / allAmount;
|
||||||
|
|
||||||
const data = {
|
allStatisticsData.push(data);
|
||||||
name: legendName,
|
|
||||||
value: totalAmount,
|
|
||||||
itemStyle: {
|
|
||||||
color: `#${combinedData[legendName].color}`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
allData.push(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.query.chartType === self.$constants.statistics.allChartTypes.Bar) {
|
allStatisticsData.sort(function (data1, data2) {
|
||||||
allData.sort(function (data1, data2) {
|
return data1.totalAmount - data2.totalAmount;
|
||||||
return data1.value - data2.value;
|
});
|
||||||
|
|
||||||
|
return allStatisticsData;
|
||||||
|
},
|
||||||
|
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.length; i++) {
|
||||||
|
const data = this.statisticsData[i];
|
||||||
|
|
||||||
|
allData.push({
|
||||||
|
name: data.name,
|
||||||
|
value: data.totalAmount / 100,
|
||||||
|
itemStyle: {
|
||||||
|
color: `#${data.color}`
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,6 +358,7 @@ export default {
|
|||||||
this.$store.dispatch('updateTransactionStatisticsFilter', {
|
this.$store.dispatch('updateTransactionStatisticsFilter', {
|
||||||
chartDataType: chartDataType
|
chartDataType: chartDataType
|
||||||
});
|
});
|
||||||
|
this.showChartDataTypePopover = false;
|
||||||
},
|
},
|
||||||
skeletonChart() {
|
skeletonChart() {
|
||||||
const skeletonChartData = {
|
const skeletonChartData = {
|
||||||
@@ -357,6 +420,15 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
|
chartDataTypeName(dataType, allChartDataTypes) {
|
||||||
|
for (let i = 0; i < allChartDataTypes.length; i++) {
|
||||||
|
if (allChartDataTypes[i].type === dataType) {
|
||||||
|
return allChartDataTypes[i].name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Statistics';
|
||||||
|
},
|
||||||
dateRange() {
|
dateRange() {
|
||||||
return 'Date';
|
return 'Date';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user