添加了修改头像功能,顺便重新设计了用户组件
This commit is contained in:
		
							
								
								
									
										13
									
								
								src/App.js
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/App.js
									
									
									
									
									
								
							| @@ -1,5 +1,5 @@ | ||||
| import './App.css'; | ||||
| import { Redirect, Route, SingleRouter } from './components/SingleRouter/SingleRouter'; | ||||
| import { Route, SingleRouter } from './components/SingleRouter/SingleRouter'; | ||||
| import { AppContainer } from './index/index'; | ||||
| import { UploadContainer } from './upload/upload'; | ||||
| import { LogInContainer } from './login/login'; | ||||
| @@ -12,13 +12,13 @@ class App extends Component { | ||||
|     super(props); | ||||
|     this.state = { | ||||
|       name: '', | ||||
|       role: -1 | ||||
|       role: 0 | ||||
|     }; | ||||
|     this.setUserData = this.setUserData.bind(this); | ||||
|   } | ||||
|  | ||||
|   setUserData({ name, role }) { | ||||
|     this.setState({ name, role }); | ||||
|   setUserData({ name, role, avatar }) { | ||||
|     this.setState({ name, role, avatar }); | ||||
|   } | ||||
|  | ||||
|   render() { | ||||
| @@ -29,15 +29,10 @@ class App extends Component { | ||||
|           <Route path="/upload" component={UploadContainer} /> | ||||
|           <Route path="/login" component={LogInContainer} /> | ||||
|           <Route path="/admin/review" component={ReviewContainer} /> | ||||
|           <Route component={CheckLogIn} /> | ||||
|         </SingleRouter> | ||||
|       </UserContext.Provider> | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function CheckLogIn() { | ||||
|   return localStorage.getItem('jwt') ? null : <Redirect from={/^\/.*/} to="/login" />; | ||||
| } | ||||
|  | ||||
| export default App; | ||||
|   | ||||
| @@ -67,7 +67,7 @@ export class ReviewContainer extends Component { | ||||
|             <img src={images.icon} className="sdu-logo" alt="logo" /> | ||||
|             <img src={images.name} className="sdu-name" alt="" /> | ||||
|           </div> | ||||
|           <UserControl /> | ||||
|           <UserControl pageAuthLevel={2} /> | ||||
|         </div> | ||||
|         <div className="content"> | ||||
|           { | ||||
|   | ||||
							
								
								
									
										122
									
								
								src/components/AvatarUnit/AvatarUnit.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								src/components/AvatarUnit/AvatarUnit.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | ||||
| .user-avatar { | ||||
|   width: 40px; | ||||
|   height: 40px; | ||||
|   position: relative; | ||||
| } | ||||
|  | ||||
| .user-avatar > img { | ||||
|   border-radius: 50%; | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   cursor: pointer; | ||||
| } | ||||
|  | ||||
| .user-avatar-edit { | ||||
|   border-radius: 8px; | ||||
|   background-color: #f7f7f7; | ||||
|   border: 1px solid #e5e5e5; | ||||
|   width: 160px; | ||||
|   height: 26px; | ||||
|   position: absolute; | ||||
|   margin-top: 10px; | ||||
|   left: -60px; | ||||
|   filter: opacity(0); | ||||
|   transition-property: width, height, filter; | ||||
|   transition-duration: .3s; | ||||
|   transition-timing-function: ease-out; | ||||
| } | ||||
|  | ||||
| .user-avatar-edit > .pointer { | ||||
|   position: absolute; | ||||
|   width: 20px; | ||||
|   height: 10px; | ||||
|   top: -10px; | ||||
|   left: 70px; | ||||
| } | ||||
| .user-avatar-edit > .pointer > svg { | ||||
|   position: absolute; | ||||
| } | ||||
|  | ||||
| .user-avatar-edit-tip { | ||||
|   width: 160px; | ||||
|   height: 26px; | ||||
|   line-height: 26px; | ||||
|   padding-left: 5px; | ||||
|   box-sizing: border-box; | ||||
|   z-index: 1; | ||||
| } | ||||
|  | ||||
| .user-avatar-edit.expand { | ||||
|   width: 160px; | ||||
|   height: 160px; | ||||
| } | ||||
|  | ||||
| .user-avatar-edit-tip, | ||||
| .avatar-upload > * { | ||||
|   position: absolute; | ||||
|   filter: opacity(0); | ||||
|   transition: filter .3s ease-out; | ||||
| } | ||||
| .user-avatar-edit-tip.show, | ||||
| .avatar-upload > .show, | ||||
| .user-avatar-edit.show { | ||||
|   filter: opacity(1); | ||||
| } | ||||
|  | ||||
| .user-avatar-edit-content { | ||||
|   overflow: hidden; | ||||
|   height: 100%; | ||||
| } | ||||
| .avatar-upload { | ||||
|   display: flex; | ||||
|   justify-content: center; | ||||
|   align-items: center; | ||||
|   height: 160px; | ||||
| } | ||||
| .avatar-upload .upload-unit-progress.show { | ||||
|   z-index: 2; | ||||
| } | ||||
| .avatar-upload .upload-unit-progress > .progress-detail { | ||||
|   color: black; | ||||
| } | ||||
| .upload-unit-progress > .icon { | ||||
|   width: 40px; | ||||
|   height: 40px; | ||||
| } | ||||
|  | ||||
| .avatar-preview { | ||||
|   width: 160px; | ||||
|   height: 160px; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   align-items: center; | ||||
|   background-size: cover; | ||||
|   background-position: center; | ||||
| } | ||||
| .avatar-preview.show { | ||||
|   z-index: 1; | ||||
| } | ||||
| .avatar-preview > .upload-btns { | ||||
|   margin: 0; | ||||
|   bottom: 10px; | ||||
|   position: absolute; | ||||
| } | ||||
|  | ||||
| .btn-close { | ||||
|   position: absolute; | ||||
|   right: 3px; | ||||
|   top: 3px; | ||||
|   z-index: 2; | ||||
|   width: 20px; | ||||
|   height: 20px; | ||||
|   display: flex; | ||||
|   justify-content: center; | ||||
|   align-items: center; | ||||
|   background-color: #e5e5e5bb; | ||||
|   border-radius: 4px; | ||||
|   backdrop-filter: blur(10px); | ||||
| } | ||||
| .btn-close > svg { | ||||
|   width: 16px; | ||||
|   height: 16px; | ||||
| } | ||||
							
								
								
									
										135
									
								
								src/components/AvatarUnit/AvatarUnit.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								src/components/AvatarUnit/AvatarUnit.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,135 @@ | ||||
| import { useState, useCallback, useRef } from "react"; | ||||
| import Spinner from '../Spinner/Spinner'; | ||||
| import './AvatarUnit.css'; | ||||
| import { multiFormPost } from '../../helper/axios'; | ||||
| import { alert } from '../../helper/alert'; | ||||
| import { apis } from "../../helper/apis"; | ||||
|  | ||||
| export default function AvatarUnit({ avatar, onChangeAvatar, showTip }) { | ||||
|   const [showBubble, setShowBubble] = useState(showTip); | ||||
|   const [showUpload, setShowUpload] = useState(false); | ||||
|   const [loading, setLoading] = useState(false); | ||||
|   const [loaded, setLoaded] = useState(false); | ||||
|   const [uploading, setUploading] = useState(false); | ||||
|   const [uploaded, setUploaded] = useState(false); | ||||
|   const [dataURL, setDataURL] = useState(null); | ||||
|   const [file, setFile] = useState(null); | ||||
|  | ||||
|   const inputRef = useRef(null); | ||||
|  | ||||
|   // useEffect(() => { | ||||
|   //   setShowBubble(!localStorage.getItem("avatarTipShown")); | ||||
|   //   localStorage.setItem("avatarTipShown", true); | ||||
|   // }, []); | ||||
|  | ||||
|   const handleClose = () => { | ||||
|     setShowBubble(false); | ||||
|     setShowUpload(false); | ||||
|     setLoading(false); | ||||
|     setLoaded(false); | ||||
|     setUploading(false); | ||||
|     setUploaded(false); | ||||
|     setFile(null); | ||||
|   }; | ||||
|  | ||||
|   const handleFileChange = useCallback(file => { | ||||
|     if (file) { | ||||
|       if (!["image/jpeg", "image/png", "image/gif"].includes(file.type)) { | ||||
|         alert('请不要上传jpg、png、gif格式以外的文件!'); | ||||
|         return; | ||||
|       } | ||||
|       setLoading(true); | ||||
|       setFile(file); | ||||
|       const reader = new FileReader(); | ||||
|       reader.onload = () => { | ||||
|         setDataURL(reader.result); | ||||
|         if (inputRef.current) { | ||||
|           inputRef.current.value = ""; | ||||
|         } | ||||
|         setLoaded(true); | ||||
|         setLoading(false); | ||||
|       }; | ||||
|       reader.readAsDataURL(file); | ||||
|     } | ||||
|   }, []); | ||||
|  | ||||
|   const handleCancel = useCallback(() => { | ||||
|     setLoaded(false); | ||||
|     setFile(null); | ||||
|   }, []); | ||||
|  | ||||
|   const handleUpload = useCallback(() => { | ||||
|     if (file) { | ||||
|       setUploading(true); | ||||
|       const formData = new FormData(); | ||||
|       formData.append("image", file); | ||||
|       multiFormPost(apis.uploadAvatar, formData).then(data => { | ||||
|         if (data.networkStatus === 200 && data.status) { | ||||
|           setUploaded(true); | ||||
|           setUploading(false); | ||||
|           setTimeout(() => { | ||||
|             handleClose(); | ||||
|             onChangeAvatar(data.data); | ||||
|           }, 800); | ||||
|         } | ||||
|         else { | ||||
|           alert(data.data); | ||||
|           setUploading(false); | ||||
|         } | ||||
|       }); | ||||
|     } | ||||
|   }, [file, onChangeAvatar]); | ||||
|  | ||||
|   return ( | ||||
|     <div className="user-avatar"> | ||||
|       <img src={avatar} alt="user avatar" onClick={() => { | ||||
|         setShowBubble(true); | ||||
|         setShowUpload(true); | ||||
|       }} /> | ||||
|       <div className={"user-avatar-edit" + (showUpload ? ' expand' : '') + (showBubble ? ' show' : '')}> | ||||
|         <div className="pointer"> | ||||
|           <svg viewBox="0 0 20 10"> | ||||
|             <path stroke="#e5e5e5" d="M0.112042+10.4465C1.43209+10.4306+4.24584+9.35904+5.0218+8.01502L6.77358+4.98086L8.52535+1.94671C9.30131+0.602693+10.5594+0.602693+11.3354+1.94671L13.0871+4.98086L14.8389+8.01502C15.6149+9.35904+18.4986+10.4864+20.0163+10.4757" fill="#f7f7f7"/> | ||||
|           </svg> | ||||
|         </div> | ||||
|         <button className="btn btn-close" onClick={handleClose}> | ||||
|           <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 className="user-avatar-edit-content"> | ||||
|           <span className={'user-avatar-edit-tip' + (showBubble && !showUpload ? ' show' : '')}>点击图标修改头像</span> | ||||
|           <div className="avatar-upload"> | ||||
|             <div className={"upload-unit-progress" + (uploaded ? ' show' : '')}> | ||||
|               <div className="icon"> | ||||
|                 <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="#47c74c"></path></svg> | ||||
|               </div> | ||||
|               <div className="progress-detail"> | ||||
|                 上传成功 | ||||
|               </div> | ||||
|             </div> | ||||
|             <div className={"upload-unit-progress" + (loading || uploading ? ' show' : '')}> | ||||
|               <Spinner isGray /> | ||||
|               <div className="progress-detail"> | ||||
|                 {loading ? '加载中' : uploading ? '上传中' : ''} | ||||
|               </div> | ||||
|             </div> | ||||
|             <div className={"avatar-preview" + (loaded && !uploading && !uploaded ? ' show' : '')} style={{ backgroundImage: `url(${dataURL})` }}> | ||||
|               <div className="upload-btns"> | ||||
|                 <button className="btn btn-circle btn-ok" onClick={handleUpload}> | ||||
|                   <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> | ||||
|                 <button className="btn btn-circle btn-gray" onClick={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> | ||||
|             </div> | ||||
|             <div className={"upload-image-btn" + (showUpload && !file ? ' show' : '')}> | ||||
|               <input type="file" accept="image/*" multiple={false} onChange={e => handleFileChange(e.target.files[0])} ref={inputRef} /> | ||||
|               <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 54"><path d="M60.342,102.091l5.331-8a2.249,2.249,0,0,1,1.872-1h18a2.246,2.246,0,0,1,1.872,1l5.332,8h7.094a6.72,6.72,0,0,1,6.7,6.7v31.6a6.722,6.722,0,0,1-6.7,6.7h-46.6a6.722,6.722,0,0,1-6.7-6.7v-31.6a6.72,6.72,0,0,1,6.7-6.7h7.094Zm5.407,0H87.341l-3-4.5H68.751l-3,4.5Zm10.8,35.25a12.75,12.75,0,1,0-12.75-12.75A12.764,12.764,0,0,0,76.545,137.341Zm0-21a8.25,8.25,0,1,1-8.25,8.25A8.261,8.261,0,0,1,76.545,116.341Z" transform="translate(-46.546 -93.091)" fill="#8A8A8A"/></svg> | ||||
|               <div className="upload-image-btn-text">添加图片</div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
| @@ -56,6 +56,8 @@ | ||||
|   color: white; | ||||
|   font-size: 16px; | ||||
|   margin-top: 10px; | ||||
|   width: 100px; | ||||
|   text-align: center; | ||||
| } | ||||
| .progress-circle { | ||||
|   width: 50px; | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| import { useEffect, useContext, Fragment } from 'react'; | ||||
|  | ||||
| import './userControl.css'; | ||||
| import { images } from '../../resources.json'; | ||||
| import { UserContext } from '../../helper/Context'; | ||||
| @@ -6,56 +8,89 @@ import { get } from '../../helper/axios'; | ||||
| import { apis } from '../../helper/apis'; | ||||
| import { alert } from '../../helper/alert'; | ||||
| import History from '../../helper/history'; | ||||
| import AvatarUnit from '../AvatarUnit/AvatarUnit'; | ||||
|  | ||||
| const exitContent = ['登录', '退出', '退出审核']; | ||||
| // page level: 0: everyone, 1: login needed, 2: admin only | ||||
| export default function UserControl({ pageAuthLevel, buttonOnly }) { | ||||
|   const { userData, setUserData } = useContext(UserContext); | ||||
|   useEffect(() => { | ||||
|     if (userData.role === -1) return; | ||||
|     if (userData.role === 0 && localStorage.getItem('jwt')) { | ||||
|       // 没有用户信息,但是有登录信息 | ||||
|       setUserData({ role: -1, name: '加载中' }); | ||||
|       get(apis.getProfile).then(({ data, status, networkStatus }) => { | ||||
|         if (networkStatus !== 200) return; | ||||
|         if (!status) return alert('获取用户信息失败:' + data + ',请稍候刷新再试'); | ||||
|         setUserData({ | ||||
|           name: data.realName, | ||||
|           role: data.role, | ||||
|           avatar: data.avatar, | ||||
|         }); | ||||
|       }); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (userData.role < pageAuthLevel) { | ||||
|       // 用户权限不足,依照页面权限跳转 | ||||
|       if (userData.role === 1) { | ||||
|         History.force('/'); | ||||
|         alert('您没有权限访问该页面,退出登录后可以使用有权限的账号登录'); | ||||
|         return; | ||||
|       } | ||||
|       History.force('/login'); | ||||
|     } | ||||
|  | ||||
|   }, [pageAuthLevel, setUserData, userData]); | ||||
|  | ||||
| export default function UserControl(props) { | ||||
|   return ( | ||||
|     <UserContext.Consumer> | ||||
|       {({ userData, setUserData }) => ( | ||||
|         userData.role === 2 | ||||
|         ? ( | ||||
|           <div className="user"> | ||||
|             <button | ||||
|               className="btn btn-hollow btn-straight" | ||||
|               onClick={() => { | ||||
|                 localStorage.setItem('jwt', ''); | ||||
|                 setUserData({ role: -1, name: '' }); | ||||
|                 History.force('/login'); | ||||
|               }} | ||||
|             >退出审核</button> | ||||
|           </div> | ||||
|         ) | ||||
|         : ( | ||||
|           <div className="user"> | ||||
|             { | ||||
|               userData.role === -1 | ||||
|               ? ( | ||||
|                 <Spinner /> | ||||
|               ) | ||||
|               : ( | ||||
|                 <div className="user-avatar"> | ||||
|                   <img src={images.avatar} alt="user avatar" /> | ||||
|                 </div> | ||||
|               ) | ||||
|             } | ||||
|             <div className="user-name">{userData.name || "加载中"}</div> | ||||
|             { | ||||
|               (() => { | ||||
|                 if (userData.role === 1 && History.getHref().match(/^\/admin.*/)) | ||||
|                   History.force('/'); | ||||
|                 if (userData.role !== -1 || !localStorage.getItem('jwt')) return null; | ||||
|                 get(apis.getProfile).then(({ data, status, networkStatus }) => { | ||||
|                   if (networkStatus !== 200) return; | ||||
|                   if (!status) return alert('获取用户信息失败:' + data + ',请稍候刷新再试'); | ||||
|                   setUserData({ | ||||
|                     name: data.realName, | ||||
|                     role: data.role | ||||
|                   }); | ||||
|                 }); | ||||
|                 return null; | ||||
|               })() | ||||
|             } | ||||
|           </div> | ||||
|         ) | ||||
|         <div className="user"> | ||||
|           {buttonOnly || ( | ||||
|             <Fragment> | ||||
|               { | ||||
|                 userData.role === -1 | ||||
|                 ? ( | ||||
|                   <Spinner isGray /> | ||||
|                 ) | ||||
|                 : userData.role > 0 && ( | ||||
|                   <AvatarUnit | ||||
|                     avatar={userData.avatar || images.avatar} | ||||
|                     onChangeAvatar={url => setUserData({ ...userData, avatar: url })} | ||||
|                     showTip={!userData.avatar} | ||||
|                   /> | ||||
|                 ) | ||||
|               } | ||||
|               { | ||||
|                 userData.role !== 0 && ( | ||||
|                   <div className="user-name">{userData.name}</div> | ||||
|                 ) | ||||
|               } | ||||
|             </Fragment> | ||||
|           )} | ||||
|           { | ||||
|             userData.role === pageAuthLevel | ||||
|             ? ( | ||||
|               <button | ||||
|                 className="btn btn-hollow btn-straight" | ||||
|                 onClick={() => { | ||||
|                   localStorage.setItem('jwt', ''); | ||||
|                   setUserData({ role: 0, name: '' }); | ||||
|                   History.force('/login'); | ||||
|                 }} | ||||
|               >{exitContent[pageAuthLevel]}</button> | ||||
|             ) | ||||
|             : userData.role === 2 && ( | ||||
|               <button | ||||
|                 className="btn btn-hollow btn-straight" | ||||
|                 onClick={() => { | ||||
|                   History.push('/admin/review'); | ||||
|                 }} | ||||
|               >进入审核</button> | ||||
|             ) | ||||
|           } | ||||
|         </div> | ||||
|       )} | ||||
|     </UserContext.Consumer> | ||||
|   ); | ||||
|   | ||||
| @@ -4,16 +4,5 @@ | ||||
|   height: 50px; | ||||
|   line-height: 50px; | ||||
|   color: white; | ||||
|   margin-left: 10px; | ||||
|   margin: 0 10px; | ||||
| } | ||||
|  | ||||
| .user-avatar { | ||||
|   width: 40px; | ||||
|   height: 40px; | ||||
| } | ||||
|  | ||||
| .user-avatar > img { | ||||
|   border-radius: 50%; | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
| } | ||||
| @@ -4,6 +4,7 @@ export const apis = { | ||||
|  | ||||
|   login: backEndBaseURL + "/user/login", | ||||
|   getProfile: backEndBaseURL + "/user/me", | ||||
|   uploadAvatar: backEndBaseURL + "/user/uploadAvatar", | ||||
|  | ||||
|   submitMessage: backEndBaseURL + "/post/submit", | ||||
|   listEssence: backEndBaseURL + "/post/listPublished?page=1", | ||||
|   | ||||
| @@ -31,6 +31,20 @@ export function post(url, data, evoker) { | ||||
|     } | ||||
|   ); | ||||
| } | ||||
| export function multiFormPost(url, data, evoker) { | ||||
|   return send( | ||||
|     axios.post(url, data, { | ||||
|       headers: { | ||||
|         Authorization: localStorage.getItem('jwt'), | ||||
|         "Allow-Control-Allow-Origin": "*" | ||||
|       } | ||||
|     }), | ||||
|     { | ||||
|       fetcher: () => multiFormPost(url, data, evoker), | ||||
|       identifier: 'mfPost:' + url + (evoker ? `@${evoker}` : '') | ||||
|     } | ||||
|   ); | ||||
| } | ||||
|  | ||||
| const waitToSend = []; | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| .poster { | ||||
|   width: 100%; | ||||
|   height: 400px; | ||||
|   background-color: #80010a; | ||||
|   background-color: #84000B; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   align-items: center; | ||||
| @@ -10,10 +10,15 @@ | ||||
| .img-poster { | ||||
|   height: 100%; | ||||
| } | ||||
| .btn-partIn { | ||||
| .index-btns { | ||||
|   position: absolute; | ||||
|   right: 40px; | ||||
|   bottom: 20px; | ||||
|   display: flex; | ||||
|   height: 40px; | ||||
| } | ||||
| .index-btns > .user { | ||||
|   margin-right: 10px; | ||||
| } | ||||
| /* 这里的卡片和分栏结构就比较接近bootstrap了,可惜视觉做的太古板,如果愿意的话改成Material Design就更好了 */ | ||||
| .split-lg > .card { | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import { Component } from 'react'; | ||||
| import { Link } from '../components/SingleRouter/SingleRouter'; | ||||
| import Spinner from '../components/Spinner/Spinner'; | ||||
| import UserControl from '../components/UserControl/UserControl'; | ||||
| import { alert } from '../helper/alert'; | ||||
| import { apis } from '../helper/apis'; | ||||
| import { get } from '../helper/axios'; | ||||
| @@ -38,7 +39,10 @@ export class AppContainer extends Component { | ||||
|       <div className="index-container"> | ||||
|         <div className="poster"> | ||||
|           <img src={images.poster} className="img-poster" alt="海报" /> | ||||
|           <Link to="/upload" className="btn btn-light btn-straight btn-partIn">点击参加</Link> | ||||
|           <div className='index-btns'> | ||||
|             <UserControl pageAuthLevel={1} buttonOnly /> | ||||
|             <Link to="/upload" className="btn btn-light btn-straight btn-partIn">点击参加</Link> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div className="content split-lg"> | ||||
|           <div className="card"> | ||||
|   | ||||
| @@ -63,7 +63,7 @@ export class UploadContainer extends Component { | ||||
|             <img src={images.icon} className="sdu-logo" alt="logo" /> | ||||
|             <img src={images.name} className="sdu-name" alt="" /> | ||||
|           </div> | ||||
|           <UserControl /> | ||||
|           <UserControl pageAuthLevel={1} /> | ||||
|         </div> | ||||
|         <div className="content"> | ||||
|           <div className="message-box"> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user