From 1bce76c0304e25829886d5ba90532241ef682c80 Mon Sep 17 00:00:00 2001 From: caishi <1149225589@qq.com> Date: Fri, 1 Sep 2023 11:09:00 +0800 Subject: [PATCH] merge issue files --- package-lock.json | 21 + package.json | 1 + src/forge/Issues/Component/addTagsBox.jsx | 65 ++ src/forge/Issues/Component/allMenus.jsx | 219 +++++ src/forge/Issues/Component/chooseMenu.jsx | 305 +++++++ src/forge/Issues/Component/colorCard.jsx | 47 + .../Issues/Component/comments/editComment.jsx | 165 ++++ src/forge/Issues/Component/comments/list.jsx | 242 +++++ src/forge/Issues/Component/comments/list.scss | 205 +++++ src/forge/Issues/Component/copy.jsx | 22 + src/forge/Issues/Component/datas.jsx | 96 ++ src/forge/Issues/Component/date.jsx | 149 +++ src/forge/Issues/Component/delBox.jsx | 32 + src/forge/Issues/Component/drop.jsx | 47 + src/forge/Issues/Component/dropMenu.jsx | 210 +++++ src/forge/Issues/Component/menus.jsx | 93 ++ src/forge/Issues/Component/newPanel.jsx | 129 +++ src/forge/Issues/Img/UploadImg.png | Bin 0 -> 2351 bytes src/forge/Issues/Img/biaoji.png | Bin 0 -> 255 bytes src/forge/Issues/Img/create.png | Bin 0 -> 133 bytes src/forge/Issues/Img/emp.png | Bin 0 -> 50682 bytes src/forge/Issues/Img/gold.png | Bin 0 -> 708 bytes src/forge/Issues/Img/issue-big.png | Bin 0 -> 2920 bytes src/forge/Issues/Img/issue.png | Bin 0 -> 608 bytes src/forge/Issues/Img/pr.png | Bin 0 -> 1025 bytes src/forge/Issues/Pages/details.jsx | 520 +++++++++++ src/forge/Issues/Pages/list.jsx | 461 ++++++++++ src/forge/Issues/Pages/new.jsx | 303 +++++++ src/forge/Issues/Pages/sign.jsx | 267 ++++++ src/forge/Issues/index.jsx | 80 ++ src/forge/Issues/index.scss | 854 ++++++++++++++++++ src/forge/Main/Detail.js | 26 +- src/forge/css/index.scss | 18 + 33 files changed, 4564 insertions(+), 13 deletions(-) create mode 100644 src/forge/Issues/Component/addTagsBox.jsx create mode 100644 src/forge/Issues/Component/allMenus.jsx create mode 100644 src/forge/Issues/Component/chooseMenu.jsx create mode 100644 src/forge/Issues/Component/colorCard.jsx create mode 100644 src/forge/Issues/Component/comments/editComment.jsx create mode 100644 src/forge/Issues/Component/comments/list.jsx create mode 100644 src/forge/Issues/Component/comments/list.scss create mode 100644 src/forge/Issues/Component/copy.jsx create mode 100644 src/forge/Issues/Component/datas.jsx create mode 100644 src/forge/Issues/Component/date.jsx create mode 100644 src/forge/Issues/Component/delBox.jsx create mode 100644 src/forge/Issues/Component/drop.jsx create mode 100644 src/forge/Issues/Component/dropMenu.jsx create mode 100644 src/forge/Issues/Component/menus.jsx create mode 100644 src/forge/Issues/Component/newPanel.jsx create mode 100644 src/forge/Issues/Img/UploadImg.png create mode 100644 src/forge/Issues/Img/biaoji.png create mode 100644 src/forge/Issues/Img/create.png create mode 100644 src/forge/Issues/Img/emp.png create mode 100644 src/forge/Issues/Img/gold.png create mode 100644 src/forge/Issues/Img/issue-big.png create mode 100644 src/forge/Issues/Img/issue.png create mode 100644 src/forge/Issues/Img/pr.png create mode 100644 src/forge/Issues/Pages/details.jsx create mode 100644 src/forge/Issues/Pages/list.jsx create mode 100644 src/forge/Issues/Pages/new.jsx create mode 100644 src/forge/Issues/Pages/sign.jsx create mode 100644 src/forge/Issues/index.jsx create mode 100644 src/forge/Issues/index.scss 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('name',{ + rules:[ + { + required:true, + message:"请输入标记名称" + } + ], + validateTrigger:"onInput", + })()} + + + + +
+ + {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( + + ) +} +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? + + { + menus.map((i,k)=>{ + return( + chooseMenu(i)}> + { + auto ? {i.name} + : + {i.name} + } + + ) + }) + } + + : +
    +

    {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( +
    +
    +
    +

    {textcolor}

    +
    + {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 &&
    0 && 'pt15'}`}>
    } + {/* 评论/操作日志 展示列表 */} + +
    + {journals && (journals.length > 0 && journals.map(item=>{return item.is_journal_detail ? (!item.closeAndSpan || item.id === item.start || open === item.start) ?
    + {/* 操作日志 */} +
    +
    +
    + +
    + + {item.user.name}  + {(item.user.name.length + item.operate_content.length) > 62 ? {item.user.name}
    }> : } +
    + {timeAgo(item.created_at)} +
    + {(item.closeAndSpan && item.id === item.start) && {setOpen(open === item.id ? undefined : item.id)}}>{open === item.id ? `点击收起操作日志` : `已折叠${item.numCount}条, 点击查看`}} +
    +
    : '' :
    + {/* 评论 */} +
    + +
    + {/* 判断是否是编辑状态 */} + {(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 &&
    + setPage(page)}/> +
    } + {/* 添加评论 */} + {category !== 'operate' &&
    + {login ? showEdit === 1 ? :
    + +
    + {setShowEdit(1)}}> + + +
    +
    :
    + {/* 未登录用户 */} + {showLoginDialog()}}>登录并参与评论与回复 +
    } +
    } +
    + ) +} +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? + + { + menus.map((i,k)=>{ + return( + chooseMenu(i)}> + { headImg && } + {i.color && } + {i.name} + + ) + }) + } + + : +
    +

    {searchValue ? 暂无{placeholder}“{searchValue}”: `暂无${placeholder}`}

    +
    + } + { onAdd &&
    {setVisible(false);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 ? + + { + data.map((i,j)=>{ + return + {imgControl && } + {i.color && } + changeMenusValue(i)}>{i.name} + + }) + } + + : +
    暂无{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 0000000000000000000000000000000000000000..cb1e7d05c4c07c307df77faf281f3ded4e3568d4 GIT binary patch literal 2351 zcmV+~3DEY5P)ocJg92*c z0IhFcPDE?5JfmdRl$0icbuh89>S>%Xs2nYP>V}Wy!d8cY0`#|ke>el%u?Xf9oN-e= z9F)TDgTRJPSbE~d>o>vzYG44Z|LdbX)Fvae$bJgWg~@S0`yHob8+7LvuK3c>v?N3v?hCL1=xLswHF>Rvz2)u8A)?jy{ zEWVE2NtzB^sFxKWi@?}^?$tOg-S(ONv)xKj-vGV)*WD~t$O6txK9XUasrGzaVO0D4 z&E2V5UjT9Gq4oGCK0V+9DSi32xRlH937`vW)p4fkJoX8vF!oR}J%C%rCa9F>q1gdD`0QnU9)ES<@9@+< zsc)B>)}?qnHXfu}Znn?82~}g7Wi{b*~A$f6@jU&x>=AAu5d=8fu66z2d2;gwL7 zg?Xyb3v~-5k8%96c;8*}s^z9~`h0DQs*LBnJ(@p#VX*n{nL@Su`;nm2=PNT`T6GKbMqBytygzUV`k(n>n8(``Epa zQrq|b=xjAZ6(v8|3|{BlFdRbq@gtEXKah&&YFUNSq14?#XQ{TU&d;YE`$UUp>pUri ziRc26slEWcc&5Gy zev-rGj}@_TMrcBRc)H$vTc_sGj`~Z`-tngwzkM{Cq0cWixThKIbjiuKKpvN%`W{Ep>dCY}K+i8V z;6FNF!s?s8wSdR^TTjlaWAuAJt0Vb^I@Dg=C8RX>odAHqpCjQzzVle6>IgmYYNG%k z=eY54=bQ%JN1iG7%)|Z?bmFHC`{wYV`EjqWBe73UoMPiT)z?jf+7y9`z9Cso9!}(- z?fHF;liafb03Cm&0SOuuefMQ9_xCHBaahfrYHR_XQS1M^#=yHXCQA7GqnG=L0QuF3ZJ= zsIVyoY^r0wbCP9n?>cX)mthngM`H2aH~r51??kIK$$4tYC_epT z##60jFw=9scldi9K1E~+(%*laF7cJyGW$>ES(2$_1zUBnDZ$euDV+~(&Q4z2NM+WK^!#{)I1_ZKoolaxo=U%6+YjS)Cnv?m#yeT=p0Es~Wl~nN~^m$%+ z)U*L+*zcTFn~HVU?s%yIGhCQ&&wff3&p2QJYn;?cLQ#w=>;Fa58QCa&i~3eK{s-N$ V1<lKP!-8DR%M<;Q)dfj+71{f19U^V~9p@ z?}SF)Lka?}tYRp}nu3g=F z%){)MMRR;^R)E({4#l!Y-2-)by)*ry*IGVjTg%Q=?Z9j1I-5J|?*aKYr{*P{^smm6 zFK@2rT_3k(=Q)GFtv^c61@2pudTed{^v|4qQ5g?jT5xUz`hdaH)z4*}Q$iB}5|dvS literal 0 HcmV?d00001 diff --git a/src/forge/Issues/Img/create.png b/src/forge/Issues/Img/create.png new file mode 100644 index 0000000000000000000000000000000000000000..63b4cc29c7d793366cb07a3ce6940865a3339e0e GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEX7WqAsj$Z!;#Vf_TOoQ1Am{x536VS>R@QY!}zXKeh3Jt-Gj@llf c4G(=8{QHz1WnG_f9cT`Nr>mdKI;Vst04gsgV*mgE literal 0 HcmV?d00001 diff --git a/src/forge/Issues/Img/emp.png b/src/forge/Issues/Img/emp.png new file mode 100644 index 0000000000000000000000000000000000000000..5db7c07786562832623fa44b2a4427464bd496f4 GIT binary patch literal 50682 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 Hu0mjfS+Ahq literal 0 HcmV?d00001 diff --git a/src/forge/Issues/Img/gold.png b/src/forge/Issues/Img/gold.png new file mode 100644 index 0000000000000000000000000000000000000000..3c64743b65d21cae1de244e63cfe3a8dde02c750 GIT binary patch literal 708 zcmV;#0z3VQP)ZzcjjVK6$b_;?x-FWdNR>6}8H=@#-)TBQclkB|bo7uGD!ZMlU`+MI^ z5ai+ajs@-UMPPJ+!36}1iRA~Vl2Rcm2vh0WLp&N)HC{i+pN=@d>DL-;M1){SdR3I{ zmDy`jE_3Yja*H?|34cI?!~saJ`lDGn*=zGyuN-xp_%dXO^l_M(5|CC-99sL9d&|*$ zzw6{zG{pGb;YdjT924ahc{@QXE^~4B=(j;bY`#6@GOGX-XSe6uar{3)QKI=>V=JK{ z*54e=OLH?amx~M@5j!HH6BI>grYWu3V=K}FK;>z#W1lG{<@|Xyv@C$E00Wd4u&vm6 zDl)*$XMHmwbXtTQ!7R(HfqlL_YlyX%gA$d*l?L=|6jM=QyOVC#LIIELv}_7AGlxBw zsyW7L~xaUdsDL1O}`;?#|oi*8K8<<);`uME2vl3I#YKGv#`(ZiTCJ zoxE?dhO0L-o+4fp(E|Q@U`IMB#3WHEh^5t3-0+cLT9m62+3&mFSo(1zbMGp1mequ&9KN(la|8L qlWvctB58#LjP*f2o78Oy`-Mx0wdKW^H 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 literal 0 HcmV?d00001 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 ? +
    + +
    + : +
    + } + +
    + {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*/} - () } - > + > */} {/* 任务详情 */} - () } - > + > */} {/* 动态 */}