feat(系统设置): 组织对接添加成员接口

This commit is contained in:
RubyLiu 2023-08-11 18:38:41 +08:00 committed by fit2-zhao
parent 5db28cfb25
commit 146e6996ce
17 changed files with 246 additions and 51 deletions

View File

@ -1,6 +1,7 @@
import MSR from '@/api/http/index'; import MSR from '@/api/http/index';
import * as orgUrl from '@/api/requrls/setting/system/organizationAndProject'; import * as orgUrl from '@/api/requrls/setting/system/organizationAndProject';
import { TableQueryParams } from '@/models/common'; import { TableQueryParams } from '@/models/common';
import { AddUserToOrgOrPorjectParams } from '@/models/setting/systemOrg';
// 获取组织列表 // 获取组织列表
export function postOrgTable(data: TableQueryParams) { export function postOrgTable(data: TableQueryParams) {
@ -21,3 +22,11 @@ export function postProjectTableByOrgId(data: TableQueryParams) {
export function postUserTableByOrgId(data: TableQueryParams) { export function postUserTableByOrgId(data: TableQueryParams) {
return MSR.post({ url: orgUrl.postOrgMemberUrl, data }); return MSR.post({ url: orgUrl.postOrgMemberUrl, data });
} }
// 给组织或项目添加成员
export function addUserToOrgOrProject(data: AddUserToOrgOrPorjectParams) {
return MSR.post({ url: data.projectId ? orgUrl.postAddProjectMemberUrl : orgUrl.postAddOrgMemberUrl, data });
}
// 获取用户下拉选项
export function getUserByOrganizationOrProject(sourceId: string) {
return MSR.get({ url: `${orgUrl.getUserByOrgOrProjectUrl}${sourceId}` });
}

View File

@ -11,7 +11,7 @@ export const postOrgMemberUrl = '/system/organization/list-member';
// 添加组织 // 添加组织
export const postAddOrgUrl = '/system/organization/add'; export const postAddOrgUrl = '/system/organization/add';
// 添加组织成员 // 添加组织成员
export const postAddOrgMemberUrl = '/system/organization/member/add'; export const postAddOrgMemberUrl = '/system/organization/add-member';
// 删除组织成员 // 删除组织成员
export const getDeleteOrgMemberUrl = '/system/organization/remove-member/'; export const getDeleteOrgMemberUrl = '/system/organization/remove-member/';
// 恢复组织 // 恢复组织
@ -35,7 +35,7 @@ export const postProjectMemberUrl = '/system/project/member/list';
// 添加项目 // 添加项目
export const postAddProjectUrl = '/system/project/add'; export const postAddProjectUrl = '/system/project/add';
// 添加项目成员 // 添加项目成员
export const postAddProjectMemberUrl = '/system/project/member/add'; export const postAddProjectMemberUrl = '/system/project/add-member';
// 撤销项目 // 撤销项目
export const getRevokeProjectUrl = '/system/project/revoke/'; export const getRevokeProjectUrl = '/system/project/revoke/';
// 移除项目成员 // 移除项目成员
@ -44,3 +44,5 @@ export const getDeleteProjectMemberUrl = '/system/project/remove-member/';
export const getProjectInfoUrl = '/system/project/get/'; export const getProjectInfoUrl = '/system/project/get/';
// 删除项目 // 删除项目
export const getDeleteProjectUrl = '/system/project/delete/'; export const getDeleteProjectUrl = '/system/project/delete/';
// 系统-组织及项目,获取用户下拉选项
export const getUserByOrgOrProjectUrl = '/system/user/get-option/';

View File

@ -1,10 +1,10 @@
<template> <template>
<a-select <a-select
:value="value" v-model="currentValue"
:disabled="props.disabled" :disabled="props.disabled"
multiple multiple
:virtual-list-props="{ height: 200 }" :virtual-list-props="{ height: 200 }"
:placeholder="props.placeholder ? t(props.placeholder) : t('common.pleaseSelect')" :placeholder="props.placeholder ? t(props.placeholder) : t('common.pleaseSelectMember')"
:options="userOptions" :options="userOptions"
:field-names="fieldNames" :field-names="fieldNames"
@search="handleSearch" @search="handleSearch"
@ -22,40 +22,66 @@
<script setup lang="ts"> <script setup lang="ts">
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { ref, onMounted } from 'vue'; import { ref, onMounted, watch } from 'vue';
import { getUserList } from '@/api/modules/setting/usergroup'; import { getUserList } from '@/api/modules/setting/usergroup';
import { getUserByOrganizationOrProject } from '@/api/modules/setting/system/organizationAndProject';
export interface MsUserSelectorProps { export interface MsUserSelectorProps {
value: string[]; value: string[];
disabled?: boolean; disabled?: boolean;
placeholder?: string; placeholder?: string;
type?: 'organization' | 'usergroup';
sourceId?: string;
disabledKey?: 'disabled' | 'memberFlag' | 'adminFlag';
} }
export interface UserItem { export interface UserItem {
id: string; id: string;
name: string; name: string;
email: string; email: string;
disabled?: boolean;
memberFlag?: boolean;
adminFlag?: boolean;
} }
const fieldNames = { value: 'id', label: 'name' }; const fieldNames = { value: 'id', label: 'name' };
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps<MsUserSelectorProps>(); const props = withDefaults(defineProps<MsUserSelectorProps>(), {
disabled: false,
type: 'usergroup',
disabledKey: 'disabled',
});
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:value', value: string[]): void; (e: 'update:value', value: string[]): void;
}>(); }>();
const currentValue = ref(props.value);
const allOption = ref<UserItem[]>([]); const allOption = ref<UserItem[]>([]);
const userOptions = ref<UserItem[]>([]); const userOptions = ref<UserItem[]>([]);
const initUserList = async () => { const initUserList = async () => {
const res = await getUserList(); let res: UserItem[] = [];
allOption.value = res; if (props.type === 'organization') {
userOptions.value = res; if (!props.sourceId) {
return;
}
res = await getUserByOrganizationOrProject(props.sourceId);
} else {
res = await getUserList();
}
res.forEach((item) => {
item.disabled = item[props.disabledKey];
});
allOption.value = [...res];
userOptions.value = [...res];
}; };
const handleSearch = (value: string) => { const handleSearch = (value: string) => {
if (value) { if (value) {
window.setTimeout(() => { window.setTimeout(() => {
userOptions.value = userOptions.value.filter((item) => item.name.includes(value)); userOptions.value = userOptions.value.filter(
(item) => item.name.includes(value) || currentValue.value.includes(item.id)
);
}, 60); }, 60);
} else { } else {
userOptions.value = allOption.value; userOptions.value = allOption.value;
@ -69,4 +95,10 @@
onMounted(() => { onMounted(() => {
initUserList(); initUserList();
}); });
watch(
() => props.value,
(value) => {
currentValue.value = value;
}
);
</script> </script>

View File

@ -0,0 +1,3 @@
export default {
'common.pleaseSelectMember': 'Please select member',
};

View File

@ -1,6 +1,7 @@
import dayjsLocale from 'dayjs/locale/en'; import dayjsLocale from 'dayjs/locale/en';
import localeSettings from './settings'; import localeSettings from './settings';
import sys from './sys'; import sys from './sys';
import common from './common';
const _Cmodules: any = import.meta.glob('../../components/**/locale/en-US.ts', { eager: true }); const _Cmodules: any = import.meta.glob('../../components/**/locale/en-US.ts', { eager: true });
const _Vmodules: any = import.meta.glob('../../views/**/locale/en-US.ts', { eager: true }); const _Vmodules: any = import.meta.glob('../../views/**/locale/en-US.ts', { eager: true });
@ -15,7 +16,7 @@ Object.keys(_Vmodules).forEach((key) => {
if (!defaultModule) return; if (!defaultModule) return;
result = { ...result, ...defaultModule }; result = { ...result, ...defaultModule };
}); });
console.log('result', result);
export default { export default {
message: { message: {
'menu.workplace': 'Workplace', 'menu.workplace': 'Workplace',
@ -45,6 +46,7 @@ export default {
...sys, ...sys,
...localeSettings, ...localeSettings,
...result, ...result,
...common,
}, },
dayjsLocale, dayjsLocale,
dayjsLocaleName: 'en-US', dayjsLocaleName: 'en-US',

View File

@ -0,0 +1,3 @@
export default {
'common.pleaseSelectMember': '请选择成员',
};

View File

@ -1,6 +1,7 @@
import dayjsLocale from 'dayjs/locale/zh-cn'; import dayjsLocale from 'dayjs/locale/zh-cn';
import localeSettings from './settings'; import localeSettings from './settings';
import sys from './sys'; import sys from './sys';
import common from './common';
const _Cmodules: any = import.meta.glob('../../components/**/locale/zh-CN.ts', { eager: true }); const _Cmodules: any = import.meta.glob('../../components/**/locale/zh-CN.ts', { eager: true });
const _Vmodules: any = import.meta.glob('../../views/**/locale/zh-CN.ts', { eager: true }); const _Vmodules: any = import.meta.glob('../../views/**/locale/zh-CN.ts', { eager: true });
@ -44,6 +45,7 @@ export default {
...sys, ...sys,
...localeSettings, ...localeSettings,
...result, ...result,
...common,
}, },
dayjsLocale, dayjsLocale,
dayjsLocaleName: 'zh-CN', dayjsLocaleName: 'zh-CN',

View File

@ -0,0 +1,7 @@
export interface AddUserToOrgOrPorjectParams {
userIds?: string[];
organizationId?: string;
projectId?: string;
// 等待接口改动 将要废弃以后用userIds
memberIds?: string[];
}

View File

@ -22,6 +22,9 @@ const msTableStore = defineStore('msTable', {
if (item.showDrag === undefined) { if (item.showDrag === undefined) {
item.showDrag = false; item.showDrag = false;
} }
if (item.showInTable === undefined) {
item.showInTable = true;
}
}); });
tmpMap.set(tableKey, { mode, column }); tmpMap.set(tableKey, { mode, column });
this.selectorColumnMap = tmpMap; this.selectorColumnMap = tmpMap;
@ -56,7 +59,7 @@ const msTableStore = defineStore('msTable', {
getShowInTableColumns(key: string): MsTableColumn { getShowInTableColumns(key: string): MsTableColumn {
if (this.selectorColumnMap.has(key)) { if (this.selectorColumnMap.has(key)) {
const tmpArr = this.selectorColumnMap.get(key)?.column; const tmpArr = this.selectorColumnMap.get(key)?.column;
return tmpArr?.filter((item) => !!item.showInTable) || []; return tmpArr?.filter((item) => item.showInTable) || [];
} }
return []; return [];
}, },

View File

@ -14,10 +14,7 @@
field="name" field="name"
required required
:label="t('system.organization.organizationName')" :label="t('system.organization.organizationName')"
:rules="[ :rules="[{ required: true, message: t('system.organization.organizationNameRequired') }]"
{ required: true, message: t('system.organization.organizationNameRequired') },
{ validator: validateName },
]"
> >
<a-input v-model="form.name" :placeholder="t('system.organization.organizationNamePlaceholder')" /> <a-input v-model="form.name" :placeholder="t('system.organization.organizationNamePlaceholder')" />
</a-form-item> </a-form-item>
@ -57,16 +54,6 @@
const currentVisible = ref(props.visible); const currentVisible = ref(props.visible);
const validateName = (value: string, callback: (error?: string) => void) => {
if (value !== '') {
// const isExist = props.list.some((item) => item.name === value);
// if (isExist) {
// callback(t('system.organization.userGroupNameIsExist', { name: value }));
// }
callback();
}
};
watchEffect(() => { watchEffect(() => {
currentVisible.value = props.visible; currentVisible.value = props.visible;
}); });

View File

@ -0,0 +1,104 @@
<template>
<a-modal
v-model:visible="currentVisible"
title-align="start"
class="ms-modal-form ms-modal-medium"
:ok-text="t('system.organization.addMember')"
unmount-on-close
@cancel="handleCancel"
>
<template #title> {{ t('system.organization.addMember') }} </template>
<div class="form">
<a-form ref="formRef" :model="form" size="large" :style="{ width: '600px' }" layout="vertical">
<a-form-item
field="name"
:label="t('system.organization.member')"
:rules="[{ required: true, message: t('system.organization.addMemberRequired') }]"
>
<MsUserSelector v-model:value="form.name" type="organization" :source-id="organizationId || projectId" />
</a-form-item>
</a-form>
</div>
<template #footer>
<a-button type="secondary" @click="handleCancel">
{{ t('system.organization.cancel') }}
</a-button>
<a-button type="primary" :loading="loading" :disabled="form.name.length === 0" @click="handleAddMember">
{{ t('system.organization.add') }}
</a-button>
</template>
</a-modal>
</template>
<script lang="ts" setup>
import { useI18n } from '@/hooks/useI18n';
import { reactive, ref, watchEffect, onUnmounted } from 'vue';
import { addUserToOrgOrProject } from '@/api/modules/setting/system/organizationAndProject';
import { Message, type FormInstance, type ValidatedError } from '@arco-design/web-vue';
import MsUserSelector from '@/components/bussiness/ms-user-selector/index.vue';
const { t } = useI18n();
const props = defineProps<{
visible: boolean;
organizationId?: string;
projectId?: string;
}>();
const emit = defineEmits<{
(e: 'cancel'): void;
(e: 'submit', value: string[]): 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 = () => {
form.name = [];
emit('cancel');
};
const handleAddMember = () => {
formRef.value?.validate(async (errors: undefined | Record<string, ValidatedError>) => {
loading.value = true;
if (errors) {
loading.value = false;
}
const { organizationId, projectId } = props;
try {
await addUserToOrgOrProject({ memberIds: form.name, organizationId, projectId });
Message.success(t('system.organization.addSuccess'));
loading.value = false;
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);
}
</style>

View File

@ -1,7 +1,7 @@
<template> <template>
<MsDrawer <MsDrawer
:width="680" :width="680"
:visible="props.visible" :visible="currentVisible"
unmount-on-close unmount-on-close
:footer="false" :footer="false"
:title="t('system.organization.projectName', { name: props.currentName })" :title="t('system.organization.projectName', { name: props.currentName })"
@ -39,9 +39,10 @@
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps<projectDrawerProps>(); const props = defineProps<projectDrawerProps>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:visible', v: boolean): void; (e: 'cancel', v: boolean): void;
}>(); }>();
const currentVisible = ref(props.visible);
const keyword = ref(''); const keyword = ref('');
const projectColumn: MsTableColumn = [ const projectColumn: MsTableColumn = [
@ -81,7 +82,7 @@
} }
const handleCancel = () => { const handleCancel = () => {
emit('update:visible', false); emit('cancel', false);
}; };
const fetchData = async () => { const fetchData = async () => {
@ -95,4 +96,10 @@
fetchData(); fetchData();
} }
); );
watch(
() => props.visible,
(visible) => {
currentVisible.value = visible;
}
);
</script> </script>

View File

@ -149,7 +149,7 @@
dataIndex: 'description', dataIndex: 'description',
}, },
{ {
title: 'system.organization.createUser', title: 'system.organization.creator',
dataIndex: 'createUser', dataIndex: 'createUser',
}, },
{ {

View File

@ -1,7 +1,7 @@
<template> <template>
<MsDrawer <MsDrawer
:width="680" :width="680"
:visible="props.visible" :visible="currentVisible"
unmount-on-close unmount-on-close
:footer="false" :footer="false"
:title="t('system.organization.addMember')" :title="t('system.organization.addMember')"
@ -20,9 +20,14 @@
@press-enter="searchUser" @press-enter="searchUser"
></a-input-search> ></a-input-search>
</div> </div>
<ms-base-table class="mt-[16px]" v-bind="propsRes" v-on="propsEvent" /> <ms-base-table class="mt-[16px]" v-bind="propsRes" v-on="propsEvent">
<template #operation="{ record }">
<ms-button @click="handleRemove(record)">{{ t('system.organization.remove') }}</ms-button>
</template>
</ms-base-table>
</div> </div>
</MsDrawer> </MsDrawer>
<AddUserModal :organization-id="props.organizationId" :visible="userVisible" @cancel="handleHideUserModal" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -33,6 +38,9 @@
import { watch, ref } from 'vue'; import { watch, ref } from 'vue';
import MsDrawer from '@/components/pure/ms-drawer/index.vue'; import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue'; import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import AddUserModal from './addUserModal.vue';
import { TableData } from '@arco-design/web-vue';
import MsButton from '@/components/pure/ms-button/index.vue';
export interface projectDrawerProps { export interface projectDrawerProps {
visible: boolean; visible: boolean;
@ -41,32 +49,29 @@
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps<projectDrawerProps>(); const props = defineProps<projectDrawerProps>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:visible', v: boolean): void; (e: 'cancel'): void;
}>(); }>();
const currentVisible = ref(props.visible);
const userVisible = ref(false);
const keyword = ref(''); const keyword = ref('');
const projectColumn: MsTableColumn = [ const projectColumn: MsTableColumn = [
{ {
title: 'system.organization.ID', title: 'system.organization.userName',
dataIndex: 'num',
},
{
title: 'system.project.name',
dataIndex: 'name', dataIndex: 'name',
}, },
{ {
title: 'system.organization.status', title: 'system.organization.email',
dataIndex: 'enable', dataIndex: 'email',
}, },
{ {
title: 'system.organization.creator', title: 'system.organization.phone',
dataIndex: 'createUser', dataIndex: 'phone',
},
{
title: 'system.organization.createTime',
dataIndex: 'createTime',
}, },
{ title: 'system.organization.operation', dataIndex: 'operation' },
]; ];
const { propsRes, propsEvent, loadList, setLoadListParams, setKeyword } = useTable(postUserTableByOrgId, { const { propsRes, propsEvent, loadList, setLoadListParams, setKeyword } = useTable(postUserTableByOrgId, {
@ -84,7 +89,7 @@
} }
const handleCancel = () => { const handleCancel = () => {
emit('update:visible', false); emit('cancel');
}; };
const fetchData = async () => { const fetchData = async () => {
@ -92,8 +97,17 @@
}; };
const handleAddMember = () => { const handleAddMember = () => {
// TODO add member userVisible.value = true;
emit('update:visible', false); };
const handleHideUserModal = () => {
userVisible.value = false;
};
const handleRemove = (record: TableData) => {
// TODO: remove user
// eslint-disable-next-line no-console
console.log(record);
}; };
watch( watch(
@ -103,4 +117,10 @@
fetchData(); fetchData();
} }
); );
watch(
() => props.visible,
(visible) => {
currentVisible.value = visible;
}
);
</script> </script>

View File

@ -6,6 +6,7 @@ export default {
'system.organization.edit': '编辑', 'system.organization.edit': '编辑',
'system.organization.save': '保存', 'system.organization.save': '保存',
'system.organization.end': '结束', 'system.organization.end': '结束',
'system.organization.remove': '移除',
'system.organization.addMember': '添加成员', 'system.organization.addMember': '添加成员',
'system.organization.addMemberPlaceholder': '请选择成员', 'system.organization.addMemberPlaceholder': '请选择成员',
'system.organization.addMemberRequired': '请选择成员', 'system.organization.addMemberRequired': '请选择成员',
@ -32,4 +33,7 @@ export default {
'system.organization.projectCount': '项目({count})', 'system.organization.projectCount': '项目({count})',
'system.organization.projectName': '项目({name})', 'system.organization.projectName': '项目({name})',
'system.project.name': '项目名称', 'system.project.name': '项目名称',
'system.organization.userName': '姓名',
'system.organization.email': '邮箱',
'system.organization.phone': '手机',
}; };

View File

@ -1,7 +1,9 @@
<template> <template>
<a-modal <a-modal
v-model:visible="currentVisible" v-model="currentVisible"
class="ms-modal-form ms-modal-medium"
width="680px" width="680px"
text-align="start"
:ok-text="t('system.userGroup.add')" :ok-text="t('system.userGroup.add')"
unmount-on-close unmount-on-close
:ok-loading="loading" :ok-loading="loading"

View File

@ -33,7 +33,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed } from 'vue'; import { ref, computed, watch } from 'vue';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import MsCard from '@/components/pure/ms-card/index.vue'; import MsCard from '@/components/pure/ms-card/index.vue';
import useUserGroupStore from '@/store/modules/setting/usergroup'; import useUserGroupStore from '@/store/modules/setting/usergroup';
@ -48,6 +48,14 @@
const store = useUserGroupStore(); const store = useUserGroupStore();
const couldShowUser = computed(() => store.userGroupInfo.currentType === 'SYSTEM'); const couldShowUser = computed(() => store.userGroupInfo.currentType === 'SYSTEM');
watch(
() => couldShowUser,
(val) => {
if (!val) {
currentTable.value = 'auth';
}
}
);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>