完成上传页面主要逻辑,网络问题仍未解决
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -21,3 +21,5 @@ | |||||||
| npm-debug.log* | npm-debug.log* | ||||||
| yarn-debug.log* | yarn-debug.log* | ||||||
| yarn-error.log* | yarn-error.log* | ||||||
|  |  | ||||||
|  | env.json | ||||||
| @@ -1 +1,4 @@ | |||||||
| #inMyHeartFrontEnd | # inMyHeartFrontEnd | ||||||
|  |  | ||||||
|  | # 开发或编译前 | ||||||
|  | 请根据`env.example.json`的格式按照自己的需求编写`env.json`,或者直接运行`cp env.example.json env.json`,否则无法通过编译! | ||||||
| @@ -3,7 +3,9 @@ | |||||||
|   "version": "0.1.0", |   "version": "0.1.0", | ||||||
|   "private": true, |   "private": true, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|  |     "axios": "^0.21.1", | ||||||
|     "cra-template": "1.1.2", |     "cra-template": "1.1.2", | ||||||
|  |     "dotenv": "^10.0.0", | ||||||
|     "react": "^17.0.2", |     "react": "^17.0.2", | ||||||
|     "react-dom": "^17.0.2", |     "react-dom": "^17.0.2", | ||||||
|     "react-dropzone": "^11.3.4", |     "react-dropzone": "^11.3.4", | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								src/App.css
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								src/App.css
									
									
									
									
									
								
							| @@ -6,10 +6,19 @@ a.btn { | |||||||
|   border-radius: .5rem; |   border-radius: .5rem; | ||||||
|   padding: .375rem 1.75rem; |   padding: .375rem 1.75rem; | ||||||
|   font-size: 1.1rem; |   font-size: 1.1rem; | ||||||
|  |   cursor: pointer; | ||||||
|  |   transition: filter .3s ease-out; | ||||||
|  | } | ||||||
|  | .btn:disabled { | ||||||
|  |   cursor: not-allowed; | ||||||
|  |   filter: grayscale(100%); | ||||||
| } | } | ||||||
| .btn:active { | .btn:active { | ||||||
|   color: white !important; |   color: white !important; | ||||||
| } | } | ||||||
|  | .btn:hover { | ||||||
|  |   filter: brightness(1.2); | ||||||
|  | } | ||||||
|  |  | ||||||
| button.btn { | button.btn { | ||||||
|   border: none; |   border: none; | ||||||
| @@ -17,4 +26,18 @@ button.btn { | |||||||
|  |  | ||||||
| .btn-primary { | .btn-primary { | ||||||
|   background-color: #318ffb; |   background-color: #318ffb; | ||||||
|  | } | ||||||
|  | .btn-ok { | ||||||
|  |   background-color: #47c74c; | ||||||
|  | } | ||||||
|  | .btn-gray { | ||||||
|  |   background-color: #d9d9d9; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .btn-circle { | ||||||
|  |   border-radius: 50%; | ||||||
|  |   width: 40px; | ||||||
|  |   height: 40px; | ||||||
|  |   padding: 8px; | ||||||
|  |   box-shadow: 1px 1px 4px 0 #0003; | ||||||
| } | } | ||||||
							
								
								
									
										17
									
								
								src/components/Spinner/Spinner.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/components/Spinner/Spinner.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | import './spinner.css'; | ||||||
|  |  | ||||||
|  | export default function Spinner(props) { | ||||||
|  |   return ( | ||||||
|  |     <div className="spinner"> | ||||||
|  |       <svg viewBox="0 0 1027 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |         <path d="M101.725953 198.235466m48.271823-48.271823l0 0q48.271823-48.271823 96.543646 0l144.815469 144.815468q48.271823 48.271823 0 96.543646l0 0q-48.271823 48.271823-96.543646 0l-144.815469-144.815468q-48.271823-48.271823 0-96.543646Z" fill="#FFFD"></path> | ||||||
|  |         <path d="M341.367467 443.733333m0 68.266667l0 0q0 68.266667-68.266667 68.266667l-204.8 0q-68.266667 0-68.266667-68.266667l0 0q0-68.266667 68.266667-68.266667l204.8 0q68.266667 0 68.266667 68.266667Z" fill="#FFFC"></path> | ||||||
|  |         <path d="M343.085068 584.40542m48.271823 48.271823l0 0q48.271823 48.271823 0 96.543646l-144.815469 144.815468q-48.271823 48.271823-96.543646 0l0 0q-48.271823-48.271823 0-96.543646l144.815469-144.815468q48.271823-48.271823 96.543646 0Z" fill="#FFFB"></path> | ||||||
|  |         <path d="M443.767467 682.666667m68.266666 0l0 0q68.266667 0 68.266667 68.266666l0 204.8q0 68.266667-68.266667 68.266667l0 0q-68.266667 0-68.266666-68.266667l0-204.8q0-68.266667 68.266666-68.266666Z" fill="#FFFA"></path> | ||||||
|  |         <path d="M584.439553 680.949066m48.271823-48.271823l0 0q48.271823-48.271823 96.543646 0l144.815469 144.815468q48.271823 48.271823 0 96.543646l0 0q-48.271823 48.271823-96.543646 0l-144.815469-144.815468q-48.271823-48.271823 0-96.543646Z" fill="#FFF9"></path> | ||||||
|  |         <path d="M1024.034133 443.733333m0 68.266667l0 0q0 68.266667-68.266666 68.266667l-204.8 0q-68.266667 0-68.266667-68.266667l0 0q0-68.266667 68.266667-68.266667l204.8 0q68.266667 0 68.266666 68.266667Z" fill="#FFF8"></path> | ||||||
|  |         <path d="M825.798668 101.69182m48.271823 48.271823l0 0q48.271823 48.271823 0 96.543646l-144.815469 144.815468q-48.271823 48.271823-96.543646 0l0 0q-48.271823-48.271823 0-96.543646l144.815469-144.815468q48.271823-48.271823 96.543646 0Z" fill="#FFF7"></path> | ||||||
|  |         <path d="M443.767467 0m68.266666 0l0 0q68.266667 0 68.266667 68.266667l0 204.8q0 68.266667-68.266667 68.266666l0 0q-68.266667 0-68.266666-68.266666l0-204.8q0-68.266667 68.266666-68.266667Z" fill="#FFF6"></path></svg> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										19
									
								
								src/components/Spinner/spinner.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/components/Spinner/spinner.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | .spinner { | ||||||
|  |   width: 20px; | ||||||
|  |   height: 20px; | ||||||
|  |   margin: 0 auto; | ||||||
|  | } | ||||||
|  | .spinner > svg { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  |   animation: spin 1s steps(8, start) infinite; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @keyframes spin { | ||||||
|  |   from { | ||||||
|  |     transform: rotate(0deg); | ||||||
|  |   } | ||||||
|  |   to { | ||||||
|  |     transform: rotate(360deg); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -1,33 +1,62 @@ | |||||||
| import './upload.css'; | import './upload.css'; | ||||||
|  |  | ||||||
| import { Component } from "react"; | import { Component, Fragment } from "react"; | ||||||
| import { apis } from '../../resources.json'; | import { apis } from '../../helper/apis'; | ||||||
|  | import Spinner from '../Spinner/Spinner'; | ||||||
|  |  | ||||||
| export default class UploadUnit extends Component { | export default class UploadUnit extends Component { | ||||||
|   toBlobPromise = null; |  | ||||||
|  |  | ||||||
|   constructor(props) { |   constructor(props) { | ||||||
|     super(props); |     super(props); | ||||||
|     this.state = { |     this.state = { | ||||||
|       // 0: loading, 1: loaded, 2: compressing, 3: uploading, 4: uploaded |       // 0: loading, 1: compressing, 2: compressed, 3: uploading, 4: uploaded | ||||||
|       status: 0, |       status: -1, | ||||||
|       src: null |       src: null, | ||||||
|  |       file: null, | ||||||
|  |       progress: 0, | ||||||
|  |       width: 0 | ||||||
|     }; |     }; | ||||||
|  |     this.upload = this.upload.bind(this); | ||||||
|  |     this.handleCancel = this.handleCancel.bind(this); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   selected(file) { |   componentDidMount() { | ||||||
|     if (file) { |     setTimeout(() => { | ||||||
|       if (!["image/jpeg", "image/png", "image/gif"].includes(file.type)) |       this.readFile(this.props.file);       | ||||||
|         return alert('请不要上传jpg、png、gif格式以外的文件!') |     }, 100); | ||||||
|       let reader = new FileReader(); |   } | ||||||
|  |  | ||||||
|  |   componentDidUpdate() { | ||||||
|  |     if (this.props.upload && this.state.status === 2) | ||||||
|  |       this.upload(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   handleCancel() { | ||||||
|  |     this.setState({ status: -1 }); | ||||||
|  |     setTimeout(() => { | ||||||
|  |       this.props.onCancel(); | ||||||
|  |     }, 300); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   readFile(file) { | ||||||
|  |     if (!["image/jpeg", "image/png", "image/gif"].includes(file.type)) { | ||||||
|  |       alert('请不要上传jpg、png、gif格式以外的文件!'); | ||||||
|  |       this.handleCancel(); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     let reader = new FileReader(); | ||||||
|  |     this.setState({ status: -2 }); | ||||||
|  |     setTimeout(() => { | ||||||
|  |       this.setState({ status: 0 }); | ||||||
|       reader.onload = () => { |       reader.onload = () => { | ||||||
|         var image = new Image(); |         var image = new Image(); | ||||||
|         image.onload = () => setTimeout(() => this.convert(image), 1000); |         // 被注释掉的是用来应对ios巨大图片没来得及加载的问题 | ||||||
|  |         // image.onload = () => setTimeout(() => this.convert(image), 1000); | ||||||
|  |         image.onload = () => this.convert(image); | ||||||
|         image.src = reader.result; |         image.src = reader.result; | ||||||
|  |         this.setState({ status: 1 }); | ||||||
|       }; |       }; | ||||||
|       reader.readAsDataURL(file); |       reader.readAsDataURL(file);    | ||||||
|     } |     }, 300); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   convert(img) { |   convert(img) { | ||||||
| @@ -45,21 +74,12 @@ export default class UploadUnit extends Component { | |||||||
|     canvas.width = width; |     canvas.width = width; | ||||||
|     canvas.height = height; |     canvas.height = height; | ||||||
|     canvas.getContext('2d').drawImage(img, 0, 0, width, height); |     canvas.getContext('2d').drawImage(img, 0, 0, width, height); | ||||||
|     this.toBlobPromise = new Promise(res => { |     canvas.toBlob(blob => { | ||||||
|       canvas.toBlob(blob => { |       this.setState({ src: canvas.toDataURL('image/jpeg', 0.8), status: 2, file: blob, width: width / height * 200 }); | ||||||
|         res(blob); |     }, 'image/jpeg', 0.8); | ||||||
|       }, 'image/jpeg', 0.8); |  | ||||||
|     }) |  | ||||||
|     this.converted = true; |  | ||||||
|     this.setState({ img_base64: canvas.toDataURL('image/jpeg', 0.8) }); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   async upload() { |   async upload() { | ||||||
|     var el = document.getElementById('process-bubble'); |  | ||||||
|     el.style.display = ''; |  | ||||||
|     setTimeout(() => { |  | ||||||
|       el.className = 'show'; |  | ||||||
|     }, 100);       |  | ||||||
|     var xhr = new XMLHttpRequest(); |     var xhr = new XMLHttpRequest(); | ||||||
|     xhr.onreadystatechange = () => { |     xhr.onreadystatechange = () => { | ||||||
|       if (xhr.readyState === 4) { |       if (xhr.readyState === 4) { | ||||||
| @@ -70,49 +90,101 @@ export default class UploadUnit extends Component { | |||||||
|             this.props.onUpload(data.data.url); |             this.props.onUpload(data.data.url); | ||||||
|           } |           } | ||||||
|           else { |           else { | ||||||
|             this.setState({ status: 1 }); |             this.setState({ status: 2 }); | ||||||
|             this.props.onUploadError(data.msg); |             this.props.onUploadError(data.msg); | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|           this.setState({ status: 1 }); |           this.setState({ status: 2 }); | ||||||
|           this.props.onUploadError('上传失败:服务器出错'); |           this.props.onUploadError('上传失败:服务器出错'); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }; |     }; | ||||||
|     xhr.onerror = () => { |     xhr.onerror = () => { | ||||||
|       this.setState({ status: 1 }); |       this.setState({ status: 2 }); | ||||||
|       this.props.onUploadError('请求失败,请检查网络'); |       this.props.onUploadError('请求失败,请检查网络'); | ||||||
|     } |     } | ||||||
|  |     xhr.onprogress = e => { | ||||||
|  |       if (e.lengthComputable) { | ||||||
|  |         var percent = Math.round(e.loaded * 100 / e.total); | ||||||
|  |         this.setState({ progress: percent }); | ||||||
|  |       } | ||||||
|  |     }; | ||||||
|     xhr.open("POST", apis.uploadImage, true); |     xhr.open("POST", apis.uploadImage, true); | ||||||
|     xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); |     xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); | ||||||
|  |  | ||||||
|     var fd = new FormData(); |     var fd = new FormData(); | ||||||
|     fd.append("image", await this.toBlobPromise, 'image.jpg'); |     fd.append("image", this.state.file, 'image.jpg'); | ||||||
|     xhr.send(fd); |     xhr.send(fd); | ||||||
|  |     this.setState({ status: 3, progress: 0 }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   render() { |   render() { | ||||||
|  |     const angle = this.state.progress / 100 * Math.PI * 2; | ||||||
|     return ( |     return ( | ||||||
|       <div className="upload-unit"> |       <div className="upload-wrap"> | ||||||
|         { |         <div className={this.state.status === -1 ? 'upload-unit' : 'upload-unit open'} style={{ width: this.state.status <= 1 ? '' : `${this.state.width}px` }}> | ||||||
|           this.state.status !== 0 |           {this.state.status >= 2 | ||||||
|             ? <img src={this.state.src} alt={this.state.status === 1 ? '原图片' : '压缩后的图片'} /> |             ? <img src={this.state.src} alt="压缩后的图片" /> | ||||||
|             : null |             : null | ||||||
|         } |           } | ||||||
|         <div className="duo-animation duo-expanding-rect"> |           {this.state.status >= 0 | ||||||
|           <div></div> |             ? ( | ||||||
|           <div></div> |               <Fragment> | ||||||
|         </div> |                 <div className={this.state.status === 2 ? 'upload-unit-mask hide' : 'upload-unit-mask'}> | ||||||
|         <div className="duo-animation duo-shrinking-rect"> |                   {this.state.status === 3 | ||||||
|           <div></div> |                     ? ( | ||||||
|           <div></div> |                       <div className="upload-unit-progress"> | ||||||
|         </div> |                         <div className="progress-circle"> | ||||||
|         <div className="rising-arrow"> |                           <svg viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|           <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M170.7 381.1c-23.3 23.3-23.3 61.2 0 84.5 11.7 11.7 27 17.5 42.3 17.5s30.6-5.9 42.3-17.5l196.8-196.8v631c0 33 26.8 59.8 59.8 59.8s59.8-26.8 59.8-59.8v-631l196.8 196.8c23.3 23.3 61.2 23.3 84.5 0s23.3-61.2 0-84.5L554.1 82.2c-23.3-23.3-61.2-23.3-84.5 0L170.7 381.1z" fill="#d91604"></path></svg> |                             <path d={ | ||||||
|         </div> |                               `M 50,3 A 47,47 0 ${this.state.progress < 50 ? 0 : 1},1 ${50 + Math.sin(angle) * 47},${50 - Math.cos(angle) * 47}` | ||||||
|         <div className="checked-circle"> |                             } fill="transparent" stroke="#FFF" strokeWidth="6" strokeLinecap="round"></path> | ||||||
|           <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M512 512m-512 0a512 512 0 1 0 1024 0 512 512 0 1 0-1024 0Z" fill="#1AAC19" p-id="2905"></path><path d="M809.691429 392.777143L732.16 314.514286 447.634286 599.771429 292.571429 443.977143 214.308571 521.508571l155.794286 155.794286 77.531429 77.531429 362.057143-362.057143z" fill="#FFFFFF"></path></svg> |                           </svg> | ||||||
|  |                         </div> | ||||||
|  |                         <div className="progress-detail"> | ||||||
|  |                           上传中 | ||||||
|  |                         </div> | ||||||
|  |                       </div> | ||||||
|  |                     ) : null | ||||||
|  |                   } | ||||||
|  |                   {this.state.status <= 1 | ||||||
|  |                     ? ( | ||||||
|  |                       <div className="upload-unit-progress"> | ||||||
|  |                         <Spinner /> | ||||||
|  |                         <div className="progress-detail"> | ||||||
|  |                           {this.state.status === 0 ? '加载中' : '压缩中'} | ||||||
|  |                         </div> | ||||||
|  |                       </div> | ||||||
|  |                     ) : null | ||||||
|  |                   } | ||||||
|  |                   {this.state.status === 4 | ||||||
|  |                     ? ( | ||||||
|  |                       <div style={{ color: '#FFF', fontSize: '16px' }}>上传成功</div> | ||||||
|  |                     ) : null} | ||||||
|  |                 </div> | ||||||
|  |                 <div className="upload-btns"> | ||||||
|  |                   {this.state.status === 2 | ||||||
|  |                     ? ( | ||||||
|  |                       <button className="btn btn-circle btn-ok" onClick={this.upload}> | ||||||
|  |                         <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M939.717 106.665l-304.049 324.529-78.769 85.071-225.28 240.246-123.668-115.003-129.182-121.305-78.769 84.283 129.969 121.305 33.083 31.508 129.969 121.305 44.111 40.96 304.049-324.529 78.769-85.071 304.049-324.529-84.283-78.769z" fill="#ffffff"></path></svg> | ||||||
|  |                       </button> | ||||||
|  |                     ) : null | ||||||
|  |                   } | ||||||
|  |                   {this.state.status === 2 || this.state.status === 4 | ||||||
|  |                     ? ( | ||||||
|  |                       <button className="btn btn-circle btn-primary"> | ||||||
|  |                         <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M959.488 920.32l-39.168 39.168a27.676444 27.676444 0 0 1-39.139556 0l-162.133333-162.133333a27.648 27.648 0 0 1 0-39.168l39.139556-39.139556a27.648 27.648 0 0 1 39.168 0l162.133333 162.104889c10.808889 10.837333 10.808889 28.359111 0 39.168zM169.358222 712.419556c-149.959111-149.959111-149.959111-393.102222 0-543.061334 149.959111-149.959111 393.073778-149.959111 543.061334 0 149.959111 149.959111 149.959111 393.102222 0 543.061334-149.987556 149.959111-393.102222 149.959111-543.061334 0zM631.324444 249.742222c-105.358222-105.386667-276.195556-105.386667-381.582222 0a269.824 269.824 0 0 0 0 381.610667A269.824 269.824 0 1 0 631.324444 249.742222z" fill="#ffffff"></path><path d="M959.488 920.32l-39.168 39.168a27.676444 27.676444 0 0 1-39.139556 0l-162.133333-162.133333a27.648 27.648 0 0 1 0-39.168l39.139556-39.139556a27.648 27.648 0 0 1 39.168 0l162.133333 162.104889c10.808889 10.837333 10.808889 28.359111 0 39.168zM169.358222 712.419556c-149.959111-149.959111-149.959111-393.102222 0-543.061334 149.959111-149.959111 393.073778-149.959111 543.061334 0 149.959111 149.959111 149.959111 393.102222 0 543.061334-149.987556 149.959111-393.102222 149.959111-543.061334 0zM631.324444 249.742222c-105.358222-105.386667-276.195556-105.386667-381.582222 0a269.824 269.824 0 0 0 0 381.610667A269.824 269.824 0 1 0 631.324444 249.742222z" fill="#ffffff"></path></svg> | ||||||
|  |                       </button> | ||||||
|  |                     ) : null | ||||||
|  |                   } | ||||||
|  |                   <button className="btn btn-circle btn-gray" onClick={this.handleCancel}> | ||||||
|  |                     <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M597.333333 512l284.444445 284.444444c2.929778 3.328 2.929778 22.357333 0 28.444445l-56.888889 56.888889c-6.087111 2.929778-25.116444 2.929778-28.444445 0L512 597.333333 227.555556 881.777778c-3.328 2.929778-22.328889 2.929778-28.444445 0l-56.888889-56.888889c-2.929778-6.087111-2.929778-25.116444 0-28.444445l284.444445-284.444444L142.222222 227.555556c-2.929778-3.328-2.929778-22.328889 0-28.444445l56.888889-56.888889c6.115556-2.929778 25.116444-2.929778 28.444445 0l284.444444 284.444445L796.444444 142.222222c3.328-2.929778 22.357333-2.929778 28.444445 0l56.888889 56.888889c2.929778 6.115556 2.929778 25.116444 0 28.444445L597.333333 512z" fill="#4a4a4a"></path></svg> | ||||||
|  |                   </button> | ||||||
|  |                 </div> | ||||||
|  |               </Fragment> | ||||||
|  |             ) : null | ||||||
|  |           } | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|   | |||||||
| @@ -0,0 +1,78 @@ | |||||||
|  | .upload-wrap { | ||||||
|  |   width: 100%; | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: column; | ||||||
|  |   align-items: center; | ||||||
|  | } | ||||||
|  | .upload-unit { | ||||||
|  |   width: calc(100%); | ||||||
|  |   height: 200px; | ||||||
|  |   min-width: 150px; | ||||||
|  |   border: 2px dashed blue; | ||||||
|  |   border-radius: 8px; | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   transition: width .3s ease-out, height .3s ease-out, border .3s ease-out; | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: column; | ||||||
|  |   align-items: center; | ||||||
|  |   overflow: hidden; | ||||||
|  | } | ||||||
|  | .upload-unit.open { | ||||||
|  |   width: 200px; | ||||||
|  |   border-width: 0; | ||||||
|  | } | ||||||
|  | .upload-unit > img { | ||||||
|  |   margin-bottom: -200px; | ||||||
|  |   height: 200px; | ||||||
|  |   animation: fade-in .3s ease-out; | ||||||
|  |   border-radius: 8px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .upload-unit-mask { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  |   background: rgba(0, 0, 0, 0.5); | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: column; | ||||||
|  |   align-items: center; | ||||||
|  |   justify-content: center; | ||||||
|  |   transition: filter .3s ease-out; | ||||||
|  | } | ||||||
|  | .upload-unit-mask.hide { | ||||||
|  |   filter: opacity(0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .upload-unit-progress { | ||||||
|  |   width: 50px; | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: column; | ||||||
|  |   align-items: center; | ||||||
|  | } | ||||||
|  | .progress-detail { | ||||||
|  |   color: white; | ||||||
|  |   font-size: 16px; | ||||||
|  |   margin-top: 10px; | ||||||
|  | } | ||||||
|  | .progress-circle { | ||||||
|  |   width: 50px; | ||||||
|  |   height: 50px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .upload-btns { | ||||||
|  |   display: flex; | ||||||
|  |   justify-content: center; | ||||||
|  |   margin-top: -60px; | ||||||
|  |   z-index: 1; | ||||||
|  | } | ||||||
|  | .upload-btns > button { | ||||||
|  |   margin: 0 5px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @keyframes fade-in { | ||||||
|  |   from { | ||||||
|  |     filter: opacity(0); | ||||||
|  |   } | ||||||
|  |   to { | ||||||
|  |     filter: opacity(1); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								src/env.example.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/env.example.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | { | ||||||
|  |   "backEndBaseURL": "localhost:9444" | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								src/helper/apis.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/helper/apis.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | import { backEndBaseURL } from '../env.json'; | ||||||
|  | export const apis = { | ||||||
|  |   uploadImage: "https://image.kieng.cn/upload.html?type=jd", | ||||||
|  |  | ||||||
|  |   login: backEndBaseURL + "/user/login", | ||||||
|  |   getProfile: backEndBaseURL + "/user/me", | ||||||
|  |  | ||||||
|  |   submitMessage: backEndBaseURL + "/post/submit", | ||||||
|  |   listEssence: backEndBaseURL + "/post/listPublished", | ||||||
|  |  | ||||||
|  |   listAll: backEndBaseURL + "/admin/list", | ||||||
|  |   setStatus: backEndBaseURL + "/admin/setStatus" | ||||||
|  | }; | ||||||
							
								
								
									
										18
									
								
								src/helper/axios.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/helper/axios.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | import axios from 'axios'; | ||||||
|  |  | ||||||
|  | export function get(url) { | ||||||
|  |   return axios.get(url, { | ||||||
|  |     headers: { | ||||||
|  |       Authorization: 'Bearer ' + localStorage.getItem('jwt'), | ||||||
|  |       "Allow-Control-Allow-Origin": "*" | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | export function post(url, data) { | ||||||
|  |   return axios.post(url, data, { | ||||||
|  |     headers: { | ||||||
|  |       Authorization: 'Bearer ' + localStorage.getItem('jwt'), | ||||||
|  |       "Allow-Control-Allow-Origin": "*" | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  | } | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| { | { | ||||||
|   "apis": { |   "apis": { | ||||||
|     "uploadImage": "https://image.kieng.cn/upload.html?type=jd" |      | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -28,23 +28,40 @@ | |||||||
| .message-box { | .message-box { | ||||||
|   width: 100%; |   width: 100%; | ||||||
|   height: 200px; |   height: 200px; | ||||||
|  |   margin-top: 40px; | ||||||
| } | } | ||||||
| .message-box-textarea { | .message-box-textarea { | ||||||
|   width: 100%; |   width: 100%; | ||||||
|   resize: none; |   resize: none; | ||||||
|   height: 100%; |   height: 100%; | ||||||
|   box-sizing: border-box; |   box-sizing: border-box; | ||||||
|  |   border-radius: 8px; | ||||||
|  |   font-size: 16px; | ||||||
|  |   padding: 10px; | ||||||
|  | } | ||||||
|  | .message-box-textarea:focus { | ||||||
|  |   outline: none; | ||||||
|  |   border-color: #008cff; | ||||||
| } | } | ||||||
|  |  | ||||||
| .upload-box { | .upload-box { | ||||||
|   width: 100%; |   width: 100%; | ||||||
|   height: 200px; |   height: 200px; | ||||||
|  |   margin-top: 15px; | ||||||
| } | } | ||||||
| .upload-area { | .upload-area { | ||||||
|   width: 100%; |   width: 100%; | ||||||
|   height: 100%; |   height: 100%; | ||||||
|   border: 2px dashed blue; |   border: 2px dashed blue; | ||||||
|   border-radius: 8px; |   border-radius: 8px; | ||||||
|  |   display: flex; | ||||||
|  |   align-items: center; | ||||||
|  |   justify-content: center; | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   cursor: pointer; | ||||||
|  | } | ||||||
|  | .upload-area-text { | ||||||
|  |   font-size: 16px; | ||||||
| } | } | ||||||
|  |  | ||||||
| .btn-upload { | .btn-upload { | ||||||
|   | |||||||
| @@ -1,19 +1,48 @@ | |||||||
| import { Component } from 'react'; | import { Component } from 'react'; | ||||||
| import Dropzone from 'react-dropzone'; | import Dropzone from 'react-dropzone'; | ||||||
|  | import Spinner from '../components/Spinner/Spinner'; | ||||||
|  | import UploadUnit from '../components/UploadUnit/UploadUnit'; | ||||||
|  | import { post } from 'axios'; | ||||||
|  | import { apis } from '../helper/apis'; | ||||||
|  |  | ||||||
| import './upload.css'; | import './upload.css'; | ||||||
|  |  | ||||||
| export class UploadContainer extends Component { | export class UploadContainer extends Component { | ||||||
|   constructor(props) { |   constructor(props) { | ||||||
|     super(props); |     super(props); | ||||||
|     this.state = { |     this.state = { | ||||||
|       files: [], |       file: null, | ||||||
|       msg: "" |       msg: "", | ||||||
|  |       url: "", | ||||||
|  |       submitting: false | ||||||
|     }; |     }; | ||||||
|  |     this.handleChange = this.handleChange.bind(this); | ||||||
|  |     this.handleUploadError = this.handleUploadError.bind(this); | ||||||
|  |     this.handleSubmit = this.handleSubmit.bind(this); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   handleChange = files => { |   handleChange(files) { | ||||||
|     console.log(files); |     if (files.length > 0) | ||||||
|     this.setState({ files }); |       this.setState({ file: files[0] }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   handleUploadError(msg) { | ||||||
|  |     this.setState({ submitting: false }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   handleSubmit() { | ||||||
|  |     if ((this.state.msg === "" && this.file === null) || this.state.submitting) return; | ||||||
|  |     this.setState({ submitting: true }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   componentDidUpdate() { | ||||||
|  |     if (this.state.submitting && (!this.state.file || this.state.url !== "")) { | ||||||
|  |       // upload using axios | ||||||
|  |       post(apis.submitMessage, { content: this.state.msg, image: this.state.url }) | ||||||
|  |         .then(res => { | ||||||
|  |           this.setState({ submitting: false, msg: "", url: "" }); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   render() { |   render() { | ||||||
| @@ -36,26 +65,38 @@ export class UploadContainer extends Component { | |||||||
|               placeholder="留下您的寄语" |               placeholder="留下您的寄语" | ||||||
|               value={this.state.msg} |               value={this.state.msg} | ||||||
|               onChange={e => this.setState({ msg: e.target.value })} |               onChange={e => this.setState({ msg: e.target.value })} | ||||||
|  |               disabled={this.state.submitting} | ||||||
|             /> |             /> | ||||||
|           </div> |           </div> | ||||||
|           <div className="upload-box"> |           <div className="upload-box"> | ||||||
|             <Dropzone |             {this.state.file ? ( | ||||||
|               onDropAccepted={this.handleChange} |               <UploadUnit | ||||||
|               multiple={true} |                 file={this.state.file} | ||||||
|               accept="image/*" |                 onUpload={url => this.setState({ url })} | ||||||
|             > |                 onUploadError={this.handleUploadError} | ||||||
|               {({ getRootProps, getInputProps, isDragActive }) => ( |                 onCancel={() => this.setState({ file: null, url: "" })} | ||||||
|                 <div {...getRootProps()} className="upload-area"> |                 upload={this.state.submitting} | ||||||
|                   <input {...getInputProps()} /> |               /> | ||||||
|                   <p className="upload-area-text">拖拽</p> |             ) : ( | ||||||
|                 </div> |               <Dropzone | ||||||
|               )} |                 onDropAccepted={this.handleChange} | ||||||
|             </Dropzone> |                 multiple={false} | ||||||
|  |                 accept="image/*" | ||||||
|  |               > | ||||||
|  |                 {({ getRootProps, getInputProps, isDragActive }) => ( | ||||||
|  |                   <div {...getRootProps()} className="upload-area"> | ||||||
|  |                     <input {...getInputProps()} /> | ||||||
|  |                     <div className="upload-area-text">点击方框添加图片或者将图片拖入框内,支持jpg、png、gif,但均会被转换为jpg</div> | ||||||
|  |                   </div> | ||||||
|  |                 )} | ||||||
|  |               </Dropzone> | ||||||
|  |             )} | ||||||
|           </div> |           </div> | ||||||
|           <button |           <button | ||||||
|             className="btn btn-primary btn-upload" |             className="btn btn-primary btn-upload" | ||||||
|             onClick={this.handleUpload} |             onClick={this.handleSubmit} | ||||||
|           >提交</button> |             disabled={(this.state.msg === "" && this.state.file === null) || this.state.submitting} | ||||||
|  |           >{this.state.submitting ? (<Spinner />) : '提交'}</button> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user