feat(系统管理): 成员和项目添加下拉检索

This commit is contained in:
xinxin.wu 2023-09-12 10:51:14 +08:00 committed by 刘瑞斌
parent aef40e0a8d
commit 99fd8437c7
8 changed files with 97 additions and 41 deletions

View File

@ -46,6 +46,6 @@ export function getProjectUserGroup(projectId: string) {
} }
// 项目成员下拉选项 // 项目成员下拉选项
export function getProjectMemberOptions(projectId: string) { export function getProjectMemberOptions(projectId: string, keyword?: string) {
return MSR.get({ url: ProjectMemberOptions, params: projectId }); return MSR.get({ url: `${ProjectMemberOptions}/${projectId}`, params: { keyword } });
} }

View File

@ -40,10 +40,10 @@ export function getGlobalUserGroup(organizationId: string) {
return MSR.get({ url: getUserGroupList, params: organizationId }); return MSR.get({ url: getUserGroupList, params: organizationId });
} }
// 获取系统用户下拉 // 获取系统用户下拉
export function getUser(organizationId: string) { export function getUser(organizationId: string, keyword: string) {
return MSR.get<LinkItem[]>({ url: getUserList, params: organizationId }); return MSR.get<LinkItem[]>({ url: `${getUserList}/${organizationId}`, params: { keyword } });
} }
// 获取组织下边的项目 // 获取组织下边的项目
export function getProjectList(organizationId: string) { export function getProjectList(organizationId: string, keyword?: string) {
return MSR.get<LinkItem[]>({ url: getProjectListUrl, params: organizationId }); return MSR.get<LinkItem[]>({ url: `${getProjectListUrl}/${organizationId}`, params: { keyword } });
} }

View File

@ -5,6 +5,8 @@ import {
getUserByProjectByOrg, getUserByProjectByOrg,
} from '@/api/modules/setting/organizationAndProject'; } from '@/api/modules/setting/organizationAndProject';
import { getOrgUserGroupOption, getSystemUserGroupOption } from '@/api/modules/setting/usergroup'; import { getOrgUserGroupOption, getSystemUserGroupOption } from '@/api/modules/setting/usergroup';
import { getUser, getProjectList } from '@/api/modules/setting/member';
import { getProjectMemberOptions } from '@/api/modules/project-management/projectMember';
// eslint-disable-next-line no-shadow // eslint-disable-next-line no-shadow
export enum UserRequesetTypeEnum { export enum UserRequesetTypeEnum {
@ -17,6 +19,9 @@ export enum UserRequesetTypeEnum {
ORGANIZATION_USER_GROUP_ADMIN = 'ORGANIZATION_USER_GROUP_ADMIN', ORGANIZATION_USER_GROUP_ADMIN = 'ORGANIZATION_USER_GROUP_ADMIN',
ORGANIZATION_PROJECT = 'ORGANIZATION_PROJECT', ORGANIZATION_PROJECT = 'ORGANIZATION_PROJECT',
ORGANIZATION_PROJECT_ADMIN = 'ORGANIZATION_PROJECT_ADMIN', ORGANIZATION_PROJECT_ADMIN = 'ORGANIZATION_PROJECT_ADMIN',
SYSTEM_ORGANIZATION_PROJECT = 'SYSTEM_ORGANIZATION_PROJECT',
SYSTEM_ORGANIZATION_MEMBER = 'SYSTEM_ORGANIZATION_MEMBER',
PROJECT_PERMISSION_MEMBER = 'PROJECT_PERMISSION_MEMBER',
} }
export default function initOptionsFunc(type: string, params: Record<string, any>) { export default function initOptionsFunc(type: string, params: Record<string, any>) {
if (type === UserRequesetTypeEnum.SYSTEM_USER_GROUP) { if (type === UserRequesetTypeEnum.SYSTEM_USER_GROUP) {
@ -43,4 +48,16 @@ export default function initOptionsFunc(type: string, params: Record<string, any
// 组织 - 项目-添加管理员-下拉选项 // 组织 - 项目-添加管理员-下拉选项
return getAdminByProjectByOrg(params.organizationId, params.keyword); return getAdminByProjectByOrg(params.organizationId, params.keyword);
} }
if (type === UserRequesetTypeEnum.SYSTEM_ORGANIZATION_PROJECT) {
// 系统-组织-组织项目
return getProjectList(params.organizationId, params.keyword);
}
if (type === UserRequesetTypeEnum.SYSTEM_ORGANIZATION_MEMBER) {
// 系统-组织-组织成员
return getUser(params.organizationId, params.keyword);
}
if (type === UserRequesetTypeEnum.PROJECT_PERMISSION_MEMBER) {
// 系统-组织-项目成员
return getProjectMemberOptions(params.projectId, params.keyword);
}
} }

View File

@ -416,6 +416,9 @@
border-color: rgb(var(--primary-5)); border-color: rgb(var(--primary-5));
background: rgb(var(--primary-1)); background: rgb(var(--primary-1));
} }
.arco-icon-hover:hover::before {
background: none;
}
} }
:deep(.ms-table-select-all) { :deep(.ms-table-select-all) {
.arco-checkbox { .arco-checkbox {
@ -426,5 +429,14 @@
width: 16px; width: 16px;
height: 16px; height: 16px;
border-radius: 2px; border-radius: 2px;
&:hover {
border-color: rgb(var(--primary-5));
}
&::before {
background: none !important;
}
}
:deep(.arco-checkbox:hover .arco-checkbox-icon-hover::before) {
background: none !important;
} }
</style> </style>

View File

@ -15,9 +15,13 @@
asterisk-position="end" asterisk-position="end"
:rules="[{ required: true, message: t('project.member.selectMemberEmptyTip') }]" :rules="[{ required: true, message: t('project.member.selectMemberEmptyTip') }]"
> >
<a-select v-model="form.userIds" multiple :placeholder="t('project.member.selectMemberScope')" allow-clear> <MsUserSelector
<a-option v-for="item of memberList" :key="item.id" :value="item.id">{{ item.name }}</a-option> v-model:value="form.userIds"
</a-select> :load-option-params="{ projectId: lastProjectId }"
:type="UserRequesetTypeEnum.PROJECT_PERMISSION_MEMBER"
placeholder="project.member.selectMemberScope"
disabled-key="memberFlag"
/>
</a-form-item> </a-form-item>
<a-form-item <a-form-item
field="roleIds" field="roleIds"
@ -40,8 +44,14 @@
import { getProjectMemberOptions, addOrUpdateProjectMember } from '@/api/modules/project-management/projectMember'; import { getProjectMemberOptions, addOrUpdateProjectMember } from '@/api/modules/project-management/projectMember';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { useUserStore } from '@/store'; import { useUserStore } from '@/store';
import MsUserSelector from '@/components/business/ms-user-selector/index.vue';
import { FormInstance, Message } from '@arco-design/web-vue'; import { FormInstance, Message } from '@arco-design/web-vue';
import type { ProjectUserOption, ActionProjectMember } from '@/models/projectManagement/projectAndPermission'; import type {
ProjectUserOption,
ActionProjectMember,
AddProjectMember,
} from '@/models/projectManagement/projectAndPermission';
import { UserRequesetTypeEnum } from '@/components/business/ms-user-selector/utils';
const { t } = useI18n(); const { t } = useI18n();
const userStore = useUserStore(); const userStore = useUserStore();
@ -58,13 +68,13 @@
const visible = ref<boolean>(false); const visible = ref<boolean>(false);
const initFormValue: ActionProjectMember = { const initFormValue: AddProjectMember = {
roleIds: ['project_member'], roleIds: ['project_member'],
userIds: [], userIds: [],
projectId: lastProjectId, projectId: lastProjectId as string,
}; };
const form = ref<ActionProjectMember>({ ...initFormValue }); const form = ref<AddProjectMember>({ ...initFormValue });
const memberFormRef = ref<FormInstance | null>(null); const memberFormRef = ref<FormInstance | null>(null);

View File

@ -41,6 +41,7 @@
v-else v-else
v-model="record.selectUserList" v-model="record.selectUserList"
multiple multiple
class="w-[260px]"
:max-tag-count="2" :max-tag-count="2"
@popup-visible-change="(value) => userGroupChange(value, record)" @popup-visible-change="(value) => userGroupChange(value, record)"
> >
@ -62,6 +63,7 @@
position="br" position="br"
:title="t('project.member.deleteMemberTip', { name: characterLimit(record.name) })" :title="t('project.member.deleteMemberTip', { name: characterLimit(record.name) })"
:sub-title-tip="t('project.member.subTitle')" :sub-title-tip="t('project.member.subTitle')"
:loading="deleteLoading"
@ok="removeMember(record)" @ok="removeMember(record)"
/> />
</template> </template>
@ -157,7 +159,7 @@
title: 'project.member.tableColumnActions', title: 'project.member.tableColumnActions',
slotName: 'action', slotName: 'action',
fixed: 'right', fixed: 'right',
width: 80, width: 100,
showInTable: true, showInTable: true,
}, },
]; ];
@ -244,7 +246,10 @@
}; };
// //
const deleteLoading = ref<boolean>(false);
const removeMember = async (record: ProjectMemberItem) => { const removeMember = async (record: ProjectMemberItem) => {
deleteLoading.value = true;
try { try {
if (lastProjectId && record.id) { if (lastProjectId && record.id) {
await removeProjectMember(lastProjectId, record.id); await removeProjectMember(lastProjectId, record.id);
@ -253,6 +258,8 @@
} }
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} finally {
deleteLoading.value = false;
} }
}; };

View File

@ -16,16 +16,16 @@
</template> </template>
<div class="form"> <div class="form">
<a-form ref="memberFormRef" :model="form" size="large" layout="vertical"> <a-form ref="memberFormRef" :model="form" size="large" layout="vertical">
<!-- 编辑项目 -->
<a-form-item v-if="type === 'edit'" :label="t('organization.member.project')" asterisk-position="end"> <a-form-item v-if="type === 'edit'" :label="t('organization.member.project')" asterisk-position="end">
<a-select <MsUserSelector
v-model="form.projectIds" v-model:value="form.projectIds"
multiple :load-option-params="{ organizationId: lastOrganizationId }"
:placeholder="t('organization.member.selectProjectScope')" :type="UserRequesetTypeEnum.SYSTEM_ORGANIZATION_PROJECT"
allow-clear placeholder="organization.member.selectProjectScope"
> />
<a-option v-for="item of props.projectList" :key="item.id" :value="item.id">{{ item.name }}</a-option>
</a-select>
</a-form-item> </a-form-item>
<!-- 添加成员 -->
<a-form-item <a-form-item
v-else v-else
field="memberIds" field="memberIds"
@ -33,14 +33,12 @@
asterisk-position="end" asterisk-position="end"
:rules="[{ required: true, message: t('organization.member.selectMemberEmptyTip') }]" :rules="[{ required: true, message: t('organization.member.selectMemberEmptyTip') }]"
> >
<a-select <MsUserSelector
v-model="form.memberIds" v-model:value="form.memberIds"
multiple :load-option-params="{ organizationId: lastOrganizationId }"
:placeholder="t('organization.member.selectMemberScope')" :type="UserRequesetTypeEnum.SYSTEM_ORGANIZATION_MEMBER"
allow-clear placeholder="organization.member.selectMemberScope"
> />
<a-option v-for="item of memberList" :key="item.id" :value="item.id">{{ item.name }}</a-option>
</a-select>
</a-form-item> </a-form-item>
<a-form-item <a-form-item
field="userRoleIds" field="userRoleIds"
@ -72,9 +70,11 @@
import { ref, watchEffect, watch } from 'vue'; import { ref, watchEffect, watch } from 'vue';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { FormInstance, Message, ValidatedError } from '@arco-design/web-vue'; import { FormInstance, Message, ValidatedError } from '@arco-design/web-vue';
import { addOrUpdate, getUser } from '@/api/modules/setting/member'; import { addOrUpdate } from '@/api/modules/setting/member';
import { useUserStore } from '@/store'; import { useUserStore } from '@/store';
import type { AddorUpdateMemberModel, MemberItem, LinkList } from '@/models/setting/member'; import MsUserSelector from '@/components/business/ms-user-selector/index.vue';
import type { MemberItem, LinkList } from '@/models/setting/member';
import { UserRequesetTypeEnum } from '@/components/business/ms-user-selector/utils';
const { t } = useI18n(); const { t } = useI18n();
const userStore = useUserStore(); const userStore = useUserStore();
@ -82,10 +82,8 @@
const props = defineProps<{ const props = defineProps<{
visible: boolean; visible: boolean;
userGroupOptions: LinkList; userGroupOptions: LinkList;
projectList: LinkList;
}>(); }>();
const dialogVisible = ref<boolean>(false); const dialogVisible = ref<boolean>(false);
const memberList = ref<LinkList>([]);
const title = ref<string>(''); const title = ref<string>('');
const type = ref<string>(''); const type = ref<string>('');
const emits = defineEmits<{ const emits = defineEmits<{
@ -95,13 +93,20 @@
const confirmLoading = ref<boolean>(false); const confirmLoading = ref<boolean>(false);
const memberFormRef = ref<FormInstance | null>(null); const memberFormRef = ref<FormInstance | null>(null);
const initFormValue = { export interface InitFromType {
organizationId?: string;
userRoleIds: string[];
memberIds: string[];
projectIds: string[];
}
const initFormValue: InitFromType = {
organizationId: userStore.$state?.lastOrganizationId, organizationId: userStore.$state?.lastOrganizationId,
userRoleIds: ['org_member'], userRoleIds: ['org_member'],
memberIds: [], memberIds: [],
projectIds: [], projectIds: [],
}; };
const form = ref<AddorUpdateMemberModel>({ ...initFormValue }); const form = ref({ ...initFormValue });
const handleCancel = () => { const handleCancel = () => {
memberFormRef.value?.resetFields(); memberFormRef.value?.resetFields();
form.value = { ...initFormValue }; form.value = { ...initFormValue };
@ -157,12 +162,9 @@
} }
}); });
}; };
const getUserOptions = async () => {
memberList.value = await getUser(lastOrganizationId);
};
watchEffect(() => { watchEffect(() => {
dialogVisible.value = props.visible; dialogVisible.value = props.visible;
if (props.visible) getUserOptions();
}); });
watch( watch(
() => dialogVisible.value, () => dialogVisible.value,

View File

@ -37,6 +37,7 @@
multiple multiple
:max-tag-count="2" :max-tag-count="2"
size="small" size="small"
class="w-[260px]"
@change="(value) => selectUserOrProject(value, record, 'project')" @change="(value) => selectUserOrProject(value, record, 'project')"
@popup-visible-change="visibleChange($event, record, 'project')" @popup-visible-change="visibleChange($event, record, 'project')"
> >
@ -57,6 +58,7 @@
v-model="record.selectUserList" v-model="record.selectUserList"
multiple multiple
:max-tag-count="2" :max-tag-count="2"
class="w-[260px]"
@change="(value) => selectUserOrProject(value, record, 'user')" @change="(value) => selectUserOrProject(value, record, 'user')"
@popup-visible-change="(value) => visibleChange(value, record, 'user')" @popup-visible-change="(value) => visibleChange(value, record, 'user')"
> >
@ -79,6 +81,7 @@
position="br" position="br"
:title="t('organization.member.deleteMemberTip', { name: characterLimit(record.name) })" :title="t('organization.member.deleteMemberTip', { name: characterLimit(record.name) })"
:sub-title-tip="t('organization.member.subTitle')" :sub-title-tip="t('organization.member.subTitle')"
:loading="deleteLoading"
@ok="deleteMember(record)" @ok="deleteMember(record)"
/> />
</template> </template>
@ -126,7 +129,7 @@
import { useTableStore, useUserStore } from '@/store'; import { useTableStore, useUserStore } from '@/store';
import type { MsTableColumn } from '@/components/pure/ms-table/type'; import type { MsTableColumn } from '@/components/pure/ms-table/type';
import type { MemberItem, AddorUpdateMemberModel, LinkList, BatchAddProjectModel } from '@/models/setting/member'; import type { MemberItem, AddorUpdateMemberModel, LinkList, BatchAddProjectModel } from '@/models/setting/member';
import { characterLimit } from '@/utils'; import { characterLimit, findNodeByKey } from '@/utils';
import MsTagGroup from '@/components/pure/ms-tag/ms-tag-group.vue'; import MsTagGroup from '@/components/pure/ms-tag/ms-tag-group.vue';
const tableStore = useTableStore(); const tableStore = useTableStore();
@ -230,13 +233,18 @@
AddMemberRef.value.edit(record); AddMemberRef.value.edit(record);
} }
}; };
const deleteLoading = ref<boolean>(false);
const deleteMember = async (record: MemberItem) => { const deleteMember = async (record: MemberItem) => {
deleteLoading.value = true;
try { try {
if (lastOrganizationId) await deleteMemberReq(lastOrganizationId, record.id); if (lastOrganizationId) await deleteMemberReq(lastOrganizationId, record.id);
Message.success(t('organization.member.deleteMemberSuccess')); Message.success(t('organization.member.deleteMemberSuccess'));
initData(); initData();
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} finally {
deleteLoading.value = false;
} }
}; };
const handleTableSelect = (selectArr: (string | number)[]) => { const handleTableSelect = (selectArr: (string | number)[]) => {