Vue3 全局自动大写转换:一个配置,全站生效

做业务系统时,车牌号、零件号、订单号......各种编号字段都需要大写。后端存大写、前端也显示大写,但用户打字时往往习惯输入小写,每次都要等表单报错才知道改。

与其让用户"试错",不如在前端直接帮用户转大写------体验好,后端压力也小。本文分享一套完整方案:自定义指令 精准控制 + 全局插件一次搞定。


方案一:自定义指令 v-input-uppercase

适合对特定输入框做精细化控制。

创建指令文件

src/directives/inputUppercase.js

js 复制代码
const globalUppercaseConfig = {
  defaultEnabled: true,
  processedInputs: new WeakSet(),
  disabledInputs: new WeakSet()
};

export default {
  mounted(el, binding) {
    const inputElement = el.querySelector('.el-input__inner')
      || el.querySelector('input')
      || el.querySelector('textarea');
    if (!inputElement) return;

    const config = binding.value || {};
    const enabled = config.enabled !== false;

    if (!enabled) {
      globalUppercaseConfig.disabledInputs.add(el);
      return;
    }
    if (globalUppercaseConfig.processedInputs.has(el)) return;
    globalUppercaseConfig.processedInputs.add(el);

    let isComposing = false;

    const toUpperCase = () => {
      if (isComposing || !inputElement.value) return;
      const oldValue = inputElement.value;
      const newValue = oldValue.toUpperCase();
      if (oldValue !== newValue) {
        inputElement.value = newValue;
        inputElement.dispatchEvent(new Event('input', { bubbles: true }));
      }
    };

    if (inputElement.value) toUpperCase();
    inputElement.addEventListener('input', toUpperCase);
    inputElement.addEventListener('paste', () => setTimeout(toUpperCase, 10));
    inputElement.addEventListener('compositionstart', () => { isComposing = true; });
    inputElement.addEventListener('compositionend', () => {
      isComposing = false;
      setTimeout(toUpperCase, 10);
    });

    el._toUpperCase = toUpperCase;
    el._inputElement = inputElement;
  },

  beforeUnmount(el) {
    if (el._inputElement && el._toUpperCase) {
      el._inputElement.removeEventListener('input', el._toUpperCase);
      el._inputElement.removeEventListener('paste', el._toUpperCase);
    }
    globalUppercaseConfig.processedInputs.delete(el);
  }
};

export { globalUppercaseConfig };

注册指令

js 复制代码
// src/main.js
import uppercase from './directives/inputUppercase.js'

app.directive('input-uppercase', uppercase)

使用

vue 复制代码
<!-- 自动大写 -->
<el-input v-model="plateNo" v-input-uppercase placeholder="请输入车牌号" />

<!-- 某些字段不需要转大写 -->
<el-input v-model="name" v-input-uppercase="{ enabled: false }" />

方案二:全局插件(推荐)

一个插件,覆盖所有 el-input / el-textarea / el-select,无需逐个添加指令。

创建插件文件

src/plugins/globalUppercase.js

js 复制代码
import { globalUppercaseConfig } from '../directives/inputUppercase.js';

// 排除某些页面(如登录页不需要大写)
const globalUppercasePluginConfig = {
  excludedPaths: ['/home/login', '/qp/tableModuSet']
};

export const GlobalUppercasePlugin = {
  install(app) {
    app.mixin({
      mounted() {
        this.$nextTick(() => {
          if (!this.$el || typeof this.$el.querySelectorAll !== 'function') return;

          const currentPath = window.location.hash || window.location.pathname;
          if (globalUppercasePluginConfig.excludedPaths.some(p => currentPath.includes(p))) return;

          const applyTo = (el) => {
            if (globalUppercaseConfig.processedInputs.has(el)) return;

            const actualInput = el.querySelector('.el-input__inner')
              || el.querySelector('input')
              || el.querySelector('textarea')
              || (['INPUT', 'TEXTAREA'].includes(el.tagName) ? el : null);
            if (!actualInput) return;

            let isComposing = false;
            const toUpperCase = () => {
              if (isComposing || !actualInput.value) return;
              const newVal = actualInput.value.toUpperCase();
              if (actualInput.value !== newVal) {
                actualInput.value = newVal;
                actualInput.dispatchEvent(new Event('input', { bubbles: true }));
              }
            };

            if (actualInput.value) toUpperCase();
            actualInput.addEventListener('input', toUpperCase);
            actualInput.addEventListener('paste', () => setTimeout(toUpperCase, 10));
            actualInput.addEventListener('compositionstart', () => { isComposing = true; });
            actualInput.addEventListener('compositionend', () => {
              isComposing = false;
              setTimeout(toUpperCase, 10);
            });

            globalUppercaseConfig.processedInputs.add(el);
            el._toUpperCase = toUpperCase;
            el._inputElement = actualInput;
          };

          // 处理 el-input
          this.$el.querySelectorAll('.el-input').forEach(applyTo);
          // 处理 el-textarea
          this.$el.querySelectorAll('.el-textarea').forEach(applyTo);

          // 处理 el-select(搜索模式下的输入框)
          this.$el.querySelectorAll('.el-select').forEach(el => {
            const selectInput = el.querySelector('.el-select__input');
            if (selectInput) applyTo(selectInput);
          });
        });
      },

      beforeUnmount() {
        if (!this.$el) return;
        this.$el.querySelectorAll('.el-input, .el-textarea, .el-select').forEach(el => {
          if (el._inputElement && el._toUpperCase) {
            el._inputElement.removeEventListener('input', el._toUpperCase);
            el._inputElement.removeEventListener('paste', el._toUpperCase);
          }
          globalUppercaseConfig.processedInputs.delete(el);
        });
      }
    });
  }
};

export { globalUppercasePluginConfig };

注册插件

js 复制代码
// src/main.js
import { GlobalUppercasePlugin } from './plugins/globalUppercase.js'

app.use(GlobalUppercasePlugin)

完成! 全站所有输入框自动大写,用户输入 abc123 → 直接显示 ABC123


两种方案对比

自定义指令 全局插件
适用范围 单个输入框 全站
控制粒度 精准 全局 + 路径排除
支持组件 el-input / input / textarea el-input / el-textarea / el-select
注册方式 app.directive() app.use()

和单引号禁止方案的对比

单引号禁止 大写转换
核心操作 replace(/'/g, '') 删除 toUpperCase() 转换
行为 输入时被阻止 输入后自动转换
默认触发时机 input + blur input
特殊处理 中文输入法 中文输入法

两套方案代码结构几乎一致,如果你两个功能都要,完全可以合并成一个插件,通过配置项切换行为。


合并成一套插件

js 复制代码
export const GlobalInputFilterPlugin = {
  install(app, options = {}) {
    const { mode = 'uppercase' } = options; // 'uppercase' | 'noQuote' | 'both'

    app.mixin({
      mounted() {
        this.$nextTick(() => {
          // ...统一的处理逻辑
          const transform = (val) => {
            if (mode === 'uppercase') return val.toUpperCase();
            if (mode === 'noQuote') return val.replace(/'/g, '');
            if (mode === 'both') return val.toUpperCase().replace(/'/g, '');
            return val;
          };
          // ...
        });
      }
    });
  }
};

// 使用
app.use(GlobalInputFilterPlugin, { mode: 'both' })

总结

  • 全局覆盖 → 插件 app.use() 一次搞定
  • 局部控制 → 指令 v-input-uppercase 精准禁用
  • 两个功能都要 → 合并成一个插件,mode 参数切换
  • 中文输入法兼容,始终是标配
相关推荐
zs宝来了21 小时前
微前端架构:qiankun 沙箱隔离与样式冲突
前端·javascript·框架
M ? A1 天前
Vue 的 scoped 样式穿透 React 不支持?用 VuReact 编译就行
前端·javascript·vue.js·react.js·面试·开源·vureact
zs宝来了1 天前
Vue 3 Composition API:响应式系统与依赖追踪
前端·javascript·框架
村上小树1 天前
非常简单地学习一下slate.js的原理
前端·javascript
web打印社区1 天前
[特殊字符] 开源好物:web-print-pdf,让 Web 打印像调用接口一样简单!
前端·javascript·vue.js·electron
大家的林语冰1 天前
TS 登顶第一语言;JS 最新 Temporal 时间减屎;Node 爆发反 AI 运动;CSS 将支持图片亮暗切换《前端周刊》
前端·javascript·css
Hilaku1 天前
OpenClaw 为什么突然不火了?
前端·javascript·程序员
岩岩很哇塞!1 天前
【vue实现模仿探探卡片滑动切换效果】
前端·javascript·vue.js
d111111111d1 天前
UAER问题+修复小bug
前端·javascript·笔记·stm32·单片机·嵌入式硬件·学习
kyriewen111 天前
Next.js:让你的React应用从“裸奔”到“穿衣服”
开发语言·前端·javascript·react.js·设计模式·ecmascript