- 收敛管理端公共 Modal 链路,新增表单弹窗与普通内容弹窗包装\n- 迁移 Bot、知识库、插件、工作流、资源、MCP、数据中枢与系统管理页面级弹窗\n- 统一内容区工具栏、列表容器、导航与顶部按钮的视觉密度和交互节奏
173 lines
4.1 KiB
Vue
173 lines
4.1 KiB
Vue
<script lang="ts" setup>
|
|
import { computed, useAttrs, watch } from 'vue';
|
|
|
|
import { $t } from '@easyflow/locales';
|
|
|
|
import { useEasyFlowModal } from '@easyflow-core/popup-ui';
|
|
import { EasyFlowButton } from '@easyflow-core/shadcn-ui';
|
|
import { cn } from '@easyflow-core/shared/utils';
|
|
|
|
type BeforeClose = () => boolean | Promise<boolean | void> | void;
|
|
|
|
interface Props {
|
|
alignCenter?: boolean;
|
|
beforeClose?: BeforeClose;
|
|
cancelText?: string;
|
|
centered?: boolean;
|
|
closable?: boolean;
|
|
closeOnClickModal?: boolean;
|
|
confirmLoading?: boolean;
|
|
confirmText?: string;
|
|
description?: string;
|
|
destroyOnClose?: boolean;
|
|
draggable?: boolean;
|
|
modelValue?: boolean;
|
|
open?: boolean;
|
|
showCancelButton?: boolean;
|
|
showConfirmButton?: boolean;
|
|
showFooter?: boolean;
|
|
submitting?: boolean;
|
|
title: string;
|
|
width?: 'lg' | 'md' | 'xl' | number | string;
|
|
}
|
|
|
|
defineOptions({
|
|
name: 'EasyFlowPanelModal',
|
|
});
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
alignCenter: false,
|
|
beforeClose: undefined,
|
|
cancelText: '',
|
|
centered: false,
|
|
closable: true,
|
|
closeOnClickModal: false,
|
|
confirmLoading: false,
|
|
confirmText: '',
|
|
description: '',
|
|
destroyOnClose: true,
|
|
draggable: false,
|
|
modelValue: undefined,
|
|
open: undefined,
|
|
showCancelButton: true,
|
|
showConfirmButton: true,
|
|
showFooter: true,
|
|
submitting: false,
|
|
width: 'lg',
|
|
});
|
|
|
|
const emit = defineEmits<{
|
|
cancel: [];
|
|
confirm: [];
|
|
'update:modelValue': [boolean];
|
|
'update:open': [boolean];
|
|
}>();
|
|
|
|
const attrs = useAttrs();
|
|
|
|
const [Modal, modalApi] = useEasyFlowModal({
|
|
async onBeforeClose() {
|
|
const result = await props.beforeClose?.();
|
|
return result !== false;
|
|
},
|
|
onOpenChange(isOpen) {
|
|
emit('update:modelValue', isOpen);
|
|
emit('update:open', isOpen);
|
|
},
|
|
});
|
|
|
|
const widthClassMap: Record<string, string> = {
|
|
'482': 'sm:w-[482px]',
|
|
'482px': 'sm:w-[482px]',
|
|
'50%': 'sm:w-[min(50vw,720px)]',
|
|
'500px': 'sm:w-[500px]',
|
|
'520px': 'sm:w-[520px]',
|
|
'550px': 'sm:w-[550px]',
|
|
'600px': 'sm:w-[600px]',
|
|
'762': 'sm:w-[762px]',
|
|
'762px': 'sm:w-[762px]',
|
|
'800px': 'sm:w-[800px]',
|
|
'80%': 'sm:w-[min(80vw,1120px)]',
|
|
'min(920px, 92vw)': 'sm:w-[min(92vw,920px)]',
|
|
'min(980px, 92vw)': 'sm:w-[min(92vw,980px)]',
|
|
md: 'sm:w-[560px]',
|
|
lg: 'sm:w-[720px]',
|
|
xl: 'sm:w-[960px]',
|
|
};
|
|
|
|
const modalClass = computed(() => {
|
|
const widthKey = String(props.width);
|
|
return cn(
|
|
'w-[calc(100vw-24px)] max-w-[calc(100vw-24px)] sm:max-w-[min(calc(100vw-48px),1120px)]',
|
|
widthClassMap[widthKey] || widthClassMap.lg,
|
|
attrs.class,
|
|
);
|
|
});
|
|
|
|
watch(
|
|
() => props.open ?? props.modelValue ?? false,
|
|
(value) => {
|
|
modalApi.setState({ isOpen: value });
|
|
},
|
|
{ immediate: true },
|
|
);
|
|
|
|
function handleCancel() {
|
|
emit('cancel');
|
|
modalApi.close();
|
|
}
|
|
|
|
function handleConfirm() {
|
|
emit('confirm');
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<Modal
|
|
:class="modalClass"
|
|
:centered="centered || alignCenter"
|
|
:closable="closable"
|
|
:close-on-click-modal="closeOnClickModal"
|
|
:confirm-loading="confirmLoading"
|
|
:description="description"
|
|
:destroy-on-close="destroyOnClose"
|
|
:draggable="draggable"
|
|
:footer="showFooter"
|
|
:fullscreen-button="false"
|
|
:show-cancel-button="false"
|
|
:show-confirm-button="false"
|
|
:submitting="submitting"
|
|
:title="title"
|
|
>
|
|
<slot></slot>
|
|
|
|
<template v-if="showFooter" #footer>
|
|
<slot name="footer">
|
|
<div class="flex w-full items-center justify-between gap-3">
|
|
<div class="min-w-0 flex-1">
|
|
<slot name="footer-extra"></slot>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<EasyFlowButton
|
|
v-if="showCancelButton"
|
|
variant="ghost"
|
|
:disabled="submitting"
|
|
@click="handleCancel"
|
|
>
|
|
{{ cancelText || $t('button.cancel') }}
|
|
</EasyFlowButton>
|
|
<EasyFlowButton
|
|
v-if="showConfirmButton"
|
|
:disabled="submitting"
|
|
:loading="confirmLoading || submitting"
|
|
@click="handleConfirm"
|
|
>
|
|
{{ confirmText || $t('button.confirm') }}
|
|
</EasyFlowButton>
|
|
</div>
|
|
</div>
|
|
</slot>
|
|
</template>
|
|
</Modal>
|
|
</template>
|