数学公式输入框组件实现详解
引言
在开发数学类应用时,我们经常需要让用户输入数学表达式和公式。一个优秀的数学输入框需要具备灵活的位置定位、输入验证、响应式布局等特性。本文将详细介绍我实现的数学输入框组件,它可以应用在数学教学、科学计算等场景中。
组件功能概览
这个数学输入框组件实现了以下核心功能:
- 精确定位能力(支持绝对定位)
- 输入模式切换(数字或文本)
- 双向数据绑定
- 自定义验证规则
- 动态样式配置
- 尺寸控制(小/中/大)
- 预置主题支持(默认/边框/下划线)
- 完整API(聚焦、清空、设值等方法)
代码实现详解
模板部分
<template>
<input
ref="inputRef"
type="text"
v-model="inputValue"
@input="handleInput"
@focus="handleFocus"
@blur="handleBlur"
:class="['math-input', customClass, { 'is-focused': isFocused }]"
:style="inputStyle"
:maxlength="maxLength"
:pattern="pattern"
:placeholder="placeholder"
:disabled="disabled"
v-show="visible" />
</template>
关键点说明:
ref="inputRef"
:用于获取DOM引用v-model
实现双向绑定- 根据焦点状态添加
is-focused
类 - 多种属性绑定实现灵活配置
v-show
控制显示/隐藏而非v-if
,避免布局跳动
组件配置选项
const props = defineProps({
modelValue: String, // 双向绑定值
maxLength: Number, // 最大长度(默认1)
inputMode: { // 输入模式(number/text)
type: String,
default: 'number',
validator: value => ['number', 'text'].includes(value)
},
customClass: String, // 自定义类名
position: Object, // 定位配置
size: Object, // 尺寸配置
styleConfig: Object, // 样式配置
placeholder: String, // 占位文本
disabled: Boolean, // 禁用状态
visible: Boolean, // 可见性
validator: Function // 自定义验证函数
});
这个组件通过多种配置属性实现了高度定制化能力。
响应式计算属性
// 根据输入模式生成正则模式
const pattern = computed(() => {
return props.inputMode === 'number' ? '\d*' : undefined;
});
// 合并所有样式配置
const inputStyle = computed(() => {
return {
position: 'absolute',
outline: 'none',
caretColor: 'black',
...props.position, // 位置配置
...props.size, // 尺寸配置
...props.styleConfig // 样式配置
};
});
这种合并策略允许用户灵活覆盖各种样式属性。
核心输入处理逻辑
function handleInput(event) {
let value = event.target.value;
// 数字模式只允许数字
if (props.inputMode === 'number') {
value = value.replace(/\D/g, '');
}
// 应用长度限制
if (value.length > props.maxLength) {
value = value.slice(0, props.maxLength);
}
// 自定义验证
if (props.validator && typeof props.validator === 'function') {
const validationResult = props.validator(value);
if (validationResult !== true) {
event.target.value = inputValue.value;
return;
}
}
// 更新值并触发事件
inputValue.value = value;
event.target.value = value;
emit('input', value, event);
emit('change', value, event);
}
该函数实现了:
- 输入过滤(数字模式)
- 长度限制
- 自定义验证
- 事件触发
暴露给外部的方法
defineExpose({
focus: () => inputRef.value?.focus(), // 手动聚焦
blur: () => inputRef.value?.blur(), // 手动失焦
clear: () => { // 清空内容
inputValue.value = '';
emit('update:modelValue', '');
},
getValue: () => inputValue.value, // 获取当前值
setValue: value => { // 设置值
inputValue.value = value;
emit('update:modelValue', value);
}
});
这些方法通过defineExpose
暴露,使父组件可以灵活控制输入框。
样式系统设计
.math-input {
position: absolute;
text-align: center;
// 尺寸预设
&.small { width: 0.6rem; height: 0.5rem; font-size: 0.4rem; }
&.medium { width: 0.8rem; height: 0.6rem; font-size: 0.5rem; }
&.large { width: 1rem; height: 0.8rem; font-size: 0.6rem; }
// 主题系统
&.theme-default {
background-color: transparent;
border: transparent;
}
&.theme-bordered {
background-color: white;
border: 1px solid #ccc;
&.is-focused {
border-color: #007bff;
box-shadow: 0 0 0 0.05rem rgba(0, 123, 255, 0.25);
}
}
&.theme-underline {
border-bottom: 1px solid #ccc;
&.is-focused { border-bottom-color: #007bff; }
}
}
样式系统特点:
- 使用rem单位保持比例缩放
- 三种预设尺寸
- 三种主题风格
- 焦点状态特殊样式
- 简洁的透明背景默认主题
使用示例
基础数学输入
<MathInput
v-model="number"
input-mode="number"
position="{ top: '20px', left: '50px' }"
size="{ width: '1.5rem', height: '1.2rem' }"
/>
带验证的复杂公式输入
<MathInput
v-model="formula"
:maxLength="10"
:validator="validateFormula"
position="{ top: '120px', left: '75px' }"
custom-class="medium theme-bordered"
/>
<script>
// 自定义验证函数
const validateFormula = (value) => {
// 只允许字母、数字和基本运算符
return /^[\w\d+-*/=^()., ]+$/.test(value);
};
</script>
在数学公式中使用
<div class="formula-container">
公式:y =
<MathInput
v-model="a"
size="{ width: '0.8rem', height: '0.6rem' }"
custom-class="theme-underline"
/> x +
<MathInput
v-model="b"
size="{ width: '0.8rem', height: '0.6rem' }"
custom-class="theme-underline"
/>
</div>
关键开发技巧分享
-
灵活定位
- 使用position对象配置,支持动态变更位置
- 通过transform居中定位:
transform: translate(-50%, -50%)
-
可扩展样式
- 样式分层配置:位置、尺寸、基础样式分离
- 自定义类名和默认类的合并策略
-
输入验证
- 内置数字模式校验
- 开放validator属性支持自定义验证
- 避免输入非法字符
-
API设计
- 通过defineExpose暴露常用方法
- 支持编程式聚焦/设值
- 清空内容时自动触发更新事件
-
响应式设计
- 深度监听modelValue变化
- 使用computed实现高效的样式合并
适用场景
该组件非常适合以下应用场景:
- 在线数学试卷/答题系统
- 科学计算器的输入界面
- 几何画板的标注输入
- 数学公式编辑器
- 数据可视化中的参数调整
例子:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>简化版数学输入框</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
.math-input {
width: 60px;
height: 40px;
font-size: 20px;
text-align: center;
border: 1px solid #ccc;
border-radius: 4px;
outline: none;
}
.math-input:focus {
border-color: #4285f4;
box-shadow: 0 0 3px rgba(66, 133, 244, 0.5);
}
.demo {
margin: 20px;
padding: 20px;
border: 1px solid #eee;
}
</style>
</head>
<body>
<div id="app">
<h1>简化版数学输入框</h1>
<div class="demo">
<h3>基本用法</h3>
<math-input v-model="number1" mode="number"></math-input>
<p>输入的值: {{ number1 }}</p>
</div>
<div class="demo">
<h3>在公式中使用</h3>
<p>
y = <math-input v-model="a" mode="number" size="small"></math-input>x +
<math-input v-model="b" mode="number" size="small"></math-input>
</p>
<p>计算结果: y = {{ a }}x + {{ b }}</p>
</div>
<div class="demo">
<h3>带验证的输入</h3>
<math-input
v-model="age"
mode="number"
:validator="validateAge"
placeholder="输入年龄"
></math-input>
<p>{{ ageMessage }}</p>
</div>
</div>
<script>
const { createApp, ref } = Vue;
// 简化版数学输入框组件
const MathInput = {
template: `
<input
class="math-input"
:class="size"
type="text"
v-model="inputValue"
@input="handleInput"
:placeholder="placeholder"
/>
`,
props: {
modelValue: String,
mode: {
type: String,
default: 'text',
validator: v => ['text', 'number'].includes(v)
},
size: {
type: String,
default: 'medium',
validator: v => ['small', 'medium', 'large'].includes(v)
},
placeholder: String,
validator: Function
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const inputValue = ref(props.modelValue || '');
function handleInput(event) {
let value = event.target.value;
// 数字模式处理
if (props.mode === 'number') {
value = value.replace(/\D/g, '');
}
// 自定义验证
if (props.validator && !props.validator(value)) {
event.target.value = inputValue.value;
return;
}
inputValue.value = value;
emit('update:modelValue', value);
}
return { inputValue, handleInput };
}
};
// 主应用
createApp({
components: { MathInput },
setup() {
const number1 = ref('3');
const a = ref('2');
const b = ref('5');
const age = ref('');
const ageMessage = ref('');
const validateAge = (value) => {
if (!value) {
ageMessage.value = '请输入年龄';
return true;
}
const num = parseInt(value);
if (isNaN(num) || num < 1 || num > 120) {
ageMessage.value = '请输入1-120之间的有效年龄';
return false;
}
ageMessage.value = '年龄有效';
return true;
};
return { number1, a, b, age, ageMessage, validateAge };
}
}).mount('#app');
</script>
</body>
</html>