This commit is contained in:
caishi 2023-03-09 13:48:11 +08:00
parent 3e2b3255c8
commit f0eae6d4db
10 changed files with 264 additions and 132 deletions

View File

@ -3,7 +3,7 @@ import { Form , Modal , Input, Button } from 'antd';
import ColorCard from './colorCard';
import axios from 'axios';
function AddTagsBox({visible,onCancel, onSuccess , form ,owner , projectsId}){
function AddTagsBox({onSuccess , form ,owner , projectsId,onCancel}){
const { getFieldDecorator , getFieldsValue } = form;
const [ colors , setColors ] = useState(undefined);
const [ defaultColor , setDefaultColor ]= useState("#F17013");
@ -23,38 +23,33 @@ function AddTagsBox({visible,onCancel, onSuccess , form ,owner , projectsId}){
}
return(
<Modal
visible={visible}
width="475px"
title={"创建新标记"}
onCancel={onCancel}
footer={null}
className="addTagsModal"
>
<Form className="addTags_form" hideRequiredMark={true}>
<Form.Item label="标记名称" colon={false}>
{getFieldDecorator('name',{
rules:[
{
required:true,
message:"请输入标记名称"
}
],
validateTrigger:"onInput",
})(<Input placeholder="名称15字以内"/>)}
</Form.Item>
<Form.Item label="标记描述" colon={false}>
{getFieldDecorator('desc',{rules:[],
validateTrigger:"onInput",
})(<Input placeholder="描述30字以内"/>)}
</Form.Item>
<Form.Item label={"标记色值"}>
<ColorCard getColor={getColor}
defaultColor={defaultColor}/>
</Form.Item>
<Button onClick={saveFunc} type="primary" style={{width:'100%',marginTop:45}} className="mb25">保存</Button>
</Form>
</Modal>
<Form className="addTags_form" hideRequiredMark={true}>
<p className="manageTitle" onClick={onCancel}><i className="iconfont icon-xiangzuojiantou font-15 color-grey mr5"/>标记管理</p>
<Form.Item colon={false}>
{getFieldDecorator('name',{
rules:[
{
required:true,
message:"请输入标记名称"
}
],
validateTrigger:"onInput",
})(<Input placeholder="标记名称15字以内" maxLength={15}/>)}
</Form.Item>
<Form.Item colon={false}>
{getFieldDecorator('desc',{rules:[],
validateTrigger:"onInput",
})(<Input placeholder="描述30字以内" maxLength={30}/>)}
</Form.Item>
<Form.Item>
<ColorCard getColor={getColor}
defaultColor={defaultColor}/>
</Form.Item>
<div style={{textAlign:'center'}}>
<Button style={{width:"80px"}} onClick={onCancel}>取消</Button>
<Button style={{width:"80px"}} className="ml20" type="primary" onClick={saveFunc}>确认</Button>
</div>
</Form>
)
}
export default Form.create({ name: 'Bind' })(AddTagsBox);

View File

@ -2,6 +2,7 @@ import React ,{ useState , useEffect , useRef } from 'react';
import { findDOMNode } from 'react-dom';
import { Dropdown , Menu , Input , message, Button} from 'antd';
import { getImageUrl } from 'educoder';
import AddTagsBox from './addTagsBox';
const { Search } = Input;
/**
@ -30,12 +31,13 @@ function ChooseMenu({
onAdd,
headImg,
menus,
chooseFunc,double,colorFlag,mustFlag,removeFlag
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);
@ -43,7 +45,9 @@ function ChooseMenu({
const refBox = useRef(null);
useEffect(() => {
document.addEventListener('click', clickMe , false);
if(saveList && saveList.length>0){
document.addEventListener('click', clickMe , false);
}
}, [])
const clickMe = ({ target }) => {
@ -53,10 +57,12 @@ function ChooseMenu({
if (faComponent && boxComponent) {
const isChild = faComponent.contains(target);
const isBox = boxComponent.contains(target);
console.log(target);
const pointer = !target || (target && target.className && (target.className.indexOf("removeicon")===-1 && target.className.indexOf("icon-guanbi")===-1));
if(!isChild && !isBox && pointer){
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"];
console.log(target.className);
const pointer = arr.includes(target.className);
if(!isChild && !isBox && !pointer){
setVisible(false);
setContent(false);
}
}
}
@ -154,6 +160,7 @@ function ChooseMenu({
setCount(relist ? relist.length :0);
if(!double){
setVisible(false);
setContent(false);
chooseFunc(relist);
}
}
@ -161,6 +168,7 @@ function ChooseMenu({
//
function onSureFunc(){
setVisible(false);
setContent(false);
chooseFunc(saveList);
}
//
@ -181,6 +189,10 @@ function ChooseMenu({
setCount(filter ? filter.length :0);
chooseFunc(filter);
}
function onSuccess(){
}
return(
<li>
<span>
@ -193,71 +205,82 @@ function ChooseMenu({
overlay={
<div ref={refFa}>
{
double && saveList && saveList.length>0?
<ul className="choosedul">
{
saveList.map((i,k)=>{
console.log(i)
return(
<li style={{backgroundColor:`${i.color}` || "#eff2ff",borderColor:`${i.color}` || "#466aff",color:`${colorFlag?"#FFF":"#466aff"}`}}>
<span className="task-hide">{i.name}</span>
<a className="removeicon" onClick={()=>removeSaveFunc(i.id)}><i className={`iconfont icon-guanbi font-12 ${colorFlag? "color-white":"color-blue"}`}></i></a>
</li>
)
})
}
</ul>
:""
}
{ 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} className={colorFlag ?"colorli":"commonli"} style={{backgroundColor:colorFlag?`${i.color || "#000"}`:"#f4f6fe"}} onClick={()=>chooseMenu(i)}>
<span><span className="task-hide">{i.name}</span></span>
</Menu.Item>
)
})
}
</Menu>
:
<div className="menusEmpty">
<p>{searchValue ? <span>暂无{placeholder}{searchValue}</span>: `暂无${placeholder}`}</p>
</div>
}
<div className="counttips">
!content ?
<div>
{ onAdd &&
<a className="color-blue font-15" onClick={()=>{setVisible(false);onAdd()}}>
<i className="iconfont icon-a-bianji12 font-14 mr5"></i>标记管理</a>
{
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="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)}>
<span><span className="task-hide">{i.name}</span></span>
</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 && (count<0 ?
<p className="color-red font-13">最多添加{double}{placeholder}</p>
:
<p className="font-13" style={{color:"#898d9d"}}>还可添加{double - count}{placeholder}</p>
)
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>
{
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>
:
<AddTagsBox
owner={owner}
projectsId={projectsId}
visible={visible}
onCancel={()=>setContent(false)}
onSuccess={onSuccess} />
}
</div>
}>

View File

@ -1,5 +1,5 @@
import React , { useEffect , useState , forwardRef } from 'react';
import { Spin , Form } from 'antd';
import React , { useEffect , useState , forwardRef , useRef } from 'react';
import { Spin , Form , InputNumber } from 'antd';
import { getImageUrl } from 'educoder';
import { Link } from 'react-router-dom';
import { Box , LongWidth } from '../../Component/layout';
@ -7,7 +7,6 @@ import RenderHtml from "../../../components/render-html";
import DropMenu from '../Component/dropMenu';
import NewPanel from '../Component/newPanel';
import Attachments from "../../Upload/attachment";
import AddTagsBox from '../Component/addTagsBox';
import DelBox from '../Component/delBox';
import Date from '../Component/date';
import moment from 'moment';
@ -51,11 +50,14 @@ function Details(props){
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 ref = useRef(null);
const {projectDetail} = props;
const {projectDetail , open_blockchain} = props;
useEffect(()=>{
if(pathname === `/${owner}/${projectsId}/issues/${index}/copy`){
setEdit(true);
@ -96,6 +98,7 @@ function Details(props){
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'));
@ -220,7 +223,7 @@ function Details(props){
}
// (!copy:)
function saveForeach(c,s,p,t,m,b,sDate,eDate){
function saveForeach(c,s,p,t,m,b,sDate,eDate,rewardAmount){
if(!copy){
const url = `/v1/${owner}/${projectsId}/issues/${index}`;
axios.patch(url,{
@ -231,7 +234,8 @@ function Details(props){
issue_tag_ids: t?t:undefined,
assigner_ids: c?c:undefined,
start_date:sDate,
due_date:eDate
due_date:eDate,
blockchain_token_num:rewardAmount
}).then(result=>{
if(result){
Init();
@ -315,6 +319,22 @@ function Details(props){
//
setCommentReload(Math.random());
}
function editReward(){
setRewardFlag(true);
setTimeout(() => {
ref.current && ref.current.focus();
}, 100);
}
//
function amountBlur(e){
setRewardFlag(false);
const value = e.target.value;
if(value && value >= 0){
saveForeach(undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined,value);
}
}
return(
details ?
<Box>
@ -325,12 +345,6 @@ function Details(props){
onSuccess={deleteFunc}
content={<div style={{paddingTop:"3px"}}><p className="font-15 mb20">您确定要删除当前疑修</p></div>}
/>
<AddTagsBox
owner={owner}
projectsId={projectsId}
visible={visible}
onCancel={()=>setVisible(false)} onSuccess={onSuccess}
/>
{
edit ?
<div style={{paddingTop:"25px"}} >
@ -435,6 +449,8 @@ function Details(props){
removeFlag
editFlag={details && !details.user_permission}
onAdd={permission && permission !== "Reporter" ? ()=>setVisible(true) :false}
owner
projectsId
/>
<ChooseMenu
placeholder="里程碑"
@ -452,6 +468,16 @@ function Details(props){
editFlag={details && !details.user_permission}
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>
悬赏金额
{!(details && !details.user_permission) && <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"}} ref={ref} value={rewardAmount} onChange={(value)=>setRewardAmount(value)} className="borderNo mt5" disabled={!rewardFlag}/>
</li>
}
<Date name="开始日期" today={start_date} setDate={(date)=>{setStartDate(date);saveForeach(undefined,undefined,undefined,undefined,undefined,undefined,date)}} editFlag={details && !details.user_permission}/>
<Date name="结束日期" today={due_date} setDate={(date)=>{setDueDate(date);saveForeach(undefined,undefined,undefined,undefined,undefined,undefined,undefined,date)}} editFlag={details && !details.user_permission}/>
</div>

View File

@ -1,5 +1,5 @@
import React , { useEffect , useState , forwardRef } from 'react';
import { Form } from 'antd';
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';
@ -39,10 +39,14 @@ function New(props){
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){
@ -197,7 +201,8 @@ function New(props){
issue_tag_ids:t,
assigner_ids:a,
attachment_ids:fileList,
start_date,due_date,receivers_login
start_date,due_date,receivers_login,
blockchain_token_num:rewardAmount
}).then(result=>{
if(result && result.data && result.data.project_issues_index){
props.showNotification("任务创建成功!");
@ -207,6 +212,19 @@ function New(props){
}
function editReward(){
setRewardFlag(true);
setTimeout(() => {
ref.current && ref.current.focus();
}, 100);
}
//
function amountBlur(e){
setRewardFlag(false);
const value = e.target.value;
value && value>=0 && setRewardAmount(value);
}
return(
<div>
<AddTagsBox
@ -256,6 +274,7 @@ function New(props){
chooseFunc={(list)=>{setTag_choose(list);}}
double={3}
colorFlag="2"
removeFlag
onAdd={permission && permission !== "Reporter" ? ()=>setVisible(true) :false}
/>
<ChooseMenu
@ -273,6 +292,16 @@ function New(props){
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 onBlur={amountBlur} placeholder="未设置" style={{width:"100%",color:rewardAmount?"#333":"#acb0bf"}} ref={ref} value={rewardAmount} onChange={(value)=>setRewardAmount(value)} className="borderNo mt5" disabled={!rewardFlag}/>
</li>
}
<Date name="开始日期" today={start_date} setDate={(date)=>setStartDate(date)}/>
<Date name="结束日期" today={due_date} setDate={(date)=>setDueDate(date)}/>
</div>

View File

@ -1,3 +1,17 @@
.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;
@ -504,6 +518,19 @@
}
}
}
.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;
}
}
.addTagsModal{
.ant-modal-content{
background-image:linear-gradient(359.37deg,#ebf3ff 0%,#f8fbff 55.01%,#f1f5ff 100%);
@ -525,15 +552,6 @@
.ant-modal-close{
top:0px !important;
}
.addTags_form{
.ant-row.ant-form-item{
margin-bottom: 12px;
}
.ant-col.ant-form-item-label{
height: 28px;
line-height: 28px;
}
}
.popover{
left: 94px;
top:0px;

View File

@ -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>

View File

@ -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 ? (

View File

@ -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>
):""}

View File

@ -91,24 +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_issue_ids ? data.attached_issue_ids :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) => {
@ -212,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) {
@ -255,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 {
@ -404,7 +407,7 @@ class MergeForm extends Component {
</Select>
)}
</Form.Item>
<Form.Item>
<Form.Item style={{display:open_blockchain ? "block" :"none"}}>
{getFieldDecorator("issue_id", {
initialValue: issue_id,
})(

View File

@ -224,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;
}