feat: 刷题页面
This commit is contained in:
67
src/components/category-list/constant.js
Normal file
67
src/components/category-list/constant.js
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 难度筛选
|
||||
*/
|
||||
export const filterDifficulty = [
|
||||
{
|
||||
id: 0,
|
||||
title: '全部',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: 'T4',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'T5',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'T6',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'T7',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'T8',
|
||||
},
|
||||
];
|
||||
|
||||
export const apiName = {
|
||||
/**
|
||||
* 获取二级和三级标签
|
||||
*/
|
||||
getCategoryLabelInfo: '/admin/question/category/getCategoryLabelInfo',
|
||||
};
|
||||
|
||||
export const imgObject = {
|
||||
clickImg:
|
||||
'../../views/imgs/clickImg.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:
|
||||
'https://img14.360buyimg.com/imagetools/jfs/t1/213752/24/2703/4803/617f4fc4E037da291/5f8050641d4d73d2.png',
|
||||
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',
|
||||
};
|
362
src/components/category-list/index.jsx
Normal file
362
src/components/category-list/index.jsx
Normal file
@@ -0,0 +1,362 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
|
||||
import {
|
||||
RightOutlined,
|
||||
UpOutlined,
|
||||
DownOutlined,
|
||||
CaretDownOutlined,
|
||||
CaretUpOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import req from '@utils/request';
|
||||
import { Divider } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import './index.less';
|
||||
import { apiName, imgObject } from './constant';
|
||||
|
||||
/**
|
||||
* 大分类中的背景图
|
||||
*/
|
||||
export const categoryBackImg = {
|
||||
0: imgObject.backAllImg,
|
||||
1: imgObject.dataImg,
|
||||
2: imgObject.javaImg,
|
||||
3: imgObject.npmImg,
|
||||
4: imgObject.parallelComputingImg,
|
||||
5: imgObject.springbootImg,
|
||||
6: imgObject.sqlImg,
|
||||
7: imgObject.systemDesignImg,
|
||||
8: imgObject.algorithmImg,
|
||||
};
|
||||
|
||||
const categoryShowCount = 4;
|
||||
|
||||
export default class CategoryList extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
secondCategoryList: [],
|
||||
currentActive: '',
|
||||
isPutAway: true, // 是否收起 默认收起状态
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// this.initCategoryList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化数据,默认选择第一个
|
||||
*/
|
||||
initCategoryList() {
|
||||
const { categoryList, primaryCategoryId } = this.props;
|
||||
let currentActive = primaryCategoryId ?? categoryList[0].primaryCategoryId;
|
||||
this.props.onChangeCategory(currentActive);
|
||||
this.getSecondCategoryList(currentActive);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得二级三级分类数据
|
||||
*/
|
||||
getSecondCategoryList(currentActive) {
|
||||
const { categoryListMap } = this.props;
|
||||
// 调用接口返回二级三级数据
|
||||
let params = {
|
||||
primaryCategoryId: currentActive,
|
||||
subjectTypeList: [4],
|
||||
};
|
||||
req({
|
||||
method: 'post',
|
||||
data: params,
|
||||
url: apiName.getCategoryLabelInfo,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.data && res.data.length > 0) {
|
||||
let secondCategoryList = res.data;
|
||||
let listLen =
|
||||
categoryListMap &&
|
||||
categoryListMap[currentActive] &&
|
||||
categoryListMap[currentActive].length;
|
||||
let objActive = {};
|
||||
listLen > 0 &&
|
||||
categoryListMap &&
|
||||
categoryListMap[currentActive] &&
|
||||
categoryListMap[currentActive].forEach((item) => {
|
||||
objActive[item] = item;
|
||||
});
|
||||
secondCategoryList.forEach((categoryItem) => {
|
||||
categoryItem.labelInfoList.forEach((item) => {
|
||||
if (listLen > 0 && objActive[item.assembleId]) {
|
||||
item.active = true;
|
||||
} else {
|
||||
item.active = false;
|
||||
}
|
||||
});
|
||||
categoryItem.isOpen = false;
|
||||
});
|
||||
this.setState(
|
||||
{
|
||||
secondCategoryList,
|
||||
currentActive,
|
||||
},
|
||||
() => {
|
||||
let activeList = [];
|
||||
secondCategoryList.forEach((categoryItem) => {
|
||||
categoryItem.labelInfoList.forEach((item) => {
|
||||
if (item.active) {
|
||||
activeList.push(item.assembleId);
|
||||
}
|
||||
});
|
||||
});
|
||||
secondCategoryList.forEach((item, index) => {
|
||||
let height = document.getElementById('id_' + index)?.scrollHeight;
|
||||
let displayHeight = height > 43 ? 'flex' : 'none';
|
||||
!this.props.isHideSec &&
|
||||
(document.getElementById('second_id_' + index).style.display =
|
||||
displayHeight);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((err) => console.log(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换一级分类
|
||||
* @param {*} item
|
||||
* @returns
|
||||
*/
|
||||
onChangeCategory = (primaryCategoryId) => () => {
|
||||
let { currentActive } = this.state;
|
||||
if (currentActive === primaryCategoryId) {
|
||||
return;
|
||||
}
|
||||
this.props.isHideSec &&
|
||||
this.setState({
|
||||
currentActive: primaryCategoryId,
|
||||
});
|
||||
this.props.onChangeCategory(primaryCategoryId);
|
||||
!this.props.isHideSec && this.getSecondCategoryList(primaryCategoryId);
|
||||
};
|
||||
|
||||
/**
|
||||
* 选择标签-支持单选(多选)
|
||||
* @param {*} categoryId 一级分类id
|
||||
* @param {*} childrenLevelList 二级分类对象的标签列表
|
||||
* @param {*} secondCategoryIndex 二级分类对象index
|
||||
* @param {*} thirdCategoryIndex 三级标签index
|
||||
* @param {*} active 三级标签当前的选中状态
|
||||
* @returns
|
||||
*/
|
||||
onChangeLabel = (childrenLevelList, secondCategoryIndex, thirdCategoryIndex, active) => () => {
|
||||
let { secondCategoryList, currentActive } = this.state;
|
||||
const { isMultipleChoice } = this.props;
|
||||
if (isMultipleChoice) {
|
||||
// 三级标签支持多选
|
||||
_.set(childrenLevelList, [thirdCategoryIndex, 'active'], !active);
|
||||
_.set(secondCategoryList, [secondCategoryIndex, 'labelInfoList'], childrenLevelList);
|
||||
} else {
|
||||
// 三级标签支持单选
|
||||
let formatLabelList = childrenLevelList.map((item, index) => {
|
||||
let flag = false;
|
||||
if (index === thirdCategoryIndex) {
|
||||
flag = !active; // 将三级标签设置选中/未选中
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
active: flag,
|
||||
};
|
||||
});
|
||||
_.set(secondCategoryList, [secondCategoryIndex, 'labelInfoList'], formatLabelList);
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
secondCategoryList,
|
||||
},
|
||||
() => {
|
||||
let activeList = [];
|
||||
secondCategoryList.forEach((categoryItem) => {
|
||||
categoryItem.labelInfoList.forEach((item) => {
|
||||
if (item.active) {
|
||||
activeList.push(item.assembleId);
|
||||
}
|
||||
});
|
||||
});
|
||||
this.props.onChangeLabel(currentActive, activeList);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 展开/收起
|
||||
* @param {*} secondCategoryIndex
|
||||
* @returns
|
||||
*/
|
||||
onChangeOpenStatus = (secondCategoryIndex, isOpen) => () => {
|
||||
let { secondCategoryList } = this.state;
|
||||
_.set(secondCategoryList, [secondCategoryIndex, 'isOpen'], !isOpen);
|
||||
this.setState({
|
||||
secondCategoryList,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 展开/收起
|
||||
*/
|
||||
onChangePutAway = () => {
|
||||
let { isPutAway } = this.state;
|
||||
this.setState({
|
||||
isPutAway: !isPutAway,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { categoryList } = this.props;
|
||||
const { secondCategoryList } = this.state;
|
||||
return (
|
||||
<div className="category-box">
|
||||
<Fragment>{categoryList?.length > 0 && this.renderFirstContainer()}</Fragment>
|
||||
{!this.props.isHideSec && (
|
||||
<Fragment>
|
||||
{secondCategoryList?.length > 0 && this.renderSecondContainer()}
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 一级分类模块
|
||||
* @returns
|
||||
*/
|
||||
renderFirstContainer = () => {
|
||||
const { categoryList } = this.props;
|
||||
const { currentActive } = this.state;
|
||||
return (
|
||||
<div className="first-category-list">
|
||||
{categoryList.slice(0, 7).map((categoryModuleItem, categoryModuleIndex) => {
|
||||
return (
|
||||
<div
|
||||
className={`first-category-item ${categoryModuleItem.primaryCategoryId === currentActive &&
|
||||
'first-category-item-active'
|
||||
}`}
|
||||
key={`first_category_${categoryModuleItem.primaryCategoryId}`}
|
||||
style={{
|
||||
backgroundImage: `url(${categoryBackImg[categoryModuleIndex]})`,
|
||||
}}
|
||||
onClick={this.onChangeCategory(categoryModuleItem.primaryCategoryId)}>
|
||||
<div className="first-category-item-title">
|
||||
{categoryModuleItem.levelName}
|
||||
</div>
|
||||
<div className="first-category-item-count">
|
||||
{categoryModuleItem.count}道题
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{categoryList.length > 7 && (
|
||||
<div className="first-category-more">
|
||||
更多
|
||||
<RightOutlined />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 二级分类模块
|
||||
* @returns
|
||||
*/
|
||||
renderSecondContainer = () => {
|
||||
const { secondCategoryList, isPutAway } = this.state;
|
||||
return (
|
||||
<div className="second-category-list">
|
||||
{secondCategoryList.map((secondCategoryItem, secondCategoryIndex) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display:
|
||||
secondCategoryIndex >= categoryShowCount && isPutAway
|
||||
? 'none'
|
||||
: 'flex',
|
||||
}}
|
||||
className="second-category-item"
|
||||
key={`second_category_${secondCategoryItem.categoryId}`}>
|
||||
<div className="second-category-item-title">
|
||||
{secondCategoryItem.categoryName}:
|
||||
</div>
|
||||
{secondCategoryItem?.labelInfoList?.length > 0 && (
|
||||
<div className="second-category-item-box">
|
||||
<div
|
||||
style={{
|
||||
height: secondCategoryItem.isOpen ? 'auto' : 43,
|
||||
}}
|
||||
className="second-category-item-list"
|
||||
id={`id_${secondCategoryIndex}`}>
|
||||
{secondCategoryItem.labelInfoList.map(
|
||||
(thirdCategoryItem, thirdCategoryIndex) => {
|
||||
return (
|
||||
<div
|
||||
className={`third-category-item ${thirdCategoryItem.active
|
||||
? 'third-category-item-active'
|
||||
: ''
|
||||
}`}
|
||||
key={`third_category_${thirdCategoryItem.id}`}
|
||||
onClick={this.onChangeLabel(
|
||||
secondCategoryItem.labelInfoList,
|
||||
secondCategoryIndex,
|
||||
thirdCategoryIndex,
|
||||
thirdCategoryItem.active
|
||||
)}>
|
||||
{thirdCategoryItem.labelName}·
|
||||
{thirdCategoryItem.subjectCount}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
id={`second_id_${secondCategoryIndex}`}
|
||||
className="second-category-item-status"
|
||||
onClick={this.onChangeOpenStatus(
|
||||
secondCategoryIndex,
|
||||
secondCategoryItem.isOpen
|
||||
)}>
|
||||
<div className="second-category-item-type" style={{ fontSize: 12 }}>
|
||||
{secondCategoryItem.isOpen ? '收起' : '展开'}
|
||||
</div>
|
||||
<div className="second-category-item-icon" style={{ fontSize: 12 }}>
|
||||
{secondCategoryItem.isOpen ? (
|
||||
<UpOutlined />
|
||||
) : (
|
||||
<DownOutlined />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{secondCategoryList?.length >= categoryShowCount && (
|
||||
<Divider
|
||||
onClick={this.onChangePutAway}
|
||||
dashed
|
||||
style={{
|
||||
marginTop: 10,
|
||||
fontSize: 13,
|
||||
}}>
|
||||
{isPutAway ? '展开' : '收起'}
|
||||
{isPutAway ? (
|
||||
<CaretDownOutlined style={{ marginLeft: 4 }} />
|
||||
) : (
|
||||
<CaretUpOutlined style={{ marginLeft: 4 }} />
|
||||
)}
|
||||
</Divider>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
128
src/components/category-list/index.less
Normal file
128
src/components/category-list/index.less
Normal file
@@ -0,0 +1,128 @@
|
||||
.category-box {
|
||||
.first-category-list {
|
||||
display: flex;
|
||||
.first-category-item {
|
||||
flex-shrink: 1;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
margin-right: 18px;
|
||||
padding: 10px 12px;
|
||||
width: 120px;
|
||||
height: 76px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
background-size: 100% 100%;
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
font-weight: 400;
|
||||
transition: all 0.5s;
|
||||
.first-category-item-title {
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 1;
|
||||
overflow: hidden;
|
||||
/* autoprefixer: off */
|
||||
-webkit-box-orient: vertical;
|
||||
display: -webkit-box;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
.first-category-item-count {
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
line-height: 16px;
|
||||
}
|
||||
&:hover {
|
||||
transition: all 0.5s;
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
}
|
||||
.first-category-item-active {
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
.first-category-more {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 88px;
|
||||
height: 76px;
|
||||
font-size: 16px;
|
||||
color: #13b4ff;
|
||||
line-height: 20px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
background-color: rgba(19, 180, 255, 0.08);
|
||||
}
|
||||
}
|
||||
.second-category-list {
|
||||
padding-top: 15px;
|
||||
border-radius: 4px;
|
||||
.second-category-item {
|
||||
display: flex;
|
||||
.second-category-item-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 16px;
|
||||
min-width: 60px;
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
font-weight: bold;
|
||||
}
|
||||
.second-category-item-box {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
.second-category-item-list {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
height: 43px;
|
||||
overflow: hidden;
|
||||
.third-category-item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 2px 8px;
|
||||
margin: 8px 15px 8px 0;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.04);
|
||||
border-radius: 4px;
|
||||
&:hover {
|
||||
@include box-backgroundColor(0.1);
|
||||
@include box-border();
|
||||
@include font-color();
|
||||
}
|
||||
}
|
||||
.third-category-item-active {
|
||||
@include box-backgroundColor(0.1);
|
||||
@include box-border();
|
||||
@include font-color();
|
||||
}
|
||||
}
|
||||
.second-category-item-status {
|
||||
display: flex;
|
||||
margin-top: 8px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4px 4px 4px 8px;
|
||||
width: 54px;
|
||||
height: 28px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-divider-horizontal.ant-divider-with-text-center {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user