diff --git a/src/forge/Head/NoticeContent.jsx b/src/forge/Head/NoticeContent.jsx index 634a7adc8..395467079 100644 --- a/src/forge/Head/NoticeContent.jsx +++ b/src/forge/Head/NoticeContent.jsx @@ -224,7 +224,7 @@ function NoticeContent({ visible, showNotification, resetUserInfo, current_user:
- " + (item.sender ? item.sender.name : '') + "   " + item.content + " 中@我" }}> + {item.time_ago}
diff --git a/src/forge/SecuritySetting/notice/myNotice/Index.jsx b/src/forge/SecuritySetting/notice/myNotice/Index.jsx index 226606919..3d4957226 100644 --- a/src/forge/SecuritySetting/notice/myNotice/Index.jsx +++ b/src/forge/SecuritySetting/notice/myNotice/Index.jsx @@ -17,7 +17,7 @@ function MyNotice(props) { const [selectedNum, setSelectedNum] = useState(0);//@我批量删除选择消息条数 const [isBatchDelete, setIsBatchDelete] = useState(false);//@我是否批量删除 const [batchDeleteCheckedAll, setBatchDeleteCheckAll] = useState(false);//@我批量删除--全选 - + const [messageType, setMessageType] = useState(undefined); const [noticeUnreadCount, setNoticeUnreadCount] = useState();//未读系统通知数量 // const [letterUnreadCount, setLetterUnreadCount] = useState(0);//未读私信数量 const [atUnreadCount, setAtUnreadCount] = useState();//未读@我数量 @@ -65,6 +65,7 @@ function MyNotice(props) { setAtUnreadCount(response.data.unread_atme); setMessageList(response.data.messages); setMessTotalCount(response.data.total_count); + setMessageType(response.data.type); } }); } @@ -203,7 +204,46 @@ function MyNotice(props) { } - {messageList && messageList.map(item => { + {/* 系统消息 */} + {messageType === "notification" && messageList && messageList.map(item =>{ + return ( +
+
+ {item.status === 1 ? : } + + {turnToMess(item)}} dangerouslySetInnerHTML={{__html: item.content}}> +
+
+ {item.time_ago} + {item.status === 1 && readNotice([item.id])}>标记为已读} +
+
+ ) + })} + + {/* @我消息 */} + {messageType === "atme" && messageList && messageList.map(item =>{ + return ( +
+
+ + {item.sender && {window.open(`/${item.sender && item.sender.login}`);}}/>} +
{turnToMess(item)}}> + {item.status === 1 ? : } + {item.sender && } +
+
+
+ {item.time_ago} + {!isBatchDelete && item.status === 1 && readNotice([item.id])}>标记为已读}    + {!isBatchDelete && deleteNotice([item.id])}>删除} +
+
+ ) + })} + + {false && messageList && messageList.map(item => { + console.log('item',item); // 系统消息 if (noticeType === "0") { // 消息类别 @@ -229,7 +269,7 @@ function MyNotice(props) { {item.sender && {window.open(`/${item.sender && item.sender.login}`);}}/>}
{turnToMess(item)}}> {item.status === 1 ? : } - {item.sender && " + item.sender.name+ " "+ item.content +" 中@我"}}>} + {item.sender && " + item.sender.name+ " "+ item.content}}>}
diff --git a/src/modules/tpm/challengesnew/css/newquestion.css b/src/modules/tpm/challengesnew/css/newquestion.css index 81d7afcba..eb1a67eb1 100644 --- a/src/modules/tpm/challengesnew/css/newquestion.css +++ b/src/modules/tpm/challengesnew/css/newquestion.css @@ -26,7 +26,7 @@ border-bottom: 1px solid rgba(212, 212, 212, 0.5); padding: 0 4px; } -.active{ +.at_who.active{ background: #F3F4F6; } .at_who img{ @@ -39,8 +39,4 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; -} -.blur_atWho{ - position: absolute; - top: -100px; } \ No newline at end of file diff --git a/src/modules/tpm/challengesnew/tpm-md-editor.js b/src/modules/tpm/challengesnew/tpm-md-editor.js index 385005b65..988e9381e 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -75,30 +75,35 @@ function md_elocalStorage(editor, mdu, id) { } export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, className = '', noStorage = false, imageExpand = true, placeholder = '', width = '100%', height = 400, initValue = '', emoji, watch, showNullButton = false, showResizeBar = false, startInit = true , forMember = true , isCanAtme = false , changeAtWhoLoginList, owner, projectsId }) => { - + const editorEl = useRef(); const resizeBarEl = useRef(); const [editorInstance, setEditorInstance] = useState(); const [atWhoVisible, setAtWhoVisible] = useState(false); const [atWhoLoginListState, setAtWhoLoginListState] = useState([]); + //调用member.json接口获取到的用户列表 const [users, setUsers] = useState([]); - //用户输入的@之后的搜索词 - const [search, setSeacrch] = useState(undefined); + //可以@的全部用户 + const [allUsers, setAllUsers] = useState([]); const atWhoLoginList = useRef([]); + const atWhoVisibleRef = useRef(false); const containerId = `mdEditor_${mdID}`; const editorBodyId = `mdEditors_${mdID}`; const tipId = `e_tips_mdEditor_${mdID}`; useEffect(()=>{ - isCanAtme && axios.get(`/${owner}/${projectsId}/members.json`,{ - // params: { - // search: 'admin', - // }, - }).then(response=>{ - if(response && response.status === 200){ + //请求members接口获取全部可@列表 + isCanAtme && axios.get(`/${owner}/${projectsId}/members.json`).then(response=>{ + if(response.data.total_count !== 0){ + setAllUsers(response.data.users); setUsers(response.data.users); } }) + //点击其他地方关闭弹框 + document.addEventListener('click',()=>{ + atWhoVisibleRef.current = false; + setAtWhoVisible(false); + }) },[]) function onLayout() { @@ -119,14 +124,16 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla } function selectAtWho(username){ + atWhoVisibleRef.current = false; setAtWhoVisible(false); const cm = editorInstance.cm; //获取鼠标所在行的行数和ch const cursor = cm.doc.getCursor(); const line = cursor.line;//行 const ch = cursor.ch;//列 + const startIndex = cm.getRange({line,ch:0},{line,ch}).lastIndexOf("@")+1; //替换最后的内容 - cm.replaceRange(username+" ",{line,ch},{line,ch}); + cm.replaceRange(username+" ",{line,ch:startIndex},{line,ch}); //鼠标聚焦 cm.focus(); //将此user的login存储到atWhoLoginList集合中 @@ -136,155 +143,58 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla }) atWhoLoginList.current = Array.from(list); setAtWhoLoginListState(Array.from(list)); - //销毁atWhoKeyDown键盘监听事件 - // document.removeEventListener("keydown",atWhoKeyDown); } function onMouseOver(key){ - document.getElementsByClassName("at_who active")[0].className="at_who"; - document.getElementsByClassName("at_who")[key].className="at_who active"; + document.getElementsByClassName("at_who active")[0] && (document.getElementsByClassName("at_who active")[0].className="at_who"); + document.getElementsByClassName("at_who")[key] && (document.getElementsByClassName("at_who")[key].className="at_who active"); } //markdown编辑器中输入的键盘监听事件 function mdKeyDown(e){ - console.log("markdown编辑器--------键盘监听事件"); - // document.onkeydown = e=>{ - if (e.shiftKey && e.key === "@") { - // 输入@键后在对应的位置显示可选的项目成员 - setAtWhoVisible(true); - //获取光标位置 - const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style; - //设置弹框位置 - document.getElementById("at_who_list").style.top = (parseInt(cssStyle.getPropertyValue("top").replace("px",""))+60)+"px"; - document.getElementById("at_who_list").style.left = (parseInt(cssStyle.getPropertyValue("left").replace("px",""))+20)+"px"; - //将第一个用户默认选中 - const at_who_divs = document.getElementsByClassName("at_who"); - at_who_divs[0].className = "at_who active"; - } - //处理本来@了某人 -> 删掉 -> 撤回 的情况 - if(e.ctrlKey && e.code === "KeyZ" && users.length != 0){ - const codemirror = editorInstance.cm; - let value = codemirror.getValue(); - //处理初始内容就自带@谁的情况 - if(initValue){ - const del = []; - users.map(item=>{ - if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){ - //初始内容中有符合@+名字的格式并且当前内容未删除初始内容 - del[del.length] = `@${item.username}`; - } - }) - del.length!=0 && del.map(str=>{ - value = value.replace(str,""); - }) - } - //判断value是否包含@符号 - value.indexOf("@") != -1 && users.map(item =>{ - if(value.indexOf(item.username)!=-1 && value.charAt(value.indexOf(item.username)-1) ==="@"){ - //将此user的login存储到atWhoLoginList集合中 - const list = new Set(atWhoLoginList.current); - list.add(item.login); - atWhoLoginList.current = Array.from(list); - setAtWhoLoginListState(Array.from(list)); + if (e.shiftKey && e.code === "Digit2") { + // 输入@键后在对应的位置显示可选的项目成员 + atWhoVisibleRef.current = true; + setAtWhoVisible(true); + //获取光标位置 + const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style; + //设置弹框位置 + const newTop = placeholder === "添加评论..." ? 159: placeholder === "请输入合并请求的描述..." ? 172:62; + const newLeft = placeholder === "添加评论..." ? 80: 20; + document.getElementById("at_who_list").style.top = parseInt(cssStyle.getPropertyValue("top").replace("px","")) + newTop +"px"; + document.getElementById("at_who_list").style.left = parseInt(cssStyle.getPropertyValue("left").replace("px",""))+newLeft+"px"; + } + //处理本来@了某人 -> 删掉 -> 撤回 的情况 + if(e.ctrlKey && e.code === "KeyZ" && allUsers.length != 0){ + const codemirror = editorInstance.cm; + let value = codemirror.getValue(); + //处理初始内容就自带@谁的情况 + if(initValue){ + const del = []; + allUsers.map(item=>{ + if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){ + //初始内容中有符合@+名字的格式并且当前内容未删除初始内容 + del[del.length] = `@${item.username}`; } }) + del.length!=0 && del.map(str=>{ + value = value.replace(str,""); + }) } - // const atWhoListDiv = document.getElementById("at_who_list"); - // if(atWhoListDiv && (e.key === "ArrowUp" || e.key === "ArrowDown" || e.key === "Enter")){ - // return false; - // } - // const atWhoDivs = document.getElementsByClassName("at_who"); - // let index; - // for(let i = 0; i 0){ - // e.preventDefault(); - // index <=atWhoDivs.length-4 && (atWhoListDiv.scrollTop -=40) - // atWhoDivs[index].className = "at_who"; - // atWhoDivs[index-1].className = "at_who active"; - // } - // if(e.key === "ArrowDown"){ - // e.preventDefault(); - // console.log("ArrowDown",atWhoVisible); - // const atWhoListDiv = document.getElementById("at_who_list"); - // const atWhoDivs = document.getElementsByClassName("at_who"); - // let index; - // for(let i = 0; i=3 && (atWhoListDiv.scrollTop +=40) - // atWhoDivs[index].className = "at_who"; - // atWhoDivs[index+1].className = "at_who active"; - // } - // if(e.key === "Enter"){ - // //阻止默认事件 - // e.preventDefault(); - // //找到classname为at_who active的div,执行click事件 - // document.getElementsByClassName("at_who active")[0].click(); - // } - - // editorInstance.addKeyMap({ - // 'ArrowUp':()=>{ - // index <=atWhoDivs.length-4 && (atWhoListDiv.scrollTop -=40) - // atWhoDivs[index].className = "at_who"; - // atWhoDivs[index-1].className = "at_who active"; - // }, - // 'ArrowDown':()=>{ - // index >=3 && (atWhoListDiv.scrollTop +=40) - // atWhoDivs[index].className = "at_who"; - // atWhoDivs[index+1].className = "at_who active"; - // }, - // 'Enter':()=>{ - // //找到classname为at_who active的div,执行click事件 - // document.getElementsByClassName("at_who active")[0].click(); - // }, - // }) - - // } - // } + //判断value是否包含@符号 + value.indexOf("@") != -1 && allUsers.map(item =>{ + if(value.indexOf(item.username)!=-1 && value.charAt(value.indexOf(item.username)-1) ==="@"){ + //将此user的login存储到atWhoLoginList集合中 + const list = new Set(atWhoLoginList.current); + list.add(item.login); + atWhoLoginList.current = Array.from(list); + setAtWhoLoginListState(Array.from(list)); + } + }) + } } - //弹出可选@用户列表之后的键盘监听事件 - function atWhoKeyDown(e){ - //监听上下和enter键 - // document.onkeydown = (e) =>{ - const atWhoListDiv = document.getElementById("at_who_list"); - const atWhoDivs = document.getElementsByClassName("at_who"); - let index; - for(let i = 0; i 0){ - index <=atWhoDivs.length-4 && (atWhoListDiv.scrollTop -=40) - atWhoDivs[index].className = "at_who"; - atWhoDivs[index-1].className = "at_who active"; - return; - } - if(e.key === "ArrowDown" && index < atWhoDivs.length-1){ - index >=3 && (atWhoListDiv.scrollTop +=40) - atWhoDivs[index].className = "at_who"; - atWhoDivs[index+1].className = "at_who active"; - return; - } - if(e.key === "Enter"){ - //阻止默认事件 - e.preventDefault(); - //找到classname为at_who active的div,执行click事件 - document.getElementsByClassName("at_who active")[0].click(); - } - // } - } - - //点击其他地方关闭弹框 useEffect(()=>{ - document.addEventListener('click',()=>{setAtWhoVisible(false)}) - },[]) - - useEffect(()=>{ - console.log('@谁列表发生变化,atWhoLoginList.current:',atWhoLoginList.current); changeAtWhoLoginList && changeAtWhoLoginList(atWhoLoginListState); },[atWhoLoginListState]) @@ -292,8 +202,8 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
{users && users.map((item,key)=>{ return( -
{selectAtWho(item.username)}} onMouseOver={()=>{onMouseOver(key)}}> - {item.image_url && } +
{selectAtWho(item.username)}} onMouseOver={()=>{onMouseOver(key)}}> + {item.image_url && } {item.username}
) @@ -301,25 +211,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
) - useEffect(()=>{ - //当atWhoVisible为true的时候,失焦,监听上下和enter键 - // atWhoVisible && editorInstance.cm. - if(atWhoVisible){ - const cm = editorInstance.cm; - //获取鼠标所在行的行数和ch - const line = cm.doc.getCursor().line;//行 - console.log('useEffect',cm.getLine(line)); - // document.activeElement.id !== "blur_atWho" && document.getElementById("blur_atWho").focus(); - // const atWhoDivs = document.getElementsByClassName("at_who"); - // let index = 0; - // for(let i = 0; i { if (editorInstance) { return @@ -402,6 +293,69 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla const cmEl = editorInstance && editorInstance.cm + useEffect(()=>{ + if(atWhoVisibleRef.current){ + // 添加上下键、enter键监听事件 + cmEl.addKeyMap({ + 'Up':()=>{ + const atWhoListDiv = document.getElementById("at_who_list"); + const atWhoDivs = document.getElementsByClassName("at_who"); + let index; + for(let i = 0; i0){ + index <=atWhoDivs.length-4 && (atWhoListDiv.scrollTop -=40) + atWhoDivs[index].className = "at_who"; + atWhoDivs[index-1].className = "at_who active"; + } + }, + 'Down':()=>{ + const atWhoListDiv = document.getElementById("at_who_list"); + const atWhoDivs = document.getElementsByClassName("at_who"); + let index; + for(let i = 0; i=3 && (atWhoListDiv.scrollTop +=40) + atWhoDivs[index].className = "at_who"; + atWhoDivs[index+1].className = "at_who active"; + } + }, + 'Enter':()=>{ + //找到classname为at_who active的div,执行click事件 + if(document.getElementsByClassName("at_who active")[0]){ + document.getElementsByClassName("at_who active")[0].click() + }else{ + const cm = editorInstance.cm; + const cursor = cm.doc.getCursor(); + const line = cursor.line;//行 + const ch = cursor.ch;//列 + //添加换行 + cm.replaceRange("\n",{line,ch},{line,ch}); + setAtWhoVisible(false); + atWhoVisibleRef.current = false; + } + } + }) + } else { + //移除上下、enter键监听 + cmEl && cmEl.removeKeyMap(); + } + },[atWhoVisible]) + + useEffect(()=>{ + //当users数组发生变化时改变框的位置 + if(atWhoVisibleRef.current && users){ + //获取光标位置 + const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style; + //设置弹框位置 + const newLeft = placeholder === "添加评论..."? 80: 10; + document.getElementById("at_who_list").style.left = (parseInt(cssStyle.getPropertyValue("left").replace("px",""))+newLeft)+"px"; + } + },[users]) + useEffect(() => { if (cmEl) { let tid = null @@ -426,7 +380,34 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla document.removeEventListener("keydown",mdKeyDown); }); editorInstance.cm.on("change", (cm) => { + //调用父组件的onchange方法,将输入内容传入父级组件 onChange && onChange(cm.getValue()); + if(atWhoVisibleRef.current){ + //搜索用户(弹框之后用户输入用户名信息) + const cur = cm.doc.getCursor(); + const line = cur.line; + const ch = cur.ch; + let rangeCont = cmEl.getRange({line,ch:0},{line,ch}); + //处理已经弹出列表框,但用户删除@符号 + if(rangeCont.indexOf("@")===-1){ + setAtWhoVisible(false); + atWhoVisibleRef.current = false; + }else{ + rangeCont = rangeCont.substring(rangeCont.lastIndexOf("@")+1); + rangeCont ? axios.get(`/${owner}/${projectsId}/members.json`,{ + params: { + search: rangeCont, + }, + }).then(response=>{ + if(response && response.data && response.data.total_count !== 0){ + setUsers(response.data.users); + }else{ + setUsers(undefined); + } + }):setUsers(allUsers) + } + } + //当内容发生改变并且有已@列表时 if(atWhoLoginList.current.length != 0){ const codemirror = editorInstance.cm; @@ -434,7 +415,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla //处理初始内容就自带@谁的情况 if(initValue){ const del = []; - users.map(item=>{ + allUsers.map(item=>{ if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){ //初始内容中有符合@+名字的格式并且当前内容未删除初始内容 del[del.length] = `@${item.username}`; @@ -447,7 +428,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla //以username为主键,login为value的map集合 let atWhoMap = new Map(); Array.from(atWhoLoginList.current).map(item=>{ - users.map(i=>{ + allUsers.map(i=>{ if(i.login === item){ atWhoMap.set(i.username,i.login); } @@ -471,8 +452,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla return; } //处理有名字但是无@符号,有@但是名字对不上的情况 - const a = value.indexOf(username)===-1; - const b = value.charAt(value.indexOf(username)-1) !="@"; if(value.indexOf(username)===-1 || value.charAt(value.indexOf(username)-1) !="@"){ //符合任意一种情况->踢掉这个人 不给他发消息 const list = new Set(atWhoLoginList.current); @@ -541,14 +520,14 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla }, [ editorInstance, resizeBarEl ]) + return ( + {atWhoVisible && atWhoList}
- - {atWhoVisible && atWhoList}
{showResizeBar ? : null}