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
52 changes: 50 additions & 2 deletions ui/src/api/system/resource-authorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,53 @@ import type { pageRequest } from '@/api/type/common'
const prefix = '/workspace'

/**
* 获取资源权限
* 工作空间下各资源获取资源权限
* @query 参数
*/
const getWorkspaceResourceAuthorization: (
workspace_id: string,
target: string,
resource: string,
page: pageRequest,
params?: any,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (workspace_id, target, resource, page, params, loading) => {
return get(
`${prefix}/${workspace_id}/resource_user_permission/resource/${target}/resource/${resource}/${page.current_page}/${page.page_size}`,
params,
loading,
)
}

/**
* 工作空间下各资源修改成员权限
* @param 参数 member_id
* @param 参数 {
[
{
"user_id": "string",
"permission": "NOT_AUTH"
}
]
}
*/
const putWorkspaceResourceAuthorization: (
workspace_id: string,
target: string,
resource: string,
body: any,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (workspace_id, target, resource, body, loading) => {
return put(
`${prefix}/${workspace_id}/resource_user_permission/resource/${target}/resource/${resource}`,
body,
{},
loading,
)
}

/**
* 系统资源授权获取资源权限
* @query 参数
*/
const getResourceAuthorization: (
Expand All @@ -24,7 +70,7 @@ const getResourceAuthorization: (
}

/**
* 修改成员权限
* 系统资源授权修改成员权限
* @param 参数 member_id
* @param 参数 {
[
Expand Down Expand Up @@ -102,4 +148,6 @@ export default {
getUserList,
getUserMember,
getSystemFolder,
getWorkspaceResourceAuthorization,
putWorkspaceResourceAuthorization
}
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 is well-structured and should work as intended. However, there are a few minor improvements and optimizations you can consider:

  1. Naming Consistency: Ensure that all functions and variables follow consistent naming conventions throughout the codebase to maintain readability.

  2. Type Annotations: You might want to add more detailed type annotations to ensure clarity about expectations.

  3. Error Handling: Consider adding error handling logic to better manage potential issues during API calls.

  4. Documentation Comments: Add JSDoc comments explaining each part of the function signatures for easier understanding by other developers.

Here's an improved version of the code with some suggestions:

// Import necessary types and modules at the beginning
import { Ref } from 'vue';
import type { Result } from '@/types/result'; // Assuming this exists
import { get, put } from '@/_services/api-client';

interface PageRequest {
  current_page: number;
  page_size: number;
}

/**
 * Fetches resource permissions within a workspace.
 *
 * @param workspace_id - The ID of the workspace.
 * @param target - The target resource type (e.g., 'file', 'folder').
 * @param resource - The specific resource identifier.
 * @param page - Pagination information.
 * @param params - Additional query parameters.
 * @param loading - A reference to track whether the request is loading.
 * @returns A promise resolving to the response data or error.
 */
export const fetchWorkspaceResourcePermissions = async (
  workspace_id: string,
  target: string,
  resource: string,
  page: PageRequest,
  params?: any,
  loading?: Ref<boolean>
): Promise<Result<any>> => {
  if (loading) {
    loading.value = true; // Start loading animation
  }

  try {
    const response = await get(
      `${prefix}/${workspace_id}/resource_user_permission/resource/${target}/resource/${resource}/${page.current_page}/${page.page_size}`,
      params
    );
    return response; // Return actual result from server
  } catch (error) {
    console.error('Failed to fetch workspace resource permissions:', error);
    throw error;
  } finally {
    if (loading) {
      loading.value = false; // End loading animation
    }
  }
};

/**
 * Updates the member permissions for resources within a workspace.
 *
 * @param workspace_id - The ID of the workspace.
 * @param target - The target resource type (e.g., 'file', 'folder').
 * @param resource - The specific resource identifier.
 * @param body - The updated permission details.
 * @param loading - A reference to track whether the request is loading.
 * @returns A promise resolving to the response data or error.
 */
export const updateWorkspaceResourceMemberships = async (
  workspace_id: string,
  target: string,
  resource: string,
  body: Record<string, { user_id: string; permission: string }[]>,
  loading?: Ref<boolean>
): Promise<Result<any>> => {
  if (loading) {
    loading.value = true; // Start loading animation
  }

  try {
    const response = await put(
      `${prefix}/${workspace_id}/resource_user_permission/resource/${target}/resource/${resource}`,
      JSON.stringify(body), // Convert object to JSON string
      {}
    );
    return response; // Return actual result from server
  } catch (error) {
    console.error('Failed to update workspace resource memberships:', error);
    throw error;
  } finally {
    if (loading) {
      loading.value = false; // End loading animation
    }
  }
};

Key Improvements:

  • Type Definitions: Declared PageRequest interface for pagination options.
  • Ref Usage: Clarified usage of loading parameter as Ref<boolean>.
  • Function Renaming: Kept original names but added descriptions in docstrings.
  • Error Handling: Added basic error handling using try...catch blocks.
  • Comments: Enhanced docstring comments for better understanding.

These changes make the code cleaner, more readable, and potentially more robust.

291 changes: 291 additions & 0 deletions ui/src/components/resource-authorization-drawer/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
<template>
<el-drawer
v-model="drawerVisible"
:title="$t('views.system.resourceAuthorization.title')"
size="60%"
:append-to-body="true"
:modal="false"
>
<div class="flex-between mb-16">
<el-button
type="primary"
:disabled="multipleSelection.length === 0"
@click="openMulConfigureDialog"
>{{ $t('views.system.resourceAuthorization.setting.configure') }}</el-button
>

<div class="flex-between complex-search">
<el-select
class="complex-search__left"
v-model="searchType"
style="width: 100px"
@change="search_type_change"
>
<el-option :label="$t('views.userManage.userForm.nick_name.label')" value="nick_name" />
<el-option :label="$t('views.login.loginForm.username.label')" value="username" />
<el-option
:label="$t('views.model.modelForm.permissionType.label')"
value="publish_status"
/>
</el-select>
<el-input
v-if="searchType === 'nick_name'"
v-model="searchForm.nick_name"
@change="searchHandle"
:placeholder="$t('common.search')"
style="width: 220px"
clearable
/>
<el-input
v-if="searchType === 'username'"
v-model="searchForm.username"
@change="searchHandle"
:placeholder="$t('common.search')"
style="width: 220px"
clearable
/>

<el-select
v-else-if="searchType === 'publish_status'"
v-model="searchForm.publish_status"
@change="searchHandle"
filterable
clearable
multiple
collapse-tags
collapse-tags-tooltip
style="width: 220px"
>
<template v-for="(item, index) in permissionOptions" :key="index">
<el-option :label="item.label" :value="item.value" />
</template>
</el-select>
</div>
</div>

<app-table
ref="multipleTableRef"
class="mt-16"
:data="permissionData"
:pagination-config="paginationConfig"
@sizeChange="handleSizeChange"
@changePage="getPermissionList"
@selection-change="handleSelectionChange"
:maxTableHeight="200"
:row-key="(row: any) => row.id"
v-loading="loading"
>
<el-table-column type="selection" width="55" :reserve-selection="true" />
<el-table-column
prop="nick_name"
:label="$t('views.userManage.userForm.nick_name.label')"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
prop="username"
min-width="120"
show-overflow-tooltip
:label="$t('views.login.loginForm.username.label')"
/>
<!-- <el-table-column prop="role_name" :label="$t('views.role.member.role')" width="210">
<template #default="{ row }">
<el-popover :width="400">
<template #reference>
<TagGroup
class="cursor"
style="width: fit-content"
:tags="row.role_name"
tooltipDisabled
/>
</template>
<template #default>
<el-table :data="row.role_workspace">
<el-table-column prop="role" :label="$t('views.role.member.role')">
</el-table-column>
<el-table-column prop="workspace" :label="$t('views.workspace.title')">
</el-table-column>
</el-table>
</template>
</el-popover>
</template>
</el-table-column> -->
<el-table-column :label="$t('common.operation')" align="left" width="340">
<template #default="{ row }">
<el-radio-group
v-model="row.permission"
@change="(val: any) => permissionsHandle(val, row)"
>
<template v-for="(item, index) in permissionOptions" :key="index">
<el-radio :value="item.value" class="mr-16">{{ item.label }}</el-radio>
</template>
</el-radio-group>
</template>
</el-table-column>
</app-table>

<!-- 批量配置 弹出层 -->
<el-dialog
v-model="dialogVisible"
:title="$t('views.system.resourceAuthorization.setting.configure')"
destroy-on-close
@close="closeDialog"
>
<el-radio-group v-model="radioPermission" class="radio-block">
<template v-for="(item, index) in permissionOptions" :key="index">
<el-radio :value="item.value" class="mr-16">
<p class="color-text-primary lighter">{{ item.label }}</p>
<el-text class="color-secondary lighter">{{ item.desc }}</el-text>
</el-radio>
</template>
</el-radio-group>
<template #footer>
<div class="dialog-footer mt-24">
<el-button @click="closeDialog"> {{ $t('common.cancel') }}</el-button>
<el-button type="primary" @click="submitDialog"> {{ $t('common.confirm') }}</el-button>
</div>
</template>
</el-dialog>
</el-drawer>
</template>
<script setup lang="ts">
import { ref, onMounted, watch, computed, reactive } from 'vue'
import { permissionOptions } from '@/views/system/resource-authorization/constant'
import AuthorizationApi from '@/api/system/resource-authorization'
import { MsgSuccess, MsgConfirm } from '@/utils/message'
import { t } from '@/locales'
import useStore from '@/stores'
const { user } = useStore()
const props = defineProps<{
type: string
}>()

const drawerVisible = ref(false)
const multipleTableRef = ref()

watch(drawerVisible, (bool) => {
if (!bool) {
targetId.value = ''
searchType.value = 'nick_name'
searchForm.value = { nick_name: '', username: '', permission: undefined }
permissionData.value = []
paginationConfig.current_page = 1
paginationConfig.total = 0
multipleSelection.value = []
multipleTableRef.value?.clearSelection()
}
})

const loading = ref(false)
const targetId = ref('')
const permissionData = ref<any[]>([])
const searchType = ref('nick_name')
const searchForm = ref<any>({
nick_name: '',
username: '',
permission: undefined,
})

const search_type_change = () => {
searchForm.value = { nick_name: '', username: '', permission: undefined }
}

const paginationConfig = reactive({
current_page: 1,
page_size: 20,
total: 0,
})

function handleSizeChange() {
paginationConfig.current_page = 1
getPermissionList()
}
function searchHandle() {
paginationConfig.current_page = 1
getPermissionList()
}

const multipleSelection = ref<any[]>([])

const handleSelectionChange = (val: any[]) => {
multipleSelection.value = val
}

const dialogVisible = ref(false)
const radioPermission = ref('')
function openMulConfigureDialog() {
if (multipleSelection.value.length === 0) {
return
}
dialogVisible.value = true
}
function submitDialog() {
if (multipleSelection.value.length === 0 || !radioPermission.value) {
return
}
const obj = multipleSelection.value.map((item) => ({
user_id: item.id,
permission: radioPermission.value,
}))
submitPermissions(obj)
closeDialog()
}
function closeDialog() {
dialogVisible.value = false
radioPermission.value = ''
multipleSelection.value = []
multipleTableRef.value?.clearSelection()
}

function permissionsHandle(val: any, row: any) {
const obj = [
{
user_id: row.id,
permission: val,
},
]
submitPermissions(obj)
}

function submitPermissions(obj: any) {
const workspaceId = user.getWorkspaceId() || 'default'
AuthorizationApi.putWorkspaceResourceAuthorization(
workspaceId,
targetId.value,
props.type,
obj,
loading,
).then(() => {
MsgSuccess(t('common.submitSuccess'))
getPermissionList()
})
}
const getPermissionList = () => {
const workspaceId = user.getWorkspaceId() || 'default'
const params: any = {}
if (searchForm.value[searchType.value]) {
params[searchType.value] = searchForm.value[searchType.value]
}
AuthorizationApi.getWorkspaceResourceAuthorization(
workspaceId,
targetId.value,
props.type,
paginationConfig,
params,
loading,
).then((res) => {
permissionData.value = res.data.records || []
paginationConfig.total = res.data.total || 0
})
}

const open = (id: string) => {
targetId.value = id
drawerVisible.value = true
getPermissionList()
}
defineExpose({
open,
})
</script>
<style lang="scss" scoped></style>
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.

  1. Template: Ensure that all imported components (ElDrawer, AppTable) are properly defined in your Vue component's imports section.

  2. Script Setup:

    • Use explicit types for props to improve clarity and reduce potential bugs.
    • Add comments for better readability.
    • The function definitions should be consistently named and typed.
  3. Style Scoping: Check if there are any syntax errors or typos in the CSS/scoped styles.

  4. Logic Review:

    • Modal Handling: The modal should only be closed when explicitly called through closeDialog().
    • Permissions Validation: Validate that radioPermission is set before submitting permissions updates.
    • Pagination Logic: Ensure that paginationConfig.current_page is reset correctly when closing the drawer.
  5. Function Calls:

    • Verify that all functions, including those inside asynchronous calls like fetches, are correctly defined and not referencing any out-of-scope variables or methods.

Here’s an improved version with some of these considerations:

<script setup lang="ts">
import { ref, onMounted, watch, computed, reactive } from 'vue';
import { PermissionOptions } from '@/views/system/resource-authorization/constant'; 
import AuthorizationApi from '@/api/system/resource-authorization';
import { MsgSuccess, MsgConfirm } from '@/utils/message';
import { t } from '@/locales';
import useStore from '@/stores';

const { user } = useStore();
interface SearchForm {
  nick_name?: string;
  username?: string;
  publish_status?: number[];
}

defineProps({
  type: String,
});

const drawerVisible = ref<boolean>(false);
const multipleTableRef = ref(null); // Ensure correct ref declaration

onMounted(() => {
  
})

watch(drawerVisible, (bool) => {
  if (!bool) {
    targetId.value = '';
    searchType.value = 'nick_name';
    searchForm.value = {} as SearchForm; // Initialize form with empty object
    permissionData.value = [];
    paginationConfig.current_page = 1;
    paginationConfig.total = 0;
    multipleSelection.value = [];
    clearMultipleSelection(); // Custom method to avoid using reference directly
  }
});

const loading = ref<boolean>(false);
const targetId = ref<string>(''); 
const permissionData = ref<any[]>([]);
const searchType = ref<String>('nick_name');
const searchForm = ref<SearchForm>({});

onMounted(() => {
  updateSearchFields();
});

search_type_change = () => {
  updateSearchFields({ nickname: '' }); // Reset form fields appropriately
}

const paginationConfig = reactive({
  current_page: 1,
  page_size: 20,
  total: 0,       
});
const handleSizeChange = async () => {     
	paginationConfig.current_page = 1
	await getPermissionList();
};

function searchHandle() {      
	paginationConfig.current_page = 1
	getPermissionList();
}

async function getPermissionList() {
	const workspaceId = user.getWorkspaceId() || 'default';
	const params: any = {};
	
	if(searchForm[nickName].length !== 0){
		params['nickname'] = searchForm.nickname;
		
	else if(searchForm.username).length !== 0){                
			params['username'] = searchForm.username;

	else if(searchForm.publishStatus && Array.isArray(searchForm.publishStatus)){            
			params['publish_status'] = [...new Set(searchForm.publishStatus)];           
	}
    
	try{
           const res:any= await AuthorizationApi.getWorkspaceResourceAuthorization(workspaceId,targetId,type,paginationConfig,params,loading);
		   if(res.status === 200 && res.data){
                   permissionData.value = res.data.records || [];   
				   paginationConfig.total = res.data.total || 0;                    
               } else {
				   	console.error('Failed:', result.message);
			    }         
			} catch(error){
			   console.error("Error while fetching records:", error);
			 };
	 }

function open(id:string): void {
	targetId.value = id;                 
	drawerVisible.value = true;
	await getPermissionList();
	clearSearchFields();          
	  }

// Additional helper functions...
const updateSearchFields={ ... };
const clearMultipleSelection ={ ... };
const clearSearchFields={ ... };
</script>

<style scoped>
/* Define your styles here */
</style>

Ensure that you import ElDrawer, AppTable at the top of your script and they match what they're referenced by.

Also, consider creating custom utility functions for clearing selection and input values to maintain consistency across the codebase.

1 change: 1 addition & 0 deletions ui/src/enums/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ export enum SourceTypeEnum {
KNOWLEDGE = 'KNOWLEDGE',
APPLICATION = 'APPLICATION',
TOOL = 'TOOL',
MODEL = 'MODEL',
}
4 changes: 0 additions & 4 deletions ui/src/enums/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ export enum AuthorizationEnum {
VIEW = 'VIEW',
ROLE = 'ROLE',
NOT_AUTH = 'NOT_AUTH',
KNOWLEDGE = 'KNOWLEDGE',
APPLICATION = 'APPLICATION',
MODEL = 'MODEL',
TOOL = 'TOOL',
}

export enum RoleTypeEnum {
Expand Down
Loading
Loading