This commit is contained in:
2025-08-12 15:04:01 +08:00
parent df3f5f5269
commit 6708fb8eeb
544 changed files with 75169 additions and 17 deletions
@@ -0,0 +1,100 @@
export default {
props: {
// 金额数值
value: {
type: Number,
default: ()=> uni.$u.props.amount.value
},
// #ifdef VUE3
modelValue: {
type: Number,
default: ()=> uni.$u.props.amount.value
},
// #endif
// 金融符号
symbol: {
type: String,
default: ()=> uni.$u.props.amount.symbol
},
// 是否置后金额符号位置
reverse: {
type: Boolean,
default: ()=> uni.$u.props.amount.reverse
},
// 数字精度,小数点后保留几位
precision: {
type: Number,
default: ()=> uni.$u.props.amount.precision
},
// 数字精度取舍是否四舍五入
roundUp: {
type: Boolean,
default: ()=> uni.$u.props.amount.roundUp
},
// 数字变化是否使用动画
transition: {
type: Boolean,
default: ()=> uni.$u.props.amount.transition
},
// 数字变化动画时长
duration: {
type: Number,
default: ()=> uni.$u.props.amount.duration
},
// 分隔符位置 (3为千分位,4为万分位)
separatorDigits: {
type: Number,
default: ()=> uni.$u.props.amount.separatorDigits
},
// 分隔符
separator: {
type: String,
default: ()=> uni.$u.props.amount.separator
},
// 是否显示金融符号
showSymbol: {
type: Boolean,
default: ()=> uni.$u.props.amount.showSymbol
},
// 是否显示小数
showDecimal: {
type: Boolean,
default: ()=> uni.$u.props.amount.showDecimal
},
// 是否显示分隔符
showSeparator: {
type: Boolean,
default: ()=> uni.$u.props.amount.showSeparator
},
// 数字是否转换为大写中文
capital: {
type: Boolean,
default: ()=> uni.$u.props.amount.capital
},
// 整数字体大小
fontSize: {
type: String,
default: ()=> uni.$u.props.amount.fontSize
},
// 金融符号小数与整数的比例或字体大小
fontSizeRatio: {
type: [String, Number],
default: ()=> uni.$u.props.amount.fontSizeRatio
},
// 颜色
color: {
type: String,
default: ()=> uni.$u.props.amount.color
},
// 金融符号与小数字体颜色
colorRatio: {
type: String,
default: ()=> uni.$u.props.amount.colorRatio
},
// 自定义样式
customStyle: {
type: Object,
default: ()=> uni.$u.props.amount.customStyle
}
}
}
@@ -0,0 +1,321 @@
<template>
<view class="u-amount" :style="[$u.addStyle(customStyle)]">
<!-- 插槽模式 -->
<slot v-if="$slots.default || $slots.$default" :integer="integerPart" :decimal="decimalPart" :capital="capitalText"></slot>
<!-- 默认显示模式 -->
<template v-else>
<!-- 大写中文模式 -->
<text v-if="capital" class="u-amount__capital" :style="[textStyle]">{{ capitalText }}</text>
<!-- 普通数字模式 -->
<template v-else>
<!-- 前置符号 -->
<text v-if="showSymbol && !reverse" class="u-amount__symbol" :style="[symbolStyle]">{{ symbol }}</text>
<!-- 整数部分 -->
<text class="u-amount__integer" :style="[integerStyle]">{{ formattedInteger }}</text>
<!-- 小数部分 -->
<text v-if="showDecimal && decimalPart" class="u-amount__decimal" :style="[decimalStyle]">.{{ decimalPart }}</text>
<!-- 后置符号 -->
<text v-if="showSymbol && reverse" class="u-amount__symbol" :style="[symbolStyle]">{{ symbol }}</text>
</template>
</template>
</view>
</template>
<script>
import props from './props.js';
import mixin from '../../libs/mixin/mixin'
import mpMixin from '../../libs/mixin/mpMixin';
/**
* amount 金融数字
* @description 用于展示金额、价格等,可以转换为人民币大写,支持动画效果
* @tutorial https://uviewui.com/components/amount.html
*
* @property {Number} v-model/modelValue 金额数值 (默认 0 )
* @property {String} symbol 金融符号 (默认 '¥' )
* @property {Boolean} reverse 是否置后金额符号位置 (默认 false )
* @property {Number} precision 数字精度,小数点后保留几位 (默认 2 )
* @property {Boolean} roundUp 数字精度取舍是否四舍五入 (默认 true )
* @property {Boolean} transition 数字变化是否使用动画 (默认 false )
* @property {Number} duration 数字变化动画时长 (默认 1000 )
* @property {Number} separatorDigits 分隔符位置 (默认 4 )
* @property {String} separator 分隔符 (默认 ',' )
* @property {Boolean} showSymbol 是否显示金融符号 (默认 true )
* @property {Boolean} showDecimal 是否显示小数 (默认 true )
* @property {Boolean} showSeparator 是否显示分隔符 (默认 false )
* @property {Boolean} capital 数字是否转换为大写中文 (默认 false )
* @property {String} fontSize 整数字体大小 (默认 '' )
* @property {String|Number} fontSizeRatio 金融符号小数字体大小 (默认 0.7 )
* @property {String} color 颜色 (默认 '' )
* @property {String} colorRatio 金融符号与小数字体颜色
* @property {Object} customStyle 定义需要用到的外部样式
* @example <u-amount v-model="123456.789" :show-separator="true" />
*/
export default {
name: 'u-amount',
mixins: [mpMixin, mixin, props],
data() {
return {
displayValue: 0,
animationTimer: null
}
},
computed: {
// 格式化后的数值
formattedValue() {
let num = this.displayValue;
if (this.roundUp) {
num = Math.round(num * Math.pow(10, this.precision)) / Math.pow(10, this.precision);
} else {
num = Math.floor(num * Math.pow(10, this.precision)) / Math.pow(10, this.precision);
}
return num.toFixed(this.precision);
},
// 整数部分
integerPart() {
const parts = this.formattedValue.split('.');
return parts[0];
},
// 小数部分
decimalPart() {
const parts = this.formattedValue.split('.');
return parts[1] || '';
},
// 格式化的整数(带分隔符)
formattedInteger() {
if (!this.showSeparator) return this.integerPart;
return this.addSeparator(this.integerPart);
},
// 中文大写
capitalText() {
if (!this.capital) return '';
return this.toChinese(this.displayValue);
},
// 文本通用样式
textStyle() {
const style = {};
if (this.color){
style.color = this.color;
}
if (this.fontSize){
style.fontSize = this.fontSize;
}
return style;
},
// 符号样式
symbolStyle() {
const style = {};
if (this.color){
style.color = this.color;
}
if (this.colorRatio){
style.color = this.colorRatio;
}
if (this.fontSize) {
if (this.fontSizeRatio) {
style.fontSize = this.fontSizeRatio;
} else {
const size = parseFloat(this.fontSize);
const ratio = parseFloat(this.fontSizeRatio);
style.fontSize = uni.$u.addUnit(size * ratio);
}
}
return style;
},
// 整数样式
integerStyle() {
const style = {};
if (this.color){
style.color = this.color;
}
if (this.fontSize){
style.fontSize = this.fontSize;
}
return style;
},
// 小数样式
decimalStyle() {
const style = {};
if (this.color){
style.color = this.color;
}
if (this.colorRatio){
style.color = this.colorRatio;
}
if (this.fontSize) {
if (this.fontSizeRatio) {
style.fontSize = this.fontSizeRatio;
} else {
const size = parseFloat(this.fontSize);
const ratio = parseFloat(this.fontSizeRatio);
style.fontSize = uni.$u.addUnit((size * ratio));
}
}
return style;
}
},
watch: {
value: {
handler(newVal) {
if(!newVal) return;
if (this.transition) {
this.startAnimation(newVal);
} else {
this.displayValue = newVal;
}
},
immediate: true
},
// #ifdef VUE3
modelValue: {
handler(newVal) {
if(!newVal) return;
if (this.transition) {
this.startAnimation(newVal);
} else {
this.displayValue = newVal;
}
},
immediate: true
}
// #endif
},
methods: {
// 添加分隔符
addSeparator(num) {
const str = num.toString();
const len = str.length;
let result = '';
for (let i = 0; i < len; i++) {
if (i > 0 && (len - i) % this.separatorDigits === 0) {
result += this.separator;
}
result += str[i];
}
return result;
},
// 启动动画
startAnimation(targetValue) {
if (this.animationTimer) {
clearInterval(this.animationTimer);
}
const startValue = this.displayValue;
const diff = targetValue - startValue;
const stepTime = 16; // 60fps
const steps = this.duration / stepTime;
const stepValue = diff / steps;
let currentStep = 0;
this.animationTimer = setInterval(() => {
currentStep++;
if (currentStep >= steps) {
this.displayValue = targetValue;
clearInterval(this.animationTimer);
this.animationTimer = null;
} else {
this.displayValue = startValue + (stepValue * currentStep);
}
}, stepTime);
},
// 转换为中文大写
toChinese(amount) {
// 小数单位:角、分、毫、厘
const fractionUnits = ['角', '分', '毫', '厘'];
// 数字对应的中文大写
const chineseDigits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
// 整数单位
const integerUnits = [
['元', '万', '亿'],
['', '拾', '佰', '仟']
];
// 处理负数
const prefix = amount < 0 ? '欠' : '';
amount = Math.abs(amount);
let result = '';
// 处理小数部分(角、分、毫、厘)
for (let i = 0; i < fractionUnits.length; i++) {
const decimalDigit = Math.floor(amount * Math.pow(10, i + 1)) % 10;
if (decimalDigit > 0) {
result += chineseDigits[decimalDigit] + fractionUnits[i];
}
}
// 如果小数部分为空,则显示"整"
result = result || '整';
// 处理整数部分
let integerPart = Math.floor(amount);
for (let i = 0; i < integerUnits[0].length && integerPart > 0; i++) {
let sectionResult = '';
for (let j = 0; j < integerUnits[1].length && integerPart > 0; j++) {
sectionResult = chineseDigits[integerPart % 10] + integerUnits[1][j] + sectionResult;
integerPart = Math.floor(integerPart / 10);
}
result = sectionResult.replace(/(零.)*零$/, '').replace(/^$/, '零') + integerUnits[0][i] + result;
}
// 处理特殊情况和格式化
return prefix + result.replace(/(零.)*零元/, '元')
.replace(/(零.)+/g, '零')
.replace(/^整$/, '零元整')
.replace(/零角零分零毫零厘/, '整')
.replace(/零角零分零毫/, '')
.replace(/零角零分/, '')
.replace(/零角/, '');
}
},
// #ifdef VUE2
beforeDestroy() {
// #endif
// #ifdef VUE3
beforeUnmount() {
// #endif
if (this.animationTimer) {
clearInterval(this.animationTimer);
}
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/components.scss";
.u-amount {
@include flex(row);
align-items: baseline;
&__symbol {
font-weight: normal;
}
&__integer {
font-weight: bold;
}
&__decimal {
font-weight: normal;
}
&__capital {
font-weight: normal;
}
}
</style>