pull代码
|
|
@ -1,8 +1,8 @@
|
|||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 2340181 */
|
||||
src: url('iconfont.woff2?t=1673486678957') format('woff2'),
|
||||
url('iconfont.woff?t=1673486678957') format('woff'),
|
||||
url('iconfont.ttf?t=1673486678957') format('truetype');
|
||||
src: url('iconfont.woff2?t=1678758582072') format('woff2'),
|
||||
url('iconfont.woff?t=1678758582072') format('woff'),
|
||||
url('iconfont.ttf?t=1678758582072') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
|
|
@ -13,6 +13,98 @@
|
|||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-kaiqi:before {
|
||||
content: "\e95a";
|
||||
}
|
||||
|
||||
.icon-a-31shijian:before {
|
||||
content: "\e944";
|
||||
}
|
||||
|
||||
.icon-chengyuan2:before {
|
||||
content: "\e945";
|
||||
}
|
||||
|
||||
.icon-a-bianji12:before {
|
||||
content: "\e946";
|
||||
}
|
||||
|
||||
.icon-fuzhi-shanchu:before {
|
||||
content: "\e947";
|
||||
}
|
||||
|
||||
.icon-chuangjianqianbao:before {
|
||||
content: "\e948";
|
||||
}
|
||||
|
||||
.icon-biaoji2:before {
|
||||
content: "\e949";
|
||||
}
|
||||
|
||||
.icon-lichengbeiicon1:before {
|
||||
content: "\e94a";
|
||||
}
|
||||
|
||||
.icon-biaoji:before {
|
||||
content: "\e94b";
|
||||
}
|
||||
|
||||
.icon-fenzhi3:before {
|
||||
content: "\e94c";
|
||||
}
|
||||
|
||||
.icon-riqi:before {
|
||||
content: "\e94d";
|
||||
}
|
||||
|
||||
.icon-ruliriqixiao:before {
|
||||
content: "\e94e";
|
||||
}
|
||||
|
||||
.icon-shanchu8:before {
|
||||
content: "\e94f";
|
||||
}
|
||||
|
||||
.icon-sousuo6:before {
|
||||
content: "\e950";
|
||||
}
|
||||
|
||||
.icon-quanxian:before {
|
||||
content: "\e951";
|
||||
}
|
||||
|
||||
.icon-lichengbeiicon2:before {
|
||||
content: "\e952";
|
||||
}
|
||||
|
||||
.icon-xinjian3:before {
|
||||
content: "\e953";
|
||||
}
|
||||
|
||||
.icon-xuanzhong4:before {
|
||||
content: "\e954";
|
||||
}
|
||||
|
||||
.icon-youxian:before {
|
||||
content: "\e955";
|
||||
}
|
||||
|
||||
.icon-a-xiaoxi1:before {
|
||||
content: "\e956";
|
||||
}
|
||||
|
||||
.icon-yixiuicon2:before {
|
||||
content: "\e957";
|
||||
}
|
||||
|
||||
.icon-xiugaibaobiaomoban:before {
|
||||
content: "\e958";
|
||||
}
|
||||
|
||||
.icon-a-zu1614:before {
|
||||
content: "\e959";
|
||||
}
|
||||
|
||||
.icon-BOTpeizhi:before {
|
||||
content: "\e942";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,167 @@
|
|||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "34197190",
|
||||
"name": "开启",
|
||||
"font_class": "kaiqi",
|
||||
"unicode": "e95a",
|
||||
"unicode_decimal": 59738
|
||||
},
|
||||
{
|
||||
"icon_id": "33949760",
|
||||
"name": "3.1 时间",
|
||||
"font_class": "a-31shijian",
|
||||
"unicode": "e944",
|
||||
"unicode_decimal": 59716
|
||||
},
|
||||
{
|
||||
"icon_id": "33949761",
|
||||
"name": "成员",
|
||||
"font_class": "chengyuan2",
|
||||
"unicode": "e945",
|
||||
"unicode_decimal": 59717
|
||||
},
|
||||
{
|
||||
"icon_id": "33949762",
|
||||
"name": "编辑 (1)",
|
||||
"font_class": "a-bianji12",
|
||||
"unicode": "e946",
|
||||
"unicode_decimal": 59718
|
||||
},
|
||||
{
|
||||
"icon_id": "33949763",
|
||||
"name": "复制-删除",
|
||||
"font_class": "fuzhi-shanchu",
|
||||
"unicode": "e947",
|
||||
"unicode_decimal": 59719
|
||||
},
|
||||
{
|
||||
"icon_id": "33949764",
|
||||
"name": "创建钱包",
|
||||
"font_class": "chuangjianqianbao",
|
||||
"unicode": "e948",
|
||||
"unicode_decimal": 59720
|
||||
},
|
||||
{
|
||||
"icon_id": "33949765",
|
||||
"name": "标记2",
|
||||
"font_class": "biaoji2",
|
||||
"unicode": "e949",
|
||||
"unicode_decimal": 59721
|
||||
},
|
||||
{
|
||||
"icon_id": "33949766",
|
||||
"name": "里程碑icon",
|
||||
"font_class": "lichengbeiicon1",
|
||||
"unicode": "e94a",
|
||||
"unicode_decimal": 59722
|
||||
},
|
||||
{
|
||||
"icon_id": "33949767",
|
||||
"name": "标记",
|
||||
"font_class": "biaoji",
|
||||
"unicode": "e94b",
|
||||
"unicode_decimal": 59723
|
||||
},
|
||||
{
|
||||
"icon_id": "33949768",
|
||||
"name": "分支",
|
||||
"font_class": "fenzhi3",
|
||||
"unicode": "e94c",
|
||||
"unicode_decimal": 59724
|
||||
},
|
||||
{
|
||||
"icon_id": "33949769",
|
||||
"name": "日期",
|
||||
"font_class": "riqi",
|
||||
"unicode": "e94d",
|
||||
"unicode_decimal": 59725
|
||||
},
|
||||
{
|
||||
"icon_id": "33949770",
|
||||
"name": "入离日期小",
|
||||
"font_class": "ruliriqixiao",
|
||||
"unicode": "e94e",
|
||||
"unicode_decimal": 59726
|
||||
},
|
||||
{
|
||||
"icon_id": "33949771",
|
||||
"name": "删除",
|
||||
"font_class": "shanchu8",
|
||||
"unicode": "e94f",
|
||||
"unicode_decimal": 59727
|
||||
},
|
||||
{
|
||||
"icon_id": "33949772",
|
||||
"name": "搜索",
|
||||
"font_class": "sousuo6",
|
||||
"unicode": "e950",
|
||||
"unicode_decimal": 59728
|
||||
},
|
||||
{
|
||||
"icon_id": "33949773",
|
||||
"name": "权限",
|
||||
"font_class": "quanxian",
|
||||
"unicode": "e951",
|
||||
"unicode_decimal": 59729
|
||||
},
|
||||
{
|
||||
"icon_id": "33949774",
|
||||
"name": "里程碑icon2",
|
||||
"font_class": "lichengbeiicon2",
|
||||
"unicode": "e952",
|
||||
"unicode_decimal": 59730
|
||||
},
|
||||
{
|
||||
"icon_id": "33949775",
|
||||
"name": "新建",
|
||||
"font_class": "xinjian3",
|
||||
"unicode": "e953",
|
||||
"unicode_decimal": 59731
|
||||
},
|
||||
{
|
||||
"icon_id": "33949776",
|
||||
"name": "选中",
|
||||
"font_class": "xuanzhong4",
|
||||
"unicode": "e954",
|
||||
"unicode_decimal": 59732
|
||||
},
|
||||
{
|
||||
"icon_id": "33949777",
|
||||
"name": "优先",
|
||||
"font_class": "youxian",
|
||||
"unicode": "e955",
|
||||
"unicode_decimal": 59733
|
||||
},
|
||||
{
|
||||
"icon_id": "33949778",
|
||||
"name": "消息 (1)",
|
||||
"font_class": "a-xiaoxi1",
|
||||
"unicode": "e956",
|
||||
"unicode_decimal": 59734
|
||||
},
|
||||
{
|
||||
"icon_id": "33949779",
|
||||
"name": "易修icon",
|
||||
"font_class": "yixiuicon2",
|
||||
"unicode": "e957",
|
||||
"unicode_decimal": 59735
|
||||
},
|
||||
{
|
||||
"icon_id": "33949780",
|
||||
"name": "修改报表模版",
|
||||
"font_class": "xiugaibaobiaomoban",
|
||||
"unicode": "e958",
|
||||
"unicode_decimal": 59736
|
||||
},
|
||||
{
|
||||
"icon_id": "33949781",
|
||||
"name": "组 1614",
|
||||
"font_class": "a-zu1614",
|
||||
"unicode": "e959",
|
||||
"unicode_decimal": 59737
|
||||
},
|
||||
{
|
||||
"icon_id": "33731642",
|
||||
"name": "BOT配置",
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
<script src="https://gw.alipayobjects.com/os/lib/react-dom/16.14.0/umd/react-dom.%REACT_URL%.js"></script>
|
||||
<script src="%PUBLIC_URL%js/jquery-1.8.3.min.js"></script>
|
||||
<script src="%PUBLIC_URL%js/js_min_all.js"></script>
|
||||
<script src="%PUBLIC_URL%js/common.js"></script>
|
||||
<!-- <script src="%PUBLIC_URL%js/common.js"></script> -->
|
||||
<script src="%PUBLIC_URL%js/codemirror/codemirror.js"></script>
|
||||
<script src="%PUBLIC_URL%js/editormd/editormd.min.js"></script>
|
||||
<script src="%PUBLIC_URL%js/codemirror/merge/merge.js"></script>
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ class Activity extends Component{
|
|||
}
|
||||
<div className="commentsBox">
|
||||
<div className="trendsTop">
|
||||
<span>Issue/Pr记录</span>
|
||||
<span>疑修/合并请求记录</span>
|
||||
<div className="branchDropdown f-wrap-alignCenter">
|
||||
<Dropdown overlay={this.menu()} trigger={['click']} placement="bottomLeft">
|
||||
<a className="ant-dropdown-link">
|
||||
|
|
|
|||
|
|
@ -61,6 +61,11 @@ function Contributors({owner,projectsId,currentLogin}){
|
|||
return str && str.substr(0,str.length - 1);
|
||||
}
|
||||
|
||||
function getBlockchain(userLogin){
|
||||
const user = list.filter(item =>{ return item.login === userLogin});
|
||||
return user[0] && user[0].contribution_perc;
|
||||
}
|
||||
|
||||
function setMenusFunc(data){
|
||||
if(data){
|
||||
let ele = (
|
||||
|
|
@ -94,6 +99,10 @@ function Contributors({owner,projectsId,currentLogin}){
|
|||
<span>{data.following_count}</span>
|
||||
<span>关注数</span>
|
||||
</Link>
|
||||
{getBlockchain(data.login) && <a>
|
||||
<span>{getBlockchain(data.login)}</span>
|
||||
<span>贡献度</span>
|
||||
</a>}
|
||||
</AlignCenter>
|
||||
<div className={"pb20"} style={{display:"flex",justifyContent:'center'}}>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
import React from 'react';
|
||||
|
||||
function Profile({children,sureFunc,showCompeleteDialog , completeProfile, className}) {
|
||||
|
||||
function Profile({children,sureFunc,showCompeleteDialog , completeProfile, className,checkIfLogin,showLoginDialog,checklogin}) {
|
||||
|
||||
function checkProfile() {
|
||||
if(checklogin){
|
||||
if(checkIfLogin()===false){
|
||||
showLoginDialog();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(!completeProfile){
|
||||
showCompeleteDialog && showCompeleteDialog();
|
||||
}else{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
import React , { useState , useEffect } from 'react';
|
||||
import { Form , Input, Button } from 'antd';
|
||||
import ColorCard from './colorCard';
|
||||
import axios from 'axios';
|
||||
|
||||
function AddTagsBox({onSuccess , form ,owner , projectsId,onCancel}){
|
||||
const { getFieldDecorator , getFieldsValue , validateFields } = form;
|
||||
const [ colors , setColors ] = useState(undefined);
|
||||
const [ defaultColor , setDefaultColor ]= useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
const c = Math.random().toString(16).substr(-6);
|
||||
c && setDefaultColor(`#${c}`);
|
||||
},[])
|
||||
|
||||
function saveFunc(){
|
||||
validateFields((error,values)=>{
|
||||
if(!error){
|
||||
const { desc , name } = getFieldsValue();
|
||||
const url = `/v1/${owner}/${projectsId}/issue_tags`;
|
||||
axios.post(url,{
|
||||
name,description:desc,color:colors || defaultColor
|
||||
}).then(result=>{
|
||||
result && onSuccess();
|
||||
}).catch(error=>{})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getColor(colors){
|
||||
setColors(colors);
|
||||
}
|
||||
|
||||
return(
|
||||
<Form className="addTags_form" hideRequiredMark={true}>
|
||||
<p className="manageTitle"><i className="iconfont icon-xiangzuojiantou font-15 color-grey mr5 cursor" onClick={onCancel}/>创建标记</p>
|
||||
<div className="inline">
|
||||
<Form.Item colon={false} style={{flex:1,marginRight:'20px'}}>
|
||||
{getFieldDecorator('name',{
|
||||
rules:[
|
||||
{
|
||||
required:true,
|
||||
message:"请输入标记名称"
|
||||
}
|
||||
],
|
||||
validateTrigger:"onInput",
|
||||
})(<Input placeholder="标记名称15字以内" maxLength={15}/>)}
|
||||
</Form.Item>
|
||||
<Form.Item style={{marginTop:"7px"}}>
|
||||
<ColorCard getColor={getColor} defaultColor={defaultColor}/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Form.Item colon={false}>
|
||||
{getFieldDecorator('desc',{rules:[],
|
||||
validateTrigger:"onInput",
|
||||
})(<Input placeholder="描述30字以内" maxLength={30}/>)}
|
||||
</Form.Item>
|
||||
<div style={{textAlign:'center'}}>
|
||||
<Button style={{width:"80px"}} className="cancelTags" onClick={onCancel}>取消</Button>
|
||||
<Button style={{width:"80px"}} className="ml20" type="primary" onClick={saveFunc}>确认</Button>
|
||||
</div>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
export default Form.create({ name: 'Bind' })(AddTagsBox);
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
import React,{ useState , useEffect , forwardRef ,useImperativeHandle , useRef } from 'react';
|
||||
import Menus from './menus';
|
||||
import axios from 'axios';
|
||||
|
||||
const array =[
|
||||
{id:1,name:"最新创建"},
|
||||
{id:2,name:"最早创建"},
|
||||
{id:3,name:"最新更新"},
|
||||
{id:4,name:"最早更新"},
|
||||
{id:5,name:"高优先级"},
|
||||
{id:6,name:"低优先级"},
|
||||
{id:7,name:"高悬赏金额"},
|
||||
{id:8,name:"低悬赏金额"}
|
||||
]
|
||||
const array1 =[
|
||||
{id:1,name:"最新创建"},
|
||||
{id:2,name:"最早创建"},
|
||||
{id:3,name:"最新更新"},
|
||||
{id:4,name:"最早更新"},
|
||||
{id:5,name:"高优先级"},
|
||||
{id:6,name:"低优先级"},
|
||||
]
|
||||
|
||||
function AllMenus({owner,projectsId,chooseFunc,update,defaultNames,defaultIds,open_blockchain},ref){
|
||||
// 列表右侧所有筛选项的值
|
||||
const [ authorList , setAuthorList ] = useState(undefined);
|
||||
const [ author , setAuthor ] = useState(undefined);
|
||||
const [ tagList , setTagList ] = useState(undefined);
|
||||
const [ tag , setTag ] = useState(undefined);
|
||||
const [ millstoneList , setMillstoneList ] = useState(undefined);
|
||||
const [ millstone , setMillstone ] = useState(undefined);
|
||||
const [ chargeList , setChargeList ] = useState(undefined);
|
||||
const [ charge , setCharge ] = useState(undefined);
|
||||
const [ statusList , setStatusList ] = useState(undefined);
|
||||
const [ prioritiesList , setPrioritiesList ] = useState(undefined);
|
||||
const [ ids , setIds ] = useState({author_id:undefined,issue_priorities_id:undefined,issue_tag_ids:undefined,milestone_id:undefined,sort_by:undefined,status_id:undefined,assigner_id:undefined});
|
||||
const [ names , setNames ] = useState({author_name:undefined,issue_priorities_name:undefined,issue_tag_name:undefined,milestone_name:undefined,sortby_name:undefined,status_name:undefined,assigner_name:undefined});
|
||||
|
||||
useEffect(()=>{
|
||||
if(defaultNames){
|
||||
setNames(defaultNames);
|
||||
}
|
||||
},[defaultNames])
|
||||
|
||||
useEffect(()=>{
|
||||
if(defaultIds){
|
||||
setIds(defaultIds);
|
||||
}
|
||||
},[defaultIds])
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
clearChoose: () => {
|
||||
// 父组件按钮:清除筛选条件,将Ids\names里的字段全部改为undefined
|
||||
setIds({author_id:undefined,issue_tag_ids:undefined,issue_priorities_id:undefined,milestone_id:undefined,sort_by:undefined,status_id:undefined,assigner_id:undefined});
|
||||
setNames({author_name:undefined,issue_tag_name:undefined,issue_priorities_name:undefined,milestone_name:undefined,sortby_name:undefined,status_name:undefined,assigner_name:undefined});
|
||||
}
|
||||
}))
|
||||
// 获取发布人列表
|
||||
useEffect(()=>{
|
||||
getSendPerson();
|
||||
},[author])
|
||||
|
||||
function getSendPerson(){
|
||||
const url = `/v1/${owner}/${projectsId}/issue_authors`;
|
||||
axios.get(url,{params:{keyword:author,only_name:true}}).then(result=>{
|
||||
if(result && result.data){
|
||||
setAuthorList(result.data.authors);
|
||||
}
|
||||
})
|
||||
}
|
||||
// 获取标记列表
|
||||
useEffect(()=>{
|
||||
getSign();
|
||||
},[tag])
|
||||
function getSign(){
|
||||
const url = `/v1/${owner}/${projectsId}/issue_tags`;
|
||||
axios.get(url,{params:{keyword:tag,only_name:true}}).then(result=>{
|
||||
if(result && result.data){
|
||||
setTagList(result.data.issue_tags);
|
||||
}
|
||||
})
|
||||
}
|
||||
// 获取里程碑列表
|
||||
useEffect(()=>{
|
||||
getMillstone();
|
||||
},[millstone])
|
||||
|
||||
function getMillstone(){
|
||||
const url = `/v1/${owner}/${projectsId}/milestones`;
|
||||
axios.get(url,{params:{keyword:millstone,only_name:true}}).then(result=>{
|
||||
if(result && result.data){
|
||||
setMillstoneList(result.data.milestones);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取负责人列表
|
||||
useEffect(()=>{
|
||||
getCharge();
|
||||
},[charge,update])
|
||||
|
||||
function getCharge(){
|
||||
if(update){
|
||||
const url = `/v1/${owner}/${projectsId}/collaborators`;
|
||||
axios.get(url,{params:{keyword:charge,only_name:true}}).then(result=>{
|
||||
if(result && result.data){
|
||||
setChargeList(result.data.collaborators);
|
||||
}
|
||||
})
|
||||
}else{
|
||||
const url = `/v1/${owner}/${projectsId}/issue_assigners`;
|
||||
axios.get(url,{params:{keyword:charge,only_name:true}}).then(result=>{
|
||||
if(result && result.data){
|
||||
setChargeList(result.data.assigners);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
// 获取优先级列表
|
||||
useEffect(()=>{
|
||||
getPriorities();
|
||||
},[])
|
||||
|
||||
function getPriorities(){
|
||||
const url = `/v1/${owner}/${projectsId}/issue_priorities`;
|
||||
axios.get(url).then(result=>{
|
||||
if(result && result.data){
|
||||
setPrioritiesList(result.data.priorities);
|
||||
}
|
||||
})
|
||||
}
|
||||
// 获取状态列表
|
||||
useEffect(()=>{
|
||||
getStatus();
|
||||
},[])
|
||||
|
||||
function getStatus(){
|
||||
const url = `/v1/${owner}/${projectsId}/issue_statues`;
|
||||
axios.get(url).then(result=>{
|
||||
if(result && result.data){
|
||||
setStatusList(result.data.statues);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function choose(id,name){
|
||||
let copy = {...ids,author_id:id && id.length>0 ? id.join(","):undefined};
|
||||
let copyname = { ...names,author_name:name};
|
||||
setIds(copy);setNames(copyname);
|
||||
chooseFunc(copy,copyname);
|
||||
}
|
||||
|
||||
return(
|
||||
<ul className="dropboxul">
|
||||
{ !update && <Menus
|
||||
update={update}
|
||||
ids={ids && ids.author_id}
|
||||
names={names && names.author_name}
|
||||
name={"发布人" } size={"large"} imgControl
|
||||
lists={authorList} searchFunc={(value)=>setAuthor(value)}
|
||||
chooseFunc={(id,name)=>choose(id,name,'author_name')}
|
||||
/>
|
||||
}
|
||||
{update && <Menus
|
||||
update={update}
|
||||
ids={ids && ids.issue_priorities_id}
|
||||
name={"优先级" } size={"small"}
|
||||
lists={prioritiesList}
|
||||
names={names && names.issue_priorities_name}
|
||||
chooseFunc={(id,name)=>{let copy = {...ids,issue_priorities_id:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,issue_priorities_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}}
|
||||
/>
|
||||
}
|
||||
<Menus
|
||||
update={update}
|
||||
ids={ids && ids.issue_tag_ids}
|
||||
name={"标记"} size={"large"}
|
||||
double
|
||||
names={names && names.issue_tag_name}
|
||||
lists={tagList} searchFunc={(value)=>setTag(value)}
|
||||
chooseFunc={(id,name)=>{let copy = {...ids,issue_tag_ids:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,issue_tag_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}}
|
||||
/>
|
||||
<Menus
|
||||
update={update}
|
||||
ids={ids && ids.milestone_id}
|
||||
name={"里程碑"} size={"large"}
|
||||
names={names && names.milestone_name}
|
||||
lists={millstoneList} searchFunc={(value)=>setMillstone(value)}
|
||||
chooseFunc={(id,name)=>{let copy = {...ids,milestone_id:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,milestone_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}}
|
||||
/>
|
||||
<Menus
|
||||
update={update}
|
||||
ids={ids && ids.assigner_id}
|
||||
name={"负责人"} size={"large"} imgControl
|
||||
names={names && names.assigner_name}
|
||||
lists={chargeList} searchFunc={(value)=>setCharge(value)}
|
||||
double={update ? true : false }
|
||||
chooseFunc={(id,name)=>{let copy = {...ids,assigner_id:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,assigner_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}}
|
||||
/>
|
||||
<Menus
|
||||
update={update}
|
||||
ids={ids && ids.status_id}
|
||||
name={"状态"} size={"small"}
|
||||
lists={statusList}
|
||||
names={names && names.status_name}
|
||||
chooseFunc={(id,name)=>{let copy = {...ids,status_id:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,status_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}}
|
||||
/>
|
||||
{ !update && <Menus
|
||||
ids={ids && ids.sort_by}
|
||||
name={"排序"} size={"small"}
|
||||
lists={open_blockchain ? array : array1}
|
||||
names={names && names.sortby_name}
|
||||
chooseFunc={(id,name)=>{let copy = {...ids,sort_by:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,sortby_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}}
|
||||
/>
|
||||
}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
export default forwardRef(AllMenus);
|
||||
|
|
@ -0,0 +1,302 @@
|
|||
import React ,{ useState , useEffect , useRef , forwardRef } from 'react';
|
||||
import { findDOMNode } from 'react-dom';
|
||||
import { Dropdown , Menu , Input , Button, Form , Tooltip } from 'antd';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import AddTagsBox from './addTagsBox';
|
||||
|
||||
const { Search } = Input;
|
||||
/**
|
||||
*
|
||||
* @param {placeholder} placeholder:默认显示内容(为空时)
|
||||
* @param {editFlag} editFlag:是否有编辑权限
|
||||
* @param {searchFlag} searchFlag:是否显示搜索框
|
||||
* @param {selectValueList} selectValueList:将选中的list保存
|
||||
* @param {searchFunc} searchFunc:搜索方法
|
||||
* @param {onAdd} onAdd:标记管理
|
||||
* @param {headImg} headImg:是否显示头像
|
||||
* @param {menus} menus:下拉列表
|
||||
* @param {chooseFunc} chooseFunc:选择下拉列表
|
||||
* @param {double} double:undefined为单选,有值就是最多选的数量
|
||||
* @param {colorFlag} colorFlag:选中值需根据颜色显示背景
|
||||
* @param {mustFlag} mustFlag:必须选择一个
|
||||
* @param {removeFlag} removeFlag:移除按钮
|
||||
* @returns
|
||||
*/
|
||||
function ChooseMenu({
|
||||
placeholder ="未设置",
|
||||
editFlag,
|
||||
searchFlag,
|
||||
selectValueList,
|
||||
searchFunc,
|
||||
onAdd,
|
||||
headImg,
|
||||
menus,
|
||||
auto,
|
||||
chooseFunc,double,colorFlag,mustFlag,removeFlag,owner,projectsId
|
||||
}){
|
||||
const [ visible , setVisible ] = useState(false);
|
||||
const [ searchValue , setSearchValue ]= useState(undefined);
|
||||
const [ valuesId , setValuesId ] = useState([]);
|
||||
const [ count , setCount ] = useState(double);
|
||||
const [ content , setContent ] = useState(false);
|
||||
|
||||
const [ saveList , setSaveList ] = useState(undefined);
|
||||
|
||||
const refFa = useRef(null);
|
||||
const refBox = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('click', clickMe , false);
|
||||
}, [])
|
||||
|
||||
const clickMe = ({ target }) => {
|
||||
// 查找父组件
|
||||
const faComponent = findDOMNode(refFa.current);
|
||||
const boxComponent = findDOMNode(refBox.current);
|
||||
if (faComponent && boxComponent) {
|
||||
const isChild = faComponent.contains(target);
|
||||
const isBox = boxComponent.contains(target);
|
||||
const arr = ["removeicon","iconfont icon-guanbi font-12 color-white","iconfont icon-guanbi font-12 color-blue","icon-a-bianji12","color-blue font-15 tagManage","iconfont icon-xiangzuojiantou font-15 color-grey mr5 cursor","cover","ant-btn cancelTags"];
|
||||
const pointer = arr.includes(target.className);
|
||||
if(!isChild && !isBox && !pointer){
|
||||
setVisible(false);
|
||||
setContent(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 根据选中的列表循环出id数组
|
||||
useEffect(()=>{
|
||||
if(selectValueList && selectValueList.length > 0 && visible ){
|
||||
renderSelectList(selectValueList);
|
||||
setSaveList(selectValueList);
|
||||
setCount(selectValueList.length);
|
||||
}else{
|
||||
setValuesId([]);
|
||||
setCount(0);
|
||||
setSaveList(undefined);
|
||||
}
|
||||
if(!visible){
|
||||
setSearchValue(undefined);
|
||||
searchFunc(undefined);
|
||||
}
|
||||
},[selectValueList,visible])
|
||||
|
||||
function renderSelectList(list){
|
||||
let a = list && list.length > 0 && list.map((i,k)=>{
|
||||
return i.id ? i.id.toString() : i.name
|
||||
})
|
||||
setValuesId(a);
|
||||
}
|
||||
|
||||
// 搜索
|
||||
function changeSearchvalue(e){
|
||||
setSearchValue(e.target.value);
|
||||
searchFunc(e.target.value);
|
||||
}
|
||||
function renderNames(nameArrs){
|
||||
return <React.Fragment>
|
||||
{
|
||||
nameArrs && nameArrs.length>0?
|
||||
nameArrs.map((i,k)=>{
|
||||
return(
|
||||
<p style={{display:"flex",alignItems:"center"}} className={removeFlag?"removeFlag":''}>
|
||||
{i.image_url && <img src={getImageUrl(i.image_url)} alt="" width="18px" height="18px" style={{borderRadius:"50%"}} className="mr5"/>}
|
||||
{
|
||||
colorFlag ?
|
||||
(
|
||||
colorFlag === "2" ?
|
||||
<span className={"colorsquare task-hide"} style={{backgroundColor:`${i.color || "#000"}`,paddingRight:"18px"}}>
|
||||
{i.name}
|
||||
</span>
|
||||
:
|
||||
<span className={"colorsborder task-hide"} style={{borderColor:`${i.color}`,color:`${i.color}`}}>{i.name}</span>
|
||||
)
|
||||
:
|
||||
<span className={"task-hide"}>{i.name}</span>
|
||||
}
|
||||
{ !colorFlag && removeFlag && <a className="removeicon" onClick={()=>removeValueFunc(i.id)}><i className="iconfont icon-shanchu8 font-14"></i></a>}
|
||||
{ colorFlag && removeFlag && <a className="removeicon" onClick={()=>removeValueFunc(i.id)} style={{display:'block',right:"19px",position:"absolute"}}><i className="iconfont icon-guanbi font-12 color-white"></i></a>}
|
||||
</p>
|
||||
)
|
||||
})
|
||||
:<span>{placeholder}</span>
|
||||
}
|
||||
</React.Fragment>
|
||||
}
|
||||
|
||||
// 选择项
|
||||
function chooseMenu(i){
|
||||
let list = saveList && saveList.length > 0 ? [...saveList]:[] ;
|
||||
let relist = [];
|
||||
relist = list && list.length > 0 ? list:[];
|
||||
let filter = [];
|
||||
if(i.id){
|
||||
filter = list.filter(j=>j.id === i.id);
|
||||
}else{
|
||||
filter = list.filter(j=>j.name === i.name);
|
||||
}
|
||||
if(filter && filter.length > 0){
|
||||
if(i.id){
|
||||
relist = list.filter(j=>j.id !== i.id);
|
||||
}else{
|
||||
relist = list.filter(j=>j.name !== i.name);
|
||||
}
|
||||
}else{
|
||||
if(double && (saveList && saveList.length >= double)){
|
||||
setCount(-1);
|
||||
return;
|
||||
}
|
||||
if(double){
|
||||
relist.push(i);
|
||||
}else{
|
||||
relist = [i];
|
||||
}
|
||||
}
|
||||
renderSelectList(relist);
|
||||
setSaveList(relist);
|
||||
setCount(relist ? relist.length :0);
|
||||
if(!double){
|
||||
setVisible(false);
|
||||
setContent(false);
|
||||
chooseFunc(relist);
|
||||
}
|
||||
}
|
||||
|
||||
// 确认按钮
|
||||
function onSureFunc(){
|
||||
setVisible(false);
|
||||
setContent(false);
|
||||
chooseFunc(saveList);
|
||||
}
|
||||
// 移除选择的项
|
||||
function removeSaveFunc(id){
|
||||
let list = [...saveList];
|
||||
let filter = list.filter(j=>j.id !== id);
|
||||
setSaveList(filter);
|
||||
renderSelectList(filter);
|
||||
setCount(filter ? filter.length :0);
|
||||
}
|
||||
|
||||
// 移除后保存
|
||||
function removeValueFunc(id){
|
||||
let list = [...selectValueList];
|
||||
let filter = list.filter(j=>j.id !== id);
|
||||
setSaveList(filter);
|
||||
renderSelectList(filter);
|
||||
setCount(filter ? filter.length :0);
|
||||
chooseFunc(filter);
|
||||
}
|
||||
|
||||
function onSuccess(){
|
||||
setContent(false);
|
||||
onAdd();
|
||||
}
|
||||
return(
|
||||
<li>
|
||||
<span>
|
||||
{placeholder}
|
||||
{
|
||||
!editFlag &&
|
||||
<Dropdown
|
||||
visible={visible}
|
||||
overlayClassName={"overlayChooseStyle"}
|
||||
placement="bottomRight"
|
||||
trigger={['click']}
|
||||
overlay={
|
||||
<div ref={refFa}>
|
||||
{
|
||||
!content ?
|
||||
<div>
|
||||
{
|
||||
double && saveList && saveList.length>0?
|
||||
<ul className="choosedul">
|
||||
{
|
||||
saveList.map((i,k)=>{
|
||||
return(
|
||||
<li style={{backgroundColor:`${i.color}` || "#eff2ff",borderColor:`${i.color}` || "#466aff",color:`${colorFlag?"#FFF":"#466aff"}`}}>
|
||||
<span className="task-hide">{i.name}</span>
|
||||
<span className="removeicon" onClick={()=>removeSaveFunc(i.id)}><i className={`iconfont icon-guanbi font-12 ${colorFlag? "color-white":"color-blue"}`}></i></span>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
:""
|
||||
}
|
||||
{ searchFunc &&
|
||||
<div className="searchbox">
|
||||
<Search
|
||||
placeholder={`请输入${placeholder}名称进行搜索`}
|
||||
value={searchValue}
|
||||
onChange={changeSearchvalue}
|
||||
style={{marginRight:"18px"}}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
{
|
||||
menus && menus.length >0?
|
||||
<Menu className={auto && "piecemenu"} selectedKeys={valuesId}>
|
||||
{
|
||||
menus.map((i,k)=>{
|
||||
return(
|
||||
<Menu.Item key={i.id || i.name} className={colorFlag ?"colorli":"commonli"} style={{backgroundColor:colorFlag?`${i.color || "#000"}`:"#f4f6fe"}} onClick={()=>chooseMenu(i)}>
|
||||
{
|
||||
auto ? <span><span className="task-hide">{i.name}</span></span>
|
||||
:
|
||||
<Tooltip display={auto?false:true} placement={"bottom"} title={i.name}><span><span className="task-hide">{i.name}</span></span></Tooltip>
|
||||
}
|
||||
</Menu.Item>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Menu>
|
||||
:
|
||||
<div className="menusEmpty">
|
||||
<p>{searchValue ? <span>暂无{placeholder}“{searchValue}”</span>: `暂无${placeholder}`}</p>
|
||||
</div>
|
||||
}
|
||||
<div className="counttips">
|
||||
<div>
|
||||
{ onAdd &&
|
||||
<a className="color-blue font-15 tagManage" onClick={()=>{setContent(true)}}>
|
||||
<i className="iconfont icon-a-bianji12 font-14 mr5"></i>创建标记</a>
|
||||
}
|
||||
</div>
|
||||
{
|
||||
double && (count<0 ?
|
||||
<p className="color-red font-13">最多添加{double}个{placeholder}!</p>
|
||||
:
|
||||
<p className="font-13" style={{color:"#898d9d"}}>还可添加{double - count}个{placeholder}!</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{
|
||||
double &&
|
||||
<div style={{textAlign:'center'}}>
|
||||
<Button style={{width:"80px"}} onClick={()=>setVisible(false)}>取消</Button>
|
||||
<Button style={{width:"80px"}} className="ml20" type="primary" onClick={onSureFunc}>确认</Button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
:
|
||||
<AddTagsBox
|
||||
owner={owner}
|
||||
projectsId={projectsId}
|
||||
visible={visible}
|
||||
onCancel={()=>setContent(false)}
|
||||
onSuccess={onSuccess} />
|
||||
}
|
||||
</div>
|
||||
}>
|
||||
<a ref={refBox} onClick={()=>setVisible(visible ? false : true)}>
|
||||
<i className="iconfont icon-a-bianji12 font-13" style={{color:"#898d9d"}}></i>
|
||||
</a>
|
||||
</Dropdown>
|
||||
}
|
||||
</span>
|
||||
<div className={selectValueList && selectValueList.length > 0 ? "operatevalue color-grey-3":"operatevalue"} style={{display:colorFlag?"flex":"block"}}>{renderNames(selectValueList)}</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
export default Form.create()(forwardRef(ChooseMenu));
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import React,{ useState , useEffect } from 'react';
|
||||
import { SketchPicker } from 'react-color';
|
||||
|
||||
function ColorCard({getColor,defaultColor}){
|
||||
const [ displayColorPicker , setDisplayColorPicker ] = useState(false);
|
||||
const [ textcolor , setTextColor ] = useState("#F17013");
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
if(defaultColor){
|
||||
setTextColor(defaultColor);
|
||||
}
|
||||
},[defaultColor])
|
||||
|
||||
// 切换色卡
|
||||
function handleChange(color){
|
||||
setTextColor(color.hex);
|
||||
getColor(color ? color.hex : defaultColor);
|
||||
}
|
||||
|
||||
// 隐藏色卡
|
||||
function handleClick(){
|
||||
setDisplayColorPicker(!displayColorPicker);
|
||||
// reduction();
|
||||
}
|
||||
|
||||
// 还原色卡
|
||||
function reduction(){
|
||||
setTextColor("#F17013");
|
||||
// setColor({r: '241',g: '112',b: '19',a: '1',});
|
||||
}
|
||||
return(
|
||||
<div>
|
||||
<div className="swatch" onClick={handleClick}>
|
||||
<div className="color" style={{backgroundColor:`${textcolor}`}}></div>
|
||||
<p style={{ paddingLeft: 5 }}>{textcolor}</p>
|
||||
</div>
|
||||
{displayColorPicker ? (
|
||||
<div className="popover">
|
||||
<div className="cover" onClick={()=>{setDisplayColorPicker(false)}} />
|
||||
<SketchPicker color={textcolor} onChange={handleChange} />
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default ColorCard;
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
import React from 'react';
|
||||
import { Input, Button, message } from 'antd';
|
||||
import { getImageUrl, timeAgo } from 'educoder';
|
||||
import MDEditor from "../../../../modules/tpm/challengesnew/tpm-md-editor";
|
||||
import Upload from "../../../../forge/Upload/Index";
|
||||
import UploadImg from "../../../../forge/Images/upload.png";
|
||||
import './list.scss';
|
||||
import '../../../../forge/Order/order.scss';
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import axios from 'axios';
|
||||
|
||||
function EditComment(props){
|
||||
const {owner, projectsId, index} = props.match.params;
|
||||
const {current_user, current_user:{login}, showLoginDialog, showNotification, reloadComment, cancelMd, parentId, replyId, updateId, showUserImg=true} = props;
|
||||
const [content, setContent] = useState(props.content);
|
||||
const [quillFlag, setQuillFlag] = useState(false);
|
||||
// 确认评论按钮loading效果
|
||||
const [journalSpin, setJournalSpin] = useState(false);
|
||||
const [atWhoLoginList, setAtWhoLoginList] = useState(undefined);
|
||||
const [fileList, setFileList] = useState(undefined);
|
||||
const [attachmentClean, setAttachmentClean] = useState(true);
|
||||
|
||||
// 评论点击函数
|
||||
function addJournals(){
|
||||
if(!content){
|
||||
setQuillFlag(true);
|
||||
return
|
||||
}
|
||||
setJournalSpin(true);
|
||||
if(updateId){
|
||||
// 修改评论
|
||||
axios.patch(`/v1/${owner}/${projectsId}/issues/${index}/journals/${updateId}`,{
|
||||
notes: content,
|
||||
attachment_ids: fileList,
|
||||
receivers_login:atWhoLoginList
|
||||
}).then(res=>{
|
||||
if(res && res.data){
|
||||
// 刷新评论列表
|
||||
reloadComment();
|
||||
message.success('评论成功');
|
||||
cancelMd();
|
||||
setContent(undefined);
|
||||
}
|
||||
setJournalSpin(false);
|
||||
})
|
||||
}else{
|
||||
// 新建评论
|
||||
const params = {
|
||||
parent_id: parentId,
|
||||
reply_id: replyId,
|
||||
notes: content,
|
||||
attachment_ids: fileList,
|
||||
receivers_login:atWhoLoginList
|
||||
}
|
||||
axios.post(`/v1/${owner}/${projectsId}/issues/${index}/journals`, params).then(res=>{
|
||||
if(res && res.data){
|
||||
// 刷新评论列表
|
||||
reloadComment();
|
||||
message.success('评论成功');
|
||||
cancelMd();
|
||||
setContent(undefined);
|
||||
}
|
||||
setJournalSpin(false);
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
return(
|
||||
<div className="grid-item-top pb10">
|
||||
<Link
|
||||
to={`/${current_user && current_user.login}`}
|
||||
className="show-user-link mr10"
|
||||
>
|
||||
<img
|
||||
className="radius"
|
||||
src={getImageUrl(
|
||||
`/${current_user && current_user.image_url}`
|
||||
)}
|
||||
alt=""
|
||||
width="30"
|
||||
height="30"
|
||||
style={{display: showUserImg ? '' : 'none'}}
|
||||
/>
|
||||
</Link>
|
||||
<div style={{position:"relative"}}>
|
||||
<MDEditor
|
||||
placeholder={"添加评论..."}
|
||||
height={300}
|
||||
mdID={"orderdetail-add-descriptions" + replyId}
|
||||
initValue={content}
|
||||
onChange={(value)=>{setQuillFlag(false);setContent(value);}}
|
||||
isCanAtme = {true}
|
||||
changeAtWhoLoginList = {(loginList)=>{setAtWhoLoginList(loginList); setAttachmentClean(true)}}
|
||||
owner = {owner}
|
||||
projectsId = {projectsId}
|
||||
></MDEditor>
|
||||
<p className="quillFlagBox">
|
||||
{quillFlag && <span>请输入评论内容</span>}
|
||||
</p>
|
||||
<Upload
|
||||
className="commentStyle"
|
||||
isComplete={attachmentClean}
|
||||
load={(fileList)=>{setFileList(fileList)}}
|
||||
icon={
|
||||
<img
|
||||
src={UploadImg}
|
||||
width="58"
|
||||
alt=""
|
||||
style={{ marginBottom: 15 }}
|
||||
/>
|
||||
}
|
||||
size={100}
|
||||
showNotification={showNotification}
|
||||
defaultFileList={props.defaultFileList}
|
||||
/>
|
||||
<p className="clearfix mt20">
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={addJournals}
|
||||
loading={journalSpin}
|
||||
className="mr15"
|
||||
>
|
||||
评论
|
||||
</Button>
|
||||
<Button onClick={()=>{cancelMd()}}>取消</Button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default EditComment;
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { Button, Popconfirm, Radio, Input, message, Tooltip, Spin, Pagination } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { getImageUrl, timeAgo } from 'educoder';
|
||||
import RenderHtml from '../../../../components/render-html';
|
||||
import Attachment from '../../../Upload/attachment';
|
||||
import EditComment from './editComment';
|
||||
import Nodata from '../../../Nodata';
|
||||
import CheckProfile from '../../../Component/ProfileModal/Profile';
|
||||
import './list.scss';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
function IssueCommentList(props){
|
||||
const{history, history: {location}, reload, reloadComment, showNotification, current_user:{login, admin, image_url, user_id}, isManager, showLoginDialog, issueInfo:{author}} = props;
|
||||
const {owner, projectsId, index} = props.match.params;
|
||||
const [category, setCategory] = useState('comment');
|
||||
const [journals, setJournals] = useState(undefined);
|
||||
// 是否展示新建/编辑评论markdown部分新建评论1 编辑父级评论2 回复评论3 编辑回复内容4 回复子机评论5
|
||||
const [showEdit, setShowEdit] = useState(false);
|
||||
const [parentId, setParentId] = useState(undefined);
|
||||
const [replyId, setReplyId] = useState(undefined);
|
||||
// 修改评论id
|
||||
const [updateId, setUpdateId] = useState(undefined);
|
||||
// 操作日志展开效果
|
||||
const [open, setOpen] = useState(undefined);
|
||||
// 加载中效果(缓冲css 线条效果)
|
||||
const [spin, setSpin] = useState(false);
|
||||
// issue 评论总数
|
||||
const [journalsCount, setJournalsCount] = useState(undefined);
|
||||
// 分页
|
||||
const limit = 50;
|
||||
const [page, setPage] = useState(1);
|
||||
const [totalCount, setTotalCount] = useState(undefined);
|
||||
// 操作记录 icon
|
||||
const journalsIcon ={
|
||||
'issue': 'icon-chuangjianqianbao',
|
||||
'branch_name': 'icon-fenzhi3',
|
||||
'assigner': 'icon-chengyuan2',
|
||||
'status_id': 'icon-xiugaibaobiaomoban',
|
||||
'fixed_version_id': 'icon-lichengbeiicon2',
|
||||
'due_date': 'icon-riqi',
|
||||
'issue_tag': 'icon-biaoji2',
|
||||
'description': 'icon-xiugaibaobiaomoban',
|
||||
'subject': 'icon-xiugaibaobiaomoban',
|
||||
'start_date': 'icon-riqi',
|
||||
'priority_id': 'icon-youxian',
|
||||
'attachment': 'icon-xiugaibaobiaomoban'
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
setSpin(true);
|
||||
axios.get(`/v1/${owner}/${projectsId}/issues/${index}/journals`,{params:{
|
||||
category,
|
||||
page,
|
||||
limit
|
||||
}}).then(res=>{
|
||||
if(res && res.data){
|
||||
const {journals, total_comment_journals_count, total_count} = res.data;
|
||||
if(category === 'all'){
|
||||
const array = [];
|
||||
let start = undefined;
|
||||
let isJournalCount = 0;
|
||||
journals.map((item, index)=>{
|
||||
!isJournalCount && (start = index)
|
||||
item.is_journal_detail && (isJournalCount++)
|
||||
if(!item.is_journal_detail && isJournalCount < 10){
|
||||
isJournalCount = 0;
|
||||
return
|
||||
}
|
||||
if(isJournalCount >= 10 && (!item.is_journal_detail || journals.length-1 === index)){
|
||||
array.push({start, count: isJournalCount});
|
||||
isJournalCount = 0;
|
||||
}
|
||||
})
|
||||
array.map((item, i)=>{
|
||||
journals[item.start].numCount = item.count-1;
|
||||
for (let index = 0; index < item.count; index++) {
|
||||
journals[item.start+index].start = journals[item.start].id;
|
||||
journals[item.start+index].closeAndSpan = true;
|
||||
}
|
||||
})
|
||||
}
|
||||
setTotalCount(total_count);
|
||||
setJournalsCount(total_comment_journals_count);
|
||||
setJournals(journals);
|
||||
setSpin(false)
|
||||
}
|
||||
})
|
||||
}, [reload, category, page])
|
||||
|
||||
function commentCtx(v){
|
||||
return <RenderHtml className="break_word_comments imageLayerParent commentRenderHtml" value={v} url={location}/>;
|
||||
};
|
||||
|
||||
// 删除评论内容
|
||||
function deleteComment(id){
|
||||
axios.delete(`/v1/${owner}/${projectsId}/issues/${index}/journals/${id}`).then(res=>{
|
||||
if(res && res.data && !res.data.status){
|
||||
reloadComment();
|
||||
message.success('删除成功');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// markdown 取消操作
|
||||
function cancelMd(){
|
||||
setShowEdit(false);
|
||||
}
|
||||
|
||||
return(
|
||||
<div className="commentListBox">
|
||||
{/* 全部 / 评论 / 操作日志 */}
|
||||
<div className='typeActionBox mt30 mb20'>
|
||||
<Radio.Group onChange={(e)=>{setSpin(true);setPage(1);setCategory(e.target.value)}} value={category}>
|
||||
<Radio value={'comment'} className='typeActionRadio font-14'>评论<span className='journalsCount font-13 ml5'>{journalsCount}</span></Radio>
|
||||
<Radio value={'operate'} className='typeActionRadio font-14'>操作日志</Radio>
|
||||
<Radio value={'all'} className='typeActionRadio font-14'>全部</Radio>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
{/* 添加评论 */}
|
||||
{category !== 'operate' && <div className="pt5 pb30">
|
||||
{login ? showEdit === 1 ? <EditComment {...props} cancelMd={cancelMd}/> : <div className="addComments">
|
||||
<img src={getImageUrl(image_url)} alt="" />
|
||||
<div style={{flex:1}}>
|
||||
<CheckProfile {...props} sureFunc={()=>{setShowEdit(1)}}>
|
||||
<Input className='addCommentBox' placeholder="添加评论"/>
|
||||
</CheckProfile>
|
||||
</div>
|
||||
</div> : <div className='unLoginComment font-15 pl20'>
|
||||
{/* 未登录用户 */}
|
||||
<a className='mr5 loginBtn' onClick={()=>{showLoginDialog()}}>登录</a>并参与评论与回复
|
||||
</div>}
|
||||
</div>}
|
||||
{/* 评论/操作日志 展示列表 */}
|
||||
<Spin spinning={spin}>
|
||||
<div className={`issueCommentsBox ${category === 'comment' && !spin && 'justComment'}`}>
|
||||
{journals && (journals.length > 0 && journals.map(item=>{return item.is_journal_detail ? (!item.closeAndSpan || item.id === item.start || open === item.start) ? <div key={item.id} className='operationLog'>
|
||||
{/* 操作日志 */}
|
||||
<div className='operationCommentBor'></div>
|
||||
<div className='flexCenter font-14' style={{width: '100%'}}>
|
||||
<div className='flexCenter opBox'>
|
||||
<span className='iconBackBox mr10'><i className={`iconfont font-12 ${journalsIcon[item.operate_category]}`}></i></span>
|
||||
<div className='task-hide' style={{maxWidth: item.closeAndSpan && item.id === item.start? '550px' : '700px'}}>
|
||||
<Link to={`/${item.user.login}`}><img src={getImageUrl(item.user.image_url)} alt="" className='commentUserImg mr5'/></Link>
|
||||
<Link to={`/${item.user.login}`}>{item.user.name} </Link>
|
||||
{(item.user.name.length + item.operate_content.length) > 62 ? <Tooltip title={<div><span>{item.user.name} </span><span dangerouslySetInnerHTML={{__html:item.operate_content}}></span></div>}><span dangerouslySetInnerHTML={{__html:item.operate_content}}></span></Tooltip> : <span dangerouslySetInnerHTML={{__html:item.operate_content}}></span>}
|
||||
</div>
|
||||
<span className='ml15 timeAgo font-14'>{timeAgo(item.created_at)}</span>
|
||||
</div>
|
||||
{(item.closeAndSpan && item.id === item.start) && <a className='primaryColor' onClick={()=>{setOpen(open === item.id ? undefined : item.id)}}>{open === item.id ? `点击收起操作日志` : `已折叠${item.numCount}条, 点击查看`}<i className={`iconfont ${open === item.id ? `icon-sanjiaoxing-up` : 'icon-sanjiaoxing-down'} font-15`}></i></a>}
|
||||
</div>
|
||||
</div> : '' : <div key={item.id} className='commentContentBox pb30'>
|
||||
{/* 评论 */}
|
||||
<div className='commentOperationBor'></div>
|
||||
<Link to={`/${item.user.login}`}><img src={getImageUrl(item.user.image_url)} alt="" className='commentUserImg mr15'/></Link>
|
||||
<div className='commentContentRight'>
|
||||
{/* 判断是否是编辑状态 */}
|
||||
{(showEdit === 2 && updateId === item.id) ? <div className='mt15 mr20'><EditComment {...props} cancelMd={cancelMd} updateId={updateId} reloadComment={reloadComment} content={item.notes} defaultFileList={item.attachments} showUserImg={false}/></div> : <div>
|
||||
<div className='commentContent'>
|
||||
<div className='flexCenter font-14'>
|
||||
<div>
|
||||
<Link to={`/${item.user.login}`}>{item.user.name}</Link>
|
||||
<span className='ml15 timeAgo font-14'>{timeAgo(item.created_at)}</span>
|
||||
</div>
|
||||
{login && <div>
|
||||
{/* 平台管理员/仓库管理员/发布评论者/issue创建者 */}
|
||||
{(admin || isManager || login === item.user.login || user_id === author.id) && <Popconfirm
|
||||
placement="bottom"
|
||||
title={`确定要删除此条评论吗?${item.children_journals.length > 0 ? '子评论也将被一起删除。' : ''}`}
|
||||
okText="是"
|
||||
cancelText="否"
|
||||
onConfirm={() => deleteComment(item.id)}
|
||||
>
|
||||
<Button type='link' className='color-grey-89'><i className='iconfont icon-fuzhi-shanchu font-14 mr8'></i>删除</Button>
|
||||
</Popconfirm>}
|
||||
{/* 仅评论者可修改 */}
|
||||
{login === item.user.login && <Button type='link' className='color-grey-89' onClick={()=>{setUpdateId(item.id); setShowEdit(2)}}><i className='iconfont icon-a-bianji12 font-14 mr8'></i>修改</Button>}
|
||||
<CheckProfile {...props} sureFunc={()=>{setParentId(item.id); setReplyId(item.id); setShowEdit(3)}}><Button type='link' className='color-grey-89'><i className='iconfont icon-a-xiaoxi1 font-14 mr8'></i>回复</Button></CheckProfile>
|
||||
</div>}
|
||||
</div>
|
||||
<div className='contentHtml mb5'>{commentCtx(item.notes)}</div>
|
||||
{item && item.attachments && item.attachments.length > 0 && <div className='attachmentBox mb5'><Attachment
|
||||
attachments={item.attachments}
|
||||
showNotification={showNotification}
|
||||
canDelete={false}
|
||||
/></div>}
|
||||
</div>
|
||||
</div>}
|
||||
{showEdit === 3 && replyId === item.id && <div className='contentHtml mr20'><EditComment {...props} cancelMd={cancelMd} parentId={parentId} replyId={replyId} reloadComment={reloadComment}/></div>}
|
||||
{/* 评论回复部分 */}
|
||||
{item.children_journals.map(i =>{return <div className='commentReply' key={i.id}>
|
||||
{(showEdit === 4 && updateId === i.id) ? <div className='mr20'><EditComment {...props} cancelMd={cancelMd} updateId={updateId} reloadComment={reloadComment} content={i.notes} defaultFileList={i.attachments}/></div> : <div>
|
||||
<div className='flexCenter'>
|
||||
<div>
|
||||
<Link to={`/${i.user.login}`}><img src={getImageUrl(i.user.image_url)} alt="" className='commentUserImg mr8'/></Link>
|
||||
<Link to={`/${i.user.login}`}>{i.user.name}</Link>
|
||||
{i.reply_user && <span className='ml5 timeAgo mr3'>回复</span>}
|
||||
<span>{i.reply_user && i.reply_user.name}</span>
|
||||
<span className='ml15 timeAgo font-14'>{timeAgo(i.created_at)}</span>
|
||||
</div>
|
||||
{login && <div>
|
||||
{/* 平台管理员/仓库管理员/发布评论者/回复评论者/issue创建者 */}
|
||||
{(admin || isManager || login === i.user.login || login === i.reply_user.login || user_id === author.id) && <Popconfirm
|
||||
placement="bottom"
|
||||
title={"确定要删除当前回复吗?"}
|
||||
okText="是"
|
||||
cancelText="否"
|
||||
onConfirm={() => deleteComment(i.id)}
|
||||
>
|
||||
<Button type='link' className='color-grey-89'><i className='iconfont icon-fuzhi-shanchu font-14 mr8'></i>删除</Button>
|
||||
</Popconfirm>}
|
||||
{/* 仅回复评论者可修改 */}
|
||||
{login === i.user.login && <Button type='link' className='color-grey-89' onClick={()=>{setUpdateId(i.id); setShowEdit(4)}}><i className='iconfont icon-a-bianji12 font-14 mr8'></i>修改</Button>}
|
||||
<CheckProfile {...props} sureFunc={()=>{setParentId(item.id);setReplyId(i.id); setShowEdit(5)}}>
|
||||
<Button type='link' className='color-grey-89'><i className='iconfont icon-a-xiaoxi1 font-14 mr8'></i>回复</Button>
|
||||
</CheckProfile>
|
||||
</div>}
|
||||
</div>
|
||||
<div className='contentHtml mt5 mb10'>{commentCtx(i.notes)}</div>
|
||||
{i && i.attachments && i.attachments.length > 0 && <div className='attachmentBox'><Attachment
|
||||
attachments={i.attachments}
|
||||
showNotification={showNotification}
|
||||
canDelete={false}
|
||||
/></div>}
|
||||
</div>}
|
||||
{showEdit === 5 && replyId === i.id && <div className='contentHtml mr20'><EditComment {...props} cancelMd={cancelMd} parentId={parentId} replyId={replyId} reloadComment={reloadComment}/></div>}
|
||||
</div>})}
|
||||
</div>
|
||||
</div>}))}
|
||||
</div>
|
||||
</Spin>
|
||||
{totalCount>limit && <div className='mt20 paginationIssueComment mb20'>
|
||||
<Pagination simple current={page} pageSize={limit} total={totalCount} onChange={(page)=>setPage(page)}/>
|
||||
</div>}
|
||||
{/* 无数据 新建第一条评论时暂无数据隐藏 用户未登录隐藏*/}
|
||||
{journals && !journals.length && showEdit !== 1 && login && <Nodata _html="暂无数据"/>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default IssueCommentList;
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
.typeActionBox{
|
||||
padding: 10px 20px 4px;
|
||||
background-color:rgba(241, 243, 252, 0.55);
|
||||
border-radius:4px;
|
||||
.typeActionRadio{
|
||||
color: #333;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-right: 25px;
|
||||
&.ant-radio-wrapper-checked{
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
.journalsCount{
|
||||
color:#5e6685;
|
||||
background-color:rgba(70, 106, 255, 0.09);
|
||||
border-radius:10px;
|
||||
display: inline-block;
|
||||
padding: 0px 6px;
|
||||
}
|
||||
}
|
||||
.commentUserImg{
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.issueCommentsBox{
|
||||
.iconBackBox{
|
||||
display: inline-block;
|
||||
width:24px;
|
||||
height:24px;
|
||||
border-radius: 50%;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
background-color:#f2f3f5;
|
||||
}
|
||||
.operationLog, .commentContentBox{
|
||||
position: relative;
|
||||
min-height: 62px;
|
||||
display: flex;
|
||||
>.flexCenter, >a{
|
||||
z-index: 2;
|
||||
}
|
||||
&::before, &::after{
|
||||
content: '';
|
||||
width: 1px;
|
||||
height: 50%;
|
||||
position: absolute;
|
||||
background: #eee;
|
||||
left: 12px;
|
||||
z-index: 1;
|
||||
}
|
||||
&::after{
|
||||
background: #eee;
|
||||
top: 50%;
|
||||
}
|
||||
}
|
||||
.commentContentBox+.operationLog .operationCommentBor, .operationLog+.commentContentBox .commentOperationBor{
|
||||
border-top: 1px solid #eee;
|
||||
width: 98.5%;
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
}
|
||||
.operationLog+.commentContentBox{
|
||||
&>a, &>.commentContentRight{
|
||||
margin-top: 25px;
|
||||
}
|
||||
}
|
||||
.timeAgo{
|
||||
color:#acb0bf;
|
||||
}
|
||||
.operationLog:last-child::after, .operationLog:first-child::before, .commentContentBox:last-child::after, &.justComment .commentContentBox::before, &.justComment .commentContentBox::after{
|
||||
display: none;
|
||||
}
|
||||
.operationLog:first-child, .commentContentBox+.operationLog{
|
||||
margin-top: -15px;
|
||||
}
|
||||
.commentContentBox:last-child::before{
|
||||
height: 35%;
|
||||
}
|
||||
.flexCenter{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.commentRenderHtml.markdown-body p{
|
||||
font-size: 14px !important;
|
||||
}
|
||||
.commentReply{
|
||||
padding: 15px 0 0 20px;
|
||||
background-color: rgba(238, 240, 246, 0.41);
|
||||
&>div{
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
&+.commentReply>div{
|
||||
margin-top: -15px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px dashed #eee;
|
||||
}
|
||||
}
|
||||
.opBox{
|
||||
justify-content: flex-start;
|
||||
// flex: 1;
|
||||
// width: 100%;
|
||||
}
|
||||
.commentContentBox{
|
||||
display: flex;
|
||||
}
|
||||
.commentContentRight{
|
||||
flex: 1;
|
||||
background-color:#fafafc;
|
||||
border:1px solid rgba(42, 97, 255, 0.23);
|
||||
border-radius:6px;
|
||||
position: relative;
|
||||
top: -6px;
|
||||
padding: 10px 15px 16px;
|
||||
&::before{
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 13px;
|
||||
left: -14px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
border: 7px;
|
||||
border-style: solid;
|
||||
border-color: transparent rgba(42, 97, 255, 0.23) transparent transparent;
|
||||
}
|
||||
&::after{
|
||||
content: '';
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
border: 6px;
|
||||
border-style: solid;
|
||||
border-color: transparent #fafafc transparent transparent;
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
left: -11px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.primaryColor, .primaryColor:link{
|
||||
color: $primary-color;
|
||||
}
|
||||
.attachmentBox{
|
||||
margin-top: -8px;
|
||||
}
|
||||
.color-grey-89{
|
||||
color: #898d9d;
|
||||
}
|
||||
// 添加评论样式
|
||||
.unLoginComment{
|
||||
height:58px;
|
||||
line-height: 58px;
|
||||
background-color:rgba(241, 243, 252, 0.55);
|
||||
border-radius:4px;
|
||||
color: rgba(84, 87, 103, 1);
|
||||
.loginBtn{
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
.quillFlagBox{
|
||||
color: red;
|
||||
margin-top: -10px;
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
.addComments{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color:rgba(241, 243, 252, 0.55);
|
||||
border-radius:4px;
|
||||
padding:7px 10px;
|
||||
&>img{
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
border-radius: 50%;
|
||||
margin-right: 13px;
|
||||
}
|
||||
}
|
||||
.addCommentBox{
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
border: none;
|
||||
}
|
||||
.paginationIssueComment{
|
||||
text-align: center;
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import React ,{ useState } from 'react';
|
||||
import { Tooltip } from 'antd';
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
|
||||
|
||||
function Copy({ children , value }){
|
||||
const [ title , setTitle ] = useState("点击复制链接");
|
||||
let host = window.location.hostname === "localhost" ? "testforgeplus.trustie.net" : window.location.hostname;
|
||||
let protocol = window.location.protocol;
|
||||
return(
|
||||
<CopyToClipboard text={`${protocol}//${host}${value}`}
|
||||
onCopy={() => setTitle("复制成功")}>
|
||||
<Tooltip
|
||||
placement="bottom"
|
||||
title={title}
|
||||
>
|
||||
{children}
|
||||
</Tooltip>
|
||||
</CopyToClipboard>
|
||||
)
|
||||
}
|
||||
export default Copy;
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
import React from 'react';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import gold from '../Img/gold.png';
|
||||
import { Link } from "react-router-dom";
|
||||
import Copy from '../Component/copy';
|
||||
import { Tooltip } from 'antd';
|
||||
|
||||
|
||||
// issue列表显示
|
||||
function Datas({checkbox ,item , projectsId,owner}){
|
||||
|
||||
function statusTag(name){
|
||||
switch (name) {
|
||||
case "低":
|
||||
return "status low";
|
||||
case "正常":
|
||||
return "status normals";
|
||||
case "高":
|
||||
return "status hight";
|
||||
default:
|
||||
return "status urgent";
|
||||
}
|
||||
}
|
||||
|
||||
function renderName(list){
|
||||
let l = list.map(i=>{return i.name});
|
||||
return l.join(",");
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
<div className="issuedetail">
|
||||
{checkbox}
|
||||
<div style={{flex:1}}>
|
||||
<div className="idetails">
|
||||
<span className={statusTag(item.priority_name)}>{item.priority_name}</span>
|
||||
{/* <img src={issue} alt="" width="16px" className="mr5" /> */}
|
||||
<Link to={`/${owner}/${projectsId}/issues/${item.project_issues_index}`} style={{maxWidth:`${item.tags ? 735 - item.tags.length*120 : 735}px`}} title={item.subject}>{item.subject}</Link>
|
||||
{
|
||||
item.tags && item.tags.length>0?
|
||||
item.tags.map((i,k)=>{
|
||||
return(
|
||||
<span style={{backgroundColor: `${i.color}`}} className="ml8 tagscolor task-hide" title={i.name}>{i.name}</span>
|
||||
)
|
||||
})
|
||||
:""
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
<div className="infos">
|
||||
<div className="ilog">
|
||||
{ item.project_issues_index &&
|
||||
<Copy value={`/${owner}/${projectsId}/issues/${item.project_issues_index}`}><span className="number">#{item.project_issues_index}</span></Copy>
|
||||
}
|
||||
</div>
|
||||
<Link to={`/${item.author && item.author.login}`}><i className="iconfont icon-chengyuan2 mr3 font-12" style={{color:'#898d9d'}}></i></Link>
|
||||
<span className="mr12"><Link style={{color:"#898d9d"}} to={`/${item.author && item.author.login}`}>{item.author && item.author.name}</Link></span>
|
||||
<span className="mr12">{item.created_at} 发布</span>
|
||||
<span className="mr20">{item.updated_at}更新</span>
|
||||
{item.blockchain_token_num && <span className="mr30"><img src={gold} alt="" width="13px" className="mr3"/>{item.blockchain_token_num}</span>}
|
||||
{item.milestone_name && <span style={{maxWidth:item.blockchain_token_num ? "261px":"340px"}} title={item.milestone_name} className="task-hide"><i className="iconfont icon-lichengbeiicon1 font-12 mr3"></i>{item.milestone_name}</span> }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="issuecondition">
|
||||
{
|
||||
item.assigners && item.assigners.length > 0 ?
|
||||
<Tooltip title={renderName(item.assigners)} placement="bottomRight">
|
||||
<div className={item.assigners.length > 1 ?"principal hovers":"principal"}>
|
||||
{/* {
|
||||
item.assigners.map((i,k)=>{
|
||||
return(
|
||||
k<5 && <Link to={`/${i.login}`} style={{right:`${(item.assigners.length-k-1)*(22-7)}px`,zIndex:k+1}}><img src={getImageUrl(i.image_url)} alt="" /></Link>
|
||||
)
|
||||
})
|
||||
} */}
|
||||
<span className="task-hide" style={{wordBreak:'break-all'}}>{renderName(item.assigners)}</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
:""
|
||||
}
|
||||
<div style={{color:item.status_name === "已解决" ?"#28bd6c":'#40424a'}}>{item.status_name}</div>
|
||||
<div className="commentnum">
|
||||
<Link to={`/${owner}/${projectsId}/issues/${item.project_issues_index}#commentList`}><i className="iconfont icon-a-xiaoxi1 mr5 font-15"></i>{item.comment_journals_count}</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Datas;
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
import React,{useRef,useEffect, useState , forwardRef } from 'react';
|
||||
import { Dropdown , Calendar , Select, Radio, Col, Row } from 'antd';
|
||||
import { findDOMNode } from 'react-dom';
|
||||
import moment from 'moment';
|
||||
const { Group , Button } = Radio;
|
||||
|
||||
function Date({name , today , setDate , editFlag},ref){
|
||||
const [ visible , setVisible ] = useState(false);
|
||||
const [ time , setTime ] = useState();
|
||||
|
||||
useEffect(()=>{
|
||||
if(today){
|
||||
setTime(today);
|
||||
}
|
||||
},[today])
|
||||
|
||||
const refFa = useRef(null);
|
||||
const refBox = useRef(null);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('click', clickMe , false);
|
||||
}, [])
|
||||
|
||||
const clickMe = ({ target }) => {
|
||||
// 查找父组件
|
||||
const faComponent = findDOMNode(refFa.current);
|
||||
const boxComponent = findDOMNode(refBox.current);
|
||||
if (faComponent && boxComponent) {
|
||||
const isChild = faComponent.contains(target);
|
||||
const isBox = boxComponent.contains(target);
|
||||
if(!isChild && !isBox){
|
||||
setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
function onSelect(t){
|
||||
setTime(moment(t).format('YYYY-MM-DD'));
|
||||
setDate(moment(t).format('YYYY-MM-DD'));
|
||||
setVisible(false);
|
||||
}
|
||||
const overlay=(
|
||||
<div ref={refFa} style={{ width: 280, border: '1px solid #d9d9d9', borderRadius: 4 ,backgroundColor:"#fff",marginTop:"-10px"}}>
|
||||
<Calendar
|
||||
fullscreen={false}
|
||||
headerRender={({ value, type, onChange, onTypeChange }) => {
|
||||
const start = 0;
|
||||
const end = 12;
|
||||
const monthOptions = [];
|
||||
const current = value.clone();
|
||||
const localeData = value.localeData();
|
||||
const months = [];
|
||||
for (let i = 0; i < 12; i++) {
|
||||
current.month(i);
|
||||
months.push(localeData.monthsShort(current));
|
||||
}
|
||||
|
||||
for (let index = start; index < end; index++) {
|
||||
monthOptions.push(
|
||||
<Select.Option className="month-item" key={`${index}`}>
|
||||
{months[index]}
|
||||
</Select.Option>,
|
||||
);
|
||||
}
|
||||
const month = value.month();
|
||||
|
||||
const year = value.year();
|
||||
const options = [];
|
||||
for (let i = year - 10; i < year + 10; i += 1) {
|
||||
options.push(
|
||||
<Select.Option key={i} value={i} className="year-item">
|
||||
{i}
|
||||
</Select.Option>,
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div style={{ padding: 10 }}>
|
||||
|
||||
<Row type="flex" justify="space-between">
|
||||
<Col>
|
||||
<Group size="small" onChange={e => onTypeChange(e.target.value)} value={type}>
|
||||
<Button value="month">日期</Button>
|
||||
<Button value="year">月份</Button>
|
||||
</Group>
|
||||
</Col>
|
||||
<Col>
|
||||
<Select
|
||||
size="small"
|
||||
dropdownMatchSelectWidth={false}
|
||||
className="my-year-select"
|
||||
style={{ width: 80 }}
|
||||
onChange={newYear => {
|
||||
const now = value.clone().year(newYear);
|
||||
onChange(now);
|
||||
}}
|
||||
value={String(year)}
|
||||
>
|
||||
{options}
|
||||
</Select>
|
||||
</Col>
|
||||
<Col>
|
||||
<Select
|
||||
size="small"
|
||||
dropdownMatchSelectWidth={false}
|
||||
value={String(month)}
|
||||
onChange={selectedMonth => {
|
||||
const newValue = value.clone();
|
||||
newValue.month(parseInt(selectedMonth, 10));
|
||||
onChange(newValue);
|
||||
}}
|
||||
>
|
||||
{monthOptions}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
return(
|
||||
<Dropdown
|
||||
placement={"bottomLeft"}
|
||||
overlay={overlay}
|
||||
visible={visible}
|
||||
trigger={['click']}
|
||||
>
|
||||
<li>
|
||||
<span>
|
||||
{name}
|
||||
{!editFlag && <a ref={refBox} onClick={()=>setVisible(visible ? false : true)}><i className="iconfont icon-riqi font-14" style={{color:"#898d9d"}}></i></a> }
|
||||
</span>
|
||||
<p className={time ? "operatevalue color-grey-3 task-hide" : "operatevalue task-hide"}>{time || "未设置"}</p>
|
||||
</li>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
export default forwardRef(Date);
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import React from "react";
|
||||
import Modals from '../../Component/PublicModal/Index';
|
||||
import { AlignTop } from '../../Component/layout';
|
||||
import { Button } from 'antd';
|
||||
|
||||
function DelBox({visible,onCancel,onSuccess,content,title}){
|
||||
return(
|
||||
<Modals
|
||||
visible={visible}
|
||||
onCancel={onCancel}
|
||||
title={title || "删除疑修"}
|
||||
btn={
|
||||
<div>
|
||||
<Button size={'large'} onClick={onCancel}>取消</Button>
|
||||
<Button type={"danger"} size={"large"} onClick={onSuccess}>确认删除</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="desc">
|
||||
<AlignTop className="deldesc">
|
||||
<i className="iconfont icon-jinggao1 mr10 font-20 red"></i>
|
||||
{ content ?
|
||||
content
|
||||
:
|
||||
<div style={{paddingTop:"3px"}}><p className="font-15 mb20">您确定要删除所有选中的疑修?</p><p className="color-grey-6">此操作将清空所有已选中的疑修,请谨慎操作</p></div>
|
||||
}
|
||||
</AlignTop>
|
||||
</div>
|
||||
</Modals>
|
||||
)
|
||||
}
|
||||
export default DelBox;
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import React,{useState, useEffect, useRef, useImperativeHandle,forwardRef} from 'react';
|
||||
import { Dropdown } from 'antd';
|
||||
import { findDOMNode } from 'react-dom';
|
||||
|
||||
function Drop({overlay , children , placement, overlayClassName},ref){
|
||||
const [ visible , setVisible ] = useState(false);
|
||||
const refFa = useRef(null);
|
||||
const refBox = useRef(null);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
clearVisible: (v) => {
|
||||
// 父组件按钮:清除筛选条件,将dropmenu里的choose字段全部改为0
|
||||
setVisible(v);
|
||||
}
|
||||
}))
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('click', clickMe , false);
|
||||
}, [])
|
||||
|
||||
const clickMe = ({ target }) => {
|
||||
// 查找父组件
|
||||
const faComponent = findDOMNode(refFa.current);
|
||||
const boxComponent = findDOMNode(refBox.current);
|
||||
if (faComponent && boxComponent) {
|
||||
const isChild = faComponent.contains(target);
|
||||
const isBox = boxComponent.contains(target);
|
||||
if(!isChild && !isBox){
|
||||
setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return(
|
||||
<Dropdown
|
||||
placement={placement}
|
||||
visible={visible}
|
||||
overlay={<div ref={refFa}>{overlay}</div>}
|
||||
trigger={['click']}
|
||||
overlayClassName={overlayClassName}
|
||||
>
|
||||
<li ref={refBox} onClick={()=>setVisible(visible ? false : true)}>
|
||||
{children}
|
||||
</li>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
export default forwardRef(Drop);
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
import React ,{ useState , useEffect , useRef } from 'react';
|
||||
import { findDOMNode } from 'react-dom';
|
||||
import { Dropdown , Menu , Input , message} from 'antd';
|
||||
import { getImageUrl } from 'educoder';
|
||||
|
||||
const { Search } = Input;
|
||||
/**
|
||||
*
|
||||
* @param {placeholder} placeholder:默认显示内容(为空时)
|
||||
* @param {editFlag} editFlag:是否有编辑权限
|
||||
* @param {searchFlag} searchFlag:是否显示搜索框
|
||||
* @param {selectValueList} selectValueList:将选中的list保存
|
||||
* @param {searchFunc} searchFunc:搜索方法
|
||||
* @param {onAdd} onAdd:标记管理
|
||||
* @param {headImg} headImg:是否显示头像
|
||||
* @param {menus} menus:下拉列表
|
||||
* @param {chooseFunc} chooseFunc:选择下拉列表
|
||||
* @param {double} double:undefined为单选,有值就是最多选的数量
|
||||
* @param {colorFlag} colorFlag:选中值需根据颜色显示背景
|
||||
* @param {mustFlag} mustFlag:必须选择一个
|
||||
* @param {removeFlag} removeFlag:移除按钮
|
||||
* @returns
|
||||
*/
|
||||
function DropMenu({
|
||||
placeholder ="未设置",
|
||||
editFlag,
|
||||
searchFlag,
|
||||
selectValueList,
|
||||
searchFunc,
|
||||
onAdd,
|
||||
headImg,
|
||||
menus,
|
||||
chooseFunc,double,colorFlag,mustFlag,removeFlag
|
||||
}){
|
||||
const [ visible , setVisible ] = useState(false);
|
||||
const [ searchValue , setSearchValue ]= useState(undefined);
|
||||
const [ valuesId , setValuesId ] = useState([]);
|
||||
const [ valuesName , setValuesName ] = useState([]);
|
||||
|
||||
const refFa = useRef(null);
|
||||
const refBox = useRef(null);
|
||||
|
||||
// 根据选中的列表循环出id数组
|
||||
useEffect(()=>{
|
||||
if(selectValueList && selectValueList.length > 0 ){
|
||||
renderSelectList(selectValueList);
|
||||
}else{
|
||||
setValuesId([])
|
||||
}
|
||||
},[selectValueList])
|
||||
|
||||
function renderSelectList(list){
|
||||
let a = list && list.length > 0 && list.map((i,k)=>{
|
||||
return i.id ? i.id.toString() : i.name
|
||||
})
|
||||
setValuesId(a);
|
||||
setValuesName(list);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('click', clickMe , false);
|
||||
}, [])
|
||||
|
||||
const clickMe = ({ target }) => {
|
||||
// 查找父组件
|
||||
const faComponent = findDOMNode(refFa.current);
|
||||
const boxComponent = findDOMNode(refBox.current);
|
||||
if (faComponent && boxComponent) {
|
||||
const isChild = faComponent.contains(target);
|
||||
const isBox = boxComponent.contains(target);
|
||||
if(!isChild && !isBox){
|
||||
setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 搜索
|
||||
function changeSearchvalue(e){
|
||||
setSearchValue(e.target.value);
|
||||
searchFunc(e.target.value);
|
||||
}
|
||||
|
||||
function chooseMenu(i){
|
||||
let list = selectValueList;
|
||||
let re = [];
|
||||
re = list && list.length > 0 ? list:[];
|
||||
let filter = [];
|
||||
if(i.id){
|
||||
filter = list.filter(j=>j.id === i.id);
|
||||
}else{
|
||||
filter = list.filter(j=>j.name === i.name);
|
||||
}
|
||||
if(filter && filter.length > 0){
|
||||
if(!mustFlag){
|
||||
if(i.id){
|
||||
re = list.filter(j=>j.id !== i.id);
|
||||
}else{
|
||||
re = list.filter(j=>j.name !== i.name);
|
||||
}
|
||||
renderSelectList(re);
|
||||
chooseFunc(re);
|
||||
!double && setVisible(false);
|
||||
}else{
|
||||
setVisible(false);
|
||||
}
|
||||
}else{
|
||||
if(double && (selectValueList && selectValueList.length >= double)){
|
||||
message.info(`最多只能添加${double}个${placeholder}`);
|
||||
return;
|
||||
}
|
||||
if(double){
|
||||
re.push(i);
|
||||
}else{
|
||||
re = [i];
|
||||
setVisible(false);
|
||||
}
|
||||
renderSelectList(re);
|
||||
chooseFunc(re);
|
||||
}
|
||||
}
|
||||
|
||||
function showRemoveFunc(item){
|
||||
let l = selectValueList ;
|
||||
l = l.filter(i=>(i.id ? i.id.toString() : i.name) !== item.toString());
|
||||
renderSelectList(l);
|
||||
chooseFunc(l);
|
||||
}
|
||||
|
||||
function renderNames(nameArrs){
|
||||
return <React.Fragment>
|
||||
{
|
||||
nameArrs && nameArrs.length>0?
|
||||
nameArrs.map((i,k)=>{
|
||||
return(
|
||||
<p style={{display:"flex",alignItems:"center"}} className={removeFlag?"removeFlag":''}>
|
||||
{i.image_url && <img src={getImageUrl(i.image_url)} alt="" width="18px" height="18px" style={{borderRadius:"50%"}} className="mr5"/>}
|
||||
{
|
||||
colorFlag ?
|
||||
(
|
||||
colorFlag === "2" ?
|
||||
<span className={"colorsquare task-hide"} style={{backgroundColor:`${i.color}`}}>{i.name}</span>
|
||||
:
|
||||
<span className={"colorsborder task-hide"} style={{borderColor:`${i.color}`,color:`${i.color}`}}>{i.name}</span>
|
||||
)
|
||||
:
|
||||
<span className={"task-hide"}>{i.name}</span>
|
||||
}
|
||||
{ removeFlag && <a className="removeicon" onClick={()=>showRemoveFunc(i.id || i.name)}><i className="iconfont icon-shanchu8 font-14"></i></a>}
|
||||
</p>
|
||||
)
|
||||
})
|
||||
:<span>{placeholder}</span>
|
||||
}
|
||||
</React.Fragment>
|
||||
}
|
||||
return(
|
||||
<li>
|
||||
<span>
|
||||
{placeholder}
|
||||
{!editFlag &&
|
||||
<a ref={refBox} onClick={()=>setVisible(visible ? false : true)}>
|
||||
<i className="iconfont icon-a-bianji12 font-13" style={{color:"#898d9d"}}></i>
|
||||
</a>
|
||||
}
|
||||
</span>
|
||||
<Dropdown
|
||||
visible={visible}
|
||||
overlayClassName={"overlayStyle"}
|
||||
placement="bottomLeft"
|
||||
trigger={['click']}
|
||||
overlay={
|
||||
<div ref={refFa}>
|
||||
{ searchFunc &&
|
||||
<div className="searchbox">
|
||||
<Search
|
||||
placeholder={`搜索${placeholder}`}
|
||||
value={searchValue}
|
||||
onChange={changeSearchvalue}
|
||||
style={{marginRight:"18px"}}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
{
|
||||
menus && menus.length >0?
|
||||
<Menu className="piecemenu" selectedKeys={valuesId}>
|
||||
{
|
||||
menus.map((i,k)=>{
|
||||
return(
|
||||
<Menu.Item key={i.id || i.name} onClick={()=>chooseMenu(i)}>
|
||||
{ headImg && <img src={getImageUrl(i.image_url)} alt="" width="22px" className="mr5 radius"/>}
|
||||
{i.color && <span style={{backgroundColor:i.color}} className="colorpiece"></span>}
|
||||
<span className="task-hide">{i.name}</span>
|
||||
</Menu.Item>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Menu>
|
||||
:
|
||||
<div className="menusEmpty">
|
||||
<p>{searchValue ? <span>暂无{placeholder}“{searchValue}”</span>: `暂无${placeholder}`}</p>
|
||||
</div>
|
||||
}
|
||||
{ onAdd && <div className="pl35 pr20 pb20"><a className="color-blue font-15" onClick={()=>{setVisible(false);onAdd()}}><i className="iconfont icon-a-bianji12 font-14 mr5"></i>创建标记</a></div>}
|
||||
</div>
|
||||
}>
|
||||
<div className={selectValueList && selectValueList.length > 0 ? "operatevalue color-grey-3":"operatevalue"}>{renderNames(selectValueList)}</div>
|
||||
</Dropdown>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
export default DropMenu;
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
import React ,{ useState , useRef , useEffect } from 'react';
|
||||
import { Input , Menu , Icon } from 'antd';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import Drop from './drop';
|
||||
const { Search } = Input;
|
||||
|
||||
/**
|
||||
* @param {name} 未选择时显示的内容
|
||||
* @param {lists} 下拉列表,从接口获取,需一一填充
|
||||
* @param {imgControl} 控制是否显示头像
|
||||
* @param {size} 下拉框宽度,small:120px,large:260px
|
||||
* @param {ids} 默认选择的项的id数组
|
||||
* @param {search} 搜索方法
|
||||
* @param {chooseFunc} 选择项后需调用列表的查询方法
|
||||
* @param {update} 编辑状态
|
||||
* @param {double} 是否可以多选
|
||||
*/
|
||||
function Menus({name, ids , lists , size , imgControl , searchFunc , chooseFunc , update , double ,names },ref){
|
||||
const dropRef = useRef(null);
|
||||
const [ chooseValue , setChooseValue ] = useState([]);
|
||||
const [ showValue , setShowValue ] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
setChooseValue(ids);
|
||||
console.log(ids);
|
||||
setShowValue(names);
|
||||
},[ids,names])
|
||||
|
||||
|
||||
// 修改menus数组里的字段值
|
||||
function changeMenusValue(i){
|
||||
let l = ids;
|
||||
let id = i.id.toString();
|
||||
if(double){
|
||||
l = ids ? ids.split(",") : [] ;
|
||||
let nameArr = names ? names.split(",") : [] ;
|
||||
if(l && l.indexOf(id)>=0){
|
||||
l = l.filter(k=>k.toString() !== id);
|
||||
nameArr = nameArr.filter(k=>k.toString() !== i.name);
|
||||
}else{
|
||||
l.push(id);
|
||||
nameArr.push(i.name);
|
||||
}
|
||||
setChooseValue(l);
|
||||
setShowValue(nameArr.join(","));
|
||||
chooseFunc(l,nameArr.join(","));
|
||||
console.log("choosed:",l);
|
||||
}else{
|
||||
if(l && l.indexOf(id)>=0){
|
||||
setChooseValue([]);
|
||||
setShowValue(undefined);
|
||||
chooseFunc([]);
|
||||
}else{
|
||||
setChooseValue([`${i.id}`]);
|
||||
setShowValue([i.name]);
|
||||
chooseFunc([id],i.name);
|
||||
}
|
||||
dropRef.current && dropRef.current.clearVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
function menu(data){
|
||||
return <div className={`overlaydrop ${size}`}>
|
||||
{ size !== "small" &&
|
||||
<div className="pb10"><Search placeholder={`搜索${name}`} onChange={(e)=>searchFunc(e.target.value)}/></div>
|
||||
}
|
||||
{
|
||||
data && data.length>0 ?
|
||||
<Menu selectedKeys={chooseValue}>
|
||||
{
|
||||
data.map((i,j)=>{
|
||||
return <Menu.Item key={i.id}>
|
||||
{imgControl && <img src={getImageUrl(i.image_url)} alt=""/>}
|
||||
{i.color && <span style={{backgroundColor:i.color,marginRight:"0px"}} className="colorpiece"></span>}
|
||||
<span className="task-hide" onClick={()=>changeMenusValue(i)}>{i.name}</span>
|
||||
</Menu.Item>
|
||||
})
|
||||
}
|
||||
</Menu>
|
||||
:
|
||||
<div className="pl15">暂无{ids ? <span>'{name}'</span>: name}</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
return(
|
||||
<Drop ref={dropRef} overlay={menu(lists)} placement={"bottomRight"} >
|
||||
<span className="task-hide">{showValue || (update ? `更换${name}` : name) }</span>
|
||||
<Icon type="caret-down" className="ml5 color-grey-6" />
|
||||
</Drop>
|
||||
)
|
||||
}
|
||||
export default Menus;
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
import React , { forwardRef , useState } from 'react';
|
||||
import { Form , Input , Button } from 'antd';
|
||||
import MDEditor from "../../../modules/tpm/challengesnew/tpm-md-editor";
|
||||
import Upload from "../../Upload/Index";
|
||||
import Attachments from "../../Upload/attachment";
|
||||
import UploadImg from '../Img/UploadImg.png';
|
||||
|
||||
function NewPanel(props,ref){
|
||||
const [ description , setDescription ] = useState("");
|
||||
const [ fileList , setFileList] = useState(undefined);
|
||||
const [ attachments , setAttachments ] = useState([]);
|
||||
const [ receivers_login , setReceiversLogin ] = useState(undefined);
|
||||
|
||||
const { createFunc , title , desc , files , onCancel , owner , projectsId } = props;
|
||||
|
||||
const { form: { getFieldDecorator, validateFields , setFieldsValue } } = props;
|
||||
|
||||
useState(()=>{
|
||||
title && setTimeout(()=>{
|
||||
setFieldsValue({subject:title});
|
||||
},100)
|
||||
desc && setDescription(desc);
|
||||
files && setAttachments(files);
|
||||
},[title , desc , files])
|
||||
|
||||
function onContentChange(value){
|
||||
setDescription(value);
|
||||
}
|
||||
function UploadFunc(f){
|
||||
let list = [];
|
||||
let arr = [];
|
||||
if(files && files.length>0 ){
|
||||
arr = files.map(i=>{return i.id});
|
||||
}
|
||||
list = arr && arr.length>0 ? f.concat(arr) : f ;
|
||||
setFileList(list);
|
||||
};
|
||||
|
||||
function deleteLoad(id){
|
||||
let arr = [];
|
||||
let list = files
|
||||
if(list && list.length>0 ){
|
||||
arr = list.filter(i=>i.id !== id);
|
||||
}
|
||||
setFileList(arr.map(i=>{return i.id}));
|
||||
}
|
||||
|
||||
function changeAtWhoLoginList(loginList){
|
||||
let list = new Set(receivers_login);
|
||||
loginList.map(item => list.add(item));
|
||||
setReceiversLogin(Array.from(list));
|
||||
};
|
||||
|
||||
function sureFunc(){
|
||||
validateFields((error,values)=>{
|
||||
if (!error) {
|
||||
createFunc(values,fileList,receivers_login,description);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function cancelFunc(){
|
||||
if(onCancel){
|
||||
onCancel();
|
||||
}else{
|
||||
window.history.back(-1);
|
||||
}
|
||||
}
|
||||
return(
|
||||
<React.Fragment>
|
||||
<Form className="explain">
|
||||
<Form.Item>
|
||||
{getFieldDecorator("subject",{
|
||||
rules:[{required:true,message:"请输入疑修标题"}]
|
||||
})(
|
||||
<Input placeholder="标题" maxLength={100}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<div style={{position: 'relative'}}>
|
||||
<MDEditor
|
||||
placeholder={"请输入描述信息"}
|
||||
height={392}
|
||||
mdID={"order-new-description"}
|
||||
initValue={description}
|
||||
onChange={onContentChange}
|
||||
className="mt20"
|
||||
isCanAtme = {true}
|
||||
owner = {owner}
|
||||
projectsId = {projectsId}
|
||||
changeAtWhoLoginList = {changeAtWhoLoginList}
|
||||
></MDEditor>
|
||||
</div>
|
||||
<div className="pb20">
|
||||
<Upload
|
||||
className="commentStyle mt20"
|
||||
isComplete={true}
|
||||
load={UploadFunc}
|
||||
icon={
|
||||
<img
|
||||
src={UploadImg}
|
||||
width="58"
|
||||
alt=""
|
||||
style={{ marginBottom: 15 }}
|
||||
/>
|
||||
}
|
||||
size={100}
|
||||
showNotification={props.showNotification}
|
||||
/>
|
||||
{attachments && attachments.length > 0 &&
|
||||
<Attachments
|
||||
attachments={attachments}
|
||||
showNotification={props.showNotification}
|
||||
canDelete={true}
|
||||
deleteLoad={deleteLoad}
|
||||
></Attachments>
|
||||
}
|
||||
</div>
|
||||
<div style={{display:"flex"}}>
|
||||
<Button type="primary" className="operateButton" style={{width:"100px"}} onClick={sureFunc}>{ title ? "保存" :"创建"}</Button>
|
||||
<Button className="ml30" style={{width:"100px"}} onClick={cancelFunc}>取消</Button>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
export default forwardRef(NewPanel);
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 255 B |
|
After Width: | Height: | Size: 133 B |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 708 B |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 608 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
|
@ -0,0 +1,514 @@
|
|||
import React , { useEffect , useState , forwardRef , useRef } from 'react';
|
||||
import { Spin , Form , InputNumber, message } from 'antd';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Box , LongWidth } from '../../Component/layout';
|
||||
import RenderHtml from "../../../components/render-html";
|
||||
import DropMenu from '../Component/dropMenu';
|
||||
import NewPanel from '../Component/newPanel';
|
||||
import Attachments from "../../Upload/attachment";
|
||||
import DelBox from '../Component/delBox';
|
||||
import Date from '../Component/date';
|
||||
import moment from 'moment';
|
||||
import Copy from '../Component/copy';
|
||||
import axios from 'axios';
|
||||
import CommentList from '../Component/comments/list';
|
||||
import Claims from '../../claims/claims';
|
||||
import ChooseMenu from '../Component/chooseMenu';
|
||||
|
||||
function Details(props){
|
||||
const owner = props.match.params.owner;
|
||||
const projectsId = props.match.params.projectsId;
|
||||
const index = props.match.params.index;
|
||||
|
||||
const [ details , setDetails ] = useState(undefined);
|
||||
const [ statusList , setStatusList ] = useState([]);
|
||||
const [ prioritiesList , setPrioritiesList ] = useState([]);
|
||||
const [ chargeList , setChargeList ] = useState([]);
|
||||
const [ charge , setCharge ] = useState(undefined);
|
||||
const [ millstoneList , setMillstoneList ] = useState([]);
|
||||
const [ millstone , setMillstone ] = useState(undefined);
|
||||
const [ tagList , setTagList ] = useState(undefined);
|
||||
const [ tag , setTag ] = useState(undefined);
|
||||
const [ branchList , setBranchList ] = useState(undefined);
|
||||
const [ branch , setBranch ] = useState(undefined);
|
||||
|
||||
const [ orderId , setOrderId ] = useState(undefined);
|
||||
|
||||
const [ delVisible , setDelVisible] = useState(false);
|
||||
const [ edit , setEdit] = useState(false);
|
||||
const [ copy , setCopy] = useState(false);
|
||||
|
||||
const [ start_date, setStartDate ] = useState("");
|
||||
const [ due_date, setDueDate ] = useState("");
|
||||
const [ commentReload, setCommentReload] = useState(undefined);
|
||||
const [ editFlag , setEditFlag ] = useState(false);
|
||||
const [ amountEditFlag , setAmountEditFlag ] = useState(false);
|
||||
|
||||
const [ assigner_choose , setAssigner_choose ] = useState([]);
|
||||
const [ prioritie_choose , setPrioritie_choose ] = useState([]);
|
||||
const [ status_choose , setStatus_choose ] = useState([]);
|
||||
const [ tag_choose , setTag_choose ] = useState([]);
|
||||
const [ millstone_choose , setMillstone_choose ] = useState([]);
|
||||
const [ branch_choose , setBranch_choose ] = useState([]);
|
||||
const [ rewardAmount , setRewardAmount ] = useState(undefined);
|
||||
const [ rewardFlag , setRewardFlag ] = useState(false);
|
||||
|
||||
const pathname = props.history.location.pathname;
|
||||
const permission = props && props.projectDetail && props.projectDetail.permission;
|
||||
let colors = ["#1abcb1","#28be6c","#e67e22","#db3d1d"];
|
||||
|
||||
const {projectDetail , open_blockchain ,current_user } = props;
|
||||
useEffect(()=>{
|
||||
if(pathname === `/${owner}/${projectsId}/issues/${index}/copy`){
|
||||
setEdit(true);
|
||||
setCopy(true);
|
||||
}else{
|
||||
setEdit(false);
|
||||
setCopy(false);
|
||||
}
|
||||
},[pathname])
|
||||
|
||||
useEffect(()=>{
|
||||
if(projectDetail && details){
|
||||
const { author, name} = projectDetail;
|
||||
document.title = `${details.subject}-疑修-${author.name}/${name}`;
|
||||
}
|
||||
},[details,projectDetail])
|
||||
|
||||
useEffect(()=>{
|
||||
if(index){
|
||||
Init();
|
||||
}
|
||||
},[index])
|
||||
|
||||
function Init(){
|
||||
const url = `/v1/${owner}/${projectsId}/issues/${index}`;
|
||||
axios.get(url).then(result=>{
|
||||
if(result && result.data){
|
||||
let data = result.data;
|
||||
setDetails(data);
|
||||
let per = data && !data.user_permission;
|
||||
setEditFlag(per);
|
||||
|
||||
const aut = current_user && data.author && (data.author.login === current_user.login);
|
||||
const con = data.pull_fixed === false && aut;
|
||||
setAmountEditFlag(con);
|
||||
|
||||
setAssigner_choose(data.assigners);
|
||||
if(data.priority && data.priority.id){
|
||||
setPrioritie_choose([{...data.priority,color:colors[data.priority.id-1]}]);
|
||||
}else{
|
||||
setPrioritie_choose([]);
|
||||
}
|
||||
setStatus_choose(data.status && data.status.id ? [data.status]:[]);
|
||||
setTag_choose(data.tags);
|
||||
setMillstone_choose(data.milestone && data.milestone.id ? [data.milestone]:[]);
|
||||
setBranch_choose(data.branch_name ? [{name:data.branch_name}] : []);
|
||||
setRewardAmount(data.blockchain_token_num);
|
||||
|
||||
data.start_date && setStartDate(moment(data.start_date).format('YYYY-MM-DD'));
|
||||
data.due_date && setDueDate(moment(data.due_date).format('YYYY-MM-DD'));
|
||||
|
||||
setOrderId(data.id);
|
||||
}
|
||||
}).catch(error=>{
|
||||
console.log(error);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function statusTag(name){
|
||||
switch (name) {
|
||||
case "低":
|
||||
return "status low";
|
||||
case "正常":
|
||||
return "status normals";
|
||||
case "高":
|
||||
return "status hight";
|
||||
default:
|
||||
return "status urgent";
|
||||
}
|
||||
}
|
||||
// 获取状态列表
|
||||
useEffect(()=>{
|
||||
getStatus();
|
||||
},[])
|
||||
function getStatus(){
|
||||
const url = `/v1/${owner}/${projectsId}/issue_statues`;
|
||||
axios.get(url).then(result=>{
|
||||
if(result && result.data){
|
||||
setStatusList(result.data.statues);
|
||||
}
|
||||
})
|
||||
}
|
||||
// 获取优先级列表
|
||||
useEffect(()=>{
|
||||
getPriorities();
|
||||
},[])
|
||||
|
||||
function getPriorities(){
|
||||
const url = `/v1/${owner}/${projectsId}/issue_priorities`;
|
||||
axios.get(url).then(result=>{
|
||||
if(result && result.data){
|
||||
let priorities = result.data.priorities;
|
||||
let array = addColorForPriorities(priorities);
|
||||
setPrioritiesList(array);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function addColorForPriorities(list){
|
||||
let array =[];
|
||||
if(list && list.length>0){
|
||||
array = list.map((i,k)=>{
|
||||
let arr = {...i,color:colors[k]}
|
||||
return arr;
|
||||
})
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
// 获取负责人列表
|
||||
useEffect(()=>{
|
||||
getCharge();
|
||||
},[charge])
|
||||
|
||||
function getCharge(){
|
||||
const url = `/v1/${owner}/${projectsId}/collaborators`;
|
||||
axios.get(url,{params:{keyword:charge,only_name:true}}).then(result=>{
|
||||
if(result && result.data){
|
||||
setChargeList(result.data.collaborators);
|
||||
}
|
||||
})
|
||||
}
|
||||
// 获取里程碑列表
|
||||
useEffect(()=>{
|
||||
getMillstone();
|
||||
},[millstone])
|
||||
|
||||
function getMillstone(){
|
||||
const url = `/v1/${owner}/${projectsId}/milestones`;
|
||||
axios.get(url,{params:{keyword:millstone,only_name:true}}).then(result=>{
|
||||
if(result && result.data){
|
||||
setMillstoneList(result.data.milestones);
|
||||
}
|
||||
})
|
||||
}
|
||||
// 获取标记列表
|
||||
useEffect(()=>{
|
||||
getSign();
|
||||
},[tag])
|
||||
function getSign(){
|
||||
const url = `/v1/${owner}/${projectsId}/issue_tags`;
|
||||
axios.get(url,{params:{keyword:tag,only_name:true}}).then(result=>{
|
||||
if(result && result.data){
|
||||
setTagList(result.data.issue_tags);
|
||||
}
|
||||
})
|
||||
}
|
||||
// 获取标记列表
|
||||
useEffect(()=>{
|
||||
getBranch(branch);
|
||||
},[branch])
|
||||
function getBranch(branch){
|
||||
if(branch){
|
||||
let b = [...branchList];
|
||||
let l = b.filter(i=>i.name.indexOf(branch)>-1);
|
||||
setBranchList(l);
|
||||
return ;
|
||||
}
|
||||
const url = `/${owner}/${projectsId}/branches.json`;
|
||||
axios.get(url,{params:{keyword:tag}}).then(result=>{
|
||||
if(result && result.data){
|
||||
setBranchList(result.data);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 可单个编辑保存右侧的项(!copy:非复制状态下)
|
||||
function saveForeach(c,s,p,t,m,b,sDate,eDate,rewardAmount){
|
||||
if(!copy){
|
||||
const url = `/v1/${owner}/${projectsId}/issues/${index}`;
|
||||
axios.patch(url,{
|
||||
branch_name: b ? b.join(",") :undefined,
|
||||
status_id: s ? s.join(",") : undefined,
|
||||
priority_id: p ? p.join(",") :undefined,
|
||||
milestone_id: m ? m.join(",") :undefined,
|
||||
issue_tag_ids: t?t:undefined,
|
||||
assigner_ids: c?c:undefined,
|
||||
start_date:sDate,
|
||||
due_date:eDate,
|
||||
blockchain_token_num:rewardAmount
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
Init();
|
||||
// 更新操作日志
|
||||
setCommentReload(Math.random());
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
}
|
||||
|
||||
function deleteFunc(){
|
||||
const url = `/v1/${owner}/${projectsId}/issues/${index}`;
|
||||
axios.delete(url).then(result=>{
|
||||
if(result){
|
||||
props.showNotification("疑修删除成功!");
|
||||
props.history.push(`/${owner}/${projectsId}/issues`);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
// 编辑保存
|
||||
function createFunc(values,fileList,receivers_login,description){
|
||||
let params ={
|
||||
subject: values.subject,
|
||||
attachment_ids: fileList,
|
||||
receivers_login: receivers_login,
|
||||
description: description
|
||||
}
|
||||
if(!copy){
|
||||
// 调用编辑保存接口
|
||||
const url = `/v1/${owner}/${projectsId}/issues/${index}`;
|
||||
axios.patch(url,{
|
||||
...params
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
props.showNotification("疑修更新成功!");
|
||||
// 刷新操作记录
|
||||
setCommentReload(Math.random());
|
||||
Init();
|
||||
setEdit(false);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}else{
|
||||
// 调用新建保存接口
|
||||
const url = `/v1/${owner}/${projectsId}/issues`;
|
||||
let b = branch_choose && branch_choose.length>0 ? branch_choose.map(i=>{return i.name}) :undefined;
|
||||
let t = tag_choose && tag_choose.length>0 ? tag_choose.map(i=>{return i.id}) :undefined;
|
||||
let a = assigner_choose && assigner_choose.length>0 ? assigner_choose.map(i=>{return i.id}) :undefined;
|
||||
let m = millstone_choose && millstone_choose.length>0 ? millstone_choose.map(i=>{return i.id}) :undefined;
|
||||
let p = prioritie_choose && prioritie_choose.length>0 ? prioritie_choose.map(i=>{return i.id}) :undefined;
|
||||
let s = status_choose && status_choose.length>0 ? status_choose.map(i=>{return i.id}) :undefined;
|
||||
axios.post(url,{
|
||||
...params,
|
||||
branch_name: b && b.join(","),
|
||||
status_id: s && s.join(","),
|
||||
priority_id: p && p.join(","),
|
||||
milestone_id: m && m.join(","),
|
||||
issue_tag_ids: t,
|
||||
assigner_ids: a,
|
||||
start_date,
|
||||
due_date
|
||||
}).then(result=>{
|
||||
if(result && result.data && result.data.project_issues_index){
|
||||
props.showNotification("任务复制成功!");
|
||||
props.history.push(`/${owner}/${projectsId}/issues/${result.data.project_issues_index}`);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
}
|
||||
|
||||
// 取消编辑或复制
|
||||
function onCancel(){
|
||||
if(copy){
|
||||
props.history.push(`/${owner}/${projectsId}/issues/${index}`);
|
||||
}else{
|
||||
setEdit(false);
|
||||
}
|
||||
}
|
||||
|
||||
function refreshFunc(){
|
||||
// 更新操作日志
|
||||
setCommentReload(Math.random());
|
||||
}
|
||||
|
||||
|
||||
// 悬赏金额 失去焦点保存数据
|
||||
function amountBlur(e){
|
||||
if(!editFlag){
|
||||
setRewardFlag(false);
|
||||
const value = e.target.value;
|
||||
if(value<0){
|
||||
message.info("请输入大于0的正整数");
|
||||
setRewardAmount(rewardAmount);
|
||||
return;
|
||||
}
|
||||
setRewardAmount(e.target.value);
|
||||
if((value && value >= 0) || !value){
|
||||
saveForeach(undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined,value);
|
||||
}
|
||||
}
|
||||
}
|
||||
function changeAmount(value){
|
||||
if(amountEditFlag){
|
||||
setRewardAmount(value);
|
||||
}else{
|
||||
setRewardAmount(rewardAmount);
|
||||
}
|
||||
}
|
||||
return(
|
||||
details ?
|
||||
<Box>
|
||||
<LongWidth>
|
||||
<DelBox
|
||||
visible={delVisible}
|
||||
onCancel={()=>setDelVisible(false)}
|
||||
onSuccess={deleteFunc}
|
||||
content={<div style={{paddingTop:"3px"}}><p className="font-15 mb20">您确定要删除当前疑修?</p></div>}
|
||||
/>
|
||||
{
|
||||
edit ?
|
||||
<div style={{paddingTop:"25px"}} >
|
||||
<NewPanel
|
||||
{...props}
|
||||
onCancel={onCancel}
|
||||
title={details.subject}
|
||||
desc={details.description}
|
||||
files={copy ? undefined : details.attachments}
|
||||
createFunc={createFunc}
|
||||
owner = {owner} projectsId = {projectsId}
|
||||
/>
|
||||
</div>
|
||||
:
|
||||
<div>
|
||||
<div className="editpanel">
|
||||
<div className="detailbanner">
|
||||
<div className="detailtitle">
|
||||
<div className="mb12">
|
||||
{details.priority && <span className={statusTag(details.priority.name)}>{ details.priority.name }</span> }
|
||||
<p className="name">{details.subject}</p>
|
||||
</div>
|
||||
<div>
|
||||
<div className="ilog mt5">
|
||||
<Copy value={`/${owner}/${projectsId}/issues/${index}`}><span className="number">#{details.project_issues_index}</span></Copy>
|
||||
</div>
|
||||
{
|
||||
details.author &&
|
||||
<div>
|
||||
<Link to={`/${details.author.login}`} className="author"><img src={getImageUrl(details.author.image_url)} alt="" />{details.author.name}</Link>
|
||||
<span className="ml10" style={{color:"#898d9d"}}>添加于{details.created_at}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
details.user_permission &&
|
||||
<ul className="detailoperate">
|
||||
<li><a className="color-blue" onClick={()=>setEdit(true)}><i className="iconfont icon-a-bianji12 font-12 mr5"></i>编辑</a></li>
|
||||
<li><Link to={`/${owner}/${projectsId}/issues/${index}/copy`} className="color-blue ml20"><i className="iconfont icon-a-fuzhi2 font-12 mr5"></i>复制</Link></li>
|
||||
<li><a className="color-red ml20" onClick={()=>setDelVisible(true)}><i className="iconfont icon-fuzhi-shanchu font-12 mr5"></i>删除</a></li>
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
<div class="descPanel">
|
||||
{details.description ?
|
||||
<RenderHtml className="break_word_comments imageLayerParent" value={details.description} url={props.history.location} />
|
||||
:
|
||||
<span className="color-grey-9 ml3 mr3">暂无描述</span>
|
||||
}
|
||||
{details.attachments && details.attachments.length > 0 ?
|
||||
<Attachments
|
||||
attachments={details.attachments}
|
||||
showNotification={props.showNotification}
|
||||
/>
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div id="commentList">
|
||||
<CommentList {...props} issueInfo={details} reload = {commentReload} reloadComment = {()=>{setCommentReload(Math.random())}}/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</LongWidth>
|
||||
<div className="shortwidth mt25">
|
||||
{orderId && <div className="claimpart"><Claims issue_id={orderId} {...props} refreshFunc={refreshFunc}/></div>}
|
||||
<ChooseMenu
|
||||
placeholder="负责人"
|
||||
menus={chargeList}
|
||||
searchFunc={(value)=>{setCharge(value)}}
|
||||
headImg
|
||||
selectValueList={assigner_choose}
|
||||
editFlag={editFlag}
|
||||
chooseFunc={(list)=>{setAssigner_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(l);}}
|
||||
double={5}
|
||||
removeFlag
|
||||
/>
|
||||
<DropMenu
|
||||
placeholder="状态"
|
||||
menus={statusList}
|
||||
selectValueList={status_choose}
|
||||
mustFlag
|
||||
editFlag={editFlag}
|
||||
chooseFunc={(list)=>{setStatus_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(undefined,l);}}
|
||||
/>
|
||||
<DropMenu
|
||||
placeholder="优先级"
|
||||
menus={prioritiesList}
|
||||
selectValueList={prioritie_choose}
|
||||
colorFlag="1"
|
||||
mustFlag
|
||||
editFlag={editFlag}
|
||||
chooseFunc={(list)=>{setPrioritie_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(undefined,undefined,l);}}
|
||||
/>
|
||||
<ChooseMenu
|
||||
placeholder="标记"
|
||||
menus={tagList}
|
||||
searchFunc={(value)=>{setTag(value)}}
|
||||
selectValueList={tag_choose}
|
||||
chooseFunc={(list)=>{setTag_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(undefined,undefined,undefined,l);}}
|
||||
double={3}
|
||||
colorFlag="2"
|
||||
removeFlag
|
||||
editFlag={editFlag}
|
||||
onAdd={permission && permission !== "Reporter" ? ()=>{getSign();} :false}
|
||||
owner={owner}
|
||||
projectsId={projectsId}
|
||||
/>
|
||||
<ChooseMenu
|
||||
placeholder="里程碑"
|
||||
menus={millstoneList}
|
||||
searchFunc={(value)=>{setMillstone(value)}}
|
||||
selectValueList={millstone_choose}
|
||||
editFlag={editFlag}
|
||||
auto
|
||||
chooseFunc={(list)=>{setMillstone_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(undefined,undefined,undefined,undefined,l);}}
|
||||
/>
|
||||
<ChooseMenu
|
||||
placeholder="关联分支"
|
||||
menus={branchList}
|
||||
auto
|
||||
searchFunc={(value)=>{setBranch(value)}}
|
||||
selectValueList={branch_choose}
|
||||
editFlag={editFlag}
|
||||
chooseFunc={(list)=>{setBranch_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(undefined,undefined,undefined,undefined,undefined,l);}}
|
||||
/>
|
||||
{
|
||||
open_blockchain &&
|
||||
<li style={{paddingBottom:"0px"}}>
|
||||
<span>
|
||||
悬赏金额
|
||||
{/* {!editFlag && <a onClick={editReward}><i className="iconfont icon-a-bianji12 font-13" style={{color:"#898d9d"}}></i></a> } */}
|
||||
</span>
|
||||
<InputNumber
|
||||
onBlur={amountBlur}
|
||||
placeholder="请输入悬赏金额"
|
||||
style={{width:"100%",color:rewardAmount?"#333":"#acb0bf"}}
|
||||
value={rewardAmount}
|
||||
onChange={changeAmount}
|
||||
className="borderNo mt5"
|
||||
disabled={!amountEditFlag}
|
||||
/>
|
||||
</li>
|
||||
}
|
||||
<Date name="开始日期" today={start_date} setDate={(date)=>{setStartDate(date);saveForeach(undefined,undefined,undefined,undefined,undefined,undefined,date)}} editFlag={editFlag}/>
|
||||
<Date name="结束日期" today={due_date} setDate={(date)=>{setDueDate(date);saveForeach(undefined,undefined,undefined,undefined,undefined,undefined,undefined,date)}} editFlag={editFlag}/>
|
||||
</div>
|
||||
</Box>
|
||||
:
|
||||
<div style={{width:"100%",display:"flex",alignItems:"center",justifyContent:'center',height:"400px"}}><Spin /></div>
|
||||
)
|
||||
}
|
||||
export default Form.create()(forwardRef(Details));
|
||||
|
|
@ -0,0 +1,449 @@
|
|||
import React , { useRef , useState , useEffect } from 'react';
|
||||
import { Dropdown , Menu , Icon , Input , Checkbox , Pagination, Button , Tooltip ,Spin , DatePicker } from 'antd';
|
||||
import bj from '../Img/biaoji.png';
|
||||
import issueEmp from '../Img/issue-big.png';
|
||||
import { Link } from "react-router-dom";
|
||||
import AllMenus from '../Component/allMenus';
|
||||
import CheckProfile from '../../Component/ProfileModal/Profile';
|
||||
import Datas from '../Component/datas';
|
||||
import DelBox from '../Component/delBox';
|
||||
import axios from 'axios';
|
||||
import cookie from 'react-cookies';
|
||||
import emp from '../Img/emp.png';
|
||||
import moment from 'moment';
|
||||
|
||||
const { Search } = Input;
|
||||
const defaultLimit = 15;
|
||||
|
||||
function List(props){
|
||||
const [ visible , setVisible ] = useState(false);
|
||||
const [ issueList , setIssueList ] = useState(undefined);
|
||||
const [ closedCount , setClosedCount ] = useState(0);
|
||||
const [ openedCount , setOpenedCount ] = useState(0);
|
||||
|
||||
const[ aboutMe , setAboutMe ] = useState("all");
|
||||
const[ value , setValue ] = useState("");
|
||||
const[ keyword , setKeyword ] = useState(undefined);
|
||||
|
||||
const [ category , setCategory] = useState("opened");
|
||||
const [ begin_date , setBegin] = useState(undefined);
|
||||
const [ end_date , setEnd] = useState(undefined);
|
||||
|
||||
const [ allValue , setAllValue ] = useState([]);
|
||||
const [ allIds , setAllIds ] = useState([]);
|
||||
const [ checkAll , setCheckAll ] = useState(false);
|
||||
|
||||
const [ names , setNames ] = useState(undefined);
|
||||
const [ updateIds , setUpdateIds ] = useState(undefined);
|
||||
const [ updateChooseIds , setUpdateChooseIds ] = useState(undefined);
|
||||
|
||||
const [ page , setPage ] = useState(1);
|
||||
const [ limit , setLimit ] = useState(15);
|
||||
const [ total , setTotal ] = useState(undefined);
|
||||
const [ issueTotal , setIssueTotal ] = useState(0);
|
||||
|
||||
const [ clearFlag , setClearFlag ] = useState(false);
|
||||
const [ has_created_issues , setHas_created_issues] = useState(false);
|
||||
|
||||
const menuRef = useRef(null);
|
||||
const owner = props.match.params.owner;
|
||||
const projectsId = props.match.params.projectsId;
|
||||
const permission = props && props.projectDetail && props.projectDetail.permission;
|
||||
const { projectDetail , current_user , open_blockchain} = props;
|
||||
|
||||
useEffect(()=>{
|
||||
if(projectDetail){
|
||||
const { author, name } = projectDetail;
|
||||
document.title = `疑修-${author.name}/${name}`;
|
||||
}
|
||||
},[projectDetail])
|
||||
|
||||
useEffect(()=>{
|
||||
const datas = cookie.load('issuestates');
|
||||
let states = datas === "undefined" ? undefined : datas;
|
||||
if(states){
|
||||
setAboutMe(states.participant_category);
|
||||
setCategory(states.category);
|
||||
setPage(states.page);
|
||||
setLimit(states.limit || defaultLimit);
|
||||
setUpdateIds({...states});
|
||||
setNames(states.names);
|
||||
setBegin(states.begin_date);
|
||||
setEnd(states.end_date);
|
||||
if(states.participant_category ==="all" && states.category === "opened" && states.page === 1 && states.limit===defaultLimit)
|
||||
Init({...states},states.page,states.names);
|
||||
}else{
|
||||
Init();
|
||||
}
|
||||
},[])
|
||||
|
||||
useEffect(()=>{
|
||||
let v = allValue && allValue > 0;
|
||||
if((updateIds && !v && (updateIds.author_id || updateIds.issue_priorities_id || updateIds.issue_tag_ids || updateIds.milestone_id || updateIds.sort_by || updateIds.status_id ||updateIds.assigner_id)) || value || end_date || begin_date){
|
||||
setClearFlag(true);
|
||||
}else{
|
||||
setClearFlag(false);
|
||||
}
|
||||
},[updateIds,value,allValue,begin_date,end_date])
|
||||
// 自定义一个初始不更新的hook
|
||||
const useUpdateEffect = (fn, inputs) => {
|
||||
const didMountRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (didMountRef.current) fn();
|
||||
else didMountRef.current = true;
|
||||
}, inputs);
|
||||
};
|
||||
|
||||
// 定义处理函数
|
||||
function handleAllDisabled(){
|
||||
Init(updateIds,page,names);
|
||||
}
|
||||
|
||||
// 使用自定义hook
|
||||
useUpdateEffect(handleAllDisabled, [aboutMe,keyword,category,limit,begin_date,end_date]);
|
||||
|
||||
|
||||
function getDirection(id){
|
||||
if(!id) return undefined;
|
||||
if(id === "1" || id === "3" || id === "5"|| id === "7"){
|
||||
return "desc"
|
||||
}else{
|
||||
return "asc"
|
||||
}
|
||||
}
|
||||
function getBy(id){
|
||||
if(!id) return undefined;
|
||||
if(id === "1" || id === "2"){
|
||||
return "issues.created_on"
|
||||
}else if(id === "3" || id === "4"){
|
||||
return "issues.updated_on"
|
||||
}else if(id === "5" || id === "6"){
|
||||
return "issue_priorities.position"
|
||||
}else{
|
||||
return "issues.blockchain_token_num"
|
||||
}
|
||||
}
|
||||
|
||||
// 获取issue列表数据
|
||||
function Init(params,p,names){
|
||||
const datas = cookie.load('issuestates');
|
||||
let states = datas === "undefined" ? undefined : datas;
|
||||
if(states){
|
||||
cookie.remove('issuestates');
|
||||
}
|
||||
const url = `/v1/${owner}/${projectsId}/issues`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
...params,
|
||||
page:p || 1,
|
||||
keyword,
|
||||
participant_category:aboutMe,
|
||||
category,
|
||||
limit,
|
||||
begin_date,end_date,
|
||||
sort_direction:getDirection(params && params.sort_by),
|
||||
sort_by:getBy(params && params.sort_by),
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
setIssueList(result.data.issues);
|
||||
setTotal(result.data.total_count);
|
||||
setClosedCount(result.data.closed_count);
|
||||
setOpenedCount(result.data.opened_count);
|
||||
const ids = result.data.issues.length>0 ? result.data.issues.map(i=>{return i.id}) : [];
|
||||
setAllIds(ids);
|
||||
setIssueTotal(result.data.total_issues_count);
|
||||
setHas_created_issues(result.data.has_created_issues);
|
||||
|
||||
const d = {...params,keyword,participant_category:aboutMe,category,limit,page:p || 1,
|
||||
sort_direction:getDirection(params && params.sort_by),begin_date,end_date,
|
||||
sort_by:getBy(params && params.sort_by),names:names};
|
||||
|
||||
let inFifteenMinutes = new Date(new Date().getTime() + 24 * 3600 * 1000);
|
||||
cookie.save('issuestates', {...d},{ expires: inFifteenMinutes,path:`/` });
|
||||
}
|
||||
}).then(error=>{})
|
||||
}
|
||||
|
||||
// 第一个下拉搜索
|
||||
const menu = (
|
||||
<Menu selectedKeys={[`${aboutMe}`]} onClick={chooseAboutMe}>
|
||||
<Menu.Item key={"all"}>全部</Menu.Item>
|
||||
<Menu.Item key={"aboutme"}><Tooltip title="指我创建的、我负责的和@我的疑修">与我相关</Tooltip></Menu.Item>
|
||||
<Menu.Item key={"assignedme"}>我负责的</Menu.Item>
|
||||
<Menu.Item key={"authoredme"}>我创建的</Menu.Item>
|
||||
<Menu.Item key={"atme"}>@我的</Menu.Item>
|
||||
</Menu>
|
||||
)
|
||||
function chooseAboutMe(e){
|
||||
setPage(1);
|
||||
setCategory("opened");
|
||||
setAboutMe(e.key);
|
||||
}
|
||||
|
||||
//清除筛选条件
|
||||
function clearCondition(){
|
||||
setKeyword(undefined);
|
||||
setAboutMe("all");
|
||||
setCategory("opened");
|
||||
setTotal(undefined);
|
||||
setUpdateIds(undefined);
|
||||
setAllValue([]);
|
||||
setValue(undefined);
|
||||
setNames(undefined);
|
||||
setEnd(undefined);
|
||||
setBegin(undefined);
|
||||
(!value || begin_date || end_date ) && Init();
|
||||
// 清除下拉选项
|
||||
menuRef.current && menuRef.current.clearChoose();
|
||||
}
|
||||
|
||||
// 全选所有issue
|
||||
function chooseAll(e){
|
||||
setCheckAll(e.target.checked);
|
||||
// 清除下拉选项
|
||||
menuRef.current && menuRef.current.clearChoose();
|
||||
if(e.target.checked){
|
||||
setAllValue(allIds);
|
||||
}else{
|
||||
setAllValue([]);
|
||||
}
|
||||
}
|
||||
|
||||
// 选择列表里的issue
|
||||
function checkIssues(value){
|
||||
// 清除下拉选项
|
||||
menuRef.current && menuRef.current.clearChoose();
|
||||
setAllValue(value);
|
||||
if(value.length === allIds.length){
|
||||
setCheckAll(true);
|
||||
}else{
|
||||
setCheckAll(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 取消
|
||||
function cancelUpdate(){
|
||||
setAllValue([]);
|
||||
setCheckAll(false);
|
||||
// 清除下拉选项
|
||||
menuRef.current && menuRef.current.clearChoose();
|
||||
}
|
||||
// 确认修改 allValue updateIds
|
||||
function sureUpdate(){
|
||||
const url = `/v1/${owner}/${projectsId}/issues/batch_update`;
|
||||
axios.patch(url,{
|
||||
assigner_ids: updateChooseIds && updateChooseIds.assigner_id && updateChooseIds.assigner_id.split(","),
|
||||
ids: allValue,
|
||||
issue_tag_ids: updateChooseIds && updateChooseIds.issue_tag_ids && updateChooseIds.issue_tag_ids.split(","),
|
||||
milestone_id: updateChooseIds && updateChooseIds.milestone_id,
|
||||
priority_id: updateChooseIds && updateChooseIds.issue_priorities_id,
|
||||
status_id: updateChooseIds && updateChooseIds.status_id
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
setUpdateIds(undefined);
|
||||
setAllValue([]);
|
||||
setTotal(undefined);
|
||||
Init();
|
||||
cancelUpdate();
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
// 切换页码
|
||||
function changepage(p){
|
||||
setPage(p);
|
||||
Init(updateIds,p,names);
|
||||
if (document) { // 可以排除不需要置顶的页面
|
||||
if (document.documentElement || document.body) {
|
||||
document.documentElement.scrollTop = document.body.scrollTop = 0; // 切换路由时手动置顶
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除issue相关 func
|
||||
function onSuccess(){
|
||||
const url = `/v1/${owner}/${projectsId}/issues/batch_destroy`;
|
||||
axios.delete(url,{
|
||||
params:{ids:allValue}
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
setUpdateIds(undefined);
|
||||
setAllValue([]);
|
||||
setVisible(false);
|
||||
setCheckAll(false);
|
||||
props.showNotification("疑修删除成功!");
|
||||
Init();
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
function chooseFunc(ids,n){
|
||||
setNames(n);
|
||||
setPage(1);
|
||||
if(allValue && allValue.length>0){
|
||||
// 将ids保存下来以便修改
|
||||
// setUpdateIds(ids);
|
||||
setUpdateChooseIds(ids);
|
||||
}else{
|
||||
setUpdateIds(ids);
|
||||
setTotal(undefined);
|
||||
Init(ids,1,n);
|
||||
}
|
||||
}
|
||||
|
||||
function chageLimit(c,p){
|
||||
setPage(1);
|
||||
setLimit(p);
|
||||
}
|
||||
function changeCategory(value){
|
||||
if(value !== category){
|
||||
setCategory(value);
|
||||
setPage(1);
|
||||
setTotal(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
function changeSearchValueFunc(e){
|
||||
setValue(e.target.value);
|
||||
if(e.target.value===""){
|
||||
setKeyword(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
// 选择开始时间
|
||||
function changeBeginTime(data, value){
|
||||
setPage(1);
|
||||
setBegin(value);
|
||||
}
|
||||
// 选择结束时间
|
||||
function changeEndTime(data, value){
|
||||
setPage(1);
|
||||
setEnd(value);
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
<DelBox visible={visible} onCancel={()=>setVisible(false)} onSuccess={onSuccess}/>
|
||||
<div className="pageheader">
|
||||
<div>
|
||||
{
|
||||
(current_user && current_user.login) &&
|
||||
<Dropdown overlay={menu} trigger={['click']} placement="bottomLeft" arrow={{pointAtCenter: true}}>
|
||||
<span className="dorpdownButton mr20">
|
||||
<span>{aboutMe === "all" ? "全部":aboutMe === "aboutme"?"与我相关":aboutMe === "assignedme"?"我负责的":aboutMe === "authoredme"?"我创建的":"@我的"}</span>
|
||||
<Icon type="caret-down" className="ml5 color-grey-6" />
|
||||
</span>
|
||||
</Dropdown>
|
||||
}
|
||||
<Search
|
||||
placeholder="输入关键字搜索疑修"
|
||||
value={value}
|
||||
onChange={changeSearchValueFunc}
|
||||
onSearch={()=>setKeyword(value)}
|
||||
style={{ width: 354 , height : 32 }}
|
||||
allowClear
|
||||
/>
|
||||
{
|
||||
clearFlag &&
|
||||
<a className="color-blue ml25" onClick={clearCondition} style={{ display: "flex" , alignItems: "center"}}>
|
||||
<i className="iconfont icon-roundclose font-16 mr5"></i>清除筛选条件</a>
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
{/* <DatePicker
|
||||
value={begin_date ? moment(begin_date, 'YYYY-MM-DD') : ""}
|
||||
style={{ marginRight: "20px",width:125 }}
|
||||
placeholder="选择开始时间"
|
||||
onChange={changeBeginTime}
|
||||
/>
|
||||
<DatePicker style={{ marginRight: "20px",width:125 }} value={end_date ? moment(end_date, 'YYYY-MM-DD') : ""} placeholder="选择结束时间" onChange={changeEndTime} /> */}
|
||||
{
|
||||
permission && permission !== "Reporter" &&
|
||||
<Link to={`/${owner}/${projectsId}/issues/sign`} className="dorpdownButton"><img src={bj} alt="" className="mr5" />标记管理</Link>
|
||||
}
|
||||
<CheckProfile {...props} sureFunc={()=>{props.history.push(`/${owner}/${projectsId}/issues/new`)}} checklogin className="operateButton ml20">创建疑修</CheckProfile>
|
||||
</div>
|
||||
</div>
|
||||
<div className="lists">
|
||||
<div className="listheader">
|
||||
<div style={{display:"flex"}}>
|
||||
<Checkbox value="all" style={{marginRight: "16px",display: (permission && permission !== "Reporter") ?"block":"none"}} checked={checkAll} onChange={chooseAll}></Checkbox>
|
||||
{
|
||||
allValue && allValue.length>0 ?
|
||||
<span>选择{allValue.length}个issue</span>
|
||||
:
|
||||
<ul className="statusul">
|
||||
<li className={category === "all" ?"active":""} onClick={()=>{changeCategory("all")}}>全部<span>{issueTotal}</span></li>
|
||||
<li className={category === "opened" ?"active":""} onClick={()=>{changeCategory("opened")}}>开启中<span>{openedCount}</span></li>
|
||||
<li className={category === "closed" ?"active":""} onClick={()=>{changeCategory("closed")}}>已关闭<span>{closedCount}</span></li>
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
<div className="menusul">
|
||||
<AllMenus
|
||||
ref={menuRef}
|
||||
update={allValue && allValue.length>0}
|
||||
owner={owner}
|
||||
projectsId={projectsId}
|
||||
chooseFunc={chooseFunc}
|
||||
defaultNames={names}
|
||||
defaultIds={allValue && allValue.length>0 ? undefined : updateIds}
|
||||
open_blockchain={open_blockchain}
|
||||
/>
|
||||
{
|
||||
allValue && allValue.length>0 ?
|
||||
<div>
|
||||
<Button type="primary" ghost onClick={sureUpdate}>确定</Button>
|
||||
<Button type="danger" ghost className="ml10" onClick={()=>setVisible(true)}>删除</Button>
|
||||
<Button ghost className="ml10 mr10" onClick={cancelUpdate}>取消</Button>
|
||||
</div>
|
||||
:""
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
total === 0 &&
|
||||
(!has_created_issues ?
|
||||
<div className="listempty">
|
||||
<img src={issueEmp} alt="" width="68px" />
|
||||
<p className="font-22 mt5 mb10">欢迎使用疑修(Issue)</p>
|
||||
<p className="font-15">疑修用于记录与跟踪待办事项、项目bug、功能需求等。在使用之前,请您先<CheckProfile {...props} checklogin sureFunc={()=>{props.history.push(`/${owner}/${projectsId}/issues/new`)}} className="color-blue">创建一个疑修</CheckProfile></p>
|
||||
</div>
|
||||
:
|
||||
<div className="dataempty"><img src={emp} alt="" /></div>
|
||||
)
|
||||
}
|
||||
{
|
||||
total > 0 &&
|
||||
<React.Fragment>
|
||||
<Checkbox.Group name="issues" onChange={checkIssues} value={allValue} style={{ width: "100%"}}>
|
||||
<div className="listdatas">
|
||||
{issueList.map((item,key)=>{
|
||||
return(
|
||||
<Datas
|
||||
key={key}
|
||||
checkbox={(permission && permission !== "Reporter") && <Checkbox value={item.id} key={item.id} style={{marginRight: "16px"}}></Checkbox> }
|
||||
item={item}
|
||||
owner={owner}
|
||||
projectsId={projectsId}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</Checkbox.Group>
|
||||
{
|
||||
total > defaultLimit &&
|
||||
<div className="pt25 pb30" style={{textAlign:"right"}}>
|
||||
<Pagination total={total} onShowSizeChange={chageLimit} current={page} pageSize={limit} onChange={changepage} showSizeChanger pageSizeOptions={[15,30,40,50]} showQuickJumper />
|
||||
</div>
|
||||
}
|
||||
</React.Fragment>
|
||||
}
|
||||
{total === undefined && <div style={{height:344,display:"flex",alignItems:"center",justifyContent:"center"}}><Spin /></div> }
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default List;
|
||||
|
|
@ -0,0 +1,302 @@
|
|||
import React , { useEffect , useState , forwardRef , useRef } from 'react';
|
||||
import { Form , InputNumber } from 'antd';
|
||||
import { Box , LongWidth } from '../../Component/layout';
|
||||
import DropMenu from '../Component/dropMenu';
|
||||
import ChooseMenu from '../Component/chooseMenu';
|
||||
import AddTagsBox from '../Component/addTagsBox';
|
||||
import Date from '../Component/date';
|
||||
import NewPanel from '../Component/newPanel';
|
||||
import axios from 'axios';
|
||||
|
||||
function New(props){
|
||||
// history search 是否含有意见反馈标识
|
||||
const {location:{search}, projectDetail} = props;
|
||||
const feedBack = search && search.indexOf('type=feedback') !== -1;
|
||||
// 里程碑id
|
||||
const milepostId = props.match.params.milepostId;
|
||||
|
||||
|
||||
const [ statusList , setStatusList ] = useState([]);
|
||||
const [ prioritiesList , setPrioritiesList ] = useState([]);
|
||||
const [ chargeList , setChargeList ] = useState([]);
|
||||
const [ charge , setCharge ] = useState(undefined);
|
||||
const [ millstoneList , setMillstoneList ] = useState([]);
|
||||
const [ millstone , setMillstone ] = useState(undefined);
|
||||
const [ tagList , setTagList ] = useState(undefined);
|
||||
const [ tag , setTag ] = useState(undefined);
|
||||
const [ branchList , setBranchList ] = useState(undefined);
|
||||
const [ branch , setBranch ] = useState(undefined);
|
||||
|
||||
|
||||
const [ assigner_choose , setAssigner_choose ] = useState([]);
|
||||
const [ prioritie_choose , setPrioritie_choose ] = useState([]);
|
||||
const [ status_choose , setStatus_choose ] = useState([]);
|
||||
const [ tag_choose , setTag_choose ] = useState([]);
|
||||
const [ millstone_choose , setMillstone_choose ] = useState([]);
|
||||
const [ branch_choose , setBranch_choose ] = useState([]);
|
||||
|
||||
const [ start_date, setStartDate ] = useState("");//moment().format('YYYY-MM-DD')
|
||||
const [ due_date, setDueDate ] = useState("");
|
||||
|
||||
const [ rewardAmount , setRewardAmount ] = useState(undefined);
|
||||
const [ rewardFlag , setRewardFlag ] = useState(false);
|
||||
|
||||
const owner = props.match.params.owner;
|
||||
const projectsId = props.match.params.projectsId;
|
||||
const permission = props && props.projectDetail && props.projectDetail.permission;
|
||||
const { open_blockchain } = props;
|
||||
const ref = useRef(null);
|
||||
|
||||
useEffect(()=>{
|
||||
if(projectDetail){
|
||||
if(feedBack){
|
||||
document.title = `意见反馈`;
|
||||
return;
|
||||
}
|
||||
const { author, name} = projectDetail;
|
||||
document.title = `新建疑修-${author.name}/${name}`
|
||||
}
|
||||
},[projectDetail, feedBack])
|
||||
|
||||
useEffect(()=>{
|
||||
if(milepostId){
|
||||
getMillstone(milepostId);
|
||||
}
|
||||
},[milepostId])
|
||||
|
||||
|
||||
// 获取状态列表
|
||||
useEffect(()=>{
|
||||
getStatus();
|
||||
},[])
|
||||
function getStatus(){
|
||||
const url = `/v1/${owner}/${projectsId}/issue_statues`;
|
||||
axios.get(url).then(result=>{
|
||||
if(result && result.data){
|
||||
let status = result.data.statues;
|
||||
setStatusList(status);
|
||||
if(status && status.length>0){
|
||||
setStatus_choose(status.filter(i=>i.id === 1));
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// 获取优先级列表
|
||||
useEffect(()=>{
|
||||
getPriorities();
|
||||
},[])
|
||||
|
||||
function getPriorities(){
|
||||
const url = `/v1/${owner}/${projectsId}/issue_priorities`;
|
||||
axios.get(url).then(result=>{
|
||||
if(result && result.data){
|
||||
let priorities = result.data.priorities;
|
||||
let array =[];
|
||||
let colors = ["#1abcb1","#28be6c","#e67e22","#db3d1d"];
|
||||
if(priorities && priorities.length>0){
|
||||
array = priorities.map((i,k)=>{
|
||||
let arr = {...i,color:colors[k]}
|
||||
return arr;
|
||||
})
|
||||
}
|
||||
setPrioritiesList(array);
|
||||
setPrioritie_choose(array.filter(i=>i.id === 2));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取负责人列表
|
||||
useEffect(()=>{
|
||||
getCharge();
|
||||
},[charge])
|
||||
|
||||
function getCharge(){
|
||||
const url = `/v1/${owner}/${projectsId}/collaborators`;
|
||||
axios.get(url,{params:{keyword:charge,only_name:true}}).then(result=>{
|
||||
if(result && result.data){
|
||||
setChargeList(result.data.collaborators);
|
||||
if(feedBack && !charge){
|
||||
// 指定指派成员-意见反馈
|
||||
const {collaborators} = result.data;
|
||||
const id = collaborators.filter(item=>item.id === 86107);
|
||||
setAssigner_choose(id.length === 0 ? [collaborators[0]] : id)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// 获取里程碑列表
|
||||
useEffect(()=>{
|
||||
getMillstone(millstone);
|
||||
},[millstone])
|
||||
|
||||
function getMillstone(keyword){
|
||||
const url = `/v1/${owner}/${projectsId}/milestones`;
|
||||
axios.get(url,{params:{keyword,only_name:true}}).then(result=>{
|
||||
if(result && result.data){
|
||||
let milestones = result.data.milestones;
|
||||
if(milepostId && milepostId === keyword){
|
||||
setMillstone_choose(milestones);
|
||||
}else{
|
||||
setMillstoneList(milestones);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// 获取标记列表
|
||||
useEffect(()=>{
|
||||
getSign();
|
||||
},[tag])
|
||||
function getSign(){
|
||||
const url = `/v1/${owner}/${projectsId}/issue_tags`;
|
||||
axios.get(url,{params:{keyword:tag,only_name:true}}).then(result=>{
|
||||
if(result && result.data){
|
||||
setTagList(result.data.issue_tags);
|
||||
}
|
||||
})
|
||||
}
|
||||
// 获取标记列表
|
||||
useEffect(()=>{
|
||||
getBranch(branch);
|
||||
},[branch])
|
||||
function getBranch(branch){
|
||||
if(branch){
|
||||
let b = [...branchList];
|
||||
let l = b.filter(i=>i.name.indexOf(branch)>-1);
|
||||
setBranchList(l);
|
||||
return ;
|
||||
}
|
||||
const url = `/${owner}/${projectsId}/branches.json`;
|
||||
axios.get(url,{params:{keyword:tag}}).then(result=>{
|
||||
if(result && result.data){
|
||||
setBranchList(result.data);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 创建
|
||||
function createFunc(values,fileList,receivers_login,description){
|
||||
const { subject } = values;
|
||||
const url = `/v1/${owner}/${projectsId}/issues`;
|
||||
let b = branch_choose && branch_choose.length>0 ? branch_choose.map(i=>{return i.name}) :undefined;
|
||||
let t = tag_choose && tag_choose.length>0 ? tag_choose.map(i=>{return i.id}) :undefined;
|
||||
let a = assigner_choose && assigner_choose.length>0 ? assigner_choose.map(i=>{return i.id}) :undefined;
|
||||
let m = millstone_choose && millstone_choose.length>0 ? millstone_choose.map(i=>{return i.id}) :undefined;
|
||||
let p = prioritie_choose && prioritie_choose.length>0 ? prioritie_choose.map(i=>{return i.id}) :undefined;
|
||||
let s = status_choose && status_choose.length>0 ? status_choose.map(i=>{return i.id}) :undefined;
|
||||
axios.post(url,{
|
||||
description,subject,
|
||||
branch_name:b && b.join(","),
|
||||
status_id:s && s.join(","),
|
||||
priority_id:p && p.join(","),
|
||||
milestone_id:m && m.join(","),
|
||||
issue_tag_ids:t,
|
||||
assigner_ids:a,
|
||||
attachment_ids:fileList,
|
||||
start_date,due_date,receivers_login,
|
||||
blockchain_token_num:rewardAmount
|
||||
}).then(result=>{
|
||||
if(result && result.data && result.data.project_issues_index){
|
||||
props.showNotification("任务创建成功!");
|
||||
props.history.push(`/${owner}/${projectsId}/issues/${result.data.project_issues_index}`);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
|
||||
function changeAmount(value){
|
||||
if(value && value > 0){
|
||||
setRewardAmount(value);
|
||||
}
|
||||
if(!value){
|
||||
setRewardAmount(value);
|
||||
}
|
||||
}
|
||||
return(
|
||||
<div>
|
||||
<p className="font-17 color-grey-3 mt20 mb15">新建疑修</p>
|
||||
<Box>
|
||||
<LongWidth>
|
||||
{/* 意见反馈初始化内容 */}
|
||||
<NewPanel {...props} createFunc={createFunc} owner = {owner} projectsId = {projectsId} desc={feedBack ? "####问题描述\n\n\n####重现问题步骤\n\n\n####截图\n\n\n####建议解决办法\n" : undefined}/>
|
||||
</LongWidth>
|
||||
<div className="shortwidth">
|
||||
<ChooseMenu
|
||||
placeholder="负责人"
|
||||
menus={chargeList}
|
||||
searchFunc={(value)=>{setCharge(value)}}
|
||||
headImg
|
||||
selectValueList={assigner_choose}
|
||||
chooseFunc={(list)=>{setAssigner_choose(list);}}
|
||||
double={5}
|
||||
removeFlag
|
||||
/>
|
||||
<DropMenu
|
||||
placeholder="状态"
|
||||
menus={statusList}
|
||||
selectValueList={status_choose}
|
||||
mustFlag
|
||||
editFlag={false}
|
||||
chooseFunc={(list)=>{setStatus_choose(list);}}
|
||||
/>
|
||||
<DropMenu
|
||||
placeholder="优先级"
|
||||
menus={prioritiesList}
|
||||
selectValueList={prioritie_choose}
|
||||
colorFlag="1"
|
||||
mustFlag
|
||||
chooseFunc={(list)=>{setPrioritie_choose(list);}}
|
||||
/>
|
||||
<ChooseMenu
|
||||
placeholder="标记"
|
||||
menus={tagList}
|
||||
searchFunc={(value)=>{setTag(value)}}
|
||||
selectValueList={tag_choose}
|
||||
chooseFunc={(list)=>{setTag_choose(list);}}
|
||||
double={3}
|
||||
colorFlag="2"
|
||||
removeFlag
|
||||
onAdd={permission && permission !== "Reporter" ? ()=>{getSign();} :false}
|
||||
owner={owner}
|
||||
projectsId={projectsId}
|
||||
/>
|
||||
<ChooseMenu
|
||||
placeholder="里程碑"
|
||||
menus={millstoneList}
|
||||
auto
|
||||
searchFunc={(value)=>{setMillstone(value)}}
|
||||
selectValueList={millstone_choose}
|
||||
editFlag={milepostId ? true : false}
|
||||
chooseFunc={(list)=>{setMillstone_choose(list);}}
|
||||
/>
|
||||
<ChooseMenu
|
||||
placeholder="关联分支"
|
||||
menus={branchList}
|
||||
auto
|
||||
searchFunc={(value)=>{setBranch(value)}}
|
||||
selectValueList={branch_choose}
|
||||
chooseFunc={(list)=>{setBranch_choose(list);}}
|
||||
/>
|
||||
{
|
||||
open_blockchain &&
|
||||
<li style={{paddingBottom:"0px"}}>
|
||||
<span>
|
||||
悬赏金额
|
||||
{/* {!(milepostId ? true : false) && <a onClick={editReward}><i className="iconfont icon-a-bianji12 font-13" style={{color:"#898d9d"}}></i></a> } */}
|
||||
</span>
|
||||
<InputNumber
|
||||
placeholder="请输入悬赏金额"
|
||||
style={{width:"100%",color:rewardAmount?"#333":"#acb0bf"}}
|
||||
value={rewardAmount}
|
||||
onChange={changeAmount}
|
||||
className="borderNo mt5" disabled={milepostId ? true : false}/>
|
||||
</li>
|
||||
}
|
||||
<Date name="开始日期" today={start_date} setDate={(date)=>setStartDate(date)}/>
|
||||
<Date name="结束日期" today={due_date} setDate={(date)=>setDueDate(date)}/>
|
||||
</div>
|
||||
</Box>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Form.create()(forwardRef(New));
|
||||
|
|
@ -0,0 +1,267 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import create from '../Img/create.png';
|
||||
import { Menu , Dropdown , Icon, Input, Button , Spin , Pagination } from 'antd';
|
||||
import ColorCard from '../Component/colorCard';
|
||||
import DelBox from '../Component/delBox';
|
||||
import axios from 'axios';
|
||||
import {Link} from 'react-router-dom';
|
||||
const limit = 15;
|
||||
function Sign(props){
|
||||
const [ edit , setEdit ] = useState(false);
|
||||
const [ colors , setColors ] = useState(undefined);
|
||||
const [ delVisible , setDelVisible ] = useState(false);
|
||||
const [ editId , setEditId ] = useState(undefined);
|
||||
const [ list , setList ] = useState(undefined);
|
||||
const [ total , setTotal ] = useState(undefined);
|
||||
const [ page , setPage ] = useState(1);
|
||||
|
||||
const [ name , setName ] = useState(undefined);
|
||||
const [ desc , setDesc ] = useState(undefined);
|
||||
const [ order_name , setOrderName ] = useState(undefined);
|
||||
const [ order_type , setOrderType ] = useState(undefined);
|
||||
const [ nameFlag , setNameFlag ] = useState(undefined);
|
||||
const [ defaultColor , setDefaultColor ]= useState(undefined);
|
||||
|
||||
const [ arrayName , setArrayName ] = useState("标记");
|
||||
const [ key , setKey ] = useState("0");
|
||||
const owner = props.match.params.owner;
|
||||
const projectsId = props.match.params.projectsId;
|
||||
const { projectDetail } = props;
|
||||
const permission = projectDetail && props.projectDetail.permission;
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
if(permission !== undefined){
|
||||
if(!permission || (permission && permission === "Reporter")){
|
||||
window.location.href = "/403";
|
||||
}
|
||||
}
|
||||
},[permission])
|
||||
|
||||
useEffect(()=>{
|
||||
if(projectDetail){
|
||||
const { author, name } = projectDetail;
|
||||
document.title = `项目标记-${author.name}/${name}`;
|
||||
}
|
||||
},[projectDetail])
|
||||
|
||||
useEffect(()=>{
|
||||
Init();
|
||||
},[page,order_name,order_type])
|
||||
|
||||
function Init(){
|
||||
const url = `/v1/${owner}/${projectsId}/issue_tags`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
page,limit,sort_by:order_name,sort_direction:order_type
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
setList(result.data.issue_tags);
|
||||
setTotal(result.data.total_count);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function arrayList(e){
|
||||
const { eventKey , value , children , createName } = e.item.props;
|
||||
setPage(1);
|
||||
if(value === order_type && createName === order_name){
|
||||
setOrderName(undefined);
|
||||
setOrderType(undefined);
|
||||
setArrayName("标记");
|
||||
setKey("0");
|
||||
}else{
|
||||
setOrderName(createName);
|
||||
setOrderType(value);
|
||||
setArrayName(children);
|
||||
setKey(eventKey);
|
||||
}
|
||||
}
|
||||
|
||||
const menu = (
|
||||
<Menu onClick={arrayList} selectedKeys={[`${key}`]}>
|
||||
<Menu.Item key={"1"} value="desc" createName="created_at">
|
||||
按创建时间降序排序
|
||||
</Menu.Item>
|
||||
<Menu.Item key={"2"} value="asc" createName="created_at">
|
||||
按创建时间升序排序
|
||||
</Menu.Item>
|
||||
<Menu.Item key={"3"} value="desc" createName="issues_count">
|
||||
按疑修数量降序排序
|
||||
</Menu.Item>
|
||||
<Menu.Item key={"4"} value="asc" createName="issues_count">
|
||||
按疑修数量升序排序
|
||||
</Menu.Item>
|
||||
<Menu.Item key={"5"} value="desc" createName="pull_requests_count">
|
||||
按合并请求数量降序排序
|
||||
</Menu.Item>
|
||||
<Menu.Item key={"6"} value="asc" createName="pull_requests_count">
|
||||
按合并请求数量升序排序
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
function getColor(colors){
|
||||
setColors(colors);
|
||||
}
|
||||
|
||||
function deleteFunc(){
|
||||
if(editId){
|
||||
const url = `/v1/${owner}/${projectsId}/issue_tags/${editId}`;
|
||||
axios.delete(url).then(result=>{
|
||||
if(result){
|
||||
props.showNotification("项目标记删除成功!");
|
||||
if(list && list.length === 1 && page>1){
|
||||
setPage(page-1);
|
||||
}else{
|
||||
Init();
|
||||
}
|
||||
setDelVisible(false);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
}
|
||||
|
||||
function sureSave(){
|
||||
if(!name){
|
||||
setNameFlag(true);
|
||||
}else{
|
||||
setNameFlag(false);
|
||||
if(editId){
|
||||
// 编辑
|
||||
const url = `/v1/${owner}/${projectsId}/issue_tags/${editId}`;
|
||||
axios.patch(url,{
|
||||
color: colors || defaultColor,
|
||||
description: desc,
|
||||
name
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
props.showNotification("标记编辑成功!");
|
||||
Init();
|
||||
cancel();
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}else{
|
||||
// 保存
|
||||
const url = `/v1/${owner}/${projectsId}/issue_tags`;
|
||||
axios.post(url,{
|
||||
color: colors || defaultColor,
|
||||
description: desc,
|
||||
name
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
props.showNotification("标记新增成功!");
|
||||
Init();
|
||||
cancel();
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cancel(){
|
||||
setNameFlag(false);
|
||||
setName(undefined);
|
||||
setEdit(false);
|
||||
setDesc(undefined);
|
||||
}
|
||||
|
||||
function editFunc(i){
|
||||
setEditId(i.id);
|
||||
setDefaultColor(i.color);
|
||||
setName(i.name);
|
||||
setDesc(i.description);
|
||||
setEdit(true);
|
||||
}
|
||||
|
||||
function onChangeValue(e){
|
||||
setName(e.target.value);
|
||||
if(e.target.value){
|
||||
setNameFlag(false);
|
||||
}else{
|
||||
setNameFlag(true);
|
||||
}
|
||||
}
|
||||
|
||||
function createSign(){
|
||||
const c = Math.random().toString(16).substr(-6);
|
||||
c && setDefaultColor(`#${c}`);
|
||||
setEdit(true);
|
||||
}
|
||||
return(
|
||||
<div>
|
||||
<DelBox
|
||||
visible={delVisible}
|
||||
onCancel={()=>setDelVisible(false)}
|
||||
onSuccess={deleteFunc}
|
||||
title="删除标记"
|
||||
content={<div style={{paddingTop:"3px"}}><p className="font-15 mb20">您确定要删除当前标记?</p></div>}
|
||||
/>
|
||||
<div className="between mt30 mb25">
|
||||
<span><Link to={`/${owner}/${projectsId}/issues`}>疑修</Link> / 项目标记</span>
|
||||
<a className="operateButton" onClick={createSign}><img src={create} alt="" className="mr5"/>创建标记</a>
|
||||
</div>
|
||||
{
|
||||
edit &&
|
||||
<div className="editbar">
|
||||
<div>
|
||||
<Input style={{width:"163px"}} className={nameFlag ?"inputred":""} value={name} onChange={onChangeValue} placeholder="名称15字以内"/>
|
||||
{ nameFlag && <p className="red">请输入标记名称</p>}
|
||||
</div>
|
||||
<Input style={{width:"276px"}} value={desc} onChange={(e)=>setDesc(e.target.value)} placeholder="描述30字以内"/>
|
||||
<ColorCard
|
||||
getColor={getColor}
|
||||
defaultColor={defaultColor}
|
||||
/>
|
||||
<span>
|
||||
<Button type="primary" ghost onClick={sureSave}>确定</Button>
|
||||
<Button ghost className="ml15" onClick={cancel}>取消</Button>
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
<div className="between bluebar">
|
||||
<span className="font-17">项目标记({total || 0})</span>
|
||||
<Dropdown overlay={menu} trigger={["click"]} placement="bottomRight">
|
||||
<span className="cursor">{arrayName}<Icon type="caret-down" className="ml5 color-grey-6" /></span>
|
||||
</Dropdown>
|
||||
</div>
|
||||
{
|
||||
list && list.length > 0 ?
|
||||
<ul className="signlist">
|
||||
{
|
||||
list.map((i,k)=>{
|
||||
return(
|
||||
<li>
|
||||
<p>
|
||||
<span className="square mr10" style={{backgroundColor:`${i.color}`}}></span>
|
||||
<i className="iconfont icon-biaoji mr3 font-12 " style={{color:`${i.color}`}}></i>
|
||||
<span className="task-hide" title={i.name} style={{maxWidth:210}}>{i.name}</span>
|
||||
</p>
|
||||
<p style={{flex:2}} className="task-hide">{i.description}</p>
|
||||
<p>
|
||||
<span className="mr12">{i.issues_count || 0} 疑修</span>
|
||||
<span className="line">{i.pull_requests_count || 0} 合并请求</span>
|
||||
</p>
|
||||
<p>
|
||||
<a className="color-blue mr12" onClick={()=>editFunc(i)}>编辑</a>
|
||||
<a className="color-red line" onClick={()=>{setEditId(i.id);setDelVisible(true);}}>删除</a>
|
||||
</p>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
:""
|
||||
}
|
||||
{
|
||||
total > limit &&
|
||||
<div style={{paddingBottom:"30px",textAlign:"center"}}>
|
||||
<Pagination total={total} current={page} onChange={(p)=>setPage(p)} pageSize={limit}/>
|
||||
</div>
|
||||
}
|
||||
{total === undefined && <div style={{height:344,display:"flex",alignItems:"center",justifyContent:"center"}}><Spin /></div> }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Sign;
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
import React,{useEffect} from 'react';
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
import Loadable from "react-loadable";
|
||||
import Loading from "../../Loading";
|
||||
import './index.scss';
|
||||
|
||||
const List = Loadable({
|
||||
loader: () => import("./Pages/list"),
|
||||
loading: Loading,
|
||||
});
|
||||
const Detail = Loadable({
|
||||
loader: () => import("./Pages/details"),
|
||||
loading: Loading,
|
||||
});
|
||||
const Sign = Loadable({
|
||||
loader: () => import("./Pages/sign"),
|
||||
loading: Loading,
|
||||
});
|
||||
const New = Loadable({
|
||||
loader: () => import("./Pages/new"),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
function Index(props){
|
||||
const pathname = props.history.location.pathname;
|
||||
const { project } = props;
|
||||
const open_blockchain = project && project.open_blockchain;
|
||||
|
||||
useEffect(() => {
|
||||
if (document) { // 可以排除不需要置顶的页面
|
||||
if (document.documentElement || document.body) {
|
||||
document.documentElement.scrollTop = document.body.scrollTop = 0; // 切换路由时手动置顶
|
||||
}
|
||||
}
|
||||
}, [pathname]);
|
||||
return(
|
||||
<div className="pagebox">
|
||||
<Switch>
|
||||
<Route
|
||||
path="/:owner/:projectsId/issues/:index/copy"
|
||||
render={(p) => (
|
||||
<Detail {...props} {...p} open_blockchain={open_blockchain}/>
|
||||
)}
|
||||
></Route>
|
||||
{/* 里程碑创建issue */}
|
||||
<Route
|
||||
path="/:owner/:projectsId/issues/:milepostId/new"
|
||||
render={(p) => (
|
||||
<New {...props} {...p} open_blockchain={open_blockchain}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/:owner/:projectsId/issues/new"
|
||||
render={(p) => (
|
||||
<New {...props} {...p} open_blockchain={open_blockchain}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/:owner/:projectsId/issues/sign"
|
||||
render={(p) => (
|
||||
<Sign {...props} {...p} open_blockchain={open_blockchain}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/:owner/:projectsId/issues/:index"
|
||||
render={(p) => (
|
||||
<Detail {...props} {...p} open_blockchain={open_blockchain}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/:owner/:projectsId/issues"
|
||||
render={(p) => (
|
||||
<List {...props} {...p} open_blockchain={open_blockchain}/>
|
||||
)}
|
||||
></Route>
|
||||
</Switch>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Index;
|
||||
|
|
@ -0,0 +1,860 @@
|
|||
.borderNo.ant-input,.borderNo .ant-input-number-input,.borderNo,.borderNo.ant-input-number-focused{
|
||||
border:none!important;
|
||||
padding:4px 0px!important;
|
||||
background-color: transparent!important;
|
||||
box-shadow: none!important;
|
||||
.ant-input-number-handler-wrap{
|
||||
display: none;
|
||||
}
|
||||
&:focus{
|
||||
border:none!important;
|
||||
box-shadow: none!important;
|
||||
}
|
||||
}
|
||||
|
||||
.pagebox{
|
||||
width: 1200px;
|
||||
margin:0px auto;
|
||||
}
|
||||
// 删除issue弹框
|
||||
.deldesc{
|
||||
justify-content: center;
|
||||
.red{
|
||||
color: #DF0002;
|
||||
}
|
||||
}
|
||||
// 可公用样式(在issue的页面中)
|
||||
// 可点击(有向下箭头且点击出现下拉框,移入灰色背景加深)的灰色按钮样式
|
||||
.dorpdownButton{
|
||||
padding:0px 15px;
|
||||
height:32px;
|
||||
color: #333333!important;
|
||||
cursor: pointer;
|
||||
line-height: 32px;
|
||||
background-color:#fafbfc;
|
||||
border:1px solid #d0d0d0;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 107px;
|
||||
box-sizing: border-box;
|
||||
&>span{
|
||||
display: block;
|
||||
width: 56px;
|
||||
text-align: center;
|
||||
}
|
||||
&:hover{
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
}
|
||||
// 操作按钮-蓝色
|
||||
.operateButton{
|
||||
padding:0px 15px;
|
||||
height:32px;
|
||||
color: #fff!important;
|
||||
cursor: pointer;
|
||||
line-height: 30px;
|
||||
background-color:rgba(70, 106, 255, 1);
|
||||
border:1px solid rgba(70, 106, 255, 1);
|
||||
border-radius: 4px;
|
||||
display: block;
|
||||
&:hover{
|
||||
background-color:#708cff;
|
||||
border-color:#708cff;
|
||||
}
|
||||
}
|
||||
// flex-左右,上下居中
|
||||
.between{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
// 蓝色背景操作栏
|
||||
.bluebar{
|
||||
height:50px;
|
||||
background-color:#fafcff;
|
||||
border:1px solid rgba(42, 97, 255, 0.23);
|
||||
border-radius:4px 4px 0px 0px;
|
||||
padding:0px 26px;
|
||||
}
|
||||
// 按钮样式
|
||||
.ant-btn-background-ghost{
|
||||
border-color:#acb0bf;
|
||||
color: #acb0bf;
|
||||
background-color: #fff!important;
|
||||
&.ant-btn-primary{
|
||||
color: #466aff;
|
||||
border-color: #466aff;
|
||||
background-color: #fff!important;
|
||||
}
|
||||
&.ant-btn-danger{
|
||||
color: #f60011;
|
||||
border-color: #f60011;
|
||||
background-color:rgba(196, 0, 14, 0.09)!important;
|
||||
}
|
||||
}
|
||||
.lists{
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
.dataempty{
|
||||
height: 420px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid;
|
||||
border-color: #d9d9d9;
|
||||
border-radius: 0px 0px 4px 4px;
|
||||
border-top: none;
|
||||
}
|
||||
// 列表为空样式
|
||||
.listempty{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color:#fafcff;
|
||||
border:1px solid rgba(42, 97, 255, 0.23);
|
||||
border-top: none;
|
||||
border-radius:0px 0px 4px 4px;
|
||||
height: 344px;
|
||||
padding-top: 62px;
|
||||
color: #333;
|
||||
}
|
||||
// 列表页面样式
|
||||
.pageheader{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding:30px 0px;
|
||||
align-items: center;
|
||||
&>div{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
.listheader{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height:50px;
|
||||
background-color:#fafcff;
|
||||
border:1px solid;
|
||||
border-color:rgba(42, 97, 255, 0.23);
|
||||
border-radius:4px 4px 0px 0px;
|
||||
padding-left: 18px;
|
||||
.menusul{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
ul{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&.statusul{
|
||||
li{
|
||||
width: 106px;
|
||||
font-size: 15px;
|
||||
&>span{
|
||||
height:19px;
|
||||
line-height: 17px;
|
||||
color: #666;
|
||||
background-color:rgba(70, 106, 255, 0.09);
|
||||
border-radius:10px;
|
||||
margin-left: 5px;
|
||||
padding:0px 6px;
|
||||
font-size: 13px;
|
||||
display: block;
|
||||
border:1px solid ;
|
||||
border-color: rgba(70, 106, 255, 0.01);
|
||||
min-width: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
li{
|
||||
color: #898d9d;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
&.active{
|
||||
color: #466aff;
|
||||
// font-weight: 500;
|
||||
&>span{
|
||||
font-weight: normal;
|
||||
color: #466aff;
|
||||
border-color: #466aff;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.dropboxul{
|
||||
padding-right: 20px;
|
||||
li{
|
||||
min-width: 106px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
&>span{
|
||||
width: 80px;
|
||||
display: block;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// issue列表
|
||||
.listdatas{
|
||||
border:1px solid #d0d0d0;
|
||||
border-top: none;
|
||||
border-radius: 0px 0px 2px 2px;
|
||||
&>div:not(.none_panels){
|
||||
padding:20px 18px;
|
||||
border-bottom: 1px solid #eee;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
&:hover{
|
||||
background-color:rgba(231, 233, 242, 0.26);
|
||||
}
|
||||
&:last-child{
|
||||
border-bottom: none;
|
||||
}
|
||||
.issuedetail{
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex:1;
|
||||
}
|
||||
}
|
||||
.idetails{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&>a{
|
||||
color: #40424a;
|
||||
margin-right: 10px;
|
||||
font-size: 15px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 375px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tagscolor{
|
||||
padding:0px 6px;
|
||||
border-radius: 2px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
max-width: 108px;
|
||||
font-size: 13px;
|
||||
cursor: default;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.infos{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #898d9d;
|
||||
margin-top: 12px;
|
||||
font-size: 13px;
|
||||
// img{
|
||||
// height: 22px;
|
||||
// width: 22px;
|
||||
// margin-right: 4px;
|
||||
// border-radius: 50%;
|
||||
// }
|
||||
}
|
||||
.issuecondition{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #40424a;
|
||||
.principal{
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 150px;
|
||||
text-align: justify;
|
||||
// &.hovers:hover{
|
||||
// a{
|
||||
// position:initial;
|
||||
// }
|
||||
// }
|
||||
// a{
|
||||
// position: absolute;
|
||||
// right: 0px;
|
||||
// transition: 0.6s;
|
||||
// }
|
||||
// img,span{
|
||||
// width: 22px;
|
||||
// height: 22px;
|
||||
// border-radius: 50%;
|
||||
// }
|
||||
// span{
|
||||
// background-color:#ced5ef;
|
||||
// text-align: center;
|
||||
// line-height: 12px;
|
||||
// color:#000000;
|
||||
// z-index: 6;
|
||||
// }
|
||||
}
|
||||
&>div{
|
||||
width: 90px;
|
||||
text-align: right;
|
||||
}
|
||||
.commentnum{
|
||||
padding-right: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 状态样式-公用
|
||||
|
||||
.status{
|
||||
display: block;
|
||||
height:22px;
|
||||
border-radius:6px;
|
||||
text-align: center;
|
||||
margin-right: 10px;
|
||||
line-height: 20px;
|
||||
width: 45px;
|
||||
font-size: 14px;
|
||||
border:1px solid #fff;
|
||||
&.normals{
|
||||
border-color:#28be6c;
|
||||
color:#28be6c;
|
||||
}
|
||||
&.hight{
|
||||
border-color: #e67e22;
|
||||
color: #e67e22;
|
||||
}
|
||||
&.urgent{
|
||||
border-color: #db3d1d;
|
||||
color: #db3d1d;
|
||||
}
|
||||
&.low{
|
||||
border-color: #1abcb1;
|
||||
color: #1abcb1;
|
||||
}
|
||||
}
|
||||
.ilog{
|
||||
min-width: 45px;
|
||||
margin-right: 10px;
|
||||
clear: both;
|
||||
.number{
|
||||
background-color:rgba(213, 220, 246, 0.36);
|
||||
border-radius:4px;
|
||||
height: 19px;
|
||||
line-height: 19px;
|
||||
color: #666666;
|
||||
text-align: center;
|
||||
float: left;
|
||||
padding:0px 4px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
// colorCard
|
||||
.color{
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.swatch{
|
||||
padding: 5px;
|
||||
background: #fff;
|
||||
border-radius: 1px;
|
||||
width: 92px;
|
||||
height: 28px;
|
||||
line-height: 20px;
|
||||
box-shadow: 0 0 0 1px rgba(0,0,0,.1);
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
}
|
||||
.popover {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
}
|
||||
.cover {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
}
|
||||
.modalcolor {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
// 标记管理页面
|
||||
.editbar{
|
||||
padding:18px 20px;
|
||||
background-color:rgba(218, 225, 251, 0.24);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
justify-content: space-between;
|
||||
&>div{
|
||||
position: relative;
|
||||
.inputred{
|
||||
border-color: red;
|
||||
}
|
||||
.red{
|
||||
position: absolute;
|
||||
left: 166px;
|
||||
width: 100%;
|
||||
top: 2px;
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
.signlist{
|
||||
padding:10px 0px;
|
||||
margin-bottom: 40px!important;
|
||||
&>li{
|
||||
display: flex;
|
||||
margin-bottom: 5px;
|
||||
padding:10px 20px;
|
||||
&>p{
|
||||
flex:1;
|
||||
color: 40424a;
|
||||
&:first-child{
|
||||
flex:1.2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
&:last-child{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.square{
|
||||
display: block;
|
||||
width: 22px;
|
||||
height: 15px;
|
||||
border-radius:2px;
|
||||
}
|
||||
.line{
|
||||
position: relative;
|
||||
padding-left: 13px;
|
||||
&::before{
|
||||
position: absolute;
|
||||
height:13px;
|
||||
width: 1px;
|
||||
left: 0px;
|
||||
top:7px;
|
||||
background-color:#d5d5d5;
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 新建页面样式
|
||||
.shortwidth{
|
||||
width: 330px;
|
||||
padding-left: 50px;
|
||||
&>li{
|
||||
border-bottom: 1px solid rgba(172, 176, 191, 0.17);
|
||||
padding-bottom: 8px;
|
||||
margin-bottom: 22px!important;
|
||||
&>span{
|
||||
font-size: 15px;
|
||||
position: relative;
|
||||
color: #40424a;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
font-weight: 600;
|
||||
a{
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
}
|
||||
}
|
||||
.operatevalue{
|
||||
line-height: 30px;
|
||||
color: #acb0bf;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
.colorsborder{
|
||||
padding: 0px 8px;
|
||||
height: 20px;
|
||||
line-height: 18px;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
border:1px solid ;
|
||||
font-size: 13px;
|
||||
}
|
||||
.colorsquare{
|
||||
padding: 0px 8px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
}
|
||||
&>p{
|
||||
margin-top: 8px!important;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
position: relative;
|
||||
padding-right: 15px;
|
||||
&.removeFlag>span{
|
||||
max-width: 230px;
|
||||
}
|
||||
.removeicon{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.removeFlag:hover{
|
||||
border-radius:2px;
|
||||
.removeicon{
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
&>p:first-child{
|
||||
margin-top: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.addTags_form{
|
||||
.ant-row.ant-form-item{
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.manageTitle{
|
||||
margin-bottom: 20px!important;
|
||||
padding-bottom: 15px;
|
||||
line-height: 22px;
|
||||
color:#5f6872;
|
||||
font-size:16px;
|
||||
border-bottom:1px solid #e1e3e8;
|
||||
}
|
||||
.inline{
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
.addTagsModal{
|
||||
.ant-modal-content{
|
||||
background-image:linear-gradient(359.37deg,#ebf3ff 0%,#f8fbff 55.01%,#f1f5ff 100%);
|
||||
}
|
||||
.ant-modal-header{
|
||||
background-color: unset;
|
||||
padding:0px 24px;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
.ant-modal-title{
|
||||
text-align: left;
|
||||
color:#333333;
|
||||
font-size:17px;
|
||||
font-weight: normal!important;
|
||||
height: 50px;
|
||||
line-height: 50px!important;
|
||||
}
|
||||
}
|
||||
.ant-modal-close{
|
||||
top:0px !important;
|
||||
}
|
||||
.popover{
|
||||
left: 94px;
|
||||
top:0px;
|
||||
}
|
||||
}
|
||||
.menusEmpty{
|
||||
padding:12px 20px 10px 16px;
|
||||
&>p{
|
||||
padding:10px 16px;
|
||||
}
|
||||
&>a{
|
||||
padding:10px 16px;
|
||||
display: block;
|
||||
border-top: 1px solid rgba(172, 176, 191, 0.17);
|
||||
}
|
||||
}
|
||||
.piecemenu{
|
||||
li{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
.colorpiece{
|
||||
display: block;
|
||||
width: 22px!important;
|
||||
height: 15px;
|
||||
margin-right: 9px;
|
||||
border-radius:2px;
|
||||
}
|
||||
.overlayStyle{
|
||||
background-color:#ffffff;
|
||||
border-radius:6px;
|
||||
box-shadow:0px 0px 10px rgba(24, 54, 181, 0.17);
|
||||
max-width: 280px;
|
||||
z-index: 100;
|
||||
.searchbox{
|
||||
padding:12px 20px 0px 16px;
|
||||
}
|
||||
.ant-menu{
|
||||
padding:10px 16px 10px 16px;
|
||||
max-height: 324px;
|
||||
overflow-y: auto;
|
||||
li{
|
||||
padding:0px 16px;
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
border-bottom: none;
|
||||
color:#898d9d;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
&:hover,&.ant-menu-item-selected{
|
||||
background-color:rgba(70, 106, 255, 0.07);
|
||||
color:#40424a;
|
||||
}
|
||||
&.ant-menu-item-selected::before{
|
||||
content:"✓";
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
color: #acb0bf;
|
||||
top: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//详情页面
|
||||
.editpanel{
|
||||
padding:20px;
|
||||
margin-top: 25px;
|
||||
border:1px solid rgba(42, 97, 255, 0.23);
|
||||
border-radius: 2px;
|
||||
}
|
||||
.detailbanner{
|
||||
// margin-top: 25px;
|
||||
// height:82px;
|
||||
// background-color:#fafcff;
|
||||
// border:1px solid rgba(42, 97, 255, 0.23);
|
||||
// border-radius:4px;
|
||||
// padding:12px 25px 12px 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.detailtitle{
|
||||
flex: 1;
|
||||
&>div{
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.name{
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color:#333;
|
||||
line-height: 22px;
|
||||
max-width: 580px;
|
||||
word-break: break-all;
|
||||
}
|
||||
.author{
|
||||
img{
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
margin-right: 4px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
color: #466aff;
|
||||
}
|
||||
}
|
||||
.detailoperate{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
.descPanel{
|
||||
margin-top:30px;
|
||||
// padding:20px;
|
||||
// background-color: #f7f8fd;
|
||||
// border:1px solid #c9d8ff;
|
||||
// border-radius: 2px;
|
||||
}
|
||||
// 新建页面
|
||||
.explain{
|
||||
.ant-form-explain{
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.overlaydrop{
|
||||
&.large{
|
||||
width: 260px;
|
||||
}
|
||||
&.small{
|
||||
width: 120px;
|
||||
padding:12px 0px!important;
|
||||
}
|
||||
padding:18px 15px;
|
||||
position: relative;
|
||||
border-radius:6px;
|
||||
box-shadow:0px 0px 10px rgba(24, 54, 181, 0.17);
|
||||
background-color: #fff;
|
||||
.ant-menu{
|
||||
max-height: 305px;
|
||||
overflow-y: auto;
|
||||
li{
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
border-bottom: none!important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 6px;
|
||||
padding-right: 0px;
|
||||
img{
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
span{
|
||||
display: block;
|
||||
width:100%;
|
||||
margin-left: 10px;
|
||||
padding-right: 25px;
|
||||
}
|
||||
&:hover,&.ant-menu-item-selected{
|
||||
background-color:rgba(70, 106, 255, 0.07);
|
||||
color:#40424a;
|
||||
}
|
||||
&.ant-menu-item-selected::before{
|
||||
content:"✓";
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
color: #acb0bf;
|
||||
top: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 声明
|
||||
.claimpart{
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid #eee;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
// issue版本2
|
||||
.overlayChooseStyle{
|
||||
width: 636px;
|
||||
background-color:#ffffff;
|
||||
border-radius:6px;
|
||||
box-shadow:0px 0px 10px rgba(24, 54, 181, 0.17);
|
||||
z-index: 100;
|
||||
padding:30px 20px;
|
||||
.choosedul{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #e4e4e6;
|
||||
margin-bottom: 20px!important;
|
||||
li{
|
||||
height:24px;
|
||||
background-color:#eff2ff;
|
||||
border:1px solid;
|
||||
border-color:#466aff;
|
||||
border-radius:2px;
|
||||
color:#466aff;
|
||||
font-size:14px;
|
||||
margin-right: 15px;
|
||||
margin-bottom: 10px!important;
|
||||
padding:0px 7px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
span{
|
||||
display: block;
|
||||
max-width:120px ;
|
||||
}
|
||||
}
|
||||
}
|
||||
.counttips{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.ant-menu{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding:20px 0px 5px 0px;
|
||||
box-sizing: border-box;
|
||||
max-height: 307px;
|
||||
overflow-y: auto;
|
||||
&.piecemenu li{
|
||||
width: auto;
|
||||
span.task-hide{
|
||||
max-width: unset;
|
||||
}
|
||||
&:nth-child(5n){
|
||||
margin-right: 24px!important;
|
||||
}
|
||||
}
|
||||
li{
|
||||
width: 100px;
|
||||
height:32px;
|
||||
line-height:30px;
|
||||
text-align: center;
|
||||
font-size:14px;
|
||||
border-radius:2px;
|
||||
margin-right: 24px!important;
|
||||
margin-bottom: 15px!important;
|
||||
border:1px solid!important;
|
||||
border-color: #f4f6fe!important;
|
||||
padding:0px 5px;
|
||||
&.commonli{
|
||||
background-color:#f4f6fe!important;
|
||||
}
|
||||
.removeicon{
|
||||
display: block;
|
||||
}
|
||||
span.task-hide{
|
||||
color: #3f455b;
|
||||
margin:0px auto;
|
||||
position: relative;
|
||||
color:#3f455b;
|
||||
max-width: 85px;
|
||||
display: block;
|
||||
}
|
||||
span{
|
||||
position: relative;
|
||||
margin:0px auto;
|
||||
}
|
||||
&.colorli{
|
||||
border-color: transparent!important;
|
||||
span{
|
||||
color: #fff;
|
||||
position: relative;
|
||||
margin:0px auto;
|
||||
}
|
||||
}
|
||||
&.commonli.ant-menu-item-selected{
|
||||
background-color:#e2e8ff!important;
|
||||
color:#3f5097;
|
||||
border-color:#466aff!important;
|
||||
}
|
||||
&.colorli.ant-menu-item-selected{
|
||||
border-color: transparent!important;
|
||||
}
|
||||
&.colorli.ant-menu-item-selected{
|
||||
&>span::before{
|
||||
content:"✓";
|
||||
position: absolute;
|
||||
right: -12px;
|
||||
color: #fff;
|
||||
top: 0px;
|
||||
}
|
||||
}
|
||||
&:nth-child(5n){
|
||||
margin-right: 0px!important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -476,7 +476,7 @@ function CoderDepot(props){
|
|||
}
|
||||
{
|
||||
issuesFlag &&
|
||||
<a onClick={createIssue}>+ 疑修</a>
|
||||
<CheckProfile {...props} sureFunc={createIssue} checklogin>+ 疑修</CheckProfile>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ function CoderDepotReadme({ operate , history , readme , ChangeFile }){
|
|||
const items = $.map($("#readme").find("h1,h2,h3,h4,h5,h6"), function (el, _) {
|
||||
const anchor = el.id;
|
||||
const level = el.tagName.replace("H", "");
|
||||
const href = `#${anchor}`;
|
||||
let id = anchor.replace(/[.,/#!$%^&*;:{}=\-_`~():,。¥;「」|?》《~·【】‘、!]/g,"");
|
||||
const href = `#${id}`;
|
||||
return { href:`${href}`,text:el.textContent , level:level }
|
||||
});
|
||||
setMenuList(items);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ const OrderDetail = Loadable({
|
|||
loading: Loading,
|
||||
})
|
||||
const OrderIndex = Loadable({
|
||||
loader: () => import('../Order/order'),
|
||||
loader: () => import('../Issues/index'),
|
||||
loading: Loading,
|
||||
})
|
||||
const CoderRootIndex = Loadable({
|
||||
|
|
@ -89,7 +89,7 @@ const UpdateMerge = Loadable({
|
|||
})
|
||||
|
||||
const MilepostDetail = Loadable({
|
||||
loader: () => import('../Order/MilepostDetail'),
|
||||
loader: () => import('../Order/MilepostDetail.jsx'),
|
||||
loading: Loading,
|
||||
})
|
||||
const WatchUsers = Loadable({
|
||||
|
|
@ -247,6 +247,7 @@ class Detail extends Component {
|
|||
let currentIssue = pathname.indexOf(`/${owner}/${projectsId}/issues`) === -1;
|
||||
if (currentIssue) {
|
||||
cookie.save('states', undefined, { expires: 0, path: `/` });
|
||||
cookie.save('issuestates', undefined, { expires: 0, path: `/` });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -742,11 +743,11 @@ class Detail extends Component {
|
|||
}
|
||||
></Route>
|
||||
{/* 标签列表 */}
|
||||
<Route path="/:owner/:projectsId/issues/tags"
|
||||
{/* <Route path="/:owner/:projectsId/issues/tags"
|
||||
render={
|
||||
(props) => (<TagList {...this.props} {...props} {...this.state} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
></Route> */}
|
||||
{/* 仓库设置 */}
|
||||
<Route path="/:owner/:projectsId/settings"
|
||||
render={
|
||||
|
|
@ -767,7 +768,7 @@ class Detail extends Component {
|
|||
}
|
||||
></Route>
|
||||
{/*里程碑详情*/}
|
||||
<Route path="/:owner/:projectsId/milestones/:meilid"
|
||||
<Route path="/:owner/:projectsId/milestones/:mileId"
|
||||
render={
|
||||
(props) => (<MilepostDetail {...this.props} {...props} {...this.state} {...common} />)
|
||||
}
|
||||
|
|
@ -779,36 +780,36 @@ class Detail extends Component {
|
|||
}
|
||||
></Route>
|
||||
{/* 里程碑页面新建任务 */}
|
||||
<Route path="/:owner/:projectsId/issues/:milepostId/new"
|
||||
{/* <Route path="/:owner/:projectsId/issues/:milepostId/new"
|
||||
render={
|
||||
(props) => (<OrderNew {...this.props} {...props} {...this.state} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
></Route> */}
|
||||
{/* 新建任务 */}
|
||||
<Route path="/:owner/:projectsId/issues/new"
|
||||
{/* <Route path="/:owner/:projectsId/issues/new"
|
||||
render={
|
||||
(props) => (<OrderNew {...this.props} {...props} {...this.state} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
></Route> */}
|
||||
{/* 修改详情 edit*/}
|
||||
<Route path="/:owner/:projectsId/issues/:orderId/edit"
|
||||
{/* <Route path="/:owner/:projectsId/issues/:orderId/edit"
|
||||
render={
|
||||
(props) => (<OrderupdateDetail {...this.props} {...props} {...this.state} {...common} form_type={"edit"} />)
|
||||
}
|
||||
></Route>
|
||||
></Route> */}
|
||||
{/* 复制详情 copyetail*/}
|
||||
<Route path="/:owner/:projectsId/issues/:orderId/copyetail"
|
||||
{/* <Route path="/:owner/:projectsId/issues/:orderId/copyetail"
|
||||
render={
|
||||
(props) => (<OrderupdateDetail {...this.props} {...props} {...this.state} {...common} form_type={"copy"} />)
|
||||
}
|
||||
></Route>
|
||||
></Route> */}
|
||||
|
||||
{/* 任务详情 */}
|
||||
<Route path="/:owner/:projectsId/issues/:orderId"
|
||||
{/* <Route path="/:owner/:projectsId/issues/:orderId"
|
||||
render={
|
||||
(props) => (<OrderDetail {...this.props} {...this.state} {...props} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
></Route> */}
|
||||
{/* 动态 */}
|
||||
<Route path="/:owner/:projectsId/activity"
|
||||
render={
|
||||
|
|
|
|||
|
|
@ -8,14 +8,25 @@ import { getImageUrl } from 'educoder';
|
|||
import { Link } from 'react-router-dom';
|
||||
|
||||
function Contribute(props){
|
||||
console.log('props', props);
|
||||
const [ list , setList ] = useState(undefined);
|
||||
const [ page , setPage ] = useState(1);
|
||||
const [ total , setTotal ] = useState(0);
|
||||
const [ isSpin , setIsSpin ] = useState(true);
|
||||
|
||||
const {project} = props;
|
||||
const owner = props.match.params.owner;
|
||||
const projectsId = props.match.params.projectsId;
|
||||
const LIMIT = 20;
|
||||
|
||||
useEffect(()=>{
|
||||
if(project){
|
||||
// 更改网页标题
|
||||
const {name, author} = project;
|
||||
document.title = `贡献者列表-${author.name}/${name}`;
|
||||
}
|
||||
}, [project])
|
||||
|
||||
useEffect(()=>{
|
||||
if(owner && projectsId){
|
||||
getData();
|
||||
|
|
@ -49,11 +60,11 @@ function Contribute(props){
|
|||
{
|
||||
list.map((item,key)=>{
|
||||
return(
|
||||
<AlignCenter>
|
||||
<AlignCenter className='mb15'>
|
||||
<img alt="" style={{borderRadius:"50%",marginRight:"10px"}} src={getImageUrl(`/${item.image_url}`)} width="50px" height="50px"/>
|
||||
<div>
|
||||
<Link to={`/${item.login}`} className="font-16">{item.name}</Link>
|
||||
<p className="font-12 color-grey-9">提交{item.contributions}次</p>
|
||||
<p className="font-12 color-grey-9">提交{item.contributions}次{item.contribution_perc ? ` I 贡献占比${item.contribution_perc}` : ''}</p>
|
||||
</div>
|
||||
</AlignCenter>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ function ReadmeCatelogue({ menuList , hash }) {
|
|||
menu.map((item,key)=>{
|
||||
return(
|
||||
<div style={{paddingLeft:`${item.level *15}px`}} className={goHref===item.href?"items active":"items"}>
|
||||
<Link href={`#${item.text}`} title={item.text} />
|
||||
<Link href={`${item.href}`} title={item.text} />
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -73,7 +73,6 @@ function Index(props) {
|
|||
dataIndex: "name",
|
||||
key: 1,
|
||||
ellipsis: true,
|
||||
width: "160px",
|
||||
className: "branchNameColumn",
|
||||
render: (txt, item) => {
|
||||
return (
|
||||
|
|
@ -112,7 +111,7 @@ function Index(props) {
|
|||
dataIndex: "commit_id",
|
||||
key: 4,
|
||||
ellipsis: true,
|
||||
width: "300px",
|
||||
width: "250px",
|
||||
render: (txt, item) => {
|
||||
return <div className="treecopy">
|
||||
<div className='treeCopyBox'>
|
||||
|
|
@ -130,6 +129,7 @@ function Index(props) {
|
|||
title: "分支类型",
|
||||
dataIndex: "default_branch",
|
||||
key: 5,
|
||||
width: "150px",
|
||||
render: (txt, item) => {
|
||||
return isManager ? txt === item.name ? <a className='btn-83' style={{width: '93px'}}>默认分支</a> : <Select value={1} onChange={(value)=>{updateDefaultBranch(value, item)}}>
|
||||
<Select.Option value={0}>默认分支</Select.Option>
|
||||
|
|
@ -263,16 +263,10 @@ function Index(props) {
|
|||
// 新建分支按钮点击函数
|
||||
function addBranchBtn(){
|
||||
setAddBranch(true);
|
||||
const url = `/v1/${owner}/${projectsId}/branches.json`;
|
||||
Axios.get(url,{
|
||||
params:{
|
||||
page: 1,
|
||||
limit: total
|
||||
}
|
||||
}).then(result=>{
|
||||
const url = `/v1/${owner}/${projectsId}/branches/all.json`;
|
||||
Axios.get(url).then(result=>{
|
||||
if(result){
|
||||
const {branches} = result.data;
|
||||
setBranchList(branches);
|
||||
setBranchList(result.data);
|
||||
}
|
||||
}).catch(error=>{console.log('error', error);})
|
||||
}
|
||||
|
|
@ -393,7 +387,7 @@ function Index(props) {
|
|||
<Input placeholder='请输入分支名称' className='createBranchInput' onChange={checkNewBranchName} maxLength={100}/>
|
||||
<div className='newBranchError mt5'>{newBranchError}</div>
|
||||
<div className='font-15 createBranchLabel mt20'>分支起点</div>
|
||||
<Select showSearch style={{width: '340px'}} className='createBranchInput' defaultValue={list[0] && list[0].name} onChange={(value)=>{setNewFormBranchName(value)}} getPopupContainer={triggerNode => triggerNode.parentNode}>
|
||||
<Select showSearch style={{width: '340px'}} className='createBranchInput' placeholder="请选择分支起点" onChange={(value)=>{setNewFormBranchName(value)}} getPopupContainer={triggerNode => triggerNode.parentNode}>
|
||||
{branchList && branchList.map((item, index)=>{
|
||||
return <Select.Option value={item.name} key={index}>{item.name}</Select.Option>
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class MergeItem extends Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { issues, project_name, project_author_name , user_admin_or_developer} = this.props;
|
||||
const { issues, project_name, project_author_name , user_admin_or_developer , open_blockchain} = this.props;
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
const { current_user } = this.props;
|
||||
const renderList = () => {
|
||||
|
|
@ -143,6 +143,17 @@ class MergeItem extends Component {
|
|||
onMouseMove={() => this.onMouseMove(item.id)}
|
||||
onMouseOut={() => this.onMouseOut()}
|
||||
>
|
||||
{item.attached_issues && item.attached_issues.length>0 && open_blockchain &&
|
||||
<li>
|
||||
{
|
||||
item.attached_issues.map((i,k)=>{
|
||||
return(
|
||||
<Link to={`/${owner}/${projectsId}/issues/${i.project_issues_index}`} className="issueIndex">#{i.project_issues_index}</Link>
|
||||
)
|
||||
})
|
||||
}
|
||||
</li>
|
||||
}
|
||||
<li>{item.priority}</li>
|
||||
<li>{this.set_issue_tags(item.issue_tags)}</li>
|
||||
<li>
|
||||
|
|
|
|||
|
|
@ -350,7 +350,7 @@ class MessageCount extends Component {
|
|||
pull_request,
|
||||
conflict_files
|
||||
} = this.state;
|
||||
const { current_user, projectDetail } = this.props;
|
||||
const { current_user, projectDetail ,project } = this.props;
|
||||
const menu = (
|
||||
<Menu onClick={(e) => this.getOption(e)}>
|
||||
<Menu.Item key={"merge"} value="合并请求">
|
||||
|
|
@ -370,6 +370,7 @@ class MessageCount extends Component {
|
|||
const permission = projectDetail && (projectDetail.permission === "Admin" || projectDetail.permission === "Owner" || projectDetail.permission === "Manager");
|
||||
const userLogin = current_user && current_user.login;
|
||||
const operate = userLogin && projectDetail && pr_status === 0 && permission;
|
||||
const open_blockchain = project && project.open_blockchain;
|
||||
return (
|
||||
<div>
|
||||
{data ? (
|
||||
|
|
@ -444,6 +445,19 @@ class MessageCount extends Component {
|
|||
: "更新于"}
|
||||
</span>
|
||||
<span className="color-grey-8 ml5">{data.pr_time}</span>
|
||||
{
|
||||
open_blockchain && data.attached_issues && data.attached_issues.length>0 ?
|
||||
<span className="color-grey-8 ml25">关联issue:
|
||||
{
|
||||
data.attached_issues.map((i,k)=>{
|
||||
return(
|
||||
<Link className="issueIndex ml5" to={`/${owner}/${projectsId}/issues/${i.project_issues_index}`}>#{i.project_issues_index}</Link>
|
||||
)
|
||||
})
|
||||
}
|
||||
</span>
|
||||
:""
|
||||
}
|
||||
<span className="ml25">
|
||||
<span className="color-grey-8">审查人员:</span>
|
||||
{data.issue.assign_user_name ? (
|
||||
|
|
|
|||
|
|
@ -236,6 +236,8 @@ class merge extends Component {
|
|||
}
|
||||
render() {
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
const { project } = this.props;
|
||||
const open_blockchain = project && project.open_blockchain;
|
||||
const {
|
||||
issue_chosen,
|
||||
issues,
|
||||
|
|
@ -318,6 +320,7 @@ class merge extends Component {
|
|||
</ul>
|
||||
</div>
|
||||
<ul className="topWrapper_select">
|
||||
{ open_blockchain && <li>关联疑修</li> }
|
||||
<li>
|
||||
<Dropdown
|
||||
className="topWrapperSelect"
|
||||
|
|
@ -417,6 +420,7 @@ class merge extends Component {
|
|||
{...this.props}
|
||||
{...this.state}
|
||||
user_admin_or_developer={data && data.user_admin_or_developer}
|
||||
open_blockchain={open_blockchain}
|
||||
></MergeItem>
|
||||
</div>
|
||||
):""}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ class MergeForm extends Component {
|
|||
issue_tags: undefined,
|
||||
issue_versions: undefined,
|
||||
issue_priories: undefined,
|
||||
atWhoLoginList:undefined
|
||||
atWhoLoginList:undefined,
|
||||
issue_lists:undefined,
|
||||
issue_id:undefined
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -33,6 +35,7 @@ class MergeForm extends Component {
|
|||
// this.check_is_login();
|
||||
this.get_default_selects();
|
||||
this.set_defatul();
|
||||
this.getIssueList();
|
||||
};
|
||||
componentDidUpdate=(prevPros)=>{
|
||||
const { projectsId ,owner } = this.props.match.params;
|
||||
|
|
@ -47,6 +50,22 @@ class MergeForm extends Component {
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
getIssueList=()=>{
|
||||
const { projectsId ,owner } = this.props.match.params;
|
||||
const url = `/v1/${owner}/${projectsId}/issues`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
only_name:true
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
this.setState({
|
||||
issue_lists:result.data.issues
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
get_default_selects = () => {
|
||||
const { projectsId ,owner } = this.props.match.params;
|
||||
|
|
@ -72,23 +91,25 @@ class MergeForm extends Component {
|
|||
set_defatul = () => {
|
||||
const { data, merge_type } = this.props;
|
||||
if (data && merge_type === "edit") {
|
||||
console.log("dddddddd",data);
|
||||
this.setState({
|
||||
desc: data.body,
|
||||
issue_tag_ids: data.issue_tag_ids ? data.issue_tag_ids[0] : undefined,
|
||||
fixed_version_id: data.fixed_version_id ? String(data.fixed_version_id) : undefined,
|
||||
assigned_to_id: data.assigned_to_id ? String(data.assigned_to_id) : undefined,
|
||||
priority_id: data.priority_id ? String(data.priority_id) :undefined,
|
||||
issue_id:data.attached_issues && data.attached_issues.length>0 ? String(data.attached_issues[0].id) :undefined,
|
||||
title: data.title,
|
||||
},()=>{
|
||||
this.InitData();
|
||||
});
|
||||
}
|
||||
this.InitData();
|
||||
};
|
||||
InitData = () => {
|
||||
setTimeout(() => {
|
||||
this.props.form.setFieldsValue({
|
||||
...this.state,
|
||||
});
|
||||
}, 100);
|
||||
console.log(this.state.issue_id);
|
||||
this.props.form.setFieldsValue({
|
||||
...this.state,
|
||||
});
|
||||
};
|
||||
|
||||
onPanelChange = (time, mode) => {
|
||||
|
|
@ -125,7 +146,7 @@ class MergeForm extends Component {
|
|||
return list.map((item, key) => {
|
||||
return (
|
||||
<Option key={key + 1} value={item.id + ""}>
|
||||
{item.name}
|
||||
{item.name || item.subject}
|
||||
</Option>
|
||||
);
|
||||
});
|
||||
|
|
@ -161,6 +182,7 @@ class MergeForm extends Component {
|
|||
files_count,
|
||||
commits_count,
|
||||
receivers_login:atWhoLoginList,
|
||||
attached_issue_ids:values.issue_id ? [values.issue_id] :undefined
|
||||
})
|
||||
.then((result) => {
|
||||
if (result) {
|
||||
|
|
@ -191,6 +213,7 @@ class MergeForm extends Component {
|
|||
head: pull,
|
||||
base: merge,
|
||||
receivers_login:atWhoLoginList,
|
||||
attached_issue_ids:values.issue_id ? [values.issue_id] :undefined
|
||||
})
|
||||
.then((result) => {
|
||||
if (result) {
|
||||
|
|
@ -234,7 +257,8 @@ class MergeForm extends Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { merge_type } = this.props;
|
||||
const { merge_type , project } = this.props;
|
||||
const open_blockchain = project && project.open_blockchain;
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
const { projectsId, mergeId ,owner } = this.props.match.params;
|
||||
const {
|
||||
|
|
@ -249,6 +273,8 @@ class MergeForm extends Component {
|
|||
issue_tags,
|
||||
issue_versions,
|
||||
issue_priories,
|
||||
issue_lists,
|
||||
issue_id
|
||||
} = this.state;
|
||||
return (
|
||||
<div>
|
||||
|
|
@ -275,17 +301,19 @@ class MergeForm extends Component {
|
|||
initialValue: title,
|
||||
})(<Input placeholder="标题" maxLength={200} />)}
|
||||
</Form.Item>
|
||||
<MDEditor
|
||||
placeholder={"请输入合并请求的描述..."}
|
||||
height={450}
|
||||
mdID={"merge-new-description"}
|
||||
initValue={desc}
|
||||
onChange={this.onContentChange}
|
||||
isCanAtme = {true}
|
||||
changeAtWhoLoginList = {this.changeAtWhoLoginList}
|
||||
owner = {owner}
|
||||
projectsId = {projectsId}
|
||||
></MDEditor>
|
||||
<div style={{position:"relative"}}>
|
||||
<MDEditor
|
||||
placeholder={"请输入合并请求的描述..."}
|
||||
height={450}
|
||||
mdID={"merge-new-description"}
|
||||
initValue={desc}
|
||||
onChange={this.onContentChange}
|
||||
isCanAtme = {true}
|
||||
changeAtWhoLoginList = {this.changeAtWhoLoginList}
|
||||
owner = {owner}
|
||||
projectsId = {projectsId}
|
||||
></MDEditor>
|
||||
</div>
|
||||
<p className="clearfix mt20">
|
||||
<Button
|
||||
type="primary"
|
||||
|
|
@ -379,6 +407,25 @@ class MergeForm extends Component {
|
|||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item style={{display:open_blockchain ? "block" :"none"}}>
|
||||
{getFieldDecorator("issue_id", {
|
||||
initialValue: issue_id,
|
||||
})(
|
||||
<Select
|
||||
placeholder={"关联疑修"}
|
||||
showSearch
|
||||
className="searchIcon"
|
||||
suffixIcon={<i className="iconfont icon-sousuo1"></i>}
|
||||
optionFilterProp="children"
|
||||
filterOption={(input, option) =>
|
||||
option.props.value && option.props.children && option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
>
|
||||
<Option key={0} value={""}>{issue_lists && issue_lists.length > 0 ? "未选择疑修" : "当前仓库还没有疑修"}</Option>
|
||||
{this.renderSelect(issue_lists)}
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
{/* <Form.Item label="其他">
|
||||
{getFieldDecorator("checkbox-group")(
|
||||
<Checkbox.Group>
|
||||
|
|
|
|||
|
|
@ -51,6 +51,9 @@ class Index extends Component {
|
|||
languageFlag:false,
|
||||
ignoreFlag:false,
|
||||
licenseFlag:false,
|
||||
|
||||
// 区块链确权
|
||||
tokenConTri: false
|
||||
}
|
||||
}
|
||||
componentDidMount = () => {
|
||||
|
|
@ -200,14 +203,13 @@ 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
|
||||
} = this.state;
|
||||
const { project_language_id, project_category_id, license_id, ignore_id, owners_id, ignoreFlag, licenseFlag, categoreFlag, languageFlag, tokenConTri} = this.state;
|
||||
const decoderPass = Base64.encode(values.password);
|
||||
const url = (projectsType && projectsType === "mirror") ? "/projects/migrate.json" : "/projects.json";
|
||||
// 新建项目的时候,暂存数据,如果失败,返回的时候可以重新赋值
|
||||
sessionStorage.newProjectValue=JSON.stringify({...values,project_language_id,project_category_id,license_id,ignore_id});
|
||||
// 贡献值激励确权机制
|
||||
const {blockchain_init} = values;
|
||||
axios.post(url, {
|
||||
...values,
|
||||
auth_password:decoderPass,
|
||||
|
|
@ -215,7 +217,10 @@ class Index extends Component {
|
|||
project_category_id:categoreFlag ? project_category_id : undefined,
|
||||
license_id:licenseFlag ? license_id : undefined,
|
||||
ignore_id:ignoreFlag ? ignore_id : undefined,
|
||||
user_id:owners_id
|
||||
user_id:owners_id,
|
||||
blockchain: tokenConTri,
|
||||
blockchain_token_all: 10000,
|
||||
blockchain_init_token: parseInt(blockchain_init)
|
||||
}).then((result) => {
|
||||
if (result && result.data.id) {
|
||||
projectsType && projectsType !== "mirror" && this.props.showNotification(`项目创建成功!`);
|
||||
|
|
@ -343,6 +348,7 @@ class Index extends Component {
|
|||
const { getFieldDecorator } = this.props.form;
|
||||
// 项目类型:deposit-托管项目,mirror-镜像项目
|
||||
const { projectsType } = this.props.match.params;
|
||||
const {current_user:{open_blockchain}} = this.props;
|
||||
|
||||
const {
|
||||
CategoryList,
|
||||
|
|
@ -365,7 +371,8 @@ class Index extends Component {
|
|||
ignoreFlag,
|
||||
licenseFlag,
|
||||
languageFlag,
|
||||
categoreFlag
|
||||
categoreFlag,
|
||||
tokenConTri
|
||||
} = this.state;
|
||||
return (
|
||||
<div className="main back-white" style={{padding:"0px",border:"none"}}>
|
||||
|
|
@ -630,6 +637,34 @@ class Index extends Component {
|
|||
)}
|
||||
</Form.Item>
|
||||
}
|
||||
|
||||
{/* 确权 仅在新建项目时 */}
|
||||
{open_blockchain && projectsType === "deposit" && <div className='mt10 mb10'>
|
||||
<p className='font-16'>确权</p>
|
||||
<div className='mt5 mb5 color-grey3'><Checkbox checked={tokenConTri} onChange={(e)=>{this.setState({tokenConTri:e.target.checked})}}>加入贡献值激励确权机制</Checkbox></div>
|
||||
{tokenConTri && <Form.Item
|
||||
className="flex1"
|
||||
extra="Token指本项目的虚拟货币,决定用户在本仓库贡献度的比例,可以通过issue悬赏或在个人主页贡献确权页进行转移。创建项目时,每个项目的Token总量为10000,您可以分配您可获取的Token数量(未分配的Token将保留至项目Token余额)"
|
||||
>
|
||||
{getFieldDecorator('blockchain_init', {
|
||||
rules: [{
|
||||
required: true, message: '初始token值为一个不超过10000的正整数'
|
||||
},
|
||||
{ validator: (rule,val,callback) =>{
|
||||
if(!/^[1-9]+[0-9]*]*$/.test(val)){
|
||||
callback('初始token值为正整数');
|
||||
}if(val>10000){
|
||||
callback('初始token值最大值10000');
|
||||
}else{
|
||||
callback();
|
||||
}
|
||||
}}],
|
||||
})(
|
||||
<Input placeholder='请填写您(项目创建者)获取的初始Token值'/>
|
||||
)}
|
||||
</Form.Item>}
|
||||
</div>}
|
||||
|
||||
<div className="mt20">
|
||||
注:<span className="ant-form-item-required"></span> 为必填项,否则为选填
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,29 +1,23 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Dropdown, Icon, Menu, Pagination, Typography, Popconfirm, Spin } from 'antd';
|
||||
import NoneData from '../Nodata';
|
||||
import { Dropdown, Icon, Menu, Pagination, Popconfirm, Spin, Button, Tooltip } from 'antd';
|
||||
import emp from '../../forge/Issues/Img/emp.png';
|
||||
import axios from 'axios';
|
||||
import './order.scss';
|
||||
import CheckProfile from '../Component/ProfileModal/Profile';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
import './milepost.scss';
|
||||
class Milepost extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
data: undefined,
|
||||
limit: 15,
|
||||
limit: 10,
|
||||
page: 1,
|
||||
order_type: undefined,
|
||||
//新建标签区域是否显示 none 隐藏 block 显示
|
||||
display: 'none',
|
||||
// 里程碑 开启/关闭 状态
|
||||
status: 'open',
|
||||
openselect: 1,
|
||||
closeselect: undefined,
|
||||
order_name: undefined,
|
||||
|
||||
spinings: true
|
||||
spinings: true,
|
||||
// tooltip显隐
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
componentDidUpdate(){
|
||||
|
|
@ -31,7 +25,7 @@ class Milepost extends Component {
|
|||
}
|
||||
|
||||
componentDidMount = () => {
|
||||
this.getList(1, this.state.status, 'desc');
|
||||
this.getList(1, this.state.limit, this.state.status);
|
||||
this.updateDocumentTitle();
|
||||
}
|
||||
|
||||
|
|
@ -44,13 +38,12 @@ class Milepost extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
getList = (page, status, order_type, order_name) => {
|
||||
getList = (page, limit, category, sort_by, sort_direction) => {
|
||||
const { projectsId ,owner } = this.props.match.params;
|
||||
const { limit } = this.state;
|
||||
const url = `/${owner}/${projectsId}/milestones.json`;
|
||||
const url = `/v1/${owner}/${projectsId}/milestones.json`;
|
||||
axios.get(url, {
|
||||
params: {
|
||||
page, limit, status, order_type, order_name
|
||||
page, limit: limit, category, sort_by, sort_direction
|
||||
}
|
||||
}).then((result) => {
|
||||
if (result) {
|
||||
|
|
@ -65,45 +58,24 @@ class Milepost extends Component {
|
|||
}
|
||||
|
||||
opneMilelist = (type) => {
|
||||
const { order_name } = this.state;
|
||||
if (type) {
|
||||
const { current_user } = this.props;
|
||||
if (type === 1) {
|
||||
this.setState({
|
||||
status: 'open',
|
||||
openselect: current_user.user_id,
|
||||
closeselect: undefined,
|
||||
|
||||
})
|
||||
this.getList(1, 'open', 'desc', order_name);
|
||||
} else {
|
||||
this.setState({
|
||||
status: 'closed',
|
||||
openselect: undefined,
|
||||
closeselect: current_user.user_id
|
||||
})
|
||||
this.getList(1, 'closed', 'desc', order_name);
|
||||
}
|
||||
this.setState({
|
||||
status: type === 1 ? 'open' : 'closed'
|
||||
})
|
||||
this.getList(1, this.state.limit, type === 1 ? 'open' : 'closed', this.state.order_name, this.state.order_type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updatestatusemile = (status, arr) => {
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
const url = `/${owner}/${projectsId}/milestones/${arr.id}/update_status.json`;
|
||||
const { current_user } = this.props;
|
||||
axios.post(url, {
|
||||
project_id: projectsId,
|
||||
id: arr.id,
|
||||
status: status
|
||||
}).then(result => {
|
||||
if (result) {
|
||||
this.setState({
|
||||
status: status,
|
||||
closeselect: status === "closed" ? current_user.user_id : undefined,
|
||||
openselect: status === "closed" ? undefined : current_user.user_id
|
||||
})
|
||||
this.getList(1, status, 'desc');
|
||||
this.getList(1, this.state.limit, this.state.status, this.state.order_name, this.state.order_type);
|
||||
const { getDetail } = this.props;
|
||||
getDetail && getDetail();
|
||||
}
|
||||
|
|
@ -122,7 +94,7 @@ class Milepost extends Component {
|
|||
}
|
||||
}).then((result) => {
|
||||
if (result) {
|
||||
this.getList(1, this.state.status, 'desc');
|
||||
this.getList(1, this.state.limit, this.state.status, this.state.order_name, this.state.order_type);
|
||||
const { getDetail } = this.props;
|
||||
getDetail && getDetail();
|
||||
}
|
||||
|
|
@ -136,39 +108,44 @@ class Milepost extends Component {
|
|||
this.setState({
|
||||
page
|
||||
})
|
||||
const { status } = this.state;
|
||||
this.getList( page , status );
|
||||
this.getList(page, this.state.limit, this.state.status, this.state.order_name, this.state.order_type);
|
||||
}
|
||||
|
||||
// 排序
|
||||
arrayList = (e) => {
|
||||
const name = e.key !== 'order' ? e.key : undefined;
|
||||
const type = e.item.props.value !== 'order' ? e.item.props.value : undefined;
|
||||
this.setState({
|
||||
order_name: e.key,
|
||||
order_type: e.item.props.value
|
||||
order_name: name,
|
||||
order_type: type
|
||||
})
|
||||
this.getList(1, this.state.status, e.item.props.value, e.key);
|
||||
this.getList(1, this.state.limit, this.state.status, name, type);
|
||||
}
|
||||
|
||||
|
||||
//控制新建标签页是否显示
|
||||
newshow = () => {
|
||||
// 分页组件
|
||||
onShowSizeChange = (current, pageSize) =>{
|
||||
this.setState({
|
||||
display: 'block'
|
||||
});
|
||||
|
||||
};
|
||||
newclose = () => {
|
||||
this.setState({
|
||||
display: 'none'
|
||||
});
|
||||
};
|
||||
page: 1,
|
||||
limit: pageSize
|
||||
})
|
||||
this.getList(1, pageSize, this.state.status, this.state.order_name, this.state.order_type);
|
||||
}
|
||||
|
||||
// tooltip判断是否显示
|
||||
// onVisibleChange = (visible, value) => {
|
||||
// console.log('visible, value', visible, value);
|
||||
// this.setState({
|
||||
// visible: value.length >24 ? visible : false
|
||||
// })
|
||||
// }
|
||||
|
||||
render() {
|
||||
const { data, limit, page, openselect, closeselect, spinings } = this.state;
|
||||
const { data, limit, page, spinings, status, order_name, order_type, visible } = this.state;
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
const { isManager, isDeveloper} = this.props;
|
||||
const menu = (
|
||||
<Menu className="orderCondition" onClick={this.arrayList}>
|
||||
<Menu className="orderCondition milepostSort" onClick={this.arrayList}>
|
||||
<Menu.Item key={'order'} value={'order'}>默认排序</Menu.Item>
|
||||
<Menu.Item key={'effective_date'} value="desc">到期日从后到先</Menu.Item>
|
||||
<Menu.Item key={'effective_date'} value="asc">到期日从先到后</Menu.Item>
|
||||
<Menu.Item key={'percent'} value="asc">完成度从低到高</Menu.Item>
|
||||
|
|
@ -178,103 +155,90 @@ class Milepost extends Component {
|
|||
</Menu>
|
||||
)
|
||||
|
||||
// 排序字段 选中显示文本
|
||||
const sortByMile = {
|
||||
'effective_date':{
|
||||
'desc': '到期日从后到先',
|
||||
'asc': '到期日从先到后'
|
||||
},
|
||||
'percent':{
|
||||
'desc': '完成度从低到高',
|
||||
'asc': '完成度从高到低'
|
||||
},
|
||||
'issues_count':{
|
||||
'desc': '任务从多到少',
|
||||
'asc': '任务从少到多'
|
||||
},
|
||||
'order':{
|
||||
'order': '排序'
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Spin spinning={spinings}>
|
||||
<div className="main mt20" style={{minHeight:"400px"}}>
|
||||
<div style={{ display: this.state.display }}>
|
||||
<div className="tagdiv" >
|
||||
<span>里程碑{data && data.issue_tags_count}已创建</span>
|
||||
<div className="main milepostBox">
|
||||
{/* 创建里程碑按钮 */}
|
||||
{/* <div className='createMilepost mb25'>
|
||||
{(isManager || isDeveloper) && <Button type='primary' onClick={() => {this.props.history.push(`/${owner}/${projectsId}/milestones/new`)}} className='createMilepostBtn'>+ 创建里程碑</Button>}
|
||||
</div> */}
|
||||
{/* 里程碑列表表头 */}
|
||||
<div className='milepostHead flexSpaceBetween'>
|
||||
<div>
|
||||
<span className={`pointBox font-15 postStatus ${status === "closed" ? "" : "active"}`} onClick={() => this.opneMilelist(1)}>开启中<span className='statusCount font-13'>{data && data.opening_milestone_count}</span></span>
|
||||
<span className={`pointBox ml35 font-15 postStatus ${status === "closed" ? "active" : ""}`} onClick={() => this.opneMilelist(2)}>已关闭<span className='statusCount font-13'>{data && data.closed_milestone_count}</span></span>
|
||||
</div>
|
||||
<div>
|
||||
{(isManager || isDeveloper) && <Button type='primary' onClick={() => {this.props.history.push(`/${owner}/${projectsId}/milestones/new`)}} className='createMilepostBtn mr20'>+ 创建里程碑</Button>}
|
||||
<Dropdown className="topWrapperSelect" overlay={menu} trigger={['click']} placement="bottomCenter">
|
||||
<span className='pointBox'>{order_name ? sortByMile[order_name][order_type] : '排序'}<Icon type="caret-down" className="ml5"/></span>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div className="topWrapper" style={{borderBottom:"1px solid #eee"}}>
|
||||
<div className="topWrapper_type_infos">
|
||||
<li className={openselect ? "active" : ""} onClick={() => this.opneMilelist(1)}>{data && data.open_count}个开启中</li>
|
||||
<li className={closeselect ? "active" : ""} onClick={() => this.opneMilelist(2)}>{data && data.closed_count}个已关闭</li>
|
||||
</div>
|
||||
<div className="topWrapper_select">
|
||||
<ul className="topWrapper_select mb-0">
|
||||
<li>
|
||||
<Dropdown className="topWrapperSelect" overlay={menu} trigger={['click']} placement="bottomCenter">
|
||||
<span>排序<Icon type="caret-down" className="ml5" /></span>
|
||||
</Dropdown>
|
||||
</li>
|
||||
</ul>
|
||||
{
|
||||
data && data.user_admin_or_member ?
|
||||
<CheckProfile {...this.props} className="topWrapper_btn" sureFunc={() => {this.props.history.push(`/${owner}/${projectsId}/milestones/new`)}}>新的里程碑</CheckProfile>
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
data && data.versions && data.versions.length > 0
|
||||
&&
|
||||
<div className="tagList">
|
||||
{
|
||||
data.versions.map((item, key) => {
|
||||
return (
|
||||
<div style={{ display: 'block' }} key={key}>
|
||||
<div className="milepostdiv">
|
||||
<div className="milepostwidth">
|
||||
<div className="grid-item width100">
|
||||
<i className="iconfont icon-lubiaosignpost3 font-12 mr3"></i>
|
||||
<Link to={`/${owner}/${projectsId}/milestones/${item.id}`} className="font-16">{item.name}</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="milepostdiv" style={{ marginTop: 5 }}>
|
||||
<div className="milepostrighe">
|
||||
<div className="grid-item mr10">
|
||||
<i className="iconfont icon-rili font-14 mr5"></i>
|
||||
<span className={item.effective_date ? "color-red" : "color-grey-c"}>{item.effective_date || "暂无截止时间"}</span>
|
||||
</div>
|
||||
<div className="grid-item mr10 color-grey-9">
|
||||
<i className="iconfont icon-issue font-14 mr5"></i>
|
||||
<span>{item.open_issues_count}个开启</span>
|
||||
</div>
|
||||
<div className="grid-item mr10 color-grey-9">
|
||||
<i className="iconfont icon-shanchudiao font-14 mr5"></i>
|
||||
<span>{item.close_issues_count}个关闭</span>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
data && data.user_admin_or_member ?
|
||||
<div className="milepostleft">
|
||||
<div className="grid-item ml15 color-grey-9">
|
||||
<i className="iconfont icon-bianji3 font-14 mr5"></i>
|
||||
<Link to={`/${owner}/${projectsId}/milestones/${item.id}/edit`} className="color-grey-9">编辑</Link>
|
||||
</div>
|
||||
<div className="grid-item ml15 color-grey-9">
|
||||
<i className={item.status === "closed" ? "iconfont icon-gouxuan font-14 mr5":"iconfont icon-yiguanbi1 font-14 mr5"}></i>
|
||||
<a onClick={() => this.updatestatusemile(item.status === "closed" ? "open" : "closed", item)} className="color-grey-9">{this.state.status === "closed" ? "开启" : "关闭"}</a>
|
||||
</div>
|
||||
<div className="grid-item ml15 color-grey-9">
|
||||
<i className="iconfont icon-lajitong font-14 mr5" ></i>
|
||||
<Popconfirm placement="bottom" title={'是否删除里程碑?'} okText="是" cancelText="否" onConfirm={() => this.closemile(item)}>
|
||||
<a className="color-grey-9">删除</a>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
<div className="milepostdiv" style={{ marginTop: 5 }}>
|
||||
<div className="textwidth">
|
||||
<Text type="secondary" ellipsis={{ rows: 30, expandable: false, onExpand: Function }} >{item.description}</Text>
|
||||
</div>
|
||||
</div>
|
||||
{/* 里程碑列表展示 */}
|
||||
{data && data.milestones && data.milestones.length === 0 && <div className="milestonesNoDate"><img src={emp} alt=""/></div>}
|
||||
{data && data.milestones && data.milestones.length > 0 && <div className='milepostList'>
|
||||
{data.milestones.map((item, key)=>{
|
||||
return <div className='flexSpaceBetween milepostItemBox'>
|
||||
<div>
|
||||
<div className="flexSpaceBetween">
|
||||
<i className="iconfont icon-lichengbeiicon1 font-12 mr10 primaryColor"></i>
|
||||
<Link to={`/${owner}/${projectsId}/milestones/${item.id}`} className="font-16 task-hide milepostInfo">{item.name}</Link>
|
||||
</div>
|
||||
{item.description && item.description.length > 44 ? <Tooltip title={item.description} placement={'topLeft'} overlayStyle={{width: '600px'}}><span className='color-grey-89 task-hide milepostInfo'>{item.description}</span></Tooltip> : <span className='color-grey-89 milepostInfo'>{item.description}</span>}
|
||||
</div>
|
||||
<div className="flexSpaceBetween actionMileBox">
|
||||
<div className="grid-item effectiveDate">
|
||||
<i className={`iconfont icon-a-31shijian font-15 mr10 ${item.effective_date ? "effectiveDate" : "color-grey-89"}`}></i>
|
||||
<span className={`${item.effective_date ? "" : "color-grey-89"} ${item.effective_date && status !== 'closed' && new Date().getTime()-new Date(`${item.effective_date} 24:0`).getTime() > 0 ? 'outTime' : ''}`}>{item.effective_date || "暂无截止时间"}</span>
|
||||
</div>
|
||||
<div className="mr10 effectiveDate">
|
||||
<span>{item.opened_issues_count || 0}个开启</span>
|
||||
<span className='color-grey-89'> | </span>
|
||||
<span>{item.close_issues_count || 0}个关闭</span>
|
||||
</div>
|
||||
{
|
||||
(isManager || isDeveloper) && <div className="flexSpaceBetween">
|
||||
<Link to={`/${owner}/${projectsId}/milestones/${item.id}/edit`} className="primaryColor"><i className="iconfont icon-a-bianji12 font-14 mr5 primaryColor"></i>编辑</Link>
|
||||
<div className="grid-item ml15">
|
||||
<i className={`effectiveDate iconfont font-14 mr5 ${item.status === "closed" ? "icon-gouxuan":"icon-shanchu8"}`}></i>
|
||||
<a onClick={() => this.updatestatusemile(item.status === "closed" ? "open" : "closed", item)} className="effectiveDate">{item.status === "closed" ? "开启" : "关闭"}</a>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
<div className="grid-item ml15">
|
||||
<i className="iconfont icon-fuzhi-shanchu font-14 mr5 colorRed" ></i>
|
||||
<Popconfirm placement="bottom" title={'是否删除里程碑?'} okText="是" cancelText="否" onConfirm={() => this.closemile(item)}>
|
||||
<a className="colorRed">删除</a>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
{ data && data.versions && data.versions.length === 0 && <NoneData _html="暂无里程碑"></NoneData> }
|
||||
})}
|
||||
</div>}
|
||||
{
|
||||
data && data.versions_count > limit ?
|
||||
<div className="mt30 mb50 edu-txt-center">
|
||||
<Pagination simple current={page} total={data && data.versions_count} pageSize={limit} onChange={this.ChangePage}></Pagination>
|
||||
data && data.total_count > limit ?
|
||||
<div className="mt30 mb50 edu-txt-right">
|
||||
<Pagination current={page} total={data && data.total_count} pageSize={limit} onChange={this.ChangePage} showQuickJumper showSizeChanger onShowSizeChange={this.onShowSizeChange}></Pagination>
|
||||
</div> : ""
|
||||
}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,267 @@
|
|||
import React , { useRef , useState , useEffect } from 'react';
|
||||
import { Menu, Input , Checkbox , Pagination, Button , Tooltip ,Spin } from 'antd';
|
||||
import { Link } from "react-router-dom";
|
||||
import AllMenus from './component/allMenus';
|
||||
import Datas from '../Issues/Component/datas';
|
||||
import axios from 'axios';
|
||||
import { FlexAJ } from '../Component/layout';
|
||||
import CheckProfile from '../Component/ProfileModal/Profile';
|
||||
import emp from '../../forge/Issues/Img/emp.png';
|
||||
import './milepost.scss';
|
||||
import './order.scss';
|
||||
import '../Issues/index.scss';
|
||||
|
||||
function MilepostDetail(props){
|
||||
const [ milepost, setMilepost] = useState(undefined);
|
||||
const [ issueList , setIssueList ] = useState(undefined);
|
||||
const [ closedCount , setClosedCount ] = useState(0);
|
||||
const [ openedCount , setOpenedCount ] = useState(0);
|
||||
|
||||
const[ aboutMe , setAboutMe ] = useState("aboutme");
|
||||
const[ keyword , setKeyword ] = useState(undefined);
|
||||
|
||||
const [ category , setCategory] = useState("all");
|
||||
|
||||
const [ allValue , setAllValue ] = useState([]);
|
||||
const [ allIds , setAllIds ] = useState([]);
|
||||
const [ checkAll , setCheckAll ] = useState(false);
|
||||
|
||||
const [ updateIds , setUpdateIds ] = useState([]);
|
||||
const [ limit, setLimit] = useState(10);
|
||||
const [ page , setPage ] = useState(1);
|
||||
const [ total , setTotal ] = useState(undefined);
|
||||
const [ issuesCount, setIssueCount] = useState(undefined);
|
||||
|
||||
const menuRef = useRef(null);
|
||||
const { projectsId, mileId , owner } = props.match.params;
|
||||
const {current_user, projectDetail, showLoginDialog, history} = props;
|
||||
const permission = props && props.projectDetail && props.projectDetail.permission;
|
||||
// 排序字段
|
||||
const sortBy = {
|
||||
'1': {'sort_by': 'issues.created_on', 'sort_direction': 'desc'},
|
||||
'2': {'sort_by': 'issues.created_on', 'sort_direction': 'asc'},
|
||||
'3': {'sort_by': 'issues.updated_on', 'sort_direction': 'desc'},
|
||||
'4': {'sort_by': 'issues.updated_on', 'sort_direction': 'asc'},
|
||||
'5': {'sort_by': 'issue_priorities.position', 'sort_direction': 'desc'},
|
||||
'6': {'sort_by': 'issue_priorities.position', 'sort_direction': 'asc'}
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
Init();
|
||||
},[aboutMe, keyword, category, page, limit])
|
||||
|
||||
useEffect(()=>{
|
||||
updateDocumentTitle()
|
||||
}, [projectDetail, milepost])
|
||||
|
||||
// 更新网页标题
|
||||
function updateDocumentTitle(){
|
||||
if(projectDetail && milepost){
|
||||
const { author, name} = projectDetail;
|
||||
document.title = `${milepost.name}-里程碑-${author.name}/${name}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取issue列表数据
|
||||
function Init(params){
|
||||
const url = `/v1/${owner}/${projectsId}/milestones/${mileId}.json`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
page,
|
||||
limit,
|
||||
category,
|
||||
...params
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
const {milestone, issues, total_count, closed_issues_count, opened_issues_count, total_issues_count} = result.data;
|
||||
if(issues.length === 0 && page>1){
|
||||
setPage(page-1);
|
||||
return;
|
||||
}
|
||||
setMilepost(milestone);
|
||||
setIssueList(issues);
|
||||
setTotal(total_count);
|
||||
setClosedCount(closed_issues_count);
|
||||
setOpenedCount(opened_issues_count);
|
||||
setIssueCount(total_issues_count)
|
||||
const ids = issues.length>0 ? issues.map(i=>{return i.id}) : [];
|
||||
setAllIds(ids);
|
||||
}
|
||||
}).then(error=>{console.log("error:",error)})
|
||||
}
|
||||
|
||||
// 第一个下拉搜索
|
||||
const menu = (
|
||||
<Menu selectedKeys={[`${aboutMe}`]} onClick={chooseAboutMe}>
|
||||
<Menu.Item key={"all"}>全部</Menu.Item>
|
||||
<Menu.Item key={"aboutme"}><Tooltip title="指我创建的、我负责的和@我的疑修">与我相关</Tooltip></Menu.Item>
|
||||
<Menu.Item key={"assignedme"}>我负责的</Menu.Item>
|
||||
<Menu.Item key={"authoredme"}>我创建的</Menu.Item>
|
||||
<Menu.Item key={"atme"}>@我的</Menu.Item>
|
||||
</Menu>
|
||||
)
|
||||
function chooseAboutMe(e){
|
||||
setCategory("all");
|
||||
setAboutMe(e.key);
|
||||
}
|
||||
|
||||
// 全选所有issue
|
||||
function chooseAll(e){
|
||||
setCheckAll(e.target.checked);
|
||||
if(e.target.checked){
|
||||
setAllValue(allIds);
|
||||
}else{
|
||||
setAllValue([]);
|
||||
}
|
||||
}
|
||||
|
||||
// 选择列表里的issue
|
||||
function checkIssues(value){
|
||||
setAllValue(value);
|
||||
if(value.length === allIds.length){
|
||||
setCheckAll(true);
|
||||
}else{
|
||||
setCheckAll(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 取消
|
||||
function cancelUpdate(){
|
||||
setAllValue([]);
|
||||
setCheckAll(false);
|
||||
// 清除下拉选项
|
||||
menuRef.current && menuRef.current.clearChoose();
|
||||
}
|
||||
|
||||
// 确认修改 allValue updateIds
|
||||
function sureUpdate(){
|
||||
const url = `/v1/${owner}/${projectsId}/issues/batch_update`;
|
||||
axios.patch(url,{
|
||||
assigner_ids: updateIds && updateIds.assigner_id && updateIds.assigner_id.split(","),
|
||||
ids: allValue,
|
||||
issue_tag_ids: updateIds && updateIds.issue_tag_ids && updateIds.issue_tag_ids.split(","),
|
||||
milestone_id: updateIds && updateIds.milestone_id,
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
setUpdateIds(undefined);
|
||||
setAllValue([]);
|
||||
setTotal(undefined);
|
||||
Init();
|
||||
cancelUpdate();
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
// 切换页码
|
||||
function changepage(page){
|
||||
setPage(page);
|
||||
}
|
||||
|
||||
function chooseFunc(ids){
|
||||
if(allValue && allValue.length>0){
|
||||
// 将ids保存下来以便修改
|
||||
setUpdateIds(ids);
|
||||
}else{
|
||||
const {sort_by} = ids;
|
||||
const params = {...ids, ...sortBy[sort_by]}
|
||||
Init(params);
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
<div className="main milepostDetail">
|
||||
{milepost && <FlexAJ>
|
||||
<div>
|
||||
<p className="font-17 font-bd color-black" style={{width: '900px'}}>{milepost.name}</p>
|
||||
<p className="mt7">
|
||||
<span className="mr20">
|
||||
<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> :"" }
|
||||
</p>
|
||||
</div>
|
||||
<div className="milepostdiv">
|
||||
{
|
||||
(current_user && current_user.login) && (permission && permission !== "Reporter") ?
|
||||
<Link to={`/${owner}/${projectsId}/milestones/${mileId}/edit`} className="grayButton" style={{ marginRight: 15 }}><i className="iconfont icon-lichengbeiicon1 font-12 mr5"></i>编辑里程碑</Link>
|
||||
:""
|
||||
}
|
||||
{/* {
|
||||
current_user && current_user.login ?
|
||||
<Button type='primary' onClick={() => {history.push(`/${owner}/${projectsId}/issues/${mileId}/new`)}} className='createMilepostBtn' style={{width: '83px'}}>+ 创建疑修</Button>
|
||||
:<Button type='primary' onClick={showLoginDialog} className='createMilepostBtn' style={{width: '83px'}}>+ 创建疑修</Button>
|
||||
} */}
|
||||
<CheckProfile {...props} sureFunc={()=>{history.push(`/${owner}/${projectsId}/issues/${mileId}/new`)}} checklogin className="operateButton ml20">+ 创建疑修</CheckProfile>
|
||||
</div>
|
||||
</FlexAJ>}
|
||||
<div className="lists mt25">
|
||||
<div className="listheader">
|
||||
<div style={{display:"flex"}}>
|
||||
<Checkbox value="all" style={{marginRight: "16px", display: (permission && permission !== "Reporter") ?"block":"none"}} checked={checkAll} onChange={chooseAll}></Checkbox>
|
||||
{
|
||||
allValue && allValue.length>0 ?
|
||||
<span>选择{allValue.length}个issue</span>
|
||||
:
|
||||
<ul className="statusul">
|
||||
<li className={category === "all" ?"active":""} onClick={()=>setCategory("all")}>全部<span>{issuesCount}</span></li>
|
||||
<li className={category === "opened" ?"active":""} onClick={()=>setCategory("opened")}>开启中<span>{openedCount}</span></li>
|
||||
<li className={category === "closed" ?"active":""} onClick={()=>setCategory("closed")}>已关闭<span>{closedCount}</span></li>
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
<div className="menusul">
|
||||
<AllMenus
|
||||
ref={menuRef}
|
||||
update={allValue && allValue.length>0}
|
||||
owner={owner}
|
||||
projectsId={projectsId}
|
||||
chooseFunc={chooseFunc}
|
||||
/>
|
||||
{
|
||||
allValue && allValue.length>0 ?
|
||||
<div>
|
||||
<Button type="primary" ghost onClick={sureUpdate}>确定</Button>
|
||||
<Button ghost className="ml10 mr10" onClick={cancelUpdate}>取消</Button>
|
||||
</div>
|
||||
:""
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
total === 0 && <div className="milestonesNoDate"><img src={emp} alt=""/></div>
|
||||
}
|
||||
{
|
||||
total > 0 &&
|
||||
<React.Fragment>
|
||||
<Checkbox.Group name="issues" onChange={checkIssues} value={allValue} style={{ width: "100%" }}>
|
||||
<div className="listdatas">
|
||||
{issueList.map((item,key)=>{
|
||||
return(
|
||||
<Datas
|
||||
key={key}
|
||||
checkbox={ <Checkbox value={item.id} key={item.id} style={{marginRight: "16px", display: (permission && permission !== "Reporter") ?"block":"none"}}></Checkbox> }
|
||||
item={item}
|
||||
owner={owner}
|
||||
projectsId={projectsId}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</Checkbox.Group>
|
||||
{
|
||||
total > 10 &&
|
||||
<div className="pt25 pb30" style={{textAlign:"right"}}>
|
||||
<Pagination total={total} pageSize={limit} current={page} onChange={changepage} showSizeChanger showQuickJumper onShowSizeChange={(current, pageSize)=>{setPage(1);setLimit(pageSize);}}/>
|
||||
</div>
|
||||
}
|
||||
</React.Fragment>
|
||||
}
|
||||
{total === undefined && <div style={{height:344,display:"flex",alignItems:"center",justifyContent:"center"}}><Spin /></div> }
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default MilepostDetail;
|
||||
|
|
@ -1,14 +1,15 @@
|
|||
import React, { Component } from "react";
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Dropdown, Menu, Icon, Pagination, Spin } from 'antd';
|
||||
import { Dropdown, Menu, Icon, Pagination, Spin, Button, Checkbox } from 'antd';
|
||||
import './order.scss';
|
||||
import { FlexAJ } from '../Component/layout';
|
||||
import CheckProfile from '../Component/ProfileModal/Profile';
|
||||
|
||||
import AllMenus from "../Issues/Component/allMenus";
|
||||
import NoneData from '../Nodata';
|
||||
import OrderItem from './OrderItem';
|
||||
|
||||
import axios from 'axios';
|
||||
import './milepost.scss';
|
||||
|
||||
/**
|
||||
* issue_chosen:下拉的筛选列表,
|
||||
|
|
@ -22,6 +23,7 @@ import axios from 'axios';
|
|||
* search_count:列表总条数
|
||||
* issue_type:搜索条件
|
||||
* status_type: issue的关闭和开启,1表示开启中的,2表示关闭的
|
||||
* category: issue状态: 全部、开启、关闭
|
||||
*/
|
||||
class MilepostDetail extends Component {
|
||||
constructor(props) {
|
||||
|
|
@ -47,7 +49,11 @@ class MilepostDetail extends Component {
|
|||
status_ids: "状态",
|
||||
done_ratios: '完成度',
|
||||
paix: '排序',
|
||||
issueFlag:true
|
||||
issueFlag:true,
|
||||
checkAll: false,
|
||||
allValue: [],
|
||||
allIds: [],
|
||||
category: 'all'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +61,6 @@ class MilepostDetail extends Component {
|
|||
this.getSelectList();
|
||||
const { page } = this.state;
|
||||
this.getIssueList(page);
|
||||
//
|
||||
}
|
||||
|
||||
componentDidUpdate=(prevProps)=>{
|
||||
|
|
@ -122,7 +127,7 @@ class MilepostDetail extends Component {
|
|||
const { projectsId, meilid , owner } = this.props.match.params;
|
||||
const { limit , order_name , order_type , issue_tag_id , author_id , assigned_to_id , tracker_id , status_id , done_ratio , status_type } = this.state;
|
||||
|
||||
const url = `/${owner}/${projectsId}/milestones/${meilid}.json`;
|
||||
const url = `/v1/${owner}/${projectsId}/milestones/${meilid}.json`;
|
||||
let params = update ? {
|
||||
page, limit , order_name:value , order_type:updateValue , issue_tag_id ,
|
||||
author_id , assigned_to_id , tracker_id , status_id , done_ratio,
|
||||
|
|
@ -138,7 +143,7 @@ class MilepostDetail extends Component {
|
|||
}).then((result) => {
|
||||
if (result) {
|
||||
this.setState({
|
||||
data: result.data,
|
||||
data: result.data.milestone,
|
||||
issues: result.data.issues,
|
||||
search_count: params.status_type ==="1" ? result.data.open_issues_count : result.data.close_issues_count,
|
||||
isSpin: false
|
||||
|
|
@ -211,7 +216,7 @@ class MilepostDetail extends Component {
|
|||
this.getIssueList(page);
|
||||
}
|
||||
|
||||
openorder = (type) => {
|
||||
category = (type) => {
|
||||
this.setState({
|
||||
status_type: type,
|
||||
issue_tag_id : undefined,
|
||||
|
|
@ -233,9 +238,18 @@ class MilepostDetail extends Component {
|
|||
|
||||
}
|
||||
|
||||
// 全选所有issue
|
||||
chooseAll = (e) =>{
|
||||
const {allIds} = this.state;
|
||||
this.setState({
|
||||
checkAll: e.target.checked,
|
||||
allValue: e.target.checked ? allIds : []
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
const { issue_chosen, issues, limit, page, search_count, data, isSpin , status_type , issueFlag } = this.state;
|
||||
const { issue_chosen, issues, limit, page, search_count, data, isSpin , status_type , issueFlag, checkAll, allValue, category } = this.state;
|
||||
const { projectsId, meilid ,owner} = this.props.match.params;
|
||||
const { current_user , showLoginDialog , projectDetail } = this.props;
|
||||
const menu = (
|
||||
|
|
@ -247,42 +261,74 @@ class MilepostDetail extends Component {
|
|||
</Menu>
|
||||
)
|
||||
return (
|
||||
<div className="main" style={{padding:"0px"}}>
|
||||
<div className="miledetail">
|
||||
<p className="font-20">{data && data.name}</p>
|
||||
<FlexAJ>
|
||||
<span className="mt7">
|
||||
<span className="mr10">
|
||||
<i className="iconfont icon-rili font-14 mr5">
|
||||
<div className="main milepostDetail">
|
||||
<FlexAJ>
|
||||
<div>
|
||||
<p className="font-17 font-bd color-black" style={{width: '900px'}}>{data && data.name}</p>
|
||||
<p className="mt7">
|
||||
<span className="mr20">
|
||||
<i className="iconfont icon-a-31shijian font-14 mr5 color-grey-89">
|
||||
</i>
|
||||
{
|
||||
data && data.effective_date ?
|
||||
<span >{data && data.effective_date}</span>
|
||||
<span className="color-grey-89">{data && data.effective_date}</span>
|
||||
:
|
||||
<span >暂无截止时间</span>
|
||||
<span className="color-grey-89">暂无截止时间</span>
|
||||
}
|
||||
</span>
|
||||
{data && (data.percent || data.percent===0) ? <span className="font-weight-bold"> {data.percent > 0 ? data.percent.toFixed(2):data.percent}%完成 </span> :"" }
|
||||
</span>
|
||||
<div className="milepostdiv">
|
||||
{data && (data.percent || data.percent===0) ? <span style={{color: '#1aaf42'}}> {data.percent > 0 ? data.percent.toFixed(2):data.percent}%完成 </span> :"" }
|
||||
</p>
|
||||
</div>
|
||||
<div className="milepostdiv">
|
||||
{
|
||||
(current_user && current_user.login) && ( projectDetail && projectDetail.permission && projectDetail.permission !== "Reporter") ?
|
||||
<Link to={`/${owner}/${projectsId}/milestones/${meilid}/edit`} className="grayButton" style={{ marginRight: 15 }}><i className="iconfont icon-lichengbeiicon1 font-12 mr5"></i>编辑里程碑</Link>
|
||||
:""
|
||||
}
|
||||
{
|
||||
issueFlag ? (current_user && current_user.login ?
|
||||
<Button type='primary' onClick={() => {this.props.history.push(`/${owner}/${projectsId}/issues/${meilid}/new`)}} className='createMilepostBtn' style={{width: '83px'}}>+ 创建疑修</Button>
|
||||
:<Button type='primary' onClick={showLoginDialog} className='createMilepostBtn' style={{width: '83px'}}>+ 创建疑修</Button>)
|
||||
:""
|
||||
}
|
||||
</div>
|
||||
</FlexAJ>
|
||||
|
||||
<Spin spinning={isSpin}>
|
||||
<div className="listheader">
|
||||
<div style={{display:"flex"}}>
|
||||
<Checkbox value="all" style={{marginRight: "16px"}} checked={checkAll} onChange={this.chooseAll}></Checkbox>
|
||||
{
|
||||
(current_user && current_user.login) && ( projectDetail && projectDetail.permission && projectDetail.permission !== "Reporter") ?
|
||||
<Link to={`/${owner}/${projectsId}/milestones/${meilid}/edit`} className="topWrapper_btn" style={{ marginRight: 15 }} >编辑里程碑</Link>
|
||||
:""
|
||||
}
|
||||
{
|
||||
issueFlag ? (current_user && current_user.login ?
|
||||
<CheckProfile {...this.props} sureFunc={()=>{this.props.history.push(`/${owner}/${projectsId}/issues/${meilid}/new`)}} className="topWrapper_btn">创建疑修</CheckProfile>
|
||||
allValue && allValue.length>0 ?
|
||||
<span>选择{allValue.length}个issue</span>
|
||||
:
|
||||
<a className="topWrapper_btn" onClick={showLoginDialog}>创建疑修</a>
|
||||
):""
|
||||
<ul className="statusul">
|
||||
<li className={category === "all" ?"active":""} onClick={() => this.category("all")}>全部<span>{total}</span></li>
|
||||
<li className={category === "opened" ?"active":""} onClick={() => this.category("opened")}>开启中<span>{openedCount}</span></li>
|
||||
<li className={category === "closed" ?"active":""} onClick={() => this.category("closed")}>已关闭<span>{closedCount}</span></li>
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
</FlexAJ>
|
||||
</div>
|
||||
|
||||
<Spin spinning={isSpin}>
|
||||
<div className="pl20 pr20">
|
||||
<div className="menusul">
|
||||
<AllMenus
|
||||
ref={menuRef}
|
||||
update={allValue && allValue.length>0}
|
||||
owner={owner}
|
||||
projectsId={projectsId}
|
||||
chooseFunc={chooseFunc}
|
||||
/>
|
||||
{
|
||||
allValue && allValue.length>0 ?
|
||||
<div>
|
||||
<Button type="primary" ghost onClick={sureUpdate}>确定</Button>
|
||||
<Button type="danger" ghost className="ml10" onClick={()=>setVisible(true)}>删除</Button>
|
||||
<Button ghost className="ml10 mr10" onClick={cancelUpdate}>取消</Button>
|
||||
</div>
|
||||
:""
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className="pl20 pr20">
|
||||
<div className="f-wrap-between pt15 pb15 bor-bottom-greyE">
|
||||
<ul className="topWrapper_type_infos">
|
||||
<li className={status_type==="1" ? "active" : ""} onClick={() => this.openorder("1")}>{data && data.open_issues_count}个开启中</li>
|
||||
|
|
@ -352,7 +398,7 @@ class MilepostDetail extends Component {
|
|||
<Pagination simple current={page} total={search_count} pageSize={limit} onChange={this.ChangePage}></Pagination>
|
||||
</div>:""
|
||||
}
|
||||
</div>
|
||||
</div> */}
|
||||
</Spin>
|
||||
</div>
|
||||
)
|
||||
|
|
@ -92,8 +92,7 @@ class UpdateMilepost extends Component {
|
|||
}).then(result => {
|
||||
if (result) {
|
||||
this.setState({ isSpin: false })
|
||||
this.props.history.push(`/${owner}/${projectsId}/milestones`);
|
||||
|
||||
this.props.history.push(`/${owner}/${projectsId}/milestones/${meilid}`);
|
||||
}
|
||||
|
||||
}).catch(error => {
|
||||
|
|
@ -133,7 +132,7 @@ class UpdateMilepost extends Component {
|
|||
required: true, message: '请输入标题'
|
||||
}],
|
||||
})(
|
||||
<Input placeholder="标题" />
|
||||
<Input placeholder="标题" maxLength={30}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
|
@ -144,8 +143,7 @@ class UpdateMilepost extends Component {
|
|||
required: true, message: '请输入描述内容'
|
||||
}],
|
||||
})(
|
||||
<TextArea placeholder="请输入描述内容..." style={{ height: "150px" }} />
|
||||
|
||||
<TextArea placeholder="请输入描述内容..." style={{ height: "150px" }} maxLength={500}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,155 @@
|
|||
import React,{ useState , useEffect , forwardRef ,useImperativeHandle , useRef } from 'react';
|
||||
import Menus from '../../Issues/Component/menus';
|
||||
import axios from 'axios';
|
||||
|
||||
const array =[
|
||||
{id:1,name:"最新创建"},
|
||||
{id:2,name:"最早创建"},
|
||||
{id:3,name:"最新更新"},
|
||||
{id:4,name:"最早更新"},
|
||||
{id:5,name:"高优先级"},
|
||||
{id:6,name:"低优先级"}
|
||||
]
|
||||
|
||||
function AllMenus({owner,projectsId,chooseFunc,update},ref){
|
||||
// 列表右侧所有筛选项的值
|
||||
const [ authorList , setAuthorList ] = useState(undefined);
|
||||
const [ author , setAuthor ] = useState(undefined);
|
||||
const [ tagList , setTagList ] = useState(undefined);
|
||||
const [ tag , setTag ] = useState(undefined);
|
||||
const [ chargeList , setChargeList ] = useState(undefined);
|
||||
const [ charge , setCharge ] = useState(undefined);
|
||||
const [ millstone , setMillstone ] = useState(undefined);
|
||||
const [ millstoneList , setMillstoneList ] = useState(undefined);
|
||||
const [ ids , setIds ] = useState({author_id:undefined,issue_priorities_id:undefined,issue_tag_ids:undefined,milestone_id:undefined,sort_by:undefined,status_id:undefined,assigner_id:undefined});
|
||||
const [ names , setNames ] = useState({author_name:undefined,issue_priorities_name:undefined,issue_tag_name:undefined,milestone_name:undefined,sortby_name:undefined,status_name:undefined,assigner_name:undefined});
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
clearChoose: () => {
|
||||
// 父组件按钮:清除筛选条件,将Ids\names里的字段全部改为undefined
|
||||
setIds({author_id:undefined,issue_tag_ids:undefined,issue_priorities_id:undefined,milestone_id:undefined,sort_by:undefined,status_id:undefined,assigner_id:undefined});
|
||||
setNames({author_name:undefined,issue_tag_name:undefined,issue_priorities_name:undefined,milestone_name:undefined,sortby_name:undefined,status_name:undefined,assigner_name:undefined});
|
||||
}
|
||||
}))
|
||||
|
||||
// 获取发布人列表
|
||||
useEffect(()=>{
|
||||
getSendPerson();
|
||||
},[author])
|
||||
|
||||
function getSendPerson(){
|
||||
const url = `/v1/${owner}/${projectsId}/issue_authors`;
|
||||
axios.get(url,{params:{keyword:author,only_name:true}}).then(result=>{
|
||||
if(result && result.data){
|
||||
setAuthorList(result.data.authors);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取标记列表
|
||||
useEffect(()=>{
|
||||
getSign();
|
||||
},[tag])
|
||||
|
||||
function getSign(){
|
||||
const url = `/v1/${owner}/${projectsId}/issue_tags`;
|
||||
axios.get(url,{params:{keyword:tag,only_name:true}}).then(result=>{
|
||||
if(result && result.data){
|
||||
setTagList(result.data.issue_tags);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 获取负责人列表
|
||||
useEffect(()=>{
|
||||
getCharge();
|
||||
},[charge,update])
|
||||
|
||||
function getCharge(){
|
||||
if(update){
|
||||
const url = `/v1/${owner}/${projectsId}/collaborators`;
|
||||
axios.get(url,{params:{keyword:charge,only_name:true}}).then(result=>{
|
||||
if(result && result.data){
|
||||
setChargeList(result.data.collaborators);
|
||||
}
|
||||
})
|
||||
}else{
|
||||
const url = `/v1/${owner}/${projectsId}/issue_assigners`;
|
||||
axios.get(url,{params:{keyword:charge,only_name:true}}).then(result=>{
|
||||
if(result && result.data){
|
||||
setChargeList(result.data.assigners);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function choose(id,name){
|
||||
let copy = {...ids,author_id:id && id.length>0 ? id.join(","):undefined};
|
||||
let copyname = { ...names,author_name:name};
|
||||
setIds(copy);setNames(copyname);
|
||||
chooseFunc(copy,copyname);
|
||||
}
|
||||
|
||||
// 获取里程碑列表
|
||||
useEffect(()=>{
|
||||
getMillstone();
|
||||
},[millstone])
|
||||
|
||||
function getMillstone(){
|
||||
const url = `/v1/${owner}/${projectsId}/milestones`;
|
||||
axios.get(url,{params:{keyword:millstone,only_name:true}}).then(result=>{
|
||||
if(result && result.data){
|
||||
setMillstoneList(result.data.milestones);
|
||||
}
|
||||
})
|
||||
}
|
||||
return(
|
||||
<ul className="dropboxul">
|
||||
{ !update && <Menus
|
||||
update={update}
|
||||
ids={ids && ids.author_id}
|
||||
names={names && names.author_name}
|
||||
name={"发布人" } size={"large"} imgControl
|
||||
lists={authorList} searchFunc={(value)=>setAuthor(value)}
|
||||
chooseFunc={(id,name)=>choose(id,name,'author_name')}
|
||||
/>
|
||||
}
|
||||
<Menus
|
||||
update={update}
|
||||
ids={ids && ids.issue_tag_ids}
|
||||
name={"标记"} size={"large"}
|
||||
double
|
||||
names={names && names.issue_tag_name}
|
||||
lists={tagList} searchFunc={(value)=>setTag(value)}
|
||||
chooseFunc={(id,name)=>{let copy = {...ids,issue_tag_ids:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,issue_tag_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}}
|
||||
/>
|
||||
<Menus
|
||||
update={update}
|
||||
ids={ids && ids.assigner_id}
|
||||
name={"负责人"} size={"large"} imgControl
|
||||
names={names && names.assigner_name}
|
||||
lists={chargeList} searchFunc={(value)=>setCharge(value)}
|
||||
double={update ? true : false }
|
||||
chooseFunc={(id,name)=>{let copy = {...ids,assigner_id:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,assigner_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}}
|
||||
/>
|
||||
{ !update && <Menus
|
||||
ids={ids && ids.sort_by}
|
||||
name={"排序"} size={"small"}
|
||||
lists={array}
|
||||
names={names && names.sortby_name}
|
||||
chooseFunc={(id,name)=>{let copy = {...ids,sort_by:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,sortby_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}}
|
||||
/>
|
||||
}
|
||||
{update && <Menus
|
||||
update={update}
|
||||
ids={ids && ids.milestone_id}
|
||||
name={"里程碑"} size={"large"}
|
||||
names={names && names.milestone_name}
|
||||
lists={millstoneList} searchFunc={(value)=>setMillstone(value)}
|
||||
chooseFunc={(id,name)=>{let copy = {...ids,milestone_id:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,milestone_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}}
|
||||
/>}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
export default forwardRef(AllMenus);
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
.milepostBox{
|
||||
border: none;
|
||||
padding: 0;
|
||||
.createMilepost{
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
.flexSpaceBetween{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.milepostHead{
|
||||
background-color:#fafcff;
|
||||
border:1px solid rgba(42, 97, 255, 0.23);
|
||||
color:#898d9d;
|
||||
padding: 15px 15px 15px 20px;
|
||||
border-radius:4px 4px 0px 0px;
|
||||
.postStatus.active{
|
||||
color:$primary-color;
|
||||
.statusCount{
|
||||
color: $primary-color;
|
||||
border: 1px solid $primary-color;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
.statusCount{
|
||||
background-color:rgba(70, 106, 255, 0.09);
|
||||
border-radius:10px;
|
||||
margin-left: 5px;
|
||||
color:#666666;
|
||||
font-weight: normal;
|
||||
height: 19px;
|
||||
line-height: 19px;
|
||||
display: inline-block;
|
||||
min-width: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.pointBox{
|
||||
cursor: pointer;
|
||||
}
|
||||
.milepostSort{
|
||||
box-shadow:0px 0px 10px rgba(24, 54, 181, 0.17);
|
||||
}
|
||||
.milepostList{
|
||||
border-left: 1px solid #d0d0d0;
|
||||
border-right: 1px solid #d0d0d0;
|
||||
.milepostItemBox{
|
||||
border-bottom: 1px solid #d0d0d0;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
.milepostInfo{
|
||||
display: inline-block;
|
||||
width: 640px;
|
||||
}
|
||||
.actionMileBox{
|
||||
width: 550px;
|
||||
.outTime{
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
.createMilepostBtn{
|
||||
width: 96px;
|
||||
padding: 0;
|
||||
}
|
||||
.primaryColor, .primaryColor:link, .flexSpaceBetween .primaryColor{
|
||||
color: $primary-color;
|
||||
}
|
||||
.effectiveDate, .flexSpaceBetween .effectiveDate{
|
||||
color:#40424a;
|
||||
}
|
||||
.color-grey-89{
|
||||
color: #898d9d;
|
||||
}
|
||||
.colorRed, .colorRed:hover{
|
||||
color:#f30000;
|
||||
}
|
||||
.milestonesNoDate{
|
||||
border:1px solid#d0d0d0;
|
||||
border-top: none;
|
||||
text-align: center;
|
||||
min-height: 500px;
|
||||
line-height: 500px;
|
||||
}
|
||||
// 浅灰色按钮
|
||||
.grayButton{
|
||||
width:108px;
|
||||
height:32px;
|
||||
text-align: center;
|
||||
background-color:#fafbfc;
|
||||
border:1px solid #d0d0d0;
|
||||
border-radius:4px;
|
||||
&:hover{
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
}
|
||||
|
||||
// 里程碑详情页面
|
||||
.milepostDetail{
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
.noDataMile{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
|
@ -29,14 +29,6 @@
|
|||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.miledetail {
|
||||
padding:15px 20px;
|
||||
box-sizing: border-box;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #eeeeee;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.topWrapper_nav {
|
||||
display: flex;
|
||||
}
|
||||
|
|
@ -232,6 +224,15 @@
|
|||
border-bottom: 1px solid #eee;
|
||||
padding: 16px 0px 16px 20px;
|
||||
}
|
||||
.issueIndex{
|
||||
display: inline-block;
|
||||
height:19px;
|
||||
line-height:19px;
|
||||
background-color:rgba(213, 220, 246, 0.36);
|
||||
border-radius:4px;
|
||||
padding:0px 8px;
|
||||
color: #666!important;
|
||||
}
|
||||
.issueItem:last-child{
|
||||
border-bottom: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import React , { forwardRef, useState, useEffect } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Button, Form , Input } from 'antd';
|
||||
import { Form , Input } from 'antd';
|
||||
import Axios from 'axios';
|
||||
|
||||
const { TextArea } = Input;
|
||||
function New({ form , showNotification , history }) {
|
||||
function New({ form , showNotification , history },ref) {
|
||||
const { getFieldDecorator, validateFields , setFieldsValue } = form;
|
||||
const [ msg , setMsg ] = useState(undefined);
|
||||
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ class Index extends Component {
|
|||
</Link>
|
||||
</p>
|
||||
</li>
|
||||
<li
|
||||
{/* <li
|
||||
className={pathname.indexOf("settings/labels") > -1 ? "active" : ""}
|
||||
>
|
||||
<p>
|
||||
|
|
@ -110,6 +110,7 @@ class Index extends Component {
|
|||
项目标记
|
||||
</Link>
|
||||
</p>
|
||||
<<<<<<< HEAD
|
||||
</li>
|
||||
<li
|
||||
className={pathname.indexOf("settings/softbot") > -1 ? "active" : ""}
|
||||
|
|
@ -121,6 +122,9 @@ class Index extends Component {
|
|||
</Link>
|
||||
</p>
|
||||
</li>
|
||||
=======
|
||||
</li> */}
|
||||
>>>>>>> cff2301204b3e1748e9bb52f996780c35b38cd0f
|
||||
|
||||
{/* <li
|
||||
className={
|
||||
|
|
@ -165,13 +169,13 @@ class Index extends Component {
|
|||
<Collaborator {...this.props} {...props} {...this.state} />
|
||||
)}
|
||||
></Route>
|
||||
{/* 修改仓库信息 */}
|
||||
<Route
|
||||
{/* 修改仓库标记 */}
|
||||
{/* <Route
|
||||
path="/:owner/:projectsId/settings/labels"
|
||||
render={(props) => (
|
||||
<Tags {...this.props} {...props} {...this.state} />
|
||||
)}
|
||||
></Route>
|
||||
></Route> */}
|
||||
<Route
|
||||
path="/:owner/:projectsId/settings/branches/:branch"
|
||||
render={(props) => (
|
||||
|
|
|
|||
|
|
@ -12,6 +12,19 @@ class Index extends Component {
|
|||
}
|
||||
}
|
||||
componentDidMount=()=>{
|
||||
const {defaultFileList} = this.props;
|
||||
if(defaultFileList){
|
||||
const files = [];
|
||||
defaultFileList.map(i=>{
|
||||
files.push({
|
||||
uid: i.id,
|
||||
name: i.title,
|
||||
status: 'done',
|
||||
url: i.url
|
||||
})
|
||||
})
|
||||
this.setState({fileList: files})
|
||||
}
|
||||
this.checkInitFile();
|
||||
}
|
||||
componentDidUpdate=(prevProps)=>{
|
||||
|
|
@ -70,10 +83,12 @@ class Index extends Component {
|
|||
|
||||
fileIdList = (fileList) => {
|
||||
let array = [];
|
||||
fileList && fileList.length > 0 && fileList.map((item) => {
|
||||
return array.push(item.response && (item.response.id || (item.response.data && item.response.data.id)));
|
||||
})
|
||||
array && this.props.load && this.props.load(array);
|
||||
if(fileList && fileList.length > 0){
|
||||
fileList.map((item) => {
|
||||
return item.response && item.status==="done" && array.push((item.response.id || (item.response.data && item.response.data.id)));
|
||||
})
|
||||
array && array.length>0 && this.props.load(array);
|
||||
}
|
||||
}
|
||||
|
||||
checkFile=(str)=>{
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ class Attachment extends Component {
|
|||
};
|
||||
|
||||
deleteAttachment = (id) => {
|
||||
const { deleteLoad } = this.props;
|
||||
const url = `/attachments/${id}.json`;
|
||||
axios
|
||||
.delete(url, {})
|
||||
|
|
@ -86,6 +87,7 @@ class Attachment extends Component {
|
|||
Deleted: this.state.Deleted.concat(id),
|
||||
});
|
||||
this.props.showNotification("附件删除成功");
|
||||
deleteLoad && deleteLoad(id);
|
||||
} else {
|
||||
this.props.showNotification(response.data.message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@
|
|||
|
||||
.menuPanels{
|
||||
width: 295px;
|
||||
.ant-popover-arrow{
|
||||
bottom: 10.2px!important;
|
||||
}
|
||||
&.maxWidth{
|
||||
width: 400px;
|
||||
}
|
||||
|
|
@ -60,11 +63,13 @@
|
|||
flex-wrap: wrap;
|
||||
padding-bottom: 2px;
|
||||
a{
|
||||
margin: 0px 10px 10px 0px;
|
||||
margin: 0px 2px 0px 0px;
|
||||
height: 22px;
|
||||
line-height: 20px;
|
||||
img{
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
&:nth-child(4n){
|
||||
margin-right: 0px;
|
||||
|
|
|
|||
|
|
@ -173,21 +173,21 @@ class claims extends React.Component {
|
|||
const renderClaim = () => {
|
||||
return (
|
||||
<div className="claimNote_input" style={{ width: "400" }}>
|
||||
<div className="mb5"> 声明留言:</div>
|
||||
<div className="mb10"> 声明留言:</div>
|
||||
<TextArea
|
||||
rows={6}
|
||||
rows={4}
|
||||
type="text"
|
||||
placeholder="例如可以留言你的解决思路......"
|
||||
onChange={this.changeClaimNote}
|
||||
value={claimNoteBody}
|
||||
className={claimNoteBodyError ? 'error' : ''}
|
||||
/>
|
||||
<div className="mt10" style={{color: 'red'}}>{claimNoteBodyError}</div>
|
||||
<div style={{ display: "flex", justifyContent: "center" }} className="mt10">
|
||||
<Button onClick={() => this.cancelClaim()}>取 消</Button>
|
||||
<div style={{color: 'red',height:"20px",lineHeight:"20px"}}>{claimNoteBodyError}</div>
|
||||
<div style={{ display: "flex", justifyContent: "center" }}>
|
||||
<Button style={{width:"67px" }} onClick={() => this.cancelClaim()}>取 消</Button>
|
||||
<Button
|
||||
onClick={() => this.yesClaim()}
|
||||
style={{ marginLeft: "20px" }}
|
||||
style={{ marginLeft: "20px",width:"67px" }}
|
||||
type={"primary"}
|
||||
>
|
||||
确 认
|
||||
|
|
@ -211,26 +211,19 @@ class claims extends React.Component {
|
|||
className="radius"
|
||||
src={getImageUrl(`/${item && item.user_picture}`)}
|
||||
alt=""
|
||||
width="30"
|
||||
height="30"
|
||||
width="46"
|
||||
height="46"
|
||||
/>
|
||||
</Link>
|
||||
<Link
|
||||
to={`/${item && item.user_login}`}
|
||||
className="show-user-link color-black ml10 fwb"
|
||||
className="show-user-link color-black ml10"
|
||||
>
|
||||
<div>
|
||||
{current_user.login == item.user_login ? (
|
||||
<div>你</div>
|
||||
) : (
|
||||
<div>{item && item.user_name}</div>
|
||||
)}
|
||||
<div className="font-16" style={{fontWeight:500,color:"#1e1d1d"}}>{current_user.login === item.user_login ? "你": item.user_name} </div>
|
||||
<span className="color-grey-8 font-12"> 声明于 {item.created_at}</span>
|
||||
</div>
|
||||
</Link>
|
||||
<div style={{ marginLeft: "10px" }}>
|
||||
{" "}
|
||||
<span className="color-grey-8"> 声明于 {item.created_at}</span>
|
||||
</div>
|
||||
</AlignCenter>
|
||||
</FlexAJ>
|
||||
<div className="claimNote">
|
||||
|
|
@ -245,6 +238,7 @@ class claims extends React.Component {
|
|||
justifyContent: "center",
|
||||
paddingBottom: "10px",
|
||||
paddingTop: "10px",
|
||||
width:"83px"
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
|
|
@ -293,8 +287,6 @@ class claims extends React.Component {
|
|||
</Button>}
|
||||
</Popover>
|
||||
|
||||
<div className="ant-divider ant-divider-horizontal"> </div>
|
||||
|
||||
{claimerData && claimerData.length > 0 ? (
|
||||
<div>
|
||||
<div className="mt15">
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ ul,ol,dl{
|
|||
.markdown-body{
|
||||
overflow: inherit;
|
||||
}
|
||||
.markdown-body .editormd-tex .katex-display>.katex{
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.markdown-body a{
|
||||
color: #466AFF;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export default withRouter(
|
|||
secondRouter = props.location.pathname.split('/')[2];
|
||||
}
|
||||
|
||||
let userRouterArr = ['statistics', 'projects', 'notice', 'devops', 'organizes', 'info', 'following', 'followers', 'password' , "general"];
|
||||
let userRouterArr = ['statistics', 'projects', 'notice', 'devops', 'organizes', 'info', 'following', 'followers', 'password' , "general", "blockchain"];
|
||||
return (
|
||||
<Switch>
|
||||
|
||||
|
|
|
|||
|
|
@ -278,7 +278,7 @@ ul.ant-menu.menuStyle{
|
|||
.infosRightMenu{
|
||||
.ant-menu-item{
|
||||
padding:0px;
|
||||
margin:0px 30px 0px 0px !important;
|
||||
margin:0px 17px 0px 0px !important;
|
||||
font-size: 17px;
|
||||
height: 32px;
|
||||
line-height: 0px;
|
||||
|
|
|
|||
|
|
@ -63,6 +63,11 @@ const Personal = Loadable({
|
|||
loading: Loading,
|
||||
})
|
||||
|
||||
// 贡献确权
|
||||
const Contribution = Loadable({
|
||||
loader: () => import('./contribution/index'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
class Infos extends Component {
|
||||
constructor(props) {
|
||||
|
|
@ -108,6 +113,8 @@ class Infos extends Component {
|
|||
this.setState({menuKey:undefined,route_type:"following"});
|
||||
}else if(pathname === `/${username}/followers`){
|
||||
this.setState({menuKey:undefined,route_type:"followers"});
|
||||
}else if(pathname === `/${username}/blockchain`){
|
||||
this.setState({menuKey:'7',route_type:undefined});
|
||||
}else{
|
||||
this.setState({menuKey:"6",route_type:undefined});
|
||||
}
|
||||
|
|
@ -394,6 +401,10 @@ class Infos extends Component {
|
|||
{ user && user.user_org_count && user.user_org_count > 0 ? <span className="menuNum">({user.user_org_count})</span>:""}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
{/* 当前用户+配置了贡献确权 */}
|
||||
{current_user && user && user.login === current_user.login && current_user.open_blockchain && <Menu.Item key="7">
|
||||
<Link to={`/${user && user.login}/blockchain`}><i className="iconfont icon-zuzhi"></i>贡献确权</Link>
|
||||
</Menu.Item>}
|
||||
</Menu>
|
||||
}
|
||||
{user && (
|
||||
|
|
@ -440,6 +451,12 @@ class Infos extends Component {
|
|||
return <Organize {...this.props} {...this.state} />;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/:username/blockchain"
|
||||
render={() => {
|
||||
return <Contribution {...this.props} {...this.state} />;
|
||||
}}
|
||||
></Route>
|
||||
{/* <Route
|
||||
path="/:username/info"
|
||||
render={() => {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export default Form.create()(
|
|||
function checkPsd(rule, value, callback){
|
||||
const map = passMap;
|
||||
if(value){
|
||||
current_user && Axios.post(`/v1/${current_user.login}/check_password.json`,{
|
||||
current_user && value.length > 7 && Axios.post(`/v1/${current_user.login}/check_password.json`,{
|
||||
password: value
|
||||
}).then(res=>{
|
||||
if(res && !res.data.status){
|
||||
|
|
@ -50,7 +50,7 @@ export default Form.create()(
|
|||
callback("请先输入登录密码");
|
||||
}
|
||||
if(value){
|
||||
current_user && value && value.length > 4 && Axios.post(`/v1/${current_user.login}/check_phone_verify_code.json`,{
|
||||
current_user && value && value.length > 5 && Axios.post(`/v1/${current_user.login}/check_phone_verify_code.json`,{
|
||||
code_type: 4,
|
||||
phone: phoneValue,
|
||||
code: value
|
||||
|
|
@ -192,7 +192,7 @@ export default Form.create()(
|
|||
{required:true,message:"请输入登录密码"},
|
||||
{validator:(rule, value, callback)=>checkPsd(rule, value, callback)}
|
||||
],
|
||||
validateTrigger:"onBlur",
|
||||
validateTrigger:"onChange",
|
||||
})(
|
||||
<Input.Password placeholder="请输入登录密码" autoComplete={"new-password"} style={{width:"400px"}}/>
|
||||
)}
|
||||
|
|
@ -203,7 +203,7 @@ export default Form.create()(
|
|||
{required:true,message:"请输入新手机号码"},
|
||||
{validator:(rule, value, callback)=>checkPhone(rule, value, callback)}
|
||||
],
|
||||
validateTrigger:"onBlur",
|
||||
validateTrigger:"onChange",
|
||||
})(
|
||||
<Input placeholder="请输入新手机号码" style={{width:"400px"}} autoComplete={"off"}/>
|
||||
)}
|
||||
|
|
@ -215,7 +215,7 @@ export default Form.create()(
|
|||
{required:true,message:"请输入短信验证码"},
|
||||
{validator:(rule, value, callback)=>checkCode(rule, value, callback)}
|
||||
],
|
||||
validateTrigger:"onBlur",
|
||||
validateTrigger:"onChange",
|
||||
})(
|
||||
<Input placeholder="请输入短信验证码" style={{width:"400px"}}/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,170 @@
|
|||
|
||||
import React , { useEffect , useState } from 'react';
|
||||
import { Button, Dropdown, Input, Menu , message, Pagination } from 'antd';
|
||||
import Search from '../../Component/Search';
|
||||
import Item from '../Team-item';
|
||||
import Nodata from '../../Nodata';
|
||||
import axios from 'axios';
|
||||
import CheckProfile from '../../Component/ProfileModal/Profile';
|
||||
import './index.scss';
|
||||
import SearchUser from '../../Component/SearchUser';
|
||||
import { FlexAJ } from '../../Component/layout';
|
||||
|
||||
// 个人中心-贡献确权
|
||||
function Contribution(props){
|
||||
const limit = 10;
|
||||
const [page, setPage] = useState(1);
|
||||
const [total, setTotal] = useState(undefined);
|
||||
// 转账列表
|
||||
const [reload, setReload] = useState(undefined);
|
||||
const [list, setList] = useState(undefined);
|
||||
// 选项 1 转账 0 记录
|
||||
const [ type, setType] = useState(1);
|
||||
// 目标转账用户id/login
|
||||
const [userLogin, setUserLogin] = useState({});
|
||||
const [error1, setError1] = useState(undefined);
|
||||
// 转账数值
|
||||
const [tokenCount, setTokenCount] = useState({});
|
||||
const [error2, setError2] = useState(undefined);
|
||||
// 操作仓库id
|
||||
const [proId, setProId] = useState(undefined);
|
||||
const {current_user:{user_id, login, open_blockchain}, current_user} = props;
|
||||
const {user} = props;
|
||||
const username = props.match.params.username;
|
||||
|
||||
useEffect(()=>{
|
||||
if((username && current_user && (current_user.login !== username)) || !open_blockchain){
|
||||
props.history.push(`/${username}`);
|
||||
}
|
||||
},[current_user,username])
|
||||
|
||||
useEffect(()=>{
|
||||
// token转账列表
|
||||
axios.get(`/users/blockchain/balance.json`,{params: {
|
||||
user_id,
|
||||
limit,
|
||||
page
|
||||
}}).then(res=>{
|
||||
if(res && res.data){
|
||||
const {projects} = res.data;
|
||||
const newProjects = [];
|
||||
let deleteProjectCount = 0;
|
||||
projects.map(item=>{
|
||||
if(item.id){
|
||||
newProjects.unshift(item);
|
||||
}else{
|
||||
deleteProjectCount++;
|
||||
}
|
||||
})
|
||||
deleteProjectCount && newProjects.push({
|
||||
'balance': deleteProjectCount
|
||||
})
|
||||
setList(newProjects);
|
||||
setTotal(res.data.total_count);
|
||||
}
|
||||
})
|
||||
}, [reload, page])
|
||||
|
||||
useEffect(()=>{
|
||||
if(user){
|
||||
// 更改网页标题
|
||||
const {username, login} = user;
|
||||
document.title = `贡献确权-${username}/${login}`;
|
||||
}
|
||||
},[user])
|
||||
|
||||
// 目标转账用户
|
||||
function getUser(id, login){
|
||||
// 使用JSON进行对象深拷贝
|
||||
const userLoginObject = JSON.parse(JSON.stringify(userLogin));
|
||||
userLoginObject[id] = login;
|
||||
setUserLogin(userLoginObject);
|
||||
}
|
||||
|
||||
// token转账值
|
||||
function setToken(id, token){
|
||||
const tokenCountObject = JSON.parse(JSON.stringify(tokenCount));
|
||||
tokenCountObject[id] = token;
|
||||
setTokenCount(tokenCountObject);
|
||||
}
|
||||
|
||||
// 确认转账
|
||||
function confirmTransfer(id, identifier, max){
|
||||
setError1(undefined);
|
||||
setError2(undefined);
|
||||
setProId(id);
|
||||
const userLoginById = userLogin[id];
|
||||
const tokenCountById = tokenCount[id];
|
||||
if(!userLoginById || !tokenCountById){
|
||||
!userLoginById && setError1('请确定目标转账用户');
|
||||
if(!tokenCountById){
|
||||
setError2('请输入token数额');
|
||||
}
|
||||
}else if(!/^[1-9]+[0-9]*]*$/.test(tokenCountById) || tokenCountById>max){
|
||||
setError2('转账token数额必须为不超过token总数的正整数');
|
||||
}else{
|
||||
axios.post(`/users/blockchain/transfer`, {
|
||||
owner_login: login,
|
||||
project_identifier: identifier,
|
||||
user_id,
|
||||
transfer_amount: tokenCountById,
|
||||
transfer_login: userLoginById,
|
||||
payer_id: user_id,
|
||||
project_id: id
|
||||
}).then(res=>{
|
||||
if(res && res.data && res.data.status === 2){
|
||||
getUser(id, undefined);
|
||||
setToken(id, undefined);
|
||||
setReload(Math.random());
|
||||
message.success('转账成功');
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return(<div className='mt15 contribution'>
|
||||
{/* 转账/记录切换 */}
|
||||
{/* <div className='contHead'>
|
||||
<a className={`itemCont ${type ? 'active' : ''}`} onClick={()=>{setType(1)}}>token转账</a>
|
||||
<a className={`itemCont ${type ? '' : 'active'}`} onClick={()=>{setType(0)}}>token记录</a>
|
||||
</div> */}
|
||||
{/* token转账 */}
|
||||
{type ? <div className='contributionTable mt25'>
|
||||
{list && list.length > 0 && <div className='flexCenterCont tableHeadTri mb5'>
|
||||
<div className='tableColumn1'>项目名称</div>
|
||||
<div className='tableColumn2'>token总量</div>
|
||||
<div className='tableColumn3'>目标转账用户</div>
|
||||
<div className='tableColumn4'>转账token数额</div>
|
||||
<div className='tableColumn5'>操作</div>
|
||||
</div>}
|
||||
{list && !list.length && <Nodata _html="暂无数据"/>}
|
||||
{list && list.map(item=>{return item.id ? <div key={item.id} className='flexCenterCont transferAccount'>
|
||||
<div className='tableColumn1'>{item.name}</div>
|
||||
<div className='tableColumn2'>{item.balance}</div>
|
||||
<div className='tableColumn3'>
|
||||
<SearchUser getUser={(login)=>{getUser(item.id, login)}} width={"100%"} placeholder="搜索转账目标用户" value={userLogin[item.id]}/>
|
||||
{proId === item.id && error1 && <div className='errorTipTri'>{error1}</div>}
|
||||
</div>
|
||||
<div className='tableColumn4'>
|
||||
<Input value={tokenCount[item.id]} onChange={(e)=>{setToken(item.id, e.target.value)}} placeholder='请输入token数额'/>
|
||||
{proId === item.id && error2 && <div className='errorTipTri'>{error2}</div>}
|
||||
</div>
|
||||
<div className='tableColumn5'><Button className='confirmTransfer' onClick={()=>{confirmTransfer(item.id, item.identifier, item.balance)}}>确认</Button></div>
|
||||
</div>: <div className='flexCenterCont transferAccount deleteRepo'>{item.balance}个项目被删除</div>})}
|
||||
</div> : <div className='contributionTable mt20 record'>
|
||||
<FlexAJ className='contributionRecordItem font-15'>
|
||||
<span><span className='themeSpan'>蒋宇航</span>在<span className='themeSpan'>GilLink/确实开源</span>给你转账了1000个tiken</span>
|
||||
<span className='tableHeadTri'>6小时前</span>
|
||||
</FlexAJ>
|
||||
<FlexAJ className='contributionRecordItem font-15'>
|
||||
<span><span className='themeSpan'>蒋宇航</span>在<span className='themeSpan'>GilLink/确实开源</span>给你转账了1000个tiken</span>
|
||||
<span className='tableHeadTri'>6小时前</span>
|
||||
</FlexAJ>
|
||||
</div>}
|
||||
{total > limit && <div className='mt20 paginationTri mb50'>
|
||||
<Pagination simple current={page} pageSize={limit} total={total} onChange={(page)=>{setPage(page);window.scrollTo(0,0);}}/>
|
||||
</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Contribution;
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
.contHead{
|
||||
&>.itemCont{
|
||||
display: inline-block;
|
||||
width:83px;
|
||||
height:32px;
|
||||
background-color:rgba(250, 251, 252, 0);
|
||||
border:1px solid #d0d0d0;
|
||||
border-radius:0px 4px 4px 0px;
|
||||
text-align: center;
|
||||
color: #333;
|
||||
&:first-of-type{
|
||||
border-radius:4px 0px 0px 4px;
|
||||
border-right: none;
|
||||
}
|
||||
&:last-of-type{
|
||||
border-left: none;
|
||||
}
|
||||
&.active{
|
||||
border-color: $primary-color;
|
||||
background-color: $primary-color;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
.contribution{
|
||||
.flexCenterCont{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.contributionTable{
|
||||
background-color:rgba(250, 252, 255, 0.89);
|
||||
border-radius:4px 4px 0px 0px;
|
||||
padding: 20px 20px 40px 20px;
|
||||
}
|
||||
.tableHeadTri{
|
||||
color:#898d9d;
|
||||
}
|
||||
.transferAccount{
|
||||
color:#40424a;
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
.deleteRepo{
|
||||
color: slategrey;
|
||||
}
|
||||
.tableColumn3, .tableColumn4{
|
||||
width: 150px;
|
||||
}
|
||||
.tableColumn1{
|
||||
width: 200px;
|
||||
}
|
||||
.tableColumn5{
|
||||
width: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
.tableColumn2{
|
||||
flex: 0.5;
|
||||
}
|
||||
.confirmTransfer{
|
||||
border-color: $primary-color;
|
||||
color: $primary-color;
|
||||
}
|
||||
.errorTipTri{
|
||||
width: 400px;
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
.record.contributionTable{
|
||||
padding: 0px 20px;
|
||||
.contributionRecordItem{
|
||||
padding: 15px 0;
|
||||
}
|
||||
.contributionRecordItem+.contributionRecordItem{
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
.themeSpan{
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
.paginationTri{
|
||||
text-align: center;
|
||||
}
|
||||
|
|
@ -161,8 +161,8 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
|||
//获取光标位置
|
||||
const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style;
|
||||
//设置弹框位置
|
||||
const newTop = placeholder === "添加评论..." ? 159: placeholder === "请输入合并请求的描述..." ? 172:62;
|
||||
const newLeft = placeholder === "添加评论..." ? 80: 20;
|
||||
const newTop = 62
|
||||
const newLeft = 20;
|
||||
document.getElementById("at_who_list").style.top = parseInt(cssStyle.getPropertyValue("top").replace("px","")) + newTop +"px";
|
||||
document.getElementById("at_who_list").style.left = parseInt(cssStyle.getPropertyValue("left").replace("px",""))+newLeft+"px";
|
||||
}
|
||||
|
|
|
|||