🆕 添加图片库,支持用户在线选择图片素材

This commit is contained in:
xujiang 2020-10-05 00:12:56 +08:00
parent 75facb2b2a
commit eb26adabb5
3 changed files with 232 additions and 35 deletions

View File

@ -7,3 +7,51 @@
margin-top: 8px; margin-top: 8px;
color: #666; color: #666;
} }
.avatarUploader {
display: inline-block;
}
.wallBtn {
position: absolute;
left: 120px;
bottom: 56px;
display: inline-block;
color: #2f54eb;
cursor: pointer;
border-bottom: 1px solid #2f54eb;
}
.imgBox {
display: flex;
flex-wrap: wrap;
max-height: 520px;
overflow: auto;
.imgItem {
position: relative;
margin-right: 16px;
margin-bottom: 16px;
width: 320px;
max-height: 220px;
overflow: hidden;
cursor: pointer;
img {
width: 100%;
}
&:hover,
&.seleted {
.iconBtn {
visibility: visible;
}
}
.iconBtn {
position: absolute;
visibility: hidden;
top: 6px;
right: 10px;
font-size: 18px;
color: rgb(8, 156, 8);
}
}
}

View File

@ -1,10 +1,22 @@
import React from 'react'; import React from 'react';
import { Upload, Modal, message } from 'antd'; import { Upload, Modal, message, Tabs, Result } from 'antd';
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined, CheckCircleFilled } from '@ant-design/icons';
import ImgCrop from 'antd-img-crop'; import ImgCrop from 'antd-img-crop';
import styles from './index.less'; import classnames from 'classnames';
import { UploadFile, UploadChangeParam, RcFile } from 'antd/lib/upload/interface'; import { UploadFile, UploadChangeParam, RcFile } from 'antd/lib/upload/interface';
import { isDev } from '@/utils/tool'; import { isDev, unParams, uuid } from '@/utils/tool';
import req from '@/utils/req';
import styles from './index.less';
const { TabPane } = Tabs;
// 维护图片分类映射
const wallCateName: any = {
photo: '照片',
bg: '背景',
chahua: '插画',
};
function getBase64(file: File | Blob) { function getBase64(file: File | Blob) {
return new Promise<string>((resolve, reject) => { return new Promise<string>((resolve, reject) => {
const reader = new FileReader(); const reader = new FileReader();
@ -13,6 +25,7 @@ function getBase64(file: File | Blob) {
reader.onerror = error => reject(error); reader.onerror = error => reject(error);
}); });
} }
interface PicturesWallType { interface PicturesWallType {
fileList?: UploadFile<any>[]; fileList?: UploadFile<any>[];
action?: string; action?: string;
@ -20,7 +33,7 @@ interface PicturesWallType {
withCredentials?: boolean; withCredentials?: boolean;
maxLen?: number; maxLen?: number;
onChange?: (v: any) => void; onChange?: (v: any) => void;
cropRate?: boolean; cropRate?: number | boolean;
isCrop?: boolean; isCrop?: boolean;
} }
@ -28,12 +41,21 @@ class PicturesWall extends React.Component<PicturesWallType> {
state = { state = {
previewVisible: false, previewVisible: false,
previewImage: '', previewImage: '',
wallModalVisible: false,
previewTitle: '', previewTitle: '',
imgBed: {
photo: [],
bg: [],
chahua: [],
},
curSelectedImg: '',
fileList: this.props.fileList || [], fileList: this.props.fileList || [],
}; };
handleCancel = () => this.setState({ previewVisible: false }); handleCancel = () => this.setState({ previewVisible: false });
handleModalCancel = () => this.setState({ wallModalVisible: false });
handlePreview = async (file: UploadFile<any>) => { handlePreview = async (file: UploadFile<any>) => {
if (!file.url && !file.preview) { if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj!); file.preview = await getBase64(file.originFileObj!);
@ -46,6 +68,37 @@ class PicturesWall extends React.Component<PicturesWallType> {
}); });
}; };
handleWallSelect = (url: string) => {
this.setState({
wallModalVisible: true,
});
};
handleImgSelected = (url: string) => {
this.setState({
curSelectedImg: url,
});
};
handleWallShow = () => {
this.setState({
wallModalVisible: true,
});
};
handleModalOk = () => {
const fileList = [
{
uid: uuid(8, 16),
name: 'h5-dooring图片库',
status: 'done',
url: this.state.curSelectedImg,
},
];
this.props.onChange && this.props.onChange(fileList);
this.setState({ fileList, wallModalVisible: false });
};
handleChange = ({ file, fileList }: UploadChangeParam<UploadFile<any>>) => { handleChange = ({ file, fileList }: UploadChangeParam<UploadFile<any>>) => {
this.setState({ fileList }); this.setState({ fileList });
if (file.status === 'done') { if (file.status === 'done') {
@ -74,14 +127,32 @@ class PicturesWall extends React.Component<PicturesWallType> {
return isJpgOrPng && isLt2M; return isJpgOrPng && isLt2M;
}; };
componentDidMount() {
req.get(`/visible/bed/get?tid=${unParams(location.search)!.tid}`).then(res => {
res &&
this.setState({
imgBed: res,
});
});
}
render() { render() {
const { previewVisible, previewImage, fileList, previewTitle } = this.state;
const { const {
// 配置自己的服务器地址 previewVisible,
action = isDev ? 'http://192.168.1.6:3000/api/xxx' : 'http://xxxx', previewImage,
fileList,
previewTitle,
wallModalVisible,
imgBed,
curSelectedImg,
} = this.state;
const {
action = isDev ? 'http://192.168.1.8:3000/api/v0/files/upload/free' : '你的服务器地址',
headers, headers,
withCredentials = true, withCredentials = true,
maxLen = 1, maxLen = 1,
cropRate = 375 / 158,
isCrop,
} = this.props; } = this.props;
const uploadButton = ( const uploadButton = (
@ -91,41 +162,110 @@ class PicturesWall extends React.Component<PicturesWallType> {
</div> </div>
); );
const cates = Object.keys(imgBed);
return ( return (
<ImgCrop <>
modalTitle="裁剪图片" {isCrop ? (
modalOk="确定" <ImgCrop
modalCancel="取消" modalTitle="裁剪图片"
rotate={true} modalOk="确定"
aspect={375 / 158} modalCancel="取消"
> rotate={true}
<Upload aspect={cropRate}
fileList={fileList} >
onPreview={this.handlePreview} <Upload
onChange={this.handleChange} fileList={fileList}
name="file" onPreview={this.handlePreview}
listType="picture-card" onChange={this.handleChange}
className={styles.avatarUploader} name="file"
action={action} listType="picture-card"
withCredentials={withCredentials} className={styles.avatarUploader}
headers={{ action={action}
'x-requested-with': localStorage.getItem('user') || '', withCredentials={withCredentials}
authorization: localStorage.getItem('token') || '', headers={{
...headers, 'x-requested-with': localStorage.getItem('user') || '',
}} authorization: localStorage.getItem('token') || '',
beforeUpload={this.handleBeforeUpload} ...headers,
> }}
{fileList.length >= maxLen ? null : uploadButton} beforeUpload={this.handleBeforeUpload}
</Upload> >
{fileList.length >= maxLen ? null : uploadButton}
</Upload>
</ImgCrop>
) : (
<Upload
fileList={fileList}
onPreview={this.handlePreview}
onChange={this.handleChange}
name="file"
listType="picture-card"
className={styles.avatarUploader}
action={action}
withCredentials={withCredentials}
headers={{
'x-requested-with': localStorage.getItem('user') || '',
authorization: localStorage.getItem('token') || '',
...headers,
}}
beforeUpload={this.handleBeforeUpload}
>
{fileList.length >= maxLen ? null : uploadButton}
</Upload>
)}
<div className={styles.wallBtn} onClick={this.handleWallShow}>
</div>
<Modal <Modal
visible={previewVisible} visible={previewVisible}
title={previewTitle} title={previewTitle}
footer={null} footer={null}
onCancel={this.handleCancel} onCancel={this.handleCancel}
> >
<img alt="example" style={{ width: '100%' }} src={previewImage} /> <img alt="预览图片" style={{ width: '100%' }} src={previewImage} />
</Modal> </Modal>
</ImgCrop> <Modal
visible={wallModalVisible}
title="图片库"
okText="确定"
cancelText="取消"
width={860}
onCancel={this.handleModalCancel}
onOk={this.handleModalOk}
>
<Tabs defaultActiveKey={cates[0]} tabPosition="left" style={{ height: 520 }}>
{cates.map((item, i) => {
return (
<TabPane tab={wallCateName[item]} key={item}>
<div className={styles.imgBox}>
{(imgBed as any)[item] &&
(imgBed as any)[item].map((item: string, i: number) => {
return (
<div
className={classnames(
styles.imgItem,
curSelectedImg === item ? styles.seleted : '',
)}
key={i}
onClick={() => this.handleImgSelected(item)}
>
<img src={item} alt="趣谈前端-h5-dooring" />
<span className={styles.iconBtn}>
<CheckCircleFilled />
</span>
</div>
);
})}
</div>
</TabPane>
);
})}
<TabPane tab="更多" key="more">
<Result status="500" title="Dooring温馨提示" subTitle="更多素材, 正在筹备中..." />
</TabPane>
</Tabs>
</Modal>
</>
); );
} }
} }

View File

@ -81,3 +81,12 @@ export function useAnimation(state: boolean, delay: number) {
}, [delay, display, state]); }, [delay, display, state]);
return [display, setDisplay]; return [display, setDisplay];
} }
export function unParams(params = '?a=1&b=2&c=3') {
let obj: any = {};
params &&
params.replace(/((\w*)=([\.a-z0-9A-Z]*)?)?/g, (m, a, b, c): any => {
if (b || c) obj[b] = c;
});
return obj;
}