Merge pull request 'fix 合并主干开发分支' (#267) from develop into gitlink_server
This commit is contained in:
commit
8c66d817b5
|
@ -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>
|
||||||
|
|
|
@ -61,7 +61,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
|
||||||
<li className={pathname==="pulls" ? "active" : ""}>
|
<li className={pathname==="pulls" ? "active" : ""}>
|
||||||
<Link to={{ pathname: `/${owner}/${projectsId}/pulls`, state }}>
|
<Link to={{ pathname: `/${owner}/${projectsId}/pulls`, state }}>
|
||||||
<i className={"iconfont icon-hebingqingqiu1 color-grey-3 mr5 font-14"}></i>
|
<i className={"iconfont icon-hebingqingqiu1 color-grey-3 mr5 font-14"}></i>
|
||||||
<span>合并请求</span>
|
<span>合并请求(PR)</span>
|
||||||
{projectDetail && projectDetail.pull_requests_count ? <span className="num">{numFormat(projectDetail.pull_requests_count)}</span> : ""}
|
{projectDetail && projectDetail.pull_requests_count ? <span className="num">{numFormat(projectDetail.pull_requests_count)}</span> : ""}
|
||||||
</Link>
|
</Link>
|
||||||
</li>:""
|
</li>:""
|
||||||
|
@ -71,7 +71,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
|
||||||
<li className={pathname === "wiki" ? "active" : ""}>
|
<li className={pathname === "wiki" ? "active" : ""}>
|
||||||
<Link to={{ pathname: `/${owner}/${projectsId}/wiki`, state }}>
|
<Link to={{ pathname: `/${owner}/${projectsId}/wiki`, state }}>
|
||||||
<i className={"iconfont icon-a-wikiicon1 color-grey-3 mr5 font-14"}></i>
|
<i className={"iconfont icon-a-wikiicon1 color-grey-3 mr5 font-14"}></i>
|
||||||
<span>Wiki</span>
|
<span>维基(Wiki)</span>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
|
||||||
<li className={pathname==="devops" ? "active" : ""}>
|
<li className={pathname==="devops" ? "active" : ""}>
|
||||||
{/* <Link to={{ pathname: `/${owner}/${projectsId}/devops${open_devops ? `/dispose`:""}`, state }}> */}
|
{/* <Link to={{ pathname: `/${owner}/${projectsId}/devops${open_devops ? `/dispose`:""}`, state }}> */}
|
||||||
<Link to={{ pathname: `/${owner}/${projectsId}/devops`, state:{...state,open_devops} }}>
|
<Link to={{ pathname: `/${owner}/${projectsId}/devops`, state:{...state,open_devops} }}>
|
||||||
<i className="iconfont icon-gongzuoliuicon font-13 mr5 color-grey-3"></i>工作流(beta版)
|
<i className="iconfont icon-gongzuoliuicon font-13 mr5 color-grey-3"></i>引擎(Engine)
|
||||||
{projectDetail && projectDetail.ops_count ? <span>{projectDetail.ops_count}</span> : ""}
|
{projectDetail && projectDetail.ops_count ? <span>{projectDetail.ops_count}</span> : ""}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -18,7 +18,7 @@ function Files({ data,history,owner,projectsId , parentsSha }){
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
document.addEventListener('click',()=>{setIsOpen(false)})
|
document.addEventListener('click',()=>{setIsOpen(false)})
|
||||||
})
|
},[])
|
||||||
|
|
||||||
function showDown(flag,index,isBin){
|
function showDown(flag,index,isBin){
|
||||||
if(!isBin){
|
if(!isBin){
|
||||||
|
|
|
@ -54,7 +54,7 @@ class UpdateMerge extends Component {
|
||||||
const { data, isSpin, pull, merge } = this.state;
|
const { data, isSpin, pull, merge } = this.state;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="main">
|
<div className="main updateMerge">
|
||||||
<Spin spinning={isSpin}>
|
<Spin spinning={isSpin}>
|
||||||
{" "}
|
{" "}
|
||||||
{data ? (
|
{data ? (
|
||||||
|
|
|
@ -223,3 +223,7 @@ form .ant-cascader-picker, form .ant-select {
|
||||||
max-width: 450px;
|
max-width: 450px;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
/* 距离底部加大 @列表被遮挡 */
|
||||||
|
.updateMerge{
|
||||||
|
margin: 30px auto 60px;
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ class MergeForm extends Component {
|
||||||
issue_tags: undefined,
|
issue_tags: undefined,
|
||||||
issue_versions: undefined,
|
issue_versions: undefined,
|
||||||
issue_priories: undefined,
|
issue_priories: undefined,
|
||||||
|
atWhoLoginList:undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +147,7 @@ class MergeForm extends Component {
|
||||||
} else {
|
} else {
|
||||||
values.issue_tag_ids = [];
|
values.issue_tag_ids = [];
|
||||||
}
|
}
|
||||||
const { desc } = this.state;
|
const { desc , atWhoLoginList } = this.state;
|
||||||
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, {
|
||||||
|
@ -158,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) {
|
||||||
|
@ -189,6 +191,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) {
|
||||||
|
@ -225,6 +228,13 @@ class MergeForm extends Component {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//合并请求中at谁列表(存储:login)
|
||||||
|
changeAtWhoLoginList = (loginList) =>{
|
||||||
|
this.setState({
|
||||||
|
atWhoLoginList:loginList,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { merge_type } = this.props;
|
const { merge_type } = this.props;
|
||||||
const { getFieldDecorator } = this.props.form;
|
const { getFieldDecorator } = this.props.form;
|
||||||
|
@ -273,6 +283,10 @@ class MergeForm extends Component {
|
||||||
mdID={"merge-new-description"}
|
mdID={"merge-new-description"}
|
||||||
initValue={desc}
|
initValue={desc}
|
||||||
onChange={this.onContentChange}
|
onChange={this.onContentChange}
|
||||||
|
isCanAtme = {true}
|
||||||
|
changeAtWhoLoginList = {this.changeAtWhoLoginList}
|
||||||
|
owner = {owner}
|
||||||
|
projectsId = {projectsId}
|
||||||
></MDEditor>
|
></MDEditor>
|
||||||
<p className="clearfix mt20">
|
<p className="clearfix mt20">
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -35,7 +35,8 @@ class order_form extends Component {
|
||||||
get_attachments: undefined,
|
get_attachments: undefined,
|
||||||
show_token: false,
|
show_token: false,
|
||||||
cannot_edit: false,
|
cannot_edit: false,
|
||||||
issue_current_user: true
|
issue_current_user: true,
|
||||||
|
atWhoLoginList:undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
componentDidUpdate=(prevPros)=>{
|
componentDidUpdate=(prevPros)=>{
|
||||||
|
@ -152,7 +153,7 @@ class order_form extends Component {
|
||||||
if (values.issue_tag_ids.length > 0) {
|
if (values.issue_tag_ids.length > 0) {
|
||||||
values.issue_tag_ids = [values.issue_tag_ids];
|
values.issue_tag_ids = [values.issue_tag_ids];
|
||||||
}
|
}
|
||||||
const { description, start_date, due_date, issue_type } = this.state;
|
const { description, start_date, due_date, issue_type , atWhoLoginList } = this.state;
|
||||||
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, {
|
||||||
|
@ -162,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("任务创建成功!");
|
||||||
|
@ -188,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}`);
|
||||||
|
@ -221,6 +224,14 @@ class order_form extends Component {
|
||||||
description: value,
|
description: value,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//issue中at谁列表(存储:login)
|
||||||
|
changeAtWhoLoginList = (loginList) =>{
|
||||||
|
this.setState({
|
||||||
|
atWhoLoginList:loginList,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 修改开始时间
|
// 修改开始时间
|
||||||
changeBeginTime = (start_date, value) => {
|
changeBeginTime = (start_date, value) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -329,6 +340,10 @@ class order_form extends Component {
|
||||||
mdID={"order-new-description"}
|
mdID={"order-new-description"}
|
||||||
initValue={description}
|
initValue={description}
|
||||||
onChange={this.onContentChange}
|
onChange={this.onContentChange}
|
||||||
|
isCanAtme = {true}
|
||||||
|
changeAtWhoLoginList = {this.changeAtWhoLoginList}
|
||||||
|
owner = {owner}
|
||||||
|
projectsId = {projectsId}
|
||||||
></MDEditor>
|
></MDEditor>
|
||||||
</div>
|
</div>
|
||||||
{get_attachments && get_attachments.length > 0 ? (
|
{get_attachments && get_attachments.length > 0 ? (
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -13,9 +13,9 @@ const menu = [
|
||||||
{name:"主页",index:"home"},
|
{name:"主页",index:"home"},
|
||||||
{name:"代码库",index:"code"},
|
{name:"代码库",index:"code"},
|
||||||
{name:"易修 (Issue)",index:"issues"},
|
{name:"易修 (Issue)",index:"issues"},
|
||||||
{name:"合并请求",index:"pulls"},
|
{name:"合并请求 (PR)",index:"pulls"},
|
||||||
{name:"Wiki",index:"wiki"},
|
{name:"维基 (Wiki)",index:"wiki"},
|
||||||
{name:"工作流(beta版)",index:"devops"},
|
{name:"引擎 (Engine)",index:"devops"},
|
||||||
// {name:"资源库",index:"resources"},
|
// {name:"资源库",index:"resources"},
|
||||||
{name:"里程碑",index:"versions"},
|
{name:"里程碑",index:"versions"},
|
||||||
{name:"动态",index:"activity"},
|
{name:"动态",index:"activity"},
|
||||||
|
|
|
@ -27,7 +27,7 @@ class ForkUsers extends Component {
|
||||||
});
|
});
|
||||||
const { projectsId , owner } = this.props.match.params;
|
const { projectsId , owner } = this.props.match.params;
|
||||||
|
|
||||||
const url = `/${owner}/${projectsId}/members.json`;
|
const url = `/${owner}/${projectsId}/forks.json`;
|
||||||
axios
|
axios
|
||||||
.get(url, {
|
.get(url, {
|
||||||
params: {
|
params: {
|
||||||
|
|
|
@ -30,6 +30,7 @@ class comments extends Component {
|
||||||
reply_id: undefined,
|
reply_id: undefined,
|
||||||
reply_content: undefined,
|
reply_content: undefined,
|
||||||
new_journal_id: undefined,
|
new_journal_id: undefined,
|
||||||
|
atWhoLoginList:undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +52,7 @@ class comments extends Component {
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.form.validateFieldsAndScroll((err, values) => {
|
this.props.form.validateFieldsAndScroll((err, values) => {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
const {
|
const {
|
||||||
|
@ -60,10 +62,12 @@ class comments extends Component {
|
||||||
orderId,
|
orderId,
|
||||||
reply_id,
|
reply_id,
|
||||||
is_reply,
|
is_reply,
|
||||||
|
atWhoLoginList,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const url = `/issues/${orderId}/journals.json`;
|
|
||||||
|
|
||||||
|
|
||||||
|
const url = `/issues/${orderId}/journals.json`;
|
||||||
axios
|
axios
|
||||||
.post(url, {
|
.post(url, {
|
||||||
...values,
|
...values,
|
||||||
|
@ -71,6 +75,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) {
|
||||||
|
@ -248,18 +253,29 @@ class comments extends Component {
|
||||||
onContentChange = (value) => {
|
onContentChange = (value) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
this.setState({
|
this.setState({
|
||||||
content: value,
|
|
||||||
quillFlag: false,
|
quillFlag: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
this.setState({
|
||||||
|
content: value,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
replyContentChange = (value) => {
|
replyContentChange = (value) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
this.setState({
|
this.setState({
|
||||||
reply_content: value,
|
|
||||||
quillFlag: false,
|
quillFlag: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
this.setState({
|
||||||
|
reply_content: value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//评论中at谁列表(存储:login)
|
||||||
|
changeAtWhoLoginList = (loginList) =>{
|
||||||
|
this.setState({
|
||||||
|
atWhoLoginList:loginList,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onRef = (ref) => {
|
onRef = (ref) => {
|
||||||
|
@ -308,6 +324,7 @@ class comments extends Component {
|
||||||
new_journal_id,
|
new_journal_id,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const { current_user, only_show_content } = this.props;
|
const { current_user, only_show_content } = this.props;
|
||||||
|
const { projectsId ,owner } = this.props.match.params;
|
||||||
|
|
||||||
const new_comment = (is_reply, item_id) => {
|
const new_comment = (is_reply, item_id) => {
|
||||||
return (
|
return (
|
||||||
|
@ -339,6 +356,10 @@ class comments extends Component {
|
||||||
onChange={
|
onChange={
|
||||||
is_reply ? this.replyContentChange : this.onContentChange
|
is_reply ? this.replyContentChange : this.onContentChange
|
||||||
}
|
}
|
||||||
|
isCanAtme = {true}
|
||||||
|
changeAtWhoLoginList = {this.changeAtWhoLoginList}
|
||||||
|
owner = {owner}
|
||||||
|
projectsId = {projectsId}
|
||||||
></MDEditor>
|
></MDEditor>
|
||||||
<p className="quillFlag">
|
<p className="quillFlag">
|
||||||
{quillFlag && <span className="">请输入评论内容</span>}
|
{quillFlag && <span className="">请输入评论内容</span>}
|
||||||
|
|
|
@ -5,3 +5,38 @@
|
||||||
color:#5091FF !important;
|
color:#5091FF !important;
|
||||||
border-color: #5091FF !important;
|
border-color: #5091FF !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*md编辑器中输入@弹出可选人列表样式*/
|
||||||
|
.at_who_list{
|
||||||
|
position: absolute;
|
||||||
|
z-index: 100;
|
||||||
|
width: 180px;
|
||||||
|
max-height: 160px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
box-shadow: 0px 4px 8px 2px rgba(212, 212, 212, 0.5);
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.at_who{
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid rgba(212, 212, 212, 0.5);
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
.at_who.active{
|
||||||
|
background: #F3F4F6;
|
||||||
|
}
|
||||||
|
.at_who img{
|
||||||
|
width:30px;
|
||||||
|
height:30px;
|
||||||
|
border-radius:50%;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.at_who span{
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
|
@ -1,11 +1,12 @@
|
||||||
|
|
||||||
import React, { Fragment, useEffect, useRef, useState } from 'react';
|
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 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';
|
||||||
|
import './css/newquestion.css';
|
||||||
const $ = window.$
|
const $ = window.$
|
||||||
|
|
||||||
const mdIcons = ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "inline-latex", "latex", '|', "image", "table", '|', "line-break", "watch", "clear"];
|
const mdIcons = ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "inline-latex", "latex", '|', "image", "table", '|', "line-break", "watch", "clear"];
|
||||||
|
@ -39,7 +40,6 @@ function md_rec_data(k, mdu, id) {
|
||||||
}
|
}
|
||||||
window.md_rec_data = md_rec_data;
|
window.md_rec_data = md_rec_data;
|
||||||
|
|
||||||
|
|
||||||
function md_elocalStorage(editor, mdu, id) {
|
function md_elocalStorage(editor, mdu, id) {
|
||||||
let oc = window.sessionStorage.getItem('content' + mdu)
|
let oc = window.sessionStorage.getItem('content' + mdu)
|
||||||
if (oc !== null && oc !== editor.getValue()) {
|
if (oc !== null && oc !== editor.getValue()) {
|
||||||
|
@ -74,16 +74,38 @@ function md_elocalStorage(editor, mdu, id) {
|
||||||
return tid
|
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 }) => {
|
|
||||||
|
|
||||||
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 [atWhoLoginListState, setAtWhoLoginListState] = useState([]);
|
||||||
|
//调用member.json接口获取到的用户列表
|
||||||
|
const [users, setUsers] = useState([]);
|
||||||
|
//可以@的全部用户
|
||||||
|
const [allUsers, setAllUsers] = useState([]);
|
||||||
|
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(()=>{
|
||||||
|
//请求members接口获取全部可@列表
|
||||||
|
isCanAtme && axios.get(`/${owner}/${projectsId}/members.json`).then(response=>{
|
||||||
|
if(response.data.total_count !== 0){
|
||||||
|
setAllUsers(response.data.users);
|
||||||
|
setUsers(response.data.users);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
//点击其他地方关闭弹框
|
||||||
|
document.addEventListener('click',()=>{
|
||||||
|
atWhoVisibleRef.current = false;
|
||||||
|
setAtWhoVisible(false);
|
||||||
|
})
|
||||||
|
},[])
|
||||||
|
|
||||||
function onLayout() {
|
function onLayout() {
|
||||||
let ro;
|
let ro;
|
||||||
if (editorEl.current) {
|
if (editorEl.current) {
|
||||||
|
@ -101,6 +123,96 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
||||||
return ro;
|
return ro;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selectAtWho(username){
|
||||||
|
atWhoVisibleRef.current = false;
|
||||||
|
setAtWhoVisible(false);
|
||||||
|
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("@");
|
||||||
|
let selectUserLogin = undefined;
|
||||||
|
users.map((item)=>{
|
||||||
|
item.username === username && (selectUserLogin = item.login);
|
||||||
|
})
|
||||||
|
//替换内容
|
||||||
|
cm.replaceRange("[@"+username+"]"+`(/${selectUserLogin}) `,{line,ch:startIndex},{line,ch});
|
||||||
|
//鼠标聚焦
|
||||||
|
cm.focus();
|
||||||
|
//将此user的login存储到atWhoLoginList集合中
|
||||||
|
const list = new Set(atWhoLoginList.current);
|
||||||
|
list.add(selectUserLogin);
|
||||||
|
atWhoLoginList.current = Array.from(list);
|
||||||
|
setAtWhoLoginListState(Array.from(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseOver(key){
|
||||||
|
document.getElementsByClassName("at_who active")[0] && (document.getElementsByClassName("at_who active")[0].className="at_who");
|
||||||
|
document.getElementsByClassName("at_who")[key] && (document.getElementsByClassName("at_who")[key].className="at_who active");
|
||||||
|
}
|
||||||
|
|
||||||
|
//markdown编辑器中输入的键盘监听事件
|
||||||
|
function mdKeyDown(e){
|
||||||
|
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 = placeholder === "添加评论..." ? 159: placeholder === "请输入合并请求的描述..." ? 172:62;
|
||||||
|
const newLeft = placeholder === "添加评论..." ? 80: 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){
|
||||||
|
const codemirror = editorInstance.cm;
|
||||||
|
let value = codemirror.getValue();
|
||||||
|
//处理初始内容就自带@谁的情况
|
||||||
|
if(initValue){
|
||||||
|
const del = [];
|
||||||
|
allUsers.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}](/${item.login})`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
del.length!=0 && del.map(str=>{
|
||||||
|
value = value.replace(str,"");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//判断value是否包含@符号
|
||||||
|
value.indexOf("@") != -1 && allUsers.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));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
changeAtWhoLoginList && changeAtWhoLoginList(atWhoLoginListState);
|
||||||
|
},[atWhoLoginListState])
|
||||||
|
|
||||||
|
const atWhoList = (
|
||||||
|
<div className="at_who_list" id="at_who_list" >
|
||||||
|
{users && users.map((item,key)=>{
|
||||||
|
return(
|
||||||
|
<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>}
|
||||||
|
<span>{item.username}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (editorInstance) {
|
if (editorInstance) {
|
||||||
return
|
return
|
||||||
|
@ -183,6 +295,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
|
||||||
|
@ -198,19 +373,138 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
||||||
if (!noStorage) {
|
if (!noStorage) {
|
||||||
tid = md_elocalStorage(editorInstance, `MDEditor__${containerId}`, containerId)
|
tid = md_elocalStorage(editorInstance, `MDEditor__${containerId}`, containerId)
|
||||||
}
|
}
|
||||||
if (onChange) {
|
//isCanAtme:只有issue和合并请求以及评论部分可以@他人操作
|
||||||
editorInstance.cm.on('change', (cm) => {
|
//绑定@事件
|
||||||
// if(forMember){
|
isCanAtme && editorInstance.cm.on("focus", () => {
|
||||||
// document.onkeydown = (e) => {
|
document.addEventListener("keydown", mdKeyDown);
|
||||||
// if (e.key === "@") {
|
});
|
||||||
// // 输入@键后在对应的位置显示可选的项目成员
|
isCanAtme && editorInstance.cm.on("blur", () => {
|
||||||
|
document.removeEventListener("keydown",mdKeyDown);
|
||||||
|
});
|
||||||
|
editorInstance.cm.on("change", (cm) => {
|
||||||
|
//调用父组件的onchange方法,将输入内容传入父级组件
|
||||||
|
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){
|
||||||
// }
|
const codemirror = editorInstance.cm;
|
||||||
onChange(cm.getValue())
|
//startValue:触发change方法时的内容,value:处理了初始内容带@用户的情况
|
||||||
})
|
let startValue = codemirror.getValue();
|
||||||
}
|
let value = codemirror.getValue();
|
||||||
|
//处理初始内容就自带@谁的情况
|
||||||
|
if(initValue){
|
||||||
|
const del = [];
|
||||||
|
allUsers.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}](/${item.login})`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
del.length!=0 && del.map(str=>{
|
||||||
|
value = value.replace(str,"");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//以username为主键,login为value的map集合
|
||||||
|
let atWhoMap = new Map();
|
||||||
|
Array.from(atWhoLoginList.current).map(item=>{
|
||||||
|
allUsers.map(i=>{
|
||||||
|
if(i.login === item){
|
||||||
|
atWhoMap.set(i.username,i.login);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const cursor = codemirror.doc.getCursor();
|
||||||
|
const line = cursor.line;
|
||||||
|
const ch = cursor.ch;
|
||||||
|
//处理全部内容中不包含“@”的情况
|
||||||
|
if(value.indexOf("@") === -1){
|
||||||
|
//markdown嵌套的链接删掉
|
||||||
|
// Array.from(atWhoMap.keys()).map(username=>{
|
||||||
|
// startValue = startValue.replaceAll(`[${username}](/${atWhoMap.get(username)}) `,username);
|
||||||
|
// })
|
||||||
|
//替换全部内容
|
||||||
|
// codemirror.setValue(startValue);
|
||||||
|
//全部内容已经有要@的列表,但是没有@符号 -> 清空@集合
|
||||||
|
atWhoLoginList.current = [];
|
||||||
|
setAtWhoLoginListState([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//截取第一个字符到光标的内容
|
||||||
|
const curAfterCont = codemirror.getRange({line,ch:0},{line,ch});
|
||||||
|
const content = codemirror.getLine(line);
|
||||||
|
//处理光标所在行 有“@”的情况
|
||||||
|
if(content && content.indexOf("@") !== -1){
|
||||||
|
Array.from(atWhoMap.keys()).map(username=>{
|
||||||
|
//判断content是不是以列表中的某个username结尾
|
||||||
|
const userCont = `[@${username}](/${atWhoMap.get(username)})`;
|
||||||
|
//删除空格->选中@用户区域
|
||||||
|
if(curAfterCont.endsWith(userCont)){
|
||||||
|
codemirror.setSelection({line,ch:curAfterCont.lastIndexOf("@")-1},{line,ch});
|
||||||
|
}
|
||||||
|
//处理已经有@列表但是value中不包含完整[@用户名](/login)的情况
|
||||||
|
if(value.indexOf(userCont)===-1){
|
||||||
|
// //markdown嵌套的链接删掉,删[]、()的情况不用处理,markdown会自动认为不是链接
|
||||||
|
// //找到[和)的index,将区域内容替换成[]包裹的内容
|
||||||
|
// //光标之后的内容
|
||||||
|
// const curLeterCont = codemirror.getRange({line,ch},{line,ch:content.length});
|
||||||
|
// console.log('光标之后的内容curLeterCont',curLeterCont);
|
||||||
|
// //删除用户名 -> ]在curLeterCont中
|
||||||
|
// //删除login -> ]在curAfterCont中
|
||||||
|
// const a = curAfterCont.lastIndexOf('[');
|
||||||
|
// const b = curLeterCont.indexOf(')')
|
||||||
|
// const c = curLeterCont.indexOf(']') === -1 ? curAfterCont.lastIndexOf(']') : curLeterCont.indexOf(']')+curAfterCont.length;
|
||||||
|
// console.log('[',a,')',b,']',c);
|
||||||
|
// const newCont = codemirror.getRange({line,ch:a+1},{line,ch:c});
|
||||||
|
// console.log('newCont',newCont);
|
||||||
|
// codemirror.replaceRange(newCont,{line,ch:a-1},{line,ch:b+curAfterCont.length+1})
|
||||||
|
|
||||||
|
//符合情况->踢掉这个人 不给他发消息
|
||||||
|
const list = new Set(atWhoLoginList.current);
|
||||||
|
list.delete(atWhoMap.get(username));
|
||||||
|
atWhoLoginList.current = Array.from(list);
|
||||||
|
setAtWhoLoginListState(Array.from(list));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
//处理所在行没有“@”的情况
|
||||||
|
Array.from(atWhoMap.keys()).map(username=>{
|
||||||
|
const userCont = `[@${username}](/${atWhoMap.get(username)})`;
|
||||||
|
if(value.indexOf(userCont)===-1){
|
||||||
|
//符合情况->踢掉这个人 不给他发消息
|
||||||
|
const list = new Set(atWhoLoginList.current);
|
||||||
|
list.delete(atWhoMap.get(username));
|
||||||
|
atWhoLoginList.current = Array.from(list);
|
||||||
|
setAtWhoLoginListState(Array.from(list));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
ro = onLayout()
|
ro = onLayout()
|
||||||
return () => {
|
return () => {
|
||||||
if (!noStorage) {
|
if (!noStorage) {
|
||||||
|
@ -271,7 +565,8 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div ref={editorEl} className={`df ${className} ${imageExpand && 'editormd-image-click-expand'} `} >
|
{atWhoVisible && atWhoList}
|
||||||
|
<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>
|
||||||
|
|
Loading…
Reference in New Issue