Merge pull request '更新' (#466) from durian/forgeplus-react:gitlink_server into gitlink_server

This commit is contained in:
tongChong 2022-10-28 16:26:28 +08:00
commit 0a19859bf7
17 changed files with 6003 additions and 4575 deletions

2
.gitignore vendored
View File

@ -5,6 +5,7 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
yarn.lock
package-lock.json
# Runtime data
pids
@ -86,3 +87,4 @@ typings/
.DS_Store
.idea/*

10016
package-lock.json generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 B

View File

@ -253,11 +253,11 @@ class Index extends Component {
newItem = ()=>{
return(
<ul>
<li onClick={()=>{this.props.history.push('/projects/deposit/new')}}>新建项目
{/* <CheckProfile {...this.props} sureFunc={()=>{this.props.history.push('/projects/deposit/new')}}>新建项目</CheckProfile> */}
<li >
<CheckProfile {...this.props} sureFunc={()=>{this.props.history.push('/projects/deposit/new')}}>新建项目</CheckProfile>
</li>
<li onClick={()=>{this.props.history.push('/projects/mirror/new')}}>导入项目
{/* <CheckProfile {...this.props} sureFunc={()=>{this.props.history.push('/projects/mirror/new')}}>导入项目</CheckProfile> */}
<li >
<CheckProfile {...this.props} sureFunc={()=>{this.props.history.push('/projects/mirror/new')}}>导入项目</CheckProfile>
</li>
</ul>
)

View File

@ -90,7 +90,7 @@ function Index(props){
<ul className="securityUl ul-border-buttom">
<li>个人信息</li>
<li className={pathname.indexOf("/settings/profile")>-1 ?"active":""}><Link to={`/settings/profile`}><i className="iconfont icon-gerenziliao mr5 font-14"></i><span className="text-shodow-bold">基本资料</span></Link></li>
<li className={pathname.indexOf("/settings/password")>-1 ?"active":""}><Link to={`/settings/password`}><i className="iconfont icon-xuanzhonganquanshezhi_icon mr5 font-14"></i><span className="text-shodow-bold">密码管理</span></Link></li>
<li className={pathname.indexOf("/settings/account")>-1 ?"active":""}><Link to={`/settings/account`}><i className="iconfont icon-xuanzhonganquanshezhi_icon mr5 font-14"></i><span className="text-shodow-bold">账号管理</span></Link></li>
</ul>
{notice_url && <ul className="securityUl ul-border-buttom">
<li>消息通知</li>
@ -136,7 +136,7 @@ function Index(props){
)}
></Route>
<Route
path="/settings/password"
path="/settings/account"
render={(p) => (
<Password {...props} {...p}/>
)}

View File

@ -61,11 +61,11 @@ function List(props){
)
const menu_new=(
<ul>
<li onClick={()=>{this.props.history.push(`/projects/deposit/new/${OIdentifier}`)}}>新建项目
{/* <CheckProfile {...props} sureFunc={()=>{props.history.push(`/projects/deposit/new/${OIdentifier}`)}}>新建项目</CheckProfile> */}
<li >
<CheckProfile {...props} sureFunc={()=>{props.history.push(`/projects/deposit/new/${OIdentifier}`)}}>新建项目</CheckProfile>
</li>
<li onClick={()=>{this.props.history.push(`/projects/mirror/new/${OIdentifier}`)}}>导入项目
{/* <CheckProfile {...props} sureFunc={()=>{props.history.push(`/projects/mirror/new/${OIdentifier}`)}}>导入项目</CheckProfile> */}
<li >
<CheckProfile {...props} sureFunc={()=>{props.history.push(`/projects/mirror/new/${OIdentifier}`)}}>导入项目</CheckProfile>
</li>
</ul>
)

View File

@ -91,9 +91,9 @@ class Read extends Component {
<p className="ant-upload-text">
拖动文件或<span className="color-blue">点击此处上传</span>
</p>
<p className="mt10">
{/* <p className="mt10">
暂仅支持文件格式不支持图片,excel等不可以txt读取的文件
</p>
</p> */}
<p className="mt10">
文件名请使用英文且不得超过{size}MB
</p>

View File

@ -115,11 +115,11 @@ class InfosUser extends Component {
newItem =()=> (
<ul>
<li onClick={()=>{this.props.history.push('/projects/deposit/new')}}>新建项目
{/* <CheckProfile {...this.props} sureFunc={()=>{this.props.history.push('/projects/deposit/new')}}>新建项目</CheckProfile> */}
<li >
<CheckProfile {...this.props} sureFunc={()=>{this.props.history.push('/projects/deposit/new')}}>新建项目</CheckProfile>
</li>
<li onClick={()=>{this.props.history.push('/projects/mirror/new')}}>导入项目
{/* <CheckProfile {...this.props} sureFunc={()=>{this.props.history.push('/projects/mirror/new')}}>导入项目</CheckProfile> */}
<li >
<CheckProfile {...this.props} sureFunc={()=>{this.props.history.push('/projects/mirror/new')}}>导入项目</CheckProfile>
</li>
{/* <li>
<CheckProfile {...this.props} sureFunc={()=>{this.props.history.push('/projects/deposit/new')}} >新建项目</CheckProfile>

View File

@ -8,8 +8,8 @@ import '../../SecuritySetting/notice/manager/Index.scss';
function Index(props){
const { pathname } = props && props.location;
const [ key , setKey ] = useState("0");
const [ type, setType] = useState('0');
useEffect(()=>{
if(pathname){
@ -24,24 +24,22 @@ function Index(props){
return(
<div className="notice01">
<div className="sshHead">
<Menu mode={'horizontal'} className="infosRightMenu">
{
key === "0" ?
<Menu.Item key="0" className="font-16">基本资料</Menu.Item>
:
<Menu.Item key="1" className="font-16">密码管理</Menu.Item>
}
</Menu>
<div className="sshHead personalInfo mb20">
{key === "0" ? <Menu mode={'horizontal'} className="infosRightMenu">
<Menu.Item key="0" className="font-16">基本资料</Menu.Item>
</Menu> : <Menu mode={'horizontal'} className="infosRightMenu" selectedKeys={type} onClick={(e)=>{setType(e.key)}}>
<Menu.Item key="0" className="font-16">邮箱管理</Menu.Item>
<Menu.Item key="1" className="font-16">密码管理</Menu.Item>
<Menu.Item key="2" className="font-16">账号注销</Menu.Item>
</Menu>}
</div>
<div style={{padding:"20px"}}>
<div>
{
key === "0" ?
<Base {...props}/>
:
<Password {...props}/>
<Password {...props} type={type}/>
}
</div>
</div>
)

View File

@ -27,4 +27,141 @@
color: #333333;
}
}
}
.personalInfo{
.ant-menu-item{
color:#333333;
line-height: 20px;
}
.ant-menu-item-selected{
color:#2a61ff;
}
}
.tipsBox{
height: 45px;
color:#ffa13a;
background-color:#fff5eb;
border:1px solid #ffa13a;
border-radius:2px;
padding: 7px 20px;
letter-spacing: 1.5px;
display: flex;
align-items: center;
&.emailBox{
background-image: url('../../Images/personalInfo2.png');
background-size: 100% 100%;
border: none;
color:#181818;
.email{color:#666666;}
}
.spanTil{
letter-spacing: normal;
}
}
.tipCont{
min-height: 330px;
background-image: url('../../Images/personalInfo1.png');
background-size: 100% 100%;
color: rgba(24, 24, 24, 1);
padding: 20px 23px;
.orange{
color: rgba(255, 161, 58, 1);
}
.oneTipBox{
color: rgba(102, 102, 102, 1);
display: block;
position: relative;
padding-left: 15px;
&::before{
content: '';
position: absolute;
top: 11px;
left: 0px;
width:6px;
height:6px;
background-color:#8f8f8f;
border-radius: 50%;
}
}
}
.updateEmail{
font-weight:700;
color:#181818;
position: relative;
padding-left: 8px;
&::before{
content: '';
position: absolute;
top: 7px;
left: 0px;
width:3px;
height:15px;
background-color:#466aff;
border-radius:2px;
}
}
.personalUpdateInfoForm{
.ant-input{
height: 38px;
}
.ant-row.ant-form-item{
margin-bottom: 20px;
}
.ant-form-item-control{
left: 12px;
}
.ant-form-item-label > label{
color: #181818;
}
.ant-form-explain{
position: relative;
margin-top: 10px;
color: #FF4C4C;
&::before{
content: '!';
width:18px;
height:18px;
border-radius: 50%;
display: inline-block;
line-height: 18px;
text-align: center;
font-size: 12px;
color: #FF4C4C;
background-color: #FFDADA;
margin-right: 8px;
}
}
.has-error .ant-input{
border-color: #ff4c4c;
background-color: rgba(102, 102, 102, 0) !important;
}
.has-error::after{
content: '\e6e9';
color: #FF4C4C;
font-size: 19px;
position: absolute;
right: -32px;
top: 0;
}
.hasSuccess .ant-form-item-control::after{
content: '\e892';
color: #1FD148;
font-size: 19px;
position: absolute;
right: -32px;
top: 0;
}
}
.getCaptchaBut{
height:38px;
border-color: #466aff;
color: #466aff;
&:hover {
border-color: #5d7cff;
color: #5d7cff;
}
&:focus {
border-color: #1140ff;
color: #1140ff;
}
}

View File

@ -1,102 +1,359 @@
import React , { forwardRef, useState } from 'react';
import { Form , Input , Button } from 'antd';
import React , { forwardRef, Fragment, useState } from 'react';
import { Form , Input , Button, message } from 'antd';
import { AlignCenter } from '../../Component/layout';
import Modals from '../../Component/PublicModal/Index';
import Axios from 'axios';
import img1 from '../../Images/personalInfo3.png';
import { setmiyah } from '../../../common/Component';
import { useEffect } from 'react';
export default Form.create()(
forwardRef((props)=>{
const { getFieldDecorator, validateFields , setFieldsValue } = props && props.form;
const { current_user} = props;
const { getFieldDecorator, validateFields, resetFields, getFieldsValue } = props && props.form;
const { current_user, type, resetUserInfo} = props;
const [ NewPass , setNewPass ] = useState(undefined);
const [ NewPassRepeat , setNewPassRepeat ] = useState(undefined);
const [ countDown, setCountDown] = useState(undefined);
const [ emailValue, setEmailValue] = useState(undefined);
const [ passMap, setPassMap] = useState({password: undefined, email: undefined, code: undefined});
const [ updatePsdMap, setUpdatePsdMap] = useState({oldPassword: undefined, password: undefined, password1: undefined});
const [ visible, setVisible] = useState(false);
useEffect(()=>{
setPassMap({password: undefined, email: undefined, code: undefined});
setUpdatePsdMap({oldPassword: undefined, password: undefined, password1: undefined});
}, type)
function submit() {
validateFields((error,values)=>{
if(!error){
submitFunc(values);
setVisible(true);
}
})
}
function submitFunc(values) {
const url = `/accounts/change_password.json`;
Axios.post(url,{
login:current_user && current_user.login,
...values
}).then(result=>{
if(result && result.data){
props.showNotification("密码重置成功!")
}
}).catch(error=>{})
}
//
var reg = /(?!.*\s)(?!^[\u4e00-\u9fa5]+$)^.{8,16}$/;
var reg = /(?!.*\s)(?!^[\u4e00-\u9fa5]+$)^.{8,16}$/;
function checkIdentifier(rule, value, callback , inputValue){
if(!value){
const map = updatePsdMap;
if (value && !inputValue && !reg.test(value)) {
map.password1 = undefined;
setUpdatePsdMap(map);
callback("请输入8-16位密码区分大小写、不能使用空格");
}else if (value && inputValue && value !== inputValue) {
map.password1 = undefined;
setUpdatePsdMap(map);
callback("两次输入的密码不一致");
}else{
map.password1 = value;
setUpdatePsdMap(map);
callback();
}
if (value && !inputValue && !reg.test(value)) {
callback("请输入8-16位密码区分大小写、不能使用空格");
}
if (value && inputValue && value !== inputValue) {
callback("两次输入的密码不一致");
}
callback();
}
}
function checkNewPass(rule, value, callback,newpassrepeat) {
if(!value){
const map = updatePsdMap;
if (!reg.test(value)) {
map.password = undefined;
setUpdatePsdMap(map);
callback("请输入8-16位密码区分大小写、不能使用空格");
}else if(newpassrepeat && newpassrepeat!==value){
map.password = undefined;
setUpdatePsdMap(map);
callback("两次输入的密码不一致");
}else{
map.password = value;
setUpdatePsdMap(map);
callback();
}
if (!reg.test(value)) {
callback("请输入8-16位密码区分大小写、不能使用空格");
}
if(value && newpassrepeat && newpassrepeat!==value){
callback("两次输入的密码不一致");
}
callback();
}
return(
<Form layout={'inline'} className="formBase passMan">
<Form.Item label="旧密码">
//
function getCaptcha() {
//
current_user && Axios.get(`/v1/${current_user.login}/send_email_vefify_code.json`,{params: {
code_type: 10,
email: emailValue,
smscode: setmiyah(emailValue)
}}).then(res=>{
if(res && !res.data.status){
message.success('发送成功')
}
})
//
startCountDown();
}
//
function startCountDown(){
let countDownNum = 60;
const timer = setInterval(() => {
countDownNum -= 1;
setCountDown(countDownNum);
if(countDownNum <= -1){
clearInterval(timer);
setCountDown(undefined)
}
}, 1000);
}
//
function checkPsd(rule, value, callback){
const map = type === '0' ? passMap : updatePsdMap;
if(value){
current_user && Axios.post(`/v1/${current_user.login}/check_password.json`,{
password: value
}).then(res=>{
if(res && !res.data.status){
if(type === '0'){
map.password = value;
setPassMap(map);
}else{
map.oldPassword = value;
setUpdatePsdMap(map);
}
callback();
}else{
if(type === '0'){
map.password = undefined;
setPassMap(map);
}else{
map.oldPassword = undefined;
setUpdatePsdMap(map);
}
callback(res.data.message);
}
})
}else{
if(type === '0'){
map.password = undefined;
setPassMap(map);
}else{
map.oldPassword = undefined;
setUpdatePsdMap(map);
}
callback();
}
}
//
function checkEmail(rule, value, callback){
const map = passMap;
if(value){
current_user && Axios.post(`/v1/${current_user.login}/check_email.json`,{
email: value
}).then(res=>{
if(res && !res.data.status){
map.email = value;
setPassMap(map);
setEmailValue(value);
callback();
}else{
setEmailValue(undefined);
map.email = undefined;
setPassMap(map);
callback(res.data.message);
}
})
}else{
setEmailValue(undefined);
map.email = undefined;
setPassMap(map);
callback();
}
}
//
function checkCode(rule, value, callback){
const map = passMap;
if(value){
current_user && value && value.length > 4 && Axios.post(`/v1/${current_user.login}/check_email_verify_code.json`,{
code_type: 10,
email: emailValue,
code: value
}).then(res=>{
if(res && !res.data.status){
map.code = value;
setPassMap(map);
callback();
}else{
map.code = undefined;
setPassMap(map);
callback(res.data.message);
}
})
}else{
map.code = undefined;
setPassMap(map);
callback();
}
}
//
function confirmUpdateEmail(e){
validateFields((error,values)=>{
if(!error){
setVisible(true);
}
})
}
function updateEmailOrPsd(){
if(type === '0'){
const {code, email, password1} = getFieldsValue();
if(code && email && password1){
current_user && Axios.patch(`/v1/${current_user.login}/update_email.json`,{
code, email, password: password1
}).then(res=>{
if(res && !res.data.status){
setPassMap({password: undefined, email: undefined, code: undefined});
resetFields();
resetUserInfo();
setVisible(false);
message.success('更新成功');
}else{
setVisible(false);
message.error(res.data.message || '更新失败');
}
})
}
}else{
const values = getFieldsValue();
const url = `/accounts/change_password.json`;
Axios.post(url,{
login:current_user && current_user.login,
...values
}).then(result=>{
if(result && result.data){
resetFields();
setUpdatePsdMap({oldPassword: undefined, password: undefined, password1: undefined});
setVisible(false);
setNewPass(undefined);
setNewPassRepeat(undefined);
message.success('密码重置成功!');
}
}).catch(error=>{})
}
}
// type: 0 1 2
return(
<Fragment>
{type === '0' ? <div>
<div className='tipsBox mb15'><img src={img1} alt="" width={16} className='mr10'/>更改邮箱后您在<span className='spanTil'>GitLink</span> 的登录账号接收通知的邮件地址将同步变更请谨慎更改!</div>
<div className='tipsBox mb20 emailBox'>现邮箱地址<span className='ml10 email spanTil'>{current_user && current_user.email}</span></div>
<div className='updateEmail mb20'>更改邮箱</div>
<Form layout={'inline'} className="formBase passMan personalUpdateInfoForm">
<Form.Item label="登录密码" className={`${passMap.password ? 'hasSuccess' : ''}`}>
{getFieldDecorator("password1",{
rules:[
{required:true,message:"请输入登录密码"},
{validator:(rule, value, callback)=>checkPsd(rule, value, callback)}
],
validateTrigger:"onBlur",
})(
<Input.Password placeholder="请输入登录密码" autoComplete={"new-password"} style={{width:"400px"}}/>
)}
</Form.Item>
<Form.Item label="新邮箱地址" className={`${passMap.email ? 'hasSuccess' : ''}`}>
{getFieldDecorator("email",{
rules:[
{required:true,message:"请输入新邮箱地址"},
{type: 'email',message: '请输入正确的邮箱格式'},
{validator:(rule, value, callback)=>checkEmail(rule, value, callback)}
],
validateTrigger:"onBlur",
})(
<Input placeholder="请输入新邮箱地址" style={{width:"400px"}} autoComplete={"off"}/>
)}
</Form.Item>
<div>
<Form.Item label="邮箱验证码" className={`${passMap.code ? 'hasSuccess' : ''}`}>
{getFieldDecorator("code",{
rules:[
{required:true,message:"请输入邮箱验证码"},
{validator:(rule, value, callback)=>checkCode(rule, value, callback)}
],
validateTrigger:"onBlur",
})(
<Input placeholder="请输入邮箱验证码" style={{width:"400px"}}/>
)}
</Form.Item>
{countDown ? <Button className='getCaptchaBut ml50' disabled>重发 ({countDown})</Button> : <Button className='getCaptchaBut ml50' onClick={getCaptcha} disabled={!emailValue}>获取验证码</Button>}
</div>
<AlignCenter style={{marginTop:"20px"}}>
<span className="ant-form-item-label mr10"></span>
<Button className={`${passMap.password && passMap.email && passMap.code ? 'but25' : ''}`} type={"primary"} onClick={confirmUpdateEmail} disabled={!(passMap.password && passMap.email && passMap.code)}>确认更改</Button>
</AlignCenter>
</Form>
</div> : type === '1' ? <Form layout={'inline'} className="formBase passMan personalUpdateInfoForm mt30">
<Form.Item label="旧密码" className={`${updatePsdMap.oldPassword ? 'hasSuccess' : ''}`}>
{getFieldDecorator("old_password",{
rules:[
{required:true,message:"请输入旧密码"}
]
{required:true,message:"请输入旧密码"},
{validator:(rule, value, callback)=>checkPsd(rule, value, callback)}
],
validateTrigger:"onBlur",
})(
<Input.Password placeholder="请输入旧密码" autoComplete={"new-password"} style={{width:"400px"}}/>
)}
</Form.Item>
<Form.Item label="新密码">
<Form.Item label="新密码" className={`${updatePsdMap.password ? 'hasSuccess' : ''}`}>
{getFieldDecorator("password",{
rules:[
{required:true,message:"请输入新密码"},
{validator:(rule, value, callback)=>checkNewPass(rule, value, callback,NewPassRepeat)}
]
{validator:(rule, value, callback)=>checkNewPass(rule, value, callback, NewPassRepeat)}
],
validateFirst: true,
validateTrigger:"onBlur",
})(
<Input.Password placeholder="请输入新密码" style={{width:"400px"}} onChange={(e)=>{setNewPass(e.target.value)}}/>
)}
</Form.Item>
<Form.Item label="确认新密码">
<Form.Item label="确认新密码" className={`${updatePsdMap.password1 ? 'hasSuccess' : ''}`}>
{getFieldDecorator("new_password_repeat",{
rules:[
{required:true,message:"请重新输入新密码"},
{validator:(rule, value, callback)=>checkIdentifier(rule, value, callback,NewPass)}
]
{validator:(rule, value, callback)=>checkIdentifier(rule, value, callback, NewPass)}
],
validateFirst: true,
validateTrigger:"onBlur",
})(
<Input.Password placeholder="请重新输入新密码" style={{width:"400px"}} onChange={(e)=>{setNewPassRepeat(e.target.value)}}/>
)}
</Form.Item>
<AlignCenter style={{marginTop:"20px"}}>
<span className="ant-form-item-label"></span>
<Button className="but25" type={"primary"} onClick={submit}>提交</Button>
<Button type={"default"} onClick={()=>props.history.push(`/${current_user && current_user.login}`)} className="ml20">取消</Button>
<span className="ant-form-item-label mr10"></span>
<Button className={`${updatePsdMap.oldPassword && updatePsdMap.password && updatePsdMap.password1 ? 'but25' : ''}`} type={"primary"} onClick={submit} disabled={!(updatePsdMap.oldPassword && updatePsdMap.password && updatePsdMap.password1)}>确认更改</Button>
</AlignCenter>
</Form>
)
})
</Form> : <div>
<div className='tipsBox mb15'><img src={img1} alt="" width={16} className='mr10'/>请您谨慎操作注销后帐号内所有数据都会被清空且无法恢复帐号!</div>
<div className='tipCont'>
GitLink目前提供邮件渠道帐号注销服务如需注销帐号请通过发送邮件注销申请邮件到 <span className='orange'>gitlink@ccf.org.cn</span><br/>
相关注意事项如下:<br/>
<span className='oneTipBox mt10'>必须使用要注销的 GitLink 帐号所绑定的邮箱地址发送邮件</span>
<span className='oneTipBox mt5'>发送注销申请邮件之前确认帐号下无创建/加入组织帐号名下无仓库信息</span>
<span className='oneTipBox mt5'>邮件标题: 注销 GitLink 帐号邮件正文请注明要注销帐号的邮箱和昵称</span>
<span className='oneTipBox mt5'>发送邮件后我们会积极处理请耐心等候我们的邮件回复</span>
</div>
</div>}
<Modals
visible={visible}
onCancel={()=>{setVisible(false)}}
title={type==='0'? '更改邮箱' : '更改密码'}
btn={
<div>
<Button size={'large'} onClick={()=>{setVisible(false)}}>取消</Button>
<Button size={"large"} type={"primary"} onClick={updateEmailOrPsd}>确认更改</Button>
</div>
}
>
<div className="desc">
<AlignCenter className="descMain">
<i className="iconfont icon-jinggao1 mr10 font-20 red"></i>确认更改{type === '0' ? '邮箱' : '密码'}</AlignCenter>
<p>{type === '0' ? '更改邮箱后您在GitLink的登录账号、接收通知的邮件地址将同步变更。请再次确认。' : '更改密码后您在GitLink的登录密码将同步变更。请再次确认。'}</p>
</div>
</Modals>
</Fragment>
)})
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -164,7 +164,7 @@ function SecondEdition({setValue}) {
<div className="highDoContent">
<div>
<img src={ImgS1} alt="" width="794px"/>
<div style={{marginBottom:'74px',marginTop:"25px",position:"relative"}}>
<div style={{marginBottom:'100px',marginTop:"25px",position:"relative"}}>
<img src={ImgS2} alt="" width="520px"/>
{/* <div className="highCodes">
{