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/kityminder-core": "1.4.53",
"@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",
"@vueuse/core": "^9.13.0",
"ace-builds": "^1.22.0",

View File

@ -1,7 +1,16 @@
import MSR from '@/api/http/index';
import { updateUserGroupU, getUserGroupU, addUserGroupU, deleteUserGroupU } from '@/api/requrls/system/usergroup';
// import { QueryParams, CommonList } from '@/models/common';
import { UserGroupItem } from '@/models/system/usergroup';
import {
updateUserGroupU,
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>) {
return MSR.post<UserGroupItem>({
@ -28,3 +37,19 @@ export function deleteUserGroup(id: string) {
export function getUsergroupInfo(id: string) {
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 editGlobalUSetting = `/user/role/global/permission/update`;
export const editGlobalUSettingUrl = `/user/role/global/permission/update`;
/** 添加用户组 */
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`;
/** 获取单个用户组信息 */
@ -14,8 +14,8 @@ export const getUsergroupInfoU = `/user/role/global/get/`;
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 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 setLoadPaListrams = (params?: object) => {
const setLoadListParams = (params?: object) => {
loadListParams.value = params || {};
};
@ -140,6 +140,7 @@ export default function useTableProps(
sort: sortItem.value,
filter: filterItem.value,
keyword: keyword.value,
...loadListParams.value,
});
const tmpArr = data.list as unknown as MsTableData;
propsRes.value.data = tmpArr.map((item: TableData) => {
@ -155,8 +156,8 @@ export default function useTableProps(
return data;
} catch (err) {
// eslint-disable-next-line no-console
console.log(err);
// TODO 表格异常放到solt的empty
console.log(err);
} finally {
setLoading(false);
}
@ -213,7 +214,7 @@ export default function useTableProps(
setLoading,
loadList,
setPagination,
setLoadPaListrams,
setLoadListParams,
setKeyword,
};
}

View File

@ -39,3 +39,63 @@ export interface UserGroupItem {
// 自定义排序
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';
const useUserStore = defineStore('user', {
// 开启数据持久化
persist: true,
state: (): UserState => ({
name: undefined,
avatar: undefined,

View File

@ -1,16 +1,62 @@
<template>
<a-table
:scroll="{ y: '860px', x: '1440px' }"
:columns="columns"
:data="data"
:bordered="{ wrapper: true, cell: true }"
size="small"
/>
<div class="relative">
<a-table
:span-method="dataSpanMethod"
:scroll="{ y: '860px', x: '1440px' }"
:data="tableData"
:loading="loading"
:bordered="{ wrapper: true, cell: true }"
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>
<script setup lang="ts">
import { TableData } from '@arco-design/web-vue';
import { RenderFunction, VNodeChild } from 'vue';
import { useI18n } from '@/hooks/useI18n';
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';
@ -23,59 +69,229 @@
isLastLeftFixed?: boolean;
}
const columns = [
{
title: 'Name',
dataIndex: 'name',
},
{
title: 'Salary',
dataIndex: 'salary',
},
{
title: 'Address',
dataIndex: 'address',
},
{
title: 'Email',
dataIndex: 'email',
},
];
const data = [
{
key: '1',
name: 'Jane Doe',
salary: 23000,
address: '32 Park Road, London',
email: 'jane.doe@example.com',
},
{
key: '2',
name: 'Alisa Ross',
salary: 25000,
address: '35 Park Road, London',
email: 'alisa.ross@example.com',
},
{
key: '3',
name: 'Kevin Sandra',
salary: 22000,
address: '31 Park Road, London',
email: 'kevin.sandra@example.com',
},
{
key: '4',
name: 'Ed Hellen',
salary: 17000,
address: '42 Park Road, London',
email: 'ed.hellen@example.com',
},
{
key: '5',
name: 'William Smith',
salary: 27000,
address: '62 Park Road, London',
email: 'william.smith@example.com',
},
];
const loading = ref(false);
const store = useUserGroupStore();
const systemSpan = ref(1);
const projectSpan = ref(1);
const organizationSpan = ref(1);
//
const allChecked = ref(false);
const allIndeterminate = ref(false);
const tableData = ref<AuthTableItem[]>();
//
const canSave = ref(false);
const dataSpanMethod = (data: {
record: TableData;
column: TableColumnData | TableOperationColumn;
rowIndex: number;
columnIndex: number;
}) => {
const { record, column } = data;
if ((column as TableColumnData).dataIndex === 'ability') {
if (record.isSystem) {
return {
rowspan: 2,
};
}
if (record.isOrganization) {
return {
rowspan: organizationSpan.value,
};
}
if (record.isProject) {
return {
rowspan: projectSpan.value,
};
}
}
};
const { t } = useI18n();
/**
* 生成数据
* @param type
* @param idx
*/
const makeData = (item: UserGroupAuthSeting, type: AuthScopeType) => {
const result: AuthTableItem[] = [];
item.children?.forEach((child, index) => {
const perChecked =
child?.permissions?.reduce((acc: string[], cur) => {
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>
<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"
class="w-[252px]"
:placeholder="t('system.userGroup.searchHolder')"
@press-enter="searchData"
@press-enter="enterData"
@search="searchData"
/>
<div class="mt-2 flex flex-col">
<div class="flex h-[38px] items-center justify-between px-[8px] leading-[24px]">
@ -109,6 +110,33 @@
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 = () => {
// eslint-disable-next-line no-console
@ -139,6 +167,7 @@
try {
await deleteUserGroup(id);
Message.success(t('system.user.deleteUserSuccess'));
initData();
return true;
} catch (error) {
// 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
const handlePopConfirmCancel = (id: string) => {
popVisible.value = { ...popVisible.value, [id]: false };
};
//
const handlePopConfirmSubmit = async (item: CustomMoreActionItem, id: string) => {
popVisible.value = { ...popVisible.value, [id]: false };
if (item.eventKey === 'rename') {
//
try {
@ -207,10 +209,11 @@
console.error(error);
}
}
popVisible.value = { ...popVisible.value, [id]: false };
initData();
};
function searchData(eve: Event) {
function enterData(eve: Event) {
if (!(eve.target as HTMLInputElement).value) {
initData();
return;
@ -219,6 +222,15 @@
const tmpArr = userGroupList.value.filter((ele) => ele.name.includes(keyword));
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>) => {
try {
@ -226,6 +238,7 @@
const res = await updateOrAddUserGroup(value);
if (res) {
Message.success(t('system.userGroup.addUserGroupSuccess'));
addUserGroupVisible.value = false;
initData();
}
} catch (error) {

View File

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

View File

@ -1,94 +1,45 @@
<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>
<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 MsBaseTable from '@/components/pure/ms-table/base-table.vue';
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 = [
{
title: 'ID',
dataIndex: 'num',
filterable: {
filters: [
{
text: '> 20000',
value: '20000',
},
{
text: '> 30000',
value: '30000',
},
],
filter: (value, record) => record.salary > value,
multiple: true,
},
},
{
title: '接口名称',
title: 'system.userGroup.name',
dataIndex: 'name',
width: 200,
},
{
title: '请求类型',
dataIndex: 'method',
title: 'system.userGroup.email',
dataIndex: 'email',
},
{
title: '责任人',
dataIndex: 'username',
title: 'system.userGroup.phone',
dataIndex: 'email',
},
{
title: '路径',
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: '操作',
title: 'system.userGroup.operation',
slotName: 'action',
fixed: 'right',
width: 200,
},
];
const { propsRes, propsEvent, loadList } = useTable(getTableList, {
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(postUserByUserGroup, {
columns,
scroll: { y: 750, x: 2000 },
selectable: true,
@ -96,7 +47,14 @@
const fetchData = async () => {
await loadList();
};
onMounted(() => {
fetchData();
const handleRemove = async (record: UserTableItem) => {
await deleteUserFromUserGroup(record.id);
await fetchData();
};
watchEffect(() => {
if (store.currentId) {
setLoadListParams({ roleId: store.currentId });
fetchData();
}
});
</script>

View File

@ -1,7 +1,11 @@
<template>
<div class="user-group flex flex-row bg-white">
<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 class="grow-1 w-[100%] overflow-x-scroll p-[24px]">
<div class="grow-1 flex flex-row items-center justify-between">
@ -35,6 +39,7 @@
import AuthTable from './components/authTable.vue';
const currentTable = ref('auth');
const collapse = ref(true);
const { t } = useI18n();
@ -52,5 +57,21 @@
position: relative;
padding: 24px;
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>

View File

@ -1,6 +1,7 @@
export default {
system: {
userGroup: {
addUserGroupSuccess: 'Add user group success',
searchHolder: 'Please input user group name',
inSystem: 'In system',
customUserGroup: 'Custom user group',
@ -40,6 +41,34 @@ export default {
beforeDeleteUserGroup:
'After deletion, the project data under the organization will be deleted together. Please operate with caution!',
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 {
system: {
userGroup: {
addUserGroupSuccess: '添加用户组成功',
global: '全局用户组',
searchHolder: '请输入用户组名称',
inSystem: '系统内置',
@ -39,6 +40,34 @@ export default {
isDeleteUserGroup: '是否删除: {name}?',
beforeDeleteUserGroup: '删除后,该组织下的项目数据将一起删除,请谨慎操作!',
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: '删除',
},
},
};