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:
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}