123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- <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.$uColor('color');
- }
- if (this.fontSize){
- style.fontSize = this.fontSize;
- }
- return style;
- },
-
- // 符号样式
- symbolStyle() {
- const style = {};
- if (this.color){
- style.color = this.$uColor('color');
- }
- if (this.colorRatio){
- style.color = this.$uColor('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.$uColor('color');
- }
- if (this.fontSize){
- style.fontSize = this.fontSize;
- }
- return style;
- },
-
- // 小数样式
- decimalStyle() {
- const style = {};
- if (this.color){
- style.color = this.$uColor('color');
- }
- if (this.colorRatio){
- style.color = this.$uColor('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: {
- immediate: true,
- handler(newVal) {
- if(!newVal) return;
- if (this.transition) {
- this.startAnimation(newVal);
- } else {
- this.displayValue = newVal;
- }
- }
- }
- },
- 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>
|