mirror of
https://github.com/vbenjs/vben-admin-thin-next.git
synced 2025-02-02 18:08:40 +08:00
feat(preview): add more features
为Preview组件添加新的属性及事件
This commit is contained in:
parent
98749ec6fe
commit
e23bd2696d
@ -1,3 +1,7 @@
|
||||
### ✨ Features
|
||||
|
||||
- **Preview** 添加新的属性及事件
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **ApiTreeSelect** 修复未能正确监听`params`变化的问题
|
||||
|
@ -38,13 +38,33 @@
|
||||
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,
|
||||
setup(props: Props) {
|
||||
emits: ['img-load', 'img-error'],
|
||||
setup(props: 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,
|
||||
@ -96,6 +116,14 @@
|
||||
};
|
||||
}
|
||||
|
||||
const getScaleStep = computed(() => {
|
||||
if (props.scaleStep > 0 && props.scaleStep < 100) {
|
||||
return props.scaleStep / 100;
|
||||
} else {
|
||||
return imgState.imgScale / 10;
|
||||
}
|
||||
});
|
||||
|
||||
// 监听鼠标滚轮
|
||||
function scrollFunc(e: any) {
|
||||
e = e || window.event;
|
||||
@ -104,11 +132,11 @@
|
||||
e.preventDefault();
|
||||
if (e.delta > 0) {
|
||||
// 滑轮向上滚动
|
||||
scaleFunc(0.015);
|
||||
scaleFunc(getScaleStep.value);
|
||||
}
|
||||
if (e.delta < 0) {
|
||||
// 滑轮向下滚动
|
||||
scaleFunc(-0.015);
|
||||
scaleFunc(-getScaleStep.value);
|
||||
}
|
||||
}
|
||||
// 缩放函数
|
||||
@ -134,11 +162,54 @@
|
||||
imgState.status = StatueEnum.LOADING;
|
||||
const img = new Image();
|
||||
img.src = url;
|
||||
img.onload = () => {
|
||||
img.onload = (e: Event) => {
|
||||
if (imgState.currentUrl !== url) {
|
||||
const ele: HTMLElement[] = 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 = () => {
|
||||
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;
|
||||
};
|
||||
}
|
||||
@ -146,6 +217,10 @@
|
||||
// 关闭
|
||||
function handleClose(e: MouseEvent) {
|
||||
e && e.stopPropagation();
|
||||
close();
|
||||
}
|
||||
|
||||
function close() {
|
||||
imgState.show = false;
|
||||
// 移除火狐浏览器下的鼠标滚动事件
|
||||
document.body.removeEventListener('DOMMouseScroll', scrollFunc);
|
||||
@ -158,6 +233,19 @@
|
||||
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;
|
||||
},
|
||||
} as PreviewActions);
|
||||
|
||||
// 上一页下一页
|
||||
function handleChange(direction: 'left' | 'right') {
|
||||
const { currentIndex } = imgState;
|
||||
@ -205,6 +293,7 @@
|
||||
transform: `scale(${imgScale}) rotate(${imgRotate}deg)`,
|
||||
marginTop: `${imgTop}px`,
|
||||
marginLeft: `${imgLeft}px`,
|
||||
maxWidth: props.defaultWidth ? 'unset' : '100%',
|
||||
};
|
||||
});
|
||||
|
||||
@ -222,6 +311,16 @@
|
||||
}
|
||||
});
|
||||
|
||||
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}>
|
||||
@ -246,10 +345,16 @@
|
||||
const renderController = () => {
|
||||
return (
|
||||
<div class={`${prefixCls}__controller`}>
|
||||
<div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(-0.15)}>
|
||||
<div
|
||||
class={`${prefixCls}__controller-item`}
|
||||
onClick={() => scaleFunc(-getScaleStep.value)}
|
||||
>
|
||||
<img src={unScaleSvg} />
|
||||
</div>
|
||||
<div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(0.15)}>
|
||||
<div
|
||||
class={`${prefixCls}__controller-item`}
|
||||
onClick={() => scaleFunc(getScaleStep.value)}
|
||||
>
|
||||
<img src={scaleSvg} />
|
||||
</div>
|
||||
<div class={`${prefixCls}__controller-item`} onClick={resume}>
|
||||
@ -279,7 +384,12 @@
|
||||
return () => {
|
||||
return (
|
||||
imgState.show && (
|
||||
<div class={prefixCls} ref={wrapElRef} onMouseup={handleMouseUp}>
|
||||
<div
|
||||
class={prefixCls}
|
||||
ref={wrapElRef}
|
||||
onMouseup={handleMouseUp}
|
||||
onClick={handleMaskClick}
|
||||
>
|
||||
<div class={`${prefixCls}-content`}>
|
||||
{/*<Spin*/}
|
||||
{/* indicator={<LoadingOutlined style="font-size: 24px" spin />}*/}
|
||||
|
@ -6,15 +6,12 @@ import { createVNode, render } from 'vue';
|
||||
let instance: ReturnType<typeof createVNode> | null = null;
|
||||
export function createImgPreview(options: Options) {
|
||||
if (!isClient) return;
|
||||
const { imageList, show = true, index = 0 } = options;
|
||||
|
||||
const propsData: Partial<Props> = {};
|
||||
const container = document.createElement('div');
|
||||
propsData.imageList = imageList;
|
||||
propsData.show = show;
|
||||
propsData.index = index;
|
||||
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;
|
||||
}
|
||||
|
@ -2,6 +2,12 @@ export interface Options {
|
||||
show?: boolean;
|
||||
imageList: string[];
|
||||
index?: number;
|
||||
scaleStep?: number;
|
||||
defaultWidth?: number;
|
||||
maskClosable?: boolean;
|
||||
rememberState?: boolean;
|
||||
onImgLoad?: (img: HTMLImageElement) => void;
|
||||
onImgError?: (img: HTMLImageElement) => void;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
@ -9,6 +15,19 @@ export interface Props {
|
||||
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 {
|
||||
|
@ -8,6 +8,7 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import { createImgPreview, ImagePreview } from '/@/components/Preview/index';
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
// import { PreviewActions } from '/@/components/Preview/src/typing';
|
||||
|
||||
const imgList: string[] = [
|
||||
'https://picsum.photos/id/66/346/216',
|
||||
@ -18,7 +19,11 @@
|
||||
components: { PageWrapper, ImagePreview },
|
||||
setup() {
|
||||
function openImg() {
|
||||
createImgPreview({ imageList: imgList });
|
||||
const onImgLoad = ({ index, url, dom }) => {
|
||||
console.log(`第${index + 1}张图片已加载,URL为:${url}`, dom);
|
||||
};
|
||||
// 可以使用createImgPreview返回的 PreviewActions 来控制预览逻辑,实现类似幻灯片、自动旋转之类的骚操作
|
||||
createImgPreview({ imageList: imgList, defaultWidth: 700, rememberState: true, onImgLoad });
|
||||
}
|
||||
return { imgList, openImg };
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user