代替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

相关推荐
计算机魔术师1 小时前
AMD砸82亿买下李飞飞,这次不卖芯片卖什么?
前端
hzxpaipai2 小时前
杭州企业官网怎么建设?从策划到上线的5步建站方法
运维·服务器·前端·网络
怕浪猫2 小时前
2026年金九银十,我面了22个前端
前端·javascript·面试
IT_陈寒2 小时前
Vue 这个响应式陷阱,我的头发都掉没了
前端·人工智能·后端
FYKJ_20102 小时前
django电影推荐系统55066-计算机课程设计、毕业设计
vue.js·spring boot·python·mysql·typescript·spark·django
前端的日常2 小时前
一个人+AI做情侣食谱小程序,30天纯赚
前端·javascript·后端
qq_316837753 小时前
阿里云百炼 fun-asr-realtime uniapp vue3 调用
javascript·vue.js·uni-app
庄园特聘拆椅狂魔3 小时前
Java 后端转全栈的第一课:从前端项目搭建到技术选
java·开发语言·前端
triumph_passion3 小时前
前端路由策略:Memory、Hash 与 History
前端
李剑一3 小时前
AntDesign老版本没有月份和年份区间选择组件?我封装了一个完美版,附源码!
前端·vue.js