mirror of
https://github.com/MrXujiang/h5-Dooring.git
synced 2026-01-19 20:28:13 +00:00
🆕 添加图片库,支持用户在线选择图片素材
This commit is contained in:
parent
75facb2b2a
commit
eb26adabb5
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user