@我功能。
This commit is contained in:
parent
b1ead191a6
commit
f003a99779
|
@ -224,7 +224,7 @@ function NoticeContent({ visible, showNotification, resetUserInfo, current_user:
|
||||||
<Badge color="#FA2020" />
|
<Badge color="#FA2020" />
|
||||||
</span>
|
</span>
|
||||||
<div className="noticeCont-text">
|
<div className="noticeCont-text">
|
||||||
<span className="content-span atme-cont-span" dangerouslySetInnerHTML={{ __html: "<b>" + (item.sender ? item.sender.name : '') + "</b> " + item.content + " 中@我" }}></span>
|
<span className="content-span atme-cont-span" dangerouslySetInnerHTML={{ __html: item.content }}></span>
|
||||||
<span className="timeSpan">{item.time_ago}</span>
|
<span className="timeSpan">{item.time_ago}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,7 +17,7 @@ function MyNotice(props) {
|
||||||
const [selectedNum, setSelectedNum] = useState(0);//@我批量删除选择消息条数
|
const [selectedNum, setSelectedNum] = useState(0);//@我批量删除选择消息条数
|
||||||
const [isBatchDelete, setIsBatchDelete] = useState(false);//@我是否批量删除
|
const [isBatchDelete, setIsBatchDelete] = useState(false);//@我是否批量删除
|
||||||
const [batchDeleteCheckedAll, setBatchDeleteCheckAll] = useState(false);//@我批量删除--全选
|
const [batchDeleteCheckedAll, setBatchDeleteCheckAll] = useState(false);//@我批量删除--全选
|
||||||
|
const [messageType, setMessageType] = useState(undefined);
|
||||||
const [noticeUnreadCount, setNoticeUnreadCount] = useState();//未读系统通知数量
|
const [noticeUnreadCount, setNoticeUnreadCount] = useState();//未读系统通知数量
|
||||||
// const [letterUnreadCount, setLetterUnreadCount] = useState(0);//未读私信数量
|
// const [letterUnreadCount, setLetterUnreadCount] = useState(0);//未读私信数量
|
||||||
const [atUnreadCount, setAtUnreadCount] = useState();//未读@我数量
|
const [atUnreadCount, setAtUnreadCount] = useState();//未读@我数量
|
||||||
|
@ -65,6 +65,7 @@ function MyNotice(props) {
|
||||||
setAtUnreadCount(response.data.unread_atme);
|
setAtUnreadCount(response.data.unread_atme);
|
||||||
setMessageList(response.data.messages);
|
setMessageList(response.data.messages);
|
||||||
setMessTotalCount(response.data.total_count);
|
setMessTotalCount(response.data.total_count);
|
||||||
|
setMessageType(response.data.type);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -203,7 +204,46 @@ function MyNotice(props) {
|
||||||
</div>
|
</div>
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
{messageList && messageList.map(item => {
|
{/* 系统消息 */}
|
||||||
|
{messageType === "notification" && messageList && messageList.map(item =>{
|
||||||
|
return (
|
||||||
|
<div className="mynotice-content vertical-center-style" key={item.id}>
|
||||||
|
<div className="mynotice-cont stretch-style">
|
||||||
|
{item.status === 1 ? <Badge color="#FA2020" /> : <span className="system-notice-blank"></span>}
|
||||||
|
<i className={"iconfont "+noticeSourceType[item.source]}></i>
|
||||||
|
<span className={`sysNotice-length ${item.notification_url?'highlightSpan':''}`} onClick={() => {turnToMess(item)}} dangerouslySetInnerHTML={{__html: item.content}}></span>
|
||||||
|
</div>
|
||||||
|
<div className="mynotice-cont vertical-center-style float-left-little">
|
||||||
|
<span className={item.status === 1?"timeSpan":""}>{item.time_ago}</span>
|
||||||
|
{item.status === 1 && <span className="invisable-read" onClick={()=>readNotice([item.id])}>标记为已读</span>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
|
||||||
|
{/* @我消息 */}
|
||||||
|
{messageType === "atme" && messageList && messageList.map(item =>{
|
||||||
|
return (
|
||||||
|
<div className={`mynotice-content vertical-center-style ${isBatchDelete?'batchDel':''}`} key={item.id}>
|
||||||
|
<div className="mynotice-cont vertical-center-style">
|
||||||
|
<Checkbox value={item.id} className={isBatchDelete ? 'visible-checkbox' : 'invisible-checkbox'} onChange={onChange} checked={item.checkedBatch}></Checkbox>
|
||||||
|
{item.sender && <img src={`https://testforgeplus.trustie.net//${item.sender.image_url}`} className="currentImg" onClick={()=>{window.open(`/${item.sender && item.sender.login}`);}}/>}
|
||||||
|
<div className={`atme-notice-text stretch-style ${item.notification_url && 'highlightSpan'}`} onClick={() => {turnToMess(item)}}>
|
||||||
|
{item.status === 1 ? <Badge color="#FA2020"/> : <span className="system-notice-blank"></span>}
|
||||||
|
{item.sender && <span className="atme-length" dangerouslySetInnerHTML={{__html: item.content}}></span>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mynotice-cont vertical-center-style">
|
||||||
|
<span className={!isBatchDelete && item.status === 1?"timeSpan":""}>{item.time_ago}</span>
|
||||||
|
{!isBatchDelete && item.status === 1 && <span className="invisable-read" onClick={()=>readNotice([item.id])}>标记为已读</span>}
|
||||||
|
{!isBatchDelete && <span className="invisable-read float-left-little" onClick={()=>deleteNotice([item.id])}>删除</span>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
|
||||||
|
{false && messageList && messageList.map(item => {
|
||||||
|
console.log('item',item);
|
||||||
// 系统消息
|
// 系统消息
|
||||||
if (noticeType === "0") {
|
if (noticeType === "0") {
|
||||||
// 消息类别
|
// 消息类别
|
||||||
|
@ -229,7 +269,7 @@ function MyNotice(props) {
|
||||||
{item.sender && <img src={`https://testforgeplus.trustie.net//${item.sender.image_url}`} className="currentImg" onClick={()=>{window.open(`/${item.sender && item.sender.login}`);}}/>}
|
{item.sender && <img src={`https://testforgeplus.trustie.net//${item.sender.image_url}`} className="currentImg" onClick={()=>{window.open(`/${item.sender && item.sender.login}`);}}/>}
|
||||||
<div className={`atme-notice-text stretch-style ${item.notification_url && 'highlightSpan'}`} onClick={() => {turnToMess(item)}}>
|
<div className={`atme-notice-text stretch-style ${item.notification_url && 'highlightSpan'}`} onClick={() => {turnToMess(item)}}>
|
||||||
{item.status === 1 ? <Badge color="#FA2020"/> : <span className="system-notice-blank"></span>}
|
{item.status === 1 ? <Badge color="#FA2020"/> : <span className="system-notice-blank"></span>}
|
||||||
{item.sender && <span className="atme-length" dangerouslySetInnerHTML={{__html: "<b class = 'atme-notice-name'>" + item.sender.name+ "</b> "+ item.content +" 中@我"}}></span>}
|
{item.sender && <span className="atme-length" dangerouslySetInnerHTML={{__html: "<b class = 'atme-notice-name'>" + item.sender.name+ "</b> "+ item.content}}></span>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mynotice-cont vertical-center-style">
|
<div className="mynotice-cont vertical-center-style">
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
border-bottom: 1px solid rgba(212, 212, 212, 0.5);
|
border-bottom: 1px solid rgba(212, 212, 212, 0.5);
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
}
|
}
|
||||||
.active{
|
.at_who.active{
|
||||||
background: #F3F4F6;
|
background: #F3F4F6;
|
||||||
}
|
}
|
||||||
.at_who img{
|
.at_who img{
|
||||||
|
@ -39,8 +39,4 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
|
||||||
.blur_atWho{
|
|
||||||
position: absolute;
|
|
||||||
top: -100px;
|
|
||||||
}
|
}
|
|
@ -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 }) => {
|
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 editorEl = useRef();
|
||||||
const resizeBarEl = useRef();
|
const resizeBarEl = useRef();
|
||||||
const [editorInstance, setEditorInstance] = useState();
|
const [editorInstance, setEditorInstance] = useState();
|
||||||
const [atWhoVisible, setAtWhoVisible] = useState(false);
|
const [atWhoVisible, setAtWhoVisible] = useState(false);
|
||||||
const [atWhoLoginListState, setAtWhoLoginListState] = useState([]);
|
const [atWhoLoginListState, setAtWhoLoginListState] = useState([]);
|
||||||
|
//调用member.json接口获取到的用户列表
|
||||||
const [users, setUsers] = useState([]);
|
const [users, setUsers] = useState([]);
|
||||||
//用户输入的@之后的搜索词
|
//可以@的全部用户
|
||||||
const [search, setSeacrch] = useState(undefined);
|
const [allUsers, setAllUsers] = useState([]);
|
||||||
const atWhoLoginList = useRef([]);
|
const atWhoLoginList = useRef([]);
|
||||||
|
const atWhoVisibleRef = useRef(false);
|
||||||
const containerId = `mdEditor_${mdID}`;
|
const containerId = `mdEditor_${mdID}`;
|
||||||
const editorBodyId = `mdEditors_${mdID}`;
|
const editorBodyId = `mdEditors_${mdID}`;
|
||||||
const tipId = `e_tips_mdEditor_${mdID}`;
|
const tipId = `e_tips_mdEditor_${mdID}`;
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
isCanAtme && axios.get(`/${owner}/${projectsId}/members.json`,{
|
//请求members接口获取全部可@列表
|
||||||
// params: {
|
isCanAtme && axios.get(`/${owner}/${projectsId}/members.json`).then(response=>{
|
||||||
// search: 'admin',
|
if(response.data.total_count !== 0){
|
||||||
// },
|
setAllUsers(response.data.users);
|
||||||
}).then(response=>{
|
|
||||||
if(response && response.status === 200){
|
|
||||||
setUsers(response.data.users);
|
setUsers(response.data.users);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
//点击其他地方关闭弹框
|
||||||
|
document.addEventListener('click',()=>{
|
||||||
|
atWhoVisibleRef.current = false;
|
||||||
|
setAtWhoVisible(false);
|
||||||
|
})
|
||||||
},[])
|
},[])
|
||||||
|
|
||||||
function onLayout() {
|
function onLayout() {
|
||||||
|
@ -119,14 +124,16 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectAtWho(username){
|
function selectAtWho(username){
|
||||||
|
atWhoVisibleRef.current = false;
|
||||||
setAtWhoVisible(false);
|
setAtWhoVisible(false);
|
||||||
const cm = editorInstance.cm;
|
const cm = editorInstance.cm;
|
||||||
//获取鼠标所在行的行数和ch
|
//获取鼠标所在行的行数和ch
|
||||||
const cursor = cm.doc.getCursor();
|
const cursor = cm.doc.getCursor();
|
||||||
const line = cursor.line;//行
|
const line = cursor.line;//行
|
||||||
const ch = cursor.ch;//列
|
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();
|
cm.focus();
|
||||||
//将此user的login存储到atWhoLoginList集合中
|
//将此user的login存储到atWhoLoginList集合中
|
||||||
|
@ -136,155 +143,58 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
||||||
})
|
})
|
||||||
atWhoLoginList.current = Array.from(list);
|
atWhoLoginList.current = Array.from(list);
|
||||||
setAtWhoLoginListState(Array.from(list));
|
setAtWhoLoginListState(Array.from(list));
|
||||||
//销毁atWhoKeyDown键盘监听事件
|
|
||||||
// document.removeEventListener("keydown",atWhoKeyDown);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMouseOver(key){
|
function onMouseOver(key){
|
||||||
document.getElementsByClassName("at_who active")[0].className="at_who";
|
document.getElementsByClassName("at_who active")[0] && (document.getElementsByClassName("at_who active")[0].className="at_who");
|
||||||
document.getElementsByClassName("at_who")[key].className="at_who active";
|
document.getElementsByClassName("at_who")[key] && (document.getElementsByClassName("at_who")[key].className="at_who active");
|
||||||
}
|
}
|
||||||
|
|
||||||
//markdown编辑器中输入的键盘监听事件
|
//markdown编辑器中输入的键盘监听事件
|
||||||
function mdKeyDown(e){
|
function mdKeyDown(e){
|
||||||
console.log("markdown编辑器--------键盘监听事件");
|
if (e.shiftKey && e.code === "Digit2") {
|
||||||
// document.onkeydown = e=>{
|
// 输入@键后在对应的位置显示可选的项目成员
|
||||||
if (e.shiftKey && e.key === "@") {
|
atWhoVisibleRef.current = true;
|
||||||
// 输入@键后在对应的位置显示可选的项目成员
|
setAtWhoVisible(true);
|
||||||
setAtWhoVisible(true);
|
//获取光标位置
|
||||||
//获取光标位置
|
const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style;
|
||||||
const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style;
|
//设置弹框位置
|
||||||
//设置弹框位置
|
const newTop = placeholder === "添加评论..." ? 159: placeholder === "请输入合并请求的描述..." ? 172:62;
|
||||||
document.getElementById("at_who_list").style.top = (parseInt(cssStyle.getPropertyValue("top").replace("px",""))+60)+"px";
|
const newLeft = placeholder === "添加评论..." ? 80: 20;
|
||||||
document.getElementById("at_who_list").style.left = (parseInt(cssStyle.getPropertyValue("left").replace("px",""))+20)+"px";
|
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";
|
||||||
const at_who_divs = document.getElementsByClassName("at_who");
|
}
|
||||||
at_who_divs[0].className = "at_who active";
|
//处理本来@了某人 -> 删掉 -> 撤回 的情况
|
||||||
}
|
if(e.ctrlKey && e.code === "KeyZ" && allUsers.length != 0){
|
||||||
//处理本来@了某人 -> 删掉 -> 撤回 的情况
|
const codemirror = editorInstance.cm;
|
||||||
if(e.ctrlKey && e.code === "KeyZ" && users.length != 0){
|
let value = codemirror.getValue();
|
||||||
const codemirror = editorInstance.cm;
|
//处理初始内容就自带@谁的情况
|
||||||
let value = codemirror.getValue();
|
if(initValue){
|
||||||
//处理初始内容就自带@谁的情况
|
const del = [];
|
||||||
if(initValue){
|
allUsers.map(item=>{
|
||||||
const del = [];
|
if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){
|
||||||
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[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));
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
del.length!=0 && del.map(str=>{
|
||||||
|
value = value.replace(str,"");
|
||||||
|
})
|
||||||
}
|
}
|
||||||
// const atWhoListDiv = document.getElementById("at_who_list");
|
//判断value是否包含@符号
|
||||||
// if(atWhoListDiv && (e.key === "ArrowUp" || e.key === "ArrowDown" || e.key === "Enter")){
|
value.indexOf("@") != -1 && allUsers.map(item =>{
|
||||||
// return false;
|
if(value.indexOf(item.username)!=-1 && value.charAt(value.indexOf(item.username)-1) ==="@"){
|
||||||
// }
|
//将此user的login存储到atWhoLoginList集合中
|
||||||
// const atWhoDivs = document.getElementsByClassName("at_who");
|
const list = new Set(atWhoLoginList.current);
|
||||||
// let index;
|
list.add(item.login);
|
||||||
// for(let i = 0; i<atWhoDivs.length;i++){
|
atWhoLoginList.current = Array.from(list);
|
||||||
// atWhoDivs[i].className === "at_who active" && (index = i);
|
setAtWhoLoginListState(Array.from(list));
|
||||||
// }
|
}
|
||||||
// //当可选@列表div弹出之后监听上下键(不能通过判断atWhoVisible,因为atWhoVisible还未更新为true)
|
})
|
||||||
// if(e.key === "ArrowUp" && index > 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<atWhoDivs.length;i++){
|
|
||||||
// atWhoDivs[i].className === "at_who active" && (index = i);
|
|
||||||
// }
|
|
||||||
// index >=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();
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//弹出可选@用户列表之后的键盘监听事件
|
|
||||||
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<atWhoDivs.length;i++){
|
|
||||||
atWhoDivs[i].className === "at_who active" && (index = i);
|
|
||||||
}
|
|
||||||
console.log("监听上下和enter键",e,atWhoListDiv,atWhoDivs,index);
|
|
||||||
if(e.key === "ArrowUp" && index > 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(()=>{
|
useEffect(()=>{
|
||||||
document.addEventListener('click',()=>{setAtWhoVisible(false)})
|
|
||||||
},[])
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
console.log('@谁列表发生变化,atWhoLoginList.current:',atWhoLoginList.current);
|
|
||||||
changeAtWhoLoginList && changeAtWhoLoginList(atWhoLoginListState);
|
changeAtWhoLoginList && changeAtWhoLoginList(atWhoLoginListState);
|
||||||
},[atWhoLoginListState])
|
},[atWhoLoginListState])
|
||||||
|
|
||||||
|
@ -292,8 +202,8 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
||||||
<div className="at_who_list" id="at_who_list" >
|
<div className="at_who_list" id="at_who_list" >
|
||||||
{users && users.map((item,key)=>{
|
{users && users.map((item,key)=>{
|
||||||
return(
|
return(
|
||||||
<div key={key} className="at_who" onClick={()=>{selectAtWho(item.username)}} onMouseOver={()=>{onMouseOver(key)}}>
|
<div key={key} className={`at_who ${key===0 && `active`}`} onClick={()=>{selectAtWho(item.username)}} onMouseOver={()=>{onMouseOver(key)}}>
|
||||||
{item.image_url && <img src={getImageUrl(item.image_url)}></img>}
|
{item.image_url && <img src={getImageUrl(`/${item.image_url}`)}></img>}
|
||||||
<span>{item.username}</span>
|
<span>{item.username}</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -301,25 +211,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
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<atWhoDivs.length;i++){
|
|
||||||
// atWhoDivs[i].className === "at_who active" && (index = i);
|
|
||||||
// }
|
|
||||||
// const atWhoListDiv = document.getElementById("at_who_list");
|
|
||||||
// document.addEventListener("keydown",atWhoKeyDown);
|
|
||||||
}
|
|
||||||
},[atWhoVisible])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (editorInstance) {
|
if (editorInstance) {
|
||||||
return
|
return
|
||||||
|
@ -402,6 +293,69 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
||||||
|
|
||||||
const cmEl = editorInstance && editorInstance.cm
|
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; i<atWhoDivs.length;i++){
|
||||||
|
atWhoDivs[i].className === "at_who active" && (index = i);
|
||||||
|
}
|
||||||
|
if(index>0){
|
||||||
|
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<atWhoDivs.length;i++){
|
||||||
|
atWhoDivs[i].className === "at_who active" && (index = i);
|
||||||
|
}
|
||||||
|
if(index<atWhoDivs.length-1){
|
||||||
|
index >=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(() => {
|
useEffect(() => {
|
||||||
if (cmEl) {
|
if (cmEl) {
|
||||||
let tid = null
|
let tid = null
|
||||||
|
@ -426,7 +380,34 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
||||||
document.removeEventListener("keydown",mdKeyDown);
|
document.removeEventListener("keydown",mdKeyDown);
|
||||||
});
|
});
|
||||||
editorInstance.cm.on("change", (cm) => {
|
editorInstance.cm.on("change", (cm) => {
|
||||||
|
//调用父组件的onchange方法,将输入内容传入父级组件
|
||||||
onChange && onChange(cm.getValue());
|
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){
|
if(atWhoLoginList.current.length != 0){
|
||||||
const codemirror = editorInstance.cm;
|
const codemirror = editorInstance.cm;
|
||||||
|
@ -434,7 +415,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
||||||
//处理初始内容就自带@谁的情况
|
//处理初始内容就自带@谁的情况
|
||||||
if(initValue){
|
if(initValue){
|
||||||
const del = [];
|
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}`)){
|
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[del.length] = `@${item.username}`;
|
||||||
|
@ -447,7 +428,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
||||||
//以username为主键,login为value的map集合
|
//以username为主键,login为value的map集合
|
||||||
let atWhoMap = new Map();
|
let atWhoMap = new Map();
|
||||||
Array.from(atWhoLoginList.current).map(item=>{
|
Array.from(atWhoLoginList.current).map(item=>{
|
||||||
users.map(i=>{
|
allUsers.map(i=>{
|
||||||
if(i.login === item){
|
if(i.login === item){
|
||||||
atWhoMap.set(i.username,i.login);
|
atWhoMap.set(i.username,i.login);
|
||||||
}
|
}
|
||||||
|
@ -471,8 +452,6 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
||||||
return;
|
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) !="@"){
|
if(value.indexOf(username)===-1 || value.charAt(value.indexOf(username)-1) !="@"){
|
||||||
//符合任意一种情况->踢掉这个人 不给他发消息
|
//符合任意一种情况->踢掉这个人 不给他发消息
|
||||||
const list = new Set(atWhoLoginList.current);
|
const list = new Set(atWhoLoginList.current);
|
||||||
|
@ -541,14 +520,14 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
||||||
}, [
|
}, [
|
||||||
editorInstance, resizeBarEl
|
editorInstance, resizeBarEl
|
||||||
])
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
{atWhoVisible && atWhoList}
|
||||||
<div ref={editorEl} className={`df ${className} ${imageExpand && 'editormd-image-click-expand'} `}>
|
<div ref={editorEl} className={`df ${className} ${imageExpand && 'editormd-image-click-expand'} `}>
|
||||||
<div className={`edu-back-greyf5 radius4 editormd ${error ? 'error' : ''}`} id={containerId} >
|
<div className={`edu-back-greyf5 radius4 editormd ${error ? 'error' : ''}`} id={containerId} >
|
||||||
<textarea style={{ display: 'none' }} id={editorBodyId} name="content"></textarea>
|
<textarea style={{ display: 'none' }} id={editorBodyId} name="content"></textarea>
|
||||||
<div className="CodeMirror cm-s-defualt"></div>
|
<div className="CodeMirror cm-s-defualt"></div>
|
||||||
<input id ="blur_atWho" className="blur_atWho"/>
|
|
||||||
{atWhoVisible && atWhoList}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{showResizeBar ? <a ref={resizeBarEl} className='editor-resize'></a> : null}
|
{showResizeBar ? <a ref={resizeBarEl} className='editor-resize'></a> : null}
|
||||||
|
|
Loading…
Reference in New Issue