Merge remote-tracking branch 'upstream/pre_dev_military' into feature/signOnline

This commit is contained in:
黄心宇 2023-09-25 11:44:33 +08:00
commit e1c3247493
31 changed files with 2146 additions and 1849 deletions

3564
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -134,12 +134,12 @@ li.ant-menu-item{
}
@media screen and (max-width: 1700px){
.handleBox{
right:140px;
right:100px;
}
}
@media screen and (max-width: 1600px){
.handleBox{
right:90px;
right:75px;
}
}
@media screen and (max-width: 1450px){

View File

@ -5,7 +5,7 @@ import './Component.scss';
export default (()=>{
return(
<div className="handleBox">
<a href="https://help.osredm.com/" target="_blank" >
<a href="https://help.osredm.com/#/chuangke" target="_blank">
<img src={Handbook} alt=""/>
</a>
</div>

View File

@ -151,23 +151,26 @@ const Competition = (props) => {
if (qzDetail) {
const {start_at, enroll_date, upload_date} = qzDetail
applyStatus = start_at ? (getTime() > getTime(start_at) && getTime() < getTime(enroll_date)) : getTime() < getTime(enroll_date);
referStatus = !applyStatus && getTime() < getTime(upload_date);
referStatus = competitionId.toLowerCase() === 'jqtj' ? getTime() < getTime(upload_date) : !applyStatus && getTime() < getTime(upload_date);
referEnd = getTime() > getTime(upload_date);
}
// 参赛报名
function applyCompetition(){
if(!applyStatus){
props.showNotification(getTime() > getTime(qzDetail && qzDetail.enroll_date) ? "报名时间已截止!" : "暂未开始报名");
}else if(current_user && !current_user.login){
if(current_user && !current_user.login){
props.showLoginDialog();
}else if(['zstp2022', 'zstp2023'].includes(competitionId) && (applyStatus || ( enrollStatus && enrollStatus.status>1))){
// ccks竞赛已报名用户允许进入报名详情页查看信息
history.push(`/competition/${competitionId}/apply`);
}else if(!applyStatus){
props.showNotification(getTime() > getTime(qzDetail && qzDetail.enroll_date) ? "报名时间已截止!" : "暂未开始报名");
}else{
history.push(`/competition/${competitionId}/apply`);
}
}
function goToRefer() {
if(applyStatus){
if(applyStatus && competitionId.toLowerCase() !== 'jqtj'){
props.showNotification("竞赛尚未开始提交作品!");
}else if(referEnd){
props.showNotification("比赛已结束!");
@ -249,7 +252,7 @@ const Competition = (props) => {
</li>
{/* 处于报名阶段正常跳转到报名页面,不处于右侧弹消息 */}
<li className={active === "apply" ? "active" : ""}><a onClick={applyCompetition}>参赛报名</a></li>
<li className={['refer', 'record'].includes(active) ? "active" : ""}><a onClick={goToRefer}>提案提交</a></li>
<li className={['refer', 'record'].includes(active) ? "active" : ""}><a onClick={goToRefer}>{ competitionId.toLowerCase() === 'jqtj' ? '算法模拟' : '提案提交' }</a></li>
{/* {competitionId === 'qz2022' ? <li className={active === "apply" ? "active" : ""}>
<Link to={{ pathname: applyStatus && current_user && current_user.login ? `/competition/${competitionId}/apply` : '' }} onClick={() => { current_user && current_user.login ? !applyStatus && props.showNotification("报名时间已截止") : props.showLoginDialog() }}>参赛报名</Link>
</li> : <li className={active === "apply" ? "active" : ""}>
@ -272,7 +275,7 @@ const Competition = (props) => {
{competitionId === 'qz2022' && <li className={active === "statistics" ? "active" : ""}>
<Link to={{ pathname: `/competition/${competitionId}/statistics` }}>数据统计</Link>
</li>}
{competitionId!== 'JQTJ' &&<li className={active === "chat" ? "active" : ""}>
{competitionId.toLowerCase()!== 'jqtj' &&<li className={active === "chat" ? "active" : ""}>
<Link to={{ pathname: `/competition/${competitionId}/chat` }}>交流互动</Link>
</li>}
<li className={active === "contact" ? "active" : ""}>

View File

@ -170,3 +170,12 @@ export function matchReplay(data) {
data: data
});
}
// 报名列表-获取用户模拟成绩
export function getScoreList(data) {
return fetch({
url: '/api/match/simulated_match_team_list',
method: 'post',
data: data
});
}

View File

@ -197,7 +197,8 @@ export default Form.create()((props) => {
//
function beforeUpload(file) {
const isLt100M = file.size / 1024 / 1024 < 10;
const isType = file.type === "image/png" || file.type === "image/jpg" || file.type === "image/jpeg" || file.name.endsWith(".zip") || file.name.endsWith(".rar");
const isType = ["image/png", "image/jpg", "image/jpeg"].includes(file.type) ||
file.name.endsWith(".zip") || file.name.endsWith(".rar") || file.name.endsWith(".doc") || file.name.endsWith(".docx") || file.name.endsWith(".pdf");
if (!isType) {
message.error("只能上传指定类型文件");
}
@ -343,7 +344,7 @@ export default Form.create()((props) => {
"enroll_template_id",
[{ required: true, message: "请上传队长单位证明" }],
<Upload
accept=".jpg,.png,.jpeg,.zip,.rar"
accept=".jpg,.png,.jpeg,.zip,.rar,.pdf,.doc,.docx"
action={getUploadActionUrl}
fileList={files}
onChange={handleChange}
@ -361,11 +362,11 @@ export default Form.create()((props) => {
上传
</Button>
<div className="tips mt10">
1请上传加盖单位公章照片证明文件名称命名为CCKS2023军事语言图谱问答评测_参赛队名称_报名
1请上传加盖单位公章报名申请表测试阶段无需盖章文件名称命名为无人机-无人车协同任务挑战赛_参赛队名称_报名
<br />
2文件大小必须小于10MB
<br />
3文件类型仅支持jpgpngjpegziprar格式
3文件类型仅支持jpgpngjpegziprarpdfdocdocx格式
</div>
</Upload>,
'',

View File

@ -0,0 +1,227 @@
import React, { memo, useEffect, useState } from 'react';
import { Modal, Table } from 'antd';
import ColumnGroup from "antd/lib/table/ColumnGroup.js";
import Column from "antd/lib/table/Column.js";
import { encryptionuserId, gradeDetailSimulate } from '../api';
import '../refer/index.scss';
function RecordByJQTJModal(props) {
const {repoId, user_id, id} = props;
const [dataSource1, setDataSource1] = useState(undefined);
const [dataSource2, setDataSource2] = useState(undefined);
const [dataSource3, setDataSource3] = useState(undefined);
const [visible, setVisible] = useState(false);
useEffect(()=>{
id && gradeDetailSimulate({
index: repoId,
user_id: user_id+"",
match_id: id,
sign: encryptionuserId(user_id)
}).then(async res=>{
if(res && res.code === "000"){
await gradeDetail(res.result.grade1_info);
setVisible(true);
}
})
}, [id])
async function gradeDetail(info){
const {score, score_info:{add, minus}, score_info, basic, extra} = info;
//
setDataSource1([{
score, add, basic: score_info.basic, minus
}]);
//
const detail2 = [];
basic && basic.map((item, index)=>{
item.content.map((content1, index1)=>{
content1.content.map((content2, index2)=>{
detail2.push({
item1: item.item,
item1_score: item.item_score,
item1_rowSpan: item.content.length > 1 ? index1 > 0 ? 0 : item.content.length : 1,
item2: content1.sub_item,
item2_score: content1.sub_item_score,
item2_rowSpan: content1.content.length > 1 ? index2 > 0 ? 0 : content1.content.length : 1,
item3: content2.indicator,
item3_score: typeof(content2.indicator_score) === "object" ? content2.indicator_score.join(' | ') : content2.indicator_score,
score: score_info.basic,
score_rowSpan: 0
})
})
})
})
detail2[0].score_rowSpan = detail2.length;
setDataSource2(detail2);
//
const detail3 = [];
extra && extra.map(item=>{
let count = 0;
item.content.map((content1, index1)=>{
content1.content.map((content2, index2)=>{
count++;
detail3.push({
type: item.item,
type_rowSpan: 0,
//
repo: content1.item,
repo_rowSpan: content1.content.length > 1 ? index2 > 0 ? 0 : content1.content.length : 1,
//
repoDetail: content2.sub_item,
//
repoDetailScore: content2.sub_item_score,
// --
repoDetailNum: '--',
//
tpyeScore: item.item === '加分' ? add : minus,
tpyeScore_rowSpan: 0
})
})
})
item.count = count;
})
let index = 0
extra && extra.map(item => {
detail3[index].type_rowSpan = item.count;
detail3[index].tpyeScore_rowSpan = item.count;
index += item.count
})
setDataSource3(detail3);
}
// table
const columns1 = [
{
title: '基础评分',
dataIndex: 'basic',
key: 0
},
{
title: '加分评分',
dataIndex: 'add',
key: 1
},
{
title: '减分评分',
dataIndex: 'minus',
key: 2
},
{
title: '综合评分',
dataIndex: 'score',
key: 3
}
]
// table
const columns3 = [
{
title: '类型',
dataIndex: 'type',
key: 0,
render: (value, row)=>{
return {
children: value,
props:{rowSpan: row.type_rowSpan}
}
}
},
{
title: '加减分项',
dataIndex: 'repo',
key: 1,
render: (value, row)=>{
return {
children: value,
props:{rowSpan: row.repo_rowSpan}
}
}
},
{
title: '加减分子项',
dataIndex: 'repoDetail',
key: 2
},
{
title: '分值',
dataIndex: 'repoDetailScore',
key: 3
},
{
title: '数量',
dataIndex: 'repoDetailNum',
key: 4
},
{
title: '汇总分',
dataIndex: 'tpyeScore',
key: 5,
render: (value, row)=>{
return {
children: value,
props:{rowSpan: row.tpyeScore_rowSpan}
}
}
}
]
return (
<Modal
title="查看详情"
visible={visible}
onCancel={()=>{setVisible(false)}}
footer={null}
width={900}
className="jqtjModal recordDetailModal"
>
<div className="mb15 modalInfoLabel">综合评分</div>
<Table columns={columns1} dataSource={dataSource1} pagination={false} bordered></Table>
<div className="mt20 mb15 modalInfoLabel">基础评分</div>
<Table dataSource={dataSource2} pagination={false} bordered>
<ColumnGroup title="测评项">
<Column title="名称" dataIndex="item1" key={0} render={(value, row)=>{
return {
children: value,
props:{rowSpan: row.item1_rowSpan}
}
}}></Column>
<Column title="分数" dataIndex="item1_score" key={1} render={(value, row)=>{
return {
children: value,
props:{rowSpan: row.item1_rowSpan}
}
}}></Column>
</ColumnGroup>
<ColumnGroup title="测评子项">
<Column title="名称" dataIndex="item2" key={2} render={(value, row)=>{
return {
children: value,
props:{rowSpan: row.item2_rowSpan}
}
}}></Column>
<Column title="分数" dataIndex="item2_score" key={3} render={(value, row)=>{
return {
children: value,
props:{rowSpan: row.item2_rowSpan}
}
}}></Column>
</ColumnGroup>
<Column title="实测指标" dataIndex="item3" key={4}></Column>
<Column title="客观评分" dataIndex="item3_score" key={5}></Column>
<Column title="基础评分" dataIndex="score" key={6} render={(value, row)=>{
return {
children: value,
props:{rowSpan: row.score_rowSpan}
}
}}></Column>
</Table>
<div className="mt20 mb15 modalInfoLabel">加减分</div>
<Table columns={columns3} dataSource={dataSource3} pagination={false} bordered></Table>
</Modal>
)
}
export default memo(RecordByJQTJModal);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 602 KiB

After

Width:  |  Height:  |  Size: 549 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 474 KiB

After

Width:  |  Height:  |  Size: 459 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 435 KiB

After

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 557 KiB

After

Width:  |  Height:  |  Size: 423 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 612 KiB

After

Width:  |  Height:  |  Size: 590 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 472 KiB

After

Width:  |  Height:  |  Size: 448 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 KiB

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 451 KiB

After

Width:  |  Height:  |  Size: 435 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 197 KiB

View File

@ -2,7 +2,7 @@
width: 1200px;
margin: 35px auto 0;
background: #fff;
padding-bottom: 20px;
padding-bottom: 100px;
.qz_manage_head{
color: #181818;
padding: 12px 0 13px 30px;

View File

@ -3,8 +3,10 @@ import { Base64 } from 'js-base64';
import { Select, Button, Tooltip, Input, Popconfirm, message } from 'antd';
import { current_main_site_url } from '../fetch';
import PaginationTable from "../../components/paginationTable";
import { getEnrollList, getProList, updateEnroll, createUser, encryptionuserId } from "../api.js";
import RecordModal from '../components/recordByJQTJModal';
import { getEnrollList, updateEnroll, createUser, encryptionuserId, getScoreList, gradeDetailSimulate } from "../api.js";
import moment from "moment";
import Nodata from "src/forge/Nodata";
const Option = Select.Option;
const { Search } = Input;
@ -20,6 +22,9 @@ function Introduce({ history: { location: { pathname } }, qzDetail, match }) {
const [keyword, setKeyword] = useState(undefined);
const [reload, setReload] = useState(undefined);
const [searchValue, setSearchValue] = useState(undefined);
const [scoreList, setScoreList] = useState({});
const [loadingBut, setLoadingBut] = useState(false);
const [params, setParams] = useState(undefined);
useEffect(() => {
setKeyword(undefined);
@ -34,10 +39,9 @@ function Introduce({ history: { location: { pathname } }, qzDetail, match }) {
page: curPage,
limit: 10,
keyword,
status
status: type === "applys" ? status : '2'
}
if (type === "applys") {
//
//
getEnrollList(params, competitionId).then(response => {
if (response && response.status === 200) {
setTotal(response.data.count);
@ -46,18 +50,6 @@ function Introduce({ history: { location: { pathname } }, qzDetail, match }) {
}).finally(() => {
setLoading(false);
})
} else {
//
delete params.status;
getProList(params, competitionId).then(response => {
if (response && response.status === 200) {
setTotal(response.data.count);
setDataList(response.data.data);
}
}).finally(() => {
setLoading(false);
})
}
}, [type, curPage, keyword, status, reload])
//
@ -81,6 +73,13 @@ function Introduce({ history: { location: { pathname } }, qzDetail, match }) {
})
}
//
function seeDeatil(repoId, user_id, id){
setParams({
repoId, user_id, id
});
}
let columns_apply = useMemo(() => {
return [
{
@ -169,24 +168,9 @@ function Introduce({ history: { location: { pathname } }, qzDetail, match }) {
return <Tooltip title={text} placement="topLeft">{text}</Tooltip>;
}
},
{
title: '作品',
dataIndex: 'attachments',
render: (text, record) => {
return <Tooltip title={text[0] && text[0].title} placement="topLeft"><a href={current_main_site_url + (text[0] && text[0].url)} className="attachments_a">{text[0] && text[0].title}</a></Tooltip>;
}
},
{
title: '操作',
dataIndex: "status",
align: "center",
render: (text, record) => {
return text === 2 ? <Button size="small" disabled>已驳回</Button> : <Popconfirm
title="您确定驳回此用户的作品信息?"
icon={<i className="iconfont icon-shanchu_tc_icon mr3 font-15" style={{ float: 'left', color: "red" }}></i>}
onConfirm={() => { reject(record.user_id, 2) }}
><Button size="small">驳回</Button></Popconfirm>
}
align: "center"
}
];
}, [qzDetail]);
@ -252,6 +236,30 @@ function Introduce({ history: { location: { pathname } }, qzDetail, match }) {
</div>
}
const expandRow1 = (record) => {
const score = scoreList[record.user_id];
return score && score.length ? <div className="expandRowManage">
<div className="row">
<div className="index">序号</div>
<div>模拟项目</div>
<div>模拟时间</div>
<div>模拟分数</div>
<div>操作</div>
</div>
{score.map((item, index) => {
return <div className="row" key={index}>
<div className="index">{index + 1}</div>
<div>{item.subject_name}</div>
<div>{moment(item.submit_time).format('YYYY-MM-DD HH:mm:ss')}</div>
<div>{item.score}</div>
<div>
<Button onClick={()=>{seeDeatil(item.subject_index, record.user_id, item.id)}}>查看详情</Button>
</div>
</div>
})}
</div> : <Nodata _html="暂无数据"/>
}
const customExpandIcon = (props) => {
if (props.record.members && props.record.members.length > 0) {
if (props.expanded) {
@ -264,7 +272,40 @@ function Introduce({ history: { location: { pathname } }, qzDetail, match }) {
}}>查看成员<i className="iconfont icon-jiantou9 font-12 ml5"></i></a>
}
} else {
return <span style={{ color: 'gray' }}>查看成员</span>
return <span style={{ color: 'gray', marginRight: 8 }}>查看成员<i className="iconfont icon-jiantou9 font-12 ml5"></i></span>
}
}
const customExpandIcon1 = (props) => {
if (props.expanded) {
return <Button type="link" onClick={e => {
props.onExpand(props.record, e);
}}>查看模拟详情<i className="iconfont icon-changyongtubiao-xianxingdaochu-zhuanqu- font-12 ml5"></i></Button>
} else {
return <Button type="link" loading={loadingBut} onClick={e => {
const user_id = props.record.user_id;
if(scoreList[user_id]){
props.onExpand(props.record, e);
}else{
setLoadingBut(true);
getScoreList({
user_id: user_id+"",
sign: encryptionuserId(user_id)
}).then(response=>{
if (response && response.msg === "成功") {
const list = {...scoreList};
list[user_id] = response.result
setScoreList(list);
setTimeout(() => {
props.onExpand(props.record, e);
}, 0);
}else{
message.error(response.msg);
}
setLoadingBut(false);
})
}
}}>查看模拟详情<i className="iconfont icon-jiantou9 font-12 ml5"></i></Button>
}
}
@ -286,8 +327,9 @@ function Introduce({ history: { location: { pathname } }, qzDetail, match }) {
</div>
<div>
{type === "applys" && <a href={current_main_site_url + `/api/competition_infos/${competitionId}/enroll_template.zip`}><Button className="but_2e5 ml10">导出单位证明</Button></a>}
{type === "production" && <a href={current_main_site_url + `/api/competition_infos/${competitionId}/enroll_list.zip?upload=true`}><Button className="but_2e5 ml10">导出作品文件</Button></a>}
<a href={current_main_site_url + `/api/competition_infos/${competitionId}/enroll_list.xlsx${type === "applys" ? '' : "?upload=true"}`}><Button className="but_2e5 ml10">导出{type === "applys" ? '报名信息' : '作品信息'}</Button></a>
{type === "applys" && <a href={current_main_site_url + `/api/competition_infos/${competitionId}/enroll_list.xlsx`}><Button className="but_2e5 ml10">导出报名信息</Button></a>}
{/* {type === "production" && <a href={current_main_site_url + `/api/competition_infos/${competitionId}/enroll_list.zip?upload=true`}><Button className="but_2e5 ml10">导出作品文件</Button></a>} */}
{/* <a href={current_main_site_url + `/api/competition_infos/${competitionId}/enroll_list.xlsx${type === "applys" ? '' : "?upload=true"}`}><Button className="but_2e5 ml10">导出{type === "applys" ? '报名信息' : '作品信息'}</Button></a> */}
</div>
</div>
@ -299,11 +341,15 @@ function Introduce({ history: { location: { pathname } }, qzDetail, match }) {
total={total}
setCurPage={(page) => setCurPage(page)}
current={curPage}
expandedRowRender={qzDetail && qzDetail.is_local ? expandRow : expandRowWai}
expandIconColumnIndex={type === "applys" ? qzDetail && qzDetail.is_local ? 7 : 4 : 20}
expandedRowRender={type === "applys" ? qzDetail && qzDetail.is_local ? expandRow : expandRowWai : expandRow1}
expandIconColumnIndex={type === "applys" ? qzDetail && qzDetail.is_local ? 7 : 4 : 4}
expandIconAsCell={false}
expandIcon={customExpandIcon}
expandIcon={type === "applys" ? customExpandIcon : customExpandIcon1}
pageSize={10} />
{/* 模拟成绩详情 */}
<RecordModal
{...params}
/>
</div>
)
}

View File

@ -105,7 +105,7 @@ function Introduce({ form, match, history, current_user }) {
//
function beforeUpload(file){
const isZip = file.type === 'application/x-zip-compressed';
const isZip = file.name.endsWith(".zip");
if (!isZip) {
message.error('只能zip格式!');
}

View File

@ -104,10 +104,12 @@ function Introduce(props) {
})
item.count = count;
})
detail3[0].type_rowSpan = extra[0].count;
detail3[0].tpyeScore_rowSpan = extra[0].count;
detail3[extra[0].count].type_rowSpan = extra[1].count;
detail3[extra[0].count].tpyeScore_rowSpan = extra[1].count;
let index = 0
extra && extra.map(item => {
detail3[index].type_rowSpan = item.count;
detail3[index].tpyeScore_rowSpan = item.count;
index += item.count
})
setRecordDetail3(detail3);
setRecordDetailModalVis(true);
}

View File

@ -269,3 +269,9 @@ span.list-gray {
.ant-modal-confirm-confirm{
top:150px;
}
.helpByTask{
position: fixed;
bottom: 25vh;
right: 15vw;
}

View File

@ -11,6 +11,7 @@ import polify from '../image/polify.png';
import '../index.scss';
import '../phone.scss';
import { Link } from 'react-router-dom';
import Handbook from 'src/forge/Component/Handbook';
const Search = Input.Search;
@ -188,6 +189,8 @@ export default ({ history, current_user, showLoginDialog, location, mygetHelmeta
return (
<div className="centerbox" style={{ marginTop: '20px' }}>
{/* 帮助中心 */}
<Handbook/>
{/* 选项条件 */}
<div className="nav-content">