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

相关推荐
AiMan7 分钟前
先确认能不能一次把Microsofat Copilot所有内容打包一个文件导出
前端
总要学点什么11 分钟前
第97篇:三维数据加载-城市建筑白模自定义着色器——让一条光带沿高度扫上去,快慢却由帧率说了算
前端·gis·webgl·cesium·着色器·空间分析·白模
上海懒图科技37 分钟前
工厂厂区电子地图定位导航:厂区人车精准定位,安全合规双保障
前端·arcgis·vue
西安小哥1 小时前
端侧跑大模型,真正的门槛不在参数量:一次 596 秒到 10 秒的压缩路径复盘
前端·人工智能
陈随易1 小时前
Cloudflare 出钱,Vite 团队挑人,44 个维护者分 30 万美元
前端·后端·程序员
恋猫de小郭2 小时前
Flutter THLC 的 WebView 滚动终于从 70 FPS 提升到 120 FPS
android·前端·flutter
严肃小白兔3 小时前
TanStack Router 更新构建工具链:Node.js 兼容性、路由分割与前端工程实践
前端·node.js·状态模式
IT_陈寒3 小时前
Vue这个奇葩的响应式问题让我加班到凌晨
前端·人工智能·后端
风骏时光牛马3 小时前
功能可用性自动化测试验证
前端
LucianaiB13 小时前
【元宝搜索开发者征文大赛】AI时代,学得慢就不用学?用元宝搜索打造一个教程保质期,确保每一个教程符合你的现状
前端·人工智能·ai编程