From a7dcd4e704ddd9005cb097db0c7c88665c324dd9 Mon Sep 17 00:00:00 2001 From: MaysWind Date: Thu, 12 Nov 2020 23:51:16 +0800 Subject: [PATCH] support sort account --- cmd/webserver.go | 1 + pkg/api/accounts.go | 34 ++++++++++ pkg/models/account.go | 9 +++ pkg/services/accounts.go | 19 ++++++ src/Mobile.vue | 3 + src/lib/services.js | 5 ++ src/lib/utils.js | 19 ++++++ src/locales/en.js | 1 + src/locales/zh_Hans.js | 1 + src/views/mobile/accounts/AccountList.vue | 81 +++++++++++++++++++++-- 10 files changed, 168 insertions(+), 5 deletions(-) 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'); }