diff --git a/package-lock.json b/package-lock.json index 9cc2464ea..6dad7d060 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7245,8 +7245,7 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "optional": true + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "aproba": { "version": "1.2.0", @@ -7267,14 +7266,12 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "optional": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7289,20 +7286,17 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "optional": true + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "optional": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "optional": true + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "core-util-is": { "version": "1.0.2", @@ -7419,8 +7413,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "optional": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { "version": "1.3.5", @@ -7432,7 +7425,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -7447,7 +7439,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -7455,14 +7446,12 @@ "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "optional": true + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "minipass": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -7481,7 +7470,6 @@ "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", - "optional": true, "requires": { "minimist": "^1.2.5" } @@ -7543,8 +7531,7 @@ "npm-normalize-package-bin": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "optional": true + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" }, "npm-packlist": { "version": "1.4.8", @@ -7572,8 +7559,7 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "optional": true + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "object-assign": { "version": "4.1.1", @@ -7585,7 +7571,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "optional": true, "requires": { "wrappy": "1" } @@ -7663,8 +7648,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "optional": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safer-buffer": { "version": "2.1.2", @@ -7700,7 +7684,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -7720,7 +7703,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -7764,14 +7746,12 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "optional": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "optional": true + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" } } }, diff --git a/src/forge/DevOps/Index.jsx b/src/forge/DevOps/Index.jsx index e6aceea36..87357d0c4 100644 --- a/src/forge/DevOps/Index.jsx +++ b/src/forge/DevOps/Index.jsx @@ -1,7 +1,7 @@ import React, {useEffect} from 'react'; import { WhiteBack } from '../Component/layout'; import './ops.scss'; - +import devops from '../Images/devops.png'; import { Route, Switch } from 'react-router-dom'; import Loadable from 'react-loadable'; import Loading from '../../Loading'; @@ -32,8 +32,7 @@ const Params = Loadable({ }) export default ((props)=>{ - console.log('props', props); - const {jianmu_devops} = props; + const {jianmu_devops, isManager, project} = props; useEffect(() => { window.addEventListener("message", iframeHeight, false); @@ -44,7 +43,6 @@ export default ((props)=>{ function iframeHeight(e){ - console.log('e',e&&typeof(e.data), e); if (e && e.data && typeof(e.data) === "string") { let myHeight = JSON.parse(e.data); if (document.querySelector("#devopsIframe")) { @@ -65,9 +63,16 @@ export default ((props)=>{ } return( - + {/* 嵌入devops */} - {jianmu_devops && } + {jianmu_devops && project && isManager && } + {!isManager &&
+
引擎配置
+
+ +
暂无权限,仅仓库管理员可访问
+
+
} {/* 旧引擎页面 */} {/* {}) } - + return(
setVisible(false)} name={projectDetail && projectDetail.name} onSuccess={onSuccess}/> { - menuName && projectDetail ? + menuName && projectDetail ?
    { - Array.isArray(menuName)&& menuName.map((item,key)=>{ + Array.isArray(menuName)&& menuName.map((item,key)=>{ return( { @@ -85,7 +85,7 @@ function DetailBanner({ history,list , owner , projectsId ,showNotification , ur } {/* 引擎仅对当前仓库管理员可见 */} { - item.menu_name === "devops" && isManager ? + item.menu_name === "devops" ?
  • {/* */} diff --git a/src/forge/Server/Index.jsx b/src/forge/Server/Index.jsx index cdcb8bbcd..4a6547d47 100644 --- a/src/forge/Server/Index.jsx +++ b/src/forge/Server/Index.jsx @@ -13,10 +13,20 @@ const Data = Loadable({ loader: () => import('./data'), loading: Loading, }) +// 跨平台代码同步服务 +const Reposyncer = Loadable({ + loader: () => import('./reposyncer'), + loading: Loading, +}) function ServerIndex(props){ return(
    + () + } + > () diff --git a/src/forge/Server/List.jsx b/src/forge/Server/List.jsx index 3e49e8158..05ccab673 100644 --- a/src/forge/Server/List.jsx +++ b/src/forge/Server/List.jsx @@ -1,4 +1,5 @@ import React,{useState , useEffect} from 'react'; +import { Link } from 'react-router-dom'; import AgreementModal from './agreementModal'; function Main(props){ @@ -6,10 +7,12 @@ function Main(props){ const { owner , projectsId } = props.match.params; const [ has_trace_user , setHas_trace_user ] = useState(false); - const { current_user , resetUserInfo } = props; + const { current_user , resetUserInfo, projectDetail, showNotification } = props; useEffect(()=>{ - if(current_user){ + if(current_user && !current_user.login){ + props.history.push(`/login?go_page=/${owner}/${projectsId}/server`); + }else{ setHas_trace_user(current_user.has_trace_user); } },[current_user]) @@ -21,7 +24,9 @@ function Main(props){ } function openDetail(){ - if(!has_trace_user){ + if(projectDetail && projectDetail.forked_from_project_id){ + showNotification("fork仓库暂不支持代码溯源服务,敬请谅解。"); + }else if(!has_trace_user){ setVisible(true); }else{ props.history.push(`/${owner}/${projectsId}/server/1`); @@ -41,6 +46,16 @@ function Main(props){ 查看详情
  • +
  • + + + Reposyncer仓库同步系统 + +

    支持不同开源托管平台自动同步推送/拉取相关代码,实现多平台项目同步开发功能

    + + 查看详情 + +
) diff --git a/src/forge/Server/data.jsx b/src/forge/Server/data.jsx index 5af9ad3ad..ea021bd62 100644 --- a/src/forge/Server/data.jsx +++ b/src/forge/Server/data.jsx @@ -3,22 +3,23 @@ import DataEmpty from './dataEmpty'; import DetectionModal from './detectionModal'; import { Table, Spin, message } from 'antd'; import axios from 'axios'; - +import img from '../Images/img1.png'; function Data(props) { const [detectionVisible, setDetectionVisible] = useState(false); const [dataSource, setDataSource] = useState(undefined); const [page, setPage] = useState(1); - const [spining, setSpining] = useState(true); + const [spining, setSpining] = useState(false); const [relayCount, setRelayCount] = useState(5); const [repeatId, setRepeatId] = useState(undefined); const [repeatBranch, setRepeatBranch] = useState(undefined); const [lookResultUrl, setLookResultUrl] = useState(undefined); const [openResultTaskId, setOpenResultTaskId] = useState(undefined); const [viewBase, setViewBase] = useState('https://cjntest.trustie.net/'); + const [operateTime, setOperateTime] = useState(undefined); const { owner, projectsId } = props.match.params; - const { current_user, isManager } = props; + const { isManager, projectDetail, history } = props; const limit = 15; @@ -37,8 +38,9 @@ function Data(props) { } }).then(result => { if (result) { - - if (Array.isArray(result.data.data)) { + if(result.data.code === 501){ + setOperateTime(result.data.data.operate_time); + }else if (Array.isArray(result.data.data)) { setDataSource(result.data.data); setRelayCount(result.data.left_tasks_count); setLookResultUrl(result.data.view_base); @@ -47,21 +49,28 @@ function Data(props) { if (item.detect_status === "detecting" || item.detect_status === "waiting" || (item.detect_status=="detected"&& item.is_reported=="0")) { setTimeout(Init, 2000); break; - } + } } } else { result && result.data.message && props.showNotification(result.data.message); } - + setSpining(false); + }else{ setSpining(false); } }).catch(error => { }) } useEffect(() => { - setSpining(true); - Init(); - }, []) + if(projectDetail){ + if(projectDetail.forked_from_project_id){ + history.go(-1); + }else{ + setSpining(true); + Init(); + } + } + }, [projectDetail]) useEffect(() => { window.addEventListener("message", iframeHeight, false); @@ -154,12 +163,17 @@ function Data(props) { />
重晴鸟代码溯源系统 - {isManager && 新建分析} + {!operateTime && isManager && 新建分析}
+ {operateTime &&
+ +
系统维护中
+
预计{operateTime}小时后代码溯源系统将恢复正常访问,给您带来不便,敬请谅解!
+
} { - dataSource && dataSource.length > 0 && + !operateTime && dataSource && dataSource.length > 0 &&
  • @@ -229,7 +243,7 @@ function Data(props) {
} { - (dataSource === null || (dataSource && dataSource.length === 0)) && + !operateTime && (dataSource === null || (dataSource && dataSource.length === 0)) && }
diff --git a/src/forge/Server/img/anniu.png b/src/forge/Server/img/anniu.png new file mode 100644 index 000000000..39bfa5cd0 Binary files /dev/null and b/src/forge/Server/img/anniu.png differ diff --git a/src/forge/Server/img/anniu2.png b/src/forge/Server/img/anniu2.png new file mode 100644 index 000000000..f825492dc Binary files /dev/null and b/src/forge/Server/img/anniu2.png differ diff --git a/src/forge/Server/img/bg1.png b/src/forge/Server/img/bg1.png new file mode 100644 index 000000000..229ff93d6 Binary files /dev/null and b/src/forge/Server/img/bg1.png differ diff --git a/src/forge/Server/img/bg2.png b/src/forge/Server/img/bg2.png new file mode 100644 index 000000000..47602e0ef Binary files /dev/null and b/src/forge/Server/img/bg2.png differ diff --git a/src/forge/Server/img/gitee.png b/src/forge/Server/img/gitee.png new file mode 100644 index 000000000..221862be2 Binary files /dev/null and b/src/forge/Server/img/gitee.png differ diff --git a/src/forge/Server/img/gitee1.svg b/src/forge/Server/img/gitee1.svg new file mode 100644 index 000000000..3b1577126 --- /dev/null +++ b/src/forge/Server/img/gitee1.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/forge/Server/img/github.png b/src/forge/Server/img/github.png new file mode 100644 index 000000000..213968a36 Binary files /dev/null and b/src/forge/Server/img/github.png differ diff --git a/src/forge/Server/img/github2.svg b/src/forge/Server/img/github2.svg new file mode 100644 index 000000000..6dde707a9 --- /dev/null +++ b/src/forge/Server/img/github2.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/forge/Server/img/logo2.png b/src/forge/Server/img/logo2.png new file mode 100644 index 000000000..be5d0257c Binary files /dev/null and b/src/forge/Server/img/logo2.png differ diff --git a/src/forge/Server/index.scss b/src/forge/Server/index.scss index a0de01e46..ab9749dfe 100644 --- a/src/forge/Server/index.scss +++ b/src/forge/Server/index.scss @@ -451,4 +451,14 @@ } } } +} +.operateBox{ + color: #333; + text-align: center; + background-color:#fafcff; + border-radius:4px 4px 0px 0px; + padding: 50px 0 80px; + .timeBox{ + color: rgba(70, 106, 255, 1) + } } \ No newline at end of file diff --git a/src/forge/Server/reposyncer/component/createJobModal.jsx b/src/forge/Server/reposyncer/component/createJobModal.jsx new file mode 100644 index 000000000..1c0eb1e27 --- /dev/null +++ b/src/forge/Server/reposyncer/component/createJobModal.jsx @@ -0,0 +1,75 @@ +import React, {useState} from 'react'; +import { Button, Modal, Input, Select, message } from 'antd'; +import axios from 'axios'; +const {Option} = Select; + +function DeleteBox({owner, projectsId, visible, setVisible, createJobBy, branchOptions, reload}) { + const [error, setError] = useState(undefined); + const [errorByOther, setErrorByOther] = useState(undefined); + const [branch, setBranch] = useState(undefined); + const [branchByOther, setBranchByOther] = useState(undefined); + + // 新建同步分支点击函数 + function createNew(){ + if(branch && branchByOther){ + // 新建同步分支 + const param = { + gitlink_branch: branch, + job_type: 'TwoWay', + github_branch: undefined, + gitee_branch: undefined + } + createJobBy === 'Github' ? (param.github_branch = branchByOther) : (param.gitee_branch = branchByOther) + axios.post(`/${owner}/${projectsId}/synchronizes/create_jobs.json`,param).then(res=>{ + if(res && res.data.message === "success"){ + setBranch(undefined); + setBranchByOther(undefined); + reload && reload(Math.random()); + message.success('新建成功'); + setVisible(false); + } + }) + }else{ + !branch && setError('请选择仓库分支') + !branchByOther && setErrorByOther('请输入仓库分支') + } + } + + return( + {setVisible(false)}} + footer={
} + width={550} + className="cancelBound createJobBox" + > +
+ + setBranchByOther(e.target.value)} maxLength={50}/> +
{errorByOther}
+
+
+ + +
{error}
+
+
+ ) +} +export default DeleteBox; \ No newline at end of file diff --git a/src/forge/Server/reposyncer/component/editStore.jsx b/src/forge/Server/reposyncer/component/editStore.jsx new file mode 100644 index 000000000..751a2a727 --- /dev/null +++ b/src/forge/Server/reposyncer/component/editStore.jsx @@ -0,0 +1,97 @@ +import React, {forwardRef} from "react"; +import { Button, Form, Input, message } from "antd"; +import gitHub from '../../img/github.png'; +import gitee from '../../img/gitee.png'; +import '../index.scss'; +import axios from "axios"; + +function EditStore(props){ + const { form, history} = props; + const { owner , projectsId } = props.match.params; + const { getFieldDecorator, validateFields , setFieldsValue, setFields } = form; + + function submit() { + validateFields((error,values)=>{ + setFields({ + 'github_address':{value: values.github_address, errors: null}, + 'github_token':{value: values.github_token, errors: null}, + 'gitee_address':{value: values.gitee_address, errors: null}, + 'gitee_token':{value: values.gitee_token, errors: null} + }) + if(!error){ + // 处理空值 + for(let i in values){ + if(!values[i]){ + delete values[i]; + } + } + const keysArr = Object.keys(values); + if(keysArr.indexOf('github_address') === -1 && keysArr.indexOf('gitee_address') === -1){ + // 校验必填一个github或者gitee地址 + form.setFields({github_address: {value:values.github_address,errors:[new Error('请至少输入一个地址')]}}); + form.setFields({gitee_address: {value:values.gitee_address,errors:[new Error('请至少输入一个地址')]}}); + }else if(keysArr.indexOf('github_address') !== -1 && keysArr.indexOf('github_token') === -1){ + form.setFields({github_token: {value:values.github_token,errors:[new Error('请输入Github的授权token')]}}); + }else if(keysArr.indexOf('gitee_address') !== -1 && keysArr.indexOf('gitee_token') === -1){ + form.setFields({gitee_token: {value:values.gitee_token,errors:[new Error('请输入Gitee的授权token')]}}); + }else{ + axios.post(`/${owner}/${projectsId}/synchronizes.json`,values).then(res=>{ + if(res && res.data.message === "success"){ + message.success('绑定成功'); + window.location.href = `/${owner}/${projectsId}/server/reposyncer`; + }else{ + // message.error('绑定失败'); + } + }) + } + } + }) + } + + return
+
+
+
+ + {getFieldDecorator("github_address",{ + rules:[] + })( + + )} + + + {getFieldDecorator("github_token",{ + rules:[] + })( + + )} + +
+ + {getFieldDecorator("gitee_address",{ + rules:[] + })( + + )} + + + {getFieldDecorator("gitee_token",{ + rules:[] + })( + + )} + +
+ 1、在开启仓库同步前,您需要绑定并授权目标同步仓库;
+ 2、完成仓库绑定后,需对仓库内的分支进行二次绑定。当已绑定的分支有代码提交、代码推送变更,将实时同步更新至其他仓库;
+ 3、目前仅提供Github与Gitee平台的仓库同步功能,每个平台支持同时同步一个仓库; +
+ + + + +
+
+
+} +export default Form.create()(forwardRef(EditStore)); \ No newline at end of file diff --git a/src/forge/Server/reposyncer/component/recordList.jsx b/src/forge/Server/reposyncer/component/recordList.jsx new file mode 100644 index 000000000..5b25636d5 --- /dev/null +++ b/src/forge/Server/reposyncer/component/recordList.jsx @@ -0,0 +1,165 @@ +import React, {useState} from "react"; +import { Table, Modal, Button, message } from 'antd'; +import { Link } from "react-router-dom"; +import gitHub1 from '../../img/github2.svg'; +import gitee1 from '../../img/gitee1.svg'; +import anniu from '../../img/anniu.png'; +import anniu2 from '../../img/anniu2.png'; +import '../index.scss'; +import { useEffect } from "react"; +import axios from "axios"; +import CreateJobModal from './createJobModal'; +import Nodata from "../../../Nodata"; + +function RecordList(props){ + const { owner , projectsId, type } = props.match.params; + const { storeDetail} = props; + const [loading, setLoading] = useState(false); + const [data, setData] = useState([]); + // table + const [current, setCurrent] = useState(1); + const [total, setTotal] = useState(0); + const [pageSize, setPageSize] = useState(10); + const [expandedRowKeys, setExpandedRowKeys] = useState([]); + const [deleteRecordId, setDeleteRecordId] = useState(undefined); + const [visible, setVisible] = useState(false); + const [reload, setReload] = useState(undefined); + const [visibleNew, setVisibleNew] = useState(false); + const [branchOptions, setBranchOptions] = useState([]); + const [logInfo, setLogInfo]= useState([]); + + useEffect(()=>{ + axios.get(`/${owner}/${projectsId}/pulls/get_branches.json`, {}).then(res=>{ + res && setBranchOptions(res.data); + }) + }, []) + + useEffect(()=>{ + // 获取仓库同步记录 + axios.get(`/${owner}/${projectsId}/synchronizes/jobs.json`, {params: {type: type, limit: pageSize, page: current}}).then(res=>{ + if(res && res.data && res.data.message === "success"){ + // 处理从后往前删除 + if(current !== 1 && res.data.data.length === 0){ + setCurrent(current-1); + }else{ + res.data.data.map(item=>{ + item.create_time = item.create_time.replace('T', ' ') + }) + setData(res.data.data); + setTotal(res.data.count); + } + } + }) + },[reload, pageSize, current]) + + // 同步状态errorBox + let columns = [ + { title: '序号', dataIndex: 'index', className:"recordColumns", render: (text, item, index) => {(current-1)*pageSize+index + 1} }, + { title: type === 'github' ? 'Github分支' : 'Gitee分支', dataIndex: type === 'github' ? 'github_branch': 'gitee_branch', className:"recordColumns taskName"}, + { title: 'GitLink分支', dataIndex: 'gitlink_branch', className:"recordColumns"}, + { title: '创建时间', dataIndex: 'create_time', className:"primaryColor recordColumns"}, + { title: '同步状态', dataIndex: 'status', className:"recordColumns", render: ()=>开启中}, + { title: '操作', dataIndex: 'action', align: 'center', className:"primaryColor recordColumns", render: (text, item)=>{setVisible(true);setDeleteRecordId(item.id);}}>删除}, + ] + + const customExpandIcon = (props) => { + if (props.expanded) { + return { + props.onExpand(props.record, e); + }}>查看日志 + } else { + return { + axios.get(`/${owner}/${projectsId}/synchronizes/job_logs.json`, {params:{job_id: props.record.id}}).then(res=>{ + if(res && res.data){ + setLogInfo(res.data.data); + props.onExpand(props.record, e); + } + }) + }}>查看日志 + } + } + + const expandRow = (record) => { + return logInfo && logInfo.length > 0 ?
+ {logInfo.map(item=>{ + return
+ {item.create_time + ' [' + item.log_type + '] ' + item.log} +
+ })} +
: } + + // 展开收起行回调 + function onExpand(expanded, record){ + const keys = new Set(expandedRowKeys); + if(expanded){ + keys.add(record.id); + }else{ + keys.delete(record.id); + } + setExpandedRowKeys(Array.from(keys)); + } + + // 改变pagesize + function onShowSizeChange(current, pageSize){ + window.scrollTo(0, 0); + setCurrent(1); + setPageSize(pageSize); + } + + // 切换页数 + function changePage(page, pageSize){ + window.scrollTo(0, 0); + setCurrent(page); + } + + // 删除同步分支 + function deleteRecord(){ + deleteRecordId && axios.delete(`/${owner}/${projectsId}/synchronizes/delete_job.json`, {data: {job_id: deleteRecordId}}).then(res=>{ + if(res && res.data.message === "success"){ + setReload(Math.random()); + message.success('删除成功'); + setVisible(false); + } + }) + } + + return
+
+ 仓库绑定 + > 同步分支 +
+
+ + {type === 'github' ? 'Github' : 'Gitee'}仓库地址 + {storeDetail && {type === 'github' ? storeDetail.github_address : storeDetail.gitee_address}} + +
+ + {setVisible(false)}} + footer={
} + width={535} + className="cancelBound" + > +
!确认删除此同步分支?
+
删除同步分支后,系统将清除此条同步数据及日志,对应分支也将停止自动同步
+
+ + +} +export default RecordList; \ No newline at end of file diff --git a/src/forge/Server/reposyncer/component/storeList.jsx b/src/forge/Server/reposyncer/component/storeList.jsx new file mode 100644 index 000000000..c38930f6e --- /dev/null +++ b/src/forge/Server/reposyncer/component/storeList.jsx @@ -0,0 +1,89 @@ +import React, { useEffect, useState} from "react"; +import { Button, Modal, Icon, message, Select, Input, Tooltip } from "antd"; +import { Link } from "react-router-dom"; +import gitHub1 from '../../img/github2.svg'; +import gitee1 from '../../img/gitee1.svg'; +import logo from '../../img/logo2.png'; +import '../index.scss'; +import axios from "axios"; +import CreateJobModal from './createJobModal'; + +function StoreList(props){ + const { storeDetail} = props; + const { owner , projectsId } = props.match.params; + const [visible, setVisible] = useState(false); + const [visibleNew, setVisibleNew] = useState(false); + const [createJobBy, setCreateJobBy] = useState("Github"); + const [branchOptions, setBranchOptions] = useState([]); + + useEffect(()=>{ + axios.get(`/${owner}/${projectsId}/pulls/get_branches.json`, {}).then(res=>{ + res && setBranchOptions(res.data); + }) + }, []) + + // 取消仓库绑定点击函数 + function updateStore(){ + axios.delete(`/${owner}/${projectsId}/synchronizes/delete.json`).then(res=>{ + if(res && res.data.message === "success"){ + message.success('取消绑定成功'); + setVisible(false); + props.history.push(`/${owner}/${projectsId}/server`) + } + }) + } + + + return
+
Reposyncer仓库同步系统 + {storeDetail !== null && ?} +
+ {/* 空数据 */} + {!storeDetail &&
+ +

欢迎使用跨平台代码同步系统

+
跨平台代码同步系统提供跨托管平台的项目协同开发同步功能。支持用户在任何一个托管平台上的代码提交、代码推送、合并请求等操作自动同步至其他托管平台。不仅增加每个开源项目与开发者的流量,也使不同平台的开源项目维护与更新变得方便与快捷
+
+ +
} + {/* 已绑定仓库信息 */} + {storeDetail &&
+
已绑定仓库地址对已绑定的仓库,请添加同步分支实现分支的跨平台双向同步
+ {storeDetail && storeDetail.github_address &&
+
+
Github仓库地址
+ {storeDetail && storeDetail.github_address} +
+
+ + +
+
} + {storeDetail && storeDetail.gitee_address &&
+
+
Gitee仓库地址
+ {storeDetail.gitee_address} +
+
+ + +
+
} + {/* */} + +
} + {setVisible(false)}} + footer={
} + width={535} + className="cancelBound" + > +
!您确定要清空已绑定仓库?
+
此操作将清空所有绑定仓库/绑定分支及同步日志,请谨慎操作
+
+ +
+} +export default StoreList; \ No newline at end of file diff --git a/src/forge/Server/reposyncer/index.jsx b/src/forge/Server/reposyncer/index.jsx new file mode 100644 index 000000000..5a5d82d8b --- /dev/null +++ b/src/forge/Server/reposyncer/index.jsx @@ -0,0 +1,64 @@ +import React, {useState, useEffect} from "react"; +import { Button, Tooltip } from "antd"; +import './index.scss'; +import logo from '../img/logo2.png'; +import Loadable from "react-loadable"; +import { Route, Switch } from "react-router"; +import Loading from "../../../Loading"; +import { Link } from "react-router-dom"; +import axios from 'axios'; + +// 编辑同步仓库页面 +const EditStore = Loadable({ + loader: () => import("./component/editStore"), + loading: Loading, +}); +// 同步仓库页面 +const StoreList = Loadable({ + loader: () => import("./component/storeList"), + loading: Loading, +}); +// 跨平台代码同步服务-同步记录页面 +const RecordList = Loadable({ + loader: () => import("./component/recordList"), + loading: Loading, +}); + +function Reposyncer(propsF){ + const { owner , projectsId } = propsF.match.params; + + const [storeDetail, setStoreDetail] = useState(null); + + useEffect(()=>{ + // 获取仓库同步详情 + axios.get(`/${owner}/${projectsId}/synchronizes.json`).then(res=>{ + if(res && res.data.message === "success"){ + setStoreDetail(res.data.data); + } + }) + },[]) + + return
+ + ( + + )} + > + ( + + )} + > + ( + + )} + > + +
+} +export default Reposyncer; \ No newline at end of file diff --git a/src/forge/Server/reposyncer/index.scss b/src/forge/Server/reposyncer/index.scss new file mode 100644 index 000000000..449531821 --- /dev/null +++ b/src/forge/Server/reposyncer/index.scss @@ -0,0 +1,295 @@ +.red_border_but, .red_border_but:focus{ + color:#f60011; + background-color:rgba(196, 0, 14, 0.09); + border:1px solid #f60011; + border-radius:5px; + &:hover{ + color:#f60011; + background-color:rgba(196, 0, 14, 0.18); + border-color:#ff727c; + } + &:active{ + color:#f60011; + background-color:rgba(196, 0, 14, 0.22); + border-color:#f60011; + } +} +.blue_border_but, .blue_border_but:focus{ + color:$primary-color; + background-color:rgba(70, 106, 255, 0.09); + border:1px solid #1a47ff; + border-radius:5px; + &:hover{ + color:#6684fe; + background-color:rgba(70, 106, 255, 0.09);; + border-color:#6684fe; + } + &:active{ + color:#1a47ff; + background-color:rgba(70, 106, 255, 0.09); + border-color:#1a47ff; + } +} +.reposyncerBox .headBox, .storeListBox .headBox{ + height:60px; + line-height: 60px; + background-color:#fafcff; + border:1px solid rgba(42, 97, 255, 0.23); + border-radius:3px 3px 0px 0px; + color:#333333; + position: relative; +} +.reposyncerBox{ + padding-bottom: 60px; + .helpBox1{ + width: 14px; + height: 14px; + border-radius: 50%; + color: white; + background-color: #466aff; + line-height: normal; + display: inline-flex; + justify-content: center; + align-items: center; + cursor: default; + } + .nullStoreBox{ + background-color:#fafcff; + border-radius:4px 4px 0px 0px; + text-align: center; + color:#333333; + padding-bottom: 65px; + .loBox{width: 68px;} + .introBox{ + color:#666666; + width: 57%; + margin: 15px auto; + } + .borBox{ + width: 45%; + margin: 0 auto 20px; + border-bottom: 1px solid rgba(90, 117, 193, 0.23); + } + } + .rightContentBox{ + display: flex; + .leftNav{ + width: 185px; + margin-right: 36px; + background-size: 100% 100%; + background-image: url('../img/bg1.png'); + .oneBox{ + padding: 15px; + color:#4c5b76; + display: block; + &.active{ + position: relative; + background-color:rgba(70, 106, 255, 0.06); + color: $primary-color; + &::before{ + content: ''; + position: absolute; + width: 4px; + height: 86%; + left: 0; + top: 4px; + background-color: $primary-color; + } + } + } + } + } +} +.storeListBox{ + .storeListTable{ + width: 100%; + } + .blueSpan{ + color: $primary-color; + } + .createJobBut{ + position: absolute; + top: 11px; + right: 40px; + } + .storeTitle{ + font-weight:700; + color:#151d40; + border-bottom: 1px solid #e0e6f5;; + } + .registerBox{ + width: 85%; + .storeLogo{ + width: 75px; + } + .storeFormItem{ + width: 70%; + } + .has-error .ant-input, .has-error .ant-input:hover, .has-error .storeInput .ant-input{ + border-color:#f60011; + } + #gitHubToken, #giteeToken, .storeInput{ + border-color: #9eaacb; + height: 36px; + &:hover{border-color: $primary-color;} + } + .storeInput .ant-input-group-addon, .storeInput .ant-input{ + border-color: #9eaacb; + } + .tipStoreBox{ + color: #4c5b76; + line-height: 30px; + padding: 10px 0 30px; + } + } + .listStore{ + .showStoreInfo{ + padding: 20px 0 10px; + display: flex; + justify-content: space-between; + align-items: center; + &.dashedBor{ + border-bottom: 1px dashed #e0e6f5; + } + } + .linkSpan{ + color:$primary-color; + &:hover{color: $primary-color-hover;} + } + .storeInfoBox{color: #666;} + .sTil{ + color:#202d40; + display: flex; + align-items: flex-start; + } + } + .expandBox{ + background-color: #171b23; + color: white; + margin: -16px; + padding-bottom: 16px; + .expandCont{ + word-break: break-all; + padding: 16px 16px 0; + } + } + th.recordColumns{ + background-color: rgba(90, 117, 193, 0); + .ant-table-column-title{ + font-family:PingFang SC; + font-size: 16px; + font-weight: 700; + color:#202d40; + } + } + .ant-table-tbody > tr > td.recordColumns{ + color: #333; + font-size: 15px; + border-bottom: 1px dashed #e0e6f5; + &.primaryColor{color: $primary-color;} + } + tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected) td.recordColumns{ + background-color:#f8f9ff !important; + } + .statusBox{ + width:58px; + height:32px; + border-radius:4px; + display: inline-block; + text-align: center; + &.errorBox{ + color:#ff0c0c; + background-color:rgba(230, 0, 6, 0.1); + border:1px solid #fcb6c2;; + } + &.accomplish{ + color:#009c44; + background-color:rgba(83, 255, 163, 0.1); + border:1px solid #00b843;; + } + } + .deleteRecord{ + color:#f60011; + cursor: pointer; + position: relative; + padding-left: 20px; + &::before{ + content: ''; + position: absolute; + top: 6px; + left: 5px; + width: 1px; + height: 9px; + background-color: #dfdfdf;; + } + } + .primaryColor{color: $primary-color;} +} +.cancelBound{ + .ant-modal-header{ + padding: 10px 25px; + background-color: #f8f8f8; + .ant-modal-title{ + font-size: 16px; + text-align: left; + font-weight: normal !important; + } + } + .ant-modal-close{ + top: 0 !important; + .ant-modal-close-x{font-size: 22px;} + } + .bTilModal{ + color:#333333; + .errorRedSpan{ + display: inline-block; + width: 36px; + height: 36px; + border-radius: 50%; + color: white; + background-color:#ca0002; + line-height: 36px; + text-align: center; + } + } + .sTilModal{ + color:#666666; + margin: 30px 0 40px 70px; + } + .ant-modal-footer{ + border-top: none; + text-align: center; + padding-bottom: 50px; + .okBut, .okBut:focus{ + color:#df0002; + border-color: #d9d9d9; + margin-left: 43px; + &:hover{ + border-color:#ff727c; + } + &:active{ + border-color:#f60011; + } + } + } +} +.createJobBox{ + .itemBox{ + display: flex; + align-items: center; + position: relative; + .errorBox{ + position: absolute; + top: 35px; + left: 130px; + color:#f60011; + } + } + .labelBox{ + width: 125px; + .icon-a-bitian2x{color: #fe1010;} + } + .inputBox{ + width: 80%; + } +} \ No newline at end of file diff --git a/src/forge/Settings/Webhooks/Index.jsx b/src/forge/Settings/Webhooks/Index.jsx index 3be76bd3f..652965a09 100644 --- a/src/forge/Settings/Webhooks/Index.jsx +++ b/src/forge/Settings/Webhooks/Index.jsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'; import { Banner , AlignCenter } from '../../Component/layout'; import DeleteBox from '../../Component/DeleteModal/Index'; import './Index.scss'; -import { Button , List, Pagination, Modal, Tooltip } from 'antd'; +import { Button , List, Pagination, Modal, Tooltip, Popover } from 'antd'; import axios from 'axios'; const limit = 15; @@ -118,10 +118,14 @@ function Index(props) { {i.isJianMu ? {i.url} : i.url} - + {!i.isJianMu && - + } + {i.isJianMu && + + + } ) })} diff --git a/src/forge/Settings/Webhooks/Index.scss b/src/forge/Settings/Webhooks/Index.scss index 0c4dc0e62..b173ee17f 100644 --- a/src/forge/Settings/Webhooks/Index.scss +++ b/src/forge/Settings/Webhooks/Index.scss @@ -177,4 +177,7 @@ } .editWebhookModalTitle{ justify-content: center; +} +.disabledButPopover{ + width: 300px; } \ No newline at end of file diff --git a/src/glcc/api.js b/src/glcc/api.js index 61a02bab9..adcec1fd9 100644 --- a/src/glcc/api.js +++ b/src/glcc/api.js @@ -141,4 +141,56 @@ export function getPassList(params) { method: 'get', params, }); +} + +// 中期考核-查看中期考核信息(返回导师评分) +export function getMediumTermExamineInfo(taskId) { + return fetch({ + url: `/api/mediumTermExamineMaterial/getMediumTermExamineInfo/${taskId}`, + method: 'get' + }); +} + +// 中期考核-查看中期考核信息(不返回返回导师评分) +export function getBriefMediumTermExamineMaterial(taskId) { + return fetch({ + url: `/api/mediumTermExamineMaterial/getBriefMediumTermExamineMaterial/${taskId}`, + method: 'get' + }); +} + +// 中期考核-提交中期考核信息 +export function submitMedium(data) { + return fetch({ + url: `/api/mediumTermExamineMaterial/create`, + method: 'post', + data + }); +} + +// 中期考核-导师提交中期考核评分 +export function submitTutorEvaluation(data) { + return fetch({ + url: `/api/tutorEvaluation/create`, + method: 'post', + data + }); +} + +// 中期考核-导师更新中期考核评分 +export function updateTutorEvaluation(data) { + return fetch({ + url: `/api/tutorEvaluation/update`, + method: 'post', + data + }); +} + +// 中期考核结果list +export function getMediumTermExamineInfoList(params) { + return fetch({ + url: `/api/mediumTermExamineMaterial/getMediumTermExamineInfoList`, + method: 'get', + params, + }); } \ No newline at end of file diff --git a/src/glcc/home/index.jsx b/src/glcc/home/index.jsx index 35c85a917..093891d3b 100644 --- a/src/glcc/home/index.jsx +++ b/src/glcc/home/index.jsx @@ -11,13 +11,15 @@ import banner from "../img/banner.png"; import introduce from "../img/introduce.png"; import apply1 from "../img/apply1.png"; import apply2 from "../img/apply2.png"; +import img1 from "../img/img1.png"; +import img2 from "../img/img2.png"; import teacher from "../img/teacher.png"; import logo from "../img/openmmlab/logo1.png"; import { hasAuditRole } from '../api'; import './index.scss'; export default (props) => { - const { current_user, isGlccApplyDate, showNotification, studentApplyStart, history, secondStudentApplyDate, isStudentApplyDate } = props; + const { current_user, isGlccApplyDate, showNotification, studentApplyStart, history, secondStudentApplyDate, isStudentApplyDate, checkedTaskId, checkTime1, checkTime2, checkTime3 } = props; // function goToApply() { // if (isGlccApplyDate) { // if (current_user && current_user.login) { @@ -31,9 +33,9 @@ export default (props) => { // } // } - const [hasRole, setHhasRole] = useState(false); + const [hasRole, setHasRole] = useState(false); const resultTime1 = new Date().getTime() > new Date('2022-06-28 1:0').getTime() && new Date().getTime() < new Date('2022-07-01 0:0').getTime(); - const resultTime2 = new Date().getTime() > new Date('2022-07-01 12:0').getTime(); + const resultTime2 = new Date().getTime() > new Date('2022-07-01 12:0').getTime() && new Date().getTime() < new Date('2022-08-12 0:0').getTime(); useEffect(() => { if (!current_user.user_id) { @@ -41,7 +43,7 @@ export default (props) => { } hasAuditRole({ userId: current_user.user_id }).then(res => { if (res && res.message == 'success' && res.data.hasRole) { - setHhasRole(true); + setHasRole(true); } }) }, []) @@ -85,6 +87,32 @@ export default (props) => {
查看各课题入选学生名单
} + + {/* 学生中期考核 */} + {!hasRole && checkTime1 && checkedTaskId && +
+ + 中期考核 +
+
学生提交考核材料
+ } + {/* 导师中期考核 */} + {hasRole && checkTime2 && +
+ + 中期考核 +
+
导师拟定中期考核结果
+ } + {/* 中期考核结果公示页 */} + {checkTime3 && +
+ + 考核结果 +
+
中期课题考核结果公示
+ } + {/* 项目报名 */}
@@ -102,7 +130,7 @@ export default (props) => {
选择课题,开启您的开源之旅
*/} - + {/* 导师审核 */} {/* {hasRole && new Date().getTime() < new Date('2022-07-01 0:0').getTime() &&
diff --git a/src/glcc/img/banner-interim.png b/src/glcc/img/banner-interim.png new file mode 100644 index 000000000..38f6bb7d9 Binary files /dev/null and b/src/glcc/img/banner-interim.png differ diff --git a/src/glcc/img/img1.png b/src/glcc/img/img1.png new file mode 100644 index 000000000..084309c1e Binary files /dev/null and b/src/glcc/img/img1.png differ diff --git a/src/glcc/img/img2.png b/src/glcc/img/img2.png new file mode 100644 index 000000000..5f8c18c29 Binary files /dev/null and b/src/glcc/img/img2.png differ diff --git a/src/glcc/img/resultBanner3.png b/src/glcc/img/resultBanner3.png new file mode 100644 index 000000000..b15d8c1e8 Binary files /dev/null and b/src/glcc/img/resultBanner3.png differ diff --git a/src/glcc/index.jsx b/src/glcc/index.jsx index f5908e001..d4277253d 100644 --- a/src/glcc/index.jsx +++ b/src/glcc/index.jsx @@ -61,6 +61,21 @@ const Result = Loadable({ loader: () => import("./checkResult"), loading: Loading, }); +// 中期考核-学生 +const StudentSubmit = Loadable({ + loader: () => import("./interimReview/studentSubmit"), + loading: Loading, +}); +// 中期考核-导师 +const TutorReview = Loadable({ + loader: () => import("./interimReview/tutorReview"), + loading: Loading, +}); +// 中期考核-结果公示 +const MiddleResult = Loadable({ + loader: () => import("./interimReview/result"), + loading: Loading, +}); const Glcc = (propsF) => { const {current_user, showLoginDialog} = propsF; // 判断时间是否在开源夏令营报名时间内(4月15日~5月20日) @@ -70,6 +85,12 @@ const Glcc = (propsF) => { const studentApplyStart = new Date().getTime() > new Date('2022-05-26').getTime(); const isStudentApplyDate = new Date().getTime() > new Date('2022-05-26').getTime() && new Date().getTime() < new Date('2022-06-25 0:0').getTime(); const secondStudentApplyDate = new Date().getTime() > new Date('2022-06-29 1:0').getTime() && new Date().getTime() < new Date('2022-06-30 0:0').getTime(); + // 学生中期考核入口可见时间 + const checkTime1 = new Date().getTime() > new Date('2022-08-12 8:0').getTime() && new Date().getTime() < new Date('2022-08-25 24:0').getTime(); + // 导师中期考核时间 + const checkTime2 = new Date().getTime() > new Date('2022-08-12 10:0').getTime() && new Date().getTime() < new Date('2022-08-25 24:0').getTime(); + // 中期考核结果公示8月26日0点 + const checkTime3 = new Date().getTime() > new Date('2022-08-26 0:0').getTime(); // 用户已报名课题id数组 const [applyTaskId, setApplyTaskId] = useState({}); @@ -78,6 +99,9 @@ const Glcc = (propsF) => { const [cancelCount,setCancelCount]=useState(0); // 用户是否已经被导师选中 const [lockedTaskName, setLockedTaskName] = useState(undefined); + // 用户与导师双向选定的课题id + const [checkedTaskId, setCheckedTaskId] = useState(undefined); + const [studentRegId, setStudentRegId] = useState(undefined); useEffect(()=>{ // 获取用户课题报名信息current_user user_id @@ -88,6 +112,8 @@ const Glcc = (propsF) => { response.data && response.data.registrationStudentTaskList.map(item=>{ data[item.taskId] = item.id; item.locked && setLockedTaskName(item.taskName); + item.passStatus && setCheckedTaskId(item.taskId); + item.passStatus && setStudentRegId(item.studentRegId); }) setApplyTaskId(data); response.data&&setCancelCount(Number(response.data.cancelCount)); @@ -183,11 +209,35 @@ const Glcc = (propsF) => { )} > + {/* 中期审核-结果公示 */} + ( + + )} + > + + {/* 中期审核-学生 */} + ( + + )} + > + + {/* 中期审核-导师 */} + ( + + )} + > + {/* 首页 */} ( - + )} > diff --git a/src/glcc/interimReview/index.scss b/src/glcc/interimReview/index.scss new file mode 100644 index 000000000..3628ae54c --- /dev/null +++ b/src/glcc/interimReview/index.scss @@ -0,0 +1,232 @@ +.interimBox{ + background-image:linear-gradient(180deg,#ebf2ff 0%,#ebf2ff 43.09%,#f3f4f8 100%); + padding-bottom: 125px; + position: relative; + .bg1, .bg2{ + width: 150px; + position: absolute; + top: 45%; + left: 6%; + } + .bg2{ + top: auto; + left: auto; + right: 20%; + bottom: 0; + } + &.glcc-check .bg1{top: 36%;} + .bannerInterim{ + width: 100%; + } + .navBox{ + padding: 20px 0 10px; + color:#202d40; + border-bottom: 1px dashed #bec5d5; + .linkBox{ + color:#a4aabb; + &:hover{color: $primary-color;} + } + } + .mainBox{ + width: 1200px; + margin: 0 auto; + z-index: 1; + position: relative; + } + .tipBox{ + padding: 12px 20px; + color:#6c7283; + line-height: 2.5; + background-color:#e4edff; + .spanBox{color: #000;} + div{line-height: 1.8;margin-bottom: 10px;} + .blueSpan{ + color: $primary-color; + &:hover{color: $primary-color-hover;} + } + } + .titleBox{color: #333;} + .referBox{ + margin-top: 25px; + background-color:rgba(255, 255, 255, 0.27); + border:1px solid #ffffff; + display: flex; + flex-wrap: wrap; + justify-content: space-around; + padding: 30px 0; + position: relative; + .referItem{ + width: 47%; + display: flex; + .ant-form-item-control-wrapper{flex-grow: 1;} + &.oneCont{ + width: 97%; + .ant-upload-list-item{width: 30%;} + .ant-upload-list-item:hover .ant-upload-list-item-info{background: none;} + } + .ant-input, .uploadBox{ + border-color:#b3c3db; + background: none !important; + } + .contentBox{ + padding: 10px 11px 20px + } + } + &.tutor{ + border: none; + margin-top: -20px; + background: none; + .remarkBox{ + position: relative; + flex: 0.97; + .has-error .ant-form-explain{ + position: absolute; + } + } + .oneCont{display: block;} + .wordNum{ + position: absolute; + right: 55px; + bottom: 10px; + } + .referItem{margin-bottom: 5px;} + } + } + .tutorContent{ + border: 1px solid #fff; + margin-top: 40px; + background-color: #f1f6ff; + .task-tabs { + margin-top: 0px; + border: none; + background: none; + .task-title-stuName{ + font-weight:700; + color:#333; + padding-bottom: 25px; + border-bottom:1px dashed #bec5d5; + } + } + } + .reviewBox{ + .successReviewBox{ + color:#2dab4d; + background-color:rgba(82, 206, 152, 0.11); + border:1px solid rgba(36, 167, 67, 0.61); + margin: -5px 20px 20px; + padding: 6px 15px; + } + .title{ + color:#333333; + display: flex; + align-items: center; + .blueBox{ + display: inline-block; + width:5px; + height:14px; + background-color:#466aff; + margin-right: 8px; + } + } + .flexBox{ + color:#202d40; + border-bottom: 1px dashed #bec5d5; + div{ + display: flex; + align-items: baseline; + margin-bottom: 10px; + .blueSpan{ + color: $primary-color; + word-break: break-all; + display: block; + flex: 1; + } + .pptAttachment{ + color: $primary-color; + max-width: 80%; + } + } + } + } + .resultBox{ + padding: 30px 20px 50px 20px; + .nullDateTip{color: #ff3838;} + &.nullData .flexBox div{align-items: center;} + .blueBg{ + width: 85%; + min-height: 36px; + display: inline-block; + padding: 4px 10px; + background-color:rgba(70, 106, 255, 0.05); + border-radius:4px; + word-break: break-all; + } + .mustSpan{ + position: relative; + padding-left: 10px; + &::before{ + content: '*'; + color: #ff3838; + font-size: 18px; + position: absolute; + left: 8px; + top: 3px; + } + &.ppt::before{ + top: -2px; + left: 12px; + } + } + .flexBox{ + border: none; + width: 100%; + } + .tutorRes{ + width: 100%; + background-color:rgba(107, 136, 255, 0.05); + border-radius:4px; + color:#6c7283; + padding: 16px 30px 30px 20px; + .smallTil{ + color: #202d40; + } + .passStatusBox{ + display: inline-flex; + justify-content: center; + align-items: center; + width:59px; + height:28px; + color:#d2001d; + background-color:rgba(251, 0, 34, 0.06); + border:1px solid #df001f; + border-radius:6px; + &.pass{ + color:#2dab4d; + background-color:rgba(82, 206, 152, 0.11); + border-color:rgba(36, 167, 67, 0.61); + } + } + .blueSpan{color: $primary-color;} + } + .tutorRemark{ + word-break: break-all; + flex: 1; + color:#6c7283; + } + } +} +.interimBox.resultListBox{ + .searchBox{ + display: flex; + align-items: center; + } + .resultListTable th.columnsResult.actionBox .ant-table-column-title{ + visibility: visible; + } + .projectDetailBox.nodata{ + margin: 0 auto; + } + .resultListTable .ant-table{ + border: none; + } +} \ No newline at end of file diff --git a/src/glcc/interimReview/result.jsx b/src/glcc/interimReview/result.jsx new file mode 100644 index 000000000..56efa7664 --- /dev/null +++ b/src/glcc/interimReview/result.jsx @@ -0,0 +1,134 @@ +import React, { useEffect, useState } from 'react'; +import { Input, Table, Tooltip, Checkbox } from 'antd'; +import { getMediumTermExamineInfoList } from '../api'; +import ProjectDetail from '../project/component/projectDetail'; +import resultBanner from "../img/resultBanner3.png"; +import bgPng from "../img/bgPng.png"; +import './index.scss'; +import '../checkResult/index.scss'; +import '../project/taskList/index.scss'; +import '../project/index.scss'; +const { Search } = Input; + +// 课题列表 +function CheckResult({current_user, history, checkTime3}) { + // 输入搜索框 + const [keyword, setKeyword] = useState(undefined); + const [data, setData] = useState([]); + // table + const [current, setCurrent] = useState(1); + const [total, setTotal] = useState(0); + const [pageSize, setPageSize] = useState(20); + const [loading, setLoading] = useState(false); + const [expandedRowKeys, setExpandedRowKeys] = useState([]); + + useEffect(()=>{ + // 未到时间 + if(!checkTime3){ + history.push('/glcc'); + } + },[]) + + useEffect(() => { + setLoading(true); + setExpandedRowKeys([]); + const params = { + curPage: current, + keyword, + pageSize + } + getMediumTermExamineInfoList(params).then(response => { + if (response && response.message === "success") { + response.data.rows.map((item, index)=>{ + item.id = (current-1)*pageSize+index + 1; + }) + setData(response.data.rows); + setTotal(response.data.total); + } + setLoading(false); + }) + }, [keyword, current, pageSize]) + + const columns = [ + { title: '序号', dataIndex: 'index', align: 'center', className:"columnsResult", width: '6%', render: (text, item, index) => {(current-1)*pageSize+index + 1} }, + { title: '入选学生', dataIndex: 'studentName', className:"columnsResult taskName", width: '10%', ellipsis: true}, + { title: '课题导师', dataIndex: 'tutorName', className:"columnsResult", width: '10%', ellipsis: true}, + { title: '课题名称', dataIndex: 'taskName', className:"columnsResult", width: '20%', ellipsis: true, render: (text, item) => {window.open(`/glcc/subjects/detail/${item.taskId}`)}}>{text} }, + { title: '项目名称', dataIndex: 'projectName', className:"columnsResult", ellipsis: true, width: '14%', render: (text) => {text} }, + { title: '项目简介', dataIndex: 'introduce', className:"columnsResult", width: '13%', ellipsis: true}, + { title: '答辩视频', dataIndex: 'defenceVideoUrl', className:"columnsResult", width: '15%', ellipsis: true, render: (text, item) => {window.open(`/glcc/subjects/detail/${item.taskId}`)}}>{text}}, + { title: '考核结果', dataIndex: 'totalityEvaluation', align: 'center', className:"columnsResult actionBox", render: (text, item, index) => {text === 'D' || !text ? '未通过' : '通过'}}, + ] + + const customExpandIcon = (props) => { + if (props.expanded) { + return { + props.onExpand(props.record, e); + }}>项目简介 + } else { + return { + props.onExpand(props.record, e); + }}>项目简介 + } + } + + const expandRow = (record) => { + return + } + + // 展开收起行回调 + function onExpand(expanded, record){ + const keys = new Set(expandedRowKeys); + if(expanded){ + keys.add(record.id); + }else{ + keys.delete(record.id); + } + setExpandedRowKeys(Array.from(keys)); + } + + // 改变pagesize + function onShowSizeChange(current, pageSize){ + window.scrollTo(0, 0); + setCurrent(1); + setPageSize(pageSize); + } + + // 切换页数 + function changePage(page, pageSize){ + window.scrollTo(0, 0); + setCurrent(page); + } + + return ( +
+ +
+
+
开源夏令营 / 中期课题考核结果公示
+
+ { setCurrent(1); setKeyword(value) }} /> +
+
+
+ + + + + + ) +} +export default CheckResult; \ No newline at end of file diff --git a/src/glcc/interimReview/studentSubmit.jsx b/src/glcc/interimReview/studentSubmit.jsx new file mode 100644 index 000000000..c20198aca --- /dev/null +++ b/src/glcc/interimReview/studentSubmit.jsx @@ -0,0 +1,134 @@ +import React, { useEffect, useState } from "react"; +import { Form, Upload, Input, Icon, Button } from "antd"; +import { Link } from "react-router-dom"; +import banner from '../img/banner-interim.png'; +import img1 from '../img/img1.png'; +import bg from '../img/bgPng.png'; +import { httpUrl, main_site_url } from '../fetch'; +import {getMediumTermExamineInfo, submitMedium, getBriefMediumTermExamineMaterial} from '../api'; +import './index.scss'; + +function StudentSubmit(props){ + const {form, checkedTaskId, checkTime1, studentRegId, history} = props; + const {getFieldDecorator, validateFieldsAndScroll } = form; + const [detail, setDetail] = useState(undefined); + const [reload, setReload] = useState(undefined); + const [fileList, setFileList] = useState(undefined); + // 学生提交材料时间 + const submitTime = new Date().getTime() < new Date('2022-08-21 24:0').getTime(); + // 学生查看导师评分时间 + const lookTime = new Date().getTime() > new Date('2022-08-24 24:0').getTime() && new Date().getTime() < new Date('2022-08-25 24:0').getTime(); + + useEffect(()=>{ + if(!checkTime1){ + history.push("/glcc"); + } + }, []) + + useEffect(()=>{ + // 查询是否已经提交考核材料 + if(lookTime){ + // 8.25学生可见导师评分 + checkedTaskId && getMediumTermExamineInfo(checkedTaskId).then(res=>{ + if(res && res.message === "success"){ + setDetail(res.data); + } + }) + }else{ + // 25号之前不可见 + checkedTaskId && getBriefMediumTermExamineMaterial(checkedTaskId).then(res=>{ + if(res && res.message === "success"){ + setDetail(res.data); + } + }) + } + },[checkedTaskId, reload]) + + // 学生提交材料 + function submit(e){ + e.preventDefault(); + validateFieldsAndScroll((err, values) => { + if(!err){ + const {pptAttachment, defenceVideoUrl, codeOrPrUrl} = values; + const pptAttachmentId = pptAttachment.file.response.data.id; + const params = { + pptAttachmentId, + codeOrPrUrl, + defenceVideoUrl, + taskId: checkedTaskId, + studentRegId: studentRegId + } + submitMedium(params).then(res=>{ + if(res && res.message === "success"){ + setReload(Math.random()); + } + }) + } + }) + } + // 检查文件上传是否符合规定 + function changeFileList(e) { + const {fileList} = e; + const lastFile = fileList.splice(fileList.length-1, fileList.length); + setFileList(lastFile); + } + return
+ + + +
+
开源夏令营 / 提交中期考核材料
+
+
材料提交说明:
+
1、请各位学生下载PPT模板 ,根据课题开发进展,按照PPT模板要求填写课题学习调研方案、开发进度及开发成果、下半阶段工作计划等考核材料;
+
2、欢迎各位学生录制本课题答辩视频,将视频链接填写至下方视频介绍填写栏;
+
3、学生提交考核材料的时间为2022年8月12日8点至2022年8月21日24点,请在截止时间前提交。若在截止时间未提交中期考核表,则默认自动放弃,该课题将自动终止;
+
4、学生在2022年8月25日可在此页面查看自己考核成绩,若对考核成绩有异议,请及时联系导师进行更改。
+
+
+ + 中期考核 +
+ {submitTime && !detail ?
+ + {getFieldDecorator('defenceVideoUrl', { + rules: [{ required: true, message: '请输入视频链接!'}, {pattern: /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/,message: "请正确输入链接"}], + })()} + + + {getFieldDecorator('codeOrPrUrl', { + rules: [{pattern: /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/,message: "请正确输入链接"}], + })()} + + + {getFieldDecorator('pptAttachment', { + rules: [{ required: true, message: '请上传PPT附件!' }], + })( + )} + + + + + :
+
+ + +
   PPT附件{detail && }{detail ? {`${detail.pptAttachment.fileName}`} : }{detail && detail.pptAttachment && detail.pptAttachment.fileSizeString}
+
+ {!detail &&
很遗憾,您在指定时间内未提交考核材料
} + {lookTime && detail && !detail.glccTutorEvaluation &&
您的课题导师暂未评分,请提醒导师尽快提交中期考核评分
} + {detail && detail.glccTutorEvaluation &&
+
您的课题导师对您的中期考核评价如下,如有异议,请及时联系导师进行更改。
+
+ 工作态度: {detail.glccTutorEvaluation.workAttitudeEvaluation}
+ 开发进度: {detail.glccTutorEvaluation.developProgressEvaluation}
+ 项目完成质量: {detail.glccTutorEvaluation.projectQualityEvaluation}
+ 总体评分: {detail.glccTutorEvaluation.totalityEvaluation}{detail.glccTutorEvaluation.totalityEvaluation === 'D' ? '未通过' : '通过'} +
+
导师评语: {detail.glccTutorEvaluation.comment}
+
} +
} +
+
+} +export default Form.create()(StudentSubmit); \ No newline at end of file diff --git a/src/glcc/interimReview/tutorReview.jsx b/src/glcc/interimReview/tutorReview.jsx new file mode 100644 index 000000000..9e3289a76 --- /dev/null +++ b/src/glcc/interimReview/tutorReview.jsx @@ -0,0 +1,183 @@ +import React, {useState, useEffect, useCallback} from "react"; +import { Form, Input, Icon, Button, Tabs, Radio, message } from "antd"; +import { Link } from "react-router-dom"; +import bg from '../img/bgPng.png'; +import { getAuditList, getMediumTermExamineInfo, submitTutorEvaluation, updateTutorEvaluation, hasAuditRole } from '../api'; +import './index.scss'; +import '../check/index.scss' +import Nodata from "../../forge/Nodata"; +import {httpUrl} from '../fetch'; +const { TabPane } = Tabs; +const { TextArea } = Input; + +function TutorReview(props){ + const {form, current_user, showNotification, checkTime2, history} = props; + const {getFieldDecorator, setFieldsValue, validateFieldsAndScroll, resetFields } = form; + const [taskId, setTaskId] = useState(); + const [taskList, setTaskList] = useState([]); + const [wordNum, setWordNum] = useState(0); + const [detail, setDetail] = useState(undefined); + const [reload, setReload] = useState(undefined); + // 导师提交过之后是否是编辑状态 + const [isEdit, setIsEdit] = useState(false); + + useEffect(() => { + if(!checkTime2){ + history.push("/glcc"); + }else if (!current_user.login) { + history.push('/login?go_page=/glcc/middle/examination'); + }else{ + hasAuditRole({ userId: current_user.user_id }).then(res => { + if (!(res && res.message == 'success' && res.data.hasRole)) { + history.push('/glcc'); + } + }) + } + getAuditList({ userId: current_user.user_id, pass: 1 }).then(res => { + if (res.message === 'success') { + const filterStuNull = res.data.rows.filter(item=>{return item.studentName !== null}) + setTaskList(filterStuNull); + filterStuNull.length && setTaskId(filterStuNull[0].id); + } else { + res && showNotification(res.message || '查询课题列表失败'); + } + }) + }, []) + + useEffect(()=>{ + // 查询是否已经提交考核材料 + taskId && getMediumTermExamineInfo(taskId).then(res=>{ + if(res && res.message === "success"){ + setDetail(res.data); + if(res && res.data && res.data.glccTutorEvaluation){ + setWordNum(res.data.glccTutorEvaluation.comment.length); + setFieldsValue({...res.data.glccTutorEvaluation}); + } + } + }) + },[taskId, reload]) + + // 学生提交材料 + function submit(e){ + e.preventDefault(); + validateFieldsAndScroll((err, values) => { + if(!err){ + const params = { + ...values, + mediumTermExamineMaterialId: detail.id, + tutorUserId: detail.studentRegId + } + if(detail.glccTutorEvaluation){ + params['id'] = detail.glccTutorEvaluation.id; + updateTutorEvaluation(params).then(res=>{ + if(res && res.message === "success"){ + setIsEdit(false); + message.success("修改成功"); + setReload(Math.random()); + } + }) + }else{ + submitTutorEvaluation(params).then(res=>{ + if(res && res.message === "success"){ + message.success("您已成功评分"); + setReload(Math.random()); + } + }) + } + } + }) + } + + const RadioGroup = + S + A + B + C + D + ; + + const helper = useCallback( + (label, name, rules, widget, className) => ( + + {getFieldDecorator(name, { rules, validateFirst: true })(widget)} + + ),[taskId, reload] + ); + + return
+ + +
+
开源夏令营 / 导师拟定考核结果
+
+
导师考核说明:
+
1、请各位导师根据“工作态度”“开发进度”“项目完成质量”“总体评分”四个角度,根据学生提交的考核材料与实际开发情况客观地进行打分。打分标准分为:S:特别优秀、A:优秀、B:良好、C:合格、D:不合格五个等级。
+
2、“总体评分”这一项将决定学生是否通过本次考核。若总体评分为“S、A、B、C”,则视为通过中期考核。若该结果为“D”,则该课题中期考核不通过,课题将自动终止。请各位导师谨慎做出评价。
+
3、导师提交打分结果后,可对考核结果进行更改,更改考核结果截止日期为2022年8月25日24点
+
4、北京时间2022年8月26日20点前GLCC官网将公布中期考核结果,敬请留意。
+
+
+ { resetFields(); setTaskId(e); setIsEdit(false); setWordNum(0); }} activeKey={taskId + ''}> + { + taskList.map((item, i) => { + return +
{item.studentName} — {item.taskName}
+
+ }) + } +
+ {detail ?
+ {/* 已评分提示语句 */} + {detail.glccTutorEvaluation && !isEdit &&
您已评分成功! 在审核期间,您可对评分结果进行更改
} + {/* 学生提交的资料 */} +
+
基本信息
+
+
     答辩视频:{detail.defenceVideoUrl}
+
代码/pr地址:{detail.codeOrPrUrl}
+
     PPT附件:{detail.pptAttachment.fileName}{detail.pptAttachment.fileSizeString}
+
+
+
导师评分
+ {/* 导师打分 */} +
+ {helper('工作态度', + 'workAttitudeEvaluation', + [{ required: true, message: "请打分" }], + RadioGroup + )} + {helper('工作进度', + 'developProgressEvaluation', + [{ required: true, message: "请打分" }], + RadioGroup + )} + {helper('项目完成质量', + 'projectQualityEvaluation', + [{ required: true, message: "请打分" }], + RadioGroup + )} + {helper('总体评分', + 'totalityEvaluation', + [{ required: true, message: "请打分" }], + RadioGroup + )} +
+ {helper('填写评语', + 'comment', + [{ required: true, message: "请输入评语!" }], +