xianniu-ui 0.8.58 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/xianniu-ui.common.js +1454 -27
- package/lib/xianniu-ui.css +1 -1
- package/lib/xianniu-ui.umd.js +1454 -27
- package/lib/xianniu-ui.umd.min.js +10 -10
- package/package.json +1 -1
- package/packages/amount/animate.js +222 -0
- package/packages/amount/formate-value.js +79 -0
- package/packages/amount/index.js +7 -0
- package/packages/amount/main.vue +164 -0
- package/packages/amount/noop.js +1 -0
- package/packages/amount/number-capital.js +114 -0
- package/src/area/index.js +1 -1
- package/src/index.js +5 -2
package/package.json
CHANGED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
|
|
2
|
+
const root = typeof window !== 'undefined' ? window : global
|
|
3
|
+
|
|
4
|
+
/* istanbul ignore file */
|
|
5
|
+
const Animate = (global => {
|
|
6
|
+
/* istanbul ignore next */
|
|
7
|
+
const time =
|
|
8
|
+
Date.now ||
|
|
9
|
+
(() => {
|
|
10
|
+
return +new Date()
|
|
11
|
+
})
|
|
12
|
+
const desiredFrames = 60
|
|
13
|
+
const millisecondsPerSecond = 1000
|
|
14
|
+
|
|
15
|
+
let running = {}
|
|
16
|
+
let counter = 1
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
/**
|
|
20
|
+
* A requestAnimationFrame wrapper / polyfill.
|
|
21
|
+
*
|
|
22
|
+
* @param callback {Function} The callback to be invoked before the next repaint.
|
|
23
|
+
* @param root {HTMLElement} The root element for the repaint
|
|
24
|
+
*/
|
|
25
|
+
requestAnimationFrame: (() => {
|
|
26
|
+
// Check for request animation Frame support
|
|
27
|
+
const requestFrame =
|
|
28
|
+
global.requestAnimationFrame ||
|
|
29
|
+
global.webkitRequestAnimationFrame ||
|
|
30
|
+
global.mozRequestAnimationFrame ||
|
|
31
|
+
global.oRequestAnimationFrame
|
|
32
|
+
let isNative = !!requestFrame
|
|
33
|
+
|
|
34
|
+
if (requestFrame && !/requestAnimationFrame\(\)\s*\{\s*\[native code\]\s*\}/i.test(requestFrame.toString())) {
|
|
35
|
+
isNative = false
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (isNative) {
|
|
39
|
+
return (callback, root) => {
|
|
40
|
+
requestFrame(callback, root)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const TARGET_FPS = 60
|
|
45
|
+
let requests = {}
|
|
46
|
+
// eslint-disable-next-line no-unused-vars
|
|
47
|
+
let requestCount = 0
|
|
48
|
+
let rafHandle = 1
|
|
49
|
+
let intervalHandle = null
|
|
50
|
+
let lastActive = +new Date()
|
|
51
|
+
|
|
52
|
+
return callback => {
|
|
53
|
+
const callbackHandle = rafHandle++
|
|
54
|
+
|
|
55
|
+
// Store callback
|
|
56
|
+
requests[callbackHandle] = callback
|
|
57
|
+
requestCount++
|
|
58
|
+
|
|
59
|
+
// Create timeout at first request
|
|
60
|
+
if (intervalHandle === null) {
|
|
61
|
+
intervalHandle = setInterval(() => {
|
|
62
|
+
const time = +new Date()
|
|
63
|
+
const currentRequests = requests
|
|
64
|
+
|
|
65
|
+
// Reset data structure before executing callbacks
|
|
66
|
+
requests = {}
|
|
67
|
+
requestCount = 0
|
|
68
|
+
|
|
69
|
+
for (const key in currentRequests) {
|
|
70
|
+
if (Object.prototype.hasOwnProperty.call(currentRequests, key)) {
|
|
71
|
+
currentRequests[key](time)
|
|
72
|
+
lastActive = time
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Disable the timeout when nothing happens for a certain
|
|
77
|
+
// period of time
|
|
78
|
+
if (time - lastActive > 2500) {
|
|
79
|
+
clearInterval(intervalHandle)
|
|
80
|
+
intervalHandle = null
|
|
81
|
+
}
|
|
82
|
+
}, 1000 / TARGET_FPS)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return callbackHandle
|
|
86
|
+
}
|
|
87
|
+
})(),
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Stops the given animation.
|
|
91
|
+
*
|
|
92
|
+
* @param id {Integer} Unique animation ID
|
|
93
|
+
* @return {Boolean} Whether the animation was stopped (aka, was running before)
|
|
94
|
+
*/
|
|
95
|
+
stop(id) {
|
|
96
|
+
const cleared = running[id] != null
|
|
97
|
+
cleared && (running[id] = null)
|
|
98
|
+
return cleared
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Whether the given animation is still running.
|
|
103
|
+
*
|
|
104
|
+
* @param id {Integer} Unique animation ID
|
|
105
|
+
* @return {Boolean} Whether the animation is still running
|
|
106
|
+
*/
|
|
107
|
+
isRunning(id) {
|
|
108
|
+
return running[id] != null
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Start the animation.
|
|
113
|
+
*
|
|
114
|
+
* @param stepCallback {Function} Pointer to function which is executed on every step.
|
|
115
|
+
* Signature of the method should be `function(percent, now, virtual) { return continueWithAnimation; }`
|
|
116
|
+
* @param verifyCallback {Function} Executed before every animation step.
|
|
117
|
+
* Signature of the method should be `function() { return continueWithAnimation; }`
|
|
118
|
+
* @param completedCallback {Function}
|
|
119
|
+
* Signature of the method should be `function(droppedFrames, finishedAnimation) {}`
|
|
120
|
+
* @param duration {Integer} Milliseconds to run the animation
|
|
121
|
+
* @param easingMethod {Function} Pointer to easing function
|
|
122
|
+
* Signature of the method should be `function(percent) { return modifiedValue; }`
|
|
123
|
+
* @param root {Element ? document.body} Render root, when available. Used for internal
|
|
124
|
+
* usage of requestAnimationFrame.
|
|
125
|
+
* @return {Integer} Identifier of animation. Can be used to stop it any time.
|
|
126
|
+
*/
|
|
127
|
+
start(stepCallback, verifyCallback, completedCallback, duration, easingMethod, root) {
|
|
128
|
+
const start = time()
|
|
129
|
+
let lastFrame = start
|
|
130
|
+
let percent = 0
|
|
131
|
+
let dropCounter = 0
|
|
132
|
+
const id = counter++
|
|
133
|
+
|
|
134
|
+
if (!root) {
|
|
135
|
+
root = document.body
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Compacting running db automatically every few new animations
|
|
139
|
+
if (id % 20 === 0) {
|
|
140
|
+
const newRunning = {}
|
|
141
|
+
for (const usedId in running) {
|
|
142
|
+
newRunning[usedId] = true
|
|
143
|
+
}
|
|
144
|
+
running = newRunning
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// This is the internal step method which is called every few milliseconds
|
|
148
|
+
const step = virtual => {
|
|
149
|
+
// Normalize virtual value
|
|
150
|
+
const render = virtual !== true
|
|
151
|
+
|
|
152
|
+
// Get current time
|
|
153
|
+
const now = time()
|
|
154
|
+
|
|
155
|
+
// Verification is executed before next animation step
|
|
156
|
+
if (!running[id] || (verifyCallback && !verifyCallback(id))) {
|
|
157
|
+
running[id] = null
|
|
158
|
+
completedCallback &&
|
|
159
|
+
completedCallback(desiredFrames - dropCounter / ((now - start) / millisecondsPerSecond), id, false)
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// For the current rendering to apply let's update omitted steps in memory.
|
|
164
|
+
// This is important to bring internal state variables up-to-date with progress in time.
|
|
165
|
+
if (render) {
|
|
166
|
+
const droppedFrames = Math.round((now - lastFrame) / (millisecondsPerSecond / desiredFrames)) - 1
|
|
167
|
+
for (let j = 0; j < Math.min(droppedFrames, 4); j++) {
|
|
168
|
+
step(true)
|
|
169
|
+
dropCounter++
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Compute percent value
|
|
174
|
+
if (duration) {
|
|
175
|
+
percent = (now - start) / duration
|
|
176
|
+
if (percent > 1) {
|
|
177
|
+
percent = 1
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Execute step callback, then...
|
|
182
|
+
let value = easingMethod ? easingMethod(percent) : percent
|
|
183
|
+
value = isNaN(value) ? 0 : value
|
|
184
|
+
if ((stepCallback(value, now, render) === false || percent === 1) && render) {
|
|
185
|
+
running[id] = null
|
|
186
|
+
completedCallback &&
|
|
187
|
+
completedCallback(
|
|
188
|
+
desiredFrames - dropCounter / ((now - start) / millisecondsPerSecond),
|
|
189
|
+
id,
|
|
190
|
+
percent === 1 || duration == null,
|
|
191
|
+
)
|
|
192
|
+
} else if (render) {
|
|
193
|
+
lastFrame = now
|
|
194
|
+
this.requestAnimationFrame(step, root)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Mark as running
|
|
199
|
+
running[id] = true
|
|
200
|
+
|
|
201
|
+
// Init first step
|
|
202
|
+
this.requestAnimationFrame(step, root)
|
|
203
|
+
|
|
204
|
+
// Return unique animation ID
|
|
205
|
+
return id
|
|
206
|
+
},
|
|
207
|
+
}
|
|
208
|
+
})(root)
|
|
209
|
+
|
|
210
|
+
export const easeOutCubic = pos => {
|
|
211
|
+
return Math.pow(pos - 1, 3) + 1
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export const easeInOutCubic = pos => {
|
|
215
|
+
if ((pos /= 0.5) < 1) {
|
|
216
|
+
return 0.5 * Math.pow(pos, 3)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return 0.5 * (Math.pow(pos - 2, 3) + 2)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export default Animate
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// 根据间隔规则格式化值
|
|
2
|
+
export function formatValueByGapRule(gapRule, value, gap = ' ', range, isAdd = 1) {
|
|
3
|
+
const arr = value ? value.split('') : [] // 将值转换为字符数组
|
|
4
|
+
let showValue = '' // 格式化后的值
|
|
5
|
+
const rule = [] // 间隔规则数组
|
|
6
|
+
gapRule.split('|').some((n, j) => {
|
|
7
|
+
rule[j] = +n + (rule[j - 1] ? +rule[j - 1] : 0) // 计算间隔位置
|
|
8
|
+
})
|
|
9
|
+
let j = 0
|
|
10
|
+
arr.some((n, i) => {
|
|
11
|
+
// 移除多余部分
|
|
12
|
+
if (i > rule[rule.length - 1] - 1) {
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
if (i > 0 && i === rule[j]) {
|
|
16
|
+
showValue = showValue + gap + n // 添加间隔符
|
|
17
|
+
j++
|
|
18
|
+
} else {
|
|
19
|
+
showValue = showValue + '' + n
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
let adapt = 0
|
|
23
|
+
rule.some((n, j) => {
|
|
24
|
+
if (range === +n + 1 + j) {
|
|
25
|
+
adapt = 1 * isAdd // 调整光标位置
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
range = typeof range !== 'undefined' ? (range === 0 ? 0 : range + adapt) : showValue.length
|
|
29
|
+
return { value: showValue, range: range } // 返回格式化后的值和光标位置
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 根据步长格式化值
|
|
33
|
+
export function formatValueByGapStep(step, value, gap = ' ', direction = 'right', range, isAdd = 1, oldValue = '') {
|
|
34
|
+
if (value.length === 0) {
|
|
35
|
+
return { value, range }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const arr = value && value.split('')
|
|
39
|
+
let _range = range
|
|
40
|
+
let showValue = ''
|
|
41
|
+
|
|
42
|
+
if (direction === 'right') {
|
|
43
|
+
for (let j = arr.length - 1, k = 0; j >= 0; j--, k++) {
|
|
44
|
+
const m = arr[j]
|
|
45
|
+
showValue = k > 0 && k % step === 0 ? m + gap + showValue : m + '' + showValue // 从右向左添加间隔符
|
|
46
|
+
}
|
|
47
|
+
if (isAdd === 1) {
|
|
48
|
+
// 在添加的情况下,如果添加前字符串的长度减去新的字符串的长度为2,说明多了一个间隔符,需要调整range
|
|
49
|
+
if (oldValue.length - showValue.length === -2) {
|
|
50
|
+
_range = range + 1
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
// 在删除情况下,如果删除前字符串的长度减去新的字符串的长度为2,说明少了一个间隔符,需要调整range
|
|
54
|
+
if (oldValue.length - showValue.length === 2) {
|
|
55
|
+
_range = range - 1
|
|
56
|
+
}
|
|
57
|
+
// 删除到最开始,range 保持 0
|
|
58
|
+
if (_range <= 0) {
|
|
59
|
+
_range = 0
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
arr.some((n, i) => {
|
|
64
|
+
showValue = i > 0 && i % step === 0 ? showValue + gap + n : showValue + '' + n // 从左向右添加间隔符
|
|
65
|
+
})
|
|
66
|
+
const adapt = range % (step + 1) === 0 ? 1 * isAdd : 0
|
|
67
|
+
_range = typeof range !== 'undefined' ? (range === 0 ? 0 : range + adapt) : showValue.length
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { value: showValue, range: _range } // 返回格式化后的值和光标位置
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 去除值中的间隔符
|
|
74
|
+
export function trimValue(value, gap = ' ') {
|
|
75
|
+
value = typeof value === 'undefined' ? '' : value
|
|
76
|
+
const reg = new RegExp(gap, 'g')
|
|
77
|
+
value = value.toString().replace(reg, '') // 去除所有间隔符
|
|
78
|
+
return value
|
|
79
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<span class="xn-amount" :class="{ numerical: !isCapital }">
|
|
3
|
+
<template v-if="!isCapital">
|
|
4
|
+
<i class="xn-amount-prefix" :style="prefixStyle">{{ prefix }}</i>{{ formatValue | doPrecision(legalPrecision, isRoundUp) | doFormat(hasSeparator, separator) }}
|
|
5
|
+
</template>
|
|
6
|
+
<template v-else>
|
|
7
|
+
{{ formatValue | doPrecision(4, isRoundUp) | doCapital }}
|
|
8
|
+
</template>
|
|
9
|
+
</span>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script>
|
|
13
|
+
import { formatValueByGapStep } from "./formate-value.js";
|
|
14
|
+
import numberCapital from "./number-capital.js";
|
|
15
|
+
import { noop } from "./noop.js";
|
|
16
|
+
import Animate from "./animate.js";
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
name: "XnAmount",
|
|
20
|
+
inheritAttrs: false,
|
|
21
|
+
props: {
|
|
22
|
+
value: {
|
|
23
|
+
type: Number,
|
|
24
|
+
default: 0,
|
|
25
|
+
},
|
|
26
|
+
precision: {
|
|
27
|
+
type: Number,
|
|
28
|
+
default: 2,
|
|
29
|
+
},
|
|
30
|
+
isRoundUp: {
|
|
31
|
+
type: Boolean,
|
|
32
|
+
default: true,
|
|
33
|
+
},
|
|
34
|
+
hasSeparator: {
|
|
35
|
+
type: Boolean,
|
|
36
|
+
default: true,
|
|
37
|
+
},
|
|
38
|
+
separator: {
|
|
39
|
+
type: String,
|
|
40
|
+
default: ",",
|
|
41
|
+
},
|
|
42
|
+
isAnimated: {
|
|
43
|
+
type: Boolean,
|
|
44
|
+
default: false,
|
|
45
|
+
},
|
|
46
|
+
transition: {
|
|
47
|
+
type: Boolean,
|
|
48
|
+
default: false,
|
|
49
|
+
},
|
|
50
|
+
isCapital: {
|
|
51
|
+
type: Boolean,
|
|
52
|
+
default: false,
|
|
53
|
+
},
|
|
54
|
+
duration: {
|
|
55
|
+
type: Number,
|
|
56
|
+
default: 1000,
|
|
57
|
+
},
|
|
58
|
+
prefix: {
|
|
59
|
+
type: String,
|
|
60
|
+
default: "¥",
|
|
61
|
+
},
|
|
62
|
+
prefixStyle: {
|
|
63
|
+
type: Object,
|
|
64
|
+
default: () => ({}),
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
filters: {
|
|
68
|
+
doPrecision(value, precision, isRoundUp) {
|
|
69
|
+
const exponentialForm = Number(`${value}e${precision}`);
|
|
70
|
+
const rounded = isRoundUp
|
|
71
|
+
? Math.round(exponentialForm)
|
|
72
|
+
: Math.floor(exponentialForm);
|
|
73
|
+
return Number(`${rounded}e-${precision}`).toFixed(precision);
|
|
74
|
+
},
|
|
75
|
+
doFormat(value, hasSeparator, separator) {
|
|
76
|
+
if (!hasSeparator) {
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const numberParts = value.split(".");
|
|
81
|
+
let integerValue = numberParts[0];
|
|
82
|
+
const decimalValue = numberParts[1] || "";
|
|
83
|
+
|
|
84
|
+
let sign = "";
|
|
85
|
+
if (integerValue.startsWith("-")) {
|
|
86
|
+
integerValue = integerValue.substring(1);
|
|
87
|
+
sign = "-";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const formateValue = formatValueByGapStep(
|
|
91
|
+
3,
|
|
92
|
+
integerValue,
|
|
93
|
+
separator,
|
|
94
|
+
"right",
|
|
95
|
+
0,
|
|
96
|
+
1
|
|
97
|
+
);
|
|
98
|
+
return decimalValue
|
|
99
|
+
? `${sign}${formateValue.value}.${decimalValue}`
|
|
100
|
+
: `${sign}${formateValue.value}`;
|
|
101
|
+
},
|
|
102
|
+
doCapital(value) {
|
|
103
|
+
return numberCapital(value);
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
watch: {
|
|
107
|
+
value: {
|
|
108
|
+
handler(val, oldVal) {
|
|
109
|
+
/* istanbul ignore if */
|
|
110
|
+
if (!this.isMounted) {
|
|
111
|
+
this.formatValue = val;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (this.isAnimated || this.transition) {
|
|
115
|
+
this.$_doAnimateDisplay(oldVal, val);
|
|
116
|
+
} else {
|
|
117
|
+
this.formatValue = val;
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
immediate: true,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
computed: {
|
|
124
|
+
legalPrecision() {
|
|
125
|
+
return this.precision > 0 ? this.precision : 0;
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
data() {
|
|
129
|
+
return {
|
|
130
|
+
formatValue: 0,
|
|
131
|
+
isMounted: false,
|
|
132
|
+
};
|
|
133
|
+
},
|
|
134
|
+
mounted() {
|
|
135
|
+
this.isMounted = true;
|
|
136
|
+
},
|
|
137
|
+
methods: {
|
|
138
|
+
// MARK: private methods
|
|
139
|
+
$_doAnimateDisplay(fromValue = 0, toValue = 0) {
|
|
140
|
+
/* istanbul ignore next */
|
|
141
|
+
const step = (percent) => {
|
|
142
|
+
if (percent === 1) {
|
|
143
|
+
this.formatValue = toValue;
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
this.formatValue = fromValue + (toValue - fromValue) * percent;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/* istanbul ignore next */
|
|
150
|
+
const verify = (id) => id;
|
|
151
|
+
Animate.start(step, verify, noop, this.duration);
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
</script>
|
|
156
|
+
|
|
157
|
+
<style lang="scss" scoped>
|
|
158
|
+
.xn-amount{
|
|
159
|
+
&-prefix{
|
|
160
|
+
font-style: normal;
|
|
161
|
+
font-size: inherit;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function noop() {}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// 中文数字
|
|
2
|
+
const cnNums = ['\u96f6', '\u58f9', '\u8d30', '\u53c1', '\u8086', '\u4f0d', '\u9646', '\u67d2', '\u634c', '\u7396']
|
|
3
|
+
|
|
4
|
+
// 整数部分的单位
|
|
5
|
+
const cnIntRadice = ['', '\u62fe', '\u4f70', '\u4edf']
|
|
6
|
+
|
|
7
|
+
// 整数部分的进位单位
|
|
8
|
+
const cnIntUnits = ['', '\u4e07', '\u4ebf', '兆']
|
|
9
|
+
|
|
10
|
+
// 小数部分的单位
|
|
11
|
+
const cnDecUnits = ['\u89d2', '\u5206', '\u5398', '\u6beb']
|
|
12
|
+
|
|
13
|
+
// 整数部分的结尾字符
|
|
14
|
+
const cnInteger = '\u6574' // 整
|
|
15
|
+
|
|
16
|
+
// 整数部分的货币单位
|
|
17
|
+
const cnIntLast = '\u5143' // 元
|
|
18
|
+
|
|
19
|
+
// 负数的符号
|
|
20
|
+
const cnNegative = '\u8d1f' // 负
|
|
21
|
+
|
|
22
|
+
// 最大处理的数字
|
|
23
|
+
const maxNum = 999999999999999.9999
|
|
24
|
+
|
|
25
|
+
// 将数字转换为中文大写金额的函数
|
|
26
|
+
export default function(number) {
|
|
27
|
+
let negative // 负数标志
|
|
28
|
+
let integerNum // 整数部分
|
|
29
|
+
let decimalNum // 小数部分
|
|
30
|
+
let capitalStr = '' // 中文大写金额字符串
|
|
31
|
+
|
|
32
|
+
let parts // 数字的整数和小数部分
|
|
33
|
+
|
|
34
|
+
/* istanbul ignore if */
|
|
35
|
+
if (number === '') {
|
|
36
|
+
return ''
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
number = parseFloat(number) // 将输入转换为浮点数
|
|
40
|
+
|
|
41
|
+
if (number < 0) {
|
|
42
|
+
negative = true // 如果是负数,设置负数标志
|
|
43
|
+
number = Math.abs(number) // 取绝对值
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* istanbul ignore if */
|
|
47
|
+
if (number >= maxNum) {
|
|
48
|
+
return ''
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* istanbul ignore if */
|
|
52
|
+
if (number === 0) {
|
|
53
|
+
capitalStr = cnNums[0] + cnIntLast + cnInteger // 如果数字为0,直接返回“零元整”
|
|
54
|
+
return capitalStr
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 将数字转换为字符串
|
|
58
|
+
number += ''
|
|
59
|
+
|
|
60
|
+
if (number.indexOf('.') === -1) {
|
|
61
|
+
integerNum = number // 如果没有小数点,整数部分为整个数字
|
|
62
|
+
decimalNum = '' // 小数部分为空
|
|
63
|
+
} else {
|
|
64
|
+
parts = number.split('.') // 分割整数和小数部分
|
|
65
|
+
integerNum = parts[0]
|
|
66
|
+
decimalNum = parts[1].substr(0, 4) // 取小数部分的前四位
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 转换整数部分
|
|
70
|
+
if (parseInt(integerNum, 10) > 0) {
|
|
71
|
+
let zeroCount = 0 // 连续零的计数器
|
|
72
|
+
for (let i = 0, IntLen = integerNum.length; i < IntLen; i++) {
|
|
73
|
+
const n = integerNum.substr(i, 1) // 当前位的数字
|
|
74
|
+
const p = IntLen - i - 1 // 当前位的权重
|
|
75
|
+
const q = p / 4 // 当前位的进位单位
|
|
76
|
+
const m = p % 4 // 当前位的单位
|
|
77
|
+
if (n === '0') {
|
|
78
|
+
zeroCount++ // 如果当前位是零,增加零计数器
|
|
79
|
+
} else {
|
|
80
|
+
if (zeroCount > 0) {
|
|
81
|
+
capitalStr += cnNums[0] // 如果有连续的零,添加一个零
|
|
82
|
+
}
|
|
83
|
+
zeroCount = 0 // 重置零计数器
|
|
84
|
+
capitalStr += cnNums[parseInt(n)] + cnIntRadice[m] // 添加当前位的中文数字和单位
|
|
85
|
+
}
|
|
86
|
+
if (m === 0 && zeroCount < 4) {
|
|
87
|
+
capitalStr += cnIntUnits[q] // 添加进位单位
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
capitalStr += cnIntLast // 添加货币单位
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 转换小数部分
|
|
94
|
+
if (decimalNum !== '') {
|
|
95
|
+
for (let i = 0, decLen = decimalNum.length; i < decLen; i++) {
|
|
96
|
+
const n = decimalNum.substr(i, 1) // 当前位的小数
|
|
97
|
+
if (n !== '0') {
|
|
98
|
+
capitalStr += cnNums[Number(n)] + cnDecUnits[i] // 添加当前位的小数的中文数字和单位
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* istanbul ignore if */
|
|
104
|
+
if (capitalStr === '') {
|
|
105
|
+
capitalStr += cnNums[0] + cnIntLast + cnInteger // 如果没有任何转换结果,返回“零元整”
|
|
106
|
+
} else if (decimalNum === '') {
|
|
107
|
+
capitalStr += cnInteger // 如果没有小数部分,添加“整”
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (negative) {
|
|
111
|
+
capitalStr = `${cnNegative}${capitalStr}` // 如果是负数,添加负号
|
|
112
|
+
}
|
|
113
|
+
return capitalStr // 返回中文大写金额字符串
|
|
114
|
+
}
|