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 000000000..cb1e7d05c Binary files /dev/null and b/src/forge/Issues/Img/UploadImg.png differ diff --git a/src/forge/Issues/Img/biaoji.png b/src/forge/Issues/Img/biaoji.png new file mode 100644 index 000000000..e40377459 Binary files /dev/null and b/src/forge/Issues/Img/biaoji.png differ diff --git a/src/forge/Issues/Img/create.png b/src/forge/Issues/Img/create.png new file mode 100644 index 000000000..63b4cc29c Binary files /dev/null and b/src/forge/Issues/Img/create.png differ diff --git a/src/forge/Issues/Img/emp.png b/src/forge/Issues/Img/emp.png new file mode 100644 index 000000000..5db7c0778 Binary files /dev/null and b/src/forge/Issues/Img/emp.png differ diff --git a/src/forge/Issues/Img/gold.png b/src/forge/Issues/Img/gold.png new file mode 100644 index 000000000..3c64743b6 Binary files /dev/null and b/src/forge/Issues/Img/gold.png differ diff --git a/src/forge/Issues/Img/issue-big.png b/src/forge/Issues/Img/issue-big.png new file mode 100644 index 000000000..8d73e1d93 Binary files /dev/null and b/src/forge/Issues/Img/issue-big.png differ diff --git a/src/forge/Issues/Img/issue.png b/src/forge/Issues/Img/issue.png new file mode 100644 index 000000000..7a85ffff7 Binary files /dev/null and b/src/forge/Issues/Img/issue.png differ diff --git a/src/forge/Issues/Img/pr.png b/src/forge/Issues/Img/pr.png new file mode 100644 index 000000000..fc3deacf0 Binary files /dev/null and b/src/forge/Issues/Img/pr.png differ diff --git a/src/forge/Issues/Pages/details.jsx b/src/forge/Issues/Pages/details.jsx new file mode 100644 index 000000000..1bf881440 --- /dev/null +++ b/src/forge/Issues/Pages/details.jsx @@ -0,0 +1,520 @@ +import React , { useEffect , useState , forwardRef , useRef } from 'react'; +import { Spin , Form , InputNumber, message } from 'antd'; +import { getImageUrl } from 'educoder'; +import { Link } from 'react-router-dom'; +import { Box , LongWidth } from '../../Component/layout'; +import RenderHtml from "../../../components/render-html"; +import DropMenu from '../Component/dropMenu'; +import NewPanel from '../Component/newPanel'; +import Attachments from "../../Upload/attachment"; +import DelBox from '../Component/delBox'; +import Date from '../Component/date'; +import moment from 'moment'; +import Copy from '../Component/copy'; +import axios from 'axios'; +import CommentList from '../Component/comments/list'; +import Claims from '../../claims/claims'; +import ChooseMenu from '../Component/chooseMenu'; + +function Details(props){ + const owner = props.match.params.owner; + const projectsId = props.match.params.projectsId; + const index = props.match.params.index; + + const [ details , setDetails ] = useState(undefined); + const [ statusList , setStatusList ] = useState([]); + const [ prioritiesList , setPrioritiesList ] = useState([]); + const [ chargeList , setChargeList ] = useState([]); + const [ charge , setCharge ] = useState(undefined); + const [ millstoneList , setMillstoneList ] = useState([]); + const [ millstone , setMillstone ] = useState(undefined); + const [ tagList , setTagList ] = useState(undefined); + const [ tag , setTag ] = useState(undefined); + const [ branchList , setBranchList ] = useState(undefined); + const [ branch , setBranch ] = useState(undefined); + const [ desc , setDesc ] = useState(undefined); + + const [ orderId , setOrderId ] = useState(undefined); + + const [ delVisible , setDelVisible] = useState(false); + const [ edit , setEdit] = useState(false); + const [ copy , setCopy] = useState(false); + + const [ start_date, setStartDate ] = useState(""); + const [ due_date, setDueDate ] = useState(""); + const [ commentReload, setCommentReload] = useState(undefined); + const [ editFlag , setEditFlag ] = useState(false); + const [ amountEditFlag , setAmountEditFlag ] = useState(false); + + const [ assigner_choose , setAssigner_choose ] = useState([]); + const [ prioritie_choose , setPrioritie_choose ] = useState([]); + const [ status_choose , setStatus_choose ] = useState([]); + const [ tag_choose , setTag_choose ] = useState([]); + const [ millstone_choose , setMillstone_choose ] = useState([]); + const [ branch_choose , setBranch_choose ] = useState([]); + const [ rewardAmount , setRewardAmount ] = useState(undefined); + const [ rewardFlag , setRewardFlag ] = useState(false); + + const pathname = props.history.location.pathname; + const permission = props && props.projectDetail && props.projectDetail.permission; + let colors = ["#1abcb1","#28be6c","#e67e22","#db3d1d"]; + + const {projectDetail , open_blockchain ,current_user } = props; + useEffect(()=>{ + if(pathname === `/${owner}/${projectsId}/issues/${index}/copy`){ + setEdit(true); + setCopy(true); + }else{ + setEdit(false); + setCopy(false); + } + },[pathname]) + + useEffect(()=>{ + if(projectDetail && details){ + const { author, name} = projectDetail; + document.title = `${details.subject}-疑修-${author.name}/${name}`; + } + },[details,projectDetail]) + + useEffect(()=>{ + if(index){ + Init(); + } + },[index]) + + function Init(){ + const url = `/v1/${owner}/${projectsId}/issues/${index}`; + axios.get(url).then(result=>{ + if(result && result.data){ + let data = result.data; + setDetails(data); + let per = data && !data.user_permission; + setEditFlag(per); + + setDesc(data && data.description); + + const aut = current_user && data.author && (data.author.login === current_user.login); + const con = data.pull_fixed === false && aut; + setAmountEditFlag(con); + + setAssigner_choose(data.assigners); + if(data.priority && data.priority.id){ + setPrioritie_choose([{...data.priority,color:colors[data.priority.id-1]}]); + }else{ + setPrioritie_choose([]); + } + setStatus_choose(data.status && data.status.id ? [data.status]:[]); + setTag_choose(data.tags); + setMillstone_choose(data.milestone && data.milestone.id ? [data.milestone]:[]); + setBranch_choose(data.branch_name ? [{name:data.branch_name}] : []); + setRewardAmount(data.blockchain_token_num); + + data.start_date && setStartDate(moment(data.start_date).format('YYYY-MM-DD')); + data.due_date && setDueDate(moment(data.due_date).format('YYYY-MM-DD')); + + setOrderId(data.id); + } + }).catch(error=>{ + console.log(error); + }) + } + + + function statusTag(name){ + switch (name) { + case "低": + return "status low"; + case "正常": + return "status normals"; + case "高": + return "status hight"; + default: + return "status urgent"; + } + } + // 获取状态列表 + useEffect(()=>{ + getStatus(); + },[]) + function getStatus(){ + const url = `/v1/${owner}/${projectsId}/issue_statues`; + axios.get(url).then(result=>{ + if(result && result.data){ + setStatusList(result.data.statues); + } + }) + } + // 获取优先级列表 + useEffect(()=>{ + getPriorities(); + },[]) + + function getPriorities(){ + const url = `/v1/${owner}/${projectsId}/issue_priorities`; + axios.get(url).then(result=>{ + if(result && result.data){ + let priorities = result.data.priorities; + let array = addColorForPriorities(priorities); + setPrioritiesList(array); + } + }) + } + + function addColorForPriorities(list){ + let array =[]; + if(list && list.length>0){ + array = list.map((i,k)=>{ + let arr = {...i,color:colors[k]} + return arr; + }) + } + return array; + } + + // 获取负责人列表 + useEffect(()=>{ + getCharge(); + },[charge]) + + function getCharge(){ + const url = `/v1/${owner}/${projectsId}/collaborators`; + axios.get(url,{params:{keyword:charge,only_name:true}}).then(result=>{ + if(result && result.data){ + setChargeList(result.data.collaborators); + } + }) + } + // 获取里程碑列表 + useEffect(()=>{ + getMillstone(); + },[millstone]) + + function getMillstone(){ + const url = `/v1/${owner}/${projectsId}/milestones`; + axios.get(url,{params:{keyword:millstone,only_name:true}}).then(result=>{ + if(result && result.data){ + setMillstoneList(result.data.milestones); + } + }) + } + // 获取标记列表 + useEffect(()=>{ + getSign(); + },[tag]) + function getSign(){ + const url = `/v1/${owner}/${projectsId}/issue_tags`; + axios.get(url,{params:{keyword:tag,only_name:true}}).then(result=>{ + if(result && result.data){ + setTagList(result.data.issue_tags); + } + }) + } + // 获取标记列表 + useEffect(()=>{ + getBranch(branch); + },[branch]) + function getBranch(branch){ + if(branch){ + let b = [...branchList]; + let l = b.filter(i=>i.name.indexOf(branch)>-1); + setBranchList(l); + return ; + } + const url = `/${owner}/${projectsId}/branches.json`; + axios.get(url,{params:{keyword:tag}}).then(result=>{ + if(result && result.data){ + setBranchList(result.data); + } + }) + } + + // 可单个编辑保存右侧的项(!copy:非复制状态下) + function saveForeach(c,s,p,t,m,b,sDate,eDate,rewardAmount){ + if(!copy){ + const url = `/v1/${owner}/${projectsId}/issues/${index}`; + axios.patch(url,{ + branch_name: b ? b.join(",") :undefined, + status_id: s ? s.join(",") : undefined, + priority_id: p ? p.join(",") :undefined, + milestone_id: m ? m.join(",") :undefined, + issue_tag_ids: t?t:undefined, + assigner_ids: c?c:undefined, + start_date:sDate, + due_date:eDate, + blockchain_token_num:rewardAmount + }).then(result=>{ + if(result){ + Init(); + // 更新操作日志 + setCommentReload(Math.random()); + } + }).catch(error=>{}) + } + } + + function deleteFunc(){ + const url = `/v1/${owner}/${projectsId}/issues/${index}`; + axios.delete(url).then(result=>{ + if(result){ + props.showNotification("疑修删除成功!"); + props.history.push(`/${owner}/${projectsId}/issues`); + } + }).catch(error=>{}) + } + + // 编辑保存 + function createFunc(values,fileList,receivers_login,description){ + let params ={ + subject: values.subject, + attachment_ids: fileList, + receivers_login: receivers_login, + description: description + } + if(!copy){ + // 调用编辑保存接口 + const url = `/v1/${owner}/${projectsId}/issues/${index}`; + axios.patch(url,{ + ...params + }).then(result=>{ + if(result){ + window.scrollTo(0,0); + props.showNotification("疑修更新成功!"); + Init(); + setEdit(false); + // 刷新操作记录 + setCommentReload(Math.random()); + } + }).catch(error=>{}) + }else{ + // 调用新建保存接口 + const url = `/v1/${owner}/${projectsId}/issues`; + let b = branch_choose && branch_choose.length>0 ? branch_choose.map(i=>{return i.name}) :undefined; + let t = tag_choose && tag_choose.length>0 ? tag_choose.map(i=>{return i.id}) :undefined; + let a = assigner_choose && assigner_choose.length>0 ? assigner_choose.map(i=>{return i.id}) :undefined; + let m = millstone_choose && millstone_choose.length>0 ? millstone_choose.map(i=>{return i.id}) :undefined; + let p = prioritie_choose && prioritie_choose.length>0 ? prioritie_choose.map(i=>{return i.id}) :undefined; + let s = status_choose && status_choose.length>0 ? status_choose.map(i=>{return i.id}) :undefined; + axios.post(url,{ + ...params, + branch_name: b && b.join(","), + status_id: s && s.join(","), + priority_id: p && p.join(","), + milestone_id: m && m.join(","), + issue_tag_ids: t, + assigner_ids: a, + start_date, + due_date + }).then(result=>{ + if(result && result.data && result.data.project_issues_index){ + window.scrollTo(0,0); + props.showNotification("任务复制成功!"); + props.history.push(`/${owner}/${projectsId}/issues/${result.data.project_issues_index}`); + } + }).catch(error=>{}) + } + } + + // 取消编辑或复制 + function onCancel(){ + if(copy){ + props.history.push(`/${owner}/${projectsId}/issues/${index}`); + }else{ + setEdit(false); + window.scrollTo(0,0); + } + } + + function refreshFunc(){ + // 更新操作日志 + setCommentReload(Math.random()); + } + + + // 悬赏金额 失去焦点保存数据 + function amountBlur(e){ + if(!editFlag){ + setRewardFlag(false); + const value = e.target.value; + if(value<0){ + message.info("请输入大于0的正整数"); + setRewardAmount(rewardAmount); + return; + } + setRewardAmount(e.target.value); + if((value && value >= 0) || !value){ + saveForeach(undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined,value); + } + } + } + function changeAmount(value){ + if(amountEditFlag){ + setRewardAmount(value); + }else{ + setRewardAmount(rewardAmount); + } + } + return( + details ? + + + setDelVisible(false)} + onSuccess={deleteFunc} + content={

    您确定要删除当前疑修?

    } + /> + { + edit ? +
    + +
    + : +
    +
    +
    +
    +
    + {details.priority && { details.priority.name } } +

    {details.subject}

    +
    +
    +
    + #{details.project_issues_index} +
    + { + details.author && +
    + {details.author.name} + 添加于{details.created_at} +
    + } +
    +
    + { + details.user_permission && + + } +
    +
    + {desc ? + + : + 暂无描述 + } + {details.attachments && details.attachments.length > 0 ? + + : "" + } +
    +
    +
    + {setCommentReload(Math.random())}}/> +
    +
    + } +
    +
    + {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*/} - () } - > + > */} {/* 任务详情 */} - () } - > + > */} {/* 动态 */}