Commit 96b7e634b5bdcc32cd7aee0d8a489a02414b2293

Authored by Aaron.Liu
1 parent b288b114
Exists in master

[新增] 金融数字

examples/router/routes.js
@@ -76,6 +76,12 @@ const _components = [ @@ -76,6 +76,12 @@ const _components = [
76 meta: { title: 'Tag 标签' }, 76 meta: { title: 'Tag 标签' },
77 component: () => import('@/views/docs/component/tag.md'), 77 component: () => import('@/views/docs/component/tag.md'),
78 }, 78 },
  79 + {
  80 + path: 'amount',
  81 + name: 'amount',
  82 + meta: { title: 'Amount 金融数字' },
  83 + component: () => import('@/views/docs/component/amount.md'),
  84 + },
79 ] 85 ]
80 }, 86 },
81 { 87 {
examples/views/docs/component/amount.md 0 → 100644
@@ -0,0 +1,136 @@ @@ -0,0 +1,136 @@
  1 +# Amount 金融数字
  2 +
  3 +金融数字,一般用于金额,数量等。参考 <a href="https://didi.github.io/mand-mobile/#/zh-CN/docs/components/business/amount" target="_blank">Mand Mobile - Amount</a>
  4 +
  5 +## 基础用法
  6 +
  7 +::: snippet 设置`value`为基本数值,数值必须是`Number`类型
  8 +
  9 +```html
  10 +<template>
  11 + <div class="amount-demo">
  12 + <p><zui-amount :value="1234"></zui-amount></p>
  13 + </div>
  14 +</template>
  15 +
  16 +<style>
  17 +.amount-demo p {
  18 + font-size: 24px;
  19 +}
  20 +</style>
  21 +```
  22 +
  23 +:::
  24 +
  25 +## 千分位分隔符
  26 +
  27 +::: snippet 设置`has-separator`显示分隔符
  28 +```html
  29 +<template>
  30 + <div class="amount-demo">
  31 + <p><zui-amount :value="1234" has-separator ></zui-amount></p>
  32 + <p><zui-amount :value="-123456.123" has-separator></zui-amount></p>
  33 + </div>
  34 +</template>
  35 +
  36 +<style>
  37 +.amount-demo p {
  38 + font-size: 24px;
  39 +}
  40 +</style>
  41 +```
  42 +
  43 +:::
  44 +
  45 +## 变化动效
  46 +
  47 +::: snippet 设置`transition`使用动效,设置`duration`改变数字变化动画时长
  48 +```html
  49 +<template>
  50 + <div class="amount-demo">
  51 + <p><zui-amount :value="val" :duration="800" transition></zui-amount></p>
  52 + </div>
  53 +</template>
  54 +
  55 +<script>
  56 +export default {
  57 + data() {
  58 + return {
  59 + val: 1000,
  60 + };
  61 + },
  62 + mounted() {
  63 + setTimeout(() => {
  64 + this.val = 1500;
  65 + setTimeout(() => {
  66 + this.val = 500;
  67 + }, 2000);
  68 + }, 2000);
  69 + },
  70 +}
  71 +</script>
  72 +
  73 +
  74 +<style>
  75 +.amount-demo p {
  76 + font-size: 24px;
  77 +}
  78 +</style>
  79 +```
  80 +
  81 +:::
  82 +
  83 +## 大写中文
  84 +
  85 +::: snippet 设置`is-capital`显示大写中文
  86 +```html
  87 +<template>
  88 + <div class="amount-demo">
  89 + <p><zui-amount :value="1234" is-capital></zui-amount></p>
  90 + </div>
  91 +</template>
  92 +
  93 +<style>
  94 +.amount-demo p {
  95 + font-size: 24px;
  96 +}
  97 +</style>
  98 +```
  99 +
  100 +:::
  101 +
  102 +## 前缀
  103 +
  104 +::: snippet 设置`prefix`显示前缀,也可以通过`prefix 插槽`自定义前缀
  105 +```html
  106 +<template>
  107 + <div class="amount-demo">
  108 + <p><zui-amount :value="1234" :precision="2" prefix="¥"></zui-amount></p>
  109 + <p><zui-amount :value="1234" :precision="3"><span slot="prefix" style="font-size: 12px">RMB</span></zui-amount></p>
  110 + </div>
  111 +</template>
  112 +
  113 +<style>
  114 +.amount-demo p {
  115 + font-size: 24px;
  116 +}
  117 +</style>
  118 +```
  119 +
  120 +:::
  121 +
  122 +## API
  123 +
  124 +## Attribute 属性
  125 +
  126 +参数|说明|类型|默认值|备注
  127 +-|-|-|-|-
  128 +value|数值|Number|0|-
  129 +precision|数字精度|Number|2|小数点后保留几位
  130 +is-round-up|数字精度取舍是否四舍五入|Boolean|true|-
  131 +has-separator|数字是否有千位分隔符|Boolean|false|-
  132 +separator|数字千位分隔符|String|,|-
  133 +is-capital|数字是否转换为大写中文|Boolean|false|-
  134 +transition|数字变化是否使用动画|Boolean|false|-
  135 +duration|数字变化动画时长|Number|1000|单位ms
  136 +prefix|前缀|String|-|-
0 \ No newline at end of file 137 \ No newline at end of file
packages/amount/index.css 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +.zui-amount.numerical {
  2 + font-family: DINPro-Medium, DIN Alternate, "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
  3 +}
0 \ No newline at end of file 4 \ No newline at end of file
packages/amount/index.vue 0 → 100644
@@ -0,0 +1,400 @@ @@ -0,0 +1,400 @@
  1 +<template>
  2 + <span class="zui-amount" :class="{numerical: !isCapital}">
  3 + <slot v-if="$slots.prefix" name="prefix"></slot>
  4 + <template v-else-if="prefix">{{ prefix }}</template>
  5 + <template v-if="!isCapital">{{ formatValue | doPrecision(legalPrecision, isRoundUp) | doFormat(hasSeparator, separator) }}</template>
  6 + <template v-else> {{ formatValue | doPrecision(4, isRoundUp) | doCapital }} </template>
  7 + </span>
  8 +</template>
  9 +
  10 +<script>
  11 +var noop = function() {};
  12 +var inBrowser = true;
  13 +var root = window;
  14 +var easeOutCubic = function(pos) {
  15 + return Math.pow(pos - 1, 3) + 1
  16 +}
  17 +var easeInOutCubic = function(pos) {
  18 + if ((pos /= 0.5) < 1) {
  19 + return 0.5 * Math.pow(pos, 3);
  20 + }
  21 + return 0.5 * (Math.pow(pos - 2, 3) + 2);
  22 +}
  23 +/* istanbul ignore file */
  24 +var Animate = (function(global) {
  25 + /* istanbul ignore next */
  26 + var time =
  27 + Date.now ||
  28 + (function() {
  29 + return +new Date()
  30 + })
  31 + var desiredFrames = 60
  32 + var millisecondsPerSecond = 1000
  33 +
  34 + var running = {}
  35 + var counter = 1
  36 +
  37 + return {
  38 + requestAnimationFrame: (function() {
  39 + var requestFrame =
  40 + global.requestAnimationFrame ||
  41 + global.webkitRequestAnimationFrame ||
  42 + global.mozRequestAnimationFrame ||
  43 + global.oRequestAnimationFrame;
  44 + var isNative = !!requestFrame;
  45 + var regPass = /requestAnimationFrame\(\)\s*\{\s*\[native code\]\s*\}/i.test(requestFrame.toString());
  46 + if (requestFrame && !regPass) {
  47 + isNative = false;
  48 + }
  49 + if (isNative) {
  50 + return function(callback, root) {
  51 + requestFrame(callback, root);
  52 + }
  53 + }
  54 + var TARGET_FPS = 60;
  55 + var requests = {};
  56 + var requestCount = 0;
  57 + var rafHandle = 1;
  58 + var intervalHandle = null;
  59 + var lastActive = +new Date();
  60 + return function(callback) {
  61 + var callbackHandle = rafHandle++;
  62 + requests[callbackHandle] = callback;
  63 + requestCount++;
  64 + if (intervalHandle == null) {
  65 + intervalHandle = setInterval(function() {
  66 + var time = +new Date();
  67 + var currentRequests = requests;
  68 + requests = {};
  69 + requestCount = 0;
  70 + for (var key in currentRequests) {
  71 + if (currentRequests.hasOwnProperty(key)) {
  72 + currentRequests[key](time);
  73 + lastActive = time;
  74 + }
  75 + }
  76 + if (time - lastActive > 2500) {
  77 + clearInterval(intervalHandle);
  78 + intervalHandle = null;
  79 + }
  80 + }, 1000 / TARGET_FPS);
  81 + }
  82 + return callbackHandle;
  83 + }
  84 + })(),
  85 + stop: function(id) {
  86 + var cleared = running[id] != null;
  87 + cleared && (running[id] = null);
  88 + return cleared;
  89 + },
  90 + isRunning: function(id) {
  91 + return running[id] != null;
  92 + },
  93 + start: function(stepCallback, verifyCallback, completedCallback, duration, easingMethod, root) {
  94 + var start = time();
  95 + var lastFrame = start;
  96 + var percent = 0;
  97 + var dropCounter = 0;
  98 + var id = counter++;
  99 + if (!root) {
  100 + root = document.body;
  101 + }
  102 + if (id % 20 == 0) {
  103 + var newRunning = {};
  104 + for (var usedId in running) {
  105 + newRunning[usedId] = true;
  106 + }
  107 + running = newRunning;
  108 + }
  109 + var self = this;
  110 + var step = function(virtual) {
  111 + var render = virtual !== true;
  112 + var now = time();
  113 + if (!running[id] || (verifyCallback && !verifyCallback(id))) {
  114 + running[id] = null;
  115 + completedCallback &&
  116 + completedCallback(desiredFrames - dropCounter / ((now - start) / millisecondsPerSecond), id, false);
  117 + return;
  118 + }
  119 + if (render) {
  120 + var droppedFrames = Math.round((now - lastFrame) / (millisecondsPerSecond / desiredFrames)) - 1;
  121 + for (var j = 0; j < Math.min(droppedFrames, 4); j++) {
  122 + step(true);
  123 + dropCounter++;
  124 + }
  125 + }
  126 + if (duration) {
  127 + percent = (now - start) / duration;
  128 + if (percent > 1) {
  129 + percent = 1;
  130 + }
  131 + }
  132 + var value = easingMethod ? easingMethod(percent) : percent;
  133 + value = isNaN(value) ? 0 : value;
  134 + if ((stepCallback(value, now, render) == false || percent == 1) && render) {
  135 + running[id] = null;
  136 + completedCallback &&
  137 + completedCallback(
  138 + desiredFrames - dropCounter / ((now - start) / millisecondsPerSecond),
  139 + id,
  140 + percent == 1 || duration == null,
  141 + );
  142 + } else if (render) {
  143 + lastFrame = now;
  144 + self.requestAnimationFrame(step, root);
  145 + }
  146 + }
  147 + running[id] = true;
  148 + this.requestAnimationFrame(step, root);
  149 + return id;
  150 + },
  151 + }
  152 +})(root);
  153 +var formatValueByGapStep = function(step, value, gap = ' ', direction = 'right', range, isAdd = 1, oldValue = '') {
  154 + if (value.length == 0) {
  155 + return {value, range};
  156 + }
  157 + var arr = value && value.split('');
  158 + var _range = range;
  159 + var showValue = '';
  160 + if (direction == 'right') {
  161 + for (var j = arr.length - 1, k = 0; j >= 0; j--, k++) {
  162 + var m = arr[j];
  163 + showValue = k > 0 && k % step == 0 ? m + gap + showValue : m + '' + showValue;
  164 + }
  165 + if (isAdd == 1) {
  166 + // 在添加的情况下,如果添加前字符串的长度减去新的字符串的长度为2,说明多了一个间隔符,需要调整range
  167 + if (oldValue.length - showValue.length == -2) {
  168 + _range = range + 1;
  169 + }
  170 + } else {
  171 + // 在删除情况下,如果删除前字符串的长度减去新的字符串的长度为2,说明少了一个间隔符,需要调整range
  172 + if (oldValue.length - showValue.length == 2) {
  173 + _range = range - 1;
  174 + }
  175 + // 删除到最开始,range 保持 0
  176 + if (_range <= 0) {
  177 + _range = 0;
  178 + }
  179 + }
  180 + } else {
  181 + arr.some(function(n, i) {
  182 + showValue = i > 0 && i % step == 0 ? showValue + gap + n : showValue + '' + n;
  183 + })
  184 + var adapt = range % (step + 1) == 0 ? 1 * isAdd : 0;
  185 + _range = typeof range !== 'undefined' ? (range == 0 ? 0 : range + adapt) : showValue.length;
  186 + }
  187 + return {value: showValue, range: _range};
  188 +}
  189 +var numberCapital = function(number) {
  190 + var cnNums = ['\u96f6', '\u58f9', '\u8d30', '\u53c1', '\u8086', '\u4f0d', '\u9646', '\u67d2', '\u634c', '\u7396'];
  191 + // 拾 \u62fe 佰 \u4f70 仟 \u4edf
  192 + var cnIntRadice = ['', '\u62fe', '\u4f70', '\u4edf'];
  193 + // 万 \u4e07 亿 \u4ebf 兆 \u5146
  194 + var cnIntUnits = ['', '\u4e07', '\u4ebf', '兆'];
  195 + // 角 \u89d2 分 \u5206 毫 \u6beb 厘 \u5398
  196 + var cnDecUnits = ['\u89d2', '\u5206', '\u6beb', '\u5398'];
  197 + var cnInteger = '\u6574'; // 整 \u6574
  198 + var cnIntLast = '\u5143'; // 元 \u5143
  199 + var cnNegative = '\u8d1f'; // 负
  200 + // Maximum number
  201 + var maxNum = 999999999999999.9999;
  202 + var negative;
  203 + // Integral part
  204 + var integerNum;
  205 + // Decimal part
  206 + var decimalNum;
  207 + // Capital number
  208 + var capitalStr = '';
  209 + var parts;
  210 + /* istanbul ignore if */
  211 + if (number == '') {
  212 + return '';
  213 + }
  214 + number = parseFloat(number);
  215 + if (number < 0) {
  216 + negative = true;
  217 + number = Math.abs(number);
  218 + }
  219 + /* istanbul ignore if */
  220 + if (number >= maxNum) {
  221 + return '';
  222 + }
  223 + /* istanbul ignore if */
  224 + if (number == 0) {
  225 + capitalStr = cnNums[0] + cnIntLast + cnInteger;
  226 + return capitalStr;
  227 + }
  228 + // Convert to String
  229 + number += '';
  230 + if (number.indexOf('.') == -1) {
  231 + integerNum = number;
  232 + decimalNum = '';
  233 + } else {
  234 + parts = number.split('.');
  235 + integerNum = parts[0];
  236 + decimalNum = parts[1].substr(0, 4);
  237 + }
  238 + // Convert integer part
  239 + if (parseInt(integerNum, 10) > 0) {
  240 + var zeroCount = 0;
  241 + for (var i = 0, IntLen = integerNum.length; i < IntLen; i++) {
  242 + var n = integerNum.substr(i, 1);
  243 + var p = IntLen - i - 1;
  244 + var q = p / 4;
  245 + var m = p % 4;
  246 + if (n == '0') {
  247 + zeroCount++;
  248 + } else {
  249 + if (zeroCount > 0) {
  250 + capitalStr += cnNums[0];
  251 + }
  252 + zeroCount = 0;
  253 + capitalStr += cnNums[parseInt(n)] + cnIntRadice[m];
  254 + }
  255 + if (m == 0 && zeroCount < 4) {
  256 + capitalStr += cnIntUnits[q];
  257 + }
  258 + }
  259 + capitalStr += cnIntLast;
  260 + }
  261 + // Convert decimal part
  262 + if (decimalNum !== '') {
  263 + for (var i = 0, decLen = decimalNum.length; i < decLen; i++) {
  264 + var n = decimalNum.substr(i, 1);
  265 + if (n !== '0') {
  266 + capitalStr += cnNums[Number(n)] + cnDecUnits[i];
  267 + }
  268 + }
  269 + }
  270 + /* istanbul ignore if */
  271 + if (capitalStr == '') {
  272 + capitalStr += cnNums[0] + cnIntLast + cnInteger;
  273 + } else if (decimalNum == '') {
  274 + capitalStr += cnInteger;
  275 + }
  276 + if (negative) {
  277 + capitalStr = '' + cnNegative + capitalStr;
  278 + }
  279 + return capitalStr;
  280 +};
  281 +export default {
  282 + name: 'Amount',
  283 + filters: {
  284 + doPrecision: function(value, precision, isRoundUp) {
  285 + var exponentialForm = Number(value + 'e' + precision);
  286 + var rounded = isRoundUp ? Math.round(exponentialForm) : Math.floor(exponentialForm);
  287 + return Number(rounded + 'e-' + precision).toFixed(precision);
  288 + },
  289 + doFormat: function(value, hasSeparator, separator) {
  290 + if (!hasSeparator) {
  291 + return value;
  292 + }
  293 + var numberParts = value.split('.');
  294 + var integerValue = numberParts[0];
  295 + var decimalValue = numberParts[1] || '';
  296 + var sign = '';
  297 + if (integerValue.startsWith('-')) {
  298 + integerValue = integerValue.substring(1);
  299 + sign = '-';
  300 + }
  301 + var formateValue = formatValueByGapStep(3, integerValue, separator, 'right', 0, 1);
  302 + return decimalValue ? '' + sign + formateValue.value + '.' + decimalValue : '' + sign + formateValue.value;
  303 + },
  304 + doCapital: function(value) {
  305 + return numberCapital(value);
  306 + },
  307 + },
  308 + props: {
  309 + value: {
  310 + type: Number,
  311 + default: 0,
  312 + },
  313 + precision: {
  314 + type: Number,
  315 + default: 2,
  316 + },
  317 + isRoundUp: {
  318 + type: Boolean,
  319 + default: true,
  320 + },
  321 + hasSeparator: {
  322 + type: Boolean,
  323 + default: false,
  324 + },
  325 + separator: {
  326 + type: String,
  327 + default: ',',
  328 + },
  329 + isAnimated: {
  330 + type: Boolean,
  331 + default: false,
  332 + },
  333 + transition: {
  334 + type: Boolean,
  335 + default: false,
  336 + },
  337 + isCapital: {
  338 + type: Boolean,
  339 + default: false,
  340 + },
  341 + duration: {
  342 + type: Number,
  343 + default: 1000,
  344 + },
  345 + prefix: String,
  346 + },
  347 + data: function() {
  348 + return {
  349 + formatValue: 0,
  350 + isMounted: false,
  351 + }
  352 + },
  353 + watch: {
  354 + value: {
  355 + handler: function(val, oldVal) {
  356 + /* istanbul ignore if */
  357 + if (!inBrowser && !this.isMounted) {
  358 + this.formatValue = val;
  359 + return;
  360 + }
  361 + if (this.isAnimated || this.transition) {
  362 + this.$_doAnimateDisplay(oldVal, val);
  363 + } else {
  364 + this.formatValue = val;
  365 + }
  366 + },
  367 + immediate: true,
  368 + },
  369 + },
  370 + computed: {
  371 + legalPrecision: function() {
  372 + return this.precision > 0 ? this.precision : 0;
  373 + },
  374 + },
  375 + mounted: function() {
  376 + this.isMounted = true;
  377 + },
  378 + methods: {
  379 + // MARK: private methods
  380 + $_doAnimateDisplay: function(fromValue = 0, toValue = 0) {
  381 + /* istanbul ignore next */
  382 + var self = this;
  383 + var step = function(percent) {
  384 + if (percent == 1) {
  385 + self.formatValue = toValue;
  386 + return;
  387 + }
  388 + self.formatValue = fromValue + (toValue - fromValue) * percent;
  389 + }
  390 + /* istanbul ignore next */
  391 + var verify = function(id) { return id };
  392 + Animate.start(step, verify, noop, this.duration);
  393 + },
  394 + },
  395 +}
  396 +</script>
  397 +
  398 +<style>
  399 +@import "./index.css";
  400 +</style>
0 \ No newline at end of file 401 \ No newline at end of file