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 参数切换
  • 中文输入法兼容,始终是标配
相关推荐
张元清2 小时前
不用 Server Components 也能做 React 流式 SSR —— 实战指南
前端·javascript·面试
苏瞳儿3 小时前
前端/后端-配置跨域
前端·javascript·node.js·vue
竹林8183 小时前
从轮询到订阅:我在 React 项目中实现实时监听 ERC-20 转账事件的完整踩坑记录
前端·javascript
视觉CG3 小时前
【tailwindcss】网页标题样式
javascript·ecmascript·tailwindcss
之歆3 小时前
Vue Router 深度解析 — 从浏览器导航模型到 SPA 路由工程
前端·javascript·vue.js
guojb8243 小时前
Vue3 高阶技巧:使用 AST 将 HTML 字符串优雅渲染为自定义组件
前端·javascript·vue.js
条tiao条3 小时前
TypeScript 网络编程从零到一:net 模块全解析(入门专属)
javascript·网络·typescript
程序员库里3 小时前
第 3 章:Tiptap 与 React 集成
前端·javascript·面试
Moment3 小时前
AI全栈入门指南:使用 NestJs 创建第一个后端项目
前端·javascript·后端