atwho列表

This commit is contained in:
谢思 2021-10-23 19:39:54 +08:00
parent 3498390974
commit 8021d96cd8
5 changed files with 125 additions and 90 deletions

View File

@ -147,15 +147,7 @@ class MergeForm extends Component {
} else { } else {
values.issue_tag_ids = []; values.issue_tag_ids = [];
} }
const { desc , atWhoLoginList } = this.state; const { desc , atWhoLoginList } = this.state;
//发送消息
if(atWhoLoginList.length != 0){
axios.post(`/users/${owner}/messages.json`,{
type:"atme",
receivers_login:atWhoLoginList,
atmeable_type:"PullRequest"
})
}
if (merge_type === "new") { if (merge_type === "new") {
let url = `/${owner}/${projectsId}/pulls.json`; let url = `/${owner}/${projectsId}/pulls.json`;
axios.post(url, { axios.post(url, {
@ -167,7 +159,8 @@ class MergeForm extends Component {
fork_project_id: data && data.fork_project_id, fork_project_id: data && data.fork_project_id,
merge_user_login: data && data.merge_user_login, merge_user_login: data && data.merge_user_login,
files_count, files_count,
commits_count commits_count,
receivers_login:atWhoLoginList,
}) })
.then((result) => { .then((result) => {
if (result) { if (result) {
@ -197,6 +190,7 @@ class MergeForm extends Component {
body: desc, body: desc,
head: pull, head: pull,
base: merge, base: merge,
receivers_login:atWhoLoginList,
}) })
.then((result) => { .then((result) => {
if (result) { if (result) {

View File

@ -154,15 +154,6 @@ class order_form extends Component {
values.issue_tag_ids = [values.issue_tag_ids]; values.issue_tag_ids = [values.issue_tag_ids];
} }
const { description, start_date, due_date, issue_type , atWhoLoginList } = this.state; 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") { if (form_type !== "edit") {
const url = `/${owner}/${projectsId}/issues.json`; const url = `/${owner}/${projectsId}/issues.json`;
axios.post(url, { axios.post(url, {
@ -172,6 +163,7 @@ class order_form extends Component {
start_date: start_date, start_date: start_date,
due_date: due_date, due_date: due_date,
issue_type: issue_type, issue_type: issue_type,
receivers_login:atWhoLoginList,
}).then((result) => { }).then((result) => {
if (result && result.data.id) { if (result && result.data.id) {
this.props.showNotification("任务创建成功!"); this.props.showNotification("任务创建成功!");
@ -198,6 +190,7 @@ class order_form extends Component {
due_date: due_date, due_date: due_date,
issue_type: issue_type, issue_type: issue_type,
...values, ...values,
receivers_login:atWhoLoginList,
}).then((result) => { }).then((result) => {
if (result) { if (result) {
this.props.history.push(`/${owner}/${projectsId}/issues/${orderId}`); this.props.history.push(`/${owner}/${projectsId}/issues/${orderId}`);

View File

@ -65,15 +65,6 @@ class comments extends Component {
const { owner } = this.props.match.params; 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`; const url = `/issues/${orderId}/journals.json`;
axios axios
.post(url, { .post(url, {
@ -82,6 +73,7 @@ class comments extends Component {
issue_id: orderId, issue_id: orderId,
attachment_ids: fileList, attachment_ids: fileList,
parent_id: reply_id, parent_id: reply_id,
receivers_login:atWhoLoginList,
}) })
.then((result) => { .then((result) => {
if (result && result.data.status === 0) { if (result && result.data.status === 0) {

View File

@ -9,7 +9,7 @@
/*md编辑器中输入@弹出可选人列表样式*/ /*md编辑器中输入@弹出可选人列表样式*/
.at_who_list{ .at_who_list{
position: absolute; position: absolute;
z-index: 99; z-index: 100;
width: 180px; width: 180px;
max-height: 160px; max-height: 160px;
background: #FFFFFF; background: #FFFFFF;
@ -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;
} }
.at_who:hover{ .active{
background: #F3F4F6; background: #F3F4F6;
} }
.at_who img{ .at_who img{
@ -34,4 +34,8 @@
height:30px; height:30px;
border-radius:50%; border-radius:50%;
margin-right: 10px; margin-right: 10px;
}
.blur_atWho{
position: absolute;
top: -100px;
} }

View File

@ -2,6 +2,7 @@ import React, { Fragment, useEffect, useRef, useState } from 'react';
import { getUploadActionUrl, getUrl } from 'educoder'; import { getUploadActionUrl, getUrl } from 'educoder';
import ResizeObserver from 'resize-observer-polyfill'; import ResizeObserver from 'resize-observer-polyfill';
import { getImageUrl } from 'educoder'; import { getImageUrl } from 'educoder';
import axios from 'axios';
import '../../courses/css/Courses.css'; import '../../courses/css/Courses.css';
import './css/TPMchallengesnew.css'; import './css/TPMchallengesnew.css';
import 'codemirror/lib/codemirror.css'; import 'codemirror/lib/codemirror.css';
@ -80,16 +81,23 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
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([]);
const [users, setUsers] = useState([]);
const atWhoLoginList = useRef([]); const atWhoLoginList = useRef([]);
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}`;
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"}, useEffect(()=>{
{image_url: "system/lets/letter_avatars/2/E/122_185_146/120.png",login: "Eo9ygbqns",profile_completed: false,user_id: 84963,username: "Eo9ygbqns"}, isCanAtme && axios.get('/users/list.json',{
{image_url: "system/lets/letter_avatars/2/P/238_117_19/120.png",login: "postwoman",profile_completed: true,user_id: 84961,username: "PostWoman"}, params: {
{image_url: "system/lets/letter_avatars/2/X/70_163_90/120.png",login: "xuzhun",profile_completed: false,user_id: 89516,username: "徐准"}, search: 'admin',
{image_url: "system/lets/letter_avatars/2/X/70_163_90/120.png",login: "Es5ghtfik",profile_completed: false,user_id: 89516,username: "徐准1"}] },
}).then(response=>{
if(response && response.status === 200){
setUsers(response.data.users);
}
})
},[])
function onLayout() { function onLayout() {
let ro; let ro;
@ -116,10 +124,10 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
const line = cursor.line;//行 const line = cursor.line;//行
const ch = cursor.ch;//列 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}); cm.replaceRange(username+" ",{line,ch},{line,ch});
//鼠标聚焦到此行的最后 //鼠标聚焦
editorInstance.focus(); cm.focus();
editorInstance.setCursor({line,ch:ch+username.length+1});
//将此user的login存储到atWhoLoginList集合中 //将此user的login存储到atWhoLoginList集合中
const list = new Set(atWhoLoginList.current); const list = new Set(atWhoLoginList.current);
users.map((item)=>{ users.map((item)=>{
@ -129,6 +137,92 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
setAtWhoLoginListState(Array.from(list)); 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<atWhoDivs.length;i++){
atWhoDivs[i].className === "at_who active" && (index = i);
}
if(e.key === "ArrowUp" && index > 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(()=>{ useEffect(()=>{
console.log('@谁列表发生变化,atWhoLoginList.current',atWhoLoginList.current,'atWhoLoginListState: ',atWhoLoginListState); console.log('@谁列表发生变化,atWhoLoginList.current',atWhoLoginList.current,'atWhoLoginListState: ',atWhoLoginListState);
changeAtWhoLoginList && changeAtWhoLoginList(atWhoLoginListState); changeAtWhoLoginList && changeAtWhoLoginList(atWhoLoginListState);
@ -138,8 +232,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)}}> <div key={key} className="at_who" 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>
) )
@ -153,10 +247,9 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
useEffect(()=>{ useEffect(()=>{
//当atWhoVisible为true的时候失焦监听上下和enter键 //当atWhoVisible为true的时候失焦监听上下和enter键
if(atWhoVisible && editorInstance && editorInstance.cm){ if(atWhoVisible){
console.log('弹框啦'); document.activeElement.id !== "blur_atWho" && document.getElementById("blur_atWho").focus();
//焦点转移到非monaco-editer编辑器上 document.addEventListener("keydown",atWhoKeyDown());
editorInstance.cm.addEventListener('blur',()=>{})
} }
},[atWhoVisible]) },[atWhoVisible])
@ -260,52 +353,10 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
//isCanAtme:只有issue和合并请求以及评论部分可以@他人操作 //isCanAtme:只有issue和合并请求以及评论部分可以@他人操作
//当光标或选中内容时触发绑定@事件 //当光标或选中内容时触发绑定@事件
isCanAtme && editorInstance.cm.on("focus", () => { isCanAtme && editorInstance.cm.on("focus", () => {
document.onkeydown = (e) => { document.addEventListener("keydown", mdKeyDown());
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));
}
})
}
}
};
}); });
editorInstance.cm.on("blur", () => { isCanAtme && editorInstance.cm.on("blur", () => {
document.onkeydown = null ; document.removeEventListener("keydown",mdKeyDown());
}); });
editorInstance.cm.on("change", (cm) => { editorInstance.cm.on("change", (cm) => {
onChange && onChange(cm.getValue()); onChange && onChange(cm.getValue());
@ -428,6 +479,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
<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} {atWhoVisible && atWhoList}
</div> </div>
</div> </div>