feat: 刷题页面

This commit is contained in:
秋水浮尘
2023-10-09 00:41:35 +08:00
parent 05284248dd
commit 4ed48fbe2b
71 changed files with 10534 additions and 152 deletions

View File

@@ -1,10 +0,0 @@
const Home = () => {
return (
<div>
home
</div>
)
}
export default Home

BIN
src/views/imgs/clickImg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
src/views/imgs/head.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
src/views/imgs/javaImg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
src/views/imgs/logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -0,0 +1,99 @@
import React, { Fragment, Component } from 'react';
import req from '@utils/request';
import RankingBox from '../ranking-box';
import { imgObject, apiName, RankingType } from '../../constant';
import { mockRankingModuleList } from '../../mock';
const rankingBackImg = {
0: imgObject.ranking1Img,
1: imgObject.ranking2Img,
2: imgObject.ranking3Img,
};
class ContributionList extends Component {
constructor(props) {
super(props);
this.state = {
contributionList: mockRankingModuleList[1].rankingList || [],
contributeType: 1,
isLoading: false,
};
}
componentDidMount() {
// this.getContributeList();
}
/**
* 获得贡献榜
*/
getContributeList() {
const { contributeType } = this.state;
let params = {
contributeType: contributeType,
};
req({
method: 'post',
data: params,
url: apiName.getContributeList,
})
.then((res) => {
if (res.data && res.data.length > 0) {
this.setState({
contributionList: res.data,
isLoading: false,
});
} else {
this.setState({
contributionList: [],
isLoading: false,
});
}
})
.catch((err) => console.log(err));
}
/**
* 切换排行榜
* @param {*} type
* @returns
*/
onChangeRanking = (type) => {
this.setState(
{
contributeType: type,
isLoading: true,
},
() => {
this.getContributeList();
}
);
};
/**
* 去录题
*/
onChangeJump = () => {
this.props.history.push('/upload-questions');
};
render() {
const { contributionList, isLoading, contributeType } = this.state;
return (
<Fragment>
{contributionList?.length > 0 && (
<RankingBox
isLoading={isLoading}
rankingList={contributionList}
currentActive={contributeType}
rankingType={RankingType.contribution}
onHandleRanking={this.onChangeRanking}
onHandleJump={this.onChangeJump}
/>
)}
</Fragment>
);
}
}
export default ContributionList;

View File

@@ -0,0 +1,151 @@
.ranking-list-box {
display: flex;
flex-direction: column;
justify-content: center;
margin-bottom: 20px;
padding: 0px 16px 0px;
width: 100%;
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.08);
.ranking-list-header {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 50px;
font-size: 16px;
color: rgba(0, 0, 0, 0.85);
border-bottom: 1px solid #f3f3f6;
.ranking-list-title {
}
.ranking-list-btns {
display: flex;
.ranking-list-btn {
display: flex;
justify-content: center;
align-items: center;
margin-right: 4px;
width: 48px;
height: 30px;
font-size: 12px;
cursor: pointer;
transition: all 0.5s;
&:last-child {
margin-right: 0px;
}
&:hover {
@include font-color();
}
}
.ranking-list-btn-active {
font-weight: 600;
@include font-color();
border-bottom: 1px solid rgba(60, 110, 238, 1);
}
}
}
.ranking-list {
// height: 326px;
// overflow-y: scroll;
font-size: 14px;
.ranking-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 12px 10px 6px;
color: #999999;
font-size: 14px;
cursor: pointer;
.ranking-left {
display: flex;
align-items: center;
.ranking-icon {
margin-right: 10px;
// margin-top: 14px;
width: 20px;
height: 26px;
line-height: 17px;
text-align: center;
background-size: 100% 100%;
}
.ranking-head-img {
margin-right: 4px;
width: 40px;
height: 40px;
border-radius: 50%;
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.08);
.ranking-head-icon {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.ranking-info {
.ranking-name {
margin-bottom: 2px;
color: #666666;
}
.ranking-department {
font-size: 12px;
}
}
}
.ranking-right {
display: flex;
align-items: center;
color: #c3c3c6;
}
&:hover {
@include box-backgroundColor(0.05);
}
}
}
.ranking-btn-go {
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
margin-top: 12px;
margin-bottom: 12px;
cursor: pointer;
width: 230px;
height: 36px;
border-radius: 30px;
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.1);
@include box-backgroundColor(0.9);
.ranking-btn-go-icon {
width: 22px;
height: 22px;
background-size: 100% 100%;
margin-right: 4px;
}
.ranking-btn-text {
font-size: 14px;
font-weight: bold;
color: #fff;
}
&:hover {
font-weight: bold;
}
}
&:last-child {
margin-bottom: 0;
}
}
::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
}
.tooltip-info {
font-size: 12px;
}
.popover-img {
margin-left: 4px;
cursor: pointer;
width: 16px;
height: 16px;
}

View File

@@ -0,0 +1,96 @@
import React from 'react';
import { Popover, Spin } from 'antd';
import { debounce } from '@utils';
import { imgObject, RankingTypeText, RankingTypeBtnText } from '../../constant';
import './index.less';
const rankingBackImg = {
0: imgObject.ranking1Img,
1: imgObject.ranking2Img,
2: imgObject.ranking3Img,
};
export default function RankingBox(props) {
const { isLoading, currentActive, rankingList, rankingType } = props;
const onChangeRanking = (type) =>
debounce(() => {
props.onHandleRanking && props.onHandleRanking(type);
});
const onJump = debounce(() => {
props.onHandleJump && props.onHandleJump();
});
return (
<div className="ranking-list-box">
<div className="ranking-list-header">
<div className="ranking-list-title">{RankingTypeText[rankingType]}</div>
<div className="ranking-list-btns">
<div
onClick={onChangeRanking(1)}
className={`ranking-list-btn ${currentActive === undefined || currentActive === 1
? 'ranking-list-btn-active'
: ''
}`}>
本月排行
</div>
<div
onClick={onChangeRanking(2)}
className={`ranking-list-btn ${currentActive === 2 ? 'ranking-list-btn-active' : ''
}`}>
总排行
</div>
</div>
</div>
<Spin spinning={isLoading}>
<div className="ranking-list">
{rankingList?.length > 0 &&
rankingList.map((item, index) => {
return (
<div className="ranking-item" key={item.id}>
<div className="ranking-left">
<div
className="ranking-icon"
style={{
backgroundImage: `url(${rankingBackImg[index]})`,
}}>
{index + 1}
</div>
<div className="ranking-head-img">
<img src={item.headImg} className="ranking-head-icon" />
</div>
<Popover
title={
<div>
{item.name}
</div>
}
content={
<div className="tooltip-info">
<div>{item.erp}</div>
<div>{item.organizationFullName}</div>
</div>
}>
<div className="ranking-info">
<div className="ranking-name">{item.name}</div>
<div className="ranking-department">
{item.organizationName}
</div>
</div>
</Popover>
</div>
<div className="ranking-right">🔥 {item.count}</div>
</div>
);
})}
</div>
</Spin>
<div className="ranking-btn-go" onClick={onJump}>
<div
className="ranking-btn-go-icon"
style={{
backgroundImage: `url(${imgObject.clickImg})`,
}}></div>
<div className="ranking-btn-text">{RankingTypeBtnText[rankingType]}</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,149 @@
.ranking-list-box {
display: flex;
flex-direction: column;
justify-content: center;
margin-bottom: 20px;
padding: 0px 16px 0px;
width: 100%;
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.08);
.ranking-list-header {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 50px;
font-size: 16px;
color: rgba(0, 0, 0, 0.85);
border-bottom: 1px solid #f3f3f6;
.ranking-list-btns {
display: flex;
.ranking-list-btn {
display: flex;
justify-content: center;
align-items: center;
margin-right: 4px;
width: 48px;
height: 30px;
font-size: 12px;
cursor: pointer;
transition: all 0.5s;
&:last-child {
margin-right: 0px;
}
&:hover {
@include font-color();
}
}
.ranking-list-btn-active {
font-weight: 600;
// @include font-color();
border-bottom: 1px solid rgba(60, 110, 238, 1);
}
}
}
.ranking-list {
// height: 326px;
// overflow-y: scroll;
font-size: 14px;
.ranking-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 12px 10px 6px;
color: #999999;
font-size: 14px;
cursor: pointer;
.ranking-left {
display: flex;
align-items: center;
.ranking-icon {
margin-right: 10px;
// margin-top: 14px;
width: 20px;
height: 26px;
line-height: 17px;
text-align: center;
background-size: 100% 100%;
}
.ranking-head-img {
margin-right: 4px;
width: 40px;
height: 40px;
border-radius: 50%;
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.08);
.ranking-head-icon {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.ranking-info {
.ranking-name {
margin-bottom: 2px;
color: #666666;
}
.ranking-department {
font-size: 12px;
}
}
}
.ranking-right {
display: flex;
align-items: center;
color: #c3c3c6;
}
&:hover {
@include box-backgroundColor(0.05);
}
}
}
.ranking-btn-go {
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
margin-top: 12px;
margin-bottom: 12px;
cursor: pointer;
width: 230px;
height: 36px;
border-radius: 30px;
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.1);
background-color: rgba(60, 110, 238, 0.9);
.ranking-btn-go-icon {
width: 22px;
height: 22px;
background-size: 100% 100%;
margin-right: 4px;
}
.ranking-btn-text {
font-size: 14px;
font-weight: bold;
color: #fff;
}
&:hover {
font-weight: bold;
}
}
&:last-child {
margin-bottom: 0;
}
}
::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
}
.tooltip-info {
font-size: 12px;
}
.popover-img {
margin-left: 4px;
cursor: pointer;
width: 16px;
height: 16px;
}

View File

@@ -0,0 +1,109 @@
import React, { Fragment, Component } from 'react';
import req from '@utils/request';
import { mockRankingModuleList } from '../../mock';
import { imgObject, apiName, RankingType } from '../../constant';
import RankingBox from '../ranking-box';
const rankingBackImg = {
0: imgObject.ranking1Img,
1: imgObject.ranking2Img,
2: imgObject.ranking3Img,
};
class RankingList extends Component {
constructor(props) {
super(props);
this.state = {
moduleList: [],
contributeType: 2,
isLoading: true,
};
}
componentDidMount() {
this.getContributeList();
}
/**
* 获得贡献榜
*/
getContributeList() {
// let params = {
// contributeType: this.contributeType,
// };
// req({
// method: 'post',
// data: params,
// url: apiName.getContributeList,
// })
// .then((res) => {
// if (res.data && res.data.length > 0) {
// this.setState(
// {
// firstCategoryList: res.data,
// isShowSpin: false,
// },
// () => {
// this.getInterviewSubjectList();
// }
// );
// } else {
// this.primaryCategoryId = '';
// this.setState({
// isShowSpin: false,
// });
// }
// })
// .catch((err) => console.log(err));
this.setState({
moduleList: mockRankingModuleList[0].rankingList,
isLoading: false,
});
}
/**
* 切换排行榜
* @param {*} type
* @returns
*/
onChangeRanking = (type, index) => () => {
let { moduleList } = this.state;
moduleList[index].currentActive = type;
this.setState(
{
moduleList,
isLoading: true,
},
() => {
this.getData();
}
);
};
onJump = (e) => () => {
if (e === 2) {
this.props.history.push('/upload-questions');
} else {
this.props.history.push('/practice-questions');
}
};
render() {
const { moduleList, isLoading, contributeType } = this.state;
return (
<Fragment>
<RankingBox
isLoading={isLoading}
rankingList={moduleList}
currentActive={contributeType}
rankingType={RankingType.practice}
onHandleRanking={this.onChangeRanking}
onHandleJump={this.onChangeJump}
/>
</Fragment>
);
}
}
export default RankingList;

View File

@@ -0,0 +1,151 @@
.ranking-list-box {
display: flex;
flex-direction: column;
justify-content: center;
margin-bottom: 20px;
padding: 0px 16px 0px;
width: 100%;
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.08);
.ranking-list-header {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 50px;
font-size: 16px;
color: rgba(0, 0, 0, 0.85);
border-bottom: 1px solid #f3f3f6;
.ranking-list-title {
}
.ranking-list-btns {
display: flex;
.ranking-list-btn {
display: flex;
justify-content: center;
align-items: center;
margin-right: 4px;
width: 48px;
height: 30px;
font-size: 12px;
cursor: pointer;
transition: all 0.5s;
&:last-child {
margin-right: 0px;
}
&:hover {
@include font-color();
}
}
.ranking-list-btn-active {
font-weight: 600;
@include font-color();
border-bottom: 1px solid rgba(60, 110, 238, 1);
}
}
}
.ranking-list {
// height: 326px;
// overflow-y: scroll;
font-size: 14px;
.ranking-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 12px 10px 6px;
color: #999999;
font-size: 14px;
cursor: pointer;
.ranking-left {
display: flex;
align-items: center;
.ranking-icon {
margin-right: 10px;
// margin-top: 14px;
width: 20px;
height: 26px;
line-height: 17px;
text-align: center;
background-size: 100% 100%;
}
.ranking-head-img {
margin-right: 4px;
width: 40px;
height: 40px;
border-radius: 50%;
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.08);
.ranking-head-icon {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.ranking-info {
.ranking-name {
margin-bottom: 2px;
color: #666666;
}
.ranking-department {
font-size: 12px;
}
}
}
.ranking-right {
display: flex;
align-items: center;
color: #c3c3c6;
}
&:hover {
@include box-backgroundColor(0.05);
}
}
}
.ranking-btn-go {
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
margin-top: 12px;
margin-bottom: 12px;
cursor: pointer;
width: 230px;
height: 36px;
border-radius: 30px;
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.1);
@include box-backgroundColor(0.9);
.ranking-btn-go-icon {
width: 22px;
height: 22px;
background-size: 100% 100%;
margin-right: 4px;
}
.ranking-btn-text {
font-size: 14px;
font-weight: bold;
color: #fff;
}
&:hover {
font-weight: bold;
}
}
&:last-child {
margin-bottom: 0;
}
}
::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
}
.tooltip-info {
font-size: 12px;
}
.popover-img {
margin-left: 4px;
cursor: pointer;
width: 16px;
height: 16px;
}

View File

@@ -0,0 +1,133 @@
import JavaImg from '@views/imgs/javaImg.png'
/**
* 难度等级
*/
export const gradeObject = {
1: {
color: 'rgba(60, 110, 238, 0.7)',
title: '初级',
},
2: {
color: 'rgba(60, 110, 238, 0.7)',
title: '中级',
},
3: {
color: 'rgba(60, 110, 238, 0.7)',
title: '高级',
},
4: {
color: 'rgba(60, 110, 238, 0.7)',
title: '资深',
},
5: {
color: 'rgba(60, 110, 238, 0.7)',
title: '专家',
},
};
/**
* 难度筛选
*/
export const filterDifficulty = [
{
id: 0,
title: '全部',
},
{
id: 1,
title: '初级',
},
{
id: 2,
title: '中级',
},
{
id: 3,
title: '高级',
},
{
id: 4,
title: '资深',
},
{
id: 5,
title: '专家',
},
];
export const apiName = {
/**
* 获取一级分类
*/
getPrimaryCategoryInfo: '/admin/question/category/getPrimaryCategoryInfo',
/**
* 获取题目列表
*/
getInterviewSubjectList: '/admin/question/subject/getSubjectList',
/**
* 获得贡献榜
*/
getContributeList: '/admin/question/subject/getContributeList',
};
export const imgObject = {
clickImg:
'https://img13.360buyimg.com/imagetools/jfs/t1/222669/25/807/6590/617f4f06Eb2094586/64c39ce3769b8a16.png',
ranking1Img:
'https://img14.360buyimg.com/imagetools/jfs/t1/206730/39/7751/986/617f4fbaE4e23097a/aa94ca31a9c132b2.png',
ranking2Img:
'https://img10.360buyimg.com/imagetools/jfs/t1/156125/21/27968/948/617f4fbaEcf1da9a9/722ad0917497697a.png',
ranking3Img:
'https://img12.360buyimg.com/imagetools/jfs/t1/213197/17/2682/958/617f4fbbE06c277a9/03ef4c389c52ab8d.png',
timeline:
'https://img13.360buyimg.com/imagetools/jfs/t1/210387/35/7564/555/617f4fbbE0cb305c1/728913d21e650794.png',
backAllImg:
'https://img11.360buyimg.com/imagetools/jfs/t1/206213/24/13307/2603/617f4fc4E676d448d/622d5287fbf5a919.png',
dataImg:
'https://img12.360buyimg.com/imagetools/jfs/t1/207558/34/7606/3672/617f4fc4E1ca685fc/3953a92a6072fba4.png',
javaImg: JavaImg,
npmImg: 'https://img11.360buyimg.com/imagetools/jfs/t1/200551/24/15367/3145/617f4fc4Ea153dc2e/b4bbf2de8807f42d.png',
parallelComputingImg:
'https://img14.360buyimg.com/imagetools/jfs/t1/207198/23/7638/3037/617f4fc4E0e20ab9d/40197a6c79c5a33f.png',
springbootImg:
'https://img13.360buyimg.com/imagetools/jfs/t1/171775/10/24915/4127/617f4fc4Eeb3d356e/cfbfe8d7c3155047.png',
sqlImg: 'https://img13.360buyimg.com/imagetools/jfs/t1/208027/11/7347/3074/617f4fc4Ef11e9495/1093903301db1d1d.png',
systemDesignImg:
'https://img12.360buyimg.com/imagetools/jfs/t1/206967/24/7622/3629/617f4fc4E60a188b3/cb659847c5d4232a.png',
algorithmImg:
'https://img14.360buyimg.com/imagetools/jfs/t1/215758/34/2633/4128/617f4fc4E5dcdab66/727be155858a06a5.png',
defaultImg:
'https://img13.360buyimg.com/imagetools/jfs/t1/155957/24/22934/2028/617a147cE8bcbb57a/7a4885e4ae99a895.png',
};
/**
* 模块类型
*/
export const RankingType = {
/**
* 贡献榜
*/
contribution: 1,
/**
* 排行榜
*/
practice: 2,
};
/**
* 模块名称
*/
export const RankingTypeText = {
[RankingType.contribution]: '贡献榜',
[RankingType.practice]: '综合练习榜',
};
/**
* 对应按钮名字
*/
export const RankingTypeBtnText = {
[RankingType.contribution]: '去出题',
[RankingType.practice]: '去练习',
};

View File

@@ -0,0 +1,35 @@
.question-bank-box {
display: flex;
width: 1439px;
margin: 0 auto;
background-color: #f3f4f6;
border-radius: 5px;
.ant-spin-nested-loading {
flex: 1;
overflow-y: auto;
border-radius: 8px;
.question-box {
// flex: 1;
// overflow-y: auto;
// border-radius: 8px;
.category-list-box {
padding: 24px 24px 6px;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
background-color: #fff;
}
.question-list-box {
margin-top: 1px;
}
}
}
.ranking-box {
margin-left: 16px;
overflow-y: auto;
width: 310px;
}
.ant-spin-nested-loading {
background-color: #fff;
}
}

View File

@@ -0,0 +1,177 @@
import { Component } from 'react';
import QuestionList from '@components/question-list';
import CategoryList from '@components/category-list';
import ContributionList from './components/contribution-list';
import RankingList from './components/ranking-list'
import { apiName } from './constant';
import req from '@utils/request';
import { Spin } from 'antd';
import { mockTabList, mockDataList } from './mock'
import './index.less';
export default class QuestionBank extends Component {
constructor(props) {
super(props);
this.state = {
firstCategoryList: mockTabList || [],
questionList: mockDataList || [],
isShowSpin: false,
};
}
labelList = []; // 选中的标签列表
difficulty = 0; //困难度(全部)
total = 0; // 总条数
pageIndex = 1;
primaryCategoryId = ''; //第一个大类id
componentDidMount() {
// this.getPrimaryCategoryInfo();
// console.log(this.props.route);
}
/**
* 获取一级分类数据
*/
getPrimaryCategoryInfo() {
req({
method: 'post',
data: { subjectTypeList: [4] },
url: apiName.getPrimaryCategoryInfo,
})
.then((res) => {
if (res.data && res.data.length > 0) {
this.primaryCategoryId = res.data[0].primaryCategoryId;
this.setState(
{
firstCategoryList: res.data,
isShowSpin: false,
},
() => {
this.getInterviewSubjectList();
}
);
} else {
this.primaryCategoryId = '';
this.setState({
isShowSpin: false,
});
}
})
.catch((err) => console.log(err));
}
/**
* 获取题目列表
*/
getInterviewSubjectList() {
let params = {
pageInfo: {
pageIndex: this.pageIndex,
pageSize: 10,
},
difficulty: this.difficulty,
primaryCategoryId: this.primaryCategoryId,
assembleIds: this.labelList,
};
req({
method: 'post',
data: params,
url: apiName.getInterviewSubjectList,
})
.then((res) => {
if (res.data && res.data?.pageList?.length > 0) {
this.total = res.data.pageInfo.total;
this.setState({
questionList: res.data.pageList,
isShowSpin: false,
});
} else {
this.total = 0;
this.setState({
questionList: [],
isShowSpin: false,
});
}
})
.catch((err) => console.log(err));
}
/**
* 选择标签时,请求列表数据
* @param {*} primaryCategoryId 一级分类id
* @param {*} assembleIds 三级标签 assembleIds
*/
onChangeLabel = (primaryCategoryId, assembleIds) => {
this.labelList = assembleIds;
this.primaryCategoryId = primaryCategoryId;
this.pageIndex = 1;
this.getInterviewSubjectList();
};
/**
* 切换一级分类
* @param {*} e
*/
onChangeCategory = (e) => {
this.labelList = [];
this.primaryCategoryId = e;
this.pageIndex = 1;
this.getInterviewSubjectList();
};
/**
* 筛选列表数据
* @param {*} id
*/
handleChangeSelect = (id) => {
this.difficulty = id;
this.pageIndex = 1;
this.getInterviewSubjectList();
};
/**
* 分页功能
* @param {*} pageIndex 当前页码
*/
onChangePagination = (pageIndex) => {
this.pageIndex = pageIndex;
this.getInterviewSubjectList();
};
render() {
const { firstCategoryList, questionList, isShowSpin } = this.state;
return (
<div className="question-bank-box">
<Spin spinning={isShowSpin}>
<div className="question-box">
<div className="category-list-box">
{firstCategoryList?.length > 0 && (
<CategoryList
onChangeCategory={this.onChangeCategory}
categoryList={firstCategoryList}
onChangeLabel={this.onChangeLabel}
/>
)}
</div>
<div className="question-list-box">
<QuestionList
pageIndex={this.pageIndex}
total={this.total}
questionList={questionList}
handleChangeSelect={this.handleChangeSelect}
onChangePagination={this.onChangePagination}
difficulty={this.difficulty}
primaryCategoryId={this.primaryCategoryId}
labelList={this.labelList}
/>
</div>
</div>
</Spin>
<div className="ranking-box">
<ContributionList />
<RankingList />
</div>
</div>
);
}
}

View File

@@ -0,0 +1,580 @@
export const mockRankingModuleList = [
{
id: 1,
title: '综合练习榜',
rankingList: [
{
id: 1,
wechatName: 'jcdw',
headImg:
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg1.doubanio.com%2Fview%2Fnote%2Flarge%2Fpublic%2Fp37015927.jpg&refer=http%3A%2F%2Fimg1.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1699286185&t=00db8ff5a1e11783f6c8eba954a5891f',
name: '鸡翅大王1',
count: 160,
},
{
id: 2,
wechatName: 'jcdw1',
headImg:
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg1.doubanio.com%2Fview%2Fnote%2Flarge%2Fpublic%2Fp37015927.jpg&refer=http%3A%2F%2Fimg1.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1699286185&t=00db8ff5a1e11783f6c8eba954a5891f',
name: '鸡翅大王2',
count: 140,
},
{
id: 3,
wechatName: 'jcdw2',
headImg:
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg1.doubanio.com%2Fview%2Fnote%2Flarge%2Fpublic%2Fp37015927.jpg&refer=http%3A%2F%2Fimg1.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1699286185&t=00db8ff5a1e11783f6c8eba954a5891f',
name: '鸡翅大王',
count: 101,
},
{
id: 4,
wechatName: 'jcdw3',
headImg:
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg1.doubanio.com%2Fview%2Fnote%2Flarge%2Fpublic%2Fp37015927.jpg&refer=http%3A%2F%2Fimg1.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1699286185&t=00db8ff5a1e11783f6c8eba954a5891f',
name: '鸡翅小王',
count: 100,
},
{
id: 5,
wechatName: 'jcdw4',
headImg:
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg1.doubanio.com%2Fview%2Fnote%2Flarge%2Fpublic%2Fp37015927.jpg&refer=http%3A%2F%2Fimg1.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1699286185&t=00db8ff5a1e11783f6c8eba954a5891f',
name: '鸡翅大王5',
count: 99,
},
],
},
{
id: 2,
title: '贡献榜',
rankingList: [
{
id: 1,
wechatName: 'jcdw5',
headImg:
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg1.doubanio.com%2Fview%2Fnote%2Flarge%2Fpublic%2Fp37015927.jpg&refer=http%3A%2F%2Fimg1.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1699286185&t=00db8ff5a1e11783f6c8eba954a5891f',
name: '鸡翅小王',
count: 160,
},
{
id: 2,
wechatName: 'jcdw6',
headImg:
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg1.doubanio.com%2Fview%2Fnote%2Flarge%2Fpublic%2Fp37015927.jpg&refer=http%3A%2F%2Fimg1.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1699286185&t=00db8ff5a1e11783f6c8eba954a5891f',
name: '鸡翅大王2',
count: 150,
},
{
id: 3,
wechatName: 'jcdw7',
headImg:
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg1.doubanio.com%2Fview%2Fnote%2Flarge%2Fpublic%2Fp37015927.jpg&refer=http%3A%2F%2Fimg1.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1699286185&t=00db8ff5a1e11783f6c8eba954a5891f',
name: '鸡翅大王',
count: 101,
},
{
id: 4,
wechatName: 'jcdw8',
headImg:
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg1.doubanio.com%2Fview%2Fnote%2Flarge%2Fpublic%2Fp37015927.jpg&refer=http%3A%2F%2Fimg1.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1699286185&t=00db8ff5a1e11783f6c8eba954a5891f',
name: '鸡翅大王4',
count: 100,
},
{
id: 5,
wechatName: 'jcdw9',
headImg:
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg1.doubanio.com%2Fview%2Fnote%2Flarge%2Fpublic%2Fp37015927.jpg&refer=http%3A%2F%2Fimg1.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1699286185&t=00db8ff5a1e11783f6c8eba954a5891f',
name: '鸡翅大王5',
count: 99,
},
],
},
];
export const mockDataList = [
{
questionTitle: '什么是防抖和节流?有什么区别?如何实现',
tags: [
{ name: '排序', id: 590 },
{ name: '数学', id: 5050 },
{ name: '穷举', id: 596 },
{ name: '贪心', id: 592 },
{ name: '二分', id: 5058 },
],
id: 0,
grade: 1,
questionType: 6,
difficulty: 1
},
{
questionTitle: '非空数组某元素只出现1次其余出现2次找到出现1次的元素',
tags: [
{ name: '数学', id: 5050 },
{ name: '穷举', id: 596 },
],
id: 1,
grade: 2,
questionType: 6,
difficulty: 1
},
{
questionTitle: 'Proxy、Observable的区别',
tags: [
{ name: '贪心', id: 592 },
{ name: '二分', id: 5058 },
],
id: 2,
grade: 3,
questionType: 3,
},
{
questionTitle: '谈谈浏览器的回流与重绘如何优化dom渲染呢',
tags: [
{ name: '字符串', id: 579 },
{ name: '模拟', id: 595 },
],
id: 3,
grade: 1,
questionType: 1,
},
{
questionTitle: 'API指标有哪些为什么有白屏现象考虑哪些方向优化',
tags: [
{ name: '字符串', id: 579 },
{ name: '模拟', id: 595 },
],
id: 4,
grade: 2,
questionType: 4,
},
{
questionTitle: '将两个升序链表合并为一个新的升序链表并返回',
tags: [{ name: '数学', id: 5050 }],
id: 5,
grade: 1,
questionType: 2,
},
{
questionTitle: '对MVP架构的理解',
tags: [
{ name: '递归', id: 591 },
{ name: '动态规划', id: 593 },
],
id: 6,
grade: 3,
questionType: 5,
},
{
questionTitle: 'SPA页面的前端路由实现方案',
tags: [
{ name: '排序', id: 590 },
{ name: '数组', id: 578 },
],
id: 7,
grade: 2,
questionType: 1,
},
{
questionTitle: '怎么看 nodejs 可支持高并发',
tags: [
{ name: '数学', id: 5050 },
{ name: '模拟', id: 595 },
],
id: 8,
grade: 2,
questionType: 2,
},
{
questionTitle: '二叉树遍历',
tags: [
{ name: '树', id: 583 },
{ name: '搜索', id: 3381 },
],
id: 9,
grade: 3,
questionType: 4,
},
{
questionTitle: '玛雅人的密码',
tags: [
{ name: '图', id: 584 },
{ name: '搜索', id: 3381 },
],
id: 10,
grade: 1,
questionType: 3,
},
{ questionTitle: '求最大最小数', tags: [], id: 11, grade: 1, questionType: 1 },
{
questionTitle: '最小邮票数',
tags: [{ name: '动态规划', id: 593 }],
id: 12,
grade: 1,
questionType: 1,
},
{ questionTitle: 'abc', tags: [{ name: '穷举', id: 596 }], id: 13, grade: 1, questionType: 5 },
{
questionTitle: '求root(N, k)',
tags: [
{ name: '递归', id: 591 },
{ name: '数学', id: 5050 },
{ name: '二分', id: 5058 },
],
id: 14,
grade: 1,
questionType: 3,
},
{
questionTitle: 'n的阶乘',
tags: [{ name: '数学', id: 5050 }],
id: 15,
grade: 1,
questionType: 3,
},
{
questionTitle: '特殊乘法',
tags: [
{ name: '模拟', id: 595 },
{ name: '数组', id: 578 },
{ name: '数学', id: 5050 },
],
id: 16,
grade: 1,
questionType: 3,
},
{
questionTitle: '今年的第几天?',
tags: [
{ name: '递归', id: 591 },
{ name: '数学', id: 5050 },
{ name: '穷举', id: 596 },
],
id: 17,
grade: 1,
questionType: 2,
},
{
questionTitle: '完数VS盈数',
tags: [{ name: '数学', id: 5050 }],
id: 18,
grade: 1,
questionType: 3,
},
{
questionTitle: '递推数列',
tags: [
{ name: '动态规划', id: 593 },
{ name: '数学', id: 5050 },
],
id: 19,
grade: 1,
questionType: 1,
},
{ questionTitle: '最大序列和', tags: [{ name: '动态规划', id: 593 }], id: 20, grade: 1 },
{
questionTitle: '最小花费',
tags: [
{ name: '动态规划', id: 593 },
{ name: '图', id: 584 },
],
id: 21,
grade: 1,
questionType: 2,
},
{ questionTitle: 'N的阶乘', tags: [{ name: '数学', id: 5050 }], id: 22, grade: 1 },
{
questionTitle: '剩下的树',
tags: [
{ name: '数组', id: 578 },
{ name: '数学', id: 5050 },
{ name: '哈希', id: 585 },
{ name: '栈', id: 581 },
],
id: 23,
grade: 1,
questionType: 5,
},
{
questionTitle: '10进制 VS 2进制',
tags: [
{ name: '数学', id: 5050 },
{ name: '位运算', id: 5074 },
],
id: 24,
grade: 1,
questionType: 3,
},
{
questionTitle: '查找学生信息',
tags: [
{ name: '数组', id: 578 },
{ name: '模拟', id: 595 },
],
id: 25,
grade: 1,
questionType: 2,
},
];
/**
* 一级分类
*/
export const mockTabList = [
{ id: 1, levelName: '全部', count: 100 },
{ id: 2, levelName: '前端', count: 1001 },
{ id: 3, levelName: '后端', count: 1005 },
{ id: 4, levelName: '测试', count: 1100 },
{ id: 5, levelName: '人工智能', count: 1200 },
{ id: 6, levelName: '产品', count: 1 },
{ id: 7, levelName: '视觉', count: 100 },
{ id: 8, levelName: '产品', count: 1 },
{ id: 9, levelName: '视觉', count: 100 },
{ id: 10, levelName: '产品', count: 1 },
{ id: 11, levelName: '视觉', count: 100 },
];
// 二级数据
export const mockCategoryList = {
// 全部
1: [
{
id: 1,
levelName: '算法',
childrenLevelList: [
{ id: 1, levelName: '双指针', count: 107 },
{ id: 2, levelName: '同向双指针', count: 57 },
{ id: 3, levelName: '相向双指针', count: 31 },
{ id: 4, levelName: '二分法', count: 95 },
{ id: 5, levelName: '二分答案', count: 28 },
{ id: 6, levelName: '分治法', count: 77 },
{ id: 7, levelName: '宽度优先搜索', count: 135 },
{ id: 8, levelName: '深度优先搜索/回溯法', count: 10 },
{ id: 9, levelName: '背包型动态规划', count: 224 },
{ id: 10, levelName: '状态压缩动态规划', count: 240 },
{ id: 11, levelName: '拓扑排序', count: 28 },
{ id: 12, levelName: '坐标型动态规划', count: 40 },
{ id: 13, levelName: '划分型动态规划', count: 15 },
{ id: 14, levelName: '记忆化搜索', count: 23 },
{ id: 15, levelName: '区间型动态规划', count: 22 },
{ id: 16, levelName: '动态规划', count: 3 },
{ id: 17, levelName: '博弈型动态规划', count: 4 },
{ id: 18, levelName: '匹配型动态规划', count: 15 },
{ id: 19, levelName: '树型动态规划', count: 4 },
{ id: 20, levelName: '排序算法', count: 83 },
{ id: 21, levelName: '外排序算法', count: 2 },
{ id: 22, levelName: '快速选择算法', count: 12 },
{ id: 23, levelName: '欧拉路径', count: 1 },
{ id: 24, levelName: '模拟法', count: 282 },
{ id: 25, levelName: '扫描线算法', count: 19 },
{ id: 26, levelName: '枚举法', count: 109 },
{ id: 27, levelName: '最短路', count: 6 },
{ id: 28, levelName: '贪心法', count: 85 },
{ id: 29, levelName: '最小生成树', count: 3 },
],
},
{
id: 2,
levelName: '数据结构',
childrenLevelList: [
{ id: 1, levelName: '数组', count: 179 },
{ id: 2, levelName: '前缀和数组', count: 41 },
{ id: 3, levelName: '字符串', count: 257 },
{ id: 4, levelName: '链表', count: 52 },
{ id: 5, levelName: '双向链表', count: 1 },
{ id: 6, levelName: '队列', count: 18 },
{ id: 7, levelName: '单调队列', count: 3 },
{ id: 8, levelName: '双向队列', count: 3 },
{ id: 9, levelName: '栈', count: 74 },
{ id: 10, levelName: '单调栈', count: 27 },
{ id: 11, levelName: '二叉树', count: 120 },
{ id: 12, levelName: '树', count: 5 },
{ id: 13, levelName: '二叉搜索树', count: 30 },
{ id: 14, levelName: '迭代器', count: 6 },
{ id: 15, levelName: '堆', count: 45 },
{ id: 16, levelName: '图', count: 17 },
{ id: 17, levelName: '二分图', count: 5 },
{ id: 18, levelName: '哈希表', count: 156 },
{ id: 19, levelName: '字典树', count: 19 },
{ id: 20, levelName: '并查集', count: 36 },
{ id: 21, levelName: '树状数组', count: 7 },
{ id: 22, levelName: '线段树', count: 21 },
{ id: 23, levelName: '平衡树', count: 7 },
],
},
{
id: 3,
levelName: 'SQL 基础语法',
childrenLevelList: [
{ id: 1, levelName: '比较运算符', count: 52 },
{ id: 2, levelName: '嵌套查询', count: 35 },
{ id: 3, levelName: '基础语法', count: 27 },
{ id: 4, levelName: '逻辑运算符', count: 25 },
{ id: 5, levelName: 'GROUP BY', count: 18 },
{ id: 6, levelName: '算术运算符', count: 17 },
{ id: 7, levelName: 'IN', count: 17 },
{ id: 8, levelName: 'ORDER BY', count: 16 },
{ id: 9, levelName: 'AS', count: 15 },
{ id: 10, levelName: 'HAVING', count: 8 },
{ id: 11, levelName: 'SELECT', count: 7 },
{ id: 12, levelName: 'DISTINCT', count: 6 },
{ id: 13, levelName: 'LIKE', count: 5 },
{ id: 14, levelName: 'LIMIT', count: 4 },
{ id: 15, levelName: 'IS NULL', count: 4 },
{ id: 16, levelName: 'UNION', count: 3 },
{ id: 17, levelName: '约束', count: 3 },
{ id: 18, levelName: 'IFNULL/COLLAPSE', count: 2 },
{ id: 19, levelName: 'ANY', count: 2 },
{ id: 20, levelName: 'ALL', count: 2 },
],
},
],
// 前端
2: [
{
id: 1,
levelName: '移动端',
childrenLevelList: [
{
id: 1,
levelName: 'IOSIOSIOSIOS',
count: 13,
},
{
id: 2,
levelName: '安卓',
count: 130,
},
{
id: 3,
levelName: '鸿蒙',
count: 134,
},
],
},
{
id: 2,
levelName: '框架',
childrenLevelList: [
{
id: 1,
levelName: 'Vue.js',
count: 13,
},
{
id: 2,
levelName: 'React.js',
count: 13,
},
{
id: 3,
levelName: 'Bootstrap',
count: 13,
},
],
},
{
id: 3,
levelName: '小程序',
childrenLevelList: [
{ id: 1, levelName: '微信小程序', count: 13 },
{ id: 2, levelName: '京东小程序', count: 13 },
{ id: 3, levelName: '支付宝小程序', count: 13 },
{ id: 4, levelName: '百度小程序', count: 13 },
{ id: 5, levelName: 'QQ小程序', count: 13 },
],
},
],
// 后端
3: [
{
id: 1,
levelName: '框架',
childrenLevelList: [
{
id: 1,
levelName: 'Spring框架',
count: 17,
},
{
id: 2,
levelName: 'Struts框架',
count: 127,
},
{
id: 3,
levelName: 'Hibernate框架',
count: 170,
},
],
},
{
id: 2,
levelName: 'SSM框架组合',
childrenLevelList: [
{
id: 1,
levelName: 'SpringMVC框架',
count: 170,
},
{
id: 2,
levelName: 'Mybatis框架',
count: 1,
},
],
},
],
// 测试
4: [
{
id: 1,
levelName: '自动化测试',
childrenLevelList: [
{
id: 1,
levelName: '管理工具',
count: 16,
},
{
id: 2,
levelName: 'UI自动化',
count: 16,
},
{
id: 3,
levelName: '接口自动化',
count: 16,
},
],
},
],
// 人工智能
5: [
{
id: 1,
levelName: '数据处理',
childrenLevelList: [
{ id: 1, levelName: '工具库', count: 107 },
{ id: 2, levelName: '数据预处理', count: 127 },
{ id: 3, levelName: 'Numpy', count: 170 },
{ id: 4, levelName: 'Matplotlib', count: 170 },
{ id: 5, levelName: 'Pandas', count: 170 },
],
},
{
id: 2,
levelName: '机器学习',
childrenLevelList: [
{ id: 1, levelName: '工具库', count: 170 },
{ id: 2, levelName: '分类', count: 1 },
{ id: 3, levelName: '模型', count: 170 },
{ id: 4, levelName: '决策树', count: 170 },
],
},
],
// 产品
6: [],
// 视觉
7: [],
};

View File

@@ -1,10 +0,0 @@
const UploadExam = () => {
return (
<div>
upload exam
</div>
)
}
export default UploadExam

View File

@@ -0,0 +1,360 @@
import React, { Component, Fragment } from 'react';
import { Input, Modal, message, Spin } from 'antd';
import req from '@utils/request';
import { debounce } from '@utils';
import KindEditor from '../kind-editor';
import RankLabelBox from '../rank-label-box';
import RepeatContentBox from '../repeat-content-box';
import { apiName } from '../../constant';
import './index.less';
export default class BriefQuestions extends Component {
constructor(props) {
super(props);
this.state = {
subjectName: '', // 题目
isDisabledSubmit: true, //是否禁止输入
isShowModalBox: false, // 是否展示重复率弹框
isSubmit: true, // 是否支持提交
};
}
kindEditor = KindEditor | null;
rankLabelBox = RankLabelBox | null;
rankId = 1; //职级
subjectAnswer = ''; // 答案
firstCategoryValue = ''; // 一级分类的值
secondCategoryValue = []; // 二级分类的值
thirdCategoryValue = []; // 三级标签的值
repeatInfo = {}; // 重复率
/**
* 输入题目
* @param {*} e
*/
onChangeSubjectName = (e) => {
let str = e.target.value.trim();
this.setState(
{
subjectName: str,
},
() => {
this.rankLabelBox.getThirdCategoryList();
let isDisabledSubmit = this.checkData();
this.setState({
isDisabledSubmit,
});
}
);
};
/**
* 富文本编辑器
* @param {*} e
*/
onChangeEditor = (e) => {
this.subjectAnswer = e;
let isDisabledSubmit = this.checkData();
this.setState({
isDisabledSubmit,
});
};
/**
* 一次确认录入
*/
onSubmit = debounce(() => {
const { subjectName, isDisabledSubmit, isSubmit } = this.state;
if (isDisabledSubmit || !isSubmit) {
return;
}
// if (!isSubmit) {
// return;
// }
if (!!!subjectName) {
message.warning('请输入题目名称');
return;
}
if (!!!this.subjectAnswer) {
message.warning('请输入题目答案');
return;
}
if (!!!this.firstCategoryValue) {
message.warning('请选择一级分类');
return;
}
if (this.secondCategoryValue.length <= 0) {
message.warning('请选择二级分类');
return;
}
if (this.thirdCategoryValue.length <= 0) {
message.warning('请选择三级标签');
return;
}
this.setState({
isSubmit: false,
});
let params = {
subjectName: subjectName,
difficulty: this.rankId,
subjectType: 4,
subjectScore: 1,
subjectAnswer: this.subjectAnswer,
categoryIds: this.secondCategoryValue,
labelIds: this.thirdCategoryValue,
};
console.log('录入 ----', params);
req({
method: 'post',
data: params,
url: apiName.addInterviewSubject,
})
.then((res) => {
this.repeatInfo = {};
if (res.data && res.data.insertStatus) {
this.setState(
{
isSubmit: true,
},
() => {
this.successModalConfirm();
}
);
} else if (!res.data.insertStatus) {
this.repeatInfo = {
repeatDocId: res.data.docId, // 重复题目id
repeatRate: res.data.repeatRate, // 重复率
repeatSubjectName: res.data.subjectName, // 重复题目
repeatSubjectAnswe: res.data.subjectAnswer, // 重复答案
repeatSetterErp: res.data.subjectSetterErp, // 出题人erp
repeatSetterName: res.data.subjectSetterName, // 出题人姓名
};
this.setState({
isShowModalBox: true,
isSubmit: true,
});
}
})
.catch((err) => {
this.setState({
isSubmit: true,
});
console.log(err);
});
});
/**
* 校验是否支持点击按钮
* @returns
*/
checkData = () => {
const { subjectName } = this.state;
let isDisabledSubmit = false;
if (
!!!subjectName ||
!!!this.subjectAnswer ||
!!!this.firstCategoryValue ||
this.secondCategoryValue.length <= 0 ||
this.thirdCategoryValue.length <= 0
) {
isDisabledSubmit = true;
}
return isDisabledSubmit;
};
/**
* 取消
*/
onCancel = () => {
this.subjectAnswer = ''; // 答案
this.rankId = 1;
this.firstCategoryValue = '';
this.secondCategoryValue = [];
this.thirdCategoryValue = [];
this.repeatInfo = {};
this.kindEditor.onClear();
this.rankLabelBox.initRankLabel();
this.setState({
subjectName: '',
isShowModalBox: false,
isSubmit: true, // 是否支持提交
});
};
/**
* 重复率弹框-确认录入
*/
onSubmitRepeatModal = debounce(
() => {
let params = {
docId: this.repeatInfo.repeatDocId,
};
req({
method: 'post',
data: params,
url: apiName.addRepeatInterviewSubject,
})
.then((res) => {
if (res.data) {
this.successModalConfirm();
} else {
message.info('请再次确认');
}
})
.catch((err) => {
console.log(err);
message.error('请再次确认');
});
},
300,
true
);
/**
* 重复率弹框-取消录入
*/
onCancelRepeatModal = () => {
this.repeatInfo = {};
this.setState({
isShowModalBox: false,
});
};
/**
* 录入成功的弹框
*/
successModalConfirm = () => {
Modal.confirm({
title: (
<div
style={{
textAlign: 'center',
color: 'rgba(60, 110, 238, 1)',
fontSize: 16,
}}>
录入成功贡献榜火力值 + 1
</div>
),
closable: false,
maskClosable: false,
icon: ' ',
onOk: this.onAgainSuccessModal,
onCancel: this.onGoHomeSuccessModal,
okText: '再录一题',
cancelText: '去首页',
className: 'questions-success-modal-confirm',
});
};
/**
* 录入成功弹框-再来一题
*/
onAgainSuccessModal = () => {
this.onCancel();
};
/**
* 录入成功弹框-去首页
*/
onGoHomeSuccessModal = () => {
window.location.href = '/cms-supplier/question-bank';
};
/**
* 分类选择
* @param {*} e
*/
onChangeRankLabel = (firstCategoryValue, secondCategoryValue, thirdCategoryValue) => {
this.firstCategoryValue = firstCategoryValue; // 一级分类的值
this.secondCategoryValue = secondCategoryValue; // 二级分类的值
this.thirdCategoryValue = thirdCategoryValue; // 三级标签的值
let isDisabledSubmit = this.checkData();
this.setState({
isDisabledSubmit,
});
};
/**
* 职级选择
* @param {*} list
*/
handleChangeRank = (list) => {
this.rankId = list[0];
let isDisabledSubmit = this.checkData();
this.setState({
isDisabledSubmit,
});
};
render() {
const { subjectName, isDisabledSubmit, isSubmit, isShowModalBox } = this.state;
const { questionsType } = this.props;
// this.successModalConfirm();
return (
<Spin spinning={!isSubmit}>
<Fragment>
<div className="brief-questions-container">
<div className="brief-questions-title">题目名称</div>
<div className="brief-questions-main">
<Input
placeholder="输入题目"
className="brief-questions-input"
value={subjectName}
maxLength={64}
onChange={this.onChangeSubjectName}
/>
</div>
</div>
<div className="brief-questions-container">
<div className="brief-questions-title">题目答案</div>
{this.reanderAnser()}
</div>
<RankLabelBox
ref={(ref) => {
this.rankLabelBox = ref;
}}
subjectName={subjectName}
onChangeRankLabel={this.onChangeRankLabel}
handleChangeRank={this.handleChangeRank}
/>
<div className="brief-questions-btns-container">
<div className="brief-questions-btn" onClick={this.onCancel}>
清空
</div>
<div
className={`brief-questions-btn brief-questions-submit ${isDisabledSubmit && 'brief-questions-disabled-submit'
}`}
onClick={this.onSubmit}>
提交
</div>
</div>
<RepeatContentBox
isShowModalBox={isShowModalBox}
repeatQuestionsType={questionsType}
repeatInfo={this.repeatInfo}
handleSubmitRepeatModal={this.onSubmitRepeatModal}
handleCancelRepeatModal={this.onCancelRepeatModal}
/>
</Fragment>
</Spin>
);
}
/**
* 问答题-答案
*/
reanderAnser = () => {
return (
<div className="brief-questions-main">
<KindEditor
onChange={this.onChangeEditor}
ref={(ref) => {
this.kindEditor = ref;
}}
/>
</div>
);
};
}

View File

@@ -0,0 +1,58 @@
.brief-questions-container {
width: 1000px;
display: flex;
align-items: center;
padding: 0 24px;
padding-top: 36px;
.brief-questions-title {
display: flex;
align-items: center;
justify-content: flex-end;
width: 140px;
line-height: 40px;
font-size: 16px;
color: rgba(51, 51, 51, 1);
&:before {
display: inline-block;
margin-right: 4px;
margin-top: 1px;
color: #f5222d;
font-size: 16px;
content: '*';
}
}
.brief-questions-main {
width: 100%;
// 题目输入框
.brief-questions-input {
height: 48px;
}
}
}
.brief-questions-btns-container {
display: flex;
justify-content: flex-end;
align-items: center;
margin: 20px auto;
width: 952px;
.brief-questions-btn {
display: flex;
align-items: center;
justify-content: center;
width: 150px;
height: 40px;
font-size: 16px;
cursor: pointer;
border: 1px solid #d9d9d9;
border-radius: 10px;
}
.brief-questions-submit {
margin-left: 40px;
background-color: #4390f7;
color: #fff;
border: 1px solid #4390f7;
}
.brief-questions-disabled-submit {
opacity: 0.5;
}
}

View File

@@ -0,0 +1,327 @@
import React, { Component, Fragment } from 'react';
import { Input, Modal, message, Spin } from 'antd';
import req from '@utils/request';
import { debounce } from '@utils';
import RankLabelBox from '../rank-label-box';
import OptionInputBox from '../option-input-box';
import RepeatContentBox from '../repeat-content-box';
import { apiName } from '../../constant';
import './index.less';
export default class JudgeQuestions extends Component {
constructor(props) {
super(props);
this.state = {
subjectName: '', // 题目
isDisabledSubmit: true, //是否禁止输入
isShowModalBox: false, // 是否展示重复率弹框
isSubmit: true, // 是否支持提交
};
}
rankLabelBox = RankLabelBox | null;
optionInputBox = OptionInputBox | null;
currentActive = []; // 当前选中的项
scoreValue = ''; // 分数
subjectAnalysis = ''; //试题解析
rankId = 1; //职级
firstCategoryValue = ''; // 一级分类的值
secondCategoryValue = []; // 二级分类的值
thirdCategoryValue = []; // 三级标签的值
repeatInfo = {}; // 重复率
/**
* 输入题目
* @param {*} e
*/
onChangeSubjectName = (e) => {
let str = e.target.value.trim();
this.setState(
{
subjectName: str,
},
() => {
this.rankLabelBox.getThirdCategoryList();
let isDisabledSubmit = this.checkData();
this.setState({
isDisabledSubmit,
});
}
);
};
/**
* 一次确认录入
*/
onSubmit = debounce(() => {
const { subjectName, isDisabledSubmit, isSubmit } = this.state;
if (isDisabledSubmit || !isSubmit) {
return;
}
this.setState({
isSubmit: false,
});
let params = {
subjectName: subjectName,
difficulty: this.rankId,
subjectType: 3,
subjectScore: this.scoreValue,
subjectParse: this.subjectAnalysis,
isCorrect: this.currentActive[0],
categoryIds: this.secondCategoryValue,
labelIds: this.thirdCategoryValue,
};
console.log('判断录入 ----', params);
req({
method: 'post',
data: params,
url: apiName.addInterviewSubject,
})
.then((res) => {
this.repeatInfo = {};
if (res.data && res.data.insertStatus) {
this.setState(
{
isSubmit: true,
},
() => {
this.successModalConfirm();
}
);
} else if (!res.data.insertStatus) {
this.repeatInfo = {
repeatDocId: res.data.docId, // 重复题目id
repeatRate: res.data.repeatRate, // 重复率
repeatIsCorrect: res.data.isCorrect, // 答案
repeatSubjectName: res.data.subjectName, // 重复题目
repeatSetterErp: res.data.subjectSetterErp, // 出题人erp
repeatSetterName: res.data.subjectSetterName, // 出题人姓名
};
this.setState({
isShowModalBox: true,
isSubmit: true,
});
}
})
.catch((err) => {
this.setState({
isSubmit: true,
});
console.log(err);
});
});
/**
* 校验是否支持点击按钮
* @returns
*/
checkData = () => {
const { subjectName } = this.state;
let isDisabledSubmit = false;
if (
!!!subjectName ||
this.currentActive?.length <= 0 ||
!!!this.firstCategoryValue ||
this.secondCategoryValue.length <= 0 ||
this.thirdCategoryValue.length <= 0 ||
!!!this.scoreValue
) {
isDisabledSubmit = true;
}
return isDisabledSubmit;
};
/**
* 取消
*/
onCancel = () => {
this.currentActive = []; // 选项列表
this.scoreValue = ''; // 分数
this.subjectAnalysis = ''; //试题解析
this.rankId = 1;
this.firstCategoryValue = ''; // 一级分类的值
this.secondCategoryValue = []; // 二级分类的值
this.thirdCategoryValue = []; // 三级标签的值
this.repeatInfo = {};
this.rankLabelBox.initRankLabel();
this.optionInputBox.handleClearOption();
this.setState({
subjectName: '',
isShowModalBox: false,
isSubmit: true, // 是否支持提交
});
};
/**
* 重复率弹框-确认录入
*/
onSubmitRepeatModal = debounce(
() => {
let params = {
docId: this.repeatInfo.repeatDocId,
};
req({
method: 'post',
data: params,
url: apiName.addRepeatInterviewSubject,
})
.then((res) => {
if (res.data) {
this.successModalConfirm();
} else {
message.info('请再次确认');
}
})
.catch((err) => {
console.log(err);
message.error('请再次确认');
});
},
300,
true
);
/**
* 重复率弹框-取消录入
*/
onCancelRepeatModal = () => {
this.repeatInfo = {};
this.setState({
isShowModalBox: false,
});
};
/**
* 录入成功的弹框
*/
successModalConfirm = () => {
Modal.confirm({
title: (
<div
style={{
textAlign: 'center',
color: 'rgba(60, 110, 238, 1)',
fontSize: 16,
}}>
录入成功贡献榜火力值 + 1
</div>
),
closable: false,
maskClosable: false,
icon: ' ',
onOk: this.onAgainSuccessModal,
onCancel: this.onGoHomeSuccessModal,
okText: '再录一题',
cancelText: '去首页',
className: 'questions-success-modal-confirm',
});
};
/**
* 录入成功弹框-再来一题
*/
onAgainSuccessModal = () => {
this.onCancel();
};
/**
* 录入成功弹框-去首页
*/
onGoHomeSuccessModal = () => {
// this.onCancel();
window.location.href = '/cms-supplier/question-bank';
};
/**
* 分类选择
* @param {*} e
*/
onChangeRankLabel = (firstCategoryValue, secondCategoryValue, thirdCategoryValue) => {
this.firstCategoryValue = firstCategoryValue; // 一级分类的值
this.secondCategoryValue = secondCategoryValue; // 二级分类的值
this.thirdCategoryValue = thirdCategoryValue; // 三级标签的值
let isDisabledSubmit = this.checkData();
this.setState({
isDisabledSubmit,
});
};
/**
* 职级选择
* @param {*} list
*/
handleChangeRank = (list) => {
this.rankId = list[0];
let isDisabledSubmit = this.checkData();
this.setState({
isDisabledSubmit,
});
};
/**
* 选项操作
* @param {*} currentActive 选项列表
* @param {*} scoreValue 分值
* @param {*} subjectAnalysis 解析
*/
handleChangeOption = (currentActive, scoreValue, subjectAnalysis) => {
this.currentActive = currentActive;
this.scoreValue = scoreValue;
this.subjectAnalysis = subjectAnalysis;
let isDisabledSubmit = this.checkData();
this.setState({
isDisabledSubmit,
});
};
render() {
const { subjectName, isDisabledSubmit, isSubmit, isShowModalBox } = this.state;
const { questionsType } = this.props;
return (
<Spin spinning={!isSubmit}>
<Fragment>
<div className="judge-questions-container">
<div className="judge-questions-title">题目名称</div>
<Input
placeholder="输入题目"
style={{ height: 48, width: '100%' }}
value={subjectName}
maxLength={64}
onChange={(e) => this.onChangeSubjectName(e)}
/>
</div>
<OptionInputBox
ref={(ref) => {
this.optionInputBox = ref;
}}
isJudge={true}
handleChangeOption={this.handleChangeOption}
/>
<RankLabelBox
ref={(ref) => {
this.rankLabelBox = ref;
}}
subjectName={subjectName}
onChangeRankLabel={this.onChangeRankLabel}
handleChangeRank={this.handleChangeRank}
/>
<div className="judge-questions-btns-container">
<div className="judge-questions-btn" onClick={this.onCancel}>
清空
</div>
<div
className={`judge-questions-btn judge-questions-submit ${isDisabledSubmit && 'judge-questions-disabled-submit'
}`}
onClick={this.onSubmit}>
提交
</div>
</div>
<RepeatContentBox
isShowModalBox={isShowModalBox}
repeatQuestionsType={questionsType}
repeatInfo={this.repeatInfo}
handleSubmitRepeatModal={this.onSubmitRepeatModal}
handleCancelRepeatModal={this.onCancelRepeatModal}
/>
</Fragment>
</Spin>
);
}
}

View File

@@ -0,0 +1,52 @@
.judge-questions-container {
width: 1000px;
display: flex;
align-items: center;
padding: 0 24px;
padding-top: 36px;
// label名字title
.judge-questions-title {
display: flex;
align-items: center;
justify-content: flex-end;
width: 140px;
line-height: 40px;
font-size: 16px;
color: rgba(51, 51, 51, 1);
&:before {
display: inline-block;
margin-right: 4px;
margin-top: 1px;
color: #f5222d;
font-size: 16px;
content: '*';
}
}
}
.judge-questions-btns-container {
display: flex;
justify-content: flex-end;
align-items: center;
margin: 20px auto;
width: 952px;
.judge-questions-btn {
display: flex;
align-items: center;
justify-content: center;
width: 150px;
height: 40px;
font-size: 16px;
cursor: pointer;
border: 1px solid #d9d9d9;
border-radius: 10px;
}
.judge-questions-submit {
margin-left: 40px;
background-color: #4390f7;
color: #fff;
border: 1px solid #4390f7;
}
.judge-questions-disabled-submit {
opacity: 0.5;
}
}

View File

@@ -0,0 +1,153 @@
import React, { Component } from 'react';
import './index.less';
import Editor from 'wangeditor';
export default class KindEditor extends Component {
defaultValueHead = `<div style='font-size:14px !important;line-height:22px !important;margin-bottom: -15px !important;word-break: break-word !important;'>`;
defaultValueFoot = '</div>';
editor = Editor;
constructor(props) {
super(props);
this.state = { editorContent: '', isActive: false };
}
/**
* 清空内容
*/
onClear = () => {
this.editor.txt.clear();
this.editor.config.focus = false;
this.setState({
isActive: false,
editorContent: '',
});
};
/**
* 回现代码
*/
onCashBack = () => {
let { cashBackText } = this.props;
if (!!!cashBackText) {
return;
}
this.editor.txt.html(`${cashBackText}`);
this.editor.config.focus = true;
};
/**
* 获得焦点
*/
onFocus = () => {
this.editor.config.focus = true;
this.setState({
isActive: true,
});
};
componentDidMount() {
const elemMenu = this.refs.editorElemMenu;
const elemBody = this.refs.editorElemBody;
this.editor = new Editor(elemMenu, elemBody);
// // 使用 onchange 函数监听内容的变化,并实时更新到 state 中
this.editor.config.onchange = (html) => {
let htmlStr = this.editor.txt.html();
// console.log('htmlStr ---', htmlStr);
if (htmlStr?.indexOf('<div style=') < 0) {
htmlStr = this.defaultValueHead + htmlStr + this.defaultValueFoot;
}
let isActive = false;
if (this.state.editorContent) {
isActive = true;
}
this.setState(
{
// editorContent: editor.txt.text()
editorContent: htmlStr,
isActive: isActive,
},
() => {
this.props.onChange(htmlStr);
}
);
};
this.editor.config.onfocus = () => {
this.setState({
isActive: true,
});
};
this.editor.config.onblur = () => {
this.setState({
isActive: false,
});
};
this.editor.config.menus = [
// 'head', // 标题
// 'bold', // 粗体
// 'fontSize', // 字号
// 'fontName', // 字体
// 'italic', // 斜体
// 'underline', // 下划线
// 'strikeThrough', // 删除线
'foreColor', // 文字颜色
// 'backColor', // 背景颜色
// 'link', // 插入链接
'list', // 列表
// 'justify', // 对齐方式
// 'quote', // 引用
// 'emoticon', // 表情
// 'image', // 插入图片
// 'table', // 表格
// 'video', // 插入视频
'code', // 插入代码
'undo', // 撤销
// 'redo', // 重复
];
// this.editor.customConfig.uploadImgShowBase64 = true;
// 取消自动 focus
this.editor.config.focus = false;
this.editor.config.pasteFilterStyle = true; // 样式过滤
this.editor.config.pasteIgnoreImg = true; // 如果复制的内容有图片又有文字,则只粘贴文字,不粘贴图片
this.editor.config.placeholder = '请输入';
this.editor.create();
}
render() {
const { isActive } = this.state;
const { bodyHeight, bodyWidth, borderRadius } = this.props;
return (
<div
className={`text-area ${isActive && 'kind-editor-active-box'}`}
style={{
borderRadius: borderRadius,
}}>
<div
ref="editorElemMenu"
className="editorelem-menu"
style={{
width: bodyWidth,
borderTopLeftRadius: borderRadius,
borderTopRightRadius: borderRadius,
}}></div>
<div
ref="editorElemBody"
className="editorelem-body"
style={{
height: bodyHeight,
width: bodyWidth,
borderBottomLeftRadius: borderRadius,
borderBottomRightRadius: borderRadius,
}}></div>
</div>
);
}
}
KindEditor.defaultProps = {
bodyHeight: 320,
bodyWidth: '100%',
borderRadius: '4px',
};

View File

@@ -0,0 +1,32 @@
.text-area {
background-color: #fff;
font-size: 14px;
.editorelem-menu {
border: 1px solid #d9d9d9;
.w-e-menu-tooltip {
padding: 0px 6px;
line-height: 28px;
}
.w-e-toolbar {
z-index: 2 !important;
border-radius: 12px;
}
}
.editorelem-body {
width: 100%;
height: 100%;
padding: 0 10px 10px;
overflow-y: scroll;
border: 1px solid #d9d9d9;
border-top: none;
border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px;
}
}
.w-e-text-container {
z-index: 1 !important;
}
.kind-editor-active-box {
border: 1px solid #40a9ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}

View File

@@ -0,0 +1,343 @@
import React, { Component, Fragment } from 'react';
import { Input, Modal, message, Spin } from 'antd';
import _ from 'lodash';
import req from '@utils/request';
import { debounce } from '@utils';
import KindEditor from '../kind-editor';
import RankLabelBox from '../rank-label-box';
import OptionInputBox from '../option-input-box';
import RepeatContentBox from '../repeat-content-box';
import { apiName } from '../../constant';
import './index.less';
const defalutLabel = '请使用富文本编辑器输入选项内容';
export default class MultipleQuestions extends Component {
constructor(props) {
super(props);
this.state = {
subjectName: '', // 题目
isDisabledSubmit: true, //是否禁止输入
isShowModalBox: false, // 是否展示重复率弹框
isSubmit: true, // 是否支持提交
};
}
kindEditor = KindEditor | null;
rankLabelBox = RankLabelBox | null;
optionInputBox = OptionInputBox | null;
currentActive = []; // 选项列表
scoreValue = ''; // 分数
subjectAnalysis = ''; //试题解析
rankId = 1; //职级
subjectAnswer = ''; // 选项内容
firstCategoryValue = ''; // 一级分类的值
secondCategoryValue = []; // 二级分类的值
thirdCategoryValue = []; // 三级标签的值
repeatInfo = {}; // 重复率
/**
* 输入题目
* @param {*} e
*/
onChangeSubjectName = (e) => {
let str = e.target.value.trim();
this.setState(
{
subjectName: str,
},
() => {
this.rankLabelBox.getThirdCategoryList();
let isDisabledSubmit = this.checkData();
this.setState({
isDisabledSubmit,
});
}
);
};
/**
* 一次确认录入
*/
onSubmit = debounce(() => {
const { subjectName, isDisabledSubmit, isSubmit } = this.state;
if (isDisabledSubmit || !isSubmit) {
return;
}
this.setState({
isSubmit: false,
});
let params = {
subjectName: subjectName,
difficulty: this.rankId,
subjectType: 2,
subjectScore: this.scoreValue,
subjectParse: this.subjectAnalysis,
categoryIds: this.secondCategoryValue,
labelIds: this.thirdCategoryValue,
optionList: this.currentActive,
};
console.log('多选录入 ----', params);
req({
method: 'post',
data: params,
url: apiName.addInterviewSubject,
})
.then((res) => {
this.repeatInfo = {};
if (res.data && res.data.insertStatus) {
this.setState(
{
isSubmit: true,
},
() => {
this.successModalConfirm();
}
);
} else if (!res.data.insertStatus) {
this.repeatInfo = {
repeatDocId: res.data.docId, // 重复题目id
repeatRate: res.data.repeatRate, // 重复率
repeatSubjectName: res.data.subjectName, // 重复题目
repeatOptionList: res.data.optionList, // 重复列表项
repeatSetterErp: res.data.subjectSetterErp, // 出题人erp
repeatSetterName: res.data.subjectSetterName, // 出题人姓名
};
this.setState({
isShowModalBox: true,
isSubmit: true,
});
}
})
.catch((err) => {
this.setState({
isSubmit: true,
});
console.log(err);
});
});
/**
* 校验是否支持点击按钮
* @returns
*/
/**
* 校验是否支持点击按钮
* @returns
*/
checkData = () => {
const { subjectName } = this.state;
let list = this.currentActive.filter((item) => item.optionContent === defalutLabel);
let isDisabledSubmit = false;
if (
!!!subjectName ||
list.length > 0 ||
!!!this.firstCategoryValue ||
this.secondCategoryValue.length <= 0 ||
this.thirdCategoryValue.length <= 0 ||
!!!this.scoreValue
) {
isDisabledSubmit = true;
}
return isDisabledSubmit;
};
/**
* 取消
*/
onCancel = () => {
this.currentActive = []; // 选项列表
this.scoreValue = ''; // 分数
this.subjectAnalysis = ''; //试题解析
this.rankId = 1;
this.subjectAnswer = ''; // 选项内容
this.firstCategoryValue = ''; // 一级分类的值
this.secondCategoryValue = []; // 二级分类的值
this.thirdCategoryValue = []; // 三级标签的值
this.repeatInfo = {};
this.kindEditor && this.kindEditor.onClear();
this.rankLabelBox.initRankLabel();
this.optionInputBox.handleClearOption();
this.setState({
subjectName: '',
isShowModalBox: false,
isSubmit: true, // 是否支持提交
});
};
/**
* 重复率弹框-确认录入
*/
onSubmitRepeatModal = debounce(
() => {
let params = {
docId: this.repeatInfo.repeatDocId,
};
req({
method: 'post',
data: params,
url: apiName.addRepeatInterviewSubject,
})
.then((res) => {
if (res.data) {
this.successModalConfirm();
} else {
message.info('请再次确认');
}
})
.catch((err) => {
console.log(err);
message.error('请再次确认');
});
},
300,
true
);
/**
* 重复率弹框-取消录入
*/
onCancelRepeatModal = () => {
this.repeatInfo = {};
this.setState({
isShowModalBox: false,
});
};
/**
* 录入成功的弹框
*/
successModalConfirm = () => {
Modal.confirm({
title: (
<div
style={{
textAlign: 'center',
color: 'rgba(60, 110, 238, 1)',
fontSize: 16,
}}>
录入成功贡献榜火力值 + 1
</div>
),
closable: false,
maskClosable: false,
icon: ' ',
onOk: this.onAgainSuccessModal,
onCancel: this.onGoHomeSuccessModal,
okText: '再录一题',
cancelText: '去首页',
className: 'questions-success-modal-confirm',
});
};
/**
* 录入成功弹框-再来一题
*/
onAgainSuccessModal = () => {
this.onCancel();
};
/**
* 录入成功弹框-去首页
*/
onGoHomeSuccessModal = () => {
// this.onCancel();
window.location.href = '/cms-supplier/question-bank';
};
/**
* 分类选择
* @param {*} e
*/
onChangeRankLabel = (firstCategoryValue, secondCategoryValue, thirdCategoryValue) => {
this.firstCategoryValue = firstCategoryValue; // 一级分类的值
this.secondCategoryValue = secondCategoryValue; // 二级分类的值
this.thirdCategoryValue = thirdCategoryValue; // 三级标签的值
let isDisabledSubmit = this.checkData();
this.setState({
isDisabledSubmit,
});
};
/**
* 职级选择
* @param {*} list
*/
handleChangeRank = (list) => {
this.rankId = list[0];
let isDisabledSubmit = this.checkData();
this.setState({
isDisabledSubmit,
});
};
/**
* 选项操作
* @param {*} currentActive 选项列表
* @param {*} scoreValue 分值
* @param {*} subjectAnalysis 解析
*/
handleChangeOption = (currentActive, scoreValue, subjectAnalysis) => {
this.currentActive = currentActive;
this.scoreValue = scoreValue;
this.subjectAnalysis = subjectAnalysis;
let isDisabledSubmit = this.checkData();
this.setState({
isDisabledSubmit,
});
};
render() {
const { subjectName, isDisabledSubmit, isSubmit, isShowModalBox } = this.state;
const { questionsType } = this.props;
return (
<Spin spinning={!isSubmit}>
<Fragment>
<div className="multiple-questions-container">
<div className="multiple-questions-title">题目名称</div>
<Input
placeholder="输入题目"
style={{ height: 48, width: '100%' }}
value={subjectName}
maxLength={64}
onChange={(e) => this.onChangeSubjectName(e)}
/>
</div>
<OptionInputBox
key="multiple-option-input"
ref={(ref) => {
this.optionInputBox = ref;
}}
isMultiple={true}
handleChangeOption={this.handleChangeOption}
/>
<RankLabelBox
ref={(ref) => {
this.rankLabelBox = ref;
}}
subjectName={subjectName}
onChangeRankLabel={this.onChangeRankLabel}
handleChangeRank={this.handleChangeRank}
/>
<div className="multiple-questions-btns-container">
<div className="multiple-questions-btn" onClick={this.onCancel}>
清空
</div>
<div
className={`multiple-questions-btn multiple-questions-submit ${isDisabledSubmit && 'multiple-questions-disabled-submit'
}`}
onClick={this.onSubmit}>
提交
</div>
</div>
<RepeatContentBox
isShowModalBox={isShowModalBox}
repeatQuestionsType={questionsType}
repeatInfo={this.repeatInfo}
handleSubmitRepeatModal={this.onSubmitRepeatModal}
handleCancelRepeatModal={this.onCancelRepeatModal}
/>
</Fragment>
</Spin>
);
}
}

View File

@@ -0,0 +1,52 @@
.multiple-questions-container {
width: 1000px;
display: flex;
align-items: center;
padding: 0 24px;
padding-top: 36px;
// label名字title
.multiple-questions-title {
display: flex;
align-items: center;
justify-content: flex-end;
width: 140px;
line-height: 40px;
font-size: 16px;
color: rgba(51, 51, 51, 1);
&:before {
display: inline-block;
margin-right: 4px;
margin-top: 1px;
color: #f5222d;
font-size: 16px;
content: '*';
}
}
}
.multiple-questions-btns-container {
display: flex;
justify-content: flex-end;
align-items: center;
margin: 20px auto;
width: 952px;
.multiple-questions-btn {
display: flex;
align-items: center;
justify-content: center;
width: 150px;
height: 40px;
font-size: 16px;
cursor: pointer;
border: 1px solid #d9d9d9;
border-radius: 10px;
}
.multiple-questions-submit {
margin-left: 40px;
background-color: #4390f7;
color: #fff;
border: 1px solid #4390f7;
}
.multiple-questions-disabled-submit {
opacity: 0.5;
}
}

View File

@@ -0,0 +1,438 @@
import React, { Component, Fragment } from 'react';
import { Input, message, Tooltip, Select } from 'antd';
import _ from 'lodash';
import { debounce } from '@utils';
import { optionLetter } from '../../constant';
import KindEditor from '../kind-editor';
import './index.less';
const { TextArea } = Input;
const { Option } = Select;
const defalutLabel = '请使用富文本编辑器输入选项内容';
// 判断题
const judgeList = [
{
label: '错误',
value: 0,
},
{
label: '正确',
value: 1,
},
];
const optionLetterLength = 7; // ABCD的长度
const showDeleteLength = 3; // 展示删除icon的最短长度
export default class OptionInputBox extends Component {
constructor(props) {
super(props);
this.state = {
optionList: [
{
label: defalutLabel,
value: 1,
},
{
label: defalutLabel,
value: 2,
},
{
label: defalutLabel,
value: 3,
},
{
label: defalutLabel,
value: 4,
},
], // 选项列表
currentActiveList: [], // 当前选中的项
scoreValue: '', // 分数
subjectAnalysis: '', //试题解析
};
}
kindEditor = KindEditor | null;
subjectAnswer = ''; // 选项内容
/**
* 新增/删除
* @param {*} len
* @param {*} type add-新增 / del-删除
* @returns
*/
onChangeAddOption = (len, type) => () => {
let { optionList, currentActiveList } = this.state;
let list = [];
// 新增
if (type === 'add') {
if (len === optionLetterLength) {
return;
}
optionList.push({ label: defalutLabel, value: optionLetter[len].value });
} else {
// 删除
currentActiveList = [];
optionList.splice(len, 1);
// 重新初始化ABCD对应的id
list = optionList.map((item, index) => {
return {
label: item.label,
value: optionLetter[index].value,
};
});
}
this.setState(
{
optionList: type === 'add' ? optionList : list,
currentActiveList,
},
() => {
this.handleChangeOption();
}
);
};
/**
* 确认/取消 编辑框
* @param {*} index
* @param {*} type submit/cancel
* @returns
*/
onChangeOptEditor = (index, type) => () => {
let { optionList } = this.state;
this.kindEditor && this.kindEditor.onClear();
if (type === 'submit') {
_.set(
optionList,
[index, 'label'],
!!this.subjectAnswer ? this.subjectAnswer : defalutLabel
);
}
_.set(optionList, [index, 'isShowEditor'], false);
this.subjectAnswer = '';
this.setState(
{
optionList,
},
() => {
this.handleChangeOption();
}
);
};
/**
* 展开 编辑项
* @param {*} index
* @returns
*/
onChangeShowEditor = (index) =>
debounce(() => {
let { optionList } = this.state;
if (optionList.filter((item) => item.isShowEditor).length > 0) {
return message.info('请先确认正在编辑的选项内容');
}
_.set(optionList, [index, 'isShowEditor'], true);
this.setState(
{
optionList,
},
() => {
this.kindEditor && this.kindEditor.onCashBack();
}
);
});
/**
* 富文本编辑器
* @param {*} e
*/
onChangeEditor = (index) => (e) => {
this.subjectAnswer = e;
};
/**
* 正确选项
* @param {*} value
*/
onChangeSelect = (value) => {
const { isMultiple } = this.props;
let str = value;
if (!isMultiple) {
// 单选,格式化成数组
str = [value];
}
this.setState(
{
currentActiveList: str,
},
() => {
this.handleChangeOption();
}
);
};
/**
* 本题分值
*/
onChangeScore = (e) => {
this.setState(
{
scoreValue: e.target.value.trim(),
},
() => {
this.handleChangeOption();
}
);
};
/**
* 试题解析
* @param {*} e
*/
onChangeSubjectAnalysis = (e) => {
this.setState(
{
subjectAnalysis: e.target.value.trim(),
},
() => {
this.handleChangeOption();
}
);
};
/**
* 清空
*/
handleClearOption = () => {
this.subjectAnswer = ''; // 选项内容
this.setState({
optionList: [
{
label: defalutLabel,
value: 1,
},
{
label: defalutLabel,
value: 2,
},
{
label: defalutLabel,
value: 3,
},
{
label: defalutLabel,
value: 4,
},
], // 选项列表
currentActiveList: [], // 当前选中的项
scoreValue: '', // 分数
subjectAnalysis: '', //试题解析
});
};
/**
* 向父组件传值
*/
handleChangeOption = () => {
let { currentActiveList, scoreValue, subjectAnalysis, optionList } = this.state;
const { isJudge } = this.props;
let activeList = [];
if (!isJudge) {
// 单选/多选
activeList = optionList.map((item) => {
let flag = 0;
if (currentActiveList.includes(item.value)) {
flag = 1;
}
return {
optionType: item.value,
optionContent: item.label,
isCorrect: flag,
};
});
} else {
// 判断
activeList = currentActiveList;
}
console.log('向父组件传值', activeList, scoreValue, subjectAnalysis);
// this.props.handleChangeOption(activeList, scoreValue, subjectAnalysis);
this.props.handleChangeOption(activeList, 1, subjectAnalysis);
};
render() {
const { subjectAnalysis } = this.state;
const { isJudge } = this.props;
return (
<Fragment>
{!isJudge && this.renderOption()}
{this.renderOptionBtn()}
<div className="option-input-container">
<div className="option-input-title">试题解析</div>
<TextArea
placeholder="试题解析(非必填 限500字"
value={subjectAnalysis}
style={{ height: 48, width: '100%' }}
maxLength={500}
autoSize={{ minRows: 3, maxRows: 4 }}
onChange={(e) => this.onChangeSubjectAnalysis(e)}
/>
</div>
</Fragment>
);
}
/**
* 选项模块
* @returns
*/
renderOption = () => {
const { optionList } = this.state;
let listLen = optionList.length;
return (
<Fragment>
{optionList.length > 0 &&
optionList.map((item, index) => {
const isShowTip = item.label === defalutLabel;
return (
<div
className="option-input-container"
style={{ flexDirection: 'column' }}
key={`option_input_${index}`}>
<div className="option-input-main">
<div className="option-input-title-option">
{optionLetter[item.value - 1].label}&nbsp;&nbsp;&nbsp;&nbsp;
</div>
<div
className="option-input-item"
key={`option_id_${item.value}`}>
<Tooltip
title="点击可输入该选项内容"
onClick={this.onChangeShowEditor(index)}>
<div
className="option-input-item-header"
style={
isShowTip
? {
color: 'rgba(51,51,51,0.3)',
}
: {
color: 'rgba(51,51,51,0.9)',
}
}
dangerouslySetInnerHTML={{
__html: item.label,
}}></div>
</Tooltip>
<div className="option-input-item-delete">
{listLen > showDeleteLength && (
<Tooltip
title="删除选项"
onClick={this.onChangeAddOption(index, 'del')}>
<img
className="option-input-item-delete-icon"
src="https://img14.360buyimg.com/imagetools/jfs/t1/212738/17/4123/399/618dd36bEd53475f5/38e899e92bbd5d5e.png"
/>
</Tooltip>
)}
</div>
</div>
</div>
{item.isShowEditor && (
<div
key={`option_editor_${index}`}
className="option-input-main"
style={{ marginTop: 19 }}>
<div className="option-input-editor">
<KindEditor
ref={(ref) => {
this.kindEditor = ref;
}}
bodyHeight={145}
borderRadius={12}
onChange={this.onChangeEditor(index)}
cashBackText={isShowTip ? '' : item.label}
/>
<div className="option-input-editor-btns">
<Tooltip title="取消后内容将不会更新到选项框内">
<div
className="option-input-editor-btn"
onClick={this.onChangeOptEditor(
index,
'cancel'
)}>
取消
</div>
</Tooltip>
<Tooltip title="确定后内容将会更新到选项框内">
<div
className="option-input-editor-btn option-input-editor-submit-btn"
onClick={this.onChangeOptEditor(
index,
'submit'
)}>
确定
</div>
</Tooltip>
</div>
</div>
</div>
)}
</div>
);
})}
</Fragment>
);
};
/**
* 选项模块-操作按钮
* @returns
*/
renderOptionBtn = () => {
const { optionList, scoreValue, currentActiveList } = this.state;
const { isMultiple, isJudge } = this.props;
let listLen = optionList.length;
return (
<div className="option-input-container">
<div className="option-input-title option-input-title-required">题目操作</div>
<div style={{ display: 'flex', width: '100%' }}>
{!isJudge && (
<div
className="option-input-option-btn"
onClick={this.onChangeAddOption(listLen, 'add')}>
添加选项
</div>
)}
<div className="option-input-option-btn option-input-option-input">
正确选项
<Select
mode={isMultiple && 'multiple'}
defaultActiveFirstOption={false}
value={currentActiveList}
placeholder="请选择"
style={{ minWidth: isMultiple ? '64px' : '68px', marginLeft: 4 }}
onChange={this.onChangeSelect}>
{isJudge
? judgeList.map((item, index) => {
return (
<Option
key={`option_select_${item.value}`}
value={item.value}>
{item.label}
</Option>
);
})
: optionList.map((item, index) => {
return (
<Option
key={`option_select_${item.value}`}
value={item.value}>
{optionLetter[index].label}
</Option>
);
})}
</Select>
</div>
</div>
</div>
);
};
}

View File

@@ -0,0 +1,157 @@
.option-input-container {
display: flex;
align-items: center;
padding: 0 24px;
padding-top: 19px;
// 选项模块
.option-input-main {
display: flex;
align-items: center;
width: 100%;
// 选项label-abcd...
.option-input-title-option {
display: flex;
align-items: center;
justify-content: flex-end;
width: 140px;
line-height: 40px;
font-size: 16px;
color: rgba(51, 51, 51, 1);
}
.option-input-item {
display: flex;
justify-content: space-between;
width: 100%;
font-size: 14px;
line-height: 22px;
transition: all 0.5s;
cursor: pointer;
.option-input-item-header {
display: flex;
align-items: center;
flex-wrap: wrap;
padding: 4px 10px;
width: 100%;
min-height: 48px;
background: rgba(255, 255, 255, 1);
border: 1px solid #d9d9d9;
border-radius: 7px;
}
.option-input-item-delete {
display: flex;
align-items: center;
margin-left: 19px;
width: 19px;
.option-input-item-delete-icon {
width: 19px;
height: 19px;
}
}
}
// 编辑器
.option-input-editor {
display: flex;
flex-direction: column;
margin-right: 38px;
margin-left: 124px;
width: 100%;
font-size: 14px;
line-height: 22px;
transition: all 0.5s;
.option-input-editor-btns {
display: flex;
margin-top: 19px;
.option-input-editor-btn {
display: flex;
justify-content: center;
align-items: center;
width: 115px;
height: 43px;
font-size: 14px;
color: rgba(24, 24, 29, 1);
cursor: pointer;
background: rgba(255, 255, 255, 1);
border: 1px solid rgba(208, 212, 222, 1);
border-radius: 22px;
}
.option-input-editor-submit-btn {
margin-left: 19px;
color: rgba(255, 255, 255, 1);
font-weight: 500;
background: rgba(60, 110, 238, 1);
border: 2px solid rgba(60, 110, 238, 1);
}
}
}
}
// 必填项
.option-input-title {
display: flex;
align-items: center;
justify-content: flex-end;
width: 140px;
line-height: 40px;
font-size: 16px;
color: rgba(51, 51, 51, 1);
}
// 非必填项
.option-input-title-required {
&:before {
display: inline-block;
margin-right: 4px;
margin-top: 1px;
color: #f5222d;
font-size: 16px;
content: '*';
}
}
// 选项模块-操作按钮
.option-input-option-btn {
margin-right: 8px;
padding: 0 32px;
height: 48px;
text-align: center;
line-height: 48px;
font-size: 14px;
font-weight: normal;
color: rgba(60, 110, 238, 1);
cursor: pointer;
background: rgba(255, 255, 255, 1);
border: 1px solid rgba(240, 240, 240, 1);
border-radius: 7px;
}
// 选项模块-操作按钮-输入框
.option-input-option-input {
padding-left: 14px;
padding-right: 4px;
color: #333;
.ant-select,
.ant-select-open,
.ant-select-focused,
.ant-select-enabled {
box-shadow: none !important;
-webkit-box-shadow: none !important;
}
// 正确选项:去除两边外边距
.ant-select-selection--single .ant-select-selection__rendered {
margin-right: 0;
margin-left: 0;
}
// 正确选项:去除边框
.ant-select-selection {
border: none;
}
// 正确选项:值的位置优化
.ant-select-selection-selected-value {
margin-left: 10px;
}
.ant-input {
padding: 4px;
}
// 正确选项:距离右边的位置
.ant-select-selection__rendered {
margin-right: 0;
}
}
}

View File

@@ -0,0 +1,350 @@
import React, { Component, Fragment } from 'react';
import req from '@utils/request';
import TagsEditor from '@components/tags-editor';
import { apiName, ModuleType, starList } from '../../constant';
import './index.less';
export default class RankLabelBox extends Component {
constructor(props) {
super(props);
this.state = {
firstCategoryList: [],
secondCategoryList: [],
thirdCategoryList: [],
rankList: starList,
};
}
componentDidMount() {
this.geFirstCategoryList();
}
firstValue = ''; // 一级分类id
firstCategoryValue = ''; // 一级分类的值
secondCategoryValue = []; // 二级分类的值
thirdCategoryValue = []; // 三级标签的值
/**
* 初始化数据
*/
initRankLabel = () => {
this.firstCategoryValue = ''; // 一级分类的值
this.secondCategoryValue = []; // 二级分类的值
this.thirdCategoryValue = []; // 三级标签的值
this.firstValue = '';
this.setState(
{
firstCategoryList: [],
secondCategoryList: [],
thirdCategoryList: [],
rankList: starList,
},
() => {
this.geFirstCategoryList();
}
);
};
/**
* 获得一级分类数据
*/
geFirstCategoryList() {
const params = { categoryType: 1 };
req({
method: 'post',
data: params,
url: apiName.getInterviewCategory,
})
.then((res) => {
if (res.data && res.data.length > 0) {
let list = res.data.map((item, index) => {
return {
...item,
active: index == 0 ? true : false,
};
});
this.setState(
{
firstCategoryList: list,
secondCategoryList: [],
thirdCategoryList: [],
},
() => {
this.firstValue = list[0].categoryId;
this.getSecondCategoryList(this.firstValue);
this.getThirdCategoryList(this.firstValue);
}
);
} else {
this.setState({
firstCategoryList: [],
secondCategoryList: [],
thirdCategoryList: [],
});
}
})
.catch((err) => console.log(err));
}
/**
* 获得二级分类数据
* @param {*} id 一级分类id
*/
getSecondCategoryList(id) {
const params = { parentId: id, categoryType: 2 };
req({
method: 'post',
data: params,
url: apiName.getInterviewCategory,
})
.then((res) => {
this.firstCategoryValue = id;
this.secondCategoryValue = [];
this.thirdCategoryValue = [];
if (res.data && res.data.length > 0) {
this.setState({
secondCategoryList: res.data,
});
} else {
// 若需要新增时则需要将数组第一个item重置如下
this.setState({
secondCategoryList: [{ categoryName: '空', categoryId: -9999 }],
});
}
})
.catch((err) => console.log(err));
}
/**
* 获得三级分类数据
* @param {*} id 二级分类id
*/
getThirdCategoryList(id) {
const { subjectName } = this.props;
const params = {
primaryCategoryId: id || this.firstValue,
subjectName: subjectName,
};
req({
method: 'post',
data: params,
url: apiName.getRecommendLabel,
})
.then((res) => {
if (res.data && res.data.length > 0) {
let list = res.data.map((item) => {
return {
categoryName: item.labelName,
categoryId: item.labelId,
active: item?.isRecommend === 1 ? true : false,
};
});
this.thirdCategoryValue = this.formatList(list);
if (this.thirdCategoryValue.length >= 0) {
this.onChangeRankLabel();
}
this.setState({
thirdCategoryList: list,
});
} else {
// 若需要新增时则需要将数组第一个item重置如下
this.setState({
thirdCategoryList: [{ categoryName: '空', categoryId: -9999 }],
});
}
})
.catch((err) => console.log(err));
}
/**
* 选择职级-单选
* @param {*} handleStatusList
* @param {*} selectList
*/
onHandleChangeRank = (handleStatusList, selectList) => {
this.setState({ rankList: handleStatusList });
this.props.handleChangeRank(selectList);
};
/**
* 选择一级分类-单选
* @param {*} handleStatusList 带有是否选中状态的原数组
* @param {*} selectList 选中id的数组
*/
onChangeFirst = (handleStatusList, selectList) => {
this.setState({ firstCategoryList: handleStatusList });
this.firstValue = selectList[0];
// 获得二级分类
this.getSecondCategoryList(this.firstValue);
// 获得三级标签
this.getThirdCategoryList(this.firstValue);
};
/**
* 选择二级分类
* @param {*} handleStatusList 带有是否选中状态的原数组
* @param {*} selectList 选中id的数组
*/
onChangeSecondTags = (handleStatusList, selectList) => {
this.secondCategoryValue = selectList;
this.setState({ secondCategoryList: handleStatusList });
this.onChangeRankLabel();
};
/**
* 选择三级标签
* @param {*} handleStatusList 带有是否选中状态的原数组
* @param {*} selectList 选中id的数组
*/
onChangeThirdTags = (handleStatusList, selectList) => {
this.thirdCategoryValue = selectList;
this.setState({ thirdCategoryList: handleStatusList });
this.onChangeRankLabel();
};
/**
* 格式化数据-获得选中项id列表
* @param {*} list
* @returns
*/
formatList = (list) => {
let labelList = [];
list.forEach((item) => {
if (item.active) {
labelList.push(item.categoryId);
}
});
return labelList;
};
/**
* 向父组件传递
*/
onChangeRankLabel = () => {
console.log(
'问答题 -------',
this.firstCategoryValue,
this.secondCategoryValue,
this.thirdCategoryValue
);
this.props.onChangeRankLabel(
this.firstCategoryValue,
this.secondCategoryValue,
this.thirdCategoryValue
);
};
render() {
const { firstCategoryList, secondCategoryList, thirdCategoryList, rankList } = this.state;
return (
<Fragment>
{this.rendeRrankModule(rankList)}
{this.renderFirstModule(firstCategoryList)}
{secondCategoryList?.length > 0 && (
<Fragment>
{this.renderSecondModule(secondCategoryList)}
{thirdCategoryList?.length > 0 && this.renderThirdModule(thirdCategoryList)}
</Fragment>
)}
</Fragment>
);
}
/**
* 职级选择
* @param {*} rankList
* @returns
*/
rendeRrankModule = (rankList) => {
return (
<div className="upload-single-container">
<div className="upload-single-title">职级选择</div>
<div className="upload-single-main">
<TagsEditor
categoryList={rankList}
isSingleChoice={true}
onChangeLabel={this.onHandleChangeRank}
isDisabledReverseSelection={true}
/>
<span style={{ marginLeft: '8px', color: 'red' }}>
所选职级应为熟练掌握该题的最低职级
</span>
</div>
</div>
);
};
/**
* 一级分类选择
* @param {*} firstCategoryList
* @returns
*/
renderFirstModule = (firstCategoryList) => {
return (
<Fragment>
{firstCategoryList?.length > 0 && (
<div className="upload-single-container">
<div className="upload-single-title">一级分类</div>
<div className="upload-single-main">
<TagsEditor
categoryList={firstCategoryList}
isSingleChoice={true}
onChangeLabel={this.onChangeFirst}
isDisabledReverseSelection={true}
/>
</div>
</div>
)}
</Fragment>
);
};
/**
* 二级分类选择
* @param {*} secondCategoryList
* @returns
*/
renderSecondModule = (secondCategoryList) => {
return (
<div className="upload-single-container">
<div className="upload-single-title">二级分类</div>
<div className="upload-single-main">
<TagsEditor
moduleType={ModuleType.second}
categoryList={secondCategoryList}
isSingleChoice={false}
onChangeLabel={this.onChangeSecondTags}
// parentCategoryValue={[this.firstCategoryValue]}
// isAddTag={true}
// isDeleteTag={true}
/>
</div>
</div>
);
};
/**
* 三级标签选择
* @param {*} thirdCategoryList
* @returns
*/
renderThirdModule = (thirdCategoryList) => {
return (
<div className="upload-single-container">
<div className="upload-single-title">三级标签</div>
<div className="upload-single-main">
<TagsEditor
moduleType={ModuleType.third}
categoryList={thirdCategoryList}
isSingleChoice={false}
onChangeLabel={this.onChangeThirdTags}
// parentCategoryValue={[this.firstCategoryValue]}
// isAddTag={true}
// isDeleteTag={true}
/>
</div>
</div>
);
};
}

View File

@@ -0,0 +1,44 @@
.upload-single-container {
width: 1000px;
display: flex;
align-items: flex-start;
padding: 0 24px;
padding-top: 36px;
.upload-single-title {
display: flex;
align-items: center;
justify-content: flex-end;
width: 140px;
line-height: 40px;
font-size: 16px;
color: rgba(51, 51, 51, 1);
&:before {
display: inline-block;
margin-right: 4px;
margin-top: 1px;
color: #f5222d;
font-size: 16px;
content: '*';
}
}
.upload-single-main {
width: 100%;
// 题目输入框
.upload-single-input {
height: 40px;
}
// 一级大类
.ant-radio-group {
display: flex;
align-items: center;
flex: 1;
flex-wrap: wrap;
height: 40px;
}
.tag-active {
@include box-backgroundColor(0.1);
@include box-border();
@include font-color();
}
}
}

View File

@@ -0,0 +1,234 @@
import React, { Fragment } from 'react';
import { Modal, Tooltip } from 'antd';
import { letterList, judgeList } from '../../constant';
import './index.less';
export default function RepeatContentBox(props) {
const { isShowModalBox, repeatInfo, repeatQuestionsType } = props;
// const { isShowModalBox, repeatQuestionsType } = props;
// const repeatInfo = {
// repeatSubjectName:
// 'Chrome如Chrome如何支持小于12px的字Chrome如何支持小于12px的字Chrome如何支持小于12px的字Chrome如何支持小于12px的字何支持小于12px的字',
// repeatSubjectAnswe:
// 'Chrome如何支持小于12px的字Chrome如何支持小于12px的字Chrome如何支持小于12px的字Chrome如何支持小于12px的字Chrome如何支持小于12px的字Chrome如何支持小于12px的字',
// repeatSetterErp: 'suchunping3',
// repeatSetterName: '苏春萍',
// };
// const repeatInfo = {
// repeatSubjectName:
// 'Chrome如Chrome如何支持小于12px的字Chrome如何支持小于12px的字Chrome如何支持小于12px的字Chrome如何支持小于12px的字何支持小于12px的字',
// repeatOptionList: [
// {
// isCorrect: '',
// optionContent: 'Chrome如何支持小于12px的字Chrome如何支持小于12px的',
// optionType: 1,
// },
// {
// isCorrect: '',
// optionContent: 'Chrome如何支持小于12px的字Chrome如何支持小于12px的',
// optionType: 2,
// },
// {
// isCorrect: 1,
// optionContent: 'Chrome如何支持小于12px的字Chrome如何支持小于12px的',
// optionType: 3,
// },
// ],
// repeatSetterErp: 'suchunping3',
// repeatSetterName: '苏春萍',
// };
/**
* 确认录入
*/
const onSubmitRepeatModal = (e) => {
props.handleSubmitRepeatModal && props.handleSubmitRepeatModal();
};
/**
* 取消录入
*/
const onCancelRepeatModal = () => {
props.handleCancelRepeatModal && props.handleCancelRepeatModal();
};
const renderRepeat = (type, repeatInfo) => {
switch (type) {
case 1:
return renderBriefQuestions(repeatInfo);
case 2:
case 3:
return renderSelectQuestions(type, repeatInfo);
case 4:
return renderJudgeQuestions(repeatInfo);
}
};
/**
* 展示重复内容-问答型
* @returns
*/
const renderBriefQuestions = (repeatInfo) => {
return (
<div className="repeat-content-box">
<div className="repeat-subject-box">
<div className="repeat-subject-title">问答题</div>
<div className="repeat-subject-text">{repeatInfo.repeatSubjectName}</div>
</div>
<div className="repeat-subject-box">
<div className="repeat-subject-title">参考答案</div>
<div
className="repeat-subject-text"
dangerouslySetInnerHTML={{
__html: repeatInfo.repeatSubjectAnswe,
}}></div>
</div>
<div className="repeat-subject-box repeat-subject-info-box">
<div className="repeat-subject-title">来自</div>
<Tooltip
title={repeatInfo.repeatSetterErp}
placement="right"
style={{ fontSize: 14 }}>
{repeatInfo.repeatSetterName}
</Tooltip>
</div>
</div>
);
};
/**
* 展示重复内容-单选/多选
* @returns
*/
const renderSelectQuestions = (type, repeatInfo) => {
// 过滤获得正确选项
let repeatRightKey = repeatInfo?.repeatOptionList?.filter((item) => item.isCorrect === 1);
return (
<div className="repeat-content-box">
<div className="repeat-subject-box">
<div className="repeat-subject-title">{type === 2 ? '单选题' : '多选题'}</div>
<div className="repeat-subject-text">{repeatInfo.repeatSubjectName}</div>
</div>
{repeatInfo?.repeatOptionList?.length > 0 && (
<div className="repeat-subject-box">
<div className="repeat-subject-title">选项内容</div>
<div className="repeat-subject-list">
{repeatInfo.repeatOptionList.map((item, index) => {
return (
<div
className="repeat-subject-item"
key={`repeat_option_${index}`}>
{/* <div className="repeat-subject-label">
{letterList[item.optionType]}
</div> */}
<div
className="repeat-subject-text"
dangerouslySetInnerHTML={{
__html: item.optionContent,
}}></div>
</div>
);
})}
</div>
</div>
)}
{repeatRightKey?.length > 0 && (
<div className="repeat-subject-box">
<div className="repeat-subject-title">答案</div>
<div className="repeat-subject-list">
{repeatRightKey.map((item, index) => {
return (
<span key={`repeat_answe_${index}`}>
{letterList[item.optionType]}{' '}
</span>
);
})}
</div>
</div>
)}
{!!repeatInfo.repeatSubjectAnswe && (
<div className="repeat-subject-box">
<div className="repeat-subject-title">题目解析</div>
<div
className="repeat-subject-text"
dangerouslySetInnerHTML={{
__html: repeatInfo.repeatSubjectAnswe,
}}></div>
</div>
)}
<div className="repeat-subject-box repeat-subject-info-box">
<div className="repeat-subject-title">来自</div>
<Tooltip
title={repeatInfo.repeatSetterErp}
placement="right"
style={{ fontSize: 14 }}>
{repeatInfo.repeatSetterName}
</Tooltip>
</div>
</div>
);
};
/**
* 展示重复内容-判断
* @returns
*/
const renderJudgeQuestions = (repeatInfo) => {
return (
<div className="repeat-content-box">
<div className="repeat-subject-box">
<div className="repeat-subject-title">判断题</div>
<div className="repeat-subject-text">{repeatInfo.repeatSubjectName}</div>
</div>
<div className="repeat-subject-box">
<div className="repeat-subject-title">答案</div>
<div className="repeat-subject-list">
{judgeList[repeatInfo.repeatIsCorrect]}
</div>
</div>
{!!repeatInfo.repeatSubjectAnswe && (
<div className="repeat-subject-box">
<div className="repeat-subject-title">题目解析</div>
<div
className="repeat-subject-text"
dangerouslySetInnerHTML={{
__html: repeatInfo.repeatSubjectAnswe,
}}></div>
</div>
)}
<div className="repeat-subject-box repeat-subject-info-box">
<div className="repeat-subject-title">来自</div>
<Tooltip
title={repeatInfo.repeatSetterErp}
placement="right"
style={{ fontSize: 14 }}>
{repeatInfo.repeatSetterName}
</Tooltip>
</div>
</div>
);
};
return (
<Modal
className="repeat-content-repeat-box"
visible={isShowModalBox}
title={
<Fragment>
<span
style={{
color: 'rgba(60, 110, 238, 0.8)',
fontSize: 50,
marginRight: 10,
}}>
{repeatInfo.repeatRate || '10%'}
</span>
重复率
</Fragment>
}
onOk={onSubmitRepeatModal}
onCancel={onCancelRepeatModal}
okText="确认录入"
cancelText="取消录入">
{renderRepeat(repeatQuestionsType, repeatInfo)}
</Modal>
);
}

View File

@@ -0,0 +1,88 @@
.repeat-content-box {
font-size: 14px;
.repeat-subject-box {
padding-bottom: 14px;
margin-bottom: 14px;
line-height: 22px;
border-bottom: 1px dotted #e4e4e4;
.repeat-subject-title {
margin-right: 8px;
margin-bottom: 4px;
font-size: 12px;
font-weight: 600;
color: #3c6eee;
line-height: 22px;
}
.repeat-subject-text {
flex: 1;
line-height: 18px;
font-size: 14px;
color: rgba(51, 51, 51, 0.8);
}
.repeat-subject-list {
.repeat-subject-item {
display: flex;
margin-bottom: 20px;
padding: 12px 10px 13px;
background: #fff;
border: 1px solid #d4d4d4;
border-radius: 4px;
// .repeat-subject-label {
// margin-right: 4px;
// width: 20px;
// height: 20px;
// line-height: 20px;
// text-align: center;
// color: rgba(51, 51, 51, 0.8);
// border-radius: 50%;
// @include box-backgroundColor(0.3);
// }
.repeat-subject-text {
line-height: 22px;
font-size: 14px;
color: rgba(51, 51, 51, 0.8);
}
&:last-child {
margin-bottom: 0;
}
}
}
}
.repeat-subject-info-box {
display: flex;
margin-bottom: 0;
}
}
.repeat-content-repeat-box {
// 弹框宽度
.ant-modal-content {
width: 630px;
// 提示框-头部title位置
.ant-modal-header {
border-bottom: none;
.ant-modal-title {
display: flex;
justify-content: center;
align-items: baseline;
margin-top: 10px;
font-size: 13px;
line-height: 40px;
color: rgba(51, 51, 51, 0.8);
}
}
// 提示框-两个按钮
.ant-modal-footer {
display: flex;
justify-content: center;
padding: 14px 0;
border-top: none;
.ant-btn {
border-radius: 18px;
}
.ant-btn-primary {
background: rgba(60, 110, 238, 1);
margin-left: 60px;
}
}
}
}

View File

@@ -0,0 +1,338 @@
import React, { Component, Fragment } from 'react';
import { Input, Modal, message, Spin } from 'antd';
import _ from 'lodash';
import req from '@utils/request';
import { debounce } from '@utils';
import KindEditor from '../kind-editor';
import RankLabelBox from '../rank-label-box';
import OptionInputBox from '../option-input-box';
import RepeatContentBox from '../repeat-content-box';
import { apiName } from '../../constant';
import './index.less';
const defalutLabel = '请使用富文本编辑器输入选项内容';
export default class SingleQuestions extends Component {
constructor(props) {
super(props);
this.state = {
subjectName: '', // 题目
isDisabledSubmit: true, //是否禁止输入
isShowModalBox: false, // 是否展示重复率弹框
isSubmit: true, // 是否支持提交
};
}
kindEditor = KindEditor | null;
rankLabelBox = RankLabelBox | null;
optionInputBox = OptionInputBox | null;
currentActive = []; // 选项列表
scoreValue = ''; // 分数
subjectAnalysis = ''; //试题解析
rankId = 1; //职级
subjectAnswer = ''; // 选项内容
firstCategoryValue = ''; // 一级分类的值
secondCategoryValue = []; // 二级分类的值
thirdCategoryValue = []; // 三级标签的值
repeatInfo = {}; // 重复率
/**
* 输入题目
* @param {*} e
*/
onChangeSubjectName = (e) => {
let str = e.target.value.trim();
this.setState(
{
subjectName: str,
},
() => {
this.rankLabelBox.getThirdCategoryList();
let isDisabledSubmit = this.checkData();
this.setState({
isDisabledSubmit,
});
}
);
};
/**
* 一次确认录入
*/
onSubmit = debounce(() => {
const { subjectName, isDisabledSubmit, isSubmit } = this.state;
if (isDisabledSubmit || !isSubmit) {
return;
}
this.setState({
isSubmit: false,
});
let params = {
subjectName: subjectName,
difficulty: this.rankId,
subjectType: 1,
subjectScore: this.scoreValue,
subjectParse: this.subjectAnalysis,
categoryIds: this.secondCategoryValue,
labelIds: this.thirdCategoryValue,
optionList: this.currentActive,
};
console.log('单选题录入 ----', params);
req({
method: 'post',
data: params,
url: apiName.addInterviewSubject,
})
.then((res) => {
this.repeatInfo = {};
if (res.data && res.data.insertStatus) {
this.setState(
{
isSubmit: true,
},
() => {
this.successModalConfirm();
}
);
} else if (!res.data.insertStatus) {
this.repeatInfo = {
repeatDocId: res.data.docId, // 重复题目id
repeatRate: res.data.repeatRate, // 重复率
repeatSubjectName: res.data.subjectName, // 重复题目
repeatOptionList: res.data.optionList, // 重复列表项
repeatSetterErp: res.data.subjectSetterErp, // 出题人erp
repeatSetterName: res.data.subjectSetterName, // 出题人姓名
};
this.setState({
isShowModalBox: true,
isSubmit: true,
});
}
})
.catch((err) => {
this.setState({
isSubmit: true,
});
console.log(err);
});
});
/**
* 校验是否支持点击按钮
* @returns
*/
checkData = () => {
const { subjectName } = this.state;
let list = this.currentActive.filter((item) => item.optionContent === defalutLabel);
let isDisabledSubmit = false;
if (
!!!subjectName ||
list.length > 0 ||
!!!this.firstCategoryValue ||
this.secondCategoryValue.length <= 0 ||
this.thirdCategoryValue.length <= 0 ||
!!!this.scoreValue
) {
isDisabledSubmit = true;
}
return isDisabledSubmit;
};
/**
* 取消
*/
onCancel = () => {
this.currentActive = []; // 选项列表
this.scoreValue = ''; // 分数
this.subjectAnalysis = ''; //试题解析
this.rankId = 1;
this.subjectAnswer = ''; // 选项内容
this.firstCategoryValue = ''; // 一级分类的值
this.secondCategoryValue = []; // 二级分类的值
this.thirdCategoryValue = []; // 三级标签的值
this.kindEditor && this.kindEditor.onClear();
this.rankLabelBox.initRankLabel();
this.optionInputBox.handleClearOption();
this.repeatInfo = {};
this.setState({
subjectName: '',
isShowModalBox: false,
isSubmit: true, // 是否支持提交
});
};
/**
* 重复率弹框-确认录入
*/
onSubmitRepeatModal = debounce(
() => {
let params = {
docId: this.repeatInfo.repeatDocId,
};
req({
method: 'post',
data: params,
url: apiName.addRepeatInterviewSubject,
})
.then((res) => {
if (res.data) {
this.successModalConfirm();
} else {
message.info('请再次确认');
}
})
.catch((err) => {
console.log(err);
message.error('请再次确认');
});
},
300,
true
);
/**
* 重复率弹框-取消录入
*/
onCancelRepeatModal = () => {
this.repeatInfo = {};
this.setState({
isShowModalBox: false,
});
};
/**
* 录入成功的弹框
*/
successModalConfirm = () => {
Modal.confirm({
title: (
<div
style={{
textAlign: 'center',
color: 'rgba(60, 110, 238, 1)',
fontSize: 16,
}}>
录入成功贡献榜火力值 + 1
</div>
),
closable: false,
maskClosable: false,
icon: ' ',
onOk: this.onAgainSuccessModal,
onCancel: this.onGoHomeSuccessModal,
okText: '再录一题',
cancelText: '去首页',
className: 'questions-success-modal-confirm',
});
};
/**
* 录入成功弹框-再来一题
*/
onAgainSuccessModal = () => {
this.onCancel();
};
/**
* 录入成功弹框-去首页
*/
onGoHomeSuccessModal = () => {
window.location.href = '/cms-supplier/question-bank';
};
/**
* 分类选择
* @param {*} e
*/
onChangeRankLabel = (firstCategoryValue, secondCategoryValue, thirdCategoryValue) => {
this.firstCategoryValue = firstCategoryValue; // 一级分类的值
this.secondCategoryValue = secondCategoryValue; // 二级分类的值
this.thirdCategoryValue = thirdCategoryValue; // 三级标签的值
let isDisabledSubmit = this.checkData();
this.setState({
isDisabledSubmit,
});
};
/**
* 职级选择
* @param {*} list
*/
handleChangeRank = (list) => {
this.rankId = list[0];
let isDisabledSubmit = this.checkData();
this.setState({
isDisabledSubmit,
});
};
/**
* 选项操作
* @param {*} currentActive 选项列表
* @param {*} scoreValue 分值
* @param {*} subjectAnalysis 解析
*/
handleChangeOption = (currentActive, scoreValue, subjectAnalysis) => {
this.currentActive = currentActive;
this.scoreValue = scoreValue;
this.subjectAnalysis = subjectAnalysis;
let isDisabledSubmit = this.checkData();
this.setState({
isDisabledSubmit,
});
};
render() {
const { subjectName, isDisabledSubmit, isSubmit, isShowModalBox } = this.state;
const { questionsType } = this.props;
return (
<Spin spinning={!isSubmit}>
<Fragment>
<div className="single-questions-container">
<div className="single-questions-title">题目名称</div>
<Input
placeholder="输入题目"
style={{ height: 48, width: '100%' }}
value={subjectName}
maxLength={64}
onChange={(e) => this.onChangeSubjectName(e)}
/>
</div>
<OptionInputBox
key="single-option-input"
ref={(ref) => {
this.optionInputBox = ref;
}}
handleChangeOption={this.handleChangeOption}
/>
<RankLabelBox
ref={(ref) => {
this.rankLabelBox = ref;
}}
subjectName={subjectName}
onChangeRankLabel={this.onChangeRankLabel}
handleChangeRank={this.handleChangeRank}
/>
<div className="single-questions-btns-container">
<div className="single-questions-btn" onClick={this.onCancel}>
清空
</div>
<div
className={`single-questions-btn single-questions-submit ${isDisabledSubmit && 'single-questions-disabled-submit'
}`}
onClick={this.onSubmit}>
提交
</div>
</div>
<RepeatContentBox
isShowModalBox={isShowModalBox}
repeatQuestionsType={questionsType}
repeatInfo={this.repeatInfo}
handleSubmitRepeatModal={this.onSubmitRepeatModal}
handleCancelRepeatModal={this.onCancelRepeatModal}
/>
</Fragment>
</Spin>
);
}
}

View File

@@ -0,0 +1,53 @@
.single-questions-container {
width: 1000px;
display: flex;
align-items: center;
padding: 0 24px;
padding-top: 36px;
// label名字title
.single-questions-title {
display: flex;
align-items: center;
justify-content: flex-end;
width: 140px;
line-height: 40px;
font-size: 16px;
color: rgba(51, 51, 51, 1);
&:before {
display: inline-block;
margin-right: 4px;
margin-top: 1px;
color: #f5222d;
font-size: 16px;
content: '*';
}
}
}
.single-questions-btns-container {
display: flex;
justify-content: flex-end;
align-items: center;
margin: 20px auto;
width: 952px;
.single-questions-btn {
display: flex;
align-items: center;
justify-content: center;
width: 150px;
height: 40px;
font-size: 16px;
cursor: pointer;
border: 1px solid #d9d9d9;
border-radius: 10px;
}
.single-questions-submit {
margin-left: 40px;
background-color: #4390f7;
color: #fff;
border: 1px solid #4390f7;
}
.single-questions-disabled-submit {
opacity: 0.5;
}
}

View File

@@ -0,0 +1,27 @@
import React from 'react';
import { Affix } from 'antd';
import { RightOutlined } from '@ant-design/icons';
import './index.less';
export default function UploadLeftLayout(props) {
return (
<Affix offsetTop={150}>
<div className="upload-left-layout">
{props.layoutList.map((item, index) => {
return (
<div
className={`upload-left-layout-item ${item.active ? 'upload-left-layout-item-active' : ''
}`}
onClick={() => {
props.onChange(index);
}}
key={`upload_left_layout_${item.id}`}>
{item.title}
<RightOutlined style={{ marginLeft: 54 }} />
</div>
);
})}
</div>
</Affix>
);
}

View File

@@ -0,0 +1,32 @@
.upload-left-layout {
width: 233px;
display: flex;
flex-direction: column;
padding-left: 26px;
.upload-left-layout-item {
display: flex;
justify-content: flex-end;
align-items: center;
padding-right: 10px;
margin-bottom: 20px;
width: 207px;
height: 48px;
font-size: 16px;
color: rgba(57, 60, 76, 1);
transition: all 0.3s;
background: rgba(255, 255, 255, 1);
border: 1px solid rgba(208, 212, 222, 1);
border-radius: 12px;
&:hover {
@include font-color(0.9);
@include box-backgroundColor(0.3);
}
}
.upload-left-layout-item-active {
color: rgba(60, 110, 238, 1);
font-weight: bold;
background: rgba(60, 110, 238, 0.1);
border: 1px solid rgba(60, 110, 238, 1);
}
}

View File

@@ -0,0 +1,139 @@
/**
* API名称
*/
export const apiName = {
/**
* 获取一级/二级分类
*/
getInterviewCategory: '/admin/question/category/getCategory',
/**
* 获取三级分类标签
*/
getRecommendLabel: '/admin/question/label/getRecommendLabel',
/**
* 新增题目
*/
addInterviewSubject: '/admin/question/subject/add',
/**
* 新增重复题目
*/
addRepeatInterviewSubject: '/admin/question/subject/addRepeatSubject',
};
/**
* 模块类型
*/
export const ModuleType = {
default: 'default',
second: 'second',
third: 'third',
};
/**
* 导入职级对应的星
*/
export const starList = [
{
categoryId: 1,
categoryName: 'T4',
active: true,
},
{
categoryId: 2,
categoryName: 'T5',
},
{
categoryId: 3,
categoryName: 'T6',
},
{
categoryId: 4,
categoryName: 'T7',
},
{
categoryId: 5,
categoryName: 'T8',
},
];
/**
* 模块类型
*/
export const uploadLayout = [
{
id: 1,
title: '问答题',
active: true,
},
{
id: 2,
title: '单选题',
active: false,
},
{
id: 3,
title: '多选题',
active: false,
},
{
id: 4,
title: '判断题',
active: false,
},
];
/**
* 数组索引对应字母
*/
export const optionLetter = {
0: {
label: 'A',
value: 1,
},
1: {
label: 'B',
value: 2,
},
2: {
label: 'C',
value: 3,
},
3: {
label: 'D',
value: 4,
},
4: {
label: 'E',
value: 5,
},
5: {
label: 'F',
value: 6,
},
6: {
label: 'G',
value: 7,
},
};
/**
* 字母id对应字母
*/
export const letterList = {
1: 'A',
2: 'B',
3: 'C',
4: 'D',
5: 'E',
6: 'F',
7: 'G',
};
/**
* 正确/错误
*/
export const judgeList = {
0: '错误',
1: '正确',
};

View File

@@ -0,0 +1,19 @@
.upload-questions-box {
position: relative;
margin: 0 auto;
width: 1439px;
overflow-y: auto;
border-radius: 5px;
.ant-card-head {
position: sticky;
top: 0;
left: 0;
right: 0;
padding: 0 48px;
z-index: 10;
background-color: #fff;
}
.ant-card-body {
display: flex;
}
}

View File

@@ -0,0 +1,49 @@
import React, { Component } from 'react';
import { Card } from 'antd';
import SingleBox from './pages/single-box';
import BatchleBox from './pages/batch-box';
import './index.less';
const tabList = [
{
key: 'singleBox',
tab: '单题录入',
},
// {
// key: 'batchBox',
// tab: '批量导入',
// },
];
export default class UploadQuestions extends Component {
constructor(props) {
super(props);
this.state = { currentKey: 'singleBox' };
}
contentList = {
singleBox: <SingleBox />,
batchBox: <BatchleBox />,
};
onTabChange = (e) => {
this.setState({
currentKey: e,
});
};
render() {
const { currentKey } = this.state;
return (
<div className="upload-questions-box">
<Card
style={{ width: '100%' }}
tabList={tabList}
bordered={false}
activeTabKey={currentKey}
onTabChange={(key) => {
this.onTabChange(key, 'key');
}}>
{this.contentList[currentKey]}
</Card>
</div>
);
}
}

View File

@@ -0,0 +1,12 @@
import React, { Component } from 'react';
import './index.less';
export default class BatchBox extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return <div className="upload-batch-questions-box">批量上传</div>;
}
}

View File

@@ -0,0 +1,7 @@
.upload-batch-questions-box {
margin: 0 auto;
width: 1000px;
display: flex;
border-radius: 5px;
border: 1px solid #3d3d3d;
}

View File

@@ -0,0 +1,64 @@
import React, { Component } from 'react';
import UploadLeftLayout from '../../components/upload-left-layout';
import BriefQuestions from '../../components/brief-questions';
import SingleQuestions from '../../components/single-questions';
import MultipleQuestions from '../../components/multiple-questions';
import JudgeQuestions from '../../components/judge-questions';
import { uploadLayout } from '../../constant';
import './index.less';
export default class SingleBox extends Component {
constructor(props) {
super(props);
this.state = {
layoutList: uploadLayout,
currentIndex: 0,
};
}
/**
* 切换题型
* @param {*} id
*/
onChangeQuestionsType = (layoutIndex) => {
let { layoutList, currentIndex } = this.state;
if (currentIndex === layoutIndex) {
return;
}
let list = layoutList.map((item, index) => {
let flag = false;
if (layoutIndex === index) {
flag = true;
}
return {
...item,
active: flag,
};
});
this.setState({
layoutList: list,
currentIndex: layoutIndex,
});
};
render() {
const { currentIndex, layoutList } = this.state;
return (
<div style={{ display: 'flex' }}>
<UploadLeftLayout layoutList={layoutList} onChange={this.onChangeQuestionsType} />
<div className="upload-questions-modular">{this.changeReander(currentIndex)}</div>
</div>
);
}
changeReander = (i) => {
switch (i) {
case 0:
return <BriefQuestions questionsType={i + 1} key={`question_${i}`} />;
case 1:
return <SingleQuestions questionsType={i + 1} key={`question_${i}`} />;
case 2:
return <MultipleQuestions questionsType={i + 1} key={`question_${i}`} />;
case 3:
return <JudgeQuestions questionsType={i + 1} key={`question_${i}`} />;
}
};
}

View File

@@ -0,0 +1,29 @@
.ant-modal-body {
padding: 12px 24px 0;
}
.upload-questions-modular {
display: flex;
flex-direction: column;
margin: 0 auto;
margin-left: 35px;
background: rgba(249, 250, 252, 1);
border: 2px solid rgba(240, 240, 240, 1);
border-radius: 12px;
}
.questions-success-modal-confirm {
width: 300px;
// 录入成功弹框
.ant-modal-confirm-btns {
display: flex;
justify-content: center;
width: 100%;
.ant-btn {
width: 100px;
border-radius: 18px;
}
.ant-btn-primary {
background: rgba(60, 110, 238, 1);
margin-left: 60px;
}
}
}