前言:
????????uniapp-vue3來實現一個金額千分位展示效果
實現效果:
實現目標:
1、封裝組件,組件內部要實現,
- input輸入金額后,聚焦離開后,金額以千分位效果展示,
- 聚焦后展示大寫金額的彈框
- 隨時寫的內容,可以用v-model傳輸給父級
- 金額要有最大值,還要只能輸入數字,小數后只能有2位
2、實現方法:
1)input框輸入金額,并添加聚焦,離開事件
頁面上添加內容:
<inputv-model="displayValue"@input="handleInput"@blur="handleBlur"@focus="handleFocus"placeholder="請輸入金額"
/>
js中增加配置
const displayValue = ref('')
const rawValue = ref(0)const handleInput = (e) => {let val = e.detail.value
}// 處理失去焦點
const handleBlur = () => {
}// 處理獲取焦點
const handleFocus = () => {
}
2)輸入的內容,處理非數字內容,還有只能有一個小數點,小數點后添加2位限制,最大值限制
const displayValue = ref('') //千分位處理后的字段 const rawValue = ref(0) //拿到的實際內容數據 const maxFloatNum = ref(2) //限制小數點后幾位 const maxNum = ref(100000000000) //設置最大值
const handleInput = (e) => {let val = e.detail.valueif(!val) return ''let value = val.toString().replace(/[^\d.]/g, '')if (value.length > 1 && value.startsWith('0')) {value = value.substring(1)}const min = 0const max = maxNum.value// 確保只有一個點const parts = value.split('.')if (parts.length > 2) {value = parts[0] + '.' + parts.slice(1).join('')}// 限制小數點后兩位if (parts.length === maxFloatNum.value) {value = parts[0] + '.' + parts[1].slice(0, maxFloatNum.value)}// 轉換為數字const numValue = parseFloat(value || 0)// 檢查范圍if (numValue > max) {value = max.toString()} else if (numValue < min) {value = min.toString()}rawValue.value = numValuedisplayValue.value = value}
3)聚焦和離開時候將數據轉換位千分位,通過正則
// 格式化顯示值(添加千分位)const formatDisplay = (value) => {const num = parseFloat(value || 0)return num.toFixed(maxFloatNum.value).replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
// 處理失去焦點
const handleBlur = () => {displayValue.value = formatDisplay(rawValue.value)
}// 處理獲取焦點
const handleFocus = () => {displayValue.value = rawValue.value.toString()
}
4)來寫一個大小寫轉換的功能,并展示到界面input上面,配合樣式
js中封裝小寫轉大寫方法
//封裝的小寫轉大寫方法,適合各種場景const amountToChinese = (num)=> {const cnNums = ['零', '壹', '貳', '叁', '肆', '伍', '陸', '柒', '捌', '玖']const cnIntRadice = ['', '拾', '佰', '仟']const cnIntUnits = ['', '萬', '億', '兆']const cnDecUnits = ['角', '分', '毫', '厘']const cnInteger = '整'const cnIntLast = '元'if(num > maxNum.value){return '-'}// 處理負數let sign = '';if (num < 0) {sign = '負';num = Math.abs(num);}// 分離整數和小數部分let numStr = num.toString();let integerStr = '';let decimalStr = '';if (numStr.indexOf('.') !== -1) {const parts = numStr.split('.');integerStr = parts[0];decimalStr = parts[1].substring(0, 4); // 最多支持4位小數} else {integerStr = numStr;}// 處理整數部分let chineseInteger = '';if (parseInt(integerStr, 10) > 0) {let zeroCount = 0;const intLen = integerStr.length;for (let i = 0; i < intLen; i++) {const n = integerStr.charAt(i);const p = intLen - i - 1;const q = p / 4;const m = p % 4;if (n === '0') {zeroCount++;} else {if (zeroCount > 0) {chineseInteger += cnNums[0];}zeroCount = 0;chineseInteger += cnNums[parseInt(n)] + cnIntRadice[m];}if (m === 0 && zeroCount < 4) {chineseInteger += cnIntUnits[q];}}chineseInteger += cnIntLast;}// 處理小數部分let chineseDecimal = '';if (decimalStr) {for (let i = 0; i < decimalStr.length; i++) {const n = decimalStr.charAt(i);if (n !== '0') {chineseDecimal += cnNums[parseInt(n)] + cnDecUnits[i];}}}// 組合結果let result = sign + chineseInteger + chineseDecimal;if (!chineseInteger && !chineseDecimal) {result = cnNums[0] + cnIntLast + cnInteger;} else if (!chineseDecimal) {result += cnInteger;}return result;}
樣式中通過定位,來實現input上面內容的展示
<style lang="scss" scoped>.moneyInputBox{position: relative;input{width: 100%;padding: 0 10rpx !important;box-sizing: border-box !important;border: 1rpx solid #dadbde;height: 62rpx !important;line-height: 42rpx!important;border-radius: 4px;}.bigNumBox{position: absolute;bottom: 76rpx;width:auto;background: rgba(0,0,0,.5);padding:10rpx;color:#fff;border-radius: 20rpx;border:1rpx solid #dadbde;}}
</style>
5)將我們當前的組件,用watch監聽+emit發送的方法,實現數據的雙向綁定,可以在父級用v-model來綁定內容
js具體配置:
<script setup>import { ref, watch, defineProps, defineEmits } from 'vue'const props = defineProps({modelValue: {type: [Number, String],default: 0}})const emit = defineEmits(['update:modelValue'])// 監聽外部傳入的modelValue變化watch(() => props.modelValue, (newVal) => {if (newVal !== rawValue.value) {rawValue.value = parseFloat(newVal) || 0displayValue.value = formatDisplay(rawValue.value)bigNumCont.value = amountToChinese(rawValue.value)}}, { immediate: true }) </script>
父級調用:
<moneyInput v-model="moneyNum"></moneyInput><script setup>const moneyNum = ref(100)
封裝代碼源碼? moneyInput.vue
<template><view class="moneyInputBox"><inputv-model="displayValue"@input="handleInput"@blur="handleBlur"@focus="handleFocus"placeholder="請輸入金額"/><view v-if="showBigNum" class="bigNumBox">{{bigNumCont}}</view></view>
</template><script setup>import { ref, watch, defineProps, defineEmits } from 'vue'const props = defineProps({modelValue: {type: [Number, String],default: 0}})const emit = defineEmits(['update:modelValue'])const displayValue = ref('')const rawValue = ref(0)const maxFloatNum = ref(2)const maxNum = ref(100000000000)const showBigNum = ref(false)const bigNumCont = ref('')// 格式化顯示值(添加千分位)const formatDisplay = (value) => {const num = parseFloat(value || 0)return num.toFixed(maxFloatNum.value).replace(/\B(?=(\d{3})+(?!\d))/g, ',')}// 處理輸入變化const handleInput = (e) => {let val = e.detail.valueif(!val) return ''let value = val.toString().replace(/[^\d.]/g, '')if (value.length > 1 && value.startsWith('0')) {value = value.substring(1)}const min = 0const max = maxNum.value// 確保只有一個點const parts = value.split('.')if (parts.length > 2) {value = parts[0] + '.' + parts.slice(1).join('')}// 限制小數點后兩位if (parts.length === maxFloatNum.value) {value = parts[0] + '.' + parts[1].slice(0, maxFloatNum.value)}// 轉換為數字const numValue = parseFloat(value || 0)// 檢查范圍if (numValue > max) {value = max.toString()} else if (numValue < min) {value = min.toString()}rawValue.value = numValuedisplayValue.value = valuebigNumCont.value = amountToChinese(rawValue.value)emit('update:modelValue', rawValue.value)}// 處理失去焦點const handleBlur = () => {showBigNum.value = falsedisplayValue.value = formatDisplay(rawValue.value)bigNumCont.value = amountToChinese(rawValue.value)}// 處理獲取焦點const handleFocus = () => {showBigNum.value = truedisplayValue.value = rawValue.value.toString()bigNumCont.value = amountToChinese(rawValue.value)}const amountToChinese = (num)=> {const cnNums = ['零', '壹', '貳', '叁', '肆', '伍', '陸', '柒', '捌', '玖']const cnIntRadice = ['', '拾', '佰', '仟']const cnIntUnits = ['', '萬', '億', '兆']const cnDecUnits = ['角', '分', '毫', '厘']const cnInteger = '整'const cnIntLast = '元'if(num > maxNum.value){return '-'}// 處理負數let sign = '';if (num < 0) {sign = '負';num = Math.abs(num);}// 分離整數和小數部分let numStr = num.toString();let integerStr = '';let decimalStr = '';if (numStr.indexOf('.') !== -1) {const parts = numStr.split('.');integerStr = parts[0];decimalStr = parts[1].substring(0, 4); // 最多支持4位小數} else {integerStr = numStr;}// 處理整數部分let chineseInteger = '';if (parseInt(integerStr, 10) > 0) {let zeroCount = 0;const intLen = integerStr.length;for (let i = 0; i < intLen; i++) {const n = integerStr.charAt(i);const p = intLen - i - 1;const q = p / 4;const m = p % 4;if (n === '0') {zeroCount++;} else {if (zeroCount > 0) {chineseInteger += cnNums[0];}zeroCount = 0;chineseInteger += cnNums[parseInt(n)] + cnIntRadice[m];}if (m === 0 && zeroCount < 4) {chineseInteger += cnIntUnits[q];}}chineseInteger += cnIntLast;}// 處理小數部分let chineseDecimal = '';if (decimalStr) {for (let i = 0; i < decimalStr.length; i++) {const n = decimalStr.charAt(i);if (n !== '0') {chineseDecimal += cnNums[parseInt(n)] + cnDecUnits[i];}}}// 組合結果let result = sign + chineseInteger + chineseDecimal;if (!chineseInteger && !chineseDecimal) {result = cnNums[0] + cnIntLast + cnInteger;} else if (!chineseDecimal) {result += cnInteger;}return result;}// 監聽外部傳入的modelValue變化watch(() => props.modelValue, (newVal) => {if (newVal !== rawValue.value) {rawValue.value = parseFloat(newVal) || 0displayValue.value = formatDisplay(rawValue.value)bigNumCont.value = amountToChinese(rawValue.value)}}, { immediate: true })
</script><style lang="scss" scoped>.moneyInputBox{position: relative;input{width: 100%;padding: 0 10rpx !important;box-sizing: border-box !important;border: 1rpx solid #dadbde;height: 62rpx !important;line-height: 42rpx!important;border-radius: 4px;}.bigNumBox{position: absolute;bottom: 76rpx;width:auto;background: rgba(0,0,0,.5);padding:10rpx;color:#fff;border-radius: 20rpx;border:1rpx solid #dadbde;}}
</style>