diff --git a/frontend/quasar.config.js b/frontend/quasar.config.js
index 40530ff..20a249d 100644
--- a/frontend/quasar.config.js
+++ b/frontend/quasar.config.js
@@ -37,7 +37,8 @@ module.exports = configure(function (/* ctx */) {
// https://v2.quasar.dev/quasar-cli-vite/boot-files
boot: [
'axios',
- 'error-handler'
+ 'error-handler',
+ 'permissions'
],
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
diff --git a/frontend/src/boot/permissions.ts b/frontend/src/boot/permissions.ts
new file mode 100644
index 0000000..4d5050a
--- /dev/null
+++ b/frontend/src/boot/permissions.ts
@@ -0,0 +1,34 @@
+// src/boot/permissions.ts
+import { boot } from 'quasar/wrappers';
+import { useAuthStore } from 'src/stores/useAuthStore';
+import { DirectiveBinding } from 'vue';
+
+export const MenuMapping = {
+ home: null,
+ app: null,
+ version: null,
+ user: 'user',
+ role: 'role',
+ domain: null,
+ userDomain: null,
+};
+
+const store = useAuthStore();
+
+export default boot(({ app }) => {
+ app.directive('permissions', {
+ mounted(el: HTMLElement, binding: DirectiveBinding) {
+ const { value } = binding;
+ if (!value || store.isSuperAdmin) {
+ return;
+ }
+ if (!store.permissions[value]) {
+ if (el.parentNode) {
+ el.parentNode.removeChild(el);
+ } else {
+ el.style.display = 'none';
+ }
+ }
+ },
+ });
+});
diff --git a/frontend/src/components/EssentialLink.vue b/frontend/src/components/EssentialLink.vue
index 3a677c5..4d4be1b 100644
--- a/frontend/src/components/EssentialLink.vue
+++ b/frontend/src/components/EssentialLink.vue
@@ -1,5 +1,6 @@
(), {
caption: '',
diff --git a/frontend/src/components/ShareDomain/ShareDomainDialog.vue b/frontend/src/components/ShareDomain/ShareDomainDialog.vue
index 54d4327..f47d2ca 100644
--- a/frontend/src/components/ShareDomain/ShareDomainDialog.vue
+++ b/frontend/src/components/ShareDomain/ShareDomainDialog.vue
@@ -53,7 +53,7 @@
-
+
@@ -68,7 +68,7 @@
diff --git a/frontend/src/pages/RoleManagement.vue b/frontend/src/pages/RoleManagement.vue
index d5e5cb8..8d5ef20 100644
--- a/frontend/src/pages/RoleManagement.vue
+++ b/frontend/src/pages/RoleManagement.vue
@@ -27,7 +27,7 @@
:pagination="pagination" >
-
+
@@ -132,7 +132,12 @@ const statusFilterOptions = [
const pagination = ref({ sortBy: 'id', descending: true, rowsPerPage: 20 });
-const roles = computed(() => userStore.roles.concat(EMPTY_ROLE));
+const roles = computed(() => {
+ if (userStore.roles.length > 0) {
+ return userStore.roles.concat(EMPTY_ROLE);
+ }
+ return userStore.roles;
+});
const selected = ref();
diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts
index e6c96f4..bb9abae 100644
--- a/frontend/src/router/index.ts
+++ b/frontend/src/router/index.ts
@@ -34,13 +34,14 @@ const routerInstance = createRouter({
export default route(function (/* { store, ssrContext } */) {
- routerInstance.beforeEach(async (to) => {
+ routerInstance.beforeEach(async (to, from) => {
// clear alert on route change
//const alertStore = useAlertStore();
//alertStore.clear();
// redirect to login page if not logged in and trying to access a restricted page
- const publicPages = ['/login'];
+ const loginPage = '/login';
+ const publicPages = [loginPage];
const authRequired = !publicPages.includes(to.path);
const authStore = useAuthStore();
@@ -49,8 +50,12 @@ export default route(function (/* { store, ssrContext } */) {
return '/login';
}
+ if (authStore.token && to.path === loginPage) {
+ return from.path == '/' ? '/' : false;
+ }
+
// redirect to domain setting page if no domain exist
- const domainPages = [...publicPages, '/domain', '/userDomain', '/user'];
+ const domainPages = [...publicPages, '/domain', '/userDomain', '/user', '/role'];
if (!authStore.hasDomain && !domainPages.includes(to.path)) {
Dialog.create({
title: '注意',
diff --git a/frontend/src/router/routes.ts b/frontend/src/router/routes.ts
index 65ab3b2..6a804d5 100644
--- a/frontend/src/router/routes.ts
+++ b/frontend/src/router/routes.ts
@@ -18,12 +18,10 @@ const routes: RouteRecordRaw[] = [
children: [
{ path: '', component: () => import('pages/IndexPage.vue') },
{ path: 'ruleEditor', component: () => import('pages/RuleEditor.vue') },
- { path: 'test', component: () => import('pages/testQursar.vue') },
{ path: 'flow', component: () => import('pages/testFlow.vue') },
{ path: 'FlowChartTest', component: () => import('pages/FlowChartTest.vue') },
{ path: 'flowEditor', component: () => import('pages/FlowEditorPage.vue') },
// { path: 'FlowChart', component: () => import('pages/FlowChart.vue') },
- { path: 'right', component: () => import('pages/testRight.vue') },
{ path: 'domain', component: () => import('pages/TenantDomain.vue') },
{ path: 'userdomain', component: () => import('pages/UserDomain.vue')},
{ path: 'user', component: () => import('pages/UserManagement.vue')},
diff --git a/frontend/src/stores/useAuthStore.ts b/frontend/src/stores/useAuthStore.ts
index 114503f..1998c6d 100644
--- a/frontend/src/stores/useAuthStore.ts
+++ b/frontend/src/stores/useAuthStore.ts
@@ -1,27 +1,31 @@
import { defineStore } from 'pinia';
import { api } from 'boot/axios';
import { router } from 'src/router';
-import { IDomainInfo } from '../types/DomainTypes';
+import { IDomain, IDomainInfo } from '../types/DomainTypes';
import { jwtDecode } from 'jwt-decode';
import { useAppStore } from './useAppStore';
-import { useUserStore } from './useUserStore';
+import { userToUserRolesDisplay, useUserStore } from './useUserStore';
+import { IRolesDisplay, IUser, IUserRolesDisplay } from 'src/types/UserTypes';
+import { IResponse } from 'src/types/BaseTypes';
-interface UserInfo {
- firstName: string;
- lastName: string;
- fullName: string;
- email: string;
+interface IPermission {
+ id: number,
+ menu: string,
+ function: string,
+ privilege: string,
+ link: string
}
+type IPermissions = { [key: string]: string };
+
export interface IUserState {
token?: string;
returnUrl: string;
currentDomain: IDomainInfo;
LeftDrawer: boolean;
- userId?: string;
- userInfo: UserInfo;
- roles:string,
- permissions: string;
+ userInfo: IUserRolesDisplay;
+ tenant: string;
+ permissions: IPermissions;
}
export const useAuthStore = defineStore('auth', {
@@ -30,17 +34,22 @@ export const useAuthStore = defineStore('auth', {
returnUrl: '',
LeftDrawer: false,
currentDomain: {} as IDomainInfo,
- userId: '',
- userInfo: {} as UserInfo,
- roles:'',
- permissions: '',
+ userInfo: {} as IUserRolesDisplay,
+ tenant: '',
+ permissions: {} as IPermissions
}),
getters: {
- toggleLeftDrawer(): boolean {
- return this.LeftDrawer;
+ userId(): number {
+ return this.userInfo.id;
},
hasDomain(): boolean {
return this.currentDomain.id !== undefined;
+ },
+ getRoles(): IRolesDisplay[] {
+ return this.userInfo.roles;
+ },
+ isSuperAdmin(): boolean {
+ return this.userInfo.isSuperuser;
}
},
actions: {
@@ -56,15 +65,20 @@ export const useAuthStore = defineStore('auth', {
params.append('password', password);
try {
const result = await api.post(`api/token`, params);
- console.info(result);
+ // console.info(result);
this.token = result.data.access_token;
- const tokenJson = jwtDecode(result.data.access_token);
- this.userId = tokenJson.sub;
- this.permissions = (tokenJson as any).permissions==='ALL' ? 'admin': 'user';
+ const tokenJson = jwtDecode<{sub: number, tenant: string, roles: string}>(result.data.access_token);
+ this.tenant = tokenJson.tenant;
+ this.userInfo.id = tokenJson.sub;
+ this.userInfo.isSuperuser = tokenJson.roles === 'super';
api.defaults.headers['Authorization'] = 'Bearer ' + this.token;
- await this.loadCurrentDomain();
- this.userInfo = await this.getUserInfo();
- router.push(this.returnUrl || '/');
+ await Promise.all([
+ this.loadCurrentDomain(),
+ this.loadUserInfo(),
+ this.loadPermission()
+ ]);
+ await router.push(this.returnUrl || '/');
+ this.returnUrl = '';
return true;
} catch (e) {
console.error(e);
@@ -72,29 +86,40 @@ export const useAuthStore = defineStore('auth', {
}
},
async loadCurrentDomain() {
- const resp = await api.get(`api/defaultdomain`);
+ const resp = await api.get>(`api/defaultdomain`);
const activedomain = resp?.data?.data;
- this.currentDomain = {
- id: activedomain?.id,
- domainName: activedomain?.name,
- kintoneUrl: activedomain?.url,
- };
- },
- async getUserInfo():Promise{
- const resp = (await api.get(`api/v1/users/me`)).data.data;
- return {
- firstName: resp.first_name,
- lastName: resp.last_name,
- fullName: resp.last_name + ' ' + resp.first_name,
- email: resp.email,
+ if (!activedomain) {
+ this.currentDomain = {} as IDomainInfo;
+ } else {
+ this.currentDomain = {
+ id: activedomain.id,
+ domainName: activedomain.name,
+ kintoneUrl: activedomain.url,
+ };
}
},
- logout() {
+ async loadUserInfo() {
+ const resp = (await api.get>(`api/v1/users/me`))?.data?.data;
+ this.userInfo = userToUserRolesDisplay(resp)
+ },
+ async loadPermission() {
+ this.permissions = {} as IPermissions;
+ if (this.isSuperAdmin) return;
+ const resp = (await api.get>(`api/v1/userpermssions`)).data.data;
+ resp.forEach((permission) => {
+ this.permissions[permission.link] = permission.menu;
+ this.permissions[permission.privilege] = permission.function;
+ });
+ },
+ async logout() {
this.token = '';
this.currentDomain = {} as IDomainInfo; // 清空当前域
useAppStore().reset();
useUserStore().reset();
- router.push('/login');
+ await router.push('/login');
+ this.tenant = '';
+ this.userInfo = {} as IUserRolesDisplay;
+ this.permissions = {} as IPermissions;
},
async setCurrentDomain(domain?: IDomainInfo) {
if (!domain) {
diff --git a/frontend/src/stores/useUserStore.ts b/frontend/src/stores/useUserStore.ts
index dadf05e..caae1dd 100644
--- a/frontend/src/stores/useUserStore.ts
+++ b/frontend/src/stores/useUserStore.ts
@@ -124,6 +124,7 @@ export function userToUserDisplay(user: IUser): IUserDisplay {
}
export function userToUserRolesDisplay(user: IUser): IUserRolesDisplay {
+ if (!user) return {} as IUserRolesDisplay;
const userRolesDisplay = userToUserDisplay(user) as IUserRolesDisplay;
const roles: IRolesDisplay[] = [];
const roleIds: number[] = [];