Merge branch 'cs_news' into feature/zhuanqu

This commit is contained in:
黄心宇 2023-06-25 11:06:15 +08:00
commit 9e60cc3cdc
44 changed files with 949 additions and 92 deletions

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 2340181 */
src: url('iconfont.woff2?t=1682403222759') format('woff2'),
url('iconfont.woff?t=1682403222759') format('woff'),
url('iconfont.ttf?t=1682403222759') format('truetype');
src: url('iconfont.woff2?t=1686823554834') format('woff2'),
url('iconfont.woff?t=1686823554834') format('woff'),
url('iconfont.ttf?t=1686823554834') format('truetype');
}
.iconfont {
@ -13,6 +13,22 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-a-zu2044:before {
content: "\e96d";
}
.icon-cuo:before {
content: "\e96c";
}
.icon-dui:before {
content: "\e96b";
}
.icon-zanting:before {
content: "\e96a";
}
.icon-cangkuyuyanicon:before {
content: "\e962";
}

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,34 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "36020261",
"name": "组 2044",
"font_class": "a-zu2044",
"unicode": "e96d",
"unicode_decimal": 59757
},
{
"icon_id": "5287658",
"name": "错",
"font_class": "cuo",
"unicode": "e96c",
"unicode_decimal": 59756
},
{
"icon_id": "8802648",
"name": "对",
"font_class": "dui",
"unicode": "e96b",
"unicode_decimal": 59755
},
{
"icon_id": "887857",
"name": "暂停",
"font_class": "zanting",
"unicode": "e96a",
"unicode_decimal": 59754
},
{
"icon_id": "34965902",
"name": "仓库语言icon",

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -25,7 +25,7 @@ if (isDev) {
}
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 || 'admin'
window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'student'
}
window._debugType = debugType;
export function initAxiosInterceptors(props) {

View File

@ -11,10 +11,12 @@ export default(({ header , nav })=>{
nav && nav.list && nav.list.length>0 ?
nav.list.map((item,key)=>{
return(
<li key={key} className={nav.active === key?"active":''}>
!item.hide &&<li key={key} className={nav.active === key?"active":''}>
<p>
<Link to={item.href}>
<i className={`iconfont ${item.icon || 'icon-huabanfuben'} font-18 mr10`}></i>{item.name}
{item.icon && <i className={`iconfont ${item.icon || 'icon-huabanfuben'} font-18 mr10`}></i>}
{item.img && <img src={item.img} alt="" width="20px" className="mr10"/>}
{item.name}
</Link>
</p>
</li>

View File

@ -46,7 +46,7 @@ function NewsDetail(props){
<p className="i_name">{detail.name}</p>
<ul className="i_ul_value">
{ cmsDir && <li>{cmsDir.name}</li> }
{ cmsDir && <li>发布于{cmsDir.createdAt}</li> }
{ cmsDir && <li>发布于{detail.publishTime}</li> }
{ detail.visits && <li><img src={liulan} alt="" className="mr5" />{detail.visits}</li> }
</ul>
</div>

View File

@ -92,8 +92,8 @@ function Index(props){
function getAdminUrl(id){
getCheckZoneRole(id).then(response=>{
if(response && response.data.code === 200){
setAdminUrl(response.data);
if(response && response.data.data){
setAdminUrl(response.data.data);
}else{
setAdminUrl(undefined);
}

View File

@ -1022,12 +1022,6 @@
margin: 25px 25px 25px 0;
border-radius: 12px;
}
.informations_detail.markdown-body{
&>p{
line-height: 32px;
margin-bottom: 8px!important;
}
}
}
.zone_parter{
background-color: #fff;
@ -1195,4 +1189,10 @@
}
}
}
}
.informations_detail.markdown-body{
&>p{
line-height: 32px;
margin-bottom: 8px!important;
}
}

View File

@ -63,9 +63,9 @@ function CoderDepot(props){
branchName = returnbar(branchName);
const details = props.projectDetail;
let pathname = props.history.location.pathname;
const { platform } = props ;
const { platform , mirror_status } = props ;
//distribution
const distribution = details && details.type !== 2 && ( details.permission && details.permission !== "Reporter");
const distribution = details && details.type ===0 && ( details.permission && details.permission !== "Reporter");
const { bannerList } = props;
useEffect(()=>{
if(bannerList && bannerList.length>0){
@ -93,7 +93,7 @@ function CoderDepot(props){
useEffect(()=>{
if(details){
if(details && mirror_status===0){
setProjectDetail(details);
setTopics(details.topics);
setDesc(details.description);
@ -102,7 +102,7 @@ function CoderDepot(props){
setDefaultBranch(details.default_branch);
setInviteCode(details.invite_code);
}
},[details])
},[details,mirror_status])
useEffect(()=>{
//
@ -126,7 +126,7 @@ function CoderDepot(props){
},[treeValue])
useEffect(()=>{
if (projectsId && owner && defaultBranch){
if (projectsId && owner && defaultBranch && mirror_status===0){
let b = turnbar(branchName) ;
if(pathname.indexOf(`/${owner}/${projectsId}`) > -1 && pathname.indexOf(`/tree/${b}/`) > -1) {
let url = pathname.split(`/tree/${b}/`)[1];
@ -139,10 +139,10 @@ function CoderDepot(props){
setType("dir");
}
}
},[projectsId,owner,pathname,defaultBranch])
},[projectsId,owner,pathname,defaultBranch,mirror_status])
useEffect(()=>{
if(platform){
if(platform && mirror_status===0){
setReadOnly(props.history.location.search.indexOf('edit') !== -1);
axios.get(`/${owner}/${projectsId}/releases.json`).then((result)=>{
if(result && result.data){
@ -154,7 +154,7 @@ function CoderDepot(props){
}
})
}
},[platform])
},[platform,mirror_status])
// readme
function getReadmeInfo(path, ref) {
@ -386,14 +386,14 @@ function CoderDepot(props){
const { current_user , checkIfLogin , showLoginDialog } = props;
const baseOper = current_user && current_user.login && issuesFlag;
const baseOperate = projectDetail && projectDetail.permission && projectDetail.permission !=="Reporter" && projectDetail.type !== 2 && pullsFlag && props.platform;
const fileOperate = type === "dir" && projectDetail && projectDetail.type !== 2 && ((projectDetail.permission && projectDetail.permission !=="Reporter") || (current_user && current_user.admin));
const baseOperate = projectDetail && projectDetail.permission && projectDetail.permission !=="Reporter" && projectDetail.type ===0 && pullsFlag && props.platform;
const fileOperate = type === "dir" && projectDetail && projectDetail.type ===0 && ((projectDetail.permission && projectDetail.permission !=="Reporter") || (current_user && current_user.admin));
return(
<WhiteBack>
<UpdateDescModal desc={desc} website={website} lesson_url={lesson_url} topicsInfo={topics} visible={openModal} onCancel={()=>setOpenModal(false)} onOk={okUpdate}/>
{mirror_status ===0 && <UpdateDescModal desc={desc} website={website} lesson_url={lesson_url} topicsInfo={topics} visible={openModal} onCancel={()=>setOpenModal(false)} onOk={okUpdate}/> }
<Spin spinning={isSpin}>
{
(dirInfo || fileInfo) &&
((dirInfo || fileInfo) && mirror_status===0 ) &&
<React.Fragment>
<DrawerPanel
history={props.history}
@ -421,7 +421,7 @@ function CoderDepot(props){
<AlignCenter>
<div className="mr30">
{
props && props.platform ?
props && (props.platform && mirror_status ===0) ?
<SelectBranch
repo_id={projectDetail && projectDetail.repo_id}
projectsId={projectsId}
@ -651,9 +651,9 @@ function CoderDepot(props){
</React.Fragment>
}
{/* 贡献者 */}
<Contributors owner={owner} projectsId={projectsId} />
{mirror_status ===0 && <Contributors owner={owner} projectsId={projectsId} /> }
{/* 语言 */}
<LanguagePower owner={owner} projectsId={projectsId}/>
{ mirror_status ===0 && <LanguagePower owner={owner} projectsId={projectsId}/> }
</Gap>
</ShortWidth>
}

View File

@ -192,7 +192,7 @@ class Detail extends Component {
// 非本平台项目
platform: false,
mirror_status:1
mirror_status:2
}
}
@ -240,7 +240,6 @@ class Detail extends Component {
open_devops: result.data.open_devops,
platform: result.data.platform && result.data.platform !== 'educoder'
})
if (result.data.type !== 0 && result.data.mirror_status === 1) {
console.log("--------start channel --------");
// 是镜像项目,且未完成迁移
@ -260,16 +259,18 @@ class Detail extends Component {
mirror_status:1
})
} else if (result.data.mirror_status === 2) {
this.deleteProjectBack();
this.setState({
mirror_status:2
mirror_status:2,
firstSync: false
})
this.deleteProjectBack();
} else {
this.getBanner();
this.getDetail();
this.setState({
firstSync: false,
secondSync: false
secondSync: false,
mirror_status:0
})
}
}
@ -860,7 +861,7 @@ class Detail extends Component {
></Route>
<Route path="/:owner/:projectsId"
render={
(props) => (mirror_status!==2 ? <CoderDepot {...this.props} {...props} {...this.state} {...common} /> :<Spin />)
(props) => <CoderDepot {...this.props} {...props} {...this.state} {...common} mirror_status={mirror_status}/>
}
></Route>
</Switch>

View File

@ -53,7 +53,10 @@ class Index extends Component {
licenseFlag:false,
// 区块链确权
tokenConTri: false
tokenConTri: false,
gitHubUrl:false,
checkToken:undefined
}
}
componentDidMount = () => {
@ -203,7 +206,9 @@ class Index extends Component {
isSpin: true
})
const { projectsType } = this.props.match.params;
const { project_language_id, project_category_id, license_id, ignore_id, owners_id, ignoreFlag, licenseFlag, categoreFlag, languageFlag, tokenConTri} = this.state;
const { project_language_id, project_category_id, license_id
, ignore_id, owners_id, ignoreFlag, licenseFlag,
categoreFlag, languageFlag, tokenConTri,gitHubUrl} = this.state;
const decoderPass = Base64.encode(values.password);
const url = (projectsType && projectsType === "mirror") ? "/projects/migrate.json" : "/projects.json";
// 新建项目的时候,暂存数据,如果失败,返回的时候可以重新赋值
@ -220,7 +225,8 @@ class Index extends Component {
user_id:owners_id,
blockchain: tokenConTri,
blockchain_token_all: 10000,
blockchain_init_token: parseInt(blockchain_init)
blockchain_init_token: parseInt(blockchain_init),
auth_token:gitHubUrl ? values.auth_token : undefined
}).then((result) => {
if (result && result.data.id) {
projectsType && projectsType !== "mirror" && this.props.showNotification(`项目创建成功!`);
@ -235,6 +241,7 @@ class Index extends Component {
})
})
} else {
window.scrollTo(0,0);
this.setState({
isSpin: false
})
@ -301,6 +308,9 @@ class Index extends Component {
repository_name:first
})
}
this.setState({gitHubUrl:value.indexOf("https://github.com")>-1 ? true : false})
}else{
this.setState({gitHubUrl:false,checkToken:undefined})
}
}
@ -344,6 +354,29 @@ class Index extends Component {
}
}
blurFunc=(e)=>{
let value = e.target.value;
if(value){
const { clone_addr } = this.props.form.getFieldsValue();
if(clone_addr){
const u = `/projects/verify_auth_token.json`;
axios.post(u,{
auth_token:value,
clone_addr
}).then(response=>{
if(response && response.data){
this.setState({
checkToken:response.data.data
})
}
}).catch(error=>{})
}
}else{
this.setState({checkToken:undefined})
}
}
render() {
const { getFieldDecorator } = this.props.form;
// 项目类型deposit-托管项目mirror-镜像项目
@ -372,7 +405,9 @@ class Index extends Component {
licenseFlag,
languageFlag,
categoreFlag,
tokenConTri
tokenConTri,
gitHubUrl,
checkToken
} = this.state;
return (
<div className="main back-white" style={{padding:"0px",border:"none"}}>
@ -381,7 +416,6 @@ class Index extends Component {
<Spin spinning={isSpin}>
<Form>
<div className="newPanel_content">
{
projectsType && projectsType === "mirror" &&
<React.Fragment>
@ -398,7 +432,7 @@ class Index extends Component {
<Input placeholder="请输入需要导入到本项目的仓库地址" onBlur={this.ChangeAddr} />
)}
</Form.Item>
<p className="formTip color-orange">示例https://github.com/Stability-AI/StableStudio.git</p>
<p className="formTip" style={{color:"#D1d1c9"}}>示例https://github.com/Stability-AI/StableStudio.git</p>
</React.Fragment>
}
{
@ -406,10 +440,32 @@ class Index extends Component {
<div className="pb10">
<p className="mt10 mb10 color-grey-3 pointer" onClick={this.changeMirrorCheck}>
需要授权验证<i className={mirrorCheck ? "iconfont icon-xiajiantou font-13 ml10 color-grey-8":"iconfont icon-youjiantou font-13 ml10 color-grey-8"}></i>
<span className="ml20 font-12 color-red">如果导入项目为私有仓库则必须填写相应平台正确的用户名和密码</span>
<span className="ml20 font-12" style={{color:"#D1d1c9"}}>
如果导入项目为私有仓库{gitHubUrl ? `则需输入目标平台用户token进行授权`:"则必须填写相应平台正确的用户名和密码"}
{gitHubUrl && <a href="https://forum.gitlink.org.cn/forums/8516/detail" className="color-blue" target="_blank">如何获取token</a>}
</span>
</p>
{
mirrorCheck &&
mirrorCheck && gitHubUrl &&
<div className="df mb10 token_box">
<span className="mr10">Token</span>
<input type="password" style={{display:"none"}} />
<Form.Item
style={{ marginBottom: "0px" }}
label=""
>
{getFieldDecorator('auth_token')(
<Input placeholder="请输入对应平台的有效token" style={{width:"240px"}} onBlur={this.blurFunc} />
)}
</Form.Item>
<span style={{marginLeft:"-20px"}}>
{ checkToken === false && <i className="iconfont icon-cuo color-red"></i>}
{ checkToken === true && <i className="iconfont icon-dui color-green"></i>}
</span>
</div>
}
{
mirrorCheck && !gitHubUrl &&
<div className="df mb10" style={{alignItems:'center'}}>
<span className="mr10">用户名</span>
<input type="password" style={{display:"none"}} />
@ -447,19 +503,26 @@ class Index extends Component {
{getFieldDecorator('user_id', {
rules: [{
required: true, message: '请选择拥有者'
},{
validator:(rule, value, callback) => this.checkId(rule, value, callback, OwnerList, '拥有者')
}],
})(
<AutoComplete
style={{width:"260px",height:"35px"}}
<Select
showSearch
style={{width:"260px",height:"33px"}}
placeholder="请选择拥有者"
onChange={(value, e) => this.ChangePlatform(value, e, 'owners', OwnerList)}
className="plateAutoComplete"
onBlur={(value) => this.blurCategory(value, OwnerList, "owners")}
optionFilterProp="children"
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{owners_list}
</AutoComplete>
{OwnerList && OwnerList.length > 0 &&
OwnerList.map((i,k)=>{
return(
<Option value={i.id}>{i.name}</Option>
)
})
}
</Select>
)}
</Form.Item>
<span className="ml10 mr10 mt10 font-18">/</span>
@ -681,4 +744,6 @@ class Index extends Component {
}
}
const WrappedIndexForm = Form.create({ name: 'NewWorkForm' })(Index);
export default WrappedIndexForm;
export default WrappedIndexForm;

View File

@ -11,6 +11,12 @@
font-size: 1.6em;
border-bottom: 1px solid #f0f0f0
}
.token_box{
align-items: center;
}
.token_box .ant-form-explain{
position: absolute;
}
.newPanel_content{
padding:2rem;
}
@ -25,7 +31,7 @@
min-width: 280px;
}
.newPanel_content .ant-select-selection{
height: 37px;
height: 35px;
}
.newPanel_content .ant-select-selection__rendered,.newPanel_content input{
height: 35px;

View File

@ -34,7 +34,6 @@ class m_editor extends Component {
render() {
const { editorValue, changeValue } = this.state;
console.log("editorValue:",editorValue)
const { readOnly, editorType,onEmpty,filename,empty, currentBranch, descName, checkName, detail, match: { params },filepath,ideTheme } = this.props;
const editor_options = {
lineNumbers: "on",

View File

@ -25,7 +25,10 @@ const NoticeManager = Loadable({
loader: () => import("./notice/manager/Index"),
loading: Loading,
});
const CLAList = Loadable({
loader: () => import("./cla/Index"),
loading: Loading,
});
const SSHNew = Loadable({
loader: () => import("./sub/New"),
loading: Loading,
@ -116,6 +119,7 @@ function Index(props){
<ul className="securityUl ul-border-buttom">
<li>安全设置</li>
<li className={pathname.indexOf("/settings/SSH")>-1 ?"active":""}><Link to={`/settings/SSH`}><i className="iconfont icon-xuanzhongssh_icon mr5 font-14"></i><span className="text-shodow-bold">SSH密钥</span></Link></li>
<li className={pathname.indexOf("/settings/CLA")>-1 ?"active":""}><Link to={`/settings/CLA`}><i className="iconfont icon-a-zu2044 mr5 font-13"></i><span className="text-shodow-bold">我的CLA</span></Link></li>
</ul>
<ul className="securityUl">
<li>Bot开发</li>
@ -138,6 +142,12 @@ function Index(props){
<PrivateLetter{...props} {...p}/>
)}
></Route>
<Route
path="/settings/CLA"
render={(p) => (
<CLAList {...props} {...p}/>
)}
></Route>
<Route
path="/settings/notice"
render={(p) => (

View File

@ -0,0 +1,27 @@
import React from 'react';
import { AlignCenter } from '../../Component/layout';
import Modals from '../../Component/PublicModal/Index';
import { Button } from 'antd';
function DeleteBox({visible , onCancel,onSuccess,pr_need}){
return(
<Modals
visible={visible}
onCancel={onCancel}
title={"解除协议"}
btn={
<div>
<Button size={'large'} onClick={onCancel}>取消</Button>
<Button type={"danger"} size={"large"} onClick={onSuccess}>确定</Button>
</div>
}
>
<div className="desc">
<AlignCenter className="descMain">
<i className="iconfont icon-jinggao1 mr10 font-20 red"></i>确定解除协议吗</AlignCenter>
{ pr_need && <p>您解除协议后提交的合并请求对项目成员将不再可见已存在的开启的合并请求也将被隐藏</p> }
</div>
</Modals>
)
}
export default DeleteBox;

View File

@ -0,0 +1,87 @@
import React,{useEffect,useState} from 'react';
import './index.scss';
import logo from '../img/icon.svg';
import { Link } from 'react-router-dom';
import { Button , Spin } from 'antd';
import Nodata from '../../Nodata';
import DeleteBox from './DeleteBox';
import axios from 'axios';
function Index(props){
const { current_user } = props;
const[ lists, setLists] = useState(undefined);
const[ isSpin, setIsSpin] = useState(false);
const [ visible , setVisible ] = useState(false);
const [ cancelKey , setCancelKey ] = useState(undefined);
const [ pr_need , setPr_need ] = useState(true);
useEffect(()=>{
document.title = "我的CLA";
},[])
useEffect(()=>{
if(current_user && current_user.user_id){
setIsSpin(true);
getClaLists();
}
},[current_user])
function getClaLists(){
const url = `/users/${current_user.user_id}/clas.json`;
axios.get(url).then(response=>{
if(response && response.data){
setLists(response.data.user_clas);
setIsSpin(false);
}
}).catch(error=>{})
}
function onSuccess(){
if(cancelKey && (current_user && current_user.user_id)){
const url = `/users/${current_user && current_user.user_id}/clas/${cancelKey}.json`;
axios.delete(url).then(response=>{
if(response){
setVisible(false);
props.showNotification("协议解除成功!");
getClaLists();
}
}).catch(error=>{})
}
}
return(
<div className="cla_personal">
<DeleteBox visible={visible} pr_need={pr_need} onCancel={()=>{setVisible(false);setCancelKey(undefined)}} onSuccess={onSuccess}/>
<div className="default_head">我签署的CLA开源贡献者协议</div>
{
lists && lists.length>0 &&
<ul className="cla_lists">
{
lists.map((i,k)=>{
return(
<li key={k}>
<img src={logo} alt="" />
<div>
<Link to={`/${i.organization && i.organization.name}/cla/${i.cla && i.cla.key}`} className="agree_name task-hide">{i.cla && i.cla.name}</Link>
{i.state && <span className="agree_status">签署状态<span className={i.state === "signed" ? "green_tip" :"grey_tip"}>{i.state === "signed" ? "生效中" : "已解除"}</span></span> }
<span>签署邮箱<span className="ml10">{i.email}</span></span>
<span>签署时间<span className="ml10">{i.created_at}</span></span>
<p className="mt8">
<Button onClick={()=>{props.history.push(`/${i.organization && i.organization.name}/cla/${i.cla && i.cla.key}`)}} style={{color:"#466aff",borderColor:"#466aff"}}>查看协议</Button>
{ i.state === "signed" && <Button style={{color:"#ff0c0c",borderColor:"#ff0c0c"}} className="ml20" onClick={()=>{setVisible(true);setCancelKey(i.id);setPr_need(i.cla.pr_need)}}>解除协议</Button>}
</p>
</div>
</li>
)
})
}
</ul>
}
{
lists && lists.length === 0 && <Nodata _html="暂无数据"/>
}
{
!lists && <div style={{minHeight:"300px",display:'flex',alignItems:"center",justifyContent:"center"}}><Spin spinning={isSpin}></Spin></div>
}
</div>
)
}
export default Index;

View File

@ -0,0 +1,75 @@
.default_head{
height: 65px;
line-height: 64px;
border-bottom: 1px solid rgba(224, 230, 245, 1);
color:#151d40;
font-size:17px;
padding-bottom:20px;
}
.cla_lists{
display: flex;
flex-wrap: wrap;
li{
width: 478px;
margin-right: 26px;
border:1px solid;
border-color:rgba(153, 153, 153, 0.3);
border-radius:6px;
padding:30px 26px;
box-sizing: border-box;
display: flex;
align-items: flex-start;
margin-top: 25px;
&>img{
width: 65px;
margin-right: 20px;
}
&>div{
flex:1;
width: 0;
display: flex;
flex-direction: column;
&>span{
color:rgba(76, 88, 118, 1);
height: 20px;
line-height: 20px;
margin-bottom: 10px!important;
}
.agree_status{
>span{
margin-left: 20px;
position: relative;
color:#8a94a2 ;
&::before{
position: absolute;
height: 6px;
width: 6px;
border-radius: 50%;
left: -11px;
content: "";
background-color: #8a94a2;
top:50%;
margin-top: -3px;
}
&.green_tip{
color: #009b28;
}
&.green_tip::before{
background-color: #009b28;
}
}
}
.agree_name{
color:#151d40;
font-size:16px;
margin-bottom: 10px;
display: block;
height: 22px;
line-height: 22px;
}
}
&:nth-child(2n){
margin-right: 0px;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" width="65.368" height="70.129" viewBox="0 0 65.368 70.129">
<g id="icon" transform="translate(-680.606 -410)">
<path id="路径_3391" data-name="路径 3391" d="M190.644,100.032H130.724A2.724,2.724,0,0,0,128,102.756v64.682a2.724,2.724,0,0,0,2.724,2.724h59.921a2.724,2.724,0,0,0,2.724-2.724V102.756A2.724,2.724,0,0,0,190.644,100.032Z" transform="translate(552.606 309.968)" fill="#e6e6e6"/>
<rect id="矩形_367" data-name="矩形 367" width="49" height="53" transform="translate(689 419)" fill="#fff"/>
<g id="协议" transform="translate(687.555 417.455)">
<path id="路径_3391-2" data-name="路径 3391" d="M177.325,100.032H130.145A2.145,2.145,0,0,0,128,102.177v50.93a2.145,2.145,0,0,0,2.145,2.145h47.181a2.145,2.145,0,0,0,2.145-2.145v-50.93A2.145,2.145,0,0,0,177.325,100.032Zm1.187,1.058v53.25H129.125V101.09Z" transform="translate(-128 -100.032)" fill="#aeb4bb"/>
<path id="路径_3392" data-name="路径 3392" d="M308.3,544v4.289H288V544Zm-11.6,8.578v4.289H288v-4.289Z" transform="translate(-280.604 -510.521)" fill="#c4c7cc"/>
<text id="CLA" transform="translate(25.444 22.609)" fill="#5f6872" stroke="rgba(0,0,0,0)" stroke-width="1" font-size="17" font-family="STFangsong"><tspan x="-16.023" y="0">CLA</tspan></text>
</g>
<path id="icon_徽章" d="M204.579,70.459a6.149,6.149,0,1,0-10.543,4.3,6.22,6.22,0,0,0,.855.729v5a.194.194,0,0,0,.278.174l3.246-1.569,3.246,1.569a.194.194,0,0,0,.278-.174V75.509a6.212,6.212,0,0,0,.837-.7A6.108,6.108,0,0,0,204.579,70.459Zm-11.071,0a4.922,4.922,0,1,1,4.922,4.922A4.928,4.928,0,0,1,193.507,70.459Zm7.206,8.384-2.213-1.07a.194.194,0,0,0-.168,0l-2.213,1.07V76.16a6.19,6.19,0,0,0,4.6.011v2.672Z" transform="translate(526.76 384.613)" fill="#009b28"/>
<circle id="椭圆_67" data-name="椭圆 67" cx="3" cy="3" r="3" transform="translate(722 452)" fill="rgba(0,155,40,0.31)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -14,6 +14,10 @@ const GroupNew = Loadable({
loader: () => import("./Group/GroupNew"),
loading: Loading,
});
const CLAinfos = Loadable({
loader: () => import("./Setting/cla/CLAinfos"),
loading: Loading,
});
const New = Loadable({
loader: () => import("./New"),
loading: Loading,
@ -40,6 +44,13 @@ export default withRouter(CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(
return (
<div className="newMain">
<Switch>
{/* 协议详情+签署协议 */}
<Route
path="/:OIdentifier/cla/:claid"
render={(p) => (
<CLAinfos {...props} {...p}/>
)}
></Route>
{/* 新建组织 */}
<Route
path="/organize/new"
@ -75,7 +86,6 @@ export default withRouter(CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(
return <TeamDetailIndex {...props} {...p}/>
}}
></Route>
{/* 组织详情(包含组织设置) */}
<Route

View File

@ -45,7 +45,7 @@ export default Form.create()(
}
const url = `/organizations.json`;
axios.post(url,{
...values,image
...values,image,visibility:"common"
}).then(result=>{
if(result && result.data){
showNotification("组织创建成功!");
@ -121,7 +121,7 @@ export default Form.create()(
[],
<Input placeholder="请输入地址" maxLength={50}/>,false
)}
{helper(
{/* {helper(
'可见性',
"visibility",
[{ required: true, message: "请选择可见性" }],
@ -130,7 +130,7 @@ export default Form.create()(
<Radio style={radioStyle} value="limited" key={2}>受限<span className="color-grey-8">仅对登录用户可见</span></Radio>
<Radio style={radioStyle} value="privacy" key={3}>私有<span className="color-grey-8">仅对组织成员可见</span></Radio>
</Radio.Group>
)}
)} */}
<p className="font-16 lables must">选择头像</p>
<UploadImage getImage={getImage}/>

View File

@ -27,6 +27,10 @@ const ListName = styled.div`{
margin-bottom:8px;
height:18px;
line-height:18px;
width:210px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}`;
const ColorListName = styled.div`{
color:rgba(31, 35, 41, 1);
@ -34,6 +38,10 @@ const ColorListName = styled.div`{
margin-bottom:8px;
height:18px;
line-height:18px;
width:230px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}`
const Img = styled.img`{
border-radius:50%;

View File

@ -58,7 +58,7 @@ export default Form.create()(
if(!error){
const url = `/organizations/${organizeDetail.id}.json`;
axios.patch(url,{
...values,image:imageFlag ? image : undefined
...values,image:imageFlag ? image : undefined,visibility:"common"
}).then(result=>{
if(result && result.data){
showNotification("组织信息更新成功!");
@ -122,7 +122,7 @@ export default Form.create()(
})
}
return(
<div>
<div style={{border:"1px solid #eee"}}>
<WhiteBack>
<Title>基本设置</Title>
<Modals visible={privacyVisible} okText={"确认"} cancelText={"取消"} onCancel={concelChangePrivacy} onOk={()=>setPrivacyVisible(false)}>
@ -172,7 +172,7 @@ export default Form.create()(
[],
<Input placeholder="请输入城市"/>
)}
{helper(
{/* {helper(
'可见性:',
"visibility",
[],
@ -181,7 +181,7 @@ export default Form.create()(
<Radio value="limited" style={radioStyle}>受限<span>仅对登录用户可见</span></Radio>
<Radio value="privacy" style={radioStyle}>私有<span>仅对组织成员可见</span></Radio>
</Radio.Group>
)}
)} */}
{helper(
'权限:',
"repo_admin_change_team_access",

View File

@ -5,7 +5,7 @@ import GroupItems from '../TeamGroupItems';
const limit = 8;
function TeamSettingGroup({organizeDetail,history,current_user}){
return(
<WhiteBack>
<WhiteBack style={{border:"1px solid #eee"}}>
<Banner>组织团队管理</Banner>
<GroupItems limit={limit} organizeDetail={organizeDetail} count={4} history={history} current_user={current_user}/>
</WhiteBack>

View File

@ -2,10 +2,12 @@ import React , { useEffect , useState } from 'react';
import { Box , Long , Short , Gap } from '../../Component/layout';
import Title from '../../Component/Title';
import Setnav from '../../Component/Setnav';
import './teamSetting.scss';
import { Route, Switch } from "react-router-dom";
import Loadable from "react-loadable";
import Loading from "../../../Loading";
import claIcon from '../images/cla-icon.png';
const Group = Loadable({
@ -28,11 +30,18 @@ const Hooks = Loadable({
loader: () => import("./TeamSettingHooks"),
loading: Loading,
});
const CLA = Loadable({
loader: () => import("./cla/TeamSettingCLA"),
loading: Loading,
});
const CLANew = Loadable({
loader: () => import("./cla/TeamSettingCLAEdit"),
loading: Loading,
});
export default (( props )=>{
const pathname = props.location.pathname;
const OIdentifier = props.match.params.OIdentifier;
const {organizeDetail} = props;
useEffect(()=>{
if(organizeDetail){
const {nickname} = organizeDetail;
@ -48,7 +57,9 @@ export default (( props )=>{
a = 1;
}else if(pathname === `/${OIdentifier}/setting/group`){
a = 3;
}else if(pathname === `/${OIdentifier}/setting/hooks`){
// }else if(pathname === `/${OIdentifier}/setting/hooks`){
// a = 4;
}else if(pathname.indexOf(`/${OIdentifier}/setting/agreement`)>-1){
a = 4;
}
return a;
@ -60,6 +71,7 @@ export default (( props )=>{
{name:'组织成员管理',icon:"icon-zuzhichengyuan",href:`/${OIdentifier}/setting/member`},
{name:'组织团队管理',icon:"icon-zuzhixiangmu",href:`/${OIdentifier}/setting/group`},
// {name:'web',icon:"icon-zhongqingdianxinicon10",href:`/${OIdentifier}/setting/hooks`}
{name:'CLA管理',img:claIcon,href:`/${OIdentifier}/setting/agreement`,hide:organizeDetail && !organizeDetail.enabling_cla}
],
active
}
@ -70,8 +82,29 @@ export default (( props )=>{
</Short>
<Long>
<Gap >
<div style={{border:"1px solid #eee"}}>
<div>
<Switch>
{organizeDetail && organizeDetail.enabling_cla &&
<Route
path="/:OIdentifier/setting/agreement/new"
render={(p) => (
<CLANew {...props} {...p}/>
)}
></Route>
}
{organizeDetail && organizeDetail.enabling_cla && <Route
path="/:OIdentifier/setting/agreement/:claid"
render={(p) => (
<CLANew {...props} {...p}/>
)}
></Route>}
{organizeDetail && organizeDetail.enabling_cla &&<Route
path="/:OIdentifier/setting/agreement"
render={(p) => (
<CLA {...props} {...p}/>
)}
></Route>
}
<Route
path="/:OIdentifier/setting/hooks"
render={() => (

View File

@ -111,7 +111,7 @@ export default (({organizeDetail,history,match})=>{
}).catch(error=>{})
}
return(
<WhiteBack>
<WhiteBack style={{border:"1px solid #eee"}}>
<Title>
<span>组织成员管理</span>
{/* <AlignCenter>

View File

@ -51,7 +51,7 @@ export default Form.create()(forwardRef((props)=>{
})
}
return(
<div>
<div style={{border:"1px solid #eee"}}>
<Title>
<span>组织首页管理</span>
</Title>

View File

@ -0,0 +1,114 @@
import React,{ forwardRef, useState , useEffect } from 'react';
import '../teamSetting.scss';
import RenderHtml from '../../../../components/render-html';
import { Button , Form , Input , Spin } from 'antd';
import { getImageUrl } from 'educoder';
import axios from 'axios';
function CLAinfos(props){
const { form , current_user , showLoginDialog } = props;
const { getFieldDecorator , validateFields , setFieldsValue , getFieldsValue} = form;
const OIdentifier = props.match.params.OIdentifier;
const claid = props.match.params.claid;
const [ details, setDetails ] = useState(undefined);
useEffect(()=>{
if(current_user && current_user.email){
setFieldsValue({
email:current_user.email
})
}
},[current_user,details])
useEffect(()=>{
if(current_user &&!current_user.login){
props.history.push(`/login?go_page=/${OIdentifier}/cla/${claid}`);
}
},[current_user])
useEffect(()=>{
if(claid){
getDetails();
}
},[claid])
function getDetails(){
const url = `/organizations/${OIdentifier}/clas/${claid}.json`;
axios.get(url).then(response=>{
if(response && response.data){
setDetails(response.data);
document.title = `${response.data.name}`;
}
}).catch(error=>{})
}
function agreeFunc(){
validateFields((error,values)=>{
if(!error){
const url = `/users/${current_user && current_user.user_id}/clas.json`;
axios.post(url,{
...values,
cla_id:details && details.id,
email:(current_user && current_user.email) || values.email
}).then(response=>{
if(response && response.data){
props.showNotification('协议签署成功!');
window.scrollTo(0,0);
getDetails();
}
}).catch(error=>{})
}
})
}
return(
details ?
<div className="cla_allinfos">
<div className="cla_infos_header">
<div>
<img src={getImageUrl(details.organization && details.organization.avatar_url)} alt="" />
<div className="cla_i">
<p className="cla_i_name">{details.name}</p>
<p className="cla_o_name">{details.organization && details.organization.nickname}</p>
{details.is_sign && details.cla_sign_email && <p className="cla_i_email">签署邮箱<span className="ml10">{details.cla_sign_email}</span></p> }
</div>
</div>
</div>
<div style={{margin:"0px auto",width:"1200px",paddingBottom:"50px"}}>
{details.is_sign && <div className="wirtes_tips"><span>您已签署协议可在个人设置 &gt; 我的 CLA中查看和管理本协议</span></div> }
<div className="cla_c">
<p className="cla_c_title">协议内容</p>
<RenderHtml className="cla_c_html imageLayerParent" value={details.content} url={props.history.location}/>
</div>
{
!details.is_sign && !details.is_member &&
<Form>
<div className="form_div">
<Form.Item label="真实姓名">
{getFieldDecorator("real_name",{
rules:[{ required: true, message: "请输入您的真实姓名" }]
})(
<Input placeholder="请输入真实姓名" maxLength={30}/>
)}
</Form.Item>
<Form.Item label="签署邮箱">
{getFieldDecorator("email",{
rules:[{ required: true, message: "请输入签署邮箱" }]
})(
<Input placeholder="请输入签署邮箱" maxLength={30} disabled={current_user && current_user.email}/>
)}
</Form.Item>
</div>
<Button type={"primary"} style={{marginTop:"20px"}} onClick={agreeFunc}>接受并签署协议</Button>
</Form>
}
</div>
</div>
:
<div style={{display:'flex',alignItems:'center',justifyContent:"center",minHeight:"500px"}}><Spin /></div>
)
}
export default Form.create()(forwardRef(CLAinfos))

View File

@ -0,0 +1,63 @@
import React,{ useState , useEffect } from 'react';
import Title from '../../../Component/Title';
import defaultimg from '../../images/cla-icon-big.png';
import Logo from '../../images/icon.svg';
import { Button, Spin } from 'antd';
import axios from 'axios';
function TeamSettingCLA(props){
const OIdentifier = props.match.params.OIdentifier;
const [ list , setList ] = useState(undefined);
const [ isSpin , setisSpin ] = useState(false);
useEffect(()=>{
if(OIdentifier){
setisSpin(true);
getList();
}
},[OIdentifier])
function getList(){
const url = `/organizations/${OIdentifier}/clas.json`;
axios.get(url).then(response=>{
if(response){
let l = {...response.data};
setList(l);
setisSpin(false);
}
}).catch(error=>{})
}
return(
<div style={{border:list && list.id ? "none":"1px solid #eee"}}>
<Title>CLA管理</Title>
{
(list && list.id) &&
<ul className="cla_list_box">
<li>
<img src={Logo} width="65px" className="mr20" alt="" />
<div className="cla_list_infos">
<p className="task-hide cla_name">{list.name}</p>
<span className="cla_personCount">签署人数<span>{list.count || 0}</span></span>
<div className="cla_btn">
<Button href={`/${OIdentifier}/cla/${list.key}`} target="_blank" style={{color:"#466aff",borderColor:"#466aff"}}>查看协议</Button>
<Button onClick={()=>{props.history.push(`/${OIdentifier}/setting/agreement/${list.key}`)}} style={{color:"#466aff",borderColor:"#466aff"}} className="ml22">编辑</Button>
</div>
</div>
</li>
</ul>
}
{
(list && !list.id) &&
<div className="cla_default_box">
<img src={defaultimg} alt="" width="68px"/>
<p className="cla_default_desc">CLA Contributor License Agreement指开源贡献者许可协议您可以为本组织配置CLA当非组织用户作为贡献者在组织仓库提交合并请求之前需要签署CLA后才可正常提交合并请求</p>
<Button type={'primary'} onClick={()=>{props.history.push(`/${OIdentifier}/setting/agreement/new`)}}>添加协议</Button>
</div>
}
{ list === undefined && <div style={{minHeight:"280px",display:"flex",alignItems:'center',justifyContent:"center"}}><Spin spinning={isSpin}/></div>}
</div>
)
}
export default TeamSettingCLA;

View File

@ -0,0 +1,133 @@
import React ,{ forwardRef , useState , useEffect } from 'react';
import Title from '../../../Component/Title';
import MdEditor from "../../../../modules/tpm/challengesnew/tpm-md-editor";
import { Button, Form , Input , Switch } from 'antd';
import { turnbar , returnbar } from 'educoder';
import { Link } from 'react-router-dom';
import axios from 'axios';
export default Form.create()(
forwardRef((props)=>{
const { form , match , showNotification , history , location } = props;
const { getFieldDecorator , validateFields , setFieldsValue , getFieldsValue} = form;
const [ switchBtn , setSwitchBtn ] = useState(false);
const [ details , setDetails] = useState(undefined);
const OIdentifier = match.params.OIdentifier;
const claid = match.params.claid;
const keyBefore = window.location.origin+`/${OIdentifier}/cla/`;
useEffect(()=>{
if(claid){
getDetails();
}
},[claid])
function getDetails(){
const url = `/organizations/${OIdentifier}/clas/${claid}.json`;
axios.get(url).then(response=>{
if(response && response.data){
setFieldsValue({
...response.data
})
setSwitchBtn(response.data.pr_need);
setDetails(response.data);
}
}).catch(error=>{})
}
//
function saveClaFunc(){
validateFields((error,values)=>{
if(!error){
const params = {
pr_need:switchBtn,
...values,
key:turnbar(values.key)
}
if(claid){
const url = `/organizations/${OIdentifier}/clas/${claid}.json`;
axios.put(url,{
...params
}).then(response=>{
if(response && response.data){
window.scrollTo(0,0);
showNotification("协议更新成功!");
history.push(`/${OIdentifier}/setting/agreement`);
}
}).catch(error=>{})
}else{
const url = `/organizations/${OIdentifier}/clas.json`;
axios.post(url,{
...params
}).then(response=>{
if(response && response.data && response.data.id){
window.scrollTo(0,0);
showNotification("协议创建成功!");
history.push(`/${OIdentifier}/setting/agreement`);
}
}).catch(error=>{})
}
}
})
}
function checkKeyReg(rule, value, callback){
let reg = /^[a-zA-Z0-9][a-zA-Z0-9_]*[a-zA-Z0-9]+$/g
if(value && !reg.test(value)){
callback("协议标识长度2-30只能以数字、字母开头结尾可包含下划线");
}
callback();
}
return(
<div style={{border:'1px solid #eee'}}>
{
claid ?
<Title><span><Link to={`/${OIdentifier}/setting/agreement`} className="color-blue mr3">CLA管理</Link>&gt;<span className="ml2">编辑协议</span></span></Title>
: <Title>创建CLA</Title>
}
<Form style={{padding:"20px 30px 30px 30px"}}>
<Form.Item label="协议名称">
{getFieldDecorator("name",{
rules:[{ required: true, message: "请输入协议名称" }]
})(
<Input placeholder="请输入协议名称" maxLength={30}/>
)}
</Form.Item>
<Form.Item label="协议标识">
{getFieldDecorator("key",{
rules:[
{ required: true, message: "请输入协议标识" },
{ validator: checkKeyReg }
]
})(
<Input addonBefore={keyBefore} autoComplete={"off"} placeholder="请输入协议标识" minLength={2} maxLength={30}/>
)}
</Form.Item>
<Form.Item label="协议内容">
{getFieldDecorator("content",{
rules:[{ required: true, message: "请输入协议内容" }]
})(
<MdEditor
height={280}
mdID={"cla_md"}
noStorage={true}
placeholder="请输入协议内容"
initValue={details && details.content}
/>
)}
</Form.Item>
<p className="mb10">合并请求准入设置:</p>
<span style={{display:"flex",alignItems:"center"}}>
<Switch checked={switchBtn} onChange={(e)=>setSwitchBtn(e)}/>
<span className={`ml10`} style={{color:switchBtn ? "#466aff" :"#4c5b76"}}>要求 Pull Request 中所有提交者必须签署 CLA 方可合并</span>
</span>
<Button onClick={saveClaFunc} className="mt30" type={"primary"}>确认</Button>
{/* { claid && <Button style={{color:"#ff0c0c",borderColor:"#ff0c0c"}} className="ml20">解除协议</Button>} */}
</Form>
</div>
)
}
))

View File

@ -0,0 +1,132 @@
.cla_default_box{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 320px;
.cla_default_desc{
width: 80%;
color:#666666;
font-size:15px;
line-height:30px;
text-align:center;
margin:20px 0px!important;
}
}
.cla_list_box{
display: flex;
flex-wrap: wrap;
padding-top: 25px;
li{
margin-right: 24px;
border:1px solid;
border-color:rgba(153, 153, 153, 0.3);
border-radius:6px;
width: 446px;
display: flex;
padding:30px 26px;
box-sizing: border-box;
justify-content: flex-start;
align-items: flex-start;
margin-bottom: 25px;
&:nth-child(2n){
margin-right: 0px;
}
.cla_list_infos{
flex:1;
width: 0;
.cla_name{
color:#151d40;
font-size:16px;
padding-bottom:20px;
height: 22px;
line-height: 22px;
word-break: break-all;
margin-bottom: 14px!important;
}
.cla_personCount{
color:#4c5876;
display: block;
margin-bottom: 15px!important;
span{
margin-left: 10px;
}
}
}
}
}
.cla_allinfos{
.cla_infos_header{
background-color:rgba(70, 106, 255, 0.05);
padding:30px 0px;
&>div{
display: flex;
align-items: center;
margin:0px auto;
width: 1200px;
&>img{
height: 94px;
margin-right: 32px;
border-radius: 4px;
}
.cla_i{
flex: 1;
width: 0;
.cla_i_name{
font-weight:700;
color:#333333;
font-size:24px;
height: 33px;
line-height: 33px;
margin-bottom: 0px!important;
}
.cla_o_name,.cla_i_email{
color:#4c5b76;
font-size:15px;
height: 20px;
line-height: 20px;
margin-top: 18px;
}
.cla_i_email{
margin-top: 12px;
}
}
}
}
.wirtes_tips{
margin:25px 0px;
span{
padding:9px 18px;
font-size:15px;
border:1px solid;
border-radius:6px;
border-color:#009b28;
color: #009b28;
}
}
.cla_c{
width: 100%;
background-color:rgba(245, 245, 245, 0.5);
border-radius: 2px;
padding:25px;
margin:16px auto;
.cla_c_title{
font-weight:700;
color:#202d40;
font-size:15px;
padding-bottom:20px;
height: 22px;
line-height: 22px;
margin-bottom: 15px!important;
}
}
.form_div{
display: flex;
&>div{
flex:1
}
&>div:first-child{
margin-right: 40px;
}
}
}

View File

@ -6,7 +6,6 @@ import { setSeoMeta } from "educoder";
import Cards from '../../Component/Cards';
import axios from 'axios';
import '../Index.scss';
const GroupDetails = Loadable({
loader: () => import("../Group/GroupDetails"),
loading: Loading,
@ -40,6 +39,9 @@ function Detail(props){
if(pathname){
if(pathname.indexOf(`/${OIdentifier}/setting`)>-1){
setFlag(false);
if(detail && !detail.is_admin ){
props.history.push("/nopage");
}
}else{
setFlag(true);
}
@ -49,7 +51,7 @@ function Detail(props){
setButtonflagFlag(false);
}
}
},[pathname])
},[pathname,detail])
useEffect(()=>{
if(OIdentifier){
@ -111,7 +113,7 @@ function Detail(props){
}
/>
}
<Switch {...props}>
<Switch>
{/* 组织团队-详情 */}
<Route
@ -134,12 +136,14 @@ function Detail(props){
return <Group {...props} {...p} organizeDetail={detail}/>
}}
></Route>
<Route
path="/:OIdentifier/setting"
render={(p) => {
return <Setting {...props} {...p} organizeDetail={detail} updateFunc={updateDetail}/>
}}
></Route>
<Route
path="/:OIdentifier/setting"
render={(p) => {
return <Setting {...props} {...p} organizeDetail={detail} updateFunc={updateDetail}/>
}}
></Route>
<Route
path="/:OIdentifier"
render={(p) => {

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" width="65.368" height="70.129" viewBox="0 0 65.368 70.129">
<g id="icon" transform="translate(-680.606 -410)">
<path id="路径_3391" data-name="路径 3391" d="M190.644,100.032H130.724A2.724,2.724,0,0,0,128,102.756v64.682a2.724,2.724,0,0,0,2.724,2.724h59.921a2.724,2.724,0,0,0,2.724-2.724V102.756A2.724,2.724,0,0,0,190.644,100.032Z" transform="translate(552.606 309.968)" fill="#e6e6e6"/>
<rect id="矩形_367" data-name="矩形 367" width="49" height="53" transform="translate(689 419)" fill="#fff"/>
<g id="协议" transform="translate(687.555 417.455)">
<path id="路径_3391-2" data-name="路径 3391" d="M177.325,100.032H130.145A2.145,2.145,0,0,0,128,102.177v50.93a2.145,2.145,0,0,0,2.145,2.145h47.181a2.145,2.145,0,0,0,2.145-2.145v-50.93A2.145,2.145,0,0,0,177.325,100.032Zm1.187,1.058v53.25H129.125V101.09Z" transform="translate(-128 -100.032)" fill="#aeb4bb"/>
<path id="路径_3392" data-name="路径 3392" d="M308.3,544v4.289H288V544Zm-11.6,8.578v4.289H288v-4.289Z" transform="translate(-280.604 -510.521)" fill="#c4c7cc"/>
<text id="CLA" transform="translate(25.444 22.609)" fill="#5f6872" stroke="rgba(0,0,0,0)" stroke-width="1" font-size="17" font-family="STFangsong"><tspan x="-16.023" y="0">CLA</tspan></text>
</g>
<path id="icon_徽章" d="M204.579,70.459a6.149,6.149,0,1,0-10.543,4.3,6.22,6.22,0,0,0,.855.729v5a.194.194,0,0,0,.278.174l3.246-1.569,3.246,1.569a.194.194,0,0,0,.278-.174V75.509a6.212,6.212,0,0,0,.837-.7A6.108,6.108,0,0,0,204.579,70.459Zm-11.071,0a4.922,4.922,0,1,1,4.922,4.922A4.928,4.928,0,0,1,193.507,70.459Zm7.206,8.384-2.213-1.07a.194.194,0,0,0-.168,0l-2.213,1.07V76.16a6.19,6.19,0,0,0,4.6.011v2.672Z" transform="translate(526.76 384.613)" fill="#009b28"/>
<circle id="椭圆_67" data-name="椭圆 67" cx="3" cy="3" r="3" transform="translate(722 452)" fill="rgba(0,155,40,0.31)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -260,7 +260,6 @@ form{
margin-bottom: 12px;
border-radius:2px;
background-color: #fff;
padding: 20px 0;
border:1px solid rgba(79, 108, 188, 0.21);
&>li{
font-size: 15px;

View File

@ -222,4 +222,13 @@ export function deleteApplyInfo(data) {
method: 'post',
data
});
}
}
// 判断课题是否有学生报名
export async function findStudentTaskByTaskId(params) {
return await fetch({
url: '/api/applyInformation/findStudentTaskByTaskId',
method: 'get',
params: params,
});
}

View File

@ -1,7 +1,7 @@
import React , { useCallback, useEffect , useState } from 'react';
import { Button, Form, Input, Select, Upload, message, Popconfirm } from 'antd';
import {getUploadActionUrl} from 'educoder';
import {applyGlcc, updateApplyGlcc} from '../api';
import {applyGlcc, findStudentTaskByTaskId, updateApplyGlcc} from '../api';
import TextArea from 'antd/lib/input/TextArea';
import { main_site_url } from '../fetch';
import TaskApply from './taskApply';
@ -13,7 +13,7 @@ function EditRepoApplyInfo(props) {
const {form, applyInfo, current_user, round, cateList, setReload, isAddRepo, setIsAddRepo, setIsEdit, setEditRepoCount} = props;
const phonereg = /(^|\s*\+?0?0?86|\D)(1\d{2})[-\s]{0,3}(\d{4})[-\s]{0,3}(\d{4})(?=\D|$)/
const emailreg = /^[a-zA-Z0-9]+([.\-_\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/
const {getFieldDecorator, validateFields, setFieldsValue, validateFieldsAndScroll } = form;
const {getFieldDecorator, validateFields, setFieldsValue, validateFieldsAndScroll, getFieldValue } = form;
const [imageUrl, setImageUrl] = useState(undefined);
// loading
const [loading, setLoading] = useState(false);
@ -27,7 +27,7 @@ function EditRepoApplyInfo(props) {
const taskCountArr = []
registrationTaskList.map((item, index)=>{
taskCountArr.push(index+1);
const{taskName, taskUrl, taskDifficulty, taskReward, tutorName, tutorMail, taskDesc, tutorPhone} = item;
const{taskName, taskUrl, taskDifficulty, taskReward, tutorName, tutorMail, taskDesc, tutorPhone, id} = item;
taskInfo[`taskName${index+1}`] = taskName;
taskInfo[`taskUrl${index+1}`] = taskUrl;
taskInfo[`taskDifficulty${index+1}`] = taskDifficulty;
@ -36,6 +36,7 @@ function EditRepoApplyInfo(props) {
taskInfo[`tutorMail${index+1}`] = tutorMail;
taskInfo[`taskDesc${index+1}`] = taskDesc;
taskInfo[`tutorPhone${index+1}`] = tutorPhone;
taskInfo[`taskId${index+1}`] = id;
})
setTaskCount(taskCountArr);
setTimeout(() => {
@ -76,6 +77,7 @@ function EditRepoApplyInfo(props) {
const registrationTaskList = []
taskCount && taskCount.map(item=>{
registrationTaskList.push({
id: values[`taskId${item}`],
taskName: values[`taskName${item}`],
taskUrl: values[`taskUrl${item}`],
taskDifficulty: values[`taskDifficulty${item}`],
@ -157,14 +159,19 @@ function EditRepoApplyInfo(props) {
//
function addTaskApplyForm(){
setTaskCount(taskCount.concat([taskCount.length+1]));
setTaskCount(taskCount.concat([taskCount[taskCount.length-1]+1]));
}
//
function deleteTaskApply(index){
const arr = [...taskCount];
arr.splice(index-1, 1)
setTaskCount(arr);
async function deleteTaskApply(index){
const delTaskId = getFieldValue(`taskId${index}`);
const res = delTaskId ? await findStudentTaskByTaskId({ taskId: delTaskId }) : "newTask";
if((res && res.data && res.data.length)){
message.error("已有学生报名,不允许删除");
}else{
const arr = taskCount.filter(item=>item!== index);
setTaskCount(arr);
}
}
//
@ -267,7 +274,7 @@ function EditRepoApplyInfo(props) {
<div className='taskApplyTitle font-16'>课题信息<span>{index+1}</span>/5)</div>
{index>0 && <Button type="link" onClick={()=>{deleteTaskApply(item)}}><i className='iconfont icon-shanchuicon3 mr5 font-15'></i>删除</Button>}
</FlexAJ>
<TaskApply helper={helper} taskKey={item} phonereg={phonereg} emailreg={emailreg}/>
<TaskApply helper={helper} taskKey={item} phonereg={phonereg} emailreg={emailreg} getFieldDecorator={getFieldDecorator}/>
</div>
})}
{taskCount && taskCount.length<5 && <div>

View File

@ -4,7 +4,7 @@ import TextArea from 'antd/lib/input/TextArea';
const Option = Select.Option;
//
function TaskApply(props) {
const {helper, disabled, taskKey, phonereg, emailreg} = props;
const {helper, disabled, taskKey, phonereg, emailreg, getFieldDecorator} = props;
//
const difficultyList = [
{id: 1, name: "低"},
@ -26,6 +26,9 @@ function TaskApply(props) {
return(
<div className="taskApplyForm">
<div className="pt20 glcc_info_form">
<Form.Item label="课题id" hidden>
{getFieldDecorator(`taskId${taskKey}`, {})(<Input/>)}
</Form.Item>
{helper('课题名称',
'',
`taskName${taskKey}`,