forked from Gitlink/forgeplus-react
atwho列表
This commit is contained in:
parent
3498390974
commit
8021d96cd8
|
@ -148,14 +148,6 @@ class MergeForm extends Component {
|
||||||
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) {
|
||||||
|
|
|
@ -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}`);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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{
|
||||||
|
@ -35,3 +35,7 @@
|
||||||
border-radius:50%;
|
border-radius:50%;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
.blur_atWho{
|
||||||
|
position: absolute;
|
||||||
|
top: -100px;
|
||||||
|
}
|
|
@ -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,7 +232,7 @@ 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>
|
||||||
|
|
Loading…
Reference in New Issue