feat(系统设置): 系统用户组管理接口对接

This commit is contained in:
RubyLiu 2023-07-07 18:53:47 +08:00 committed by 刘瑞斌
parent cc2a6b9140
commit 3a9ef7bef1
13 changed files with 542 additions and 186 deletions

View File

@ -37,7 +37,7 @@
"@7polo/kity": "2.0.8", "@7polo/kity": "2.0.8",
"@7polo/kityminder-core": "1.4.53", "@7polo/kityminder-core": "1.4.53",
"@arco-design/web-vue": "^2.47.0", "@arco-design/web-vue": "^2.47.0",
"@arco-themes/vue-ms-theme-default": "^0.0.14", "@arco-themes/vue-ms-theme-default": "^0.0.15",
"@form-create/arco-design": "^3.1.21", "@form-create/arco-design": "^3.1.21",
"@vueuse/core": "^9.13.0", "@vueuse/core": "^9.13.0",
"ace-builds": "^1.22.0", "ace-builds": "^1.22.0",

View File

@ -1,7 +1,16 @@
import MSR from '@/api/http/index'; import MSR from '@/api/http/index';
import { updateUserGroupU, getUserGroupU, addUserGroupU, deleteUserGroupU } from '@/api/requrls/system/usergroup'; import {
// import { QueryParams, CommonList } from '@/models/common'; updateUserGroupU,
import { UserGroupItem } from '@/models/system/usergroup'; getUserGroupU,
addUserGroupU,
deleteUserGroupU,
getGlobalUSettingUrl,
editGlobalUSettingUrl,
postUserByUserGroupUrl,
deleteUserFromUserGroupUrl,
} from '@/api/requrls/system/usergroup';
import { TableQueryParams, CommonList } from '@/models/common';
import { UserGroupItem, UserGroupAuthSeting, SaveGlobalUSettingData, UserTableItem } from '@/models/system/usergroup';
export function updateOrAddUserGroup(data: Partial<UserGroupItem>) { export function updateOrAddUserGroup(data: Partial<UserGroupItem>) {
return MSR.post<UserGroupItem>({ return MSR.post<UserGroupItem>({
@ -28,3 +37,19 @@ export function deleteUserGroup(id: string) {
export function getUsergroupInfo(id: string) { export function getUsergroupInfo(id: string) {
return MSR.get<UserGroupItem>({ url: `${getUserGroupU}${id}` }); return MSR.get<UserGroupItem>({ url: `${getUserGroupU}${id}` });
} }
export function getGlobalUSetting(id: string) {
return MSR.get<UserGroupAuthSeting[]>({ url: `${getGlobalUSettingUrl}${id}` });
}
export function saveGlobalUSetting(data: SaveGlobalUSettingData) {
return MSR.post<UserGroupAuthSeting[]>({ url: editGlobalUSettingUrl, data });
}
export function postUserByUserGroup(data: TableQueryParams) {
return MSR.post<CommonList<UserTableItem[]>>({ url: postUserByUserGroupUrl, data });
}
export function deleteUserFromUserGroup(id: string) {
return MSR.get<string>({ url: `${deleteUserFromUserGroupUrl}${id}` });
}

View File

@ -1,11 +1,11 @@
/** 修改用户组 */ /** 修改用户组 */
export const updateUserGroupU = `/user/role/global/update`; export const updateUserGroupU = `/user/role/global/update`;
/** 编辑用户组对应的权限配置 */ /** 编辑用户组对应的权限配置 */
export const editGlobalUSetting = `/user/role/global/permission/update`; export const editGlobalUSettingUrl = `/user/role/global/permission/update`;
/** 添加用户组 */ /** 添加用户组 */
export const addUserGroupU = `/user/role/global/add`; export const addUserGroupU = `/user/role/global/add`;
/** 获取用户组对应的权限配置 */ /** 获取用户组对应的权限配置 */
export const getGlobalUSetting = `/user/role/global/permission/list`; export const getGlobalUSettingUrl = `/user/role/global/permission/setting/`;
/** 获取用户组 */ /** 获取用户组 */
export const getUserGroupU = `/user/role/global/list`; export const getUserGroupU = `/user/role/global/list`;
/** 获取单个用户组信息 */ /** 获取单个用户组信息 */
@ -14,8 +14,8 @@ export const getUsergroupInfoU = `/user/role/global/get/`;
export const deleteUserGroupU = `/user/role/global/delete/`; export const deleteUserGroupU = `/user/role/global/delete/`;
/** 根据用户组获取用户列表 */ /** 根据用户组获取用户列表 */
export const getUserByUserGroupU = `/user/role/relation/global/list/`; export const postUserByUserGroupUrl = `/user/role/relation/global/list`;
/** 创建用户组添加用户 */ /** 创建用户组添加用户 */
export const addUserToUserGroupU = `/user/role/relation/global/add/`; export const addUserToUserGroupU = `/user/role/relation/global/add/`;
/** 删除用户组用户 */ /** 删除用户组用户 */
export const deleteUserFromUserGroupU = `/user/role/relation/global/delete/`; export const deleteUserFromUserGroupUrl = `/user/role/relation/global/delete/`;

View File

@ -121,7 +121,7 @@ export default function useTableProps(
// 设置请求参数,如果出了分页参数还有搜索参数,在模板页面调用此方法,可以加入参数 // 设置请求参数,如果出了分页参数还有搜索参数,在模板页面调用此方法,可以加入参数
const loadListParams = ref<object>({}); const loadListParams = ref<object>({});
const setLoadPaListrams = (params?: object) => { const setLoadListParams = (params?: object) => {
loadListParams.value = params || {}; loadListParams.value = params || {};
}; };
@ -140,6 +140,7 @@ export default function useTableProps(
sort: sortItem.value, sort: sortItem.value,
filter: filterItem.value, filter: filterItem.value,
keyword: keyword.value, keyword: keyword.value,
...loadListParams.value,
}); });
const tmpArr = data.list as unknown as MsTableData; const tmpArr = data.list as unknown as MsTableData;
propsRes.value.data = tmpArr.map((item: TableData) => { propsRes.value.data = tmpArr.map((item: TableData) => {
@ -155,8 +156,8 @@ export default function useTableProps(
return data; return data;
} catch (err) { } catch (err) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(err);
// TODO 表格异常放到solt的empty // TODO 表格异常放到solt的empty
console.log(err);
} finally { } finally {
setLoading(false); setLoading(false);
} }
@ -213,7 +214,7 @@ export default function useTableProps(
setLoading, setLoading,
loadList, loadList,
setPagination, setPagination,
setLoadPaListrams, setLoadListParams,
setKeyword, setKeyword,
}; };
} }

View File

@ -39,3 +39,63 @@ export interface UserGroupItem {
// 自定义排序 // 自定义排序
pos: number; pos: number;
} }
export interface UserGroupPermissionItem {
id: string;
name: string;
enable: boolean;
license: boolean;
}
export type AuthScopeType = 'SYSTEM' | 'PROJECT' | 'ORGANIZATION';
// 用户组对应的权限配置
export interface UserGroupAuthSeting {
// 菜单项ID
id: AuthScopeType;
// 菜单所属类型
type?: string;
// 菜单项名称
name: string;
// 是否企业版
license: boolean;
// 是否全选
enable: boolean;
// 菜单下的权限列表
permissions?: UserGroupPermissionItem[];
// 子菜单
children?: UserGroupAuthSeting[];
}
// 权限表格DataItem
export interface AuthTableItem {
id: string;
name?: string;
enable: boolean;
license: boolean;
ability?: string | undefined;
permissions?: UserGroupPermissionItem[];
// 对应表格权限的复选框组的绑定值
perChecked?: string[];
operationObject?: string;
isSystem?: boolean;
isOrganization?: boolean;
isProject?: boolean;
indeterminate?: boolean;
}
export interface SavePermissions {
id: string;
enable: boolean;
}
export interface SaveGlobalUSettingData {
userRoleId: string;
permissions: SavePermissions[];
}
export interface UserTableItem {
id: string;
userId: string;
name: string;
email: string;
phone: string;
}

View File

@ -9,6 +9,8 @@ import type { LoginData } from '@/models/user';
import type { UserState } from './types'; import type { UserState } from './types';
const useUserStore = defineStore('user', { const useUserStore = defineStore('user', {
// 开启数据持久化
persist: true,
state: (): UserState => ({ state: (): UserState => ({
name: undefined, name: undefined,
avatar: undefined, avatar: undefined,

View File

@ -1,16 +1,62 @@
<template> <template>
<div class="relative">
<a-table <a-table
:span-method="dataSpanMethod"
:scroll="{ y: '860px', x: '1440px' }" :scroll="{ y: '860px', x: '1440px' }"
:columns="columns" :data="tableData"
:data="data" :loading="loading"
:bordered="{ wrapper: true, cell: true }" :bordered="{ wrapper: true, cell: true }"
size="small" size="small"
:pagination="false"
>
<template #columns>
<a-table-column :width="100" :title="t('system.userGroup.function')" data-index="ability" />
<a-table-column :width="150" :title="t('system.userGroup.operationObject')" data-index="operationObject" />
<a-table-column :title="t('system.userGroup.auth')">
<template #cell="{ record, rowIndex }">
<a-checkbox-group v-model="record.perChecked" @change="(v) => handleAuthChange(v, rowIndex)">
<a-checkbox v-for="item in record.permissions" :key="item.id" :disabled="item.license" :value="item.id">{{
t(item.name)
}}</a-checkbox>
</a-checkbox-group>
</template>
</a-table-column>
<a-table-column :width="50" fixed="right" align="center" :bordered="false">
<template #title>
<a-checkbox
v-if="tableData && tableData?.length > 0"
:model-value="allChecked"
:indeterminate="allIndeterminate"
@change="handleAllChangeByCheckbox"
></a-checkbox>
</template>
<template #cell="{ record, rowIndex }">
<a-checkbox
:model-value="record.enable"
:indeterminate="record.indeterminate"
@change="(value) => handleActionChangeAll(value, rowIndex)"
/> />
</template>
</a-table-column>
</template>
</a-table>
<div class="action">
<ms-button class="btn" @click="handleReset">{{ t('system.userGroup.reset') }}</ms-button>
<a-button class="btn" :disabled="!canSave" type="primary" @click="handleSave">{{
t('system.userGroup.save')
}}</a-button>
</div>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { TableData } from '@arco-design/web-vue'; import { useI18n } from '@/hooks/useI18n';
import { RenderFunction, VNodeChild } from 'vue'; import { RenderFunction, VNodeChild, ref, watchEffect } from 'vue';
import { type TableColumnData, type TableData } from '@arco-design/web-vue';
import useUserGroupStore from '@/store/modules/system/usergroup';
import { getGlobalUSetting, saveGlobalUSetting } from '@/api/modules/system/usergroup';
import { UserGroupAuthSeting, AuthTableItem, type AuthScopeType, SavePermissions } from '@/models/system/usergroup';
import MsButton from '@/components/pure/ms-button/index.vue';
export declare type OperationName = 'selection-checkbox' | 'selection-radio' | 'expand' | 'drag-handle'; export declare type OperationName = 'selection-checkbox' | 'selection-radio' | 'expand' | 'drag-handle';
@ -23,59 +69,229 @@
isLastLeftFixed?: boolean; isLastLeftFixed?: boolean;
} }
const columns = [ const loading = ref(false);
{ const store = useUserGroupStore();
title: 'Name',
dataIndex: 'name', const systemSpan = ref(1);
}, const projectSpan = ref(1);
{ const organizationSpan = ref(1);
title: 'Salary', //
dataIndex: 'salary', const allChecked = ref(false);
}, const allIndeterminate = ref(false);
{
title: 'Address', const tableData = ref<AuthTableItem[]>();
dataIndex: 'address', //
}, const canSave = ref(false);
{
title: 'Email', const dataSpanMethod = (data: {
dataIndex: 'email', record: TableData;
}, column: TableColumnData | TableOperationColumn;
]; rowIndex: number;
const data = [ columnIndex: number;
{ }) => {
key: '1', const { record, column } = data;
name: 'Jane Doe', if ((column as TableColumnData).dataIndex === 'ability') {
salary: 23000, if (record.isSystem) {
address: '32 Park Road, London', return {
email: 'jane.doe@example.com', rowspan: 2,
}, };
{ }
key: '2', if (record.isOrganization) {
name: 'Alisa Ross', return {
salary: 25000, rowspan: organizationSpan.value,
address: '35 Park Road, London', };
email: 'alisa.ross@example.com', }
}, if (record.isProject) {
{ return {
key: '3', rowspan: projectSpan.value,
name: 'Kevin Sandra', };
salary: 22000, }
address: '31 Park Road, London', }
email: 'kevin.sandra@example.com', };
},
{ const { t } = useI18n();
key: '4',
name: 'Ed Hellen', /**
salary: 17000, * 生成数据
address: '42 Park Road, London', * @param type
email: 'ed.hellen@example.com', * @param idx
}, */
{ const makeData = (item: UserGroupAuthSeting, type: AuthScopeType) => {
key: '5', const result: AuthTableItem[] = [];
name: 'William Smith', item.children?.forEach((child, index) => {
salary: 27000, const perChecked =
address: '62 Park Road, London', child?.permissions?.reduce((acc: string[], cur) => {
email: 'william.smith@example.com', if (cur.enable) {
}, acc.push(cur.id);
]; }
return acc;
}, []) || [];
result.push({
id: child?.id,
license: child?.license,
enable: child?.enable,
permissions: child?.permissions,
indeterminate: perChecked?.length > 0,
perChecked,
ability: index === 0 ? t(`system.userGroup.${type}`) : undefined,
operationObject: t(child.name),
isSystem: index === 0 && type === 'SYSTEM',
isOrganization: index === 0 && type === 'ORGANIZATION',
isProject: index === 0 && type === 'PROJECT',
});
});
return result;
};
const transformData = (data: UserGroupAuthSeting[]) => {
const result: AuthTableItem[] = [];
data.forEach((item) => {
if (item.type === 'SYSTEM') {
systemSpan.value = item.children?.length || 0;
}
if (item.type === 'PROJECT') {
projectSpan.value = item.children?.length || 0;
}
if (item.type === 'ORGANIZATION') {
organizationSpan.value = item.children?.length || 0;
}
result.push(...makeData(item, item.id));
});
return result;
};
const initData = async (id: string) => {
try {
let tmpArr = [];
loading.value = true;
const res = await getGlobalUSetting(id);
tmpArr = transformData(res);
tableData.value = tmpArr;
} catch (error) {
tableData.value = [];
} finally {
loading.value = false;
}
};
// change
const handleAllChangeByCheckbox = () => {
if (!tableData.value) return;
allChecked.value = !allChecked.value;
allIndeterminate.value = false;
const tmpArr = tableData.value;
tmpArr.forEach((item) => {
item.enable = allChecked.value;
item.indeterminate = false;
item.perChecked = allChecked.value ? item.permissions?.map((ele) => ele.id) : [];
});
};
//
const handleAllChange = () => {
if (!tableData.value) return;
const tmpArr = tableData.value;
const { length: allLength } = tmpArr;
const { length } = tmpArr.filter((item) => item.enable);
if (length === allLength) {
allChecked.value = true;
allIndeterminate.value = false;
} else if (length === 0) {
allChecked.value = false;
allIndeterminate.value = false;
} else {
allChecked.value = false;
allIndeterminate.value = true;
}
if (!canSave.value) canSave.value = true;
};
// change
const handleActionChangeAll = (value: boolean | (string | number | boolean)[], rowIndex: number) => {
if (!tableData.value) return;
const tmpArr = tableData.value;
tmpArr[rowIndex].indeterminate = false;
if (value) {
tmpArr[rowIndex].enable = true;
tmpArr[rowIndex].perChecked = tmpArr[rowIndex].permissions?.map((item) => item.id);
} else {
tmpArr[rowIndex].enable = false;
tmpArr[rowIndex].perChecked = [];
}
tableData.value = [...tmpArr];
handleAllChange();
if (!canSave.value) canSave.value = true;
};
// change
const handleAuthChange = (values: (string | number | boolean)[], rowIndex: number) => {
if (!tableData.value) return;
const tmpArr = tableData.value;
const length = tmpArr[rowIndex].permissions?.length || 0;
if (values.length === length) {
tmpArr[rowIndex].enable = true;
tmpArr[rowIndex].indeterminate = false;
handleAllChange();
} else if (values.length === 0) {
tmpArr[rowIndex].enable = false;
tmpArr[rowIndex].indeterminate = false;
handleAllChange();
} else {
tmpArr[rowIndex].enable = false;
tmpArr[rowIndex].indeterminate = true;
}
if (!canSave.value) canSave.value = true;
};
//
const handleSave = async () => {
if (!tableData.value) return;
const permissions: SavePermissions[] = [];
const tmpArr = tableData.value;
tmpArr.forEach((item) => {
item.permissions?.forEach((ele) => {
ele.enable = item.perChecked?.includes(ele.id) || false;
permissions.push({
id: ele.id,
enable: ele.enable,
});
});
});
try {
await saveGlobalUSetting({
userRoleId: store.currentId,
permissions,
});
initData(store.currentId);
} catch (error) {
// eslint-disable-next-line no-console
console.log('error', error);
}
};
//
const handleReset = () => {
if (store.currentId) {
initData(store.currentId);
}
};
watchEffect(() => {
if (store.currentId) {
initData(store.currentId);
}
});
</script> </script>
<style scoped lang="less">
.action {
position: absolute;
right: 24px;
bottom: 0;
left: 24px;
display: flex;
justify-content: space-between;
align-items: center;
width: calc(100% - 24px);
}
</style>

View File

@ -4,7 +4,8 @@
v-model="searchKey" v-model="searchKey"
class="w-[252px]" class="w-[252px]"
:placeholder="t('system.userGroup.searchHolder')" :placeholder="t('system.userGroup.searchHolder')"
@press-enter="searchData" @press-enter="enterData"
@search="searchData"
/> />
<div class="mt-2 flex flex-col"> <div class="mt-2 flex flex-col">
<div class="flex h-[38px] items-center justify-between px-[8px] leading-[24px]"> <div class="flex h-[38px] items-center justify-between px-[8px] leading-[24px]">
@ -109,6 +110,33 @@
eventTag: 'delete', eventTag: 'delete',
}, },
]; ];
//
const handleListItemClick = (element: UserGroupItem) => {
const { id, name, type } = element;
currentId.value = id;
store.setInfo({ currentName: name, currentTitle: type, currentId: id });
};
//
const initData = async () => {
try {
const res = await getUserGroupList();
if (res.length > 0) {
userGroupList.value = res;
handleListItemClick(res[0]);
//
const tmpObj: PopVisibleItem = {};
res.forEach((element) => {
tmpObj[element.id] = false;
});
popVisible.value = tmpObj;
}
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
}
};
// //
const addUserGroup = () => { const addUserGroup = () => {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -139,6 +167,7 @@
try { try {
await deleteUserGroup(id); await deleteUserGroup(id);
Message.success(t('system.user.deleteUserSuccess')); Message.success(t('system.user.deleteUserSuccess'));
initData();
return true; return true;
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -151,39 +180,12 @@
} }
}; };
//
const handleListItemClick = (element: UserGroupItem) => {
const { id, name, type } = element;
currentId.value = id;
store.setInfo({ currentName: name, currentTitle: type });
};
//
const initData = async () => {
try {
const res = await getUserGroupList();
if (res.length > 0) {
userGroupList.value = res;
handleListItemClick(res[0]);
//
const tmpObj: PopVisibleItem = {};
res.forEach((element) => {
tmpObj[element.id] = false;
});
popVisible.value = tmpObj;
}
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
}
};
// confirm // confirm
const handlePopConfirmCancel = (id: string) => { const handlePopConfirmCancel = (id: string) => {
popVisible.value = { ...popVisible.value, [id]: false }; popVisible.value = { ...popVisible.value, [id]: false };
}; };
// //
const handlePopConfirmSubmit = async (item: CustomMoreActionItem, id: string) => { const handlePopConfirmSubmit = async (item: CustomMoreActionItem, id: string) => {
popVisible.value = { ...popVisible.value, [id]: false };
if (item.eventKey === 'rename') { if (item.eventKey === 'rename') {
// //
try { try {
@ -207,10 +209,11 @@
console.error(error); console.error(error);
} }
} }
popVisible.value = { ...popVisible.value, [id]: false };
initData(); initData();
}; };
function searchData(eve: Event) { function enterData(eve: Event) {
if (!(eve.target as HTMLInputElement).value) { if (!(eve.target as HTMLInputElement).value) {
initData(); initData();
return; return;
@ -219,6 +222,15 @@
const tmpArr = userGroupList.value.filter((ele) => ele.name.includes(keyword)); const tmpArr = userGroupList.value.filter((ele) => ele.name.includes(keyword));
userGroupList.value = tmpArr; userGroupList.value = tmpArr;
} }
function searchData(value: string) {
if (!value) {
initData();
return;
}
const keyword = value;
const tmpArr = userGroupList.value.filter((ele) => ele.name.includes(keyword));
userGroupList.value = tmpArr;
}
// //
const handleAddUserGroup = async (value: Partial<UserGroupItem>) => { const handleAddUserGroup = async (value: Partial<UserGroupItem>) => {
try { try {
@ -226,6 +238,7 @@
const res = await updateOrAddUserGroup(value); const res = await updateOrAddUserGroup(value);
if (res) { if (res) {
Message.success(t('system.userGroup.addUserGroupSuccess')); Message.success(t('system.userGroup.addUserGroupSuccess'));
addUserGroupVisible.value = false;
initData(); initData();
} }
} catch (error) { } catch (error) {

View File

@ -3,7 +3,7 @@
:popup-visible="renameVisible" :popup-visible="renameVisible"
:ok-text="t('system.userGroup.confirm')" :ok-text="t('system.userGroup.confirm')"
:cancel-text="t('system.userGroup.cancel')" :cancel-text="t('system.userGroup.cancel')"
@ok="handleSubmit" @before-ok="handleSubmit"
@cancel="handleCancel" @cancel="handleCancel"
@popup-visible-change="() => (form.name = '')" @popup-visible-change="() => (form.name = '')"
> >
@ -29,13 +29,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { watchEffect, reactive, ref, computed, onUnmounted } from 'vue'; import { watchEffect, ref, computed, onUnmounted } from 'vue';
import { CustomMoreActionItem, RenameType, UserGroupItem } from '@/models/system/usergroup'; import { CustomMoreActionItem, RenameType, UserGroupItem } from '@/models/system/usergroup';
import { ValidatedError } from '@arco-design/web-vue'; import { ValidatedError } from '@arco-design/web-vue';
const { t } = useI18n(); const { t } = useI18n();
const formRef = ref(); const formRef = ref();
const form = reactive({ const form = ref({
name: '', name: '',
}); });
@ -46,9 +46,9 @@
list: UserGroupItem[]; list: UserGroupItem[];
}>(); }>();
const validateName = (value: string, callback: (error?: string) => void) => { const validateName = (value: string | undefined, callback: (error?: string) => void) => {
if (props.type === 'rename') { if (props.type === 'rename') {
if (value === '') { if (value === undefined || value === '') {
callback(t('system.userGroup.userGroupNameIsNotNone')); callback(t('system.userGroup.userGroupNameIsNotNone'));
} else { } else {
if (value === props.defaultName) { if (value === props.defaultName) {
@ -88,20 +88,22 @@
const renameVisible = ref(props.visible); const renameVisible = ref(props.visible);
const handleSubmit = () => { const handleSubmit = async () => {
formRef.value.validate((errors: undefined | Record<string, ValidatedError>) => { await formRef.value.validate(async (errors: undefined | Record<string, ValidatedError>) => {
if (!errors) { if (!errors) {
emit('submit', { eventKey: props.type, name: form.name }); emit('submit', { eventKey: props.type, name: form.value.name });
return true;
} }
}); });
return false;
}; };
const handleCancel = () => { const handleCancel = () => {
form.name = ''; form.value.name = '';
emit('cancel'); emit('cancel');
}; };
watchEffect(() => { watchEffect(() => {
renameVisible.value = props.visible; renameVisible.value = props.visible;
form.name = props.defaultName; form.value.name = props.defaultName;
}); });
onUnmounted(() => { onUnmounted(() => {

View File

@ -1,94 +1,45 @@
<template> <template>
<MsBaseTable v-bind="propsRes" v-on="propsEvent"> </MsBaseTable> <MsBaseTable v-bind="propsRes" v-on="propsEvent">
<template #action="{ record }">
<ms-button type="link" @click="handleRemove(record)">{{ t('system.userGroup.remove') }}</ms-button>
</template>
</MsBaseTable>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { getTableList } from '@/api/modules/api-test/index'; import { useI18n } from '@/hooks/useI18n';
import useTable from '@/components/pure/ms-table/useTable'; import useTable from '@/components/pure/ms-table/useTable';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue'; import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import { MsTableColumn } from '@/components/pure/ms-table/type'; import { MsTableColumn } from '@/components/pure/ms-table/type';
import { onMounted } from 'vue'; import useUserGroupStore from '@/store/modules/system/usergroup';
import { watchEffect } from 'vue';
import { postUserByUserGroup, deleteUserFromUserGroup } from '@/api/modules/system/usergroup';
import { UserTableItem } from '@/models/system/usergroup';
const { t } = useI18n();
const store = useUserGroupStore();
const columns: MsTableColumn = [ const columns: MsTableColumn = [
{ {
title: 'ID', title: 'system.userGroup.name',
dataIndex: 'num',
filterable: {
filters: [
{
text: '> 20000',
value: '20000',
},
{
text: '> 30000',
value: '30000',
},
],
filter: (value, record) => record.salary > value,
multiple: true,
},
},
{
title: '接口名称',
dataIndex: 'name', dataIndex: 'name',
width: 200,
}, },
{ {
title: '请求类型', title: 'system.userGroup.email',
dataIndex: 'method', dataIndex: 'email',
}, },
{ {
title: '责任人', title: 'system.userGroup.phone',
dataIndex: 'username', dataIndex: 'email',
}, },
{ {
title: '路径', title: 'system.userGroup.operation',
dataIndex: 'path',
},
{
title: '标签',
dataIndex: 'tags',
},
{
title: '更新时间',
dataIndex: 'updateTime',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: '用例数',
dataIndex: 'caseTotal',
},
{
title: '用例状态',
dataIndex: 'caseStatus',
},
{
title: '用例通过率',
dataIndex: 'casePassingRate',
},
{
title: '接口状态',
dataIndex: 'status',
},
{
title: '创建时间',
slotName: 'createTime',
width: 200,
},
{
title: '描述',
dataIndex: 'description',
},
{
title: '操作',
slotName: 'action', slotName: 'action',
fixed: 'right', fixed: 'right',
width: 200, width: 200,
}, },
]; ];
const { propsRes, propsEvent, loadList } = useTable(getTableList, { const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(postUserByUserGroup, {
columns, columns,
scroll: { y: 750, x: 2000 }, scroll: { y: 750, x: 2000 },
selectable: true, selectable: true,
@ -96,7 +47,14 @@
const fetchData = async () => { const fetchData = async () => {
await loadList(); await loadList();
}; };
onMounted(() => { const handleRemove = async (record: UserTableItem) => {
await deleteUserFromUserGroup(record.id);
await fetchData();
};
watchEffect(() => {
if (store.currentId) {
setLoadListParams({ roleId: store.currentId });
fetchData(); fetchData();
}
}); });
</script> </script>

View File

@ -1,7 +1,11 @@
<template> <template>
<div class="user-group flex flex-row bg-white"> <div class="user-group flex flex-row bg-white">
<div class="user-group-left"> <div class="user-group-left">
<UserGroupLeft /> <user-group-left v-if="collapse" />
<div class="usergroup-collapse">
<icon-double-left v-if="collapse" class="icon" @click="collapse = false" />
<icon-double-right v-else class="icon" @click="collapse = true" />
</div>
</div> </div>
<div class="grow-1 w-[100%] overflow-x-scroll p-[24px]"> <div class="grow-1 w-[100%] overflow-x-scroll p-[24px]">
<div class="grow-1 flex flex-row items-center justify-between"> <div class="grow-1 flex flex-row items-center justify-between">
@ -35,6 +39,7 @@
import AuthTable from './components/authTable.vue'; import AuthTable from './components/authTable.vue';
const currentTable = ref('auth'); const currentTable = ref('auth');
const collapse = ref(true);
const { t } = useI18n(); const { t } = useI18n();
@ -52,5 +57,21 @@
position: relative; position: relative;
padding: 24px; padding: 24px;
border-right: 1px solid var(--color-border); border-right: 1px solid var(--color-border);
.usergroup-collapse {
position: absolute;
top: 50%;
right: -16px;
display: flex;
justify-content: center;
align-items: center;
width: 16px;
height: 36px;
background-color: var(--color-text-n8);
cursor: pointer;
.icon {
font-size: 12px;
color: var(--color-text-brand);
}
}
} }
</style> </style>

View File

@ -1,6 +1,7 @@
export default { export default {
system: { system: {
userGroup: { userGroup: {
addUserGroupSuccess: 'Add user group success',
searchHolder: 'Please input user group name', searchHolder: 'Please input user group name',
inSystem: 'In system', inSystem: 'In system',
customUserGroup: 'Custom user group', customUserGroup: 'Custom user group',
@ -40,6 +41,34 @@ export default {
beforeDeleteUserGroup: beforeDeleteUserGroup:
'After deletion, the project data under the organization will be deleted together. Please operate with caution!', 'After deletion, the project data under the organization will be deleted together. Please operate with caution!',
confirmDelete: 'Confirm delete', confirmDelete: 'Confirm delete',
function: 'Function',
operationObject: 'Operation object',
system: 'System',
project: 'Project',
organization: 'Organization',
save: 'Save',
reset: 'Restore default',
name: 'Name',
email: 'Email',
operation: 'Operation',
phone: 'Phone',
remove: 'Remove',
},
},
permission: {
system_user_role: {
name: 'Name',
read: 'Read',
add: 'Add',
update: 'Update',
delete: 'Delete',
},
system_test_resource_pool: {
name: 'Name',
read: 'Read',
add: 'Add',
update: 'Update',
delete: 'Delete',
}, },
}, },
}; };

View File

@ -1,6 +1,7 @@
export default { export default {
system: { system: {
userGroup: { userGroup: {
addUserGroupSuccess: '添加用户组成功',
global: '全局用户组', global: '全局用户组',
searchHolder: '请输入用户组名称', searchHolder: '请输入用户组名称',
inSystem: '系统内置', inSystem: '系统内置',
@ -39,6 +40,34 @@ export default {
isDeleteUserGroup: '是否删除: {name}?', isDeleteUserGroup: '是否删除: {name}?',
beforeDeleteUserGroup: '删除后,该组织下的项目数据将一起删除,请谨慎操作!', beforeDeleteUserGroup: '删除后,该组织下的项目数据将一起删除,请谨慎操作!',
confirmDelete: '确认删除', confirmDelete: '确认删除',
function: '功能',
operationObject: '操作对象',
system: '系统',
project: '项目',
organization: '组织',
save: '保存',
reset: '恢复默认',
name: '姓名',
email: '邮箱',
operation: '操作',
phone: '手机',
remove: '移除',
},
},
permission: {
system_user_role: {
name: '系统用户',
read: '读取',
add: '添加',
update: '更新',
delete: '删除',
},
system_test_resource_pool: {
name: '系统测试资源池',
read: '读取',
add: '添加',
update: '更新',
delete: '删除',
}, },
}, },
}; };