feat(image): add new features (#5479)
* feat(image): add new features * fix: lint error * update test * update code * update docs * perf: reset currentIndex after close * update code * update code * update code * update code * update code * add rootClassName props * fix lintpull/5502/head
parent
32a145a79f
commit
bd87079e12
|
@ -4,6 +4,7 @@ export type KeyboardEventHandler = (e: KeyboardEvent) => void;
|
||||||
export type CompositionEventHandler = (e: CompositionEvent) => void;
|
export type CompositionEventHandler = (e: CompositionEvent) => void;
|
||||||
export type ClipboardEventHandler = (e: ClipboardEvent) => void;
|
export type ClipboardEventHandler = (e: ClipboardEvent) => void;
|
||||||
export type ChangeEventHandler = (e: ChangeEvent) => void;
|
export type ChangeEventHandler = (e: ChangeEvent) => void;
|
||||||
|
export type WheelEventHandler = (e: WheelEvent) => void;
|
||||||
export type ChangeEvent = Event & {
|
export type ChangeEvent = Event & {
|
||||||
target: {
|
target: {
|
||||||
value?: string | undefined;
|
value?: string | undefined;
|
||||||
|
|
|
@ -2,6 +2,24 @@ import PreviewGroup from '../vc-image/src/PreviewGroup';
|
||||||
import { computed, defineComponent } from 'vue';
|
import { computed, defineComponent } from 'vue';
|
||||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||||
|
|
||||||
|
import RotateLeftOutlined from '@ant-design/icons-vue/RotateLeftOutlined';
|
||||||
|
import RotateRightOutlined from '@ant-design/icons-vue/RotateRightOutlined';
|
||||||
|
import ZoomInOutlined from '@ant-design/icons-vue/ZoomInOutlined';
|
||||||
|
import ZoomOutOutlined from '@ant-design/icons-vue/ZoomOutOutlined';
|
||||||
|
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
|
||||||
|
import LeftOutlined from '@ant-design/icons-vue/LeftOutlined';
|
||||||
|
import RightOutlined from '@ant-design/icons-vue/RightOutlined';
|
||||||
|
|
||||||
|
export const icons = {
|
||||||
|
rotateLeft: <RotateLeftOutlined />,
|
||||||
|
rotateRight: <RotateRightOutlined />,
|
||||||
|
zoomIn: <ZoomInOutlined />,
|
||||||
|
zoomOut: <ZoomOutOutlined />,
|
||||||
|
close: <CloseOutlined />,
|
||||||
|
left: <LeftOutlined />,
|
||||||
|
right: <RightOutlined />,
|
||||||
|
};
|
||||||
|
|
||||||
const InternalPreviewGroup = defineComponent({
|
const InternalPreviewGroup = defineComponent({
|
||||||
name: 'AImagePreviewGroup',
|
name: 'AImagePreviewGroup',
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
|
@ -13,6 +31,7 @@ const InternalPreviewGroup = defineComponent({
|
||||||
return (
|
return (
|
||||||
<PreviewGroup
|
<PreviewGroup
|
||||||
{...{ ...attrs, ...props }}
|
{...{ ...attrs, ...props }}
|
||||||
|
icons={icons}
|
||||||
previewPrefixCls={prefixCls.value}
|
previewPrefixCls={prefixCls.value}
|
||||||
v-slots={slots}
|
v-slots={slots}
|
||||||
></PreviewGroup>
|
></PreviewGroup>
|
||||||
|
|
|
@ -3,8 +3,11 @@
|
||||||
exports[`renders ./components/image/demo/basic.vue correctly 1`] = `
|
exports[`renders ./components/image/demo/basic.vue correctly 1`] = `
|
||||||
<div class="ant-image" style="width: 200px;"><img class="ant-image-img" src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png">
|
<div class="ant-image" style="width: 200px;"><img class="ant-image-img" src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png">
|
||||||
<!---->
|
<!---->
|
||||||
<!---->
|
<div class="ant-image-mask">
|
||||||
|
<div class="ant-image-mask-info"><span role="img" aria-label="eye" class="anticon anticon-eye"><svg focusable="false" class="" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path></svg></span>Preview</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<!---->
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`renders ./components/image/demo/controlled-preview.vue correctly 1`] = `
|
exports[`renders ./components/image/demo/controlled-preview.vue correctly 1`] = `
|
||||||
|
@ -13,7 +16,9 @@ exports[`renders ./components/image/demo/controlled-preview.vue correctly 1`] =
|
||||||
</button>
|
</button>
|
||||||
<div class="ant-image" style="width: 200px;"><img class="ant-image-img" style="display: none;" src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png">
|
<div class="ant-image" style="width: 200px;"><img class="ant-image-img" style="display: none;" src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png">
|
||||||
<!---->
|
<!---->
|
||||||
<!---->
|
<div class="ant-image-mask">
|
||||||
|
<div class="ant-image-mask-info"><span role="img" aria-label="eye" class="anticon anticon-eye"><svg focusable="false" class="" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path></svg></span>Preview</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!---->
|
<!---->
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,8 +27,11 @@ exports[`renders ./components/image/demo/controlled-preview.vue correctly 1`] =
|
||||||
exports[`renders ./components/image/demo/fallback.vue correctly 1`] = `
|
exports[`renders ./components/image/demo/fallback.vue correctly 1`] = `
|
||||||
<div class="ant-image" style="width: 200px; height: 200px;"><img class="ant-image-img" src="https://www.antdv.com/#error">
|
<div class="ant-image" style="width: 200px; height: 200px;"><img class="ant-image-img" src="https://www.antdv.com/#error">
|
||||||
<!---->
|
<!---->
|
||||||
<!---->
|
<div class="ant-image-mask">
|
||||||
|
<div class="ant-image-mask-info"><span role="img" aria-label="eye" class="anticon anticon-eye"><svg focusable="false" class="" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path></svg></span>Preview</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<!---->
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`renders ./components/image/demo/placeholder.vue correctly 1`] = `
|
exports[`renders ./components/image/demo/placeholder.vue correctly 1`] = `
|
||||||
|
@ -37,7 +45,9 @@ exports[`renders ./components/image/demo/placeholder.vue correctly 1`] = `
|
||||||
</div>
|
</div>
|
||||||
<!---->
|
<!---->
|
||||||
</div>
|
</div>
|
||||||
<!---->
|
<div class="ant-image-mask">
|
||||||
|
<div class="ant-image-mask-info"><span role="img" aria-label="eye" class="anticon anticon-eye"><svg focusable="false" class="" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path></svg></span>Preview</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!---->
|
<!---->
|
||||||
</div>
|
</div>
|
||||||
|
@ -52,35 +62,61 @@ exports[`renders ./components/image/demo/placeholder.vue correctly 1`] = `
|
||||||
exports[`renders ./components/image/demo/preview-group.vue correctly 1`] = `
|
exports[`renders ./components/image/demo/preview-group.vue correctly 1`] = `
|
||||||
<div class="ant-image" style="width: 200px;"><img class="ant-image-img" src="https://aliyuncdn.antdv.com/vue.png">
|
<div class="ant-image" style="width: 200px;"><img class="ant-image-img" src="https://aliyuncdn.antdv.com/vue.png">
|
||||||
<!---->
|
<!---->
|
||||||
<!---->
|
<div class="ant-image-mask">
|
||||||
|
<div class="ant-image-mask-info"><span role="img" aria-label="eye" class="anticon anticon-eye"><svg focusable="false" class="" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path></svg></span>Preview</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<!---->
|
||||||
<div class="ant-image" style="width: 200px;"><img class="ant-image-img" src="https://aliyuncdn.antdv.com/logo.png">
|
<div class="ant-image" style="width: 200px;"><img class="ant-image-img" src="https://aliyuncdn.antdv.com/logo.png">
|
||||||
<!---->
|
<!---->
|
||||||
<!---->
|
<div class="ant-image-mask">
|
||||||
|
<div class="ant-image-mask-info"><span role="img" aria-label="eye" class="anticon anticon-eye"><svg focusable="false" class="" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path></svg></span>Preview</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<!---->
|
||||||
|
<!---->
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`renders ./components/image/demo/preview-group-visible.vue correctly 1`] = `
|
exports[`renders ./components/image/demo/preview-group-visible.vue correctly 1`] = `
|
||||||
<div class="ant-image" style="width: 200px;"><img class="ant-image-img" src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp">
|
<div class="ant-image" style="width: 200px;"><img class="ant-image-img" src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp">
|
||||||
<!---->
|
<!---->
|
||||||
<!---->
|
<div class="ant-image-mask">
|
||||||
|
<div class="ant-image-mask-info"><span role="img" aria-label="eye" class="anticon anticon-eye"><svg focusable="false" class="" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path></svg></span>Preview</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<!---->
|
||||||
<div style="display: none;">
|
<div style="display: none;">
|
||||||
<div class="ant-image"><img class="ant-image-img" src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp">
|
<div class="ant-image"><img class="ant-image-img" src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp">
|
||||||
<!---->
|
<!---->
|
||||||
<!---->
|
<div class="ant-image-mask">
|
||||||
|
<div class="ant-image-mask-info"><span role="img" aria-label="eye" class="anticon anticon-eye"><svg focusable="false" class="" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path></svg></span>Preview</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!---->
|
<!---->
|
||||||
<div class="ant-image"><img class="ant-image-img" src="https://gw.alipayobjects.com/zos/antfincdn/cV16ZqzMjW/photo-1473091540282-9b846e7965e3.webp">
|
<div class="ant-image"><img class="ant-image-img" src="https://gw.alipayobjects.com/zos/antfincdn/cV16ZqzMjW/photo-1473091540282-9b846e7965e3.webp">
|
||||||
<!---->
|
<!---->
|
||||||
<!---->
|
<div class="ant-image-mask">
|
||||||
|
<div class="ant-image-mask-info"><span role="img" aria-label="eye" class="anticon anticon-eye"><svg focusable="false" class="" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path></svg></span>Preview</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!---->
|
<!---->
|
||||||
<div class="ant-image"><img class="ant-image-img" src="https://gw.alipayobjects.com/zos/antfincdn/x43I27A55%26/photo-1438109491414-7198515b166b.webp">
|
<div class="ant-image"><img class="ant-image-img" src="https://gw.alipayobjects.com/zos/antfincdn/x43I27A55%26/photo-1438109491414-7198515b166b.webp">
|
||||||
<!---->
|
<!---->
|
||||||
<!---->
|
<div class="ant-image-mask">
|
||||||
|
<div class="ant-image-mask-info"><span role="img" aria-label="eye" class="anticon anticon-eye"><svg focusable="false" class="" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path></svg></span>Preview</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!---->
|
<!---->
|
||||||
<!---->
|
<!---->
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`renders ./components/image/demo/preview-src.vue correctly 1`] = `
|
||||||
|
<div class="ant-image" style="width: 200px;"><img class="ant-image-img" src="https://aliyuncdn.antdv.com/logo.png">
|
||||||
|
<!---->
|
||||||
|
<div class="ant-image-mask">
|
||||||
|
<div class="ant-image-mask-info"><span role="img" aria-label="eye" class="anticon anticon-eye"><svg focusable="false" class="" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path></svg></span>Preview</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!---->
|
||||||
|
`;
|
||||||
|
|
|
@ -4,8 +4,9 @@
|
||||||
<fallback />
|
<fallback />
|
||||||
<placeholder />
|
<placeholder />
|
||||||
<preview-group />
|
<preview-group />
|
||||||
<controlled-preview />
|
|
||||||
<previewGroupVisibleVue />
|
<previewGroupVisibleVue />
|
||||||
|
<previewSrc />
|
||||||
|
<controlled-preview />
|
||||||
</demo-sort>
|
</demo-sort>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
import Basic from './basic.vue';
|
import Basic from './basic.vue';
|
||||||
import Fallback from './fallback.vue';
|
import Fallback from './fallback.vue';
|
||||||
import Placeholder from './placeholder.vue';
|
import Placeholder from './placeholder.vue';
|
||||||
|
import previewSrc from './preview-src.vue';
|
||||||
import PreviewGroup from './preview-group.vue';
|
import PreviewGroup from './preview-group.vue';
|
||||||
import ControlledPreview from './controlled-preview.vue';
|
import ControlledPreview from './controlled-preview.vue';
|
||||||
import previewGroupVisibleVue from './preview-group-visible.vue';
|
import previewGroupVisibleVue from './preview-group-visible.vue';
|
||||||
|
@ -26,6 +28,7 @@ export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
Basic,
|
Basic,
|
||||||
Fallback,
|
Fallback,
|
||||||
|
previewSrc,
|
||||||
Placeholder,
|
Placeholder,
|
||||||
PreviewGroup,
|
PreviewGroup,
|
||||||
ControlledPreview,
|
ControlledPreview,
|
||||||
|
|
|
@ -18,7 +18,7 @@ Preview a collection from one image.
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-image
|
<a-image
|
||||||
:preview="{ visible }"
|
:preview="{ visible: false }"
|
||||||
:width="200"
|
:width="200"
|
||||||
src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp"
|
src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp"
|
||||||
@click="visible = true"
|
@click="visible = true"
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<docs>
|
||||||
|
---
|
||||||
|
order: 4
|
||||||
|
title:
|
||||||
|
zh-CN: 自定义预览图片
|
||||||
|
en-US: Custom preview image
|
||||||
|
---
|
||||||
|
|
||||||
|
## zh-CN
|
||||||
|
|
||||||
|
可以设置不同的预览图片。
|
||||||
|
|
||||||
|
## en-US
|
||||||
|
|
||||||
|
You can set different preview image.
|
||||||
|
|
||||||
|
</docs>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a-image
|
||||||
|
:width="200"
|
||||||
|
src="https://aliyuncdn.antdv.com/logo.png"
|
||||||
|
:preview="{
|
||||||
|
src: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
export default defineComponent({});
|
||||||
|
</script>
|
|
@ -22,7 +22,9 @@ Previewable image.
|
||||||
| placeholder | Load placeholder, use default placeholder when set `true` | boolean \| slot | - | 2.0.0 |
|
| placeholder | Load placeholder, use default placeholder when set `true` | boolean \| slot | - | 2.0.0 |
|
||||||
| preview | preview config, disabled when `false` | boolean \| [previewType](#previewType) | true | 2.0.0 |
|
| preview | preview config, disabled when `false` | boolean \| [previewType](#previewType) | true | 2.0.0 |
|
||||||
| src | Image path | string | - | 2.0.0 |
|
| src | Image path | string | - | 2.0.0 |
|
||||||
|
| previewMask | custom mask | slot | - | 3.2.0 |
|
||||||
| width | Image width | string \| number | - | 2.0.0 |
|
| width | Image width | string \| number | - | 2.0.0 |
|
||||||
|
| onError | Load failed callback | (event: Event) => void | - | 3.2.0 |
|
||||||
|
|
||||||
### previewType
|
### previewType
|
||||||
|
|
||||||
|
@ -31,6 +33,9 @@ Previewable image.
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
onVisibleChange?: (visible, prevVisible) => void;
|
onVisibleChange?: (visible, prevVisible) => void;
|
||||||
getContainer?: string | HTMLElement | (() => HTMLElement);
|
getContainer?: string | HTMLElement | (() => HTMLElement);
|
||||||
|
src?: string;
|
||||||
|
maskClassName?: string;
|
||||||
|
current?: number;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import type { App, ExtractPropTypes, ImgHTMLAttributes, Plugin } from 'vue';
|
import type { App, ExtractPropTypes, ImgHTMLAttributes, Plugin } from 'vue';
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent, computed } from 'vue';
|
||||||
import ImageInternal from '../vc-image';
|
import ImageInternal from '../vc-image';
|
||||||
import { imageProps } from '../vc-image/src/Image';
|
import { imageProps } from '../vc-image/src/Image';
|
||||||
|
import defaultLocale from '../locale/en_US';
|
||||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||||
import PreviewGroup from './PreviewGroup';
|
import PreviewGroup, { icons } from './PreviewGroup';
|
||||||
|
import EyeOutlined from '@ant-design/icons-vue/EyeOutlined';
|
||||||
|
import { getTransitionName } from '../_util/transition';
|
||||||
|
|
||||||
export type ImageProps = Partial<
|
export type ImageProps = Partial<
|
||||||
ExtractPropTypes<ReturnType<typeof imageProps>> &
|
ExtractPropTypes<ReturnType<typeof imageProps>> &
|
||||||
|
@ -14,12 +17,46 @@ const Image = defineComponent<ImageProps>({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: imageProps() as any,
|
props: imageProps() as any,
|
||||||
setup(props, { slots, attrs }) {
|
setup(props, { slots, attrs }) {
|
||||||
const { prefixCls } = useConfigInject('image', props);
|
const { prefixCls, rootPrefixCls, configProvider } = useConfigInject('image', props);
|
||||||
|
|
||||||
|
const mergedPreview = computed(() => {
|
||||||
|
const { preview } = props;
|
||||||
|
|
||||||
|
if (preview === false) {
|
||||||
|
return preview;
|
||||||
|
}
|
||||||
|
const _preview = typeof preview === 'object' ? preview : {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
icons,
|
||||||
|
..._preview,
|
||||||
|
transitionName: getTransitionName(rootPrefixCls.value, 'zoom', _preview.transitionName),
|
||||||
|
maskTransitionName: getTransitionName(
|
||||||
|
rootPrefixCls.value,
|
||||||
|
'fade',
|
||||||
|
_preview.maskTransitionName,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
const imageLocale = configProvider.locale?.Image || defaultLocale.Image;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ImageInternal
|
<ImageInternal
|
||||||
{...{ ...attrs, ...props, prefixCls: prefixCls.value }}
|
{...{ ...attrs, ...props, prefixCls: prefixCls.value }}
|
||||||
v-slots={slots}
|
preview={mergedPreview.value}
|
||||||
|
v-slots={{
|
||||||
|
...slots,
|
||||||
|
previewMask:
|
||||||
|
slots.previewMask ??
|
||||||
|
(() => (
|
||||||
|
<div class={`${prefixCls.value}-mask-info`}>
|
||||||
|
<EyeOutlined />
|
||||||
|
{imageLocale?.preview}
|
||||||
|
</div>
|
||||||
|
)),
|
||||||
|
}}
|
||||||
></ImageInternal>
|
></ImageInternal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,7 +23,9 @@ cover: https://gw.alipayobjects.com/zos/antfincdn/D1dXz9PZqa/image.svg
|
||||||
| placeholder | 加载占位, 为 `true` 时使用默认占位 | boolean \| slot | - | 2.0.0 |
|
| placeholder | 加载占位, 为 `true` 时使用默认占位 | boolean \| slot | - | 2.0.0 |
|
||||||
| preview | 预览参数,为 `false` 时禁用 | boolean \| [previewType](#previewType) | true | 2.0.0 |
|
| preview | 预览参数,为 `false` 时禁用 | boolean \| [previewType](#previewType) | true | 2.0.0 |
|
||||||
| src | 图片地址 | string | - | 2.0.0 |
|
| src | 图片地址 | string | - | 2.0.0 |
|
||||||
|
| previewMask | 自定义 mask | slot | - | 3.2.0 |
|
||||||
| width | 图像宽度 | string \| number | - | 2.0.0 |
|
| width | 图像宽度 | string \| number | - | 2.0.0 |
|
||||||
|
| onError | 加载错误回调 | (event: Event) => void | - | 3.2.0 |
|
||||||
|
|
||||||
### previewType
|
### previewType
|
||||||
|
|
||||||
|
@ -32,6 +34,9 @@ cover: https://gw.alipayobjects.com/zos/antfincdn/D1dXz9PZqa/image.svg
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
onVisibleChange?: (visible, prevVisible) => void;
|
onVisibleChange?: (visible, prevVisible) => void;
|
||||||
getContainer: string | HTMLElement | (() => HTMLElement);
|
getContainer: string | HTMLElement | (() => HTMLElement);
|
||||||
|
src?: string;
|
||||||
|
maskClassName?: string;
|
||||||
|
current?: number;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -143,6 +143,7 @@ export default defineComponent({
|
||||||
maskAnimation,
|
maskAnimation,
|
||||||
zIndex,
|
zIndex,
|
||||||
wrapClassName,
|
wrapClassName,
|
||||||
|
rootClassName,
|
||||||
wrapStyle,
|
wrapStyle,
|
||||||
closable,
|
closable,
|
||||||
maskProps,
|
maskProps,
|
||||||
|
@ -154,7 +155,7 @@ export default defineComponent({
|
||||||
} = props;
|
} = props;
|
||||||
const { style, class: className } = attrs;
|
const { style, class: className } = attrs;
|
||||||
return (
|
return (
|
||||||
<div class={`${prefixCls}-root`} {...pickAttrs(props, { data: true })}>
|
<div class={[`${prefixCls}-root`, rootClassName]} {...pickAttrs(props, { data: true })}>
|
||||||
<Mask
|
<Mask
|
||||||
prefixCls={prefixCls}
|
prefixCls={prefixCls}
|
||||||
visible={mask && visible}
|
visible={mask && visible}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type { CSSProperties, ExtractPropTypes, PropType } from 'vue';
|
import type { CSSProperties, ExtractPropTypes, PropType } from 'vue';
|
||||||
import PropTypes from '../_util/vue-types';
|
import PropTypes from '../_util/vue-types';
|
||||||
|
|
||||||
function dialogPropTypes() {
|
export function dialogPropTypes() {
|
||||||
return {
|
return {
|
||||||
keyboard: { type: Boolean, default: undefined },
|
keyboard: { type: Boolean, default: undefined },
|
||||||
mask: { type: Boolean, default: undefined },
|
mask: { type: Boolean, default: undefined },
|
||||||
|
@ -25,6 +25,7 @@ function dialogPropTypes() {
|
||||||
maskStyle: { type: Object as PropType<CSSProperties>, default: undefined as CSSProperties },
|
maskStyle: { type: Object as PropType<CSSProperties>, default: undefined as CSSProperties },
|
||||||
prefixCls: String,
|
prefixCls: String,
|
||||||
wrapClassName: String,
|
wrapClassName: String,
|
||||||
|
rootClassName: String,
|
||||||
width: [String, Number],
|
width: [String, Number],
|
||||||
height: [String, Number],
|
height: [String, Number],
|
||||||
zIndex: Number,
|
zIndex: Number,
|
||||||
|
|
|
@ -1,21 +1,29 @@
|
||||||
import type { ImgHTMLAttributes, CSSProperties, PropType } from 'vue';
|
import type { ImgHTMLAttributes, CSSProperties, PropType } from 'vue';
|
||||||
import { ref, watch, defineComponent, computed, onMounted } from 'vue';
|
import { ref, watch, defineComponent, computed, onMounted, onUnmounted } from 'vue';
|
||||||
import isNumber from 'lodash-es/isNumber';
|
import isNumber from 'lodash-es/isNumber';
|
||||||
import cn from '../../_util/classNames';
|
import cn from '../../_util/classNames';
|
||||||
import PropTypes from '../../_util/vue-types';
|
import PropTypes from '../../_util/vue-types';
|
||||||
import { getOffset } from '../../vc-util/Dom/css';
|
import { getOffset } from '../../vc-util/Dom/css';
|
||||||
|
import useMergedState from '../../_util/hooks/useMergedState';
|
||||||
import type { MouseEventHandler } from './Preview';
|
|
||||||
import Preview from './Preview';
|
import Preview from './Preview';
|
||||||
|
|
||||||
|
import type { MouseEventHandler } from '../../_util/EventInterface';
|
||||||
import PreviewGroup, { context } from './PreviewGroup';
|
import PreviewGroup, { context } from './PreviewGroup';
|
||||||
|
import type { IDialogChildProps } from '../../vc-dialog/IDialogPropTypes';
|
||||||
export type GetContainer = string | HTMLElement | (() => HTMLElement);
|
export type GetContainer = string | HTMLElement | (() => HTMLElement);
|
||||||
export interface ImagePreviewType {
|
import type { PreviewProps } from './Preview';
|
||||||
|
|
||||||
|
export type ImagePreviewType = Omit<
|
||||||
|
IDialogChildProps,
|
||||||
|
'mask' | 'visible' | 'closable' | 'prefixCls' | 'onClose' | 'afterClose' | 'wrapClassName'
|
||||||
|
> & {
|
||||||
|
src?: string;
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
onVisibleChange?: (value: boolean, prevValue: boolean) => void;
|
onVisibleChange?: (value: boolean, prevValue: boolean) => void;
|
||||||
getContainer?: GetContainer | false;
|
getContainer?: GetContainer | false;
|
||||||
}
|
maskClassName?: string;
|
||||||
|
icons?: PreviewProps['icons'];
|
||||||
|
};
|
||||||
|
|
||||||
export interface ImagePropsType extends Omit<ImgHTMLAttributes, 'placeholder' | 'onClick'> {
|
export interface ImagePropsType extends Omit<ImgHTMLAttributes, 'placeholder' | 'onClick'> {
|
||||||
// Original
|
// Original
|
||||||
|
@ -27,11 +35,14 @@ export interface ImagePropsType extends Omit<ImgHTMLAttributes, 'placeholder' |
|
||||||
placeholder?: boolean;
|
placeholder?: boolean;
|
||||||
fallback?: string;
|
fallback?: string;
|
||||||
preview?: boolean | ImagePreviewType;
|
preview?: boolean | ImagePreviewType;
|
||||||
|
onClick?: MouseEventHandler;
|
||||||
|
onError?: HTMLImageElement['onerror'];
|
||||||
}
|
}
|
||||||
export const imageProps = () => ({
|
export const imageProps = () => ({
|
||||||
src: String,
|
src: String,
|
||||||
wrapperClassName: String,
|
wrapperClassName: String,
|
||||||
wrapperStyle: { type: Object as PropType<CSSProperties>, default: undefined as CSSProperties },
|
wrapperStyle: { type: Object as PropType<CSSProperties>, default: undefined as CSSProperties },
|
||||||
|
rootClassName: String,
|
||||||
prefixCls: String,
|
prefixCls: String,
|
||||||
previewPrefixCls: String,
|
previewPrefixCls: String,
|
||||||
placeholder: PropTypes.any,
|
placeholder: PropTypes.any,
|
||||||
|
@ -40,10 +51,17 @@ export const imageProps = () => ({
|
||||||
type: [Boolean, Object] as PropType<boolean | ImagePreviewType>,
|
type: [Boolean, Object] as PropType<boolean | ImagePreviewType>,
|
||||||
default: true as boolean | ImagePreviewType,
|
default: true as boolean | ImagePreviewType,
|
||||||
},
|
},
|
||||||
|
onClick: {
|
||||||
|
type: Function as PropType<MouseEventHandler>,
|
||||||
|
},
|
||||||
|
onError: {
|
||||||
|
type: Function as PropType<HTMLImageElement['onerror']>,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
type ImageStatus = 'normal' | 'error' | 'loading';
|
export type ImageProps = Partial<ReturnType<typeof imageProps>>;
|
||||||
|
export type ImageStatus = 'normal' | 'error' | 'loading';
|
||||||
|
|
||||||
const mergeDefaultValue = <T extends object>(obj: T, defaultValues: object): T => {
|
export const mergeDefaultValue = <T extends object>(obj: T, defaultValues: object): T => {
|
||||||
const res = { ...obj };
|
const res = { ...obj };
|
||||||
Object.keys(defaultValues).forEach(key => {
|
Object.keys(defaultValues).forEach(key => {
|
||||||
if (obj[key] === undefined) {
|
if (obj[key] === undefined) {
|
||||||
|
@ -57,11 +75,11 @@ const ImageInternal = defineComponent({
|
||||||
name: 'Image',
|
name: 'Image',
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: imageProps(),
|
props: imageProps(),
|
||||||
emits: ['click'],
|
emits: ['click', 'error'],
|
||||||
setup(props, { attrs, slots, emit }) {
|
setup(props, { attrs, slots, emit }) {
|
||||||
const prefixCls = computed(() => props.prefixCls);
|
const prefixCls = computed(() => props.prefixCls);
|
||||||
const previewPrefixCls = computed(() => `${prefixCls.value}-preview`);
|
const previewPrefixCls = computed(() => `${prefixCls.value}-preview`);
|
||||||
const preview = computed(() => {
|
const preview = computed<ImagePreviewType>(() => {
|
||||||
const defaultValues = {
|
const defaultValues = {
|
||||||
visible: undefined,
|
visible: undefined,
|
||||||
onVisibleChange: () => {},
|
onVisibleChange: () => {},
|
||||||
|
@ -75,16 +93,21 @@ const ImageInternal = defineComponent({
|
||||||
() => (props.placeholder && props.placeholder !== true) || slots.placeholder,
|
() => (props.placeholder && props.placeholder !== true) || slots.placeholder,
|
||||||
);
|
);
|
||||||
const previewVisible = computed(() => preview.value.visible);
|
const previewVisible = computed(() => preview.value.visible);
|
||||||
const onPreviewVisibleChange = computed(() => preview.value.onVisibleChange);
|
|
||||||
const getPreviewContainer = computed(() => preview.value.getContainer);
|
const getPreviewContainer = computed(() => preview.value.getContainer);
|
||||||
|
|
||||||
const isControlled = computed(() => previewVisible.value !== undefined);
|
const isControlled = computed(() => previewVisible.value !== undefined);
|
||||||
const isShowPreview = ref(!!previewVisible.value);
|
|
||||||
watch(previewVisible, () => {
|
const onPreviewVisibleChange = (val, preval) => {
|
||||||
isShowPreview.value = !!previewVisible.value;
|
preview.value.onVisibleChange?.(val, preval);
|
||||||
|
};
|
||||||
|
const [isShowPreview, setShowPreview] = useMergedState(!!previewVisible.value, {
|
||||||
|
value: previewVisible,
|
||||||
|
onChange: onPreviewVisibleChange,
|
||||||
|
});
|
||||||
|
watch(previewVisible, val => {
|
||||||
|
setShowPreview(Boolean(val));
|
||||||
});
|
});
|
||||||
watch(isShowPreview, (val, preVal) => {
|
watch(isShowPreview, (val, preVal) => {
|
||||||
onPreviewVisibleChange.value(val, preVal);
|
onPreviewVisibleChange(val, preVal);
|
||||||
});
|
});
|
||||||
const status = ref<ImageStatus>(isCustomPlaceholder.value ? 'loading' : 'normal');
|
const status = ref<ImageStatus>(isCustomPlaceholder.value ? 'loading' : 'normal');
|
||||||
watch(
|
watch(
|
||||||
|
@ -108,13 +131,15 @@ const ImageInternal = defineComponent({
|
||||||
const onLoad = () => {
|
const onLoad = () => {
|
||||||
status.value = 'normal';
|
status.value = 'normal';
|
||||||
};
|
};
|
||||||
const onError = () => {
|
const onError = (e: Event) => {
|
||||||
status.value = 'error';
|
status.value = 'error';
|
||||||
|
emit('error', e);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPreview: MouseEventHandler = e => {
|
const onPreview: MouseEventHandler = e => {
|
||||||
if (!isControlled.value) {
|
if (!isControlled.value) {
|
||||||
const { left, top } = getOffset(e.target);
|
const { left, top } = getOffset(e.target);
|
||||||
|
|
||||||
if (isPreviewGroup.value) {
|
if (isPreviewGroup.value) {
|
||||||
setCurrent(currentId.value);
|
setCurrent(currentId.value);
|
||||||
setGroupMousePosition({
|
setGroupMousePosition({
|
||||||
|
@ -131,13 +156,13 @@ const ImageInternal = defineComponent({
|
||||||
if (isPreviewGroup.value) {
|
if (isPreviewGroup.value) {
|
||||||
setGroupShowPreview(true);
|
setGroupShowPreview(true);
|
||||||
} else {
|
} else {
|
||||||
isShowPreview.value = true;
|
setShowPreview(true);
|
||||||
}
|
}
|
||||||
emit('click', e);
|
emit('click', e);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPreviewClose = () => {
|
const onPreviewClose = () => {
|
||||||
isShowPreview.value = false;
|
setShowPreview(false);
|
||||||
if (!isControlled.value) {
|
if (!isControlled.value) {
|
||||||
mousePosition.value = null;
|
mousePosition.value = null;
|
||||||
}
|
}
|
||||||
|
@ -163,7 +188,7 @@ const ImageInternal = defineComponent({
|
||||||
return () => {};
|
return () => {};
|
||||||
}
|
}
|
||||||
|
|
||||||
unRegister = registerImage(currentId.value, props.src);
|
unRegister = registerImage(currentId.value, props.src, canPreview.value);
|
||||||
|
|
||||||
if (!canPreview.value) {
|
if (!canPreview.value) {
|
||||||
unRegister();
|
unRegister();
|
||||||
|
@ -172,13 +197,21 @@ const ImageInternal = defineComponent({
|
||||||
{ flush: 'post', immediate: true },
|
{ flush: 'post', immediate: true },
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
onUnmounted(unRegister);
|
||||||
const toSizePx = (l: number | string) => {
|
const toSizePx = (l: number | string) => {
|
||||||
if (isNumber(l)) return l + 'px';
|
if (isNumber(l)) return l + 'px';
|
||||||
return l;
|
return l;
|
||||||
};
|
};
|
||||||
return () => {
|
return () => {
|
||||||
const { prefixCls, wrapperClassName, fallback, src, preview, placeholder, wrapperStyle } =
|
const {
|
||||||
props;
|
prefixCls,
|
||||||
|
wrapperClassName,
|
||||||
|
fallback,
|
||||||
|
src: imgSrc,
|
||||||
|
placeholder,
|
||||||
|
wrapperStyle,
|
||||||
|
rootClassName,
|
||||||
|
} = props;
|
||||||
const {
|
const {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
@ -191,11 +224,12 @@ const ImageInternal = defineComponent({
|
||||||
class: cls,
|
class: cls,
|
||||||
style,
|
style,
|
||||||
} = attrs as ImgHTMLAttributes;
|
} = attrs as ImgHTMLAttributes;
|
||||||
const wrappperClass = cn(prefixCls, wrapperClassName, {
|
const { icons, maskClassName, src: previewSrc, ...dialogProps } = preview.value;
|
||||||
|
|
||||||
|
const wrappperClass = cn(prefixCls, wrapperClassName, rootClassName, {
|
||||||
[`${prefixCls}-error`]: isError.value,
|
[`${prefixCls}-error`]: isError.value,
|
||||||
});
|
});
|
||||||
const mergedSrc = isError.value && fallback ? fallback : src;
|
const mergedSrc = isError.value && fallback ? fallback : previewSrc ?? imgSrc;
|
||||||
const previewMask = slots.previewMask && slots.previewMask();
|
|
||||||
const imgCommonProps = {
|
const imgCommonProps = {
|
||||||
crossorigin,
|
crossorigin,
|
||||||
decoding,
|
decoding,
|
||||||
|
@ -215,12 +249,13 @@ const ImageInternal = defineComponent({
|
||||||
...(style as CSSProperties),
|
...(style as CSSProperties),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
class={wrappperClass}
|
class={wrappperClass}
|
||||||
onClick={
|
onClick={
|
||||||
preview && !isError.value
|
canPreview.value
|
||||||
? onPreview
|
? onPreview
|
||||||
: e => {
|
: e => {
|
||||||
emit('click', e);
|
emit('click', e);
|
||||||
|
@ -238,7 +273,7 @@ const ImageInternal = defineComponent({
|
||||||
? {
|
? {
|
||||||
src: fallback,
|
src: fallback,
|
||||||
}
|
}
|
||||||
: { onLoad, onError, src })}
|
: { onLoad, onError, src: imgSrc })}
|
||||||
ref={img}
|
ref={img}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -248,12 +283,13 @@ const ImageInternal = defineComponent({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* Preview Click Mask */}
|
{/* Preview Click Mask */}
|
||||||
{previewMask && canPreview.value && (
|
{slots.previewMask && canPreview.value && (
|
||||||
<div class={`${prefixCls}-mask`}>{previewMask}</div>
|
<div class={[`${prefixCls}-mask`, maskClassName]}>{slots.previewMask()}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!isPreviewGroup.value && canPreview.value && (
|
{!isPreviewGroup.value && canPreview.value && (
|
||||||
<Preview
|
<Preview
|
||||||
|
{...dialogProps}
|
||||||
aria-hidden={!isShowPreview.value}
|
aria-hidden={!isShowPreview.value}
|
||||||
visible={isShowPreview.value}
|
visible={isShowPreview.value}
|
||||||
prefixCls={previewPrefixCls.value}
|
prefixCls={previewPrefixCls.value}
|
||||||
|
@ -262,6 +298,8 @@ const ImageInternal = defineComponent({
|
||||||
src={mergedSrc}
|
src={mergedSrc}
|
||||||
alt={alt}
|
alt={alt}
|
||||||
getContainer={getPreviewContainer.value}
|
getContainer={getPreviewContainer.value}
|
||||||
|
icons={icons}
|
||||||
|
rootClassName={rootClassName}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -1,47 +1,65 @@
|
||||||
import { computed, defineComponent, onMounted, onUnmounted, reactive, ref, watch } from 'vue';
|
import {
|
||||||
import RotateLeftOutlined from '@ant-design/icons-vue/RotateLeftOutlined';
|
computed,
|
||||||
import RotateRightOutlined from '@ant-design/icons-vue/RotateRightOutlined';
|
defineComponent,
|
||||||
import ZoomInOutlined from '@ant-design/icons-vue/ZoomInOutlined';
|
onMounted,
|
||||||
import ZoomOutOutlined from '@ant-design/icons-vue/ZoomOutOutlined';
|
onUnmounted,
|
||||||
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
|
reactive,
|
||||||
import LeftOutlined from '@ant-design/icons-vue/LeftOutlined';
|
ref,
|
||||||
import RightOutlined from '@ant-design/icons-vue/RightOutlined';
|
watch,
|
||||||
|
cloneVNode,
|
||||||
|
} from 'vue';
|
||||||
|
import type { VNode, PropType } from 'vue';
|
||||||
|
|
||||||
import classnames from '../../_util/classNames';
|
import classnames from '../../_util/classNames';
|
||||||
import Dialog from '../../vc-dialog';
|
import Dialog from '../../vc-dialog';
|
||||||
import getIDialogPropTypes from '../../vc-dialog/IDialogPropTypes';
|
import { type IDialogChildProps, dialogPropTypes } from '../../vc-dialog/IDialogPropTypes';
|
||||||
import { getOffset } from '../../vc-util/Dom/css';
|
import { getOffset } from '../../vc-util/Dom/css';
|
||||||
import addEventListener from '../../vc-util/Dom/addEventListener';
|
import addEventListener from '../../vc-util/Dom/addEventListener';
|
||||||
import { warning } from '../../vc-util/warning';
|
import { warning } from '../../vc-util/warning';
|
||||||
import useFrameSetState from './hooks/useFrameSetState';
|
import useFrameSetState from './hooks/useFrameSetState';
|
||||||
import getFixScaleEleTransPosition from './getFixScaleEleTransPosition';
|
import getFixScaleEleTransPosition from './getFixScaleEleTransPosition';
|
||||||
|
import type { MouseEventHandler, WheelEventHandler } from '../../_util/EventInterface';
|
||||||
|
|
||||||
import { context } from './PreviewGroup';
|
import { context } from './PreviewGroup';
|
||||||
|
|
||||||
const IDialogPropTypes = getIDialogPropTypes();
|
export interface PreviewProps extends Omit<IDialogChildProps, 'onClose' | 'mask'> {
|
||||||
export type MouseEventHandler = (payload: MouseEvent) => void;
|
|
||||||
|
|
||||||
export interface PreviewProps extends Omit<typeof IDialogPropTypes, 'onClose'> {
|
|
||||||
onClose?: (e: Element) => void;
|
onClose?: (e: Element) => void;
|
||||||
src?: string;
|
src?: string;
|
||||||
alt?: string;
|
alt?: string;
|
||||||
|
rootClassName?: string;
|
||||||
|
icons?: {
|
||||||
|
rotateLeft?: VNode;
|
||||||
|
rotateRight?: VNode;
|
||||||
|
zoomIn?: VNode;
|
||||||
|
zoomOut?: VNode;
|
||||||
|
close?: VNode;
|
||||||
|
left?: VNode;
|
||||||
|
right?: VNode;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialPosition = {
|
const initialPosition = {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
};
|
};
|
||||||
const PreviewType = {
|
export const previewProps = {
|
||||||
|
...dialogPropTypes(),
|
||||||
src: String,
|
src: String,
|
||||||
alt: String,
|
alt: String,
|
||||||
...IDialogPropTypes,
|
rootClassName: String,
|
||||||
|
icons: {
|
||||||
|
type: Object as PropType<PreviewProps['icons']>,
|
||||||
|
default: () => ({} as PreviewProps['icons']),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
const Preview = defineComponent({
|
const Preview = defineComponent({
|
||||||
name: 'Preview',
|
name: 'Preview',
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: PreviewType,
|
props: previewProps,
|
||||||
emits: ['close', 'afterClose'],
|
emits: ['close', 'afterClose'],
|
||||||
setup(props, { emit, attrs }) {
|
setup(props, { emit, attrs }) {
|
||||||
|
const { rotateLeft, rotateRight, zoomIn, zoomOut, close, left, right } = reactive(props.icons);
|
||||||
|
|
||||||
const scale = ref(1);
|
const scale = ref(1);
|
||||||
const rotate = ref(0);
|
const rotate = ref(0);
|
||||||
const [position, setPosition] = useFrameSetState<{
|
const [position, setPosition] = useFrameSetState<{
|
||||||
|
@ -65,22 +83,22 @@ const Preview = defineComponent({
|
||||||
const isMoving = ref(false);
|
const isMoving = ref(false);
|
||||||
const groupContext = context.inject();
|
const groupContext = context.inject();
|
||||||
const { previewUrls, current, isPreviewGroup, setCurrent } = groupContext;
|
const { previewUrls, current, isPreviewGroup, setCurrent } = groupContext;
|
||||||
const previewGroupCount = computed(() => Object.keys(previewUrls).length);
|
const previewGroupCount = computed(() => previewUrls.value.size);
|
||||||
const previewUrlsKeys = computed(() => Object.keys(previewUrls));
|
const previewUrlsKeys = computed(() => Array.from(previewUrls.value.keys()));
|
||||||
const currentPreviewIndex = computed(() =>
|
const currentPreviewIndex = computed(() => previewUrlsKeys.value.indexOf(current.value));
|
||||||
previewUrlsKeys.value.indexOf(String(current.value)),
|
const combinationSrc = computed(() => {
|
||||||
);
|
return isPreviewGroup.value ? previewUrls.value.get(current.value) : props.src;
|
||||||
const combinationSrc = computed(() =>
|
});
|
||||||
isPreviewGroup.value ? previewUrls[current.value] : props.src,
|
|
||||||
);
|
|
||||||
const showLeftOrRightSwitches = computed(
|
const showLeftOrRightSwitches = computed(
|
||||||
() => isPreviewGroup.value && previewGroupCount.value > 1,
|
() => isPreviewGroup.value && previewGroupCount.value > 1,
|
||||||
);
|
);
|
||||||
|
const lastWheelZoomDirection = ref({ wheelDirection: 0 });
|
||||||
|
|
||||||
const onAfterClose = () => {
|
const onAfterClose = () => {
|
||||||
scale.value = 1;
|
scale.value = 1;
|
||||||
rotate.value = 0;
|
rotate.value = 0;
|
||||||
setPosition(initialPosition);
|
setPosition(initialPosition);
|
||||||
|
emit('afterClose');
|
||||||
};
|
};
|
||||||
|
|
||||||
const onZoomIn = () => {
|
const onZoomIn = () => {
|
||||||
|
@ -106,7 +124,7 @@ const Preview = defineComponent({
|
||||||
// Without this mask close will abnormal
|
// Without this mask close will abnormal
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
if (currentPreviewIndex.value > 0) {
|
if (currentPreviewIndex.value > 0) {
|
||||||
setCurrent(previewUrlsKeys.value[String(currentPreviewIndex.value - 1)]);
|
setCurrent(previewUrlsKeys.value[currentPreviewIndex.value - 1]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -115,7 +133,7 @@ const Preview = defineComponent({
|
||||||
// Without this mask close will abnormal
|
// Without this mask close will abnormal
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
if (currentPreviewIndex.value < previewGroupCount.value - 1) {
|
if (currentPreviewIndex.value < previewGroupCount.value - 1) {
|
||||||
setCurrent(previewUrlsKeys.value[String(currentPreviewIndex.value + 1)]);
|
setCurrent(previewUrlsKeys.value[currentPreviewIndex.value + 1]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -126,28 +144,28 @@ const Preview = defineComponent({
|
||||||
const iconClassName = `${props.prefixCls}-operations-icon`;
|
const iconClassName = `${props.prefixCls}-operations-icon`;
|
||||||
const tools = [
|
const tools = [
|
||||||
{
|
{
|
||||||
icon: CloseOutlined,
|
icon: close,
|
||||||
onClick: onClose,
|
onClick: onClose,
|
||||||
type: 'close',
|
type: 'close',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: ZoomInOutlined,
|
icon: zoomIn,
|
||||||
onClick: onZoomIn,
|
onClick: onZoomIn,
|
||||||
type: 'zoomIn',
|
type: 'zoomIn',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: ZoomOutOutlined,
|
icon: zoomOut,
|
||||||
onClick: onZoomOut,
|
onClick: onZoomOut,
|
||||||
type: 'zoomOut',
|
type: 'zoomOut',
|
||||||
disabled: computed(() => scale.value === 1),
|
disabled: computed(() => scale.value === 1),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: RotateRightOutlined,
|
icon: rotateRight,
|
||||||
onClick: onRotateRight,
|
onClick: onRotateRight,
|
||||||
type: 'rotateRight',
|
type: 'rotateRight',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: RotateLeftOutlined,
|
icon: rotateLeft,
|
||||||
onClick: onRotateLeft,
|
onClick: onRotateLeft,
|
||||||
type: 'rotateLeft',
|
type: 'rotateLeft',
|
||||||
},
|
},
|
||||||
|
@ -175,6 +193,8 @@ const Preview = defineComponent({
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMouseDown: MouseEventHandler = event => {
|
const onMouseDown: MouseEventHandler = event => {
|
||||||
|
// Only allow main button
|
||||||
|
if (event.button !== 0) return;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
// Without this mask close will abnormal
|
// Without this mask close will abnormal
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
@ -193,6 +213,14 @@ const Preview = defineComponent({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onWheelMove: WheelEventHandler = event => {
|
||||||
|
if (!props.visible) return;
|
||||||
|
event.preventDefault();
|
||||||
|
const wheelDirection = event.deltaY;
|
||||||
|
lastWheelZoomDirection.value = { wheelDirection };
|
||||||
|
};
|
||||||
|
|
||||||
let removeListeners = () => {};
|
let removeListeners = () => {};
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
watch(
|
watch(
|
||||||
|
@ -204,6 +232,9 @@ const Preview = defineComponent({
|
||||||
|
|
||||||
const onMouseUpListener = addEventListener(window, 'mouseup', onMouseUp, false);
|
const onMouseUpListener = addEventListener(window, 'mouseup', onMouseUp, false);
|
||||||
const onMouseMoveListener = addEventListener(window, 'mousemove', onMouseMove, false);
|
const onMouseMoveListener = addEventListener(window, 'mousemove', onMouseMove, false);
|
||||||
|
const onScrollWheelListener = addEventListener(window, 'wheel', onWheelMove, {
|
||||||
|
passive: false,
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Resolve if in iframe lost event
|
// Resolve if in iframe lost event
|
||||||
|
@ -225,6 +256,7 @@ const Preview = defineComponent({
|
||||||
removeListeners = () => {
|
removeListeners = () => {
|
||||||
onMouseUpListener.remove();
|
onMouseUpListener.remove();
|
||||||
onMouseMoveListener.remove();
|
onMouseMoveListener.remove();
|
||||||
|
onScrollWheelListener.remove();
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
if (onTopMouseUpListener) onTopMouseUpListener.remove();
|
if (onTopMouseUpListener) onTopMouseUpListener.remove();
|
||||||
|
@ -240,6 +272,8 @@ const Preview = defineComponent({
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
const { visible, prefixCls, rootClassName } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
{...attrs}
|
{...attrs}
|
||||||
|
@ -247,11 +281,12 @@ const Preview = defineComponent({
|
||||||
maskTransitionName="fade"
|
maskTransitionName="fade"
|
||||||
closable={false}
|
closable={false}
|
||||||
keyboard
|
keyboard
|
||||||
prefixCls={props.prefixCls}
|
prefixCls={prefixCls}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
afterClose={onAfterClose}
|
afterClose={onAfterClose}
|
||||||
visible={props.visible}
|
visible={visible}
|
||||||
wrapClassName={wrapClassName}
|
wrapClassName={wrapClassName}
|
||||||
|
rootClassName={rootClassName}
|
||||||
getContainer={props.getContainer}
|
getContainer={props.getContainer}
|
||||||
>
|
>
|
||||||
<ul class={`${props.prefixCls}-operations`}>
|
<ul class={`${props.prefixCls}-operations`}>
|
||||||
|
@ -263,7 +298,7 @@ const Preview = defineComponent({
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
key={type}
|
key={type}
|
||||||
>
|
>
|
||||||
<IconType class={iconClassName} />
|
{cloneVNode(IconType, { class: iconClassName })}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -291,7 +326,7 @@ const Preview = defineComponent({
|
||||||
})}
|
})}
|
||||||
onClick={onSwitchLeft}
|
onClick={onSwitchLeft}
|
||||||
>
|
>
|
||||||
<LeftOutlined />
|
{left}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{showLeftOrRightSwitches.value && (
|
{showLeftOrRightSwitches.value && (
|
||||||
|
@ -302,7 +337,7 @@ const Preview = defineComponent({
|
||||||
})}
|
})}
|
||||||
onClick={onSwitchRight}
|
onClick={onSwitchRight}
|
||||||
>
|
>
|
||||||
<RightOutlined />
|
{right}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
|
@ -1,19 +1,39 @@
|
||||||
import type { Ref } from 'vue';
|
import type { PropType, Ref, ComputedRef } from 'vue';
|
||||||
import { ref, provide, defineComponent, inject, reactive } from 'vue';
|
import { ref, provide, defineComponent, inject, watch, reactive, computed, watchEffect } from 'vue';
|
||||||
|
import { type ImagePreviewType, mergeDefaultValue } from './Image';
|
||||||
import Preview from './Preview';
|
import Preview from './Preview';
|
||||||
|
import type { PreviewProps } from './Preview';
|
||||||
|
|
||||||
|
export interface PreviewGroupPreview
|
||||||
|
extends Omit<ImagePreviewType, 'icons' | 'mask' | 'maskClassName'> {
|
||||||
|
/**
|
||||||
|
* If Preview the show img index
|
||||||
|
* @default 0
|
||||||
|
*/
|
||||||
|
current?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface GroupConsumerProps {
|
export interface GroupConsumerProps {
|
||||||
previewPrefixCls?: string;
|
previewPrefixCls?: string;
|
||||||
|
icons?: PreviewProps['icons'];
|
||||||
|
preview?: boolean | PreviewGroupPreview;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface PreviewUrl {
|
||||||
|
url: string;
|
||||||
|
canPreview: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface GroupConsumerValue extends GroupConsumerProps {
|
export interface GroupConsumerValue extends GroupConsumerProps {
|
||||||
isPreviewGroup?: Ref<boolean | undefined>;
|
isPreviewGroup?: Ref<boolean | undefined>;
|
||||||
previewUrls: Record<number, string>;
|
previewUrls: ComputedRef<Map<number, string>>;
|
||||||
setPreviewUrls: (previewUrls: Record<number, string>) => void;
|
setPreviewUrls: (id: number, url: string, canPreview?: boolean) => void;
|
||||||
current: Ref<number>;
|
current: Ref<number>;
|
||||||
setCurrent: (current: number) => void;
|
setCurrent: (current: number) => void;
|
||||||
setShowPreview: (isShowPreview: boolean) => void;
|
setShowPreview: (isShowPreview: boolean) => void;
|
||||||
setMousePosition: (mousePosition: null | { x: number; y: number }) => void;
|
setMousePosition: (mousePosition: null | { x: number; y: number }) => void;
|
||||||
registerImage: (id: number, url: string) => () => void;
|
registerImage: (id: number, url: string, canPreview?: boolean) => () => void;
|
||||||
|
rootClassName?: string;
|
||||||
}
|
}
|
||||||
const previewGroupContext = Symbol('previewGroupContext');
|
const previewGroupContext = Symbol('previewGroupContext');
|
||||||
export const context = {
|
export const context = {
|
||||||
|
@ -23,13 +43,14 @@ export const context = {
|
||||||
inject: () => {
|
inject: () => {
|
||||||
return inject<GroupConsumerValue>(previewGroupContext, {
|
return inject<GroupConsumerValue>(previewGroupContext, {
|
||||||
isPreviewGroup: ref(false),
|
isPreviewGroup: ref(false),
|
||||||
previewUrls: reactive({}),
|
previewUrls: computed(() => new Map()),
|
||||||
setPreviewUrls: () => {},
|
setPreviewUrls: () => {},
|
||||||
current: ref(null),
|
current: ref(null),
|
||||||
setCurrent: () => {},
|
setCurrent: () => {},
|
||||||
setShowPreview: () => {},
|
setShowPreview: () => {},
|
||||||
setMousePosition: () => {},
|
setMousePosition: () => {},
|
||||||
registerImage: null,
|
registerImage: null,
|
||||||
|
rootClassName: '',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -37,14 +58,56 @@ export const context = {
|
||||||
const Group = defineComponent({
|
const Group = defineComponent({
|
||||||
name: 'PreviewGroup',
|
name: 'PreviewGroup',
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: { previewPrefixCls: String },
|
props: {
|
||||||
|
previewPrefixCls: String,
|
||||||
|
preview: {
|
||||||
|
type: [Boolean, Object] as PropType<boolean | ImagePreviewType>,
|
||||||
|
default: true as boolean | ImagePreviewType,
|
||||||
|
},
|
||||||
|
icons: {
|
||||||
|
type: Object as PropType<PreviewProps['icons']>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
setup(props, { slots }) {
|
setup(props, { slots }) {
|
||||||
const previewUrls = reactive<Record<number, string>>({});
|
const preview = computed<PreviewGroupPreview>(() => {
|
||||||
|
const defaultValues = {
|
||||||
|
visible: undefined,
|
||||||
|
onVisibleChange: () => {},
|
||||||
|
getContainer: undefined,
|
||||||
|
current: 0,
|
||||||
|
};
|
||||||
|
return typeof props.preview === 'object'
|
||||||
|
? mergeDefaultValue(props.preview, defaultValues)
|
||||||
|
: defaultValues;
|
||||||
|
});
|
||||||
|
const previewUrls = reactive<Map<number, PreviewUrl>>(new Map());
|
||||||
const current = ref<number>();
|
const current = ref<number>();
|
||||||
const isShowPreview = ref<boolean>(false);
|
|
||||||
|
const previewVisible = computed(() => preview.value.visible);
|
||||||
|
const onPreviewVisibleChange = computed(() => preview.value.onVisibleChange);
|
||||||
|
const getPreviewContainer = computed(() => preview.value.getContainer);
|
||||||
|
|
||||||
|
const isShowPreview = ref(!!previewVisible.value);
|
||||||
|
|
||||||
const mousePosition = ref<{ x: number; y: number }>(null);
|
const mousePosition = ref<{ x: number; y: number }>(null);
|
||||||
const setPreviewUrls = (val: Record<number, string>) => {
|
const isControlled = computed(() => previewVisible.value !== undefined);
|
||||||
Object.assign(previewUrls, val);
|
const previewUrlsKeys = computed(() => Array.from(previewUrls.keys()));
|
||||||
|
const currentControlledKey = computed(() => previewUrlsKeys.value[preview.value.current]);
|
||||||
|
const canPreviewUrls = computed(
|
||||||
|
() =>
|
||||||
|
new Map<number, string>(
|
||||||
|
Array.from(previewUrls)
|
||||||
|
.filter(([, { canPreview }]) => !!canPreview)
|
||||||
|
.map(([id, { url }]) => [id, url]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const setPreviewUrls = (id: number, url: string, canPreview = true) => {
|
||||||
|
previewUrls.set(id, {
|
||||||
|
url,
|
||||||
|
canPreview,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
const setCurrent = (val: number) => {
|
const setCurrent = (val: number) => {
|
||||||
current.value = val;
|
current.value = val;
|
||||||
|
@ -55,21 +118,54 @@ const Group = defineComponent({
|
||||||
const setShowPreview = (val: boolean) => {
|
const setShowPreview = (val: boolean) => {
|
||||||
isShowPreview.value = val;
|
isShowPreview.value = val;
|
||||||
};
|
};
|
||||||
const registerImage = (id: number, url: string) => {
|
|
||||||
previewUrls[id] = url;
|
|
||||||
|
|
||||||
return () => {
|
const registerImage = (id: number, url: string, canPreview = true) => {
|
||||||
delete previewUrls[id];
|
const unRegister = () => {
|
||||||
|
previewUrls.delete(id);
|
||||||
};
|
};
|
||||||
|
previewUrls.set(id, {
|
||||||
|
url,
|
||||||
|
canPreview,
|
||||||
|
});
|
||||||
|
return unRegister;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPreviewClose = (e: any) => {
|
const onPreviewClose = (e: any) => {
|
||||||
e?.stopPropagation();
|
e?.stopPropagation();
|
||||||
isShowPreview.value = false;
|
isShowPreview.value = false;
|
||||||
mousePosition.value = null;
|
mousePosition.value = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(previewVisible, () => {
|
||||||
|
isShowPreview.value = !!previewVisible.value;
|
||||||
|
});
|
||||||
|
watch(isShowPreview, (val, preVal) => {
|
||||||
|
onPreviewVisibleChange.value(val, preVal);
|
||||||
|
});
|
||||||
|
watch(
|
||||||
|
currentControlledKey,
|
||||||
|
val => {
|
||||||
|
setCurrent(val);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
flush: 'post',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
watchEffect(
|
||||||
|
() => {
|
||||||
|
if (!isShowPreview.value && isControlled.value) {
|
||||||
|
setCurrent(currentControlledKey.value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
flush: 'post',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
context.provide({
|
context.provide({
|
||||||
isPreviewGroup: ref(true),
|
isPreviewGroup: ref(true),
|
||||||
previewUrls,
|
previewUrls: canPreviewUrls,
|
||||||
setPreviewUrls,
|
setPreviewUrls,
|
||||||
current,
|
current,
|
||||||
setCurrent,
|
setCurrent,
|
||||||
|
@ -77,17 +173,22 @@ const Group = defineComponent({
|
||||||
setMousePosition,
|
setMousePosition,
|
||||||
registerImage,
|
registerImage,
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
const { ...dialogProps } = preview.value;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{slots.default && slots.default()}
|
{slots.default && slots.default()}
|
||||||
<Preview
|
<Preview
|
||||||
|
{...dialogProps}
|
||||||
ria-hidden={!isShowPreview.value}
|
ria-hidden={!isShowPreview.value}
|
||||||
visible={isShowPreview.value}
|
visible={isShowPreview.value}
|
||||||
prefixCls={props.previewPrefixCls}
|
prefixCls={props.previewPrefixCls}
|
||||||
onClose={onPreviewClose}
|
onClose={onPreviewClose}
|
||||||
mousePosition={mousePosition.value}
|
mousePosition={mousePosition.value}
|
||||||
src={previewUrls[current.value]}
|
src={canPreviewUrls.value.get(current.value)}
|
||||||
|
icons={props.icons}
|
||||||
|
getContainer={getPreviewContainer.value}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue