react 提示消息队列 (支持动态添加,删除,多实例化)
import React from 'react' import PropTypes from 'prop-types' import AnimationOperateFeedbackInfo from '../AnimationOperateFeedbackInfo' import OperateFeedbackInfo from '../OperateFeedbackInfo' import './index.less' const OPERATE_ARRAY_MAX_LENGTH = 5 export default function AssistantOperateFeedbackArea({ processingOperateList, failedOperateList, onClickCleanFailedOperateBtn, animationEndCallback, }) { const operateWrapStyle = { width: '200px', height: '28px', color: '#fff', } return ( <div className="assistant-operate-feedback-area-wrap"> { processingOperateList.length > 0 && ( <div className="operate-feedback-area" style={{ // queueMaxLength + 1 省略区域高度 height: `${processingOperateList.length > OPERATE_ARRAY_MAX_LENGTH ? (OPERATE_ARRAY_MAX_LENGTH + 1) * 28 : processingOperateList.length * 28}px`, }} > { processingOperateList.slice(0, OPERATE_ARRAY_MAX_LENGTH).map((item) => { return ( <AnimationOperateFeedbackInfo operateId={item.operateId} operate={item.operate} operateType={item.state} animationEndCallback={animationEndCallback} style={operateWrapStyle} key={item.operateId} /> ) }) } { processingOperateList.length > OPERATE_ARRAY_MAX_LENGTH && ( <div style={operateWrapStyle} className="ellipsis-operate-info" > ... ... </div> ) } </div> ) } { failedOperateList.length > 0 && ( <div className="operate-feedback-area" style={{ // queueMaxLength + 1 省略区域高度 height: `${failedOperateList.length > OPERATE_ARRAY_MAX_LENGTH ? (OPERATE_ARRAY_MAX_LENGTH + 1) * 28 : failedOperateList.length * 28}px`, }} > { failedOperateList.slice(0, OPERATE_ARRAY_MAX_LENGTH).map((item) => { return ( <div className="operate-feedback-info-wrap"> <OperateFeedbackInfo operate={item.operate} style={operateWrapStyle} iconRotate={false} iconPath={require('~/shared/assets/image/red-white-warn-icon-60-60.png')} /> </div> ) }) } <div className="clean-failed-feedback-info-btn" onClick={onClickCleanFailedOperateBtn} tabIndex={0} role="button" > 清除所有异常 </div> { failedOperateList.length > OPERATE_ARRAY_MAX_LENGTH && ( <div style={operateWrapStyle} className="ellipsis-operate-info" > ... ... </div> ) } </div> ) } { processingOperateList.length === 0 && failedOperateList.length === 0 && ( <div className="no-feedback-info-tip"> 暂无对教师端操作 </div> ) } </div> ) } AssistantOperateFeedbackArea.propTypes = { processingOperateList: PropTypes.array, failedOperateList: PropTypes.array, onClickCleanFailedOperateBtn: PropTypes.func, animationEndCallback: PropTypes.func, } AssistantOperateFeedbackArea.defaultProps = { processingOperateList: [], failedOperateList: [], animationEndCallback: () => {}, onClickCleanFailedOperateBtn: () => {}, }
import React, { useRef, useLayoutEffect } from 'react' import PropTypes from 'prop-types' import CX from 'classnames' import './index.less' export default function OperateFeedbackInfo({ operate, iconPath, style, iconRotate, resetAnimation, }) { const imgRef = useRef(null) useLayoutEffect(() => { if (resetAnimation === true) { const imgElem = imgRef.current imgElem.className = '' // 触发一次重绘 同步所有旋转的icon动画 imgElem.height = imgElem.offsetHeight imgElem.className = 'operate-icon-rotate' } }) return ( <div className="operate-feedback-Info" style={style} > <div className="operate-feedback-content">{operate}</div> <div className="operate-feedback-state-icon"> <img className={CX({ 'operate-icon-rotate': iconRotate, })} src={iconPath} alt="" ref={imgRef} /> </div> </div> ) } OperateFeedbackInfo.propTypes = { operate: PropTypes.string.isRequired, iconPath: PropTypes.string.isRequired, iconRotate: PropTypes.bool, resetAnimation: PropTypes.bool, style: PropTypes.object, } OperateFeedbackInfo.defaultProps = { style: {}, resetAnimation: false, iconRotate: false, }
import React from 'react' import PropTypes from 'prop-types' import CX from 'classnames' import OperateFeedbackInfo from '../OperateFeedbackInfo' import './index.less' export default function AnimationOperateFeedbackInfo({ operateId, operate, operateType, animationEndCallback, style, }) { return ( <div className={CX({ 'animation-operate-feedback-info-wrap': true, 'animation-operate-feedback-processing-state': operateType === 'processing', 'animation-operate-feedback-success-state': operateType === 'success', })} onAnimationEnd={() => { if (operateType === 'success') { animationEndCallback(operateId) } }} > <OperateFeedbackInfo resetAnimation={operateType !== 'success'} operate={operate} style={style} iconRotate={operateType !== 'success'} iconPath={operateType === 'success' ? require('~/shared/assets/image/icon-success-green-white-100-100.png') : require('~/shared/assets/image/processing-icon.svg')} /> </div> ) } AnimationOperateFeedbackInfo.propTypes = { operateId: PropTypes.string, operate: PropTypes.string, operateType: PropTypes.string, animationEndCallback: PropTypes.func, style: PropTypes.object, } AnimationOperateFeedbackInfo.defaultProps = { operateId: '', operate: '', operateType: '', animationEndCallback: () => {}, style: {}, }
以上是所有UI部分(包括交互):效果如下:
下面是hoc逻辑部分:
import React, { Component } from 'react' import { observable, action, } from 'mobx' import { observer, } from 'mobx-react' import uid from 'uuid' import { AssistantOperateFeedbackArea } from '@dby-h5-clients/pc-1vn-components' import { Rnd } from 'react-rnd' import _ from 'lodash' const operateListClump = observable.object({ failedOperateList: [], processingOperateList: [], }) class OperateState { @action constructor(operate = '') { this.operateId = uid() this.operate = operate operateListClump.processingOperateList.push({ operate, operateId: this.operateId, state: 'processing' }) } operateId operate @action success(operate = '') { const operateIndex = _.findIndex(operateListClump.processingOperateList, { operateId: this.operateId }) operateListClump.processingOperateList[operateIndex] = { operate: operate || this.operate, operateId: this.operateId, state: 'success' } } @action failed(operate = '') { operateListClump.failedOperateList.push({ operate: operate || this.operate, operateId: this.operateId, state: 'failed' }) _.remove(operateListClump.processingOperateList, { operateId: this.operateId }) } } @observer class AssistantOperateList extends Component { static addOperate = action((operate) => { return new OperateState(operate) }) @action removeSuccessOperate = (operateId) => { _.remove(operateListClump.processingOperateList, { operateId }) } @action handleCleanAllFailedFeedbackInfo = () => { operateListClump.failedOperateList = [] } render() { return ( <Rnd bounds=".main-space-wrap" dragHandleClassName="assistant-operate-feedback-area-wrap" lockAspectRatio={16 / 9} enableResizing={{ top: false, right: false, bottom: false, left: false, topRight: false, bottomRight: false, bottomLeft: false, topLeft: false, }} default={{ x: 30, y: 30, }} > <AssistantOperateFeedbackArea failedOperateList={operateListClump.failedOperateList.toJSON()} processingOperateList={operateListClump.processingOperateList.toJSON()} animationEndCallback={this.removeSuccessOperate} onClickCleanFailedOperateBtn={this.handleCleanAllFailedFeedbackInfo} /> </Rnd> ) } } export default AssistantOperateList
使用说明:
在其它组件中导入:
import AssistantOperateList from '../AssistantOperateList'
const msg = AssistantOperateList.addOperate('协助开启答题器')
msg.success()
msg.failed()