transaction list page supports filtering and searching, transaction add page supports setting default value by query parameters

This commit is contained in:
MaysWind
2021-01-02 23:30:18 +08:00
parent 67808bd4af
commit efaf411902
11 changed files with 616 additions and 31 deletions
+2 -2
View File
@@ -36,7 +36,7 @@ func (a *TransactionsApi) TransactionListHandler(c *core.Context) (interface{},
}
uid := c.GetCurrentUid()
transactions, err := a.transactions.GetTransactionsByMaxTime(uid, transactionListReq.MaxTime, transactionListReq.Type, transactionListReq.CategoryId, transactionListReq.AccountId, transactionListReq.Count+1)
transactions, err := a.transactions.GetTransactionsByMaxTime(uid, transactionListReq.MaxTime, transactionListReq.MinTime, transactionListReq.Type, transactionListReq.CategoryId, transactionListReq.AccountId, transactionListReq.Keyword, transactionListReq.Count+1)
if err != nil {
log.ErrorfWithRequestId(c, "[transactions.TransactionListHandler] failed to get transactions earlier than \"%d\" for user \"uid:%d\", because %s", transactionListReq.MaxTime, uid, err.Error())
@@ -96,7 +96,7 @@ func (a *TransactionsApi) TransactionMonthListHandler(c *core.Context) (interfac
}
uid := c.GetCurrentUid()
transactions, err := a.transactions.GetTransactionsInMonthByPage(uid, transactionListReq.Year, transactionListReq.Month, transactionListReq.Type, transactionListReq.CategoryId, transactionListReq.AccountId, transactionListReq.Page, transactionListReq.Count)
transactions, err := a.transactions.GetTransactionsInMonthByPage(uid, transactionListReq.Year, transactionListReq.Month, transactionListReq.Type, transactionListReq.CategoryId, transactionListReq.AccountId, transactionListReq.Keyword, transactionListReq.Page, transactionListReq.Count)
if err != nil {
log.ErrorfWithRequestId(c, "[transactions.TransactionMonthListHandler] failed to get transactions in month \"%d-%d\" for user \"uid:%d\", because %s", transactionListReq.Year, transactionListReq.Month, uid, err.Error())
+3
View File
@@ -75,7 +75,9 @@ type TransactionListByMaxTimeRequest struct {
Type TransactionDbType `form:"type" binding:"min=0,max=4"`
CategoryId int64 `form:"category_id" binding:"min=0"`
AccountId int64 `form:"account_id" binding:"min=0"`
Keyword string `form:"keyword"`
MaxTime int64 `form:"max_time" binding:"min=0"`
MinTime int64 `form:"min_time" binding:"min=0"`
Count int `form:"count" binding:"required,min=1,max=50"`
}
@@ -86,6 +88,7 @@ type TransactionListInMonthByPageRequest struct {
Type TransactionDbType `form:"type" binding:"min=0,max=4"`
CategoryId int64 `form:"category_id" binding:"min=0"`
AccountId int64 `form:"account_id" binding:"min=0"`
Keyword string `form:"keyword"`
Page int `form:"page" binding:"required,min=1"`
Count int `form:"count" binding:"required,min=1,max=50"`
}
+20 -5
View File
@@ -33,11 +33,11 @@ var (
// GetAllTransactionsByMaxTime returns all transactions before given time
func (s *TransactionService) GetAllTransactionsByMaxTime(uid int64, maxTime int64, count int) ([]*models.Transaction, error) {
return s.GetTransactionsByMaxTime(uid, maxTime, 0, 0, 0, count)
return s.GetTransactionsByMaxTime(uid, maxTime, 0, 0, 0, 0, "", count)
}
// GetTransactionsByMaxTime returns transactions before given time
func (s *TransactionService) GetTransactionsByMaxTime(uid int64, maxTime int64, transactionType models.TransactionDbType, categoryId int64, accountId int64, count int) ([]*models.Transaction, error) {
func (s *TransactionService) GetTransactionsByMaxTime(uid int64, maxTime int64, minTime int64, transactionType models.TransactionDbType, categoryId int64, accountId int64, keyword string, count int) ([]*models.Transaction, error) {
if uid <= 0 {
return nil, errs.ErrUserIdInvalid
}
@@ -50,7 +50,7 @@ func (s *TransactionService) GetTransactionsByMaxTime(uid int64, maxTime int64,
var err error
condition := "uid=? AND deleted=?"
conditionParams := make([]interface{}, 0, 10)
conditionParams := make([]interface{}, 0, 16)
conditionParams = append(conditionParams, uid)
conditionParams = append(conditionParams, false)
@@ -86,18 +86,28 @@ func (s *TransactionService) GetTransactionsByMaxTime(uid int64, maxTime int64,
conditionParams = append(conditionParams, accountId)
}
if keyword != "" {
condition = condition + " AND comment LIKE ?"
conditionParams = append(conditionParams, "%%" + keyword + "%%")
}
if maxTime > 0 {
condition = condition + " AND transaction_time<=?"
conditionParams = append(conditionParams, maxTime)
}
if minTime > 0 {
condition = condition + " AND transaction_time>=?"
conditionParams = append(conditionParams, minTime)
}
err = s.UserDataDB(uid).Where(condition, conditionParams...).Limit(count, 0).OrderBy("transaction_time desc").Find(&transactions)
return transactions, err
}
// GetTransactionsInMonthByPage returns transactions in given year and month
func (s *TransactionService) GetTransactionsInMonthByPage(uid int64, year int, month int, transactionType models.TransactionDbType, categoryId int64, accountId int64, page int, count int) ([]*models.Transaction, error) {
func (s *TransactionService) GetTransactionsInMonthByPage(uid int64, year int, month int, transactionType models.TransactionDbType, categoryId int64, accountId int64, keyword string, page int, count int) ([]*models.Transaction, error) {
if uid <= 0 {
return nil, errs.ErrUserIdInvalid
}
@@ -124,7 +134,7 @@ func (s *TransactionService) GetTransactionsInMonthByPage(uid int64, year int, m
var transactions []*models.Transaction
condition := "uid=? AND deleted=? AND transaction_time>=? AND transaction_time<?"
conditionParams := make([]interface{}, 0, 12)
conditionParams := make([]interface{}, 0, 16)
conditionParams = append(conditionParams, uid)
conditionParams = append(conditionParams, false)
conditionParams = append(conditionParams, startUnixTime)
@@ -162,6 +172,11 @@ func (s *TransactionService) GetTransactionsInMonthByPage(uid int64, year int, m
conditionParams = append(conditionParams, accountId)
}
if keyword != "" {
condition = condition + " AND comment LIKE ?"
conditionParams = append(conditionParams, "%%" + keyword + "%%")
}
err = s.UserDataDB(uid).Where(condition, conditionParams...).Limit(count, count*(page-1)).OrderBy("transaction_time desc").Find(&transactions)
return transactions, err
@@ -0,0 +1,125 @@
<template>
<f7-sheet style="height:auto" :opened="show"
@sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-page-content>
<div class="display-flex padding justify-content-space-between align-items-center">
<div style="font-size: 18px" v-if="title"><b>{{ title }}</b></div>
</div>
<div class="padding-horizontal padding-bottom">
<p class="no-margin-top margin-bottom-half" v-if="hint">{{ hint }}</p>
<slot></slot>
<f7-list no-hairlines inline-labels class="no-margin-top margin-bottom">
<f7-list-input
:label="$t('Begin Time')"
type="datetime-local"
class="date-range-sheet-time-item"
:value="currentMinDate"
@input="currentMinDate = $event.target.value"
>
</f7-list-input>
<f7-list-input
:label="$t('End Time')"
type="datetime-local"
class="date-range-sheet-time-item"
:value="currentMaxDate"
@input="currentMaxDate = $event.target.value"
>
</f7-list-input>
</f7-list>
<f7-button large fill
:class="{ 'disabled': !currentMinDate || !currentMaxDate }"
:text="$t('Continue')"
@click="confirm">
</f7-button>
<div class="margin-top text-align-center">
<f7-link @click="cancel" :text="$t('Cancel')"></f7-link>
</div>
</div>
</f7-page-content>
</f7-sheet>
</template>
<script>
export default {
props: [
'minTime',
'maxTime',
'title',
'hint',
'show'
],
data() {
const self = this;
let minDate = self.$utilities.getTodayFirstUnixTime();
let maxDate = self.$utilities.getUnixTime(new Date());
if (self.minTime) {
minDate = self.minTime;
}
if (self.maxTime) {
maxDate = self.maxTime;
}
return {
currentMinDate: self.$utilities.formatUnixTime(minDate, 'YYYY-MM-DDTHH:mm'),
currentMaxDate: self.$utilities.formatUnixTime(maxDate, 'YYYY-MM-DDTHH:mm')
}
},
watch: {
'currentMinDate': function (newValue) {
if (!newValue) {
this.currentMinDate = this.$utilities.formatDate(new Date(), 'YYYY-MM-DDTHH:mm');
}
},
'currentMaxDate': function (newValue) {
if (!newValue) {
this.currentMaxDate = this.$utilities.formatDate(new Date(), 'YYYY-MM-DDTHH:mm');
}
}
},
methods: {
onSheetOpen() {
if (this.minTime) {
this.currentMinDate = this.$utilities.formatUnixTime(this.minTime, 'YYYY-MM-DDTHH:mm');
}
if (this.maxTime) {
this.currentMaxDate = this.$utilities.formatUnixTime(this.maxTime, 'YYYY-MM-DDTHH:mm');
}
},
onSheetClosed() {
this.$emit('update:show', false);
},
confirm() {
if (!this.currentMinDate || !this.currentMaxDate) {
return;
}
const minUnixTime = this.$utilities.getMinuteFirstUnixTime(this.currentMinDate);
const maxUnixTime = this.$utilities.getMinuteLastUnixTime(this.currentMaxDate);
if (minUnixTime < 0 || maxUnixTime < 0) {
this.$toast('Date is too early');
return;
}
this.$emit('dateRange:change', minUnixTime, maxUnixTime);
},
cancel() {
this.$emit('update:show', false);
}
}
}
</script>
<style>
.date-range-sheet-time-item input[type="datetime-local"] {
max-width: inherit;
}
.list .date-range-sheet-time-item > .item-content {
padding-left: 0;
}
</style>
+2 -2
View File
@@ -219,8 +219,8 @@ export default {
id
});
},
getTransactions: ({ maxTime, type, categoryId, accountId }) => {
return axios.get(`v1/transactions/list.json?max_time=${maxTime}&type=${type}&category_id=${categoryId}&account_id=${accountId}&count=20`);
getTransactions: ({ maxTime, minTime, type, categoryId, accountId, keyword }) => {
return axios.get(`v1/transactions/list.json?max_time=${maxTime}&min_time=${minTime}&type=${type}&category_id=${categoryId}&account_id=${accountId}&keyword=${keyword}&count=20`);
},
getTransaction: ({ id }) => {
return axios.get('v1/transactions/get.json?id=' + id);
+61 -2
View File
@@ -33,7 +33,7 @@ function isBoolean(val) {
return typeof(val) === 'boolean';
}
function parseDateFromUnixtime(unixTime) {
function parseDateFromUnixTime(unixTime) {
return moment.unix(unixTime);
}
@@ -76,6 +76,54 @@ function getDayOfWeek(date) {
return moment(date).format('dddd');
}
function getUnixTimeBeforeUnixTime(unixTime, amount, unit) {
return moment.unix(unixTime).subtract(amount, unit).unix();
}
function getMinuteFirstUnixTime(date) {
const datetime = moment(date);
return datetime.set({ second: 0, millisecond: 0 }).unix();
}
function getMinuteLastUnixTime(date) {
return moment.unix(getMinuteFirstUnixTime(date)).add(1, 'minutes').subtract(1, 'seconds').unix();
}
function getTodayFirstUnixTime() {
return moment({ hour: 0, minute: 0, second: 0, millisecond: 0 }).unix();
}
function getTodayLastUnixTime() {
return moment.unix(getTodayFirstUnixTime()).add(1, 'days').subtract(1, 'seconds').unix();
}
function getThisWeekFirstUnixTime() {
const today = moment.unix(getTodayFirstUnixTime());
return today.subtract(today.day(), 'days').unix();
}
function getThisWeekLastUnixTime() {
return moment.unix(getThisWeekFirstUnixTime()).add(7, 'days').subtract(1, 'seconds').unix();
}
function getThisMonthFirstUnixTime() {
const today = moment.unix(getTodayFirstUnixTime());
return today.subtract(today.date() - 1, 'days').unix();
}
function getThisMonthLastUnixTime() {
return moment.unix(getThisMonthFirstUnixTime()).add(1, 'months').subtract(1, 'seconds').unix();
}
function getThisYearFirstUnixTime() {
const today = moment.unix(getTodayFirstUnixTime());
return today.subtract(today.dayOfYear() - 1, 'days').unix();
}
function getThisYearLastUnixTime() {
return moment.unix(getThisYearFirstUnixTime()).add(1, 'years').subtract(1, 'seconds').unix();
}
function copyObjectTo(fromObject, toObject) {
if (!isObject(fromObject)) {
return toObject;
@@ -433,7 +481,7 @@ export default {
isString,
isNumber,
isBoolean,
parseDateFromUnixtime,
parseDateFromUnixTime,
formatDate,
formatUnixTime,
getUnixTime,
@@ -442,6 +490,17 @@ export default {
getYearAndMonth,
getDay,
getDayOfWeek,
getUnixTimeBeforeUnixTime,
getMinuteFirstUnixTime,
getMinuteLastUnixTime,
getTodayFirstUnixTime,
getTodayLastUnixTime,
getThisWeekFirstUnixTime,
getThisWeekLastUnixTime,
getThisMonthFirstUnixTime,
getThisMonthLastUnixTime,
getThisYearFirstUnixTime,
getThisYearLastUnixTime,
copyObjectTo,
copyArrayTo,
appendThousandsSeparator,
+19
View File
@@ -14,6 +14,7 @@ export default {
},
'datetime': {
'long': 'MM/DD/YYYY HH:mm:ss',
'long-without-second': 'MM/DD/YYYY HH:mm',
},
'time': {
'hourMinute': 'HH:mm'
@@ -426,6 +427,22 @@ export default {
'Delete': 'Delete',
'Search': 'Search',
'Sort': 'Sort',
'Date': 'Date',
'Type': 'Type',
'All': 'All',
'Today': 'Today',
'Yesterday': 'Yesterday',
'Recent 7 days': 'Recent 7 days',
'Recent 30 days': 'Recent 30 days',
'This week': 'This week',
'Last week': 'Last week',
'This month': 'This month',
'Last month': 'Last month',
'This year': 'This year',
'Last year': 'Last year',
'Begin Time': 'Begin Time',
'End Time': 'End Time',
'Custom': 'Custom',
'User': 'User',
'Application': 'Application',
'Details': 'Details',
@@ -435,6 +452,7 @@ export default {
'Back': 'Back',
'Load More': 'Load More',
'Change Language': 'Change Language',
'Date is too early': 'Date is too early',
'Unlock': 'Unlock',
'Re-login': 'Re-login',
'Username': 'Username',
@@ -549,6 +567,7 @@ export default {
'You have added a new transaction': 'You have added a new transaction',
'You have saved this transaction': 'You have saved this transaction',
'Unable to get transaction list': 'Unable to get transaction list',
'Custom Date Range': 'Custom Date Range',
'Transaction Detail': 'Transaction Detail',
'No transaction data': 'No transaction data',
'Are you sure you want to delete this transaction?': 'Are you sure you want to delete this transaction?',
+19
View File
@@ -14,6 +14,7 @@ export default {
},
'datetime': {
'long': 'YYYY年MM月DD日 HH:mm:ss',
'long-without-second': 'YYYY年MM月DD日 HH:mm',
},
'time': {
'hourMinute': 'HH:mm'
@@ -426,6 +427,22 @@ export default {
'Delete': '删除',
'Search': '搜索',
'Sort': '排序',
'Date': '日期',
'Type': '类型',
'All': '全部',
'Today': '今天',
'Yesterday': '昨天',
'Recent 7 days': '最近7天',
'Recent 30 days': '最近30天',
'This week': '本周',
'Last week': '上周',
'This month': '本月',
'Last month': '上月',
'This year': '今年',
'Last year': '去年',
'Begin Time': '开始时间',
'End Time': '结束时间',
'Custom': '自定义',
'User': '用户',
'Application': '应用',
'Details': '详情',
@@ -435,6 +452,7 @@ export default {
'Back': '返回',
'Load More': '加载更多',
'Change Language': '修改语言',
'Date is too early': '日期过早',
'Unlock': '解锁',
'Re-login': '重新登陆',
'Username': '用户名',
@@ -549,6 +567,7 @@ export default {
'You have added a new transaction': '您已经添加新交易',
'You have saved this transaction': '您已经保存该交易',
'Unable to get transaction list': '无法获取交易列表',
'Custom Date Range': '自定义日期范围',
'Transaction Detail': '交易详情',
'No transaction data': '没有交易数据',
'Are you sure you want to delete this transaction?': '您确定要删除该交易?',
+2
View File
@@ -44,6 +44,7 @@ import tokenIconFilter from './filters/tokenIcon.js';
import PasswordInputSheet from "./components/mobile/PasswordInputSheet.vue";
import PasscodeInputSheet from "./components/mobile/PasscodeInputSheet.vue";
import PinCodeInputSheet from "./components/mobile/PinCodeInputSheet.vue";
import DateRangeSelectionSheet from "./components/mobile/DateRangeSelectionSheet.vue";
import ListItemSelectionSheet from "./components/mobile/ListItemSelectionSheet.vue";
import TwoColumnListItemSelectionSheet from "./components/mobile/TwoColumnListItemSelectionSheet.vue";
import TreeViewSelectionSheet from "./components/mobile/TreeViewSelectionSheet.vue";
@@ -61,6 +62,7 @@ Vue.component('PincodeInput', PincodeInput);
Vue.component('PasswordInputSheet', PasswordInputSheet);
Vue.component('PasscodeInputSheet', PasscodeInputSheet);
Vue.component('PinCodeInputSheet', PinCodeInputSheet);
Vue.component('DateRangeSelectionSheet', DateRangeSelectionSheet);
Vue.component('ListItemSelectionSheet', ListItemSelectionSheet);
Vue.component('TwoColumnListItemSelectionSheet', TwoColumnListItemSelectionSheet);
Vue.component('TreeViewSelectionSheet', TreeViewSelectionSheet);
+59 -5
View File
@@ -421,6 +421,12 @@ export default {
promises.push(self.$services.getTransaction({ id: self.editTransactionId }));
}
if (query.type && query.type !== '0' &&
query.type >= self.$constants.transaction.allTransactionTypes.Income &&
query.type <= self.$constants.transaction.allTransactionTypes.Transfer) {
self.transaction.type = parseInt(query.type);
}
Promise.all(promises).then(function (responses) {
const accountData = responses[0].data;
const categoryData = responses[1].data;
@@ -461,22 +467,55 @@ export default {
if (self.allCategories[self.$constants.category.allCategoryTypes.Expense] &&
self.allCategories[self.$constants.category.allCategoryTypes.Expense].length) {
self.transaction.expenseCategory = self.getFirstAvailableCategoryId(self.allCategories[self.$constants.category.allCategoryTypes.Expense]);
if (query.categoryId && query.categoryId !== '0' && self.isCategoryIdAvailable(self.allCategories[self.$constants.category.allCategoryTypes.Expense], query.categoryId)) {
self.transaction.expenseCategory = query.categoryId;
}
if (!self.transaction.expenseCategory) {
self.transaction.expenseCategory = self.getFirstAvailableCategoryId(self.allCategories[self.$constants.category.allCategoryTypes.Expense]);
}
}
if (self.allCategories[self.$constants.category.allCategoryTypes.Income] &&
self.allCategories[self.$constants.category.allCategoryTypes.Income].length) {
self.transaction.incomeCategory = self.getFirstAvailableCategoryId(self.allCategories[self.$constants.category.allCategoryTypes.Income]);
if (query.categoryId && query.categoryId !== '0' && self.isCategoryIdAvailable(self.allCategories[self.$constants.category.allCategoryTypes.Income], query.categoryId)) {
self.transaction.incomeCategory = query.categoryId;
}
if (!self.transaction.incomeCategory) {
self.transaction.incomeCategory = self.getFirstAvailableCategoryId(self.allCategories[self.$constants.category.allCategoryTypes.Income]);
}
}
if (self.allCategories[self.$constants.category.allCategoryTypes.Transfer] &&
self.allCategories[self.$constants.category.allCategoryTypes.Transfer].length) {
self.transaction.transferCategory = self.getFirstAvailableCategoryId(self.allCategories[self.$constants.category.allCategoryTypes.Transfer]);
if (query.categoryId && query.categoryId !== '0' && self.isCategoryIdAvailable(self.allCategories[self.$constants.category.allCategoryTypes.Transfer], query.categoryId)) {
self.transaction.transferCategory = query.categoryId;
}
if (!self.transaction.transferCategory) {
self.transaction.transferCategory = self.getFirstAvailableCategoryId(self.allCategories[self.$constants.category.allCategoryTypes.Transfer]);
}
}
if (self.allAccounts.length) {
self.transaction.sourceAccountId = self.allAccounts[0].id;
self.transaction.destinationAccountId = self.allAccounts[0].id;
if (query.accountId && query.accountId !== '0') {
for (let i = 0; i < self.allAccounts.length; i++) {
if (self.allAccounts[i].id === query.accountId) {
self.transaction.sourceAccountId = query.accountId;
self.transaction.destinationAccountId = query.accountId;
break;
}
}
}
if (!self.transaction.sourceAccountId) {
self.transaction.sourceAccountId = self.allAccounts[0].id;
}
if (!self.transaction.destinationAccountId) {
self.transaction.destinationAccountId = self.allAccounts[0].id;
}
}
if (self.editTransactionId) {
@@ -599,6 +638,21 @@ export default {
}
});
},
isCategoryIdAvailable(categories, categoryId) {
if (!categories || !categories.length) {
return false;
}
for (let i = 0; i < categories.length; i++) {
for (let j = 0; j < categories[i].subCategories.length; j++) {
if (categories[i].subCategories[j].id === categoryId) {
return true;
}
}
}
return false;
},
getFirstAvailableCategoryId(categories) {
if (!categories || !categories.length) {
return '';
+304 -15
View File
@@ -8,11 +8,36 @@
<f7-navbar>
<f7-nav-left :back-link="$t('Back')"></f7-nav-left>
<f7-nav-title :title="$t('Transaction List')"></f7-nav-title>
<f7-nav-right v-if="false">
<f7-link icon-f7="ellipsis" @click="showMoreActionSheet = true"></f7-link>
<f7-nav-right class="navbar-compact-icons">
<f7-link icon-f7="search" class="searchbar-enable" data-searchbar=".searchbar-keyword"></f7-link>
<f7-link icon-f7="plus" :href="'/transaction/add?type=' + query.type + '&categoryId=' + query.categoryId + '&accountId=' + query.accountId"></f7-link>
</f7-nav-right>
<f7-searchbar
expandable custom-search
class="searchbar-keyword"
:value="query.keyword"
:placeholder="$t('Search')"
:disable-button-text="$t('Cancel')"
@change="changeKeywordFilter($event.target.value)"
></f7-searchbar>
</f7-navbar>
<f7-toolbar tabbar bottom>
<f7-link popover-open=".date-popover-menu">
<span :class="{ 'tabbar-item-changed': query.maxTime > 0 || query.minTime > 0 }">{{ $t('Date') }}</span>
</f7-link>
<f7-link popover-open=".type-popover-menu">
<span :class="{ 'tabbar-item-changed': query.type > 0 }">{{ $t('Type') }}</span>
</f7-link>
<f7-link popover-open=".category-popover-menu" :class="{ 'disabled': query.type === 1 }">
<span :class="{ 'tabbar-item-changed': query.categoryId > 0 }">{{ $t('Category') }}</span>
</f7-link>
<f7-link popover-open=".account-popover-menu">
<span :class="{ 'tabbar-item-changed': query.accountId > 0 }">{{ $t('Account') }}</span>
</f7-link>
</f7-toolbar>
<f7-card class="skeleton-text" v-if="loading">
<f7-card-header>
<div class="full-line">
@@ -198,11 +223,133 @@
<f7-link :class="{ 'disabled': loadingMore }" href="#" @click="loadMore(false)">{{ $t('Load More') }}</f7-link>
</f7-block>
<f7-actions close-by-outside-click close-on-escape :opened="showMoreActionSheet" @actions:closed="showMoreActionSheet = false">
<f7-actions-group>
<f7-actions-button bold close>{{ $t('Cancel') }}</f7-actions-button>
</f7-actions-group>
</f7-actions>
<f7-popover class="date-popover-menu" :opened="showDatePopover"
@popover:opened="showDatePopover = true" @popover:closed="showDatePopover = false">
<f7-list>
<f7-list-item :title="$t('All')" @click="changeDateFilter(0)">
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.dateType === 0"></f7-icon>
</f7-list-item>
<f7-list-item :title="$t('Today')" @click="changeDateFilter(1)">
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.dateType === 1"></f7-icon>
</f7-list-item>
<f7-list-item :title="$t('Yesterday')" @click="changeDateFilter(2)">
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.dateType === 2"></f7-icon>
</f7-list-item>
<f7-list-item :title="$t('Recent 7 days')" @click="changeDateFilter(3)">
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.dateType === 3"></f7-icon>
</f7-list-item>
<f7-list-item :title="$t('Recent 30 days')" @click="changeDateFilter(4)">
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.dateType === 4"></f7-icon>
</f7-list-item>
<f7-list-item :title="$t('This week')" @click="changeDateFilter(5)">
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.dateType === 5"></f7-icon>
</f7-list-item>
<f7-list-item :title="$t('Last week')" @click="changeDateFilter(6)">
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.dateType === 6"></f7-icon>
</f7-list-item>
<f7-list-item :title="$t('This month')" @click="changeDateFilter(7)">
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.dateType === 7"></f7-icon>
</f7-list-item>
<f7-list-item :title="$t('Last month')" @click="changeDateFilter(8)">
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.dateType === 8"></f7-icon>
</f7-list-item>
<f7-list-item :title="$t('This year')" @click="changeDateFilter(9)">
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.dateType === 9"></f7-icon>
</f7-list-item>
<f7-list-item :title="$t('Last year')" @click="changeDateFilter(10)">
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.dateType === 10"></f7-icon>
</f7-list-item>
<f7-list-item :title="$t('Custom')" @click="changeDateFilter(11)">
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.dateType === 11"></f7-icon>
<div slot="footer" v-if="query.dateType === 11 && query.minTime && query.maxTime">
<span>{{ query.minTime | moment($t('format.datetime.long-without-second')) }}</span>
<span>&nbsp;-&nbsp;</span>
<br/>
<span>{{ query.maxTime | moment($t('format.datetime.long-without-second')) }}</span>
</div>
</f7-list-item>
</f7-list>
</f7-popover>
<date-range-selection-sheet :title="$t('Custom Date Range')"
:show.sync="showCustomDateRangeSheet"
:min-time="query.minTime"
:max-time="query.maxTime"
@dateRange:change="changeCustomDateFilter">
</date-range-selection-sheet>
<f7-popover class="type-popover-menu" :opened="showTypePopover"
@popover:opened="showTypePopover = true" @popover:closed="showTypePopover = false">
<f7-list>
<f7-list-item :title="$t('All')" @click="changeTypeFilter(0)">
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.type === 0"></f7-icon>
</f7-list-item>
<f7-list-item :title="$t('Modify Balance')" @click="changeTypeFilter(1)">
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.type === 1"></f7-icon>
</f7-list-item>
<f7-list-item :title="$t('Income')" @click="changeTypeFilter(2)">
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.type === 2"></f7-icon>
</f7-list-item>
<f7-list-item :title="$t('Expense')" @click="changeTypeFilter(3)">
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.type === 3"></f7-icon>
</f7-list-item>
<f7-list-item :title="$t('Transfer')" @click="changeTypeFilter(4)">
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.type === 4"></f7-icon>
</f7-list-item>
</f7-list>
</f7-popover>
<f7-popover class="category-popover-menu" :opened="showCategoryPopover"
@popover:opened="showCategoryPopover = true" @popover:closed="showCategoryPopover = false">
<f7-list>
<f7-list-item :title="$t('All')" @click="changeCategoryFilter('0')">
<f7-icon slot="media" f7="rectangle_badge_checkmark"></f7-icon>
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.categoryId === '0'"></f7-icon>
</f7-list-item>
<f7-list-item v-for="category in allCategories"
v-show="category.parentId > 0 && (!query.type || category.type === query.type - 1)"
:key="category.id"
:title="category.name"
@click="changeCategoryFilter(category.id)"
>
<f7-icon slot="media"
:icon="category.icon | categoryIcon"
:style="category.color | categoryIconStyle('var(--default-icon-color)')">
</f7-icon>
<f7-icon slot="after"
class="list-item-checked"
f7="checkmark_alt"
v-if="query.categoryId === category.id">
</f7-icon>
</f7-list-item>
</f7-list>
</f7-popover>
<f7-popover class="account-popover-menu" :opened="showAccountPopover"
@popover:opened="showAccountPopover = true" @popover:closed="showAccountPopover = false">
<f7-list>
<f7-list-item :title="$t('All')" @click="changeAccountFilter('0')">
<f7-icon slot="media" f7="rectangle_badge_checkmark"></f7-icon>
<f7-icon slot="after" class="list-item-checked" f7="checkmark_alt" v-if="query.accountId === '0'"></f7-icon>
</f7-list-item>
<f7-list-item v-for="account in allAccounts"
v-show="!account.hidden"
:key="account.id"
:title="account.name"
@click="changeAccountFilter(account.id)"
>
<f7-icon slot="media"
:icon="account.icon | accountIcon"
:style="account.color | accountIconStyle('var(--default-icon-color)')">
</f7-icon>
<f7-icon slot="after"
class="list-item-checked"
f7="checkmark_alt"
v-if="query.accountId === account.id">
</f7-icon>
</f7-list-item>
</f7-list>
</f7-popover>
<f7-actions close-by-outside-click close-on-escape :opened="showDeleteActionSheet" @actions:closed="showDeleteActionSheet = false">
<f7-actions-group>
@@ -222,9 +369,13 @@ export default {
return {
transactions: [],
query: {
dateType: 0,
maxTime: 0,
minTime: 0,
type: 0,
categoryId: 0,
accountId: 0
categoryId: '0',
accountId: '0',
keyword: ''
},
allAccounts: {},
allCategories: {},
@@ -233,7 +384,11 @@ export default {
loading: true,
loadingMore: false,
transactionToDelete: null,
showMoreActionSheet: false,
showDatePopover: false,
showTypePopover: false,
showCategoryPopover: false,
showAccountPopover: false,
showCustomDateRangeSheet: false,
showDeleteActionSheet: false
};
},
@@ -282,7 +437,11 @@ export default {
self.loading = true;
}
self.maxTime = 0;
if (self.query.maxTime > 0) {
self.maxTime = self.query.maxTime * 1000 + 999;
} else {
self.maxTime = 0;
}
const promises = [
self.$services.getAllAccounts({ visibleOnly: false }),
@@ -290,9 +449,11 @@ export default {
self.$services.getAllTransactionTags(),
self.$services.getTransactions({
maxTime: self.maxTime,
minTime: self.query.minTime * 1000,
type: self.query.type,
categoryId: self.query.categoryId,
accountId: self.query.accountId
accountId: self.query.accountId,
keyword: self.query.keyword
})
];
@@ -406,7 +567,7 @@ export default {
return;
}
if (self.loadingMore) {
if (self.loadingMore || self.loading) {
return;
}
@@ -414,9 +575,11 @@ export default {
self.$services.getTransactions({
maxTime: self.maxTime,
minTime: self.query.minTime * 1000,
type: self.query.type,
categoryId: self.query.categoryId,
accountId: self.query.accountId
accountId: self.query.accountId,
keyword: self.query.keyword
}).then(response => {
self.loadingMore = false;
@@ -440,6 +603,123 @@ export default {
}
});
},
changeDateFilter(dateType) {
if (dateType === 11) { // Custom
this.showCustomDateRangeSheet = true;
this.showDatePopover = false;
return;
} else if (this.query.dateType === dateType) {
return;
}
if (dateType === 0) { // All
this.query.maxTime = 0;
this.query.minTime = 0;
} else if (dateType === 1) { // Today
this.query.maxTime = this.$utilities.getTodayLastUnixTime();
this.query.minTime = this.$utilities.getTodayFirstUnixTime();
} else if (dateType === 2) { // Yesterday
this.query.maxTime = this.$utilities.getUnixTimeBeforeUnixTime(this.$utilities.getTodayLastUnixTime(), 1, 'days');
this.query.minTime = this.$utilities.getUnixTimeBeforeUnixTime(this.$utilities.getTodayFirstUnixTime(), 1, 'days');
} else if (dateType === 3) { // Last 7 days
this.query.maxTime = this.$utilities.getUnixTime(new Date());
this.query.minTime = this.$utilities.getUnixTimeBeforeUnixTime(this.query.maxTime, 7, 'days');
} else if (dateType === 4) { // Last 30 days
this.query.maxTime = this.$utilities.getUnixTime(new Date());
this.query.minTime = this.$utilities.getUnixTimeBeforeUnixTime(this.query.maxTime, 30, 'days');
} else if (dateType === 5) { // This week
this.query.maxTime = this.$utilities.getThisWeekLastUnixTime();
this.query.minTime = this.$utilities.getThisWeekFirstUnixTime();
} else if (dateType === 6) { // Last week
this.query.maxTime = this.$utilities.getUnixTimeBeforeUnixTime(this.$utilities.getThisWeekLastUnixTime(), 7, 'days');
this.query.minTime = this.$utilities.getUnixTimeBeforeUnixTime(this.$utilities.getThisWeekFirstUnixTime(), 7, 'days');
} else if (dateType === 7) { // This month
this.query.maxTime = this.$utilities.getThisMonthLastUnixTime();
this.query.minTime = this.$utilities.getThisMonthFirstUnixTime();
} else if (dateType === 8) { // Last month
this.query.maxTime = this.$utilities.getUnixTimeBeforeUnixTime(this.$utilities.getThisMonthLastUnixTime(), 1, 'months');
this.query.minTime = this.$utilities.getUnixTimeBeforeUnixTime(this.$utilities.getThisMonthFirstUnixTime(), 1, 'months');
} else if (dateType === 9) { // This year
this.query.maxTime = this.$utilities.getThisYearLastUnixTime();
this.query.minTime = this.$utilities.getThisYearFirstUnixTime();
} else if (dateType === 10) { // Last year
this.query.maxTime = this.$utilities.getUnixTimeBeforeUnixTime(this.$utilities.getThisYearLastUnixTime(), 1, 'years');
this.query.minTime = this.$utilities.getUnixTimeBeforeUnixTime(this.$utilities.getThisYearFirstUnixTime(), 1, 'years');
} else {
return;
}
this.transactions = [];
this.query.dateType = dateType;
this.showDatePopover = false;
this.reload(null);
},
changeCustomDateFilter(minTime, maxTime) {
if (!minTime || !maxTime) {
return;
}
this.query.maxTime = maxTime;
this.query.minTime = minTime;
console.log(this.$utilities.formatUnixTime(this.query.maxTime, 'YYYY-MM-DD HH:mm:ss'));
console.log(this.$utilities.formatUnixTime(this.query.minTime, 'YYYY-MM-DD HH:mm:ss'));
this.transactions = [];
this.query.dateType = 11;
this.showCustomDateRangeSheet = false;
this.reload(null);
},
changeTypeFilter(type) {
if (this.query.type === type) {
return;
}
if (type && this.query.categoryId) {
const category = this.allCategories[this.query.categoryId];
if (category && category.type !== type - 1) {
this.query.categoryId = 0;
}
}
this.transactions = [];
this.query.type = type;
this.showTypePopover = false;
this.reload(null);
},
changeCategoryFilter(categoryId) {
if (this.query.categoryId === categoryId) {
return;
}
this.transactions = [];
this.query.categoryId = categoryId;
this.showCategoryPopover = false;
this.reload(null);
},
changeAccountFilter(accountId) {
if (this.query.accountId === accountId) {
return;
}
this.transactions = [];
this.query.accountId = accountId;
this.showAccountPopover = false;
this.reload(null);
},
changeKeywordFilter(keyword) {
if (this.query.keyword === keyword) {
return;
}
this.transactions = [];
this.query.keyword = keyword;
this.reload(null);
},
edit(transaction) {
this.$f7router.navigate('/transaction/edit?id=' + transaction.id);
},
@@ -516,7 +796,7 @@ export default {
for (let i = 0; i < result.items.length; i++) {
const transaction = result.items[i];
const transactionTime = this.$utilities.parseDateFromUnixtime(transaction.time);
const transactionTime = this.$utilities.parseDateFromUnixTime(transaction.time);
transaction.day = this.$utilities.getDay(transactionTime);
transaction.dayOfWeek = this.$t(`datetime.${this.$utilities.getDayOfWeek(transactionTime)}.short`);
transaction.sourceAccount = this.allAccounts[transaction.sourceAccountId];
@@ -751,4 +1031,13 @@ export default {
transform-origin: 50% 100%;
transform: scaleY(calc(1 / var(--f7-device-pixel-ratio)));
}
.tabbar-item-changed {
color: var(--f7-theme-color);
}
.date-popover-menu .popover-inner, .category-popover-menu .popover-inner, .account-popover-menu .popover-inner {
max-height: 400px;
overflow-Y: auto;
}
</style>