当前位置: 首页 > news >正文

el-input 限制输入数字方法

可以,而且只用样式就能干掉 .is-error 的视觉效果,不影响 el-form 的校验逻辑 👍

但有个前提你要清楚:

👉 el-form 的错误态是加在外层 .el-form-item.is-error 上的
👉 然后通过层级选择器影响内部的 .el-input__wrapper / __inner

所以你要做的不是改 el-input,而是在你这个自定义控件作用域里“截断错误态样式”

✅ 最稳方案(Scoped + :deep 覆盖)

假设你的自定义组件叫 SmartInput

<style scoped>

/* 覆盖 error 状态下的边框 */

:deep(.el-form-item.is-error .el-input__wrapper) {

box-shadow: 0 0 0 1px var(--el-input-border-color) inset !important;

}

/* 覆盖 hover */

:deep(.el-form-item.is-error .el-input__wrapper:hover) {

box-shadow: 0 0 0 1px var(--el-input-border-color) inset !important;

}

/* 覆盖 focus */

:deep(.el-form-item.is-error .el-input__wrapper.is-focus) {

box-shadow: 0 0 0 1px var(--el-input-focus-border-color) inset !important;

}

/* 去掉红色 caret / 文字(如果你看到的话) */

:deep(.el-form-item.is-error .el-input__inner) {

color: inherit;

}

</style>

🧠 关键点解释(你需要知道的本质)

1️⃣ Element Plus 错误态本质

不是直接改 input,而是:

.el-form-item.is-error .el-input__wrapper {

box-shadow: 0 0 0 1px var(--el-color-danger) inset;

}

👉 所以你只要把这个 danger 替换回正常颜色 就行

2️⃣ 为什么必须用

:deep

因为你这个组件是:

👉 自定义组件 → 里面嵌 el-input
👉 .is-error 在父组件 el-form-item 上

scoped 默认穿不透

3️⃣ 为什么用 box-shadow 覆盖

Element Plus v2.x:

👉 边框是用 box-shadow 画的,不是 border

⚠️ 注意一个坑

如果你这样写:

.el-input__wrapper {

border: 1px solid #ccc;

}

👉 ❌ 没用,因为:

真实边框还是 box-shadow

error 状态优先级更高

🚀 更干净的方案(推荐)

如果你这个控件是“特例”,可以加一个 class 限定:

<el-form-item class="no-error-style">

然后:

:deep(.no-error-style.is-error .el-input__wrapper) {

box-shadow: 0 0 0 1px var(--el-input-border-color) inset !important;

}

<template>

<el-input

:model-value="value"

placeholder="请输入数字"

@update:model-value="handleInput"

@blur="handleBlur"

/>

</template>

<script setup lang="ts">

import { ref } from 'vue'

const value = ref('')

const handleInput = (val: string) => {

let newVal = val

// 1. 只允许数字和小数点

newVal = newVal.replace(/[^\d.]/g, '')

// 2. .5 => 0.5

if (newVal.startsWith('.')) {

newVal = '0' + newVal

}

// 3. 只保留第一个小数点

newVal = newVal.replace('.', '#')

newVal = newVal.replace(/\./g, '')

newVal = newVal.replace('#', '.')

// 4. 只允许小数点后两位

newVal = newVal.replace(/^(\d+)\.(\d{0,2}).*$/, '$1.$2')

// 5. 最大不超过 100

// 允许中间态:比如 12.

if (newVal !== '' && newVal !== '0.' && !newVal.endsWith('.')) {

const num = Number(newVal)

if (num > 100) {

newVal = '100'

}

}

value.value = newVal

}

const handleBlur = () => {

let newVal = value.value

// blur 后不允许最后是小数点

if (newVal.endsWith('.')) {

newVal = newVal.slice(0, -1)

}

// blur 后兜底最大值

if (newVal !== '' && Number(newVal) > 100) {

newVal = '100'

}

value.value = newVal

}

</script>

<template>
<el-input
:model-value="value"
placeholder="请输入数字"
@update:model-value="handleInput"
@blur="handleBlur"
/>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const value = ref('')

const handleInput = (val: string) => {
let newVal = val

// 1. 只允许数字和小数点
newVal = newVal.replace(/[^\d.]/g, '')

// 2. .5 => 0.5
if (newVal.startsWith('.')) {
newVal = '0' + newVal
}

// 3. 只保留第一个小数点
newVal = newVal.replace('.', '#')
newVal = newVal.replace(/\./g, '')
newVal = newVal.replace('#', '.')

// 4. 前导0处理(核心新增)
// 规则:
// - 允许 "0"
// - 允许 "0.xxx"
// - 禁止 "00"、"01"、"00012"
if (newVal.startsWith('0') && newVal.length > 1) {
if (newVal[1] !== '.') {
// 去掉多余前导0,例如 01 -> 1,00012 -> 12
newVal = newVal.replace(/^0+/, '')
}
}

// 5. 只允许小数点后两位
newVal = newVal.replace(/^(\d+)\.(\d{0,2}).*$/, '$1.$2')

// 6. 最大不超过 100(允许中间态 12.)
if (newVal !== '' && newVal !== '0.' && !newVal.endsWith('.')) {
const num = Number(newVal)
if (num > 100) {
newVal = '100'
}
}

value.value = newVal
}

const handleBlur = () => {
let newVal = value.value

// blur 后不允许以 . 结尾
if (newVal.endsWith('.')) {
newVal = newVal.slice(0, -1)
}

// 再做一次前导0兜底(防极端情况)
if (newVal.startsWith('0') && newVal.length > 1 && !newVal.startsWith('0.')) {
newVal = newVal.replace(/^0+/, '')
}

// 最大值兜底
if (newVal !== '' && Number(newVal) > 100) {
newVal = '100'
}

value.value = newVal
}
</script>

type Value = string | number

type Option = {
label: string
value: Value
}

type Operator = '>' | '>=' | '<' | '<=' | '='

type Condition = {
op: Operator
value: Value
}

function checkByRawListOrder(
rawList: Option[],
currentValue: Value,
conditions: Condition | Condition[]
): boolean {
const conditionList = Array.isArray(conditions) ? conditions : [conditions]

// 1️⃣ 构建 indexMap(只做一次)
const indexMap = new Map<Value, number>()
rawList.forEach((item, index) => {
indexMap.set(item.value, index)
})

// 2️⃣ 收集所有需要校验的 value
const allValues = [
currentValue,
...conditionList.map(c => c.value)
]

// 3️⃣ 统一校验:是否全部存在
const notFoundValues = allValues.filter(v => !indexMap.has(v))

if (notFoundValues.length > 0) {
console.error(
'[checkByRawListOrder] 以下 value 不存在于原始数组中:',
notFoundValues,
'\n当前 rawList:',
rawList
)
return false
}

// 4️⃣ 开始计算
const currentIndex = indexMap.get(currentValue)!

return conditionList.every(condition => {
const targetIndex = indexMap.get(condition.value)!

switch (condition.op) {
case '>':
return currentIndex > targetIndex

case '>=':
return currentIndex >= targetIndex

case '<':
return currentIndex < targetIndex

case '<=':
return currentIndex <= targetIndex

case '=':
return currentIndex === targetIndex

default:
return false
}
})
}

/* 核心控制 */
:deep(.auto-height-dialog) {
/* ❗关键1:去掉最大高度限制 */
max-height: none !important;

/* ❗关键2:不允许内部滚动 */
overflow: visible !important;
}

/* body 默认是会限制高度并滚动的,必须干掉 */
:deep(.auto-height-dialog .el-dialog__body) {
max-height: none !important;
overflow: visible !important;
}

/* 外层容器默认会帮你兜底滚动,也要关掉 */
:deep(.el-overlay-dialog) {
overflow: hidden !important;
}

http://www.jsqmd.com/news/722697/

相关文章:

  • AIDEGen工具详解:从Android 10源码里挖出来的IDE自动化神器,到底省了哪些事?
  • ARM架构PMU性能监控单元详解与实践
  • 在虚拟机 VMware 下装完操作系统后安装 vmTools 工具
  • 马斯克说的“第一性原理“是什么?
  • MyTV-Android:如何打造一款极致流畅的电视直播应用终极指南
  • 【第6篇】OneAPI 聚合配置教程:一个窗口管所有模型,团队协作必备
  • 视频扩散模型(VDMs):视觉智能的时空理解新范式
  • Horos:如何用免费开源工具实现专业级医疗影像分析
  • 高熵合金球形粉末怎么存才不氧化?实验室存储实操小技巧
  • 2026年漳州氮氢混合气供应厂家排行及性价比对比 - 优质品牌商家
  • 医疗电子中的单粒子翻转(SEU)现象与FPGA防护策略
  • 如何彻底解决彩虹岛韩服游戏转区乱码问题:Locale Remulator终极指南
  • 别再只用CBC模式了!OpenSSL AES ECB模式实战:从原理到代码,带你快速上手文件加密
  • 【PHP 8.9异步I/O工业落地白皮书】:全球首批23家制造企业实测性能提升317%,你还在用同步阻塞?
  • 手把手教你用华为云ModelArts和HiLens Studio,从零搭建一个口罩检测AI技能
  • 别再死记硬背ADC框图了!用STM32CubeMX配置F103的ADC,5分钟搞定电压采集
  • SQL事务隔离级别详解_隔离级别差异对比
  • Nordic nRF54LM20B无线SoC:集成Axon NPU的边缘AI芯片解析
  • VESTA绘图避坑指南:为什么你的晶体结构图总是不立体?从光照和投影设置找原因
  • Realtek RTL8821CE无线网卡驱动:Linux系统终极安装与配置指南
  • EVERLIGHT亿光 ITR1205ST11A/TR SMD-4 槽型光电开关
  • 共建 GEO 生态:技术 + 渠道 + 服务三位一体模式
  • TypeScript的Mapped Types:基于旧类型创建新类型
  • 从学生成绩管理系统实战:用MySQL的CASE和IF函数玩转数据透视与统计报表
  • 魔音漫创源码解析:架构总览:Electron 30 + React 18 + Zustand,构建桌面级影视生产工具
  • 会议助手选择建议 | 实测筛选的高口碑实用工具推荐
  • 注意力机制进化史:从SENet到Coordinate Attention,你的模型该‘注意’什么?
  • TVA在显示面板制造与检测中的实践与挑战(11)
  • 【C# 13委托内存优化权威指南】:20年微软生态专家实测揭示GC压力降低63%的核心技巧
  • Linux服务器宕机别慌!手把手教你用Kdump抓取内核崩溃现场(CentOS 7/8实战)