diff --git a/package-lock.json b/package-lock.json
index b99278538..219e90579 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14143,6 +14143,27 @@
"object-assign": "^4.1.1"
}
},
+ "react-copy-to-clipboard": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmmirror.com/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz",
+ "integrity": "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==",
+ "requires": {
+ "copy-to-clipboard": "^3.3.1",
+ "prop-types": "^15.8.1"
+ },
+ "dependencies": {
+ "prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ }
+ }
+ },
"react-countup": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/react-countup/-/react-countup-6.1.0.tgz",
diff --git a/package.json b/package.json
index 3b421e973..13dddda43 100644
--- a/package.json
+++ b/package.json
@@ -84,6 +84,7 @@
"react-color": "^2.18.0",
"react-content-loader": "^3.1.1",
"react-cookies": "^0.1.1",
+ "react-copy-to-clipboard": "^5.1.0",
"react-countup": "^6.1.0",
"react-cropper": "^2.1.8",
"react-datepicker": "^2.14.1",
diff --git a/src/forge/Issues/Component/addTagsBox.jsx b/src/forge/Issues/Component/addTagsBox.jsx
new file mode 100644
index 000000000..6b0c72108
--- /dev/null
+++ b/src/forge/Issues/Component/addTagsBox.jsx
@@ -0,0 +1,65 @@
+import React , { useState , useEffect } from 'react';
+import { Form , Input, Button } from 'antd';
+import ColorCard from './colorCard';
+import axios from 'axios';
+
+function AddTagsBox({onSuccess , form ,owner , projectsId,onCancel}){
+ const { getFieldDecorator , getFieldsValue , validateFields } = form;
+ const [ colors , setColors ] = useState(undefined);
+ const [ defaultColor , setDefaultColor ]= useState(undefined);
+
+ useEffect(()=>{
+ const c = Math.random().toString(16).substr(-6);
+ c && setDefaultColor(`#${c}`);
+ },[])
+
+ function saveFunc(){
+ validateFields((error,values)=>{
+ if(!error){
+ const { desc , name } = getFieldsValue();
+ const url = `/v1/${owner}/${projectsId}/issue_tags`;
+ axios.post(url,{
+ name,description:desc,color:colors || defaultColor
+ }).then(result=>{
+ result && onSuccess();
+ }).catch(error=>{})
+ }
+ })
+ }
+
+ function getColor(colors){
+ setColors(colors);
+ }
+
+ return(
+
+ {getFieldDecorator('desc',{rules:[],
+ validateTrigger:"onInput",
+ })()}
+
+
+
+
+
+
+ )
+}
+export default Form.create({ name: 'Bind' })(AddTagsBox);
\ No newline at end of file
diff --git a/src/forge/Issues/Component/allMenus.jsx b/src/forge/Issues/Component/allMenus.jsx
new file mode 100644
index 000000000..90afca543
--- /dev/null
+++ b/src/forge/Issues/Component/allMenus.jsx
@@ -0,0 +1,219 @@
+import React,{ useState , useEffect , forwardRef ,useImperativeHandle , useRef } from 'react';
+import Menus from './menus';
+import axios from 'axios';
+
+const array =[
+ {id:1,name:"最新创建"},
+ {id:2,name:"最早创建"},
+ {id:3,name:"最新更新"},
+ {id:4,name:"最早更新"},
+ {id:5,name:"高优先级"},
+ {id:6,name:"低优先级"},
+ {id:7,name:"高悬赏金额"},
+ {id:8,name:"低悬赏金额"}
+]
+const array1 =[
+ {id:1,name:"最新创建"},
+ {id:2,name:"最早创建"},
+ {id:3,name:"最新更新"},
+ {id:4,name:"最早更新"},
+ {id:5,name:"高优先级"},
+ {id:6,name:"低优先级"},
+]
+
+function AllMenus({owner,projectsId,chooseFunc,update,defaultNames,defaultIds,open_blockchain},ref){
+ // 列表右侧所有筛选项的值
+ const [ authorList , setAuthorList ] = useState(undefined);
+ const [ author , setAuthor ] = useState(undefined);
+ const [ tagList , setTagList ] = useState(undefined);
+ const [ tag , setTag ] = useState(undefined);
+ const [ millstoneList , setMillstoneList ] = useState(undefined);
+ const [ millstone , setMillstone ] = useState(undefined);
+ const [ chargeList , setChargeList ] = useState(undefined);
+ const [ charge , setCharge ] = useState(undefined);
+ const [ statusList , setStatusList ] = useState(undefined);
+ const [ prioritiesList , setPrioritiesList ] = useState(undefined);
+ const [ ids , setIds ] = useState({author_id:undefined,issue_priorities_id:undefined,issue_tag_ids:undefined,milestone_id:undefined,sort_by:undefined,status_id:undefined,assigner_id:undefined});
+ const [ names , setNames ] = useState({author_name:undefined,issue_priorities_name:undefined,issue_tag_name:undefined,milestone_name:undefined,sortby_name:undefined,status_name:undefined,assigner_name:undefined});
+
+ useEffect(()=>{
+ if(defaultNames){
+ setNames(defaultNames);
+ }
+ },[defaultNames])
+
+ useEffect(()=>{
+ if(defaultIds){
+ setIds(defaultIds);
+ }
+ },[defaultIds])
+
+ useImperativeHandle(ref, () => ({
+ clearChoose: () => {
+ // 父组件按钮:清除筛选条件,将Ids\names里的字段全部改为undefined
+ setIds({author_id:undefined,issue_tag_ids:undefined,issue_priorities_id:undefined,milestone_id:undefined,sort_by:undefined,status_id:undefined,assigner_id:undefined});
+ setNames({author_name:undefined,issue_tag_name:undefined,issue_priorities_name:undefined,milestone_name:undefined,sortby_name:undefined,status_name:undefined,assigner_name:undefined});
+ }
+ }))
+ // 获取发布人列表
+ useEffect(()=>{
+ getSendPerson();
+ },[author])
+
+ function getSendPerson(){
+ const url = `/v1/${owner}/${projectsId}/issue_authors`;
+ axios.get(url,{params:{keyword:author,only_name:true}}).then(result=>{
+ if(result && result.data){
+ setAuthorList(result.data.authors);
+ }
+ })
+ }
+ // 获取标记列表
+ useEffect(()=>{
+ getSign();
+ },[tag])
+ function getSign(){
+ const url = `/v1/${owner}/${projectsId}/issue_tags`;
+ axios.get(url,{params:{keyword:tag,only_name:true}}).then(result=>{
+ if(result && result.data){
+ setTagList(result.data.issue_tags);
+ }
+ })
+ }
+ // 获取里程碑列表
+ useEffect(()=>{
+ getMillstone();
+ },[millstone])
+
+ function getMillstone(){
+ const url = `/v1/${owner}/${projectsId}/milestones`;
+ axios.get(url,{params:{keyword:millstone,only_name:true}}).then(result=>{
+ if(result && result.data){
+ setMillstoneList(result.data.milestones);
+ }
+ })
+ }
+
+ // 获取负责人列表
+ useEffect(()=>{
+ getCharge();
+ },[charge,update])
+
+ function getCharge(){
+ if(update){
+ const url = `/v1/${owner}/${projectsId}/collaborators`;
+ axios.get(url,{params:{keyword:charge,only_name:true}}).then(result=>{
+ if(result && result.data){
+ setChargeList(result.data.collaborators);
+ }
+ })
+ }else{
+ const url = `/v1/${owner}/${projectsId}/issue_assigners`;
+ axios.get(url,{params:{keyword:charge,only_name:true}}).then(result=>{
+ if(result && result.data){
+ setChargeList(result.data.assigners);
+ }
+ })
+ }
+ }
+ // 获取优先级列表
+ useEffect(()=>{
+ getPriorities();
+ },[])
+
+ function getPriorities(){
+ const url = `/v1/${owner}/${projectsId}/issue_priorities`;
+ axios.get(url).then(result=>{
+ if(result && result.data){
+ setPrioritiesList(result.data.priorities);
+ }
+ })
+ }
+ // 获取状态列表
+ useEffect(()=>{
+ getStatus();
+ },[])
+
+ function getStatus(){
+ const url = `/v1/${owner}/${projectsId}/issue_statues`;
+ axios.get(url).then(result=>{
+ if(result && result.data){
+ setStatusList(result.data.statues);
+ }
+ })
+ }
+
+ function choose(id,name){
+ let copy = {...ids,author_id:id && id.length>0 ? id.join(","):undefined};
+ let copyname = { ...names,author_name:name};
+ setIds(copy);setNames(copyname);
+ chooseFunc(copy,copyname);
+ }
+
+ return(
+
+ { !update && setAuthor(value)}
+ chooseFunc={(id,name)=>choose(id,name,'author_name')}
+ />
+ }
+ {update && {let copy = {...ids,issue_priorities_id:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,issue_priorities_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}}
+ />
+ }
+ 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)}}
+ />
+ 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)}}
+ />
+ 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)}}
+ />
+ {let copy = {...ids,status_id:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,status_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}}
+ />
+ { !update && {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)}}
+ />
+ }
+
+ )
+}
+export default forwardRef(AllMenus);
\ No newline at end of file
diff --git a/src/forge/Issues/Component/chooseMenu.jsx b/src/forge/Issues/Component/chooseMenu.jsx
new file mode 100644
index 000000000..4da6c5e4d
--- /dev/null
+++ b/src/forge/Issues/Component/chooseMenu.jsx
@@ -0,0 +1,305 @@
+import React ,{ useState , useEffect , useRef , forwardRef } from 'react';
+import { findDOMNode } from 'react-dom';
+import { Dropdown , Menu , Input , Button, Form , Tooltip } from 'antd';
+import { getImageUrl } from 'educoder';
+import AddTagsBox from './addTagsBox';
+
+const { Search } = Input;
+/**
+ *
+ * @param {placeholder} placeholder:默认显示内容(为空时)
+ * @param {editFlag} editFlag:是否有编辑权限
+ * @param {searchFlag} searchFlag:是否显示搜索框
+ * @param {selectValueList} selectValueList:将选中的list保存
+ * @param {searchFunc} searchFunc:搜索方法
+ * @param {onAdd} onAdd:标记管理
+ * @param {headImg} headImg:是否显示头像
+ * @param {menus} menus:下拉列表
+ * @param {chooseFunc} chooseFunc:选择下拉列表
+ * @param {double} double:undefined为单选,有值就是最多选的数量
+ * @param {colorFlag} colorFlag:选中值需根据颜色显示背景
+ * @param {mustFlag} mustFlag:必须选择一个
+ * @param {removeFlag} removeFlag:移除按钮
+ * @returns
+ */
+function ChooseMenu({
+ placeholder ="未设置",
+ editFlag,
+ searchFlag,
+ selectValueList,
+ searchFunc,
+ onAdd,
+ headImg,
+ menus,
+ auto,
+ chooseFunc,double,colorFlag,mustFlag,removeFlag,owner,projectsId
+}){
+ const [ visible , setVisible ] = useState(false);
+ const [ searchValue , setSearchValue ]= useState(undefined);
+ const [ valuesId , setValuesId ] = useState([]);
+ const [ count , setCount ] = useState(double);
+ const [ content , setContent ] = useState(false);
+
+ const [ saveList , setSaveList ] = useState(undefined);
+
+ const refFa = useRef(null);
+ const refBox = useRef(null);
+
+ useEffect(() => {
+ document.addEventListener('mousedown', clickMe , false);
+ return () => {
+ window.removeEventListener("mousedown", clickMe, false);
+ }
+ }, [])
+
+ const clickMe = ({ target }) => {
+ // 查找父组件
+ const faComponent = findDOMNode(refFa.current);
+ const boxComponent = findDOMNode(refBox.current);
+ if (faComponent && boxComponent) {
+ const isChild = faComponent.contains(target);
+ const isBox = boxComponent.contains(target);
+ const arr = ["removeicon","iconfont icon-guanbi font-12 color-white","iconfont icon-guanbi font-12 color-blue","icon-a-bianji12","color-blue font-15 tagManage","iconfont icon-xiangzuojiantou font-15 color-grey mr5 cursor","cover","ant-btn cancelTags"];
+ const pointer = arr.includes(target.className);
+ if(!isChild && !isBox && !pointer){
+ setVisible(false);
+ setContent(false);
+ }
+ }
+ }
+
+ // 根据选中的列表循环出id数组
+ useEffect(()=>{
+ if(selectValueList && selectValueList.length > 0 && visible ){
+ renderSelectList(selectValueList);
+ setSaveList(selectValueList);
+ setCount(selectValueList.length);
+ }else{
+ setValuesId([]);
+ setCount(0);
+ setSaveList(undefined);
+ }
+ if(!visible){
+ setSearchValue(undefined);
+ searchFunc(undefined);
+ }
+ },[selectValueList,visible])
+
+ function renderSelectList(list){
+ let a = list && list.length > 0 && list.map((i,k)=>{
+ return i.id ? i.id.toString() : i.name
+ })
+ setValuesId(a);
+ }
+
+ // 搜索
+ function changeSearchvalue(e){
+ setSearchValue(e.target.value);
+ searchFunc(e.target.value);
+ }
+ function renderNames(nameArrs){
+ return
+ {
+ nameArrs && nameArrs.length>0?
+ nameArrs.map((i,k)=>{
+ return(
+
+ {i.image_url &&
}
+ {
+ colorFlag ?
+ (
+ colorFlag === "2" ?
+
+ {i.name}
+
+ :
+ {i.name}
+ )
+ :
+ {i.name}
+ }
+ { !colorFlag && removeFlag && removeValueFunc(i.id)}>}
+ { colorFlag && removeFlag && removeValueFunc(i.id)} style={{display:'block',right:"19px",position:"absolute"}}>}
+
+ )
+ })
+ :{placeholder}
+ }
+
+ }
+
+ // 选择项
+ function chooseMenu(i){
+ let list = saveList && saveList.length > 0 ? [...saveList]:[] ;
+ let relist = [];
+ relist = list && list.length > 0 ? list:[];
+ let filter = [];
+ if(i.id){
+ filter = list.filter(j=>j.id === i.id);
+ }else{
+ filter = list.filter(j=>j.name === i.name);
+ }
+ if(filter && filter.length > 0){
+ if(i.id){
+ relist = list.filter(j=>j.id !== i.id);
+ }else{
+ relist = list.filter(j=>j.name !== i.name);
+ }
+ }else{
+ if(double && (saveList && saveList.length >= double)){
+ setCount(-1);
+ return;
+ }
+ if(double){
+ relist.push(i);
+ }else{
+ relist = [i];
+ }
+ }
+ renderSelectList(relist);
+ setSaveList(relist);
+ setCount(relist ? relist.length :0);
+ if(!double){
+ setVisible(false);
+ setContent(false);
+ chooseFunc(relist);
+ }
+ }
+
+ // 确认按钮
+ function onSureFunc(){
+ setVisible(false);
+ setContent(false);
+ chooseFunc(saveList);
+ }
+ // 移除选择的项
+ function removeSaveFunc(id){
+ let list = [...saveList];
+ let filter = list.filter(j=>j.id !== id);
+ setSaveList(filter);
+ renderSelectList(filter);
+ setCount(filter ? filter.length :0);
+ }
+
+ // 移除后保存
+ function removeValueFunc(id){
+ let list = [...selectValueList];
+ let filter = list.filter(j=>j.id !== id);
+ setSaveList(filter);
+ renderSelectList(filter);
+ setCount(filter ? filter.length :0);
+ chooseFunc(filter);
+ }
+
+ function onSuccess(){
+ setContent(false);
+ onAdd();
+ }
+ return(
+
+
+ {placeholder}
+ {
+ !editFlag &&
+
+ {
+ !content ?
+
+ {
+ double && saveList && saveList.length>0?
+
+ {
+ saveList.map((i,k)=>{
+ return(
+ -
+ {i.name}
+ removeSaveFunc(i.id)}>
+
+ )
+ })
+ }
+
+ :""
+ }
+ { searchFunc &&
+
+
+
+ }
+ {
+ menus && menus.length >0?
+
+ :
+
+
{searchValue ? 暂无{placeholder}“{searchValue}”: `暂无${placeholder}`}
+
+ }
+
+
+ {
+ double && (count<0 ?
+
最多添加{double}个{placeholder}!
+ :
+
还可添加{double - count}个{placeholder}!
+ )
+ }
+
+ {
+ double &&
+
+
+
+
+ }
+
+ :
+ setContent(false)}
+ onSuccess={onSuccess} />
+ }
+
+ }>
+ setVisible(visible ? false : true)}>
+
+
+
+ }
+
+ 0 ? "operatevalue color-grey-3":"operatevalue"} style={{display:colorFlag?"flex":"block"}}>{renderNames(selectValueList)}
+
+ )
+}
+export default Form.create()(forwardRef(ChooseMenu));
\ No newline at end of file
diff --git a/src/forge/Issues/Component/colorCard.jsx b/src/forge/Issues/Component/colorCard.jsx
new file mode 100644
index 000000000..d63260035
--- /dev/null
+++ b/src/forge/Issues/Component/colorCard.jsx
@@ -0,0 +1,47 @@
+import React,{ useState , useEffect } from 'react';
+import { SketchPicker } from 'react-color';
+
+function ColorCard({getColor,defaultColor}){
+ const [ displayColorPicker , setDisplayColorPicker ] = useState(false);
+ const [ textcolor , setTextColor ] = useState("#F17013");
+
+
+ useEffect(()=>{
+ if(defaultColor){
+ setTextColor(defaultColor);
+ }
+ },[defaultColor])
+
+ // 切换色卡
+ function handleChange(color){
+ setTextColor(color.hex);
+ getColor(color ? color.hex : defaultColor);
+ }
+
+ // 隐藏色卡
+ function handleClick(){
+ setDisplayColorPicker(!displayColorPicker);
+ // reduction();
+ }
+
+ // 还原色卡
+ function reduction(){
+ setTextColor("#F17013");
+ // setColor({r: '241',g: '112',b: '19',a: '1',});
+ }
+ return(
+
+
+ {displayColorPicker ? (
+
+
{setDisplayColorPicker(false)}} />
+
+
+ ) : null}
+
+ )
+}
+export default ColorCard;
\ 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..a0083d2fb
--- /dev/null
+++ b/src/forge/Issues/Component/comments/editComment.jsx
@@ -0,0 +1,165 @@
+import React from 'react';
+import { Button, message } from 'antd';
+import { getImageUrl } from 'educoder';
+import MDEditor from "../../../../modules/tpm/challengesnew/tpm-md-editor";
+import Upload from "../../../../forge/Upload/Index";
+import Attachments from "../../../../forge/Upload/attachment";
+import UploadImg from "../../../../forge/Images/upload.png";
+import './list.scss';
+import '../../../../forge/Order/order.scss';
+import { useState } from 'react';
+import { Link } from 'react-router-dom';
+import axios from 'axios';
+
+function EditComment(props){
+ const {owner, projectsId, index} = props.match.params;
+ const {current_user, current_user:{login}, showLoginDialog, showNotification, reloadComment, cancelMd, parentId, replyId, updateId, showUserImg=true} = props;
+ const [content, setContent] = useState(props.content);
+ const [quillFlag, setQuillFlag] = useState(false);
+ // 确认评论按钮loading效果
+ const [journalSpin, setJournalSpin] = useState(false);
+ const [atWhoLoginList, setAtWhoLoginList] = useState(undefined);
+ const [fileList, setFileList] = useState(undefined);
+ const [attachmentClean, setAttachmentClean] = useState(true);
+ const [ save , setSave ] = useState(undefined);
+
+ useState(()=>{
+ setSave(props.defaultFileList);
+ },[props.defaultFileList])
+
+ // 评论点击函数
+ function addJournals(){
+ if(!content){
+ setQuillFlag(true);
+ return
+ }
+ setJournalSpin(true);
+ if(updateId){
+ // 修改评论
+ axios.patch(`/v1/${owner}/${projectsId}/issues/${index}/journals/${updateId}`,{
+ notes: content,
+ attachment_ids: fileList,
+ receivers_login:atWhoLoginList
+ }).then(res=>{
+ if(res && res.data){
+ // 刷新评论列表
+ reloadComment();
+ message.success('评论成功');
+ cancelMd();
+ setContent(undefined);
+ }
+ setJournalSpin(false);
+ })
+ }else{
+ // 新建评论
+ const params = {
+ parent_id: parentId,
+ reply_id: replyId,
+ notes: content,
+ attachment_ids: fileList,
+ receivers_login:atWhoLoginList
+ }
+ axios.post(`/v1/${owner}/${projectsId}/issues/${index}/journals`, params).then(res=>{
+ if(res && res.data){
+ // 刷新评论列表
+ reloadComment();
+ message.success('评论成功');
+ cancelMd();
+ setContent(undefined);
+ }
+ setJournalSpin(false);
+ })
+ }
+ };
+ function deleteLoad(id){
+ let arr = [];
+ let list = save;
+ if(list && list.length>0 ){
+ arr = list.filter(i=>i.id !== id);
+ }
+ setFileList(arr.map(i=>{return i.id}));
+ setSave(arr);
+ }
+ function UploadFunc(f){
+ let list = [];
+ let arr = [];
+ if(save && save.length>0 ){
+ arr = save.map(i=>{return i.id});
+ }
+ list = arr && arr.length>0 ? f.concat(arr) : f ;
+ setFileList(list);
+ };
+
+ return(
+
+
+

+
+
+
{setQuillFlag(false);setContent(value);}}
+ isCanAtme = {true}
+ isQuoteIssue={true}
+ changeAtWhoLoginList = {(loginList)=>{setAtWhoLoginList(loginList); setAttachmentClean(true)}}
+ owner = {owner}
+ projectsId = {projectsId}
+ >
+
+ {quillFlag && 请输入评论内容}
+
+
+ }
+ size={100}
+ showNotification={showNotification}
+ // defaultFileList={props.defaultFileList}
+ />
+ {props.defaultFileList && props.defaultFileList.length > 0 &&
+
+ }
+
+
+
+
+
+
+ )
+}
+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..afabf21a2
--- /dev/null
+++ b/src/forge/Issues/Component/comments/list.jsx
@@ -0,0 +1,242 @@
+import React, { useEffect, useState } from 'react';
+import { Button, Popconfirm, Radio, Input, message, Tooltip, Spin, Pagination } from 'antd';
+import axios from 'axios';
+import { getImageUrl, timeAgo } from 'educoder';
+import RenderHtml from '../../../../components/render-html';
+import Attachment from '../../../Upload/attachment';
+import EditComment from './editComment';
+import Nodata from '../../../Nodata';
+import CheckProfile from '../../../Component/ProfileModal/Profile';
+import './list.scss';
+import { Link } from 'react-router-dom';
+
+function IssueCommentList(props){
+ const{history, history: {location}, reload, reloadComment, showNotification, current_user:{login, admin, image_url, user_id}, isManager, showLoginDialog, issueInfo:{author}} = props;
+ const {owner, projectsId, index} = props.match.params;
+ const [category, setCategory] = useState('comment');
+ const [journals, setJournals] = useState(undefined);
+ // 是否展示新建/编辑评论markdown部分新建评论1 编辑父级评论2 回复评论3 编辑回复内容4 回复子机评论5
+ const [showEdit, setShowEdit] = useState(false);
+ const [parentId, setParentId] = useState(undefined);
+ const [replyId, setReplyId] = useState(undefined);
+ // 修改评论id
+ const [updateId, setUpdateId] = useState(undefined);
+ // 操作日志展开效果
+ const [open, setOpen] = useState(undefined);
+ // 加载中效果(缓冲css 线条效果)
+ const [spin, setSpin] = useState(false);
+ // issue 评论总数
+ const [journalsCount, setJournalsCount] = useState(undefined);
+ // 分页
+ const limit = 50;
+ const [page, setPage] = useState(1);
+ const [totalCount, setTotalCount] = useState(undefined);
+ // 操作记录 icon
+ const journalsIcon ={
+ 'issue': 'icon-chuangjianqianbao',
+ 'branch_name': 'icon-fenzhi3',
+ 'assigner': 'icon-chengyuan2',
+ 'status_id': 'icon-xiugaibaobiaomoban',
+ 'fixed_version_id': 'icon-lichengbeiicon2',
+ 'due_date': 'icon-riqi',
+ 'issue_tag': 'icon-biaoji2',
+ 'description': 'icon-xiugaibaobiaomoban',
+ 'subject': 'icon-xiugaibaobiaomoban',
+ 'start_date': 'icon-riqi',
+ 'priority_id': 'icon-youxian',
+ 'attachment': 'icon-xiugaibaobiaomoban'
+ }
+
+ useEffect(()=>{
+ setSpin(true);
+ axios.get(`/v1/${owner}/${projectsId}/issues/${index}/journals`,{params:{
+ category,
+ page,
+ limit
+ }}).then(res=>{
+ if(res && res.data){
+ const {journals, total_comment_journals_count, total_count} = res.data;
+ if(category === 'all'){
+ const array = [];
+ let start = undefined;
+ let isJournalCount = 0;
+ journals.map((item, index)=>{
+ !isJournalCount && (start = index)
+ item.is_journal_detail && (isJournalCount++)
+ if(!item.is_journal_detail && isJournalCount < 10){
+ isJournalCount = 0;
+ return
+ }
+ if(isJournalCount >= 10 && (!item.is_journal_detail || journals.length-1 === index)){
+ array.push({start, count: isJournalCount});
+ isJournalCount = 0;
+ }
+ })
+ array.map((item, i)=>{
+ journals[item.start].numCount = item.count-1;
+ for (let index = 0; index < item.count; index++) {
+ journals[item.start+index].start = journals[item.start].id;
+ journals[item.start+index].closeAndSpan = true;
+ }
+ })
+ }
+ setTotalCount(total_count);
+ setJournalsCount(total_comment_journals_count);
+ setJournals(journals);
+ setSpin(false)
+ }
+ })
+ }, [reload, category, page])
+
+ function commentCtx(v){
+ return
;
+ };
+
+ // 删除评论内容
+ 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(
+
+ {/* 全部 / 评论 / 操作日志 */}
+
0 && 'pb15'}`}>
+
+ {setSpin(true);setPage(1);setCategory(e.target.value);history.push(history.pathname)}} value={category}>
+ 评论{journalsCount}
+ 操作日志
+ 全部
+
+
+
+ {/* 评论快速入口-仅有评论且登录时展示 */}
+ {login && category !== 'operate' && journalsCount > 0 &&
}
+ {/* 评论/操作日志 展示列表 */}
+
+
+ {journals && (journals.length > 0 && journals.map(item=>{return item.is_journal_detail ? (!item.closeAndSpan || item.id === item.start || open === item.start) ?
+
: '' :
+ {/* 评论 */}
+
+
})
+
+ {/* 判断是否是编辑状态 */}
+ {(showEdit === 2 && updateId === item.id) ?
:
+
+
+
+ {item.user.name}
+ {timeAgo(item.created_at)}
+
+ {login &&
+ {/* 平台管理员/仓库管理员/发布评论者/issue创建者 */}
+ {(admin || isManager || login === item.user.login || user_id === author.id) &&
0 ? '子评论也将被一起删除。' : ''}`}
+ okText="是"
+ cancelText="否"
+ onConfirm={() => deleteComment(item.id)}
+ >
+
+ }
+ {/* 仅评论者可修改 */}
+ {login === item.user.login &&
}
+
{setParentId(item.id); setReplyId(item.id); setShowEdit(3)}}>
+
}
+
+
{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 &&
回复}
+
{i.reply_user && i.reply_user.name}
+
{timeAgo(i.created_at)}
+
+ {login &&
+ {/* 平台管理员/仓库管理员/发布评论者/回复评论者/issue创建者 */}
+ {(admin || isManager || login === i.user.login || (i.reply_user && login === i.reply_user.login) || user_id === author.id) &&
deleteComment(i.id)}
+ >
+
+ }
+ {/* 仅回复评论者可修改 */}
+ {login === i.user.login &&
}
+
{setParentId(item.id);setReplyId(i.id); setShowEdit(5)}}>
+
+
+
}
+
+
{commentCtx(i.notes)}
+ {i && i.attachments && i.attachments.length > 0 &&
}
+
}
+ {showEdit === 5 && replyId === i.id &&
}
+
})}
+
+
}))}
+
+
+ {totalCount>limit && }
+ {/* 添加评论 */}
+ {category !== 'operate' &&
+ {login ? showEdit === 1 ?
: : }
+ }
+
+ )
+}
+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..be95b6e3d
--- /dev/null
+++ b/src/forge/Issues/Component/comments/list.scss
@@ -0,0 +1,205 @@
+.typeActionBox{
+ padding: 10px 20px 4px;
+ background-color:rgba(241, 243, 252, 0.55);
+ border-radius:4px;
+ .typeActionRadio{
+ color: #333;
+ display: inline-flex;
+ align-items: center;
+ margin-right: 25px;
+ &.ant-radio-wrapper-checked{
+ color: $primary-color;
+ }
+ }
+ .journalsCount{
+ color:#5e6685;
+ background-color:rgba(70, 106, 255, 0.09);
+ border-radius:10px;
+ display: inline-block;
+ padding: 0px 6px;
+ }
+}
+.commentUserImg{
+ height: 28px;
+ width: 28px;
+ border-radius: 50%;
+ object-fit: cover;
+}
+.issueCommentsBox{
+ .iconBackBox{
+ display: inline-block;
+ width:24px;
+ height:24px;
+ border-radius: 50%;
+ line-height: 24px;
+ text-align: center;
+ background-color:#f2f3f5;
+ }
+ .operationLog, .commentContentBox{
+ position: relative;
+ min-height: 62px;
+ display: flex;
+ >.flexCenter, >a{
+ z-index: 2;
+ }
+ &::before, &::after{
+ content: '';
+ width: 1px;
+ height: 50%;
+ position: absolute;
+ background: #eee;
+ left: 12px;
+ z-index: 1;
+ }
+ &::after{
+ background: #eee;
+ top: 50%;
+ }
+ }
+ .commentContentBox+.operationLog .operationCommentBor, .operationLog+.commentContentBox .commentOperationBor{
+ border-top: 1px solid #eee;
+ width: 98.5%;
+ position: absolute;
+ left: 12px;
+ }
+ .operationLog+.commentContentBox{
+ &>a, &>.commentContentRight{
+ margin-top: 25px;
+ }
+ }
+ .timeAgo{
+ color:#acb0bf;
+ }
+ .operationLog:last-child::after, .operationLog:first-child::before, .commentContentBox:last-child::after, &.justComment .commentContentBox::before, &.justComment .commentContentBox::after{
+ display: none;
+ }
+ .operationLog:first-child, .commentContentBox+.operationLog{
+ margin-top: -15px;
+ }
+ .commentContentBox:last-child::before{
+ height: 35%;
+ }
+ .flexCenter{
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ }
+ .commentRenderHtml.markdown-body p{
+ font-size: 14px !important;
+ }
+ .commentReply{
+ padding: 15px 0 0 20px;
+ background-color: rgba(238, 240, 246, 0.41);
+ &>div{
+ padding-bottom: 5px;
+ }
+ &+.commentReply>div{
+ margin-top: -15px;
+ padding-top: 15px;
+ border-top: 1px dashed #eee;
+ }
+ }
+ .opBox{
+ justify-content: flex-start;
+ // flex: 1;
+ // width: 100%;
+ }
+ .commentContentBox{
+ display: flex;
+ align-items: flex-start;
+ }
+ .commentContentRight{
+ flex: 1;
+ background-color:#fafafc;
+ border:1px solid rgba(42, 97, 255, 0.23);
+ border-radius:6px;
+ position: relative;
+ top: -6px;
+ padding: 10px 15px 16px;
+ width: 0;
+ &::before{
+ content: '';
+ display: block;
+ position: absolute;
+ top: 13px;
+ left: -14px;
+ width: 0;
+ height: 0;
+ overflow: hidden;
+ font-size: 0;
+ line-height: 0;
+ border: 7px;
+ border-style: solid;
+ border-color: transparent rgba(42, 97, 255, 0.23) transparent transparent;
+ }
+ &::after{
+ content: '';
+ display: block;
+ width: 0;
+ height: 0;
+ overflow: hidden;
+ font-size: 0;
+ line-height: 0;
+ border: 6px;
+ border-style: solid;
+ border-color: transparent #fafafc transparent transparent;
+ position: absolute;
+ top: 14px;
+ left: -11px;
+ }
+ }
+}
+.primaryColor, .primaryColor:link{
+ color: $primary-color;
+}
+.attachmentBox{
+ margin-top: -8px;
+}
+.color-grey-89{
+ color: #898d9d;
+}
+// 添加评论样式
+.unLoginComment{
+ height:58px;
+ line-height: 58px;
+ background-color:rgba(241, 243, 252, 0.55);
+ border-radius:4px;
+ color: rgba(84, 87, 103, 1);
+ .loginBtn{
+ color: $primary-color;
+ }
+}
+.quillFlagBox{
+ color: red;
+ margin-bottom: 5px !important;
+ height: 28px;
+}
+.addComments{
+ display: flex;
+ align-items: center;
+ background-color:rgba(241, 243, 252, 0.55);
+ border-radius:4px;
+ padding:7px 10px;
+ &>img{
+ height: 28px;
+ width: 28px;
+ border-radius: 50%;
+ margin-right: 13px;
+ }
+}
+.addCommentBox{
+ width: 100%;
+ height: 30px;
+ border: none;
+}
+.paginationIssueComment{
+ text-align: center;
+}
+.gotoComment{
+ background-color:rgba(241, 243, 252, 0.55);
+ border-radius:4px;
+ color: rgba(172, 176, 191, 1);
+ padding: 8px 20px;
+ margin-top: -10px;
+ &>a{color: rgba(70, 106, 255, 1);}
+}
\ No newline at end of file
diff --git a/src/forge/Issues/Component/copy.jsx b/src/forge/Issues/Component/copy.jsx
new file mode 100644
index 000000000..c10bf90e8
--- /dev/null
+++ b/src/forge/Issues/Component/copy.jsx
@@ -0,0 +1,22 @@
+import React ,{ useState } from 'react';
+import { Tooltip } from 'antd';
+import { CopyToClipboard } from 'react-copy-to-clipboard';
+
+
+function Copy({ children , value }){
+ const [ title , setTitle ] = useState("点击复制链接");
+ let host = window.location.hostname === "localhost" ? "testforgeplus.trustie.net" : window.location.hostname;
+ let protocol = window.location.protocol;
+ return(
+ setTitle("复制成功")}>
+
+ {children}
+
+
+ )
+}
+export default Copy;
\ No newline at end of file
diff --git a/src/forge/Issues/Component/datas.jsx b/src/forge/Issues/Component/datas.jsx
new file mode 100644
index 000000000..dba2ab56e
--- /dev/null
+++ b/src/forge/Issues/Component/datas.jsx
@@ -0,0 +1,96 @@
+import React from 'react';
+import { getImageUrl } from 'educoder';
+import gold from '../Img/gold.png';
+import { Link } from "react-router-dom";
+import Copy from '../Component/copy';
+import { Tooltip } from 'antd';
+
+
+// issue列表显示
+function Datas({checkbox ,item , projectsId,owner}){
+
+ function statusTag(name){
+ switch (name) {
+ case "低":
+ return "status low";
+ case "正常":
+ return "status normals";
+ case "高":
+ return "status hight";
+ default:
+ return "status urgent";
+ }
+ }
+
+ function renderName(list){
+ let l = list.map(i=>{return i.name});
+ return l.join(",");
+ }
+
+ return(
+
+
+ {checkbox}
+
+
+
{item.priority_name}
+ {/*

*/}
+
{item.subject}
+ {
+ item.tags && item.tags.length>0?
+ item.tags.map((i,k)=>{
+ return(
+
{i.name}
+ )
+ })
+ :""
+ }
+
+
+
+
+ { item.project_issues_index &&
+ #{item.project_issues_index}
+ }
+
+
+
{item.author && item.author.name}
+
{item.created_at} 发布
+
{item.updated_at}更新
+ {item.blockchain_token_num &&
{item.blockchain_token_num}}
+ {item.milestone_name &&
+
{window.scrollTo(0,0)}} style={{maxWidth:item.blockchain_token_num ? "261px":"340px",color:"#898d9d"}} title={item.milestone_name} className="task-hide">
+
+ {item.milestone_name}
+
+ }
+
+
+
+
+
+ {
+ item.assigners && item.assigners.length > 0 ?
+
+ 1 ?"principal hovers":"principal"}>
+ {/* {
+ item.assigners.map((i,k)=>{
+ return(
+ k<5 &&
})
+ )
+ })
+ } */}
+
{renderName(item.assigners)}
+
+
+ :""
+ }
+
{item.status_name}
+
+ {item.comment_journals_count}
+
+
+
+ )
+}
+export default Datas;
\ No newline at end of file
diff --git a/src/forge/Issues/Component/date.jsx b/src/forge/Issues/Component/date.jsx
new file mode 100644
index 000000000..bd4f00a9a
--- /dev/null
+++ b/src/forge/Issues/Component/date.jsx
@@ -0,0 +1,149 @@
+import React,{useRef,useEffect, useState , forwardRef } from 'react';
+import { Dropdown , Calendar , Select, Radio, Col, Row } from 'antd';
+import { findDOMNode } from 'react-dom';
+import moment from 'moment';
+const { Group , Button } = Radio;
+
+function Date({name , today , setDate , editFlag},ref){
+ const [ visible , setVisible ] = useState(false);
+ const [ time , setTime ] = useState();
+
+ useEffect(()=>{
+ if(today){
+ setTime(today);
+ }
+ },[today])
+
+ const refFa = useRef(null);
+ const refBox = useRef(null);
+
+ useEffect(()=>{
+ if(!visible && !editFlag && (time && time !== today)){
+ setDate(time);
+ }
+ },[visible,editFlag,time])
+
+
+ useEffect(() => {
+ document.addEventListener('click', clickMe , false);
+ }, [])
+
+ const clickMe = ({ target }) => {
+ // 查找父组件
+ const faComponent = findDOMNode(refFa.current);
+ const boxComponent = findDOMNode(refBox.current);
+ if (faComponent && boxComponent) {
+ const isChild = faComponent.contains(target);
+ const isBox = boxComponent.contains(target);
+ let role = target.getAttribute("role");
+ if(!isChild && !isBox && !(role && role === "option")){
+ setVisible(false);
+ }
+ }
+ }
+ function onSelect(t){
+ setTime(moment(t).format('YYYY-MM-DD'));
+ setDate(moment(t).format('YYYY-MM-DD'));
+ setVisible(false);
+ }
+ const overlay=(
+
+
{
+ const start = 0;
+ const end = 12;
+ const monthOptions = [];
+ const current = value.clone();
+ const localeData = value.localeData();
+ const months = [];
+ for (let i = 0; i < 12; i++) {
+ current.month(i);
+ months.push(localeData.monthsShort(current));
+ }
+
+ for (let index = start; index < end; index++) {
+ monthOptions.push(
+
+ {months[index]}
+ ,
+ );
+ }
+ const month = value.month();
+
+ const year = value.year();
+ const options = [];
+ for (let i = year - 10; i < year + 10; i += 1) {
+ options.push(
+
+ {i}
+ ,
+ );
+ }
+ return (
+
+
+
+
+ {console.log("typyCHange:",e);onTypeChange(e.target.value)}} value={type}>
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }}
+ onSelect={onSelect}
+ />
+
+ )
+ return(
+
+
+
+ {name}
+ {!editFlag && setVisible(visible ? false : true)}> }
+
+ {time || "未设置"}
+
+
+ )
+}
+export default forwardRef(Date);
\ No newline at end of file
diff --git a/src/forge/Issues/Component/delBox.jsx b/src/forge/Issues/Component/delBox.jsx
new file mode 100644
index 000000000..89fed3e83
--- /dev/null
+++ b/src/forge/Issues/Component/delBox.jsx
@@ -0,0 +1,32 @@
+import React from "react";
+import Modals from '../../Component/PublicModal/Index';
+import { AlignTop } from '../../Component/layout';
+import { Button } from 'antd';
+
+function DelBox({visible,onCancel,onSuccess,content,title}){
+ return(
+
+
+
+
+ }
+ >
+
+
+
+ { content ?
+ content
+ :
+ 您确定要删除所有选中的疑修?
此操作将清空所有已选中的疑修,请谨慎操作
+ }
+
+
+
+ )
+}
+export default DelBox;
\ No newline at end of file
diff --git a/src/forge/Issues/Component/drop.jsx b/src/forge/Issues/Component/drop.jsx
new file mode 100644
index 000000000..f90a19d69
--- /dev/null
+++ b/src/forge/Issues/Component/drop.jsx
@@ -0,0 +1,47 @@
+import React,{useState, useEffect, useRef, useImperativeHandle,forwardRef} from 'react';
+import { Dropdown } from 'antd';
+import { findDOMNode } from 'react-dom';
+
+function Drop({overlay , children , placement, overlayClassName},ref){
+ const [ visible , setVisible ] = useState(false);
+ const refFa = useRef(null);
+ const refBox = useRef(null);
+
+ useImperativeHandle(ref, () => ({
+ clearVisible: (v) => {
+ // 父组件按钮:清除筛选条件,将dropmenu里的choose字段全部改为0
+ setVisible(v);
+ }
+ }))
+
+ useEffect(() => {
+ document.addEventListener('click', clickMe , false);
+ }, [])
+
+ const clickMe = ({ target }) => {
+ // 查找父组件
+ const faComponent = findDOMNode(refFa.current);
+ const boxComponent = findDOMNode(refBox.current);
+ if (faComponent && boxComponent) {
+ const isChild = faComponent.contains(target);
+ const isBox = boxComponent.contains(target);
+ if(!isChild && !isBox){
+ setVisible(false);
+ }
+ }
+ }
+ return(
+ {overlay}}
+ trigger={['click']}
+ overlayClassName={overlayClassName}
+ >
+ setVisible(visible ? false : true)}>
+ {children}
+
+
+ )
+}
+export default forwardRef(Drop);
\ No newline at end of file
diff --git a/src/forge/Issues/Component/dropMenu.jsx b/src/forge/Issues/Component/dropMenu.jsx
new file mode 100644
index 000000000..962243c0a
--- /dev/null
+++ b/src/forge/Issues/Component/dropMenu.jsx
@@ -0,0 +1,210 @@
+import React ,{ useState , useEffect , useRef } from 'react';
+import { findDOMNode } from 'react-dom';
+import { Dropdown , Menu , Input , message} from 'antd';
+import { getImageUrl } from 'educoder';
+
+const { Search } = Input;
+/**
+ *
+ * @param {placeholder} placeholder:默认显示内容(为空时)
+ * @param {editFlag} editFlag:是否有编辑权限
+ * @param {searchFlag} searchFlag:是否显示搜索框
+ * @param {selectValueList} selectValueList:将选中的list保存
+ * @param {searchFunc} searchFunc:搜索方法
+ * @param {onAdd} onAdd:标记管理
+ * @param {headImg} headImg:是否显示头像
+ * @param {menus} menus:下拉列表
+ * @param {chooseFunc} chooseFunc:选择下拉列表
+ * @param {double} double:undefined为单选,有值就是最多选的数量
+ * @param {colorFlag} colorFlag:选中值需根据颜色显示背景
+ * @param {mustFlag} mustFlag:必须选择一个
+ * @param {removeFlag} removeFlag:移除按钮
+ * @returns
+ */
+function DropMenu({
+ placeholder ="未设置",
+ editFlag,
+ searchFlag,
+ selectValueList,
+ searchFunc,
+ onAdd,
+ headImg,
+ menus,
+ chooseFunc,double,colorFlag,mustFlag,removeFlag
+}){
+ const [ visible , setVisible ] = useState(false);
+ const [ searchValue , setSearchValue ]= useState(undefined);
+ const [ valuesId , setValuesId ] = useState([]);
+ const [ valuesName , setValuesName ] = useState([]);
+
+ const refFa = useRef(null);
+ const refBox = useRef(null);
+
+ // 根据选中的列表循环出id数组
+ useEffect(()=>{
+ if(selectValueList && selectValueList.length > 0 ){
+ renderSelectList(selectValueList);
+ }else{
+ setValuesId([])
+ }
+ },[selectValueList])
+
+ function renderSelectList(list){
+ let a = list && list.length > 0 && list.map((i,k)=>{
+ return i.id ? i.id.toString() : i.name
+ })
+ setValuesId(a);
+ setValuesName(list);
+ }
+
+ useEffect(() => {
+ document.addEventListener('click', clickMe , false);
+ }, [])
+
+ const clickMe = ({ target }) => {
+ // 查找父组件
+ const faComponent = findDOMNode(refFa.current);
+ const boxComponent = findDOMNode(refBox.current);
+ if (faComponent && boxComponent) {
+ const isChild = faComponent.contains(target);
+ const isBox = boxComponent.contains(target);
+ if(!isChild && !isBox){
+ setVisible(false);
+ }
+ }
+ }
+ // 搜索
+ function changeSearchvalue(e){
+ setSearchValue(e.target.value);
+ searchFunc(e.target.value);
+ }
+
+ function chooseMenu(i){
+ let list = selectValueList;
+ let re = [];
+ re = list && list.length > 0 ? list:[];
+ let filter = [];
+ if(i.id){
+ filter = list.filter(j=>j.id === i.id);
+ }else{
+ filter = list.filter(j=>j.name === i.name);
+ }
+ if(filter && filter.length > 0){
+ if(!mustFlag){
+ if(i.id){
+ re = list.filter(j=>j.id !== i.id);
+ }else{
+ re = list.filter(j=>j.name !== i.name);
+ }
+ renderSelectList(re);
+ chooseFunc(re);
+ !double && setVisible(false);
+ }else{
+ setVisible(false);
+ }
+ }else{
+ if(double && (selectValueList && selectValueList.length >= double)){
+ message.info(`最多只能添加${double}个${placeholder}`);
+ return;
+ }
+ if(double){
+ re.push(i);
+ }else{
+ re = [i];
+ setVisible(false);
+ }
+ renderSelectList(re);
+ chooseFunc(re);
+ }
+ }
+
+ function showRemoveFunc(item){
+ let l = selectValueList ;
+ l = l.filter(i=>(i.id ? i.id.toString() : i.name) !== item.toString());
+ renderSelectList(l);
+ chooseFunc(l);
+ }
+
+ function renderNames(nameArrs){
+ return
+ {
+ nameArrs && nameArrs.length>0?
+ nameArrs.map((i,k)=>{
+ return(
+
+ {i.image_url &&
}
+ {
+ colorFlag ?
+ (
+ colorFlag === "2" ?
+ {i.name}
+ :
+ {i.name}
+ )
+ :
+ {i.name}
+ }
+ { removeFlag && showRemoveFunc(i.id || i.name)}>}
+
+ )
+ })
+ :{placeholder}
+ }
+
+ }
+ return(
+
+
+ {placeholder}
+ {!editFlag &&
+ setVisible(visible ? false : true)}>
+
+
+ }
+
+
+ { searchFunc &&
+
+
+
+ }
+ {
+ menus && menus.length >0?
+
+ :
+
+
{searchValue ? 暂无{placeholder}“{searchValue}”: `暂无${placeholder}`}
+
+ }
+ { onAdd && }
+
+ }>
+ 0 ? "operatevalue color-grey-3":"operatevalue"}>{renderNames(selectValueList)}
+
+
+ )
+}
+export default DropMenu;
\ No newline at end of file
diff --git a/src/forge/Issues/Component/menus.jsx b/src/forge/Issues/Component/menus.jsx
new file mode 100644
index 000000000..24aa86ad4
--- /dev/null
+++ b/src/forge/Issues/Component/menus.jsx
@@ -0,0 +1,93 @@
+import React ,{ useState , useRef , useEffect } from 'react';
+import { Input , Menu , Icon } from 'antd';
+import { getImageUrl } from 'educoder';
+import Drop from './drop';
+const { Search } = Input;
+
+/**
+ * @param {className} 的样式名称
+ * @param {name} 未选择时显示的内容
+ * @param {lists} 下拉列表,从接口获取,需一一填充
+ * @param {imgControl} 控制是否显示头像
+ * @param {size} 下拉框宽度,small:120px,large:260px
+ * @param {ids} 默认选择的项的id数组
+ * @param {search} 搜索方法
+ * @param {chooseFunc} 选择项后需调用列表的查询方法
+ * @param {update} 编辑状态
+ * @param {double} 是否可以多选
+ */
+function Menus({className,name, ids , lists , size , imgControl , searchFunc , chooseFunc , update , double ,names },ref){
+ const dropRef = useRef(null);
+ const [ chooseValue , setChooseValue ] = useState([]);
+ const [ showValue , setShowValue ] = useState(undefined);
+
+ useEffect(()=>{
+ ids ? setChooseValue(ids.split(",")) : setChooseValue([]);
+ setShowValue(names);
+ },[ids,names])
+
+
+ // 修改menus数组里的字段值
+ function changeMenusValue(i){
+ let l = ids;
+ let id = i.id.toString();
+ if(double){
+ l = ids ? ids.split(",") : [] ;
+ let nameArr = names ? names.split(",") : [] ;
+ if(l && l.indexOf(id)>=0){
+ l = l.filter(k=>k.toString() !== id);
+ nameArr = nameArr.filter(k=>k.toString() !== i.name);
+ }else{
+ l.push(id);
+ nameArr.push(i.name);
+ }
+ setChooseValue(l);
+ setShowValue(nameArr.join(","));
+ chooseFunc(l,nameArr.join(","));
+ }else{
+ if(l && l.indexOf(id)>=0){
+ setChooseValue([]);
+ setShowValue(undefined);
+ chooseFunc([]);
+ }else{
+ setChooseValue([`${i.id}`]);
+ setShowValue([i.name]);
+ chooseFunc([id],i.name);
+ }
+ dropRef.current && dropRef.current.clearVisible(false);
+ }
+ }
+
+ function menu(data){
+ return
+ { size !== "small" &&
+
searchFunc(e.target.value)}/>
+ }
+ {
+ data && data.length>0 ?
+
+ :
+
暂无{ids ? '{name}': name}
+ }
+
+ }
+ return(
+
+
+ {showValue || (update ? `更换${name}` : name) }
+
+
+
+ )
+}
+export default Menus;
\ No newline at end of file
diff --git a/src/forge/Issues/Component/newPanel.jsx b/src/forge/Issues/Component/newPanel.jsx
new file mode 100644
index 000000000..67a02943f
--- /dev/null
+++ b/src/forge/Issues/Component/newPanel.jsx
@@ -0,0 +1,129 @@
+import React , { forwardRef , useState } from 'react';
+import { Form , Input , Button } from 'antd';
+import MDEditor from "../../../modules/tpm/challengesnew/tpm-md-editor";
+import Upload from "../../Upload/Index";
+import Attachments from "../../Upload/attachment";
+import UploadImg from '../Img/UploadImg.png';
+
+function NewPanel(props,ref){
+ const [ description , setDescription ] = useState(undefined);
+ const [ fileList , setFileList] = useState(undefined);
+ const [ attachments , setAttachments ] = useState([]);
+ const [ save , setSave ] = useState([]);
+ const [ receivers_login , setReceiversLogin ] = useState(undefined);
+
+ const { createFunc , title , desc , files , onCancel , owner , projectsId } = props;
+
+ const { form: { getFieldDecorator, validateFields , setFieldsValue } } = props;
+ useState(()=>{
+ title && setTimeout(()=>{
+ setFieldsValue({subject:title});
+ desc && onContentChange(desc);
+ },100)
+ files && setAttachments(files);
+ files && setSave(files);
+ },[title , desc , files])
+
+ function onContentChange(value){
+ setDescription(value);
+ }
+ function UploadFunc(f){
+ let list = [];
+ let arr = [];
+ if(save && save.length>0 ){
+ arr = save.map(i=>{return i.id});
+ }
+ list = arr && arr.length>0 ? f.concat(arr) : f ;
+ setFileList(list);
+ };
+
+ function deleteLoad(id){
+ let arr = [];
+ let list = save;
+ if(list && list.length>0 ){
+ arr = list.filter(i=>i.id !== id);
+ }
+ setFileList(arr.map(i=>{return i.id}));
+ setSave(arr);
+ }
+
+ function changeAtWhoLoginList(loginList){
+ let list = new Set(receivers_login);
+ loginList.map(item => list.add(item));
+ setReceiversLogin(Array.from(list));
+ };
+
+ function sureFunc(){
+ validateFields((error,values)=>{
+ if (!error) {
+ createFunc(values,fileList,receivers_login,description);
+ }
+ })
+ }
+
+ function cancelFunc(){
+ if(onCancel){
+ onCancel();
+ }else{
+ window.history.back(-1);
+ }
+ }
+ return(
+
+
+ {getFieldDecorator("subject",{
+ rules:[{required:true,message:"请输入疑修标题"}]
+ })(
+
+ )}
+
+
+
+
+
+
+
+ }
+ size={100}
+ showNotification={props.showNotification}
+ />
+ {attachments && attachments.length > 0 &&
+
+ }
+
+
+
+
+
+
+ )
+}
+export default forwardRef(NewPanel);
diff --git a/src/forge/Issues/Img/UploadImg.png b/src/forge/Issues/Img/UploadImg.png
new file mode 100644
index 000000000..cb1e7d05c
Binary files /dev/null and b/src/forge/Issues/Img/UploadImg.png differ
diff --git a/src/forge/Issues/Img/biaoji.png b/src/forge/Issues/Img/biaoji.png
new file mode 100644
index 000000000..e40377459
Binary files /dev/null and b/src/forge/Issues/Img/biaoji.png differ
diff --git a/src/forge/Issues/Img/create.png b/src/forge/Issues/Img/create.png
new file mode 100644
index 000000000..63b4cc29c
Binary files /dev/null and b/src/forge/Issues/Img/create.png differ
diff --git a/src/forge/Issues/Img/emp.png b/src/forge/Issues/Img/emp.png
new file mode 100644
index 000000000..5db7c0778
Binary files /dev/null and b/src/forge/Issues/Img/emp.png differ
diff --git a/src/forge/Issues/Img/gold.png b/src/forge/Issues/Img/gold.png
new file mode 100644
index 000000000..3c64743b6
Binary files /dev/null and b/src/forge/Issues/Img/gold.png differ
diff --git a/src/forge/Issues/Img/issue-big.png b/src/forge/Issues/Img/issue-big.png
new file mode 100644
index 000000000..8d73e1d93
Binary files /dev/null and b/src/forge/Issues/Img/issue-big.png differ
diff --git a/src/forge/Issues/Img/issue.png b/src/forge/Issues/Img/issue.png
new file mode 100644
index 000000000..7a85ffff7
Binary files /dev/null and b/src/forge/Issues/Img/issue.png differ
diff --git a/src/forge/Issues/Img/pr.png b/src/forge/Issues/Img/pr.png
new file mode 100644
index 000000000..fc3deacf0
Binary files /dev/null and b/src/forge/Issues/Img/pr.png differ
diff --git a/src/forge/Issues/Pages/details.jsx b/src/forge/Issues/Pages/details.jsx
new file mode 100644
index 000000000..1bf881440
--- /dev/null
+++ b/src/forge/Issues/Pages/details.jsx
@@ -0,0 +1,520 @@
+import React , { useEffect , useState , forwardRef , useRef } from 'react';
+import { Spin , Form , InputNumber, message } from 'antd';
+import { getImageUrl } from 'educoder';
+import { Link } from 'react-router-dom';
+import { Box , LongWidth } from '../../Component/layout';
+import RenderHtml from "../../../components/render-html";
+import DropMenu from '../Component/dropMenu';
+import NewPanel from '../Component/newPanel';
+import Attachments from "../../Upload/attachment";
+import DelBox from '../Component/delBox';
+import Date from '../Component/date';
+import moment from 'moment';
+import Copy from '../Component/copy';
+import axios from 'axios';
+import CommentList from '../Component/comments/list';
+import Claims from '../../claims/claims';
+import ChooseMenu from '../Component/chooseMenu';
+
+function Details(props){
+ const owner = props.match.params.owner;
+ const projectsId = props.match.params.projectsId;
+ const index = props.match.params.index;
+
+ const [ details , setDetails ] = useState(undefined);
+ const [ statusList , setStatusList ] = useState([]);
+ const [ prioritiesList , setPrioritiesList ] = useState([]);
+ const [ chargeList , setChargeList ] = useState([]);
+ const [ charge , setCharge ] = useState(undefined);
+ const [ millstoneList , setMillstoneList ] = useState([]);
+ const [ millstone , setMillstone ] = useState(undefined);
+ const [ tagList , setTagList ] = useState(undefined);
+ const [ tag , setTag ] = useState(undefined);
+ const [ branchList , setBranchList ] = useState(undefined);
+ const [ branch , setBranch ] = useState(undefined);
+ const [ desc , setDesc ] = useState(undefined);
+
+ const [ orderId , setOrderId ] = useState(undefined);
+
+ const [ delVisible , setDelVisible] = useState(false);
+ const [ edit , setEdit] = useState(false);
+ const [ copy , setCopy] = useState(false);
+
+ const [ start_date, setStartDate ] = useState("");
+ const [ due_date, setDueDate ] = useState("");
+ const [ commentReload, setCommentReload] = useState(undefined);
+ const [ editFlag , setEditFlag ] = useState(false);
+ const [ amountEditFlag , setAmountEditFlag ] = useState(false);
+
+ const [ assigner_choose , setAssigner_choose ] = useState([]);
+ const [ prioritie_choose , setPrioritie_choose ] = useState([]);
+ const [ status_choose , setStatus_choose ] = useState([]);
+ const [ tag_choose , setTag_choose ] = useState([]);
+ const [ millstone_choose , setMillstone_choose ] = useState([]);
+ const [ branch_choose , setBranch_choose ] = useState([]);
+ const [ rewardAmount , setRewardAmount ] = useState(undefined);
+ const [ rewardFlag , setRewardFlag ] = useState(false);
+
+ const pathname = props.history.location.pathname;
+ const permission = props && props.projectDetail && props.projectDetail.permission;
+ let colors = ["#1abcb1","#28be6c","#e67e22","#db3d1d"];
+
+ const {projectDetail , open_blockchain ,current_user } = props;
+ useEffect(()=>{
+ if(pathname === `/${owner}/${projectsId}/issues/${index}/copy`){
+ setEdit(true);
+ setCopy(true);
+ }else{
+ setEdit(false);
+ setCopy(false);
+ }
+ },[pathname])
+
+ useEffect(()=>{
+ if(projectDetail && details){
+ const { author, name} = projectDetail;
+ document.title = `${details.subject}-疑修-${author.name}/${name}`;
+ }
+ },[details,projectDetail])
+
+ useEffect(()=>{
+ if(index){
+ Init();
+ }
+ },[index])
+
+ function Init(){
+ const url = `/v1/${owner}/${projectsId}/issues/${index}`;
+ axios.get(url).then(result=>{
+ if(result && result.data){
+ let data = result.data;
+ setDetails(data);
+ let per = data && !data.user_permission;
+ setEditFlag(per);
+
+ setDesc(data && data.description);
+
+ const aut = current_user && data.author && (data.author.login === current_user.login);
+ const con = data.pull_fixed === false && aut;
+ setAmountEditFlag(con);
+
+ setAssigner_choose(data.assigners);
+ if(data.priority && data.priority.id){
+ setPrioritie_choose([{...data.priority,color:colors[data.priority.id-1]}]);
+ }else{
+ setPrioritie_choose([]);
+ }
+ setStatus_choose(data.status && data.status.id ? [data.status]:[]);
+ setTag_choose(data.tags);
+ setMillstone_choose(data.milestone && data.milestone.id ? [data.milestone]:[]);
+ setBranch_choose(data.branch_name ? [{name:data.branch_name}] : []);
+ setRewardAmount(data.blockchain_token_num);
+
+ data.start_date && setStartDate(moment(data.start_date).format('YYYY-MM-DD'));
+ data.due_date && setDueDate(moment(data.due_date).format('YYYY-MM-DD'));
+
+ setOrderId(data.id);
+ }
+ }).catch(error=>{
+ console.log(error);
+ })
+ }
+
+
+ function statusTag(name){
+ switch (name) {
+ case "低":
+ return "status low";
+ case "正常":
+ return "status normals";
+ case "高":
+ return "status hight";
+ default:
+ return "status urgent";
+ }
+ }
+ // 获取状态列表
+ useEffect(()=>{
+ getStatus();
+ },[])
+ function getStatus(){
+ const url = `/v1/${owner}/${projectsId}/issue_statues`;
+ axios.get(url).then(result=>{
+ if(result && result.data){
+ setStatusList(result.data.statues);
+ }
+ })
+ }
+ // 获取优先级列表
+ useEffect(()=>{
+ getPriorities();
+ },[])
+
+ function getPriorities(){
+ const url = `/v1/${owner}/${projectsId}/issue_priorities`;
+ axios.get(url).then(result=>{
+ if(result && result.data){
+ let priorities = result.data.priorities;
+ let array = addColorForPriorities(priorities);
+ setPrioritiesList(array);
+ }
+ })
+ }
+
+ function addColorForPriorities(list){
+ let array =[];
+ if(list && list.length>0){
+ array = list.map((i,k)=>{
+ let arr = {...i,color:colors[k]}
+ return arr;
+ })
+ }
+ return array;
+ }
+
+ // 获取负责人列表
+ useEffect(()=>{
+ getCharge();
+ },[charge])
+
+ function getCharge(){
+ const url = `/v1/${owner}/${projectsId}/collaborators`;
+ axios.get(url,{params:{keyword:charge,only_name:true}}).then(result=>{
+ if(result && result.data){
+ setChargeList(result.data.collaborators);
+ }
+ })
+ }
+ // 获取里程碑列表
+ useEffect(()=>{
+ getMillstone();
+ },[millstone])
+
+ function getMillstone(){
+ const url = `/v1/${owner}/${projectsId}/milestones`;
+ axios.get(url,{params:{keyword:millstone,only_name:true}}).then(result=>{
+ if(result && result.data){
+ setMillstoneList(result.data.milestones);
+ }
+ })
+ }
+ // 获取标记列表
+ useEffect(()=>{
+ getSign();
+ },[tag])
+ function getSign(){
+ const url = `/v1/${owner}/${projectsId}/issue_tags`;
+ axios.get(url,{params:{keyword:tag,only_name:true}}).then(result=>{
+ if(result && result.data){
+ setTagList(result.data.issue_tags);
+ }
+ })
+ }
+ // 获取标记列表
+ useEffect(()=>{
+ getBranch(branch);
+ },[branch])
+ function getBranch(branch){
+ if(branch){
+ let b = [...branchList];
+ let l = b.filter(i=>i.name.indexOf(branch)>-1);
+ setBranchList(l);
+ return ;
+ }
+ const url = `/${owner}/${projectsId}/branches.json`;
+ axios.get(url,{params:{keyword:tag}}).then(result=>{
+ if(result && result.data){
+ setBranchList(result.data);
+ }
+ })
+ }
+
+ // 可单个编辑保存右侧的项(!copy:非复制状态下)
+ function saveForeach(c,s,p,t,m,b,sDate,eDate,rewardAmount){
+ if(!copy){
+ const url = `/v1/${owner}/${projectsId}/issues/${index}`;
+ axios.patch(url,{
+ branch_name: b ? b.join(",") :undefined,
+ status_id: s ? s.join(",") : undefined,
+ priority_id: p ? p.join(",") :undefined,
+ milestone_id: m ? m.join(",") :undefined,
+ issue_tag_ids: t?t:undefined,
+ assigner_ids: c?c:undefined,
+ start_date:sDate,
+ due_date:eDate,
+ blockchain_token_num:rewardAmount
+ }).then(result=>{
+ if(result){
+ Init();
+ // 更新操作日志
+ setCommentReload(Math.random());
+ }
+ }).catch(error=>{})
+ }
+ }
+
+ function deleteFunc(){
+ const url = `/v1/${owner}/${projectsId}/issues/${index}`;
+ axios.delete(url).then(result=>{
+ if(result){
+ props.showNotification("疑修删除成功!");
+ props.history.push(`/${owner}/${projectsId}/issues`);
+ }
+ }).catch(error=>{})
+ }
+
+ // 编辑保存
+ function createFunc(values,fileList,receivers_login,description){
+ let params ={
+ subject: values.subject,
+ attachment_ids: fileList,
+ receivers_login: receivers_login,
+ description: description
+ }
+ if(!copy){
+ // 调用编辑保存接口
+ const url = `/v1/${owner}/${projectsId}/issues/${index}`;
+ axios.patch(url,{
+ ...params
+ }).then(result=>{
+ if(result){
+ window.scrollTo(0,0);
+ props.showNotification("疑修更新成功!");
+ Init();
+ setEdit(false);
+ // 刷新操作记录
+ setCommentReload(Math.random());
+ }
+ }).catch(error=>{})
+ }else{
+ // 调用新建保存接口
+ const url = `/v1/${owner}/${projectsId}/issues`;
+ let b = branch_choose && branch_choose.length>0 ? branch_choose.map(i=>{return i.name}) :undefined;
+ let t = tag_choose && tag_choose.length>0 ? tag_choose.map(i=>{return i.id}) :undefined;
+ let a = assigner_choose && assigner_choose.length>0 ? assigner_choose.map(i=>{return i.id}) :undefined;
+ let m = millstone_choose && millstone_choose.length>0 ? millstone_choose.map(i=>{return i.id}) :undefined;
+ let p = prioritie_choose && prioritie_choose.length>0 ? prioritie_choose.map(i=>{return i.id}) :undefined;
+ let s = status_choose && status_choose.length>0 ? status_choose.map(i=>{return i.id}) :undefined;
+ axios.post(url,{
+ ...params,
+ branch_name: b && b.join(","),
+ status_id: s && s.join(","),
+ priority_id: p && p.join(","),
+ milestone_id: m && m.join(","),
+ issue_tag_ids: t,
+ assigner_ids: a,
+ start_date,
+ due_date
+ }).then(result=>{
+ if(result && result.data && result.data.project_issues_index){
+ window.scrollTo(0,0);
+ props.showNotification("任务复制成功!");
+ props.history.push(`/${owner}/${projectsId}/issues/${result.data.project_issues_index}`);
+ }
+ }).catch(error=>{})
+ }
+ }
+
+ // 取消编辑或复制
+ function onCancel(){
+ if(copy){
+ props.history.push(`/${owner}/${projectsId}/issues/${index}`);
+ }else{
+ setEdit(false);
+ window.scrollTo(0,0);
+ }
+ }
+
+ function refreshFunc(){
+ // 更新操作日志
+ setCommentReload(Math.random());
+ }
+
+
+ // 悬赏金额 失去焦点保存数据
+ function amountBlur(e){
+ if(!editFlag){
+ setRewardFlag(false);
+ const value = e.target.value;
+ if(value<0){
+ message.info("请输入大于0的正整数");
+ setRewardAmount(rewardAmount);
+ return;
+ }
+ setRewardAmount(e.target.value);
+ if((value && value >= 0) || !value){
+ saveForeach(undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined,value);
+ }
+ }
+ }
+ function changeAmount(value){
+ if(amountEditFlag){
+ setRewardAmount(value);
+ }else{
+ setRewardAmount(rewardAmount);
+ }
+ }
+ return(
+ details ?
+
+
+ setDelVisible(false)}
+ onSuccess={deleteFunc}
+ content={}
+ />
+ {
+ edit ?
+
+
+
+ :
+
+
+
+
+
+ {details.priority &&
{ details.priority.name } }
+
{details.subject}
+
+
+
+ #{details.project_issues_index}
+
+ {
+ details.author &&
+
+
})
{details.author.name}
+
添加于{details.created_at}
+
+ }
+
+
+ {
+ details.user_permission &&
+
+ }
+
+
+ {desc ?
+
+ :
+
暂无描述
+ }
+ {details.attachments && details.attachments.length > 0 ?
+
+ : ""
+ }
+
+
+
+
+ }
+
+
+ {orderId &&
}
+
{setCharge(value)}}
+ headImg
+ selectValueList={assigner_choose}
+ editFlag={editFlag}
+ chooseFunc={(list)=>{setAssigner_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(l);}}
+ double={5}
+ removeFlag={!editFlag}
+ />
+ {setStatus_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(undefined,l);}}
+ />
+ {setPrioritie_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(undefined,undefined,l);}}
+ />
+ {setTag(value)}}
+ selectValueList={tag_choose}
+ chooseFunc={(list)=>{setTag_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(undefined,undefined,undefined,l);}}
+ double={3}
+ colorFlag="2"
+ removeFlag={!editFlag}
+ editFlag={editFlag}
+ onAdd={permission && permission !== "Reporter" ? ()=>{getSign();} :false}
+ owner={owner}
+ projectsId={projectsId}
+ />
+ {setMillstone(value)}}
+ selectValueList={millstone_choose}
+ editFlag={editFlag}
+ auto
+ chooseFunc={(list)=>{setMillstone_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(undefined,undefined,undefined,undefined,l);}}
+ />
+ {setBranch(value)}}
+ selectValueList={branch_choose}
+ editFlag={editFlag}
+ chooseFunc={(list)=>{setBranch_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(undefined,undefined,undefined,undefined,undefined,l);}}
+ />
+ {
+ open_blockchain &&
+
+
+ 悬赏金额
+ {/* {!editFlag && } */}
+
+
+
+ }
+ {setStartDate(date);saveForeach(undefined,undefined,undefined,undefined,undefined,undefined,date)}} editFlag={editFlag}/>
+ {setDueDate(date);saveForeach(undefined,undefined,undefined,undefined,undefined,undefined,undefined,date)}} editFlag={editFlag}/>
+
+
+ :
+
+ )
+}
+export default Form.create()(forwardRef(Details));
\ No newline at end of file
diff --git a/src/forge/Issues/Pages/list.jsx b/src/forge/Issues/Pages/list.jsx
new file mode 100644
index 000000000..bc177ec84
--- /dev/null
+++ b/src/forge/Issues/Pages/list.jsx
@@ -0,0 +1,461 @@
+import React , { useRef , useState , useEffect } from 'react';
+import { Dropdown , Menu , Icon , Input , Checkbox , Pagination, Button , Tooltip ,Spin , DatePicker } from 'antd';
+import bj from '../Img/biaoji.png';
+import issueEmp from '../Img/issue-big.png';
+import { Link } from "react-router-dom";
+import AllMenus from '../Component/allMenus';
+import CheckProfile from '../../Component/ProfileModal/Profile';
+import Datas from '../Component/datas';
+import DelBox from '../Component/delBox';
+import axios from 'axios';
+import cookie from 'react-cookies';
+import emp from '../Img/emp.png';
+import moment from 'moment';
+
+const { RangePicker } = DatePicker;
+
+const { Search } = Input;
+const defaultLimit = 15;
+
+function List(props){
+ const [ visible , setVisible ] = useState(false);
+ const [ issueList , setIssueList ] = useState(undefined);
+ const [ closedCount , setClosedCount ] = useState(0);
+ const [ openedCount , setOpenedCount ] = useState(0);
+
+ const[ aboutMe , setAboutMe ] = useState("all");
+ const[ value , setValue ] = useState("");
+ const[ keyword , setKeyword ] = useState(undefined);
+
+ const [ category , setCategory] = useState("opened");
+ const [ begin_date , setBegin] = useState(undefined);
+ const [ end_date , setEnd] = useState(undefined);
+
+ const [ allValue , setAllValue ] = useState([]);
+ const [ allIds , setAllIds ] = useState([]);
+ const [ checkAll , setCheckAll ] = useState(false);
+
+ const [ names , setNames ] = useState(undefined);
+ const [ updateIds , setUpdateIds ] = useState(undefined);
+ const [ updateChooseIds , setUpdateChooseIds ] = useState(undefined);
+
+ const [ page , setPage ] = useState(1);
+ const [ limit , setLimit ] = useState(15);
+ const [ total , setTotal ] = useState(undefined);
+ const [ issueTotal , setIssueTotal ] = useState(0);
+
+ const [ clearFlag , setClearFlag ] = useState(false);
+ const [ has_created_issues , setHas_created_issues] = useState(false);
+
+ const menuRef = useRef(null);
+ const owner = props.match.params.owner;
+ const projectsId = props.match.params.projectsId;
+ const permission = props && props.projectDetail && props.projectDetail.permission;
+ const { projectDetail , current_user , open_blockchain} = props;
+
+ useEffect(()=>{
+ if(projectDetail){
+ const { author, name } = projectDetail;
+ document.title = `疑修-${author.name}/${name}`;
+ }
+ },[projectDetail])
+
+ useEffect(()=>{
+ const datas = cookie.load('issuestates');
+ let states = datas === "undefined" ? undefined : datas;
+ if(states){
+ setAboutMe(states.participant_category);
+ setCategory(states.category);
+ setPage(states.page);
+ setLimit(states.limit || defaultLimit);
+ setUpdateIds({...states});
+ setNames(states.names);
+ setBegin(states.begin_date);
+ setEnd(states.end_date);
+ // if(states.participant_category ==="all" && states.category === "opened" && states.page === 1 && states.limit===defaultLimit)
+ // Init({...states},states.page,states.names);
+ }else{
+ Init();
+ }
+ },[])
+
+ useEffect(()=>{
+ let v = allValue && allValue > 0;
+ if((updateIds && !v && (updateIds.author_id || updateIds.issue_priorities_id || updateIds.issue_tag_ids || updateIds.milestone_id || updateIds.sort_by || updateIds.status_id ||updateIds.assigner_id)) || value || end_date || begin_date || aboutMe !=="all"){
+ setClearFlag(true);
+ }else{
+ setClearFlag(false);
+ }
+ },[updateIds,value,allValue,begin_date,end_date,aboutMe])
+ // 自定义一个初始不更新的hook
+ const useUpdateEffect = (fn, inputs) => {
+ const didMountRef = useRef(false);
+ useEffect(() => {
+ if (didMountRef.current) fn();
+ else didMountRef.current = true;
+ }, inputs);
+ };
+
+ // 定义处理函数
+ function handleAllDisabled(){
+ Init(updateIds,page,names);
+ }
+
+ // 使用自定义hook
+ useUpdateEffect(handleAllDisabled, [aboutMe,keyword,category,limit,end_date,updateIds]);
+
+
+ function getDirection(id){
+ if(!id) return undefined;
+ if(id === "1" || id === "3" || id === "5"|| id === "7"){
+ return "desc"
+ }else{
+ return "asc"
+ }
+ }
+ function getBy(id){
+ if(!id) return undefined;
+ if(id === "1" || id === "2"){
+ return "issues.created_on"
+ }else if(id === "3" || id === "4"){
+ return "issues.updated_on"
+ }else if(id === "5" || id === "6"){
+ return "issue_priorities.position"
+ }else{
+ return "issues.blockchain_token_num"
+ }
+ }
+
+ // 获取issue列表数据
+ function Init(params,p,names){
+ setTotal(undefined);
+ const datas = cookie.load('issuestates');
+ let states = datas === "undefined" ? undefined : datas;
+ if(states){
+ cookie.remove('issuestates');
+ }
+ const url = `/v1/${owner}/${projectsId}/issues`;
+ axios.get(url,{
+ params:{
+ ...params,
+ page:p || 1,
+ keyword,
+ participant_category:aboutMe,
+ category,
+ limit,
+ begin_date,end_date,
+ sort_direction:getDirection(params && params.sort_by),
+ sort_by:getBy(params && params.sort_by),names:undefined
+ }
+ }).then(result=>{
+ if(result){
+ setIssueList(result.data.issues);
+ setTotal(result.data.total_count);
+ setClosedCount(result.data.closed_count);
+ setOpenedCount(result.data.opened_count);
+ const ids = result.data.issues.length>0 ? result.data.issues.map(i=>{return i.id}) : [];
+ setAllIds(ids);
+ setIssueTotal(result.data.total_issues_count);
+ setHas_created_issues(result.data.has_created_issues);
+
+ const d = {...params,keyword,participant_category:aboutMe,category,limit,page:p || 1,
+ sort_direction:getDirection(params && params.sort_by),begin_date,end_date,
+ sort_by:getBy(params && params.sort_by),names:names};
+
+ let inFifteenMinutes = new Date(new Date().getTime() + 24 * 3600 * 1000);
+ cookie.save('issuestates', {...d},{ expires: inFifteenMinutes,path:`/` });
+ }
+ }).then(error=>{})
+ }
+
+ // 第一个下拉搜索
+ const menu = (
+
+ )
+ function chooseAboutMe(e){
+ setPage(1);
+ // setCategory("opened");
+ setAboutMe(e.key);
+ }
+
+ //清除筛选条件
+ function clearCondition(){
+ setKeyword(undefined);
+ setAboutMe("all");
+ setCategory("opened");
+ setUpdateIds(undefined);
+ setAllValue([]);
+ setPage(1);
+ setValue(undefined);
+ setNames(undefined);
+ setEnd(undefined);
+ setBegin(undefined);
+ // if(!value){
+ // Init();
+ // }
+ // 清除下拉选项
+ menuRef.current && menuRef.current.clearChoose();
+ }
+
+ // 全选所有issue
+ function chooseAll(e){
+ setCheckAll(e.target.checked);
+ // 清除下拉选项
+ menuRef.current && menuRef.current.clearChoose();
+ if(e.target.checked){
+ setAllValue(allIds);
+ }else{
+ setAllValue([]);
+ }
+ }
+
+ // 选择列表里的issue
+ function checkIssues(value){
+ // 清除下拉选项
+ menuRef.current && menuRef.current.clearChoose();
+ setAllValue(value);
+ if(value.length === allIds.length){
+ setCheckAll(true);
+ }else{
+ setCheckAll(false);
+ }
+ }
+
+ // 取消
+ function cancelUpdate(){
+ setAllValue([]);
+ setCheckAll(false);
+ // 清除下拉选项
+ menuRef.current && menuRef.current.clearChoose();
+ }
+ // 确认修改 allValue updateIds
+ function sureUpdate(){
+ const url = `/v1/${owner}/${projectsId}/issues/batch_update`;
+ axios.patch(url,{
+ assigner_ids: updateChooseIds && updateChooseIds.assigner_id && updateChooseIds.assigner_id.split(","),
+ ids: allValue,
+ issue_tag_ids: updateChooseIds && updateChooseIds.issue_tag_ids && updateChooseIds.issue_tag_ids.split(","),
+ milestone_id: updateChooseIds && updateChooseIds.milestone_id,
+ priority_id: updateChooseIds && updateChooseIds.issue_priorities_id,
+ status_id: updateChooseIds && updateChooseIds.status_id
+ }).then(result=>{
+ if(result){
+ setUpdateIds(undefined);
+ setAllValue([]);
+ setTotal(undefined);
+ Init();
+ cancelUpdate();
+ }
+ }).catch(error=>{})
+ }
+
+ // 切换页码
+ function changepage(p){
+ setPage(p);
+ Init(updateIds,p,names);
+ if (document) { // 可以排除不需要置顶的页面
+ if (document.documentElement || document.body) {
+ document.documentElement.scrollTop = document.body.scrollTop = 0; // 切换路由时手动置顶
+ }
+ }
+ }
+
+ // 删除issue相关 func
+ function onSuccess(){
+ const url = `/v1/${owner}/${projectsId}/issues/batch_destroy`;
+ axios.delete(url,{
+ params:{ids:allValue}
+ }).then(result=>{
+ if(result){
+ setUpdateIds(undefined);
+ setAllValue([]);
+ setVisible(false);
+ setCheckAll(false);
+ props.showNotification("疑修删除成功!");
+ Init();
+ }
+ }).catch(error=>{})
+ }
+
+ function chooseFunc(ids,n){
+ setNames(n);
+ setPage(1);
+ if(allValue && allValue.length>0){
+ // 将ids保存下来以便修改
+ // setUpdateIds(ids);
+ setUpdateChooseIds(ids);
+ }else{
+ if(ids.status_id === "5"){
+ setCategory("closed");
+ }else{
+ setCategory("opened");
+ }
+ setUpdateIds(ids);
+ setTotal(undefined);
+ // Init(ids,1,n);
+ }
+ }
+
+ function chageLimit(c,p){
+ setPage(1);
+ setLimit(p);
+ }
+ function changeCategory(value){
+ if(value !== category){
+ let ids = {...updateIds};
+ let ns = {...names};
+ if(value === "closed"){
+ ns = {...ns,status_name:"关闭"}
+ ids = {...ids,status_id:'5'};
+ setNames(ns)
+ setUpdateIds(ids);
+ }else{
+ ns = {...ns,status_name:undefined}
+ ids = {...ids,status_id:undefined};
+ setNames(ns)
+ setUpdateIds(ids);
+ }
+ setCategory(value);
+ setPage(1);
+ setTotal(undefined);
+ }
+ }
+
+ function changeSearchValueFunc(e){
+ setValue(e.target.value);
+ if(e.target.value===""){
+ setKeyword(undefined);
+ }
+ }
+
+ // 选择搜索时间
+ function changeBeginTime(data, value){
+ setPage(1);
+ setBegin(value[0] || '');
+ setEnd(value[1] || '');
+ }
+ return(
+
+
setVisible(false)} onSuccess={onSuccess}/>
+
+
+ {
+ (current_user && current_user.login) &&
+
+
+ {aboutMe === "all" ? "全部":aboutMe === "aboutme"?"与我相关":aboutMe === "assignedme"?"我负责的":aboutMe === "authoredme"?"我创建的":"@我的"}
+
+
+
+ }
+
setKeyword(value)}
+ style={{ width: 354 , height : 32 }}
+ allowClear
+ />
+ {
+ clearFlag &&
+
+ 清除筛选条件
+ }
+
+
+
+ {
+ permission && permission !== "Reporter" &&
+
标记管理
+ }
+ {props.history.push(`/${owner}/${projectsId}/issues/new`)}} checklogin className="operateButton ml20">创建疑修
+
+
+
+
+
+
+ {
+ allValue && allValue.length>0 ?
+
选择{allValue.length}个issue
+ :
+
+ - {changeCategory("all")}}>全部{issueTotal}
+ - {changeCategory("opened")}}>开启中{openedCount}
+ - {changeCategory("closed")}}>已关闭{closedCount}
+
+ }
+
+
+
0}
+ owner={owner}
+ projectsId={projectsId}
+ chooseFunc={chooseFunc}
+ defaultNames={names}
+ defaultIds={allValue && allValue.length>0 ? undefined : updateIds}
+ open_blockchain={open_blockchain}
+ />
+ {
+ allValue && allValue.length>0 ?
+
+
+
+
+
+ :""
+ }
+
+
+ {
+ total === 0 &&
+ (!has_created_issues ?
+
+

+
欢迎使用疑修(Issue)
+
疑修用于记录与跟踪待办事项、项目bug、功能需求等。在使用之前,请您先{props.history.push(`/${owner}/${projectsId}/issues/new`)}} className="color-blue">创建一个疑修
+
+ :
+
+ )
+ }
+ {
+ total > 0 &&
+
+
+
+ {issueList.map((item,key)=>{
+ return(
+ }
+ item={item}
+ owner={owner}
+ projectsId={projectsId}
+ />
+ )
+ })
+ }
+
+
+ {
+ total > defaultLimit &&
+
+ }
+
+ }
+ {total === undefined &&
}
+
+
+ )
+}
+export default List;
\ No newline at end of file
diff --git a/src/forge/Issues/Pages/new.jsx b/src/forge/Issues/Pages/new.jsx
new file mode 100644
index 000000000..ce09cc6be
--- /dev/null
+++ b/src/forge/Issues/Pages/new.jsx
@@ -0,0 +1,303 @@
+import React , { useEffect , useState , forwardRef , useRef } from 'react';
+import { Form , InputNumber } from 'antd';
+import { Box , LongWidth } from '../../Component/layout';
+import DropMenu from '../Component/dropMenu';
+import ChooseMenu from '../Component/chooseMenu';
+import AddTagsBox from '../Component/addTagsBox';
+import Date from '../Component/date';
+import NewPanel from '../Component/newPanel';
+import axios from 'axios';
+
+function New(props){
+ // history search 是否含有意见反馈标识
+ const {location:{search}, projectDetail} = props;
+ const feedBack = search && search.indexOf('type=feedback') !== -1;
+ // 里程碑id
+ const milepostId = props.match.params.milepostId;
+
+
+ const [ statusList , setStatusList ] = useState([]);
+ const [ prioritiesList , setPrioritiesList ] = useState([]);
+ const [ chargeList , setChargeList ] = useState([]);
+ const [ charge , setCharge ] = useState(undefined);
+ const [ millstoneList , setMillstoneList ] = useState([]);
+ const [ millstone , setMillstone ] = useState(undefined);
+ const [ tagList , setTagList ] = useState(undefined);
+ const [ tag , setTag ] = useState(undefined);
+ const [ branchList , setBranchList ] = useState(undefined);
+ const [ branch , setBranch ] = useState(undefined);
+
+
+ const [ assigner_choose , setAssigner_choose ] = useState([]);
+ const [ prioritie_choose , setPrioritie_choose ] = useState([]);
+ const [ status_choose , setStatus_choose ] = useState([]);
+ const [ tag_choose , setTag_choose ] = useState([]);
+ const [ millstone_choose , setMillstone_choose ] = useState([]);
+ const [ branch_choose , setBranch_choose ] = useState([]);
+
+ const [ start_date, setStartDate ] = useState("");//moment().format('YYYY-MM-DD')
+ const [ due_date, setDueDate ] = useState("");
+
+ const [ rewardAmount , setRewardAmount ] = useState(undefined);
+ const [ rewardFlag , setRewardFlag ] = useState(false);
+
+ const owner = props.match.params.owner;
+ const projectsId = props.match.params.projectsId;
+ const permission = props && props.projectDetail && props.projectDetail.permission;
+ const { open_blockchain } = props;
+ const ref = useRef(null);
+
+ useEffect(()=>{
+ if(projectDetail){
+ if(feedBack){
+ document.title = `意见反馈`;
+ return;
+ }
+ const { author, name} = projectDetail;
+ document.title = `新建疑修-${author.name}/${name}`
+ }
+ },[projectDetail, feedBack])
+
+ useEffect(()=>{
+ if(milepostId){
+ getMillstone(milepostId);
+ }
+ },[milepostId])
+
+
+ // 获取状态列表
+ useEffect(()=>{
+ getStatus();
+ },[])
+ function getStatus(){
+ const url = `/v1/${owner}/${projectsId}/issue_statues`;
+ axios.get(url).then(result=>{
+ if(result && result.data){
+ let status = result.data.statues;
+ setStatusList(status);
+ if(status && status.length>0){
+ setStatus_choose(status.filter(i=>i.id === 1));
+ }
+ }
+ })
+ }
+ // 获取优先级列表
+ useEffect(()=>{
+ getPriorities();
+ },[])
+
+ function getPriorities(){
+ const url = `/v1/${owner}/${projectsId}/issue_priorities`;
+ axios.get(url).then(result=>{
+ if(result && result.data){
+ let priorities = result.data.priorities;
+ let array =[];
+ let colors = ["#1abcb1","#28be6c","#e67e22","#db3d1d"];
+ if(priorities && priorities.length>0){
+ array = priorities.map((i,k)=>{
+ let arr = {...i,color:colors[k]}
+ return arr;
+ })
+ }
+ setPrioritiesList(array);
+ setPrioritie_choose(array.filter(i=>i.id === 2));
+ }
+ })
+ }
+
+ // 获取负责人列表
+ useEffect(()=>{
+ getCharge();
+ },[charge])
+
+ function getCharge(){
+ const url = `/v1/${owner}/${projectsId}/collaborators`;
+ axios.get(url,{params:{keyword:charge,only_name:true}}).then(result=>{
+ if(result && result.data){
+ setChargeList(result.data.collaborators);
+ if(feedBack && !charge){
+ // 指定指派成员-意见反馈
+ const {collaborators} = result.data;
+ const id = collaborators.filter(item=>item.id === 86107);
+ setAssigner_choose(id.length === 0 ? [collaborators[0]] : id)
+ }
+ }
+ })
+ }
+ // 获取里程碑列表
+ useEffect(()=>{
+ getMillstone(millstone);
+ },[millstone])
+
+ function getMillstone(keyword){
+ const url = `/v1/${owner}/${projectsId}/milestones`;
+ axios.get(url,{params:{keyword,only_name:true}}).then(result=>{
+ if(result && result.data){
+ let milestones = result.data.milestones;
+ if(milepostId && milepostId === keyword){
+ setMillstone_choose(milestones);
+ }else{
+ setMillstoneList(milestones);
+ }
+ }
+ })
+ }
+ // 获取标记列表
+ useEffect(()=>{
+ getSign();
+ },[tag])
+ function getSign(){
+ const url = `/v1/${owner}/${projectsId}/issue_tags`;
+ axios.get(url,{params:{keyword:tag,only_name:true}}).then(result=>{
+ if(result && result.data){
+ setTagList(result.data.issue_tags);
+ }
+ })
+ }
+ // 获取标记列表
+ useEffect(()=>{
+ getBranch(branch);
+ },[branch])
+ function getBranch(branch){
+ if(branch){
+ let b = [...branchList];
+ let l = b.filter(i=>i.name.indexOf(branch)>-1);
+ setBranchList(l);
+ return ;
+ }
+ const url = `/${owner}/${projectsId}/branches.json`;
+ axios.get(url,{params:{keyword:tag}}).then(result=>{
+ if(result && result.data){
+ setBranchList(result.data);
+ }
+ })
+ }
+
+
+ // 创建
+ function createFunc(values,fileList,receivers_login,description){
+ const { subject } = values;
+ const url = `/v1/${owner}/${projectsId}/issues`;
+ let b = branch_choose && branch_choose.length>0 ? branch_choose.map(i=>{return i.name}) :undefined;
+ let t = tag_choose && tag_choose.length>0 ? tag_choose.map(i=>{return i.id}) :undefined;
+ let a = assigner_choose && assigner_choose.length>0 ? assigner_choose.map(i=>{return i.id}) :undefined;
+ let m = millstone_choose && millstone_choose.length>0 ? millstone_choose.map(i=>{return i.id}) :undefined;
+ let p = prioritie_choose && prioritie_choose.length>0 ? prioritie_choose.map(i=>{return i.id}) :undefined;
+ let s = status_choose && status_choose.length>0 ? status_choose.map(i=>{return i.id}) :undefined;
+ axios.post(url,{
+ description,subject,
+ branch_name:b && b.join(","),
+ status_id:s && s.join(","),
+ priority_id:p && p.join(","),
+ milestone_id:m && m.join(","),
+ issue_tag_ids:t,
+ assigner_ids:a,
+ attachment_ids:fileList,
+ start_date,due_date,receivers_login,
+ blockchain_token_num:rewardAmount
+ }).then(result=>{
+ if(result && result.data && result.data.project_issues_index){
+ window.scrollTo(0,0);
+ props.showNotification("任务创建成功!");
+ props.history.push(`/${owner}/${projectsId}/issues/${result.data.project_issues_index}`);
+ }
+ }).catch(error=>{})
+ }
+
+
+ function changeAmount(value){
+ if(value && value > 0){
+ setRewardAmount(value);
+ }
+ if(!value){
+ setRewardAmount(value);
+ }
+ }
+ return(
+
+
新建疑修
+
+
+ {/* 意见反馈初始化内容 */}
+
+
+
+
{setCharge(value)}}
+ headImg
+ selectValueList={assigner_choose}
+ chooseFunc={(list)=>{setAssigner_choose(list);}}
+ double={5}
+ removeFlag
+ />
+ {setStatus_choose(list);}}
+ />
+ {setPrioritie_choose(list);}}
+ />
+ {setTag(value)}}
+ selectValueList={tag_choose}
+ chooseFunc={(list)=>{setTag_choose(list);}}
+ double={3}
+ colorFlag="2"
+ removeFlag
+ onAdd={permission && permission !== "Reporter" ? ()=>{getSign();} :false}
+ owner={owner}
+ projectsId={projectsId}
+ />
+ {setMillstone(value)}}
+ selectValueList={millstone_choose}
+ editFlag={milepostId ? true : false}
+ chooseFunc={(list)=>{setMillstone_choose(list);}}
+ />
+ {setBranch(value)}}
+ selectValueList={branch_choose}
+ chooseFunc={(list)=>{setBranch_choose(list);}}
+ />
+ {
+ open_blockchain &&
+
+
+ 悬赏金额
+ {/* {!(milepostId ? true : false) && } */}
+
+
+
+ }
+ setStartDate(date)}/>
+ setDueDate(date)}/>
+
+
+
+ )
+}
+export default Form.create()(forwardRef(New));
\ No newline at end of file
diff --git a/src/forge/Issues/Pages/sign.jsx b/src/forge/Issues/Pages/sign.jsx
new file mode 100644
index 000000000..be3abaf0e
--- /dev/null
+++ b/src/forge/Issues/Pages/sign.jsx
@@ -0,0 +1,267 @@
+import React , { useEffect , useState } from 'react';
+import create from '../Img/create.png';
+import { Menu , Dropdown , Icon, Input, Button , Spin , Pagination } from 'antd';
+import ColorCard from '../Component/colorCard';
+import DelBox from '../Component/delBox';
+import axios from 'axios';
+import {Link} from 'react-router-dom';
+const limit = 15;
+function Sign(props){
+ const [ edit , setEdit ] = useState(false);
+ const [ colors , setColors ] = useState(undefined);
+ const [ delVisible , setDelVisible ] = useState(false);
+ const [ editId , setEditId ] = useState(undefined);
+ const [ list , setList ] = useState(undefined);
+ const [ total , setTotal ] = useState(undefined);
+ const [ page , setPage ] = useState(1);
+
+ const [ name , setName ] = useState(undefined);
+ const [ desc , setDesc ] = useState(undefined);
+ const [ order_name , setOrderName ] = useState(undefined);
+ const [ order_type , setOrderType ] = useState(undefined);
+ const [ nameFlag , setNameFlag ] = useState(undefined);
+ const [ defaultColor , setDefaultColor ]= useState(undefined);
+
+ const [ arrayName , setArrayName ] = useState("标记");
+ const [ key , setKey ] = useState("0");
+ const owner = props.match.params.owner;
+ const projectsId = props.match.params.projectsId;
+ const { projectDetail } = props;
+ const permission = projectDetail && props.projectDetail.permission;
+
+
+ useEffect(()=>{
+ if(permission !== undefined){
+ if(!permission || (permission && permission === "Reporter")){
+ window.location.href = "/403";
+ }
+ }
+ },[permission])
+
+ useEffect(()=>{
+ if(projectDetail){
+ const { author, name } = projectDetail;
+ document.title = `项目标记-${author.name}/${name}`;
+ }
+ },[projectDetail])
+
+ useEffect(()=>{
+ Init();
+ },[page,order_name,order_type])
+
+ function Init(){
+ const url = `/v1/${owner}/${projectsId}/issue_tags`;
+ axios.get(url,{
+ params:{
+ page,limit,sort_by:order_name,sort_direction:order_type
+ }
+ }).then(result=>{
+ if(result && result.data){
+ setList(result.data.issue_tags);
+ setTotal(result.data.total_count);
+ }
+ })
+ }
+
+ function arrayList(e){
+ const { eventKey , value , children , createName } = e.item.props;
+ setPage(1);
+ if(value === order_type && createName === order_name){
+ setOrderName(undefined);
+ setOrderType(undefined);
+ setArrayName("标记");
+ setKey("0");
+ }else{
+ setOrderName(createName);
+ setOrderType(value);
+ setArrayName(children);
+ setKey(eventKey);
+ }
+ }
+
+ const menu = (
+
+ );
+
+ function getColor(colors){
+ setColors(colors);
+ }
+
+ function deleteFunc(){
+ if(editId){
+ const url = `/v1/${owner}/${projectsId}/issue_tags/${editId}`;
+ axios.delete(url).then(result=>{
+ if(result){
+ props.showNotification("项目标记删除成功!");
+ if(list && list.length === 1 && page>1){
+ setPage(page-1);
+ }else{
+ Init();
+ }
+ setDelVisible(false);
+ }
+ }).catch(error=>{})
+ }
+ }
+
+ function sureSave(){
+ if(!name){
+ setNameFlag(true);
+ }else{
+ setNameFlag(false);
+ if(editId){
+ // 编辑
+ const url = `/v1/${owner}/${projectsId}/issue_tags/${editId}`;
+ axios.patch(url,{
+ color: colors || defaultColor,
+ description: desc,
+ name
+ }).then(result=>{
+ if(result){
+ props.showNotification("标记编辑成功!");
+ Init();
+ cancel();
+ }
+ }).catch(error=>{})
+ }else{
+ // 保存
+ const url = `/v1/${owner}/${projectsId}/issue_tags`;
+ axios.post(url,{
+ color: colors || defaultColor,
+ description: desc,
+ name
+ }).then(result=>{
+ if(result){
+ props.showNotification("标记新增成功!");
+ Init();
+ cancel();
+ }
+ }).catch(error=>{})
+ }
+ }
+ }
+
+ function cancel(){
+ setNameFlag(false);
+ setName(undefined);
+ setEdit(false);
+ setDesc(undefined);
+ }
+
+ function editFunc(i){
+ setEditId(i.id);
+ setDefaultColor(i.color);
+ setName(i.name);
+ setDesc(i.description);
+ setEdit(true);
+ }
+
+ function onChangeValue(e){
+ setName(e.target.value);
+ if(e.target.value){
+ setNameFlag(false);
+ }else{
+ setNameFlag(true);
+ }
+ }
+
+ function createSign(){
+ const c = Math.random().toString(16).substr(-6);
+ c && setDefaultColor(`#${c}`);
+ setEdit(true);
+ }
+ return(
+
+
setDelVisible(false)}
+ onSuccess={deleteFunc}
+ title="删除标记"
+ content={}
+ />
+
+
疑修 / 项目标记
+
创建标记
+
+ {
+ edit &&
+
+
+
+ { nameFlag &&
请输入标记名称
}
+
+
setDesc(e.target.value)} placeholder="描述30字以内"/>
+
+
+
+
+
+
+ }
+
+ 项目标记({total || 0})
+
+ {arrayName}
+
+
+ {
+ list && list.length > 0 ?
+
+ :""
+ }
+ {
+ total > limit &&
+
+
setPage(p)} pageSize={limit}/>
+
+ }
+ {total === undefined &&
}
+
+ )
+}
+export default Sign;
\ No newline at end of file
diff --git a/src/forge/Issues/index.jsx b/src/forge/Issues/index.jsx
new file mode 100644
index 000000000..a3bb5dfd4
--- /dev/null
+++ b/src/forge/Issues/index.jsx
@@ -0,0 +1,80 @@
+import React,{useEffect} from 'react';
+import { Route, Switch } from "react-router-dom";
+import Loadable from "react-loadable";
+import Loading from "../../Loading";
+import './index.scss';
+
+const List = Loadable({
+ loader: () => import("./Pages/list"),
+ loading: Loading,
+});
+const Detail = Loadable({
+ loader: () => import("./Pages/details"),
+ loading: Loading,
+});
+const Sign = Loadable({
+ loader: () => import("./Pages/sign"),
+ loading: Loading,
+});
+const New = Loadable({
+ loader: () => import("./Pages/new"),
+ loading: Loading,
+});
+
+function Index(props){
+ const pathname = props.history.location.pathname;
+ const { project } = props;
+ const open_blockchain = project && project.open_blockchain;
+
+ useEffect(() => {
+ if (document) { // 可以排除不需要置顶的页面
+ if (document.documentElement || document.body) {
+ document.documentElement.scrollTop = document.body.scrollTop = 0; // 切换路由时手动置顶
+ }
+ }
+ }, [pathname]);
+ return(
+
+
+ (
+
+ )}
+ >
+ {/* 里程碑创建issue */}
+ (
+
+ )}
+ >
+ (
+
+ )}
+ >
+ (
+
+ )}
+ >
+ (
+
+ )}
+ >
+ (
+
+ )}
+ >
+
+
+ )
+}
+export default Index;
\ No newline at end of file
diff --git a/src/forge/Issues/index.scss b/src/forge/Issues/index.scss
new file mode 100644
index 000000000..1283e3a8e
--- /dev/null
+++ b/src/forge/Issues/index.scss
@@ -0,0 +1,854 @@
+.borderNo.ant-input,.borderNo .ant-input-number-input,.borderNo,.borderNo.ant-input-number-focused{
+ border:none!important;
+ padding:4px 0px!important;
+ background-color: transparent!important;
+ box-shadow: none!important;
+ .ant-input-number-handler-wrap{
+ display: none;
+ }
+ &:focus{
+ border:none!important;
+ box-shadow: none!important;
+ }
+}
+
+.pagebox{
+ width: 1200px;
+ margin:0px auto;
+}
+// 删除issue弹框
+.deldesc{
+ justify-content: center;
+ .red{
+ color: #DF0002;
+ }
+}
+// 可公用样式(在issue的页面中)
+// 可点击(有向下箭头且点击出现下拉框,移入灰色背景加深)的灰色按钮样式
+.dorpdownButton{
+ padding:0px 15px;
+ height:32px;
+ color: #333333!important;
+ cursor: pointer;
+ line-height: 32px;
+ background-color:#fafbfc;
+ border:1px solid #d0d0d0;
+ border-radius: 4px;
+ display: flex;
+ align-items: center;
+ min-width: 107px;
+ box-sizing: border-box;
+ &>span{
+ display: block;
+ width: 56px;
+ text-align: center;
+ }
+ &:hover{
+ background-color: #f3f4f6;
+ }
+}
+// flex-左右,上下居中
+.between{
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+// 蓝色背景操作栏
+.bluebar{
+ height:50px;
+ background-color:#fafcff;
+ border:1px solid rgba(42, 97, 255, 0.23);
+ border-radius:4px 4px 0px 0px;
+ padding:0px 26px;
+}
+// 按钮样式
+.ant-btn-background-ghost{
+ border-color:#acb0bf;
+ color: #acb0bf;
+ background-color: #fff!important;
+ &.ant-btn-primary{
+ color: #466aff;
+ border-color: #466aff;
+ background-color: #fff!important;
+ }
+ &.ant-btn-danger{
+ color: #f60011;
+ border-color: #f60011;
+ background-color:rgba(196, 0, 14, 0.09)!important;
+ }
+}
+.lists{
+ margin-bottom: 50px;
+}
+.dataempty{
+ height: 420px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 1px solid;
+ border-color: #d9d9d9;
+ border-radius: 0px 0px 4px 4px;
+ border-top: none;
+}
+// 列表为空样式
+.listempty{
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ background-color:#fafcff;
+ border:1px solid rgba(42, 97, 255, 0.23);
+ border-top: none;
+ border-radius:0px 0px 4px 4px;
+ height: 344px;
+ padding-top: 62px;
+ color: #333;
+}
+// 列表页面样式
+.pageheader{
+ display: flex;
+ justify-content: space-between;
+ padding:30px 0px;
+ align-items: center;
+ &>div{
+ display: flex;
+ align-items: center;
+ }
+}
+.listheader{
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ height:50px;
+ background-color:#fafcff;
+ border:1px solid;
+ border-color:rgba(42, 97, 255, 0.23);
+ border-radius:4px 4px 0px 0px;
+ padding-left: 18px;
+ .menusul{
+ display: flex;
+ align-items: center;
+ }
+ ul{
+ display: flex;
+ align-items: center;
+ &.statusul{
+ li{
+ width: 106px;
+ font-size: 15px;
+ &>span{
+ height:19px;
+ line-height: 17px;
+ color: #666;
+ background-color:rgba(70, 106, 255, 0.09);
+ border-radius:10px;
+ margin-left: 5px;
+ padding:0px 6px;
+ font-size: 13px;
+ display: block;
+ border:1px solid ;
+ border-color: rgba(70, 106, 255, 0.01);
+ min-width: 30px;
+ text-align: center;
+ }
+ }
+ }
+ li{
+ color: #898d9d;
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+ &.active{
+ color: #466aff;
+ // font-weight: 500;
+ &>span{
+ font-weight: normal;
+ color: #466aff;
+ border-color: #466aff;
+ background-color: #fff;
+ }
+ }
+ }
+ }
+ .dropboxul{
+ padding-right: 20px;
+ li.minwidth{
+ min-width: 70px;
+ }
+ li{
+ min-width:88px;
+ display: flex;
+ justify-content: flex-end;
+ cursor: default;
+ margin-left: 12px;
+ .dropspan{
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+ &>span{
+ max-width: 80px;
+ display: inline-block;
+ text-align: right;
+ }
+ }
+ }
+ }
+
+}
+// issue列表
+.listdatas{
+ border:1px solid #d0d0d0;
+ border-top: none;
+ border-radius: 0px 0px 2px 2px;
+ &>div:not(.none_panels){
+ padding:20px 18px;
+ border-bottom: 1px solid #eee;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ &:hover{
+ background-color:rgba(231, 233, 242, 0.26);
+ }
+ &:last-child{
+ border-bottom: none;
+ }
+ .issuedetail{
+ display: flex;
+ align-items: flex-start;
+ flex:1;
+ }
+ }
+ .idetails{
+ display: flex;
+ align-items: center;
+ &>a{
+ color: #40424a;
+ margin-right: 10px;
+ font-size: 15px;
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 375px;
+ white-space: nowrap;
+ }
+ .tagscolor{
+ padding:0px 6px;
+ border-radius: 2px;
+ height: 20px;
+ line-height: 20px;
+ max-width: 108px;
+ font-size: 13px;
+ cursor: default;
+ color: #fff;
+ }
+ }
+ .infos{
+ display: flex;
+ align-items: center;
+ color: #898d9d;
+ margin-top: 12px;
+ font-size: 13px;
+ // img{
+ // height: 22px;
+ // width: 22px;
+ // margin-right: 4px;
+ // border-radius: 50%;
+ // }
+ }
+ .issuecondition{
+ display: flex;
+ align-items: center;
+ color: #40424a;
+ .principal{
+ position: relative;
+ display: flex;
+ justify-content: flex-end;
+ width: 150px;
+ text-align: justify;
+ // &.hovers:hover{
+ // a{
+ // position:initial;
+ // }
+ // }
+ // a{
+ // position: absolute;
+ // right: 0px;
+ // transition: 0.6s;
+ // }
+ // img,span{
+ // width: 22px;
+ // height: 22px;
+ // border-radius: 50%;
+ // }
+ // span{
+ // background-color:#ced5ef;
+ // text-align: center;
+ // line-height: 12px;
+ // color:#000000;
+ // z-index: 6;
+ // }
+ }
+ &>div{
+ width: 90px;
+ text-align: right;
+ }
+ .commentnum{
+ padding-right: 5px;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ }
+ }
+}
+// 状态样式-公用
+
+.status{
+ display: block;
+ height:22px;
+ border-radius:6px;
+ text-align: center;
+ margin-right: 10px;
+ line-height: 20px;
+ width: 45px;
+ font-size: 14px;
+ border:1px solid #fff;
+ &.normals{
+ border-color:#28be6c;
+ color:#28be6c;
+ }
+ &.hight{
+ border-color: #e67e22;
+ color: #e67e22;
+ }
+ &.urgent{
+ border-color: #db3d1d;
+ color: #db3d1d;
+ }
+ &.low{
+ border-color: #1abcb1;
+ color: #1abcb1;
+ }
+}
+.ilog{
+ min-width: 45px;
+ margin-right: 10px;
+ clear: both;
+ .number{
+ background-color:rgba(213, 220, 246, 0.36);
+ border-radius:4px;
+ height: 19px;
+ line-height: 19px;
+ color: #666666;
+ text-align: center;
+ float: left;
+ padding:0px 4px;
+ cursor: pointer;
+ font-size: 13px;
+ }
+}
+// colorCard
+.color{
+ width: 20px;
+ height: 20px;
+ border-radius: 2px;
+}
+.swatch{
+ padding: 5px;
+ background: #fff;
+ border-radius: 1px;
+ width: 92px;
+ height: 28px;
+ line-height: 20px;
+ box-shadow: 0 0 0 1px rgba(0,0,0,.1);
+ display: flex;
+ cursor: pointer;
+}
+.popover {
+ position: absolute;
+ z-index: 2;
+}
+.cover {
+ position: fixed;
+ top: 0px;
+ right: 0px;
+ bottom: 0px;
+ left: 0px;
+}
+.modalcolor {
+ width: 20px;
+ height: 20px;
+ border-radius: 2px;
+}
+// 标记管理页面
+.editbar{
+ padding:18px 20px;
+ background-color:rgba(218, 225, 251, 0.24);
+ display: flex;
+ align-items: center;
+ margin-bottom: 30px;
+ justify-content: space-between;
+ &>div{
+ position: relative;
+ .inputred{
+ border-color: red;
+ }
+ .red{
+ position: absolute;
+ left: 166px;
+ width: 100%;
+ top: 2px;
+ color: red;
+ }
+ }
+}
+.signlist{
+ padding:10px 0px;
+ margin-bottom: 40px!important;
+ &>li{
+ display: flex;
+ margin-bottom: 5px;
+ padding:10px 20px;
+ &>p{
+ flex:1;
+ color: 40424a;
+ &:first-child{
+ flex:1.2;
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+ }
+ &:last-child{
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+ justify-content: flex-end;
+ }
+ .square{
+ display: block;
+ width: 22px;
+ height: 15px;
+ border-radius:2px;
+ }
+ .line{
+ position: relative;
+ padding-left: 13px;
+ &::before{
+ position: absolute;
+ height:13px;
+ width: 1px;
+ left: 0px;
+ top:7px;
+ background-color:#d5d5d5;
+ content: "";
+ }
+ }
+ }
+ }
+}
+// 新建页面样式
+.shortwidth{
+ width: 330px;
+ padding-left: 50px;
+ &>li{
+ border-bottom: 1px solid rgba(172, 176, 191, 0.17);
+ padding-bottom: 8px;
+ margin-bottom: 22px!important;
+ &>span{
+ font-size: 15px;
+ position: relative;
+ color: #40424a;
+ display: flex;
+ width: 100%;
+ height: 20px;
+ line-height: 20px;
+ font-weight: 600;
+ a{
+ position: absolute;
+ right: 0px;
+ }
+ }
+ .operatevalue{
+ line-height: 30px;
+ color: #acb0bf;
+ margin-top: 10px;
+ display: flex;
+ flex-wrap: wrap;
+ .colorsborder{
+ padding: 0px 8px;
+ height: 20px;
+ line-height: 18px;
+ border-radius: 4px;
+ text-align: center;
+ border:1px solid ;
+ font-size: 13px;
+ }
+ .colorsquare{
+ padding: 0px 8px;
+ height: 20px;
+ line-height: 20px;
+ border-radius: 4px;
+ text-align: center;
+ color: #fff;
+ font-size: 13px;
+ }
+ &>p{
+ margin-top: 5px!important;
+ position: relative;
+ padding-right: 15px;
+ &.removeFlag>span{
+ max-width: 230px;
+ }
+ .removeicon{
+ display: none;
+ }
+ }
+ .removeFlag:hover{
+ border-radius:2px;
+ .removeicon{
+ display: block;
+ }
+ }
+ &>p:first-child{
+ margin-top: 0px;
+ }
+ }
+ }
+}
+.addTags_form{
+ .ant-row.ant-form-item{
+ margin-bottom: 12px;
+ }
+ .manageTitle{
+ margin-bottom: 20px!important;
+ padding-bottom: 15px;
+ line-height: 22px;
+ color:#5f6872;
+ font-size:16px;
+ border-bottom:1px solid #e1e3e8;
+ }
+ .inline{
+ display: flex;
+ align-items: flex-start;
+ }
+}
+.addTagsModal{
+ .ant-modal-content{
+ background-image:linear-gradient(359.37deg,#ebf3ff 0%,#f8fbff 55.01%,#f1f5ff 100%);
+ }
+ .ant-modal-header{
+ background-color: unset;
+ padding:0px 24px;
+ height: 50px;
+ line-height: 50px;
+ .ant-modal-title{
+ text-align: left;
+ color:#333333;
+ font-size:17px;
+ font-weight: normal!important;
+ height: 50px;
+ line-height: 50px!important;
+ }
+ }
+ .ant-modal-close{
+ top:0px !important;
+ }
+ .popover{
+ left: 94px;
+ top:0px;
+ }
+}
+.menusEmpty{
+ padding:12px 20px 10px 16px;
+ &>p{
+ padding:10px 16px;
+ }
+ &>a{
+ padding:10px 16px;
+ display: block;
+ border-top: 1px solid rgba(172, 176, 191, 0.17);
+ }
+}
+.piecemenu{
+ li{
+ display: flex;
+ align-items: center;
+ }
+}
+.colorpiece{
+ display: block;
+ width: 22px!important;
+ height: 15px;
+ margin-right: 9px;
+ border-radius:2px;
+}
+.overlayStyle{
+ background-color:#ffffff;
+ border-radius:6px;
+ box-shadow:0px 0px 10px rgba(24, 54, 181, 0.17);
+ max-width: 280px;
+ z-index: 100;
+ .searchbox{
+ padding:12px 20px 0px 16px;
+ }
+ .ant-menu{
+ padding:10px 16px 10px 16px;
+ max-height: 324px;
+ overflow-y: auto;
+ li{
+ padding:0px 16px;
+ height: 38px;
+ line-height: 38px;
+ border-bottom: none;
+ color:#898d9d;
+ position: relative;
+ width: 100%;
+ box-sizing: border-box;
+ &:hover,&.ant-menu-item-selected{
+ background-color:rgba(70, 106, 255, 0.07);
+ color:#40424a;
+ }
+ &.ant-menu-item-selected::before{
+ content:"✓";
+ position: absolute;
+ right: 15px;
+ color: #acb0bf;
+ top: 0px;
+ }
+ }
+ }
+}
+//详情页面
+.editpanel{
+ padding:20px;
+ margin-top: 25px;
+ border:1px solid rgba(42, 97, 255, 0.23);
+ border-radius: 2px;
+}
+.detailbanner{
+ // margin-top: 25px;
+ // height:82px;
+ // background-color:#fafcff;
+ // border:1px solid rgba(42, 97, 255, 0.23);
+ // border-radius:4px;
+ // padding:12px 25px 12px 20px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ .detailtitle{
+ flex: 1;
+ &>div{
+ display: flex;
+ align-items: flex-start;
+ }
+ .name{
+ font-size: 16px;
+ font-weight: 700;
+ color:#333;
+ line-height: 22px;
+ max-width: 580px;
+ word-break: break-all;
+ }
+ .author{
+ img{
+ width: 26px;
+ height: 26px;
+ margin-right: 4px;
+ border-radius: 50%;
+ }
+ color: #466aff;
+ }
+ }
+ .detailoperate{
+ display: flex;
+ align-items: center;
+ }
+}
+.descPanel{
+ margin-top:30px;
+ // padding:20px;
+ // background-color: #f7f8fd;
+ // border:1px solid #c9d8ff;
+ // border-radius: 2px;
+}
+// 新建页面
+.explain{
+ .ant-form-explain{
+ position: absolute;
+ }
+}
+
+
+.overlaydrop{
+ &.large{
+ width: 260px;
+ }
+ &.small{
+ width: 120px;
+ padding:12px 0px!important;
+ }
+ padding:18px 15px;
+ position: relative;
+ border-radius:6px;
+ box-shadow:0px 0px 10px rgba(24, 54, 181, 0.17);
+ background-color: #fff;
+ .ant-menu{
+ max-height: 305px;
+ overflow-y: auto;
+ li{
+ height: 38px;
+ line-height: 38px;
+ border-bottom: none!important;
+ display: flex;
+ align-items: center;
+ padding-left: 6px;
+ padding-right: 0px;
+ img{
+ width: 24px;
+ height: 24px;
+ border-radius: 50%;
+ }
+ span{
+ display: block;
+ width:100%;
+ margin-left: 10px;
+ padding-right: 25px;
+ }
+ &:hover,&.ant-menu-item-selected{
+ background-color:rgba(70, 106, 255, 0.07);
+ color:#40424a;
+ }
+ &.ant-menu-item-selected::before{
+ content:"✓";
+ position: absolute;
+ right: 15px;
+ color: #acb0bf;
+ top: 0px;
+ }
+ }
+ }
+}
+
+// 声明
+.claimpart{
+ padding-bottom: 15px;
+ border-bottom: 1px solid #eee;
+ margin-bottom: 20px;
+}
+
+// issue版本2
+.overlayChooseStyle{
+ width: 636px;
+ background-color:#ffffff;
+ border-radius:6px;
+ box-shadow:0px 0px 10px rgba(24, 54, 181, 0.17);
+ z-index: 100;
+ padding:30px 20px;
+ .choosedul{
+ display: flex;
+ flex-wrap: wrap;
+ padding-bottom: 10px;
+ border-bottom: 1px solid #e4e4e6;
+ margin-bottom: 20px!important;
+ li{
+ height:24px;
+ background-color:#eff2ff;
+ border:1px solid;
+ border-color:#466aff;
+ border-radius:2px;
+ color:#466aff;
+ font-size:14px;
+ margin-right: 15px;
+ margin-bottom: 10px!important;
+ padding:0px 7px;
+ display: flex;
+ align-items: center;
+ span{
+ display: block;
+ max-width:120px ;
+ }
+ }
+ }
+ .counttips{
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 20px;
+ }
+ .ant-menu{
+ display: flex;
+ flex-wrap: wrap;
+ padding:20px 0px 5px 0px;
+ box-sizing: border-box;
+ max-height: 307px;
+ overflow-y: auto;
+ &.piecemenu li{
+ width: auto;
+ &>span{
+ display: flex;
+ }
+ span.task-hide{
+ max-width: unset;
+ }
+ &:nth-child(5n){
+ margin-right: 24px!important;
+ }
+ }
+ li{
+ width: 98px;
+ height:32px;
+ line-height:30px;
+ text-align: center;
+ font-size:14px;
+ border-radius:2px;
+ margin-right: 24px!important;
+ margin-bottom: 15px!important;
+ border:1px solid!important;
+ border-color: #f4f6fe!important;
+ padding:0px 5px;
+ &.commonli{
+ background-color:#f4f6fe!important;
+ }
+ .removeicon{
+ display: block;
+ }
+ span.task-hide{
+ color: #3f455b;
+ margin:0px auto;
+ position: relative;
+ color:#3f455b;
+ max-width: 80px;
+ display: inline-block;
+ }
+ span{
+ position: relative;
+ margin:0px auto;
+ }
+ &.colorli{
+ border-color: transparent!important;
+ span{
+ color: #fff;
+ position: relative;
+ margin:0px auto;
+ }
+ }
+ &.commonli.ant-menu-item-selected{
+ background-color:#e2e8ff!important;
+ color:#3f5097;
+ border-color:#466aff!important;
+ }
+ &.colorli.ant-menu-item-selected{
+ border-color: transparent!important;
+ }
+ &.colorli.ant-menu-item-selected{
+ &>span::before{
+ content:"✓";
+ position: absolute;
+ right: -12px;
+ color: #fff;
+ top: -16px;
+ }
+ }
+ &:nth-child(5n){
+ margin-right: 0px!important;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/forge/Main/Detail.js b/src/forge/Main/Detail.js
index 213d891c8..04541655c 100644
--- a/src/forge/Main/Detail.js
+++ b/src/forge/Main/Detail.js
@@ -32,7 +32,7 @@ const OrderDetail = Loadable({
loading: Loading,
})
const OrderIndex = Loadable({
- loader: () => import('../Order/order'),
+ loader: () => import('../Issues/index'),
loading: Loading,
})
const CoderRootIndex = Loadable({
@@ -711,11 +711,11 @@ class Detail extends Component {
}
>
{/* 标签列表 */}
- ()
}
- >
+ > */}
{/* 仓库设置 */}
{/* 里程碑页面新建任务 */}
- ()
}
- >
+ > */}
{/* 新建任务 */}
- ()
}
- >
+ > */}
{/* 修改详情 edit*/}
- ()
}
- >
+ > */}
{/* 复制详情 copyetail*/}
- ()
}
- >
+ > */}
{/* 任务详情 */}
- ()
}
- >
+ > */}
{/* 动态 */}