前端:商品添加时,主图完成
This commit is contained in:
parent
6c135166c5
commit
788d6c381a
@ -40,9 +40,11 @@
|
|||||||
"bizcharts": "^3.4.3",
|
"bizcharts": "^3.4.3",
|
||||||
"bizcharts-plugin-slider": "^2.1.1-beta.1",
|
"bizcharts-plugin-slider": "^2.1.1-beta.1",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
|
"crypto": "^1.0.1",
|
||||||
"dva": "^2.4.1",
|
"dva": "^2.4.1",
|
||||||
"enquire-js": "^0.2.1",
|
"enquire-js": "^0.2.1",
|
||||||
"hash.js": "^1.1.7",
|
"hash.js": "^1.1.7",
|
||||||
|
"js-uuid": "0.0.6",
|
||||||
"lodash": "^4.17.11",
|
"lodash": "^4.17.11",
|
||||||
"lodash-decorators": "^6.0.1",
|
"lodash-decorators": "^6.0.1",
|
||||||
"memoize-one": "^5.0.0",
|
"memoize-one": "^5.0.0",
|
||||||
|
171
admin-web/src/components/Image/PicturesWall.js
Normal file
171
admin-web/src/components/Image/PicturesWall.js
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {fileGetQiniuToken} from "../../services/admin";
|
||||||
|
import uuid from "js-uuid";
|
||||||
|
import * as qiniu from "qiniu-js";
|
||||||
|
import {Icon, Modal, Upload} from "antd";
|
||||||
|
|
||||||
|
import styles from './PicturesWall.less';
|
||||||
|
|
||||||
|
class PicturesWall extends React.Component {
|
||||||
|
state = {
|
||||||
|
token: undefined, // 七牛 token
|
||||||
|
|
||||||
|
|
||||||
|
previewVisible: false,
|
||||||
|
previewImage: '',
|
||||||
|
|
||||||
|
fileList: [
|
||||||
|
// { // 目前图片
|
||||||
|
// uid: -1,
|
||||||
|
// name: 'xxx.png',
|
||||||
|
// status: 'done',
|
||||||
|
// url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||||
|
// }
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
handleCancel = () => this.setState({ previewVisible: false })
|
||||||
|
|
||||||
|
handlePreview = (file) => {
|
||||||
|
this.setState({
|
||||||
|
previewImage: file.url || file.thumbUrl,
|
||||||
|
previewVisible: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeUpload = async () => {
|
||||||
|
const tokenResult = await fileGetQiniuToken();
|
||||||
|
if (tokenResult.code !== 0) {
|
||||||
|
alert('获得七牛上传 Token 失败');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
token: tokenResult.data,
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
customRequest = ({action,
|
||||||
|
file,
|
||||||
|
headers,
|
||||||
|
onError,
|
||||||
|
onProgress,
|
||||||
|
onSuccess,
|
||||||
|
withCredentials,}) => {
|
||||||
|
// fs.readFile(path, function(err, data) {
|
||||||
|
// if (err) return;
|
||||||
|
// let md5Value= crypto.createHash('md5').update(data, 'utf8').digest('hex');
|
||||||
|
// let observable = qiniu.upload(file, md5Value, this.state.token);
|
||||||
|
// observable.subscribe(function () {
|
||||||
|
// // next
|
||||||
|
// }, function () {
|
||||||
|
// // error
|
||||||
|
// }, function (response) {
|
||||||
|
// // complete
|
||||||
|
// debugger;
|
||||||
|
// onSuccess(response, file);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// 使用 FileReader 将上传的文件转换成二进制流,满足 'application/octet-stream' 格式的要求
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
let fileData = null;
|
||||||
|
reader.onload = (e) => {
|
||||||
|
// 在文件读取结束后执行的操作
|
||||||
|
fileData = e.target.result;
|
||||||
|
// debugger;
|
||||||
|
// let md5Value= crypto.createHash('md5').update(fileData, 'utf8').digest('hex');
|
||||||
|
// 上传文件
|
||||||
|
// fileUploadQiniu(fileData);
|
||||||
|
// debugger;
|
||||||
|
// 使用 axios 进行文件上传的请求
|
||||||
|
// axios.put(action, fileData, {
|
||||||
|
// withCredentials,
|
||||||
|
// headers,
|
||||||
|
// onUploadProgress: ({ total, loaded }) => {
|
||||||
|
// // 进行上传进度输出,更加直观
|
||||||
|
// onProgress({ percent: Math.round(loaded / total * 100).toFixed(2) }, file);
|
||||||
|
// },
|
||||||
|
// }).then(response => {
|
||||||
|
// onSuccess(response, file);
|
||||||
|
// })
|
||||||
|
// .catch(onError);
|
||||||
|
let key = uuid.v4(); // TODO 芋艿,可能后面要优化。MD5?
|
||||||
|
let observable = qiniu.upload(file, key, this.state.token); // TODO 芋艿,最后后面去掉 qiniu 的库依赖,直接 http 请求,这样更轻量
|
||||||
|
observable.subscribe(function () {
|
||||||
|
// next
|
||||||
|
}, function () {
|
||||||
|
// error
|
||||||
|
// TODO 芋艿,后续补充
|
||||||
|
debugger;
|
||||||
|
}, function (response) {
|
||||||
|
// complete
|
||||||
|
// debugger;
|
||||||
|
response.url = 'http://static.shop.iocoder.cn/' + response.key; // 需要设置,用于后续 onSuccess ,合并到 file 中,从而设置到 fileList
|
||||||
|
onSuccess(response, file);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
abort() {
|
||||||
|
console.log('upload progress is aborted.');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
handleChange = ({ file, fileList }) => {
|
||||||
|
if (file.response && file.response.url) {
|
||||||
|
// debugger
|
||||||
|
// file.url =
|
||||||
|
for (let i in fileList) {
|
||||||
|
if (fileList[i].uid === file.uid) {
|
||||||
|
fileList[i].url = file.response.url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setState({ fileList });
|
||||||
|
}
|
||||||
|
|
||||||
|
getUrls = () => {
|
||||||
|
let urls = [];
|
||||||
|
for (let i in this.state.fileList) {
|
||||||
|
urls.push(this.state.fileList[i].url);
|
||||||
|
}
|
||||||
|
return urls;
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { previewVisible, previewImage, fileList } = this.state;
|
||||||
|
const uploadButton = (
|
||||||
|
<div>
|
||||||
|
<Icon type="plus" />
|
||||||
|
<div className="ant-upload-text">Upload</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<div className="clearfix">
|
||||||
|
<Upload
|
||||||
|
// action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
|
||||||
|
// action="https://up-z2.qiniu.com"
|
||||||
|
listType="picture-card"
|
||||||
|
fileList={fileList}
|
||||||
|
onPreview={this.handlePreview}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
beforeUpload={this.beforeUpload}
|
||||||
|
customRequest={this.customRequest}
|
||||||
|
>
|
||||||
|
{fileList.length >= this.props.maxLength ? null : uploadButton}
|
||||||
|
</Upload>
|
||||||
|
<Modal visible={previewVisible} footer={null} onCancel={this.handleCancel}>
|
||||||
|
<img alt="example" style={{ width: '100%' }} src={previewImage} />
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PicturesWall.propTypes = {
|
||||||
|
maxLength: Number,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PicturesWall;
|
10
admin-web/src/components/Image/PicturesWall.less
Normal file
10
admin-web/src/components/Image/PicturesWall.less
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
.ant-upload-select-picture-card i {
|
||||||
|
font-size: 32px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-upload-select-picture-card .ant-upload-text {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: #666;
|
||||||
|
}
|
@ -1,136 +1,26 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
|
||||||
import React, {PureComponent, Fragment, Component} from 'react';
|
import React, {PureComponent, Fragment, Component} from 'react';
|
||||||
|
// import crypto from 'crypto';
|
||||||
|
// import fs from 'fs';
|
||||||
import { connect } from 'dva';
|
import { connect } from 'dva';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {Card, Form, Input, Radio, Button, Modal, Select, Upload, Icon} from 'antd';
|
import {Card, Form, Input, Radio, Button, Modal, Select, Upload, Icon} from 'antd';
|
||||||
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
|
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
|
||||||
|
|
||||||
import * as qiniu from 'qiniu-js'
|
// import * as qiniu from 'qiniu-js'
|
||||||
|
// import uuid from 'js-uuid';
|
||||||
|
|
||||||
import styles from './ProductSpuAddOrUpdate.less';
|
import styles from './ProductSpuAddOrUpdate.less';
|
||||||
import ProductAttrSelectFormItem from "../../components/Product/ProductAttrSelectFormItem";
|
import ProductAttrSelectFormItem from "../../components/Product/ProductAttrSelectFormItem";
|
||||||
import ProductSkuAddOrUpdateTable from "../../components/Product/ProductSkuAddOrUpdateTable";
|
import ProductSkuAddOrUpdateTable from "../../components/Product/ProductSkuAddOrUpdateTable";
|
||||||
import {fileGetQiniuToken, fileUploadQiniu} from "../../services/admin";
|
import {fileGetQiniuToken} from "../../services/admin";
|
||||||
|
import PicturesWall from "../../components/Image/PicturesWall";
|
||||||
|
|
||||||
const FormItem = Form.Item;
|
const FormItem = Form.Item;
|
||||||
const RadioGroup = Radio.Group;
|
const RadioGroup = Radio.Group;
|
||||||
const Option = Select.Option;
|
const Option = Select.Option;
|
||||||
|
|
||||||
class PicturesWall extends React.Component {
|
|
||||||
state = {
|
|
||||||
token: undefined, // 七牛 token
|
|
||||||
|
|
||||||
|
|
||||||
previewVisible: false,
|
|
||||||
previewImage: '',
|
|
||||||
|
|
||||||
fileList: [{ // 目前图片
|
|
||||||
uid: -1,
|
|
||||||
name: 'xxx.png',
|
|
||||||
status: 'done',
|
|
||||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
|
||||||
}],
|
|
||||||
};
|
|
||||||
|
|
||||||
handleCancel = () => this.setState({ previewVisible: false })
|
|
||||||
|
|
||||||
handlePreview = (file) => {
|
|
||||||
this.setState({
|
|
||||||
previewImage: file.url || file.thumbUrl,
|
|
||||||
previewVisible: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeUpload = async () => {
|
|
||||||
const tokenResult = await fileGetQiniuToken();
|
|
||||||
if (tokenResult.code !== 0) {
|
|
||||||
alert('获得七牛上传 Token 失败');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.setState({
|
|
||||||
token: tokenResult.data,
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
customRequest = ({action,
|
|
||||||
file,
|
|
||||||
headers,
|
|
||||||
onError,
|
|
||||||
onProgress,
|
|
||||||
onSuccess,
|
|
||||||
withCredentials,}) => {
|
|
||||||
// 使用 FileReader 将上传的文件转换成二进制流,满足 'application/octet-stream' 格式的要求
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.readAsArrayBuffer(file);
|
|
||||||
let fileData = null;
|
|
||||||
reader.onload = (e) => {
|
|
||||||
// 在文件读取结束后执行的操作
|
|
||||||
fileData = e.target.result;
|
|
||||||
// 上传文件
|
|
||||||
// fileUploadQiniu(fileData);
|
|
||||||
// debugger;
|
|
||||||
// 使用 axios 进行文件上传的请求
|
|
||||||
// axios.put(action, fileData, {
|
|
||||||
// withCredentials,
|
|
||||||
// headers,
|
|
||||||
// onUploadProgress: ({ total, loaded }) => {
|
|
||||||
// // 进行上传进度输出,更加直观
|
|
||||||
// onProgress({ percent: Math.round(loaded / total * 100).toFixed(2) }, file);
|
|
||||||
// },
|
|
||||||
// }).then(response => {
|
|
||||||
// onSuccess(response, file);
|
|
||||||
// })
|
|
||||||
// .catch(onError);
|
|
||||||
let observable = qiniu.upload(file, '123', this.state.token);
|
|
||||||
observable.subscribe(function () {
|
|
||||||
// next
|
|
||||||
}, function () {
|
|
||||||
// error
|
|
||||||
}, function () {
|
|
||||||
// complete
|
|
||||||
});
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
abort() {
|
|
||||||
console.log('upload progress is aborted.');
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
handleChange = ({ fileList }) => this.setState({ fileList })
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { previewVisible, previewImage, fileList } = this.state;
|
|
||||||
const uploadButton = (
|
|
||||||
<div>
|
|
||||||
<Icon type="plus" />
|
|
||||||
<div className="ant-upload-text">Upload</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<div className="clearfix">
|
|
||||||
<Upload
|
|
||||||
// action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
|
|
||||||
action="https://up-z2.qiniu.com"
|
|
||||||
listType="picture-card"
|
|
||||||
fileList={fileList}
|
|
||||||
onPreview={this.handlePreview}
|
|
||||||
onChange={this.handleChange}
|
|
||||||
beforeUpload={this.beforeUpload}
|
|
||||||
customRequest={this.customRequest}
|
|
||||||
>
|
|
||||||
{fileList.length >= 3 ? null : uploadButton}
|
|
||||||
</Upload>
|
|
||||||
<Modal visible={previewVisible} footer={null} onCancel={this.handleCancel}>
|
|
||||||
<img alt="example" style={{ width: '100%' }} src={previewImage} />
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// roleList
|
// roleList
|
||||||
@connect(({ productSpuList, productAttrList, productSpuAddOrUpdate, loading }) => ({
|
@connect(({ productSpuList, productAttrList, productSpuAddOrUpdate, loading }) => ({
|
||||||
// list: productSpuList.list.spus,
|
// list: productSpuList.list.spus,
|
||||||
@ -175,9 +65,14 @@ class ProductSpuAddOrUpdate extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit = e => {
|
handleSubmit = e => {
|
||||||
debugger;
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const { skus, dispatch } = this.props;
|
const { skus, dispatch } = this.props;
|
||||||
|
// 获得图片
|
||||||
|
let picUrls = this.refs.picturesWall.getUrls();
|
||||||
|
if (picUrls.length === 0) {
|
||||||
|
alert('请必须上传一张图片!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
// 生成 skuStr 格式
|
// 生成 skuStr 格式
|
||||||
let skuStr = []; // 因为提交是字符串格式
|
let skuStr = []; // 因为提交是字符串格式
|
||||||
for (let i in skus) {
|
for (let i in skus) {
|
||||||
@ -199,6 +94,7 @@ class ProductSpuAddOrUpdate extends Component {
|
|||||||
alert('请设置商品规格!');
|
alert('请设置商品规格!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// debugger;
|
||||||
this.props.form.validateFields((err, values) => {
|
this.props.form.validateFields((err, values) => {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
dispatch({
|
dispatch({
|
||||||
@ -206,6 +102,7 @@ class ProductSpuAddOrUpdate extends Component {
|
|||||||
payload: {
|
payload: {
|
||||||
body: {
|
body: {
|
||||||
...values,
|
...values,
|
||||||
|
picUrls: picUrls.join(','),
|
||||||
skuStr: JSON.stringify(skuStr)
|
skuStr: JSON.stringify(skuStr)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -362,11 +259,12 @@ class ProductSpuAddOrUpdate extends Component {
|
|||||||
initialValue: '', // TODO 修改 // TODO 芋艿,和面做成下拉框
|
initialValue: '', // TODO 修改 // TODO 芋艿,和面做成下拉框
|
||||||
})(<Input placeholder="请输入" />)}
|
})(<Input placeholder="请输入" />)}
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="商品主图">
|
<FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="商品主图"
|
||||||
|
extra="建议尺寸:800*800PX,单张大小不超过 2M,最多可上传 10 张">
|
||||||
{/*{form.getFieldDecorator('picUrls', {*/}
|
{/*{form.getFieldDecorator('picUrls', {*/}
|
||||||
{/* initialValue: '', // TODO 修改 // TODO 芋艿,做成上传组件*/}
|
{/* initialValue: '', // TODO 修改 // TODO 芋艿,做成上传组件*/}
|
||||||
{/*})(<Input placeholder="请输入" />)}*/}
|
{/*})(<Input placeholder="请输入" />)}*/}
|
||||||
<PicturesWall />
|
<PicturesWall ref="picturesWall" maxLength={10} />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="是否上架">
|
<FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="是否上架">
|
||||||
{form.getFieldDecorator('visible', {
|
{form.getFieldDecorator('visible', {
|
||||||
|
@ -13,13 +13,3 @@
|
|||||||
.tableDelete {
|
.tableDelete {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-upload-select-picture-card i {
|
|
||||||
font-size: 32px;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-upload-select-picture-card .ant-upload-text {
|
|
||||||
margin-top: 8px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user