feat: 支持图片点击/鼠标移入移出事件 & 支持图片预览放大缩小动作 (#8266)

* feat: 支持图片点击/鼠标移入移出事件 & 支持图片预览放大缩小动作

* docs & test: 补充image文档及单测
This commit is contained in:
chengjinyang0 2023-11-10 11:22:29 +08:00 committed by GitHub
parent 67afdb0b05
commit 6480245738
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 808 additions and 35 deletions

View File

@ -434,7 +434,7 @@ List 的内容、Card 卡片的内容配置同上
## 属性表 ## 属性表
| 属性名 | 类型 | 默认值 | 说明 | 版本 | | 属性名 | 类型 | 默认值 | 说明 | 版本 |
| ------------------ | ------------------------------------ | --------- | --------------------------------------------------------------------------------------------- | ------- | | ------------------ | ------------------------------------------------ | --------- | --------------------------------------------------------------------------------------------- | ------- |
| type | `string` | | 如果在 Table、Card 和 List 中,为`"image"`;在 Form 中用作静态展示,为`"static-image"` | | type | `string` | | 如果在 Table、Card 和 List 中,为`"image"`;在 Form 中用作静态展示,为`"static-image"` |
| className | `string` | | 外层 CSS 类名 | | className | `string` | | 外层 CSS 类名 |
| innerClassName | `string` | | 组件内层 CSS 类名 | | innerClassName | `string` | | 组件内层 CSS 类名 |
@ -458,6 +458,8 @@ List 的内容、Card 卡片的内容配置同上
| imageMode | `string` | `thumb` | 图片展示模式,可选:`'thumb'`, `'original'` 即:缩略图模式 或者 原图模式 | | imageMode | `string` | `thumb` | 图片展示模式,可选:`'thumb'`, `'original'` 即:缩略图模式 或者 原图模式 |
| showToolbar | `boolean` | `false` | 放大模式下是否展示图片的工具栏 | `2.2.0` | | showToolbar | `boolean` | `false` | 放大模式下是否展示图片的工具栏 | `2.2.0` |
| toolbarActions | `ImageAction[]` | | 图片工具栏,支持旋转,缩放,默认操作全部开启 | `2.2.0` | | toolbarActions | `ImageAction[]` | | 图片工具栏,支持旋转,缩放,默认操作全部开启 | `2.2.0` |
| maxScale | `number` 或 [模板](../../docs/concepts/template) | | 执行调整图片比例动作时的最大百分比 | `3.4.4` |
| minScale | `number` 或 [模板](../../docs/concepts/template) | | 执行调整图片比例动作时的最小百分比 | `3.4.4` |
#### ImageAction #### ImageAction
@ -475,3 +477,170 @@ interface ImageAction {
disabled?: boolean; disabled?: boolean;
} }
``` ```
## 事件表
当前组件会对外派发以下事件,可以通过`onEvent`来监听这些事件,并通过`actions`来配置执行的动作,在`actions`中可以通过`${事件参数名}`或`${event.data.[事件参数名]}`来获取事件产生的数据,详细查看[事件动作](../../docs/concepts/event-action)。
| 事件名称 | 事件参数 | 说明 |
| ---------- | ---------- | -------------- |
| click | 上下文数据 | 点击图片时触发 |
| mouseenter | 上下文数据 | 鼠标移入时触发 |
| mouseleave | 上下文数据 | 鼠标移入时触发 |
### click / mouseenter / mouseleave
点击图片 / 鼠标移入图片 / 鼠标移出图片,可以尝试通过${event.context.nativeEvent}获取鼠标事件对象。
```schema: scope="body"
{
"type": "image",
"src": "https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80",
"onEvent": {
"click": {
"actions": [
{
"actionType": "toast",
"args": {
"msg": "图片被点击了"
}
}
]
},
"mouseenter": {
"actions": [
{
"actionType": "toast",
"args": {
"msg": "鼠标移入图片"
}
}
]
},
"mouseleave": {
"actions": [
{
"actionType": "toast",
"args": {
"msg": "鼠标移出图片"
}
}
]
}
}
}
```
## 动作表
当前组件对外暴露以下特性动作,其他组件可以通过指定`actionType: 动作名称`、`componentId: 该组件id`来触发这些动作,动作配置可以通过`args: {动作配置项名称: xxx}`来配置具体的参数,详细请查看[事件动作](../../docs/concepts/event-action#触发其他组件的动作)。
| 动作名称 | 动作配置 | 说明 |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| preview | - | 预览图片 |
| zoom | `scale: number``scale: `[模板](../../docs/concepts/template),定义每次放大或缩小图片的百分比大小,正值为放大,负值为缩小,默认 50 | 调整图片比例,将图片等比例放大或缩小 |
### preview
预览图片,可以通过配置`originalSrc`来指定预览的原图地址。
```schema: scope="body"
{
"type": "page",
"body": {
"type": "container",
"body": [
{
"type": "container",
"body": [
{
"type": "image",
"className": "mb-1",
"src": "https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80",
"originalSrc": "https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg",
"id": "previewImage"
}
]
},
{
"type": "action",
"label": "预览图片",
"onEvent": {
"click": {
"actions": [
{
"actionType": "preview",
"componentId": "previewImage"
}
]
}
}
}
]
}
}
```
### zoom
调整图片比例,将图片等比例放大或缩小。可以通过配置图片的`maxScale`和`minScale`来限制调整的比例。
```schema: scope="body"
{
"type": "page",
"body": {
"type": "container",
"body": [
{
"type": "flex",
"items": [
{
"type": "image",
"innerClassName": "no-border",
"className": "mt-5 mb-5",
"src": "https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80",
"maxScale": 200,
"minScale": 20,
"id": "zoomImage"
}
]
},
{
"type": "action",
"label": "放大图片",
"onEvent": {
"click": {
"actions": [
{
"actionType": "zoom",
"args": {
"scale": 50,
},
"componentId": "zoomImage"
}
]
}
}
},
{
"type": "action",
"label": "缩小图片",
"className": "mx-1",
"onEvent": {
"click": {
"actions": [
{
"actionType": "zoom",
"args": {
"scale": -50,
},
"componentId": "zoomImage"
}
]
}
}
}
]
}
}
```

View File

@ -1,4 +1,9 @@
import {getI18nEnabled, registerEditorPlugin} from 'amis-editor-core'; import {
RendererPluginAction,
RendererPluginEvent,
getI18nEnabled,
registerEditorPlugin
} from 'amis-editor-core';
import { import {
ActiveEventContext, ActiveEventContext,
BaseEventContext, BaseEventContext,
@ -8,6 +13,10 @@ import {
} from 'amis-editor-core'; } from 'amis-editor-core';
import {defaultValue, getSchemaTpl, tipedLabel} from 'amis-editor-core'; import {defaultValue, getSchemaTpl, tipedLabel} from 'amis-editor-core';
import {mockValue} from 'amis-editor-core'; import {mockValue} from 'amis-editor-core';
import {
getArgsWrapper,
getEventControlConfig
} from '../renderer/event-control/helper';
export class ImagePlugin extends BasePlugin { export class ImagePlugin extends BasePlugin {
static id = 'ImagePlugin'; static id = 'ImagePlugin';
@ -34,6 +43,112 @@ export class ImagePlugin extends BasePlugin {
value: mockValue({type: 'image'}) value: mockValue({type: 'image'})
}; };
// 事件定义
events: RendererPluginEvent[] = [
{
eventName: 'click',
eventLabel: '点击',
description: '点击时触发',
defaultShow: true,
dataSchema: [
{
type: 'object',
properties: {
context: {
type: 'object',
title: '上下文',
properties: {
nativeEvent: {
type: 'object',
title: '鼠标事件对象'
}
}
}
}
}
]
},
{
eventName: 'mouseenter',
eventLabel: '鼠标移入',
description: '鼠标移入时触发',
dataSchema: [
{
type: 'object',
properties: {
context: {
type: 'object',
title: '上下文',
properties: {
nativeEvent: {
type: 'object',
title: '鼠标事件对象'
}
}
}
}
}
]
},
{
eventName: 'mouseleave',
eventLabel: '鼠标移出',
description: '鼠标移出时触发',
dataSchema: [
{
type: 'object',
properties: {
context: {
type: 'object',
title: '上下文',
properties: {
nativeEvent: {
type: 'object',
title: '鼠标事件对象'
}
}
}
}
}
]
}
];
// 动作定义
actions: RendererPluginAction[] = [
{
actionType: 'preview',
actionLabel: '预览',
description: '预览图片'
},
{
actionType: 'zoom',
actionLabel: '调整图片比例',
description: '将图片等比例放大或缩小',
schema: {
type: 'container',
body: [
getArgsWrapper([
getSchemaTpl('formulaControl', {
name: 'scale',
mode: 'horizontal',
variables: '${variables}',
horizontal: {
leftFixed: 4 // 需要设置下leftFixed否则这个字段的控件没有与其他字段的控件左对齐
},
label: tipedLabel(
'调整比例',
'定义每次放大或缩小图片的百分比大小正值为放大负值为缩小默认50'
),
value: 50,
size: 'lg'
})
])
]
}
}
];
panelTitle = '图片'; panelTitle = '图片';
panelJustify = true; panelJustify = true;
panelBodyCreator = (context: BaseEventContext) => { panelBodyCreator = (context: BaseEventContext) => {
@ -63,7 +178,7 @@ export class ImagePlugin extends BasePlugin {
pipeIn: defaultValue('thumb'), pipeIn: defaultValue('thumb'),
options: [ options: [
{ {
label: '缩图', label: '缩图',
value: 'thumb' value: 'thumb'
}, },
{ {
@ -130,6 +245,24 @@ export class ImagePlugin extends BasePlugin {
getSchemaTpl('imageUrl', { getSchemaTpl('imageUrl', {
name: 'defaultImage', name: 'defaultImage',
label: tipedLabel('占位图', '无数据时显示的图片') label: tipedLabel('占位图', '无数据时显示的图片')
}),
getSchemaTpl('formulaControl', {
name: 'maxScale',
mode: 'horizontal',
label: tipedLabel(
'放大极限',
'定义动作调整图片大小的最大百分比默认200'
),
value: 200
}),
getSchemaTpl('formulaControl', {
name: 'minScale',
mode: 'horizontal',
label: tipedLabel(
'缩小极限',
'定义动作调整图片大小的最小百分比默认50'
),
value: 50
}) })
] ]
}, },
@ -245,6 +378,16 @@ export class ImagePlugin extends BasePlugin {
}, },
getSchemaTpl('theme:cssCode') getSchemaTpl('theme:cssCode')
]) ])
},
{
title: '事件',
className: 'p-none',
body: [
getSchemaTpl('eventControl', {
name: 'onEvent',
...getEventControlConfig(this.manager, context)
})
]
} }
]); ]);
}; };

View File

@ -2352,6 +2352,12 @@ export const COMMON_ACTION_SCHEMA_MAP: {
}, },
confirm: { confirm: {
descDetail: (info: any) => <div></div> descDetail: (info: any) => <div></div>
},
preview: {
descDetail: (info: any) => <div></div>
},
zoom: {
descDetail: (info: any) => <div></div>
} }
}; };

View File

@ -12,6 +12,10 @@
* 9. href * 9. href
* 10. * 10.
* 11. clickAction * 11. clickAction
* 12. click事件
* 13. mouseenter / mouseleave
* 14. preview
* 15. zoom & maxScale & minScale
* *
* * Images * * Images
* images image 使 * images image 使
@ -20,7 +24,7 @@
* 2. enlargeAble & originalSrc & source & title & description * 2. enlargeAble & originalSrc & source & title & description
*/ */
import {fireEvent, render} from '@testing-library/react'; import {fireEvent, render, waitFor} from '@testing-library/react';
import '../../src'; import '../../src';
import {render as amisRender} from '../../src'; import {render as amisRender} from '../../src';
import {makeEnv, wait} from '../helper'; import {makeEnv, wait} from '../helper';
@ -334,10 +338,231 @@ describe('Renderer:image', () => {
}) })
); );
fireEvent.click(container.querySelector('.cxd-Image-thumbWrap')!); fireEvent.click(container.querySelector('.cxd-Image-thumbWrap')!);
await waitFor(() => {
expect(getByText('这是一个弹框')!).toBeInTheDocument(); expect(getByText('这是一个弹框')!).toBeInTheDocument();
}); });
}); });
test('image:click', async () => {
const notify = jest.fn();
const {container, getByText} = render(
amisRender(
{
type: 'image',
src: 'https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80',
class: 'cursor-pointer',
onEvent: {
click: {
actions: [
{
actionType: 'toast',
args: {
msgType: 'info',
msg: '派发点击事件'
}
}
]
},
mouseenter: {
actions: [
{
actionType: 'toast',
args: {
msgType: 'info',
msg: '派发鼠标移入事件'
}
}
]
},
mouseleave: {
actions: [
{
actionType: 'toast',
args: {
msgType: 'info',
msg: '派发鼠标移出事件'
}
}
]
}
}
},
{},
makeEnv({
notify,
session: 'image-test-action-1'
})
)
);
fireEvent.click(container.querySelector('.cxd-Image-thumbWrap')!);
await waitFor(() => {
expect(notify).toHaveBeenCalledWith('info', '派发点击事件', {
msg: '派发点击事件',
msgType: 'info'
});
});
fireEvent.mouseEnter(container.querySelector('.cxd-Image-thumbWrap')!);
await waitFor(() => {
expect(notify).toHaveBeenCalledWith('info', '派发鼠标移入事件', {
msg: '派发鼠标移入事件',
msgType: 'info'
});
});
fireEvent.mouseLeave(container.querySelector('.cxd-Image-thumbWrap')!);
await waitFor(() => {
expect(notify).toHaveBeenCalledWith('info', '派发鼠标移出事件', {
msg: '派发鼠标移出事件',
msgType: 'info'
});
});
});
test('image:preview 预览动作', async () => {
const {container, getByText, baseElement} = render(
amisRender({
type: 'page',
body: [
{
type: 'image',
id: 'previewImage',
src: 'https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80',
originalSrc:
'https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg'
},
{
type: 'button',
label: '预览图片',
onEvent: {
click: {
actions: [
{
actionType: 'preview',
componentId: 'previewImage'
}
]
}
}
}
]
})
);
expect(container).toMatchSnapshot();
fireEvent.click(getByText('预览图片'));
expect(baseElement.querySelector('.cxd-ImageGallery')!).toBeInTheDocument();
expect(
baseElement.querySelector('.cxd-ImageGallery .cxd-ImageGallery-main img')!
).toHaveAttribute(
'src',
'https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg'
);
});
test('image:zoom & maxScale & minScale 调整图片比例动作', async () => {
const {container, getByText, baseElement} = render(
amisRender({
type: 'page',
body: [
{
type: 'image',
id: 'zoomImage',
src: 'https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80',
originalSrc:
'https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg',
maxScale: 200,
minScale: 20
},
{
type: 'button',
label: '放大图片',
onEvent: {
click: {
actions: [
{
actionType: 'zoom',
args: {
scale: 50
},
componentId: 'zoomImage'
}
]
}
}
},
{
type: 'button',
label: '缩小图片',
onEvent: {
click: {
actions: [
{
actionType: 'zoom',
args: {
scale: -50
},
componentId: 'zoomImage'
}
]
}
}
}
]
})
);
expect(container).toMatchSnapshot();
const imgIns = baseElement.querySelector('.cxd-ImageField--thumb')!;
expect(imgIns).toHaveStyle({
transform: 'scale(1)'
});
fireEvent.click(getByText('放大图片'));
await waitFor(() => {
expect(imgIns).toHaveStyle({
transform: 'scale(1.5)'
});
});
fireEvent.click(getByText('缩小图片'));
await waitFor(() => {
expect(imgIns).toHaveStyle({
transform: 'scale(1)'
});
});
fireEvent.click(getByText('放大图片'));
fireEvent.click(getByText('放大图片'));
await waitFor(() => {
expect(imgIns).toHaveStyle({
transform: 'scale(2)'
});
});
fireEvent.click(getByText('放大图片'));
await waitFor(() => {
expect(imgIns).toHaveStyle({
transform: 'scale(2)'
});
});
fireEvent.click(getByText('缩小图片'));
fireEvent.click(getByText('缩小图片'));
fireEvent.click(getByText('缩小图片'));
fireEvent.click(getByText('缩小图片'));
await waitFor(() => {
expect(imgIns).toHaveStyle({
transform: 'scale(0.2)'
});
});
fireEvent.click(getByText('缩小图片'));
await waitFor(() => {
expect(imgIns).toHaveStyle({
transform: 'scale(0.2)'
});
});
});
});
describe('Renderer:images', () => { describe('Renderer:images', () => {
test('images:basic', async () => { test('images:basic', async () => {
const {container} = render( const {container} = render(

View File

@ -56,6 +56,7 @@ exports[`Renderer:image image as form item 1`] = `
> >
<div <div
class="cxd-ImageField cxd-ImageField--thumb" class="cxd-ImageField cxd-ImageField--thumb"
style="transform: scale(1);"
> >
<div <div
class="cxd-Image cxd-Image--thumb no-border" class="cxd-Image cxd-Image--thumb no-border"
@ -103,6 +104,7 @@ exports[`Renderer:image image:basic 1`] = `
<div> <div>
<div <div
class="cxd-ImageField cxd-ImageField--thumb show" class="cxd-ImageField cxd-ImageField--thumb show"
style="transform: scale(1);"
> >
<div <div
class="cxd-Image cxd-Image--thumb" class="cxd-Image cxd-Image--thumb"
@ -138,6 +140,7 @@ exports[`Renderer:image image:enlargeAble & originalSrc & enlargeTitle & showToo
<div> <div>
<div <div
class="cxd-ImageField cxd-ImageField--thumb" class="cxd-ImageField cxd-ImageField--thumb"
style="transform: scale(1);"
> >
<div <div
class="cxd-Image cxd-Image--thumb" class="cxd-Image cxd-Image--thumb"
@ -178,6 +181,7 @@ exports[`Renderer:image image:enlargeAble & originalSrc & enlargeTitle & showToo
<div> <div>
<div <div
class="cxd-ImageField cxd-ImageField--thumb" class="cxd-ImageField cxd-ImageField--thumb"
style="transform: scale(1);"
> >
<div <div
class="cxd-Image cxd-Image--thumb" class="cxd-Image cxd-Image--thumb"
@ -231,6 +235,7 @@ exports[`Renderer:image image:href 1`] = `
> >
<div <div
class="cxd-ImageField cxd-ImageField--thumb" class="cxd-ImageField cxd-ImageField--thumb"
style="transform: scale(1);"
> >
<a <a
class="cxd-Link" class="cxd-Link"
@ -263,10 +268,62 @@ exports[`Renderer:image image:href 1`] = `
</div> </div>
`; `;
exports[`Renderer:image image:preview 预览动作 1`] = `
<div>
<div
class="cxd-Page"
>
<div
class="cxd-Page-content"
>
<div
class="cxd-Page-main"
>
<div
class="cxd-Page-body"
role="page-body"
>
<div
class="cxd-ImageField cxd-ImageField--thumb"
style="transform: scale(1);"
>
<div
class="cxd-Image cxd-Image--thumb"
>
<div
class="cxd-Image-thumbWrap"
>
<div
class="cxd-Image-thumb cxd-Image-thumb--contain cxd-Image-thumb--1-1"
>
<img
class="cxd-Image-image"
src="https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80"
/>
</div>
</div>
</div>
</div>
<button
class="cxd-Button cxd-Button--default cxd-Button--size-default"
type="button"
>
<span>
预览图片
</span>
</button>
</div>
</div>
</div>
</div>
</div>
`;
exports[`Renderer:image image:title & imageCaption 1`] = ` exports[`Renderer:image image:title & imageCaption 1`] = `
<div> <div>
<div <div
class="cxd-ImageField cxd-ImageField--thumb" class="cxd-ImageField cxd-ImageField--thumb"
style="transform: scale(1);"
> >
<div <div
class="cxd-Image cxd-Image--thumb" class="cxd-Image cxd-Image--thumb"
@ -308,6 +365,7 @@ exports[`Renderer:image image:width & height 1`] = `
<div> <div>
<div <div
class="cxd-ImageField cxd-ImageField--thumb" class="cxd-ImageField cxd-ImageField--thumb"
style="transform: scale(1);"
> >
<div <div
class="cxd-Image cxd-Image--thumb" class="cxd-Image cxd-Image--thumb"
@ -330,6 +388,65 @@ exports[`Renderer:image image:width & height 1`] = `
</div> </div>
`; `;
exports[`Renderer:image image:zoom & maxScale & minScale 调整图片比例动作 1`] = `
<div>
<div
class="cxd-Page"
>
<div
class="cxd-Page-content"
>
<div
class="cxd-Page-main"
>
<div
class="cxd-Page-body"
role="page-body"
>
<div
class="cxd-ImageField cxd-ImageField--thumb"
style="transform: scale(1);"
>
<div
class="cxd-Image cxd-Image--thumb"
>
<div
class="cxd-Image-thumbWrap"
>
<div
class="cxd-Image-thumb cxd-Image-thumb--contain cxd-Image-thumb--1-1"
>
<img
class="cxd-Image-image"
src="https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80"
/>
</div>
</div>
</div>
</div>
<button
class="cxd-Button cxd-Button--default cxd-Button--size-default"
type="button"
>
<span>
放大图片
</span>
</button>
<button
class="cxd-Button cxd-Button--default cxd-Button--size-default"
type="button"
>
<span>
缩小图片
</span>
</button>
</div>
</div>
</div>
</div>
</div>
`;
exports[`Renderer:images images:basic 1`] = ` exports[`Renderer:images images:basic 1`] = `
<div> <div>
<div <div

View File

@ -7090,6 +7090,7 @@ exports[`Renderer:table list 1`] = `
> >
<div <div
class="cxd-ImageField cxd-ImageField--thumb" class="cxd-ImageField cxd-ImageField--thumb"
style="transform: scale(1);"
> >
<div <div
class="cxd-Image cxd-Image--thumb" class="cxd-Image cxd-Image--thumb"
@ -7331,6 +7332,7 @@ exports[`Renderer:table list 1`] = `
> >
<div <div
class="cxd-ImageField cxd-ImageField--thumb" class="cxd-ImageField cxd-ImageField--thumb"
style="transform: scale(1);"
> >
<div <div
class="cxd-Image cxd-Image--thumb" class="cxd-Image cxd-Image--thumb"
@ -7692,6 +7694,7 @@ exports[`Renderer:table list 1`] = `
> >
<div <div
class="cxd-ImageField cxd-ImageField--thumb" class="cxd-ImageField cxd-ImageField--thumb"
style="transform: scale(1);"
> >
<div <div
class="cxd-Image cxd-Image--thumb" class="cxd-Image cxd-Image--thumb"

View File

@ -3,7 +3,13 @@ import {
Renderer, Renderer,
RendererProps, RendererProps,
CustomStyle, CustomStyle,
setThemeClassName setThemeClassName,
ActionObject,
IScopedContext,
ScopedContext,
createObject,
resolveVariableAndFilter,
isPureVariable
} from 'amis-core'; } from 'amis-core';
import {filter} from 'amis-core'; import {filter} from 'amis-core';
import {themeable, ThemeProps} from 'amis-core'; import {themeable, ThemeProps} from 'amis-core';
@ -398,6 +404,8 @@ export interface ImageFieldProps extends RendererProps {
enlargeWithGallary?: boolean; enlargeWithGallary?: boolean;
showToolbar?: boolean; showToolbar?: boolean;
toolbarActions?: ImageAction[]; toolbarActions?: ImageAction[];
maxScale?: number;
minScale?: number;
onImageEnlarge?: ( onImageEnlarge?: (
info: { info: {
src: string; src: string;
@ -414,9 +422,21 @@ export interface ImageFieldProps extends RendererProps {
target: any target: any
) => void; ) => void;
imageGallaryClassName?: string; imageGallaryClassName?: string;
onClick?:
| ((e: React.MouseEvent<any>, props: any) => void)
| string
| Function
| null;
} }
export class ImageField extends React.Component<ImageFieldProps, object> { interface ImageFieldState {
scale: number; // 放大倍率
}
export class ImageField extends React.Component<
ImageFieldProps,
ImageFieldState
> {
static defaultProps: Pick< static defaultProps: Pick<
ImageFieldProps, ImageFieldProps,
'defaultImage' | 'thumbMode' | 'thumbRatio' 'defaultImage' | 'thumbMode' | 'thumbRatio'
@ -426,6 +446,10 @@ export class ImageField extends React.Component<ImageFieldProps, object> {
thumbRatio: '1:1' thumbRatio: '1:1'
}; };
state: ImageFieldState = {
scale: 1
};
@autobind @autobind
handleEnlarge({ handleEnlarge({
src, src,
@ -468,13 +492,73 @@ export class ImageField extends React.Component<ImageFieldProps, object> {
} }
@autobind @autobind
handleClick(e: React.MouseEvent<HTMLElement>) { async handleClick(e: React.MouseEvent<HTMLElement>) {
const {dispatchEvent, data} = this.props;
const clickAction = this.props.clickAction; const clickAction = this.props.clickAction;
const rendererEvent = await dispatchEvent(
e,
createObject(data, {
nativeEvent: e
})
);
if (rendererEvent?.prevented) {
return;
}
if (clickAction) { if (clickAction) {
handleAction(e, clickAction, this.props); handleAction(e, clickAction, this.props);
} }
} }
@autobind
handleMouseEnter(e: React.MouseEvent<any>) {
const {dispatchEvent, data} = this.props;
dispatchEvent(e, data);
}
@autobind
handleMouseLeave(e: React.MouseEvent<any>) {
const {dispatchEvent, data} = this.props;
dispatchEvent(e, data);
}
handleSelfAction(actionType: string, action: ActionObject) {
let {data, maxScale = 200, minScale = 50} = this.props;
let {scale = 50} = action.args;
if (actionType === 'zoom') {
if (isPureVariable(maxScale)) {
maxScale = isNaN(
resolveVariableAndFilter(maxScale, createObject(action.data, data))
)
? 200
: resolveVariableAndFilter(maxScale, createObject(action.data, data));
}
if (isPureVariable(minScale)) {
minScale = isNaN(
resolveVariableAndFilter(minScale, createObject(action.data, data))
)
? 50
: resolveVariableAndFilter(minScale, createObject(action.data, data));
}
if (scale >= 0) {
this.setState({
scale:
this.state.scale + scale / 100 < maxScale / 100
? this.state.scale + scale / 100
: maxScale / 100
});
} else {
this.setState({
scale:
this.state.scale + scale / 100 > minScale / 100
? this.state.scale + scale / 100
: minScale / 100
});
}
}
}
render() { render() {
const { const {
className, className,
@ -520,8 +604,10 @@ export class ImageField extends React.Component<ImageFieldProps, object> {
className, className,
setThemeClassName('wrapperCustomStyle', id, wrapperCustomStyle) setThemeClassName('wrapperCustomStyle', id, wrapperCustomStyle)
)} )}
style={style} style={{...style, transform: `scale(${this.state.scale})`}}
onClick={this.handleClick} onClick={this.handleClick}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
> >
{value || (!value && !placeholder) ? ( {value || (!value && !placeholder) ? (
<ThemedImageThumb <ThemedImageThumb
@ -597,4 +683,26 @@ export class ImageField extends React.Component<ImageFieldProps, object> {
@Renderer({ @Renderer({
type: 'image' type: 'image'
}) })
export class ImageFieldRenderer extends ImageField {} export class ImageFieldRenderer extends ImageField {
static contextType = ScopedContext;
constructor(props: ImageFieldProps, context: IScopedContext) {
super(props);
const scoped = context;
scoped.registerComponent(this);
}
componentWillUnmount() {
const scoped = this.context as IScopedContext;
scoped.unRegisterComponent(this);
}
doAction(action: ActionObject) {
const actionType = action?.actionType as string;
if (actionType === 'preview') {
this.handleEnlarge(this.props as ImageThumbProps);
} else {
this.handleSelfAction(actionType, action);
}
}
}

View File

@ -115,7 +115,9 @@ export interface Action extends Button {
| 'selectAll' | 'selectAll'
| 'changeTabKey' | 'changeTabKey'
| 'click' | 'click'
| 'stopAutoRefresh'; | 'stopAutoRefresh'
| 'preview'
| 'zoom';
api?: SchemaApi; api?: SchemaApi;
asyncApi?: SchemaApi; asyncApi?: SchemaApi;
payload?: any; payload?: any;