Merge branch 'gitlink_server' of https://git.trustie.net/Gitlink/forgeplus-react into gitlink_server

This commit is contained in:
何童崇 2023-01-13 09:56:13 +08:00
commit 93ddee8c9e
27 changed files with 716 additions and 35 deletions

View File

@ -1080,7 +1080,20 @@ a.shixun-task-btn {
word-wrap: break-word;
word-break: break-word;
}
.markdown_anchors{
position: relative;
}
.markdown_anchors:hover .anchors{
display: inline-block;
}
.markdown_anchors .anchors:hover{
text-decoration: none;
}
.markdown_anchors .anchors {
color: inherit;
margin-left: -14px;
display: none;
}
.markdown code {
padding: 0;
line-height: 23px;

View File

@ -102,11 +102,16 @@ const Home = Loadable({
loading: Loading,
})
const Relaction = Loadable({
loader: () => import("./forge/Account/Index"),
loading: Loading,
});
const LoginRegisterPage = Loadable({
loader: () => import("./modules/loginRegister/LoginRegisterPage"),
loading: Loading,
});
const AboutUs = Loadable({
loader: () => import("./forge/AboutUs/AboutUs"),
loading: Loading,
@ -131,7 +136,7 @@ const WebIDE = Loadable({
// })
// 此处仅维护前端可能的一级路由,不用进行项目或者组织判断的字段。
const keyWord = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize", "login", "register", "resetPassword", "aboutus","educoder", "glcc"];
const keyWord = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize", "login", "register", "resetPassword", "aboutus","educoder", "glcc","bindlogin"];
class App extends Component {
constructor(props) {
@ -154,7 +159,7 @@ class App extends Component {
const login = cookie.load("login");
let path = this.props.history.location.pathname;
if(login && (path === "/login" || path === "/register" ||path === "/resetPassword" )){
if(login && (path === "/login" || path === "/register" || path === "/resetPassword" || path.indexOf("/bindlogin") >-1 )){
this.props.history.push(`/${login}`);
}
// 添加路由监听,决定组织还是个人
@ -371,6 +376,11 @@ class App extends Component {
)
}
/>
{/* 关联账号 */}
<Route
path="/bindlogin/:type"
render={(props) =><Relaction {...this.props} {...props} mygetHelmetapi={mygetHelmetapi}/>}
></Route>
{/* 登录 */}
<Route

View File

@ -2,7 +2,6 @@ import axios from 'axios';
import { requestProxy } from "./indexEduplus2RequestProxy";
import { broadcastChannelOnmessage, isDev, queryString } from 'educoder';
import { notification } from 'antd';
import cookie from 'react-cookies';
import './index.css';
@ -17,7 +16,7 @@ function locationurl(list) {
}
}
// TODO 开发期多个身份切换
let debugType = "";
let debugType = ""
if (isDev) {
const _search = window.location.search;
let parsed = {};

View File

@ -155,7 +155,7 @@ renderer.heading = function (text, level, raw) {
level: level,
text: text
})
return '<h' + level + ' id="' + anchor + '">' + text + '</h' + level + '>'
return '<h' + level + ' id="' + anchor + '" class="markdown_anchors"><a href="#'+anchor+'" class="anchors"><i class="iconfont icon-lianjieicon font-14"></i></a>' + text + '</h' + level + '>'
}
marked.setOptions({
silent: true,

View File

@ -31,6 +31,7 @@ export default ({
rs = rs.replace("<p>[TOC]</p>", getTocContent())
cleanToc()
}
// emoji
let matchStr = str.match(strRegexSub);
if(matchStr && matchStr.length>0){
for(var i=0;i < matchStr.length;i++){

View File

@ -0,0 +1,38 @@
import React , { useState } from 'react';
import './index.scss';
import logo from './image/logo.png';
import { Menu } from 'antd';
import Bind from './bind';
import UnBind from './unbind';
function Index(props){
const [ key , setKey ] = useState("0");
// menu
function chooseMenuFunc(e){
setKey(e.key)
}
return(
<div className={"bodies"}>
<div className="logo">
<img src={logo} alt="" height="48px" />
</div>
<div className={"content"}>
<p>关联账号</p>
<div className="panels">
<Menu selectedKeys={[key]} mode={`horizontal`} className="panelsMenu" onClick={chooseMenuFunc}>
<Menu.Item key="0">已有账号进行绑定</Menu.Item>
<Menu.Item key="1">无账号注册并绑定</Menu.Item>
</Menu>
{
key === "0" ?
<Bind {...props}/>
:
<UnBind {...props}/>
}
</div>
</div>
</div>
)
}
export default Index;

103
src/forge/Account/bind.jsx Normal file
View File

@ -0,0 +1,103 @@
import React ,{ useState } from 'react';
import { Form , Input , Button } from 'antd';
import phone from './image/phone.svg';
import lock from "./image/lock.png";
import axios from 'axios';
import cookie from 'react-cookies';
const phonereg = /^([1][3456789])\d{9}$/
const emailreg = /^[a-zA-Z0-9]+([.\-_\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/
function Bind(props){
const { form } = props;
const { getFieldDecorator , getFieldsValue } = form;
const [ bindFlag , setBindFlag] = useState(true);
const type = props.match.params.type;
//
function bindloginFunc(){
const {username, password } = getFieldsValue();
if(username && password){
var url = '/bind_user.json';
axios.post(url,{
username,password,type
}).then(result=>{
if(result && result.data.status === 0){
cookie.save("supplyphone",true);
cookie.save("login",result.data.login);
window.location.href = "/"+result.data.login;
}else{
props.showNotification(result.data.message);
}
})
}
}
function accountConfirm(r,v,c){
if(v){
const {password } = getFieldsValue();
if(!(phonereg.test(v) || emailreg.test(v))){
setBindFlag(true);
c(`请输入正确的手机号或邮箱地址`);
}else if(password){
setBindFlag(false);
}
}else{
c();
setBindFlag(true);
}
}
function psdConfirm(r,v,c){
if(v){
const { username } = getFieldsValue();
if(phonereg.test(username) || emailreg.test(username)){
setBindFlag(false);
}else{
setBindFlag(true);
}
}else{
c();
setBindFlag(true);
}
}
return(
<div>
<Form className="bind_form">
<Form.Item>
{getFieldDecorator('username',{
rules:[
{
required:true,
message:"请输入手机号或邮箱地址"
},
{
validator: (rule, value, callback) => { accountConfirm(rule, value, callback) }
}
],
validateTrigger:"onInput",
})(<Input className="account" addonBefore={<img src={phone} alt="" width="13px" />} size="large" placeholder="请输入手机号或邮箱地址"/>)}
</Form.Item>
<Form.Item>
{getFieldDecorator('password', {
rules: [
{
required:true,
message:"请输入登录密码"
},
{
validator: (rule, value, callback) => { psdConfirm(rule, value, callback) }
}
],
validateTrigger:"onInput",
})(
<Input.Password autoComplete="new-password" addonBefore={<img src={lock} alt="" width="13px" />} className="psd" size="large" placeholder="请输入登录密码" />,
)}
</Form.Item>
<Button type="primary" disabled={bindFlag} size="large" style={{width:"100%",marginTop:"40px"}} onClick={bindloginFunc}>
绑定并登录
</Button>
</Form>
</div>
)
}
export default Form.create({ name: 'Bind' })(Bind);

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="10.845" height="14.46" viewBox="0 0 10.845 14.46">
<path id="手机号" d="M129.807.994a.818.818,0,0,0-.813.813V12.652a.818.818,0,0,0,.813.813h7.23a.818.818,0,0,0,.813-.813V1.807a.818.818,0,0,0-.813-.813Zm0-.994h7.23a1.807,1.807,0,0,1,1.807,1.807V12.652a1.807,1.807,0,0,1-1.807,1.807h-7.23A1.807,1.807,0,0,1,128,12.652V1.807A1.807,1.807,0,0,1,129.807,0Zm2.35,10.845h2.53a.542.542,0,0,1,0,1.084h-2.53a.542.542,0,0,1,0-1.084Zm0,0" transform="translate(-128)" fill="#b6becc"/>
</svg>

After

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

View File

@ -0,0 +1,85 @@
.bodies{
height: 100vh;
width: 100%;
background-color: rgba(245, 248, 255, 1);
.logo{
width: 100%;
background-color: #fff;
padding:7px 26px;
}
.content{
padding-top: 50px;
margin:0px auto;
width: 570px;
&>p{
height:42px;
line-height:42px;
font-weight:400;
color:#000000;
font-size:30px;
margin-bottom: 32px!important;
text-align: center;
}
.panels{
background-color: #fff;
border-radius:6.6px;
padding:32px 42px;
.panelsMenu{
border-bottom: none!important;
margin-bottom: 36px!important;
.ant-menu-item{
padding: 0px;
margin-right: 60px!important;
}
}
.bind_form{
padding-bottom: 48px;
.has-error .ant-input:focus,.has-error .ant-input, .has-error .ant-input:hover{
border-color: #ff4d4f!important;
}
.ant-btn-primary[disabled]{
background-color:rgba(70, 106, 255,0.55);
color: #fff;
border:none
}
.ant-input-group-addon{
background-color: transparent;
}
.ant-input{
border-left: none;
background-color: #fff!important;
}
.ant-input:focus,.ant-input:hover{
border-right: 1px solid #ddd!important;
border-color: #ddd!important;
border-left: none!important;
}
.codeBut{
border:1px solid rgba(70, 106, 255, 1)!important;
span{
color: rgba(70, 106, 255, 1);
}
}
.codeBut.disable{
border:1px solid #ddd!important;
span{
color: #ddd;
}
}
.login_register_head{
display: flex;
justify-content: space-between;
align-items: center;
&>span:first-child{
font-size: 1.5em;
font-weight: 600;
color: #000000;
line-height: 1.5;
}
//注册页面的获取验证码输入框
.ant-btn{margin-left: 15px; }
}
}
}
}
}

View File

@ -0,0 +1,320 @@
import React ,{ useState , useRef } from 'react';
import { Form , Input , Button , message } from 'antd';
import user from "./image/user.png";
import lock from "./image/lock.png";
import shield from './image/shield.png';
import phone from './image/phone.svg';
import axios from 'axios';
import cookie from 'react-cookies';
import { setmiyah } from 'educoder';
const phonereg = /^([1][3456789])\d{9}$/
const emailreg = /^[a-zA-Z0-9]+([.\-_\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/
function Bind(props){
const { form } = props;
const seconds = useRef();
let interval = undefined;
const { getFieldDecorator , getFieldsValue } = form;
const [ bindFlag , setBindFlag] = useState(true);
const [ captchaFlag , setCaptchaFlag ] = useState(true);
const [secondsStr, setSecondsStr] = useState(60);
const [ getCaptchaBut , setGetCaptchaBut ] = useState(true);
const type = props.match.params.type;
//username
function usernameConfirm(rule, value, callback){
value ? axios.post(`/accounts/check.json`, {
value: value,
type: 1
}).then(response => {
if(response){
if (response.data.status === -1) {
callback('该名称已经被使用');
} else {
const { captcha , password , confirm_password,username} = getFieldsValue();
if( captcha && password && confirm_password && (password === confirm_password) &&(phonereg.test(username) || emailreg.test(username)) ){
setBindFlag(false);
}
}
}
callback();
}):callback();
}
function accountConfirm(rule, value, callback){
if(value){
const { captcha , password , confirm_password,register_username} = getFieldsValue();
if(!(phonereg.test(value) || emailreg.test(value))){
setCaptchaFlag(true);
callback(`请输入正确的手机号或邮箱地址`);
}else{
axios.get(`/accounts/valid_email_and_phone.json`, {
params: {
login: value,
type: 1
}
}).then(response => {
if(response){
if (response.data.status === -2) {
callback(response.data.message);
} else {
setCaptchaFlag(false);
if(captcha && password && confirm_password && (password === confirm_password) && register_username){
setBindFlag(false);
}
}
callback();
}
})
}
}else{
setBindFlag(true);
callback();
}
}
function psdConfirm(r,v,c){
if(!v){
setBindFlag(true);
c('请输入登录密码');
}else if(/(?!.*\s)(?!^[\u4e00-\u9fa5]+$)^.{8,16}$/.test(v)){
const { username , captcha , confirm_password , register_username } = getFieldsValue();
if(register_username && (phonereg.test(username) || emailreg.test(username)) && captcha && confirm_password && (v=== confirm_password) ){
setBindFlag(false);
}else{
setBindFlag(true);
}
c()
}else{
if(v.length<8 || v.length>16){
setBindFlag(true);
c('密码长度为8-16个字符');
}else{
setBindFlag(true);
c('密码不能使用空格');
}
}
}
function captchaConfirm(r,v,c){
if(v){
const { username , password , confirm_password , register_username } = getFieldsValue();
if(register_username && (phonereg.test(username) || emailreg.test(username)) && password && confirm_password && (password ===confirm_password) ){
setBindFlag(false);
}else{
setBindFlag(true);
}
c();
}else{
c();
setBindFlag(true);
}
}
function repsdConfirm(r,v,c){
if(!v){
setBindFlag(true);
c("请重复输入登录密码");
}else if(/(?!.*\s)(?!^[\u4e00-\u9fa5]+$)^.{8,16}$/.test(v)){
const { username , password , register_username , captcha} = getFieldsValue();
if(v !== password){
c("两次输入的密码不一样");
}else if((phonereg.test(username) || emailreg.test(username)) && password && register_username && captcha){
setBindFlag(false);
}else{
setBindFlag(true);
}
c();
}else{
if(v.length<8 || v.length>16){
setBindFlag(true);
c('密码长度为8-16个字符');
}else{
setBindFlag(true);
c('密码不能使用空格');
}
}
if(v){
const { username , password , register_username , captcha} = getFieldsValue();
if(v !== password){
c("两次输入的密码不一样");
}else{
c();
}
if((phonereg.test(username) || emailreg.test(username)) && password && register_username && captcha){
setBindFlag(false);
}else{
setBindFlag(true);
}
c();
}else{
c();
}
}
//
function getCaptcha() {
const { username }= getFieldsValue();
if (username) {
//
setCaptchaFlag(true);
setGetCaptchaBut(false);
seconds.current = 60;
setSecondsStr(60);
!interval && clearInterval(interval);
interval = setInterval(() => {
if (seconds.current > 1) {
let oldSeconds = seconds.current;
seconds.current = oldSeconds - 1;
setSecondsStr(oldSeconds - 1);
} else {
clearInterval(interval);
//->
setCaptchaFlag(false);
setGetCaptchaBut(true);
}
}, 1000)
//
axios.get(`/accounts/get_verification_code.json`, {
params: {
login: username,
type: 1,
smscode: setmiyah(username),
}
}).then(response => {
if (response.data && response.data.status === 0) {
//
setGetCaptchaBut(false);
}
})
}
}
//
function bindRegFunc(){
form.validateFields((err, values) => {
if (!err) {
var url = '/accounts/register.json';
axios.post(url,{
namespace: values.register_username,
login: values.username,
password: values.password,
password_confirmation: values.confirm_password,
code: values.captcha,
type
}).then(response=>{
if(response.data && response.data.status === -6){
//
form.setFields({captcha: {value:values.captcha,errors:[new Error('验证码错误,请重新输入')]}});
}else if(response.data && response.data.status === 0){
//forge
cookie.save("supplyphone",true);
cookie.save("login",response.data.login);
window.location.href = "/"+values.register_username;
}
})
}
})
}
return(
<div>
<Form className="bind_form">
<Form.Item>
{getFieldDecorator('register_username',{
rules:[
{
required:true,
message:"请输入用户名"
},
{
pattern: /^[a-zA-Z]/,
message: "用户名必须以字母开头"
},
{
pattern: /[a-zA-Z0-9]$/,
message: "用户名只能使用英文字母和数字"
},
{
min: 4,
max: 15,
message: "用户名长度为4到15个字符"
},
{
validator: (rule, value, callback) => { usernameConfirm(rule, value, callback) }
}
],
validateTrigger:"onBlur",
validateFirst: true,
})(<Input placeholder="请输入4-15位用户名以字母开头只能使用字母和数字" addonBefore={<img src={user} alt="" width="13px" />} size="large" autoComplete="off"/>)}
</Form.Item>
<Form.Item>
{getFieldDecorator('username',{
rules:[
{
required:true,
message:"请输入手机号或邮箱地址"
},
{
validator: (rule, value, callback) => { accountConfirm(rule, value, callback) }
}
],
validateTrigger:"onBlur",
validateFirst: true,
})(<Input className="account" addonBefore={<img src={phone} alt="" width="13px" />} size="large" placeholder="请输入手机号或邮箱地址"/>)}
</Form.Item>
<Form.Item>
<div className="login_register_head">
{getFieldDecorator('captcha', {
rules: [{
required: true,
message: "请输入验证码"
},
{
validator: (rule, value, callback) => { captchaConfirm(rule, value, callback) }
}],
validateTrigger: "onInput",
})(
<Input className="captcha" size="large" addonBefore={<img src={shield} alt="" width="13px" />} placeholder="请输入验证码" autoComplete="off"/>
)}
<Button type="primary" ghost size="large" className={captchaFlag ? 'codeBut disable':'codeBut'} disabled={captchaFlag} onClick={getCaptcha}>{getCaptchaBut ? "获取验证码":`重发(${secondsStr}s)`}</Button>
</div>
</Form.Item>
<Form.Item>
{getFieldDecorator('password', {
rules: [
{
required:true,
message:"请输入登录密码"
},
{
validator: (rule, value, callback) => { psdConfirm(rule, value, callback) }
}
],
validateTrigger:"onInput",
})(
<Input.Password autoComplete="new-password" addonBefore={<img src={lock} alt="" width="13px" />} className="psd" size="large" placeholder="请输入登录密码" />,
)}
</Form.Item>
<Form.Item>
{getFieldDecorator('confirm_password', {
rules: [
{
required:true,
message:"请再次输入登录密码"
},
{
validator: (rule, value, callback) => { repsdConfirm(rule, value, callback) }
}
],
validateTrigger:"onInput",
})(
<Input.Password autoComplete="new-password" addonBefore={<img src={lock} alt="" width="13px" />} className="psd" size="large" placeholder="请确认登录密码" />,
)}
</Form.Item>
<Button type="primary" disabled={bindFlag} size="large" style={{width:"100%",marginTop:"40px"}} onClick={bindRegFunc}>
注册并登录
</Button>
</Form>
</div>
)
}
export default Form.create({ name: 'Bind' })(Bind);

View File

@ -1,5 +1,6 @@
import React , { Component } from 'react';
import { Dropdown , Menu , Icon , Pagination , Spin } from 'antd';
import { Dropdown , Menu , Icon , Pagination , Spin , Tooltip } from 'antd';
import { getImageUrl } from 'educoder';
import '../css/index.scss';
import '../Branch/branch.scss';
import './activity.css';
@ -49,6 +50,8 @@ class Activity extends Component{
data:undefined,
project_trends:undefined,
codeStatus:undefined,
isSpin:false
}
}
@ -59,6 +62,19 @@ class Activity extends Component{
isSpin:true
})
this.getInfo(time,type,status,page);
this.getCodeInfo();
}
getCodeInfo(){
const { projectsId , owner } = this.props.match.params;
const url = `/v1/${owner}/${projectsId}/code_stats.json`;
axios.get(url).then(result=>{
if(result){
this.setState({
codeStatus:result.data
})
}
}).catch(error=>{})
}
componentDidUpdate(){
@ -140,7 +156,7 @@ class Activity extends Component{
render(){
const { time , data , page , project_trends , isSpin ,
pr_count , new_pr_count , close_issues_count , open_issues_count , pr_all_count ,issues_count,
type,status } = this.state;
type,status , codeStatus } = this.state;
let name = time ? ARRAY.filter(item=>item.id === parseInt(time)) :[{name:"全部"}];
const first_per = pr_all_count > 0 ? `${parseFloat(pr_count/pr_all_count).toFixed(2)*100}%` :"50%";
@ -148,6 +164,7 @@ class Activity extends Component{
const third_per =issues_count > 0 ?`${parseFloat(close_issues_count/issues_count).toFixed(2)*100}%` :"50%";
const fourth_per =issues_count > 0 ?`${parseFloat(open_issues_count/issues_count).toFixed(2)*100}%` :"50%";
const {projectDetail} = this.props;
return(
<div className="main mt20">
@ -187,6 +204,41 @@ class Activity extends Component{
<span className={type==="Issue"&& status==="not_delay" ?`change active`:"change"} onClick={()=>this.changeTrends("Issue","not_delay")}>未处理的疑修</span>
</li>
</ul>
{
codeStatus &&
<div className="prMsg">
<div>排除合并
<span className="fontbold">{codeStatus.author_count} 作者</span> <span className="fontbold">{codeStatus.commit_count} </span>
{codeStatus.commit_count_in_all_branches && codeStatus.commit_count_in_all_branches>0?<span> {projectDetail && projectDetail.default_branch} <span>{codeStatus.commit_count_in_all_branches} 提交</span> </span>:""} {projectDetail && projectDetail.default_branch}
{codeStatus.change_files && codeStatus.change_files >0 ? <span><span className="fontbold">{codeStatus.change_files} 文件</span> </span>:""}
{codeStatus.additions && codeStatus.additions >0 ?<span><span className="fontbold greencount">新增 {codeStatus.additions} </span></span>:""}
{(codeStatus.additions && codeStatus.additions >0) &&(codeStatus.deletions && codeStatus.deletions >0) ?<span> </span>:""}
{codeStatus.deletions && codeStatus.deletions >0 ?<span><span className="fontbold redcount"> 删除 {codeStatus.deletions} </span></span>:""}.</div>
{
codeStatus.authors && codeStatus.authors.length > 0 &&
<ul className="prAuthor">
{
codeStatus.authors.map((i,v)=>{
return(
i.author ?
!i.author.id ?
<span>
<img src={getImageUrl(i.author && i.author.image_url)} width="38px" alt=""/>
<Tooltip title={i.author && i.author.name} placement="bottom"><span>{i.author && i.author.name}</span></Tooltip>
</span>
:
<a href={i.author.login ? `/${i.author.login}` : `mailto:${i.author.email}`}>
<img src={getImageUrl(i.author && i.author.image_url)} width="38px" alt=""/>
<Tooltip title={i.author && i.author.name} placement="bottom"><span>{i.author && i.author.name}</span></Tooltip>
</a>
:""
)
})
}
</ul>
}
</div>
}
</div>
<div className="df trendsTop mt20">
<div className="branchDropdown f-wrap-alignCenter">

View File

@ -60,6 +60,48 @@
.percentBox > li:last-child{
border-right: none;
}
.prMsg{
padding:0px 20px;
border-top: 1px solid #f4f4f4;
}
.prMsg > div{
padding:20px 20px 20px 0px;
line-height: 22px;
border-right:1px solid #f4f4f4;
width: 50%;
}
.prMsg > div span.fontbold{
font-weight: 500;
}
.prMsg > div span.fontbold.greencount{
color: green;
}
.prMsg > div span.fontbold.redcount{
color: red;
}
.prAuthor{
padding-bottom: 20px;
display: flex;
border-right:1px solid #f4f4f4;
width: 50%;
flex-wrap: wrap;
}
.prAuthor a,.prAuthor> span{
display: flex;
flex-direction: column;
align-items: center;
width: 78px;
}
.prAuthor a img,.prAuthor> span img{
border-radius: 50%;
}
.prAuthor a span,.prAuthor> span span{
display: block;
max-width: 78px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.activity_list .activity_item{
border-bottom: 1px solid #f4f4f4;
padding:15px 0px;

View File

@ -19,7 +19,6 @@ function CoderDepotReadme({ operate , history , readme , ChangeFile }){
},[readme])
useEffect(()=>{
let path = history.location.pathname;
const items = $.map($("#readme").find("h1,h2,h3,h4,h5,h6"), function (el, _) {
const anchor = el.id;
const level = el.tagName.replace("H", "");

View File

@ -1,5 +1,5 @@
import React , { Component } from 'react';
import { Spin , Pagination, Timeline } from 'antd';
import { Spin , Pagination, Timeline , Tooltip } from 'antd';
import { getImageUrl , returnbar } from 'educoder';
import { truncateCommitId ,timeFormat } from '../common/util';
import { AlignTop } from '../Component/layout';
@ -200,7 +200,9 @@ class CoderRootCommit extends Component{
</span>
<CopyTool beforeText="复制commit id" afterText="复制成功" inputId={`value${k}`}/>
</div>
<button className="btn-83" onClick={()=>{window.location.href=`/${owner}/${projectsId}/tree/${truncateCommitId(item.sha)}`}}>浏览文件</button>
<Tooltip title="浏览该提交对应历史版本的所有文件">
<button className="btn-83" onClick={()=>{window.location.href=`/${owner}/${projectsId}/tree/${truncateCommitId(item.sha)}`}}>浏览文件</button>
</Tooltip>
</div>
</div>
</div>

View File

@ -521,7 +521,9 @@
}
}
}
.readmeFile{
overflow: inherit;
}
.readmeFile p{
white-space: normal;
}

View File

@ -260,9 +260,8 @@ export default Form.create()(
<div className="infosTip">
<p className="font-16 mb14 weight">附件大小说明</p>
<p>
单个附件不能超过 100MGVP 项目200M每个仓库总附件不可超过
1G推荐项目不可超过 5GGVP 项目不可超过
20G附件总容量统计包括仓库附件和发行版附件
单个附件不能超过 100M每个仓库总附件不可超过
1G附件总容量统计包括仓库附件和发行版附件
</p>
</div>
</div>

View File

@ -635,7 +635,7 @@ class Index extends Component {
</div>
<Form.Item className="formTip mt20">
<Button type="primary" onClick={this.subMitFrom} className="mr20">{projectsType && projectsType === "mirror" ? "导入" : "创建"}项目</Button>
<Link to={'/explore'} className="btn_32">取消</Link>
<a onClick={()=>window.history.back(-1)} className="btn_32">取消</a>
</Form.Item>
</div>
</Form>

View File

@ -1,4 +1,4 @@
import React, { Component, useRef } from "react";
import React, { Component } from "react";
import { Link } from "react-router-dom";
import axios from "axios";

View File

@ -1,6 +1,30 @@
ul,ol,dl{
margin-bottom: 0px;
}
.markdown-body{
overflow: inherit;
}
.markdown-body a{
color: #6e90ff;
}
.markdown_anchors{
position: relative;
}
.markdown_anchors:hover {
.anchors{
display: inline-block;
}
}
.markdown_anchors {
.anchors:hover{
text-decoration: none;
}
}
.markdown_anchors .anchors {
color: inherit;
margin-left: -14px;
display: none;
}
.newMain{
background-color: #fff;
}

View File

@ -142,8 +142,6 @@ class Infos extends Component {
});
const { current_user } = this.props;
const { username } = this.props.match.params;
const { pathname } = this.props.location;
const { notice } = this.state;
let url = `/users/${username || (current_user && current_user.login)}.json`;
axios.get(url).then((result) => {
@ -461,7 +459,7 @@ class Infos extends Component {
<Route
path="/:username"
render={(props) => {
return <GeneralView {...this.props} {...this.state} menuKey={menuKey} show_super_description={user && user.show_super_description}/>;
return <GeneralView {...this.props} {...this.state} menuKey={menuKey} show_super_description={user && user.show_super_description} generalFetchUser={this.fetchUser}/>;
}}
></Route>
{/* <Route

View File

@ -7,7 +7,7 @@ import { Divider } from 'antd';
import axios from 'axios';
function Personal(props){
const { user ,current_user , resetUserInfo } = props;
const { user ,current_user , generalFetchUser } = props;
const [ edit , setEdit ] = useState(false);
const [ showedit , setShowEdit ] = useState(false);
const [ description ,setDescription ] = useState("");
@ -18,7 +18,7 @@ function Personal(props){
setCopyDescription(user.super_description);
setShowEdit(true);
}
},[user])
},[user && user.super_description])
function onContentChange(value){
setDescription(value);
@ -51,7 +51,7 @@ function Personal(props){
setCopyDescription(description);
setEdit(false);
setShowEdit(description ?true :false);
resetUserInfo && resetUserInfo();
generalFetchUser && generalFetchUser();
}
})
}

View File

@ -5,8 +5,8 @@ import { notification , Tooltip } from 'antd';
import axios from 'axios';
import educoderLogo from './educoder.png';
import qqlogo from './qq.png';
import WeChatlogo from './WeChat.png';
import qqlogo from './qq@2x.png';
import WeChatlogo from './WeChat@2x.png';
import giteelogo from './gitee.png';
import githublogo from './github.png';
import cookie from 'react-cookies';

View File

@ -1,9 +1,6 @@
import React, { Component } from 'react';
import { SnackbarHOC } from 'educoder';
import {BrowserRouter as Router,Route,Switch,Link} from 'react-router-dom';
import {Tooltip,Menu,Pagination,Spin, Dropdown,Checkbox} from 'antd';
import {Menu,Pagination,Spin, Dropdown,Checkbox} from 'antd';
import axios from 'axios';
import {getImageUrl,WordsBtn} from 'educoder';
import moment from 'moment';
import Modals from '../../modals/Modals';
import SendTopics from '../../modals/SendTopics'
@ -313,12 +310,6 @@ class InfosTopics extends Component{
let user_type=this.props.current_user&&this.props.current_user.user_identity;
let targetuserid=this.props.data&&this.props.data.id;
// console.log(is_current)
// console.log(current_user)
// console.log(current_user.is_teacher)
// console.log(current_user.admin)
const menu = (
<Menu>
<Menu.Item onClick={()=>this.updatedlist("updated_at")}>