mirror of
https://github.com/jeecgboot/JeecgBoot.git
synced 2026-02-03 00:55:33 +08:00
前端和后端源码,合并到一个git仓库中,方便用户下载,避免前后端不匹配的问题
This commit is contained in:
2
jeecgboot-vue3/src/components/Preview/index.ts
Normal file
2
jeecgboot-vue3/src/components/Preview/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { default as ImagePreview } from './src/Preview.vue';
|
||||
export { createImgPreview } from './src/functional';
|
||||
528
jeecgboot-vue3/src/components/Preview/src/Functional.vue
Normal file
528
jeecgboot-vue3/src/components/Preview/src/Functional.vue
Normal file
@ -0,0 +1,528 @@
|
||||
<script lang="tsx">
|
||||
import { defineComponent, ref, unref, computed, reactive, watchEffect } from 'vue';
|
||||
import { CloseOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
|
||||
import resumeSvg from '/@/assets/svg/preview/resume.svg';
|
||||
import rotateSvg from '/@/assets/svg/preview/p-rotate.svg';
|
||||
import scaleSvg from '/@/assets/svg/preview/scale.svg';
|
||||
import unScaleSvg from '/@/assets/svg/preview/unscale.svg';
|
||||
import unRotateSvg from '/@/assets/svg/preview/unrotate.svg';
|
||||
|
||||
enum StatueEnum {
|
||||
LOADING,
|
||||
DONE,
|
||||
FAIL,
|
||||
}
|
||||
interface ImgState {
|
||||
currentUrl: string;
|
||||
imgScale: number;
|
||||
imgRotate: number;
|
||||
imgTop: number;
|
||||
imgLeft: number;
|
||||
currentIndex: number;
|
||||
status: StatueEnum;
|
||||
moveX: number;
|
||||
moveY: number;
|
||||
show: boolean;
|
||||
}
|
||||
const props = {
|
||||
show: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false,
|
||||
},
|
||||
imageList: {
|
||||
type: [Array] as PropType<string[]>,
|
||||
default: null,
|
||||
},
|
||||
index: {
|
||||
type: Number as PropType<number>,
|
||||
default: 0,
|
||||
},
|
||||
scaleStep: {
|
||||
type: Number as PropType<number>,
|
||||
},
|
||||
defaultWidth: {
|
||||
type: Number as PropType<number>,
|
||||
},
|
||||
maskClosable: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
},
|
||||
rememberState: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
},
|
||||
};
|
||||
|
||||
const prefixCls = 'img-preview';
|
||||
export default defineComponent({
|
||||
name: 'ImagePreview',
|
||||
props,
|
||||
emits: ['img-load', 'img-error'],
|
||||
setup(props, { expose, emit }) {
|
||||
interface stateInfo {
|
||||
scale: number;
|
||||
rotate: number;
|
||||
top: number;
|
||||
left: number;
|
||||
}
|
||||
const stateMap = new Map<string, stateInfo>();
|
||||
const imgState = reactive<ImgState>({
|
||||
currentUrl: '',
|
||||
imgScale: 1,
|
||||
imgRotate: 0,
|
||||
imgTop: 0,
|
||||
imgLeft: 0,
|
||||
status: StatueEnum.LOADING,
|
||||
currentIndex: 0,
|
||||
moveX: 0,
|
||||
moveY: 0,
|
||||
show: props.show,
|
||||
});
|
||||
|
||||
const wrapElRef = ref<HTMLDivElement | null>(null);
|
||||
const imgElRef = ref<HTMLImageElement | null>(null);
|
||||
|
||||
// 初始化
|
||||
function init() {
|
||||
initMouseWheel();
|
||||
const { index, imageList } = props;
|
||||
|
||||
if (!imageList || !imageList.length) {
|
||||
throw new Error('imageList is undefined');
|
||||
}
|
||||
imgState.currentIndex = index;
|
||||
handleIChangeImage(imageList[index]);
|
||||
}
|
||||
|
||||
// 重置
|
||||
function initState() {
|
||||
imgState.imgScale = 1;
|
||||
imgState.imgRotate = 0;
|
||||
imgState.imgTop = 0;
|
||||
imgState.imgLeft = 0;
|
||||
}
|
||||
|
||||
// 初始化鼠标滚轮事件
|
||||
function initMouseWheel() {
|
||||
const wrapEl = unref(wrapElRef);
|
||||
if (!wrapEl) {
|
||||
return;
|
||||
}
|
||||
(wrapEl as any).onmousewheel = scrollFunc;
|
||||
// 火狐浏览器没有onmousewheel事件,用DOMMouseScroll代替
|
||||
document.body.addEventListener('DOMMouseScroll', scrollFunc);
|
||||
// 禁止火狐浏览器下拖拽图片的默认事件
|
||||
document.ondragstart = function () {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
const getScaleStep = computed(() => {
|
||||
const scaleStep = props?.scaleStep ?? 0;
|
||||
if (scaleStep ?? (0 > 0 && scaleStep < 100)) {
|
||||
return scaleStep / 100;
|
||||
} else {
|
||||
return imgState.imgScale / 10;
|
||||
}
|
||||
});
|
||||
|
||||
// 监听鼠标滚轮
|
||||
function scrollFunc(e: any) {
|
||||
e = e || window.event;
|
||||
e.delta = e.wheelDelta || -e.detail;
|
||||
|
||||
e.preventDefault();
|
||||
if (e.delta > 0) {
|
||||
// 滑轮向上滚动
|
||||
scaleFunc(getScaleStep.value);
|
||||
}
|
||||
if (e.delta < 0) {
|
||||
// 滑轮向下滚动
|
||||
scaleFunc(-getScaleStep.value);
|
||||
}
|
||||
}
|
||||
// 缩放函数
|
||||
function scaleFunc(num: number) {
|
||||
if (imgState.imgScale <= 0.2 && num < 0) return;
|
||||
imgState.imgScale += num;
|
||||
}
|
||||
|
||||
// 旋转图片
|
||||
function rotateFunc(deg: number) {
|
||||
imgState.imgRotate += deg;
|
||||
}
|
||||
|
||||
// 鼠标事件
|
||||
function handleMouseUp() {
|
||||
const imgEl = unref(imgElRef);
|
||||
if (!imgEl) return;
|
||||
imgEl.onmousemove = null;
|
||||
}
|
||||
|
||||
// 更换图片
|
||||
function handleIChangeImage(url: string) {
|
||||
imgState.status = StatueEnum.LOADING;
|
||||
const img = new Image();
|
||||
img.src = url;
|
||||
img.onload = (e: Event) => {
|
||||
if (imgState.currentUrl !== url) {
|
||||
const ele: any[] = e.composedPath();
|
||||
if (props.rememberState) {
|
||||
// 保存当前图片的缩放信息
|
||||
stateMap.set(imgState.currentUrl, {
|
||||
scale: imgState.imgScale,
|
||||
top: imgState.imgTop,
|
||||
left: imgState.imgLeft,
|
||||
rotate: imgState.imgRotate,
|
||||
});
|
||||
// 如果之前已存储缩放信息,就应用
|
||||
const stateInfo = stateMap.get(url);
|
||||
if (stateInfo) {
|
||||
imgState.imgScale = stateInfo.scale;
|
||||
imgState.imgTop = stateInfo.top;
|
||||
imgState.imgRotate = stateInfo.rotate;
|
||||
imgState.imgLeft = stateInfo.left;
|
||||
} else {
|
||||
initState();
|
||||
if (props.defaultWidth) {
|
||||
imgState.imgScale = props.defaultWidth / ele[0].naturalWidth;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (props.defaultWidth) {
|
||||
imgState.imgScale = props.defaultWidth / ele[0].naturalWidth;
|
||||
}
|
||||
}
|
||||
|
||||
ele &&
|
||||
emit('img-load', {
|
||||
index: imgState.currentIndex,
|
||||
dom: ele[0] as HTMLImageElement,
|
||||
url,
|
||||
});
|
||||
}
|
||||
imgState.currentUrl = url;
|
||||
imgState.status = StatueEnum.DONE;
|
||||
};
|
||||
img.onerror = (e: Event) => {
|
||||
const ele: EventTarget[] = e.composedPath();
|
||||
ele &&
|
||||
emit('img-error', {
|
||||
index: imgState.currentIndex,
|
||||
dom: ele[0] as HTMLImageElement,
|
||||
url,
|
||||
});
|
||||
imgState.status = StatueEnum.FAIL;
|
||||
};
|
||||
}
|
||||
|
||||
// 关闭
|
||||
function handleClose(e: MouseEvent) {
|
||||
e && e.stopPropagation();
|
||||
close();
|
||||
}
|
||||
|
||||
function close() {
|
||||
imgState.show = false;
|
||||
// 移除火狐浏览器下的鼠标滚动事件
|
||||
document.body.removeEventListener('DOMMouseScroll', scrollFunc);
|
||||
// 恢复火狐及Safari浏览器下的图片拖拽
|
||||
document.ondragstart = null;
|
||||
}
|
||||
|
||||
// 图片复原
|
||||
function resume() {
|
||||
initState();
|
||||
}
|
||||
|
||||
expose({
|
||||
resume,
|
||||
close,
|
||||
prev: handleChange.bind(null, 'left'),
|
||||
next: handleChange.bind(null, 'right'),
|
||||
setScale: (scale: number) => {
|
||||
if (scale > 0 && scale <= 10) imgState.imgScale = scale;
|
||||
},
|
||||
setRotate: (rotate: number) => {
|
||||
imgState.imgRotate = rotate;
|
||||
},
|
||||
});
|
||||
|
||||
// 上一页下一页
|
||||
function handleChange(direction: 'left' | 'right') {
|
||||
const { currentIndex } = imgState;
|
||||
const { imageList } = props;
|
||||
if (direction === 'left') {
|
||||
imgState.currentIndex--;
|
||||
if (currentIndex <= 0) {
|
||||
imgState.currentIndex = imageList.length - 1;
|
||||
}
|
||||
}
|
||||
if (direction === 'right') {
|
||||
imgState.currentIndex++;
|
||||
if (currentIndex >= imageList.length - 1) {
|
||||
imgState.currentIndex = 0;
|
||||
}
|
||||
}
|
||||
handleIChangeImage(imageList[imgState.currentIndex]);
|
||||
}
|
||||
|
||||
function handleAddMoveListener(e: MouseEvent) {
|
||||
e = e || window.event;
|
||||
imgState.moveX = e.clientX;
|
||||
imgState.moveY = e.clientY;
|
||||
const imgEl = unref(imgElRef);
|
||||
if (imgEl) {
|
||||
imgEl.onmousemove = moveFunc;
|
||||
}
|
||||
}
|
||||
|
||||
function moveFunc(e: MouseEvent) {
|
||||
e = e || window.event;
|
||||
e.preventDefault();
|
||||
const movementX = e.clientX - imgState.moveX;
|
||||
const movementY = e.clientY - imgState.moveY;
|
||||
imgState.imgLeft += movementX;
|
||||
imgState.imgTop += movementY;
|
||||
imgState.moveX = e.clientX;
|
||||
imgState.moveY = e.clientY;
|
||||
}
|
||||
|
||||
// 获取图片样式
|
||||
const getImageStyle = computed(() => {
|
||||
const { imgScale, imgRotate, imgTop, imgLeft } = imgState;
|
||||
return {
|
||||
transform: `scale(${imgScale}) rotate(${imgRotate}deg)`,
|
||||
marginTop: `${imgTop}px`,
|
||||
marginLeft: `${imgLeft}px`,
|
||||
maxWidth: props.defaultWidth ? 'unset' : '100%',
|
||||
};
|
||||
});
|
||||
|
||||
const getIsMultipleImage = computed(() => {
|
||||
const { imageList } = props;
|
||||
return imageList.length > 1;
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (props.show) {
|
||||
init();
|
||||
}
|
||||
if (props.imageList) {
|
||||
initState();
|
||||
}
|
||||
});
|
||||
|
||||
const handleMaskClick = (e: MouseEvent) => {
|
||||
if (props.maskClosable && e.target && (e.target as HTMLDivElement).classList.contains(`${prefixCls}-content`)) {
|
||||
handleClose(e);
|
||||
}
|
||||
};
|
||||
|
||||
const renderClose = () => {
|
||||
return (
|
||||
<div class={`${prefixCls}__close`} onClick={handleClose}>
|
||||
<CloseOutlined class={`${prefixCls}__close-icon`} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderIndex = () => {
|
||||
if (!unref(getIsMultipleImage)) {
|
||||
return null;
|
||||
}
|
||||
const { currentIndex } = imgState;
|
||||
const { imageList } = props;
|
||||
return (
|
||||
<div class={`${prefixCls}__index`}>
|
||||
{currentIndex + 1} / {imageList.length}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderController = () => {
|
||||
return (
|
||||
<div class={`${prefixCls}__controller`}>
|
||||
<div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(-getScaleStep.value)}>
|
||||
<img src={unScaleSvg} />
|
||||
</div>
|
||||
<div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(getScaleStep.value)}>
|
||||
<img src={scaleSvg} />
|
||||
</div>
|
||||
<div class={`${prefixCls}__controller-item`} onClick={resume}>
|
||||
<img src={resumeSvg} />
|
||||
</div>
|
||||
<div class={`${prefixCls}__controller-item`} onClick={() => rotateFunc(-90)}>
|
||||
<img src={unRotateSvg} />
|
||||
</div>
|
||||
<div class={`${prefixCls}__controller-item`} onClick={() => rotateFunc(90)}>
|
||||
<img src={rotateSvg} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderArrow = (direction: 'left' | 'right') => {
|
||||
if (!unref(getIsMultipleImage)) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div class={[`${prefixCls}__arrow`, direction]} onClick={() => handleChange(direction)}>
|
||||
{direction === 'left' ? <LeftOutlined /> : <RightOutlined />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return () => {
|
||||
return (
|
||||
imgState.show && (
|
||||
<div class={prefixCls} ref={wrapElRef} onMouseup={handleMouseUp} onClick={handleMaskClick}>
|
||||
<div class={`${prefixCls}-content`}>
|
||||
{/*<Spin*/}
|
||||
{/* indicator={<LoadingOutlined style="font-size: 24px" spin />}*/}
|
||||
{/* spinning={true}*/}
|
||||
{/* class={[*/}
|
||||
{/* `${prefixCls}-image`,*/}
|
||||
{/* {*/}
|
||||
{/* hidden: imgState.status !== StatueEnum.LOADING,*/}
|
||||
{/* },*/}
|
||||
{/* ]}*/}
|
||||
{/*/>*/}
|
||||
<img
|
||||
style={unref(getImageStyle)}
|
||||
class={[`${prefixCls}-image`, imgState.status === StatueEnum.DONE ? '' : 'hidden']}
|
||||
ref={imgElRef}
|
||||
src={imgState.currentUrl}
|
||||
onMousedown={handleAddMoveListener}
|
||||
/>
|
||||
{renderClose()}
|
||||
{renderIndex()}
|
||||
{renderController()}
|
||||
{renderArrow('left')}
|
||||
{renderArrow('right')}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
.img-preview {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: @preview-comp-z-index;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
user-select: none;
|
||||
|
||||
&-content {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: @white;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&-image {
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
&__close {
|
||||
position: absolute;
|
||||
top: -40px;
|
||||
right: -40px;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
overflow: hidden;
|
||||
color: @white;
|
||||
cursor: pointer;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s;
|
||||
|
||||
&-icon {
|
||||
position: absolute;
|
||||
top: 46px;
|
||||
left: 16px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
&__index {
|
||||
position: absolute;
|
||||
bottom: 5%;
|
||||
left: 50%;
|
||||
padding: 0 22px;
|
||||
font-size: 16px;
|
||||
background: rgba(109, 109, 109, 0.6);
|
||||
border-radius: 15px;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
&__controller {
|
||||
position: absolute;
|
||||
bottom: 10%;
|
||||
left: 50%;
|
||||
display: flex;
|
||||
width: 260px;
|
||||
height: 44px;
|
||||
padding: 0 22px;
|
||||
margin-left: -139px;
|
||||
background: rgba(109, 109, 109, 0.6);
|
||||
border-radius: 22px;
|
||||
justify-content: center;
|
||||
|
||||
&-item {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
padding: 0 9px;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
img {
|
||||
width: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
font-size: 28px;
|
||||
cursor: pointer;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
&.left {
|
||||
left: 50px;
|
||||
}
|
||||
|
||||
&.right {
|
||||
right: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
94
jeecgboot-vue3/src/components/Preview/src/Preview.vue
Normal file
94
jeecgboot-vue3/src/components/Preview/src/Preview.vue
Normal file
@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<div :class="prefixCls">
|
||||
<PreviewGroup>
|
||||
<slot v-if="!imageList || $slots.default"></slot>
|
||||
<template v-else>
|
||||
<template v-for="item in getImageList" :key="item.src">
|
||||
<Image v-bind="item">
|
||||
<template #placeholder v-if="item.placeholder">
|
||||
<Image v-bind="item" :src="item.placeholder" :preview="false" />
|
||||
</template>
|
||||
</Image>
|
||||
</template>
|
||||
</template>
|
||||
</PreviewGroup>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import { defineComponent, computed } from 'vue';
|
||||
|
||||
import { Image } from 'ant-design-vue';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { isString } from '/@/utils/is';
|
||||
|
||||
interface ImageProps {
|
||||
alt?: string;
|
||||
fallback?: string;
|
||||
src: string;
|
||||
width: string | number;
|
||||
height?: string | number;
|
||||
placeholder?: string | boolean;
|
||||
preview?:
|
||||
| boolean
|
||||
| {
|
||||
visible?: boolean;
|
||||
onVisibleChange?: (visible: boolean, prevVisible: boolean) => void;
|
||||
getContainer: string | HTMLElement | (() => HTMLElement);
|
||||
};
|
||||
}
|
||||
|
||||
type ImageItem = string | ImageProps;
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ImagePreview',
|
||||
components: {
|
||||
Image,
|
||||
PreviewGroup: Image.PreviewGroup,
|
||||
},
|
||||
props: {
|
||||
functional: propTypes.bool,
|
||||
imageList: {
|
||||
type: Array as PropType<ImageItem[]>,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { prefixCls } = useDesign('image-preview');
|
||||
|
||||
const getImageList = computed((): any[] => {
|
||||
const { imageList } = props;
|
||||
if (!imageList) {
|
||||
return [];
|
||||
}
|
||||
return imageList.map((item) => {
|
||||
if (isString(item)) {
|
||||
return {
|
||||
src: item,
|
||||
placeholder: false,
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
prefixCls,
|
||||
getImageList,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'@{namespace}-image-preview';
|
||||
|
||||
.@{prefix-cls} {
|
||||
.ant-image {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.ant-image-preview-operations {
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
18
jeecgboot-vue3/src/components/Preview/src/functional.ts
Normal file
18
jeecgboot-vue3/src/components/Preview/src/functional.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import type { Options, Props } from './typing';
|
||||
import ImgPreview from './Functional.vue';
|
||||
import { isClient } from '/@/utils/is';
|
||||
import { createVNode, render } from 'vue';
|
||||
|
||||
let instance: ReturnType<typeof createVNode> | null = null;
|
||||
|
||||
export function createImgPreview(options: Options) {
|
||||
if (!isClient) return;
|
||||
const propsData: Partial<Props> = {};
|
||||
const container = document.createElement('div');
|
||||
Object.assign(propsData, { show: true, index: 0, scaleStep: 100 }, options);
|
||||
|
||||
instance = createVNode(ImgPreview, propsData);
|
||||
render(instance, container);
|
||||
document.body.appendChild(container);
|
||||
return instance.component?.exposed;
|
||||
}
|
||||
49
jeecgboot-vue3/src/components/Preview/src/typing.ts
Normal file
49
jeecgboot-vue3/src/components/Preview/src/typing.ts
Normal file
@ -0,0 +1,49 @@
|
||||
export interface Options {
|
||||
show?: boolean;
|
||||
imageList: string[];
|
||||
index?: number;
|
||||
scaleStep?: number;
|
||||
defaultWidth?: number;
|
||||
maskClosable?: boolean;
|
||||
rememberState?: boolean;
|
||||
onImgLoad?: ({ index: number, url: string, dom: HTMLImageElement }) => void;
|
||||
onImgError?: ({ index: number, url: string, dom: HTMLImageElement }) => void;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
show: boolean;
|
||||
instance: Props;
|
||||
imageList: string[];
|
||||
index: number;
|
||||
scaleStep: number;
|
||||
defaultWidth: number;
|
||||
maskClosable: boolean;
|
||||
rememberState: boolean;
|
||||
}
|
||||
|
||||
export interface PreviewActions {
|
||||
resume: () => void;
|
||||
close: () => void;
|
||||
prev: () => void;
|
||||
next: () => void;
|
||||
setScale: (scale: number) => void;
|
||||
setRotate: (rotate: number) => void;
|
||||
}
|
||||
|
||||
export interface ImageProps {
|
||||
alt?: string;
|
||||
fallback?: string;
|
||||
src: string;
|
||||
width: string | number;
|
||||
height?: string | number;
|
||||
placeholder?: string | boolean;
|
||||
preview?:
|
||||
| boolean
|
||||
| {
|
||||
visible?: boolean;
|
||||
onVisibleChange?: (visible: boolean, prevVisible: boolean) => void;
|
||||
getContainer: string | HTMLElement | (() => HTMLElement);
|
||||
};
|
||||
}
|
||||
|
||||
export type ImageItem = string | ImageProps;
|
||||
Reference in New Issue
Block a user