Merge pull request '更新' (#527) from durian/forgeplus-react:gitlink_server into gitlink_server
This commit is contained in:
commit
5a85289dba
|
|
@ -150,6 +150,11 @@ const Badge = Loadable({
|
|||
// loading: Loading,
|
||||
// })
|
||||
|
||||
const Topic = Loadable({
|
||||
loader: () => import('./forge/topic'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
// 此处仅维护前端可能的一级路由,不用进行项目或者组织判断的字段。
|
||||
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", "softbot"];
|
||||
|
||||
|
|
@ -390,6 +395,9 @@ class App extends Component {
|
|||
{/* 查询 */}
|
||||
<Route path="/search" component={Search} mygetHelmetapi={mygetHelmetapi}/>
|
||||
|
||||
{/* 项目标签列表页 */}
|
||||
<Route path="/explore/topic/:id/:name" component={Topic} mygetHelmetapi={mygetHelmetapi}/>
|
||||
|
||||
<Route exact path="/explore/all"
|
||||
render={
|
||||
(props) => (
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ class PullRefresh extends Component {
|
|||
{children}
|
||||
|
||||
{
|
||||
count < 1 && <Nodata _html="暂无未读消息"/>
|
||||
count < 1 && <Nodata _html="暂无未读消息" small/>
|
||||
}
|
||||
|
||||
{/* 大于分页数据才显示loading */}
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 115 KiB |
|
|
@ -592,7 +592,7 @@ function CoderDepot(props){
|
|||
{desc && <p className="font-14 color-grey-3 mb15 task-hide-2" style={{lineHeight:"24px",WebkitLineClamp:"4",textAlign:"justify",wordBreak:"break-all"}} title={desc}>{desc}</p>}
|
||||
{/* 项目标签 */}
|
||||
{topics && <div className='projectHomeTopics'>
|
||||
{topics.map(item=><span className='proHomeTopic mr15 mb10 font-13 task-hide'>{item.name}</span>)}
|
||||
{topics.map(item=><Link to={`/explore/topic/${item.id}/${encodeURIComponent(item.name)}`} className='proHomeTopic mr15 mb10 font-13 task-hide'>{item.name}</Link>)}
|
||||
</div>}
|
||||
{
|
||||
website &&
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ class IndexItem extends Component {
|
|||
<p className="break_word task-hide-2 mt10" style={{ maxHeight: "44px",lineHeight:"22px" }}>{item.description}</p>
|
||||
{/* 项目标签 */}
|
||||
{item.topics && <div className='projectHomeTopics myProList'>
|
||||
{item.topics.map(topic=><span className='proHomeTopic mr15 font-13 task-hide'>{topic.name}</span>)}
|
||||
{item.topics.map(topic=><Link to={`/explore/topic/${topic.id}/${encodeURIComponent(topic.name)}`} className='proHomeTopic mr15 font-13 task-hide'>{topic.name}</Link>)}
|
||||
</div>}
|
||||
<div className="p-r-about">
|
||||
<span className="p-r-detail">
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ function Index() {
|
|||
</div>
|
||||
{/* 项目标签 */}
|
||||
{i.topics && <div className='proListTopics'>
|
||||
{i.topics.map(item=><span className='proListTopic mr15 font-13 task-hide'>{item.name}</span>)}
|
||||
{i.topics.map(item=><Link to={`/explore/topic/${item.id}/${encodeURIComponent(item.name)}`} className='proListTopic mr15 font-13 task-hide'>{item.name}</Link>)}
|
||||
</div>}
|
||||
<div className="item-data">
|
||||
{i.category && i.category.id ? <span className="category">{i.category.name}</span> :"" }
|
||||
|
|
|
|||
|
|
@ -37,10 +37,13 @@ function UpdateDescModal({form , visible , onCancel , onOk,desc,website,lesson_u
|
|||
// 校验项目标签
|
||||
function checkTopics(rule, value, callback){
|
||||
const boolLength = value.filter(item=>item.length > 20);
|
||||
const bool = value.filter(item=>!/^(?![-])[a-zA-Z0-9\u4e00-\u9fa5\\-]+$/.test(item));
|
||||
if(value.length > 5){
|
||||
callback("最多创建5个标签");
|
||||
}else if(boolLength.length){
|
||||
callback("每个标签最多支持20个汉字");
|
||||
}else if(bool.length){
|
||||
callback("标签名只允许包含中文、字母、数字或者中划线(-), 不能以中划线开头");
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ function MilepostDetail(props){
|
|||
<i className="iconfont icon-a-31shijian font-14 mr5 color-grey-89"></i>
|
||||
<span className="color-grey-89">{milepost.effective_date || '暂无截止时间'}</span>
|
||||
</span>
|
||||
{(milepost.percent || milepost.percent===0) ? <span style={{color: '#1aaf42'}}> {milepost.percent > 0 ? milepost.percent.toFixed(2)*100:milepost.percent}%完成 </span> :"" }
|
||||
{(milepost.percent || milepost.percent===0) ? <span style={{color: '#1aaf42'}}> {milepost.percent > 0 ? parseInt(milepost.percent*100):milepost.percent}%完成 </span> :"" }
|
||||
</p>
|
||||
</div>
|
||||
<div className="milepostdiv">
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { Link } from "react-router-dom";
|
|||
import { getInstallBot, updateInstallBot } from "../../../../softbot/api";
|
||||
import { getImageUrl } from 'educoder';
|
||||
import './index.scss';
|
||||
import '../exploit/index.scss';
|
||||
|
||||
function DispositionDetail(props){
|
||||
const {match:{params:{id}}, current_user, current_user:{login, user_id}} = props;
|
||||
|
|
@ -16,6 +17,8 @@ function DispositionDetail(props){
|
|||
// bot状态
|
||||
const [botState, setBotState] = useState(true);
|
||||
const [error, setError] = useState(false);
|
||||
// 保存按钮loading效果
|
||||
const [saveLoading, setSaveLoading] = useState(false);
|
||||
|
||||
// 获取bot详情
|
||||
useEffect(()=>{
|
||||
|
|
@ -122,6 +125,7 @@ function DispositionDetail(props){
|
|||
repo_owner_map[item.id+""] = login;
|
||||
});
|
||||
}
|
||||
setSaveLoading(true);
|
||||
current_user && updateInstallBot({
|
||||
bot_id: id,
|
||||
password: "",
|
||||
|
|
@ -134,6 +138,7 @@ function DispositionDetail(props){
|
|||
installed_store_list: botDetail.store_id_list
|
||||
}).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
setSaveLoading(false);
|
||||
setSelectValue([]);
|
||||
const repoIds = new Set();
|
||||
options.map(item=>{
|
||||
|
|
@ -143,6 +148,7 @@ function DispositionDetail(props){
|
|||
getInsBotDetail(repoIds);
|
||||
message.success('更改成功');
|
||||
}else{
|
||||
setSaveLoading(false);
|
||||
message.error(res.message);
|
||||
}
|
||||
})
|
||||
|
|
@ -154,7 +160,10 @@ function DispositionDetail(props){
|
|||
{botDetail && <div className="oneLine">
|
||||
<img src={getImageUrl(botDetail.logo)} alt="" className="botImg imgBox"/>
|
||||
<div className="ml40 font-16">
|
||||
<div className="font-22"><a href={`/softbot/${id}`}>{botDetail && botDetail.market_name}</a></div>
|
||||
<div className="font-22">
|
||||
<a href={`/softbot/${id}`}>{botDetail && botDetail.market_name}</a>
|
||||
<span className={`font-12 ml10 statusBot myInstallBotStatus ${botDetail.is_public ? 'public' : 'private'}`}>{botDetail.is_public ? '公开' : '私有'}</span>
|
||||
</div>
|
||||
<a className="mr30 color88" href={`/${botDetail.register_login}`}>{botDetail && botDetail.register_name}</a>
|
||||
<span className="color88">{botDetail && botDetail.create_time}</span>
|
||||
</div>
|
||||
|
|
@ -192,7 +201,7 @@ function DispositionDetail(props){
|
|||
<span className='changeStatusTip'>{botState ? '开始使用该Bot,授予该Bot所需的仓库访问权限' : '暂停停止使用该bot,阻塞该bot拥有的仓库访问权限'}</span>
|
||||
</div>
|
||||
</div>
|
||||
{botDetail && !botDetail.is_public && login !== botDetail.register_login ? <Tooltip title="该bot为私有bot,仅bot开发者有权限更改"><Button type="primary" style={{width: '100px', height: '36px'}} className="mt30" disabled>保存</Button></Tooltip> : <Button type="primary" style={{width: '100px', height: '36px'}} className="mt30" onClick={updateInsBot}>保存</Button>}
|
||||
{botDetail && !botDetail.is_public && login !== botDetail.register_login ? <Tooltip title="该bot为私有bot,仅bot开发者有权限更改"><Button type="primary" style={{width: '100px', height: '36px'}} className="mt30" disabled>保存</Button></Tooltip> : <Button type="primary" style={{width: '100px', height: '36px'}} className="mt30" onClick={updateInsBot} loading={saveLoading}>保存</Button>}
|
||||
</div>
|
||||
</Spin>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,6 +106,10 @@
|
|||
}
|
||||
}
|
||||
.changeStatusTip{color:#99a2af;}
|
||||
.myInstallBotStatus{
|
||||
position: relative;
|
||||
top: -4px;
|
||||
}
|
||||
}
|
||||
.nullBotsBox{
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import { Button, Checkbox, Form, Input, message, Select } from "antd";
|
||||
import { Button, Checkbox, Form, Input, message, Select, Row, Col } from "antd";
|
||||
import { useState } from "react";
|
||||
import '../../new/index.scss';
|
||||
import './index.scss';
|
||||
|
|
@ -13,12 +13,14 @@ function Jurisdiction(props){
|
|||
const resource = [{value: 2, name: '无权限'}, {value: 0, name: '只读'}, {value: 1, name: '读写'}];
|
||||
const [codeSelectValue , setCodeSelectValue ] = useState(undefined);
|
||||
const [prSelectValue , setPrSelectValue ] = useState(undefined);
|
||||
const [ issueSelectValue , setIssueSelectValue ] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
if(botDetail){
|
||||
const {limit_and_events:{juris_diction_code, juris_diction_pr}} = botDetail
|
||||
const {limit_and_events:{juris_diction_code, juris_diction_pr, juris_diction_issue}} = botDetail
|
||||
setCodeSelectValue(juris_diction_code);
|
||||
setPrSelectValue(juris_diction_pr);
|
||||
setIssueSelectValue(juris_diction_issue);
|
||||
}
|
||||
}, [botDetail])
|
||||
|
||||
|
|
@ -32,6 +34,11 @@ function Jurisdiction(props){
|
|||
return e;
|
||||
}
|
||||
|
||||
function issueControl(e){
|
||||
setIssueSelectValue(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
// 修改注册bot的权限
|
||||
function submit(e){
|
||||
e.preventDefault();
|
||||
|
|
@ -39,15 +46,17 @@ function Jurisdiction(props){
|
|||
if(err){
|
||||
return;
|
||||
}
|
||||
const {juris_diction_code, juris_diction_pr, event_code, event_pr} = fieldsValue;
|
||||
const {juris_diction_code, juris_diction_pr, event_code, event_pr, event_issue, juris_diction_issue} = fieldsValue;
|
||||
const params = {
|
||||
...botDetail,
|
||||
bot_id: parseInt(id),
|
||||
limit_and_events: {
|
||||
event_code: event_code ? event_code.toString() : "",
|
||||
event_pr: event_pr ? event_pr.toString() : "",
|
||||
event_code: event_code ? event_code.toString() : '',
|
||||
event_pr: event_pr ? event_pr.toString() : '',
|
||||
event_issue: event_issue ? event_issue.toString(): '',
|
||||
juris_diction_code,
|
||||
juris_diction_pr
|
||||
juris_diction_pr,
|
||||
juris_diction_issue
|
||||
},
|
||||
user_id: current_user && current_user.user_id,
|
||||
}
|
||||
|
|
@ -64,8 +73,8 @@ function Jurisdiction(props){
|
|||
|
||||
// 校验仓库访问权限
|
||||
function jurisDictionPrValidator(rule, value, callback){
|
||||
const {juris_diction_code} = getFieldsValue();
|
||||
if(juris_diction_code === 2 && value === 2){
|
||||
const {juris_diction_code, juris_diction_pr} = getFieldsValue();
|
||||
if(juris_diction_code === 2 && value === 2 && juris_diction_pr === 2){
|
||||
callback('请至少选择一个权限');
|
||||
return;
|
||||
}
|
||||
|
|
@ -73,21 +82,13 @@ function Jurisdiction(props){
|
|||
}
|
||||
|
||||
// 校验用户必选一个订阅事件
|
||||
function eventValidator(rule, value, callback, type){
|
||||
const {event_code, event_pr} = getFieldsValue();
|
||||
const resourceValue = resource[0].value;
|
||||
const boolPr = (event_code === undefined || (event_code && event_code.length === 0)) && prSelectValue === resourceValue;
|
||||
const boolCode = (event_pr === undefined || (event_pr && event_pr.length === 0)) && codeSelectValue === resourceValue;
|
||||
const bool = prSelectValue !== resourceValue && codeSelectValue !== resourceValue && (event_pr === undefined || (event_pr && event_pr.length === 0)) && (event_code === undefined || (event_code && event_code.length === 0))
|
||||
if(boolPr || boolCode){
|
||||
callback("请至少选择一个订阅");
|
||||
return;
|
||||
function eventValidator(rule, value, callback){
|
||||
const {event_code, event_pr, event_issue} = getFieldsValue();
|
||||
if(event_code || event_pr || event_issue){
|
||||
callback();
|
||||
}else{
|
||||
callback("请至少选择一个订阅事件");
|
||||
}
|
||||
if(bool && type === "pr"){
|
||||
callback("请至少选择一个订阅");
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
return <Form className="createExploitForm jurisdictionBox" onSubmit={submit}>
|
||||
|
|
@ -104,7 +105,16 @@ function Jurisdiction(props){
|
|||
<div className="dashedBor"></div>
|
||||
<Form.Item label="合并请求(PR)权限" className="resourceBox botItem">
|
||||
<div className="color-99">合并请求的打开、关闭、编辑、分配</div>
|
||||
{getFieldDecorator("juris_diction_pr",{rules:[{validator: jurisDictionPrValidator}],initialValue: botDetail.limit_and_events.juris_diction_pr, getValueFromEvent: prControl})(
|
||||
{getFieldDecorator("juris_diction_pr",{initialValue: botDetail.limit_and_events.juris_diction_pr, getValueFromEvent: prControl})(
|
||||
<Select>
|
||||
{resource.map(item=>{return <Option value={item.value} key={item.value}>{item.name}</Option>})}
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
<div className="dashedBor"></div>
|
||||
<Form.Item label="疑修(Issue)权限" className="resourceBox botItem">
|
||||
<div className="color-99">疑修的打开、关闭、编辑、分配、评论</div>
|
||||
{getFieldDecorator("juris_diction_issue",{rules:[{validator: jurisDictionPrValidator}],initialValue: botDetail.limit_and_events.juris_diction_issue, getValueFromEvent: issueControl})(
|
||||
<Select>
|
||||
{resource.map(item=>{return <Option value={item.value} key={item.value}>{item.name}</Option>})}
|
||||
</Select>
|
||||
|
|
@ -114,12 +124,14 @@ function Jurisdiction(props){
|
|||
{codeSelectValue !== resource[0].value && <Form.Item label="代码库事件" className="botItem width50 checkBox three dashedBorder">
|
||||
{getFieldDecorator("event_code", {
|
||||
initialValue: botDetail.limit_and_events.event_code ? botDetail.limit_and_events.event_code.split(',').map(item=>item = parseInt(item)) : [],
|
||||
rules:[{validator: (rule, value, callback)=>{eventValidator(rule, value, callback, "code")}}]
|
||||
rules:[{validator: (rule, value, callback)=>{eventValidator(rule, value, callback)}}]
|
||||
})(
|
||||
<Checkbox.Group>
|
||||
<Checkbox value={0}>推送<br/><span className="color-99">git推送到存储库</span></Checkbox>
|
||||
<Checkbox value={1}>创建<br/><span className="color-99">创建分支或标签</span></Checkbox>
|
||||
<Checkbox value={2}>删除<br/><span className="color-99">删除分支或标签</span></Checkbox>
|
||||
<Row>
|
||||
<Col span={16}><Checkbox value={0}>推送<br/><span className="color-99">git推送到存储库</span></Checkbox></Col>
|
||||
<Col span={8}><Checkbox value={1}>创建<br/><span className="color-99">创建分支或标签</span></Checkbox></Col>
|
||||
<Col span={16}><Checkbox value={2}>删除<br/><span className="color-99">删除分支或标签</span></Checkbox></Col>
|
||||
</Row>
|
||||
</Checkbox.Group>
|
||||
)}
|
||||
</Form.Item>}
|
||||
|
|
@ -127,11 +139,30 @@ function Jurisdiction(props){
|
|||
{prSelectValue !== resource[0].value && <Form.Item label="合并请求事件" className="botItem width50 checkBox">
|
||||
{getFieldDecorator("event_pr", {
|
||||
initialValue: botDetail.limit_and_events.event_pr ? botDetail.limit_and_events.event_pr.split(',').map(item=>item = parseInt(item)) : [],
|
||||
rules:[{validator: (rule, value, callback)=>{eventValidator(rule, value, callback, "pr")}}]
|
||||
rules:[{validator: (rule, value, callback)=>{eventValidator(rule, value, callback)}}]
|
||||
})(
|
||||
<Checkbox.Group>
|
||||
<Checkbox value={3}>合并请求<br/><span className="color-99">合并请求被打开、被关闭、被重新打开或被编辑</span></Checkbox>
|
||||
<Checkbox value={4}>合并请求分配<br/><span className="color-99">合并请求被分配或取消分配</span></Checkbox>
|
||||
<Row>
|
||||
<Col span={16}><Checkbox value={3}>合并请求<br/><span className="color-99">合并请求被打开、被关闭或被编辑</span></Checkbox></Col>
|
||||
<Col span={8}><Checkbox value={4}>合并请求分配<br/><span className="color-99">合并请求被分配或取消分配</span></Checkbox></Col>
|
||||
<Col span={16}><Checkbox value={6}>合并请求被评论<br/><span className="color-99">合并请求评论被创建或删除</span></Checkbox></Col>
|
||||
</Row>
|
||||
</Checkbox.Group>
|
||||
)}
|
||||
</Form.Item>}
|
||||
{(codeSelectValue !== resource[0].value || prSelectValue !== resource[0].value) && issueSelectValue !== resource[0].value && <div className="dashedBor"></div>}
|
||||
{issueSelectValue !== resource[0].value && <Form.Item label="疑修事件" className="botItem width50 checkBox three">
|
||||
{getFieldDecorator("event_issue",{
|
||||
initialValue: botDetail.limit_and_events.event_issue ? botDetail.limit_and_events.event_issue.split(',').map(item=>item = parseInt(item)) : [],
|
||||
rules:[{validator: (rule, value, callback)=>{eventValidator(rule, value, callback)}}]
|
||||
})(
|
||||
<Checkbox.Group>
|
||||
<Row>
|
||||
<Col span={16}><Checkbox value={7}>疑修<br/><span className="color-99">疑修已打开、已关闭、已重新打开或编辑</span></Checkbox></Col>
|
||||
<Col span={8}><Checkbox value={8}>疑修已指派<br/><span className="color-99">疑修已被指派或取消指派</span></Checkbox></Col>
|
||||
<Col span={16}><Checkbox value={9}>已标记疑修<br/><span className="color-99">疑修标记被更新或清除</span></Checkbox></Col>
|
||||
<Col span={8}><Checkbox value={10}>疑修评论<br/><span className="color-99">疑修评论被创建、编辑或删除</span></Checkbox></Col>
|
||||
</Row>
|
||||
</Checkbox.Group>
|
||||
)}
|
||||
</Form.Item>}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { forwardRef, useState, useEffect } from "react";
|
||||
import { Button, Checkbox, Form, Input, message, Radio, Select } from "antd";
|
||||
import { Button, Checkbox, Col, Form, Input, message, Radio, Row, Select } from "antd";
|
||||
import { Link } from "react-router-dom";
|
||||
import MDEditor from '../../../../../modules/tpm/challengesnew/tpm-md-editor';
|
||||
import './index.scss';
|
||||
|
|
@ -14,7 +14,8 @@ function Disposition(props){
|
|||
const [ content , setContent ] = useState(undefined);
|
||||
const [ codeSelectValue , setCodeSelectValue ] = useState(resource[0].value);
|
||||
const [ prSelectValue , setPrSelectValue ] = useState(resource[0].value);
|
||||
|
||||
const [ issueSelectValue , setIssueSelectValue ] = useState(resource[0].value);
|
||||
|
||||
useEffect(()=>{
|
||||
document.title='注册新的Bot';
|
||||
}, [])
|
||||
|
|
@ -33,6 +34,11 @@ function Disposition(props){
|
|||
return e;
|
||||
}
|
||||
|
||||
function issueControl(e){
|
||||
setIssueSelectValue(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
// webhooks校验
|
||||
function checkAddr(rule, value, callback) {
|
||||
let reg = /(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?/;
|
||||
|
|
@ -47,8 +53,8 @@ function Disposition(props){
|
|||
|
||||
// 校验仓库访问权限
|
||||
function jurisDictionPrValidator(rule, value, callback){
|
||||
const {juris_diction_code} = getFieldsValue();
|
||||
if(juris_diction_code === 2 && value === 2){
|
||||
const {juris_diction_code, juris_diction_pr} = getFieldsValue();
|
||||
if(juris_diction_code === 2 && value === 2 && juris_diction_pr === 2){
|
||||
callback('请至少选择一个权限');
|
||||
return;
|
||||
}
|
||||
|
|
@ -56,21 +62,13 @@ function Disposition(props){
|
|||
}
|
||||
|
||||
// 校验用户必选一个订阅事件
|
||||
function eventValidator(rule, value, callback, type){
|
||||
const {event_code, event_pr} = getFieldsValue();
|
||||
const resourceValue = resource[0].value;
|
||||
const boolPr = (event_code === undefined || (event_code && event_code.length === 0)) && prSelectValue === resourceValue;
|
||||
const boolCode = (event_pr === undefined || (event_pr && event_pr.length === 0)) && codeSelectValue === resourceValue;
|
||||
const bool = prSelectValue !== resourceValue && codeSelectValue !== resourceValue && (event_pr === undefined || (event_pr && event_pr.length === 0)) && (event_code === undefined || (event_code && event_code.length === 0))
|
||||
if(boolPr || boolCode){
|
||||
callback("请至少选择一个订阅");
|
||||
return;
|
||||
function eventValidator(rule, value, callback){
|
||||
const {event_code, event_pr, event_issue} = getFieldsValue();
|
||||
if(event_code || event_pr || event_issue){
|
||||
callback();
|
||||
}else{
|
||||
callback("请至少选择一个订阅事件");
|
||||
}
|
||||
if(bool && type === "pr"){
|
||||
callback("请至少选择一个订阅");
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
// 注册新的bot
|
||||
|
|
@ -80,17 +78,19 @@ function Disposition(props){
|
|||
if(err){
|
||||
return;
|
||||
}
|
||||
const {bot_name, bot_des, webhook, is_public, juris_diction_code, juris_diction_pr, event_code, event_pr, oauth_callback_url} = fieldsValue;
|
||||
const {bot_name, bot_des, webhook, is_public, juris_diction_code, juris_diction_pr, event_code, event_pr, oauth_callback_url, event_issue, juris_diction_issue} = fieldsValue;
|
||||
const param = {
|
||||
bot_name, bot_des, webhook, is_public,
|
||||
login: current_user && current_user.login,
|
||||
user_id: current_user && current_user.user_id,
|
||||
logo: `/api/attachments/${window.location.host !== 'www.gitlink.org.cn' ? 347246 : 412603}`,
|
||||
limit_and_events: {
|
||||
event_code: event_code ? event_code.toString() : "",
|
||||
event_pr: event_pr ? event_pr.toString() : "",
|
||||
event_code: event_code ? event_code.toString() : '',
|
||||
event_pr: event_pr ? event_pr.toString() : '',
|
||||
event_issue: event_issue ? event_issue.toString(): '',
|
||||
juris_diction_code,
|
||||
juris_diction_pr
|
||||
juris_diction_pr,
|
||||
juris_diction_issue
|
||||
},
|
||||
oauth_callback_url
|
||||
}
|
||||
|
|
@ -157,32 +157,61 @@ function Disposition(props){
|
|||
<div className="dashedBor"></div>
|
||||
<Form.Item label="合并请求(PR)权限" className="resourceBox botItem">
|
||||
<div className="color-99">合并请求的打开、关闭、编辑、分配</div>
|
||||
{getFieldDecorator("juris_diction_pr",{rules:[{validator: jurisDictionPrValidator}],initialValue: resource[0].value, getValueFromEvent: prControl})(
|
||||
{getFieldDecorator("juris_diction_pr",{initialValue: resource[0].value, getValueFromEvent: prControl})(
|
||||
<Select>
|
||||
{resource.map(item=>{return <Option value={item.value} key={item.value}>{item.name}</Option>})}
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
<div className="dashedBor"></div>
|
||||
<Form.Item label="疑修(Issue)权限" className="resourceBox botItem">
|
||||
<div className="color-99">疑修的打开、关闭、编辑、分配、评论</div>
|
||||
{getFieldDecorator("juris_diction_issue",{rules:[{validator: jurisDictionPrValidator}],initialValue: resource[0].value, getValueFromEvent: issueControl})(
|
||||
<Select>
|
||||
{resource.map(item=>{return <Option value={item.value} key={item.value}>{item.name}</Option>})}
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
{(codeSelectValue !== resource[0].value || prSelectValue !== resource[0].value) && <div className="resourceTitle font-16 pb15 mb10">订阅事件</div>}
|
||||
{codeSelectValue !== resource[0].value && <Form.Item label="代码库事件" className="botItem width50 checkBox three dashedBorder">
|
||||
{codeSelectValue !== resource[0].value && <Form.Item label="代码库事件" className="botItem width50 checkBox dashedBorder">
|
||||
{getFieldDecorator("event_code",{
|
||||
rules:[{validator: (rule, value, callback)=>{eventValidator(rule, value, callback, "code")}}]
|
||||
rules:[{validator: (rule, value, callback)=>{eventValidator(rule, value, callback)}}]
|
||||
})(
|
||||
<Checkbox.Group>
|
||||
<Checkbox value={0}>推送<br/><span className="color-99">git推送到存储库</span></Checkbox>
|
||||
<Checkbox value={1}>创建<br/><span className="color-99">创建分支或标签</span></Checkbox>
|
||||
<Checkbox value={2}>删除<br/><span className="color-99">删除分支或标签</span></Checkbox>
|
||||
<Row>
|
||||
<Col span={16}><Checkbox value={0}>推送<br/><span className="color-99">git推送到存储库</span></Checkbox></Col>
|
||||
<Col span={8}><Checkbox value={1}>创建<br/><span className="color-99">创建分支或标签</span></Checkbox></Col>
|
||||
<Col span={16}><Checkbox value={2}>删除<br/><span className="color-99">删除分支或标签</span></Checkbox></Col>
|
||||
</Row>
|
||||
</Checkbox.Group>
|
||||
)}
|
||||
</Form.Item>}
|
||||
{(codeSelectValue !== resource[0].value && prSelectValue !== resource[0].value) && <div className="dashedBor"></div>}
|
||||
{prSelectValue !== resource[0].value && <Form.Item label="合并请求事件" className="botItem width50 checkBox">
|
||||
{getFieldDecorator("event_pr",{
|
||||
rules:[{validator: (rule, value, callback)=>{eventValidator(rule, value, callback, "pr")}}]
|
||||
rules:[{validator: (rule, value, callback)=>{eventValidator(rule, value, callback)}}]
|
||||
})(
|
||||
<Checkbox.Group>
|
||||
<Checkbox value={3}>合并请求<br/><span className="color-99">合并请求被打开、被关闭、被重新打开或被编辑</span></Checkbox>
|
||||
<Checkbox value={4}>合并请求分配<br/><span className="color-99">合并请求被分配或取消分配</span></Checkbox>
|
||||
<Row>
|
||||
<Col span={16}><Checkbox value={3}>合并请求<br/><span className="color-99">合并请求被打开、被关闭或被编辑</span></Checkbox></Col>
|
||||
<Col span={8}><Checkbox value={4}>合并请求分配<br/><span className="color-99">合并请求被分配或取消分配</span></Checkbox></Col>
|
||||
<Col span={16}><Checkbox value={6}>合并请求被评论<br/><span className="color-99">合并请求评论被创建或删除</span></Checkbox></Col>
|
||||
</Row>
|
||||
</Checkbox.Group>
|
||||
)}
|
||||
</Form.Item>}
|
||||
{(codeSelectValue !== resource[0].value || prSelectValue !== resource[0].value) && issueSelectValue !== resource[0].value && <div className="dashedBor"></div>}
|
||||
{issueSelectValue !== resource[0].value && <Form.Item label="疑修事件" className="botItem width50 checkBox">
|
||||
{getFieldDecorator("event_issue",{
|
||||
rules:[{validator: (rule, value, callback)=>{eventValidator(rule, value, callback)}}]
|
||||
})(
|
||||
<Checkbox.Group>
|
||||
<Row>
|
||||
<Col span={16}><Checkbox value={7}>疑修<br/><span className="color-99">疑修已打开、已关闭、已重新打开或编辑</span></Checkbox></Col>
|
||||
<Col span={8}><Checkbox value={8}>疑修已指派<br/><span className="color-99">疑修已被指派或取消指派</span></Checkbox></Col>
|
||||
<Col span={16}><Checkbox value={9}>已标记疑修<br/><span className="color-99">疑修标记被更新或清除</span></Checkbox></Col>
|
||||
<Col span={8}><Checkbox value={10}>疑修评论<br/><span className="color-99">疑修评论被创建、编辑或删除</span></Checkbox></Col>
|
||||
</Row>
|
||||
</Checkbox.Group>
|
||||
)}
|
||||
</Form.Item>}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
.width50{
|
||||
width: 60%;
|
||||
&.checkBox{
|
||||
width: 68%;
|
||||
width: 75%;
|
||||
}
|
||||
.height36{
|
||||
height: 36px;
|
||||
|
|
@ -50,23 +50,15 @@
|
|||
margin-bottom: 0;
|
||||
}
|
||||
&.checkBox .ant-checkbox-group{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
.ant-row{width: 100%;}
|
||||
.color-99{padding-left: 24px;}
|
||||
}
|
||||
&.three .ant-checkbox-wrapper{
|
||||
width: 34%;
|
||||
margin-left: 0px;
|
||||
padding-left: 12px;
|
||||
&:first-child, &:last-child{
|
||||
padding-left: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dashedBor{
|
||||
border-bottom: 1px dashed #e0e6f5;
|
||||
margin-bottom: 12px;
|
||||
margin-top: -20px;
|
||||
}
|
||||
.resourceBox + .dashedBor{
|
||||
margin-top: -15px;
|
||||
|
|
|
|||
|
|
@ -283,16 +283,16 @@ function New({ form , match , showNotification , history, projectDetail }) {
|
|||
<div className="colSpan">
|
||||
<span>
|
||||
<Checkbox value="pull_request_only">合并请求</Checkbox>
|
||||
<span>合并请求被打开、被关闭、被重新打开或被编辑</span>
|
||||
<span>合并请求被打开、被关闭或被编辑</span>
|
||||
</span>
|
||||
<span>
|
||||
<Checkbox value="pull_request_assign">合并请求分配</Checkbox>
|
||||
<span>合并请求被分配或取消分配</span>
|
||||
</span>
|
||||
{/* <span>
|
||||
<span>
|
||||
<Checkbox value="pull_request_comment">合并请求被评论</Checkbox>
|
||||
<span>合并请求评论被创建、编辑或删除</span>
|
||||
</span> */}
|
||||
<span>合并请求评论被创建或删除</span>
|
||||
</span>
|
||||
{/* <span>
|
||||
<Checkbox value="pull_request_milestone">合并请求收入里程碑</Checkbox>
|
||||
<span>合并请求被记录或取消记录于里程碑中</span>
|
||||
|
|
@ -310,7 +310,7 @@ function New({ form , match , showNotification , history, projectDetail }) {
|
|||
<span>合并请求被同步</span>
|
||||
</span> */}
|
||||
</div>
|
||||
{/* <p className="color-grey-3 mt10 mb10">疑修事件</p>
|
||||
<p className="color-grey-3 mt10 mb10">疑修事件</p>
|
||||
<div className="colSpan">
|
||||
<span>
|
||||
<Checkbox value="issues_only">疑修</Checkbox>
|
||||
|
|
@ -328,7 +328,7 @@ function New({ form , match , showNotification , history, projectDetail }) {
|
|||
<Checkbox value="issue_comment">疑修评论</Checkbox>
|
||||
<span>疑修评论被创建、编辑或删除</span>
|
||||
</span>
|
||||
</div> */}
|
||||
</div>
|
||||
</Checkbox.Group>
|
||||
{ eventFlag && <span style={{color:"#DF0002"}}>请选择自定义事件!</span>}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import { isArray } from 'lodash';
|
|||
import { Base64 } from 'js-base64';
|
||||
import Sidebar from './components/sidebar';
|
||||
import UploadWiki from './components/uploadWiki';
|
||||
const Search = Input.Search;
|
||||
const InputGroup = Input.Group;
|
||||
const { Option } = Select;
|
||||
|
||||
|
|
@ -31,7 +30,6 @@ export default (props) => {
|
|||
|
||||
const [fileArr, setFileArr] = useState([]);
|
||||
const [fileList, setFileList] = useState([]);
|
||||
const [fileListInit, setFileListInit] = useState([]);
|
||||
const [reload, setReload] = useState();
|
||||
|
||||
const [urlType, setUrlType] = useState('HTTPS');
|
||||
|
|
@ -76,11 +74,7 @@ export default (props) => {
|
|||
const {menuList, sidebar} = info;
|
||||
setSidebar(sidebar)
|
||||
setFileList(menuList);
|
||||
setFileListInit(menuList);
|
||||
let firstWikiNode = undefined;
|
||||
// if(!pathname.endsWith('/wiki')){
|
||||
// firstWikiNode = findNodeByKey(menuList, pathname.split('/').pop())
|
||||
// }
|
||||
const arr = pathname.split('/');
|
||||
if(!pathname.endsWith('/wiki')){
|
||||
firstWikiNode = findNodeByName(menuList, arr[arr.length-2]);
|
||||
|
|
@ -151,15 +145,6 @@ export default (props) => {
|
|||
}
|
||||
}
|
||||
|
||||
function changeSearchValue(value) {
|
||||
if(value){
|
||||
// 过滤不包含wiki的内容, 重新渲染页面
|
||||
let filterList = filterTree(fileListInit, value);
|
||||
setFileList(filterList);
|
||||
}else{
|
||||
setFileList(JSON.parse(JSON.stringify(fileListInit)));
|
||||
}
|
||||
}
|
||||
|
||||
function filterTree(list, value, arr = []){
|
||||
if(!list.length) return []
|
||||
|
|
@ -173,15 +158,6 @@ export default (props) => {
|
|||
}
|
||||
}
|
||||
return arr
|
||||
// return list.filter(item => {
|
||||
// if (item.title.includes(value)) {
|
||||
// return true
|
||||
// }
|
||||
// // if(item.children && item.children.length > 0){
|
||||
// // item.children = findUnHasValue(item.children , value);
|
||||
// return findUnHasValue(item.children , value);
|
||||
// // }
|
||||
// })
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -314,15 +290,9 @@ export default (props) => {
|
|||
<div className="wiki-nav-parent">
|
||||
<div className='wiki-nav-scroll'>
|
||||
<div className="wiki-nav">
|
||||
<Search
|
||||
allowClear
|
||||
placeholder="输入关键字搜索文件"
|
||||
className="wiki-search"
|
||||
onSearch={changeSearchValue}
|
||||
/>
|
||||
|
||||
{/* 读sidebar转换成目录树 */}
|
||||
<Sidebar fileList = {fileList} changeitem={changeitem} projectsId={projectsId} owner={owner} project={project} setReload={setReload} sidebar={sidebar} history={history} permission={permission} defaultSelectedKeys={defaultSelectedKeys}/>
|
||||
<Sidebar fileList = {fileList} changeitem={changeitem} projectsId={projectsId} owner={owner} project={project} setReload={setReload} sidebar={sidebar} history={history} permission={permission} defaultSelectedKeys={defaultSelectedKeys} fileArrInit={fileArrInit}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -236,6 +236,9 @@ body {
|
|||
.ant-tree.ant-tree-directory > li span.ant-tree-node-content-wrapper.ant-tree-node-selected, .ant-tree.ant-tree-directory .ant-tree-child-tree > li span.ant-tree-node-content-wrapper.ant-tree-node-selected{
|
||||
color: $primary-color;
|
||||
}
|
||||
.ant-tree.ant-tree-directory > li.ant-tree-treenode-selected > span.ant-tree-switcher, .ant-tree.ant-tree-directory .ant-tree-child-tree > li.ant-tree-treenode-selected > span.ant-tree-switcher{
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.wiki-nav-title {
|
||||
|
|
@ -411,4 +414,11 @@ body {
|
|||
}
|
||||
.wikiAddMenuError{
|
||||
color: red;
|
||||
}
|
||||
.expendedAllAction{
|
||||
cursor: pointer;
|
||||
text-align: right;
|
||||
padding-right: 15px;
|
||||
color: $primary-color;
|
||||
border-bottom: 1px solid #d0d0d0;
|
||||
}
|
||||
|
|
@ -111,7 +111,6 @@ export function markdownToTree(markdownText){
|
|||
let root = {
|
||||
title: "root",
|
||||
children: [],
|
||||
node: undefined,
|
||||
key: key++
|
||||
}
|
||||
lines.map((item, index) =>{
|
||||
|
|
@ -119,18 +118,17 @@ export function markdownToTree(markdownText){
|
|||
const node = {
|
||||
title: item,
|
||||
children: [],
|
||||
node: undefined,
|
||||
key: key++
|
||||
key: undefined
|
||||
}
|
||||
// level:空格的个数(层级)
|
||||
const level = item.search(/\S/);
|
||||
// 下一个节点的空格个数(层级)
|
||||
const nextNodeLevel = lines[index+1] && lines[index+1].search(/\S/);
|
||||
if(!level){
|
||||
node.key = key+"";
|
||||
key++;
|
||||
// push根目录
|
||||
stack = []
|
||||
root.children.push(node);
|
||||
stack = [];
|
||||
stack.push(node);
|
||||
root.children.push(node);
|
||||
}else{
|
||||
// 解析子目录
|
||||
// 当前节点的父节点
|
||||
|
|
@ -139,12 +137,16 @@ export function markdownToTree(markdownText){
|
|||
const rootNode = root.children.filter(i => i.key === stack[0].key)[0];
|
||||
if(level === 1){
|
||||
// 二级目录
|
||||
node.key = `${rootNode.key}-${rootNode.children.length}`;
|
||||
rootNode.children.push(node);
|
||||
}else{
|
||||
// 更深的层级 找key = stack最后一个item的key
|
||||
const parentNodeByRoot = findNodeByKey(rootNode.children, parentNode.key);
|
||||
node.key = `${parentNodeByRoot.key}-${parentNodeByRoot.children.length}`
|
||||
parentNodeByRoot && parentNodeByRoot.children.push(node);
|
||||
}
|
||||
// 下一个节点的空格个数(层级)
|
||||
const nextNodeLevel = lines[index+1] && lines[index+1].search(/\S/);
|
||||
if(nextNodeLevel>level && (title.startsWith('- ') || title.startsWith('* '))){
|
||||
stack.push(node);
|
||||
}else if(nextNodeLevel<level){
|
||||
|
|
|
|||
|
|
@ -3,14 +3,15 @@ import { Tree, Dropdown, Menu, message, Modal, Input } from 'antd';
|
|||
import DelModal from '../../components/ModalFun';
|
||||
import './index.scss';
|
||||
import '../../components/ModalFun/index.scss';
|
||||
import { deleteWiki, findNodeByChildrenKey, findNodeByKey, treeToMd, updateWiki } from '../../api';
|
||||
import { deleteWiki, findNodeByChildrenKey, findNodeByKey, getWiki, treeToMd, updateWiki } from '../../api';
|
||||
import { useEffect } from 'react';
|
||||
import { Base64 } from 'js-base64';
|
||||
const Search = Input.Search;
|
||||
const {DirectoryTree, TreeNode} = Tree;
|
||||
|
||||
// wiki目录树
|
||||
export default function Sidebar(props) {
|
||||
const {fileList, changeitem, owner, projectsId, project, setReload, sidebar, history, permission, defaultSelectedKeys} = props;
|
||||
const {fileList, changeitem, owner, projectsId, project, setReload, sidebar, history, permission, defaultSelectedKeys, fileArrInit} = props;
|
||||
const {location:{pathname}} = history;
|
||||
const [initMenuList, setInitMenuList] = useState(undefined);
|
||||
const [menuList, setMenuList] = useState(undefined);
|
||||
|
|
@ -19,10 +20,26 @@ export default function Sidebar(props) {
|
|||
const [menuName, setMenuName] = useState(undefined);
|
||||
const [addMenuError, setAddMenuError] = useState(undefined);
|
||||
const [selectedKey, setSelectedKey] = useState(undefined);
|
||||
const [expandedKeys, setExpandedKeys] = useState(undefined);
|
||||
// 是否全部展开
|
||||
const [isExpendedAll, setIsExpendedAll] = useState(false);
|
||||
// 重命名目录名称
|
||||
const [isEditName, setIsEditName] = useState(undefined);
|
||||
// wiki重命名-wiki内容
|
||||
const [wikiContent, setWikiContent] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
setSelectedKey(defaultSelectedKeys);
|
||||
if(defaultSelectedKeys){
|
||||
// 设置选中wiki
|
||||
setSelectedKey(defaultSelectedKeys);
|
||||
// 第一次渲染,默认打开选中的页面所处目录
|
||||
const keys = expandedKeys || [];
|
||||
const list = defaultSelectedKeys[0].split('-');
|
||||
list.map((item, index)=>{
|
||||
keys.push(index > 0 ? `${keys[index-1]}-${item}` : item)
|
||||
})
|
||||
setExpandedKeys(Array.from(new Set(keys)));
|
||||
}
|
||||
}, [defaultSelectedKeys])
|
||||
|
||||
useEffect(()=>{
|
||||
|
|
@ -43,7 +60,7 @@ export default function Sidebar(props) {
|
|||
</a>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<a onClick={(e) => { updateName(e, item) }}>
|
||||
<a onClick={(e) => { updateName(e, item, 1) }}>
|
||||
重命名
|
||||
</a>
|
||||
</Menu.Item>
|
||||
|
|
@ -57,6 +74,11 @@ export default function Sidebar(props) {
|
|||
|
||||
const fileMenu = (title, isFile, item) =>{
|
||||
return <Menu>
|
||||
<Menu.Item>
|
||||
<a onClick={(e) => { updateName(e, item, 2) }}>
|
||||
重命名
|
||||
</a>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<a onClick={(e) => { deleteFileModal(e, title, isFile, item) }}>
|
||||
删除
|
||||
|
|
@ -102,6 +124,7 @@ export default function Sidebar(props) {
|
|||
});
|
||||
};
|
||||
|
||||
// 选中节点
|
||||
function selectTree(keys,event){
|
||||
let {title, key} = event.node.props.dataRef;
|
||||
setSelectedKey([key+""]);
|
||||
|
|
@ -113,6 +136,19 @@ export default function Sidebar(props) {
|
|||
}
|
||||
}
|
||||
|
||||
// 收起展开
|
||||
function onExpand(keys, obj){
|
||||
const oldKeys = new Set(keys);
|
||||
const {expanded} = obj;
|
||||
let {key} = obj.node.props.dataRef;
|
||||
if(expanded){
|
||||
oldKeys.add(key);
|
||||
}else{
|
||||
oldKeys.delete(key);
|
||||
}
|
||||
setExpandedKeys(Array.from(oldKeys))
|
||||
}
|
||||
|
||||
// 删除wiki模态框
|
||||
function deleteFileModal(e, title, isFile, item) {
|
||||
e.stopPropagation();
|
||||
|
|
@ -257,14 +293,24 @@ export default function Sidebar(props) {
|
|||
history.push(`/${owner}/${projectsId}/wiki/add?key=${item.key}`)
|
||||
}
|
||||
|
||||
// 重命名目录文件
|
||||
function updateName(e, item){
|
||||
// 重命名目录文件 type:1 目录 2页面
|
||||
function updateName(e, item, type){
|
||||
e.stopPropagation();
|
||||
setVisible(item);
|
||||
setIsEditName(true);
|
||||
setIsEditName(type);
|
||||
let title = item.title.trim();
|
||||
const isFile = title.startsWith('[[') && title.endsWith(']]');
|
||||
title = isFile ? title.substring(2,title.length-2) : title.substring(2, title.length);
|
||||
type === 2 && getWiki({
|
||||
owner: owner,
|
||||
repo: projectsId,
|
||||
pageName: title,
|
||||
projectId: project.id
|
||||
}).then(res => {
|
||||
if (res && res.message === "200") {
|
||||
setWikiContent(res.data.md_content);
|
||||
}
|
||||
})
|
||||
setMenuName(title);
|
||||
}
|
||||
|
||||
|
|
@ -284,34 +330,72 @@ export default function Sidebar(props) {
|
|||
return;
|
||||
}
|
||||
if(!isEditName){
|
||||
// 校验同级目录中是否有相同名称
|
||||
const {children, node, title, key} = visible;
|
||||
// 新增目录:校验同级目录中是否有相同名称
|
||||
const {children, key} = visible;
|
||||
const mList = children.filter(item=>!item.title.trim().startsWith('[[')).map(i=>i.title.trim())
|
||||
if(mList.includes(`- ${menuName}`)){
|
||||
setAddMenuError('不能与已有目录名称相同');
|
||||
return;
|
||||
}
|
||||
const newList = menuList;
|
||||
// 设置Tree 打开的目录
|
||||
const keys = expandedKeys || [];
|
||||
const list = key.split('-');
|
||||
list.map((item, index)=>{
|
||||
keys.push(index > 0 ? `${keys[index-1]}-${item}` : item)
|
||||
})
|
||||
keys.push(`${key}-${children.length}`);
|
||||
setExpandedKeys(Array.from(new Set(keys)));
|
||||
addChildrenByKey(newList, key, menuName);
|
||||
updateSidebar(treeToMd(newList), 3)
|
||||
updateSidebar(treeToMd(newList), 3);
|
||||
}else{
|
||||
// 编辑sidebar
|
||||
// isEditName 1 编辑目录名称 2编辑页面名称
|
||||
const titleToJudge = isEditName === 2 ? `[[${menuName}]]` : `- ${menuName}`;
|
||||
const {children, node, title, key} = visible;
|
||||
const list = findNodeByChildrenKey(menuList, key) || [];
|
||||
if(title.trim() === `- ${menuName}`){
|
||||
setAddMenuError("请修改目录名称");
|
||||
if(title.trim() === titleToJudge){
|
||||
setAddMenuError("请修改名称");
|
||||
return
|
||||
}
|
||||
if(list.map(item=>item.title.trim()).includes(`- ${menuName}`)){
|
||||
if(list.map(item=>item.title.trim()).includes(titleToJudge)){
|
||||
message.error("不能与同级目录名称相同");
|
||||
}else{
|
||||
const newMenuList = JSON.parse(JSON.stringify(menuList))
|
||||
loop(newMenuList, key, (item)=>{
|
||||
const {title} = item;
|
||||
item.title = `${title.substring(0, title.indexOf('- '))}- ${menuName}`
|
||||
})
|
||||
updateSidebar(treeToMd(newMenuList), 4)
|
||||
return
|
||||
}
|
||||
// wiki名称不可重复
|
||||
const sameNameWiki = fileArrInit.filter(item=>item.name === menuName)
|
||||
if(isEditName === 2 && sameNameWiki.length){
|
||||
message.error("wiki名称不可重复");
|
||||
return
|
||||
}
|
||||
// 编辑sidebar
|
||||
const newMenuList = JSON.parse(JSON.stringify(menuList))
|
||||
loop(newMenuList, key, (item)=>{
|
||||
const {key} = item;
|
||||
item.title = `${'\t'.repeat(key.split('-').length-1)}${titleToJudge}`
|
||||
})
|
||||
if(isEditName === 2){
|
||||
// 编辑页面名称-> 修改wiki
|
||||
const oldTitle = title.substring(title.indexOf('[[')+2, title.indexOf(']]'))
|
||||
updateWiki({
|
||||
owner,
|
||||
repo: projectsId,
|
||||
projectId: project.id,
|
||||
// 原名称
|
||||
pageName: oldTitle,
|
||||
// 现名称
|
||||
title: menuName,
|
||||
content_base64: wikiContent,
|
||||
commit_message: '',
|
||||
}).then(res=>{
|
||||
if(res && res.message === '200'){
|
||||
// 修改wiki名称接口请求成功,更新sidebar
|
||||
updateSidebar(treeToMd(newMenuList), 4);
|
||||
history.push(`/${owner}/${projectsId}/wiki/${encodeURI(menuName)}/${key}`);
|
||||
}
|
||||
})
|
||||
return;
|
||||
}
|
||||
updateSidebar(treeToMd(newMenuList), 4)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -440,16 +524,99 @@ export default function Sidebar(props) {
|
|||
setMenuName(undefined);
|
||||
}
|
||||
|
||||
// 展开收起全部节点
|
||||
function expandAll(){
|
||||
setIsExpendedAll(!isExpendedAll);
|
||||
if(isExpendedAll){
|
||||
setExpandedKeys([]);
|
||||
return;
|
||||
}
|
||||
const allKeys = [];
|
||||
findMenuKey(allKeys, menuList);
|
||||
setExpandedKeys(allKeys);
|
||||
}
|
||||
|
||||
function findMenuKey(keys, list){
|
||||
list.map(item=>{
|
||||
if(item && !item.title.startsWith("[[")){
|
||||
keys.push(item.key);
|
||||
}
|
||||
if(item && item.children.length){
|
||||
findMenuKey(keys, item.children);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function changeSearchValue(value) {
|
||||
if(value){
|
||||
// 找到符合条件的子节点, 重新渲染页面
|
||||
let keysMap = findNodesToKeys(initMenuList, value);
|
||||
const newMenuList = [];
|
||||
let openedKeysMap = [];
|
||||
const oneNodeKeys = new Set();
|
||||
keysMap.map((item, index)=>{
|
||||
const {path, isFile, title} = item;
|
||||
// 把所有的一级目录key筛选出来
|
||||
oneNodeKeys.add(path[0]);
|
||||
openedKeysMap = openedKeysMap.concat(path);
|
||||
if(!index && isFile){
|
||||
setSelectedKey([path[path.length-1]]);
|
||||
const titleSplit = title.substring(2,title.length-2);
|
||||
changeitem({key: path[path.length-1], name: titleSplit})
|
||||
}
|
||||
})
|
||||
Array.from(oneNodeKeys).map(item=>loop(initMenuList, item,i=>{
|
||||
newMenuList.push(i);
|
||||
}))
|
||||
const openedKeys = Array.from(new Set(openedKeysMap));
|
||||
setExpandedKeys(openedKeys);
|
||||
setMenuList(newMenuList);
|
||||
}else{
|
||||
setMenuList(JSON.parse(JSON.stringify(initMenuList)));
|
||||
}
|
||||
}
|
||||
|
||||
function findNodesToKeys(arr, value, path, nodes) {
|
||||
path = path || [];
|
||||
nodes = nodes || [];
|
||||
if (Array.isArray(arr)) {
|
||||
arr.forEach(function (obj, index) {
|
||||
if (obj && typeof obj === 'object') {
|
||||
const {children, key} = obj;
|
||||
const title = obj.title.trim();
|
||||
const isFile = title.startsWith('[[') && title.endsWith(']]');
|
||||
if (title.match(new RegExp(value, 'i'))) {
|
||||
nodes.push({path: path.concat([key]), isFile, title});
|
||||
} else {
|
||||
findNodesToKeys(children, value, path.concat([key]), nodes);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
return(
|
||||
<div className='wikiSidebar mt5'>
|
||||
{menuList && menuList.length ? <DirectoryTree onSelect={selectTree} draggable={permission} onDrop={onDrop} selectedKeys={selectedKey}>
|
||||
<div className='wikiSidebar'>
|
||||
<Search
|
||||
allowClear
|
||||
placeholder="输入关键字搜索文件"
|
||||
className="wiki-search"
|
||||
onSearch={changeSearchValue}
|
||||
/>
|
||||
|
||||
{/* 目录是否全部展开 */}
|
||||
<div className='expendedAllAction mt5' onClick={expandAll}>{isExpendedAll ? '收起' : '展开'}所有节点</div>
|
||||
|
||||
{/* wiki目录 */}
|
||||
{menuList && menuList.length ? <DirectoryTree onSelect={selectTree} draggable={permission} onDrop={onDrop} selectedKeys={selectedKey} expandedKeys={expandedKeys} onExpand={onExpand}>
|
||||
{renderTreeNodes(menuList)}
|
||||
</DirectoryTree> : ''}
|
||||
|
||||
{/* 新增子目录弹窗 */}
|
||||
<Modal title={isEditName ? "编辑目录" : "新增目录"} visible={Boolean(visible)} onCancel={onCancel} onOk={submit} className="wikiAddMenu myself-modal" width={450} centered>
|
||||
<div className='mb10'>目录名称: </div>
|
||||
<Input placeholder='请输入目录名称' value={menuName} onChange={(e)=>{setMenuName(e.target.value);setAddMenuError(undefined)}} maxLength={50}></Input>
|
||||
<Modal title={isEditName ? `编辑${isEditName === 1 ? '目录' : '页面'}` : "新增目录"} visible={Boolean(visible)} onCancel={onCancel} onOk={submit} className="wikiAddMenu myself-modal" width={450} centered>
|
||||
<div className='mb10'>{isEditName === 2 ? '页面' : '目录'}名称: </div>
|
||||
<Input placeholder={`请输入${isEditName === 2 ? '页面' : '目录'}名称`} value={menuName} onChange={(e)=>{setMenuName(e.target.value);setAddMenuError(undefined)}} maxLength={50}></Input>
|
||||
<div className='wikiAddMenuError mt5'>{addMenuError}</div>
|
||||
</Modal>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -170,10 +170,11 @@ ul,ol,dl{
|
|||
&.small{
|
||||
height: 120px;
|
||||
img{
|
||||
width:50px;
|
||||
width:200px;
|
||||
}
|
||||
}
|
||||
img{
|
||||
width:300px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,124 @@
|
|||
import React, { useState } from 'react';
|
||||
import { withRouter } from "react-router";
|
||||
import { SnackbarHOC } from "educoder";
|
||||
import { CNotificationHOC } from "../../modules/courses/common/CNotificationHOC";
|
||||
import { TPMIndexHOC } from "../../modules/tpm/TPMIndexHOC";
|
||||
import './index.scss';
|
||||
import { useEffect } from 'react';
|
||||
import { Pagination, Select } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import Nodata from '../Nodata';
|
||||
const Option = Select.Option;
|
||||
|
||||
export default withRouter((CNotificationHOC()(SnackbarHOC()(TPMIndexHOC((props) => {
|
||||
const {match:{params:{name, id}}, current_user:{user_id}} = props;
|
||||
const [languageList, setLanguageList] = useState(undefined);
|
||||
const [languageId, setLanguageId] = useState(undefined);
|
||||
const [sort, setSort] = useState("praises_count");
|
||||
const [page, setPage] = useState(1);
|
||||
const [limit, setLimit] = useState(15);
|
||||
const [projectsList, setProjectsList] = useState(undefined);
|
||||
const [total, setTotal] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
// 设置网页标题
|
||||
name && (document.title = `项目标签-${decodeURIComponent(name)}`);
|
||||
// 获取语言列表
|
||||
axios.get(`/project_languages.json`).then(result=>{
|
||||
result && setLanguageList(result.data.project_languages)
|
||||
})
|
||||
}, [])
|
||||
|
||||
useEffect(()=>{
|
||||
axios.get(`/projects.json`, {
|
||||
params: {
|
||||
user_id: user_id,
|
||||
page,
|
||||
limit,
|
||||
sort_by: sort,
|
||||
language_id:languageId? languageId : undefined,
|
||||
topic_id:id
|
||||
}
|
||||
}).then((result) => {
|
||||
if (result) {
|
||||
setProjectsList(result.data.projects);
|
||||
setTotal(result.data.total_count);
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log('error', error);
|
||||
})
|
||||
}, [sort, page, languageId])
|
||||
|
||||
return (
|
||||
<div className='topicRepoList pb50'>
|
||||
<h2 className='mt30 pl15'>#{decodeURIComponent(name)}</h2>
|
||||
<div className='flexBoxTopic topicRepoHead'>
|
||||
<span>平台共{total}个公开项目与此标签相匹配</span>
|
||||
<div>
|
||||
<Select defaultValue={0} showSearch placeholder="请选择语言" style={{minWidth:"104px", marginRight: '20px'}} onChange={(value)=>{setLanguageId(value)}} optionFilterProp="children" filterOption={(input,option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}>
|
||||
<Option key={0} value={0}>全部语言</Option>
|
||||
{
|
||||
languageList && languageList.length>0 && languageList.map((item,key)=>{
|
||||
return(
|
||||
<Option key={item.id} value={item.id}>{item.name}</Option>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Select>
|
||||
<Select defaultValue={"praises_count"} onChange={(value)=>{setSort(value)}} style={{minWidth:"104px"}}>
|
||||
<Option value="praises_count">最多点赞</Option>
|
||||
<Option value="updated_on">最近更新</Option>
|
||||
<Option value="forked_count">最多fork</Option>
|
||||
<Option value="watched_count">最多关注</Option>
|
||||
<Option value="created_on">最近创建</Option>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
{/* 无数据 */}
|
||||
{projectsList && projectsList.length === 0 && <div className='repoByTopicNodata'><Nodata _html="暂无数据"/></div>}
|
||||
{/* 项目列表 */}
|
||||
{projectsList && projectsList.length>0 && projectsList.map(i=>{
|
||||
return <div key={i.id} className='flexBoxTopic repoTopic'>
|
||||
{/* 仓库拥有者头像 */}
|
||||
<Link to={`/${i.author && i.author.login}`}><img src={getImageUrl(`/${i.author && i.author.image_url}`)} alt="" className='repoImg mr10'/></Link>
|
||||
<div className='rightInfo'>
|
||||
<div className="flexBoxTopic">
|
||||
{/* 仓库拥有者名称 */}
|
||||
<Link to={`/${i.author && i.author.login}/${i.identifier}`} className="repoTitle task-hide font-15">{i.author && i.author.name}/{i.name}</Link>
|
||||
{/* 点赞数 */}
|
||||
{i.praises_count > 0 ? <span className='mr10'><i className="iconfont icon-xingzhuang mr3 font-14"></i>{i.praises_count}</span> :"" }
|
||||
{/* fork数 */}
|
||||
{i.forked_count > 0 ? <span><i className="iconfont icon-yifuke_icon mr3 font-14"></i>{i.forked_count}</span>:""}
|
||||
</div>
|
||||
{/* 仓库描述 */}
|
||||
<div className="descTopic task-hide-2 mb5">{i.description}</div>
|
||||
{/* 项目标签 */}
|
||||
{i.topics && <div className='proListTopics'>
|
||||
{i.topics.map(item=><Link to={`/explore/topic/${item.id}/${encodeURIComponent(item.name)}`} className='proListTopic mr15 font-13 task-hide'>{item.name}</Link>)}
|
||||
</div>}
|
||||
<div className="flexBoxTopic info3 font-13 mt5">
|
||||
{/* 仓库类别 */}
|
||||
{i.category && i.category.id ? <span className="category">{i.category.name}</span> :"" }
|
||||
{/* 仓库语言 */}
|
||||
{i.language && i.language.id ? <span className={`language mr30 ${i.category && i.category.id ? 'hasCate' : ''}`}>{i.language.name}</span> :""}
|
||||
{/* 更新时间 */}
|
||||
<span style={{lineHeight:"15px",display:"flex"}}><i className="iconfont icon-shijian font-15 mr5"></i>更新于{i.time_ago}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
})}
|
||||
{/* 分页 */}
|
||||
{total > limit && <Pagination
|
||||
className='mt20 pagTopic'
|
||||
pageSize={limit}
|
||||
showQuickJumper={total > limit}
|
||||
onChange={(page) => { setPage(page) }}
|
||||
current={page}
|
||||
total={total}
|
||||
showTotal={total => `共 ${total} 条`}
|
||||
/>}
|
||||
</div>
|
||||
)}
|
||||
)))))
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
.topicRepoList{
|
||||
width: 900px;
|
||||
margin: 0 auto;
|
||||
min-height: 700px;
|
||||
.flexBoxTopic{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
&>h2{
|
||||
font-weight: 700;
|
||||
}
|
||||
.topicRepoHead{
|
||||
color:#5f6872;
|
||||
padding: 10px 15px;
|
||||
border-bottom:1px solid rgba(153, 153, 153, 0.16);
|
||||
}
|
||||
.repoImg{
|
||||
width:32px;
|
||||
height: 32px;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.repoTopic{
|
||||
align-items: flex-start;
|
||||
border-bottom:1px solid rgba(153, 153, 153, 0.1);
|
||||
justify-content: flex-start;
|
||||
padding: 20px 0 15px;
|
||||
.rightInfo{
|
||||
flex: 1;
|
||||
}
|
||||
.repoTitle{
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
.descTopic{
|
||||
color:#414141;
|
||||
line-height: 24px;
|
||||
}
|
||||
.proListTopics{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
.proListTopic{
|
||||
background-color:#f3f8ff;
|
||||
border-radius: 4px;
|
||||
max-width: 158px;
|
||||
padding: 0 10px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
color: #4c5b76;
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
}
|
||||
.info3{
|
||||
justify-content: flex-start;
|
||||
color: #7D7D7D;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.category{
|
||||
position: relative;
|
||||
padding-left: 11px;
|
||||
margin-right: 10px;
|
||||
&::before{
|
||||
position: absolute;
|
||||
content: "";
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background-color: #466AFF;
|
||||
top: 7px;
|
||||
left: 0px;
|
||||
}
|
||||
}
|
||||
.language{
|
||||
position: relative;
|
||||
&.hasCate{
|
||||
padding-left: 11px;
|
||||
}
|
||||
&.hasCate::before{
|
||||
position: absolute;
|
||||
content: "";
|
||||
width: 1px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
border-left: 1px solid #9e9e9e;
|
||||
bottom: 5px;
|
||||
left: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.pagTopic{
|
||||
text-align: center;
|
||||
}
|
||||
.repoByTopicNodata .none_panels{
|
||||
min-height: 520px;
|
||||
}
|
||||
}
|
||||
|
|
@ -72,7 +72,7 @@ function ConcentrateProject({userLogin,current,showCompeleteDialog,completeProfi
|
|||
<p className="task-hide desc">{i.description}</p>
|
||||
{/* 项目标签 */}
|
||||
{i.topics && <div className='viewProListTopics'>
|
||||
{i.topics.map(item=><span className='viewProListTopic mr15 font-13 task-hide'>{item.name}</span>)}
|
||||
{i.topics.map(item=><Link to={`/explore/topic/${item.id}/${encodeURIComponent(item.name)}`} className='viewProListTopic mr15 font-13 task-hide'>{item.name}</Link>)}
|
||||
</div>}
|
||||
<AlignCenter>
|
||||
{ i.category && <span className="tagName">{i.category.name}</span> }
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ function Register(props){
|
|||
if (!err) {
|
||||
values.agreement && axios.post(`/accounts/register.json`, {
|
||||
login: values.email,
|
||||
namespace: values.register_username,
|
||||
namespace: values.register_username.trim(),
|
||||
password: values.register_psd,
|
||||
password_confirmation: values.psdComfirm,
|
||||
code: values.captcha
|
||||
|
|
@ -53,7 +53,7 @@ function Register(props){
|
|||
setEmailStr(values.email);
|
||||
}else if(response.data && response.data.status === 0){
|
||||
//注册成功,页面跳转即可,注册后登录流程forge处理了
|
||||
window.location.href = "/"+values.register_username;
|
||||
window.location.href = "/"+values.register_username.trim();
|
||||
} else {
|
||||
setEmailStr(values.email);
|
||||
setMess(response.data.message);
|
||||
|
|
@ -67,11 +67,11 @@ function Register(props){
|
|||
function usernameConfirm(rule, value, callback){
|
||||
setUserNameGo(true);
|
||||
value && (userNameGo || value !== loginStr) ? axios.post(`/accounts/check.json`, {
|
||||
value: value,
|
||||
value: value.trim(),
|
||||
type: 1
|
||||
}).then(response => {
|
||||
if (response.data.status === -1) {
|
||||
callback('该名称已经被使用');
|
||||
callback(response.data.message);
|
||||
} else {
|
||||
setLoginStr(value);
|
||||
setUserNameGo(false);
|
||||
|
|
@ -238,6 +238,9 @@ function Register(props){
|
|||
<Form.Item>
|
||||
{getFieldDecorator('register_username',{
|
||||
rules:[
|
||||
{
|
||||
transform: (value)=>{return value.trim()}
|
||||
},
|
||||
{
|
||||
required:true,
|
||||
message:"请输入用户名"
|
||||
|
|
@ -250,6 +253,10 @@ function Register(props){
|
|||
pattern: /[a-zA-Z0-9]$/,
|
||||
message: "用户名只能使用英文字母和数字"
|
||||
},
|
||||
{
|
||||
pattern: /^[^\s]*$/,
|
||||
message: "用户名不能包含空格"
|
||||
},
|
||||
{
|
||||
min: 4,
|
||||
max: 15,
|
||||
|
|
|
|||
|
|
@ -221,7 +221,8 @@ function SoftBotDEtail(props){
|
|||
</div>
|
||||
<div className="installTil font-18 mt20 mb10">权限信息</div>
|
||||
{detail && <div className="mb10 font-16 greenCol"><i className={`mr8 iconfont ${detail.limit_and_events.juris_diction_code !== 2 ? 'icon-wanchenggouxuan' : 'icon-tishi1 font-16'}`}></i>代码库{detail.limit_and_events.juris_diction_code === 2 ? '无' : detail.limit_and_events.juris_diction_code === 1 ? '读写' : '只读'}权限</div>}
|
||||
{detail && <div className="mb20 font-16 greenCol"><i className={`mr8 iconfont ${detail.limit_and_events.juris_diction_pr !== 2 ? 'icon-wanchenggouxuan' : 'icon-tishi1 font-16'}`}></i>合并请求(PR){detail.limit_and_events.juris_diction_pr === 2 ? '无' : detail.limit_and_events.juris_diction_pr === 1 ? '读写' : '只读'}权限</div>}
|
||||
{detail && <div className="mb10 font-16 greenCol"><i className={`mr8 iconfont ${detail.limit_and_events.juris_diction_pr !== 2 ? 'icon-wanchenggouxuan' : 'icon-tishi1 font-16'}`}></i>合并请求(PR){detail.limit_and_events.juris_diction_pr === 2 ? '无' : detail.limit_and_events.juris_diction_pr === 1 ? '读写' : '只读'}权限</div>}
|
||||
{detail && <div className="mb20 font-16 greenCol"><i className={`mr8 iconfont ${detail.limit_and_events.juris_diction_issue !== 2 ? 'icon-wanchenggouxuan' : 'icon-tishi1 font-16'}`}></i>疑修(Issue){detail.limit_and_events.juris_diction_issue === 2 ? '无' : detail.limit_and_events.juris_diction_issue === 1 ? '读写' : '只读'}权限</div>}
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@
|
|||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
&>.none_panels{
|
||||
margin: 0 auto;
|
||||
margin: 125px auto;
|
||||
}
|
||||
.botLoading{
|
||||
margin: 200px auto;
|
||||
|
|
|
|||
Loading…
Reference in New Issue