Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions ui/src/directives/hasPermission.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { App } from 'vue'
import { hasPermission } from '@/utils/permission'

const display = async (el: any, binding: any) => {
const has = hasPermission(
binding.value?.permission || binding.value,
binding.value?.compare || 'OR',
)
if (!has) {
el.style.display = 'none'
} else {
delete el.style.display
}
}

export default {
install: (app: App) => {
app.directive('hasPermission', {
async created(el: any, binding: any) {
display(el, binding)
},
async beforeUpdate(el: any, binding: any) {
display(el, binding)
},
})
},
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided code looks mostly correct and functions as intended to conditionally display elements based on user permissions using Vue directives with TypeScript types. However, there are a few minor improvements and optimizations that could be made:

  1. Type Safety: Ensure that binding.value is always an object before accessing its properties. This helps prevent runtime errors due to undefined values.

  2. Error Handling: Add some basic error handling to ensure that permission logic runs smoothly even if input is unexpected.

  3. Dynamic Permission Check: If you have dynamic or complex permission checks, consider implementing more sophisticated validation within the display function.

  4. Optimization: Since this directive only needs to run during creation and updates, you can use asynchronous methods like async created() without unnecessary complexity.

Here's the revised code:

import type { App } from 'vue'
import { hasPermission } from '@/utils/permission'

const display = async (el: HTMLElement, binding: DirectiveBinding) => {
  // Validate inputs
  if (!isObject(binding.value)) {
    console.error('Directive "hasPermission" requires an object argument.');
    return;
  }

  const has = await new Promise((resolve) => {
    resolve(hasPermission(
      binding.value.permission || binding.value,
      binding.value.compare || 'OR',
    ));
  });

  if (!has) {
    el.style.display = 'none';
  } else {
    delete el.style.display;
  }
}

export default {
  install: (app: App) => {
    app.directive('hasPermission', {
      async created(el: HTMLElement, binding: DirectiveBinding) {
        display(el, binding);
      },
      async updated(el: HTMLElement, binding: DirectiveBinding) {
        display(el, binding);
      },
    });
  },
};

// Helper function to check if a value is an object
function isObject(obj: unknown): obj is Record<string, any> {
  return typeof obj === 'object' && obj !== null;
}

These changes enhance maintainability and robustness of the code.

14 changes: 14 additions & 0 deletions ui/src/directives/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { App } from 'vue'

const directives = import.meta.glob('./*.ts', { eager: true })
const install = (app: App) => {
Object.keys(directives)
.filter((key: string) => {
return !key.endsWith('index.ts')
})
.forEach((key: string) => {
const directive: any = directives[key]
app.use(directive.default)
})
}
export default { install }
6 changes: 4 additions & 2 deletions ui/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import App from './App.vue'
import router from '@/router'
import i18n from '@/locales'
import Components from '@/components'
import directives from '@/directives'
const app = createApp(App)
app.use(createPinia())
for (const [key, component] of Object.entries(ElementPlusIcons)) {
Expand All @@ -18,11 +19,12 @@ for (const [key, component] of Object.entries(ElementPlusIcons)) {
const locale_map: any = {
'zh-CN': zhCn,
'zh-Hant': zhTW,
'en-US': enUs
'en-US': enUs,
}
app.use(ElementPlus, {
locale: locale_map[localStorage.getItem('MaxKB-locale') || navigator.language || 'en-US']
locale: locale_map[localStorage.getItem('MaxKB-locale') || navigator.language || 'en-US'],
})
app.use(directives)
app.use(router)
app.use(i18n)
app.use(Components)
Expand Down
6 changes: 3 additions & 3 deletions ui/src/stores/modules/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ const useLoginStore = defineStore('user', {
return !this.themeInfo?.theme || this.themeInfo?.theme === '#3370FF'
},
async profile() {
return UserApi.getUserProfile().then((ok: { data: User }) => {
this.userInfo = ok.data
return UserApi.getUserProfile().then((ok) => {
this.userInfo = ok
useLocalStorage<string>(localeConfigKey, 'en-US').value =
ok.data?.language || this.getLanguage()
// return this.asyncGetProfile()
Expand Down Expand Up @@ -72,7 +72,7 @@ const useLoginStore = defineStore('user', {
? [...this.userInfo?.permissions, 'x-pack']
: this.userInfo?.permissions
} else {
return []
return this.userInfo?.permissions
}
},
getRole() {
Expand Down
7 changes: 7 additions & 0 deletions ui/src/utils/permission/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Permission } from '@/utils/permission/type'
const PermissionConst = {
USER_READ: new Permission('USER:READ'),
USER_CREATE: new Permission('USER:CREATE'),
KNOWLEDGE_READ: new Permission('KNOWLEDGE:READ'),
}
export default PermissionConst
6 changes: 3 additions & 3 deletions ui/src/utils/permission/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import useStore from '@/stores';
import useStore from '@/stores'
import { Role, Permission, ComplexPermission } from '@/utils/permission/type'
/**
* 是否包含当前权限
* @param permission 当前权限
* @returns True 包含 false 不包含
*/
const hasPermissionChild = (permission: Role | string | Permission | ComplexPermission) => {
const { user } = useStore();
const { user } = useStore()
const permissions = user.getPermissions()
const role = user.getRole()
if (!permission) {
Expand Down Expand Up @@ -43,7 +43,7 @@ export const hasPermission = (
| string
| Permission
| ComplexPermission,
compare: 'OR' | 'AND'
compare: 'OR' | 'AND',
): boolean => {
if (permission instanceof Array) {
return compare === 'OR'
Expand Down
18 changes: 18 additions & 0 deletions ui/src/utils/permission/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@ export class Permission {
constructor(permission: string) {
this.permission = permission
}
/**
* 工作空间权限
* @param workspace_id 工作空间id
* @returns 工作空间权限
*/
getWorkspacePermission(workspace_id: string) {
return `${this.permission}:/WORKSPACE/${workspace_id}`
}
/**
* 工作空间资源权限
* @param workspace_id 工作空间id
* @param resource 资源
* @param resource_id 资源id
* @returns 工作空间资源权限
*/
getWorkspaceResourcePermission(workspace_id: string, resource: string, resource_id: string) {
return `${this.permission}:/WORKSPACE/${workspace_id}/${resource}/${resource_id}`
}
}
/**
* 复杂权限对象
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no obvious syntax errors or logical issues with the provided code snippet. However, here are some potential suggestions for further improvement:

  1. Type Annotations: Add type annotations to both class fields and methods to improve readability and maintainability.

  2. Error Handling: Consider adding error handling around the API calls if applicable, such as catching exceptions when retrieving permissions.

  3. Code Refactoring: Depending on the specific requirements, you might want to refactor how the permission strings are generated. For example, using template literals could make it more readable and easier to extend in the future.

Here's an updated version of the code including some minor adjustments:

import { WorkspaceId } from './workspace-id'; // Assuming WorkspaceId is defined somewhere

export interface Permission {
  permission: string;
}

export class Permission implements Permission {
  private readonly permission: string;

  constructor(permission: string) {
    this.permission = permission;
  }

  /**
   * 获取工作空间权限
   * @param workspace_id 工作空间ID
   * @returns 工作空间权限字符串
   */
  public getWorkspacePermission(workspace_id: WorkshopId): string {
    return `${this.permission}:/WORKSPACE/${workspace_id}`;
  }

  /**
   * 获取工作空间资源权限
   * @param workspace_id 工作空间ID
   * @param resource 源资源类型(如 'user', 'project' 等)
   * @param resourceId 资源ID
   * @returns 工作空间资源权限字符串
   */
  public getResourceWorkspacePermission(
    workspace_id: WorkshopId,
    resource: string,
    resourceId: number | string
  ): string {
    const resourcePartSuffix = typeof resourceId === 'number'
      ? `/r${resourceId.toString()}` : `/${resource_id}`;

    return `${this.permission}:/WORKSPACE/${workspace_id}/${resource}${resourcePartSuffix}`;
  }
}

In this revised version, I've introduced a new WorkshopId type (assuming it exists somewhere) and added parameter declarations with types. Additionally, I optimized the function names slightly and used typeof to append additional parts to the URL based on whether resourceId is a number or not. Note that these changes should be tested with actual usage patterns to ensure they meet all requirements.

Expand Down
22 changes: 20 additions & 2 deletions ui/src/views/HomeView.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
<script setup lang="ts"></script>
<script setup lang="ts">
import PermissionConst from '@/utils/permission/data'
</script>

<template>首页</template>
<template>
首页
<div v-hasPermission="PermissionConst.USER_READ.getWorkspacePermission('default')">
default工作空间用户只读
</div>
<div v-hasPermission="PermissionConst.USER_READ.getWorkspacePermission('default1')">
default1工作空间用户只读
</div>
<div v-hasPermission="PermissionConst.USER_READ">用户只读</div>
<div
v-hasPermission="
PermissionConst.KNOWLEDGE_READ.getWorkspaceResourcePermission('default', 'KNOWLEDGE', 'xxx')
"
>
default工作空间的知识库xxx权限
</div>
</template>
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code appears to be correct for the most part, but here are some minor improvements and suggestions:

  1. Ensure that v-hasPermission is properly imported and used. If it's a custom directive or function, make sure you've defined it before using it.

  2. Remove the newline after <script> tags: While this doesn't affect functionality, adhering to consistency can prevent confusion for others reading the code.

  3. Add a line break between the opening tag of the template and its content to improve readability. This isn't strictly necessary in this case but is good practice for larger templates.

  4. Consider adding more context comments explaining each section if needed, especially for newlines or significant changes like removing the previous template.

Loading