mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-26 16:46:19 +08:00
perf: perf modal and drawer
This commit is contained in:
@@ -18,12 +18,18 @@
|
|||||||
|
|
||||||
- 缓存可以配置是否加密,默认生产环境开启 Aes 加密
|
- 缓存可以配置是否加密,默认生产环境开启 Aes 加密
|
||||||
- 新增标签页拖拽排序
|
- 新增标签页拖拽排序
|
||||||
|
- 新增 LayoutFooter.默认显示,可以在配置内关闭
|
||||||
|
|
||||||
|
### ⚡ Performance Improvements
|
||||||
|
|
||||||
|
- 优化`Modal`组件全屏动画不流畅问题
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
- 修复 tree 文本超出挡住操作按钮问题
|
- 修复 tree 文本超出挡住操作按钮问题
|
||||||
- 修复通过 useRedo 刷新页面参数丢失问题
|
- 修复通过 useRedo 刷新页面参数丢失问题
|
||||||
- 修复表单校验先设置在校验及控制台错误信息问题
|
- 修复表单校验先设置在校验及控制台错误信息问题
|
||||||
|
- 修复`modal`与`drawer`组件传递数组参数问题
|
||||||
|
|
||||||
### 🎫 Chores
|
### 🎫 Chores
|
||||||
|
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
export { default as BasicDrawer } from './src/BasicDrawer';
|
import BasicDrawerLib from './src/BasicDrawer';
|
||||||
|
import { withInstall } from '../util';
|
||||||
|
|
||||||
export { useDrawer, useDrawerInner } from './src/useDrawer';
|
|
||||||
export * from './src/types';
|
export * from './src/types';
|
||||||
|
export { useDrawer, useDrawerInner } from './src/useDrawer';
|
||||||
|
export const BasicDrawer = withInstall(BasicDrawerLib);
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
import type { DrawerInstance, DrawerProps } from './types';
|
import type { DrawerInstance, DrawerProps } from './types';
|
||||||
|
import type { CSSProperties } from 'vue';
|
||||||
|
|
||||||
import { defineComponent, ref, computed, watchEffect, watch, unref, nextTick, toRaw } from 'vue';
|
import { defineComponent, ref, computed, watchEffect, watch, unref, nextTick, toRaw } from 'vue';
|
||||||
import { Drawer, Row, Col, Button } from 'ant-design-vue';
|
import { Drawer, Row, Col, Button } from 'ant-design-vue';
|
||||||
@@ -9,53 +10,96 @@ import { BasicTitle } from '/@/components/Basic';
|
|||||||
import { FullLoading } from '/@/components/Loading/index';
|
import { FullLoading } from '/@/components/Loading/index';
|
||||||
import { LeftOutlined } from '@ant-design/icons-vue';
|
import { LeftOutlined } from '@ant-design/icons-vue';
|
||||||
|
|
||||||
import { basicProps } from './props';
|
import { useI18n } from '/@/hooks/web/useI18n';
|
||||||
|
|
||||||
import { getSlot } from '/@/utils/helper/tsxHelper';
|
import { getSlot } from '/@/utils/helper/tsxHelper';
|
||||||
import { isFunction, isNumber } from '/@/utils/is';
|
import { isFunction, isNumber } from '/@/utils/is';
|
||||||
import { buildUUID } from '/@/utils/uuid';
|
|
||||||
import { deepMerge } from '/@/utils';
|
import { deepMerge } from '/@/utils';
|
||||||
import { useI18n } from '/@/hooks/web/useI18n';
|
import { tryTsxEmit } from '/@/utils/helper/vueHelper';
|
||||||
|
|
||||||
|
import { basicProps } from './props';
|
||||||
|
|
||||||
const prefixCls = 'basic-drawer';
|
const prefixCls = 'basic-drawer';
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
// inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: basicProps,
|
props: basicProps,
|
||||||
emits: ['visible-change', 'ok', 'close', 'register'],
|
emits: ['visible-change', 'ok', 'close', 'register'],
|
||||||
setup(props, { slots, emit, attrs }) {
|
setup(props, { slots, emit, attrs }) {
|
||||||
const scrollRef = ref<ElRef>(null);
|
const scrollRef = ref<ElRef>(null);
|
||||||
|
|
||||||
const visibleRef = ref(false);
|
const visibleRef = ref(false);
|
||||||
const propsRef = ref<Partial<DrawerProps> | null>(null);
|
const propsRef = ref<Partial<Nullable<DrawerProps>>>(null);
|
||||||
|
|
||||||
const { t } = useI18n('component.drawer');
|
const { t } = useI18n('component.drawer');
|
||||||
|
|
||||||
const getMergeProps = computed((): any => {
|
const getMergeProps = computed(
|
||||||
|
(): DrawerProps => {
|
||||||
return deepMerge(toRaw(props), unref(propsRef));
|
return deepMerge(toRaw(props), unref(propsRef));
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const getProps = computed(() => {
|
const getProps = computed(
|
||||||
const opt: any = {
|
(): DrawerProps => {
|
||||||
|
const opt = {
|
||||||
placement: 'right',
|
placement: 'right',
|
||||||
...attrs,
|
...attrs,
|
||||||
...props,
|
...unref(getMergeProps),
|
||||||
...(unref(propsRef) as any),
|
|
||||||
visible: unref(visibleRef),
|
visible: unref(visibleRef),
|
||||||
};
|
};
|
||||||
opt.title = undefined;
|
opt.title = undefined;
|
||||||
|
const { isDetail, width, wrapClassName, getContainer } = opt;
|
||||||
if (opt.isDetail) {
|
if (isDetail) {
|
||||||
if (!opt.width) {
|
if (!width) {
|
||||||
opt.width = '100%';
|
opt.width = '100%';
|
||||||
}
|
}
|
||||||
opt.wrapClassName = opt.wrapClassName
|
const detailCls = `${prefixCls}__detail`;
|
||||||
? `${opt.wrapClassName} ${prefixCls}__detail`
|
|
||||||
: `${prefixCls}__detail`;
|
opt.wrapClassName = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls;
|
||||||
if (!opt.getContainer) {
|
|
||||||
opt.getContainer = '.layout-content';
|
if (!getContainer) {
|
||||||
|
// TODO type error?
|
||||||
|
opt.getContainer = '.layout-content' as any;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return opt;
|
return opt as DrawerProps;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const getBindValues = computed(
|
||||||
|
(): DrawerProps => {
|
||||||
|
return {
|
||||||
|
...attrs,
|
||||||
|
...unref(getProps),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Custom implementation of the bottom button,
|
||||||
|
const getFooterHeight = computed(() => {
|
||||||
|
const { footerHeight, showFooter } = unref(getProps);
|
||||||
|
|
||||||
|
if (showFooter && footerHeight) {
|
||||||
|
return isNumber(footerHeight) ? `${footerHeight}px` : `${footerHeight.replace('px', '')}px`;
|
||||||
|
}
|
||||||
|
return `0px`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const getScrollContentStyle = computed(
|
||||||
|
(): CSSProperties => {
|
||||||
|
const footerHeight = unref(getFooterHeight);
|
||||||
|
return {
|
||||||
|
position: 'relative',
|
||||||
|
height: `calc(100% - ${footerHeight})`,
|
||||||
|
overflow: 'auto',
|
||||||
|
padding: '16px',
|
||||||
|
paddingBottom: '30px',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const getLoading = computed(() => {
|
||||||
|
return {
|
||||||
|
hidden: !unref(getProps).loading,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
@@ -74,22 +118,13 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Custom implementation of the bottom button,
|
|
||||||
const getFooterHeight = computed(() => {
|
|
||||||
const { footerHeight, showFooter }: DrawerProps = unref(getProps);
|
|
||||||
if (showFooter && footerHeight) {
|
|
||||||
return isNumber(footerHeight) ? `${footerHeight}px` : `${footerHeight.replace('px', '')}px`;
|
|
||||||
}
|
|
||||||
return `0px`;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Cancel event
|
// Cancel event
|
||||||
async function onClose(e: any) {
|
async function onClose(e: ChangeEvent) {
|
||||||
const { closeFunc } = unref(getProps);
|
const { closeFunc } = unref(getProps);
|
||||||
emit('close', e);
|
emit('close', e);
|
||||||
if (closeFunc && isFunction(closeFunc)) {
|
if (closeFunc && isFunction(closeFunc)) {
|
||||||
const res = await closeFunc();
|
const res = await closeFunc();
|
||||||
res && (visibleRef.value = false);
|
visibleRef.value = !res;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
visibleRef.value = false;
|
visibleRef.value = false;
|
||||||
@@ -98,12 +133,16 @@ export default defineComponent({
|
|||||||
function setDrawerProps(props: Partial<DrawerProps>): void {
|
function setDrawerProps(props: Partial<DrawerProps>): void {
|
||||||
// Keep the last setDrawerProps
|
// Keep the last setDrawerProps
|
||||||
propsRef.value = deepMerge(unref(propsRef) || {}, props);
|
propsRef.value = deepMerge(unref(propsRef) || {}, props);
|
||||||
|
|
||||||
if (Reflect.has(props, 'visible')) {
|
if (Reflect.has(props, 'visible')) {
|
||||||
visibleRef.value = !!props.visible;
|
visibleRef.value = !!props.visible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderFooter() {
|
function renderFooter() {
|
||||||
|
if (slots?.footer) {
|
||||||
|
return getSlot(slots, 'footer');
|
||||||
|
}
|
||||||
const {
|
const {
|
||||||
showCancelBtn,
|
showCancelBtn,
|
||||||
cancelButtonProps,
|
cancelButtonProps,
|
||||||
@@ -114,14 +153,14 @@ export default defineComponent({
|
|||||||
okButtonProps,
|
okButtonProps,
|
||||||
confirmLoading,
|
confirmLoading,
|
||||||
showFooter,
|
showFooter,
|
||||||
}: DrawerProps = unref(getProps);
|
} = unref(getProps);
|
||||||
|
if (!showFooter) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
getSlot(slots, 'footer') ||
|
|
||||||
(showFooter && (
|
|
||||||
<div class={`${prefixCls}__footer`}>
|
<div class={`${prefixCls}__footer`}>
|
||||||
{getSlot(slots, 'insertFooter')}
|
{getSlot(slots, 'insertFooter')}
|
||||||
|
|
||||||
{showCancelBtn && (
|
{showCancelBtn && (
|
||||||
<Button {...cancelButtonProps} onClick={onClose} class="mr-2">
|
<Button {...cancelButtonProps} onClick={onClose} class="mr-2">
|
||||||
{() => cancelText}
|
{() => cancelText}
|
||||||
@@ -140,17 +179,21 @@ export default defineComponent({
|
|||||||
{() => okText}
|
{() => okText}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{getSlot(slots, 'appendFooter')}
|
{getSlot(slots, 'appendFooter')}
|
||||||
</div>
|
</div>
|
||||||
))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderHeader() {
|
function renderHeader() {
|
||||||
|
if (slots?.title) {
|
||||||
|
return getSlot(slots, 'title');
|
||||||
|
}
|
||||||
const { title } = unref(getMergeProps);
|
const { title } = unref(getMergeProps);
|
||||||
return props.isDetail ? (
|
|
||||||
getSlot(slots, 'title') || (
|
if (!props.isDetail) {
|
||||||
|
return <BasicTitle>{() => title || getSlot(slots, 'title')}</BasicTitle>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
<Row type="flex" align="middle" class={`${prefixCls}__detail-header`}>
|
<Row type="flex" align="middle" class={`${prefixCls}__detail-header`}>
|
||||||
{() => (
|
{() => (
|
||||||
<>
|
<>
|
||||||
@@ -159,20 +202,15 @@ export default defineComponent({
|
|||||||
{() => <LeftOutlined />}
|
{() => <LeftOutlined />}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{title && (
|
{title && (
|
||||||
<Col style="flex:1" class={[`${prefixCls}__detail-title`, 'ellipsis', 'px-2']}>
|
<Col style="flex:1" class={[`${prefixCls}__detail-title`, 'ellipsis', 'px-2']}>
|
||||||
{() => title}
|
{() => title}
|
||||||
</Col>
|
</Col>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{getSlot(slots, 'titleToolbar')}
|
{getSlot(slots, 'titleToolbar')}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Row>
|
</Row>
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<BasicTitle>{() => title || getSlot(slots, 'title')}</BasicTitle>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,41 +218,20 @@ export default defineComponent({
|
|||||||
setDrawerProps: setDrawerProps,
|
setDrawerProps: setDrawerProps,
|
||||||
};
|
};
|
||||||
|
|
||||||
const uuid = buildUUID();
|
tryTsxEmit((instance) => {
|
||||||
emit('register', drawerInstance, uuid);
|
emit('register', drawerInstance, instance.uid);
|
||||||
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
const footerHeight = unref(getFooterHeight);
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer class={prefixCls} onClose={onClose} {...unref(getBindValues)}>
|
||||||
class={prefixCls}
|
|
||||||
onClose={onClose}
|
|
||||||
{...{
|
|
||||||
...attrs,
|
|
||||||
...unref(getProps),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{{
|
{{
|
||||||
title: () => renderHeader(),
|
title: () => renderHeader(),
|
||||||
default: () => (
|
default: () => (
|
||||||
<>
|
<>
|
||||||
<div
|
<div ref={scrollRef} style={unref(getScrollContentStyle)}>
|
||||||
ref={scrollRef}
|
<FullLoading absolute tip={t('loadingText')} class={unref(getLoading)} />
|
||||||
{...attrs}
|
{getSlot(slots)}
|
||||||
style={{
|
|
||||||
position: 'relative',
|
|
||||||
height: `calc(100% - ${footerHeight})`,
|
|
||||||
overflow: 'auto',
|
|
||||||
padding: '16px',
|
|
||||||
paddingBottom: '30px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FullLoading
|
|
||||||
absolute
|
|
||||||
tip={t('loadingText')}
|
|
||||||
class={[!unref(getProps).loading ? 'hidden' : '']}
|
|
||||||
/>
|
|
||||||
{getSlot(slots, 'default')}
|
|
||||||
</div>
|
</div>
|
||||||
{renderFooter()}
|
{renderFooter()}
|
||||||
</>
|
</>
|
||||||
|
@@ -1,72 +1,37 @@
|
|||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
|
|
||||||
import { useI18n } from '/@/hooks/web/useI18n';
|
import { useI18n } from '/@/hooks/web/useI18n';
|
||||||
|
import { propTypes } from '/@/utils/propTypes';
|
||||||
const { t } = useI18n('component.drawer');
|
const { t } = useI18n('component.drawer');
|
||||||
|
|
||||||
export const footerProps = {
|
export const footerProps = {
|
||||||
confirmLoading: Boolean as PropType<boolean>,
|
confirmLoading: propTypes.bool,
|
||||||
/**
|
/**
|
||||||
* @description: Show close button
|
* @description: Show close button
|
||||||
*/
|
*/
|
||||||
showCancelBtn: {
|
showCancelBtn: propTypes.bool.def(true),
|
||||||
type: Boolean as PropType<boolean>,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
cancelButtonProps: Object as PropType<any>,
|
cancelButtonProps: Object as PropType<any>,
|
||||||
cancelText: {
|
cancelText: propTypes.string.def(t('cancelText')),
|
||||||
type: String as PropType<string>,
|
|
||||||
default: t('cancelText'),
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* @description: Show confirmation button
|
* @description: Show confirmation button
|
||||||
*/
|
*/
|
||||||
showOkBtn: {
|
showOkBtn: propTypes.bool.def(true),
|
||||||
type: Boolean as PropType<boolean>,
|
okButtonProps: propTypes.any,
|
||||||
default: true,
|
okText: propTypes.string.def(t('okText')),
|
||||||
},
|
okType: propTypes.string.def('primary'),
|
||||||
okButtonProps: Object as PropType<any>,
|
showFooter: propTypes.bool,
|
||||||
okText: {
|
|
||||||
type: String as PropType<string>,
|
|
||||||
default: t('okText'),
|
|
||||||
},
|
|
||||||
okType: {
|
|
||||||
type: String as PropType<string>,
|
|
||||||
default: 'primary',
|
|
||||||
},
|
|
||||||
showFooter: {
|
|
||||||
type: Boolean as PropType<boolean>,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
footerHeight: {
|
footerHeight: {
|
||||||
type: [String, Number] as PropType<string | number>,
|
type: [String, Number] as PropType<string | number>,
|
||||||
default: 60,
|
default: 60,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
export const basicProps = {
|
export const basicProps = {
|
||||||
isDetail: {
|
isDetail: propTypes.bool,
|
||||||
type: Boolean as PropType<boolean>,
|
title: propTypes.string.def(''),
|
||||||
default: false,
|
showDetailBack: propTypes.bool.def(true),
|
||||||
},
|
visible: propTypes.bool,
|
||||||
title: {
|
loading: propTypes.bool,
|
||||||
type: String as PropType<string>,
|
maskClosable: propTypes.bool.def(true),
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
showDetailBack: {
|
|
||||||
type: Boolean as PropType<boolean>,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
visible: {
|
|
||||||
type: Boolean as PropType<boolean>,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
loading: {
|
|
||||||
type: Boolean as PropType<boolean>,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
maskClosable: {
|
|
||||||
type: Boolean as PropType<boolean>,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
getContainer: {
|
getContainer: {
|
||||||
type: [Object, String] as PropType<any>,
|
type: [Object, String] as PropType<any>,
|
||||||
},
|
},
|
||||||
@@ -78,10 +43,7 @@ export const basicProps = {
|
|||||||
type: [Function, Object] as PropType<any>,
|
type: [Function, Object] as PropType<any>,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
triggerWindowResize: {
|
triggerWindowResize: propTypes.bool,
|
||||||
type: Boolean as PropType<boolean>,
|
destroyOnClose: propTypes.bool,
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
destroyOnClose: Boolean as PropType<boolean>,
|
|
||||||
...footerProps,
|
...footerProps,
|
||||||
};
|
};
|
||||||
|
@@ -75,7 +75,7 @@ export interface DrawerProps extends DrawerFooterProps {
|
|||||||
* @type ScrollContainerOptions
|
* @type ScrollContainerOptions
|
||||||
*/
|
*/
|
||||||
scrollOptions?: ScrollContainerOptions;
|
scrollOptions?: ScrollContainerOptions;
|
||||||
closeFunc?: () => Promise<void>;
|
closeFunc?: () => Promise<any>;
|
||||||
triggerWindowResize?: boolean;
|
triggerWindowResize?: boolean;
|
||||||
/**
|
/**
|
||||||
* Whether a close (x) button is visible on top right of the Drawer dialog or not.
|
* Whether a close (x) button is visible on top right of the Drawer dialog or not.
|
||||||
|
@@ -6,12 +6,15 @@ import type {
|
|||||||
UseDrawerInnerReturnType,
|
UseDrawerInnerReturnType,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
import { ref, getCurrentInstance, onUnmounted, unref, reactive, watchEffect, nextTick } from 'vue';
|
import { ref, getCurrentInstance, unref, reactive, watchEffect, nextTick, toRaw } from 'vue';
|
||||||
|
|
||||||
import { isProdMode } from '/@/utils/env';
|
import { isProdMode } from '/@/utils/env';
|
||||||
import { isFunction } from '/@/utils/is';
|
import { isFunction } from '/@/utils/is';
|
||||||
|
import { tryOnUnmounted } from '/@/utils/helper/vueHelper';
|
||||||
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
const dataTransferRef = reactive<any>({});
|
const dataTransferRef = reactive<any>({});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: Applicable to separate drawer and call outside
|
* @description: Applicable to separate drawer and call outside
|
||||||
*/
|
*/
|
||||||
@@ -19,21 +22,23 @@ export function useDrawer(): UseDrawerReturnType {
|
|||||||
if (!getCurrentInstance()) {
|
if (!getCurrentInstance()) {
|
||||||
throw new Error('Please put useDrawer function in the setup function!');
|
throw new Error('Please put useDrawer function in the setup function!');
|
||||||
}
|
}
|
||||||
|
|
||||||
const drawerRef = ref<DrawerInstance | null>(null);
|
const drawerRef = ref<DrawerInstance | null>(null);
|
||||||
const loadedRef = ref<boolean | null>(false);
|
const loadedRef = ref<Nullable<boolean>>(false);
|
||||||
const uidRef = ref<string>('');
|
const uidRef = ref<string>('');
|
||||||
|
|
||||||
function getDrawer(drawerInstance: DrawerInstance, uuid: string) {
|
function register(drawerInstance: DrawerInstance, uuid: string) {
|
||||||
uidRef.value = uuid;
|
|
||||||
isProdMode() &&
|
isProdMode() &&
|
||||||
onUnmounted(() => {
|
tryOnUnmounted(() => {
|
||||||
drawerRef.value = null;
|
drawerRef.value = null;
|
||||||
loadedRef.value = null;
|
loadedRef.value = null;
|
||||||
dataTransferRef[unref(uidRef)] = null;
|
dataTransferRef[unref(uidRef)] = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (unref(loadedRef) && isProdMode() && drawerInstance === unref(drawerRef)) {
|
if (unref(loadedRef) && isProdMode() && drawerInstance === unref(drawerRef)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
uidRef.value = uuid;
|
||||||
drawerRef.value = drawerInstance;
|
drawerRef.value = drawerInstance;
|
||||||
loadedRef.value = true;
|
loadedRef.value = true;
|
||||||
}
|
}
|
||||||
@@ -55,37 +60,46 @@ export function useDrawer(): UseDrawerReturnType {
|
|||||||
getInstance().setDrawerProps({
|
getInstance().setDrawerProps({
|
||||||
visible: visible,
|
visible: visible,
|
||||||
});
|
});
|
||||||
if (data) {
|
if (!data) return;
|
||||||
dataTransferRef[unref(uidRef)] = openOnSet
|
|
||||||
? {
|
if (openOnSet) {
|
||||||
...data,
|
dataTransferRef[unref(uidRef)] = null;
|
||||||
__t__: Date.now(),
|
dataTransferRef[unref(uidRef)] = data;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
: data;
|
const equal = isEqual(toRaw(dataTransferRef[unref(uidRef)]), data);
|
||||||
|
if (!equal) {
|
||||||
|
dataTransferRef[unref(uidRef)] = data;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return [getDrawer, methods];
|
return [register, methods];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useDrawerInner = (callbackFn?: Fn): UseDrawerInnerReturnType => {
|
export const useDrawerInner = (callbackFn?: Fn): UseDrawerInnerReturnType => {
|
||||||
const drawerInstanceRef = ref<DrawerInstance | null>(null);
|
const drawerInstanceRef = ref<Nullable<DrawerInstance>>(null);
|
||||||
const currentInstall = getCurrentInstance();
|
const currentInstall = getCurrentInstance();
|
||||||
const uidRef = ref<string>('');
|
const uidRef = ref<string>('');
|
||||||
|
|
||||||
if (!currentInstall) {
|
if (!currentInstall) {
|
||||||
throw new Error('instance is undefined!');
|
throw new Error('useDrawerInner instance is undefined!');
|
||||||
}
|
}
|
||||||
|
|
||||||
const getInstance = () => {
|
const getInstance = () => {
|
||||||
const instance = unref(drawerInstanceRef);
|
const instance = unref(drawerInstanceRef);
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
throw new Error('instance is undefined!');
|
throw new Error('useDrawerInner instance is undefined!');
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
const register = (modalInstance: DrawerInstance, uuid: string) => {
|
const register = (modalInstance: DrawerInstance, uuid: string) => {
|
||||||
|
isProdMode() &&
|
||||||
|
tryOnUnmounted(() => {
|
||||||
|
drawerInstanceRef.value = null;
|
||||||
|
});
|
||||||
|
|
||||||
uidRef.value = uuid;
|
uidRef.value = uuid;
|
||||||
drawerInstanceRef.value = modalInstance;
|
drawerInstanceRef.value = modalInstance;
|
||||||
currentInstall.emit('register', modalInstance);
|
currentInstall.emit('register', modalInstance);
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
import './src/index.less';
|
import './src/index.less';
|
||||||
export { default as BasicModal } from './src/BasicModal';
|
import BasicModalLib from './src/BasicModal';
|
||||||
export { default as Modal } from './src/Modal';
|
import { withInstall } from '../util';
|
||||||
|
|
||||||
|
export { useModalContext } from './src/useModalContext';
|
||||||
export { useModal, useModalInner } from './src/useModal';
|
export { useModal, useModalInner } from './src/useModal';
|
||||||
export * from './src/types';
|
export * from './src/types';
|
||||||
|
export const BasicModal = withInstall(BasicModalLib);
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import type { ModalProps, ModalMethods } from './types';
|
import type { ModalProps, ModalMethods } from './types';
|
||||||
|
|
||||||
import { defineComponent, computed, ref, watch, unref, watchEffect } from 'vue';
|
import { defineComponent, computed, ref, watch, unref, watchEffect, toRef } from 'vue';
|
||||||
|
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import { Button } from '/@/components/Button';
|
import { Button } from '/@/components/Button';
|
||||||
@@ -11,10 +11,10 @@ import { FullscreenExitOutlined, FullscreenOutlined, CloseOutlined } from '@ant-
|
|||||||
import { getSlot, extendSlots } from '/@/utils/helper/tsxHelper';
|
import { getSlot, extendSlots } from '/@/utils/helper/tsxHelper';
|
||||||
import { isFunction } from '/@/utils/is';
|
import { isFunction } from '/@/utils/is';
|
||||||
import { deepMerge } from '/@/utils';
|
import { deepMerge } from '/@/utils';
|
||||||
import { buildUUID } from '/@/utils/uuid';
|
import { tryTsxEmit } from '/@/utils/helper/vueHelper';
|
||||||
|
|
||||||
import { basicProps } from './props';
|
import { basicProps } from './props';
|
||||||
// import { triggerWindowResize } from '@/utils/event/triggerWindowResizeEvent';
|
import { useFullScreen } from './useFullScreen';
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'BasicModal',
|
name: 'BasicModal',
|
||||||
props: basicProps,
|
props: basicProps,
|
||||||
@@ -26,31 +26,41 @@ export default defineComponent({
|
|||||||
// modal Bottom and top height
|
// modal Bottom and top height
|
||||||
const extHeightRef = ref(0);
|
const extHeightRef = ref(0);
|
||||||
// Unexpanded height of the popup
|
// Unexpanded height of the popup
|
||||||
const formerHeightRef = ref(0);
|
|
||||||
const fullScreenRef = ref(false);
|
|
||||||
|
|
||||||
// Custom title component: get title
|
// Custom title component: get title
|
||||||
const getMergeProps = computed(() => {
|
const getMergeProps = computed(
|
||||||
|
(): ModalProps => {
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
...(unref(propsRef) as any),
|
...(unref(propsRef) as any),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const { handleFullScreen, getWrapClassName, fullScreenRef } = useFullScreen({
|
||||||
|
modalWrapperRef,
|
||||||
|
extHeightRef,
|
||||||
|
wrapClassName: toRef(getMergeProps.value, 'wrapClassName'),
|
||||||
});
|
});
|
||||||
|
|
||||||
// modal component does not need title
|
// modal component does not need title
|
||||||
const getProps = computed((): any => {
|
const getProps = computed(
|
||||||
|
(): ModalProps => {
|
||||||
const opt = {
|
const opt = {
|
||||||
...props,
|
...unref(getMergeProps),
|
||||||
...((unref(propsRef) || {}) as any),
|
|
||||||
visible: unref(visibleRef),
|
visible: unref(visibleRef),
|
||||||
title: undefined,
|
title: undefined,
|
||||||
};
|
};
|
||||||
const { wrapClassName = '' } = opt;
|
|
||||||
const className = unref(fullScreenRef) ? `${wrapClassName} fullscreen-modal` : wrapClassName;
|
|
||||||
return {
|
return {
|
||||||
...opt,
|
...opt,
|
||||||
wrapClassName: className,
|
wrapClassName: unref(getWrapClassName),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const getModalBindValue = computed((): any => {
|
||||||
|
return { ...attrs, ...unref(getProps) };
|
||||||
});
|
});
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
@@ -80,7 +90,35 @@ export default defineComponent({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 取消事件
|
||||||
|
async function handleCancel(e: Event) {
|
||||||
|
e?.stopPropagation();
|
||||||
|
|
||||||
|
if (props.closeFunc && isFunction(props.closeFunc)) {
|
||||||
|
const isClose: boolean = await props.closeFunc();
|
||||||
|
visibleRef.value = !isClose;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
visibleRef.value = false;
|
||||||
|
emit('cancel');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 设置modal参数
|
||||||
|
*/
|
||||||
|
function setModalProps(props: Partial<ModalProps>): void {
|
||||||
|
// Keep the last setModalProps
|
||||||
|
propsRef.value = deepMerge(unref(propsRef) || {}, props);
|
||||||
|
if (!Reflect.has(props, 'visible')) return;
|
||||||
|
visibleRef.value = !!props.visible;
|
||||||
|
}
|
||||||
|
|
||||||
function renderContent() {
|
function renderContent() {
|
||||||
|
type OmitWrapperType = Omit<
|
||||||
|
ModalProps,
|
||||||
|
'fullScreen' | 'modalFooterHeight' | 'visible' | 'loading'
|
||||||
|
>;
|
||||||
const { useWrapper, loading, wrapperProps } = unref(getProps);
|
const { useWrapper, loading, wrapperProps } = unref(getProps);
|
||||||
if (!useWrapper) return getSlot(slots);
|
if (!useWrapper) return getSlot(slots);
|
||||||
|
|
||||||
@@ -93,7 +131,7 @@ export default defineComponent({
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
visible={unref(visibleRef)}
|
visible={unref(visibleRef)}
|
||||||
modalFooterHeight={showFooter}
|
modalFooterHeight={showFooter}
|
||||||
{...wrapperProps}
|
{...((wrapperProps as unknown) as OmitWrapperType)}
|
||||||
onGetExtHeight={(height: number) => {
|
onGetExtHeight={(height: number) => {
|
||||||
extHeightRef.value = height;
|
extHeightRef.value = height;
|
||||||
}}
|
}}
|
||||||
@@ -106,18 +144,6 @@ export default defineComponent({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 取消事件
|
|
||||||
async function handleCancel(e: Event) {
|
|
||||||
e && e.stopPropagation();
|
|
||||||
if (props.closeFunc && isFunction(props.closeFunc)) {
|
|
||||||
const isClose: boolean = await props.closeFunc();
|
|
||||||
visibleRef.value = !isClose;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
visibleRef.value = false;
|
|
||||||
emit('cancel');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 底部按钮自定义实现,
|
// 底部按钮自定义实现,
|
||||||
function renderFooter() {
|
function renderFooter() {
|
||||||
const {
|
const {
|
||||||
@@ -162,64 +188,37 @@ export default defineComponent({
|
|||||||
*/
|
*/
|
||||||
function renderClose() {
|
function renderClose() {
|
||||||
const { canFullscreen } = unref(getProps);
|
const { canFullscreen } = unref(getProps);
|
||||||
if (!canFullscreen) {
|
|
||||||
return null;
|
const fullScreen = unref(fullScreenRef) ? (
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div class="custom-close-icon">
|
|
||||||
{unref(fullScreenRef) ? (
|
|
||||||
<FullscreenExitOutlined role="full" onClick={handleFullScreen} />
|
<FullscreenExitOutlined role="full" onClick={handleFullScreen} />
|
||||||
) : (
|
) : (
|
||||||
<FullscreenOutlined role="close" onClick={handleFullScreen} />
|
<FullscreenOutlined role="close" onClick={handleFullScreen} />
|
||||||
)}
|
);
|
||||||
|
|
||||||
|
const cls = [
|
||||||
|
'custom-close-icon',
|
||||||
|
{
|
||||||
|
'can-full': canFullscreen,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={cls}>
|
||||||
|
{canFullscreen && fullScreen}
|
||||||
<CloseOutlined onClick={handleCancel} />
|
<CloseOutlined onClick={handleCancel} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFullScreen(e: Event) {
|
|
||||||
e && e.stopPropagation();
|
|
||||||
fullScreenRef.value = !unref(fullScreenRef);
|
|
||||||
|
|
||||||
const modalWrapper = unref(modalWrapperRef);
|
|
||||||
if (!modalWrapper) return;
|
|
||||||
|
|
||||||
const wrapperEl = modalWrapper.$el as HTMLElement;
|
|
||||||
if (!wrapperEl) return;
|
|
||||||
|
|
||||||
const modalWrapSpinEl = wrapperEl.querySelector('.ant-spin-nested-loading') as HTMLElement;
|
|
||||||
if (!modalWrapSpinEl) return;
|
|
||||||
|
|
||||||
if (!unref(formerHeightRef) && unref(fullScreenRef)) {
|
|
||||||
formerHeightRef.value = modalWrapSpinEl.offsetHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unref(fullScreenRef)) {
|
|
||||||
modalWrapSpinEl.style.height = `${window.innerHeight - unref(extHeightRef)}px`;
|
|
||||||
} else {
|
|
||||||
modalWrapSpinEl.style.height = `${unref(formerHeightRef)}px`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description: 设置modal参数
|
|
||||||
*/
|
|
||||||
function setModalProps(props: Partial<ModalProps>): void {
|
|
||||||
// Keep the last setModalProps
|
|
||||||
propsRef.value = deepMerge(unref(propsRef) || {}, props);
|
|
||||||
if (!Reflect.has(props, 'visible')) return;
|
|
||||||
visibleRef.value = !!props.visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
const modalMethods: ModalMethods = {
|
const modalMethods: ModalMethods = {
|
||||||
setModalProps,
|
setModalProps,
|
||||||
};
|
};
|
||||||
|
|
||||||
const uuid = buildUUID();
|
tryTsxEmit((instance) => {
|
||||||
emit('register', modalMethods, uuid);
|
emit('register', modalMethods, instance.uid);
|
||||||
|
});
|
||||||
return () => (
|
return () => (
|
||||||
<Modal onCancel={handleCancel} {...{ ...attrs, ...props, ...unref(getProps) }}>
|
<Modal onCancel={handleCancel} {...unref(getModalBindValue)}>
|
||||||
{{
|
{{
|
||||||
footer: () => renderFooter(),
|
footer: () => renderFooter(),
|
||||||
closeIcon: () => renderClose(),
|
closeIcon: () => renderClose(),
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Modal } from 'ant-design-vue';
|
import { Modal } from 'ant-design-vue';
|
||||||
import { defineComponent, watchEffect } from 'vue';
|
import { defineComponent, toRefs } from 'vue';
|
||||||
import { basicProps } from './props';
|
import { basicProps } from './props';
|
||||||
import { useTimeoutFn } from '/@/hooks/core/useTimeout';
|
import { useModalDragMove } from './useModalDrag';
|
||||||
import { extendSlots } from '/@/utils/helper/tsxHelper';
|
import { extendSlots } from '/@/utils/helper/tsxHelper';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -9,99 +9,12 @@ export default defineComponent({
|
|||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: basicProps,
|
props: basicProps,
|
||||||
setup(props, { attrs, slots }) {
|
setup(props, { attrs, slots }) {
|
||||||
const getStyle = (dom: any, attr: any) => {
|
const { visible, draggable, destroyOnClose } = toRefs(props);
|
||||||
return getComputedStyle(dom)[attr];
|
|
||||||
};
|
|
||||||
const drag = (wrap: any) => {
|
|
||||||
if (!wrap) return;
|
|
||||||
wrap.setAttribute('data-drag', props.draggable);
|
|
||||||
const dialogHeaderEl = wrap.querySelector('.ant-modal-header');
|
|
||||||
const dragDom = wrap.querySelector('.ant-modal');
|
|
||||||
|
|
||||||
if (!dialogHeaderEl || !dragDom || !props.draggable) return;
|
useModalDragMove({
|
||||||
|
visible,
|
||||||
dialogHeaderEl.style.cursor = 'move';
|
destroyOnClose,
|
||||||
|
draggable,
|
||||||
dialogHeaderEl.onmousedown = (e: any) => {
|
|
||||||
if (!e) return;
|
|
||||||
// 鼠标按下,计算当前元素距离可视区的距离
|
|
||||||
const disX = e.clientX;
|
|
||||||
const disY = e.clientY;
|
|
||||||
const screenWidth = document.body.clientWidth; // body当前宽度
|
|
||||||
const screenHeight = document.documentElement.clientHeight; // 可见区域高度(应为body高度,可某些环境下无法获取)
|
|
||||||
|
|
||||||
const dragDomWidth = dragDom.offsetWidth; // 对话框宽度
|
|
||||||
const dragDomheight = dragDom.offsetHeight; // 对话框高度
|
|
||||||
|
|
||||||
const minDragDomLeft = dragDom.offsetLeft;
|
|
||||||
|
|
||||||
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth;
|
|
||||||
const minDragDomTop = dragDom.offsetTop;
|
|
||||||
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight;
|
|
||||||
// 获取到的值带px 正则匹配替换
|
|
||||||
const domLeft = getStyle(dragDom, 'left');
|
|
||||||
const domTop = getStyle(dragDom, 'top');
|
|
||||||
let styL = +domLeft;
|
|
||||||
let styT = +domTop;
|
|
||||||
|
|
||||||
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
|
|
||||||
if (domLeft.includes('%')) {
|
|
||||||
styL = +document.body.clientWidth * (+domLeft.replace(/%/g, '') / 100);
|
|
||||||
styT = +document.body.clientHeight * (+domTop.replace(/%/g, '') / 100);
|
|
||||||
} else {
|
|
||||||
styL = +domLeft.replace(/px/g, '');
|
|
||||||
styT = +domTop.replace(/px/g, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
document.onmousemove = function (e) {
|
|
||||||
// 通过事件委托,计算移动的距离
|
|
||||||
let left = e.clientX - disX;
|
|
||||||
let top = e.clientY - disY;
|
|
||||||
|
|
||||||
// 边界处理
|
|
||||||
if (-left > minDragDomLeft) {
|
|
||||||
left = -minDragDomLeft;
|
|
||||||
} else if (left > maxDragDomLeft) {
|
|
||||||
left = maxDragDomLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-top > minDragDomTop) {
|
|
||||||
top = -minDragDomTop;
|
|
||||||
} else if (top > maxDragDomTop) {
|
|
||||||
top = maxDragDomTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移动当前元素
|
|
||||||
dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`;
|
|
||||||
};
|
|
||||||
|
|
||||||
document.onmouseup = () => {
|
|
||||||
document.onmousemove = null;
|
|
||||||
document.onmouseup = null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDrag = () => {
|
|
||||||
const dragWraps = document.querySelectorAll('.ant-modal-wrap');
|
|
||||||
for (const wrap of dragWraps as any) {
|
|
||||||
if (!wrap) continue;
|
|
||||||
const display = getStyle(wrap, 'display');
|
|
||||||
const draggable = wrap.getAttribute('data-drag');
|
|
||||||
if (display !== 'none') {
|
|
||||||
// 拖拽位置
|
|
||||||
(draggable === null || props.destroyOnClose) && drag(wrap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
if (!props.visible) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
useTimeoutFn(() => {
|
|
||||||
handleDrag();
|
|
||||||
}, 30);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import type { PropType } from 'vue';
|
|
||||||
import type { ModalWrapperProps } from './types';
|
import type { ModalWrapperProps } from './types';
|
||||||
|
import type { CSSProperties } from 'vue';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
defineComponent,
|
defineComponent,
|
||||||
@@ -18,58 +18,43 @@ import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
|
|||||||
|
|
||||||
import { getSlot } from '/@/utils/helper/tsxHelper';
|
import { getSlot } from '/@/utils/helper/tsxHelper';
|
||||||
import { useElResize } from '/@/hooks/event/useElResize';
|
import { useElResize } from '/@/hooks/event/useElResize';
|
||||||
import { provideModal } from './provideModal';
|
import { propTypes } from '/@/utils/propTypes';
|
||||||
|
import { createModalContext } from './useModalContext';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'ModalWrapper',
|
name: 'ModalWrapper',
|
||||||
props: {
|
props: {
|
||||||
loading: {
|
loading: propTypes.bool,
|
||||||
type: Boolean as PropType<boolean>,
|
modalHeaderHeight: propTypes.number.def(50),
|
||||||
default: false,
|
modalFooterHeight: propTypes.number.def(54),
|
||||||
},
|
minHeight: propTypes.number.def(200),
|
||||||
modalHeaderHeight: {
|
footerOffset: propTypes.number.def(0),
|
||||||
type: Number as PropType<number>,
|
visible: propTypes.bool,
|
||||||
default: 50,
|
fullScreen: propTypes.bool,
|
||||||
},
|
|
||||||
modalFooterHeight: {
|
|
||||||
type: Number as PropType<number>,
|
|
||||||
default: 70,
|
|
||||||
},
|
|
||||||
minHeight: {
|
|
||||||
type: Number as PropType<number>,
|
|
||||||
default: 200,
|
|
||||||
},
|
|
||||||
footerOffset: {
|
|
||||||
type: Number as PropType<number>,
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
visible: {
|
|
||||||
type: Boolean as PropType<boolean>,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
fullScreen: {
|
|
||||||
type: Boolean as PropType<boolean>,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
emits: ['heightChange', 'getExtHeight'],
|
emits: ['heightChange', 'getExtHeight'],
|
||||||
setup(props: ModalWrapperProps, { slots, emit }) {
|
setup(props: ModalWrapperProps, { slots, emit }) {
|
||||||
const wrapperRef = ref<HTMLElement | null>(null);
|
const wrapperRef = ref<ElRef>(null);
|
||||||
const spinRef = ref<ComponentRef>(null);
|
const spinRef = ref<ComponentRef>(null);
|
||||||
const realHeightRef = ref(0);
|
const realHeightRef = ref(0);
|
||||||
// 重试次数
|
|
||||||
// let tryCount = 0;
|
|
||||||
let stopElResizeFn: Fn = () => {};
|
let stopElResizeFn: Fn = () => {};
|
||||||
|
|
||||||
provideModal(setModalHeight);
|
useWindowSizeFn(setModalHeight);
|
||||||
|
|
||||||
const wrapStyle = computed(() => {
|
createModalContext({
|
||||||
|
redoModalHeight: setModalHeight,
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapStyle = computed(
|
||||||
|
(): CSSProperties => {
|
||||||
return {
|
return {
|
||||||
minHeight: `${props.minHeight}px`,
|
minHeight: `${props.minHeight}px`,
|
||||||
height: `${unref(realHeightRef)}px`,
|
height: `${unref(realHeightRef)}px`,
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
setModalHeight();
|
setModalHeight();
|
||||||
@@ -92,8 +77,6 @@ export default defineComponent({
|
|||||||
stopElResizeFn && stopElResizeFn();
|
stopElResizeFn && stopElResizeFn();
|
||||||
});
|
});
|
||||||
|
|
||||||
useWindowSizeFn(setModalHeight);
|
|
||||||
|
|
||||||
async function setModalHeight() {
|
async function setModalHeight() {
|
||||||
// 解决在弹窗关闭的时候监听还存在,导致再次打开弹窗没有高度
|
// 解决在弹窗关闭的时候监听还存在,导致再次打开弹窗没有高度
|
||||||
// 加上这个,就必须在使用的时候传递父级的visible
|
// 加上这个,就必须在使用的时候传递父级的visible
|
||||||
@@ -107,9 +90,8 @@ export default defineComponent({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const modalDom = bodyDom.parentElement && bodyDom.parentElement.parentElement;
|
const modalDom = bodyDom.parentElement && bodyDom.parentElement.parentElement;
|
||||||
if (!modalDom) {
|
if (!modalDom) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
const modalRect = getComputedStyle(modalDom).top;
|
const modalRect = getComputedStyle(modalDom).top;
|
||||||
const modalTop = Number.parseInt(modalRect);
|
const modalTop = Number.parseInt(modalRect);
|
||||||
let maxHeight =
|
let maxHeight =
|
||||||
@@ -135,11 +117,12 @@ export default defineComponent({
|
|||||||
|
|
||||||
if (props.fullScreen) {
|
if (props.fullScreen) {
|
||||||
realHeightRef.value =
|
realHeightRef.value =
|
||||||
window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight - 6;
|
window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight;
|
||||||
} else {
|
} else {
|
||||||
realHeightRef.value = realHeight > maxHeight ? maxHeight : realHeight + 16 + 30;
|
realHeightRef.value = realHeight > maxHeight ? maxHeight : realHeight + 16 + 30;
|
||||||
}
|
}
|
||||||
emit('heightChange', unref(realHeightRef));
|
emit('heightChange', unref(realHeightRef));
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const el = spinEl.$el;
|
const el = spinEl.$el;
|
||||||
if (el) {
|
if (el) {
|
||||||
@@ -154,8 +137,10 @@ export default defineComponent({
|
|||||||
function listenElResize() {
|
function listenElResize() {
|
||||||
const wrapper = unref(wrapperRef);
|
const wrapper = unref(wrapperRef);
|
||||||
if (!wrapper) return;
|
if (!wrapper) return;
|
||||||
|
|
||||||
const container = wrapper.querySelector('.ant-spin-container');
|
const container = wrapper.querySelector('.ant-spin-container');
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
const [start, stop] = useElResize(container, () => {
|
const [start, stop] = useElResize(container, () => {
|
||||||
setModalHeight();
|
setModalHeight();
|
||||||
});
|
});
|
||||||
|
@@ -9,6 +9,11 @@
|
|||||||
bottom: 0 !important;
|
bottom: 0 !important;
|
||||||
left: 0 !important;
|
left: 0 !important;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,9 +40,24 @@
|
|||||||
height: 95%;
|
height: 95%;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
> * {
|
> span {
|
||||||
|
margin-left: 48px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.can-full {
|
||||||
|
> span {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.can-full) {
|
||||||
|
> span:nth-child(1) {
|
||||||
|
&:hover {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
& span:nth-child(1) {
|
& span:nth-child(1) {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -76,7 +96,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-footer {
|
&-footer {
|
||||||
padding: 10px 26px 26px 16px;
|
// padding: 10px 26px 26px 16px;
|
||||||
|
|
||||||
button + button {
|
button + button {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
|
@@ -2,66 +2,38 @@ import type { PropType } from 'vue';
|
|||||||
import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes';
|
import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes';
|
||||||
|
|
||||||
import { useI18n } from '/@/hooks/web/useI18n';
|
import { useI18n } from '/@/hooks/web/useI18n';
|
||||||
|
import { propTypes } from '/@/utils/propTypes';
|
||||||
const { t } = useI18n('component.modal');
|
const { t } = useI18n('component.modal');
|
||||||
|
|
||||||
export const modalProps = {
|
export const modalProps = {
|
||||||
visible: Boolean as PropType<boolean>,
|
visible: propTypes.bool,
|
||||||
// open drag
|
// open drag
|
||||||
draggable: {
|
draggable: propTypes.bool.def(true),
|
||||||
type: Boolean as PropType<boolean>,
|
centered: propTypes.bool,
|
||||||
default: true,
|
cancelText: propTypes.string.def(t('cancelText')),
|
||||||
},
|
okText: propTypes.string.def(t('okText')),
|
||||||
centered: {
|
|
||||||
type: Boolean as PropType<boolean>,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
cancelText: {
|
|
||||||
type: String as PropType<string>,
|
|
||||||
default: t('cancelText'),
|
|
||||||
},
|
|
||||||
okText: {
|
|
||||||
type: String as PropType<string>,
|
|
||||||
default: t('okText'),
|
|
||||||
},
|
|
||||||
closeFunc: Function as PropType<() => Promise<boolean>>,
|
closeFunc: Function as PropType<() => Promise<boolean>>,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const basicProps = Object.assign({}, modalProps, {
|
export const basicProps = Object.assign({}, modalProps, {
|
||||||
// Can it be full screen
|
// Can it be full screen
|
||||||
canFullscreen: {
|
canFullscreen: propTypes.bool.def(true),
|
||||||
type: Boolean as PropType<boolean>,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
// After enabling the wrapper, the bottom can be increased in height
|
// After enabling the wrapper, the bottom can be increased in height
|
||||||
wrapperFooterOffset: {
|
wrapperFooterOffset: propTypes.number.def(0),
|
||||||
type: Number as PropType<number>,
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
// Warm reminder message
|
// Warm reminder message
|
||||||
helpMessage: [String, Array] as PropType<string | string[]>,
|
helpMessage: [String, Array] as PropType<string | string[]>,
|
||||||
// Whether to setting wrapper
|
// Whether to setting wrapper
|
||||||
useWrapper: {
|
useWrapper: propTypes.bool.def(true),
|
||||||
type: Boolean as PropType<boolean>,
|
loading: propTypes.bool,
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
loading: {
|
|
||||||
type: Boolean as PropType<boolean>,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* @description: Show close button
|
* @description: Show close button
|
||||||
*/
|
*/
|
||||||
showCancelBtn: {
|
showCancelBtn: propTypes.bool.def(true),
|
||||||
type: Boolean as PropType<boolean>,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* @description: Show confirmation button
|
* @description: Show confirmation button
|
||||||
*/
|
*/
|
||||||
showOkBtn: {
|
showOkBtn: propTypes.bool.def(true),
|
||||||
type: Boolean as PropType<boolean>,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
wrapperProps: Object as PropType<any>,
|
wrapperProps: Object as PropType<any>,
|
||||||
|
|
||||||
|
@@ -1,11 +0,0 @@
|
|||||||
import { provide, inject } from 'vue';
|
|
||||||
|
|
||||||
const key = Symbol('basic-modal');
|
|
||||||
|
|
||||||
export function provideModal(redoHeight: Fn) {
|
|
||||||
provide(key, redoHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function injectModal(): Fn {
|
|
||||||
return inject(key, () => {}) as Fn;
|
|
||||||
}
|
|
@@ -8,9 +8,11 @@ export interface ModalMethods {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type RegisterFn = (modalMethods: ModalMethods, uuid?: string) => void;
|
export type RegisterFn = (modalMethods: ModalMethods, uuid?: string) => void;
|
||||||
|
|
||||||
export interface ReturnMethods extends ModalMethods {
|
export interface ReturnMethods extends ModalMethods {
|
||||||
openModal: <T = any>(props?: boolean, data?: T, openOnSet?: boolean) => void;
|
openModal: <T = any>(props?: boolean, data?: T, openOnSet?: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UseModalReturnType = [RegisterFn, ReturnMethods];
|
export type UseModalReturnType = [RegisterFn, ReturnMethods];
|
||||||
|
|
||||||
export interface ReturnInnerMethods extends ModalMethods {
|
export interface ReturnInnerMethods extends ModalMethods {
|
||||||
@@ -18,6 +20,7 @@ export interface ReturnInnerMethods extends ModalMethods {
|
|||||||
changeLoading: (loading: boolean) => void;
|
changeLoading: (loading: boolean) => void;
|
||||||
changeOkLoading: (loading: boolean) => void;
|
changeOkLoading: (loading: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UseModalInnerReturnType = [RegisterFn, ReturnInnerMethods];
|
export type UseModalInnerReturnType = [RegisterFn, ReturnInnerMethods];
|
||||||
|
|
||||||
export interface ModalProps {
|
export interface ModalProps {
|
||||||
|
44
src/components/Modal/src/useFullScreen.ts
Normal file
44
src/components/Modal/src/useFullScreen.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { computed, Ref, ref, unref } from 'vue';
|
||||||
|
|
||||||
|
export interface UseFullScreenContext {
|
||||||
|
wrapClassName: Ref<string | undefined>;
|
||||||
|
modalWrapperRef: Ref<ComponentRef>;
|
||||||
|
extHeightRef: Ref<number>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useFullScreen(context: UseFullScreenContext) {
|
||||||
|
const formerHeightRef = ref(0);
|
||||||
|
const fullScreenRef = ref(false);
|
||||||
|
|
||||||
|
const getWrapClassName = computed(() => {
|
||||||
|
const clsName = unref(context.wrapClassName) || '';
|
||||||
|
|
||||||
|
return unref(fullScreenRef) ? `fullscreen-modal ${clsName} ` : unref(clsName);
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleFullScreen(e: Event) {
|
||||||
|
e && e.stopPropagation();
|
||||||
|
fullScreenRef.value = !unref(fullScreenRef);
|
||||||
|
|
||||||
|
const modalWrapper = unref(context.modalWrapperRef);
|
||||||
|
|
||||||
|
if (!modalWrapper) return;
|
||||||
|
|
||||||
|
const wrapperEl = modalWrapper.$el as HTMLElement;
|
||||||
|
if (!wrapperEl) return;
|
||||||
|
const modalWrapSpinEl = wrapperEl.querySelector('.ant-spin-nested-loading') as HTMLElement;
|
||||||
|
|
||||||
|
if (!modalWrapSpinEl) return;
|
||||||
|
|
||||||
|
if (!unref(formerHeightRef) && unref(fullScreenRef)) {
|
||||||
|
formerHeightRef.value = modalWrapSpinEl.offsetHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unref(fullScreenRef)) {
|
||||||
|
modalWrapSpinEl.style.height = `${window.innerHeight - unref(context.extHeightRef)}px`;
|
||||||
|
} else {
|
||||||
|
modalWrapSpinEl.style.height = `${unref(formerHeightRef)}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { getWrapClassName, handleFullScreen, fullScreenRef };
|
||||||
|
}
|
@@ -5,9 +5,21 @@ import type {
|
|||||||
ReturnMethods,
|
ReturnMethods,
|
||||||
UseModalInnerReturnType,
|
UseModalInnerReturnType,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { ref, onUnmounted, unref, getCurrentInstance, reactive, watchEffect, nextTick } from 'vue';
|
|
||||||
|
import {
|
||||||
|
ref,
|
||||||
|
onUnmounted,
|
||||||
|
unref,
|
||||||
|
getCurrentInstance,
|
||||||
|
reactive,
|
||||||
|
watchEffect,
|
||||||
|
nextTick,
|
||||||
|
toRaw,
|
||||||
|
} from 'vue';
|
||||||
import { isProdMode } from '/@/utils/env';
|
import { isProdMode } from '/@/utils/env';
|
||||||
import { isFunction } from '/@/utils/is';
|
import { isFunction } from '/@/utils/is';
|
||||||
|
import { isEqual } from 'lodash-es';
|
||||||
|
import { tryOnUnmounted } from '/@/utils/helper/vueHelper';
|
||||||
const dataTransferRef = reactive<any>({});
|
const dataTransferRef = reactive<any>({});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,6 +32,7 @@ export function useModal(): UseModalReturnType {
|
|||||||
const modalRef = ref<Nullable<ModalMethods>>(null);
|
const modalRef = ref<Nullable<ModalMethods>>(null);
|
||||||
const loadedRef = ref<Nullable<boolean>>(false);
|
const loadedRef = ref<Nullable<boolean>>(false);
|
||||||
const uidRef = ref<string>('');
|
const uidRef = ref<string>('');
|
||||||
|
|
||||||
function register(modalMethod: ModalMethods, uuid: string) {
|
function register(modalMethod: ModalMethods, uuid: string) {
|
||||||
uidRef.value = uuid;
|
uidRef.value = uuid;
|
||||||
|
|
||||||
@@ -52,13 +65,16 @@ export function useModal(): UseModalReturnType {
|
|||||||
visible: visible,
|
visible: visible,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (data) {
|
if (!data) return;
|
||||||
dataTransferRef[unref(uidRef)] = openOnSet
|
|
||||||
? {
|
if (openOnSet) {
|
||||||
...data,
|
dataTransferRef[unref(uidRef)] = null;
|
||||||
__t__: Date.now(),
|
dataTransferRef[unref(uidRef)] = data;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
: data;
|
const equal = isEqual(toRaw(dataTransferRef[unref(uidRef)]), data);
|
||||||
|
if (!equal) {
|
||||||
|
dataTransferRef[unref(uidRef)] = data;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -66,7 +82,7 @@ export function useModal(): UseModalReturnType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
|
export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
|
||||||
const modalInstanceRef = ref<ModalMethods | null>(null);
|
const modalInstanceRef = ref<Nullable<ModalMethods>>(null);
|
||||||
const currentInstall = getCurrentInstance();
|
const currentInstall = getCurrentInstance();
|
||||||
const uidRef = ref<string>('');
|
const uidRef = ref<string>('');
|
||||||
|
|
||||||
@@ -83,6 +99,11 @@ export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const register = (modalInstance: ModalMethods, uuid: string) => {
|
const register = (modalInstance: ModalMethods, uuid: string) => {
|
||||||
|
isProdMode() &&
|
||||||
|
tryOnUnmounted(() => {
|
||||||
|
modalInstanceRef.value = null;
|
||||||
|
});
|
||||||
|
|
||||||
uidRef.value = uuid;
|
uidRef.value = uuid;
|
||||||
modalInstanceRef.value = modalInstance;
|
modalInstanceRef.value = modalInstance;
|
||||||
currentInstall.emit('register', modalInstance);
|
currentInstall.emit('register', modalInstance);
|
||||||
|
16
src/components/Modal/src/useModalContext.ts
Normal file
16
src/components/Modal/src/useModalContext.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { InjectionKey } from 'vue';
|
||||||
|
import { createContext, useContext } from '/@/hooks/core/useContext';
|
||||||
|
|
||||||
|
export interface ModalContextProps {
|
||||||
|
redoModalHeight: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modalContextInjectKey: InjectionKey<ModalContextProps> = Symbol();
|
||||||
|
|
||||||
|
export function createModalContext(context: ModalContextProps) {
|
||||||
|
return createContext<ModalContextProps>(context, modalContextInjectKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useModalContext() {
|
||||||
|
return useContext<ModalContextProps>(modalContextInjectKey);
|
||||||
|
}
|
107
src/components/Modal/src/useModalDrag.ts
Normal file
107
src/components/Modal/src/useModalDrag.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import { Ref, unref, watchEffect } from 'vue';
|
||||||
|
import { useTimeoutFn } from '/@/hooks/core/useTimeout';
|
||||||
|
|
||||||
|
export interface UseModalDragMoveContext {
|
||||||
|
draggable: Ref<boolean>;
|
||||||
|
destroyOnClose: Ref<boolean | undefined> | undefined;
|
||||||
|
visible: Ref<boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useModalDragMove(context: UseModalDragMoveContext) {
|
||||||
|
const getStyle = (dom: any, attr: any) => {
|
||||||
|
return getComputedStyle(dom)[attr];
|
||||||
|
};
|
||||||
|
const drag = (wrap: any) => {
|
||||||
|
if (!wrap) return;
|
||||||
|
wrap.setAttribute('data-drag', unref(context.draggable));
|
||||||
|
const dialogHeaderEl = wrap.querySelector('.ant-modal-header');
|
||||||
|
const dragDom = wrap.querySelector('.ant-modal');
|
||||||
|
|
||||||
|
if (!dialogHeaderEl || !dragDom || !unref(context.draggable)) return;
|
||||||
|
|
||||||
|
dialogHeaderEl.style.cursor = 'move';
|
||||||
|
|
||||||
|
dialogHeaderEl.onmousedown = (e: any) => {
|
||||||
|
if (!e) return;
|
||||||
|
// 鼠标按下,计算当前元素距离可视区的距离
|
||||||
|
const disX = e.clientX;
|
||||||
|
const disY = e.clientY;
|
||||||
|
const screenWidth = document.body.clientWidth; // body当前宽度
|
||||||
|
const screenHeight = document.documentElement.clientHeight; // 可见区域高度(应为body高度,可某些环境下无法获取)
|
||||||
|
|
||||||
|
const dragDomWidth = dragDom.offsetWidth; // 对话框宽度
|
||||||
|
const dragDomheight = dragDom.offsetHeight; // 对话框高度
|
||||||
|
|
||||||
|
const minDragDomLeft = dragDom.offsetLeft;
|
||||||
|
|
||||||
|
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth;
|
||||||
|
const minDragDomTop = dragDom.offsetTop;
|
||||||
|
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight;
|
||||||
|
// 获取到的值带px 正则匹配替换
|
||||||
|
const domLeft = getStyle(dragDom, 'left');
|
||||||
|
const domTop = getStyle(dragDom, 'top');
|
||||||
|
let styL = +domLeft;
|
||||||
|
let styT = +domTop;
|
||||||
|
|
||||||
|
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
|
||||||
|
if (domLeft.includes('%')) {
|
||||||
|
styL = +document.body.clientWidth * (+domLeft.replace(/%/g, '') / 100);
|
||||||
|
styT = +document.body.clientHeight * (+domTop.replace(/%/g, '') / 100);
|
||||||
|
} else {
|
||||||
|
styL = +domLeft.replace(/px/g, '');
|
||||||
|
styT = +domTop.replace(/px/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
document.onmousemove = function (e) {
|
||||||
|
// 通过事件委托,计算移动的距离
|
||||||
|
let left = e.clientX - disX;
|
||||||
|
let top = e.clientY - disY;
|
||||||
|
|
||||||
|
// 边界处理
|
||||||
|
if (-left > minDragDomLeft) {
|
||||||
|
left = -minDragDomLeft;
|
||||||
|
} else if (left > maxDragDomLeft) {
|
||||||
|
left = maxDragDomLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-top > minDragDomTop) {
|
||||||
|
top = -minDragDomTop;
|
||||||
|
} else if (top > maxDragDomTop) {
|
||||||
|
top = maxDragDomTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动当前元素
|
||||||
|
dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`;
|
||||||
|
};
|
||||||
|
|
||||||
|
document.onmouseup = () => {
|
||||||
|
document.onmousemove = null;
|
||||||
|
document.onmouseup = null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDrag = () => {
|
||||||
|
const dragWraps = document.querySelectorAll('.ant-modal-wrap');
|
||||||
|
for (const wrap of Array.from(dragWraps)) {
|
||||||
|
if (!wrap) continue;
|
||||||
|
const display = getStyle(wrap, 'display');
|
||||||
|
const draggable = wrap.getAttribute('data-drag');
|
||||||
|
if (display !== 'none') {
|
||||||
|
// 拖拽位置
|
||||||
|
if (draggable === null || unref(context.destroyOnClose)) {
|
||||||
|
drag(wrap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (!unref(context.visible) || !unref(context.draggable)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
useTimeoutFn(() => {
|
||||||
|
handleDrag();
|
||||||
|
}, 30);
|
||||||
|
});
|
||||||
|
}
|
@@ -65,7 +65,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
tryTsxEmit((instance) => {
|
tryTsxEmit<any>((instance) => {
|
||||||
instance.wrap = unref(wrapElRef);
|
instance.wrap = unref(wrapElRef);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,20 +1,19 @@
|
|||||||
import type { BasicTableProps } from '../types/table';
|
import type { BasicTableProps } from '../types/table';
|
||||||
import { computed, Ref, onMounted, unref, ref, nextTick, ComputedRef, watch } from 'vue';
|
import { computed, Ref, onMounted, unref, ref, nextTick, ComputedRef, watch } from 'vue';
|
||||||
|
|
||||||
import { injectModal } from '/@/components/Modal/src/provideModal';
|
|
||||||
|
|
||||||
import { getViewportOffset } from '/@/utils/domUtils';
|
import { getViewportOffset } from '/@/utils/domUtils';
|
||||||
import { isBoolean } from '/@/utils/is';
|
import { isBoolean } from '/@/utils/is';
|
||||||
|
|
||||||
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
|
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
|
||||||
import { useProps } from './useProps';
|
import { useProps } from './useProps';
|
||||||
|
import { useModalContext } from '/@/components/Modal';
|
||||||
|
|
||||||
export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRef: Ref<any>) {
|
export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRef: Ref<any>) {
|
||||||
const { propsRef } = useProps(refProps);
|
const { propsRef } = useProps(refProps);
|
||||||
|
|
||||||
const tableHeightRef: Ref<number | null> = ref(null);
|
const tableHeightRef: Ref<number | null> = ref(null);
|
||||||
|
|
||||||
const redoModalHeight = injectModal();
|
const modalFn = useModalContext();
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => unref(propsRef).canResize,
|
() => unref(propsRef).canResize,
|
||||||
@@ -93,7 +92,7 @@ export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRe
|
|||||||
tableHeightRef.value =
|
tableHeightRef.value =
|
||||||
tableHeightRef.value! > maxHeight! ? (maxHeight as number) : tableHeightRef.value;
|
tableHeightRef.value! > maxHeight! ? (maxHeight as number) : tableHeightRef.value;
|
||||||
// 解决表格放modal内的时候,modal自适应高度计算问题
|
// 解决表格放modal内的时候,modal自适应高度计算问题
|
||||||
redoModalHeight && redoModalHeight();
|
modalFn?.redoModalHeight?.();
|
||||||
}, 16);
|
}, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
import type { ReplaceFields, TreeItem, Keys, CheckKeys } from './types';
|
import type { ReplaceFields, TreeItem, Keys, CheckKeys, TreeActionType } from './types';
|
||||||
|
|
||||||
import { defineComponent, reactive, computed, unref, ref, watchEffect, CSSProperties } from 'vue';
|
import { defineComponent, reactive, computed, unref, ref, watchEffect, CSSProperties } from 'vue';
|
||||||
import { Tree } from 'ant-design-vue';
|
import { Tree } from 'ant-design-vue';
|
||||||
@@ -124,7 +124,6 @@ export default defineComponent({
|
|||||||
title: () => (
|
title: () => (
|
||||||
<span class={`${prefixCls}-title`}>
|
<span class={`${prefixCls}-title`}>
|
||||||
<span class={`${prefixCls}__content`} style={unref(getContentStyle)}>
|
<span class={`${prefixCls}__content`} style={unref(getContentStyle)}>
|
||||||
{' '}
|
|
||||||
{titleField && anyItem[titleField]}
|
{titleField && anyItem[titleField]}
|
||||||
</span>
|
</span>
|
||||||
<span class={`${prefixCls}__actions`}> {renderAction(item)}</span>
|
<span class={`${prefixCls}__actions`}> {renderAction(item)}</span>
|
||||||
@@ -183,7 +182,7 @@ export default defineComponent({
|
|||||||
state.checkedKeys = props.checkedKeys;
|
state.checkedKeys = props.checkedKeys;
|
||||||
});
|
});
|
||||||
|
|
||||||
tryTsxEmit((currentInstance) => {
|
tryTsxEmit<TreeActionType>((currentInstance) => {
|
||||||
currentInstance.setExpandedKeys = setExpandedKeys;
|
currentInstance.setExpandedKeys = setExpandedKeys;
|
||||||
currentInstance.getExpandedKeys = getExpandedKeys;
|
currentInstance.getExpandedKeys = getExpandedKeys;
|
||||||
currentInstance.setSelectedKeys = setSelectedKeys;
|
currentInstance.setSelectedKeys = setSelectedKeys;
|
||||||
|
@@ -10,7 +10,7 @@ export function useTree(
|
|||||||
getReplaceFields: ComputedRef<ReplaceFields>
|
getReplaceFields: ComputedRef<ReplaceFields>
|
||||||
) {
|
) {
|
||||||
// 更新节点
|
// 更新节点
|
||||||
function updateNodeByKey(key: string, node: TreeItem, list: TreeItem[]) {
|
function updateNodeByKey(key: string, node: TreeItem, list?: TreeItem[]) {
|
||||||
if (!key) return;
|
if (!key) return;
|
||||||
const treeData = list || unref(treeDataRef);
|
const treeData = list || unref(treeDataRef);
|
||||||
const { key: keyField, children: childrenField } = unref(getReplaceFields);
|
const { key: keyField, children: childrenField } = unref(getReplaceFields);
|
||||||
@@ -75,7 +75,7 @@ export function useTree(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 删除节点
|
// 删除节点
|
||||||
function deleteNodeByKey(key: string, list: TreeItem[]) {
|
function deleteNodeByKey(key: string, list?: TreeItem[]) {
|
||||||
if (!key) return;
|
if (!key) return;
|
||||||
const treeData = list || unref(treeDataRef);
|
const treeData = list || unref(treeDataRef);
|
||||||
const { key: keyField, children: childrenField } = unref(getReplaceFields);
|
const { key: keyField, children: childrenField } = unref(getReplaceFields);
|
||||||
|
@@ -6,6 +6,7 @@ import { getSlot } from '/@/utils/helper/tsxHelper';
|
|||||||
import './DragVerify.less';
|
import './DragVerify.less';
|
||||||
import { CheckOutlined, DoubleRightOutlined } from '@ant-design/icons-vue';
|
import { CheckOutlined, DoubleRightOutlined } from '@ant-design/icons-vue';
|
||||||
import { tryTsxEmit } from '/@/utils/helper/vueHelper';
|
import { tryTsxEmit } from '/@/utils/helper/vueHelper';
|
||||||
|
import type { DragVerifyActionType } from './types';
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'BaseDargVerify',
|
name: 'BaseDargVerify',
|
||||||
props: basicProps,
|
props: basicProps,
|
||||||
@@ -210,7 +211,7 @@ export default defineComponent({
|
|||||||
contentEl.style.width = unref(getContentStyleRef).width;
|
contentEl.style.width = unref(getContentStyleRef).width;
|
||||||
}
|
}
|
||||||
|
|
||||||
tryTsxEmit((instance) => {
|
tryTsxEmit<DragVerifyActionType>((instance) => {
|
||||||
instance.resume = resume;
|
instance.resume = resume;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -46,7 +46,7 @@ export function useRootSetting() {
|
|||||||
unref(getRootSetting).contentMode === ContentEnum.FULL ? ContentEnum.FULL : ContentEnum.FIXED
|
unref(getRootSetting).contentMode === ContentEnum.FULL ? ContentEnum.FULL : ContentEnum.FIXED
|
||||||
);
|
);
|
||||||
|
|
||||||
function setRootSetting(setting: RootSetting) {
|
function setRootSetting(setting: Partial<RootSetting>) {
|
||||||
appStore.commitProjectConfigState(setting);
|
appStore.commitProjectConfigState(setting);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,6 +7,7 @@ import {
|
|||||||
onUnmounted,
|
onUnmounted,
|
||||||
nextTick,
|
nextTick,
|
||||||
reactive,
|
reactive,
|
||||||
|
ComponentInternalInstance,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
|
|
||||||
export function explicitComputed<T, S>(source: WatchSource<S>, fn: () => T) {
|
export function explicitComputed<T, S>(source: WatchSource<S>, fn: () => T) {
|
||||||
@@ -29,8 +30,10 @@ export function tryOnUnmounted(fn: () => Promise<void> | void) {
|
|||||||
getCurrentInstance() && onUnmounted(fn);
|
getCurrentInstance() && onUnmounted(fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function tryTsxEmit(fn: (_instance: any) => Promise<void> | void) {
|
export function tryTsxEmit<T extends any = ComponentInternalInstance>(
|
||||||
const instance = getCurrentInstance();
|
fn: (_instance: T) => Promise<void> | void
|
||||||
|
) {
|
||||||
|
const instance = getCurrentInstance() as any;
|
||||||
instance && fn.call(null, instance);
|
instance && fn.call(null, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user