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
43 changes: 43 additions & 0 deletions ui/src/api/type/role.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { RoleTypeEnum } from '@/enums/system'

interface RoleItem {
id: string,
role_name: string,
type: RoleTypeEnum,
create_user: string,
internal: boolean,
}

interface ChildrenPermissionItem {
id: string
name: string
enable: boolean
}

interface RolePermissionItem {
id: string,
name: string,
children: {
id: string,
name: string,
permission: ChildrenPermissionItem[],
enable: boolean,
}[]
}

interface RoleTableDataItem {
module: string
name: string
permission: ChildrenPermissionItem[]
enable: boolean
perChecked: string[]
indeterminate: boolean
}

interface CreateOrUpdateParams {
role_id?: string,
role_name: string,
role_type?: RoleTypeEnum,
}

export type { RoleItem, RolePermissionItem, RoleTableDataItem, CreateOrUpdateParams, ChildrenPermissionItem }
67 changes: 67 additions & 0 deletions ui/src/api/user/role.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { get, post, del } from '@/request/index'
import type { Ref } from 'vue'
import { Result } from '@/request/Result'
import type { RoleItem, RolePermissionItem, CreateOrUpdateParams } from '@/api/type/role'
import { RoleTypeEnum } from '@/enums/system'

const prefix = '/system/role'
/**
* 获取角色列表
*/
const getRoleList: (loading?: Ref<boolean>) => Promise<Result<{ internal_role: RoleItem[], custom_role: RoleItem[] }>> = (loading) => {
return get(`${prefix}`, undefined, loading)
}

/**
* 根据类型获取角色权限模版列表
*/
const getRoleTemplate: (role_type: RoleTypeEnum, loading?: Ref<boolean>) => Promise<Result<RolePermissionItem[]>> = (role_type, loading) => {
return get(`${prefix}/template/${role_type}`, undefined, loading)
}

/**
* 获取角色权限选中
*/
const getRolePermissionList: (role_id: string, loading?: Ref<boolean>) => Promise<Result<RolePermissionItem[]>> = (role_id, loading) => {
return get(`${prefix}/${role_id}/permission`, undefined, loading)
}

/**
* 新建或更新角色
*/
const CreateOrUpdateRole: (
data: CreateOrUpdateParams,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (data, loading) => {
return post(`${prefix}`, data, undefined, loading)
}

/**
* 删除角色
*/
const deleteRole: (role_id: string, loading?: Ref<boolean>) => Promise<Result<boolean>> = (
role_id,
loading,
) => {
return del(`${prefix}/${role_id}`, undefined, {}, loading)
}

/**
* 保存角色权限
*/
const saveRolePermission: (
role_id: string,
data: { id: string, enable: boolean }[],
loading?: Ref<boolean>,
) => Promise<Result<any>> = (role_id, data, loading) => {
return post(`${prefix}/${role_id}/permission`, data, undefined, loading)
}

export default {
getRoleList,
getRolePermissionList,
getRoleTemplate,
CreateOrUpdateRole,
deleteRole,
saveRolePermission
}
6 changes: 6 additions & 0 deletions ui/src/enums/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ export enum AuthorizationEnum {
KNOWLEDGE = 'KNOWLEDGE',
APPLICATION = 'APPLICATION',
}

export enum RoleTypeEnum {
ADMIN = 'ADMIN',
USER = 'USER',
WORKSPACE_MANAGE = 'WORKSPACE_MANAGE',
}
2 changes: 2 additions & 0 deletions ui/src/locales/lang/en-US/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,11 @@ export default {
addParam: 'Add Parameter',
},
inputPlaceholder: 'Please input',
selectPlaceholder: 'Please select',
title: 'Title',
content: 'Content',
rename: 'Rename',
renameSuccess: 'Successful',
EditAvatarDialog: {
title: 'App Logo',
customizeUpload: 'Custom Upload',
Expand Down
4 changes: 3 additions & 1 deletion ui/src/locales/lang/en-US/views/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import notFound from './404'
import application from './application'
import role from './role'
import applicationOverview from './application-overview'
import knowledge from './knowledge'
import system from './system'
Expand Down Expand Up @@ -32,5 +33,6 @@ export default {
problem,
log,
login,
operateLog
operateLog,
role
}
22 changes: 22 additions & 0 deletions ui/src/locales/lang/en-US/views/role.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export default {
title: 'Role management',
internalRole: 'System built-in roles',
customRole: 'Custom roles',
systemAdmin: 'System admin',
workspaceAdmin: 'Workspace admin',
user: 'Regular user',
roleName: 'Role name',
inheritingRole: 'Inherited role',
delete: {
confirmTitle: 'Confirm to delete role:',
confirmMessage: 'After deletion, all members under this role will be removed. Please proceed with caution.',
},
permission: {
title: 'Permission configuration',
operationTarget: 'Operation target',
moduleName: 'Module name'
},
member: {
title: 'Members'
}
};
2 changes: 2 additions & 0 deletions ui/src/locales/lang/zh-CN/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,11 @@ export default {
addParam: '添加参数',
},
inputPlaceholder: '请输入',
selectPlaceholder: '请选择',
title: '标题',
content: '内容',
rename: '重命名',
renameSuccess: '重命名成功',
EditAvatarDialog: {
title: '应用头像',
customizeUpload: '自定义上传',
Expand Down
2 changes: 2 additions & 0 deletions ui/src/locales/lang/zh-CN/views/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import document from './document'
import system from './system'
import userManage from './user-manage'
import resourceAuthorization from './resource-authorization'
import role from './role'
import application from './application'
import problem from './problem'
import applicationOverview from './application-overview'
Expand All @@ -24,6 +25,7 @@ export default {
system,
userManage,
resourceAuthorization,
role,
application,
problem,
applicationOverview,
Expand Down
22 changes: 22 additions & 0 deletions ui/src/locales/lang/zh-CN/views/role.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export default {
title: '角色管理',
internalRole: '系统内置角色',
customRole: '自定义角色',
systemAdmin: '系统管理员',
workspaceAdmin: '工作空间管理员',
user: '普通用户',
roleName: '角色名称',
inheritingRole: '继承角色',
delete: {
confirmTitle: '是否删除角色:',
confirmMessage: '删除后,该角色下的成员都会被移除,请谨慎操作。',
},
permission: {
title: '权限配置',
operationTarget: '操作对象',
moduleName: '模块名称'
},
member: {
title: '成员'
}
}
2 changes: 2 additions & 0 deletions ui/src/locales/lang/zh-Hant/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,11 @@ export default {
addParam: '新增參數',
},
inputPlaceholder: '請輸入',
selectPlaceholder: '請選擇',
title: '標題',
content: '内容',
rename: '重命名',
renameSuccess: '重命名成功',
EditAvatarDialog: {
title: '應用頭像',
customizeUpload: '自訂上傳',
Expand Down
4 changes: 3 additions & 1 deletion ui/src/locales/lang/zh-Hant/views/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import notFound from './404'
import application from './application'
import role from './role'
import applicationOverview from './application-overview'
import knowledge from './knowledge'
import system from './system'
Expand Down Expand Up @@ -32,5 +33,6 @@ export default {
problem,
log,
login,
operateLog
operateLog,
role
}
22 changes: 22 additions & 0 deletions ui/src/locales/lang/zh-Hant/views/role.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export default {
title: '角色管理',
internalRole: '系統內置角色',
customRole: '自定義角色',
systemAdmin: '系統管理員',
workspaceAdmin: '工作空間管理員',
user: '普通用戶',
roleName: '角色名稱',
inheritingRole: '繼承角色',
delete: {
confirmTitle: '是否刪除角色:',
confirmMessage: '刪除後,該角色下的成員都會被移除,請謹慎操作。',
},
permission: {
title: '權限配置',
operationTarget: '操作對象',
moduleName: '模塊名稱'
},
member: {
title: '成員'
}
};
13 changes: 13 additions & 0 deletions ui/src/router/modules/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ const systemRouter = {
},
component: () => import('@/views/resource-authorization/index.vue'),
},
{
path: '/system/role',
name: 'role',
meta: {
icon: 'app-resource-authorization', // TODO
iconActive: 'app-resource-authorization-active', // TODO
title: 'views.role.title',
activeMenu: '/system',
parentPath: '/system',
parentName: 'system',
},
component: () => import('@/views/role/index.vue'),
},
{
path:'/system/setting',
name: 'setting',
Expand Down
3 changes: 3 additions & 0 deletions ui/src/styles/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,9 @@ h5 {
.color-success {
color: var(--el-color-success);
}
.color-input-placeholder {
color: var(--app-input-color-placeholder);
}
.avatar-purple {
background: #7f3bf5;
}
Expand Down
82 changes: 82 additions & 0 deletions ui/src/views/role/component/CreateOrUpdateRoleDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<template>
<el-dialog :title="`${!form.role_id ? $t('common.create') : $t('common.rename')}${$t('views.role.customRole')}`"
v-model="dialogVisible" :close-on-click-modal="false" :close-on-press-escape="false" :destroy-on-close="true">
<el-form label-position="top" ref="formRef" :rules="rules" :model="form" require-asterisk-position="right">
<el-form-item :label="$t('views.role.roleName')" prop="role_name">
<el-input v-model="form.role_name" maxlength="64"
:placeholder="`${$t('common.inputPlaceholder')}${$t('views.role.roleName')}`" />
</el-form-item>
<el-form-item v-if="!form.role_id" :label="$t('views.role.inheritingRole')" prop="role_type">
<el-select v-model="form.role_type"
:placeholder="`${$t('common.selectPlaceholder')}${$t('views.role.inheritingRole')}`">
<el-option v-for="(label, value) in roleTypeMap" :key="value" :label="label" :value="value" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }} </el-button>
<el-button type="primary" @click="submit(formRef)" :loading="loading">
{{ !form.role_id ? $t('common.create') : $t('common.save') }}
</el-button>
</span>
</template>
</el-dialog>
</template>

<script setup lang="ts">
import { ref, reactive } from 'vue'
import type { FormInstance } from 'element-plus'
import { MsgSuccess } from '@/utils/message'
import { t } from '@/locales'
import type { RoleItem, CreateOrUpdateParams } from '@/api/type/role'
import RoleApi from '@/api/user/role'
import { roleTypeMap } from '../index'

const emit = defineEmits<{
(e: 'refresh', currentRole: RoleItem): void;
}>();

const dialogVisible = ref<boolean>(false)
const defaultForm = {
role_name: ''
}
const form = ref<CreateOrUpdateParams>({
...defaultForm,
})
function open(item?: RoleItem) {
if (item) {
form.value = {
role_name: item.role_name,
role_type: item.type,
role_id: item.id,
}
} else {
form.value = { ...defaultForm }
}
dialogVisible.value = true
}

const formRef = ref<FormInstance>();

const rules = reactive({
role_name: [{ required: true, message: `${t('common.inputPlaceholder')}${t('views.role.roleName')}`, trigger: 'blur' }],
role_type: [{ required: true, message: `${t('common.selectPlaceholder')}${t('views.role.inheritingRole')}`, trigger: 'blur' }]
})

const loading = ref<boolean>(false)
const submit = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid) => {
if (valid) {
RoleApi.CreateOrUpdateRole(form.value, loading).then((res: any) => {
MsgSuccess(!form.value.role_id ? t('common.createSuccess') : t('common.renameSuccess'))
emit('refresh', res.data)
dialogVisible.value = false
})
}
})
}

defineExpose({ open })
</script>
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 you've provided seems to be a Vue.js component using Element Plus. Here are a few points of concern:

  1. Duplicate Import Statements: You have two imports for MsgSuccess which is unnecessary and could potentially lead to conflicts.

  2. Variable Naming Consistency: It's good practice to use consistent naming, such as PascalCase for variable names unless they align with existing conventions or specific libraries.

  3. Conditional Check for Role ID: The logic for opening the dialog is inconsistent. When passing an item from parent components, it should handle both creating new roles (no role_id) and renaming them (has role_id).

  4. Loading State Management: Ensure that the loading state (loading) is properly reset after successful operations, especially if multiple submissions occur.

  5. Error Handling: Implement proper error handling for API calls to RoleApi.CreateOrUpdateRole.

  6. Accessibility Considerations: Ensure that all elements have appropriate accessibility attributes like aria-labels and alt text, depending on their usage.

  7. Type Definitions: Verify that the types defined in interface match those used throughout the code to prevent runtime errors.

Here’s an updated version of the script part based on these considerations:

<script setup lang="ts">
import { ref, reactive } from 'vue';
import type { FormInstance } from 'element-plus';
import { MsgSuccess } from '@/utils/message';
import { t } from '@/locales';
import type { RoleItem, CreateOrUpdateParams } from '@/api/type/role';
import RoleApi from '@/api/user/role';
import { roleTypeMap } from '../index';

const emit = defineEmits<{
  (e: 'refresh', currentRole: RoleItem): void;
}>();

const dialogVisible = ref<boolean>(false);
const defaultForm = {
  role_name: ""
};
const form = ref<CreateOrUpdateParams>({
  ...defaultForm,
});

function open(data?: RoleItem) {
  if (data) {
    const { id, role_name, type } = data || {};
    // Use defaults instead of empty strings to maintain consistency
    form.value = {
      role_name,
      role_type: '' + (type !== undefined && typeof type === 'number' ? type : ''),
      role_id: id !== undefined && typeof id === 'number' ? id : '',
    };
  } else {
    form.value = { ...defaultForm };
  }
  dialogVisible.value = true;
}

const formRef = ref<FormInstance>();

// Adjust the validation rules to only trigger blur when necessary
let fieldBlurred = '';

const rules = reactive({
  role_name: [
    { required: true, message: `${t('common.inputPlaceholder')}${t('views.role.roleName')}`, trigger: ['blur'] },
  ],
  role_type: [
    { required: true, message: `${t('common.selectPlaceholder')}${t('views.role.inheritingRole')}`, trigger: ['change'] }, // Change to change event since this might trigger dynamically
  ]
});

const loading = ref(false);

function submit(formEl: FormInstance | undefined) {
  if (!formEl) return;

  // Resetting the blurred flag here ensures correct feedback behavior
  fieldBlurred = '';
  formEl.validate(async (valid) => {
    if (valid) {
      try {
        loading.value = true;
        const response = await RoleApi.CreateOrUpdateRole(form.value, loading);
        MsgSuccess(!form.value.role_id ? t('common.createSuccess') : t('common.renameSuccess'));
        emit('refresh', response?.data);
        dialogVisible.value = false;
      } catch (error) {
        console.error("An error occurred", error);
        // Optionally display a human-readable/error-capturing message
        MsgSuccess(t('views.common.genericOperationFailure'), "danger");
      } finally {
        loading.value = false; // Reset loading state regardless of success
      }
    } else {
      // Trigger validation error messages manually
      document.activeElement.focus();
      setTimeout(() => {
        // Wait for focus transition before showing error message
        document.getElementById(fieldBlurred)?.focus(); // Using elementId for simplicity assuming unique ids per input
      });
    }
  });
}

defineExpose({ open });
</script>

This revision addresses some of the mentioned concerns while maintaining readability and adhering to best practices. Make sure to update other parts of your application accordingly, such as updating any usages or additional styling changes where needed.

5 changes: 5 additions & 0 deletions ui/src/views/role/component/Member.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div></div>
</template>

<script setup lang="ts"></script>
Loading
Loading