feat(系统设置): 系统&组织-添加成员交互优化-新增用户组

This commit is contained in:
teukkk 2024-09-13 15:01:47 +08:00 committed by Craftsman
parent 3bb88dfc90
commit bea89c4f28
14 changed files with 237 additions and 236 deletions

View File

@ -5,6 +5,7 @@ import {
BatchAddUserGroupUrl,
DeleteMemberUrl,
GetMemberListUrl,
GetOrganizationMemberListPageUrl,
getProjectListUrl,
getUserGroupList,
getUserList,
@ -64,3 +65,7 @@ export function getProjectList(organizationId: string, keyword?: string) {
export function inviteOrgMember(data: InviteOrgMemberParams) {
return MSR.post({ url: inviteOrgMemberUrl, data }, { isReturnNativeResponse: true });
}
// 系统设置-组织-项目-分页获取成员列表
export function getOrganizationMemberListPage(data: TableQueryParams) {
return MSR.post({ url: GetOrganizationMemberListPageUrl, data });
}

View File

@ -85,6 +85,10 @@ export function addUserToOrgOrProject(data: AddUserToOrgOrProjectParams) {
export function getUserByOrganizationOrProject(sourceId: string, keyword: string) {
return MSR.get({ url: `${orgUrl.getUserByOrgOrProjectUrl}${sourceId}`, params: { keyword } });
}
// 系统设置-系统-组织与项目-获取添加成员列表
export function getSystemMemberListPage(data: TableQueryParams) {
return MSR.post({ url: orgUrl.getMemberListPageUrl, data });
}
// 系统-获取管理员下拉选项
export function getAdminByOrganizationOrProject(keyword: string) {

View File

@ -18,3 +18,5 @@ export const getSystemProjectListUrl = '/system/project/list';
export const inviteOrgMemberUrl = '/organization/user/invite';
// 系统设置-系统-组织与项目-组织-成员-更新成员用户组
export const UpdateSystemOrganizationMemberUrl = '/system/organization/update-member';
// 系统设置-组织-项目-分页获取成员列表
export const GetOrganizationMemberListPageUrl = '/organization/project/user-list';

View File

@ -27,6 +27,8 @@ export const getDisableOrgUrl = '/system/organization/disable/';
export const getDeleteOrgUrl = '/system/organization/delete/';
// 获取系统默认组织
export const getOrgDefaultUrl = '/system/organization/default';
// 系统设置-系统-组织与项目-获取添加成员列表
export const getMemberListPageUrl = '/system/organization/member-list';
// 系统-项目
// 更新项目信息
@ -74,8 +76,6 @@ export const postModifyProjectNameByOrgUrl = '/organization/project/rename';
export const postAddProjectByOrgUrl = '/organization/project/add';
// 添加项目成员
export const postAddProjectMemberByOrgUrl = '/organization/project/add-members';
// 获取用户列表
export const getUserTableByOrgIdOrProjectIdUrl = '/organization/project/user-list/';
// 恢复项目
export const getRecoverProjectByOrgUrl = '/organization/project/revoke/';
// 移除项目成员

View File

@ -141,7 +141,10 @@
if (item.children && item.children.length > 0 && !props.rowSelectionDisabledConfig?.disabledChildren) {
return hasUnselectedChildren(item.children, selectedKeys, rowKey);
}
return !selectedKeys.has(item[rowKey]);
return (
!selectedKeys.has(item[rowKey]) &&
!(props?.rowSelectionDisabledConfig?.disabledKey && item[props?.rowSelectionDisabledConfig?.disabledKey])
);
});
}

View File

@ -365,7 +365,11 @@ export default function useTableProps<T>(
};
const collectIds = (data: MsTableDataItem<T>[], rowKey: string) => {
data.forEach((item: MsTableDataItem<T>) => {
if (item[rowKey] && !propsRes.value.selectedKeys.has(item[rowKey])) {
if (
item[rowKey] &&
!propsRes.value.selectedKeys.has(item[rowKey]) &&
!(props?.rowSelectionDisabledConfig?.disabledKey && item[props?.rowSelectionDisabledConfig?.disabledKey])
) {
propsRes.value.selectedKeys.add(item[rowKey]);
propsRes.value.excludeKeys.delete(item[rowKey]);
}

View File

@ -4,4 +4,5 @@ export interface AddUserToOrgOrProjectParams {
projectId?: string;
// 等待接口改动 将要废弃以后用userIds
memberIds?: string[];
userRoleIds?: string[];
}

View File

@ -1,115 +0,0 @@
<template>
<a-modal
v-model:visible="currentVisible"
title-align="start"
class="ms-modal-form ms-modal-medium"
:ok-text="t('system.organization.addMember')"
:mask-closable="false"
unmount-on-close
@cancel="handleCancel(false)"
>
<template #title> {{ t('system.organization.addMember') }} </template>
<div class="form">
<a-form ref="formRef" class="rounded-[4px]" :model="form" layout="vertical">
<a-form-item
field="name"
:label="t('system.organization.member')"
asterisk-position="end"
:rules="[{ required: true, message: t('system.organization.addMemberRequired') }]"
>
<MsUserSelector
v-model="form.name"
:type="UserRequestTypeEnum.ORGANIZATION_PROJECT"
:load-option-params="{ organizationId: currentOrgId, projectId: props.projectId }"
disabled-key="memberFlag"
/>
</a-form-item>
</a-form>
</div>
<template #footer>
<a-button type="secondary" :loading="loading" @click="handleCancel(false)">
{{ t('common.cancel') }}
</a-button>
<a-button type="primary" :loading="loading" :disabled="form.name.length === 0" @click="handleAddMember">
{{ t('common.add') }}
</a-button>
</template>
</a-modal>
</template>
<script lang="ts" setup>
import { computed, onUnmounted, reactive, ref, watchEffect } from 'vue';
import { type FormInstance, Message, type ValidatedError } from '@arco-design/web-vue';
import MsUserSelector from '@/components/business/ms-user-selector/index.vue';
import { UserRequestTypeEnum } from '@/components/business/ms-user-selector/utils';
import { addProjectMemberByOrg } from '@/api/modules/setting/organizationAndProject';
import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store';
const { t } = useI18n();
const props = defineProps<{
visible: boolean;
projectId?: string;
}>();
const appStore = useAppStore();
const currentOrgId = computed(() => appStore.currentOrgId);
const emit = defineEmits<{
(e: 'cancel', shouldSearch: boolean): void;
}>();
const currentVisible = ref(props.visible);
const loading = ref(false);
const form = reactive({
name: [],
});
const formRef = ref<FormInstance>();
watchEffect(() => {
currentVisible.value = props.visible;
});
const handleCancel = (shouldSearch: boolean) => {
form.name = [];
emit('cancel', shouldSearch);
};
const handleAddMember = () => {
formRef.value?.validate(async (errors: undefined | Record<string, ValidatedError>) => {
if (errors) {
loading.value = false;
}
const { projectId } = props;
try {
loading.value = true;
await addProjectMemberByOrg({ userIds: form.name, projectId });
Message.success(t('system.organization.addSuccess'));
handleCancel(true);
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
} finally {
loading.value = false;
}
});
};
onUnmounted(() => {
form.name = [];
loading.value = false;
});
</script>
<style lang="less" scoped>
.option-name {
color: var(--color-text-1);
}
.option-email {
color: var(--color-text-4);
}
</style>

View File

@ -41,6 +41,7 @@
:tag-list="record.userRoleList"
type="primary"
theme="outline"
allow-edit
@click="handleTagClick(record)"
/>
<MsSelect
@ -78,10 +79,12 @@
</div>
</MsDrawer>
<AddUserModal
v-model:visible="userVisible"
is-organization
:user-group-options="userGroupOptions"
:project-id="props.projectId"
:organization-id="props.organizationId"
:visible="userVisible"
@cancel="handleHideUserModal"
@submit="handleAddMemberSubmit"
/>
</template>
@ -96,7 +99,7 @@
import MsTagGroup from '@/components/pure/ms-tag/ms-tag-group.vue';
import MsRemoveButton from '@/components/business/ms-remove-button/MsRemoveButton.vue';
import MsSelect from '@/components/business/ms-select';
import AddUserModal from './addUserModal.vue';
import AddUserModal from '@/views/setting/system/organizationAndProject/components/addUserModal.vue';
import { getProjectUserGroup, updateProjectMember } from '@/api/modules/project-management/projectMember';
import { deleteProjectMemberByOrg, postProjectMemberByProjectId } from '@/api/modules/setting/organizationAndProject';
@ -139,6 +142,7 @@
title: 'system.user.tableColumnUserGroup',
dataIndex: 'userRoleList',
slotName: 'userGroup',
allowEditTag: true,
isTag: true,
width: 300,
},
@ -235,12 +239,9 @@
userVisible.value = true;
};
const handleHideUserModal = (shouldSearch: boolean) => {
userVisible.value = false;
if (shouldSearch) {
fetchData();
emit('requestFetchData');
}
const handleAddMemberSubmit = () => {
fetchData();
emit('requestFetchData');
};
const handleRemove = async (record: TableData) => {

View File

@ -89,8 +89,19 @@
:current-project="currentUpdateProject"
@cancel="handleAddProjectModalCancel"
/>
<AddUserModal :project-id="currentProjectId" :visible="userVisible" @cancel="handleAddUserModalCancel" />
<UserDrawer v-bind="currentUserDrawer" @request-fetch-data="fetchData" @cancel="handleUserDrawerCancel" />
<AddUserModal
v-model:visible="userVisible"
is-organization
:project-id="currentProjectId"
:organization-id="currentOrgId"
@submit="fetchData"
/>
<UserDrawer
v-bind="currentUserDrawer"
:organization-id="currentOrgId"
@request-fetch-data="fetchData"
@cancel="handleUserDrawerCancel"
/>
</MsCard>
</template>
@ -111,8 +122,8 @@
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import MsUserAdminDiv from '@/components/pure/ms-user-admin-div/index.vue';
import AddProjectModal from './components/addProjectModal.vue';
import AddUserModal from './components/addUserModal.vue';
import UserDrawer from './components/userDrawer.vue';
import AddUserModal from '@/views/setting/system/organizationAndProject/components/addUserModal.vue';
import {
deleteProjectByOrg,
@ -332,12 +343,6 @@
currentUserDrawer.visible = false;
};
const handleAddUserModalCancel = (shouldSearch: boolean) => {
userVisible.value = false;
if (shouldSearch) {
fetchData();
}
};
const handleAddProjectModalCancel = (shouldSearch: boolean) => {
addProjectVisible.value = false;
currentUpdateProject.value = undefined;

View File

@ -1,114 +1,223 @@
<template>
<a-modal
v-model:visible="currentVisible"
:width="680"
title-align="start"
class="ms-modal-form ms-modal-medium"
:ok-text="t('system.organization.addMember')"
:title="t('system.organization.addMember')"
:mask-closable="false"
unmount-on-close
@cancel="handleCancel"
>
<template #title> {{ t('system.organization.addMember') }} </template>
<div class="form">
<a-form ref="formRef" class="rounded-[4px]" :model="form" layout="vertical">
<a-form-item
field="name"
asterisk-position="end"
:label="t('system.organization.member')"
:rules="[{ required: true, message: t('system.organization.addMemberRequired') }]"
>
<MsUserSelector
v-model="form.name"
:type="UserRequestTypeEnum.SYSTEM_ORGANIZATION"
disabled-key="memberFlag"
:load-option-params="{ sourceId: props.organizationId || props.projectId }"
/>
</a-form-item>
</a-form>
<div class="mb-[16px] flex justify-end">
<a-input-search
v-model="keyword"
:placeholder="t('ms.case.associate.searchPlaceholder')"
allow-clear
class="w-[240px]"
@press-enter="initData"
@search="initData"
@clear="initData"
/>
</div>
<MsBaseTable
v-bind="propsRes"
:action-config="{
baseAction: [],
moreAction: [],
}"
v-on="propsEvent"
></MsBaseTable>
<template #footer>
<a-button type="secondary" :loading="loading" @click="handleCancel">
{{ t('common.cancel') }}
</a-button>
<a-button type="primary" :loading="loading" :disabled="form.name.length === 0" @click="handleAddMember">
{{ t('common.add') }}
</a-button>
<div class="flex justify-between">
<div class="flex items-center gap-[8px]">
<div class="text-nowrap">{{ t('project.member.tableColumnUserGroup') }}</div>
<MsSelect
v-model:model-value="userGroupIds"
multiple
mode="static"
allow-clear
class="!w-[240px] text-start"
:search-keys="['name']"
value-key="id"
label-key="name"
:placeholder="t('project.member.selectUserScope')"
:options="currentUserGroupOptions"
/>
</div>
<div class="flex gap-[12px]">
<a-button type="secondary" :loading="loading" @click="handleCancel">
{{ t('common.cancel') }}
</a-button>
<a-button
type="primary"
:loading="loading"
:disabled="!userGroupIds.length || !tableSelected.length"
@click="handleAddMember"
>
{{ t('common.add') }}
</a-button>
</div>
</div>
</template>
</a-modal>
</template>
<script lang="ts" setup>
import { onUnmounted, reactive, ref, watchEffect } from 'vue';
import { type FormInstance, Message, type ValidatedError } from '@arco-design/web-vue';
import { Message } from '@arco-design/web-vue';
import MsUserSelector from '@/components/business/ms-user-selector/index.vue';
import { UserRequestTypeEnum } from '@/components/business/ms-user-selector/utils';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import useTable from '@/components/pure/ms-table/useTable';
import MsSelect from '@/components/business/ms-select';
import { addUserToOrgOrProject } from '@/api/modules/setting/organizationAndProject';
import { getProjectUserGroup } from '@/api/modules/project-management/projectMember';
import { getGlobalUserGroup, getOrganizationMemberListPage } from '@/api/modules/setting/member';
import {
addProjectMemberByOrg,
addUserToOrgOrProject,
getSystemMemberListPage,
} from '@/api/modules/setting/organizationAndProject';
import { useI18n } from '@/hooks/useI18n';
const { t } = useI18n();
import type { LinkList } from '@/models/setting/member';
const props = defineProps<{
visible: boolean;
isOrganization?: boolean; //
userGroupOptions?: LinkList;
organizationId?: string;
projectId?: string;
}>();
const emit = defineEmits<{
(e: 'cancel'): void;
(e: 'submit'): void;
}>();
const currentVisible = ref(props.visible);
const loading = ref(false);
const form = reactive({
name: [],
const currentVisible = defineModel<boolean>('visible', {
required: true,
});
const formRef = ref<FormInstance>();
const { t } = useI18n();
watchEffect(() => {
currentVisible.value = props.visible;
});
const columns = [
{
title: 'common.name',
dataIndex: 'name',
width: 200,
showTooltip: true,
},
{
title: 'system.organization.email',
dataIndex: 'email',
showTooltip: true,
width: 250,
},
];
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector, resetPagination } = useTable(
!props.isOrganization ? getSystemMemberListPage : getOrganizationMemberListPage,
{
columns,
scroll: { x: '100%' },
size: 'mini',
selectable: true,
showSelectAll: true,
showSelectorAll: false,
rowSelectionDisabledConfig: {
disabledKey: 'memberFlag',
},
paginationSize: 'mini',
}
);
const keyword = ref<string>('');
const currentUserGroupOptions = ref<LinkList>([]);
const userGroupIds = ref<string[]>([]);
const tableSelected = computed(() => [...propsRes.value.selectedKeys]);
function initData() {
setLoadListParams({
keyword: keyword.value,
sourceId: props.projectId ?? props.organizationId,
projectId: props.projectId,
organizationId: props.organizationId,
});
loadList();
}
const getUserGroupOptions = async () => {
try {
if (props.organizationId && !props.isOrganization) {
// ---
currentUserGroupOptions.value = await getGlobalUserGroup(props.organizationId);
} else if (props.projectId) {
// --- --
currentUserGroupOptions.value = await getProjectUserGroup(props.projectId);
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
};
watch(
() => currentVisible.value,
(value) => {
if (value) {
initData();
if (!props.userGroupOptions) {
getUserGroupOptions();
} else {
currentUserGroupOptions.value = props.userGroupOptions;
}
if (props.projectId) {
userGroupIds.value = ['project_member'];
} else if (props.organizationId) {
userGroupIds.value = ['org_member'];
}
}
}
);
const handleCancel = () => {
form.name = [];
emit('cancel');
currentVisible.value = false;
keyword.value = '';
userGroupIds.value = [];
resetPagination();
resetSelector();
};
const handleAddMember = () => {
formRef.value?.validate(async (errors: undefined | Record<string, ValidatedError>) => {
if (errors) {
loading.value = false;
const loading = ref(false);
const handleAddMember = async () => {
try {
loading.value = true;
if (!props.isOrganization) {
// -
await addUserToOrgOrProject({
userRoleIds: userGroupIds.value,
userIds: tableSelected.value,
projectId: props.projectId,
organizationId: props.organizationId,
});
} else {
// -
await addProjectMemberByOrg({
userRoleIds: userGroupIds.value,
userIds: tableSelected.value,
projectId: props.projectId,
});
}
const { organizationId, projectId } = props;
try {
loading.value = true;
await addUserToOrgOrProject({ userIds: form.name, organizationId, projectId });
Message.success(t('system.organization.addSuccess'));
handleCancel();
emit('submit');
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
} finally {
loading.value = false;
}
});
Message.success(t('system.organization.addSuccess'));
emit('submit');
handleCancel();
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
} finally {
loading.value = false;
}
};
onUnmounted(() => {
form.name = [];
loading.value = false;
});
</script>
<style lang="less" scoped>
.option-name {
color: var(--color-text-1);
}
.option-email {
color: var(--color-text-4);
:deep(.ms-pagination) {
gap: 8px;
.ms-pagination-item-previous {
margin-left: 0;
}
}
</style>

View File

@ -79,12 +79,7 @@
:visible="orgVisible"
@cancel="handleAddOrgModalCancel"
/>
<AddUserModal
:organization-id="currentOrganizationId"
:visible="userVisible"
@cancel="handleAddUserModalCancel"
@submit="fetchData"
/>
<AddUserModal v-model:visible="userVisible" :organization-id="currentOrganizationId" @submit="fetchData" />
<ProjectDrawer v-bind="currentProjectDrawer" @cancel="handleProjectDrawerCancel" />
<UserDrawer v-bind="currentUserDrawer" @request-fetch-data="fetchData" @cancel="handleUserDrawerCancel" />
</template>
@ -334,9 +329,6 @@
currentUserDrawer.visible = false;
};
const handleAddUserModalCancel = () => {
userVisible.value = false;
};
const handleAddOrgModalCancel = (shouldSearch: boolean) => {
orgVisible.value = false;
if (shouldSearch) {

View File

@ -64,12 +64,7 @@
:visible="addProjectVisible"
@cancel="handleAddProjectModalCancel"
/>
<AddUserModal
:project-id="currentProjectId"
:visible="userVisible"
@submit="fetchData"
@cancel="handleAddUserModalCancel"
/>
<AddUserModal v-model:visible="userVisible" :project-id="currentProjectId" @submit="fetchData" />
<UserDrawer v-bind="currentUserDrawer" @request-fetch-data="fetchData" @cancel="handleUserDrawerCancel" />
</template>
@ -301,9 +296,6 @@
currentUserDrawer.currentName = '';
};
const handleAddUserModalCancel = () => {
userVisible.value = false;
};
const handleAddProjectModalCancel = (shouldSearch: boolean) => {
if (shouldSearch) {
fetchData();

View File

@ -42,6 +42,7 @@
:tag-list="record.userRoleList"
type="primary"
theme="outline"
allow-edit
@click="handleTagClick(record)"
/>
<MsSelect
@ -79,11 +80,11 @@
</div>
</ms-drawer>
<AddUserModal
v-model:visible="userVisible"
:user-group-options="userGroupOptions"
:project-id="props.projectId"
:organization-id="props.organizationId"
:visible="userVisible"
@cancel="handleHideUserModal"
@submit="handleAddMembeSubmit"
@submit="handleAddMemberSubmit"
/>
</template>
@ -152,6 +153,7 @@
title: 'system.user.tableColumnUserGroup',
dataIndex: 'userRoleList',
slotName: 'userGroup',
allowEditTag: true,
isTag: true,
width: 300,
},
@ -255,15 +257,11 @@
const handleAddMember = () => {
userVisible.value = true;
};
const handleAddMembeSubmit = () => {
const handleAddMemberSubmit = () => {
fetchData();
emit('requestFetchData');
};
const handleHideUserModal = () => {
userVisible.value = false;
};
const handleRemove = async (record: TableData) => {
try {
if (props.organizationId) {