mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-19 01:04:25 +08:00
add category edit ui
This commit is contained in:
@@ -125,6 +125,25 @@ func (a *TransactionCategoriesApi) CategoryCreateHandler(c *core.Context) (inter
|
|||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
|
|
||||||
|
if categoryCreateReq.ParentId > 0 {
|
||||||
|
parentCategory, err := a.categories.GetCategoryByCategoryId(uid, categoryCreateReq.ParentId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[transaction_categories.CategoryCreateHandler] failed to get parent category \"id:%d\" for user \"uid:%d\", because %s", categoryCreateReq.ParentId, uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if parentCategory == nil {
|
||||||
|
log.WarnfWithRequestId(c, "[transaction_categories.CategoryCreateHandler] parent category \"id:%d\" does not exist for user \"uid:%d\"", categoryCreateReq.ParentId, uid)
|
||||||
|
return nil, errs.ErrParentTransactionCategoryNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if parentCategory.ParentCategoryId > 0 {
|
||||||
|
log.WarnfWithRequestId(c, "[transaction_categories.CategoryCreateHandler] parent category \"id:%d\" has another parent category \"id:%d\" for user \"uid:%d\"", parentCategory.CategoryId, parentCategory.ParentCategoryId, uid)
|
||||||
|
return nil, errs.ErrCannotAddToSecondaryTransactionCategory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var maxOrderId = 0
|
var maxOrderId = 0
|
||||||
|
|
||||||
if categoryCreateReq.ParentId <= 0 {
|
if categoryCreateReq.ParentId <= 0 {
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ package errs
|
|||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrTransactionCategoryIdInvalid = NewNormalError(NORMAL_SUBCATEGORY_CATEGORY, 0, http.StatusBadRequest, "transaction category id is invalid")
|
ErrTransactionCategoryIdInvalid = NewNormalError(NORMAL_SUBCATEGORY_CATEGORY, 0, http.StatusBadRequest, "transaction category id is invalid")
|
||||||
ErrTransactionCategoryNotFound = NewNormalError(NORMAL_SUBCATEGORY_CATEGORY, 1, http.StatusBadRequest, "transaction category not found")
|
ErrTransactionCategoryNotFound = NewNormalError(NORMAL_SUBCATEGORY_CATEGORY, 1, http.StatusBadRequest, "transaction category not found")
|
||||||
ErrTransactionCategoryTypeInvalid = NewNormalError(NORMAL_SUBCATEGORY_CATEGORY, 2, http.StatusBadRequest, "transaction category type is invalid")
|
ErrTransactionCategoryTypeInvalid = NewNormalError(NORMAL_SUBCATEGORY_CATEGORY, 2, http.StatusBadRequest, "transaction category type is invalid")
|
||||||
|
ErrParentTransactionCategoryNotFound = NewNormalError(NORMAL_SUBCATEGORY_CATEGORY, 3, http.StatusBadRequest, "parent transaction category not found")
|
||||||
|
ErrCannotAddToSecondaryTransactionCategory = NewNormalError(NORMAL_SUBCATEGORY_CATEGORY, 4, http.StatusBadRequest, "cannot add to secondary transaction category")
|
||||||
)
|
)
|
||||||
|
|||||||
+6
-4
@@ -1,5 +1,5 @@
|
|||||||
const defaultAccountColor = '000000';
|
const defaultColor = '000000';
|
||||||
const allAccountColors = [
|
const allAvailableColors = [
|
||||||
'000000', // black
|
'000000', // black
|
||||||
'8e8e93', // gray
|
'8e8e93', // gray
|
||||||
'ff3b30', // red
|
'ff3b30', // red
|
||||||
@@ -17,6 +17,8 @@ const allAccountColors = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
allAccountColors: allAccountColors,
|
allAccountColors: allAvailableColors,
|
||||||
defaultAccountColor: defaultAccountColor,
|
defaultAccountColor: defaultColor,
|
||||||
|
allCategoryColors: allAvailableColors,
|
||||||
|
defaultCategoryColor: defaultColor,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -157,6 +157,15 @@ const allAccountIcons = {
|
|||||||
icon: 'lab la-weixin'
|
icon: 'lab la-weixin'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const defaultCategoryIconId = '1';
|
||||||
|
const allCategoryIcons = {
|
||||||
|
// 1 - 99 : Expense - Food & Drink
|
||||||
|
'1': {
|
||||||
|
icon: 'las la-utensils'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const deviceIcons = {
|
const deviceIcons = {
|
||||||
mobile: {
|
mobile: {
|
||||||
f7Icon: 'device_phone_portrait'
|
f7Icon: 'device_phone_portrait'
|
||||||
@@ -179,5 +188,8 @@ export default {
|
|||||||
allAccountIcons: allAccountIcons,
|
allAccountIcons: allAccountIcons,
|
||||||
defaultAccountIconId: defaultAccountIconId,
|
defaultAccountIconId: defaultAccountIconId,
|
||||||
defaultAccountIcon: allAccountIcons[defaultAccountIconId],
|
defaultAccountIcon: allAccountIcons[defaultAccountIconId],
|
||||||
|
allCategoryIcons: allCategoryIcons,
|
||||||
|
defaultCategoryIconId: defaultCategoryIconId,
|
||||||
|
defaultCategoryIcon: allCategoryIcons[defaultCategoryIconId],
|
||||||
deviceIcons: deviceIcons,
|
deviceIcons: deviceIcons,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import icons from "../consts/icon.js";
|
||||||
|
import utils from "../lib/utils.js";
|
||||||
|
|
||||||
|
export default function (iconId) {
|
||||||
|
if (utils.isNumber(iconId)) {
|
||||||
|
iconId = iconId.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!icons.allCategoryIcons[iconId]) {
|
||||||
|
return icons.defaultCategoryIcon.icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
return icons.allCategoryIcons[iconId].icon;
|
||||||
|
}
|
||||||
@@ -224,6 +224,8 @@ export default {
|
|||||||
'transaction category id is invalid': 'Transaction category ID is invalid',
|
'transaction category id is invalid': 'Transaction category ID is invalid',
|
||||||
'transaction category not found': 'Transaction category is not found',
|
'transaction category not found': 'Transaction category is not found',
|
||||||
'transaction category type is invalid': 'Transaction category type is invalid',
|
'transaction category type is invalid': 'Transaction category type is invalid',
|
||||||
|
'parent transaction category not found': 'Parent transaction category is not found',
|
||||||
|
'cannot add to secondary transaction category': 'Cannot add transaction category to secondary transaction category',
|
||||||
},
|
},
|
||||||
'parameter': {
|
'parameter': {
|
||||||
'id': 'ID',
|
'id': 'ID',
|
||||||
@@ -399,6 +401,36 @@ export default {
|
|||||||
'Current': 'Current',
|
'Current': 'Current',
|
||||||
'Other Device': 'Other Device',
|
'Other Device': 'Other Device',
|
||||||
'Unknown Device': 'Unknown Device',
|
'Unknown Device': 'Unknown Device',
|
||||||
|
'Transaction Categories': 'Transaction Categories',
|
||||||
|
'Expense Primary Categories': 'Expense Primary Categories',
|
||||||
|
'Income Primary Categories': 'Income Primary Categories',
|
||||||
|
'Transfer Primary Categories': 'Transfer Primary Categories',
|
||||||
|
'Transaction Primary Categories': 'Transaction Primary Categories',
|
||||||
|
'Expense Secondary Categories': 'Expense Secondary Categories',
|
||||||
|
'Income Secondary Categories': 'Income Secondary Categories',
|
||||||
|
'Transfer Secondary Categories': 'Transfer Secondary Categories',
|
||||||
|
'Transaction Secondary Categories': 'Transaction Secondary Categories',
|
||||||
|
'No available category': 'No available category',
|
||||||
|
'Unable to get category list': 'Unable to get category list',
|
||||||
|
'Unable to move category': 'Unable to move category',
|
||||||
|
'Unable to hide this category': 'Unable to hide this category',
|
||||||
|
'Unable to unhide this category': 'Unable to unhide this category',
|
||||||
|
'Are you sure you want to delete this category?': 'Are you sure you want to delete this category?',
|
||||||
|
'Unable to delete this category': 'Unable to delete this category',
|
||||||
|
'Add Primary Category': 'Add Primary Category',
|
||||||
|
'Add Secondary Category': 'Add Secondary Category',
|
||||||
|
'Edit Category': 'Edit Category',
|
||||||
|
'Category Name': 'Category Name',
|
||||||
|
'Your category name': 'Your category name',
|
||||||
|
'Category Icon': 'Category Icon',
|
||||||
|
'Category Color': 'Category Color',
|
||||||
|
'Your category description (optional)': 'Your category description (optional)',
|
||||||
|
'Category name cannot be empty': 'Category name cannot be empty',
|
||||||
|
'Unable to get category': 'Unable to get category',
|
||||||
|
'Unable to add category': 'Unable to add category',
|
||||||
|
'Unable to save category': 'Unable to save category',
|
||||||
|
'You have added a new category': 'You have added a new category',
|
||||||
|
'You have saved this category': 'You have saved this category',
|
||||||
'Are you sure you want to logout from this session?': 'Are you sure you want to logout from this session?',
|
'Are you sure you want to logout from this session?': 'Are you sure you want to logout from this session?',
|
||||||
'Unable to logout from this session': 'Unable to logout from this session',
|
'Unable to logout from this session': 'Unable to logout from this session',
|
||||||
'Are you sure you want to logout all other sessions?': 'Are you sure you want to logout all other sessions?',
|
'Are you sure you want to logout all other sessions?': 'Are you sure you want to logout all other sessions?',
|
||||||
@@ -447,4 +479,5 @@ export default {
|
|||||||
'Official Website': 'Official Website',
|
'Official Website': 'Official Website',
|
||||||
'License': 'License',
|
'License': 'License',
|
||||||
'An error has occurred': 'An error has occurred',
|
'An error has occurred': 'An error has occurred',
|
||||||
|
'Parameter Invalid': 'Parameter Invalid',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -224,6 +224,8 @@ export default {
|
|||||||
'transaction category id is invalid': '交易分类ID无效',
|
'transaction category id is invalid': '交易分类ID无效',
|
||||||
'transaction category not found': '交易分类不存在',
|
'transaction category not found': '交易分类不存在',
|
||||||
'transaction category type is invalid': '交易分类类型无效',
|
'transaction category type is invalid': '交易分类类型无效',
|
||||||
|
'parent transaction category not found': '父级交易分类不存在',
|
||||||
|
'cannot add to secondary transaction category': '不能在二级交易分类中添加',
|
||||||
},
|
},
|
||||||
'parameter': {
|
'parameter': {
|
||||||
'id': 'ID',
|
'id': 'ID',
|
||||||
@@ -399,6 +401,36 @@ export default {
|
|||||||
'Current': '当前',
|
'Current': '当前',
|
||||||
'Other Device': '其他设备',
|
'Other Device': '其他设备',
|
||||||
'Unknown Device': '未知设备',
|
'Unknown Device': '未知设备',
|
||||||
|
'Transaction Categories': '交易分类',
|
||||||
|
'Expense Primary Categories': '支出一级分类',
|
||||||
|
'Income Primary Categories': '收入一级分类',
|
||||||
|
'Transfer Primary Categories': '转账一级分类',
|
||||||
|
'Transaction Primary Categories': '交易一级分类',
|
||||||
|
'Expense Secondary Categories': '支出二级分类',
|
||||||
|
'Income Secondary Categories': '收入二级分类',
|
||||||
|
'Transfer Secondary Categories': '转账二级分类',
|
||||||
|
'Transaction Secondary Categories': '交易二级分类',
|
||||||
|
'No available category': '没有可用的分类',
|
||||||
|
'Unable to get category list': '无法获取分类列表',
|
||||||
|
'Unable to move category': '无法移动分类',
|
||||||
|
'Unable to hide this category': '无法隐藏该分类',
|
||||||
|
'Unable to unhide this category': '无法取消隐藏该分类',
|
||||||
|
'Are you sure you want to delete this category?': '您确定要删除该分类?',
|
||||||
|
'Unable to delete this category': '无法删除该分类',
|
||||||
|
'Add Primary Category': '添加一级分类',
|
||||||
|
'Add Secondary Category': '添加二级分类',
|
||||||
|
'Edit Category': '编辑分类',
|
||||||
|
'Category Name': '分类名称',
|
||||||
|
'Your category name': '你的分类名称',
|
||||||
|
'Category Icon': '分类图标',
|
||||||
|
'Category Color': '分类颜色',
|
||||||
|
'Your category description (optional)': '你的分类描述 (可选)',
|
||||||
|
'Category name cannot be empty': '分类名称不能为空',
|
||||||
|
'Unable to get category': '无法获取分类',
|
||||||
|
'Unable to add category': '无法添加分类',
|
||||||
|
'Unable to save category': '无法保存分类',
|
||||||
|
'You have added a new category': '您已经添加新分类',
|
||||||
|
'You have saved this category': '您已经保存该分类',
|
||||||
'Are you sure you want to logout from this session?': '您确定要退出该会话?',
|
'Are you sure you want to logout from this session?': '您确定要退出该会话?',
|
||||||
'Unable to logout from this session': '无法退出该会话',
|
'Unable to logout from this session': '无法退出该会话',
|
||||||
'Are you sure you want to logout all other sessions?': '您确定要退出其他所有会话?',
|
'Are you sure you want to logout all other sessions?': '您确定要退出其他所有会话?',
|
||||||
@@ -447,4 +479,5 @@ export default {
|
|||||||
'Official Website': '官方网站',
|
'Official Website': '官方网站',
|
||||||
'License': '许可协议',
|
'License': '许可协议',
|
||||||
'An error has occurred': '发生错误',
|
'An error has occurred': '发生错误',
|
||||||
|
'Parameter Invalid': '参数错误',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import webauthn from './lib/webauthn.js';
|
|||||||
import utils from './lib/utils.js';
|
import utils from './lib/utils.js';
|
||||||
import currencyFilter from './filters/currency.js';
|
import currencyFilter from './filters/currency.js';
|
||||||
import accountIconFilter from './filters/accountIcon.js';
|
import accountIconFilter from './filters/accountIcon.js';
|
||||||
|
import categoryIconFilter from './filters/categoryIcon.js';
|
||||||
import tokenDeviceFilter from './filters/tokenDevice.js';
|
import tokenDeviceFilter from './filters/tokenDevice.js';
|
||||||
import tokenIconFilter from './filters/tokenIcon.js';
|
import tokenIconFilter from './filters/tokenIcon.js';
|
||||||
import App from './Mobile.vue';
|
import App from './Mobile.vue';
|
||||||
@@ -178,6 +179,7 @@ Vue.prototype.$user = userstate;
|
|||||||
|
|
||||||
Vue.filter('currency', (value, currencyCode) => currencyFilter({ i18n }, value, currencyCode));
|
Vue.filter('currency', (value, currencyCode) => currencyFilter({ i18n }, value, currencyCode));
|
||||||
Vue.filter('accountIcon', (value) => accountIconFilter(value));
|
Vue.filter('accountIcon', (value) => accountIconFilter(value));
|
||||||
|
Vue.filter('categoryIcon', (value) => categoryIconFilter(value));
|
||||||
Vue.filter('tokenDevice', (value) => tokenDeviceFilter(value));
|
Vue.filter('tokenDevice', (value) => tokenDeviceFilter(value));
|
||||||
Vue.filter('tokenIcon', (value) => tokenIconFilter(value));
|
Vue.filter('tokenIcon', (value) => tokenIconFilter(value));
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,15 @@ import SettingsPage from '../views/mobile/Settings.vue';
|
|||||||
import ApplicationLockPage from '../views/mobile/ApplicationLock.vue';
|
import ApplicationLockPage from '../views/mobile/ApplicationLock.vue';
|
||||||
import ExchangeRatesPage from "../views/mobile/ExchangeRates.vue";
|
import ExchangeRatesPage from "../views/mobile/ExchangeRates.vue";
|
||||||
import AboutPage from "../views/mobile/About.vue";
|
import AboutPage from "../views/mobile/About.vue";
|
||||||
|
|
||||||
import UserProfilePage from "../views/mobile/users/UserProfile.vue";
|
import UserProfilePage from "../views/mobile/users/UserProfile.vue";
|
||||||
import TwoFactorAuthPage from "../views/mobile/users/TwoFactorAuth.vue";
|
import TwoFactorAuthPage from "../views/mobile/users/TwoFactorAuth.vue";
|
||||||
import SessionListPage from "../views/mobile/users/SessionList.vue";
|
import SessionListPage from "../views/mobile/users/SessionList.vue";
|
||||||
|
|
||||||
|
import CategoryAllPage from "../views/mobile/categories/CategoryAll.vue";
|
||||||
|
import CategoryListPage from "../views/mobile/categories/CategoryList.vue";
|
||||||
|
import CategoryEditPage from "../views/mobile/categories/CategoryEdit.vue";
|
||||||
|
|
||||||
function checkLogin(to, from, resolve, reject) {
|
function checkLogin(to, from, resolve, reject) {
|
||||||
const router = this;
|
const router = this;
|
||||||
|
|
||||||
@@ -179,6 +184,26 @@ const routes = [
|
|||||||
component: SessionListPage,
|
component: SessionListPage,
|
||||||
beforeEnter: checkLogin
|
beforeEnter: checkLogin
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/category/all',
|
||||||
|
component: CategoryAllPage,
|
||||||
|
beforeEnter: checkLogin
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/category/list',
|
||||||
|
component: CategoryListPage,
|
||||||
|
beforeEnter: checkLogin
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/category/add',
|
||||||
|
component: CategoryEditPage,
|
||||||
|
beforeEnter: checkLogin
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/category/edit',
|
||||||
|
component: CategoryEditPage,
|
||||||
|
beforeEnter: checkLogin
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '(.*)',
|
path: '(.*)',
|
||||||
redirect: '/'
|
redirect: '/'
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
<f7-card-content class="no-safe-areas" :padding="false">
|
<f7-card-content class="no-safe-areas" :padding="false">
|
||||||
<f7-list>
|
<f7-list>
|
||||||
<f7-list-item :title="$t('User Profile')" link="/user/profile"></f7-list-item>
|
<f7-list-item :title="$t('User Profile')" link="/user/profile"></f7-list-item>
|
||||||
|
<f7-list-item :title="$t('Transaction Categories')" link="/category/all"></f7-list-item>
|
||||||
<f7-list-item :title="$t('Two-Factor Authentication')" link="/user/2fa"></f7-list-item>
|
<f7-list-item :title="$t('Two-Factor Authentication')" link="/user/2fa"></f7-list-item>
|
||||||
<f7-list-item :title="$t('Device & Sessions')" link="/user/sessions"></f7-list-item>
|
<f7-list-item :title="$t('Device & Sessions')" link="/user/sessions"></f7-list-item>
|
||||||
<f7-list-button :class="{ 'disabled': logouting }" @click="logout">{{ $t('Log Out') }}</f7-list-button>
|
<f7-list-button :class="{ 'disabled': logouting }" @click="logout">{{ $t('Log Out') }}</f7-list-button>
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
<f7-page>
|
||||||
|
<f7-navbar :title="$t('Transaction Categories')" :back-link="$t('Back')"></f7-navbar>
|
||||||
|
|
||||||
|
<f7-card>
|
||||||
|
<f7-card-content class="no-safe-areas" :padding="false">
|
||||||
|
<f7-list>
|
||||||
|
<f7-list-item :title="$t('Expense')" link="/category/list?type=1"></f7-list-item>
|
||||||
|
<f7-list-item :title="$t('Income')" link="/category/list?type=2"></f7-list-item>
|
||||||
|
<f7-list-item :title="$t('Transfer')" link="/category/list?type=3"></f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
</f7-card-content>
|
||||||
|
</f7-card>
|
||||||
|
</f7-page>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,373 @@
|
|||||||
|
<template>
|
||||||
|
<f7-page>
|
||||||
|
<f7-navbar>
|
||||||
|
<f7-nav-left :back-link="$t('Back')"></f7-nav-left>
|
||||||
|
<f7-nav-title :title="$t(title)"></f7-nav-title>
|
||||||
|
<f7-nav-right>
|
||||||
|
<f7-link :class="{ 'disabled': inputIsEmpty || submitting }" :text="$t(saveButtonTitle)" @click="save"></f7-link>
|
||||||
|
</f7-nav-right>
|
||||||
|
</f7-navbar>
|
||||||
|
|
||||||
|
<f7-card class="skeleton-text" v-if="loading">
|
||||||
|
<f7-card-content class="no-safe-areas" :padding="false">
|
||||||
|
<f7-list>
|
||||||
|
<f7-list-input label="Category Name" placeholder="Your category name"></f7-list-input>
|
||||||
|
<f7-list-item header="Category Icon" after="Icon"></f7-list-item>
|
||||||
|
<f7-list-item header="Category Color" after="Color"></f7-list-item>
|
||||||
|
<f7-list-input type="textarea" label="Description" placeholder="Your category description (optional)"></f7-list-input>
|
||||||
|
</f7-list>
|
||||||
|
</f7-card-content>
|
||||||
|
</f7-card>
|
||||||
|
|
||||||
|
<f7-card v-else-if="!loading">
|
||||||
|
<f7-card-content class="no-safe-areas" :padding="false">
|
||||||
|
<f7-list>
|
||||||
|
<f7-list-input
|
||||||
|
type="text"
|
||||||
|
clear-button
|
||||||
|
:label="$t('Category Name')"
|
||||||
|
:placeholder="$t('Your category name')"
|
||||||
|
:value="category.name"
|
||||||
|
@input="category.name = $event.target.value"
|
||||||
|
></f7-list-input>
|
||||||
|
|
||||||
|
<f7-list-item :header="$t('Category Icon')" key="singleTypeCategoryIconSelection" link="#"
|
||||||
|
@click="showIconSelectionSheet(category)">
|
||||||
|
<f7-icon slot="after" :icon="category.icon | categoryIcon" :style="{ color: '#' + category.color }"></f7-icon>
|
||||||
|
</f7-list-item>
|
||||||
|
|
||||||
|
<f7-list-item :header="$t('Category Color')" key="singleTypeCategoryColorSelection" link="#"
|
||||||
|
@click="showColorSelectionSheet(category)">
|
||||||
|
<f7-icon slot="after" f7="app_fill" :style="{ color: '#' + category.color }"></f7-icon>
|
||||||
|
</f7-list-item>
|
||||||
|
|
||||||
|
<f7-list-input
|
||||||
|
type="textarea"
|
||||||
|
:label="$t('Description')"
|
||||||
|
:placeholder="$t('Your category description (optional)')"
|
||||||
|
:value="category.comment"
|
||||||
|
@input="category.comment = $event.target.value"
|
||||||
|
></f7-list-input>
|
||||||
|
|
||||||
|
<f7-list-item :header="$t('Visible')" v-if="editCategoryId">
|
||||||
|
<f7-toggle :checked="category.visible" @toggle:change="category.visible = $event"></f7-toggle>
|
||||||
|
</f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
</f7-card-content>
|
||||||
|
</f7-card>
|
||||||
|
|
||||||
|
<f7-sheet :opened="showIconSelection" @sheet:closed="hideIconSelectionSheet">
|
||||||
|
<f7-toolbar>
|
||||||
|
<div class="left"></div>
|
||||||
|
<div class="right">
|
||||||
|
<f7-link sheet-close :text="$t('Done')"></f7-link>
|
||||||
|
</div>
|
||||||
|
</f7-toolbar>
|
||||||
|
<f7-page-content>
|
||||||
|
<f7-block class="margin-vertical">
|
||||||
|
<f7-row class="padding-vertical-half padding-horizontal-half" v-for="(row, idx) in allCategoryIconRows" :key="idx">
|
||||||
|
<f7-col v-for="categoryIcon in row" :key="categoryIcon.id">
|
||||||
|
<f7-icon :icon="categoryIcon.icon" :style="{ color: '#' + (categoryChoosingIcon ? categoryChoosingIcon.color : '000000') }" @click.native="setSelectedIcon(categoryIcon)">
|
||||||
|
<f7-badge color="default" class="right-bottom-icon" v-if="categoryChoosingIcon && categoryChoosingIcon.icon === categoryIcon.id">
|
||||||
|
<f7-icon f7="checkmark_alt"></f7-icon>
|
||||||
|
</f7-badge>
|
||||||
|
</f7-icon>
|
||||||
|
</f7-col>
|
||||||
|
<f7-col v-for="idx in (iconCountPerRow - row.length)" :key="idx"></f7-col>
|
||||||
|
</f7-row>
|
||||||
|
</f7-block>
|
||||||
|
</f7-page-content>
|
||||||
|
</f7-sheet>
|
||||||
|
|
||||||
|
<f7-sheet :opened="showColorSelection" @sheet:closed="hideColorSelectionSheet">
|
||||||
|
<f7-toolbar>
|
||||||
|
<div class="left"></div>
|
||||||
|
<div class="right">
|
||||||
|
<f7-link sheet-close :text="$t('Done')"></f7-link>
|
||||||
|
</div>
|
||||||
|
</f7-toolbar>
|
||||||
|
<f7-page-content>
|
||||||
|
<f7-block class="margin-vertical">
|
||||||
|
<f7-row class="padding-vertical padding-horizontal-half" v-for="(row, idx) in allCategoryColorRows" :key="idx">
|
||||||
|
<f7-col v-for="categoryColor in row" :key="categoryColor.color">
|
||||||
|
<f7-icon f7="app_fill" :style="{ color: '#' + categoryColor.color }" @click.native="setSelectedColor(categoryColor.color)">
|
||||||
|
<f7-badge color="default" class="right-bottom-icon" v-if="categoryChoosingColor && categoryChoosingColor.color === categoryColor.color">
|
||||||
|
<f7-icon f7="checkmark_alt"></f7-icon>
|
||||||
|
</f7-badge>
|
||||||
|
</f7-icon>
|
||||||
|
</f7-col>
|
||||||
|
<f7-col v-for="idx in (iconCountPerRow - row.length)" :key="idx"></f7-col>
|
||||||
|
</f7-row>
|
||||||
|
</f7-block>
|
||||||
|
</f7-page-content>
|
||||||
|
</f7-sheet>
|
||||||
|
</f7-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
const self = this;
|
||||||
|
const query = self.$f7route.query;
|
||||||
|
|
||||||
|
return {
|
||||||
|
editCategoryId: null,
|
||||||
|
loading: false,
|
||||||
|
category: {
|
||||||
|
type: query.type,
|
||||||
|
name: '',
|
||||||
|
parentId: query.parentId,
|
||||||
|
icon: self.$constants.icons.defaultCategoryIconId,
|
||||||
|
color: self.$constants.colors.defaultCategoryColor,
|
||||||
|
comment: '',
|
||||||
|
visible: true
|
||||||
|
},
|
||||||
|
iconCountPerRow: 7,
|
||||||
|
categoryChoosingIcon: null,
|
||||||
|
categoryChoosingColor: null,
|
||||||
|
submitting: false,
|
||||||
|
showIconSelection: false,
|
||||||
|
showColorSelection: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
title() {
|
||||||
|
if (!this.editCategoryId) {
|
||||||
|
if (this.category.parentId === '0') {
|
||||||
|
return 'Add Primary Category';
|
||||||
|
} else {
|
||||||
|
return 'Add Secondary Category';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 'Edit Category';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
saveButtonTitle() {
|
||||||
|
if (!this.editCategoryId) {
|
||||||
|
return 'Add';
|
||||||
|
} else {
|
||||||
|
return 'Save';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
allCategoryIconRows() {
|
||||||
|
const allCategoryIcons = this.$constants.icons.allCategoryIcons;
|
||||||
|
const ret = [];
|
||||||
|
let rowCount = 0;
|
||||||
|
|
||||||
|
for (let categoryIconId in allCategoryIcons) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(allCategoryIcons, categoryIconId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const categoryIcon = allCategoryIcons[categoryIconId];
|
||||||
|
|
||||||
|
if (!ret[rowCount]) {
|
||||||
|
ret[rowCount] = [];
|
||||||
|
} else if (ret[rowCount] && ret[rowCount].length >= this.iconCountPerRow) {
|
||||||
|
rowCount++;
|
||||||
|
ret[rowCount] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
ret[rowCount].push({
|
||||||
|
id: categoryIconId,
|
||||||
|
icon: categoryIcon.icon
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
allCategoryColorRows() {
|
||||||
|
const allCategoryColors = this.$constants.colors.allCategoryColors;
|
||||||
|
const ret = [];
|
||||||
|
let rowCount = -1;
|
||||||
|
|
||||||
|
for (let i = 0; i < allCategoryColors.length; i++) {
|
||||||
|
if (i % this.iconCountPerRow === 0) {
|
||||||
|
ret[++rowCount] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
ret[rowCount].push({
|
||||||
|
color: allCategoryColors[i]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
inputIsEmpty() {
|
||||||
|
return !!this.inputEmptyProblemMessage;
|
||||||
|
},
|
||||||
|
inputEmptyProblemMessage() {
|
||||||
|
if (!this.category.name) {
|
||||||
|
return 'Category name cannot be empty';
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
const self = this;
|
||||||
|
const query = self.$f7route.query;
|
||||||
|
const router = self.$f7router;
|
||||||
|
|
||||||
|
if (!query.id && !query.parentId) {
|
||||||
|
self.$toast('Parameter Invalid');
|
||||||
|
router.back();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.id) {
|
||||||
|
self.loading = true;
|
||||||
|
|
||||||
|
self.editCategoryId = query.id;
|
||||||
|
self.$services.getTransactionCategory({
|
||||||
|
id: self.editCategoryId
|
||||||
|
}).then(response => {
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
self.$toast('Unable to get category');
|
||||||
|
router.back();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const category = data.result;
|
||||||
|
self.category.id = category.id;
|
||||||
|
self.category.type = category.type.toString();
|
||||||
|
self.category.parentId = category.type.parentId;
|
||||||
|
self.category.name = category.name;
|
||||||
|
self.category.icon = category.icon;
|
||||||
|
self.category.color = category.color;
|
||||||
|
self.category.comment = category.comment;
|
||||||
|
self.category.visible = !category.hidden;
|
||||||
|
|
||||||
|
self.loading = false;
|
||||||
|
}).catch(error => {
|
||||||
|
self.$logger.error('failed to load category info', error);
|
||||||
|
|
||||||
|
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
self.$toast({ error: error.response.data });
|
||||||
|
router.back();
|
||||||
|
} else if (!error.processed) {
|
||||||
|
self.$toast('Unable to get category');
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (query.parentId) {
|
||||||
|
if (query.type !== '1' && query.type !== '2' && query.type !== '3') {
|
||||||
|
self.$toast('Parameter Invalid');
|
||||||
|
router.back();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showIconSelectionSheet(category) {
|
||||||
|
this.categoryChoosingIcon = category;
|
||||||
|
this.showIconSelection = true;
|
||||||
|
},
|
||||||
|
setSelectedIcon(categoryIcon) {
|
||||||
|
if (!this.categoryChoosingIcon) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.categoryChoosingIcon.icon = categoryIcon.id;
|
||||||
|
this.categoryChoosingIcon = null;
|
||||||
|
this.showIconSelection = false;
|
||||||
|
},
|
||||||
|
hideIconSelectionSheet() {
|
||||||
|
this.categoryChoosingIcon = null;
|
||||||
|
this.showIconSelection = false;
|
||||||
|
},
|
||||||
|
showColorSelectionSheet(category) {
|
||||||
|
this.categoryChoosingColor = category;
|
||||||
|
this.showColorSelection = true;
|
||||||
|
},
|
||||||
|
setSelectedColor(color) {
|
||||||
|
if (!this.categoryChoosingColor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.categoryChoosingColor.color = color;
|
||||||
|
this.categoryChoosingColor = null;
|
||||||
|
this.showColorSelection = false;
|
||||||
|
},
|
||||||
|
hideColorSelectionSheet() {
|
||||||
|
this.categoryChoosingColor = null;
|
||||||
|
this.showColorSelection = false;
|
||||||
|
},
|
||||||
|
save() {
|
||||||
|
const self = this;
|
||||||
|
const router = self.$f7router;
|
||||||
|
|
||||||
|
const problemMessage = self.inputEmptyProblemMessage;
|
||||||
|
|
||||||
|
if (problemMessage) {
|
||||||
|
self.$alert(problemMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.submitting = true;
|
||||||
|
self.$showLoading(() => self.submitting);
|
||||||
|
|
||||||
|
const submitCategory = {
|
||||||
|
type: parseInt(self.category.type),
|
||||||
|
name: self.category.name,
|
||||||
|
parentId: self.category.parentId,
|
||||||
|
icon: self.category.icon,
|
||||||
|
color: self.category.color,
|
||||||
|
comment: self.category.comment
|
||||||
|
};
|
||||||
|
|
||||||
|
let promise = null;
|
||||||
|
|
||||||
|
if (!self.editCategoryId) {
|
||||||
|
promise = self.$services.addTransactionCategory(submitCategory);
|
||||||
|
} else {
|
||||||
|
submitCategory.id = self.category.id;
|
||||||
|
submitCategory.hidden = !self.category.visible;
|
||||||
|
promise = self.$services.modifyTransactionCategory(submitCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.then(response => {
|
||||||
|
self.submitting = false;
|
||||||
|
self.$hideLoading();
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
if (!self.editCategoryId) {
|
||||||
|
self.$toast('Unable to add category');
|
||||||
|
} else {
|
||||||
|
self.$toast('Unable to save category');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self.editCategoryId) {
|
||||||
|
self.$toast('You have added a new category');
|
||||||
|
} else {
|
||||||
|
self.$toast('You have saved this category');
|
||||||
|
}
|
||||||
|
|
||||||
|
router.back();
|
||||||
|
}).catch(error => {
|
||||||
|
self.$logger.error('failed to save category', error);
|
||||||
|
|
||||||
|
self.submitting = false;
|
||||||
|
self.$hideLoading();
|
||||||
|
|
||||||
|
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
self.$toast({ error: error.response.data });
|
||||||
|
} else if (!error.processed) {
|
||||||
|
if (!self.editCategoryId) {
|
||||||
|
self.$toast('Unable to add category');
|
||||||
|
} else {
|
||||||
|
self.$toast('Unable to save category');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,424 @@
|
|||||||
|
<template>
|
||||||
|
<f7-page :ptr="!sortable" @ptr:refresh="reload" @page:afterin="onPageAfterIn">
|
||||||
|
<f7-navbar>
|
||||||
|
<f7-nav-left :back-link="$t('Back')"></f7-nav-left>
|
||||||
|
<f7-nav-title :title="$t(title)"></f7-nav-title>
|
||||||
|
<f7-nav-right class="navbar-compact-icons">
|
||||||
|
<f7-link icon-f7="ellipsis" v-if="!sortable" @click="showMoreActionSheet = true"></f7-link>
|
||||||
|
<f7-link :href="'/category/add?type=' + categoryType + '&parentId=' + categoryId" icon-f7="plus" v-if="!sortable"></f7-link>
|
||||||
|
<f7-link :text="$t('Done')" :class="{ 'disabled': displayOrderSaving }" @click="saveSortResult" v-else-if="sortable"></f7-link>
|
||||||
|
</f7-nav-right>
|
||||||
|
</f7-navbar>
|
||||||
|
|
||||||
|
<f7-card class="skeleton-text" v-if="loading">
|
||||||
|
<f7-card-content class="no-safe-areas" :padding="false">
|
||||||
|
<f7-list>
|
||||||
|
<f7-list-item title="Category Name"></f7-list-item>
|
||||||
|
<f7-list-item title="Category Name 2"></f7-list-item>
|
||||||
|
<f7-list-item title="Category Name 3"></f7-list-item>
|
||||||
|
<f7-list-item title="Category Name 4"></f7-list-item>
|
||||||
|
<f7-list-item title="Category Name 5"></f7-list-item>
|
||||||
|
<f7-list-item title="Category Name 6"></f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
</f7-card-content>
|
||||||
|
</f7-card>
|
||||||
|
|
||||||
|
<f7-card v-else-if="!loading">
|
||||||
|
<f7-card-content class="no-safe-areas" :padding="false">
|
||||||
|
<f7-list v-if="noAvailableCategory">
|
||||||
|
<f7-list-item :title="$t('No available category')"></f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
|
||||||
|
<f7-list sortable :sortable-enabled="sortable" @sortable:sort="onSort">
|
||||||
|
<f7-list-item v-for="category in categories"
|
||||||
|
:key="category.id"
|
||||||
|
:id="category | categoryDomId"
|
||||||
|
:title="category.name"
|
||||||
|
:link="hasSubCategories ? '/category/list?type=' + categoryType + '&id=' + category.id : null"
|
||||||
|
v-show="showHidden || !category.hidden"
|
||||||
|
swipeout @taphold.native="setSortable()">
|
||||||
|
<f7-icon slot="media" :icon="category.icon | categoryIcon" :style="{ color: '#' + category.color }">
|
||||||
|
<f7-badge color="gray" class="right-bottom-icon" v-if="category.hidden">
|
||||||
|
<f7-icon f7="eye_slash_fill"></f7-icon>
|
||||||
|
</f7-badge>
|
||||||
|
</f7-icon>
|
||||||
|
<f7-swipeout-actions left v-if="sortable">
|
||||||
|
<f7-swipeout-button :color="category.hidden ? 'blue' : 'gray'" class="padding-left padding-right"
|
||||||
|
overswipe @click="hide(category, !category.hidden)">
|
||||||
|
<f7-icon :f7="category.hidden ? 'eye' : 'eye_slash'"></f7-icon>
|
||||||
|
</f7-swipeout-button>
|
||||||
|
</f7-swipeout-actions>
|
||||||
|
<f7-swipeout-actions right v-if="!sortable">
|
||||||
|
<f7-swipeout-button color="orange" :text="$t('Edit')" @click="edit(category)"></f7-swipeout-button>
|
||||||
|
<f7-swipeout-button color="red" class="padding-left padding-right" @click="remove(category)">
|
||||||
|
<f7-icon f7="trash"></f7-icon>
|
||||||
|
</f7-swipeout-button>
|
||||||
|
</f7-swipeout-actions>
|
||||||
|
</f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
</f7-card-content>
|
||||||
|
</f7-card>
|
||||||
|
|
||||||
|
<f7-actions close-by-outside-click close-on-escape :opened="showMoreActionSheet" @actions:closed="showMoreActionSheet = false">
|
||||||
|
<f7-actions-group>
|
||||||
|
<f7-actions-button @click="setSortable()">{{ $t('Sort') }}</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-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 category?') }}</f7-actions-label>
|
||||||
|
<f7-actions-button color="red" @click="remove(categoryToDelete)">{{ $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>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
categories: {},
|
||||||
|
hasSubCategories: false,
|
||||||
|
categoryType: '',
|
||||||
|
categoryId: '',
|
||||||
|
loading: true,
|
||||||
|
showHidden: false,
|
||||||
|
sortable: false,
|
||||||
|
categoryToDelete: null,
|
||||||
|
showMoreActionSheet: false,
|
||||||
|
showDeleteActionSheet: false,
|
||||||
|
displayOrderModified: false,
|
||||||
|
displayOrderSaving: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
title() {
|
||||||
|
let title = '';
|
||||||
|
|
||||||
|
switch (this.categoryType) {
|
||||||
|
case '1':
|
||||||
|
title = 'Expense';
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
title = 'Income';
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
title = 'Transfer';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
title = 'Transaction';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this.hasSubCategories) {
|
||||||
|
case true:
|
||||||
|
title += ' Primary';
|
||||||
|
break;
|
||||||
|
case false:
|
||||||
|
title += ' Secondary';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return title + ' Categories';
|
||||||
|
},
|
||||||
|
noAvailableCategory() {
|
||||||
|
for (let i = 0; i < this.categories.length; i++) {
|
||||||
|
if (this.showHidden || !this.categories[i].hidden) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
const self = this;
|
||||||
|
const query = self.$f7route.query;
|
||||||
|
const router = self.$f7router;
|
||||||
|
|
||||||
|
if (query.type !== '1' && query.type !== '2' && query.type !== '3') {
|
||||||
|
self.$toast('Parameter Invalid');
|
||||||
|
router.back();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.categoryType = query.type;
|
||||||
|
|
||||||
|
if (query.id && query.id !== '0') {
|
||||||
|
self.categoryId = query.id;
|
||||||
|
self.hasSubCategories = false;
|
||||||
|
} else {
|
||||||
|
self.categoryId = '0';
|
||||||
|
self.hasSubCategories = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.loading = true;
|
||||||
|
|
||||||
|
self.$services.getAllTransactionCategories({
|
||||||
|
type: self.categoryType,
|
||||||
|
parentId: self.categoryId
|
||||||
|
}).then(response => {
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
self.$toast('Unable to get category list');
|
||||||
|
router.back();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.result[self.categoryType]) {
|
||||||
|
self.categories = data.result[self.categoryType];
|
||||||
|
} else {
|
||||||
|
self.categories = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
self.loading = false;
|
||||||
|
}).catch(error => {
|
||||||
|
self.$logger.error('failed to load category list', error);
|
||||||
|
|
||||||
|
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
self.$toast({ error: error.response.data });
|
||||||
|
router.back();
|
||||||
|
} else if (!error.processed) {
|
||||||
|
self.$toast('Unable to get category list');
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onPageAfterIn() {
|
||||||
|
const self = this;
|
||||||
|
const previousRoute = self.$f7router.previousRoute;
|
||||||
|
|
||||||
|
if (previousRoute && (previousRoute.path === '/category/add' || previousRoute.path === '/category/edit') && !self.loading) {
|
||||||
|
self.reload(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reload(done) {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
self.$services.getAllTransactionCategories({
|
||||||
|
type: self.categoryType,
|
||||||
|
parentId: self.categoryId
|
||||||
|
}).then(response => {
|
||||||
|
if (done) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
self.$toast('Unable to get category list');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.result[self.categoryType]) {
|
||||||
|
self.categories = data.result[self.categoryType];
|
||||||
|
} else {
|
||||||
|
self.categories = [];
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
self.$logger.error('failed to reload category list', error);
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 category list');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setSortable() {
|
||||||
|
if (this.sortable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showHidden = true;
|
||||||
|
this.sortable = true;
|
||||||
|
this.displayOrderModified = false;
|
||||||
|
},
|
||||||
|
onSort(event) {
|
||||||
|
if (!event || !event.el || !event.el.id || event.el.id.indexOf('category_') !== 0) {
|
||||||
|
this.$toast('Unable to move category');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = event.el.id.substr(9);
|
||||||
|
let category = null;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.categories.length; i++) {
|
||||||
|
if (this.categories[i].id === id) {
|
||||||
|
category = this.categories[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!category || !this.categories[event.to]) {
|
||||||
|
this.$toast('Unable to move category');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.categories.splice(event.to, 0, this.categories.splice(event.from, 1)[0]);
|
||||||
|
|
||||||
|
this.displayOrderModified = true;
|
||||||
|
},
|
||||||
|
saveSortResult() {
|
||||||
|
const self = this;
|
||||||
|
const newDisplayOrders = [];
|
||||||
|
|
||||||
|
if (!self.displayOrderModified) {
|
||||||
|
self.showHidden = false;
|
||||||
|
self.sortable = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.displayOrderSaving = true;
|
||||||
|
|
||||||
|
for (let i = 0; i < self.categories.length; i++) {
|
||||||
|
newDisplayOrders.push({
|
||||||
|
id: self.categories[i].id,
|
||||||
|
displayOrder: i + 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.$showLoading();
|
||||||
|
|
||||||
|
self.$services.moveTransactionCategory({
|
||||||
|
newDisplayOrders: newDisplayOrders
|
||||||
|
}).then(response => {
|
||||||
|
self.displayOrderSaving = false;
|
||||||
|
self.$hideLoading();
|
||||||
|
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
self.$toast('Unable to move category');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.showHidden = false;
|
||||||
|
self.sortable = false;
|
||||||
|
self.displayOrderModified = false;
|
||||||
|
}).catch(error => {
|
||||||
|
self.$logger.error('failed to save categories display order', error);
|
||||||
|
|
||||||
|
self.displayOrderSaving = false;
|
||||||
|
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 move category');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
edit(category) {
|
||||||
|
this.$f7router.navigate('/category/edit?id=' + category.id);
|
||||||
|
},
|
||||||
|
hide(category, hidden) {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
self.$showLoading();
|
||||||
|
|
||||||
|
self.$services.hideTransactionCategory({
|
||||||
|
id: category.id,
|
||||||
|
hidden: hidden
|
||||||
|
}).then(response => {
|
||||||
|
self.$hideLoading();
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
if (hidden) {
|
||||||
|
self.$toast('Unable to hide this category');
|
||||||
|
} else {
|
||||||
|
self.$toast('Unable to unhide this category');
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
category.hidden = hidden;
|
||||||
|
}).catch(error => {
|
||||||
|
self.$logger.error('failed to change category visibility', error);
|
||||||
|
|
||||||
|
self.$hideLoading();
|
||||||
|
|
||||||
|
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
self.$toast({ error: error.response.data });
|
||||||
|
} else if (!error.processed) {
|
||||||
|
if (hidden) {
|
||||||
|
self.$toast('Unable to hide this category');
|
||||||
|
} else {
|
||||||
|
self.$toast('Unable to unhide this category');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
remove(category) {
|
||||||
|
const self = this;
|
||||||
|
const app = self.$f7;
|
||||||
|
const $$ = app.$;
|
||||||
|
|
||||||
|
if (!category) {
|
||||||
|
self.$alert('An error has occurred');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self.showDeleteActionSheet) {
|
||||||
|
self.categoryToDelete = category;
|
||||||
|
self.showDeleteActionSheet = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.showDeleteActionSheet = false;
|
||||||
|
self.categoryToDelete = null;
|
||||||
|
self.$showLoading();
|
||||||
|
|
||||||
|
self.$services.deleteTransactionCategory({
|
||||||
|
id: category.id
|
||||||
|
}).then(response => {
|
||||||
|
self.$hideLoading();
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
self.$toast('Unable to delete this category');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.swipeout.delete($$(`#${self.$options.filters.categoryDomId(category)}`), () => {
|
||||||
|
for (let i = 0; i < self.categories.length; i++) {
|
||||||
|
if (self.categories[i].id === category.id) {
|
||||||
|
self.categories.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
self.$logger.error('failed to delete category', 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 category');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
categoryDomId(category) {
|
||||||
|
return 'category_' + category.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user