markdown #引用
This commit is contained in:
parent
4910bbcfea
commit
64d19a051a
|
|
@ -86,6 +86,7 @@ function NewPanel(props,ref){
|
|||
onChange={onContentChange}
|
||||
className="mt20"
|
||||
isCanAtme = {true}
|
||||
isQuoteIssue={true}
|
||||
owner = {owner}
|
||||
projectsId = {projectsId}
|
||||
changeAtWhoLoginList = {changeAtWhoLoginList}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,41 @@
|
|||
color:#5091FF !important;
|
||||
border-color: #5091FF !important;
|
||||
}
|
||||
|
||||
.quoteDiv{
|
||||
width: 240px!important;
|
||||
max-height: 257px!important;
|
||||
}
|
||||
.quoteDiv > li{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
padding:0px 10px;
|
||||
border-bottom: 1px solid rgba(212, 212, 212, 0.5);
|
||||
}
|
||||
.quoteDiv > li.active{
|
||||
background-color: #F3F4F6;
|
||||
}
|
||||
.quoteDiv > li:last-child{
|
||||
border-bottom: none;
|
||||
}
|
||||
.issueIndex{
|
||||
background-color: rgba(213, 220, 246, 0.36);
|
||||
border-radius: 4px;
|
||||
height: 19px;
|
||||
line-height: 19px;
|
||||
color: #666666;
|
||||
text-align: center;
|
||||
float: left;
|
||||
padding: 0px 2px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
margin-right: 7px;
|
||||
}
|
||||
.issueName{
|
||||
flex:1
|
||||
}
|
||||
/*md编辑器中输入@弹出可选人列表样式*/
|
||||
.at_who_list{
|
||||
.at_who_list,.quoteDiv{
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
width: 180px;
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ function md_elocalStorage(editor, mdu, id) {
|
|||
return tid
|
||||
}
|
||||
|
||||
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 , isQuoteIssue = false , changeAtWhoLoginList, owner, projectsId }) => {
|
||||
|
||||
const editorEl = useRef();
|
||||
const resizeBarEl = useRef();
|
||||
|
|
@ -91,6 +91,10 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
|||
const editorBodyId = `mdEditors_${mdID}`;
|
||||
const tipId = `e_tips_mdEditor_${mdID}`;
|
||||
|
||||
// quote相关
|
||||
const [issues, setIssues ] = useState([]);
|
||||
const [ quoteVisible , setQuoteVisible ] = useState(false);
|
||||
|
||||
useEffect(()=>{
|
||||
//请求members接口获取全部可@列表
|
||||
isCanAtme && axios.get(`/${owner}/${projectsId}/members.json`).then(response=>{
|
||||
|
|
@ -104,8 +108,55 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
|||
atWhoVisibleRef.current = false;
|
||||
setAtWhoVisible(false);
|
||||
})
|
||||
|
||||
isQuoteIssue && getIssueList();
|
||||
},[])
|
||||
|
||||
// 请求issues接口获取全部可quote的issue列表
|
||||
function getIssueList(keywords){
|
||||
axios.get(`/v1/${owner}/${projectsId}/issues`,{params:{
|
||||
only_name:true,keywords
|
||||
}}).then(result=>{
|
||||
if(result && result.data.total_count!== 0){
|
||||
setIssues(result.data.issues);
|
||||
}
|
||||
})
|
||||
}
|
||||
// # 引用的弹框div
|
||||
const QuoteDiv = (
|
||||
<div id="quoteDiv" className="quoteDiv">
|
||||
{
|
||||
issues && issues.length>0 && issues.map((i,k)=>{
|
||||
return(
|
||||
<li className={`quote ${k === 0 && "active"}`} onClick={()=>selectQuoteIssue(i)} onMouseOver={()=>onMouseOverQuote(k)}>
|
||||
<span style={{minWidth:"40px"}}><span className="issueIndex" title={i.project_issues_index}>#{i.project_issues_index}</span></span>
|
||||
<span className="issueName task-hide">{i.subject}</span>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
// 点击选择要引用的issue
|
||||
function selectQuoteIssue(i){
|
||||
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("@");
|
||||
//替换内容
|
||||
cm.replaceRange(`[#${i.project_issues_index}](/${owner}/${projectsId}/issues/${i.project_issues_index}) `,{line,ch:startIndex},{line,ch});
|
||||
//鼠标聚焦
|
||||
cm.focus();
|
||||
setQuoteVisible(false);
|
||||
// 保存引用的issue
|
||||
}
|
||||
function onMouseOverQuote(key){
|
||||
document.getElementsByClassName("quote active")[0] && (document.getElementsByClassName("quote active")[0].className="quote");
|
||||
document.getElementsByClassName("quote")[key] && (document.getElementsByClassName("quote")[key].className="quote active");
|
||||
}
|
||||
|
||||
function onLayout() {
|
||||
let ro;
|
||||
if (editorEl.current) {
|
||||
|
|
@ -153,21 +204,20 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
|||
}
|
||||
|
||||
//markdown编辑器中输入的键盘监听事件
|
||||
function mdKeyDown(e){
|
||||
function mdKeyDown(e){ //获取光标位置
|
||||
const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style;
|
||||
//设置弹框位置
|
||||
const newTop = 62
|
||||
const newLeft = 20;
|
||||
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 = 62
|
||||
const newLeft = 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){
|
||||
if(e.ctrlKey && e.code === "KeyZ" && allUsers.length !== 0){
|
||||
const codemirror = editorInstance.cm;
|
||||
let value = codemirror.getValue();
|
||||
//处理初始内容就自带@谁的情况
|
||||
|
|
@ -194,6 +244,16 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
|||
}
|
||||
})
|
||||
}
|
||||
if(e.shiftKey && e.code === "Digit3"){
|
||||
// 输入#键后在对应的位置显示可选的项目issue
|
||||
setQuoteVisible(true);
|
||||
document.getElementById("quoteDiv").style.top = parseInt(cssStyle.getPropertyValue("top").replace("px","")) + newTop +"px";
|
||||
document.getElementById("quoteDiv").style.left = parseInt(cssStyle.getPropertyValue("left").replace("px",""))+newLeft+"px";
|
||||
}
|
||||
if(e.code==="Space"){
|
||||
// 输入# 后又输入空格:隐藏issue列表
|
||||
setQuoteVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
|
|
@ -374,10 +434,10 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
|||
}
|
||||
//isCanAtme:只有issue和合并请求以及评论部分可以@他人操作
|
||||
//绑定@事件
|
||||
isCanAtme && editorInstance.cm.on("focus", () => {
|
||||
(isCanAtme || isQuoteIssue) && editorInstance.cm.on("focus", () => {
|
||||
document.addEventListener("keydown", mdKeyDown);
|
||||
});
|
||||
isCanAtme && editorInstance.cm.on("blur", () => {
|
||||
(isCanAtme || isQuoteIssue) && editorInstance.cm.on("blur", () => {
|
||||
document.removeEventListener("keydown",mdKeyDown);
|
||||
});
|
||||
editorInstance.cm.on("change", (cm) => {
|
||||
|
|
@ -565,6 +625,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
|||
return (
|
||||
<Fragment>
|
||||
{atWhoVisible && atWhoList}
|
||||
{quoteVisible && QuoteDiv }
|
||||
<div ref={editorEl} className={`df editormd-editing ${className} ${imageExpand && 'editormd-image-click-expand'} `}>
|
||||
<div className={`edu-back-greyf5 radius4 editormd ${error ? 'error' : ''}`} id={containerId} >
|
||||
<textarea style={{ display: 'none' }} id={editorBodyId} name="content"></textarea>
|
||||
|
|
|
|||
Loading…
Reference in New Issue