import React, { useState, memo, useEffect } from 'react'; import { Input, View, Image } from '@tarojs/components'; import { debounce } from 'lodash'; const reduce = 'https://wanmi-b2b-x-site.oss-cn-shanghai.aliyuncs.com/pandora-ui/assets/components/images/stepper/icon-reduce.png'; const reduceDisabled = 'https://wanmi-b2b-x-site.oss-cn-shanghai.aliyuncs.com/pandora-ui/assets/components/images/stepper/icon-reduce-disabled.png'; const add = 'https://wanmi-b2b-x-site.oss-cn-shanghai.aliyuncs.com/pandora-ui/assets/components/images/stepper/icon-add.png'; const addDisabled = 'https://wanmi-b2b-x-site.oss-cn-shanghai.aliyuncs.com/pandora-ui/assets/components/images/stepper/icon-add-disabled.png'; export interface IStepperProps { /** 默认值 */ count?: number; /** 最小值 */ min?: number; /** 最大值 */ max?: number; /** 输入框是否可用 */ inputDisabled?: boolean; /** 禁用 */ disabled?: boolean; isNumber?: boolean; /** 回调方法 */ getNum: (count: number, flag?: boolean) => void; // 点击加号、减号、input输入框输入内容时,在render前,拦截处理value handleValue?: (obj: { // type操作类型 - 加号add、减号minus、文本输入input type: string; // 当前value currentValue: number; // 下一个value,即:加1 或 减1后的value nextValue?: number; }) => number; } export interface IStepperState { number: number; } /** * 步进器组件 */ // 是否正在执行中 let isRun = false; const Stepper: React.FC = ({ count = 0, min = 0, max = 999, inputDisabled = false, disabled = false, isNumber = false, //是否支持输入小数 getNum = (_count: number, _flag?: boolean) => {}, handleValue = (obj: { type: string; currentValue: number; nextValue?: number; }) => undefined, }) => { // 考虑输入框和按钮两种录入方式,使用useReducer时type类型不统一 // 仅第一次同步尝试修正count,等效于defaultValue const [number, setNumber] = useState(() => { const defaultValue = count > max ? max : (min > count ? min : count) || 0; if (defaultValue !== count) { getNum(Number(defaultValue)); } return defaultValue; }); // renderKey用于强制更新Input组件 const [renderKey, setRenderKey] = useState(0); /* eslint-disable */ useEffect(() => { if (count !== number){ if(count < min){ getNum(Number(min)) setNumber(Number(min)); } else if(count > max){ getNum(Number(max)) setNumber(Number(max)); } else { setNumber(Number(count)); } } }, [count]); /* eslint-enable */ /** * 输入失焦 * @param e */ const onBlur = (e) => { if (isRun) { return; } isRun = true; let num = e.detail.value === '' || e.detail.value === '-' ? 0 : isNumber ? Number(e.detail.value) : Number(e.detail.value).toFixed(0); num = num || min; let result = checkNum(num); if (num) { // 通过handleValue方法外部处理value const val = handleValue({ type: 'input', currentValue: checkNum(num), nextValue: checkNum(num), }); // 当val为undefined 或 null时,用result result = checkNum(val ?? result); } setNumber(result); // 用于解决某种情况下输入框输入,如:06,input输入框失去焦点时,输入框依旧展示06,正确的是展示6 if (e.detail.value || result !== number) { getNum(result); } /** * 强制更新Input组件 * 用于解决当number、result都为0,.detail.value为''时,input输入框失去焦点时输入框空白的问题; */ setRenderKey(Date.now()); setTimeout(() => { isRun = false; }, 1000); }; /** * 输入 * @param e */ const onChange = (e) => { if (e.detail.value === '') { return; } const num = e.detail.value === '-' ? 0 : Number(e.detail.value); if (num > max) { setNumber(num); } else if (min > num) { setNumber(num); } else { setNumber(num); } }; const reduceNum = (e) => { e.stopPropagation(); if (isRun) { return; } let result = checkNum(Number(number) - 1); // 当前value const currentValue = checkNum(Number(number)); // 下一个value,即:加1 或 减1后的value const nextValue = checkNum(Number(number) - 1); // 通过handleValue方法外部处理value const val = handleValue({ type: 'minus', currentValue, nextValue, }); // 当val为undefined 或 null时,用result result = checkNum(val ?? result); setNumber(result); getNum(result, false); }; const addNum = (e) => { e.stopPropagation(); if (isRun) { return; } let result = checkNum(Number(number) + 1); // 当前value const currentValue = checkNum(Number(number)); // 下一个value,即:加1 或 减1后的value const nextValue = checkNum(Number(number) + 1); // 通过handleValue方法外部处理value const val = handleValue({ type: 'add', currentValue, nextValue, }); // 当val为undefined 或 null时,用result result = checkNum(val ?? result); setNumber(result); getNum(result, true); }; /** * 校验数量合法性 * @param num */ const checkNum = (num) => { if (num > max) { return max; } else if (num < min) { return min; } else if (num === '' || num <= 0) { return 0; } return num; }; return ( e.stopPropagation()}> {disabled || Number(number) === min || Number(number) === 0 ? ( ) : ( {/**可点击区域 */} e.stopPropagation()} /> )} {!(inputDisabled || disabled) ? ( e.stopPropagation()} onInput={onChange} onBlur={onBlur} maxlength={7} /> ) : ( )} {disabled || number >= max ? ( ) : ( {/**可点击区域 */} )} ); }; /** * 减少列表不必要渲染 等效于shouldComponentUpdate * @param prevProps * @param nextProps */ const equal = (prevProps, nextProps) => { return JSON.stringify(prevProps) === JSON.stringify(nextProps); }; export default memo(Stepper, equal);