代替vue-currency-input使用原生货币符号

代替vue-currency-input原生货币符号,

vue-currency-input,使用缺点

  1. 小数点输入光标会跳动
  2. 货币符号无法设置输入框右侧
  3. 输入内容无法展示小数点
  4. 兼容 element-plus的el-input功能差

使用环境UI组件库element-plus,直接上代码,

CurrencyInput.vue

vue 复制代码
<template>
  <div class="currency-input">
    <label v-if="label" :for="id" class="label">{{ label }}</label>
    <div class="input-wrapper">
      <el-input :id="id" v-model="inputValue" :placeholder="placeholder" :disabled="disabled" @focus="onFocus"
        @blur="onBlur">
        <template #[slotName]>
          <span class="currency-symbol">{{ currencySymbol }}</span>
        </template>
      </el-input>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, watch, onMounted,  } from 'vue';

// 计算属性返回插槽名
const slotName = computed(() => props.appendMode);

interface Props {
  modelValue?: number | null;
  label?: string;
  placeholder?: string;
  disabled?: boolean;
  id?: string;
  currency?: string;
  locale?: string;
  decimalPlaces?: number;
  min?: number;
  max?: number;
  precision?: number; // 小数点后保留位数
  appendMode?: 'prefix' | 'suffix'; // 追加模式,前缀或后缀
}

const props = withDefaults(defineProps<Props>(), {
  modelValue: null,
  label: '',
  placeholder: '',
  disabled: false,
  id: 'currency-input',
  currency: 'CNY',
  locale: 'zh-CN',
  decimalPlaces: 2,
  min: -Infinity,
  max: Infinity,
  precision: 2,
  appendMode: 'suffix', // 追加模式,默认为后缀
});

const emit = defineEmits(['update:modelValue', 'focus', 'blur']);

// 输入框显示值
const inputValue = ref('');

// 是否聚焦
const isFocused = ref(false);

// 获取货币符号
const currencySymbol = computed(() => {
  try {
    const formatter = new Intl.NumberFormat(props.locale, {
      style: 'currency',
      currency: props.currency,
      minimumFractionDigits: 0,
      maximumFractionDigits: 0
    });
    const parts = formatter.formatToParts(1);
    const symbolPart = parts.find(part => part.type === 'currency');
    return symbolPart ? symbolPart.value : props.currency;
  } catch {
    return props.currency;
  }
});

// 数字转为千分位格式
const toFormatted = (num: number | null): string => {
  if (num === null || isNaN(num)) return '';
  return new Intl.NumberFormat(props.locale, {
    minimumFractionDigits: props.decimalPlaces,
    maximumFractionDigits: props.decimalPlaces
  }).format(num);
};

// 字符串解析为数字
const toNumber = (str: string): number | null => {
  if (!str) return null;
  const clean = str.replace(/[^\d.-]/g, '');
  if (!clean || clean === '-' || clean === '.') return null;
  const num = parseFloat(clean);
  if (isNaN(num)) return null;
  if (num < props.min) return props.min;
  if (num > props.max) return props.max;
  return num;
};

// 监听外部 modelValue 变化
watch(() => props.modelValue, (val) => {
  if (!isFocused.value) {
    inputValue.value = toFormatted(val);
  }
});

// 聚焦事件
const onFocus = (event: FocusEvent) => {
  isFocused.value = true;
  // 聚焦时显示纯数字,方便编辑
  const num = toNumber(inputValue.value);
  inputValue.value = num !== null ? num.toString() : '';
  emit('focus', event);
};

// 失焦事件
const onBlur = (event: FocusEvent) => {
  isFocused.value = false;
  const num = toNumber(inputValue.value);
  inputValue.value = toFormatted(num);
  emit('update:modelValue', num);
  emit('blur', event);
};

// 初始化
onMounted(() => {
  if (props.modelValue !== null && props.modelValue !== undefined) {
    inputValue.value = toFormatted(props.modelValue);
  }
});
</script>

<style scoped>
.currency-input {
  display: flex;
  flex-direction: column;
  margin-bottom: 12px;
}

.label {
  margin-bottom: 4px;
  font-weight: 500;
  color: #333;
}

.input-wrapper {
  position: relative;
}

.currency-symbol {
  color: #999;
  font-size: 14px;
}
</style>

index.vue

js 复制代码
<template>
  <div>
    <CurrencyInput v-model="amount" label="金额" placeholder="请输入金额" currency="HKD" locale="zh-CN" :precision="4" append-mode="prefix"
      :min="0" :max="9999999" />
    <p>当前值:{{ amount }}</p>
  </div>
</template>

<script setup>
defineOptions({
  name: 'CurrencyInputIndex'
});

import { ref } from 'vue';
import CurrencyInput from './CurrencyInput.vue';
import CurrencyInput1 from './CurrencyInput1.vue';

const amount = ref(null);

</script>

输入框左侧

输入框右侧

参考文献

API | Vue Currency Input

相关推荐
Moment1 小时前
从多人编辑到 Agent 写文档,Hocuspocus v4 正在改写协同系统 😍😍😍
前端·后端·面试
星环科技2 小时前
数据标准Agent ,让企业数据说同一种语言
java·开发语言·前端
橘子星2 小时前
深入理解 AJAX 中的 JSON 序列化与 JS 异步处理
前端·javascript·后端
旧曲重听12 小时前
2026前端技术从「夯」到「拉」
前端·程序人生·职场和发展·软件工程
Kapaseker2 小时前
我找到了最适合程序员的 PPT 工具 — Slidev
前端
雾削木2 小时前
B语言经典教程现代化重构
java·前端·stm32·单片机·嵌入式硬件
Cobyte2 小时前
20.Vue Vapor 的应用初始化
前端·javascript·vue.js
乘风gg2 小时前
手把手带你实践历时一年总结的 AI Code Review 最佳工作流!
前端·ai编程·cursor