里程碑"

This commit is contained in:
caishi 2023-09-28 14:55:58 +08:00
parent 37d70dd18b
commit eff1b706c2
19 changed files with 1189 additions and 221 deletions

View File

@ -32,7 +32,7 @@ export function initAxiosInterceptors(props) {
// 判断网络是否连接
initOnlineOfflineListener();
var proxy = "https://testforgeplus.educoder.net";
var proxy = "https://testforgeplus.educoder.net/";
//响应前的设置
axios.interceptors.request.use(
config => {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -93,7 +93,7 @@ const UpdateMerge = Loadable({
})
const MilepostDetail = Loadable({
loader: () => import('../Order/MilepostDetail'),
loader: () => import('../Order/MilepostDetail.jsx'),
loading: Loading,
})
const WatchUsers = Loadable({
@ -736,7 +736,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} />)
}

View File

@ -1,7 +1,7 @@
import React from 'react';
import { Modal } from 'antd';
import './order.css';
import './order.scss';
function AmplifyImg(props) {
return (

View File

@ -1,4 +1,4 @@
import React, { Component, useRef } from "react";
import React, { Component } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
@ -8,7 +8,7 @@ import Attachments from "../Upload/attachment";
import RenderHtml from "../../components/render-html";
import Comments from "../comments/comments";
import Claims from "../claims/claims"
import "./order.css";
import "./order.scss";
class Detail extends Component {
constructor(props) {
@ -44,6 +44,20 @@ class Detail extends Component {
this.getDetail();
};
componentDidUpdate=()=>{
this.updateDocumentTitle();
}
// 更新网页标题
updateDocumentTitle(){
const {projectDetail} = this.props;
const {data} = this.state;
if(projectDetail && data){
const { author, name} = projectDetail;
document.title = `${data.subject}-疑修-${author.name}/${name}`;
}
}
getDetail = () => {
const { projectsId, orderId, owner } = this.props.match.params;
const url = `/${owner}/${projectsId}/issues/${orderId}.json`;
@ -55,11 +69,6 @@ class Detail extends Component {
data: result.data,
isSpins: false
});
const {projectDetail} = this.props;
if(projectDetail){
const { author, name} = projectDetail;
document.title = `${result.data && result.data.subject}-疑修-${author.name}/${name}`;
}
}
})
.catch((error) => {

View File

@ -1,54 +1,49 @@
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.css';
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(){
this.updateDocumentTitle();
}
componentDidMount = () => {
this.getList(1, this.state.status, 'desc');
}
componentDidUpdate = () => {
this.getList(1, this.state.limit, this.state.status);
this.updateDocumentTitle();
}
// 更新网页标题
updateDocumentTitle(){
const {projectDetail} = this.props;
if(projectDetail){
const { author, name} = projectDetail;
document.title = `新建合并请求-${author.name}/${name}`;
document.title = `里程碑-${author.name}/${name}`;
}
}
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: {
projectsId, page, limit, status, order_type, order_name
page, limit: limit, category, sort_by, sort_direction
}
}).then((result) => {
if (result) {
@ -63,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();
}
@ -120,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();
}
@ -134,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>
@ -176,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" 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=""/><span>暂无数据~</span></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>

View File

@ -110,7 +110,7 @@ class MilepostDetail extends Component {
getIssueList = ( page , item , value , update , updateValue , type ) => {
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;
console.log(this.props);
const url = `/${owner}/${projectsId}/milestones/${meilid}.json`;
let params = update ? {
page, limit , order_name:value , order_type:updateValue , issue_tag_id ,

View File

@ -0,0 +1,264 @@
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 millestones from '../../forge/Issues/Img/millestones-big.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 [ category , setCategory] = useState("opened");
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 [ chooseValue, setChooseValue ] = 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(chooseValue);
},[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)})
}
// 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);
window.scrollTo(0,0);
}
function chooseFunc(ids){
if(allValue && allValue.length>0){
// ids便
setUpdateIds(ids);
setChooseValue(undefined);
}else{
const {sort_by} = ids;
const params = {...ids, ...sortBy[sort_by]}
setChooseValue(params);
page===1 && Init(params);
setPage(1);
}
}
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 ? parseInt(milepost.percent*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="color-grey-89 mt10">{milepost && milepost.description}</div>
<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="listempty">
<img src={millestones} style={{width:"68px"}} alt="" />
<p className="font-22 mt5 mb10">欢迎使用里程碑</p>
<p className="font-15">里程碑用于集中归类管理项目的疑修及进度在使用之前您可以先<CheckProfile {...props} checklogin sureFunc={()=>{props.history.push(`/${owner}/${projectsId}/issues/${mileId}/new`)}} className="color-blue">创建一个疑修</CheckProfile></p>
</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;

View File

@ -0,0 +1,407 @@
import React, { Component } from "react";
import { Link } from 'react-router-dom';
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:下拉的筛选列表,
* data:列表接口返回的所有数据,
* issues:列表数组,
* isSpin:加载中,
* author_id:发布者id,
* assigned_to_id:指派给的id
* limit:每页条数,
* page:当前页,
* search_count:列表总条数
* issue_type:搜索条件
* status_type: issue的关闭和开启1表示开启中的2表示关闭的
* category: issue状态 全部开启关闭
*/
class MilepostDetail extends Component {
constructor(props) {
super(props);
this.state = {
issue_chosen: undefined,
data: undefined,
issues: undefined,
isSpin: true,
author_id: undefined,
status_id: undefined,
assigned_to_id: undefined,
limit: 15,
page: 1,
search_count: undefined,
issue_type: undefined,
status_type: '1',
//设置选择高亮
issue_tag_ids: '标记',
tracker_ids: '类型',
author_ids: '发布人',
assigned_to_ids: '负责人',
status_ids: "状态",
done_ratios: '完成度',
paix: '排序',
issueFlag:true,
checkAll: false,
allValue: [],
allIds: [],
category: 'all'
}
}
componentDidMount = () => {
this.getSelectList();
const { page } = this.state;
this.getIssueList(page);
}
componentDidUpdate=(prevProps)=>{
this.updateDocumentTitle();
const prebannerList = prevProps && prevProps.bannerList;
const bannerList = this.props && this.props.bannerList;
if(prebannerList !== bannerList){
if(bannerList && bannerList.length>0){
let i = bannerList.filter(i=>i.menu_name === "issues");
this.setState({
issueFlag:i && i.length === 0 ? false :true
})
}
}
}
// 更新网页标题
updateDocumentTitle(){
const {projectDetail} = this.props;
const {data} = this.state;
if(projectDetail && data){
const { author, name} = projectDetail;
document.title = `${data.name}-里程碑-${author.name}/${name}`;
}
}
getSelectList = () => {
const { projectsId , owner } = this.props.match.params;
const url = `/${owner}/${projectsId}/issues/index_chosen.json`;
axios.get(url).then((result) => {
if (result) {
this.setState({
issue_chosen: result.data.issue_chosen
})
}
}).catch((error) => {
console.log(error);
})
}
deletedetail = (id) => {
const { projectsId , owner } = this.props.match.params;
const url = `/${owner}/${projectsId}/issues/${id}.json`;
axios.delete(url, {
data: {
project_id: projectsId,
id: id,
},
})
.then((result) => {
if (result) {
const { page } = this.state;
this.getIssueList(page);
const { getDetail } = this.props;
getDetail && getDetail();
}
})
.catch((error) => {
console.log(error);
});
};
// 获取列表数据
getIssueList = ( page , item , value , update , updateValue , type ) => {
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 = `/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,
status_type:type || status_type,
}:{
page, limit , order_name , order_type , issue_tag_id ,
author_id , assigned_to_id , tracker_id , status_id , done_ratio,
status_type:type || status_type,
[item]:value
}
axios.get(url, {
params: params
}).then((result) => {
if (result) {
this.setState({
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
})
}
}).catch((error) => {
console.log(error);
})
}
getOption = (e, id, name) => {
if (e.key === "created_on") {
if (e.item.props.value === "desc") {
this.setState({
paix: '最新创建'
})
} else {
this.setState({
paix: '最早创建'
})
}
} else if (e.key === "updated_on") {
if (e.item.props.value === "desc") {
this.setState({
paix: '最新更新'
})
} else {
this.setState({
paix: '最早更新'
})
}
}
this.setState({
[id]: e.key,
[id+"s"]:name,
page:1
})
if (e.key === "created_on" || e.key === "updated_on") {
this.setState({
order_name: e.key,
order_type: e.item.props.value
})
this.getIssueList(1,id,e.key,true,e.item.props.value);
}else{
this.getIssueList(1,id,e.key);
}
}
renderMenu = (array, name, id) => {
return (
<Menu className="orderCondition">
<Menu.Item key={"all"} onClick={(e) => this.getOption(e, id, name)}>{name}</Menu.Item>
{
array && array.length > 0 && array.map((item, key) => {
return (
<Menu.Item key={item.id} onClick={(e) => this.getOption(e, id, item.name)}>{item.name}</Menu.Item>
)
})
}
</Menu>
)
}
// 翻页
ChangePage = (page) => {
this.setState({
page,
isSpin: true
})
this.getIssueList(page);
}
category = (type) => {
this.setState({
status_type: type,
issue_tag_id : undefined,
author_id : undefined,
tracker_id : undefined,
done_ratio : undefined,
status_id: undefined,
assigned_to_id: undefined,
issue_tag_ids: '标记',
tracker_ids: '类型',
author_ids: '发布人',
assigned_to_ids: '负责人',
status_ids: "状态",
done_ratios: '完成度',
paix: '排序'
},() => {
this.getIssueList(1,undefined,undefined,undefined,undefined,type);
});
}
// 全选所有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, checkAll, allValue, category } = this.state;
const { projectsId, meilid ,owner} = this.props.match.params;
const { current_user , showLoginDialog , projectDetail } = this.props;
const menu = (
<Menu onClick={(e) => this.getOption(e)}>
<Menu.Item key={'created_on'} value="desc">最新创建</Menu.Item>
<Menu.Item key={'created_on'} value="asc">最早创建</Menu.Item>
<Menu.Item key={'updated_on'} value="desc">最新更新</Menu.Item>
<Menu.Item key={'updated_on'} value="asc">最早更新</Menu.Item>
</Menu>
)
return (
<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 className="color-grey-89">{data && data.effective_date}</span>
:
<span className="color-grey-89">暂无截止时间</span>
}
</span>
{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>
{
allValue && allValue.length>0 ?
<span>选择{allValue.length}个issue</span>
:
<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>
<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>
<li className={status_type==="2" ? "active" : ""} onClick={() => this.openorder("2")}>{data && data.close_issues_count}个已关闭</li>
</ul>
<ul className="topWrapper_select">
<li>
<Dropdown className="topWrapperSelect" overlay={this.renderMenu(issue_chosen && issue_chosen.issue_tag, '标记', 'issue_tag_id')} trigger={['click']} placement="bottomCenter">
<span>{this.state.issue_tag_ids}<Icon type="caret-down" className="ml5" /></span>
</Dropdown>
</li>
<li>
<Dropdown className="topWrapperSelect" overlay={this.renderMenu(issue_chosen && issue_chosen.assign_user, '发布人', 'author_id')} trigger={['click']} placement="bottomCenter">
<span>{this.state.author_ids}<Icon type="caret-down" className="ml5" /></span>
</Dropdown>
</li>
<li>
<Dropdown className="topWrapperSelect" overlay={this.renderMenu(issue_chosen && issue_chosen.assign_user, '负责人', 'assigned_to_id')} trigger={['click']} placement="bottomCenter">
<span>{this.state.assigned_to_ids}<Icon type="caret-down" className="ml5" /></span>
</Dropdown>
</li>
<li>
<Dropdown className="topWrapperSelect" overlay={this.renderMenu(issue_chosen && issue_chosen.tracker, '类型', 'tracker_id')} trigger={['click']} placement="bottomCenter">
<span>{this.state.tracker_ids}<Icon type="caret-down" className="ml5" /></span>
</Dropdown>
</li>
<li>
<Dropdown className="topWrapperSelect" overlay={this.renderMenu(issue_chosen && issue_chosen.issue_status, '状态', 'status_id')} trigger={['click']} placement="bottomCenter">
<span>{this.state.status_ids}<Icon type="caret-down" className="ml5" /></span>
</Dropdown>
</li>
<li>
<Dropdown className="topWrapperSelect" overlay={this.renderMenu(issue_chosen && issue_chosen.done_ratio, '完成度', 'done_ratio')} trigger={['click']} placement="bottomCenter">
<span>{this.state.done_ratios}<Icon type="caret-down" className="ml5" /></span>
</Dropdown>
</li>
<li>
<Dropdown className="topWrapperSelect" overlay={menu} trigger={['click']} placement="bottomCenter">
<span>{this.state.paix}<Icon type="caret-down" className="ml5" /></span>
</Dropdown>
</li>
</ul>
</div>
<div className="setItemStyle">
{
search_count === 0 ?
<NoneData _html="暂时还没有相关数据!" />
:
issues && issues.length>0 && issues.map((item,key)=>{
return(
<OrderItem
key={key} mile
item={item}
search_count={search_count}
page={page}
limit={limit}
{...this.props} {...this.state}
deletedetail={this.deletedetail}
></OrderItem>
)
})
}
</div>
{
search_count > limit?
<div className="pt30 pb30 edu-txt-center" style={{borderTop:"1px solid #eee"}}>
<Pagination simple current={page} total={search_count} pageSize={limit} onChange={this.ChangePage}></Pagination>
</div>:""
}
</div> */}
</Spin>
</div>
)
}
}
export default MilepostDetail;

View File

@ -1,6 +1,6 @@
import React , { Component } from "react";
import { NavLink } from 'react-router-dom';
import './order.css'
import './order.scss'
class Nav extends Component{
render(){

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { Popconfirm } from 'antd'
import { Popconfirm,Tooltip } from 'antd'
import { TagInfo } from '../Utils/TagColor';
class OrderItem extends Component {
constructor(props) {
@ -84,7 +84,9 @@ class OrderItem extends Component {
}
</li>
<li>{item.tracker || "--"}</li>
{ !mile ?<li>{item.version || "--"}</li>:""}
{!mile ? <li>
{item.version ? <Tooltip placement="topLeft" title={item.version}>{item.version}</Tooltip> : "--"}
</li> : ""}
<li>{item.issue_status || "--"}</li>
<li style={{color:`${item.done_ratio === "100%"?"#28BD6C":"#F73030"}`}}>{item.done_ratio || "--"}</li>
<li>

View File

@ -76,11 +76,6 @@ class Tags extends Component {
componentDidMount = () => {
this.getList();
const {projectDetail} = this.props;
if(projectDetail){
const { author, name} = projectDetail;
document.title = `标签列表-${author.name}/${name}`;
}
}
@ -110,7 +105,6 @@ class Tags extends Component {
const url = `/projects/${owner}/${projectsId}/labels.json`;
axios.post(url, {
...values,
project_id: projectsId,
color: this.state.textcolor
}).then(result => {
if (result) {

View File

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import { Calendar, Select, Radio, Col, Row, Divider, Input, Form, Spin } from 'antd';
import axios from 'axios';
import moment from 'moment';
import '../Order/order.css';
import '../Order/order.scss';
const TextArea = Input.TextArea;
@ -22,6 +22,20 @@ class UpdateMilepost extends Component {
this.getmeil();
}
componentDidUpdate=()=>{
this.updateDocumentTitle();
}
// 更新网页标题
updateDocumentTitle(){
const {projectDetail} = this.props;
const {data} = this.state;
if(projectDetail && data){
const { author, name} = projectDetail;
document.title = `${data.name}-里程碑-${author.name}/${name}`;
}
}
onPanelChange = (time, mode) => {
this.setState({
value: time
@ -53,7 +67,6 @@ class UpdateMilepost extends Component {
name: result.data.name,
description: result.data.description
});
}
}).catch((error) => {
console.log(error);
@ -79,8 +92,8 @@ class UpdateMilepost extends Component {
}).then(result => {
if (result) {
this.setState({ isSpin: false })
this.props.history.push(`/${owner}/${projectsId}/milestones`);
window.scrollTo(0,0);
this.props.history.push(`/${owner}/${projectsId}/milestones/${meilid}`);
}
}).catch(error => {
@ -120,7 +133,7 @@ class UpdateMilepost extends Component {
required: true, message: '请输入标题'
}],
})(
<Input placeholder="标题" />
<Input placeholder="标题" maxLength={30}/>
)}
</Form.Item>
</div>
@ -131,8 +144,7 @@ class UpdateMilepost extends Component {
required: true, message: '请输入描述内容'
}],
})(
<TextArea placeholder="请输入描述内容..." style={{ height: "150px" }} />
<TextArea placeholder="请输入描述内容..." style={{ height: "150px" }} maxLength={500}/>
)}
</Form.Item>
</div>

View File

@ -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\namesundefined
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);

View File

@ -1,9 +1,14 @@
.screenWrap{
background:rgba(250,250,250,1);
border-top:1px solid rgba(221,221,221,1);
border-bottom:1px solid rgba(221,221,221,1);
background:#fafcff;
border:1px solid rgba(42, 97, 255, 0.23);
padding-left: 20px;
box-sizing: border-box;
border-radius: 4px 4px 0px 0px;
}
.mergeData{
border:1px solid #d0d0d0;
border-top: none;
border-radius: 0px 0px 2px 2px;
}
.attachment-list-div:hover {
background-color: #e6f7ff;
@ -39,7 +44,7 @@
content: '';
left: 0px;
bottom:0px;
background-color:#5091FF;
background-color:$primary-color;
}
}
.milepostleft{

View File

@ -0,0 +1,113 @@
.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;
min-height: 500px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
span{
color: #666;
}
}
// 浅灰色按钮
.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;
}

View File

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import { Calendar, Select, Radio, Col, Row, Divider, Input, Form, Spin } from 'antd';
import axios from 'axios';
import moment from 'moment';
import '../Order/order.css';
import '../Order/order.scss';
const TextArea = Input.TextArea;
const { Group, Button } = Radio;
@ -16,19 +16,30 @@ class NewMilepost extends Component {
isSpin: false
}
}
componentDidMount = () => {
this.updateDocumentTitle();
}
componentDidUpdate=(prevPros)=>{
if(prevPros && this.props && !this.props.checkIfLogin()){
this.props.history.push("/403")
return
}
this.updateDocumentTitle();
}
componentDidMount = () => {
// 更新网页标题
updateDocumentTitle(){
const {projectDetail} = this.props;
if(projectDetail){
const { author, name} = projectDetail;
document.title = `新建里程碑-${author.name}/${name}`;
}
};
}
// componentDidMount = () => {
// this.check_is_login();
// };
// check_is_login =() =>{
// if(!this.props.checkIfLogin()){
// this.props.history.push("/403")
@ -61,12 +72,12 @@ class NewMilepost extends Component {
}
axios.post(url, {
...values,
project_id: projectsId,
effective_date: time,
status: 'open'
}).then(result => {
if (result) {
this.setState({ isSpin: false })
window.scrollTo(0,0);
this.props.history.push(`/${owner}/${projectsId}/milestones`);
const { getDetail } = this.props;
getDetail && getDetail();
@ -106,7 +117,7 @@ class NewMilepost extends Component {
required: true, message: '请输入标题'
}],
})(
<Input placeholder="标题" autoComplete="off" maxLength="30" />
<Input placeholder="标题" autoComplete="off" maxLength={30} />
)}
</Form.Item>
</div>

View File

@ -1,6 +1,6 @@
import React, { Component } from "react";
import { Input, Dropdown, Menu, Icon, Pagination, Spin, DatePicker, Checkbox } from "antd";
import "./order.css";
import "./order.scss";
import './index.scss';
import moment from 'moment';
@ -78,17 +78,9 @@ class order extends Component {
componentDidUpdate(){
this.updateDocumentTitle();
}
// 更新网页标题
updateDocumentTitle(){
const {projectDetail} = this.props;
if(projectDetail){
const { author, name} = projectDetail;
document.title = `疑修-${author.name}/${name}`;
}
}
componentDidMount = () => {
this.updateDocumentTitle();
const datas = cookie.load('states');
let states = datas === "undefined" ? undefined : datas;
if(states){
@ -104,6 +96,14 @@ class order extends Component {
}
};
// 更新网页标题
updateDocumentTitle(){
const {projectDetail} = this.props;
if(projectDetail){
const { author, name} = projectDetail;
document.title = `疑修-${author.name}/${name}`;
}
}
getSelectList = () => {
this.setState({
@ -126,11 +126,11 @@ class order extends Component {
};
// 获取列表数据
getIssueList = (status_type, begin, end) => {
getIssueList = (status_type,) => {
this.setState({
isSpin: true
})
const { select_params } = this.state;
const { select_params ,begin,end} = this.state;
const { projectsId, owner } = this.props.match.params;
const url = `/${owner }/${projectsId}/issues.json`;
axios
@ -164,7 +164,6 @@ class order extends Component {
};
saveCookies=(status_type)=>{
const { projectsId, owner } = this.props.match.params;
const {
select_params,
issue_tag_ids,//标记
@ -292,13 +291,17 @@ class order extends Component {
// 翻页
ChangePage = (page) => {
window.scrollTo(0,0);
const { status_type } = this.state;
const { status_type,select_params } = this.state;
this.setState({
isSpin: true,
checkedValue: [],
all: false,
select_params:{
...select_params,
page
}
},()=>{
this.state.select_params.page=page;
// this.state.select_params.page=page;
this.getIssueList(status_type);
});
};
@ -442,18 +445,28 @@ class order extends Component {
// 修改开始时间
changeBeginTime = (data, value) => {
const { status_type } = this.state;
const { status_type,select_params } = this.state;
this.setState({
begin: value
begin: value,
select_params:{
...select_params,
page:1
}
},()=>{
this.getIssueList(status_type, value, this.state.end);
})
this.getIssueList(status_type, value, this.state.end);
}
changeEndTime = (data, value) => {
const { status_type } = this.state;
const { status_type,select_params } = this.state;
this.setState({
end: value
})
this.getIssueList(status_type, this.state.begin, value);
end: value,
select_params:{
...select_params,
page:1
}
},()=>{
this.getIssueList(status_type, this.state.begin, value);
});
}
// 选择列表里面的checkbox
@ -639,15 +652,14 @@ class order extends Component {
issues,
search_count,
data,
author_id,
assigned_to_id,
isSpin,
status_type,
select_params,
begin, end, checkedValue, all,search
} = this.state;
const { assigned_to_id , author_id } = this.state.select_params;
return (
<div className="main" style={{padding:"0px"}}>
<div className="main mt20" style={{padding:"0px"}}>
<div style={{padding:"10px 20px 0px 20px"}}>
<div className="topWrapper" style={{ paddingTop: "10px" }}>
<ul className="topWrapper_type">

View File

@ -4,7 +4,7 @@ import Upload from "../Upload/Index";
import UploadImg from "../Images/upload.png";
import MDEditor from "../../modules/tpm/challengesnew/tpm-md-editor";
import moment from "moment";
import "./order.css";
import "./order.scss";
import Attachments from "../Upload/attachment";
import axios from "axios";
@ -44,11 +44,28 @@ class order_form extends Component {
this.props.history.push("/403")
return
}
this.updateDocumentTitle();
}
componentDidMount = () => {
this.getSelectList();
};
// 更新网页标题
updateDocumentTitle(){
const {form_type, projectDetail} = this.props;
const {subject} = this.state;
if(projectDetail && form_type){
const { author, name} = projectDetail;
if(form_type === 'feedback'){
document.title = `意见反馈`
}else if(form_type === 'new'){
document.title = `新建疑修-${author.name}/${name}`
}else{
subject && (document.title = `${subject}-疑修-${author.name}/${name}`);
}
}
}
copyIssueAtWhoInit = (description) =>{
if (description.indexOf("@") !== -1){
this.setState({isSpin: true});
@ -102,7 +119,6 @@ class order_form extends Component {
subject: result.data.subject,
}
if (this.props.form_type === "copy" && result.data.description){
this.copyIssueAtWhoInit(result.data.description);
}
this.props.form.setFieldsValue({ ...data });
@ -213,6 +229,7 @@ class order_form extends Component {
this.props.history.push(`/${owner}/${projectsId}/issues/${result.data.id}`);
const { getDetail } = this.props;
getDetail && getDetail();
this.props.showNpsModal("createIssue", 1);
}
this.setState({
isSpin: false,
@ -239,6 +256,7 @@ class order_form extends Component {
this.props.showNotification("任务更新成功!");
const { getDetail } = this.props;
getDetail && getDetail();
this.props.showNpsModal("submitIssue", 1);
}
this.setState({
isSpin: false