diff --git a/src/forge/Merge/merge_form.js b/src/forge/Merge/merge_form.js index 7bdfc04e..8ca1125d 100644 --- a/src/forge/Merge/merge_form.js +++ b/src/forge/Merge/merge_form.js @@ -147,15 +147,7 @@ class MergeForm extends Component { } else { values.issue_tag_ids = []; } - const { desc , atWhoLoginList } = this.state; - //发送消息 - if(atWhoLoginList.length != 0){ - axios.post(`/users/${owner}/messages.json`,{ - type:"atme", - receivers_login:atWhoLoginList, - atmeable_type:"PullRequest" - }) - } + const { desc , atWhoLoginList } = this.state; if (merge_type === "new") { let url = `/${owner}/${projectsId}/pulls.json`; axios.post(url, { @@ -167,7 +159,8 @@ class MergeForm extends Component { fork_project_id: data && data.fork_project_id, merge_user_login: data && data.merge_user_login, files_count, - commits_count + commits_count, + receivers_login:atWhoLoginList, }) .then((result) => { if (result) { @@ -197,6 +190,7 @@ class MergeForm extends Component { body: desc, head: pull, base: merge, + receivers_login:atWhoLoginList, }) .then((result) => { if (result) { diff --git a/src/forge/Order/order_form.js b/src/forge/Order/order_form.js index b019c098..2cb96ed7 100644 --- a/src/forge/Order/order_form.js +++ b/src/forge/Order/order_form.js @@ -154,15 +154,6 @@ class order_form extends Component { values.issue_tag_ids = [values.issue_tag_ids]; } const { description, start_date, due_date, issue_type , atWhoLoginList } = this.state; - //发送消息 - if(atWhoLoginList.length != 0){ - console.log('issue发送消息',atWhoLoginList); - axios.post(`/users/${owner}/messages.json`,{ - type:"atme", - receivers_login:atWhoLoginList, - atmeable_type:"Issue" - }) - } if (form_type !== "edit") { const url = `/${owner}/${projectsId}/issues.json`; axios.post(url, { @@ -172,6 +163,7 @@ class order_form extends Component { start_date: start_date, due_date: due_date, issue_type: issue_type, + receivers_login:atWhoLoginList, }).then((result) => { if (result && result.data.id) { this.props.showNotification("任务创建成功!"); @@ -198,6 +190,7 @@ class order_form extends Component { due_date: due_date, issue_type: issue_type, ...values, + receivers_login:atWhoLoginList, }).then((result) => { if (result) { this.props.history.push(`/${owner}/${projectsId}/issues/${orderId}`); diff --git a/src/forge/comments/comments.js b/src/forge/comments/comments.js index aeb59224..84478d7c 100644 --- a/src/forge/comments/comments.js +++ b/src/forge/comments/comments.js @@ -65,15 +65,6 @@ class comments extends Component { const { owner } = this.props.match.params; - //发送消息 - if(atWhoLoginList.length != 0){ - axios.post(`/users/${owner}/messages.json`,{ - type:"atme", - receivers_login:atWhoLoginList, - atmeable_type:"Issue" - }) - } - const url = `/issues/${orderId}/journals.json`; axios .post(url, { @@ -82,6 +73,7 @@ class comments extends Component { issue_id: orderId, attachment_ids: fileList, parent_id: reply_id, + receivers_login:atWhoLoginList, }) .then((result) => { if (result && result.data.status === 0) { diff --git a/src/modules/tpm/challengesnew/css/newquestion.css b/src/modules/tpm/challengesnew/css/newquestion.css index 13353547..c6359f52 100644 --- a/src/modules/tpm/challengesnew/css/newquestion.css +++ b/src/modules/tpm/challengesnew/css/newquestion.css @@ -9,7 +9,7 @@ /*md编辑器中输入@弹出可选人列表样式*/ .at_who_list{ position: absolute; - z-index: 99; + z-index: 100; width: 180px; max-height: 160px; background: #FFFFFF; @@ -26,7 +26,7 @@ border-bottom: 1px solid rgba(212, 212, 212, 0.5); padding: 0 4px; } -.at_who:hover{ +.active{ background: #F3F4F6; } .at_who img{ @@ -34,4 +34,8 @@ height:30px; border-radius:50%; margin-right: 10px; +} +.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 2aaa879e..437d2bce 100644 --- a/src/modules/tpm/challengesnew/tpm-md-editor.js +++ b/src/modules/tpm/challengesnew/tpm-md-editor.js @@ -2,6 +2,7 @@ import React, { Fragment, useEffect, useRef, useState } from 'react'; import { getUploadActionUrl, getUrl } from 'educoder'; import ResizeObserver from 'resize-observer-polyfill'; import { getImageUrl } from 'educoder'; +import axios from 'axios'; import '../../courses/css/Courses.css'; import './css/TPMchallengesnew.css'; import 'codemirror/lib/codemirror.css'; @@ -80,16 +81,23 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla const [editorInstance, setEditorInstance] = useState(); const [atWhoVisible, setAtWhoVisible] = useState(false); const [atWhoLoginListState, setAtWhoLoginListState] = useState([]); + const [users, setUsers] = useState([]); const atWhoLoginList = useRef([]); const containerId = `mdEditor_${mdID}`; const editorBodyId = `mdEditors_${mdID}`; const tipId = `e_tips_mdEditor_${mdID}`; - - const users = [{image_url: "system/lets/letter_avatars/2/J/241_157_191/120.png",login: "jasonjun",profile_completed: false,user_id: 84965,username: "jasonjun"}, - {image_url: "system/lets/letter_avatars/2/E/122_185_146/120.png",login: "Eo9ygbqns",profile_completed: false,user_id: 84963,username: "Eo9ygbqns"}, - {image_url: "system/lets/letter_avatars/2/P/238_117_19/120.png",login: "postwoman",profile_completed: true,user_id: 84961,username: "PostWoman"}, - {image_url: "system/lets/letter_avatars/2/X/70_163_90/120.png",login: "xuzhun",profile_completed: false,user_id: 89516,username: "徐准"}, - {image_url: "system/lets/letter_avatars/2/X/70_163_90/120.png",login: "Es5ghtfik",profile_completed: false,user_id: 89516,username: "徐准1"}] + + useEffect(()=>{ + isCanAtme && axios.get('/users/list.json',{ + params: { + search: 'admin', + }, + }).then(response=>{ + if(response && response.status === 200){ + setUsers(response.data.users); + } + }) + },[]) function onLayout() { let ro; @@ -116,10 +124,10 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla const line = cursor.line;//行 const ch = cursor.ch;//列 //替换最后的内容 + // cm.getLine(line).endsWith("@") ? cm.replaceRange(username+" ",{line,ch},{line,ch}) : cm.replaceRange("@"+username+" ",{line,ch},{line,ch}) cm.replaceRange(username+" ",{line,ch},{line,ch}); - //鼠标聚焦到此行的最后 - editorInstance.focus(); - editorInstance.setCursor({line,ch:ch+username.length+1}); + //鼠标聚焦 + cm.focus(); //将此user的login存储到atWhoLoginList集合中 const list = new Set(atWhoLoginList.current); users.map((item)=>{ @@ -129,6 +137,92 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla setAtWhoLoginListState(Array.from(list)); } + function onMouseOver(key){ + document.getElementsByClassName("at_who active")[0].className="at_who"; + document.getElementsByClassName("at_who")[key].className="at_who active"; + } + + //markdown编辑器中输入的键盘监听事件 + function mdKeyDown(){ + document.onkeydown = e=>{ + console.log("markdown",atWhoVisible); + if (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"; + } else { + setAtWhoVisible(false); + } + //处理本来@了某人 -> 删掉 -> 撤回 的情况 + if(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)); + } + }) + } + } + } + + //弹出可选@用户列表之后的键盘监听事件 + function atWhoKeyDown(){ + //监听上下和enter键 + document.onkeydown = (e) =>{ + console.log("atwho列表",atWhoVisible); + const atWhoListDiv = document.getElementById("at_who_list"); + const atWhoDivs = document.getElementsByClassName("at_who"); + let index; + for(let i = 0; i 0){ + // index >=4 && (atWhoListDiv.scrollTop -=40) + atWhoListDiv.scrollTop -= 40; + atWhoDivs[index].className = "at_who"; + atWhoDivs[index-1].className = "at_who active"; + } + if(e.key === "ArrowDown" && index < atWhoDivs.length-1){ + // index >=3 && (atWhoListDiv.scrollTop +=40) + 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(); + } + } + } + useEffect(()=>{ console.log('@谁列表发生变化,atWhoLoginList.current:',atWhoLoginList.current,'atWhoLoginListState: ',atWhoLoginListState); changeAtWhoLoginList && changeAtWhoLoginList(atWhoLoginListState); @@ -138,8 +232,8 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
{users && users.map((item,key)=>{ return( -
{selectAtWho(item.username)}}> - { item.image_url && } +
{selectAtWho(item.username)}} onMouseOver={()=>{onMouseOver(key)}}> + {item.image_url && } {item.username}
) @@ -153,10 +247,9 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla useEffect(()=>{ //当atWhoVisible为true的时候,失焦,监听上下和enter键 - if(atWhoVisible && editorInstance && editorInstance.cm){ - console.log('弹框啦'); - //焦点转移到非monaco-editer编辑器上 - editorInstance.cm.addEventListener('blur',()=>{}) + if(atWhoVisible){ + document.activeElement.id !== "blur_atWho" && document.getElementById("blur_atWho").focus(); + document.addEventListener("keydown",atWhoKeyDown()); } },[atWhoVisible]) @@ -260,52 +353,10 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla //isCanAtme:只有issue和合并请求以及评论部分可以@他人操作 //当光标或选中内容时触发绑定@事件 isCanAtme && editorInstance.cm.on("focus", () => { - document.onkeydown = (e) => { - if (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"; - } else { - setAtWhoVisible(false); - } - //处理本来@了某人 -> 删掉 -> 撤回 的情况 - if(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是否包含@符号 - if(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)); - } - }) - } - } - }; + document.addEventListener("keydown", mdKeyDown()); }); - editorInstance.cm.on("blur", () => { - document.onkeydown = null ; + isCanAtme && editorInstance.cm.on("blur", () => { + document.removeEventListener("keydown",mdKeyDown()); }); editorInstance.cm.on("change", (cm) => { onChange && onChange(cm.getValue()); @@ -428,6 +479,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
+ {atWhoVisible && atWhoList}