<template>
	<view class="flex" :style="{ width, height }">
		<view class="count-box flex align-center flex-1" :class="[wrong ? 'box-red' : '']" :style="[{borderColor: customBorderColor, borderRadius: addUnitRpx(radius)}]">
			<view v-if="showStepButton" class="count-less count-pub" :style="[stepLessStyle]" @tap.stop="less" @longpress="longpressLess" @touchend="handletouchend">
				<slot name="left">
					-
				</slot>
			</view>
			<!-- #ifdef H5 -->
			<input
				type="text"
				@input="inputChange"
				:placeholder="placeholder"
				:disabled="inputDisable"
				v-model="myValue"
				@focus="onFocus"
				@blur="onBlur"
				class="count-input flex-1"
				:style="[inputStyle]"
				:inputmode="isDecimal?'text':'numeric'"
			/>
			<!-- #endif -->
			<!-- #ifndef H5 -->
			<input
				type="digit"
				@input="inputChange"
				:placeholder="placeholder"
				:disabled="inputDisable"
				v-model="myValue"
				@focus="onFocus"
				@blur="onBlur"
				class="count-input flex-1"
				:style="[inputStyle]"
			/>
			<!-- #endif -->
			<view v-if="showStepButton" class="count-add count-pub" :style="[stepAddStyle]" @tap.stop="add" @longpress="longpressAdd" @touchend="handletouchend">
				<slot name="right">
					+
				</slot>
			</view>
			<view :ref="elId" :id="elId" v-if="isShowUnit" class="px-2 unit count-pub text-center" :style="unitStyle">{{ unit }}</view>
		</view>
	</view>
</template>

<script>
export default {
	name: 'yl-input-number',
	props: {
		// 计数器中的值
		value: {
			type: [Number, String],
			default: () => ''
		},
		// 输入框提示文字
		placeholder: {
			type: String,
			default: ''
		},
		// 输入框字体颜色
		inputColor: {
			type: String,
			default: 'inherit'
		},
		// 输入框背景颜色
		inputBg: {
			type: String,
			default: ''
		},
		// 字体大小
		fontSize: {
			type: String,
			default: 'inherit'
		},
		// 边框颜色
		borderColor: {
			type: String,
			default: '#e4e4e4'
		},
		// 边框圆角
		radius: {
			type: String | Number,
			default: '10rpx'
		},
		// 是否禁用
		disabled: {
			type: Boolean,
			default: false
		},
		// 是否是小数
		isDecimal: {
			type: Boolean,
			default: false
		},
		// 小数位数
		decimalNumber: {
			type: Number,
			default: 2
		},
		// 最大值
		max: {
			type: Number,
			default: 10000
		},
		// 最小值
		min: {
			type: Number,
			default: -10000
		},
		// 是否展示+-按钮
		showStepButton: {
			type: Boolean,
			default: false
		},
		// 步进器背景色
		stepBg: {
			type: String,
			default: '#f5f7fa'
		},
		// 步进器字体颜色
		stepColor: {
			type: String,
			default: '#555555'
		},
		// 加减按钮点击计时器
		delayed: {
			type: Number,
			default: 200
		},
		step: {
			type: Number,
			default: 1
		},
		/* 盒子宽度 */
		width: {
			type: String,
			default: '220rpx'
		},
		/* 盒子高度 */
		height: {
			type: String,
			default: '64rpx'
		},
		/* 对齐方式 */
		textAlign: {
			type: String,
			default: ''
		},
		/* 单位 */
		unit: {
			type: String,
			default: ''
		},
		/* 单位背景色 */
		unitBg: {
			type: String,
			default: '#f5f7fa'
		},
		/* 单位字体色 */
		unitColor: {
			type: String,
			default: '#555555'
		},
		/* 是否显示单位 */
		isShowUnit: {
			type: Boolean,
			default: false
		},
	},
	data() {
		const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`;
		return {
			myValue: '', // input绑定值
			status: false, // 是否聚焦
			timer: null, // 计时器
			wrong: false, // 错误
			elId: elId, // 唯一标识
			addPositon:0,// 加号距离右侧的位置
		};
	},
	created() {
		this.myValue = this.value;
	},
	mounted() {
		if (this.isShowUnit) {
			// #ifdef H5
			this.addPositon = this.$refs[this.elId].$el.scrollWidth
			// #endif
		}
	},
	watch: {
		value(val) {
			this.myValue = val;
		}
	},
	computed:{
		// 边框颜色
		customBorderColor () {
			if(this.borderColor == 'transparent') {
				return 'transparent'
			} else {
				return this.status ? this.$getDarkColor(this.borderColor, 0.3) : this.borderColor	
			}
		},
		// 是否禁用
		inputDisable(){
			return this.disable
		},
		// 单位区域style
		unitStyle(){
			return {
				background: this.unitBg,
				color: this.unitColor
			}
		},
		// 输入框样式
		inputStyle() {
			let style = { 'text-align': this.textAlign, color: this.inputColor, background: this.inputBg, fontSize: this.fontSize}
			return {...style}
		},
		// 步进器【减】背景样式和字体
		stepLessStyle(){
			return {
				backgroundColor: this.stepBg,
				color: this.stepColor,
				opacity: this.myValue <= this.min ? 0.4 : 1 
			}
		},
		// 步进器【加】背景样式和字体
		stepAddStyle(){
			return {
				backgroundColor: this.stepBg,
				color: this.stepColor,
				opacity: this.myValue >= this.max ? 0.4 : 1 
			}
		}
	},
	methods: {
        // 添加rpx单位
        addUnitRpx(value) {
	        if(/\d$/.test(value)){
		        return value + 'rpx'
	        } else {
		        return value
    	    }
        },
		onFocus() {
			this.$emit('focus')
			this.status = true;
		},
		/**
		 * 失去焦点
		 */
		onBlur() {
			this.$emit('blur')
			this.status = false
			this.wrong = false
			if (this.myValue !== '' && this.myValue < this.min) {
				this.myValue = '';
				uni.showToast({
					icon: 'none',
					title: `输入范围在${this.min}到${this.max}之间`
				});
				this.$emit('changeVal', this.myValue);
				this.$emit('update:value', this.myValue);
				this.$emit('input', this.myValue)
			}
		},
		/**
		 * 加
		 */
		add() {
			if (this.disabled) {
				return;
			}
			this.addPublick();
		},
		/**
		 * 执行值加
		 */
		addPublick() {
			if (this.myValue >= this.max) {
				this.status = false;
				this.myValue = this.max;
				clearInterval(this.timer);
			} else {
				this.myValue = +this.myValue + this.step;
			}
			this.$emit('update:value', this.myValue);
			this.$emit('input', this.myValue)
			this.handleChange();
		},
		/**
		 * 长按加
		 */
		longpressAdd() {
			if (this.disabled) {
				return;
			}
			this.timer = setInterval(() => {
				this.addPublick();
			}, this.delayed);
		},
		/**
		 * 减
		 */
		less() {
			if (this.disabled) {
				return;
			}
			this.lessPublick();
		},
		/**
		 * 执行减
		 */
		lessPublick() {
			if (this.myValue <= this.min) {
				clearInterval(this.timer);
				this.status = false;
				this.myValue = this.min;
			} else {
				// this.status = true;
				this.myValue = this.myValue - this.step;
			}
			this.$emit('update:value', this.myValue);
			this.$emit('input', this.myValue);
			this.handleChange();
		},
		/**
		 * 长按减
		 */
		longpressLess() {
			if (this.disabled) {
				return;
			}
			this.timer = setInterval(() => {
				this.lessPublick();
			}, this.delayed);
		},

		handletouchend() {
			clearInterval(this.timer);
		},
		/**
		 * 输入
		 * @param {Object} e
		 */
		inputChange(e) {
			let value = e.detail.value;
			let reg = /^(-?\d*(\.\d*)?).*$/;
			value = value.match(reg)[1];
			this.$nextTick(() => {
				this.myValue = value;
			});
			if (value !== '') {
				// 是否位小数
				if (this.isDecimal) {
					//正则表达试,默认两位小数
					let reg = new RegExp(`^-?\\d*(\\.?\\d{0,${this.decimalNumber}})`, 'g');
					value = value.match(reg)[0] || null;
					let flag = false;
					flag = value === '.' || value === '-' || /\.$/.test(value) || /0$/.test(value);
					//重新赋值给input
					this.$nextTick(() => {
						this.myValue = flag ? value : Number(value);
					});
				} else {
					if (/(\d*)\.*$/.test(value)) {
						// 整数
						value = value.match(/^(-?\d*)\.*$/)[1] || null;
					}
					// 新赋值给input
					this.$nextTick(() => {
						this.myValue = value === '-' ? value : +value;
					});
				}
				// 输入超过最大取最大,小于最小取最小
				if (+value > this.max) {
					this.$nextTick(() => {
						this.myValue = +this.max;
					});
				} else if (+value < this.min) {
					this.wrong = true;
					this.$nextTick(() => {
						this.myValue = +this.min
					});
				} else {
					this.wrong = false;
				}
			}
			this.$nextTick(() => {
				this.$emit('update:value', this.myValue);
				this.$emit('input', this.myValue);
				this.$emit('changeVal', this.myValue);
				this.handleChange();
			});
		},

		/**
		 * change事件
		 */
		handleChange() {
			this.$emit('change', this.myValue);
		},
		
		//将hex颜色值str转化成rgb数组
		$HexToRgb (str) {
			let r = /^\#?[0-9a-fA-F]{6}$/;
			//test方法检查在字符串中是否存在一个模式,如果存在则返回true,否则返回false
			if (!r.test(str)) return console.error("输入错误的hex颜色值");
			//replace替换查找的到的字符串
			str = str.replace("#", "");
			//match得到查询数组
			let hxs = str.match(/../g);
			for (let i = 0; i < 3; i++) hxs[i] = parseInt(hxs[i], 16);
			return hxs;
		},
		//将rgb颜色值为a,b,c转化成hex颜色值
		$RgbToHex (a, b, c) {
		    let r = /^\d{1,3}$/;
		    if (!r.test(a) || !r.test(b) || !r.test(c)) return console.error("输入错误的rgb颜色值");
		    let hexs = [a.toString(16), b.toString(16), c.toString(16)];
		    for (let i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = "0" + hexs[i];
		    return "#" + hexs.join("");
		},
		//得到hex颜色值为color的加深颜色值,level为加深的程度,限0-1之间
		$getDarkColor (color, level) {
			color = color.toLowerCase()
		    let r = /^\#?[0-9a-f]{6}$/;
		    if (!r.test(color)) return console.error("输入错误的hex颜色值");
		    let rgbc = this.$HexToRgb(color);
		    //floor 向下取整
		    for (let i = 0; i < 3; i++) rgbc[i] = Math.floor(rgbc[i] * (1 - level));
		    return this.$RgbToHex(rgbc[0], rgbc[1], rgbc[2]);
		}
	}
};
</script>
<style lang="less" scoped>
.flex{
    display: flex;
	flex-direction: row;
}
.align-center{
    align-items: center;
}
.flex-1{
    flex: 1;
}
.gray {
	background: #eef3f9;
	color: #555555;
}
.light {
	background: #f5f7fa;
	color: #c8c7cc;
}
.count-box {
	position: relative;
	height: 100%;
	box-sizing: border-box;
	z-index: 1;
	transition: all 0.3s;
	border-width: 1px;
	border-style: solid;
}
.box-red {
	border: 1px solid #f56c6c;
}
.count-pub {
	min-width: 60rpx;
	line-height: 60rpx;
	height: 100%;
	text-align: center;
	font-size: 20px;
}
.count-less {
	left: 0;
	border-top-left-radius: 4px;
	border-bottom-left-radius: 4px;
}
.count-add {
	right: 0;
	border-top-right-radius: 4px;
	border-bottom-right-radius: 4px;
}
.count-input {
	flex: 1;
	height: 100% !important;
	padding: 6rpx 10rpx;
	border-radius: 5px;
	box-sizing: border-box;
	color: #808080;
	font-size: 26rpx;
	text-align: center;
}
.unit {
	right: 0;
	font-size: 28rpx;
	border-top-right-radius: 4px;
	border-bottom-right-radius: 4px;
}
</style>

使用:

<!-- 基本用法 -->
<yl-input-number :value="value1"></yl-input-number>
<!-- 输入字体颜色 -->
<yl-input-number inputColor="#77fBf9" :value="value1"></yl-input-number>
<!-- 边框颜色 -->
<yl-input-number borderColor="#77fBf9" :value="value1"></yl-input-number>
<!-- 最大、最小值(max:1000、min:-1000) -->
<yl-input-number :max="1000" :min="-1000" :value="value1"></yl-input-number>
<!-- 显示步进器按钮(showStepButton) -->
<yl-input-number :value="value2" showStepButton></yl-input-number>
<!-- 步进器按钮背景色和字体色 -->
<yl-input-number :value="value2" showStepButton stepBg="#7799FF" stepColor="#77FFFF"></yl-input-number>
<!-- 自定义步进器按钮 -->
<yl-input-number :value="value2" showStepButton>
    <template slot="left">
        <!-- 自定义icon(需替换) -->
        <yl-icon color="#7799FF" name="direction-left"></yl-icon>
    </template>
    <template slot="right">
        <yl-icon color="red" name="direction-right"></yl-icon>
    </template>
</yl-input-number>
<!-- 步进器步长(step:10) -->
<yl-input-number :value="value2" showStepButton :step="10"></yl-input-number>
<!-- 显示单位 -->
<yl-input-number isDecimal :value="value3" isShowDw unit="kg"></yl-input-number>
<!-- 对齐方式(left, center, right) -->
<yl-input-number textAlign="left" :value="value2"></yl-input-number>
<yl-input-number textAlign="center" :value="value4"></yl-input-number>
<yl-input-number textAlign="right" :value="value4"></yl-input-number>
<!-- 宽度(width:100%) -->
<yl-input-number width="100%" :value="value2"></yl-input-number>

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐