diff --git a/.env.development b/.env.development index 204eaff..6a06899 100644 --- a/.env.development +++ b/.env.development @@ -1,2 +1,2 @@ -VITE_API_HOST=http://117.72.10.84:3010 -VITE_IMG_HOST=http://117.72.14.166:9000 \ No newline at end of file +VITE_API_HOST=http://117.72.10.84:5000 +VITE_IMG_HOST=http://117.72.14.166:9000 diff --git a/src/App.less b/src/App.less index cb96a0e..8b051e6 100644 --- a/src/App.less +++ b/src/App.less @@ -1,159 +1,160 @@ @import '@assets/base.less'; .app-main { - display: flex; - flex-direction: row; - border-radius: 4px; + display: flex; + flex-direction: row; + border-radius: 4px; + margin: 0 auto; + position: absolute; + min-width: 1439px; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #f3f4f6; + padding: 66px 16px 32px; + overflow: hidden; + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Chrome/Safari/Opera */ + -khtml-user-select: none; /* Konqueror */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; /* Non-prefixed version, currently not supported by any browser */ + .content-box { + width: 1439px; margin: 0 auto; - position: absolute; - min-width: 1439px; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: #f3f4f6; - padding: 66px 16px 32px; - overflow: hidden; - -webkit-touch-callout: none; /* iOS Safari */ - -webkit-user-select: none; /* Chrome/Safari/Opera */ - -khtml-user-select: none; /* Konqueror */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* Internet Explorer/Edge */ - user-select: none; /* Non-prefixed version, currently not supported by any browser */ - .content-box{ - width: 1439px; - margin: 0 auto; - } + overflow: auto; + } } .header-navigator { - position: absolute; - top: 0; - left: 0; - right: 0; - height: 50px; - min-width: 1439px; - background-color: #fff; - border-radius: 4px; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 50px; + min-width: 1439px; + background-color: #fff; + border-radius: 4px; } .nav-title { - display: flex; - justify-content: space-between; - cursor: pointer; - width: 1407px; - margin: 0 auto; - line-height: 50px; - color: #1890ff; - font-size: 24px; - // font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", 微软雅黑, - // Arial, sans-serif; + display: flex; + justify-content: space-between; + cursor: pointer; + width: 1407px; + margin: 0 auto; + line-height: 50px; + color: #1890ff; + font-size: 24px; + // font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", 微软雅黑, + // Arial, sans-serif; } .header-navigator .user { - width: 36px; - height: 36px; - float: right; - /* background: #1890ff; */ - border-radius: 50%; - color: #fff; - margin-top: 7px; - line-height: 36px; - text-align: center; - font-size: 16px; - overflow: hidden; - box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.08); + width: 36px; + height: 36px; + float: right; + /* background: #1890ff; */ + border-radius: 50%; + color: #fff; + margin-top: 7px; + line-height: 36px; + text-align: center; + font-size: 16px; + overflow: hidden; + box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.08); } .jump-box { - font-size: 14px; - margin-right: 20px; + font-size: 14px; + margin-right: 20px; } .info-time-box { - display: flex; + display: flex; } .time-box { - margin-top: 8px; - margin-right: 120px; + margin-top: 8px; + margin-right: 120px; } .head-navigator-box { - position: absolute; - top: 0; - left: 0; - right: 0; - height: 50px; - background-color: #fff; - border-radius: 4px; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 50px; + background-color: #fff; + border-radius: 4px; } .head-navigator { - display: flex; - align-items: center; - justify-content: space-between; - margin: 0 auto; - width: 1435px; + display: flex; + align-items: center; + justify-content: space-between; + margin: 0 auto; + width: 1435px; } .head-navigator-left { - display: flex; - align-items: center; + display: flex; + align-items: center; } .head-navigator-logo { - margin-right: 20px; - // line-height: 50px; - cursor: pointer; - color: #1890ff; - font-size: 24px; - // font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, 微软雅黑, - // Arial, sans-serif; + margin-right: 20px; + // line-height: 50px; + cursor: pointer; + color: #1890ff; + font-size: 24px; + // font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, 微软雅黑, + // Arial, sans-serif; } .head-navigator-select-box { - display: flex; - justify-content: space-between; - align-items: center; - width: 500px; + display: flex; + justify-content: space-between; + align-items: center; + width: 500px; } .head-navigator-menu-box { - display: flex; + display: flex; } .head-navigator-menu-box .ant-menu-horizontal { - border-bottom: 0; + border-bottom: 0; } .ant-menu-horizontal > .ant-menu-item, .ant-menu-horizontal > .ant-menu-submenu { - padding: 0px; - margin: 0 12px; + padding: 0px; + margin: 0 12px; } .head-navigator-input-box { - margin-right: 24px; + margin-right: 24px; } .head-navigator-input-box .ant-input { - border-radius: 16px; + border-radius: 16px; } .head-navigator-user-box { - display: flex; - justify-content: center; - align-items: center; + display: flex; + justify-content: center; + align-items: center; } .head-navigator-bell { - display: flex; - justify-content: center; - align-items: center; - margin-right: 24px; - width: 28px; - height: 28px; - border-radius: 50%; - cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + margin-right: 24px; + width: 28px; + height: 28px; + border-radius: 50%; + cursor: pointer; } .head-navigator-bell:hover { - background-color: rgba(0, 10, 32, 0.03); + background-color: rgba(0, 10, 32, 0.03); } .head-navigator-user-img { - width: 36px; - height: 36px; - color: #fff; - cursor: pointer; - border-radius: 50%; - box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.08); + width: 36px; + height: 36px; + color: #fff; + cursor: pointer; + border-radius: 50%; + box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.08); } diff --git a/src/components/category-list/index.jsx b/src/components/category-list/index.jsx index 02a68ac..efec0c5 100644 --- a/src/components/category-list/index.jsx +++ b/src/components/category-list/index.jsx @@ -1,373 +1,353 @@ -import React, { Fragment, useState, useEffect } from 'react'; +import React, { Fragment, useEffect, useState } from 'react' import { - RightOutlined, - UpOutlined, - DownOutlined, - CaretDownOutlined, - CaretUpOutlined, -} from '@ant-design/icons'; -import req from '@utils/request'; -import { Divider, Spin, Modal } from 'antd'; -import _ from 'lodash'; -import './index.less'; -import { apiName } from './constant'; -import { imgObject } from '@constants' + CaretDownOutlined, + CaretUpOutlined, + DownOutlined, + RightOutlined, + UpOutlined +} from '@ant-design/icons' +import req from '@utils/request' +import { Divider, Modal, Spin } from 'antd' +import _ from 'lodash' +import { apiName } from './constant' +import './index.less' /** - * 大分类中的背景图 + * 大分类中的背景图颜色 */ -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 categoryBackColor = { + 0: '#23b2ff', + 1: '#ea7d4d', + 2: '#e93532', + 3: '#343d71', + 4: '#dc4ad6', + 5: '#72b633', + 6: '#9047de', + 7: '#dc4077' +} -const categoryShowCount = 4; +const categoryShowCount = 4 const cacheMap = {} /** - * 上万后展示 W+ - * @param {*} total - * @returns - */ -const formatTotal = (total) => { - if (total >= 10000) { - return Math.floor(total / 10000) + 'W+'; - } - return total; + * 上万后展示 W+ + * @param {*} total + * @returns + */ +const formatTotal = total => { + if (total >= 10000) { + return Math.floor(total / 10000) + 'W+' + } + return total } const CategoryList = ({ primaryCategoryId, categoryList, ...props }) => { - const [secondCategoryList, setSecondCategoryList] = useState([]) - const [currentActive, setCurrentActive] = useState(categoryList[0]) - const [isPutAway, setIsPutAway] = useState(true) - const [loading, setLoading] = useState(false) - const [currentLabelIndex, setCurrentLabelIndex] = useState([]) - const [openMoreFlag, setOpenMoreFlag] = useState(false) + const [secondCategoryList, setSecondCategoryList] = useState([]) + const [currentActive, setCurrentActive] = useState(categoryList[0]) + const [isPutAway, setIsPutAway] = useState(true) + const [loading, setLoading] = useState(false) + const [currentLabelIndex, setCurrentLabelIndex] = useState([]) + const [openMoreFlag, setOpenMoreFlag] = useState(false) - - const getLabels = (id) => { - return new Promise(resolve => { - req({ - method: 'post', - url: apiName.queryLabelByCategoryId, - data: { categoryId: id } - }).then(res => { - if (cacheMap[id]) { - resolve(cacheMap[id]) - } else { - cacheMap[id] = res.data - resolve(res.data) - } - }) - }) - } - - // 获取大类下分类 - const getCategoryByPrimary = () => { - req({ - method: 'post', - url: apiName.queryCategoryByPrimary, - data: { categoryType: 2, parentId: currentActive.id } - }).then(async res => { - let list = res.data - for (let i = 0; i < list.length; i++) { - list[i].children = await getLabels(list[i].id) - if (i === 0 && list[i].children.length) { - list[i].children[0].active = true - } - } - setSecondCategoryList(_.cloneDeep(list)) - setCurrentLabelIndex([0, 0]) - props.onChangeLabel(_.get(list, [0, 'id']), _.get(list, [0, 'children', 0, 'id'])) - }) - } - - useEffect(() => { - if (primaryCategoryId) { - getCategoryByPrimary() - } - }, [primaryCategoryId]) - - - - /** - * 切换一级分类 - * @param {*} item - * @returns - */ - const onChangeCategory = (item) => () => { - if (currentActive.id === item.id) { - return; - } - setCurrentActive(item) - props.onChangeCategory(item); - }; - - /** - * 一级分类模块 - * @returns - */ - const renderFirstContainer = () => { - return ( -
- {categoryList.slice(0, 7).map((categoryModuleItem, categoryModuleIndex) => { - return ( -
-
- {categoryModuleItem.categoryName} -
-
- {categoryModuleItem.count || 50}道题 -
-
- ); - })} - {categoryList.length > 7 && ( -
setOpenMoreFlag(true)}> - 更多 - -
- )} -
- ); - }; - - - /** - * 点击更多的分类项 - * @param {*} id - * @returns - */ - const onChangeCategoryMore = (cur) => () => { - setOpenMoreFlag(false) - setCurrentActive(cur) - let list = [...categoryList].reduce((pre, item) => { - if (item.id !== cur.id) { - pre.push(item); - } else { - pre.unshift(item); - } - return pre; - }, []); - props.onChangeCategoryMore && props.onChangeCategoryMore(cur.id, list); - }; - - /** - * 更多分类 - * @returns - */ - const renderMoreBox = () => { - return ( -
- {categoryList.slice(7).map((categoryModuleItem, categoryModuleIndex) => { - return ( -
-
- {categoryModuleItem.categoryName} -
-
- {formatTotal(categoryModuleItem.subjectCount)}道题 -
-
- ); - })} -
- ); - }; - - /** - * 选择标签-支持单选(多选) - * @param {*} categoryId 一级分类id - * @param {*} secondCategoryIndex 二级分类对象index - * @param {*} thirdCategoryIndex 三级标签index - * @param {*} active 三级标签当前的选中状态 - * @returns - */ - const onChangeLabel = (secondCategoryIndex, thirdCategoryIndex, active) => () => { - const { isMultipleChoice } = props; - const list = _.cloneDeep(secondCategoryList) - if (isMultipleChoice) { - // 三级标签支持多选 - _.set(list, [secondCategoryIndex, 'children', thirdCategoryIndex, 'active'], !active) - setSecondCategoryList(list) + const getLabels = id => { + return new Promise(resolve => { + req({ + method: 'post', + url: apiName.queryLabelByCategoryId, + data: { categoryId: id } + }).then(res => { + if (cacheMap[id]) { + resolve(cacheMap[id]) } else { - // 三级标签支持单选 - if (currentLabelIndex.length) { - _.set(list, [currentLabelIndex[0], 'children', currentLabelIndex[1], 'active'], false) - } - _.set(list, [secondCategoryIndex, 'children', thirdCategoryIndex, 'active'], !active) - setCurrentLabelIndex([secondCategoryIndex, thirdCategoryIndex]) - setSecondCategoryList(list) + cacheMap[id] = res.data + resolve(res.data) } - props.onChangeLabel(_.get(list, [secondCategoryIndex, 'id']), _.get(list, [secondCategoryIndex, 'children', thirdCategoryIndex, 'id'])) - }; + }) + }) + } - /** - * 展开/收起 - * @param {*} secondCategoryIndex - * @returns - */ - const onChangeOpenStatus = (secondCategoryIndex, isOpen) => () => { - const _list = _.cloneDeep(secondCategoryList) - _.set(_list, [secondCategoryIndex, 'isOpen'], !isOpen); - setSecondCategoryList(_list) - }; + // 获取大类下分类 + const getCategoryByPrimary = () => { + req({ + method: 'post', + url: apiName.queryCategoryByPrimary, + data: { categoryType: 2, parentId: currentActive.id } + }).then(async res => { + let list = res.data + for (let i = 0; i < list.length; i++) { + list[i].children = await getLabels(list[i].id) + if (i === 0 && list[i].children.length) { + list[i].children[0].active = true + } + } + setSecondCategoryList(_.cloneDeep(list)) + setCurrentLabelIndex([0, 0]) + props.onChangeLabel(_.get(list, [0, 'id']), _.get(list, [0, 'children', 0, 'id'])) + }) + } - /** - * 展开/收起 - */ - const onChangePutAway = () => { - setIsPutAway(!isPutAway) - }; + useEffect(() => { + if (primaryCategoryId) { + getCategoryByPrimary() + } + }, [primaryCategoryId]) - /** - * 二级分类模块 - * @returns - */ - const renderSecondContainer = () => { - return ( - -
- {secondCategoryList.map((secondCategoryItem, secondCategoryIndex) => { - return ( -
= categoryShowCount && isPutAway - ? 'none' - : 'flex', - }} - className="second-category-item" - key={`second_category_${secondCategoryItem.id}`}> -
- {secondCategoryItem.categoryName}: -
- {secondCategoryItem?.children?.length > 0 && ( -
-
- {secondCategoryItem.children.map( - (thirdCategoryItem, thirdCategoryIndex) => { - return ( -
- {thirdCategoryItem.labelName} -
- ); - } - )} -
- { - secondCategoryItem.children.length > 5 ?
-
- {secondCategoryItem.isOpen ? '收起' : '展开'} -
-
- {secondCategoryItem.isOpen ? ( - - ) : ( - - )} -
-
: null - } - -
- )} -
- ); - })} - {secondCategoryList?.length >= categoryShowCount && ( - - {isPutAway ? '展开' : '收起'} - {isPutAway ? ( - - ) : ( - - )} - - )} -
-
- - ); - }; + /** + * 切换一级分类 + * @param {*} item + * @returns + */ + const onChangeCategory = item => () => { + if (currentActive.id === item.id) { + return + } + setCurrentActive(item) + props.onChangeCategory(item) + } + /** + * 一级分类模块 + * @returns + */ + const renderFirstContainer = () => { return ( -
- {categoryList?.length && renderFirstContainer()} - - {secondCategoryList?.length > 0 && renderSecondContainer()} - - {/* {!this.props.isHideSec && ( +
+ {categoryList.slice(0, 7).map((categoryModuleItem, categoryModuleIndex) => { + return ( +
+
{categoryModuleItem.categoryName}
+
{categoryModuleItem.count || 50}道题
+
+ ) + })} + {categoryList.length > 7 && ( +
setOpenMoreFlag(true)}> + 更多 + +
+ )} +
+ ) + } + + /** + * 点击更多的分类项 + * @param {*} id + * @returns + */ + const onChangeCategoryMore = cur => () => { + setOpenMoreFlag(false) + setCurrentActive(cur) + let list = [...categoryList].reduce((pre, item) => { + if (item.id !== cur.id) { + pre.push(item) + } else { + pre.unshift(item) + } + return pre + }, []) + props.onChangeCategoryMore && props.onChangeCategoryMore(cur.id, list) + } + + /** + * 更多分类 + * @returns + */ + const renderMoreBox = () => { + return ( +
+ {categoryList.slice(7).map((categoryModuleItem, categoryModuleIndex) => { + return ( +
+
{categoryModuleItem.categoryName}
+
+ {formatTotal(categoryModuleItem.subjectCount)}道题 +
+
+ ) + })} +
+ ) + } + + /** + * 选择标签-支持单选(多选) + * @param {*} categoryId 一级分类id + * @param {*} secondCategoryIndex 二级分类对象index + * @param {*} thirdCategoryIndex 三级标签index + * @param {*} active 三级标签当前的选中状态 + * @returns + */ + const onChangeLabel = (secondCategoryIndex, thirdCategoryIndex, active) => () => { + const { isMultipleChoice } = props + const list = _.cloneDeep(secondCategoryList) + if (isMultipleChoice) { + // 三级标签支持多选 + _.set(list, [secondCategoryIndex, 'children', thirdCategoryIndex, 'active'], !active) + setSecondCategoryList(list) + } else { + // 三级标签支持单选 + if (currentLabelIndex.length) { + _.set(list, [currentLabelIndex[0], 'children', currentLabelIndex[1], 'active'], false) + } + _.set(list, [secondCategoryIndex, 'children', thirdCategoryIndex, 'active'], !active) + setCurrentLabelIndex([secondCategoryIndex, thirdCategoryIndex]) + setSecondCategoryList(list) + } + props.onChangeLabel( + _.get(list, [secondCategoryIndex, 'id']), + _.get(list, [secondCategoryIndex, 'children', thirdCategoryIndex, 'id']) + ) + } + + /** + * 展开/收起 + * @param {*} secondCategoryIndex + * @returns + */ + const onChangeOpenStatus = (secondCategoryIndex, isOpen) => () => { + const _list = _.cloneDeep(secondCategoryList) + _.set(_list, [secondCategoryIndex, 'isOpen'], !isOpen) + setSecondCategoryList(_list) + } + + /** + * 展开/收起 + */ + const onChangePutAway = () => { + setIsPutAway(!isPutAway) + } + + /** + * 二级分类模块 + * @returns + */ + const renderSecondContainer = () => { + return ( + +
+ {secondCategoryList.map((secondCategoryItem, secondCategoryIndex) => { + return ( +
= categoryShowCount && isPutAway ? 'none' : 'flex' + }} + className='second-category-item' + key={`second_category_${secondCategoryItem.id}`} + > +
+ {secondCategoryItem.categoryName}: +
+ {secondCategoryItem?.children?.length > 0 && ( +
+
+ {secondCategoryItem.children.map((thirdCategoryItem, thirdCategoryIndex) => { + return ( +
+ {thirdCategoryItem.labelName} +
+ ) + })} +
+ {secondCategoryItem.children.length > 5 ? ( +
+
+ {secondCategoryItem.isOpen ? '收起' : '展开'} +
+
+ {secondCategoryItem.isOpen ? : } +
+
+ ) : null} +
+ )} +
+ ) + })} + {secondCategoryList?.length >= categoryShowCount && ( + + {isPutAway ? '展开' : '收起'} + {isPutAway ? ( + + ) : ( + + )} + + )} +
+
+ ) + } + + return ( +
+ {categoryList?.length && renderFirstContainer()} + {secondCategoryList?.length > 0 && renderSecondContainer()} + {/* {!this.props.isHideSec && ( {secondCategoryList?.length > 0 && this.renderSecondContainer()} )} */} - setOpenMoreFlag(false)}> - {renderMoreBox()} - -
- ) + setOpenMoreFlag(false)} + > + {renderMoreBox()} + +
+ ) } export default CategoryList - - diff --git a/src/constants/index.ts b/src/constants/index.ts index 8371d10..2f1b4de 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -3,86 +3,60 @@ const host = import.meta.env.VITE_IMG_HOST * 难度筛选 */ export const filterDifficulty = [ - { - id: 0, - title: '全部', - }, - { - id: 1, - title: '初级', - }, - { - id: 2, - title: '中级', - }, - { - id: 3, - title: '高级', - }, - { - id: 4, - title: '资深', - }, - { - id: 5, - title: '专家', - }, -]; + { + id: 0, + title: '全部' + }, + { + id: 1, + title: '初级' + }, + { + id: 2, + title: '中级' + }, + { + id: 3, + title: '高级' + }, + { + id: 4, + title: '资深' + }, + { + id: 5, + title: '专家' + } +] /** * 难度等级 */ export const gradeObject = { - 1: { - color: 'rgba(60, 110, 238, 0.5)', - title: '初级', - }, - 2: { - color: 'rgba(60, 110, 238, 0.6)', - title: '中级', - }, - 3: { - color: 'rgba(60, 110, 238, 0.7)', - title: '高级', - }, - 4: { - color: 'rgba(60, 110, 238, 0.8)', - title: '资深', - }, - 5: { - color: 'rgba(60, 110, 238, 0.9)', - title: '专家', - }, -}; + 1: { + color: 'rgba(60, 110, 238, 0.5)', + title: '初级' + }, + 2: { + color: 'rgba(60, 110, 238, 0.6)', + title: '中级' + }, + 3: { + color: 'rgba(60, 110, 238, 0.7)', + title: '高级' + }, + 4: { + color: 'rgba(60, 110, 238, 0.8)', + title: '资深' + }, + 5: { + color: 'rgba(60, 110, 238, 0.9)', + title: '专家' + } +} export const imgObject = { - clickImg: - host + '/jichi/icon/%E7%83%AD%E9%97%A8.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=minioadmin%2F20231102%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20231102T153146Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=e6b8cdb3231b1c3d7114212cb9278ecc17cf6d4ec0f759ea0200e04156d4c8b7', - ranking1Img: - 'https://img12.360buyimg.com/imagetools/jfs/t1/110906/3/22471/3750/6214a3bfE392596cf/122c9e4b30948682.png', - ranking2Img: - 'https://img13.360buyimg.com/imagetools/jfs/t1/211695/8/12987/4360/6214a3bfEd4679fde/4f3c55783bb9119c.png', - ranking3Img: - 'https://img10.360buyimg.com/imagetools/jfs/t1/175261/19/28428/4566/6214a3bfE476e1b0f/ea59084c55001c06.png', - rankingImg: - 'https://img11.360buyimg.com/imagetools/jfs/t1/167264/35/27633/603/6214a3bfEf8feff1d/8d833235e6bc468d.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', -}; \ No newline at end of file + clickImg: + host + + '/jichi/icon/%E7%83%AD%E9%97%A8.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=minioadmin%2F20231102%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20231102T153146Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=e6b8cdb3231b1c3d7114212cb9278ecc17cf6d4ec0f759ea0200e04156d4c8b7' +} diff --git a/src/imgs/ranking1Img.png b/src/imgs/ranking1Img.png new file mode 100644 index 0000000..0ec2a1d Binary files /dev/null and b/src/imgs/ranking1Img.png differ diff --git a/src/imgs/ranking2Img.png b/src/imgs/ranking2Img.png new file mode 100644 index 0000000..367aae0 Binary files /dev/null and b/src/imgs/ranking2Img.png differ diff --git a/src/imgs/ranking3Img.png b/src/imgs/ranking3Img.png new file mode 100644 index 0000000..aaeb2f1 Binary files /dev/null and b/src/imgs/ranking3Img.png differ diff --git a/src/imgs/rankingImg.png b/src/imgs/rankingImg.png new file mode 100644 index 0000000..bfd4c2d Binary files /dev/null and b/src/imgs/rankingImg.png differ diff --git a/src/utils/request.ts b/src/utils/request.ts index f689f5f..ea1e690 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -1,66 +1,71 @@ -import axios from 'axios'; -import { message, Modal } from 'antd'; -import { useLocation, useNavigate } from 'react-router-dom'; +import { message } from 'antd' +import axios from 'axios' export const baseHttp = () => { - const http = axios.create({ - baseURL: "/subject", - timeout: 5 * 60 * 1000, // request timeout - withCredentials: true, // send cookies when cross-domain requests - headers: { - 'Content-Type': 'application/json; charset=utf-8', - }, - }); + const http = axios.create({ + baseURL: '/subject', + timeout: 5 * 60 * 1000, // request timeout + withCredentials: true, // send cookies when cross-domain requests + headers: { + 'Content-Type': 'application/json; charset=utf-8' + } + }) - return http; -}; + return http +} export default function request(config, url) { - // const navigate = useNavigate() + console.log(config, url, 'config url') + // const navigate = useNavigate() + const userInfoStorage = localStorage.getItem('userInfo') + const userInfo = userInfoStorage ? JSON.parse(userInfoStorage) : {} + const baseURL = url || '/subject/subject' + // 1.创建axios的实例 + const instance = axios.create({ + baseURL, + timeout: 5 * 60 * 1000, // request timeout + withCredentials: true, // send cookies when cross-domain requests + headers: { + 'Content-Type': 'application/json; charset=utf-8', + [userInfo.tokenName]: 'jichi ' + userInfo.tokenValue + } + }) - const baseURL = url || '/subject'; - // 1.创建axios的实例 - const instance = axios.create({ - baseURL, - timeout: 5 * 60 * 1000, // request timeout - withCredentials: true, // send cookies when cross-domain requests - headers: { - 'Content-Type': 'application/json; charset=utf-8', - }, - }); + // 2.axios的拦截器 + // 2.1.请求拦截的作用 + instance.interceptors.request.use( + config => { + return config + }, + err => { + console.log(err) + } + ) - // 2.axios的拦截器 - // 2.1.请求拦截的作用 - instance.interceptors.request.use( - (config) => { - return config; - }, - (err) => { - console.log(err); - } - ); + // 2.2.响应拦截 + instance.interceptors.response.use( + res => { + let { code } = res.data + if (code === 500) { + message.error(res.data.message) + } + if (code === 401) { + window.location.replace('/login') + } + return res.data + }, + err => { + let { status } = err?.response ?? {} + if (status === 401 || !status) { + message.info('页面异常') + window.location.replace('/login') + } else if (status === 500 || status === 503) { + message.error('服务器错误') + } + return Promise.reject(err) + } + ) - // 2.2.响应拦截 - instance.interceptors.response.use( - (res) => { - let { code } = res.data; - if (code === 500) { - message.error(res.data.message); - } - return res.data; - }, - (err) => { - let { status } = err?.response ?? {}; - if (status === 401 || !status) { - message.info('页面异常') - window.location.replace('/login') - } else if (status === 500 || status === 503) { - message.error('服务器错误'); - } - return Promise.reject(err); - } - ); - - // 3.发送真正的网络请求 - return instance(config); + // 3.发送真正的网络请求 + return instance(config) } diff --git a/src/views/login/index.less b/src/views/login/index.less index 4423abd..7be6d1f 100644 --- a/src/views/login/index.less +++ b/src/views/login/index.less @@ -1,35 +1,39 @@ -.login-box{ +.login-box { + width: 100%; + height: calc(100% - 100px); + background: url(../../imgs/login_bg.jpg) no-repeat 50%; + background-size: cover; + min-height: 600px; + position: relative; + .login-container-inner { + background: transparent; + position: absolute; + top: 50%; + left: 50%; + height: 534px; + transform: translate(-50%, -50%); + clear: both; + max-width: 1520px; + display: flex; + justify-content: space-between; + align-items: center; width: 100%; - height: calc(100% - 100px); - background: url(../../imgs/login_bg.jpg) no-repeat 50%; - background-size: cover; - min-height: 600px; - position: relative; - .login-container-inner{ - background: transparent; - position: absolute; - top: 50%; - left: 50%; - height: 534px; - transform: translate(-50%,-50%); - clear: both; - max-width: 1520px; - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; - .notes{ - color: white; - max-width: 400px; - line-height: 30px; - } - .qrcode-box{ - width: 400px; - height: 500px; - background: white; - border-radius: 10px; - text-align: center; - padding: 20px; - } + .notes { + color: white; + max-width: 400px; + line-height: 30px; } -} \ No newline at end of file + .qrcode-box { + width: 400px; + height: 500px; + background: white; + border-radius: 10px; + text-align: center; + padding: 20px; + .qrcode-desc { + padding: 20px 0; + line-height: 30px; + } + } + } +} diff --git a/src/views/login/index.tsx b/src/views/login/index.tsx index e4a398f..fed0a22 100644 --- a/src/views/login/index.tsx +++ b/src/views/login/index.tsx @@ -1,45 +1,70 @@ -import { useState } from 'react' -import { Input, Button, Space } from 'antd' import LoginQrcode from '@imgs/login_qrcode.jpg' -import req from '@utils/request'; +import req from '@utils/request' +import { Button, Input, Space, message } from 'antd' +import { useState } from 'react' +import { useNavigate } from 'react-router-dom' import './index.less' const loginApiName = '/user/doLogin' const Login = () => { + const [validCode, setValidCode] = useState('') + const navigate = useNavigate() - const [validCode, setValidCode] = useState('') - const changeCode = e => { - setValidCode(e.target.value) - } + const changeCode = e => { + setValidCode(e.target.value) + } - const doLogin = () => { - if (!validCode) return - req({ - method: 'get', - url: loginApiName, - data: { validCode } - }) - } + const doLogin = () => { + console.log(validCode) + if (!validCode) return + req( + { + method: 'get', + url: loginApiName, + params: { validCode } + }, + '/auth' + ).then(res => { + if (res.success && res.data) { + message.success('登录成功') + localStorage.setItem('userInfo', JSON.stringify(res.data)) + setTimeout(() => { + navigate('/question-bank') + }, 1000) + } + }) + } - return (
-
-
LeNet-5 诞生 - 1998 年 11 月,早期经典卷积神经网络 LeNet-5 诞生。杨立昆、莱昂·伯托等发表经典论文“Gradient-Based Learning Applied to Document Recognition”,文章总结了应用于手写字符识别的各种模型并进行了比对,结果显示卷积神经网络表现超群。
-
-
- -
-
- - - - -
-
+ return ( +
+
+
+ LeNet-5 诞生 1998 年 11 月,早期经典卷积神经网络 LeNet-5 + 诞生。杨立昆、莱昂·伯托等发表经典论文“Gradient-Based Learning Applied to Document + Recognition”,文章总结了应用于手写字符识别的各种模型并进行了比对,结果显示卷积神经网络表现超群。
-
) +
+
+

微信扫码关注公众号

+

获取验证码登录

+
+
+ +
+
+ + + + +
+
+
+
+ ) } -export default Login \ No newline at end of file +export default Login diff --git a/src/views/question-bank/components/ranking-box/index.jsx b/src/views/question-bank/components/ranking-box/index.jsx index 1094e54..a255149 100644 --- a/src/views/question-bank/components/ranking-box/index.jsx +++ b/src/views/question-bank/components/ranking-box/index.jsx @@ -1,20 +1,23 @@ -import React from 'react' -import { Popover, Spin } from 'antd' +import ClickImg from '@/imgs/clickImg.png' +import Ranking1Img from '@/imgs/ranking1Img.png' +import Ranking2Img from '@/imgs/ranking2Img.png' +import Ranking3Img from '@/imgs/ranking3Img.png' +import RankingImg from '@/imgs/rankingImg.png' import { debounce } from '@utils' -import { RankingTypeText, RankingTypeBtnText } from '../../constant' +import { Popover, Spin, message } from 'antd' +import React from 'react' +import { RankingTypeBtnText, RankingTypeText } from '../../constant' import './index.less' -import { message } from 'antd' -import { imgObject } from '@constants' const rankingBackImg = { - 0: imgObject.ranking1Img, - 1: imgObject.ranking2Img, - 2: imgObject.ranking3Img, + 0: Ranking1Img, + 1: Ranking2Img, + 2: Ranking3Img } export default function RankingBox(props) { const { isLoading = false, currentActive, rankingType, contributionList } = props - const onChangeRanking = (index) => + const onChangeRanking = index => debounce(() => { props.onHandleRanking && props.onHandleRanking(index) }) @@ -36,17 +39,19 @@ export default function RankingBox(props) { let rankingList = contributionList || [] return ( -
-
-
{RankingTypeText[rankingType]}
-
+
+
+
{RankingTypeText[rankingType]}
+
{tabList.length > 0 && tabList.map((item, index) => { return (
{item.tab}
@@ -55,56 +60,52 @@ export default function RankingBox(props) {
-
+
{rankingList?.length > 0 && rankingList.map((item, index) => { return ( -
-
+
+
{index > 2 && index + 1}
-
- +
+
- {item.name} -
- } + title={
{item.name}
} content={ -
+
{item.name}
{/*
{item.organizationFullName}
*/}
} > -
-
{item.name}
+
+
{item.name}
{/*
{item.organizationName}
*/}
-
🔥 {item.count}
+
🔥 {item.count}
) })}
-
+
-
{RankingTypeBtnText[rankingType]}
+
{RankingTypeBtnText[rankingType]}
) diff --git a/src/views/user-info/index.tsx b/src/views/user-info/index.tsx index 92105c1..93473ce 100644 --- a/src/views/user-info/index.tsx +++ b/src/views/user-info/index.tsx @@ -1,10 +1,14 @@ import Head from '@/imgs/head.jpg' import { LoadingOutlined, PlusOutlined } from '@ant-design/icons' -import { Button, Card, Col, Form, Input, Radio, Row } from 'antd' +import req from '@utils/request' +import { Button, Card, Col, Form, Input, Radio, Row, message } from 'antd' import { memo, useState } from 'react' +import { useNavigate } from 'react-router-dom' + import './index.less' const { TextArea } = Input +const apiName = '/user/update' const layout = { labelCol: { span: 4 }, @@ -15,9 +19,43 @@ const UserInfo = () => { const [form] = Form.useForm() const [editFlag, setEditFlag] = useState(false) const [loading, setLoading] = useState(false) + const navigate = useNavigate() - const onFinish = values => { - console.log(values) + const onFinish = () => { + setLoading(true) + const userInfoStorage = localStorage.getItem('userInfo') + const { loginId = '' } = userInfoStorage ? JSON.parse(userInfoStorage) : {} + const values = form.getFieldsValue() + if (!Object.values(values).filter(Boolean).length) { + setLoading(false) + return + } + const params = { + userName: loginId, + ...values + } + req( + { + method: 'post', + url: apiName, + data: { ...params } + }, + '/auth' + ) + .then(res => { + if (res.success) { + message.success('更新成功') + setTimeout(() => { + navigate('/question-bank') + }, 1000) + } + setLoading(false) + setEditFlag(false) + }) + .catch(() => { + message.error('更新失败') + setLoading(false) + }) } const uploadButton = ( @@ -50,10 +88,7 @@ const UserInfo = () => { {editFlag ? ( - + @@ -65,6 +100,17 @@ const UserInfo = () => { )} + + {editFlag ? ( + + + + ) : ( + + <>暂无 + + )} + {editFlag ? ( { {editFlag ? ( <> - diff --git a/vite.config.ts b/vite.config.ts index 4d474fd..1501217 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,10 +1,10 @@ -import { defineConfig, loadEnv } from 'vite' import react from '@vitejs/plugin-react' +import { defineConfig, loadEnv } from 'vite' const path = require('path') // https://vitejs.dev/config/ export default ({ mode }) => { - const env = loadEnv(mode, process.cwd()); + const env = loadEnv(mode, process.cwd()) return defineConfig({ resolve: { alias: { @@ -14,17 +14,21 @@ export default ({ mode }) => { '@utils': path.resolve(__dirname, 'src/utils'), '@components': path.resolve(__dirname, 'src/components'), '@imgs': path.resolve(__dirname, 'src/imgs'), - '@constants': path.resolve(__dirname, 'src/constants'), + '@constants': path.resolve(__dirname, 'src/constants') } }, plugins: [react()], server: { proxy: { - "/subject": { + '/subject': { target: env.VITE_API_HOST, - changeOrigin: true, + changeOrigin: true }, - }, + '/auth': { + target: env.VITE_API_HOST, + changeOrigin: true + } + } } }) }