This commit is contained in:
caishi 2021-06-04 09:43:38 +08:00
parent f86acfebdf
commit 03cff6ec2e
11 changed files with 440 additions and 108 deletions

View File

@ -2,7 +2,7 @@ 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';
let message501 = false;
@ -11,10 +11,8 @@ broadcastChannelOnmessage('refreshPage', () => {
})
function locationurl(list) {
if (window.location.port === "3007") {
} else {
window.location.href = list
if (window.location.port !== "3007") {
window.location.href = list
}
}
// TODO 开发期多个身份切换
@ -26,53 +24,22 @@ if (isDev) {
parsed = queryString.parse(_search);
}
debugType = window.location.search.indexOf('debug=t') !== -1 ? 'teacher' :
window.location.search.indexOf('debug=s') !== -1 ? 'student' :
window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || ''
window.location.search.indexOf('debug=s') !== -1 ? 'student' :
window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'student'
}
function clearAllCookie() {
cookie.remove('_educoder_session', { path: '/' });
cookie.remove('autologin_trustie', { path: '/' });
setpostcookie()
}
clearAllCookie();
function setpostcookie() {
const str = window.location.pathname;
if (str.indexOf("/wxcode") !== -1) {
cookie.remove('_educoder_session', { path: '/' });
cookie.remove('autologin_trustie', { path: '/' });
const _params = window.location.search;
if (_params) {
let _search = _params.split('?')[1];
let _educoder_sessions = _search.split('&')[0].split('=');
cookie.save('_educoder_session', _educoder_sessions[1], { domain: '.educoder.net', path: '/' });
let autologin_trusties = _search.split('&')[1].split('=');
cookie.save('autologin_trustie', autologin_trusties[1], { domain: '.educoder.net', path: '/' });
}
}
}
setpostcookie();
window._debugType = debugType;
export function initAxiosInterceptors(props) {
// 判断网络是否连接
initOnlineOfflineListener();
var proxy = "http://localhost:3000";
proxy = "http://192.168.1.37:3000";
const requestMap = {};
window.setfalseInRequestMap = function (keyName) {
requestMap[keyName] = false;
}
var proxy = "https://testforgeplus.trustie.net";
//响应前的设置
axios.interceptors.request.use(
config => {
setpostcookie()
clearAllCookie()
if (config.url.indexOf(proxy) !== -1) {
if(config.url.indexOf("http") !== -1) {
return config
}
requestProxy(config)
requestProxy(config);
let url = `/api${config.url}`;
if (`${config[0]}` !== `true`) {
@ -86,12 +53,6 @@ export function initAxiosInterceptors(props) {
} else {
config.url = url;
}
setpostcookie();
}
if (config.url.indexOf('update_file') === -1) {
requestMap[config.url] = true;
window.setTimeout("setfalseInRequestMap('" + config.url + "')", 900)
}
return config;
},
@ -146,8 +107,6 @@ export function initAxiosInterceptors(props) {
message501 = false
}, 2000);
}
requestMap[response.config.url] = false;
setpostcookie();
return response;
}, function (error) {
return Promise.reject(error);

View File

@ -401,6 +401,7 @@ class Detail extends Component {
const urlFlag = (urlArr.length === 3);
const { projectsId , owner } = this.props.match.params;
const { current_user } = this.props;
let pathname = checkPathname(projectsId,owner,url);
const { state } = this.props.history.location;
@ -461,8 +462,8 @@ class Detail extends Component {
firstSync ? "":
<span className="df mt25">
{
projectDetail && projectDetail.type && projectDetail.type === 2 ?
<a className="synchronism ml30" onClick={this.synchronismMirror}>同步镜像</a> : ""
current_user && current_user.login && (projectDetail && projectDetail.type && projectDetail.type === 2) ?
<a className="synchronism ml30" onClick={this.synchronismMirror}>同步镜像</a> : ""
}
<span className="detail_tag_btn">
<a className="detail_tag_btn_name" style={{cursor:platform?"pointer":"default"}} onClick={() => this.focusFunc(watched)}>

View File

@ -5338,10 +5338,10 @@ export const locData = Array.from(province.keys()).map(p=>({
label:p,
children:Array.from(province.get(p).keys()).map((c,key)=>({
value:c,
label:c,
children:Array.from(area.keys())[key].map(a=>({
value:a,
label:a,
}))
label:c,
// children:Array.from(area.keys())[key].map(a=>({
// value:a,
// label:a,
// }))
}))
}));

View File

@ -48,6 +48,7 @@ function Index(props) {
//
function chooseTime(data) {
if(data){
setPage(1);
setActivityDate(data[0]);
}
}

View File

@ -97,7 +97,61 @@ $flex:flex;
}
}
}
.userDescription{
color: #666666;
line-height: 18px;
text-align: left;
margin:10px 0px;
word-break: break-all;
text-align: justify;
}
.focusBox,.infoBox{
width: 100%!important;
display: inline-block;
margin-top: 30px;
padding-top: 30px;
border-top: 1px solid #f1f1f1;
}
.infoBox{
padding-bottom: 10px;
text-align: left;
line-height: 28px;
color: #666;
margin-top: 20px;
i{
color: #DEDEDE;
font-size: 15px!important;
}
span{
margin-left:10px ;
}
}
.headimg{
position: relative;
display: block;
img{
width: 110px;
height: 110px;
border-radius: 50%;
}
span{
position: absolute;
bottom: -6px;
right: 0px;
left: 65px;
i{
font-size: 25px!important;
border-radius: 50%;
background-color: #fff;
&.icon-nan{
color: #1890FF;
}
&.icon-nv{
color: pink;
}
}
}
}
ul.ant-menu.menuStyle{
padding:0px 30px;
font-size: 16px;

View File

@ -1,6 +1,6 @@
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { Avatar, Tag, Button, Spin , Menu } from "antd";
import { Button, Spin , Menu } from "antd";
import FocusButton from "../UsersList/focus_button";
import axios from "axios";
@ -14,7 +14,10 @@ import './Index.scss';
import Loadable from "react-loadable";
import Loading from "../../Loading";
const UpdateInfo = Loadable({
loader: () => import("./Material/Index"),
loading: Loading,
});
const InfosDevOps = Loadable({
loader: () => import("./devOpsCI"),
loading: Loading,
@ -86,6 +89,8 @@ class Infos extends Component {
this.setState({menuKey:undefined,route_type:"watchers"});
}else if(pathname === `/users/${username}/fan_users`){
this.setState({menuKey:undefined,route_type:"fan_users"});
}else{
this.setState({menuKey:undefined,route_type:undefined});
}
}
@ -123,41 +128,25 @@ class Infos extends Component {
const { notice } = this.state;
let url = `/users/${username || (current_user && current_user.login)}.json`;
axios
.get(url)
.then((result) => {
let e = result.data && result.data.undo_events;
let p = result.data && result.data.undo_messages;
let n = notice || pathname === `/users/${username}/notice` ;
this.setState({
user: result.data,
isSpin: false,
undo_events:n ? (e-p) : e,
undo_messages:0,
notice:n
});
})
.catch((error) => {
this.setState({
isSpin: false,
});
axios.get(url).then((result) => {
let e = result.data && result.data.undo_events;
let p = result.data && result.data.undo_messages;
let n = notice || pathname === `/users/${username}/notice` ;
this.setState({
user: result.data,
isSpin: false,
undo_events:n ? (e-p) : e,
undo_messages:0,
notice:n
});
})
.catch((error) => {
this.setState({
isSpin: false,
});
});
};
// change_project_type = (type) => {
// const {user} = this.state
// this.setState({
// project_type: type ,
// route_type: undefined
// })
// let url = `/users/${user && user.login}`
// if (type){
// url = `/users/${user && user.login}/projects/${type}`
// }
// this.props.history.push(url)
// };
change_devops_type=(type)=>{
const {user} = this.state;
this.setState({
@ -194,39 +183,51 @@ class Infos extends Component {
this.props.history.push(`/users/${user && user.login}/organizes`)
}
resetUser=(data)=>{
this.setState({
user:data
})
}
render() {
const { current_user, mygetHelmetapi } = this.props;
const { username } = this.props.match.params;
const { user, isSpin, project_type, route_type , undo_events , undo_messages , menuKey } = this.state;
return (
<div className="newMain clearfix">
<Spin spinning={isSpin}>
<div className="new-content-flex">
<div className="list-left" style={{border:"none"}}>
<div className="bgcF">
<div className="list-l-Menu text-center pd20">
<Avatar size={110} src={getImageUrl(`/${user && user.image_url}`)} />
{user && user.user_identity && (
<div className="mt-n15 position-relative">
<Tag color="#FF6E21" style={{marginRight:"0px"}}>{user && user.user_identity}</Tag>
</div>
)}
<div className="list-l-Menu text-center" style={{padding:"20px 25px"}}>
<span className="headimg">
<img src={getImageUrl(`/${user && user.image_url}`)} alt=""/>
<span>
{
user && user.gender===1?
<i className="iconfont icon-nan"></i>
:
<i className="iconfont icon-nv"></i>
}
</span>
</span>
<div className="text-center mt15 font-16 fwb">
{user && user.username}
</div>
<div className="userDescription">
{user && user.description}
</div>
<div>
{user && current_user && user.login === current_user.login && (
<div className="user-info-star-button ">
<Button
block
className="text-button-grey"
href={`${
mygetHelmetapi &&mygetHelmetapi.new_course&&
mygetHelmetapi.new_course.edit_account
}`}
target="_blank"
onClick={()=>this.props.history.push(`/users/${user.login}/info`)}
type="primary"
>
{" "}
<i className="iconfont icon-shezhi4 font-15 mr5"></i>
@ -246,7 +247,7 @@ class Infos extends Component {
</div>
)}
</div>
<div className="width100 inline-block mt20">
<div className="focusBox">
<Link
to={`/users/${user && user.login}/watchers`}
className={`with50 text-center pull-left ${route_type === "watchers" ? "text-primary" : ""}`}
@ -264,11 +265,20 @@ class Infos extends Component {
<span>{user && user.watched_count}</span>
</Link>
</div>
{
user && (user.province || user.custom_department || user.email) ?
<div className="infoBox">
{ user.province && <div><i className="iconfont icon-weizhi"></i><span>{user.province}</span><span>{user.city}</span></div> }
{ user.custom_department && <div><i className="iconfont icon-danwei"></i><span>{user.custom_department}</span></div> }
{ user.email && <div><i className="iconfont icon-youxiangrenzheng"></i><span>{user.email}</span></div> }
</div>
:""
}
</div>
</div>
</div>
<div className="list-right">
{ !route_type &&
{ !route_type && menuKey &&
<Menu selectedKeys={[menuKey]} mode={`horizontal`} className="infosRightMenu">
<Menu.Item key="0"><Link to={`/users/${user && user.login}`}><i className="iconfont icon-gailan"></i></Link></Menu.Item>
<Menu.Item key="1"><Link to={`/users/${user && user.login}/statistics`}><i className="iconfont icon-shujutongji"></i></Link></Menu.Item>
@ -330,6 +340,19 @@ class Infos extends Component {
return <Organize {...this.props} {...this.state} />;
}}
></Route>
<Route
path="/users/:username/info"
render={() => {
return <UpdateInfo {...this.props} {...this.state} resetUser={this.resetUser}/>;
}}
></Route>
<Route
path="/users/:username/password"
render={() => {
return <UpdateInfo {...this.props} {...this.state}/>;
}}
></Route>
<Route
path="/users/:username/statistics"
render={(props) => {

View File

@ -0,0 +1,132 @@
import React , { forwardRef, useEffect } from 'react';
import { Form , Checkbox , Input, Radio , Button , Cascader } from 'antd';
import { AlignCenter } from '../../Component/layout';
import './Index.scss';
import { locData } from '../../Utils/locData';
import Axios from 'axios';
const { TextArea } = Input;
export default Form.create()(
forwardRef((props)=>{
const { getFieldDecorator, validateFields , setFieldsValue } = props && props.form;
const { username } = props && props.match && props.match.params;
const { current_user , resetUser } = props;
useEffect(()=>{
if(current_user && current_user.login){
setFieldsValue({
...current_user,
location:current_user.province && [current_user.province,current_user.city]
})
}
},[current_user])
function submit() {
validateFields((error,values)=>{
if(!error){
submitFunc(values);
}
})
}
function submitFunc(values) {
const url = `/users/${username}.json`;
const params={
user: {
nickname: values.real_name,
user_extension_attributes: {
province: values.location && values.location[0],
city: values.location && values.location[1],
...values
}
}
}
Axios.put(url,params).then(result=>{
if(result && result.data){
resetUser && resetUser(result.data);
}
}).catch(error=>{})
}
return(
<Form layout={'inline'} className="formBase">
<Form.Item label="邮箱">
{getFieldDecorator("email",{
rules:[{required:true,message:"请输入邮箱账号"}]
})(
<Input placeholder="请输入您的邮箱账号" style={{width:"400px"}}/>
)}
</Form.Item>
<Form.Item label="">
{getFieldDecorator("show_email",{
rules:[],
valuePropName:"checked"
})(
<Checkbox>在个人主页展示</Checkbox>
)}
</Form.Item>
<Form.Item label="姓名">
{getFieldDecorator("real_name",{
rules:[{required:true,message:"请输入姓名"}]
})(
<Input placeholder="请输入您的姓名" style={{width:"400px"}}/>
)}
</Form.Item>
<div>
<Form.Item label="性别">
{getFieldDecorator("gender",{
rules:[{required:true,message:"请选择性别"}]
})(
<Radio.Group>
<Radio value={1}></Radio>
<Radio value={0}></Radio>
</Radio.Group>
)}
</Form.Item>
</div>
<Form.Item label="单位名称">
{getFieldDecorator("custom_department",{
rules:[{required:true,message:"请输入单位名称"}]
})(
<Input placeholder="请输入单位名称" style={{width:"400px"}}/>
)}
</Form.Item>
<Form.Item label="">
{getFieldDecorator("show_department",{
rules:[],
valuePropName:"checked"
})(
<Checkbox>在个人主页展示</Checkbox>
)}
</Form.Item>
<Form.Item label="地区">
{getFieldDecorator("location",{
rules:[]
})(
<Cascader placeholder="请选择省份城市" options={locData} style={{width:"400px"}}></Cascader>
)}
</Form.Item>
<Form.Item label="">
{getFieldDecorator("show_location",{
rules:[],
valuePropName:"checked"
})(
<Checkbox>在个人主页展示</Checkbox>
)}
</Form.Item>
<Form.Item label="简介">
{getFieldDecorator("description",{
rules:[]
})(
<TextArea placeholder="请输入您的自我理解" rows={4} maxLength="140" style={{width:"600px"}}/>
)}
</Form.Item>
<AlignCenter>
<span className="ant-form-item-label"></span>
<Button type={"primary"} onClick={submit}>提交</Button>
<Button type={"default"} onClick={()=>props.history.push(`/users/${username}`)} className="ml20">取消</Button>
</AlignCenter>
</Form>
)
})
)

View File

@ -0,0 +1,50 @@
import React , { useEffect , useState } from 'react';
import './Index.scss';
import { Menu } from 'antd';
import { Link } from 'react-router-dom';
import Base from './Base';
import Password from './Password';
function Index(props){
const { username } = props && props.match && props.match.params;
const { pathname } = props && props.location;
const { current_user } = props;
useEffect(()=>{
if((username && current_user && (current_user.login !== username))){
props.history.push(`/users/${username}`);
}
},[current_user,username])
const [ key , setKey ] = useState("0");
useEffect(()=>{
if(pathname){
if(pathname === `/users/${username}/info`){
setKey("0");
}else{
setKey("1");
}
}
},[pathname])
return(
<div>
<Menu selectedKeys={[key]} mode={'horizontal'} className="infosRightMenu">
<Menu.Item key="0"><Link to={`/users/${username}/info`}>基本资料</Link></Menu.Item>
<Menu.Item key="1"><Link to={`/users/${username}/password`}>密码管理</Link></Menu.Item>
</Menu>
<div style={{padding:"20px"}}>
{
key === "0" ?
<Base {...props}/>
:
<Password {...props}/>
}
</div>
</div>
)
}
export default Index;

View File

@ -0,0 +1,14 @@
.formBase{
.ant-form-item-label{
width: 80px;
display: block;
}
.ant-form-explain{
position: absolute;
}
}
.formBase.passMan{
.ant-form-item-label{
width: 92px;
}
}

View File

@ -0,0 +1,98 @@
import React , { forwardRef, useState } from 'react';
import { Form , Input , Button } from 'antd';
import { AlignCenter } from '../../Component/layout';
import Axios from 'axios';
export default Form.create()(
forwardRef((props)=>{
const { getFieldDecorator, validateFields , setFieldsValue } = props && props.form;
const { username } = props && props.match && props.match.params;
const { current_user} = props;
const [ oldPass , setOldPass ] = useState(undefined);
const [ oldPassRepeat , setOldPassRepeat ] = useState(undefined);
function submit() {
validateFields((error,values)=>{
if(!error){
submitFunc(values);
}
})
}
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=>{})
}
//
function checkIdentifier(rule, value, callback , inputValue){
if(!value){
callback();
}
if (value && inputValue && value !== inputValue) {
callback("两次输入的密码不一致");
}
callback();
}
var reg = /(?!.*\s)(?!^[\u4e00-\u9fa5]+$)(?!^[0-9]+$)(?!^[A-z]+$)(?!^[^A-z0-9]+$)^.{8,16}$/;
function checkNewPass(rule, value, callback) {
if(!value){
callback();
}
if (!reg.test(value)) {
callback("8-16个字符,不包含空格,必须包含数字,字母或字符至少两种");
}
callback();
}
return(
<Form layout={'inline'} className="formBase passMan">
<Form.Item label="旧密码">
{getFieldDecorator("old_password",{
rules:[
{required:true,message:"请输入旧密码"},
{validator:(rule, value, callback)=>checkIdentifier(rule, value, callback,oldPassRepeat)}
]
})(
<Input.Password placeholder="请输入旧密码" style={{width:"400px"}} onChange={(e)=>{setOldPass(e.target.value)}}/>
)}
</Form.Item>
<Form.Item label="重复旧密码">
{getFieldDecorator("old_password_repeat",{
rules:[
{required:true,message:"请重新输入旧密码"},
{validator:(rule, value, callback)=>checkIdentifier(rule, value, callback,oldPass)}
]
})(
<Input.Password placeholder="请重新输入旧密码" style={{width:"400px"}} onChange={(e)=>{setOldPassRepeat(e.target.value)}}/>
)}
</Form.Item>
<Form.Item label="新密码">
{getFieldDecorator("password",{
rules:[
{required:true,message:"请输入新密码"},
{validator:checkNewPass}
]
})(
<Input.Password placeholder="请输入新密码" style={{width:"400px"}}/>
)}
</Form.Item>
<AlignCenter style={{marginTop:"20px"}}>
<span className="ant-form-item-label"></span>
<Button type={"primary"} onClick={submit}>提交</Button>
<Button type={"default"} onClick={()=>props.history.push(`/users/${username}`)} className="ml20">取消</Button>
</AlignCenter>
</Form>
)
})
)

View File

@ -30,7 +30,7 @@
.position-relative{position: relative;}
.mr-5{margin-right: 5px;}
a.text-button-grey{
color: rgba(0, 0, 0, 0.65)
color: #fff;
}
.user-info-star-button{margin: 20px 50px 0 50px}
.list-l-p{