diff --git a/src/forge/Component/Cards.jsx b/src/forge/Component/Cards.jsx index 498b375c..f9a3d52e 100644 --- a/src/forge/Component/Cards.jsx +++ b/src/forge/Component/Cards.jsx @@ -3,7 +3,7 @@ import { getImageUrl } from 'educoder'; import { Link } from 'react-router-dom'; import './Component.scss'; -function Cards({img , title, desc , rightBtn , src}){ +function Cards({img , title, desc , rightBtn , src , bottomInfos}){ return(
{img &&
} @@ -15,6 +15,7 @@ function Cards({img , title, desc , rightBtn , src}){
{desc}
+ {bottomInfos}
) diff --git a/src/forge/Component/EAccount.scss b/src/forge/Component/EAccount.scss index 6f0bb97f..4bcd6b91 100644 --- a/src/forge/Component/EAccount.scss +++ b/src/forge/Component/EAccount.scss @@ -1,8 +1,8 @@ .ant-modal-mask{ - z-index: 10000; + z-index: 1001; } .ant-modal-wrap{ - z-index: 10001; + z-index: 1002; .ant-form-explain{ position: absolute; } diff --git a/src/forge/Component/SearchUser.jsx b/src/forge/Component/SearchUser.jsx index b9841123..79d23fb6 100644 --- a/src/forge/Component/SearchUser.jsx +++ b/src/forge/Component/SearchUser.jsx @@ -5,9 +5,15 @@ import axios from 'axios'; const Option = AutoComplete.Option; -export default ({ getUser , placeholder, width })=>{ +export default ({ getUser , placeholder, width ,value })=>{ const [ source , setSource ] = useState(undefined); const [ searchKey , setSearchKey ] = useState(undefined); + + useEffect(()=>{ + if(!value){ + setSearchKey(undefined); + } + },[value]) useEffect(()=>{ getUserList(); diff --git a/src/forge/Divert/DivertModal.jsx b/src/forge/Divert/DivertModal.jsx new file mode 100644 index 00000000..1b9c486f --- /dev/null +++ b/src/forge/Divert/DivertModal.jsx @@ -0,0 +1,174 @@ +import React ,{ forwardRef, useEffect, useState } from 'react'; +import { Modal , Form , Input , Radio , Select } from 'antd'; +import SearchUser from '../Component/SearchUser'; +import './Index.scss'; +import Axios from 'axios'; + +const { Option } = Select; +function DivertModal({form , visible , onSuccess , onCancel,owner,repo}){ + const { getFieldDecorator, validateFields , setFieldsValue } = form; + const [ cate , setCate ] = useState(0); + const [ value , setValue ] = useState(undefined); + + const [ organizations , setOrganizations ] = useState(undefined); + + useEffect(()=>{ + setFieldsValue({goal:cate}) + },[]) + + useEffect(()=>{ + if(owner && repo && visible===true){ + getTeam(); + } + if(!visible){ + setFieldsValue({ + owner_name:undefined, + identifier:undefined + }) + setValue(undefined) + } + },[repo,owner,visible]) + + function getTeam(){ + const url = `/${owner}/${repo}/applied_transfer_projects/organizations.json`; + Axios.get(url).then(result=>{ + if(result){ + setOrganizations(result.data.organizations); + } + }).catch(error=>{}) + } + + // 确认转移 + function onOk(){ + validateFields((error,values)=>{ + console.log(...values); + if(!error){ + const url = `/${owner}/${repo}/applied_transfer_projects.json`; + Axios.post(url,{ + ...values + }).then(result=>{ + if(result){ + onSuccess(result.data && result.data.owner); + } + }).catch(error=>{}) + } + }) + + } + function changeType(e){ + setCate(e.target.value); + setFieldsValue({ + owner_name:undefined + }) + } + + function checkIdentifier(rule, value, callback){ + if(!value){ + callback(); + } + if (repo && value !== repo) { + callback("请输入当前项目的标识!"); + } + callback(); + } + + const layout = { + labelCol: { span: 5 }, + wrapperCol: { span: 18 }, + }; + + function getUser(id){ + setValue(id); + setFieldsValue({ + owner_name:id + }) + } + return( + +
+ { + cate === 0 ? + + : + + } +
+ + {getFieldDecorator("goal",{ + rules:[] + })( + + 个人 + 组织 + + )} + + { + cate === 0 && + + {getFieldDecorator("owner_name",{ + rules:[{required:true,message:"请输入目标用户名"}] + })( + // + + )} + + } + { + cate === 1 && + + {getFieldDecorator("owner_name", + {rules:[{required:true,message:"请选择目标组织"}]} + )( + + )} + + } + + + {getFieldDecorator("identifier", + { + rules:[ + {required:true,message:"请输入仓库标识"}, + { + validator:checkIdentifier + } + ] + } + )( + + )} + +
+
+
+ ) +} +export default Form.create()(forwardRef(DivertModal)); \ No newline at end of file diff --git a/src/forge/Divert/Index.scss b/src/forge/Divert/Index.scss new file mode 100644 index 00000000..0a39e4e2 --- /dev/null +++ b/src/forge/Divert/Index.scss @@ -0,0 +1,12 @@ +.diverModal{ + .descUl{ + background-color: #fffae6; + border-radius: 4px; + padding:10px 15px; + color: #efc16b; + border:1px solid #efc16b; + } + .ant-form-item-required::before{ + content: ""; + } +} \ No newline at end of file diff --git a/src/forge/Main/sub/DetailBanner.jsx b/src/forge/Main/sub/DetailBanner.jsx index 331790c8..aa801773 100644 --- a/src/forge/Main/sub/DetailBanner.jsx +++ b/src/forge/Main/sub/DetailBanner.jsx @@ -75,7 +75,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa :"" } - { + {/* { item.menu_name === "resources" &&
  • @@ -84,7 +84,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa {projectDetail && projectDetail.source_count ? {projectDetail.source_count} :""}
  • - } + } */} { item.menu_name === "versions" &&
  • diff --git a/src/forge/Merge/NewMerge.js b/src/forge/Merge/NewMerge.js index 2ef27efa..3cbe4ddc 100644 --- a/src/forge/Merge/NewMerge.js +++ b/src/forge/Merge/NewMerge.js @@ -178,7 +178,7 @@ class NewMerge extends Component { let arr = projects_names && projects_names.filter(item=>item.id===value); let identifier = arr && arr[0].project_id; let login = arr && arr[0].project_user_login; - let is_fork_id = parseInt(value) !== parseInt(id) + let is_fork_id = parseInt(value) !== parseInt(id); this.setState({ isSpin: true, merge_head: is_fork_id, diff --git a/src/forge/Merge/merge_form.js b/src/forge/Merge/merge_form.js index 57886d04..1c75a9f9 100644 --- a/src/forge/Merge/merge_form.js +++ b/src/forge/Merge/merge_form.js @@ -34,17 +34,19 @@ class MergeForm extends Component { this.set_defatul(); }; componentDidUpdate=(prevPros)=>{ + const { projectsId ,owner } = this.props.match.params; + const pId = prevPros.match.params.projectsId; + const oId = prevPros.match.params.owner; + if(pId !== projectsId || oId !== owner ){ + // console.log("切换了项目分支···········"); + this.get_default_selects(); + } if(prevPros && this.props && !this.props.checkIfLogin()){ this.props.history.push("/403") return } } - // check_is_login =() =>{ - // if(!this.props.checkIfLogin()){ - // this.props.history.push("/403") - // return - // } - // }; + get_default_selects = () => { const { projectsId ,owner } = this.props.match.params; this.setState({ isSpin: true }); diff --git a/src/forge/Notice/Index.jsx b/src/forge/Notice/Index.jsx new file mode 100644 index 00000000..d9d04d59 --- /dev/null +++ b/src/forge/Notice/Index.jsx @@ -0,0 +1,88 @@ +import React, { useEffect, useState } from "react"; +import { Link } from 'react-router-dom'; +import './Index.scss'; + +import Loadable from "react-loadable"; +import Loading from "../../Loading"; +import { Route, Switch } from "react-router-dom"; + +const Notify = Loadable({ + loader: () => import("./Notify"), + loading: Loading, +}); +const UndoEvent = Loadable({ + loader: () => import("./UndoEvent"), + loading: Loading, +}); +function Index(props){ + const username = props.match.params.username; + const pathname = props.history.location.pathname; + const user = props.user; + const undo_messages = props.undo_messages; + + const [ menu , setMenu ] = useState("notify"); + const [ messages , setMessages ] = useState(0); + const [ transferProjects , setTransferProjects ] = useState(0); + + useEffect(()=>{ + if(user){ + setTransferProjects(user.undo_transfer_projects); + } + if(undo_messages){ + setMessages(undo_messages); + } + },[user,undo_messages]) + + useEffect(()=>{ + if(pathname && username){ + if(pathname === `/users/${username}/notice`){ + setMenu("notify"); + } + if(pathname === `/users/${username}/notice/undo`){ + setMenu("undo"); + } + } + },[pathname]) + + function fetchUser(){ + props && props.fetchUser(); + } + + function changeNum(){ + fetchUser(); + } + + return ( +
    + + + { + return ; + }} + > + { + return ; + }} + > + +
    + ); +} +export default Index; diff --git a/src/forge/Notice/Index.scss b/src/forge/Notice/Index.scss new file mode 100644 index 00000000..c7bb50eb --- /dev/null +++ b/src/forge/Notice/Index.scss @@ -0,0 +1,72 @@ +.noticeMenu{ + padding:0px 20px; + display: flex; + border-bottom: 1px solid #eee; + height: 54px; + line-height: 54px; + li{ + font-size: 16px; + padding:0px; + margin:0px 30px 0px 20px!important; + height: 54px; + line-height: 54px; + position: relative; + transform: none; + a{ + display: flex; + &>span{ + position: relative; + } + } + &.active a span:first-child::after{ + position: absolute; + bottom: 0px; + height: 2px; + left: 0px; + content: ""; + background-color: #1890ff; + width: 100%; + } + .unNum{ + color: #d38900; + font-size: 12px; + border-radius: 13px; + height: 16px; + line-height: 16px; + padding:0px 4px; + min-width: 23px; + text-align: center; + background-color: #ffe4b3; + margin-top: 19px; + margin-left: 10px; + display: block; + } + } +} +.notifyList{ + padding:0px 20px; + min-height: 400px; + li{ + display: flex; + border-bottom: 1px solid #eee; + padding:20px 0px; + .notifyImg{ + width: 48px; + height: 48px; + border-radius: 50%; + margin-right: 15px; + } + .notifyFlex{ + flex:1; + p{ + margin:0px; + } + .notifyInfos{ + margin-bottom: 8px; + } + } + &:last-child{ + border-bottom: none; + } + } +} \ No newline at end of file diff --git a/src/forge/Notice/Notify.jsx b/src/forge/Notice/Notify.jsx new file mode 100644 index 00000000..d927f56f --- /dev/null +++ b/src/forge/Notice/Notify.jsx @@ -0,0 +1,91 @@ +import React, { useEffect, useState } from "react"; +import Nodata from '../Nodata'; +import {Pagination } from 'antd'; +import { Link } from 'react-router-dom'; +import { getImageUrl } from 'educoder'; +import Axios from "axios"; + +const limit = 15; +function Notify(props){ + const username = props.match.params.username; + const [ list , setList ] = useState(undefined); + const [ page , setPage ] = useState(1); + const [ total , setTotal ] = useState(0); + + useEffect(()=>{ + if(username){ + getList(); + } + },[username,page]) + + function getList(){ + const url = `/users/${username}/applied_messages.json`; + Axios.get(url,{ + params:{ + page,per_page:limit + } + }).then(result=>{ + if(result){ + setList(result.data.applied_messages); + setTotal(result.data.total_count); + } + }).catch(error=>{}) + } + + function renderStatus(status,applied){ + let { project , owner} = applied + if(status){ + switch(status){ + case 'canceled': + return

    取消转移【{project && project.name}】仓库

    + case 'common': + return

    正在将【{project && project.name}】仓库转移给【{owner && owner.name}】

    + case 'successed': + return

    {project && project.name}】仓库成功转移给【{owner && owner.name}】

    + default: + return

    拒绝转移【{project && project.name}】仓库

    + } + + }else{ + return "" + } + } + + return( +
    + { + list && list.length > 0 ? +
    +
      + { + list.map((i,k)=>{ + return( +
    • + +
      +

      + {i.applied_user && i.applied_user.name} + {i.time_ago} +

      + {renderStatus(i.status,i.applied)} +
      +
    • + ) + }) + } +
    +
    + : + "" + } + {list && list.length === 0 && } + { + total > limit && +
    + {setPage(p)}}/> +
    + } +
    + ) +} +export default Notify; \ No newline at end of file diff --git a/src/forge/Notice/UndoEvent.jsx b/src/forge/Notice/UndoEvent.jsx new file mode 100644 index 00000000..afb5c706 --- /dev/null +++ b/src/forge/Notice/UndoEvent.jsx @@ -0,0 +1,118 @@ +import React, { useEffect, useState } from "react"; +import Nodata from '../Nodata'; +import { FlexAJ } from '../Component/layout'; +import { Pagination , Popconfirm } from 'antd'; +import { Link } from 'react-router-dom'; +import { getImageUrl } from 'educoder'; +import Axios from 'axios'; + +const limit = 15; +function UndoEvent(props){ + const username = props.match.params.username; + const [ list , setList ] = useState(undefined); + const [ page , setPage ] = useState(1); + const [ total , setTotal ] = useState(0); + + useEffect(()=>{ + if(username){ + getList(); + } + },[username,page]) + + function getList(){ + const url = `/users/${username}/applied_transfer_projects.json`; + Axios.get(url,{ + params:{ + page,per_page:limit + } + }).then(result=>{ + if(result){ + setList(result.data.applied_transfer_projects); + setTotal(result.data.total_count); + } + }).catch(error=>{}) + } + + // 接受 + function acceptDivert(id){ + const url = `/users/${username}/applied_transfer_projects/${id}/accept.json`; + Axios.post(url).then(result=>{ + if(result && result.data){ + getList(); + props && props.fetchUser(); + } + }).catch(error=>{}) + } + + // 拒绝 + function revertDivert(id){ + const url = `/users/${username}/applied_transfer_projects/${id}/refuse.json`; + Axios.post(url).then(result=>{ + if(result && result.data){ + getList(); + props && props.fetchUser(); + } + }).catch(error=>{}) + } + + return( +
    + { + list && list.length > 0 ? +
    +
      + { + list.map((i,k)=>{ + return( +
    • + +
      +

      + {i.user && i.user.name} + {i.time_ago} +

      + +

      请求将仓库【{i.project && i.project.name}】 + 转移给【{i.owner && i.owner.name}】,是否接受?

      + { + i.status === "common" && + + acceptDivert(i.id)}> + 接受 + + revertDivert(i.id)}> + 拒绝 + + + } + { + i.status === "canceled" && 对方已取消转移 + } + { + i.status === "accepted" && 已接受 + } + { + i.status === "refused" && 已拒绝 + } +
      +
      +
    • + ) + }) + } +
    +
    + : + "" + } + {list && list.length === 0 && } + { + total > limit && +
    + {setPage(p)}}/> +
    + } +
    + ) +} +export default UndoEvent; \ No newline at end of file diff --git a/src/forge/Order/MilepostDetail.js b/src/forge/Order/MilepostDetail.js index 2cfb2f89..7954630d 100644 --- a/src/forge/Order/MilepostDetail.js +++ b/src/forge/Order/MilepostDetail.js @@ -218,7 +218,7 @@ class MilepostDetail extends Component {
    编辑里程碑 - 创建任务 + 创建易修
    diff --git a/src/forge/Order/order.js b/src/forge/Order/order.js index ac9e7a0e..8875aa85 100644 --- a/src/forge/Order/order.js +++ b/src/forge/Order/order.js @@ -352,12 +352,12 @@ class order extends Component { if (this.props.checkIfLogin()) { return( - + 创建任务 + + 创建易修 ) }else{ return( - + 创建任务 + + 创建易修 ) } } diff --git a/src/forge/Order/order_form.js b/src/forge/Order/order_form.js index db36c3ea..ed8d4d8b 100644 --- a/src/forge/Order/order_form.js +++ b/src/forge/Order/order_form.js @@ -310,14 +310,14 @@ class order_form extends Component {

    - {form_type === "new" ? "新建" :( form_type === "copy" ? "复制" : "编辑")}任务 + {form_type === "new" ? "新建" :( form_type === "copy" ? "复制" : "编辑")}易修

    {getFieldDecorator("subject", { rules: [ { required: true, - message: "请填写任务标题", + message: "请填写易修标题", }, ] })()} diff --git a/src/forge/Settings/Setting.js b/src/forge/Settings/Setting.js index a85e159b..4c719968 100644 --- a/src/forge/Settings/Setting.js +++ b/src/forge/Settings/Setting.js @@ -2,6 +2,8 @@ import React, { Component } from "react"; import { Form, Input, Checkbox, Select , Spin } from "antd"; import Title from '../Component/Title'; import {WhiteBack} from '../Component/layout'; +import DivertModal from '../Divert/DivertModal'; +import { Link } from 'react-router-dom'; import axios from "axios"; import "./setting.scss"; const { TextArea } = Input; @@ -13,7 +15,7 @@ const menu = [ {name:"易修 (Issue)",index:"issues"}, {name:"合并请求",index:"pulls"}, {name:"工作流(beta版)",index:"devops"}, - {name:"资源库",index:"resources"}, + // {name:"资源库",index:"resources"}, {name:"里程碑",index:"versions"}, {name:"动态",index:"activity"}, ] @@ -25,7 +27,9 @@ class Setting extends Component { LanguageList: undefined, private_check: undefined, loading:true, - project_units:['home',"activity","code"] + project_units:['home',"activity","code"], + divertVisible:false, + is_transfering:undefined, }; } @@ -73,7 +77,9 @@ class Setting extends Component { this.setState({ private_check: result.data.private, loading:false, - project_units:units + project_units:units, + transfer:result.data.transfer, + is_transfering:result.data.is_transfering, }); } }) @@ -186,12 +192,55 @@ class Setting extends Component { }); }; + // 转移仓库 + DivertProject=()=>{ + this.setState({ + divertVisible:true + }) + } + // 取消仓库转移 + CancelDivertProject=()=>{ + this.props.confirm({ + content: "是否确认取消将此项目转移给他人?", + onOk: () => { + const { projectsId , owner } = this.props.match.params; + const url = `/${owner}/${projectsId}/applied_transfer_projects/cancel.json`; + axios.post(url).then(result=>{ + if(result && result.data){ + this.setState({ + is_transfering:false + }) + } + }).catch(error=>{}) + }, + }); + + } + // 确定转移仓库 + onSuccess=(owner)=>{ + this.setState({ + is_transfering:true, + divertVisible:false, + transfer:owner + }) + } + render() { const { getFieldDecorator } = this.props.form; + const { projectsId , owner } = this.props.match.params; + const { projectDetail } = this.props; - const { CategoryList, LanguageList, private_check ,loading } = this.state; + const { CategoryList, LanguageList, private_check ,loading , divertVisible , is_transfering, transfer } = this.state; + console.log(transfer); return (
    + {this.setState({divertVisible:false})}} + /> 基本设置 @@ -279,23 +328,48 @@ class Setting extends Component { {/* 镜像设置部分,暂无接口,先不显示 */} {/* */} - -
    -
    危险操作区
    -
    + { + projectDetail && projectDetail.permission && (projectDetail.permission === "Admin" || projectDetail.permission === "Owner")? +
    -

    删除本仓库

    -

    - 删除仓库是永久性的, - 无法撤消,且删除后,与仓库关联的项目/任务/合并请求/版本发布等,均会被删除 -

    +
    危险操作区
    +
    +
    +

    转移仓库

    +

    + { + is_transfering ? + 此仓库正在转移给【 + {transfer && {transfer.name}} + 】,请联系对方接收此仓库。 + : + `将此仓库转移给其他用户或组织` + } +

    +
    + { + is_transfering ? + 取消转移 + : + 转移 + } +
    +
    +
    +

    删除本仓库

    +

    + 删除仓库是永久性的, + 无法撤消,且删除后,与仓库关联的项目/任务/合并请求/版本发布等,均会被删除 +

    +
    + + 删除本仓库 + +
    - - 删除本仓库 - -
    -
    -
    + + :"" + }
    ); diff --git a/src/forge/Team/Group/GroupDetails.jsx b/src/forge/Team/Group/GroupDetails.jsx index 8d1c519b..49720c17 100644 --- a/src/forge/Team/Group/GroupDetails.jsx +++ b/src/forge/Team/Group/GroupDetails.jsx @@ -84,11 +84,12 @@ export default ((props) => { // 移除成员 function removeUser(username) { - const url = `/organizations/${OIdentifier}/teams/${groupId}/team_users/${username}.json`; if (username) { + const url = `/organizations/${OIdentifier}/teams/${groupId}/team_users/quit.json`; axios.delete(url).then((result) => { if (result && result.data) { - + props.showNotification(`已成功退出团队!`); + props.history.push(`/organize/${OIdentifier}`); } }).catch((error) => { }); } diff --git a/src/forge/Team/ListItem.jsx b/src/forge/Team/ListItem.jsx index 767a23c2..3c2cf3f4 100644 --- a/src/forge/Team/ListItem.jsx +++ b/src/forge/Team/ListItem.jsx @@ -7,7 +7,7 @@ function ListItem({item,key,OIdentifier}) {

    - {item.identifier} + {item.name} { item.forked_from_project_id && } { item.type && item.type !== 0 ? diff --git a/src/forge/users/Infos.js b/src/forge/users/Infos.js index e3febfce..f7a1d887 100644 --- a/src/forge/users/Infos.js +++ b/src/forge/users/Infos.js @@ -41,8 +41,8 @@ const FanUser = Loadable({ loading: Loading, }) -const UndoEvents = Loadable({ - loader: () => import("./undo_events"), +const Notice = Loadable({ + loader: () => import("../Notice/Index"), loading: Loading, }) class Infos extends Component { @@ -52,7 +52,9 @@ class Infos extends Component { isSpin: false, user: undefined, project_type: undefined, - route_type: undefined + route_type: undefined, + undo_events:0, + undo_messages:0 }; } @@ -73,14 +75,22 @@ class Infos extends Component { }); const { current_user } = this.props; const { username } = this.props.match.params; + const { pathname } = this.props.location; + const { notice } = this.state; let url = `/users/${username || (current_user && current_user.login)}.json`; axios .get(url) .then((result) => { + let e = result.data && result.data.undo_events; + let p = result.data && result.data.undo_messages; + let n = notice || pathname === `/users/${username}/notice` ; this.setState({ user: result.data, isSpin: false, + undo_events:n ? (e-p) : e, + undo_messages:0, + notice:n }); }) .catch((error) => { @@ -114,11 +124,15 @@ class Infos extends Component { } undo_link = () => { - const {user} = this.state + const {user } = this.state; this.setState({ - route_type: undefined + route_type: undefined, + project_type:"notice", + notice:true + },()=>{ + this.props.history.push(`/users/${user && user.login}/notice`); + this.fetchUser(); }) - this.props.history.push(`/users/${user && user.login}/undo_events`) } route_link = (type) => { @@ -130,7 +144,8 @@ class Infos extends Component { organize_link = () => { const {user} = this.state this.setState({ - route_type: undefined + route_type: undefined, + project_type:"organizes" }) this.props.history.push(`/users/${user && user.login}/organizes`) } @@ -140,7 +155,7 @@ class Infos extends Component { const { current_user, mygetHelmetapi } = this.props; const { username } = this.props.match.params; - const { user, isSpin, project_type, route_type } = this.state; + const { user, isSpin, project_type, route_type , undo_events , undo_messages } = this.state; return (

    @@ -211,21 +226,23 @@ class Infos extends Component {
    - {/* {current_user && user && current_user.id === user.id && ( + {current_user && user && user.login === current_user.login ? (
    -

    this.undo_link()}> - - - 待办事项 - - - {user.undo_events} - -

    +
  • +

    this.undo_link()}> + + + 待办事项 + + + {undo_events} + +

    +
  • - )} */} + ):""}
    } - -
    -

    this.organize_link()} > - - - 组织 - - - {user && user.user_org_count} - -

    +
  • +

    this.organize_link()} > + + + 组织 + + + {user && user.user_org_count} + +

    +
  • @@ -312,9 +329,9 @@ class Infos extends Component { }} > { - return ; + return ; }} > -
    -

    待办事项

    -
    - - - ); - } -} -export default UndoEvents; diff --git a/src/modules/tpm/TPMBanner.js b/src/modules/tpm/TPMBanner.js index 3f591a40..6760fe04 100644 --- a/src/modules/tpm/TPMBanner.js +++ b/src/modules/tpm/TPMBanner.js @@ -1062,7 +1062,7 @@ class TPMBanner extends Component { :

    - 尚未创建任务的实训,不能申请发布 + 尚未创建易修的实训,不能申请发布

    }