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

相关推荐
咩咩啃树皮5 分钟前
第43篇:Vue3计算属性(computed)完全精讲——缓存机制、依赖计算、业务最优解
前端·vue.js·缓存
徐小夕2 小时前
花了一周,3亿tokens,我开源了一款 Word 文档智能审查平台,文稿自动质检+可视化分析,告别低效人工审核
前端·算法·github
kyriewen3 小时前
别再乱用useEffect了——你写的10个里有8个不该存在
前端·javascript·react.js
Ivanqhz3 小时前
Rust &‘static str浅析
java·前端·javascript·rust
IT_陈寒4 小时前
SpringBoot这个分页坑,我踩了三天才爬出来
前端·人工智能·后端
颜酱4 小时前
05 | 召回前置准备:根据业务数据库生成各数据库(读取配置阶段)
前端·人工智能·后端
zandy10114 小时前
衡石 Agentic BI的ReAct 推理框架在 Agentic BI 中的工程化实践
前端·javascript·react.js
罗超驿5 小时前
JavaEE进阶之路:从Web架构原理到HTML标签全解析
前端·html·web·javaee
西安小哥5 小时前
从前端到AI工程师:一场跨越鸿沟的真实蜕变之旅
前端
Prince4185 小时前
侧边栏收起缩放适配方案
前端