合并请求-评论-使用issue的组件

This commit is contained in:
caishi 2026-01-22 14:30:15 +08:00
parent 94d459a639
commit 22ef5c374d
18 changed files with 2384 additions and 29 deletions

File diff suppressed because one or more lines are too long

View File

@ -12,8 +12,9 @@ import { Link } from 'react-router-dom';
import axios from 'axios';
function EditComment(props){
const {owner, projectsId, index} = props.match.params;
const {current_user, current_user:{login}, showLoginDialog, showNotification, reloadComment, cancelMd, parentId, replyId, updateId, showUserImg=true} = props;
const {owner, projectsId } = props.match.params;
const {current_user, current_user:{login}, showLoginDialog, showNotification, reloadComment, cancelMd, parentId, replyId, updateId, showUserImg=true , issueId} = props;
const index = props.match.params.index || issueId;
const [content, setContent] = useState(props.content);
const [quillFlag, setQuillFlag] = useState(false);
// loading

View File

@ -10,9 +10,11 @@ import CheckProfile from '../../../Component/ProfileModal/Profile';
import './list.scss';
import { Link } from 'react-router-dom';
// 2026-01-22 使issueIdissueid
function IssueCommentList(props){
const{history, history: {location}, reload, reloadComment, showNotification, current_user:{login, admin, image_url, user_id}, isManager, showLoginDialog, issueInfo:{author}} = props;
const {owner, projectsId, index} = props.match.params;
const{history, history: {location}, reload, reloadComment, showNotification, current_user:{login, admin, image_url, user_id}, isManager, showLoginDialog, issueInfo:{author},issueId} = props;
const {owner, projectsId } = props.match.params;
const index = props.match.params.index || issueId;
const [category, setCategory] = useState('comment');
const [journals, setJournals] = useState(undefined);
// /markdown1 2 3 4 5
@ -84,6 +86,8 @@ function IssueCommentList(props){
setJournalsCount(total_comment_journals_count);
setJournals(journals);
setSpin(false)
const { updateCommentsNum } = props;
updateCommentsNum && updateCommentsNum(total_comment_journals_count);
}
})
}, [reload, category, page])
@ -110,7 +114,7 @@ function IssueCommentList(props){
return(
<div className="commentListBox">
{/* 全部 / 评论 / 操作日志 */}
<div className={`${!login && journalsCount > 0 && 'pb15'}`}>
{!issueId && <div className={`${!login && journalsCount > 0 && 'pb15'}`}>
<div className='typeActionBox mt30 mb20'>
<Radio.Group onChange={(e)=>{setSpin(true);setPage(1);setCategory(e.target.value);history.push(history.pathname)}} value={category}>
<Radio value={'comment'} className='typeActionRadio font-14'>评论<span className='journalsCount font-13 ml5'>{journalsCount}</span></Radio>
@ -118,7 +122,7 @@ function IssueCommentList(props){
<Radio value={'all'} className='typeActionRadio font-14'>全部</Radio>
</Radio.Group>
</div>
</div>
</div>}
{/* 评论快速入口-仅有评论且登录时展示 */}
{login && category !== 'operate' && journalsCount > 0 && <div className={`pb30 ${journalsCount > 0 && 'pt15'}`}><div className='gotoComment'>点击<a className='ml5' href='#addComments'>添加评论</a></div></div>}
{/* 评论/操作日志 展示列表 */}
@ -220,7 +224,7 @@ function IssueCommentList(props){
canDelete={false}
/></div>}
</div>}
{showEdit === 5 && replyId === i.id && <div className='contentHtml mr20'><EditComment {...props} cancelMd={cancelMd} parentId={parentId} replyId={replyId} reloadComment={reloadComment}/></div>}
{showEdit === 5 && replyId === i.id && <div className='contentHtml mr20'><EditComment {...props} cancelMd={cancelMd} parentId={parentId} replyId={replyId} reloadComment={reloadComment} issueId={issueId}/></div>}
</div>})}
</div>
</div>}))}

View File

@ -155,6 +155,14 @@ const DevopsCard = Loadable({
loader: () => import('../DevOps/cardList'),
loading: Loading,
});
const Template=Loadable({
loader: () => import('../template/index'),
loading: Loading,
});
const TemplateEdit=Loadable({
loader: () => import('../template/new'),
loading: Loading,
});
/**
* permissionManager:管理员Reporter报告人员(只有读取权限)Developer开发人员除不能设置仓库信息外
*/
@ -941,12 +949,21 @@ class Detail extends Component {
(props) => (<MessageCount {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route path="/:owner/:projectsId/pulls"
render={
(props) => (<MergeIndexDetail {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route path="/:owner/:projectsId/:type/temp/:operate"
render={
(props) => (<TemplateEdit {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route path="/:owner/:projectsId/:type/temp"
render={
(props) => (<Template {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route path="/:owner/:projectsId/following"
render={
(props) => (<WatchUsers {...this.props} {...props} {...this.state} {...common} />)

View File

@ -10,6 +10,7 @@ import moment from 'moment';
import Tree from '../img/tree.png';
import Axios from 'axios';
import { Base64 } from 'js-base64';
const format = "YYYY-MM-DD HH:mm";
@ -197,7 +198,7 @@ function Index(props) {
//
function deleteLabel(){
Axios.delete(`/v1/${owner}/${projectsId}/branches/${visible.name}.json`).then(res=>{
Axios.delete(`/v1/${owner}/${projectsId}/branches/${Base64.encode(visible.name)}.json`).then(res=>{
if(res && res.data){
if(current>1 && (list && list.length===1)){
setCurrent(current-1);

View File

@ -4,7 +4,7 @@ import { Link } from 'react-router-dom';
import axios from 'axios';
import Commits from './Commits';
import Comments from '../comments/comments';
import Comments from '../Issues/Component/comments/list';
import Files from './Files';
import OperateDate from './components/operateDate';
@ -29,7 +29,8 @@ class MergeFooter extends Component {
page:1,
hasMore:true,
dropLoading:false,
limit:100
limit:100,
commentReload:undefined
};
}
componentDidMount() {
@ -143,8 +144,8 @@ class MergeFooter extends Component {
render() {
const { projectsId, owner, mergeId } = this.props.match.params;
const { order_id, data = {} , pullOwnerLogin } = this.props;
const { order_id, data = {} } = this.props;
const {
isSpin,
activeKey,
@ -153,7 +154,8 @@ class MergeFooter extends Component {
filesData,
commitsData = [],
unitData,
dropLoading
dropLoading,
commentReload
} = this.state;
// Comment0
@ -181,14 +183,12 @@ class MergeFooter extends Component {
key="1"
>
<Comments
order_id={order_id}
showNotification={this.props.showNotification}
only_show_content={true}
updateCommentsNum={(commentsCount) => {
this.setState({ commentsTotalCount: commentsCount || 0 });
}}
issueId={order_id}
issueInfo={{author:data.author}}
{...this.props}
bindCommentRef={this.bindCommentRef}
reload = {commentReload}
reloadComment = {()=>{this.setState({commentReload:Math.random()})}}
updateCommentsNum={(totalCount)=>{this.setState({commentsTotalCount:totalCount})}}
/>
</TabPane>
{commitCount > 0 && (

View File

@ -528,7 +528,7 @@ class MessageCount extends Component {
{
pr_status === 2 && permission && <Button type="primary" ghost onClick={this.restartFunc}>重新开启</Button>
}
{(userLogin && projectDetail && (pr_status === 0 || pr_status === 1) && permission) && (
{userLogin && projectDetail && (pr_status === 0 || pr_status === 1) && permission && (
<Button
type="green"
ghost
@ -663,6 +663,7 @@ class MessageCount extends Component {
order_id={data && data.issue.id}
{...this.props}
{...this.state}
data={data}
bindFootRef={this.bindFootRef}
pullOwnerLogin={data.pull_request && (data.pull_request.is_original ? data.pull_request.fork_project_user:data.issue.project_author)}
></MergeLinkFooter>

View File

@ -13,6 +13,7 @@ class UpdateMerge extends Component {
merge: undefined,
pull: undefined,
isSpin: false,
pull_request_staus:""
};
}
@ -48,6 +49,7 @@ class UpdateMerge extends Component {
data: result.data,
pull: result.data.head,
merge: result.data.base,
pull_request_staus:result.data.pull_request_staus
});
} else {
this.setState({
@ -64,7 +66,7 @@ class UpdateMerge extends Component {
};
render() {
const { data, isSpin, pull, merge } = this.state;
const { data, isSpin, pull, merge , pull_request_staus } = this.state;
return (
<div>
<div className="main updateMerge">
@ -113,6 +115,7 @@ class UpdateMerge extends Component {
data={data}
merge={merge}
pull={pull}
pull_request_staus={pull_request_staus}
></MergeForm>{" "}
</div>
) : (

View File

@ -34,7 +34,7 @@ function MergeForm(props){
const [issue_id,setIssueId] = useState(undefined);
const [disable_pr_vew,setDisableprview] = useState(undefined);
const { projectsId, mergeId ,owner } = props.match.params;
const { checkIfLogin , form , merge_type , project ,data , merge , files_count , commits_count ,pull } = props;
const { checkIfLogin , form , merge_type , project ,data , merge , files_count , commits_count ,pull , pull_request_staus } = props;
const open_blockchain = project && project.open_blockchain;
const permission = props && props.projectDetail && props.projectDetail.permission;
const { getFieldDecorator } = props.form;
@ -226,7 +226,7 @@ function MergeForm(props){
{merge_type === "new" ? "新建" : "编辑"}合并请求:
</span>
<Tag color="#28BD6C" className="ver-middle">
可合并的
{pull_request_staus === "merged" ? "已合并":"可合并的"}
</Tag>
</div>
<Form>
@ -241,7 +241,7 @@ function MergeForm(props){
},
],
initialValue: title,
})(<Input placeholder="标题" maxLength={200} />)}
})(<Input placeholder="标题" maxLength={200} disabled={pull_request_staus === "merged"}/>)}
</Form.Item>
<div style={{position:"relative"}}>
<MDEditor
@ -254,6 +254,7 @@ function MergeForm(props){
changeAtWhoLoginList = {changeAtWhoLoginList}
owner = {owner}
projectsId = {projectsId}
disabled={pull_request_staus === "merged"}
></MDEditor>
</div>
<p className="clearfix mt20">
@ -287,6 +288,7 @@ function MergeForm(props){
selectValueList={assigned_to_id}
chooseFunc={(list)=>{setAssignedToId(list);}}
removeFlag
editFlag={pull_request_staus === "merged"}
/>
<DropMenu
placeholder="里程碑"

View File

@ -1,5 +1,5 @@
.panels{
width: 1200px;
width: 1400px;
margin:20px auto;
}
.btnhover{

View File

@ -4,7 +4,7 @@
align-items: center;
}
.wiki-main {
width: 1200px;
width: 1400px;
min-height: 400px;
margin: 20px auto 60px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,62 @@
import React, { useState , useEffect } from 'react';
import "./index.scss";
import "../Issues/index.scss";
import AddImg from './img/add.png';
import { Button } from 'antd';
import { Link } from 'react-router-dom';
import RenderHtml from '../../components/render-html';
import axios from 'axios';
function Template(props){
const owner = props.match.params.owner;
const type = props.match.params.type;
const projectsId = props.match.params.projectsId;
const [ tempLists , setTempLists ] = useState(undefined);
useEffect(()=>{
InitList();
},[type])
function InitList(){
const url = `/v1/${owner}/${projectsId}/project_templates.json`;
axios.get(url).then(response=>{
if(response){
let allTemp = response.data.project_templates || [];
setTempLists(allTemp.filter(i=>i.type.toLowerCase().indexOf(type)>-1));
}
}).catch(error=>{})
}
return(
<div className='pagebox'>
<ul className='tempType'>
<Link to={`/${owner}/${projectsId}/issue/temp`} className={type===`issue`?"active":""}>疑修模版</Link>
<Link to={`/${owner}/${projectsId}/pull/temp`} className={type===`pull`?"active":""}>合并请求模版</Link>
</ul>
<div className='tempList'>
<div className='tempItem tempCreateItem' onClick={()=>{props.history.push(`/${owner}/${projectsId}/${type}/temp/new`)}}>
<img src={AddImg} width={69}/>
<span>创建模板</span>
</div>
{
tempLists && tempLists.map((i,k)=>{
return(
<div className='tempItem'>
<a href=""><i className='iconfont icon-quxiaoshenqingicon font-16 deltemp' /></a>
<p className='tempName'>{i.name}</p>
<div className='tempContent'>
<RenderHtml className="break_word_comments imageLayerParent" value={i.content} url={props.history.location}/>
</div>
<div className='tempOperate'>
<Button type='primary'>预览</Button>
<Button>编辑</Button>
</div>
</div>
)
})
}
</div>
</div>
)
}
export default Template;

View File

@ -0,0 +1,145 @@
.tempType{
background-color:#f4f6fe;
border-radius:20px;
padding: 4px;
display: inline-flex;
width: auto;
margin-top: 30px;
a{
padding:0 14px ;
height: 30px;
line-height: 30px;
color:#3f455b;
&.active{
background-color: #fff;
border-radius:20px;
font-weight:700;
color:#333333;
font-size:15px;
}
}
}
.tempList{
display: flex;
flex-wrap: wrap;
padding:25px 0;
.tempItem{
width: 23.8%;
margin-right: 1.6%;
margin-bottom: 20px;
min-height: 286px;
border-radius:8px;
padding:0 10px;
position: relative;
box-shadow:0px 6px 6px rgba(24, 54, 181, 0.04);
background-color: rgba(24, 54, 181, 0.05);
border:1px solid #fff;
display: flex;
flex-direction: column;
background-size: 100% 100%;
&:hover:not(:first-child){
border:1px solid #466aff;
&::after{
position: absolute;
top:-5px;
left:-5px;
right:-5px;
bottom:-5px;
border:5px solid rgba(24, 54, 181, 0.05);
content: "";
border-radius:8px;
z-index: 1;
}
.deltemp{
color: #000;
}
}
&:nth-child(4n + 2){
background-image: unset;
}
&:nth-child(4n + 3){
background-image: url(./img/2.png);
&::after{
border-color: rgba(192 ,153 ,226,0.15)!important;
}
}
&:nth-child(4n + 4){
background-image: url(./img/3.png);
&::after{
border-color: rgba(142 ,208 ,233,0.15)!important;
}
}
&:nth-child(4n + 5){
background-image: url(./img/4.png);
&::after{
border-color: rgba(239, 189, 113,0.15)!important;
}
}
&:nth-child(4n){
margin-right: 0px;
}
.tempContent{
flex:1;
width: 100%;
background-color: #fff;
border-radius: 8px;
padding:5px;
max-height: 265px;
overflow-y: hidden;
.markdown-body{
background-color: #fcfcfc;
border-radius: 8px;
h1,h2,h3,h4,h5,h6{
font-size: 14px;
}
p,ul,ol,li{
font-size: 12px!important;
}
}
}
.tempOperate{
position: absolute;
z-index: 3;
width: 100%;
left: 0px;
bottom: 0px;
text-align: center;
padding:18px;
border-radius:0px 0px 8px 8px;
background-color: #fff;
box-shadow:0px 0px 6px rgba(24, 54, 181, 0.05);
button{
margin:0px 20px;
width: 82px;
height: 32px;
line-height: 30px;
}
}
}
.tempName{
padding:0 5px;
text-align: center;
height: 40px;
line-height: 40px;
color:#333333;
position: relative;
}
.deltemp{
position: absolute;
right: 15px;
top:4px;
z-index: 2;
color: #999;
}
.tempCreateItem{
align-items: center;
justify-content: center;
color:#333333;
font-size:16px;
cursor: pointer;
background-color: #fff;
&:hover{
color: #466aff;
}
}
}

View File

@ -0,0 +1,60 @@
import React, { useState , useEffect } from 'react';
import "./index.scss";
import "../Issues/index.scss";
import { Breadcrumb, Button } from 'antd';
import { Link } from 'react-router-dom';
import MDEditor from "../../modules/tpm/challengesnew/tpm-md-editor";
import axios from 'axios';
function Template(props){
const owner = props.match.params.owner;
const type = props.match.params.type;
const operate = props.match.params.operate;
const projectsId = props.match.params.projectsId;
const [ description , setDescription ] = useState(undefined);
function onContentChange(value){
console.log("-----------------",value);
setDescription(value);
}
useEffect(()=>{
if(operate === "edit"){
InitDetail();
}
},[operate])
function InitDetail(){
const url = `/v1/${owner}/${projectsId}/project_templates.json`;
axios.get(url).then(response=>{
}).catch(error=>{})
}
function sureFunc(){
}
return(
<div className='pagebox'>
<Breadcrumb className='mt30' separator={">"}>
<Breadcrumb.Item href={`/${owner}/${projectsId}/${type}/temp`}>{type.toLowerCase()===`issue`?"疑修模板":"合并请求模板"}</Breadcrumb.Item>
<Breadcrumb.Item>{operate==="edit"?"模板详情":"新建模板"}</Breadcrumb.Item>
</Breadcrumb>
<MDEditor
placeholder={"请输入模板内容"}
height={400}
mdID={"tempContent"}
onChange={onContentChange}
className="mt20"
owner = {owner}
projectsId = {projectsId}
></MDEditor>
<div className='mt20 pb30'>
<Button type='primary' className='mr20' style={{width:"90px"}} onClick={sureFunc}>确定</Button>
<Button onClick={()=>history.back(-1)} style={{width:"90px"}}>取消</Button>
</div>
</div>
)
}
export default Template;