feat(系统设置): 系统_组织_成员_页面&联调

This commit is contained in:
xinxin.wu 2023-07-28 14:56:22 +08:00 committed by 刘瑞斌
parent 4809d95320
commit cd691e377f
13 changed files with 556 additions and 395 deletions

View File

@ -1,12 +1,49 @@
import MSR from '@/api/http/index'; import MSR from '@/api/http/index';
import { GetMemberListUrl, AddMemberUrl } from '@/api/requrls/setting/member'; import {
import type { UserListItem, CreateUserParams } from '@/models/setting/user'; GetMemberListUrl,
AddMemberUrl,
UpdateMemberUrl,
BatchAddProjectUrl,
BatchAddUserGroupUrl,
DeleteMemberUrl,
getUserGroupList,
getUserList,
getProjectListUrl,
} from '@/api/requrls/setting/member';
import type { MemberList, AddorUpdateMemberModel, BatchAddProjectModel } from '@/models/setting/member';
import type { TableQueryParams } from '@/models/common'; import type { TableQueryParams } from '@/models/common';
// 获取成员列表
export function getMemberList(data: TableQueryParams) { export function getMemberList(data: TableQueryParams) {
return MSR.post<UserListItem[]>({ url: GetMemberListUrl, data }); return MSR.post<MemberList>({ url: GetMemberListUrl, data });
} }
// 添加成员
export function batchCreateUser(data: CreateUserParams) { export function addOrUpdate(data: AddorUpdateMemberModel, type: string) {
return MSR.post({ url: AddMemberUrl, data }); if (type === 'add') {
return MSR.post({ url: AddMemberUrl, data });
}
return MSR.post({ url: UpdateMemberUrl, data });
}
// 添加到项目
export function batchAddProject(data: BatchAddProjectModel) {
return MSR.post({ url: BatchAddProjectUrl, data });
}
// 添加到用户
export function batchAddUserGroup(data: AddorUpdateMemberModel) {
return MSR.post({ url: BatchAddUserGroupUrl, data });
}
// 移除成员
export function deleteMemberReq(organizationId: string, userId: string) {
return MSR.get({ url: DeleteMemberUrl, params: `/${organizationId}/${userId}` });
}
// 获取全局用户组下拉
export function getGlobalUserGroup() {
return MSR.get({ url: getUserGroupList });
}
// 获取用户下拉
export function getUser() {
return MSR.get({ url: getUserList });
}
// 【暂用】获取组织下边的项目
export function getProjectList(data: any) {
return MSR.post({ url: getProjectListUrl, data });
} }

View File

@ -1,2 +1,15 @@
export const GetMemberListUrl = '/member/page'; export const GetMemberListUrl = '/organization/member/list';
export const AddMemberUrl = '/member/add'; export const AddMemberUrl = '/organization/add-member';
export const UpdateMemberUrl = '/organization/update-member';
// 添加至项目
export const BatchAddProjectUrl = '/organization/project/add-member';
// 添加至用户组
export const BatchAddUserGroupUrl = '/organization/role/update-member';
export const DeleteMemberUrl = '/organization/remove-member';
// 暂用
// 用户组下拉
export const getUserGroupList = '/user/role/global/list';
// 用户下拉
export const getUserList = '/system/user/list';
// 获取弹窗里边的穿梭项目列表
export const getProjectListUrl = '/system/project/page';

View File

@ -64,12 +64,11 @@
visible: false, visible: false,
} }
); );
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:visible', val: boolean): void; (e: 'update:visible', val: boolean): void;
(e: 'addProject'): void; (e: 'addProject', targetValue: string[]): void;
(e: 'addUserGroup'): void; (e: 'addUserGroup', targetValue: string[]): void;
(e: 'addOrgnization'): void; (e: 'addOrgnization', targetValue: string[]): void;
}>(); }>();
const showBatchModal = ref(false); const showBatchModal = ref(false);
@ -150,7 +149,6 @@
return travel(props.treeData); return travel(props.treeData);
}; };
const transferData = getTransferData(props.treeData, []); const transferData = getTransferData(props.treeData, []);
function cancelBatch() { function cancelBatch() {
@ -158,18 +156,21 @@
target.value = []; target.value = [];
} }
async function confirmBatch() { const confirmBatch = () => {
batchLoading.value = true; batchLoading.value = true;
try { try {
if (target.value.length < 1) {
return;
}
switch (props.action) { switch (props.action) {
case 'batchAddProject': case 'batchAddProject':
emit('addProject'); emit('addProject', target.value);
break; break;
case 'batchAddUsergroup': case 'batchAddUsergroup':
emit('addUserGroup'); emit('addUserGroup', target.value);
break; break;
case 'batchAddOrgnization': case 'batchAddOrgnization':
emit('addOrgnization'); emit('addOrgnization', target.value);
break; break;
default: default:
break; break;
@ -180,7 +181,7 @@
} finally { } finally {
batchLoading.value = false; batchLoading.value = false;
} }
} };
</script> </script>
<style lang="less" scoped></style> <style lang="less" scoped></style>

View File

@ -11,4 +11,5 @@ export enum TableKeyEnum {
USERGROUPUSER = 'userGroupUser', USERGROUPUSER = 'userGroupUser',
SYSTEM_USER = 'systemUser', SYSTEM_USER = 'systemUser',
SYSTEM_RESOURCEPOOL = 'systemResourcePool', SYSTEM_RESOURCEPOOL = 'systemResourcePool',
ORGANNATIONMEMBER = 'member',
} }

View File

@ -6,6 +6,5 @@ import './api-test';
import './system/user'; import './system/user';
import './system/project'; import './system/project';
import './system/resourcePool'; import './system/resourcePool';
import './system/member';
MOCK.onAny().passThrough(); MOCK.onAny().passThrough();

View File

@ -1,253 +0,0 @@
import { mock } from '@/utils/setup-mock';
import { RequestEnum } from '@/enums/httpEnum';
const getMemberList = () => {
return [
{
id: '103423',
name: '成员1',
email: 'dehihu@kds.sd',
enable: true,
createTime: 1686905750716,
updateTime: 0,
lastOrganizationId: 'string',
phone: '18473647583',
source: 'string',
lastProjectId: 'string',
createUser: 'string',
updateUser: 'string',
projectList: [
{
id: 'string',
num: 0,
name: '项目 1',
description: 'blabla',
createTime: 0,
updateTime: 0,
createUser: 'string',
updateUser: 'string',
deleted: true,
deleteUser: 'string',
deleteTime: 0,
enable: true,
},
{
id: 'string',
num: 0,
name: '项目 2',
description: 'blabla',
createTime: 0,
updateTime: 0,
createUser: 'string',
updateUser: 'string',
deleted: true,
deleteUser: 'string',
deleteTime: 0,
enable: true,
},
{
id: 'string',
num: 0,
name: '项目 3',
description: 'blabla',
createTime: 0,
updateTime: 0,
createUser: 'string',
updateUser: 'string',
deleted: true,
deleteUser: 'string',
deleteTime: 0,
enable: true,
},
{
id: 'string',
num: 0,
name: '项目 4',
description: 'blabla',
createTime: 0,
updateTime: 0,
createUser: 'string',
updateUser: 'string',
deleted: true,
deleteUser: 'string',
deleteTime: 0,
enable: true,
},
],
userRoleList: [
{
id: 'string',
name: '角色 1',
description: 'sdadasd',
internal: true,
type: 'string',
createTime: 0,
updateTime: 0,
createUser: 'string',
scopeId: 'string',
pos: 0,
},
{
id: 'string',
name: '角色 2',
description: 'sdadasd',
internal: true,
type: 'string',
createTime: 0,
updateTime: 0,
createUser: 'string',
scopeId: 'string',
pos: 0,
},
{
id: 'string',
name: '角色 3',
description: 'sdadasd',
internal: true,
type: 'string',
createTime: 0,
updateTime: 0,
createUser: 'string',
scopeId: 'string',
pos: 0,
},
{
id: 'string',
name: '角色 4',
description: 'sdadasd',
internal: true,
type: 'string',
createTime: 0,
updateTime: 0,
createUser: 'string',
scopeId: 'string',
pos: 0,
},
],
},
{
id: '103233',
name: '成员2',
email: 'dehihu@kds.sd',
enable: true,
createTime: 1686905750716,
updateTime: 0,
lastOrganizationId: 'string',
phone: '18473647583',
source: 'string',
lastProjectId: 'string',
createUser: 'string',
updateUser: 'string',
projectList: [
{
id: 'string',
num: 0,
name: '项目 1',
description: 'blabla',
createTime: 0,
updateTime: 0,
createUser: 'string',
updateUser: 'string',
deleted: true,
deleteUser: 'string',
deleteTime: 0,
enable: true,
},
{
id: 'string',
num: 0,
name: '项目 2',
description: 'blabla',
createTime: 0,
updateTime: 0,
createUser: 'string',
updateUser: 'string',
deleted: true,
deleteUser: 'string',
deleteTime: 0,
enable: true,
},
{
id: 'string',
num: 0,
name: '组织 3',
description: 'blabla',
createTime: 0,
updateTime: 0,
createUser: 'string',
updateUser: 'string',
deleted: true,
deleteUser: 'string',
deleteTime: 0,
enable: true,
},
{
id: 'string',
num: 0,
name: '项目 4',
description: 'blabla',
createTime: 0,
updateTime: 0,
createUser: 'string',
updateUser: 'string',
deleted: true,
deleteUser: 'string',
deleteTime: 0,
enable: true,
},
],
userRoleList: [
{
id: 'string',
name: '角色 1',
description: 'sdadasd',
internal: true,
type: 'string',
createTime: 0,
updateTime: 0,
createUser: 'string',
scopeId: 'string',
pos: 0,
},
{
id: 'string',
name: '角色 2',
description: 'sdadasd',
internal: true,
type: 'string',
createTime: 0,
updateTime: 0,
createUser: 'string',
scopeId: 'string',
pos: 0,
},
{
id: 'string',
name: '角色 3',
description: 'sdadasd',
internal: true,
type: 'string',
createTime: 0,
updateTime: 0,
createUser: 'string',
scopeId: 'string',
pos: 0,
},
{
id: 'string',
name: '角色 4',
description: 'sdadasd',
internal: true,
type: 'string',
createTime: 0,
updateTime: 0,
createUser: 'string',
scopeId: 'string',
pos: 0,
},
],
},
];
};
mock(RequestEnum.POST, '/member/page', getMemberList(), 200, true);

View File

@ -1,4 +1,58 @@
export interface AddMemberForm { // 项目下拉列表
members: string[]; export type ItemMap = {
userGroups: string; name: string;
id: string;
};
// 成员项
export interface MemberItem {
id: string;
name: string;
email: string;
password: string;
enable: boolean; // 状态
createTime: string | number;
updateTime: string | number;
language: string;
lastOrganizationId: string; // 选中组织
phone: string;
source: string;
lastProjectId: string[]; // 选中项目
createUser: string;
updateUser: string;
deleted: boolean;
// 项目
projectIdNameMap: ItemMap[];
// 用户组
userRoleIdNameMap: ItemMap[];
// 编辑模式用户组
showUserSelect?: boolean;
// 编辑模式项目
showProjectSelect?: boolean;
// 添加到用户组下拉收集的用户组id 列表
selectUserList: string[];
// 添加到用户组下拉收集的项目id 列表
selectProjectList: string[];
} }
// 成员列表
export type MemberList = MemberItem[];
// 添加成员
export interface AddorUpdateMemberModel {
id?: string;
organizationId?: string;
memberIds?: string[];
userRoleIds?: string[];
projectIds?: string[];
}
// 添加组织成员到项目
export interface BatchAddProjectModel {
organizationId?: string;
memberIds?: Array<string | number>;
projectIds?: string[];
}
// 用户组下拉列表
export interface LinkItem {
id: string;
name: string;
}
export type LinkList = LinkItem[];

View File

@ -16,4 +16,5 @@ export interface UserState {
accountId?: string; accountId?: string;
certification?: number; certification?: number;
role: RoleType; role: RoleType;
lastOrganizationId?: string;
} }

View File

@ -28,7 +28,7 @@ export const successTableResponseWrap = (data: unknown) => {
data: { list: data }, data: { list: data },
status: 'ok', status: 'ok',
message: '请求成功', message: '请求成功',
code: 0, code: 100200,
}; };
}; };

View File

@ -3,103 +3,172 @@
v-model:visible="dialogVisible" v-model:visible="dialogVisible"
title-align="start" title-align="start"
class="ms-modal-form ms-modal-medium" class="ms-modal-form ms-modal-medium"
:ok-text="t('organization.member.Save')" :ok-text="t('organization.member.Confirm')"
@ok="handleOK" :cancel-text="t('organization.member.Cancel')"
@cancel="handleCancel"
> >
<template #title> {{ t('organization.member.addMember') }} </template> <template #title>
{{ type === 'add' ? t('organization.member.addMember') : t('organization.member.updateMember') }}
</template>
<div class="form"> <div class="form">
<a-form :model="form" size="large" layout="vertical"> <a-form ref="memberFormRef" :model="form" size="large" layout="vertical">
<a-form-item <a-form-item
v-model="form.members" v-if="type === 'edit'"
field="members" field="projectIds"
:label="t('organization.member.member')" :label="t('organization.member.proejct')"
asterisk-position="end" asterisk-position="end"
:rules="[{ required: true, message: t('organization.member.pleaseSelectMember') }]" :rules="[{ required: true, message: t('organization.member.selectProjectEmptyTip') }]"
> >
<a-select <a-select
v-model="form.members" v-model="form.projectIds"
multiple
:placeholder="t('organization.member.selectProjectScope')"
allow-clear
>
<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
v-else
field="memberIds"
:label="t('organization.member.member')"
asterisk-position="end"
:rules="[{ required: true, message: t('organization.member.selectMemberEmptyTip') }]"
>
<a-select
v-model="form.memberIds"
multiple multiple
:placeholder="t('organization.member.selectMemberScope')" :placeholder="t('organization.member.selectMemberScope')"
allow-clear allow-clear
> >
<a-option v-for="item of memberList" :key="item.value">{{ item.label }}</a-option> <a-option v-for="item of props.memberList" :key="item.id" :value="item.id">{{ item.name }}</a-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item <a-form-item
v-model="form.userGroups" field="userRoleIds"
field="userGroups"
:label="t('organization.member.tableColunmUsergroup')" :label="t('organization.member.tableColunmUsergroup')"
asterisk-position="end" asterisk-position="end"
:rules="[{ required: true }]" :rules="[{ required: true, message: t('organization.member.selectUserEmptyTip') }]"
> >
<a-select v-model="form.userGroups"> <a-select
<a-option v-for="item of userGroupOptions" :key="item.value">{{ item.label }}</a-option> v-model="form.userRoleIds"
multiple
allow-clear
:placeholder="t('organization.member.selectUserScope')"
>
<a-option v-for="item of props.userGroupOptions" :key="item.id" :value="item.id">{{ item.name }}</a-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-form> </a-form>
</div> </div>
<template #footer>
<a-space>
<a-button type="secondary" @click="handleCancel">{{ t('organization.member.Cancel') }}</a-button>
<a-button type="primary" :loading="confirmLoading" @click="handleOK">
{{ t('organization.member.Confirm') }}
</a-button>
</a-space>
</template>
</a-modal> </a-modal>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive } from 'vue'; import { ref, watchEffect, watch } from 'vue';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import type { AddMemberForm } from '@/models/setting/member'; import { FormInstance, Message, ValidatedError } from '@arco-design/web-vue';
import { useDialog } from '@/hooks/useDialog'; import { addOrUpdate } from '@/api/modules/setting/member';
import { useUserStore } from '@/store';
import type { AddorUpdateMemberModel, MemberItem, LinkList } from '@/models/setting/member';
const { t } = useI18n(); const { t } = useI18n();
const userStore = useUserStore();
const props = defineProps<{ const props = defineProps<{
visible: boolean; visible: boolean;
title?: string; memberList: LinkList;
userGroupOptions: LinkList;
projectList: LinkList;
}>(); }>();
const dialogVisible = ref<boolean>(false);
const title = ref<string>('');
const type = ref<string>('');
const emits = defineEmits<{ const emits = defineEmits<{
(event: 'update:visible', visible: boolean): void; (event: 'update:visible', visible: boolean): void;
(event: 'close'): void; (event: 'success'): void;
}>(); }>();
const { dialogVisible } = useDialog(props, emits);
const userGroupOptions = ref([
{
label: 'Beijing',
value: 'Beijing',
},
{
label: 'Shanghai',
value: 'Shanghai',
},
{
label: 'Guangzhou',
value: 'Guangzhou',
},
]);
const memberList = ref([ const confirmLoading = ref<boolean>(false);
{ const memberFormRef = ref<FormInstance | null>(null);
label: 'Beijing', const initFormValue = {
value: 'Beijing', organizationId: userStore.$state?.lastOrganizationId,
}, userRoleIds: [],
{ memberIds: [],
label: 'Shanghai', projectIds: [],
value: 'Shanghai', };
}, const form = ref<AddorUpdateMemberModel>({ ...initFormValue });
{
label: 'Guangzhou',
value: 'Guangzhou',
},
]);
const form = reactive<AddMemberForm>({
userGroups: '',
members: [],
});
const handleCancel = () => { const handleCancel = () => {
emits('close'); memberFormRef.value?.resetFields();
form.value = { ...initFormValue };
dialogVisible.value = false;
};
const edit = (record: MemberItem) => {
const { userRoleIdNameMap } = record;
form.value.memberIds = [record.id as string];
// form.value.userRoleIds = userRoleIdNameMap.map((item) => item.id);
form.value.userRoleIds = Object.keys(userRoleIdNameMap);
}; };
const handleOK = () => { const handleOK = () => {
handleCancel(); memberFormRef.value?.validate(async (errors: undefined | Record<string, ValidatedError>) => {
if (!errors) {
try {
confirmLoading.value = true;
let params = {};
const { organizationId, memberIds, userRoleIds, projectIds } = form.value;
params = Object.assign(params, {
organizationId,
userRoleIds,
});
params =
type.value === 'add'
? {
...params,
memberIds,
}
: {
...params,
projectIds,
};
await addOrUpdate(params, type.value);
Message.success(
type.value === 'add'
? t('organization.member.batchModalSuccess')
: t('organization.member.batchUpdateSuccess')
);
handleCancel();
emits('success');
} catch (error) {
console.log(error);
} finally {
confirmLoading.value = false;
}
} else {
return false;
}
});
}; };
watchEffect(() => {
dialogVisible.value = props.visible;
});
watch(
() => dialogVisible.value,
(val) => {
emits('update:visible', val);
}
);
defineExpose({
title,
type,
edit,
});
</script> </script>
<style lang="less" scoped></style> <style lang="less" scoped></style>
@/models/setting/member

View File

@ -1,31 +1,86 @@
<template> <template>
<div> <MsCard simple>
<div class="mb-4 flex items-center justify-between"> <div class="mb-4 flex items-center justify-between">
<div> <div>
<a-button class="mr-3" type="primary" @click="AddMember">{{ t('organization.member.addMember') }}</a-button> <a-button class="mr-3" type="primary" @click="addMember">{{ t('organization.member.addMember') }}</a-button>
</div> </div>
<a-input-search :placeholder="t('organization.member.searchMember')" class="w-[230px]"></a-input-search> <a-input-search
v-model="keyword"
:placeholder="t('organization.member.searchMember')"
class="w-[230px]"
@search="searchHandler"
@press-enter="searchHandler"
></a-input-search>
</div> </div>
<ms-base-table <ms-base-table
v-bind="propsRes" v-bind="propsRes"
:action-config="tableBatchActions" :action-config="tableBatchActions"
@selected-change="handleTableSelect" @selected-change="handleTableSelect"
v-on="propsEvent" v-on="propsEvent"
@batch-action="handelTableBatch"
> >
<template #project="{ record }"> <template #project="{ record }">
<a-tag v-for="pro of record.projectList.slice(0, 2)" :key="pro.id" class="mr-[4px] bg-transparent" bordered> <div v-if="!record.showProjectSelect">
{{ pro.name }} <!-- <a-tag
</a-tag> v-for="pro of record.projectIdNameMap"
:key="pro.id"
class="mr-[4px] bg-transparent"
bordered
@click="changeUserOrProject(record, 'project')"
> -->
<a-tag
v-for="pro of 3"
:key="pro"
class="mr-[4px] bg-transparent"
bordered
@click="changeUserOrProject(record, 'project')"
>
{{ pro }}
</a-tag>
</div>
<a-select
v-else
v-model="record.selectProjectList"
multiple
:max-tag-count="2"
size="small"
@change="(value) => selectUserOrProject(value, record, 'project')"
@popup-visible-change="visibleChange($event, record, 'project')"
>
<a-option v-for="item of projectList" :key="item.id" :value="item.id">{{ item.name }}</a-option>
</a-select>
</template> </template>
<template #userRole="{ record }"> <template #userRole="{ record }">
<a-tag <div v-if="!record.showUserSelect">
v-for="org of record.userRoleList.slice(0, 2)" <a-tag
:key="org.id" v-for="org of Object.keys(record.userRoleIdNameMap).slice(0, 3)"
class="mr-[4px] border-[rgb(var(--primary-5))] bg-transparent !text-[rgb(var(--primary-5))]" :key="org"
bordered class="mr-[4px] border-[rgb(var(--primary-5))] bg-transparent !text-[rgb(var(--primary-5))]"
bordered
@click="changeUserOrProject(record, 'user')"
>
{{ record.userRoleIdNameMap[org] }}
</a-tag>
<a-tag
v-if="Object.keys(record.userRoleIdNameMap).length > 3"
class="mr-[4px] border-[rgb(var(--primary-5))] bg-transparent !text-[rgb(var(--primary-5))]"
bordered
@click="changeUserOrProject(record, 'user')"
>
+{{ Object.keys(record.userRoleIdNameMap).length - 3 }}
</a-tag>
</div>
<a-select
v-else
v-model="record.selectUserList"
multiple
:max-tag-count="2"
@change="(value) => selectUserOrProject(value, record, 'user')"
@popup-visible-change="(value) => visibleChange(value, record, 'user')"
> >
{{ org.name }} <a-option v-for="item of userGroupOptions" :key="item.id" :value="item.id">{{ item.name }}</a-option>
</a-tag> </a-select>
</template> </template>
<template #enable="{ record }"> <template #enable="{ record }">
<div v-if="record.enable" class="flex items-center"> <div v-if="record.enable" class="flex items-center">
@ -37,67 +92,113 @@
{{ t('organization.member.tableDisable') }} {{ t('organization.member.tableDisable') }}
</div> </div>
</template> </template>
<template #action="{ record }"> <template #action="{ record }">
<MsButton @click="editMember(record)">{{ t('organization.member.edit') }}</MsButton> <MsButton @click="editMember(record)">{{ t('organization.member.edit') }}</MsButton>
<MsButton @click="deleteMember(record)">{{ t('organization.member.remove') }}</MsButton> <MsButton @click="deleteMember(record)">{{ t('organization.member.remove') }}</MsButton>
</template> </template>
</ms-base-table> </ms-base-table>
<!-- <add-member-modal :visible="addMemberVisible" @cancel="addMemberVisible = false" /> --> </MsCard>
</div> <AddMemberModal
ref="AddMemberRef"
v-model:visible="addMemberVisible"
:member-list="memberList"
:project-list="projectList"
:user-group-options="userGroupOptions"
@success="handlerOk"
/>
<MSBatchModal
v-if="treeData.length > 0"
v-model:visible="showBatchModal"
:table-selected="tableSelected"
:action="batchAction"
:tree-data="treeData"
@add-project="addProject"
@add-user-group="addUserGroup"
/>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref, reactive } from 'vue'; import { onMounted, ref } from 'vue';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue'; import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import MsButton from '@/components/pure/ms-button/index.vue'; import MsButton from '@/components/pure/ms-button/index.vue';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import useTable from '@/components/pure/ms-table/useTable'; import useTable from '@/components/pure/ms-table/useTable';
import addMemberModal from './components/addMemberModal.vue'; import AddMemberModal from './components/addMemberModal.vue';
import { getMemberList } from '@/api/modules/setting/member'; import MsCard from '@/components/pure/ms-card/index.vue';
import type { MsTableColumn } from '@/components/pure/ms-table/type'; import {
getMemberList,
deleteMemberReq,
addOrUpdate,
batchAddProject,
getProjectList,
getGlobalUserGroup,
getUser,
} from '@/api/modules/setting/member';
import useModal from '@/hooks/useModal'; import useModal from '@/hooks/useModal';
import { useCommandComponent } from '@/hooks/useCommandComponent'; import { TableKeyEnum } from '@/enums/tableEnum';
import MSBatchModal from '@/components/bussiness/ms-batch-modal/index.vue';
import { useTableStore, useUserStore } from '@/store';
import type { MsTableColumn } from '@/components/pure/ms-table/type';
import type { MemberItem, AddorUpdateMemberModel, LinkList, BatchAddProjectModel } from '@/models/setting/member';
const tableStore = useTableStore();
const { t } = useI18n();
const userStore = useUserStore();
const columns: MsTableColumn = [ const columns: MsTableColumn = [
{ {
title: 'organization.member.tableColunmEmail', title: 'organization.member.tableColunmEmail',
dataIndex: 'email', dataIndex: 'email',
width: 200, width: 200,
showDrag: false,
showInTable: true,
}, },
{ {
title: 'organization.member.tableColunmName', title: 'organization.member.tableColunmName',
dataIndex: 'name', dataIndex: 'name',
showDrag: false,
showInTable: true,
}, },
{ {
title: 'organization.member.tableColunmPhone', title: 'organization.member.tableColunmPhone',
dataIndex: 'phone', dataIndex: 'phone',
showDrag: false,
showInTable: true,
}, },
{ {
title: 'organization.member.tableColunmPro', title: 'organization.member.tableColunmPro',
slotName: 'project', slotName: 'project',
dataIndex: 'projectList', dataIndex: 'projectIdNameMap',
width: 250, width: 280,
showDrag: false,
showInTable: true,
}, },
{ {
title: 'organization.member.tableColunmUsergroup', title: 'organization.member.tableColunmUsergroup',
slotName: 'userRole', slotName: 'userRole',
dataIndex: 'userRoleList', dataIndex: 'userRoleIdNameMap',
width: 250, width: 250,
showDrag: false,
showInTable: true,
}, },
{ {
title: 'organization.member.tableColunmStatus', title: 'organization.member.tableColunmStatus',
slotName: 'enable', slotName: 'enable',
dataIndex: 'enable', dataIndex: 'enable',
showDrag: false,
showInTable: true,
}, },
{ {
title: 'organization.member.tableColunmActions', title: 'organization.member.tableColunmActions',
slotName: 'action', slotName: 'action',
fixed: 'right', fixed: 'right',
width: 110, width: 150,
showDrag: false,
showInTable: true,
}, },
]; ];
tableStore.initColumn(TableKeyEnum.ORGANNATIONMEMBER, columns, 'drawer');
const tableBatchActions = { const tableBatchActions = {
baseAction: [ baseAction: [
@ -111,32 +212,24 @@
}, },
], ],
}; };
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(getMemberList, {
const { propsRes, propsEvent, loadList, setKeyword } = useTable(getMemberList, { tableKey: TableKeyEnum.ORGANNATIONMEMBER,
columns, scroll: { y: 'auto', x: 1400 },
scroll: { y: 'auto', x: 1200 },
selectable: true, selectable: true,
}); });
const keyword = ref(''); const keyword = ref('');
const addMemberDialog = useCommandComponent(addMemberModal); const initData = async () => {
const addMembersOptions = reactive({ setLoadListParams({ keyword: keyword.value, organizationId: userStore.$state?.lastOrganizationId });
title: '添加成员',
visible: false,
onClose: () => {
addMemberDialog.close();
},
});
onMounted(async () => {
setKeyword(keyword.value);
await loadList(); await loadList();
}); };
const searchHandler = () => {
const { t } = useI18n(); initData();
};
const addMemberVisible = ref<boolean>(false);
const AddMemberRef = ref();
const tableSelected = ref<(string | number)[]>([]);
const { openModal } = useModal(); const { openModal } = useModal();
function deleteMember(record: MemberItem) {
function deleteMember(record: any) {
openModal({ openModal({
type: 'warning', type: 'warning',
title: t('organization.member.deleteMemberTip', { name: record.name }), title: t('organization.member.deleteMemberTip', { name: record.name }),
@ -148,26 +241,156 @@
}, },
onBeforeOk: async () => { onBeforeOk: async () => {
try { try {
await deleteMemberReq(record.lastOrganizationId, record.id);
Message.success(t('organization.member.deleteMemberSuccess')); Message.success(t('organization.member.deleteMemberSuccess'));
return true; initData();
} catch (error) { } catch (error) {
console.log(error); console.log(error);
return false;
} }
}, },
hideCancel: false, hideCancel: false,
}); });
} }
function editMember(record: any) {} function editMember(record: AddorUpdateMemberModel) {
const tableSelected = ref<(string | number)[]>([]); addMemberVisible.value = true;
AddMemberRef.value.type = 'edit';
AddMemberRef.value.edit(record);
}
function handleTableSelect(selectArr: (string | number)[]) { function handleTableSelect(selectArr: (string | number)[]) {
tableSelected.value = selectArr; tableSelected.value = selectArr;
} }
function AddMember() { function addMember() {
addMembersOptions.visible = true; addMemberVisible.value = true;
addMemberDialog(addMembersOptions); AddMemberRef.value.type = 'add';
} }
function handlerOk() {
initData();
}
const showBatchModal = ref(false);
const batchAction = ref('');
const treeData = ref([]);
const getProjectTreeData = async () => {
treeData.value = [];
try {
const result = await getProjectList({
current: 1,
pageSize: 10,
organizationId: userStore.$state?.lastOrganizationId,
});
treeData.value = result.list.map((item: any) => {
return {
title: item.name,
key: item.id,
};
});
} catch (error) {
console.log(error);
}
};
const getUserTreeData = async () => {
treeData.value = [];
};
//
const addProject = async (target: string[]) => {
try {
const params: BatchAddProjectModel = {
organizationId: userStore.$state?.lastOrganizationId,
memberIds: tableSelected.value,
projectIds: target,
};
await batchAddProject(params);
Message.success(t('organization.member.batchModalSuccess'));
initData();
} catch (error) {
console.log(error);
}
};
//
function addUserGroup() {}
function handelTableBatch(actionItem: any) {
showBatchModal.value = true;
batchAction.value = actionItem.eventTag;
if (actionItem.eventTag === 'batchAddProject') getProjectTreeData();
if (actionItem.eventTag === 'batchAddUsergroup') getUserTreeData();
}
//
const updateUserOrProject = async (record: MemberItem) => {
try {
const params = {
organizationId: record.lastOrganizationId,
projectIds: [...record.selectProjectList],
userRoleIds: [...record.selectUserList],
};
await addOrUpdate(params, 'edit');
Message.success(t('organization.member.batchModalSuccess'));
initData();
} catch (error) {
console.log(error);
} finally {
record.showUserSelect = false;
record.showProjectSelect = false;
}
};
//
const changeUserOrProject = (record: MemberItem, type: string) => {
if (type === 'project') {
record.showProjectSelect = true;
record.showUserSelect = false;
} else {
record.showUserSelect = true;
record.showProjectSelect = false;
}
record.selectProjectList = Object.keys(record.projectIdNameMap || {});
record.selectUserList = Object.keys(record.userRoleIdNameMap || {});
};
//
const selectUserOrProject = (value: any, record: MemberItem, type: string) => {
if (type === 'project') {
record.selectProjectList = value;
} else {
record.selectUserList = value;
}
};
//
const visibleChange = (visible: boolean, record: MemberItem, type: string) => {
if (!visible) {
if (type === 'user' && record.selectUserList.length < 1) {
Message.warning(t('organization.member.selectUserEmptyTip'));
return;
}
if (type === 'project' && record.selectProjectList.length < 1) {
Message.warning(t('organization.member.selectProjectScope'));
return;
}
updateUserOrProject(record);
}
};
const memberList = ref<LinkList>([]);
const userGroupOptions = ref<LinkList>([]);
const projectList = ref<LinkList>([
{
id: '1',
name: '项目一',
},
{
id: '2',
name: '项目二',
},
]);
const getUserAndGroupList = async () => {
try {
const userGroupData = await getGlobalUserGroup();
const userData = await getUser();
userGroupOptions.value = userGroupData.filter((item: any) => item.type === 'ORGANIZATION');
memberList.value = userData;
} catch (error) {
console.log(error);
}
};
onMounted(() => {
getUserAndGroupList();
initData();
});
</script> </script>
<style lang="less" scoped></style> <style lang="less" scoped></style>
@/api/modules/setting/member

View File

@ -1,5 +1,6 @@
export default { export default {
'organization.member.addMember': 'Add Member', 'organization.member.addMember': 'Add Member',
'organization.member.updateMember': 'Update Member',
'organization.member.searchMember': 'Search by name or email address', 'organization.member.searchMember': 'Search by name or email address',
'organization.member.remove': 'Remove', 'organization.member.remove': 'Remove',
'organization.member.edit': 'Edit', 'organization.member.edit': 'Edit',
@ -16,12 +17,19 @@ export default {
'organization.member.tableColunmActions': 'Actions', 'organization.member.tableColunmActions': 'Actions',
'organization.member.member': 'Member', 'organization.member.member': 'Member',
'organization.member.selectMemberScope': 'Select the member you want to add. Multiple selection is supported', 'organization.member.selectMemberScope': 'Select the member you want to add. Multiple selection is supported',
'organization.member.pleaseSelectMember': 'Please select user', 'organization.member.selectProjectScope': 'Select the project you want to add. Multiple selection is supported',
'organization.member.Save': 'Save', 'organization.member.selectMemberEmptyTip': 'The member can not be empty',
'organization.member.deleteMemberTip': 'Are you sure to delete the user `{name}` ?', 'organization.member.selectProjectEmptyTip': 'The project can not be empty',
'organization.member.selectUserEmptyTip': 'The user group can not be empty',
'organization.member.Confirm': 'Confirm',
'organization.member.Cancel': 'Cancel',
'organization.member.deleteMemberTip': 'Are you sure to remove the user `{name}` ?',
'system.user.deleteUserTip': 'Are you sure to delete the user `{name}` ?', 'system.user.deleteUserTip': 'Are you sure to delete the user `{name}` ?',
'organization.member.deleteMemberConfirm': 'Delete', 'organization.member.deleteMemberConfirm': 'Delete',
'organization.member.deleteMemberCancel': 'Cancel', 'organization.member.deleteMemberCancel': 'Cancel',
'organization.member.deleteMemberSuccess': 'Delete successful', 'organization.member.deleteMemberSuccess': 'Delete successful',
'organization.member.batchModalSuccess': 'Successfully added', 'organization.member.batchModalSuccess': 'Successfully added',
'organization.member.batchUpdateSuccess': 'Successfully updated',
'organization.member.proejct': 'Project',
'organization.member.selectUserScope': 'Please select a user group for the above members',
}; };

View File

@ -1,5 +1,6 @@
export default { export default {
'organization.member.addMember': '添加成员', 'organization.member.addMember': '添加成员',
'organization.member.updateMember': '更新成员',
'organization.member.searchMember': '通过名称或邮箱搜索搜索', 'organization.member.searchMember': '通过名称或邮箱搜索搜索',
'organization.member.remove': '移除', 'organization.member.remove': '移除',
'organization.member.edit': '编辑', 'organization.member.edit': '编辑',
@ -16,11 +17,18 @@ export default {
'organization.member.tableColunmActions': '操作', 'organization.member.tableColunmActions': '操作',
'organization.member.member': '成员', 'organization.member.member': '成员',
'organization.member.selectMemberScope': '请选择需要添加的成员支持多选', 'organization.member.selectMemberScope': '请选择需要添加的成员支持多选',
'organization.member.pleaseSelectMember': '请选择成员', 'organization.member.selectProjectScope': '请选择需要添加的项目支持多选',
'organization.member.Save': '保存', 'organization.member.selectMemberEmptyTip': '成员不能为空',
'organization.member.deleteMemberTip': '确认删除 `{name}` 这个成员吗?', 'organization.member.selectProjectEmptyTip': '项目不能为空',
'organization.member.selectUserEmptyTip': '用户组不能为空',
'organization.member.Confirm': '确定',
'organization.member.Cancel': '取消',
'organization.member.deleteMemberTip': '确认移除 `{name}` 这个成员吗?',
'organization.member.deleteMemberConfirm': '确认删除', 'organization.member.deleteMemberConfirm': '确认删除',
'organization.member.deleteMemberCancel': '取消', 'organization.member.deleteMemberCancel': '取消',
'organization.member.deleteMemberSuccess': '删除成功', 'organization.member.deleteMemberSuccess': '删除成功',
'organization.member.batchModalSuccess': '添加成功', 'organization.member.batchModalSuccess': '添加成功',
'organization.member.batchUpdateSuccess': '更新成功',
'organization.member.proejct': '项目',
'organization.member.selectUserScope': '请为以上成员选择用户组',
}; };