添加了修改头像功能,顺便重新设计了用户组件
This commit is contained in:
父節點
1b0e9d3042
當前提交
2967009dfb
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">
|
||||
|
載入中…
x
新增問題並參考
Block a user