markdown #引用

This commit is contained in:
caishi 2023-03-16 17:04:52 +08:00
parent 4910bbcfea
commit 64d19a051a
3 changed files with 107 additions and 13 deletions

View File

@ -86,6 +86,7 @@ function NewPanel(props,ref){
onChange={onContentChange}
className="mt20"
isCanAtme = {true}
isQuoteIssue={true}
owner = {owner}
projectsId = {projectsId}
changeAtWhoLoginList = {changeAtWhoLoginList}

View File

@ -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;

View File

@ -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>