@tager/admin-blog 0.3.23 → 0.3.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants/scopes.d.ts +11 -0
- package/dist/dev.d.ts +0 -2
- package/dist/hooks/index.d.ts +7 -3
- package/dist/hooks/{useBlogCategoryList.d.ts → useFetchCategories.d.ts} +2 -2
- package/dist/hooks/useFetchCategory.d.ts +8 -0
- package/dist/hooks/{useModuleConfig.d.ts → useFetchModuleConfig.d.ts} +2 -2
- package/dist/hooks/{useBlogPostList.d.ts → useFetchPosts.d.ts} +2 -2
- package/dist/hooks/useStore.d.ts +4 -0
- package/dist/hooks/useUserPermission.d.ts +3 -0
- package/dist/hooks/useUserProfile.d.ts +3 -0
- package/dist/index.esm.js +1245 -432
- package/dist/index.umd.js +1253 -438
- package/dist/locales/en.d.ts +10 -1
- package/dist/router/index.d.ts +4 -0
- package/dist/services/requests.d.ts +13 -22
- package/dist/store/index.d.ts +6 -0
- package/dist/store/user.d.ts +14 -0
- package/dist/typings/common.d.ts +1 -0
- package/dist/typings/model.d.ts +45 -1
- package/dist/utils/common.d.ts +4 -0
- package/dist/views/Categories/CategoryForm/CategoryForm.helpers.d.ts +3 -14
- package/dist/views/Categories/CategoryForm/CategoryForm.vue.d.ts +45 -22
- package/dist/views/Categories/CategoryList/CategoryList.helpers.d.ts +2 -2
- package/dist/views/Categories/CategoryList/CategoryList.vue.d.ts +6 -4
- package/dist/views/Categories/CategoryList/hooks/useAdvancedSearch.d.ts +1 -1
- package/dist/views/Posts/PostForm/PostForm.helpers.d.ts +2 -2
- package/dist/views/Posts/PostForm/PostForm.vue.d.ts +2 -0
- package/dist/views/Posts/PostList/PostList.helpers.d.ts +2 -2
- package/dist/views/Posts/PostList/PostList.vue.d.ts +8 -2
- package/dist/views/Posts/PostList/hooks/useAdvancedSearch.d.ts +1 -1
- package/package.json +9 -5
- package/src/constants/scopes.ts +11 -0
- package/src/dev.ts +11 -42
- package/src/hooks/index.ts +7 -3
- package/src/hooks/useFetchCategories.ts +25 -0
- package/src/hooks/useFetchCategory.ts +41 -0
- package/src/hooks/{useModuleConfig.ts → useFetchModuleConfig.ts} +7 -5
- package/src/hooks/useFetchPosts.ts +23 -0
- package/src/hooks/useStore.ts +8 -0
- package/src/hooks/useUserPermission.ts +16 -0
- package/src/hooks/useUserProfile.ts +13 -0
- package/src/locales/en.ts +13 -1
- package/src/locales/ru.ts +14 -2
- package/src/router/index.ts +39 -0
- package/src/services/requests.ts +30 -26
- package/src/store/index.ts +21 -0
- package/src/store/user.ts +109 -0
- package/src/typings/common.ts +1 -0
- package/src/typings/model.ts +54 -1
- package/src/utils/common.ts +23 -0
- package/src/views/App.vue +10 -2
- package/src/views/Categories/CategoryForm/CategoryForm.helpers.ts +26 -35
- package/src/views/Categories/CategoryForm/CategoryForm.vue +180 -113
- package/src/views/Categories/CategoryList/CategoryList.helpers.ts +41 -36
- package/src/views/Categories/CategoryList/CategoryList.vue +42 -25
- package/src/views/Categories/CategoryList/hooks/useAdvancedSearch.ts +1 -1
- package/src/views/Home.vue +1 -1
- package/src/views/Posts/PostForm/PostForm.helpers.ts +7 -6
- package/src/views/Posts/PostForm/PostForm.vue +57 -22
- package/src/views/Posts/PostList/PostList.helpers.ts +13 -15
- package/src/views/Posts/PostList/PostList.vue +96 -34
- package/src/views/Posts/PostList/hooks/useAdvancedSearch.ts +6 -5
- package/src/hooks/useBlogCategoryList.ts +0 -23
- package/src/hooks/useBlogPostList.ts +0 -23
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { onMounted, Ref, SetupContext, watch } from '@vue/composition-api';
|
|
2
|
+
|
|
3
|
+
import { Nullable, ResourceRef, useResource } from '@tager/admin-services';
|
|
4
|
+
|
|
5
|
+
import { Category } from '@/typings/model';
|
|
6
|
+
import { getCategory } from '@/services/requests';
|
|
7
|
+
|
|
8
|
+
export function useFetchCategory({
|
|
9
|
+
context,
|
|
10
|
+
categoryId,
|
|
11
|
+
isCreation,
|
|
12
|
+
}: {
|
|
13
|
+
context: SetupContext;
|
|
14
|
+
categoryId: Ref<string>;
|
|
15
|
+
isCreation: Ref<boolean>;
|
|
16
|
+
}): ResourceRef<Nullable<Category>> {
|
|
17
|
+
const [fetchCategory, resource] = useResource<Nullable<Category>>({
|
|
18
|
+
fetchResource: () => getCategory(categoryId.value),
|
|
19
|
+
initialValue: null,
|
|
20
|
+
context,
|
|
21
|
+
resourceName: 'Blog category',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
onMounted(() => {
|
|
25
|
+
if (isCreation.value) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
fetchCategory();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
watch(categoryId, () => {
|
|
33
|
+
if (isCreation.value) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
fetchCategory();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return resource;
|
|
41
|
+
}
|
|
@@ -2,16 +2,18 @@ import { onMounted, SetupContext } from '@vue/composition-api';
|
|
|
2
2
|
|
|
3
3
|
import { Nullable, ResourceRef, useResource } from '@tager/admin-services';
|
|
4
4
|
|
|
5
|
-
import { ModuleConfig } from '
|
|
6
|
-
import {
|
|
5
|
+
import { ModuleConfig } from '@/typings/model';
|
|
6
|
+
import { getModuleConfig } from '@/services/requests';
|
|
7
7
|
|
|
8
|
-
export function
|
|
8
|
+
export function useFetchModuleConfig({
|
|
9
|
+
context,
|
|
10
|
+
}: {
|
|
9
11
|
context: SetupContext;
|
|
10
12
|
}): ResourceRef<Nullable<ModuleConfig>> {
|
|
11
13
|
const [fetchModuleConfig, resource] = useResource<Nullable<ModuleConfig>>({
|
|
12
|
-
fetchResource:
|
|
14
|
+
fetchResource: getModuleConfig,
|
|
13
15
|
initialValue: null,
|
|
14
|
-
context:
|
|
16
|
+
context: context,
|
|
15
17
|
resourceName: 'Module configuration',
|
|
16
18
|
});
|
|
17
19
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { onMounted, SetupContext } from '@vue/composition-api';
|
|
2
|
+
|
|
3
|
+
import { ResourceRef, useResource } from '@tager/admin-services';
|
|
4
|
+
|
|
5
|
+
import { PostShort } from '@/typings/model';
|
|
6
|
+
import { getPosts } from '@/services/requests';
|
|
7
|
+
|
|
8
|
+
export function useFetchPosts(params: {
|
|
9
|
+
context: SetupContext;
|
|
10
|
+
}): ResourceRef<PostShort[]> {
|
|
11
|
+
const [fetchPosts, resource] = useResource<PostShort[]>({
|
|
12
|
+
fetchResource: () => getPosts({ pageNumber: 1, pageSize: 99999 }),
|
|
13
|
+
initialValue: [],
|
|
14
|
+
context: params.context,
|
|
15
|
+
resourceName: 'Blog posts',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
onMounted(() => {
|
|
19
|
+
fetchPosts();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return resource;
|
|
23
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { computed, ComputedRef, SetupContext } from '@vue/composition-api';
|
|
2
|
+
|
|
3
|
+
import { ScopeType } from '@/typings/model';
|
|
4
|
+
import { useStore } from '@/hooks/useStore';
|
|
5
|
+
import { userNamespace } from '@/utils/common';
|
|
6
|
+
|
|
7
|
+
export function useUserPermission(
|
|
8
|
+
context: SetupContext,
|
|
9
|
+
scope: ScopeType
|
|
10
|
+
): ComputedRef<boolean> {
|
|
11
|
+
const store = useStore(context);
|
|
12
|
+
|
|
13
|
+
return computed<boolean>(() => {
|
|
14
|
+
return store.getters[userNamespace('userHasPermission')](scope);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { computed, ComputedRef, SetupContext } from '@vue/composition-api';
|
|
2
|
+
|
|
3
|
+
import { UserModel } from '@/typings/model';
|
|
4
|
+
import { useStore } from '@/hooks/useStore';
|
|
5
|
+
import { userNamespace } from '@/utils/common';
|
|
6
|
+
|
|
7
|
+
export function useUserProfile(context: SetupContext): ComputedRef<UserModel> {
|
|
8
|
+
const store = useStore(context);
|
|
9
|
+
|
|
10
|
+
return computed<UserModel>(() => {
|
|
11
|
+
return store.getters[userNamespace('userProfile')];
|
|
12
|
+
});
|
|
13
|
+
}
|
package/src/locales/en.ts
CHANGED
|
@@ -31,7 +31,7 @@ const EN = {
|
|
|
31
31
|
actions: 'Actions',
|
|
32
32
|
updatePost: 'Update post',
|
|
33
33
|
title: 'Title',
|
|
34
|
-
|
|
34
|
+
link: 'Link',
|
|
35
35
|
excerpt: 'Excerpt',
|
|
36
36
|
body: 'Body',
|
|
37
37
|
defaultCategory: 'Default Category',
|
|
@@ -62,6 +62,18 @@ const EN = {
|
|
|
62
62
|
blogCategoryWasSuccessfullyUpdated: 'Blog category was successfully updated',
|
|
63
63
|
blogCategoryCreationWasFailed: 'Blog category creation was failed',
|
|
64
64
|
blogCategoryUpdateWasFailed: 'Blog category update was failed',
|
|
65
|
+
|
|
66
|
+
clone: 'Clone',
|
|
67
|
+
cloneConfirm: 'Are you sure you want to clone the page?',
|
|
68
|
+
cloneSuccess: 'Page have copied successfully',
|
|
69
|
+
cloneFailure: 'Page copy failure',
|
|
70
|
+
|
|
71
|
+
tabs: {
|
|
72
|
+
common: 'Common',
|
|
73
|
+
seo: 'SEO',
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
parentCategory: 'Parent category',
|
|
65
77
|
};
|
|
66
78
|
|
|
67
79
|
export default EN;
|
package/src/locales/ru.ts
CHANGED
|
@@ -19,7 +19,7 @@ const RU: typeof EN = {
|
|
|
19
19
|
edit: 'Редактировать',
|
|
20
20
|
remove: 'Удалить',
|
|
21
21
|
postsByCategory: 'Посты по категориям',
|
|
22
|
-
name: '
|
|
22
|
+
name: 'Название',
|
|
23
23
|
image: 'Изображение',
|
|
24
24
|
language: 'Язык',
|
|
25
25
|
languages: 'Языки',
|
|
@@ -33,7 +33,7 @@ const RU: typeof EN = {
|
|
|
33
33
|
actions: 'Действия',
|
|
34
34
|
updatePost: 'Обновить пост',
|
|
35
35
|
title: 'Заголовок',
|
|
36
|
-
|
|
36
|
+
link: 'Ссылка',
|
|
37
37
|
excerpt: 'Краткое описание',
|
|
38
38
|
defaultCategory: 'Категория по-умолчанию',
|
|
39
39
|
body: 'Содержание',
|
|
@@ -64,6 +64,18 @@ const RU: typeof EN = {
|
|
|
64
64
|
blogCategoryWasSuccessfullyUpdated: 'Категория блога успешно обновлена',
|
|
65
65
|
blogCategoryCreationWasFailed: 'Не удалось создать категорию блога',
|
|
66
66
|
blogCategoryUpdateWasFailed: 'Не удалось обновить категорию блога',
|
|
67
|
+
|
|
68
|
+
clone: 'Клонировать',
|
|
69
|
+
cloneConfirm: 'Вы уверены, что хотите создать копию страницы?',
|
|
70
|
+
cloneSuccess: 'Копия страницы успешно создана',
|
|
71
|
+
cloneFailure: 'Ошибка копирования страницы',
|
|
72
|
+
|
|
73
|
+
tabs: {
|
|
74
|
+
common: 'Общие',
|
|
75
|
+
seo: 'SEO',
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
parentCategory: 'Родительская категория',
|
|
67
79
|
};
|
|
68
80
|
|
|
69
81
|
export default RU;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createRouter,
|
|
3
|
+
CustomRoute,
|
|
4
|
+
CustomRouteConfig,
|
|
5
|
+
} from '@tager/admin-layout';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
BLOG_CATEGORY_FORM_ROUTE,
|
|
9
|
+
BLOG_CATEGORY_LIST_ROUTE,
|
|
10
|
+
BLOG_POST_FORM_ROUTE,
|
|
11
|
+
BLOG_POST_LIST_ROUTE,
|
|
12
|
+
} from '@/constants/routes';
|
|
13
|
+
import Home from '@/views/Home.vue';
|
|
14
|
+
|
|
15
|
+
export const HOME_ROUTE: CustomRouteConfig = {
|
|
16
|
+
path: '/',
|
|
17
|
+
component: Home,
|
|
18
|
+
name: 'Home',
|
|
19
|
+
meta: {
|
|
20
|
+
getBreadcrumbs: (route: CustomRoute) => [
|
|
21
|
+
{ url: '/', text: route.name ?? '' },
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const router = createRouter(
|
|
27
|
+
{
|
|
28
|
+
routes: [
|
|
29
|
+
HOME_ROUTE,
|
|
30
|
+
BLOG_POST_LIST_ROUTE,
|
|
31
|
+
BLOG_POST_FORM_ROUTE,
|
|
32
|
+
BLOG_CATEGORY_LIST_ROUTE,
|
|
33
|
+
BLOG_CATEGORY_FORM_ROUTE,
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
{ useTitleSync: false }
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
export default router;
|
package/src/services/requests.ts
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import { request, Nullable, ResponseBody } from '@tager/admin-services';
|
|
2
2
|
import { FieldShortType, OutgoingValueUnion } from '@tager/admin-dynamic-field';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
Category,
|
|
6
|
+
ModuleConfig,
|
|
7
|
+
PostShort,
|
|
8
|
+
PostFull,
|
|
9
|
+
UserModel,
|
|
10
|
+
CategoryPayload,
|
|
11
|
+
} from '@/typings/model';
|
|
12
|
+
|
|
13
|
+
export function getUserProfile(): Promise<ResponseBody<UserModel>> {
|
|
14
|
+
return request.get({ path: '/admin/self' });
|
|
15
|
+
}
|
|
5
16
|
|
|
6
17
|
/** Blog Posts */
|
|
7
18
|
|
|
8
|
-
export function
|
|
19
|
+
export function getPosts(params?: {
|
|
9
20
|
query?: string;
|
|
10
21
|
pageNumber?: number;
|
|
11
22
|
pageSize?: number;
|
|
@@ -13,16 +24,22 @@ export function getBlogPostList(params?: {
|
|
|
13
24
|
return request.get({ path: '/admin/blog/posts', params });
|
|
14
25
|
}
|
|
15
26
|
|
|
16
|
-
export function
|
|
27
|
+
export function getPostsCount(): Promise<ResponseBody<{ count: number }>> {
|
|
17
28
|
return request.get({ path: '/admin/blog/posts/count' });
|
|
18
29
|
}
|
|
19
30
|
|
|
20
|
-
export function
|
|
31
|
+
export function getPost(
|
|
21
32
|
postId: number | string
|
|
22
33
|
): Promise<ResponseBody<PostFull>> {
|
|
23
34
|
return request.get({ path: `/admin/blog/posts/${postId}` });
|
|
24
35
|
}
|
|
25
36
|
|
|
37
|
+
export function clonePost(
|
|
38
|
+
postId: number | string
|
|
39
|
+
): Promise<ResponseBody<PostFull>> {
|
|
40
|
+
return request.post({ path: `/admin/blog/posts/${postId}/clone` });
|
|
41
|
+
}
|
|
42
|
+
|
|
26
43
|
export interface PostCreationPayload {
|
|
27
44
|
title: string;
|
|
28
45
|
urlAlias: string;
|
|
@@ -68,7 +85,7 @@ export function deleteBlogPost(
|
|
|
68
85
|
|
|
69
86
|
/** Blog Categories */
|
|
70
87
|
|
|
71
|
-
export function
|
|
88
|
+
export function getCategories(params?: {
|
|
72
89
|
query?: string;
|
|
73
90
|
pageNumber?: number;
|
|
74
91
|
pageSize?: number;
|
|
@@ -76,34 +93,21 @@ export function getBlogCategoryList(params?: {
|
|
|
76
93
|
return request.get({ path: '/admin/blog/categories', params });
|
|
77
94
|
}
|
|
78
95
|
|
|
79
|
-
export function
|
|
96
|
+
export function getCategory(
|
|
80
97
|
categoryId: number | string
|
|
81
98
|
): Promise<ResponseBody<Category>> {
|
|
82
99
|
return request.get({ path: `/admin/blog/categories/${categoryId}` });
|
|
83
100
|
}
|
|
84
101
|
|
|
85
|
-
export
|
|
86
|
-
|
|
87
|
-
isDefault: boolean;
|
|
88
|
-
pageTitle: Nullable<string>;
|
|
89
|
-
language: Nullable<string>;
|
|
90
|
-
pageDescription: Nullable<string>;
|
|
91
|
-
openGraphImage: Nullable<string>;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function createBlogCategory(
|
|
95
|
-
payload: BlogCategoryCreationPayload
|
|
102
|
+
export function createCategory(
|
|
103
|
+
payload: CategoryPayload
|
|
96
104
|
): Promise<ResponseBody<Category>> {
|
|
97
105
|
return request.post({ path: '/admin/blog/categories', body: payload });
|
|
98
106
|
}
|
|
99
107
|
|
|
100
|
-
export
|
|
101
|
-
urlAlias: string;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
export function updateBlogCategory(
|
|
108
|
+
export function updateCategory(
|
|
105
109
|
categoryId: number | string,
|
|
106
|
-
payload:
|
|
110
|
+
payload: CategoryPayload
|
|
107
111
|
): Promise<ResponseBody<Category>> {
|
|
108
112
|
return request.put({
|
|
109
113
|
path: `/admin/blog/categories/${categoryId}`,
|
|
@@ -111,7 +115,7 @@ export function updateBlogCategory(
|
|
|
111
115
|
});
|
|
112
116
|
}
|
|
113
117
|
|
|
114
|
-
export function
|
|
118
|
+
export function deleteCategory(
|
|
115
119
|
categoryId: number | string
|
|
116
120
|
): Promise<{ success: boolean }> {
|
|
117
121
|
return request.delete({
|
|
@@ -119,7 +123,7 @@ export function deleteBlogCategory(
|
|
|
119
123
|
});
|
|
120
124
|
}
|
|
121
125
|
|
|
122
|
-
export function
|
|
126
|
+
export function moveCategory(
|
|
123
127
|
categoryId: number | string,
|
|
124
128
|
direction: 'up' | 'down'
|
|
125
129
|
): Promise<{ success: boolean }> {
|
|
@@ -128,7 +132,7 @@ export function moveBlogCategory(
|
|
|
128
132
|
});
|
|
129
133
|
}
|
|
130
134
|
|
|
131
|
-
export function
|
|
135
|
+
export function getModuleConfig(): Promise<ResponseBody<ModuleConfig>> {
|
|
132
136
|
return request.get({
|
|
133
137
|
path: `/admin/blog/module-info`,
|
|
134
138
|
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import Vue from 'vue';
|
|
2
|
+
import Vuex from 'vuex';
|
|
3
|
+
|
|
4
|
+
import userModule, {
|
|
5
|
+
USER_MODULE_NAMESPACE,
|
|
6
|
+
UserModuleStateModel,
|
|
7
|
+
} from '@/store/user';
|
|
8
|
+
|
|
9
|
+
Vue.use(Vuex);
|
|
10
|
+
|
|
11
|
+
export interface RootState {
|
|
12
|
+
user: UserModuleStateModel;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const store = new Vuex.Store({
|
|
16
|
+
modules: {
|
|
17
|
+
[USER_MODULE_NAMESPACE]: userModule,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export default store;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { GetterTree, Module } from 'vuex';
|
|
2
|
+
|
|
3
|
+
import { FETCH_STATUSES, FetchStatus } from '@tager/admin-services';
|
|
4
|
+
|
|
5
|
+
import { ScopeType, UserModel } from '@/typings/model';
|
|
6
|
+
import { getUserProfile } from '@/services/requests';
|
|
7
|
+
import { RootState } from '@/store';
|
|
8
|
+
import { Scope } from '@/constants/scopes';
|
|
9
|
+
|
|
10
|
+
export interface UserModuleStateModel {
|
|
11
|
+
profile: UserModel | null;
|
|
12
|
+
status: FetchStatus;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const USER_MODULE_NAMESPACE = 'user';
|
|
16
|
+
|
|
17
|
+
const USER_MUTATION_TYPES = {
|
|
18
|
+
PROFILE_REQUEST_STARTED: 'profileRequestStarted',
|
|
19
|
+
PROFILE_REQUEST_FULFILLED: 'profileRequestFulfilled',
|
|
20
|
+
PROFILE_REQUEST_REJECTED: 'profileRequestRejected',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const USER_ACTION_TYPES = {
|
|
24
|
+
FETCH_USER_PROFILE: 'fetchUserProfile',
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
type ReturnValueMapType<
|
|
28
|
+
T extends { [key: string]: (...args: any[]) => any }
|
|
29
|
+
> = { [key in keyof T]: ReturnType<T[key]> };
|
|
30
|
+
|
|
31
|
+
interface UserGettersType extends GetterTree<UserModuleStateModel, RootState> {
|
|
32
|
+
userScopes: (state: UserModuleStateModel) => Array<ScopeType>;
|
|
33
|
+
userHasPermission: (
|
|
34
|
+
state: UserModuleStateModel,
|
|
35
|
+
getters: ReturnValueMapType<UserGettersType>
|
|
36
|
+
) => (scope: ScopeType) => boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const userGetters: UserGettersType = {
|
|
40
|
+
userProfile(state) {
|
|
41
|
+
return state.profile;
|
|
42
|
+
},
|
|
43
|
+
userScopes(state) {
|
|
44
|
+
const roles = state.profile ? state.profile.roles : [];
|
|
45
|
+
const scopes: Array<ScopeType> = [];
|
|
46
|
+
|
|
47
|
+
roles.forEach((role) => {
|
|
48
|
+
scopes.push(...role.scopes);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return scopes;
|
|
52
|
+
},
|
|
53
|
+
userHasPermission: (state, getters) => (scope: ScopeType) => {
|
|
54
|
+
const scopes = getters.userScopes;
|
|
55
|
+
|
|
56
|
+
if (scopes.includes(Scope.All)) return true;
|
|
57
|
+
|
|
58
|
+
return scopes.includes(scope);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const userModule: Module<UserModuleStateModel, RootState> = {
|
|
63
|
+
namespaced: true,
|
|
64
|
+
state: {
|
|
65
|
+
profile: null,
|
|
66
|
+
status: FETCH_STATUSES.IDLE,
|
|
67
|
+
},
|
|
68
|
+
mutations: {
|
|
69
|
+
[USER_MUTATION_TYPES.PROFILE_REQUEST_STARTED](state) {
|
|
70
|
+
if (state.status === FETCH_STATUSES.IDLE) {
|
|
71
|
+
state.status = FETCH_STATUSES.LOADING;
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
[USER_MUTATION_TYPES.PROFILE_REQUEST_FULFILLED](state, user: UserModel) {
|
|
75
|
+
if (state.status === FETCH_STATUSES.LOADING) {
|
|
76
|
+
state.status = FETCH_STATUSES.SUCCESS;
|
|
77
|
+
state.profile = user;
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
[USER_MUTATION_TYPES.PROFILE_REQUEST_REJECTED](state) {
|
|
81
|
+
if (state.status === FETCH_STATUSES.LOADING) {
|
|
82
|
+
state.status = FETCH_STATUSES.FAILURE;
|
|
83
|
+
state.profile = null;
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
actions: {
|
|
88
|
+
[USER_ACTION_TYPES.FETCH_USER_PROFILE](store) {
|
|
89
|
+
if (store.state.status !== FETCH_STATUSES.IDLE) return;
|
|
90
|
+
|
|
91
|
+
store.commit(USER_MUTATION_TYPES.PROFILE_REQUEST_STARTED);
|
|
92
|
+
|
|
93
|
+
getUserProfile()
|
|
94
|
+
.then((response) => {
|
|
95
|
+
store.commit(
|
|
96
|
+
USER_MUTATION_TYPES.PROFILE_REQUEST_FULFILLED,
|
|
97
|
+
response.data
|
|
98
|
+
);
|
|
99
|
+
})
|
|
100
|
+
.catch((error) => {
|
|
101
|
+
console.error(error);
|
|
102
|
+
store.commit(USER_MUTATION_TYPES.PROFILE_REQUEST_REJECTED);
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
getters: userGetters,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export default userModule;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Values<T> = T[keyof T];
|
package/src/typings/model.ts
CHANGED
|
@@ -4,11 +4,36 @@ import {
|
|
|
4
4
|
FieldShortType,
|
|
5
5
|
IncomingValueUnion,
|
|
6
6
|
} from '@tager/admin-dynamic-field';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
OptionType,
|
|
9
|
+
ShortCodeItemType,
|
|
10
|
+
SingleFileInputValueType,
|
|
11
|
+
} from '@tager/admin-ui';
|
|
12
|
+
|
|
13
|
+
import { Values } from '@/typings/common';
|
|
14
|
+
import { Scope } from '@/constants/scopes';
|
|
15
|
+
|
|
16
|
+
export type ScopeType = Values<typeof Scope>;
|
|
17
|
+
|
|
18
|
+
export interface RoleModel {
|
|
19
|
+
readonly id: number;
|
|
20
|
+
readonly name: string;
|
|
21
|
+
readonly scopes: Array<ScopeType>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface UserModel {
|
|
25
|
+
readonly id: number;
|
|
26
|
+
readonly name: string;
|
|
27
|
+
readonly email: string;
|
|
28
|
+
readonly roles: Array<RoleModel>;
|
|
29
|
+
}
|
|
8
30
|
|
|
9
31
|
export interface SeoInfo {
|
|
10
32
|
readonly pageTitle: Nullable<string>;
|
|
11
33
|
readonly pageDescription: Nullable<string>;
|
|
34
|
+
readonly pageKeywords: Nullable<string>;
|
|
35
|
+
readonly openGraphTitle: Nullable<string>;
|
|
36
|
+
readonly openGraphDescription: Nullable<string>;
|
|
12
37
|
readonly openGraphImage: Nullable<FileType>;
|
|
13
38
|
}
|
|
14
39
|
|
|
@@ -21,6 +46,8 @@ export interface Category extends SeoInfo {
|
|
|
21
46
|
readonly urlTemplate: string;
|
|
22
47
|
readonly urlAlias: string;
|
|
23
48
|
readonly postsCount: string;
|
|
49
|
+
readonly parent: Nullable<{ id: number; name: string }>;
|
|
50
|
+
readonly depth: number;
|
|
24
51
|
}
|
|
25
52
|
|
|
26
53
|
export interface PostShort {
|
|
@@ -83,3 +110,29 @@ export interface ModuleConfig {
|
|
|
83
110
|
readonly shortcodes: ShortCodeItemType[];
|
|
84
111
|
readonly fileScenarios: FileScenarios;
|
|
85
112
|
}
|
|
113
|
+
|
|
114
|
+
export interface CategoryFormValues {
|
|
115
|
+
language: Nullable<OptionType>;
|
|
116
|
+
name: string;
|
|
117
|
+
parent: Nullable<OptionType<number>>;
|
|
118
|
+
urlAlias: string;
|
|
119
|
+
|
|
120
|
+
pageTitle: Nullable<string>;
|
|
121
|
+
pageDescription: Nullable<string>;
|
|
122
|
+
pageKeywords: Nullable<string>;
|
|
123
|
+
openGraphTitle: Nullable<string>;
|
|
124
|
+
openGraphDescription: Nullable<string>;
|
|
125
|
+
openGraphImage: Nullable<SingleFileInputValueType>;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface CategoryPayload {
|
|
129
|
+
language: Nullable<string>;
|
|
130
|
+
name: string;
|
|
131
|
+
parent: Nullable<number>;
|
|
132
|
+
urlAlias: Nullable<string>;
|
|
133
|
+
isDefault: boolean;
|
|
134
|
+
|
|
135
|
+
pageTitle: Nullable<string>;
|
|
136
|
+
pageDescription: Nullable<string>;
|
|
137
|
+
openGraphImage: Nullable<string>;
|
|
138
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { USER_MODULE_NAMESPACE } from '@/store/user';
|
|
2
|
+
|
|
3
|
+
export function namespace(namespace: string, name: string): string {
|
|
4
|
+
return `${namespace}/${name}`;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function userNamespace(name: string): string {
|
|
8
|
+
return namespace(USER_MODULE_NAMESPACE, name);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function generateNumberArray(length: number): Array<number> {
|
|
12
|
+
return Array.from({ length }, (_, index) => index);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getNameWithDepth(name: string, depth: number): string {
|
|
16
|
+
return (
|
|
17
|
+
generateNumberArray(depth)
|
|
18
|
+
.map(() => '—')
|
|
19
|
+
.join(' ') +
|
|
20
|
+
' ' +
|
|
21
|
+
name
|
|
22
|
+
);
|
|
23
|
+
}
|
package/src/views/App.vue
CHANGED
|
@@ -5,15 +5,23 @@
|
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
7
|
<script lang="ts">
|
|
8
|
-
import { defineComponent, SetupContext } from '@vue/composition-api';
|
|
8
|
+
import { defineComponent, onMounted, SetupContext } from '@vue/composition-api';
|
|
9
9
|
|
|
10
|
-
import { getBlogMenuItem } from '
|
|
10
|
+
import { getBlogMenuItem } from '@/constants/menu';
|
|
11
|
+
import { useStore } from '@/hooks';
|
|
12
|
+
import { USER_ACTION_TYPES } from '@/store/user';
|
|
13
|
+
import { userNamespace } from '@/utils/common';
|
|
11
14
|
|
|
12
15
|
export default defineComponent({
|
|
13
16
|
name: 'App',
|
|
14
17
|
setup(props, context: SetupContext) {
|
|
18
|
+
const store = useStore(context);
|
|
15
19
|
const blogMenuItem = getBlogMenuItem({ t: context.root.$t });
|
|
16
20
|
|
|
21
|
+
onMounted(() => {
|
|
22
|
+
store.dispatch(userNamespace(USER_ACTION_TYPES.FETCH_USER_PROFILE));
|
|
23
|
+
});
|
|
24
|
+
|
|
17
25
|
return {
|
|
18
26
|
sidebarMenuList: [blogMenuItem],
|
|
19
27
|
};
|