feat(系统设置): 系统组织与用户修改组织&撤销删除

This commit is contained in:
RubyLiu 2023-08-15 14:58:34 +08:00 committed by 刘瑞斌
parent e5fa9b4383
commit f536854655
9 changed files with 162 additions and 21 deletions

View File

@ -2,17 +2,28 @@ 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 { AddUserToOrgOrProjectParams } from '@/models/setting/systemOrg'; import { AddUserToOrgOrProjectParams } from '@/models/setting/systemOrg';
import { CreateOrUpdateSystemOrgParams } from '@/models/setting/system/orgAndProject';
// 获取组织列表 // 获取组织列表
export function postOrgTable(data: TableQueryParams) { export function postOrgTable(data: TableQueryParams) {
return MSR.post({ url: orgUrl.postOrgTableUrl, data }); return MSR.post({ url: orgUrl.postOrgTableUrl, data });
} }
// 创建或修改组织
export function createOrUpdateOrg(data: CreateOrUpdateSystemOrgParams) {
return MSR.post({ url: data.id ? orgUrl.postModifyOrgUrl : orgUrl.postAddOrgUrl, data });
}
// 删除组织 // 删除组织
export function deleteOrg(id: string) { export function deleteOrg(id: string) {
return MSR.get({ url: `${orgUrl.getDeleteOrgUrl}${id}` }); return MSR.get({ url: `${orgUrl.getDeleteOrgUrl}${id}` });
} }
// 撤销删除组织
export function revokeDeleteOrg(id: string) {
return MSR.get({ url: `${orgUrl.getRecoverOrgUrl}${id}` });
}
// 启用或禁用组织 // 启用或禁用组织
export function enableOrDisableOrg(id: string, isEnable = true) { export function enableOrDisableOrg(id: string, isEnable = true) {
return MSR.get({ url: `${isEnable ? orgUrl.getEnableOrgUrl : orgUrl.getDisableOrgUrl}${id}` }); return MSR.get({ url: `${isEnable ? orgUrl.getEnableOrgUrl : orgUrl.getDisableOrgUrl}${id}` });

View File

@ -12,6 +12,13 @@ export default {
'common.retry': 'Retry', 'common.retry': 'Retry',
'common.end': 'End', 'common.end': 'End',
'common.enable': 'Enable', 'common.enable': 'Enable',
'common.close': 'Close',
'common.confirmEnable': 'Confirm enable',
'common.confirmClose': 'Confirm close',
'common.enableSuccess': 'Enable success',
'common.enableFailed': 'Enable failed',
'common.closeSuccess': 'Close success',
'common.closeFailed': 'Close failed',
'common.all': 'All', 'common.all': 'All',
'common.operation': 'Operation', 'common.operation': 'Operation',
'common.remove': 'Remove', 'common.remove': 'Remove',
@ -30,4 +37,6 @@ export default {
'common.removeSuccess': 'Remove success', 'common.removeSuccess': 'Remove success',
'common.removeFailed': 'Remove failed', 'common.removeFailed': 'Remove failed',
'common.admin': 'Admin', 'common.admin': 'Admin',
'common.revokeDelete': 'Revoke delete',
'common.revokeDeleteSuccess': 'Revoke delete success',
}; };

View File

@ -12,6 +12,13 @@ export default {
'common.retry': '重试', 'common.retry': '重试',
'common.end': '结束', 'common.end': '结束',
'common.enable': '启用', 'common.enable': '启用',
'common.close': '关闭',
'common.confirmEnable': '确认启用',
'common.confirmClose': '确认关闭',
'common.enableSuccess': '启用成功',
'common.enableFailed': '启用失败',
'common.closeSuccess': '关闭成功',
'common.closeFailed': '关闭失败',
'common.all': '全部', 'common.all': '全部',
'common.operation': '操作', 'common.operation': '操作',
'common.remove': '移除', 'common.remove': '移除',
@ -30,4 +37,6 @@ export default {
'common.removeSuccess': '移除成功', 'common.removeSuccess': '移除成功',
'common.removeFailed': '移除失败', 'common.removeFailed': '移除失败',
'common.admin': '管理员', 'common.admin': '管理员',
'common.revokeDelete': '撤销删除',
'common.revokeDeleteSuccess': '已撤销删除',
}; };

View File

@ -0,0 +1,6 @@
export interface CreateOrUpdateSystemOrgParams {
id?: string;
name: string;
description: string;
memberIds: string[];
}

View File

@ -19,7 +19,10 @@
<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>
<a-form-item field="name" :label="t('system.organization.organizationAdmin')"> <a-form-item field="name" :label="t('system.organization.organizationAdmin')">
<MsUserSelector v-model:value="form.admin" placeholder="system.organization.organizationAdminPlaceholder" /> <MsUserSelector
v-model:value="form.memberIds"
placeholder="system.organization.organizationAdminPlaceholder"
/>
</a-form-item> </a-form-item>
<a-form-item field="description" :label="t('system.organization.description')"> <a-form-item field="description" :label="t('system.organization.description')">
<a-input v-model="form.description" :placeholder="t('system.organization.descriptionPlaceholder')" /> <a-input v-model="form.description" :placeholder="t('system.organization.descriptionPlaceholder')" />
@ -34,11 +37,14 @@
import { reactive, ref, watchEffect } from 'vue'; import { reactive, ref, watchEffect } from 'vue';
import type { FormInstance, ValidatedError } from '@arco-design/web-vue'; import type { FormInstance, ValidatedError } from '@arco-design/web-vue';
import MsUserSelector from '@/components/bussiness/ms-user-selector/index.vue'; import MsUserSelector from '@/components/bussiness/ms-user-selector/index.vue';
import { createOrUpdateOrg } from '@/api/modules/setting/system/organizationAndProject';
import { Message } from '@arco-design/web-vue';
import { CreateOrUpdateSystemOrgParams } from '@/models/setting/system/orgAndProject';
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps<{ const props = defineProps<{
visible: boolean; visible: boolean;
organizationId?: string; currentOrganization?: CreateOrUpdateSystemOrgParams;
}>(); }>();
const formRef = ref<FormInstance>(); const formRef = ref<FormInstance>();
@ -47,9 +53,9 @@
(e: 'cancel'): void; (e: 'cancel'): void;
}>(); }>();
const form = reactive({ const form = reactive<{ name: string; memberIds: string[]; description: string }>({
name: '', name: '',
admin: [], memberIds: [],
description: '', description: '',
}); });
@ -63,11 +69,31 @@
}; };
const handleBeforeOk = () => { const handleBeforeOk = () => {
formRef.value?.validate((errors: undefined | Record<string, ValidatedError>) => { formRef.value?.validate(async (errors: undefined | Record<string, ValidatedError>) => {
if (errors) { if (errors) {
return false; return false;
} }
try {
await createOrUpdateOrg({ id: props.currentOrganization?.id, ...form });
Message.success(
props.currentOrganization?.id
? t('system.organization.updateOrganizationSuccess')
: t('system.organization.createOrganizationSuccess')
);
handleCancel();
return true; return true;
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
return false;
}
}); });
}; };
watchEffect(() => {
if (props.currentOrganization) {
form.name = props.currentOrganization.name;
form.memberIds = props.currentOrganization.memberIds;
form.description = props.currentOrganization.description;
}
});
</script> </script>

View File

@ -1,5 +1,15 @@
<template> <template>
<MsBaseTable v-bind="propsRes" v-on="propsEvent"> <MsBaseTable v-bind="propsRes" v-on="propsEvent">
<template #name="{ record }">
<span>{{ record.name }}</span>
<a-tooltip background-color="#FFFFFF">
<template #content>
<span class="text-[var(--color-text-1)]">{{ t('system.organization.revokeDeleteToolTip') }}</span>
<MsButton class="ml-[8px]" @click="handleRevokeDelete(record)">{{ t('common.revokeDelete') }}</MsButton>
</template>
<MsIcon v-if="record.deleted" type="icon-icon_alarm_clock" class="ml-[4px] text-[rgb(var(--danger-6))]" />
</a-tooltip>
</template>
<template #creator="{ record }"> <template #creator="{ record }">
<span>{{ record.createUser }}</span> <span>{{ record.createUser }}</span>
<span v-if="record.orgAdmins.length > 0" class="text-[var(--color-text-4)]">{{ <span v-if="record.orgAdmins.length > 0" class="text-[var(--color-text-4)]">{{
@ -13,7 +23,10 @@
<span class="primary-color" @click="showProjectDrawer(record)">{{ record.projectCount }}</span> <span class="primary-color" @click="showProjectDrawer(record)">{{ record.projectCount }}</span>
</template> </template>
<template #operation="{ record }"> <template #operation="{ record }">
<template v-if="!record.enable"> <template v-if="record.deleted">
<MsButton @click="handleRevokeDelete(record)">{{ t('common.revokeDelete') }}</MsButton>
</template>
<template v-else-if="!record.enable">
<MsButton @click="handleEnableOrDisableOrg(record)">{{ t('common.enable') }}</MsButton> <MsButton @click="handleEnableOrDisableOrg(record)">{{ t('common.enable') }}</MsButton>
<MsButton @click="handleDelete(record)">{{ t('common.delete') }}</MsButton> <MsButton @click="handleDelete(record)">{{ t('common.delete') }}</MsButton>
</template> </template>
@ -25,7 +38,12 @@
</template> </template>
</template> </template>
</MsBaseTable> </MsBaseTable>
<AddOrganizationModal type="edit" :visible="orgVisible" @cancel="handleAddOrgModalCancel" /> <AddOrganizationModal
type="edit"
:current-organization="currentUpdateOrganization"
:visible="orgVisible"
@cancel="handleAddOrgModalCancel"
/>
<AddUserModal :organization-id="currentOrganizationId" :visible="userVisible" @cancel="handleAddUserModalCancel" /> <AddUserModal :organization-id="currentOrganizationId" :visible="userVisible" @cancel="handleAddUserModalCancel" />
<ProjectDrawer v-bind="currentProjectDrawer" @cancel="handleProjectDrawerCancel" /> <ProjectDrawer v-bind="currentProjectDrawer" @cancel="handleProjectDrawerCancel" />
<UserDrawer v-bind="currentUserDrawer" @cancel="handleUserDrawerCancel" /> <UserDrawer v-bind="currentUserDrawer" @cancel="handleUserDrawerCancel" />
@ -38,7 +56,12 @@
import { useTableStore } from '@/store'; import { useTableStore } from '@/store';
import { ref, reactive, watch } from 'vue'; import { ref, reactive, watch } from 'vue';
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types'; import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import { postOrgTable, deleteOrg, enableOrDisableOrg } from '@/api/modules/setting/system/organizationAndProject'; import {
postOrgTable,
deleteOrg,
enableOrDisableOrg,
revokeDeleteOrg,
} from '@/api/modules/setting/system/organizationAndProject';
import { TableKeyEnum } from '@/enums/tableEnum'; import { TableKeyEnum } from '@/enums/tableEnum';
import { MsTableColumn } from '@/components/pure/ms-table/type'; import { MsTableColumn } from '@/components/pure/ms-table/type';
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue'; import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
@ -49,6 +72,7 @@
import UserDrawer from './userDrawer.vue'; import UserDrawer from './userDrawer.vue';
import AddUserModal from './addUserModal.vue'; import AddUserModal from './addUserModal.vue';
import useModal from '@/hooks/useModal'; import useModal from '@/hooks/useModal';
import { CreateOrUpdateSystemOrgParams } from '@/models/setting/system/orgAndProject';
export interface SystemOrganizationProps { export interface SystemOrganizationProps {
keyword: string; keyword: string;
@ -61,18 +85,19 @@
const userVisible = ref(false); const userVisible = ref(false);
const orgVisible = ref(false); const orgVisible = ref(false);
const currentOrganizationId = ref(''); const currentOrganizationId = ref('');
const { deleteModal } = useModal(); const currentUpdateOrganization = ref<CreateOrUpdateSystemOrgParams>();
const { deleteModal, openModal } = useModal();
const organizationColumns: MsTableColumn = [ const organizationColumns: MsTableColumn = [
{ {
title: 'system.organization.ID', title: 'system.organization.ID',
dataIndex: 'num', dataIndex: 'num',
width: 100, width: 100,
ellipsis: true,
}, },
{ {
title: 'system.organization.name', title: 'system.organization.name',
dataIndex: 'name', slotName: 'name',
editable: true,
}, },
{ {
title: 'system.organization.member', title: 'system.organization.member',
@ -168,19 +193,38 @@
}; };
const handleEnableOrDisableOrg = async (record: any, isEnable = true) => { const handleEnableOrDisableOrg = async (record: any, isEnable = true) => {
const title = isEnable ? t('system.organization.enableTitle') : t('system.organization.enableTitle');
const content = isEnable ? t('system.organization.enableContent') : t('system.organization.endContent');
const okText = isEnable ? t('common.confirmEnable') : t('common.confirmClose');
openModal({
type: 'warning',
cancelText: t('common.cancel'),
title,
content,
okText,
onBeforeOk: async () => {
try { try {
await enableOrDisableOrg(record.id, isEnable); await enableOrDisableOrg(record.id, isEnable);
Message.success(t('common.updateSuccess')); Message.success(isEnable ? t('common.enableSuccess') : t('common.closeSuccess'));
fetchData(); fetchData();
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error(error); console.error(error);
} }
},
hideCancel: false,
});
}; };
const showOrganizationModal = (record: any) => { const showOrganizationModal = (record: any) => {
currentOrganizationId.value = record.id; currentOrganizationId.value = record.id;
orgVisible.value = true; orgVisible.value = true;
currentUpdateOrganization.value = {
id: record.id,
name: record.name,
description: record.description,
memberIds: record.orgAdmins.map((item: any) => item.id) || [],
};
}; };
const showAddUserModal = (record: any) => { const showAddUserModal = (record: any) => {
@ -190,6 +234,7 @@
const handleProjectDrawerCancel = () => { const handleProjectDrawerCancel = () => {
currentProjectDrawer.visible = false; currentProjectDrawer.visible = false;
fetchData();
}; };
const showProjectDrawer = (record: TableData) => { const showProjectDrawer = (record: TableData) => {
@ -205,13 +250,27 @@
const handleUserDrawerCancel = () => { const handleUserDrawerCancel = () => {
currentUserDrawer.visible = false; currentUserDrawer.visible = false;
fetchData();
}; };
const handleAddUserModalCancel = () => { const handleAddUserModalCancel = () => {
userVisible.value = false; userVisible.value = false;
fetchData();
}; };
const handleAddOrgModalCancel = () => { const handleAddOrgModalCancel = () => {
orgVisible.value = false; orgVisible.value = false;
fetchData();
};
const handleRevokeDelete = async (record: TableData) => {
try {
await revokeDeleteOrg(record.id);
Message.success(t('common.revokeDeleteSuccess'));
fetchData();
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
}
}; };
watch( watch(

View File

@ -21,6 +21,12 @@
></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 #name="{ record }">
<span>{{ record.name }}</span>
<span v-if="record.adminFlag" class="ml-[4px] text-[var(--color-text-4)]">{{
`(${t('common.admin')})`
}}</span>
</template>
<template #operation="{ record }"> <template #operation="{ record }">
<MsRemoveButton <MsRemoveButton
:title="t('system.organization.removeName', { name: record.name })" :title="t('system.organization.removeName', { name: record.name })"
@ -68,7 +74,7 @@
const projectColumn: MsTableColumn = [ const projectColumn: MsTableColumn = [
{ {
title: 'system.organization.userName', title: 'system.organization.userName',
dataIndex: 'name', slotName: 'name',
}, },
{ {
title: 'system.organization.email', title: 'system.organization.email',

View File

@ -43,4 +43,12 @@ export default {
'system.organization.deleteName': 'Are you sure to delete {name}', 'system.organization.deleteName': 'Are you sure to delete {name}',
'system.organization.deleteTip': 'system.organization.deleteTip':
'Delete the organization and delete the project data under that organization together. Please be cautious!', 'Delete the organization and delete the project data under that organization together. Please be cautious!',
'system.organization.revokeDeleteToolTip': 'The organization will be deleted automatically after 30 days',
'system.organization.createOrganizationSuccess': 'Create organization success',
'system.organization.enableTitle': 'Start organization',
'system.organization.endTitle': 'Close organization',
'system.organization.enableContent': 'The organization after opening is displayed in the organization switching list',
'system.organization.endContent':
'The organization after closing is not displayed in the organization switching list',
'system.organization.updateOrganizationSuccess': 'Update organization success',
}; };

View File

@ -40,4 +40,11 @@ export default {
'system.organization.addSuccess': '添加成功', 'system.organization.addSuccess': '添加成功',
'system.organization.deleteName': '确认删除 {name} 这个组织吗', 'system.organization.deleteName': '确认删除 {name} 这个组织吗',
'system.organization.deleteTip': '删除组织同时将该组织下的项目数据一起删除,请谨慎操作!', 'system.organization.deleteTip': '删除组织同时将该组织下的项目数据一起删除,请谨慎操作!',
'system.organization.revokeDeleteToolTip': '该组织将与 30 天后自动删除',
'system.organization.createOrganizationSuccess': '创建组织成功',
'system.organization.enableTitle': '开启组织',
'system.organization.endTitle': '关闭组织',
'system.organization.enableContent': '开启后的组织展示在组织切换列表',
'system.organization.endContent': '关闭后的组织不展示在组织切换列表',
'system.organization.updateOrganizationSuccess': '更新组织成功',
}; };