feat:图片上传支持配置占位图样式及其比例等 (#1665)

* feat:图片上传支持配置占位图样式及其比例等

* fix conflict

* 更新snapshot

* feat:表格中图片上传支持默认图片

* 增加fixedSize配置项支持用户自定义图片框尺寸

* 拆分fixedSize配置项

* 删除无用代码

Co-authored-by: 邓黔川 <dengqianchuan@baidu.com>
Co-authored-by: dqc <qianchuan.deng@gmail.com>
This commit is contained in:
qianchuan 2021-03-18 12:00:26 +08:00 committed by GitHub
parent 9084267867
commit 8bdc387bd4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 181 additions and 79 deletions

View File

@ -115,36 +115,36 @@ exports[`Renderer:button-group 1`] = `
class="resize-sensor" class="resize-sensor"
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;" style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
> >
<div <div
class="resize-sensor-expand" class="resize-sensor-expand"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;" style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
> >
<div <div
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;" style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
/> />
</div> </div>
<div <div
class="resize-sensor-shrink" class="resize-sensor-shrink"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;" style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
> >
<div <div
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%" style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
/> />
</div> </div>
<div <div
class="resize-sensor-appear" class="resize-sensor-appear"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;" style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
@ -267,36 +267,36 @@ exports[`Renderer:button-group:multiple clearable 1`] = `
class="resize-sensor" class="resize-sensor"
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;" style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
> >
<div <div
class="resize-sensor-expand" class="resize-sensor-expand"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;" style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
> >
<div <div
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;" style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
/> />
</div> </div>
<div <div
class="resize-sensor-shrink" class="resize-sensor-shrink"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;" style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
> >
<div <div
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%" style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
/> />
</div> </div>
<div <div
class="resize-sensor-appear" class="resize-sensor-appear"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;" style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
@ -419,36 +419,36 @@ exports[`Renderer:button-group:multiple clearable 2`] = `
class="resize-sensor" class="resize-sensor"
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;" style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
> >
<div <div
class="resize-sensor-expand" class="resize-sensor-expand"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;" style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
> >
<div <div
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;" style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
/> />
</div> </div>
<div <div
class="resize-sensor-shrink" class="resize-sensor-shrink"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;" style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
> >
<div <div
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%" style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
/> />
</div> </div>
<div <div
class="resize-sensor-appear" class="resize-sensor-appear"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;" style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"

View File

@ -1542,6 +1542,12 @@ exports[`Renderer:Wizard initApi reload 3`] = `
<div <div
class="a-Page-body" class="a-Page-body"
> >
<div
class="a-Spinner-overlay"
/>
<div
class="a-Spinner a-Spinner--overlay a-Spinner--lg"
/>
<div <div
class="a-Panel a-Panel--default a-Wizard a-Wizard--horizontal" class="a-Panel a-Panel--default a-Wizard a-Wizard--horizontal"
style="position: relative;" style="position: relative;"

View File

@ -160,25 +160,28 @@ order: 27
除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置 除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
| 属性名 | 类型 | 默认值 | 说明 | | 属性名 | 类型 | 默认值 | 说明 |
| ---------------- | ------------------------------- | ---------------------- | -------------------------------------------------------------------------------------------------- | | ------------------ | ------------------------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| receiver | [API](../../../docs/types/api) | | 上传文件接口 | | receiver | [API](../../../docs/types/api) | | 上传文件接口 |
| accept | `string` | `.jpeg,.jpg,.png,.gif` | 支持的图片类型格式,请配置此属性为图片后缀,例如`.jpg,.png` | | accept | `string` | `.jpeg,.jpg,.png,.gif` | 支持的图片类型格式,请配置此属性为图片后缀,例如`.jpg,.png` |
| maxSize | `number` | | 默认没有限制,当设置后,文件大小大于此值将不允许上传。单位为`B` | | maxSize | `number` | | 默认没有限制,当设置后,文件大小大于此值将不允许上传。单位为`B` |
| maxLength | `number` | | 默认没有限制,当设置后,一次只允许上传指定数量文件。 | | maxLength | `number` | | 默认没有限制,当设置后,一次只允许上传指定数量文件。 |
| multiple | `boolean` | `false` | 是否多选。 | | multiple | `boolean` | `false` | 是否多选。 |
| joinValues | `boolean` | `true` | [拼接值](./options#%E6%8B%BC%E6%8E%A5%E5%80%BC-joinvalues) | | joinValues | `boolean` | `true` | [拼接值](./options#%E6%8B%BC%E6%8E%A5%E5%80%BC-joinvalues) |
| extractValue | `boolean` | `false` | [提取值](./options#%E6%8F%90%E5%8F%96%E5%A4%9A%E9%80%89%E5%80%BC-extractvalue) | | extractValue | `boolean` | `false` | [提取值](./options#%E6%8F%90%E5%8F%96%E5%A4%9A%E9%80%89%E5%80%BC-extractvalue) |
| delimeter | `string` | `,` | [拼接符](./options#%E6%8B%BC%E6%8E%A5%E7%AC%A6-delimiter) | | delimeter | `string` | `,` | [拼接符](./options#%E6%8B%BC%E6%8E%A5%E7%AC%A6-delimiter) |
| autoUpload | `boolean` | `true` | 否选择完就自动开始上传 | | autoUpload | `boolean` | `true` | 否选择完就自动开始上传 |
| hideUploadButton | `boolean` | `false` | 隐藏上传按钮 | | hideUploadButton | `boolean` | `false` | 隐藏上传按钮 |
| fileField | `string` | `file` | 如果你不想自己存储,则可以忽略此属性。 | | fileField | `string` | `file` | 如果你不想自己存储,则可以忽略此属性。 |
| crop | `boolean`或`{"aspectRatio":""}` | | 用来设置是否支持裁剪。 | | crop | `boolean`或`{"aspectRatio":""}` | | 用来设置是否支持裁剪。 |
| crop.aspectRatio | `number` | | 裁剪比例。浮点型,默认 `1``1:1`,如果要设置 `16:9` 请设置 `1.7777777777777777``16 / 9`。。 | | crop.aspectRatio | `number` | | 裁剪比例。浮点型,默认 `1``1:1`,如果要设置 `16:9` 请设置 `1.7777777777777777``16 / 9`。。 |
| crop.rotatable | `boolean` | `false` | 裁剪时是否可旋转 | | crop.rotatable | `boolean` | `false` | 裁剪时是否可旋转 |
| crop.scalable | `boolean` | `false` | 裁剪时是否可缩放 | | crop.scalable | `boolean` | `false` | 裁剪时是否可缩放 |
| crop.viewMode | `number` | `1` | 裁剪时的查看模式0 是无限制 | | crop.viewMode | `number` | `1` | 裁剪时的查看模式0 是无限制 |
| limit | Limit | | 限制图片大小,超出不让上传。 | | limit | Limit | | 限制图片大小,超出不让上传。 |
| defaultImage | `string` | | 默认占位图地址 |
| fixedSize | `boolean` | | 是否开启固定尺寸 |
| fixedSizeClassName | `string` | | 开启固定尺寸时,根据此值控制展示尺寸。例如`h-30`,即图片框高为 h-30,最终上传图片根据此尺寸对应缩放。也可同时设置设置宽度的类名,例如`h-30 w-52`,若不设置宽度类名,则根据父元素尺寸自适应 |
### Limit 属性表 ### Limit 属性表

View File

@ -788,6 +788,20 @@ export default {
{ {
type: 'divider' type: 'divider'
}, },
{
type: 'image',
name: 'image',
inputClassName: 'w-52',
label: '图片有默认占位图',
defaultImage:
'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg',
fixedSize: true,
fixedSizeClassName: 'h-32',
thumbMode: 'contain'
},
{
type: 'divider'
},
{ {
type: 'image', type: 'image',
name: 'imageCrop', name: 'imageCrop',

View File

@ -39,6 +39,22 @@
height: px2rem(108px * 9 / 16); height: px2rem(108px * 9 / 16);
} }
&-thumb--fixed-size {
width: 100%;
padding: 0;
height: 100%;
}
&-thumb--fixed-size &-thumb {
width: 100%;
height: 100%;
> img {
width: auto;
height: 100%;
}
}
&-thumb--w-full > img { &-thumb--w-full > img {
width: 100%; width: 100%;
height: auto; height: auto;

View File

@ -94,8 +94,9 @@
width: px2rem(108px); width: px2rem(108px);
height: px2rem(108px); height: px2rem(108px);
display: none; display: none;
top: px2rem(5px); top: 50%;
left: px2rem(5px); left: 50%;
transform: translate(-50%, -50%);
justify-content: center; justify-content: center;
align-items: center; align-items: center;
@ -253,4 +254,15 @@
line-height: 120px; line-height: 120px;
text-align: center; text-align: center;
} }
&-fixed-size {
width: 100%;
height: 100%;
padding: 0;
}
&-fixed-size &-itemOverlay {
width: 100%;
height: 100%;
}
} }

View File

@ -23,6 +23,7 @@ import {
SchemaTokenizeableString, SchemaTokenizeableString,
SchemaUrlPath SchemaUrlPath
} from '../../Schema'; } from '../../Schema';
import {filter} from '../../utils/tpl';
/** /**
* Image * Image
@ -222,6 +223,21 @@ export interface ImageControlSchema extends FormBaseControl {
autoFill?: { autoFill?: {
[propName: string]: SchemaTokenizeableString; [propName: string]: SchemaTokenizeableString;
}; };
/**
*
*/
defaultImage?: SchemaUrlPath;
/**
*
*/
fixedSize?: boolean;
/**
* CSS类名
*/
fixedSizeClassName?: SchemaClassName;
} }
let preventEvent = (e: any) => e.stopPropagation(); let preventEvent = (e: any) => e.stopPropagation();
@ -933,7 +949,7 @@ export default class ImageControl extends React.Component<
Math.abs(width / height - limit.aspectRatio) > 0.01 Math.abs(width / height - limit.aspectRatio) > 0.01
) { ) {
error = __(limit.aspectRatioLabel || 'Image.limitRatio', { error = __(limit.aspectRatioLabel || 'Image.limitRatio', {
ratio: limit.aspectRatio ratio: limit.aspectRatio.toFixed(2)
}); });
} }
@ -1095,6 +1111,9 @@ export default class ImageControl extends React.Component<
thumbMode, thumbMode,
thumbRatio, thumbRatio,
reCropable, reCropable,
defaultImage,
fixedSize,
fixedSizeClassName,
translate: __ translate: __
} = this.props; } = this.props;
@ -1170,12 +1189,17 @@ export default class ImageControl extends React.Component<
? files.map((file, key) => ( ? files.map((file, key) => (
<div <div
key={file.id || key} key={file.id || key}
className={cx('ImageControl-item', { className={cx(
'is-uploaded': file.state !== 'uploading', 'ImageControl-item',
'is-invalid': {
file.state === 'error' || 'is-uploaded': file.state !== 'uploading',
file.state === 'invalid' 'is-invalid':
})} file.state === 'error' ||
file.state === 'invalid'
},
fixedSize ? 'ImageControl-fixed-size' : '',
fixedSize ? fixedSizeClassName : ''
)}
> >
{file.state === 'invalid' || {file.state === 'invalid' ||
file.state === 'error' ? ( file.state === 'error' ? (
@ -1194,9 +1218,14 @@ export default class ImageControl extends React.Component<
</a> </a>
<a <a
className={cx('ImageControl-retryBtn', { className={cx(
'is-disabled': disabled 'ImageControl-retryBtn',
})} {
'is-disabled': disabled
},
fixedSize ? 'ImageControl-fixed-size' : '',
fixedSize ? fixedSizeClassName : ''
)}
onClick={this.handleRetry.bind(this, key)} onClick={this.handleRetry.bind(this, key)}
> >
<Icon icon="retry" className="icon" /> <Icon icon="retry" className="icon" />
@ -1221,7 +1250,11 @@ export default class ImageControl extends React.Component<
</a> </a>
<div <div
key="info" key="info"
className={cx('ImageControl-itemInfo')} className={cx(
'ImageControl-itemInfo',
fixedSize ? 'ImageControl-fixed-size' : '',
fixedSize ? fixedSizeClassName : ''
)}
> >
<p>{__('File.uploading')}</p> <p>{__('File.uploading')}</p>
<div className={cx('ImageControl-progress')}> <div className={cx('ImageControl-progress')}>
@ -1242,7 +1275,10 @@ export default class ImageControl extends React.Component<
<> <>
<ImageComponent <ImageComponent
key="image" key="image"
className={cx('ImageControl-image')} className={cx(
'ImageControl-image',
fixedSize ? 'Image-thumb--fixed-size' : ''
)}
onLoad={this.handleImageLoaded.bind( onLoad={this.handleImageLoaded.bind(
this, this,
key key
@ -1333,14 +1369,29 @@ export default class ImageControl extends React.Component<
{(multiple && (!maxLength || files.length < maxLength)) || {(multiple && (!maxLength || files.length < maxLength)) ||
(!multiple && !files.length) ? ( (!multiple && !files.length) ? (
<label <label
className={cx('ImageControl-addBtn', { className={cx(
'is-disabled': disabled 'ImageControl-addBtn',
})} {
'is-disabled': disabled
},
fixedSize ? 'ImageControl-fixed-size' : '',
fixedSize ? fixedSizeClassName : ''
)}
onClick={this.handleSelect} onClick={this.handleSelect}
data-tooltip={__(placeholder)} data-tooltip={__(placeholder)}
data-position="right" data-position="right"
> >
<Icon icon="plus" className="icon" /> {defaultImage ? (
<ImageComponent
key="upload-default-image"
src={filter(defaultImage, this.props.data, '| raw')}
className={cx(
fixedSize ? 'Image-thumb--fixed-size' : ''
)}
/>
) : (
<Icon icon="plus" className="icon" />
)}
{isFocused ? ( {isFocused ? (
<span className={cx('ImageControl-pasteTip')}> <span className={cx('ImageControl-pasteTip')}>