style(系统设置): 用户组页面样式优化
This commit is contained in:
parent
097ca36387
commit
e3fd24cdba
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<a-popover
|
||||
ref="popoverRef"
|
||||
:popup-visible="currentVisible"
|
||||
position="bl"
|
||||
trigger="click"
|
||||
|
@ -7,44 +8,45 @@
|
|||
:content-class="props.id ? 'move-left' : ''"
|
||||
>
|
||||
<template #content>
|
||||
<div class="form">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
size="large"
|
||||
layout="vertical"
|
||||
:label-col-props="{ span: 0 }"
|
||||
:wrapper-col-props="{ span: 24 }"
|
||||
>
|
||||
<a-form-item>
|
||||
<div class="text-[14px] text-[var(--color-text-1)]">{{
|
||||
<div v-outer="handleOutsideClick">
|
||||
<div class="form">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
size="large"
|
||||
layout="vertical"
|
||||
:label-col-props="{ span: 0 }"
|
||||
:wrapper-col-props="{ span: 24 }"
|
||||
>
|
||||
<div class="mb-[8px] text-[14px] font-medium text-[var(--color-text-1)]">{{
|
||||
props.id ? t('system.userGroup.rename') : t('system.userGroup.createUserGroup')
|
||||
}}</div>
|
||||
</a-form-item>
|
||||
<a-form-item field="name" :rules="[{ validator: validateName }]">
|
||||
<a-input
|
||||
v-model="form.name"
|
||||
class="w-[243px]"
|
||||
:placeholder="t('system.userGroup.pleaseInputUserGroupName')"
|
||||
@press-enter="handleBeforeOk"
|
||||
@keyup.esc="handleCancel"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
<div class="flex flex-row flex-nowrap justify-end gap-2">
|
||||
<a-button type="secondary" size="mini" :disabled="loading" @click="handleCancel">
|
||||
{{ t('common.cancel') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
size="mini"
|
||||
:loading="loading"
|
||||
:disabled="form.name.length === 0"
|
||||
@click="handleBeforeOk"
|
||||
>
|
||||
{{ props.id ? t('common.rename') : t('common.create') }}
|
||||
</a-button>
|
||||
<a-form-item field="name" :rules="[{ validator: validateName }]">
|
||||
<a-input
|
||||
v-model="form.name"
|
||||
class="w-[243px]"
|
||||
:placeholder="t('system.userGroup.pleaseInputUserGroupName')"
|
||||
allow-clear
|
||||
@press-enter="handleBeforeOk"
|
||||
@keyup.esc="handleCancel"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
<div class="flex flex-row flex-nowrap justify-end gap-2">
|
||||
<a-button type="secondary" size="mini" :disabled="loading" @click="handleCancel">
|
||||
{{ t('common.cancel') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
size="mini"
|
||||
:loading="loading"
|
||||
:disabled="form.name.length === 0"
|
||||
@click="handleBeforeOk"
|
||||
>
|
||||
{{ props.id ? t('common.rename') : t('common.create') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<slot></slot>
|
||||
|
@ -82,6 +84,7 @@
|
|||
|
||||
const formRef = ref<FormInstance>();
|
||||
const currentVisible = ref(props.visible);
|
||||
// trigger相关变量
|
||||
|
||||
const form = reactive({
|
||||
name: '',
|
||||
|
@ -154,6 +157,12 @@
|
|||
currentVisible.value = props.visible;
|
||||
form.name = props.defaultName || '';
|
||||
});
|
||||
|
||||
const handleOutsideClick = () => {
|
||||
if (currentVisible.value) {
|
||||
handleCancel();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="ms-ug-left flex h-full flex-col px-[24px] pb-[24px]">
|
||||
<div class="sticky top-0 z-[999] w-[252px] bg-white pt-[24px]">
|
||||
<div class="flex flex-col px-[24px] pb-[24px]">
|
||||
<div class="sticky top-0 z-[999] bg-white pt-[24px]">
|
||||
<a-input-search
|
||||
:placeholder="t('system.userGroup.searchHolder')"
|
||||
allow-clear
|
||||
|
@ -8,7 +8,7 @@
|
|||
@search="searchData"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="showSystem" class="mt-2 w-[252px]">
|
||||
<div v-if="showSystem" class="mt-2">
|
||||
<CreateUserGroupPopup
|
||||
:list="systemUserGroupList"
|
||||
:visible="systemUserGroupVisible"
|
||||
|
@ -36,8 +36,8 @@
|
|||
{{ t('system.userGroup.systemUserGroup') }}
|
||||
</div>
|
||||
</div>
|
||||
<MsMoreAction :list="createSystemUGActionItem" @select="systemUserGroupVisible = true">
|
||||
<icon-plus-circle-fill class="text-[rgb(var(--primary-7))]" size="20" />
|
||||
<MsMoreAction :list="createSystemUGActionItem" @select="handleCreateUG(AuthScopeEnum.SYSTEM)">
|
||||
<icon-plus-circle-fill class="cursor-pointer text-[rgb(var(--primary-7))]" size="20" />
|
||||
</MsMoreAction>
|
||||
</div>
|
||||
</CreateUserGroupPopup>
|
||||
|
@ -56,10 +56,10 @@
|
|||
@cancel="handleRenameCancel(element)"
|
||||
@submit="handleRenameCancel(element, element.id)"
|
||||
>
|
||||
<div class="flex grow flex-row items-center justify-between">
|
||||
<div class="flex max-w-[100%] grow flex-row items-center justify-between">
|
||||
<a-tooltip :content="element.name">
|
||||
<div
|
||||
class="one-line-text max-w-[156px] text-[var(--color-text-1)]"
|
||||
class="one-line-text text-[var(--color-text-1)]"
|
||||
:class="{ 'text-[rgb(var(--primary-7))]': element.id === currentId }"
|
||||
>{{ element.name }}</div
|
||||
>
|
||||
|
@ -90,7 +90,7 @@
|
|||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
<div v-if="showOrg" class="mt-2 w-[252px]">
|
||||
<div v-if="showOrg" class="mt-2">
|
||||
<CreateUserGroupPopup
|
||||
:list="orgUserGroupList"
|
||||
:visible="orgUserGroupVisible"
|
||||
|
@ -119,7 +119,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<MsMoreAction :list="createOrgUGActionItem" @select="orgUserGroupVisible = true">
|
||||
<icon-plus-circle-fill class="text-[rgb(var(--primary-7))]" size="20" />
|
||||
<icon-plus-circle-fill class="cursor-pointer text-[rgb(var(--primary-7))]" size="20" />
|
||||
</MsMoreAction>
|
||||
</div>
|
||||
</CreateUserGroupPopup>
|
||||
|
@ -138,10 +138,10 @@
|
|||
@cancel="handleRenameCancel(element)"
|
||||
@submit="handleRenameCancel(element, element.id)"
|
||||
>
|
||||
<div class="flex grow flex-row items-center justify-between">
|
||||
<div class="flex max-w-[100%] grow flex-row items-center justify-between">
|
||||
<a-tooltip :content="element.name">
|
||||
<div
|
||||
class="one-line-text max-w-[156px] text-[var(--color-text-1)]"
|
||||
class="one-line-text text-[var(--color-text-1)]"
|
||||
:class="{ 'text-[rgb(var(--primary-7))]': element.id === currentId }"
|
||||
>{{ element.name }}</div
|
||||
>
|
||||
|
@ -172,7 +172,7 @@
|
|||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
<div v-if="showProject" class="mt-2 w-[252px]">
|
||||
<div v-if="showProject" class="mt-2">
|
||||
<CreateUserGroupPopup
|
||||
:list="projectUserGroupList"
|
||||
:visible="projectUserGroupVisible"
|
||||
|
@ -201,7 +201,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<MsMoreAction :list="createProjectUGActionItem" @select="projectUserGroupVisible = true">
|
||||
<icon-plus-circle-fill class="text-[rgb(var(--primary-7))]" size="20" />
|
||||
<icon-plus-circle-fill class="cursor-pointer text-[rgb(var(--primary-7))]" size="20" />
|
||||
</MsMoreAction>
|
||||
</div>
|
||||
</CreateUserGroupPopup>
|
||||
|
@ -220,10 +220,10 @@
|
|||
@cancel="handleRenameCancel(element)"
|
||||
@submit="handleRenameCancel(element, element.id)"
|
||||
>
|
||||
<div class="flex grow flex-row items-center justify-between">
|
||||
<div class="flex max-w-[100%] grow flex-row items-center justify-between">
|
||||
<a-tooltip :content="element.name">
|
||||
<div
|
||||
class="one-line-text max-w-[156px] text-[var(--color-text-1)]"
|
||||
class="one-line-text text-[var(--color-text-1)]"
|
||||
:class="{ 'text-[rgb(var(--primary-7))]': element.id === currentId }"
|
||||
>{{ element.name }}</div
|
||||
>
|
||||
|
@ -254,7 +254,7 @@
|
|||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
<AddUserModal :visible="userModalVisible" :current-id="currentItem.id" @cancel="userModalVisible = false" />
|
||||
<AddUserModal :visible="userModalVisible" :current-id="currentItem.id" @cancel="handleAddUserCancel" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -286,6 +286,7 @@
|
|||
|
||||
const emit = defineEmits<{
|
||||
(e: 'handleSelect', element: UserGroupItem): void;
|
||||
(e: 'addUserSuccess', id: string): void;
|
||||
}>();
|
||||
const appStore = useAppStore();
|
||||
const { openModal } = useModal();
|
||||
|
@ -383,16 +384,13 @@
|
|||
}
|
||||
if (res.length > 0) {
|
||||
userGroupList.value = res;
|
||||
let tmpItem = res[0];
|
||||
if (id) {
|
||||
tmpItem = res.find((i) => i.id === id) || res[0];
|
||||
}
|
||||
if (isSelect) {
|
||||
// leftCollapse 切换时不重复数据请求
|
||||
handleListItemClick(tmpItem);
|
||||
} else {
|
||||
// leftCollapse 切换时调用
|
||||
currentId.value = id || '';
|
||||
if (id) {
|
||||
handleListItemClick(res.find((i) => i.id === id) || res[0]);
|
||||
} else {
|
||||
handleListItemClick(res[0]);
|
||||
}
|
||||
}
|
||||
// 弹窗赋值
|
||||
const tmpObj: PopVisible = {};
|
||||
|
@ -452,7 +450,7 @@
|
|||
|
||||
function enterData(eve: Event) {
|
||||
if (!(eve.target as HTMLInputElement).value) {
|
||||
initData();
|
||||
initData('', false);
|
||||
return;
|
||||
}
|
||||
const keyword = (eve.target as HTMLInputElement).value;
|
||||
|
@ -461,7 +459,7 @@
|
|||
}
|
||||
function searchData(value: string) {
|
||||
if (!value) {
|
||||
initData();
|
||||
initData('', false);
|
||||
return;
|
||||
}
|
||||
const keyword = value;
|
||||
|
@ -480,6 +478,21 @@
|
|||
defineExpose({
|
||||
initData,
|
||||
});
|
||||
const handleCreateUG = (scoped: AuthScopeEnum) => {
|
||||
if (scoped === AuthScopeEnum.SYSTEM) {
|
||||
systemUserGroupVisible.value = true;
|
||||
} else if (scoped === AuthScopeEnum.ORGANIZATION) {
|
||||
orgUserGroupVisible.value = true;
|
||||
} else if (scoped === AuthScopeEnum.PROJECT) {
|
||||
projectUserGroupVisible.value = true;
|
||||
}
|
||||
};
|
||||
const handleAddUserCancel = (shouldSearch: boolean) => {
|
||||
userModalVisible.value = false;
|
||||
if (shouldSearch) {
|
||||
emit('addUserSuccess', currentId.value);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams, setKeyword } = useTable(getRequestBySystemType(), {
|
||||
columns: userGroupUsercolumns,
|
||||
scroll: { x: '100%' },
|
||||
scroll: { x: '100%', minWidth: 700 },
|
||||
selectable: false,
|
||||
noDisable: true,
|
||||
showSetting: false,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
:disabled="props.disabled"
|
||||
:filter-option="false"
|
||||
allow-clear
|
||||
:loading="loading"
|
||||
@change="change"
|
||||
@search="debouncedSearch"
|
||||
>
|
||||
|
@ -97,6 +98,8 @@
|
|||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
allOptions.value = [];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { computed, defineComponent, reactive, ref, toRefs, watch } from 'vue';
|
||||
|
||||
import Pager from './page-item.vue';
|
||||
import EllipsisPager from './page-item-ellipsis.vue';
|
||||
import StepPager from './page-item-step.vue';
|
||||
|
|
|
@ -166,7 +166,9 @@
|
|||
|
||||
// 获取当前标题的样式
|
||||
const titleClass = computed(() => {
|
||||
return props.isDelete ? 'ml-2 font-semibold' : 'mb-[8px] font-medium text-[var(--color-text-1)] text-[14px]';
|
||||
return props.isDelete
|
||||
? 'ml-2 font-[14px] text-[var(--color-text-1)]'
|
||||
: 'mb-[8px] font-medium text-[var(--color-text-1)] text-[14px]';
|
||||
});
|
||||
|
||||
watch(
|
||||
|
|
|
@ -1,53 +1,55 @@
|
|||
<script lang="ts" setup>
|
||||
import '@halo-dev/richtext-editor/dist/style.css';
|
||||
// import { unified } from 'unified';
|
||||
// import rehypeParse from 'rehype-parse';
|
||||
// import rehypeFormat from 'rehype-format';
|
||||
// import rehypeStringify from 'rehype-stringify';
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
|
||||
import useLocale from '@/locale/useLocale';
|
||||
|
||||
import '@halo-dev/richtext-editor/dist/style.css';
|
||||
import {
|
||||
ExtensionAudio,
|
||||
ExtensionBlockquote,
|
||||
ExtensionBold,
|
||||
ExtensionBulletList,
|
||||
ExtensionCode,
|
||||
ExtensionCodeBlock,
|
||||
ExtensionColor,
|
||||
ExtensionColumn,
|
||||
ExtensionColumns,
|
||||
ExtensionCommands,
|
||||
ExtensionDocument,
|
||||
ExtensionDraggable,
|
||||
ExtensionDropcursor,
|
||||
ExtensionFontSize,
|
||||
ExtensionGapcursor,
|
||||
ExtensionHardBreak,
|
||||
ExtensionHeading,
|
||||
ExtensionHighlight,
|
||||
ExtensionHistory,
|
||||
ExtensionHorizontalRule,
|
||||
ExtensionItalic,
|
||||
ExtensionOrderedList,
|
||||
ExtensionStrike,
|
||||
ExtensionText,
|
||||
ExtensionIframe,
|
||||
ExtensionImage,
|
||||
ExtensionTaskList,
|
||||
ExtensionIndent,
|
||||
ExtensionItalic,
|
||||
ExtensionLink,
|
||||
ExtensionTextAlign,
|
||||
ExtensionUnderline,
|
||||
ExtensionTable,
|
||||
ExtensionNodeSelected,
|
||||
ExtensionOrderedList,
|
||||
ExtensionPlaceholder,
|
||||
ExtensionStrike,
|
||||
ExtensionSubscript,
|
||||
ExtensionSuperscript,
|
||||
ExtensionPlaceholder,
|
||||
ExtensionHighlight,
|
||||
ExtensionCommands,
|
||||
ExtensionIframe,
|
||||
ExtensionTable,
|
||||
ExtensionTaskList,
|
||||
ExtensionText,
|
||||
ExtensionTextAlign,
|
||||
ExtensionTrailingNode,
|
||||
ExtensionUnderline,
|
||||
ExtensionVideo,
|
||||
ExtensionAudio,
|
||||
ExtensionCodeBlock,
|
||||
ExtensionColor,
|
||||
ExtensionFontSize,
|
||||
lowlight,
|
||||
RichTextEditor,
|
||||
useEditor,
|
||||
ExtensionIndent,
|
||||
ExtensionDraggable,
|
||||
ExtensionColumns,
|
||||
ExtensionColumn,
|
||||
ExtensionNodeSelected,
|
||||
ExtensionTrailingNode,
|
||||
} from '@halo-dev/richtext-editor';
|
||||
|
||||
const content = useLocalStorage('content', '');
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits(['update:width']);
|
||||
const emit = defineEmits(['update:width', 'expandChange']);
|
||||
|
||||
const innerWidth = ref(props.width || '300px');
|
||||
|
||||
|
@ -71,8 +71,10 @@
|
|||
isExpandedLeft.value = !isExpandedLeft.value;
|
||||
if (isExpandedLeft.value) {
|
||||
innerWidth.value = props.width || '300px';
|
||||
emit('expandChange', true);
|
||||
} else {
|
||||
innerWidth.value = '0px';
|
||||
emit('expandChange', false);
|
||||
}
|
||||
// 动画结束,去掉动画类
|
||||
setTimeout(() => {
|
||||
|
|
|
@ -33,5 +33,9 @@ export default {
|
|||
modify: 'Modify{name}',
|
||||
nameIsNotNull: 'Name cannot be empty',
|
||||
nameIsExist: '{name} already exists',
|
||||
empty: 'No Content',
|
||||
loading: 'Loading, please wait',
|
||||
errorStatus: 'Failed to load data, please',
|
||||
retry: 'Retry',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { App } from 'vue';
|
||||
|
||||
import outerClick from './outerClick';
|
||||
import permission from './permission';
|
||||
import validateLicense from './validateLicense';
|
||||
|
||||
|
@ -7,5 +8,6 @@ export default {
|
|||
install(Vue: App) {
|
||||
Vue.directive('permission', permission);
|
||||
Vue.directive('xpack', validateLicense);
|
||||
Vue.directive('outer', outerClick);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
import { isServerRendering } from '@/utils/dom';
|
||||
|
||||
import type { ComponentPublicInstance, DirectiveBinding, ObjectDirective } from 'vue';
|
||||
|
||||
export const isElement = (e: unknown): e is Element => {
|
||||
if (typeof Element === 'undefined') return false;
|
||||
return e instanceof Element;
|
||||
};
|
||||
|
||||
type DocumentHandler = <T extends MouseEvent>(mouseup: T, mousedown: T) => void;
|
||||
type FlushList = Map<
|
||||
HTMLElement,
|
||||
{
|
||||
documentHandler: DocumentHandler;
|
||||
bindingFn: (...args: unknown[]) => unknown;
|
||||
}[]
|
||||
>;
|
||||
|
||||
const nodeList: FlushList = new Map();
|
||||
|
||||
let startClick: MouseEvent;
|
||||
|
||||
if (!isServerRendering) {
|
||||
document.addEventListener('mousedown', (e: MouseEvent) => {
|
||||
startClick = e;
|
||||
});
|
||||
document.addEventListener('mouseup', (e: MouseEvent) => {
|
||||
nodeList.forEach((handlers) => {
|
||||
handlers.forEach(({ documentHandler }) => {
|
||||
documentHandler(e as MouseEvent, startClick);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createDocumentHandler(el: HTMLElement, binding: DirectiveBinding): DocumentHandler {
|
||||
let excludes: HTMLElement[] = [];
|
||||
if (Array.isArray(binding.arg)) {
|
||||
excludes = binding.arg;
|
||||
} else if (isElement(binding.arg)) {
|
||||
// due to current implementation on binding type is wrong the type casting is necessary here
|
||||
excludes.push(binding.arg as unknown as HTMLElement);
|
||||
}
|
||||
return (mouseup, mousedown) => {
|
||||
const { popperRef } = binding.instance as ComponentPublicInstance<{
|
||||
popperRef: HTMLElement;
|
||||
}>;
|
||||
const mouseUpTarget = mouseup.target as Node;
|
||||
const mouseDownTarget = mousedown?.target as Node;
|
||||
const isBound = !binding || !binding.instance;
|
||||
const isTargetExists = !mouseUpTarget || !mouseDownTarget;
|
||||
const isContainedByEl = el.contains(mouseUpTarget) || el.contains(mouseDownTarget);
|
||||
const isSelf = el === mouseUpTarget;
|
||||
|
||||
const isTargetExcluded =
|
||||
(excludes.length && excludes.some((item) => item?.contains(mouseUpTarget))) ||
|
||||
(excludes.length && excludes.includes(mouseDownTarget as HTMLElement));
|
||||
const isContainedByPopper = popperRef && (popperRef.contains(mouseUpTarget) || popperRef.contains(mouseDownTarget));
|
||||
if (isBound || isTargetExists || isContainedByEl || isSelf || isTargetExcluded || isContainedByPopper) {
|
||||
return;
|
||||
}
|
||||
binding.value(mouseup, mousedown);
|
||||
};
|
||||
}
|
||||
|
||||
const ClickOutside: ObjectDirective = {
|
||||
beforeMount(el: HTMLElement, binding: DirectiveBinding) {
|
||||
// there could be multiple handlers on the element
|
||||
if (!nodeList.has(el)) {
|
||||
nodeList.set(el, []);
|
||||
}
|
||||
|
||||
nodeList.get(el)?.push({
|
||||
documentHandler: createDocumentHandler(el, binding),
|
||||
bindingFn: binding.value,
|
||||
});
|
||||
},
|
||||
updated(el: HTMLElement, binding: DirectiveBinding) {
|
||||
let handlers = nodeList.get(el);
|
||||
if (!handlers) {
|
||||
handlers = [];
|
||||
nodeList.set(el, handlers);
|
||||
}
|
||||
|
||||
const oldHandlerIndex = handlers.findIndex((item) => item.bindingFn === binding.oldValue);
|
||||
const newHandler = {
|
||||
documentHandler: createDocumentHandler(el, binding),
|
||||
bindingFn: binding.value,
|
||||
};
|
||||
|
||||
if (oldHandlerIndex >= 0) {
|
||||
// replace the old handler to the new handler
|
||||
handlers.splice(oldHandlerIndex, 1, newHandler);
|
||||
} else {
|
||||
handlers.push(newHandler);
|
||||
}
|
||||
},
|
||||
unmounted(el: HTMLElement) {
|
||||
// remove all listeners when a component unmounted
|
||||
nodeList.delete(el);
|
||||
},
|
||||
};
|
||||
|
||||
export default ClickOutside;
|
|
@ -19,4 +19,16 @@ export enum MenuEnum {
|
|||
uiTest = 'uiTest',
|
||||
}
|
||||
|
||||
export default {};
|
||||
export enum ShapeFlags {
|
||||
ELEMENT = 1,
|
||||
FUNCTIONAL_COMPONENT = 2,
|
||||
STATEFUL_COMPONENT = 4,
|
||||
COMPONENT = FUNCTIONAL_COMPONENT + STATEFUL_COMPONENT,
|
||||
TEXT_CHILDREN = 8,
|
||||
ARRAY_CHILDREN = 16,
|
||||
SLOTS_CHILDREN = 32,
|
||||
TELEPORT = 64,
|
||||
SUSPENSE = 128,
|
||||
COMPONENT_SHOULD_KEEP_ALIVE = 256,
|
||||
COMPONENT_KEPT_ALIVE = 512,
|
||||
}
|
||||
|
|
|
@ -18,3 +18,46 @@ export function scrollIntoView(targetRef: HTMLElement | Element | null, options:
|
|||
|
||||
targetRef?.scrollIntoView(scrollOptions);
|
||||
}
|
||||
|
||||
export const NOOP = () => {
|
||||
return undefined;
|
||||
};
|
||||
|
||||
// 判断是否为服务端渲染
|
||||
export const isServerRendering = (() => {
|
||||
try {
|
||||
return !(typeof window !== 'undefined' && document !== undefined);
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
})();
|
||||
|
||||
// 监听事件
|
||||
export const on = (() => {
|
||||
if (isServerRendering) {
|
||||
return NOOP;
|
||||
}
|
||||
return <K extends keyof HTMLElementEventMap>(
|
||||
element: HTMLElement | Window,
|
||||
event: K,
|
||||
handler: (ev: HTMLElementEventMap[K]) => void,
|
||||
options: boolean | AddEventListenerOptions = false
|
||||
) => {
|
||||
element.addEventListener(event, handler as EventListenerOrEventListenerObject, options);
|
||||
};
|
||||
})();
|
||||
|
||||
// 移除监听事件
|
||||
export const off = (() => {
|
||||
if (isServerRendering) {
|
||||
return NOOP;
|
||||
}
|
||||
return <K extends keyof HTMLElementEventMap>(
|
||||
element: HTMLElement | Window,
|
||||
type: K,
|
||||
handler: (ev: HTMLElementEventMap[K]) => void,
|
||||
options: boolean | EventListenerOptions = false
|
||||
) => {
|
||||
element.removeEventListener(type, handler as EventListenerOrEventListenerObject, options);
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -143,7 +143,7 @@
|
|||
description: '',
|
||||
resourcePoolIds: [],
|
||||
enable: true,
|
||||
moduleIds: [],
|
||||
moduleIds: ['workstation', 'testPlan', 'bugManagement', 'caseManagement', 'apiTest', 'uiTest', 'loadTest'],
|
||||
});
|
||||
|
||||
const currentVisible = ref(props.visible);
|
||||
|
@ -162,7 +162,7 @@
|
|||
form.organizationId = currentOrgId.value;
|
||||
form.description = '';
|
||||
form.enable = true;
|
||||
form.moduleIds = [];
|
||||
form.moduleIds = ['workstation', 'testPlan', 'bugManagement', 'caseManagement', 'apiTest', 'uiTest', 'loadTest'];
|
||||
form.resourcePoolIds = [];
|
||||
};
|
||||
const handleCancel = (shouldSearch: boolean) => {
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
<template>
|
||||
<div class="card">
|
||||
<div class="flex h-full flex-row">
|
||||
<Transition>
|
||||
<div v-if="leftCollapse" class="user-group-left ms-scroll-bar">
|
||||
<UserGroupLeft ref="ugLeftRef" @handle-select="handleSelect" />
|
||||
</div>
|
||||
</Transition>
|
||||
<Transition>
|
||||
<div class="usergroup-collapse" :style="{ left: leftCollapse ? '300px' : '0' }" @click="handleCollapse">
|
||||
<icon-double-left v-if="leftCollapse" class="text-[12px] text-[var(--color-text-brand)]" />
|
||||
<icon-double-right v-else class="text-[12px] text-[var(--color-text-brand)]" />
|
||||
</div>
|
||||
</Transition>
|
||||
<div v-if="leftCollapse" class="user-group-left ms-scroll-bar">
|
||||
<UserGroupLeft ref="ugLeftRef" @handle-select="handleSelect" @add-user-success="handleAddMember" />
|
||||
</div>
|
||||
<div class="usergroup-collapse" :style="{ left: leftCollapse ? '300px' : '0' }" @click="handleCollapse">
|
||||
<icon-double-left v-if="leftCollapse" class="text-[12px] text-[var(--color-text-brand)]" />
|
||||
<icon-double-right v-else class="text-[12px] text-[var(--color-text-brand)]" />
|
||||
</div>
|
||||
<div class="p-[24px]" :style="{ width: leftCollapse ? 'calc(100% - 300px)' : '100%' }">
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<a-tooltip :content="currentUserGroupItem.name">
|
||||
|
@ -125,7 +121,7 @@
|
|||
leftCollapse.value = !leftCollapse.value;
|
||||
if (leftCollapse.value) {
|
||||
nextTick(() => {
|
||||
ugLeftRef.value?.initData(currentUserGroupItem.value.id, false);
|
||||
ugLeftRef.value?.initData(currentUserGroupItem.value.id);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -143,6 +139,11 @@
|
|||
const handleSave = () => {
|
||||
authRef.value?.handleSave();
|
||||
};
|
||||
const handleAddMember = (id: string) => {
|
||||
if (id === currentUserGroupItem.value.id) {
|
||||
tableSearch();
|
||||
}
|
||||
};
|
||||
const canSave = computed(() => {
|
||||
if (currentTable.value === 'auth') {
|
||||
return authRef.value?.canSave;
|
||||
|
@ -195,12 +196,4 @@
|
|||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.v-enter-active,
|
||||
.v-leave-active {
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
.v-enter-from,
|
||||
.v-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -141,7 +141,7 @@
|
|||
organizationId: '',
|
||||
description: '',
|
||||
enable: true,
|
||||
moduleIds: [],
|
||||
moduleIds: ['workstation', 'testPlan', 'bugManagement', 'caseManagement', 'apiTest', 'uiTest', 'loadTest'],
|
||||
resourcePoolIds: [],
|
||||
});
|
||||
|
||||
|
@ -161,7 +161,7 @@
|
|||
form.organizationId = '';
|
||||
form.description = '';
|
||||
form.enable = true;
|
||||
form.moduleIds = [];
|
||||
form.moduleIds = ['workstation', 'testPlan', 'bugManagement', 'caseManagement', 'apiTest', 'uiTest', 'loadTest'];
|
||||
};
|
||||
const handleCancel = (shouldSearch: boolean) => {
|
||||
emit('cancel', shouldSearch);
|
||||
|
|
|
@ -1,48 +1,47 @@
|
|||
<template>
|
||||
<div class="card">
|
||||
<div class="flex h-full flex-row">
|
||||
<Transition>
|
||||
<div v-if="leftCollapse" class="user-group-left ms-scroll-bar">
|
||||
<UserGroupLeft ref="ugLeftRef" @handle-select="handleSelect" />
|
||||
</div>
|
||||
</Transition>
|
||||
<Transition>
|
||||
<div class="usergroup-collapse" :style="{ left: leftCollapse ? '300px' : '0' }" @click="handleCollapse">
|
||||
<icon-double-left v-if="leftCollapse" class="text-[12px] text-[var(--color-text-brand)]" />
|
||||
<icon-double-right v-if="!leftCollapse" class="text-[12px] text-[var(--color-text-brand)]" />
|
||||
</div>
|
||||
</Transition>
|
||||
<div class="p-[24px]" :style="{ width: leftCollapse ? 'calc(100% - 300px)' : '100%' }">
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<a-tooltip :content="currentUserGroupItem.name">
|
||||
<div class="one-line-text max-w-[300px]">{{ currentUserGroupItem.name }}</div>
|
||||
</a-tooltip>
|
||||
<div class="flex items-center">
|
||||
<a-input-search
|
||||
v-if="currentTable === 'user'"
|
||||
:placeholder="t('system.user.searchUser')"
|
||||
class="w-[240px]"
|
||||
allow-clear
|
||||
@press-enter="handleEnter"
|
||||
@search="handleSearch"
|
||||
></a-input-search>
|
||||
<a-radio-group v-if="couldShowUser && couldShowAuth" v-model="currentTable" class="ml-[14px]" type="button">
|
||||
<a-radio v-if="couldShowAuth" value="auth">{{ t('system.userGroup.auth') }}</a-radio>
|
||||
<a-radio v-if="couldShowUser" value="user">{{ t('system.userGroup.user') }}</a-radio>
|
||||
</a-radio-group>
|
||||
<MsSplitBox @expand-change="handleCollapse">
|
||||
<template #left>
|
||||
<UserGroupLeft ref="ugLeftRef" @handle-select="handleSelect" @add-user-success="handleAddMember" />
|
||||
</template>
|
||||
<template #right>
|
||||
<div class="p-[24px]">
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<a-tooltip :content="currentUserGroupItem.name">
|
||||
<div class="one-line-text max-w-[300px]">{{ currentUserGroupItem.name }}</div>
|
||||
</a-tooltip>
|
||||
<div class="flex items-center">
|
||||
<a-input-search
|
||||
v-if="currentTable === 'user'"
|
||||
:placeholder="t('system.user.searchUser')"
|
||||
class="w-[240px]"
|
||||
allow-clear
|
||||
@press-enter="handleEnter"
|
||||
@search="handleSearch"
|
||||
></a-input-search>
|
||||
<a-radio-group
|
||||
v-if="couldShowUser && couldShowAuth"
|
||||
v-model="currentTable"
|
||||
class="ml-[14px]"
|
||||
type="button"
|
||||
>
|
||||
<a-radio v-if="couldShowAuth" value="auth">{{ t('system.userGroup.auth') }}</a-radio>
|
||||
<a-radio v-if="couldShowUser" value="user">{{ t('system.userGroup.user') }}</a-radio>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-[16px]">
|
||||
<UserTable
|
||||
v-if="currentTable === 'user' && couldShowUser"
|
||||
ref="userRef"
|
||||
:keyword="currentKeyword"
|
||||
:current="currentUserGroupItem"
|
||||
/>
|
||||
<AuthTable v-if="currentTable === 'auth' && couldShowAuth" ref="authRef" :current="currentUserGroupItem" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-[16px]">
|
||||
<UserTable
|
||||
v-if="currentTable === 'user' && couldShowUser"
|
||||
ref="userRef"
|
||||
:keyword="currentKeyword"
|
||||
:current="currentUserGroupItem"
|
||||
/>
|
||||
<AuthTable v-if="currentTable === 'auth' && couldShowAuth" ref="authRef" :current="currentUserGroupItem" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</MsSplitBox>
|
||||
</div>
|
||||
<div
|
||||
v-if="currentTable === 'auth'"
|
||||
|
@ -63,6 +62,7 @@
|
|||
import { computed, nextTick, onMounted, provide, ref, watchEffect } from 'vue';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
||||
import AuthTable from '@/components/business/ms-user-group-comp/authTable.vue';
|
||||
import UserGroupLeft from '@/components/business/ms-user-group-comp/msUserGroupLeft.vue';
|
||||
import UserTable from '@/components/business/ms-user-group-comp/userTable.vue';
|
||||
|
@ -119,13 +119,18 @@
|
|||
currentUserGroupItem.value = item;
|
||||
};
|
||||
|
||||
const handleAddMember = (id: string) => {
|
||||
if (id === currentUserGroupItem.value.id) {
|
||||
tableSearch();
|
||||
}
|
||||
};
|
||||
|
||||
const couldShowUser = computed(() => currentUserGroupItem.value.type === AuthScopeEnum.SYSTEM);
|
||||
const couldShowAuth = computed(() => currentUserGroupItem.value.id !== 'admin');
|
||||
const handleCollapse = () => {
|
||||
leftCollapse.value = !leftCollapse.value;
|
||||
if (leftCollapse.value) {
|
||||
const handleCollapse = (collapse: boolean) => {
|
||||
if (collapse) {
|
||||
nextTick(() => {
|
||||
ugLeftRef.value?.initData(currentUserGroupItem.value.id, false);
|
||||
ugLeftRef.value?.initData(currentUserGroupItem.value.id);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -177,6 +182,7 @@
|
|||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding-right: 6px;
|
||||
padding-bottom: 24px;
|
||||
width: 300px;
|
||||
min-width: 300px;
|
||||
height: 100%;
|
||||
|
@ -195,12 +201,4 @@
|
|||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.v-enter-active,
|
||||
.v-leave-active {
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
.v-enter-from,
|
||||
.v-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue