feat: 增加搜索页
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
VITE_API_HOST=http://117.72.14.166:3010
|
||||
VITE_API_HOST=http://117.72.10.84:5000
|
||||
# VITE_API_HOST=http://117.72.14.166:3010
|
||||
VITE_IMG_HOST=http://117.72.14.166:9000
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
|
||||
<link rel="icon" href="/src/favicon.png" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
|
BIN
src/favicon.png
Normal file
BIN
src/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 60 KiB |
@@ -30,6 +30,14 @@ const router = createBrowserRouter([
|
||||
path: 'user-info',
|
||||
// element: <UserInfo />
|
||||
Component: lazy(() => import('@views/user-info'))
|
||||
},
|
||||
{
|
||||
path: 'search-detail',
|
||||
Component: lazy(() => import('@views/search-details'))
|
||||
},
|
||||
{
|
||||
path: 'search-question',
|
||||
Component: lazy(() => import('@views/search-question'))
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -34,7 +34,7 @@ export default function request(config, url) {
|
||||
res => {
|
||||
let { code } = res.data
|
||||
if (code === 500) {
|
||||
message.error(res.data.message)
|
||||
// message.error(res.data.message)
|
||||
}
|
||||
if (code === 401) {
|
||||
window.location.replace('/login')
|
||||
|
@@ -83,14 +83,15 @@ const Header = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleJump = value => {
|
||||
navigate('/search-detail?t=' + value)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='head-navigator-box'>
|
||||
<div className='head-navigator'>
|
||||
<div className='head-navigator-left'>
|
||||
<div
|
||||
className='head-navigator-logo'
|
||||
onClick={() => (window.location.href = '/question-bank')}
|
||||
>
|
||||
<div className='head-navigator-logo'>
|
||||
<img src={Logo} style={{ height: 50 }} />
|
||||
</div>
|
||||
<TopMenu />
|
||||
@@ -101,7 +102,11 @@ const Header = () => {
|
||||
<div className='head-navigator-input-box'>
|
||||
<Search
|
||||
placeholder='请输入感兴趣的内容~'
|
||||
onSearch={value => console.log(value)}
|
||||
onSearch={value => {
|
||||
if (value) {
|
||||
handleJump(value)
|
||||
}
|
||||
}}
|
||||
style={{ width: 300, borderRadius: '10px' }}
|
||||
/>
|
||||
</div>
|
||||
|
@@ -1,86 +1,96 @@
|
||||
import React, { Fragment, Component } from 'react';
|
||||
// import { withRouter } from 'react-router-dom';
|
||||
import req from '@utils/request';
|
||||
import RankingBox from '../ranking-box';
|
||||
import { apiName, RankingType } from '../../constant';
|
||||
import { mockRankingModuleList } from '../../mock';
|
||||
import req from '@utils/request'
|
||||
import { message } from 'antd'
|
||||
import React, { Fragment, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { RankingType, apiName } from '../../constant'
|
||||
import { mockRankingModuleList } from '../../mock'
|
||||
import RankingBox from '../ranking-box'
|
||||
|
||||
class ContributionList extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
contributionList: mockRankingModuleList[1].rankingList || [],
|
||||
contributeType: 0,
|
||||
isLoading: false,
|
||||
};
|
||||
const ContributionList = props => {
|
||||
const [contributionList, setContributionList] = useState(mockRankingModuleList[1].rankingList)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [contributeType, setContributeType] = useState(0)
|
||||
const navigate = useNavigate()
|
||||
|
||||
/**
|
||||
* 获得贡献榜
|
||||
*/
|
||||
const getContributeList = () => {
|
||||
req({
|
||||
method: 'post',
|
||||
data: {},
|
||||
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 {*} index
|
||||
* @returns
|
||||
*/
|
||||
const onChangeRanking = index => {
|
||||
setContributeType(index)
|
||||
}
|
||||
|
||||
/**
|
||||
* 去录题
|
||||
*/
|
||||
const onChangeJump = () => {
|
||||
const userInfoStorage = localStorage.getItem('userInfo')
|
||||
if (!userInfoStorage) {
|
||||
return message.info('请登录')
|
||||
}
|
||||
const { loginId } = JSON.parse(userInfoStorage)
|
||||
req(
|
||||
{
|
||||
method: 'get',
|
||||
url: '/permission/getPermission',
|
||||
params: {
|
||||
userName: loginId
|
||||
}
|
||||
},
|
||||
'/auth'
|
||||
).then(res => {
|
||||
if (res.success && res.data) {
|
||||
if (res.data.includes('subject:add')) {
|
||||
navigate('/upload-questions')
|
||||
} else {
|
||||
message.info('敬请期待')
|
||||
}
|
||||
} else {
|
||||
message.info('敬请期待')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// this.getContributeList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得贡献榜
|
||||
*/
|
||||
getContributeList() {
|
||||
req({
|
||||
method: 'post',
|
||||
data: {},
|
||||
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 {*} index
|
||||
* @returns
|
||||
*/
|
||||
onChangeRanking = (index) => {
|
||||
console.log(index, 'contribute index')
|
||||
|
||||
this.setState({
|
||||
contributeType: index,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 去录题
|
||||
*/
|
||||
onChangeJump = () => {
|
||||
this.props.history.push('/upload-questions');
|
||||
};
|
||||
|
||||
render() {
|
||||
const { contributionList, isLoading, contributeType } = this.state;
|
||||
return (
|
||||
<Fragment>
|
||||
{contributionList?.length > 0 && (
|
||||
<RankingBox
|
||||
isLoading={isLoading}
|
||||
contributionList={contributionList}
|
||||
currentActive={contributeType}
|
||||
rankingType={RankingType.contribution}
|
||||
onHandleRanking={this.onChangeRanking}
|
||||
onHandleJump={this.onChangeJump}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Fragment>
|
||||
{contributionList?.length > 0 && (
|
||||
<RankingBox
|
||||
isLoading={loading}
|
||||
contributionList={contributionList}
|
||||
currentActive={contributeType}
|
||||
rankingType={RankingType.contribution}
|
||||
onHandleRanking={onChangeRanking}
|
||||
onHandleJump={onChangeJump}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export default ContributionList;
|
||||
export default ContributionList
|
||||
|
@@ -22,8 +22,12 @@ export default function RankingBox(props) {
|
||||
props.onHandleRanking && props.onHandleRanking(index)
|
||||
})
|
||||
const onJump = debounce(() => {
|
||||
if (props.onHandleJump) {
|
||||
props.onHandleJump()
|
||||
} else {
|
||||
message.info('敬请期待')
|
||||
}
|
||||
// props.onHandleJump && props.onHandleJump()
|
||||
message.info('敬请期待')
|
||||
})
|
||||
const tabList = [
|
||||
{
|
||||
|
@@ -14,7 +14,6 @@ const QuestionBank = () => {
|
||||
const [difficulty, setDiffculty] = useState('') //困难度(全部)
|
||||
const [total, setTotal] = useState(0) // 总条数
|
||||
const [pageIndex, setPageIndex] = useState(0)
|
||||
const [secondCategoryId, setSecondCategoryId] = useState('')
|
||||
|
||||
const [selectedValue, setSelectedValue] = useState({
|
||||
primaryId: '', // 大类ID
|
||||
|
205
src/views/search-details/index.jsx
Normal file
205
src/views/search-details/index.jsx
Normal file
@@ -0,0 +1,205 @@
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons'
|
||||
import { queryParse } from '@utils'
|
||||
import { Card, Input, Pagination, Skeleton, message } from 'antd'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import './index.less'
|
||||
|
||||
const { Search } = Input
|
||||
|
||||
const mockList = [
|
||||
{
|
||||
id: 100,
|
||||
subjectName: 'Redis支持哪几种数据类型?',
|
||||
subjectDifficult: 1,
|
||||
subjectType: 4,
|
||||
subjectScore: 1,
|
||||
subjectParse: '解析什么',
|
||||
subjectAnswer:
|
||||
'<p><br></p><ol><li>String(字符串)</li><li>List(列表)</li><li>Hash(字典)</li><li>Set(集合)</li><li>Sorted Set(有序集合)</li></ol><p><br></p><p><strong>String</strong></p><p><br></p><p>String是简单的 key-value 键值对,value 不仅可以是 String,也可以是数字。String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr,decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。</p><p><br></p><p><strong>List</strong></p><p><br></p><p>Redis列表是简单的字符串列表,可以类比到C++中的std::list,简单的说就是一个链表或者说是一个队列。可以从头部或尾部向Redis列表添加元素。列表的最大长度为2^32 - 1,也即每个列表支持超过40亿个元素。</p><p><br></p><p>Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。</p><p><br></p><p><strong>Hash</strong></p><p><br></p><p>Redis Hash对应Value内部实际就是一个HashMap,实际这里会有2种不同实现,这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap。</p><p><br></p><p><strong>Set</strong></p><p><br></p><p>set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。</p><p><br></p><p><strong>Sorted Set</strong></p><p><br></p><p>Redis有序集合类似Redis集合,不同的是增加了一个功能,即集合是有序的。一个有序集合的每个成员带有分数,用于进行排序。</p><p><br></p><p>Redis有序集合添加、删除和测试的时间复杂度均为O(1)(固定时间,无论里面包含的元素集合的数量)。列表的最大长度为2^32- 1元素(4294967295,超过40亿每个元素的集合)。</p><p><br></p><p>Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。</p>',
|
||||
labelName: ['Redis']
|
||||
},
|
||||
{
|
||||
id: 101,
|
||||
subjectName: 'Redis的高级数据类型有什么?',
|
||||
subjectDifficult: 2,
|
||||
subjectType: 4,
|
||||
subjectScore: 1,
|
||||
subjectParse: '解析什么',
|
||||
subjectAnswer:
|
||||
'<p><br></p><p>bitmap:bitmap是一种位数据类型,常常用于统计,大家比较知名的就是布隆过滤器。也可以统计一些大数据量的东西,比如每天有多少优惠券被使用。</p><p><br></p><p>hyperloglog:用于基数统计,基数是数据集去重后元素个数,运用了LogLog的算法。{1,3,5,7,5,7,8} 基数集:{1,3,5,7,8} 基数:5</p><p><br></p><p>geo:应用于地理位置计算,主要是经纬度的计算,常见的就是附近的人,可以以当前人的坐标获取周围附近的成员,可以计算经纬度,计算地理位置</p>',
|
||||
labelName: ['Redis']
|
||||
},
|
||||
{
|
||||
id: 102,
|
||||
subjectName: 'Redis的优点有什么?',
|
||||
subjectDifficult: 1,
|
||||
subjectType: 4,
|
||||
subjectScore: 1,
|
||||
subjectParse: '解析什么',
|
||||
subjectAnswer:
|
||||
'<p><br></p><p>(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)</p><p><br></p><p>(2) 支持丰富数据类型,支持string,list,set,Zset,hash等</p><p><br></p><p>(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行</p><p><br></p><p>(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除</p>',
|
||||
labelName: ['Redis']
|
||||
},
|
||||
{
|
||||
id: 103,
|
||||
subjectName: 'Redis相比Memcached有哪些优势?',
|
||||
subjectDifficult: 1,
|
||||
subjectType: 4,
|
||||
subjectScore: 1,
|
||||
subjectParse: '解析什么',
|
||||
subjectAnswer:
|
||||
'<p><br></p><p>(1) Memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型</p><p><br></p><p>(2) Redis的速度比Memcached快很多</p><p><br></p><p>(3) Redis可以持久化其数据</p>',
|
||||
labelName: ['Redis']
|
||||
},
|
||||
{
|
||||
id: 106,
|
||||
subjectName: 'redis过期策略都有哪些?',
|
||||
subjectDifficult: 1,
|
||||
subjectType: 4,
|
||||
subjectScore: 1,
|
||||
subjectParse: '解析什么',
|
||||
subjectAnswer:
|
||||
'<p><br></p><p>noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错。</p><p><br></p><p>allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key。</p><p><br></p><p>allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 key,这个一般没人用吧,为啥要随机,肯定是把最近最少使用的 key 给干掉啊。</p><p><br></p><p>volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key。</p><p><br></p><p>volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 key。</p><p><br></p><p>volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 key 优先移除。</p>',
|
||||
labelName: ['Redis']
|
||||
}
|
||||
]
|
||||
|
||||
const defaultValue = queryParse(location.search).t
|
||||
|
||||
const SearchDetails = () => {
|
||||
const navigate = useNavigate()
|
||||
const [isShowSkeleton, setIsShowSkeleton] = useState(true)
|
||||
const [questionList, setQuestionList] = useState(mockList)
|
||||
const [total, setTotal] = useState(0)
|
||||
const [pageIndex, setPageIndex] = useState(0)
|
||||
const [searchValue, setSearchValue] = useState(defaultValue)
|
||||
|
||||
const searchSubject = () => {
|
||||
setIsShowSkeleton(false)
|
||||
}
|
||||
|
||||
const handleJump = id => {
|
||||
navigate('/brush-question/' + id)
|
||||
}
|
||||
|
||||
const onChangePagination = () => {}
|
||||
|
||||
useEffect(() => {
|
||||
searchSubject()
|
||||
}, [searchValue])
|
||||
|
||||
return (
|
||||
<div className='search-details-box'>
|
||||
<div className='search-details-box-search'>
|
||||
<div>
|
||||
<Search
|
||||
placeholder='请输入感兴趣的内容'
|
||||
onSearch={value => {
|
||||
if (value) {
|
||||
// this.state.searchText = value
|
||||
// this.pageIndex = 1
|
||||
// this.searchSubject()
|
||||
} else {
|
||||
message.info('搜索词不能为空')
|
||||
}
|
||||
}}
|
||||
enterButton
|
||||
defaultValue={defaultValue}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Skeleton
|
||||
title={{ width: '100%' }}
|
||||
paragraph={{ rows: 20, width: new Array(20).fill('100%') }}
|
||||
active
|
||||
loading={isShowSkeleton}
|
||||
>
|
||||
<div className='search-details-box-content'>
|
||||
<div className='search-details-box-content-card'>
|
||||
<Card
|
||||
style={{ width: '100%' }}
|
||||
// tabList={this.tabList}
|
||||
// onTabChange={key => {
|
||||
// this.onTabChange(key, 'key')
|
||||
// }}
|
||||
>
|
||||
<div className='search-details-box-content-main'>
|
||||
{questionList?.length > 0 ? (
|
||||
questionList.map((item, index) => {
|
||||
if (item.subjectAnswer) {
|
||||
return (
|
||||
<div className='search-details-box-content-main-item'>
|
||||
<div
|
||||
className='search-details-box-content-main-item-question'
|
||||
key={`search-details-question_${index}`}
|
||||
onClick={() => handleJump(item.id)}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: item.subjectName
|
||||
}}
|
||||
></div>
|
||||
<div
|
||||
className='search-details-box-content-main-item-answer'
|
||||
key={`search-details-answer_${index}`}
|
||||
onClick={() => handleJump(item.id)}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: item.subjectAnswer + '...'
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div className='search-details-box-content-main-item'>
|
||||
<div
|
||||
className='search-details-box-content-main-item-question'
|
||||
key={`search-details-question_${index}`}
|
||||
onClick={() => handleJump(item.id)}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: item.subjectName
|
||||
}}
|
||||
></div>
|
||||
<div
|
||||
className='search-details-box-content-main-item-answer'
|
||||
key={`search-details-answer_${index}`}
|
||||
onClick={() => handleJump(item.id)}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: item.subjectAnswer
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
) : (
|
||||
<div className='search-null'>
|
||||
<ExclamationCircleOutlined />
|
||||
<div>很抱歉,没有找到与你搜索相关的内容</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
<div className='search-details-box-content-paging'>
|
||||
{total > 20 && (
|
||||
<Pagination
|
||||
style={{
|
||||
padding: '24px 0',
|
||||
textAlign: 'center'
|
||||
}}
|
||||
showQuickJumper
|
||||
current={pageIndex}
|
||||
defaultCurrent={1}
|
||||
total={total}
|
||||
defaultPageSize={20}
|
||||
onChange={onChangePagination}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Skeleton>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SearchDetails
|
46
src/views/search-details/index.less
Normal file
46
src/views/search-details/index.less
Normal file
@@ -0,0 +1,46 @@
|
||||
.search-details-box {
|
||||
width: 1439px;
|
||||
margin: 0 auto;
|
||||
background-color: #fff;
|
||||
padding: 20px 50px;
|
||||
border-radius: 5px;
|
||||
overflow: auto;
|
||||
.ant-card-body {
|
||||
padding: 0 24px;
|
||||
}
|
||||
.search-details-box-search {
|
||||
background-color: #f5f5f5;
|
||||
padding: 20px 40px;
|
||||
border-radius: 3px 3px 0 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
.search-details-box-content {
|
||||
.search-details-box-content-main-item {
|
||||
margin-top: 10px;
|
||||
padding: 10px 0;
|
||||
font-weight: 400;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
.search-details-box-content-main-item-question {
|
||||
color: #3c6eee;
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.search-details-box-content-main-item-answer {
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
letter-spacing: 0;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 2; /*要显示的行数*/
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
||||
.search-null {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin: 50px 0;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,438 +1,422 @@
|
||||
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 = '请使用富文本编辑器输入选项内容';
|
||||
import { debounce } from '@utils'
|
||||
import { Input, Select, Tooltip, message } from 'antd'
|
||||
import _ from 'lodash'
|
||||
import React, { Component, Fragment } from 'react'
|
||||
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的最短长度
|
||||
{
|
||||
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: '', //试题解析
|
||||
};
|
||||
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 = ''; // 选项内容
|
||||
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,
|
||||
};
|
||||
});
|
||||
/**
|
||||
* 新增/删除
|
||||
* @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>
|
||||
);
|
||||
})
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
optionList: type === 'add' ? optionList : list,
|
||||
currentActiveList
|
||||
},
|
||||
() => {
|
||||
this.handleChangeOption()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 选项模块
|
||||
* @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}
|
||||
</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>
|
||||
);
|
||||
};
|
||||
/**
|
||||
* 确认/取消 编辑框
|
||||
* @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()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 选项模块-操作按钮
|
||||
* @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>
|
||||
/**
|
||||
* 展开 编辑项
|
||||
* @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}
|
||||
</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='' />
|
||||
</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>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -35,6 +35,7 @@ interface UserInfo {
|
||||
email?: string
|
||||
sex?: string | number
|
||||
introduce?: string
|
||||
avatar?: string
|
||||
}
|
||||
|
||||
const Sex: Record<string, any> = {
|
||||
@@ -57,6 +58,7 @@ const UserInfo = () => {
|
||||
const [editFlag, setEditFlag] = useState(false)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [userInfo, setUserInfo] = useState<UserInfo>({})
|
||||
const [avatar, setAvatar] = useState()
|
||||
|
||||
const getUserInfo = async () => {
|
||||
req(
|
||||
@@ -85,7 +87,8 @@ const UserInfo = () => {
|
||||
const onFinish = () => {
|
||||
setLoading(true)
|
||||
const values = form.getFieldsValue()
|
||||
if (!Object.values(values).filter(Boolean).length) {
|
||||
// return console.log(values)
|
||||
if (!Object.values(values).filter(Boolean).length && !avatar) {
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
@@ -93,6 +96,9 @@ const UserInfo = () => {
|
||||
userName: loginId,
|
||||
...values
|
||||
}
|
||||
if (avatar) {
|
||||
params.avatar = avatar
|
||||
}
|
||||
req(
|
||||
{
|
||||
method: 'post',
|
||||
@@ -119,8 +125,11 @@ const UserInfo = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const handleChange = info => {
|
||||
console.log(info)
|
||||
const handleChange = ({ file }) => {
|
||||
// console.log(info)
|
||||
if (file.status === 'done' && file.response.success && file.response.data) {
|
||||
setAvatar(file.response.data)
|
||||
}
|
||||
}
|
||||
|
||||
const uploadButton = (
|
||||
@@ -150,23 +159,24 @@ const UserInfo = () => {
|
||||
satoken: 'jichi ' + tokenValue
|
||||
}}
|
||||
data={{
|
||||
bucket: 'jichi',
|
||||
bucket: 'user',
|
||||
objectName: 'icon'
|
||||
}}
|
||||
// beforeUpload={beforeUpload}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{uploadButton}
|
||||
{avatar ? (
|
||||
<img src={avatar} style={{ height: '80px', width: '80px' }} />
|
||||
) : (
|
||||
uploadButton
|
||||
)}
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
) : (
|
||||
<Form.Item label='用户头像'>
|
||||
<img className='user-info_header' src={Head} />
|
||||
<img className='user-info_header' src={userInfo.avatar || Head} />
|
||||
</Form.Item>
|
||||
)}
|
||||
{/* <Form.Item label='用户头像'>
|
||||
<img className='user-info_header' src={Head} />
|
||||
</Form.Item> */}
|
||||
</Col>
|
||||
<Col span={16}>
|
||||
{editFlag ? (
|
||||
|
Reference in New Issue
Block a user