diff --git a/packages/opendesign/src/figure/OFigure.vue b/packages/opendesign/src/figure/OFigure.vue
index 5ef23e773416309a91a8ee67ee44f224600f9525..2479886c9517387f1aba999c106389af524e52f2 100644
--- a/packages/opendesign/src/figure/OFigure.vue
+++ b/packages/opendesign/src/figure/OFigure.vue
@@ -1,13 +1,15 @@
+
+
+
+
![]()
+
+
+
+
{{ zoomRatio }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/opendesign/src/image-viewer/__demo__/ImageViewerBasic.vue b/packages/opendesign/src/image-viewer/__demo__/ImageViewerBasic.vue
new file mode 100644
index 0000000000000000000000000000000000000000..479b44de7af235e4a5020bd60ea77c038b5c8d3c
--- /dev/null
+++ b/packages/opendesign/src/image-viewer/__demo__/ImageViewerBasic.vue
@@ -0,0 +1,24 @@
+
+
+ 基本
+
+
+
diff --git a/packages/opendesign/src/image-viewer/__demo__/ImageViewerInFigure.vue b/packages/opendesign/src/image-viewer/__demo__/ImageViewerInFigure.vue
new file mode 100644
index 0000000000000000000000000000000000000000..8ee782857b3c7fbd7904c4fb1452632bb19d7067
--- /dev/null
+++ b/packages/opendesign/src/image-viewer/__demo__/ImageViewerInFigure.vue
@@ -0,0 +1,33 @@
+
+
+ 搭配figure使用
+
+
+
diff --git a/packages/opendesign/src/image-viewer/__demo__/ImageViewerInLayer.vue b/packages/opendesign/src/image-viewer/__demo__/ImageViewerInLayer.vue
new file mode 100644
index 0000000000000000000000000000000000000000..240df07d8b86c2bfaa030eb80be27b9ddcda4966
--- /dev/null
+++ b/packages/opendesign/src/image-viewer/__demo__/ImageViewerInLayer.vue
@@ -0,0 +1,42 @@
+
+
+ 搭配layer使用
+
+
+
diff --git a/packages/opendesign/src/image-viewer/__demo__/TheIndex.vue b/packages/opendesign/src/image-viewer/__demo__/TheIndex.vue
new file mode 100644
index 0000000000000000000000000000000000000000..e7eb2326834404e80ecf068399a773834cd2f1ee
--- /dev/null
+++ b/packages/opendesign/src/image-viewer/__demo__/TheIndex.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
切换demo
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/opendesign/src/image-viewer/index.ts b/packages/opendesign/src/image-viewer/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f1f767c91f0d8e736dd1d17b4d0ff85d2e2a9079
--- /dev/null
+++ b/packages/opendesign/src/image-viewer/index.ts
@@ -0,0 +1,11 @@
+import _OImageViewer from './OImageViewer.vue';
+import type { App } from 'vue';
+
+const OImageViewer = Object.assign(_OImageViewer, {
+ install(app: App) {
+ app.component('OImageViewer', _OImageViewer);
+ },
+});
+
+export { OImageViewer };
+export * from './types';
diff --git a/packages/opendesign/src/image-viewer/style/index.scss b/packages/opendesign/src/image-viewer/style/index.scss
new file mode 100644
index 0000000000000000000000000000000000000000..b32f7e737480df416615e3328733e83cd8258baa
--- /dev/null
+++ b/packages/opendesign/src/image-viewer/style/index.scss
@@ -0,0 +1,2 @@
+@use './var.scss' as *;
+@use './style.scss' as *;
\ No newline at end of file
diff --git a/packages/opendesign/src/image-viewer/style/index.ts b/packages/opendesign/src/image-viewer/style/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..786fce9c52bcc5902ae5ad430cbd07f8ead8a699
--- /dev/null
+++ b/packages/opendesign/src/image-viewer/style/index.ts
@@ -0,0 +1,2 @@
+import '../../_styles';
+import './index.scss';
diff --git a/packages/opendesign/src/image-viewer/style/style.scss b/packages/opendesign/src/image-viewer/style/style.scss
new file mode 100644
index 0000000000000000000000000000000000000000..24f0d33518ec5bbaf1a2b3496c8fad4327aad405
--- /dev/null
+++ b/packages/opendesign/src/image-viewer/style/style.scss
@@ -0,0 +1,80 @@
+@use '../../_styles/mixin.scss' as *;
+
+.o-image-viewer {
+ position: relative;
+ display: flex;
+ align-items: center;
+}
+
+.o-image-viewer-container {
+ position: relative;
+ user-select: none;
+ will-change: transform;
+}
+
+.o-image-viewer-dragging {
+ cursor: var(--image-viewer-cursor-type);
+}
+
+.o-image-viewer-img {
+ display: block;
+ max-width: 100vw;
+ max-height: 100vh;
+}
+
+.o-image-zoom-ratio {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate3d(-50%, -50%, 0);
+ width: var(--image-viewer-ratio-width);
+ height: var(--image-viewer-ratio-height);
+ line-height: var(--image-viewer-ratio-height);
+ text-align: center;
+ font-weight: 500;
+ color: var(--image-viewer-ratio-color);
+ background-color: var(--image-viewer-ratio-bgc);
+ border-radius: var(--image-viewer-ratio-radius);
+ backdrop-filter: blur(var(--image-viewer-ratio-backdrop-filter));
+ user-select: none;
+}
+
+.o-image-viewer-action {
+ position: absolute;
+ left: 50%;
+ bottom: var(--image-viewer-action-bottom);
+ transform: translate3d(-50%, 0, 0);
+ display: flex;
+ align-items: center;
+ width: fit-content;
+ padding: var(--image-viewer-action-padding);
+ background-color: var(--o-color-white);
+ border-radius: var(--o-radius-m);
+}
+
+.o-image-action-item {
+ cursor: pointer;
+
+ &:not(:first-child) {
+ margin-left: var(--image-viewer-action-item-gap);
+ }
+
+ &:last-child {
+ opacity: 0.8;
+ }
+
+ @include hover {
+ opacity: 1;
+
+ .o-image-action-icon {
+ color: var(--o-color-info1)
+ }
+ }
+}
+
+.o-image-action-icon {
+ display: block;
+ width: var(--o-control_size-s);
+ height: var(--o-control_size-s);
+ color: var(--o-color-info2)
+}
\ No newline at end of file
diff --git a/packages/opendesign/src/image-viewer/style/theme-ascend.index.ts b/packages/opendesign/src/image-viewer/style/theme-ascend.index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5bdacd9c6ee5aca363917db515e154542266f4c2
--- /dev/null
+++ b/packages/opendesign/src/image-viewer/style/theme-ascend.index.ts
@@ -0,0 +1,3 @@
+import '../../_styles';
+import './index.scss';
+import './theme-ascend.scss';
diff --git a/packages/opendesign/src/image-viewer/style/theme-ascend.scss b/packages/opendesign/src/image-viewer/style/theme-ascend.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e604f805c4daf24597bb24ec2b1c2f2ce0b4210f
--- /dev/null
+++ b/packages/opendesign/src/image-viewer/style/theme-ascend.scss
@@ -0,0 +1 @@
+@use '../../_styles/mixin.scss' as *;
\ No newline at end of file
diff --git a/packages/opendesign/src/image-viewer/style/theme-kunpeng.index.ts b/packages/opendesign/src/image-viewer/style/theme-kunpeng.index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c2b4508ff081979f08fbcfda03f045c172570d33
--- /dev/null
+++ b/packages/opendesign/src/image-viewer/style/theme-kunpeng.index.ts
@@ -0,0 +1,3 @@
+import '../../_styles';
+import './index.scss';
+import './theme-kunpeng.scss';
diff --git a/packages/opendesign/src/image-viewer/style/theme-kunpeng.scss b/packages/opendesign/src/image-viewer/style/theme-kunpeng.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e604f805c4daf24597bb24ec2b1c2f2ce0b4210f
--- /dev/null
+++ b/packages/opendesign/src/image-viewer/style/theme-kunpeng.scss
@@ -0,0 +1 @@
+@use '../../_styles/mixin.scss' as *;
\ No newline at end of file
diff --git a/packages/opendesign/src/image-viewer/style/theme-openeuler.index.ts b/packages/opendesign/src/image-viewer/style/theme-openeuler.index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5a4690a05640253920c989242a44e2e2e5b4b720
--- /dev/null
+++ b/packages/opendesign/src/image-viewer/style/theme-openeuler.index.ts
@@ -0,0 +1,3 @@
+import '../../_styles';
+import './index.scss';
+import './theme-openeuler.scss';
diff --git a/packages/opendesign/src/image-viewer/style/theme-openeuler.scss b/packages/opendesign/src/image-viewer/style/theme-openeuler.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e604f805c4daf24597bb24ec2b1c2f2ce0b4210f
--- /dev/null
+++ b/packages/opendesign/src/image-viewer/style/theme-openeuler.scss
@@ -0,0 +1 @@
+@use '../../_styles/mixin.scss' as *;
\ No newline at end of file
diff --git a/packages/opendesign/src/image-viewer/style/var.scss b/packages/opendesign/src/image-viewer/style/var.scss
new file mode 100644
index 0000000000000000000000000000000000000000..57418865e7eb26d41b3719e84016b1286f3df069
--- /dev/null
+++ b/packages/opendesign/src/image-viewer/style/var.scss
@@ -0,0 +1,13 @@
+.o-image-viewer {
+ --image-viewer-ratio-width: 82px;
+ --image-viewer-ratio-height: 24px;
+ --image-viewer-ratio-bgc: var(--o-color-mask2);
+ --image-viewer-ratio-color: var(--o-color-white);
+ --image-viewer-ratio-radius: var(--o-radius-xs);
+ --image-viewer-ratio-backdrop-filter: 4px;
+
+ --image-viewer-action-bottom: 72px;
+ --image-viewer-action-padding: 16px 24px;
+ --image-viewer-action-item-gap: 40px;
+ --image-viewer-cursor-type: move;
+}
\ No newline at end of file
diff --git a/packages/opendesign/src/image-viewer/types.ts b/packages/opendesign/src/image-viewer/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c1ee41e63b91ec9b6c90406f1f23b12e67a37f63
--- /dev/null
+++ b/packages/opendesign/src/image-viewer/types.ts
@@ -0,0 +1,71 @@
+import { ExtractPropTypes, PropType } from 'vue';
+
+export type ImageViewerAction = 'zoomIn' | 'zoomOut';
+
+export const imageViewerProps = {
+ /**
+ * @zh-CN 预览资源组
+ * @en-US Preview Resource Group.
+ */
+ previewList: {
+ type: Array as PropType>,
+ default: () => [],
+ },
+ /**
+ * @zh-CN 预览图片缩放的速率
+ * @en-US Preview the rate of image zooming.
+ */
+ zoomRate: {
+ type: Number,
+ default: 1.2,
+ },
+ /**
+ * @zh-CN 预览图片最小缩放比例
+ * @en-US Minimum zoom ratio for preview images.
+ */
+ minScale: {
+ type: Number,
+ default: 0.1,
+ },
+ /**
+ * @zh-CN 预览图片最大缩放比例
+ * @en-US Maximum zoom ratio for preview images.
+ */
+ maxScale: {
+ type: Number,
+ default: 8,
+ },
+ /**
+ * @zh-CN 展示预览图片实际缩放百分比
+ * @en-US Show the actual zoom percentage of the preview image.
+ */
+ showZoomRatio: {
+ type: Boolean,
+ default: true,
+ },
+ /**
+ * @zh-CN 展示预览图片实际缩放百分比的持续时间
+ * @en-US Show the duration of the actual zoom percentage of the preview image.
+ */
+ duration: {
+ type: Number,
+ default: 500,
+ },
+ /**
+ * @zh-CN 展示预览操作区域
+ * @en-US Display the preview operation area.
+ */
+ showActionArea: {
+ type: Boolean,
+ default: true,
+ },
+ /**
+ * @zh-CN 预览组起始预览图片的下标
+ * @en-US The index of the preview group's initial preview image.
+ */
+ initialIndex: {
+ type: Number,
+ default: 0,
+ },
+};
+export type ImageViewerPropsT = ExtractPropTypes;
diff --git a/packages/portal/src/router.ts b/packages/portal/src/router.ts
index 657cae2a4b9c01e2e0ead5959076befc59913e45..1e94e1067be437bd4f6b664a280a833855253f8d 100644
--- a/packages/portal/src/router.ts
+++ b/packages/portal/src/router.ts
@@ -388,6 +388,14 @@ export const routes = [
title: '骨架屏 Skeleton',
},
},
+ {
+ path: '/image-viewer',
+ name: 'ImageViewer',
+ component: () => import('@opendesign-src/image-viewer/__demo__/TheIndex.vue'),
+ meta: {
+ title: '图片形变 ImageViewer',
+ },
+ },
{
path: '/resize-observer',
name: 'ResizeObserver',