mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-19 01:04:25 +08:00
add transaction detail page
This commit is contained in:
@@ -59,7 +59,7 @@ type TransactionModifyRequest struct {
|
|||||||
|
|
||||||
// TransactionListByMaxTimeRequest represents all parameters of transaction listing by max time request
|
// TransactionListByMaxTimeRequest represents all parameters of transaction listing by max time request
|
||||||
type TransactionListByMaxTimeRequest struct {
|
type TransactionListByMaxTimeRequest struct {
|
||||||
MaxTime int64 `form:"max_time" binding:"required,min=1"`
|
MaxTime int64 `form:"max_time" binding:"min=0"`
|
||||||
Count int `form:"count" binding:"required,min=1,max=50"`
|
Count int `form:"count" binding:"required,min=1,max=50"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,5 +134,9 @@ func (c TransactionInfoResponseSlice) Swap(i, j int) {
|
|||||||
|
|
||||||
// Less reports whether the first item is less than the second one
|
// Less reports whether the first item is less than the second one
|
||||||
func (c TransactionInfoResponseSlice) Less(i, j int) bool {
|
func (c TransactionInfoResponseSlice) Less(i, j int) bool {
|
||||||
return c[i].Time < c[j].Time
|
if c[i].Time != c[j].Time {
|
||||||
|
return c[i].Time > c[j].Time
|
||||||
|
}
|
||||||
|
|
||||||
|
return c[i].Id > c[j].Id
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,13 @@ func (s *TransactionService) GetTransactionsByMaxTime(uid int64, maxTime int64,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var transactions []*models.Transaction
|
var transactions []*models.Transaction
|
||||||
err := s.UserDataDB(uid).Where("uid=? AND deleted=? AND transaction_time<=?", uid, false, maxTime).Limit(count, 0).OrderBy("transaction_time desc").Find(&transactions)
|
var err error
|
||||||
|
|
||||||
|
if maxTime > 0 {
|
||||||
|
err = s.UserDataDB(uid).Where("uid=? AND deleted=? AND transaction_time<=?", uid, false, maxTime).Limit(count, 0).OrderBy("transaction_time desc").Find(&transactions)
|
||||||
|
} else {
|
||||||
|
err = s.UserDataDB(uid).Where("uid=? AND deleted=?", uid, false).Limit(count, 0).OrderBy("transaction_time desc").Find(&transactions)
|
||||||
|
}
|
||||||
|
|
||||||
return transactions, err
|
return transactions, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,6 +213,9 @@ export default {
|
|||||||
id
|
id
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
getTransactions: ({ maxTime }) => {
|
||||||
|
return axios.get('v1/transactions/list.json?max_time=' + maxTime + '&count=20');
|
||||||
|
},
|
||||||
getTransaction: ({ id }) => {
|
getTransaction: ({ id }) => {
|
||||||
return axios.get('v1/transactions/get.json?id=' + id);
|
return axios.get('v1/transactions/get.json?id=' + id);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ function isBoolean(val) {
|
|||||||
return typeof(val) === 'boolean';
|
return typeof(val) === 'boolean';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseDateFromUnixtime(unixTime) {
|
||||||
|
return moment.unix(unixTime);
|
||||||
|
}
|
||||||
|
|
||||||
function formatDate(date, format) {
|
function formatDate(date, format) {
|
||||||
return moment(date).format(format);
|
return moment(date).format(format);
|
||||||
}
|
}
|
||||||
@@ -45,6 +49,22 @@ function getUnixTime(date) {
|
|||||||
return moment(date).unix();
|
return moment(date).unix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getYear(date) {
|
||||||
|
return moment(date).year();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMonth(date) {
|
||||||
|
return moment(date).month() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDay(date) {
|
||||||
|
return moment(date).date();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDayOfWeek(date) {
|
||||||
|
return moment(date).format('dddd');
|
||||||
|
}
|
||||||
|
|
||||||
function copyObjectTo(fromObject, toObject) {
|
function copyObjectTo(fromObject, toObject) {
|
||||||
if (!isObject(fromObject)) {
|
if (!isObject(fromObject)) {
|
||||||
return toObject;
|
return toObject;
|
||||||
@@ -369,6 +389,8 @@ function getAllFilteredAccountsBalance(categorizedAccounts, accountFilter) {
|
|||||||
if (account.type === accountConstants.allAccountTypes.SingleAccount) {
|
if (account.type === accountConstants.allAccountTypes.SingleAccount) {
|
||||||
ret.push({
|
ret.push({
|
||||||
balance: account.balance,
|
balance: account.balance,
|
||||||
|
isAsset: account.isAsset,
|
||||||
|
isLiability: account.isLiability,
|
||||||
currency: account.currency
|
currency: account.currency
|
||||||
});
|
});
|
||||||
} else if (account.type === accountConstants.allAccountTypes.MultiSubAccounts) {
|
} else if (account.type === accountConstants.allAccountTypes.MultiSubAccounts) {
|
||||||
@@ -381,6 +403,8 @@ function getAllFilteredAccountsBalance(categorizedAccounts, accountFilter) {
|
|||||||
|
|
||||||
ret.push({
|
ret.push({
|
||||||
balance: subAccount.balance,
|
balance: subAccount.balance,
|
||||||
|
isAsset: subAccount.isAsset,
|
||||||
|
isLiability: subAccount.isLiability,
|
||||||
currency: subAccount.currency
|
currency: subAccount.currency
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -398,9 +422,14 @@ export default {
|
|||||||
isString,
|
isString,
|
||||||
isNumber,
|
isNumber,
|
||||||
isBoolean,
|
isBoolean,
|
||||||
|
parseDateFromUnixtime,
|
||||||
formatDate,
|
formatDate,
|
||||||
formatUnixTime,
|
formatUnixTime,
|
||||||
getUnixTime,
|
getUnixTime,
|
||||||
|
getYear,
|
||||||
|
getMonth,
|
||||||
|
getDay,
|
||||||
|
getDayOfWeek,
|
||||||
copyObjectTo,
|
copyObjectTo,
|
||||||
copyArrayTo,
|
copyArrayTo,
|
||||||
appendThousandsSeparator,
|
appendThousandsSeparator,
|
||||||
|
|||||||
+34
-1
@@ -9,15 +9,42 @@ export default {
|
|||||||
},
|
},
|
||||||
'format': {
|
'format': {
|
||||||
'date': {
|
'date': {
|
||||||
'long': 'MM/DD/YYYY'
|
'long': 'MM/DD/YYYY',
|
||||||
|
'yearMonth': 'YYYY-MM'
|
||||||
},
|
},
|
||||||
'datetime': {
|
'datetime': {
|
||||||
'long': 'MM/DD/YYYY HH:mm:ss',
|
'long': 'MM/DD/YYYY HH:mm:ss',
|
||||||
},
|
},
|
||||||
|
'time': {
|
||||||
|
'hourMinute': 'HH:mm'
|
||||||
|
},
|
||||||
'currency': {
|
'currency': {
|
||||||
'symbol': '{amount} {symbol}'
|
'symbol': '{amount} {symbol}'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
'datetime': {
|
||||||
|
'Monday': {
|
||||||
|
'short': 'Mon'
|
||||||
|
},
|
||||||
|
'Tuesday': {
|
||||||
|
'short': 'Tue'
|
||||||
|
},
|
||||||
|
'Wednesday': {
|
||||||
|
'short': 'Wed'
|
||||||
|
},
|
||||||
|
'Thursday': {
|
||||||
|
'short': 'Thu'
|
||||||
|
},
|
||||||
|
'Friday': {
|
||||||
|
'short': 'Fri'
|
||||||
|
},
|
||||||
|
'Saturday': {
|
||||||
|
'short': 'Sat'
|
||||||
|
},
|
||||||
|
'Sunday': {
|
||||||
|
'short': 'Sun'
|
||||||
|
}
|
||||||
|
},
|
||||||
'currency': {
|
'currency': {
|
||||||
'AED': 'United Arab Emirates dirham',
|
'AED': 'United Arab Emirates dirham',
|
||||||
'AFN': 'Afghan afghani',
|
'AFN': 'Afghan afghani',
|
||||||
@@ -498,8 +525,10 @@ export default {
|
|||||||
'Unable to move account': 'Unable to move account',
|
'Unable to move account': 'Unable to move account',
|
||||||
'Are you sure you want to delete this account?': 'Are you sure you want to delete this account?',
|
'Are you sure you want to delete this account?': 'Are you sure you want to delete this account?',
|
||||||
'Unable to delete this account': 'Unable to delete this account',
|
'Unable to delete this account': 'Unable to delete this account',
|
||||||
|
'Transaction': 'Transaction',
|
||||||
'Add Transaction': 'Add Transaction',
|
'Add Transaction': 'Add Transaction',
|
||||||
'Edit Transaction': 'Edit Transaction',
|
'Edit Transaction': 'Edit Transaction',
|
||||||
|
'Modify Balance': 'Modify Balance',
|
||||||
'Expense Amount': 'Expense Amount',
|
'Expense Amount': 'Expense Amount',
|
||||||
'Income Amount': 'Income Amount',
|
'Income Amount': 'Income Amount',
|
||||||
'Transfer Out Amount': 'Transfer Out Amount',
|
'Transfer Out Amount': 'Transfer Out Amount',
|
||||||
@@ -516,6 +545,10 @@ export default {
|
|||||||
'Unable to save transaction': 'Unable to save transaction',
|
'Unable to save transaction': 'Unable to save transaction',
|
||||||
'You have added a new transaction': 'You have added a new transaction',
|
'You have added a new transaction': 'You have added a new transaction',
|
||||||
'You have saved this transaction': 'You have saved this transaction',
|
'You have saved this transaction': 'You have saved this transaction',
|
||||||
|
'Unable to get transaction list': 'Unable to get transaction list',
|
||||||
|
'No transaction data': 'No transaction data',
|
||||||
|
'Are you sure you want to delete this transaction?': 'Are you sure you want to delete this transaction?',
|
||||||
|
'Unable to delete this transaction': 'Unable to delete this transaction',
|
||||||
'User Profile': 'User Profile',
|
'User Profile': 'User Profile',
|
||||||
'Language': 'Language',
|
'Language': 'Language',
|
||||||
'Auto Update Exchange Rates Data': 'Auto Update Exchange Rates Data',
|
'Auto Update Exchange Rates Data': 'Auto Update Exchange Rates Data',
|
||||||
|
|||||||
+34
-1
@@ -9,15 +9,42 @@ export default {
|
|||||||
},
|
},
|
||||||
'format': {
|
'format': {
|
||||||
'date': {
|
'date': {
|
||||||
'long': 'YYYY年MM月DD日'
|
'long': 'YYYY年MM月DD日',
|
||||||
|
'yearMonth': 'YYYY年MM月'
|
||||||
},
|
},
|
||||||
'datetime': {
|
'datetime': {
|
||||||
'long': 'YYYY年MM月DD日 HH:mm:ss',
|
'long': 'YYYY年MM月DD日 HH:mm:ss',
|
||||||
},
|
},
|
||||||
|
'time': {
|
||||||
|
'hourMinute': 'HH:mm'
|
||||||
|
},
|
||||||
'currency': {
|
'currency': {
|
||||||
'symbol': '{symbol} {amount}'
|
'symbol': '{symbol} {amount}'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
'datetime': {
|
||||||
|
'Monday': {
|
||||||
|
'short': '周一'
|
||||||
|
},
|
||||||
|
'Tuesday': {
|
||||||
|
'short': '周二'
|
||||||
|
},
|
||||||
|
'Wednesday': {
|
||||||
|
'short': '周三'
|
||||||
|
},
|
||||||
|
'Thursday': {
|
||||||
|
'short': '周四'
|
||||||
|
},
|
||||||
|
'Friday': {
|
||||||
|
'short': '周五'
|
||||||
|
},
|
||||||
|
'Saturday': {
|
||||||
|
'short': '周六'
|
||||||
|
},
|
||||||
|
'Sunday': {
|
||||||
|
'short': '周日'
|
||||||
|
}
|
||||||
|
},
|
||||||
'currency': {
|
'currency': {
|
||||||
'AED': '阿联酋迪拉姆',
|
'AED': '阿联酋迪拉姆',
|
||||||
'AFN': '阿富汗阿富汗尼',
|
'AFN': '阿富汗阿富汗尼',
|
||||||
@@ -498,8 +525,10 @@ export default {
|
|||||||
'Unable to move account': '无法移动账户',
|
'Unable to move account': '无法移动账户',
|
||||||
'Are you sure you want to delete this account?': '您确定要删除该账户?',
|
'Are you sure you want to delete this account?': '您确定要删除该账户?',
|
||||||
'Unable to delete this account': '无法删除该账户',
|
'Unable to delete this account': '无法删除该账户',
|
||||||
|
'Transaction': '交易',
|
||||||
'Add Transaction': '添加交易',
|
'Add Transaction': '添加交易',
|
||||||
'Edit Transaction': '编辑交易',
|
'Edit Transaction': '编辑交易',
|
||||||
|
'Modify Balance': '修改余额',
|
||||||
'Expense Amount': '支出金额',
|
'Expense Amount': '支出金额',
|
||||||
'Income Amount': '收入金额',
|
'Income Amount': '收入金额',
|
||||||
'Transfer Out Amount': '转出金额',
|
'Transfer Out Amount': '转出金额',
|
||||||
@@ -516,6 +545,10 @@ export default {
|
|||||||
'Unable to save transaction': '无法保存交易',
|
'Unable to save transaction': '无法保存交易',
|
||||||
'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': '无法获取交易列表',
|
||||||
|
'No transaction data': '没有交易数据',
|
||||||
|
'Are you sure you want to delete this transaction?': '您确定要删除该交易?',
|
||||||
|
'Unable to delete this transaction': '无法删除该交易',
|
||||||
'User Profile': '用户信息',
|
'User Profile': '用户信息',
|
||||||
'Language': '语言',
|
'Language': '语言',
|
||||||
'Auto Update Exchange Rates Data': '自动更新汇率数据',
|
'Auto Update Exchange Rates Data': '自动更新汇率数据',
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import VueMoment from 'vue-moment';
|
|||||||
import VueClipboard from 'vue-clipboard2';
|
import VueClipboard from 'vue-clipboard2';
|
||||||
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import 'moment/min/locales';
|
|
||||||
|
|
||||||
import 'framework7/css/framework7.bundle.css';
|
import 'framework7/css/framework7.bundle.css';
|
||||||
import 'framework7-icons';
|
import 'framework7-icons';
|
||||||
|
|||||||
@@ -1,17 +1,586 @@
|
|||||||
<template>
|
<template>
|
||||||
<f7-page>
|
<f7-page @infinite="loadMore">
|
||||||
<f7-navbar :title="$t('Transaction Details')" :back-link="$t('Back')">
|
<f7-navbar>
|
||||||
<f7-subnavbar :inner="false">
|
<f7-nav-left :back-link="$t('Back')"></f7-nav-left>
|
||||||
<f7-searchbar
|
<f7-nav-title :title="$t('Transaction Details')"></f7-nav-title>
|
||||||
:custom-search="true"
|
<f7-nav-right v-if="false">
|
||||||
:placeholder="$t('Search')"
|
<f7-link icon-f7="ellipsis" @click="showMoreActionSheet = true"></f7-link>
|
||||||
:disable-button-text="$t('Cancel')"
|
</f7-nav-right>
|
||||||
></f7-searchbar>
|
|
||||||
</f7-subnavbar>
|
|
||||||
</f7-navbar>
|
</f7-navbar>
|
||||||
|
|
||||||
<f7-list media-list class="skeleton-text">
|
<f7-card class="skeleton-text" v-if="loading">
|
||||||
<f7-list-item title="Placeholder"></f7-list-item>
|
<f7-card-header>
|
||||||
</f7-list>
|
<div class="full-line">
|
||||||
|
<small :style="{ opacity: 0.6 }">YYYY-MM</small>
|
||||||
|
<f7-icon class="transaction-month-card-chevron-icon float-right" f7="chevron_up"></f7-icon>
|
||||||
|
</div>
|
||||||
|
</f7-card-header>
|
||||||
|
<f7-card-content class="no-safe-areas" :padding="false">
|
||||||
|
<f7-list media-list>
|
||||||
|
<f7-list-item class="transaction-info">
|
||||||
|
<div slot="media" class="display-flex no-padding-horizontal">
|
||||||
|
<div class="display-flex flex-direction-column transaction-date">
|
||||||
|
<span class="transaction-day full-line flex-direction-column">DD</span>
|
||||||
|
<span class="transaction-day-of-week full-line flex-direction-column">Sun</span>
|
||||||
|
</div>
|
||||||
|
<div class="transaction-icon display-flex align-items-center">
|
||||||
|
<f7-icon slot="media" f7="app_fill"></f7-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div slot="title" class="no-padding">
|
||||||
|
<span>Category</span>
|
||||||
|
</div>
|
||||||
|
<div slot="footer" class="no-padding-horizontal transaction-footer">
|
||||||
|
<span>HH:mm</span>
|
||||||
|
<span>·</span>
|
||||||
|
<span>Source Account</span>
|
||||||
|
</div>
|
||||||
|
<div slot="content-end" class="padding-right transaction-amount">
|
||||||
|
<span>0.00 USD</span>
|
||||||
|
</div>
|
||||||
|
</f7-list-item>
|
||||||
|
<f7-list-item class="transaction-info">
|
||||||
|
<div slot="media" class="display-flex no-padding-horizontal">
|
||||||
|
<div class="display-flex flex-direction-column transaction-date">
|
||||||
|
<span class="transaction-day full-line flex-direction-column">DD</span>
|
||||||
|
<span class="transaction-day-of-week full-line flex-direction-column">Sun</span>
|
||||||
|
</div>
|
||||||
|
<div class="transaction-icon display-flex align-items-center">
|
||||||
|
<f7-icon slot="media" f7="app_fill"></f7-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div slot="title" class="no-padding">
|
||||||
|
<span>Category 2</span>
|
||||||
|
</div>
|
||||||
|
<div slot="footer" class="no-padding-horizontal transaction-footer">
|
||||||
|
<span>HH:mm</span>
|
||||||
|
<span>·</span>
|
||||||
|
<span>Source Account</span>
|
||||||
|
</div>
|
||||||
|
<div slot="content-end" class="padding-right transaction-amount">
|
||||||
|
<span>0.00 USD</span>
|
||||||
|
</div>
|
||||||
|
</f7-list-item>
|
||||||
|
<f7-list-item class="transaction-info">
|
||||||
|
<div slot="media" class="display-flex no-padding-horizontal">
|
||||||
|
<div class="display-flex flex-direction-column transaction-date">
|
||||||
|
<span class="transaction-day full-line flex-direction-column">DD</span>
|
||||||
|
<span class="transaction-day-of-week full-line flex-direction-column">Sun</span>
|
||||||
|
</div>
|
||||||
|
<div class="transaction-icon display-flex align-items-center">
|
||||||
|
<f7-icon slot="media" f7="app_fill"></f7-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div slot="title" class="no-padding">
|
||||||
|
<span>Category 3</span>
|
||||||
|
</div>
|
||||||
|
<div slot="footer" class="no-padding-horizontal transaction-footer">
|
||||||
|
<span>HH:mm</span>
|
||||||
|
<span>·</span>
|
||||||
|
<span>Source Account</span>
|
||||||
|
</div>
|
||||||
|
<div slot="content-end" class="padding-right transaction-amount">
|
||||||
|
<span>0.00 USD</span>
|
||||||
|
</div>
|
||||||
|
</f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
</f7-card-content>
|
||||||
|
</f7-card>
|
||||||
|
|
||||||
|
<f7-card v-if="!loading && noTransaction">
|
||||||
|
<f7-card-content class="no-safe-areas" :padding="false">
|
||||||
|
<f7-list>
|
||||||
|
<f7-list-item :title="$t('No transaction data')"></f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
</f7-card-content>
|
||||||
|
</f7-card>
|
||||||
|
|
||||||
|
<f7-card v-for="transactionMonthList in transactions" :key="transactionMonthList.yearMonth">
|
||||||
|
<f7-accordion-item :opened="transactionMonthList.opened"
|
||||||
|
@accordion:open="transactionMonthList.opened = true"
|
||||||
|
@accordion:close="transactionMonthList.opened = false">
|
||||||
|
<f7-card-header>
|
||||||
|
<f7-accordion-toggle class="full-line">
|
||||||
|
<small :style="{ opacity: 0.6 }">
|
||||||
|
{{ transactionMonthList.yearMonth | moment($t('format.date.yearMonth')) }}
|
||||||
|
</small>
|
||||||
|
<f7-icon class="transaction-month-card-chevron-icon float-right" :f7="transactionMonthList.opened ? 'chevron_up' : 'chevron_down'"></f7-icon>
|
||||||
|
</f7-accordion-toggle>
|
||||||
|
</f7-card-header>
|
||||||
|
<f7-card-content class="no-safe-areas" :padding="false" accordion-list>
|
||||||
|
<f7-accordion-content style="height: auto">
|
||||||
|
<f7-list media-list>
|
||||||
|
<f7-list-item class="transaction-info"
|
||||||
|
v-for="(transaction, idx) in transactionMonthList.items"
|
||||||
|
:key="transaction.id" :id="transaction | transactionDomId"
|
||||||
|
swipeout
|
||||||
|
>
|
||||||
|
<div slot="media" class="display-flex no-padding-horizontal">
|
||||||
|
<div class="display-flex flex-direction-column transaction-date" :style="transaction | transactionDateStyle(idx > 0 ? transactionMonthList.items[idx - 1] : null)">
|
||||||
|
<span class="transaction-day full-line flex-direction-column">
|
||||||
|
{{ transaction.day }}
|
||||||
|
</span>
|
||||||
|
<span class="transaction-day-of-week full-line flex-direction-column">
|
||||||
|
{{ transaction.dayOfWeek }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="transaction-icon display-flex align-items-center">
|
||||||
|
<f7-icon v-if="transaction.category && transaction.category.color"
|
||||||
|
:icon="transaction.category.icon | categoryIcon"
|
||||||
|
:style="transaction.category.color | categoryIconStyle('var(--category-icon-color)')">
|
||||||
|
</f7-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div slot="title" class="no-padding">
|
||||||
|
<span v-if="transaction.type === $constants.transaction.allTransactionTypes.ModifyBalance">
|
||||||
|
{{ $t('Modify Balance') }}
|
||||||
|
</span>
|
||||||
|
<span v-else-if="transaction.type !== $constants.transaction.allTransactionTypes.ModifyBalance && transaction.category">
|
||||||
|
{{ transaction.category.name }}
|
||||||
|
</span>
|
||||||
|
<span v-else-if="transaction.type !== $constants.transaction.allTransactionTypes.ModifyBalance && !transaction.category">
|
||||||
|
{{ transaction.type | transactionTypeName($constants.transaction.allTransactionTypes) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div slot="footer" class="no-padding-horizontal transaction-footer">
|
||||||
|
<span>{{ transaction.time | moment($t('format.time.hourMinute')) }}</span>
|
||||||
|
<span v-if="transaction.sourceAccount">·</span>
|
||||||
|
<span v-if="transaction.sourceAccount">{{ transaction.sourceAccount.name }}</span>
|
||||||
|
</div>
|
||||||
|
<div slot="content-end" class="padding-right transaction-amount">
|
||||||
|
<span :class="{ 'text-color-teal': transaction.type === $constants.transaction.allTransactionTypes.Expense, 'text-color-red': transaction.type === $constants.transaction.allTransactionTypes.Income }">
|
||||||
|
{{ transaction.sourceAmount | currency(transaction.sourceAccount.currency) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<f7-swipeout-actions right>
|
||||||
|
<f7-swipeout-button color="orange" close :text="$t('Edit')" @click="edit(transaction)"></f7-swipeout-button>
|
||||||
|
<f7-swipeout-button color="red" class="padding-left padding-right" @click="remove(transaction)">
|
||||||
|
<f7-icon f7="trash"></f7-icon>
|
||||||
|
</f7-swipeout-button>
|
||||||
|
</f7-swipeout-actions>
|
||||||
|
</f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
</f7-accordion-content>
|
||||||
|
</f7-card-content>
|
||||||
|
</f7-accordion-item>
|
||||||
|
</f7-card>
|
||||||
|
|
||||||
|
<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-actions close-by-outside-click close-on-escape :opened="showDeleteActionSheet" @actions:closed="showDeleteActionSheet = false">
|
||||||
|
<f7-actions-group>
|
||||||
|
<f7-actions-label>{{ $t('Are you sure you want to delete this transaction?') }}</f7-actions-label>
|
||||||
|
<f7-actions-button color="red" @click="remove(transactionToDelete)">{{ $t('Delete') }}</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>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
transactions: [],
|
||||||
|
allAccounts: {},
|
||||||
|
allCategories: {},
|
||||||
|
allTags: {},
|
||||||
|
maxTime: 0,
|
||||||
|
loading: true,
|
||||||
|
transactionToDelete: null,
|
||||||
|
showMoreActionSheet: false,
|
||||||
|
showDeleteActionSheet: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
noTransaction() {
|
||||||
|
for (let i = 0; i < this.transactions.length; i++) {
|
||||||
|
const transactionMonthList = this.transactions[i];
|
||||||
|
|
||||||
|
for (let j = 0; j < transactionMonthList.items.length; j++) {
|
||||||
|
if (transactionMonthList.items[j]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.reload(null);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
reload(done) {
|
||||||
|
const self = this;
|
||||||
|
const router = self.$f7router;
|
||||||
|
|
||||||
|
self.loading = true;
|
||||||
|
self.maxTime = 0;
|
||||||
|
|
||||||
|
const promises = [
|
||||||
|
self.$services.getAllAccounts({ visibleOnly: true }),
|
||||||
|
self.$services.getAllTransactionCategories({}),
|
||||||
|
self.$services.getAllTransactionTags(),
|
||||||
|
self.$services.getTransactions({
|
||||||
|
maxTime: self.maxTime
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
Promise.all(promises).then(responses => {
|
||||||
|
if (done) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountData = responses[0].data;
|
||||||
|
const categoryData = responses[1].data;
|
||||||
|
const tagData = responses[2].data;
|
||||||
|
const transactionListData = responses[3].data;
|
||||||
|
|
||||||
|
if (!accountData || !accountData.success || !accountData.result) {
|
||||||
|
self.$toast('Unable to get account list');
|
||||||
|
if (!done) {
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!categoryData || !categoryData.success || !categoryData.result) {
|
||||||
|
self.$toast('Unable to get category list');
|
||||||
|
if (!done) {
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tagData || !tagData.success || !tagData.result) {
|
||||||
|
self.$toast('Unable to get tag list');
|
||||||
|
if (!done) {
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!transactionListData || !transactionListData.success || !transactionListData.result) {
|
||||||
|
self.$toast('Unable to get transaction list');
|
||||||
|
if (!done) {
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const allAccounts = self.$utilities.getPlainAccounts(accountData.result);
|
||||||
|
self.allAccounts = {};
|
||||||
|
|
||||||
|
for (let i = 0; i < allAccounts.length; i++) {
|
||||||
|
const account = allAccounts[i];
|
||||||
|
self.allAccounts[account.id] = account;
|
||||||
|
}
|
||||||
|
|
||||||
|
const allCategories = categoryData.result;
|
||||||
|
self.allCategories = {};
|
||||||
|
|
||||||
|
for (let categoryType in allCategories) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(allCategories, categoryType)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const categoryList = allCategories[categoryType];
|
||||||
|
|
||||||
|
for (let i = 0; i < categoryList.length; i++) {
|
||||||
|
const category = categoryList[i];
|
||||||
|
self.allCategories[category.id] = category;
|
||||||
|
|
||||||
|
for (let j = 0; j < category.subCategories.length; j++) {
|
||||||
|
const subCategory = category.subCategories[j];
|
||||||
|
self.allCategories[subCategory.id] = subCategory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const allTags = tagData.result;
|
||||||
|
self.allTags = {};
|
||||||
|
|
||||||
|
for (let i = 0; i < allTags.length; i++) {
|
||||||
|
const tag = allTags[i];
|
||||||
|
self.allTags[tag.id] = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.transactions = [];
|
||||||
|
self.setResult(transactionListData.result);
|
||||||
|
|
||||||
|
self.loading = false;
|
||||||
|
}).catch(error => {
|
||||||
|
self.$logger.error('failed to load transaction list', error);
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
self.$toast({ error: error.response.data });
|
||||||
|
if (!done) {
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
} else if (!error.processed) {
|
||||||
|
self.$toast('Unable to get transaction list');
|
||||||
|
if (!done) {
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadMore() {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
if (self.maxTime <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.$services.getTransactions({
|
||||||
|
maxTime: self.maxTime
|
||||||
|
}).then(response => {
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
self.$toast('Unable to get transaction list');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setResult(data.result);
|
||||||
|
}).catch(error => {
|
||||||
|
self.$logger.error('failed to reload transaction list', error);
|
||||||
|
|
||||||
|
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
self.$toast({ error: error.response.data });
|
||||||
|
} else if (!error.processed) {
|
||||||
|
self.$toast('Unable to get account list');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
edit(transaction) {
|
||||||
|
this.$f7router.navigate('/transaction/edit?id=' + transaction.id);
|
||||||
|
},
|
||||||
|
remove(transaction) {
|
||||||
|
const self = this;
|
||||||
|
const app = self.$f7;
|
||||||
|
const $$ = app.$;
|
||||||
|
|
||||||
|
if (!transaction) {
|
||||||
|
self.$alert('An error has occurred');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self.showDeleteActionSheet) {
|
||||||
|
self.transactionToDelete = transaction;
|
||||||
|
self.showDeleteActionSheet = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.showDeleteActionSheet = false;
|
||||||
|
self.transactionToDelete = null;
|
||||||
|
self.$showLoading();
|
||||||
|
|
||||||
|
self.$services.deleteTransaction({
|
||||||
|
id: transaction.id
|
||||||
|
}).then(response => {
|
||||||
|
self.$hideLoading();
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
self.$toast('Unable to delete this transaction');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.swipeout.delete($$(`#${self.$options.filters.transactionDomId(transaction)}`), () => {
|
||||||
|
for (let i = 0; i < self.transactions.length; i++) {
|
||||||
|
const transactionMonthList = self.transactions[i];
|
||||||
|
|
||||||
|
if (!transactionMonthList.items ||
|
||||||
|
transactionMonthList.items[0].time < transaction.time ||
|
||||||
|
transactionMonthList.items[transactionMonthList.items.length - 1].time > transaction.time) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = 0; j < transactionMonthList.items.length; j++) {
|
||||||
|
if (transactionMonthList.items[j].id === transactionMonthList.items.id) {
|
||||||
|
transactionMonthList.items.splice(j, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
self.$logger.error('failed to delete transaction', error);
|
||||||
|
|
||||||
|
self.$hideLoading();
|
||||||
|
|
||||||
|
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
self.$toast({ error: error.response.data });
|
||||||
|
} else if (!error.processed) {
|
||||||
|
self.$toast('Unable to delete this transaction');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setResult(result) {
|
||||||
|
if (result.items && result.items.length) {
|
||||||
|
let currentMonthListIndex = -1;
|
||||||
|
let currentMonthList = null;
|
||||||
|
|
||||||
|
for (let i = 0; i < result.items.length; i++) {
|
||||||
|
const transaction = result.items[i];
|
||||||
|
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];
|
||||||
|
transaction.destinationAccount = this.allAccounts[transaction.destinationAccountId];
|
||||||
|
transaction.category = this.allCategories[transaction.categoryId];
|
||||||
|
transaction.tags = [];
|
||||||
|
|
||||||
|
if (transaction.tagIds && transaction.tagIds.length) {
|
||||||
|
for (let j = 0; j < transaction.tagIds.length; j++) {
|
||||||
|
const tag = this.allTags[transaction.tagIds[j]];
|
||||||
|
|
||||||
|
if (tag) {
|
||||||
|
transaction.tags.push(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const transactionYear = this.$utilities.getYear(transactionTime);
|
||||||
|
const transactionMonth = this.$utilities.getMonth(transactionTime);
|
||||||
|
|
||||||
|
if (currentMonthList && currentMonthList.year === transactionYear && currentMonthList.month === transactionMonth) {
|
||||||
|
currentMonthList.items.push(transaction);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = currentMonthListIndex + 1; j < this.transactions.length; j++) {
|
||||||
|
if (this.transactions[j].year === transactionYear && this.transactions[j].month === transactionMonth) {
|
||||||
|
currentMonthListIndex = j;
|
||||||
|
currentMonthList = this.transactions[j];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentMonthList || !currentMonthList.year !== transactionYear || !currentMonthList.month === transactionMonth) {
|
||||||
|
this.transactions.push({
|
||||||
|
year: transactionYear,
|
||||||
|
month: transactionMonth,
|
||||||
|
yearMonth: `${transactionYear}-${transactionMonth}`,
|
||||||
|
opened: true,
|
||||||
|
items: []
|
||||||
|
});
|
||||||
|
|
||||||
|
currentMonthListIndex = this.transactions.length - 1;
|
||||||
|
currentMonthList = this.transactions[this.transactions.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMonthList.items.push(transaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.nextTimeSequenceId) {
|
||||||
|
this.maxTime = result.nextTimeSequenceId;
|
||||||
|
} else {
|
||||||
|
this.maxTime = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
transactionTypeName(type, allTransactionTypes) {
|
||||||
|
if (type === allTransactionTypes.Income) {
|
||||||
|
return 'Income';
|
||||||
|
} else if (type === allTransactionTypes.Expense) {
|
||||||
|
return 'Expense';
|
||||||
|
} else if (type === allTransactionTypes.Income) {
|
||||||
|
return 'Transfer';
|
||||||
|
} else {
|
||||||
|
return 'Transaction';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transactionDomId(transaction) {
|
||||||
|
return 'transaction_' + transaction.id;
|
||||||
|
},
|
||||||
|
transactionDateStyle(transaction, previousTransaction) {
|
||||||
|
if (!previousTransaction || transaction.day !== previousTransaction.day) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
color: 'transparent'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.transaction-month-card-chevron-icon {
|
||||||
|
color: var(--f7-list-chevron-icon-color);
|
||||||
|
font-size: var(--f7-list-chevron-icon-font-size);
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transaction-info .item-media {
|
||||||
|
height: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transaction-info .item-media + .item-inner {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transaction-date {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transaction-day {
|
||||||
|
opacity: 0.6;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transaction-day-of-week {
|
||||||
|
opacity: 0.6;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transaction-footer {
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transaction-footer > span {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transaction-amount {
|
||||||
|
color: var(--f7-list-item-after-text-color);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transaction-info .item-inner:after {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transaction-info .transaction-icon:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
background-color: var(--f7-list-item-border-color);
|
||||||
|
display: block;
|
||||||
|
z-index: 15;
|
||||||
|
top: auto;
|
||||||
|
right: auto;
|
||||||
|
bottom: 0;
|
||||||
|
height: 1px;
|
||||||
|
width: 100%;
|
||||||
|
transform-origin: 50% 100%;
|
||||||
|
transform: scaleY(calc(1 / var(--f7-device-pixel-ratio)));
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user