From af6ad43bfe5cd28062f1188ef88fc687779fb75d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=80=9D?= <2897217417@qq.com> Date: Fri, 24 Feb 2023 15:10:05 +0800 Subject: [PATCH] =?UTF-8?q?issue=E8=AF=84=E8=AE=BA+=E9=87=8C=E7=A8=8B?= =?UTF-8?q?=E7=A2=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 2 +- src/forge/Issues/Component/comments/add.jsx | 15 - .../Issues/Component/comments/editComment.jsx | 127 +++++++++ src/forge/Issues/Component/comments/list.jsx | 209 ++++++++++++++ src/forge/Issues/Component/comments/list.scss | 92 ++++++ src/forge/Issues/Pages/details.jsx | 13 +- src/forge/Issues/Pages/new.jsx | 2 + src/forge/Issues/index.jsx | 7 + src/forge/Main/Detail.js | 4 +- src/forge/Order/Milepost.js | 227 ++++++--------- src/forge/Order/MilepostDetail.jsx | 267 ++++++++++++++++++ ...ilepostDetail.js => MilepostDetail_old.js} | 116 +++++--- src/forge/Order/component/allMenus.jsx | 140 +++++++++ src/forge/Order/milepost.scss | 90 ++++++ src/forge/Order/order.scss | 8 - src/forge/Upload/Index.js | 13 + 16 files changed, 1118 insertions(+), 214 deletions(-) delete mode 100644 src/forge/Issues/Component/comments/add.jsx create mode 100644 src/forge/Issues/Component/comments/editComment.jsx create mode 100644 src/forge/Issues/Component/comments/list.jsx create mode 100644 src/forge/Issues/Component/comments/list.scss create mode 100644 src/forge/Order/MilepostDetail.jsx rename src/forge/Order/{MilepostDetail.js => MilepostDetail_old.js} (74%) create mode 100644 src/forge/Order/component/allMenus.jsx create mode 100644 src/forge/Order/milepost.scss diff --git a/package-lock.json b/package-lock.json index 03f68eeb4..a9f618723 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11299,7 +11299,7 @@ "dependencies": { "yallist": { "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "resolved": "http://173.15.15.82:8081/repository/npm-all/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true } diff --git a/src/forge/Issues/Component/comments/add.jsx b/src/forge/Issues/Component/comments/add.jsx deleted file mode 100644 index 4cad4963d..000000000 --- a/src/forge/Issues/Component/comments/add.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import { Input } from 'antd'; - - -function Add(){ - return( -
- -
- -
-
- ) -} -export default Add; \ No newline at end of file diff --git a/src/forge/Issues/Component/comments/editComment.jsx b/src/forge/Issues/Component/comments/editComment.jsx new file mode 100644 index 000000000..16cd5876a --- /dev/null +++ b/src/forge/Issues/Component/comments/editComment.jsx @@ -0,0 +1,127 @@ +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} = 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(){ + 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( +
+ + + +
+ {setQuillFlag(false);setContent(value);}} + isCanAtme = {true} + changeAtWhoLoginList = {(loginList)=>{setAtWhoLoginList(loginList); setAttachmentClean(true)}} + owner = {owner} + projectsId = {projectsId} + > +

+ {quillFlag && 请输入评论内容} +

+ {setFileList(fileList)}} + icon={ + + } + size={100} + showNotification={showNotification} + defaultFileList={props.defaultFileList} + /> +

+ + +

+
+
+ ) +} +export default EditComment; \ No newline at end of file diff --git a/src/forge/Issues/Component/comments/list.jsx b/src/forge/Issues/Component/comments/list.jsx new file mode 100644 index 000000000..53fe95a92 --- /dev/null +++ b/src/forge/Issues/Component/comments/list.jsx @@ -0,0 +1,209 @@ +import { Button, Popconfirm, Radio, Input, message } from 'antd'; +import axios from 'axios'; +import React, { Fragment } from 'react'; +import { getImageUrl, timeAgo } from 'educoder'; +import { useEffect } from 'react'; +import { useState } from 'react'; +import RenderHtml from '../../../../components/render-html'; +import Attachment from '../../../Upload/attachment'; +import EditComment from './editComment'; +import './list.scss'; +import Nodata from '../../../Nodata'; + +function IssueCommentList(props){ + const{history: {location}, reload, reloadComment, current_user, showNotification, current_user:{login, admin, image_url}, isManager, showLoginDialog} = props; + const {owner, projectsId, index} = props.match.params; + const [category, setCategory] = useState('all'); + const [journals, setJournals] = useState(undefined); + // 是否展示新建/编辑评论部分 + 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); + // 操作记录 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' + } + + useEffect(()=>{ + axios.get(`/v1/${owner}/${projectsId}/issues/${index}/journals`,{params:{ + category + }}).then(res=>{ + if(res && res.data){ + const {journals} = 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(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; + for (let index = 0; index < item.count; index++) { + journals[item.start+index].start = journals[item.start].id; + journals[item.start+index].closeAndSpan = true; + } + }) + } + console.log('journals', journals); + setJournals(journals); + } + }) + }, [reload, category]) + + function commentCtx(v){ + return ; + }; + + // 删除评论内容 + 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( +
+ {/* 添加评论 */} +
+ {login ? showEdit === 1 ? :
+ +
{setShowEdit(1)}}> + +
+
:
+ {/* 未登录用户 */} + {showLoginDialog()}}>登录并参与评论与回复 +
} +
+ {/* 全部 / 评论 / 操作日志 */} +
+ {setCategory(e.target.value)}} value={category}> + 全部 + 评论 + 操作日志 + +
+ {/* 评论/操作日志 展示列表 */} +
+ {journals && (journals.length > 0 && journals.map(item=>{return item.is_journal_detail ? (!item.closeAndSpan || item.id === item.start || open === item.start) ?
+ {/* 操作日志 */} +
+
+
+ + + {item.user.name} + + {timeAgo(item.created_at)} +
+ {(item.closeAndSpan && item.id === item.start) && {setOpen(open === item.id ? undefined : item.id)}}>{open === item.id ? '关闭' : '展开'}全部操作日志} +
+
+
: '' :
+ {/* 评论 */} + {/* 判断是否是编辑状态 */} + {(showEdit === 2 && updateId === item.id) ?
:
+
+
+
+
+ + {item.user.name} + {timeAgo(item.created_at)} +
+ {login &&
+ {/* 平台管理员/仓库管理员/发布评论者 */} + {(admin || isManager || login === item.user.login) && 0 ? '子评论也将被一起删除。' : ''}`} + okText="是" + cancelText="否" + onConfirm={() => deleteComment(item.id)} + > + + } + {/* 仅评论者可修改 */} + {login === item.user.login && } + +
} +
+
{commentCtx(item.notes)}
+ {item && item.attachments && item.attachments.length > 0 &&
} +
+
} + {showEdit === 3 && replyId === item.id &&
} + {/* 评论回复部分 */} + {item.children_journals.map(i =>{return
+ {(showEdit === 4 && updateId === i.id) ?
:
+
+
+ + {i.user.name} + 回复 + {i.reply_user.name} + {timeAgo(i.created_at)} +
+ {login &&
+ {/* 平台管理员/仓库管理员/发布评论者/回复评论者 */} + {(admin || isManager || login === i.user.login || login === i.reply_user.login) && deleteComment(item.id)} + > + + } + {/* 仅回复评论者可修改 */} + {login === i.user.login && } + +
} +
+
{commentCtx(i.notes)}
+ {i && i.attachments && i.attachments.length > 0 &&
} +
} + {showEdit === 5 && replyId === i.id &&
} +
})} +
}))} +
+ {/* 无数据 */} + {journals && !journals.length && } +
+ ) +} +export default IssueCommentList; \ No newline at end of file diff --git a/src/forge/Issues/Component/comments/list.scss b/src/forge/Issues/Component/comments/list.scss new file mode 100644 index 000000000..770d64f00 --- /dev/null +++ b/src/forge/Issues/Component/comments/list.scss @@ -0,0 +1,92 @@ +.typeActionBox{ + padding: 17px 20px 10px; + background-color:#fafcff; + border:1px solid rgba(42, 97, 255, 0.23); + border-radius:4px; + .typeActionRadio{ + color: #333; + display: inline-flex; + align-items: center; + margin-right: 35px; + } +} +.commentUserImg{ + height: 26px; + width: 26px; + border-radius: 50%; + object-fit: cover; +} +.commentsBox{ + .iconBackBox{ + display: inline-block; + width:24px; + height:24px; + border-radius: 50%; + line-height: 24px; + text-align: center; + background-color:#f2f3f5; + } + .logContent{ + display: flex; + align-items: center; + justify-content: space-between; + } + .operationLogTopBor, .operationLogBottomBor{ + width: 1px; + height: 20px; + background-color:#eeeeee; + margin-left: 12px; + } + .operationLogTopBor{ + margin-bottom: -3px; + } + .operationLogBottomBor{ + margin-top: -4px; + } + .timeAgo{ + color:#acb0bf; + } + .operationLog:last-child .operationLogBottomBor, .operationLog:first-child .operationLogTopBor{ + display: none; + } + .commentContent, .commentContentBox+.operationLog{ + border-top: 1px solid #eeeeee; + } + .commentContentBox:first-of-type .commentContent{ + border-top: none; + } + .flexCenter{ + display: flex; + align-items: center; + justify-content: space-between; + } + .contentHtml, .commentReply{ + margin-left: 34px; + } + .commentReply{ + padding: 15px 0; + border-top: 1px solid #eeeeee; + } +} +.primaryColor, .primaryColor:link{ + color: $primary-color; +} +.attachmentBox{ + margin-top: -8px; + margin-left: 27px; +} +.color-grey-89{ + color: #898d9d; +} +// 添加评论样式 +.unLoginComment{ + width:871px; + 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; + } +} \ No newline at end of file diff --git a/src/forge/Issues/Pages/details.jsx b/src/forge/Issues/Pages/details.jsx index a5d4560c8..117bf0c72 100644 --- a/src/forge/Issues/Pages/details.jsx +++ b/src/forge/Issues/Pages/details.jsx @@ -5,7 +5,6 @@ import { Link } from 'react-router-dom'; import { Box , LongWidth } from '../../Component/layout'; import RenderHtml from "../../../components/render-html"; import EditMenus from '../Component/editMenus'; -import Add from '../Component/comments/add'; import NewPanel from '../Component/newPanel'; import Attachments from "../../Upload/attachment"; import AddTagsBox from '../Component/addTagsBox'; @@ -14,6 +13,7 @@ import Date from '../Component/date'; import moment from 'moment'; import Copy from '../Component/copy'; import axios from 'axios'; +import CommentList from '../Component/comments/list'; function Details(props){ const [ details , setDetails ] = useState(undefined); @@ -48,6 +48,7 @@ function Details(props){ const [ start_date, setStartDate ] = useState(""); const [ due_date, setDueDate ] = useState(""); + const [ commentReload, setCommentReload] = useState(undefined); const pathname = props.history.location.pathname; useEffect(()=>{ @@ -228,6 +229,8 @@ function Details(props){ }).then(result=>{ if(result){ Init(); + // 更新操作日志 + setCommentReload(Math.random()); } }).catch(error=>{}) } @@ -259,13 +262,14 @@ function Details(props){ }).then(result=>{ if(result){ props.showNotification("疑修更新成功!"); + // 刷新操作记录 + setCommentReload(Math.random()); Init(); setEdit(false); } }).catch(error=>{}) }else{ // 调用新建保存接口 - console.log(branchId,statusId,prioritiesId,millstoneId,tagId, charegeId); const url = `/v1/${owner}/${projectsId}/issues`; axios.post(url,{ ...params, @@ -368,10 +372,7 @@ function Details(props){ } - - {/*
- -
*/} + {setCommentReload(Math.random())}}/>
{saveForeach(ids);let copyname = { ...names,assigner_name:name};setNames(copyname);setCharegeId(ids);}} list={chargeList} value={charegeId} searchFlag searchFunc={(value)=>{setCharge(value)}} double={5} editFlag={details && !details.user_permission}/> diff --git a/src/forge/Issues/Pages/new.jsx b/src/forge/Issues/Pages/new.jsx index 06bbc5a92..b6f1f33ef 100644 --- a/src/forge/Issues/Pages/new.jsx +++ b/src/forge/Issues/Pages/new.jsx @@ -9,6 +9,8 @@ import axios from 'axios'; import moment from 'moment'; function New(props){ + // 里程碑id + const milepostId = props.match.params.milepostId; const [ visible , setVisible ] = useState(false); diff --git a/src/forge/Issues/index.jsx b/src/forge/Issues/index.jsx index c522c78de..7fc6a36a8 100644 --- a/src/forge/Issues/index.jsx +++ b/src/forge/Issues/index.jsx @@ -40,6 +40,13 @@ function Index(props){ )} > + {/* 里程碑创建issue */} + ( + + )} + > ( diff --git a/src/forge/Main/Detail.js b/src/forge/Main/Detail.js index 07294a8fd..13f8d2a6b 100644 --- a/src/forge/Main/Detail.js +++ b/src/forge/Main/Detail.js @@ -89,7 +89,7 @@ const UpdateMerge = Loadable({ }) const MilepostDetail = Loadable({ - loader: () => import('../Order/MilepostDetail'), + loader: () => import('../Order/MilepostDetail.jsx'), loading: Loading, }) const WatchUsers = Loadable({ @@ -767,7 +767,7 @@ class Detail extends Component { } > {/*里程碑详情*/} - () } diff --git a/src/forge/Order/Milepost.js b/src/forge/Order/Milepost.js index 45121cccd..2734179aa 100644 --- a/src/forge/Order/Milepost.js +++ b/src/forge/Order/Milepost.js @@ -1,10 +1,9 @@ import React, { Component } from 'react'; import { Link } from 'react-router-dom'; -import { Dropdown, Icon, Menu, Pagination, Typography, Popconfirm, Spin } from 'antd'; +import { Dropdown, Icon, Menu, Pagination, Typography, Popconfirm, Spin, Button } from 'antd'; import NoneData from '../Nodata'; import axios from 'axios'; -import './order.scss'; -import CheckProfile from '../Component/ProfileModal/Profile'; +import './milepost.scss'; const { Text } = Typography; @@ -13,16 +12,12 @@ class Milepost extends Component { 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 } } @@ -31,7 +26,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 +39,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 +59,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'); } - } 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); const { getDetail } = this.props; getDetail && getDetail(); } @@ -122,7 +95,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); const { getDetail } = this.props; getDetail && getDetail(); } @@ -136,8 +109,7 @@ class Milepost extends Component { this.setState({ page }) - const { status } = this.state; - this.getList( page , status ); + this.getList(page, this.state.limit, this.state.status); } // 排序 @@ -146,29 +118,24 @@ class Milepost extends Component { order_name: e.key, order_type: e.item.props.value }) - this.getList(1, this.state.status, e.item.props.value, e.key); + this.getList(1, this.state.limit, this.state.status, e.key, e.item.props.value); } - - //控制新建标签页是否显示 - newshow = () => { + // 分页组件 + onShowSizeChange = (current, pageSize) =>{ this.setState({ - display: 'block' - }); - - }; - newclose = () => { - this.setState({ - display: 'none' - }); - }; - + page: 1, + limit: pageSize + }) + this.getList(1, pageSize, this.state.status); + } render() { - const { data, limit, page, openselect, closeselect, spinings } = this.state; + const { data, limit, page, spinings, status } = this.state; const { projectsId , owner } = this.props.match.params; + const { isManager, isDeveloper} = this.props; const menu = ( - + 到期日从后到先 到期日从先到后 完成度从低到高 @@ -181,100 +148,66 @@ class Milepost extends Component { return ( -
-
-
- 里程碑{data && data.issue_tags_count}已创建 -
+
+ {/* 创建里程碑按钮 */} +
+ {(isManager || isDeveloper) && }
-
-
-
  • this.opneMilelist(1)}>{data && data.open_count}个开启中
  • -
  • this.opneMilelist(2)}>{data && data.closed_count}个已关闭
  • -
    -
    -
      -
    • - - 排序 - -
    • -
    - { - data && data.user_admin_or_member ? - {this.props.history.push(`/${owner}/${projectsId}/milestones/new`)}}>新的里程碑 - : '' - } + {/* 里程碑列表表头 */} +
    +
    + this.opneMilelist(1)}>开启中{data && data.opening_milestone_count} + this.opneMilelist(2)}>已关闭{data && data.closed_milestone_count}
    + + 排序 +
    - { - data && data.versions && data.versions.length > 0 - && -
    - { - data.versions.map((item, key) => { - return ( -
    -
    -
    -
    - - {item.name} -
    -
    -
    -
    -
    -
    - - {item.effective_date || "暂无截止时间"} -
    -
    - - {item.open_issues_count}个开启 -
    -
    - - {item.close_issues_count}个关闭 -
    -
    - { - data && data.user_admin_or_member ? - - : '' - } -
    -
    -
    - {item.description} -
    -
    + {/* 里程碑列表展示 */} + {data && data.milestones && data.milestones.length === 0 &&
    } + {data && data.milestones && data.milestones.length > 0 &&
    + {data.milestones.map((item, key)=>{ + return
    +
    +
    + + {item.name} +
    + {item.description} +
    +
    +
    + + {item.effective_date || "暂无截止时间"} +
    +
    + {item.opened_issues_count || 0}个开启 + | + {item.close_issues_count || 0}个关闭 +
    + { + (isManager || isDeveloper) && + } +
    - } - { data && data.versions && data.versions.length === 0 && } + })} +
    } { - data && data.versions_count > limit ? -
    - + data && data.total_count > limit ? +
    +
    : "" }
    diff --git a/src/forge/Order/MilepostDetail.jsx b/src/forge/Order/MilepostDetail.jsx new file mode 100644 index 000000000..863c8ad64 --- /dev/null +++ b/src/forge/Order/MilepostDetail.jsx @@ -0,0 +1,267 @@ +import React , { useRef , useState , useEffect } from 'react'; +import { Dropdown , Menu , Icon , Input , Checkbox , Pagination, Button , Tooltip ,Spin } from 'antd'; +import { Link } from "react-router-dom"; +import issueEmp from '../Issues/Img/issue-big.png'; +import AllMenus from './component/allMenus'; +import Datas from '../Issues/Component/datas'; +import axios from 'axios'; +import { FlexAJ } from '../Component/layout'; +import './milepost.scss'; +import './order.scss'; +import '../Issues/index.scss'; +const { Search } = Input; + +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[ value , setValue ] = useState(""); + 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 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; + + 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, + // participant_category:aboutMe, + // keyword,category, + ...params + // sort_direction:(params && params.sort_by) ? ((params.sort_by === 1 || params.sort_by === 3) ? "desc" : "asc") :undefined, + // sort_by:(params && params.sort_by) ? ((params.sort_by === 1 || params.sort_by === 2) ? "created_on" : "updated_on") : undefined + } + }).then(result=>{ + if(result){ + const {milestone, issues, total_issues_count, closed_issues_count, opened_issues_count} = result.data; + setMilepost(milestone); + setIssueList(issues); + setTotal(total_issues_count); + setClosedCount(closed_issues_count); + setOpenedCount(opened_issues_count); + const ids = issues.length>0 ? issues.map(i=>{return i.id}) : []; + setAllIds(ids); + } + }).then(error=>{console.log("error:",error)}) + } + + // 第一个下拉搜索 + const menu = ( + + 全部 + 与我相关 + 我负责的 + 我创建的 + @我的 + + ) + function chooseAboutMe(e){ + setCategory("all"); + setAboutMe(e.key); + } + + //清除筛选条件 + function clearCondition(){ + setKeyword(undefined); + setAboutMe("aboutme"); + Init(); + // 清除下拉选项 + menuRef.current && menuRef.current.clearChoose(); + + } + + // 全选所有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], + ids: allValue, + issue_tag_ids: [updateIds && updateIds.issue_tag_ids], + milestone_id: updateIds && updateIds.milestone_id, + priority_id: updateIds && updateIds.issue_priorities_id, + status_id: updateIds && updateIds.status_id + }).then(result=>{ + if(result){ + Init(); + cancelUpdate(); + } + }).catch(error=>{}) + } + + // 切换页码 + function changepage(page){ + setPage(page); + } + + function chooseFunc(ids){ + if(allValue && allValue.length>0){ + // 将ids保存下来以便修改 + setUpdateIds(ids); + }else{ + Init(ids); + } + } + + return( +
    + {milepost && +
    +

    {milepost.name}

    +

    + + + {milepost.effective_date || '暂无截止时间'} + + {(milepost.percent || milepost.percent===0) ? {milepost.percent > 0 ? milepost.percent.toFixed(2):milepost.percent}%完成 :"" } +

    +
    +
    + { + (current_user && current_user.login) && ( projectDetail && projectDetail.permission && projectDetail.permission !== "Reporter") ? + 编辑里程碑 + :"" + } + { + current_user && current_user.login ? + + : + } +
    +
    } +
    +
    +
    + + { + allValue && allValue.length>0 ? + 选择{allValue.length}个issue + : +
      +
    • setCategory("all")}>全部{total}
    • +
    • setCategory("opened")}>开启中{openedCount}
    • +
    • setCategory("closed")}>已关闭{closedCount}
    • +
    + } +
    +
    + 0} + owner={owner} + projectsId={projectsId} + chooseFunc={chooseFunc} + /> + { + allValue && allValue.length>0 ? +
    + + +
    + :"" + } +
    +
    + { + total === 0 && +
    + +

    欢迎使用疑修(Issue)

    +

    疑修用于记录与跟踪待办事项、项目bug、功能需求等。在使用之前,请您先创建一个疑修

    +
    + } + { + total > 0 && + + +
    + {issueList.map((item,key)=>{ + return( + } + item={item} + owner={owner} + projectsId={projectsId} + /> + ) + }) + } +
    +
    + { + total > 10 && +
    + {setPage(1);setLimit(pageSize);}}/> +
    + } +
    + } + {total === undefined &&
    } +
    +
    + ) +} +export default MilepostDetail; \ No newline at end of file diff --git a/src/forge/Order/MilepostDetail.js b/src/forge/Order/MilepostDetail_old.js similarity index 74% rename from src/forge/Order/MilepostDetail.js rename to src/forge/Order/MilepostDetail_old.js index 9505fe535..eb7c30dbb 100644 --- a/src/forge/Order/MilepostDetail.js +++ b/src/forge/Order/MilepostDetail_old.js @@ -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 {
    ) return ( -
    -
    -

    {data && data.name}

    - - - - +
    + +
    +

    {data && data.name}

    +

    + + { data && data.effective_date ? - {data && data.effective_date} + {data && data.effective_date} : - 暂无截止时间 + 暂无截止时间 } - {data && (data.percent || data.percent===0) ? {data.percent > 0 ? data.percent.toFixed(2):data.percent}%完成 :"" } - -

    + {data && (data.percent || data.percent===0) ? {data.percent > 0 ? data.percent.toFixed(2):data.percent}%完成 :"" } +

    +
    +
    + { + (current_user && current_user.login) && ( projectDetail && projectDetail.permission && projectDetail.permission !== "Reporter") ? + 编辑里程碑 + :"" + } + { + issueFlag ? (current_user && current_user.login ? + + :) + :"" + } +
    + + + +
    +
    + { - (current_user && current_user.login) && ( projectDetail && projectDetail.permission && projectDetail.permission !== "Reporter") ? - 编辑里程碑 - :"" - } - { - issueFlag ? (current_user && current_user.login ? - {this.props.history.push(`/${owner}/${projectsId}/issues/${meilid}/new`)}} className="topWrapper_btn">创建疑修 + allValue && allValue.length>0 ? + 选择{allValue.length}个issue : - 创建疑修 - ):"" +
      +
    • this.category("all")}>全部{total}
    • +
    • this.category("opened")}>开启中{openedCount}
    • +
    • this.category("closed")}>已关闭{closedCount}
    • +
    }
    - -
    - - -
    +
    + 0} + owner={owner} + projectsId={projectsId} + chooseFunc={chooseFunc} + /> + { + allValue && allValue.length>0 ? +
    + + + +
    + :"" + } +
    +
    + {/*
    • this.openorder("1")}>{data && data.open_issues_count}个开启中
    • @@ -352,7 +398,7 @@ class MilepostDetail extends Component {
    :"" } -
    +
    */}
    ) diff --git a/src/forge/Order/component/allMenus.jsx b/src/forge/Order/component/allMenus.jsx new file mode 100644 index 000000000..85cd991ce --- /dev/null +++ b/src/forge/Order/component/allMenus.jsx @@ -0,0 +1,140 @@ +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:"最早更新"} +] + +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]) + + function getCharge(){ + 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.join(",")}; + let copyname = { ...names,author_name:name} + setIds(copy);setNames(copyname); + chooseFunc(copy); + } + + // 获取里程碑列表 + 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( +
      + { !update && setAuthor(value)} + chooseFunc={(id,name)=>choose(id,name,'author_name')} + /> + } + setTag(value)} + chooseFunc={(id,name)=>{let copy = {...ids,issue_tag_ids:id.join(",")};let copyname = { ...names,issue_tag_name:name};setNames(copyname);setIds(copy);chooseFunc(copy)}} + /> + setCharge(value)} + chooseFunc={(id,name)=>{let copy = {...ids,assigner_id:id.join(",")};let copyname = { ...names,assigner_name:name};setNames(copyname);setIds(copy);chooseFunc(copy)}} + /> + { !update && {let copy = {...ids,sort_by:id.join(",")};let copyname = { ...names,sortby_name:name};setNames(copyname);setIds(copy);chooseFunc(copy)}} + /> + } + {update && setMillstone(value)} + chooseFunc={(id,name)=>{let copy = {...ids,milestone_id:id.join(",")};let copyname = { ...names,milestone_name:name};setNames(copyname);setIds(copy);chooseFunc(copy)}} + />} +
    + ) +} +export default forwardRef(AllMenus); \ No newline at end of file diff --git a/src/forge/Order/milepost.scss b/src/forge/Order/milepost.scss new file mode 100644 index 000000000..884134ed0 --- /dev/null +++ b/src/forge/Order/milepost.scss @@ -0,0 +1,90 @@ +.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{ + font-weight:700; + color:#333333; + } + .statusCount{ + background-color:rgba(70, 106, 255, 0.09); + border-radius:10px; + margin-left: 5px; + padding: 4px 7px; + color:#666666; + font-weight: normal; + } +} +.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: 500px; + } + .actionMileBox{ + width: 550px; + } +} +.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; +} +// 浅灰色按钮 +.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; +} \ No newline at end of file diff --git a/src/forge/Order/order.scss b/src/forge/Order/order.scss index bff07c9a8..d0e64df29 100644 --- a/src/forge/Order/order.scss +++ b/src/forge/Order/order.scss @@ -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; } diff --git a/src/forge/Upload/Index.js b/src/forge/Upload/Index.js index ea8ab180e..616c90141 100644 --- a/src/forge/Upload/Index.js +++ b/src/forge/Upload/Index.js @@ -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)=>{