diff --git a/cmd/webserver.go b/cmd/webserver.go
index d8b3deeb..c333199d 100644
--- a/cmd/webserver.go
+++ b/cmd/webserver.go
@@ -161,6 +161,7 @@ func startWebServer(c *cli.Context) error {
// Accounts
apiV1Route.GET("/accounts/list.json", bindApi(api.Accounts.AccountListHandler))
apiV1Route.POST("/accounts/add.json", bindApi(api.Accounts.AccountCreateHandler))
+ apiV1Route.POST("/accounts/move.json", bindApi(api.Accounts.AccountMoveHandler))
apiV1Route.POST("/accounts/delete.json", bindApi(api.Accounts.AccountDeleteHandler))
}
}
diff --git a/pkg/api/accounts.go b/pkg/api/accounts.go
index 8bbb398a..06eb8b2e 100644
--- a/pkg/api/accounts.go
+++ b/pkg/api/accounts.go
@@ -124,6 +124,40 @@ func (a *AccountsApi) AccountCreateHandler(c *core.Context) (interface{}, *errs.
return accountInfoResp, nil
}
+func (a *AccountsApi) AccountMoveHandler(c *core.Context) (interface{}, *errs.Error) {
+ var accountMoveReq models.AccountMoveRequest
+ err := c.ShouldBindJSON(&accountMoveReq)
+
+ if err != nil {
+ log.WarnfWithRequestId(c, "[accounts.AccountMoveHandler] parse request failed, because %s", err.Error())
+ return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
+ }
+
+ uid := c.GetCurrentUid()
+ accounts := make([]*models.Account, len(accountMoveReq.NewDisplayOrders))
+
+ for i := 0; i < len(accountMoveReq.NewDisplayOrders); i++ {
+ newDisplayOrder := accountMoveReq.NewDisplayOrders[i]
+ account := &models.Account{
+ Uid: uid,
+ AccountId: newDisplayOrder.Id,
+ DisplayOrder: newDisplayOrder.DisplayOrder,
+ }
+
+ accounts[i] = account
+ }
+
+ err = a.accounts.ModifyAccountDisplayOrders(uid, accounts)
+
+ if err != nil {
+ log.ErrorfWithRequestId(c, "[accounts.AccountMoveHandler] failed to move accounts for user \"uid:%d\", because %s", uid, err.Error())
+ return nil, errs.Or(err, errs.ErrOperationFailed)
+ }
+
+ log.InfofWithRequestId(c, "[accounts.AccountMoveHandler] user \"uid:%d\" has moved accounts", uid)
+ return true, nil
+}
+
func (a *AccountsApi) AccountDeleteHandler(c *core.Context) (interface{}, *errs.Error) {
var accountDeleteReq models.AccountDeleteRequest
err := c.ShouldBindJSON(&accountDeleteReq)
diff --git a/pkg/models/account.go b/pkg/models/account.go
index 16271a46..bd29fb96 100644
--- a/pkg/models/account.go
+++ b/pkg/models/account.go
@@ -51,6 +51,15 @@ type AccountCreateRequest struct {
SubAccounts []*AccountCreateRequest `json:"subAccounts" binding:"omitempty"`
}
+type AccountMoveRequest struct {
+ NewDisplayOrders []*AccountNewDisplayOrderRequest `json:"newDisplayOrders"`
+}
+
+type AccountNewDisplayOrderRequest struct {
+ Id int64 `json:"id,string" binding:"required,min=1"`
+ DisplayOrder int `json:"displayOrder"`
+}
+
type AccountDeleteRequest struct {
Id int64 `json:"id,string" binding:"required,min=1"`
}
diff --git a/pkg/services/accounts.go b/pkg/services/accounts.go
index aace7b9d..94e7e26b 100644
--- a/pkg/services/accounts.go
+++ b/pkg/services/accounts.go
@@ -122,6 +122,25 @@ func (s *AccountService) CreateAccounts(mainAccount *models.Account, childrenAcc
})
}
+func (s *AccountService) ModifyAccountDisplayOrders(uid int64, accounts []*models.Account) error {
+ for i := 0; i < len(accounts); i++ {
+ accounts[i].UpdatedUnixTime = time.Now().Unix()
+ }
+
+ return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
+ for i := 0; i < len(accounts); i++ {
+ account := accounts[i]
+ _, err := sess.Cols("display_order", "updated_unix_time").Where("account_id=? AND uid=? AND deleted=?", account.AccountId, account.Uid, false).Update(account)
+
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+ })
+}
+
func (s *AccountService) DeleteAccounts(uid int64, ids []int64) error {
if uid <= 0 {
return errs.ErrUserIdInvalid
diff --git a/src/Mobile.vue b/src/Mobile.vue
index b05dc017..f986b8bf 100644
--- a/src/Mobile.vue
+++ b/src/Mobile.vue
@@ -18,6 +18,9 @@ export default {
theme: 'ios',
autoDarkTheme: self.$settings.isEnableAutoDarkMode(),
routes: routes,
+ touch: {
+ tapHold: true
+ },
sheet: {
backdrop: true,
closeOnEscape: true
diff --git a/src/lib/services.js b/src/lib/services.js
index 169e3418..f1346f9c 100644
--- a/src/lib/services.js
+++ b/src/lib/services.js
@@ -174,6 +174,11 @@ export default {
subAccounts
});
},
+ moveAccount: ({ newDisplayOrders }) => {
+ return axios.post('v1/accounts/move.json', {
+ newDisplayOrders,
+ });
+ },
deleteAccount: ({ id }) => {
return axios.post('v1/accounts/delete.json', {
id
diff --git a/src/lib/utils.js b/src/lib/utils.js
index caad77a3..0445fb8e 100644
--- a/src/lib/utils.js
+++ b/src/lib/utils.js
@@ -43,6 +43,24 @@ function getCategorizedAccounts(allAccounts) {
return ret;
}
+function getAccountByAccountId(categorizedAccounts, accountId) {
+ for (let category in categorizedAccounts) {
+ if (!Object.prototype.hasOwnProperty.call(categorizedAccounts, category)) {
+ continue;
+ }
+
+ const accountList = categorizedAccounts[category];
+
+ for (let i = 0; i < accountList.length; i++) {
+ if (accountList[i].id === accountId) {
+ return accountList[i];
+ }
+ }
+ }
+
+ return null;
+}
+
export default {
isFunction,
isObject,
@@ -51,4 +69,5 @@ export default {
isNumber,
isBoolean,
getCategorizedAccounts,
+ getAccountByAccountId,
};
diff --git a/src/locales/en.js b/src/locales/en.js
index c0d6dd3f..83580950 100644
--- a/src/locales/en.js
+++ b/src/locales/en.js
@@ -323,6 +323,7 @@ export default {
'Account currency cannot be empty': 'Account currency cannot be empty',
'You have added a new account': 'You have added a new account',
'Unable to add account': 'Unable to add 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?',
'Unable to delete this account': 'Unable to delete this account',
'User Profile': 'User Profile',
diff --git a/src/locales/zh_Hans.js b/src/locales/zh_Hans.js
index 460d31bc..95f2df76 100644
--- a/src/locales/zh_Hans.js
+++ b/src/locales/zh_Hans.js
@@ -323,6 +323,7 @@ export default {
'Account currency cannot be empty': '账户货币不能为空',
'You have added a new account': '您已经添加新账户',
'Unable to add account': '无法添加账户',
+ 'Unable to move account': '无法移动账户',
'Are you sure you want to delete this account?': '您确定要删除该账户吗?',
'Unable to delete this account': '无法删除该账户',
'User Profile': '用户信息',
diff --git a/src/views/mobile/accounts/AccountList.vue b/src/views/mobile/accounts/AccountList.vue
index fb4327cb..446b0599 100644
--- a/src/views/mobile/accounts/AccountList.vue
+++ b/src/views/mobile/accounts/AccountList.vue
@@ -4,7 +4,8 @@
-
+
+
@@ -40,11 +41,11 @@
{{ $t(accountCategory.name) }}
-
+
@@ -64,7 +65,9 @@ export default {
data() {
return {
accounts: {},
- loading: true
+ loading: true,
+ sortable: false,
+ savingSort: false
};
},
computed: {
@@ -140,6 +143,74 @@ export default {
}
});
},
+ setSortable() {
+ this.sortable = true;
+ },
+ onSort(event) {
+ if (!event || !event.el || !event.el.id || event.el.id.indexOf('account_') !== 0) {
+ this.$toast('Unable to move account');
+ return;
+ }
+
+ const id = event.el.id.substr(8);
+ const account = this.$utilities.getAccountByAccountId(this.accounts, id);
+
+ if (!account || !this.accounts[account.category] || !this.accounts[account.category][event.to]) {
+ this.$toast('Unable to move account');
+ return;
+ }
+
+ const accountList = this.accounts[account.category];
+ accountList.splice(event.to, 0, accountList.splice(event.from, 1)[0]);
+ },
+ saveSortResult() {
+ const self = this;
+ const newDisplayOrders = [];
+
+ self.savingSort = true;
+
+ for (let category in self.accounts) {
+ if (!Object.prototype.hasOwnProperty.call(self.accounts, category)) {
+ continue;
+ }
+
+ const accountList = self.accounts[category];
+
+ for (let i = 0; i < accountList.length; i++) {
+ newDisplayOrders.push({
+ id: accountList[i].id,
+ displayOrder: i + 1
+ });
+ }
+ }
+
+ self.$showLoading();
+
+ self.$services.moveAccount({
+ newDisplayOrders: newDisplayOrders
+ }).then(response => {
+ self.savingSort = false;
+ self.$hideLoading();
+
+ const data = response.data;
+
+ if (!data || !data.success || !data.result) {
+ self.$alert('Unable to move account');
+ return;
+ }
+
+ self.sortable = false;
+ }).catch(error => {
+ self.savingSort = false;
+ self.$hideLoading();
+
+ if (error.response && error.response.data && error.response.data.errorMessage) {
+ self.$alert({ error: error.response.data });
+ } else if (!error.processed) {
+ self.$alert('Unable to move account');
+ }
+ });
+ },
edit() {
},
@@ -174,7 +245,7 @@ export default {
self.$hideLoading();
if (error.response && error.response.data && error.response.data.errorMessage) {
- self.$alert({error: error.response.data});
+ self.$alert({ error: error.response.data });
} else if (!error.processed) {
self.$alert('Unable to delete this account');
}