一,设置答题器选项
import React, { useState, useEffect } from 'react'
import PropTypes from 'prop-types'
import _ from 'lodash' import CloseButtonSmall from '../CloseButtonSmall'
import LargeButton from '../LargeButton'
import CancelButton from '../CancelButton'
import OptionSettings from '../OptionSettings' import './index.less' /**
* 答题设置组件
*
* @param {*} props
* @returns
*/
function AnswererSettings(props) {
const { style, onConfirm, onClose } = props
const wrapperStyle = _.assign({}, style) const [selectedCount, setSelectedCount] = useState(2)
const [isShowLimitTip, setIsShowLimitTip] = useState(false)
const minOptionCount = 2 useEffect(() => {
const tipTimeout = setTimeout(() => {
setIsShowLimitTip(false)
}, 3000)
return () => {
clearTimeout(tipTimeout)
}
}, [isShowLimitTip]) const handleSelectOption = (optionCount) => {
setSelectedCount(optionCount)
} const handleSelectMinOption = (optionCount) => {
setIsShowLimitTip(true)
setSelectedCount(optionCount)
} const handleConfirm = () => {
onConfirm(selectedCount)
onClose()
} const handleCancel = () => {
onClose()
} return (
<div className="answerer-settings-component-wrap" style={wrapperStyle}>
<div className="header">
<div className="title-wrap">
<div className="title-tip">
<span className="title-icon" />
<div className="title-contents">
<p className="title">答题器</p>
<p className="sub-title">设置选项让学生实时参与答题</p>
</div>
</div>
<CloseButtonSmall onClick={handleCancel} />
</div>
</div>
<div className="body">
<div className="title-wrap">
<span className="options-icon" />
<div className="title-contents">
<p className="title">设定选项</p>
</div>
{ isShowLimitTip
? (
<div className="title-tip">
<span className="tip-icon" />
<span className="tip-contents">请至少保留两个选择选项</span>
<CloseButtonSmall
style={{
marginLeft: '20px',
}}
onClick={() => {
setIsShowLimitTip(false)
}}
/>
</div>
)
: ''}
</div>
<OptionSettings
style={{
marginTop: '6px',
marginRight: '23px',
}}
onSelectOption={handleSelectOption}
onSelectMinOption={handleSelectMinOption}
minOptionCount={minOptionCount}
/>
</div>
<div className="footer">
<div className="answerer-btns-wrap">
<LargeButton
text="确 定"
className="btn-confirm"
onClick={handleConfirm}
style={{
marginLeft: '29px',
}}
/>
<CancelButton
text="取 消"
className="btn-cancel"
onClick={handleCancel}
style={{
marginLeft: '37px',
}}
/>
</div>
</div>
</div>
)
} AnswererSettings.propTypes = {
style: PropTypes.object,
onConfirm: PropTypes.func.isRequired,
onClose: PropTypes.func,
} AnswererSettings.defaultProps = {
style: {},
onClose: _.noop,
} export default AnswererSettings
import React, { useState, useRef } from 'react'
import PropTypes from 'prop-types'
import CX from 'classnames'
import _ from 'lodash' import './index.less' const optionItemImgs = [
require('~/shared/assets/image/answerer-option-letter-a.svg'),
require('~/shared/assets/image/answerer-option-letter-b.svg'),
require('~/shared/assets/image/answerer-option-letter-c.svg'),
require('~/shared/assets/image/answerer-option-letter-d.svg'),
require('~/shared/assets/image/answerer-option-letter-e.svg'),
// require('~/shared/assets/image/answerer-option-letter-f.svg'),
] /**
* 选项设置组件
*
* @param {*} props
* @returns
*/
function OptionSettings(props) {
const {
style, onSelectOption, onSelectMinOption, minOptionCount,
} = props
const wrapperStyle = _.assign({}, style) const [selectedCount, setSelectedCount] = useState(2)
const options = useRef(null) const handleClickItem = (e) => {
const { target } = e
if (target.classList.contains('last-selected')) {
if (selectedCount !== minOptionCount) {
setSelectedCount(selectedCount - 1)
onSelectOption(selectedCount - 1)
} else {
onSelectMinOption(selectedCount)
}
} else if (target.classList.contains('first-non-selected')) {
setSelectedCount(selectedCount + 1)
onSelectOption(selectedCount + 1)
}
} const renderOptionItems = () => {
return optionItemImgs.map((img, index) => {
return (
<div
className={CX({
item: true,
selected: index < selectedCount,
'last-selected': index === selectedCount - 1,
'first-non-selected': index === selectedCount,
})}
key={img}
>
<img className="letter" alt="" src={img} />
<img className="add" alt="" />
<span className="tip" />
</div>
)
})
} return (
<div className="option-settings-component-wrap" style={wrapperStyle}>
<div className="option-items" onClick={handleClickItem} role="button" tabIndex={0} ref={options}>
{renderOptionItems()}
</div>
</div>
)
} OptionSettings.propTypes = {
style: PropTypes.object,
onSelectOption: PropTypes.func,
onSelectMinOption: PropTypes.func,
minOptionCount: PropTypes.number,
} OptionSettings.defaultProps = {
style: {},
onSelectOption: _.noop,
onSelectMinOption: _.noop,
minOptionCount: 2,
} export default OptionSettings

效果如下:

二,展示答题器状态

import React, { useState } from 'react'
import PropTypes from 'prop-types'
import _ from 'lodash'
import CX from 'classnames' import CloseButtonSmall from '../CloseButtonSmall'
import AnswererStatItem from '../AnswererStatItem' import './index.less' // 默认支持5个选项,名称、背景样式和进度条样式
const itemsOption = [
{
itemName: 'A',
iconStyle: { backgroundImage: 'linear-gradient(135deg, #ff758c, #ffb867)' },
processStyle: { backgroundImage: 'linear-gradient(273deg, #ff758c, #ffb867)' },
},
{
itemName: 'B',
iconStyle: { backgroundImage: 'linear-gradient(135deg, #ffb867, #fdde74)' },
processStyle: { backgroundImage: 'linear-gradient(273deg, #ffb867, #fdde74)' },
},
{
itemName: 'C',
iconStyle: { backgroundImage: 'linear-gradient(135deg, #4cf27d, #6affcc)' },
processStyle: { backgroundImage: 'linear-gradient(273deg, #4cf27d, #6affcc)' },
},
{
itemName: 'D',
iconStyle: { backgroundImage: 'linear-gradient(135deg, #63e4e4, #8affff)' },
processStyle: { backgroundImage: 'linear-gradient(273deg, #63e4e4, #8affff)' },
},
{
itemName: 'E',
iconStyle: { backgroundImage: 'linear-gradient(135deg, #3977f6, #81d5fa)' },
processStyle: { backgroundImage: 'linear-gradient(273deg, #3977f6, #81d5fa)' },
},
] /**
* 答题结果展示组件
* @param {onRestart} 重新开始
* @param {onClose} 关闭
* @param {itemNum} 选项的个数
* @param {statData} 对象,{选项:次数}
* @param {userCount} 投票人数
*/
function AnswererStat(props) {
const {
style, onRestart, onClose, itemNum, statData, userCount,
} = props
const wrapperStyle = _.assign({}, style) const [isRestarting, setIsRestarting] = useState(false) const renderItems = () => {
return itemsOption.slice(0, itemNum).map((option, index) => {
return (
<AnswererStatItem
style={{
marginBottom: '12px',
}}
key={option.itemName}
maxValue={userCount}
value={statData[index]}
{...option}
/>
)
})
}
return (
<div className="answerer-stat-wrap" style={wrapperStyle}>
<div className="operation">
<div
className="restart"
role="button"
onClick={() => {
setIsRestarting(!isRestarting)
onRestart()
}}
tabIndex={0}
>
<img
className={CX({
'restart-icon': true,
active: isRestarting,
})}
src={require('~/shared/assets/image/loading-icon-circle-50-50.svg')}
alt=""
/>
<span className="restart-name">重新答题</span>
</div>
<CloseButtonSmall
theme="dark"
style={{
backgroundColor: 'rgba(54, 65, 82, 0.4)',
boxShadow: '0 2px 4px 0 rgba(51, 51, 51, 0.2)',
}}
onClick={onClose}
/>
</div>
<div className="item-area">
{renderItems()}
</div>
</div>
)
} AnswererStat.propTypes = {
style: PropTypes.object,
itemNum: PropTypes.number.isRequired,
userCount: PropTypes.number,
statData: PropTypes.object.isRequired,
onRestart: PropTypes.func,
onClose: PropTypes.func,
} AnswererStat.defaultProps = {
style: {},
onClose: _.noop,
onRestart: _.noop,
userCount: 1,
} export default AnswererStat
import React from 'react'
import PropTypes from 'prop-types'
import _ from 'lodash' import './index.less'
/**
* 答题选项组件
*
* @param {选项的名称} itemName
* @param {名称所在区域的样式} iconStyle
* @param {进度条的样式} processStyle
* @param {当前值} value
* @param {最大值} maxValue
* @returns
*/
function AnswererStatItem(props) {
const {
style, itemName, iconStyle, processStyle, value, maxValue,
} = props
const wrapperStyle = _.assign({}, style)
const wrapperIconStyle = _.assign({}, iconStyle) let dataPercentage // 数据计算出来的百分比
if (maxValue === 0) {
dataPercentage = 0
} else {
dataPercentage = value / maxValue
} const textPercentage = Math.round(dataPercentage * 100) // 在进度条展示的百分比文本 // 计算进度条样式相关的百分比
// 根据可见长度的百分比换算符合可见长度比例的全部长度
// 例,10%的区域被遮挡,此时若数据是50%,则需要填充整体宽度为 10%+50%*90%=55%
const computeProcessBarOffset = () => {
if (dataPercentage === 0) {
return 0
}
const visibleLength = 0.9
return ((dataPercentage * visibleLength + 1 - visibleLength) * 100).toFixed()
} const wrapProcessStyle = _.assign({}, processStyle, { right: `${100 - computeProcessBarOffset()}%` }) return (
<div className="answerer-stat-item-wrap" style={wrapperStyle}>
<div className="answerer-stat-item">
<div className="stat-item-icon" style={wrapperIconStyle}>
<div className="item-name-wrap">
{itemName}
</div>
</div>
<div className="stat-item-present">
<div className="stat-item-percentage">
<div className="filler" style={wrapProcessStyle} />
<div className="stat-item-data">
<span className="percentage">
{`${textPercentage}%`}
</span>
{ ' / ' }
<span className="numbers">
{`${value} 次`}
</span>
</div>
</div>
</div>
</div>
</div>
)
} AnswererStatItem.propTypes = {
style: PropTypes.object,
iconStyle: PropTypes.object,
processStyle: PropTypes.object,
itemName: PropTypes.string.isRequired,
value: PropTypes.number,
maxValue: PropTypes.number,
} AnswererStatItem.defaultProps = {
style: {},
iconStyle: {},
processStyle: {},
value: 0,
maxValue: 0,
} export default AnswererStatItem

效果如下:

react实现设置答题器选项个数的更多相关文章

  1. jQuery通过text值来设置选定,以及遍历select的选项个数和遍历

    真是醉了,网上搜了很久,全都是千篇一律的. 大家都拷贝来拷贝去,全是错的. 通过text值来设置select选定 $("#CompanyID").find("option ...

  2. Delphi应用程序的调试(十)调试器选项(在IDE中不要使用异常)

    可在两个级别上设置调试选项:工程级和环境级.在前面的讲解中讲解了工程级调试选项,通过主菜单[Project | Options…]打开如下对话框: 可在Debugger Options对话框中设置全局 ...

  3. curl_setopt — 设置 cURL 传输选项

    curl_setopt (PHP 4 >= 4.0.2, PHP 5, PHP 7) curl_setopt — 设置 cURL 传输选项 bool curl_setopt ( resource ...

  4. Delphi应用程序的调试(十)调试器选项

    可在两个级别上设置调试选项:工程级和环境级.在前面的讲解中讲解了工程级调试选项,通过主菜单[Project | Options…]打开如下对话框: 可在Debugger Options对话框中设置全局 ...

  5. 一个基于chrome扩展的自动答题器

    1.写在前面 首先感谢小茗同学的文章-[干货]Chrome插件(扩展)开发全攻略, 基于这篇入门教程和demo,我才能写出这款 基于chrome扩展的自动答题器. git地址: https://git ...

  6. Windows 8 动手实验系列教程 实验6:设置和首选项

    动手实验 实验6:设置和首选项 2012年9月 简介 实验3介绍了合约并演示了应用程序如何轻松地与共享和搜索合约实现集成.合约同样包含设置超级按钮,它对活动的Windows应用商店应用的设置进行修改. ...

  7. Linker Scripts2--链接器选项概述

    1.前言 为了尽可能的与其它链接器兼容,GNU链接器ld涵盖了很多情况.因此,有很多选项可以控制链接器的行为. 2. 命令行选项概述 链接器支持很多命令行选项,在特定的上下文,实际应用中只有很少一部分 ...

  8. Excel设置下拉选项的方法

    前些日子参加提高班组织的数据采集工作,到各个二级学院搜集数据,当然离不开我们常用的Excel表格了.在这次采集数据的过程过程中还真学到了一两招.就比如在Excel中设置下拉选项的方法. 例如我们要在A ...

  9. React Native 设置RGBA背景色

    React Native 设置RGBA背景色: 可以先用Mac自带吸色工具,获取RGB值,然后设置背景如下: backgroundColor: 'rgba(52, 52, 52, 0.8)', 透明度 ...

随机推荐

  1. MQTT 客户端应用及常见问题(C#)

    https://blog.csdn.net/dengyaan/article/details/51752327 最近因为工作需要,需要使用C# 语言编写一个通过MQTT协议 ,上传数据到云端的工具.因 ...

  2. 获取div下的input type为file的所有对象

    var files = $(".profile-content").find("input[type='file']"); files.each(functio ...

  3. 前端性能测试工具 : dynaTrace Ajax (还没写完)

    今天开始写这个工具, #什么是dynaTrace Ajax? 随着 jQuery.Dojo.YUI 等框架的兴起让构建 Web2.0 应用更加容易,但随之带来的定位等应用问题也越来越难,尤其是与性能相 ...

  4. sublime 删除重复行

    sublime 删除重复行 - 国内版 Binghttps://cn.bing.com/search?q=sublime+%E5%88%A0%E9%99%A4%E9%87%8D%E5%A4%8D%E8 ...

  5. leetcode 874. Walking Robot Simulation

    874. Walking Robot Simulation https://www.cnblogs.com/grandyang/p/10800993.html 每走一步(不是没走commands里的一 ...

  6. 使用Varnish为网站加速

    (1).Varnish概述 Varnish是一款高性能的开源HTTP加速器,可以有效降低web服务器的负载,提升访问速度.根据官方的说法,Varnish是一个cache型的HTTP反向代理. Varn ...

  7. Nginx搭建负载均衡集群

    (1).实验环境 youxi1 192.168.5.101 负载均衡器 youxi2 192.168.5.102 主机1 youxi3 192.168.5.103 主机2 (2).Nginx负载均衡策 ...

  8. C# 子线程与主线程通讯方法一

    最近在项目中要用到子线程运行结束向主线程通知的需求,利用线程上下文来实现线程之间的同步. 子线程结束后调用同步函数,向主线程发送时间字符串,改变主窗体的label标签 label标签改变事件触发处理函 ...

  9. DevExpress v18.1 下载和教程文档

    http://www.zdfans.com/html/18682.html 教程文档 https://blog.csdn.net/AABBbaby/article/details/81094482 下 ...

  10. pipline中替换tag变量

    实验架构: 192.168.0.96 gitlab 192.168.0.97 jenkins 192.168.0.98 harbor.docker集群 说明:下面代码编译镜像那一步的代码必须靠左,目的 ...