sh-tools 2.3.7 → 2.3.9
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/package.json +1 -1
- package/packages/expr/index.js +413 -387
- package/packages/index.js +2 -0
- package/packages/utils/index.js +2 -0
- package/packages/utils/number.js +1 -1
- package/packages/utils/other.js +0 -21
- package/packages/utils/string.js +13 -6
- package/packages/utils/time.js +85 -1
package/package.json
CHANGED
package/packages/expr/index.js
CHANGED
|
@@ -1,125 +1,137 @@
|
|
|
1
1
|
import utilsModule from '../utils'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* 表达式求值器 - 支持JSON标记解析、常用数学/数组/对象运算
|
|
5
|
+
* @class ExprEvaluator
|
|
6
|
+
* @description 通过#JSON#双标记精准解析嵌套JSON,聚焦核心运算场景
|
|
5
7
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
'
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
'
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
|
|
54
|
-
'getWhatMonth',
|
|
55
|
-
'getWhatWeek',
|
|
56
|
-
'getWhatDay',
|
|
57
|
-
'getDayOfYear',
|
|
58
|
-
'getYearDay',
|
|
59
|
-
'getYearWeek',
|
|
60
|
-
'getMonthWeek',
|
|
61
|
-
'getDayOfMonth'
|
|
62
|
-
],
|
|
63
|
-
number: ['random', 'min', 'max', 'ceil', 'floor', 'round', 'truncate', 'toFixed', 'toNumber', 'toNumberString', 'toInteger', 'add', 'subtract', 'multiply', 'divide'],
|
|
64
|
-
string: ['toValueString', 'trim', 'trimLeft', 'trimRight', 'camelCase', 'kebabCase', 'startsWith', 'endsWith']
|
|
65
|
-
}
|
|
8
|
+
class ExprEvaluator {
|
|
9
|
+
// 静态常量 - 仅保留核心方法
|
|
10
|
+
static MATH_METHODS = ['abs', 'sin', 'cos', 'tan', 'log', 'exp', 'pow']
|
|
11
|
+
static UTIL_METHODS = {
|
|
12
|
+
base: [
|
|
13
|
+
'isNaN',
|
|
14
|
+
'isFinite',
|
|
15
|
+
'isArray',
|
|
16
|
+
'isFloat',
|
|
17
|
+
'isInteger',
|
|
18
|
+
'isBoolean',
|
|
19
|
+
'isString',
|
|
20
|
+
'isNumber',
|
|
21
|
+
'isPlainObject',
|
|
22
|
+
'isDate',
|
|
23
|
+
'isEmpty',
|
|
24
|
+
'isNull',
|
|
25
|
+
'isMatch',
|
|
26
|
+
'isEqual',
|
|
27
|
+
'getSize',
|
|
28
|
+
'first',
|
|
29
|
+
'last'
|
|
30
|
+
],
|
|
31
|
+
object: ['has', 'get', 'assign', 'merge', 'pick', 'omit'],
|
|
32
|
+
array: [
|
|
33
|
+
'slice',
|
|
34
|
+
'indexOf',
|
|
35
|
+
'includes',
|
|
36
|
+
'includeArrays',
|
|
37
|
+
'sum',
|
|
38
|
+
'mean',
|
|
39
|
+
'zip',
|
|
40
|
+
'unzip',
|
|
41
|
+
'uniq',
|
|
42
|
+
'union',
|
|
43
|
+
'flatten',
|
|
44
|
+
'pluck',
|
|
45
|
+
'invoke',
|
|
46
|
+
'orderBy',
|
|
47
|
+
'groupBy',
|
|
48
|
+
'countBy',
|
|
49
|
+
'toArrayTree',
|
|
50
|
+
'toTreeArray'
|
|
51
|
+
],
|
|
52
|
+
date: ['toDateString', 'getYearDiff', 'getMonthDiff', 'getDayDiff', 'getHourDiff', 'getMinuteDiff', 'getSecondDiff', 'getYearDay', 'getYearWeek', 'getMonthWeek', 'getDayOfMonth'],
|
|
53
|
+
number: ['random', 'min', 'max', 'ceil', 'floor', 'round', 'truncate', 'toFixed', 'toNumber', 'toNumberString', 'toInteger', 'add', 'subtract', 'multiply', 'divide'],
|
|
54
|
+
string: ['toValueString', 'trim', 'trimLeft', 'trimRight', 'camelCase', 'kebabCase', 'startsWith', 'endsWith']
|
|
55
|
+
}
|
|
66
56
|
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
// 1. 最前面新增 ("[^"\\]*(?:\\.[^"\\]*)*"|'[^'\\]*(?:\\.[^'\\]*)*') 匹配单/双引号字符串
|
|
70
|
-
// 2. 处理转义字符(如 \' / \"),兼容带转义的字符串
|
|
71
|
-
const TOKEN_REG = /\s*((?:"[^"\\]*(?:\\.[^"\\]*)*"|'[^'\\]*(?:\\.[^'\\]*)*')|(if|else)|[a-zA-Z_$][a-zA-Z0-9_$]*|[\d.]+|,|==|===|!=|!==|<=|>=|<<|>>|>>>|\|\||&&|\*\*|[-+*/%^&|<>!=?:()\[\]])\s*/g
|
|
57
|
+
// TOKEN_REG匹配
|
|
58
|
+
static TOKEN_REG = /\s*(?:#JSON#([\s\S]*?)#JSON#|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|if|else|[a-zA-Z_$][a-zA-Z0-9_$]*|\d+\.?\d*|\.\d+|===|!==|==|!=|<=|>=|\|\||&&|[,.+\-*/%^&|<>!?()\[\]:])\s*/g
|
|
72
59
|
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
60
|
+
// 精简版OP_MAP:移除unary、shift,仅保留核心运算
|
|
61
|
+
static OP_MAP = {
|
|
62
|
+
equality: { '==': true, '!=': true, '===': true, '!==': true },
|
|
63
|
+
relational: { '<': true, '>': true, '<=': true, '>=': true },
|
|
64
|
+
arithmetic: { '+': true, '-': true, '*': true, '/': true, '%': true }
|
|
65
|
+
}
|
|
79
66
|
|
|
80
|
-
/**
|
|
81
|
-
* 表达式求值器(最终完整版:支持if-else+换行+字符串字面量)
|
|
82
|
-
*/
|
|
83
|
-
class ExprEvaluator {
|
|
84
67
|
constructor() {
|
|
85
|
-
this.functions = {}
|
|
86
|
-
this.operators = {}
|
|
87
|
-
this._isInited = false
|
|
88
|
-
this.
|
|
68
|
+
this.functions = {} // 工具函数映射
|
|
69
|
+
this.operators = {} // 运算符执行函数
|
|
70
|
+
this._isInited = false // 懒加载标记
|
|
71
|
+
this._cachedUtilMethods = null // 缓存扁平化的工具方法列表
|
|
72
|
+
|
|
73
|
+
// 绑定this
|
|
74
|
+
this.parsePrimary = this.parsePrimary.bind(this)
|
|
75
|
+
this.parseFunctionArgs = this.parseFunctionArgs.bind(this)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 类型归一化 - 为比较运算统一值类型(日期→数字→字符串)
|
|
80
|
+
* @private
|
|
81
|
+
* @param {any} value 待归一化的值
|
|
82
|
+
* @returns {number|string} 归一化后的值(日期转时间戳,数字串转数字,其余转字符串)
|
|
83
|
+
*/
|
|
84
|
+
_normalizeCompareValue(value) {
|
|
85
|
+
// 1. 处理日期类型(日期对象/可解析的日期字符串)
|
|
86
|
+
if (value instanceof Date) {
|
|
87
|
+
return value.getTime() // 转时间戳(数字)
|
|
88
|
+
}
|
|
89
|
+
if (typeof value === 'string') {
|
|
90
|
+
// 尝试解析为日期(支持YYYY-MM-DD、YYYY/MM/DD、带时分秒的格式)
|
|
91
|
+
const date = new Date(value)
|
|
92
|
+
if (!isNaN(date.getTime())) {
|
|
93
|
+
return date.getTime()
|
|
94
|
+
}
|
|
95
|
+
// 尝试解析为数字(如"123"、"45.6")
|
|
96
|
+
const num = Number(value)
|
|
97
|
+
if (!isNaN(num)) {
|
|
98
|
+
return num
|
|
99
|
+
}
|
|
100
|
+
// 普通字符串(去空格后比较)
|
|
101
|
+
return value.trim()
|
|
102
|
+
}
|
|
103
|
+
// 2. 处理数字类型(含数字对象)
|
|
104
|
+
if (typeof value === 'number' || (value && typeof value === 'object' && value.constructor === Number)) {
|
|
105
|
+
return Number(value)
|
|
106
|
+
}
|
|
107
|
+
// 3. 其他类型(布尔值、null、undefined等)转字符串
|
|
108
|
+
return String(value)
|
|
89
109
|
}
|
|
90
110
|
|
|
91
111
|
/**
|
|
92
|
-
*
|
|
112
|
+
* 懒加载初始化 - 仅首次调用evaluate时执行
|
|
113
|
+
* @private
|
|
93
114
|
*/
|
|
94
115
|
_lazyInit() {
|
|
95
116
|
if (this._isInited) return
|
|
96
117
|
const utils = utilsModule.__esModule ? utilsModule.default : utilsModule
|
|
97
118
|
|
|
98
|
-
|
|
99
|
-
|
|
119
|
+
// 1. 初始化数学函数(仅保留核心)
|
|
120
|
+
ExprEvaluator.MATH_METHODS.forEach(method => {
|
|
121
|
+
this.functions[method] = Math[method]
|
|
122
|
+
})
|
|
100
123
|
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
124
|
+
// 2. 初始化工具函数
|
|
125
|
+
this._cachedUtilMethods = Object.values(ExprEvaluator.UTIL_METHODS).flat()
|
|
126
|
+
this._cachedUtilMethods.forEach(method => {
|
|
104
127
|
if (typeof utils[method] === 'function') {
|
|
105
|
-
|
|
106
|
-
if (method === 'sum') {
|
|
107
|
-
funcs[method] = (...args) => {
|
|
108
|
-
if (args.length === 1 && Array.isArray(args[0])) {
|
|
109
|
-
return utils.sum(args[0])
|
|
110
|
-
}
|
|
111
|
-
return utils.sum(args)
|
|
112
|
-
}
|
|
113
|
-
} else {
|
|
114
|
-
funcs[method] = utils[method]
|
|
115
|
-
}
|
|
128
|
+
this.functions[method] = utils[method]
|
|
116
129
|
} else if (process.env.NODE_ENV === 'development') {
|
|
117
|
-
console.warn(`[ExprEvaluator]
|
|
130
|
+
console.warn(`[ExprEvaluator] 工具方法不存在: ${method}`)
|
|
118
131
|
}
|
|
119
|
-
}
|
|
120
|
-
this.functions = funcs
|
|
132
|
+
})
|
|
121
133
|
|
|
122
|
-
//
|
|
134
|
+
// 3. 初始化核心运算符
|
|
123
135
|
this.operators = {
|
|
124
136
|
'?:': { precedence: 1, associativity: 'right', ternary: true, fn: (c, a, b) => (c ? a : b) },
|
|
125
137
|
'||': { precedence: 1, associativity: 'left', fn: (a, b) => a || b },
|
|
@@ -129,23 +141,17 @@ class ExprEvaluator {
|
|
|
129
141
|
'&': { precedence: 5, associativity: 'left', fn: (a, b) => a & b },
|
|
130
142
|
'==': { precedence: 6, associativity: 'left', fn: (a, b) => a == b },
|
|
131
143
|
'!=': { precedence: 6, associativity: 'left', fn: (a, b) => a != b },
|
|
132
|
-
'===': { precedence: 6, associativity: 'left', fn: (a, b) => a === b },
|
|
133
144
|
'!==': { precedence: 6, associativity: 'left', fn: (a, b) => a !== b },
|
|
134
|
-
'<': { precedence: 7, associativity: 'left', fn: (a, b) => a < b },
|
|
135
|
-
'>': { precedence: 7, associativity: 'left', fn: (a, b) => a > b },
|
|
136
|
-
'<=': { precedence: 7, associativity: 'left', fn: (a, b) => a <= b },
|
|
137
|
-
'>=': { precedence: 7, associativity: 'left', fn: (a, b) => a >= b },
|
|
138
|
-
'
|
|
139
|
-
'>>': { precedence: 8, associativity: 'left', fn: (a, b) => a >> b },
|
|
140
|
-
'>>>': { precedence: 8, associativity: 'left', fn: (a, b) => a >>> b },
|
|
145
|
+
'<': { precedence: 7, associativity: 'left', fn: (a, b) => this._normalizeCompareValue(a) < this._normalizeCompareValue(b) },
|
|
146
|
+
'>': { precedence: 7, associativity: 'left', fn: (a, b) => this._normalizeCompareValue(a) > this._normalizeCompareValue(b) },
|
|
147
|
+
'<=': { precedence: 7, associativity: 'left', fn: (a, b) => this._normalizeCompareValue(a) <= this._normalizeCompareValue(b) },
|
|
148
|
+
'>=': { precedence: 7, associativity: 'left', fn: (a, b) => this._normalizeCompareValue(a) >= this._normalizeCompareValue(b) },
|
|
149
|
+
'===': { precedence: 6, associativity: 'left', fn: utils.isEqual },
|
|
141
150
|
'+': { precedence: 9, associativity: 'left', fn: utils.add },
|
|
142
151
|
'-': { precedence: 9, associativity: 'left', fn: utils.subtract },
|
|
143
152
|
'*': { precedence: 10, associativity: 'left', fn: utils.multiply },
|
|
144
153
|
'/': { precedence: 10, associativity: 'left', fn: utils.divide },
|
|
145
154
|
'%': { precedence: 10, associativity: 'left', fn: (a, b) => a % b },
|
|
146
|
-
'**': { precedence: 11, associativity: 'right', fn: Math.pow },
|
|
147
|
-
'unary +': { precedence: 12, associativity: 'right', prefix: true, fn: a => +a },
|
|
148
|
-
'unary -': { precedence: 12, associativity: 'right', prefix: true, fn: a => -a },
|
|
149
155
|
'!': { precedence: 12, associativity: 'right', prefix: true, fn: a => !a },
|
|
150
156
|
'~': { precedence: 12, associativity: 'right', prefix: true, fn: a => ~a }
|
|
151
157
|
}
|
|
@@ -154,332 +160,346 @@ class ExprEvaluator {
|
|
|
154
160
|
}
|
|
155
161
|
|
|
156
162
|
/**
|
|
157
|
-
*
|
|
163
|
+
* 解析函数参数
|
|
164
|
+
* @private
|
|
165
|
+
* @param {string} closeChar 闭合字符
|
|
166
|
+
* @returns {Array} 参数列表
|
|
158
167
|
*/
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
args.push(parseTernary())
|
|
170
|
-
while (tokens[pos] === ',') {
|
|
171
|
-
pos++ // 跳过逗号
|
|
172
|
-
if (tokens[pos] === closeChar) break // 处理多余逗号:sum(1,)
|
|
173
|
-
args.push(parseTernary())
|
|
174
|
-
}
|
|
168
|
+
parseFunctionArgs(closeChar) {
|
|
169
|
+
const args = []
|
|
170
|
+
this.pos++
|
|
171
|
+
|
|
172
|
+
if (this.tokens[this.pos] !== closeChar) {
|
|
173
|
+
args.push(this.parseTernary())
|
|
174
|
+
while (this.tokens[this.pos] === ',') {
|
|
175
|
+
this.pos++
|
|
176
|
+
if (this.tokens[this.pos] === closeChar) break
|
|
177
|
+
args.push(this.parseTernary())
|
|
175
178
|
}
|
|
179
|
+
}
|
|
176
180
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
pos++ // 跳过右括号 ) 或 ]
|
|
181
|
-
return args
|
|
181
|
+
if (this.tokens[this.pos] !== closeChar) {
|
|
182
|
+
throw new Error(`函数参数未闭合:缺少 ${closeChar}(当前Token:${this.tokens[this.pos] || '空'}, 位置:${this.pos})`)
|
|
182
183
|
}
|
|
184
|
+
this.pos++
|
|
185
|
+
return args
|
|
186
|
+
}
|
|
183
187
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
188
|
+
/**
|
|
189
|
+
* 解析数组字面量
|
|
190
|
+
* @private
|
|
191
|
+
* @returns {Array} 解析后的数组
|
|
192
|
+
*/
|
|
193
|
+
parseArrayLiteral() {
|
|
194
|
+
const startPos = this.pos
|
|
195
|
+
this.pos++
|
|
196
|
+
const arr = []
|
|
197
|
+
|
|
198
|
+
if (this.tokens[this.pos] !== ']') {
|
|
199
|
+
arr.push(this.parseTernary())
|
|
200
|
+
while (this.tokens[this.pos] === ',') {
|
|
201
|
+
this.pos++
|
|
202
|
+
if (this.tokens[this.pos] === ']') break
|
|
203
|
+
arr.push(this.parseTernary())
|
|
197
204
|
}
|
|
205
|
+
}
|
|
198
206
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
pos++ // 跳过右中括号 ]
|
|
203
|
-
return arr
|
|
207
|
+
if (this.tokens[this.pos] !== ']') {
|
|
208
|
+
throw new Error(`数组未闭合:在位置 ${startPos} 开始的数组缺少 ](当前Token:${this.tokens[this.pos] || '空'}, 位置:${this.pos})`)
|
|
204
209
|
}
|
|
210
|
+
this.pos++
|
|
211
|
+
return arr
|
|
212
|
+
}
|
|
205
213
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
214
|
+
/**
|
|
215
|
+
* 校验JSON格式
|
|
216
|
+
* @private
|
|
217
|
+
* @param {string} jsonStr JSON字符串
|
|
218
|
+
* @returns {boolean} 是否有效
|
|
219
|
+
*/
|
|
220
|
+
_isValidJson(jsonStr) {
|
|
221
|
+
if (!jsonStr) return false
|
|
222
|
+
try {
|
|
223
|
+
JSON.parse(jsonStr)
|
|
224
|
+
return true
|
|
225
|
+
} catch {
|
|
226
|
+
return false
|
|
227
|
+
}
|
|
228
|
+
}
|
|
210
229
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
230
|
+
/**
|
|
231
|
+
* 解析基础Token
|
|
232
|
+
* @private
|
|
233
|
+
* @returns {any} 解析后的值
|
|
234
|
+
*/
|
|
235
|
+
parsePrimary() {
|
|
236
|
+
if (this.pos >= this.tokens.length) throw new Error('表达式意外结束')
|
|
237
|
+
const currentToken = this.tokens[this.pos]
|
|
238
|
+
|
|
239
|
+
// 解析双标记JSON
|
|
240
|
+
if (typeof currentToken === 'string' && currentToken.includes('#JSON#')) {
|
|
241
|
+
this.pos++
|
|
242
|
+
const jsonMatch = currentToken.match(/#JSON#([\s\S]*?)#JSON#/)
|
|
243
|
+
if (!jsonMatch || !this._isValidJson(jsonMatch[1])) {
|
|
244
|
+
throw new Error(`JSON解析失败:${jsonMatch?.[1] || currentToken}(原Token:${currentToken})`)
|
|
216
245
|
}
|
|
246
|
+
return JSON.parse(jsonMatch[1])
|
|
247
|
+
}
|
|
217
248
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
249
|
+
// 解析字符串字面量
|
|
250
|
+
if (typeof currentToken === 'string' && (currentToken.startsWith('"') || currentToken.startsWith("'"))) {
|
|
251
|
+
this.pos++
|
|
252
|
+
return currentToken.slice(1, -1)
|
|
253
|
+
}
|
|
222
254
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
pos++
|
|
226
|
-
const res = parseTernary()
|
|
227
|
-
if (tokens[pos] !== ')') {
|
|
228
|
-
throw new Error(`括号未闭合:缺少 )(当前Token:${tokens[pos] || '空'}, 位置:${pos})`)
|
|
229
|
-
}
|
|
230
|
-
pos++
|
|
231
|
-
return res
|
|
232
|
-
}
|
|
255
|
+
// 解析数组字面量
|
|
256
|
+
if (currentToken === '[') return this.parseArrayLiteral()
|
|
233
257
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
if (tokens[pos] === '(') {
|
|
241
|
-
const args = parseFunctionArgs(')')
|
|
242
|
-
if (!self.functions[funcName]) throw new Error(`未定义函数: ${funcName}`)
|
|
243
|
-
return self.functions[funcName](...args)
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// 中括号调用:sum[1,2,3]
|
|
247
|
-
if (tokens[pos] === '[') {
|
|
248
|
-
const args = parseFunctionArgs(']')
|
|
249
|
-
if (!self.functions[funcName]) throw new Error(`未定义函数: ${funcName}`)
|
|
250
|
-
return self.functions[funcName](...args)
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
throw new Error(`不支持变量: ${funcName}(仅支持函数调用)`)
|
|
258
|
+
// 解析括号表达式
|
|
259
|
+
if (currentToken === '(') {
|
|
260
|
+
this.pos++
|
|
261
|
+
const res = this.parseTernary()
|
|
262
|
+
if (this.tokens[this.pos] !== ')') {
|
|
263
|
+
throw new Error(`括号未闭合:缺少 )(当前Token:${this.tokens[this.pos] || '空'}, 位置:${this.pos})`)
|
|
254
264
|
}
|
|
265
|
+
this.pos++
|
|
266
|
+
return res
|
|
267
|
+
}
|
|
255
268
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
269
|
+
// 解析函数调用
|
|
270
|
+
if (typeof currentToken === 'string' && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(currentToken)) {
|
|
271
|
+
const funcName = currentToken
|
|
272
|
+
this.pos++
|
|
273
|
+
|
|
274
|
+
const callType = this.tokens[this.pos]
|
|
275
|
+
if (callType === '(' || callType === '[') {
|
|
276
|
+
const args = this.parseFunctionArgs(callType === '(' ? ')' : ']')
|
|
277
|
+
if (!this.functions[funcName]) throw new Error(`未定义函数: ${funcName}`)
|
|
278
|
+
return this.functions[funcName](...args)
|
|
261
279
|
}
|
|
262
280
|
|
|
263
|
-
throw new Error(
|
|
281
|
+
throw new Error(`不支持变量: ${funcName}(仅支持函数调用和字面量)`)
|
|
264
282
|
}
|
|
265
283
|
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const opName = UNARY_OP_MAP[token]
|
|
271
|
-
pos++
|
|
272
|
-
return self.operators[opName].fn(parsePrimary())
|
|
273
|
-
}
|
|
274
|
-
return parsePrimary()
|
|
284
|
+
// 解析数字
|
|
285
|
+
if (typeof currentToken === 'number' || /^\d+\.?\d*|\.\d+$/.test(currentToken)) {
|
|
286
|
+
this.pos++
|
|
287
|
+
return Number(currentToken)
|
|
275
288
|
}
|
|
276
289
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
let res = parseUnary()
|
|
280
|
-
while (tokens[pos] === '**') {
|
|
281
|
-
pos++
|
|
282
|
-
res = self.operators['**'].fn(res, parseUnary())
|
|
283
|
-
}
|
|
284
|
-
return res
|
|
285
|
-
}
|
|
290
|
+
throw new Error(`无效Token: ${currentToken}(位置:${this.pos})`)
|
|
291
|
+
}
|
|
286
292
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
293
|
+
// ========== 精简后的解析链(仅保留核心运算) ==========
|
|
294
|
+
/**
|
|
295
|
+
* 解析乘/除/取模运算
|
|
296
|
+
* @private
|
|
297
|
+
* @returns {number} 运算结果
|
|
298
|
+
*/
|
|
299
|
+
parseMultiplicative() {
|
|
300
|
+
let res = this.parsePrimary()
|
|
301
|
+
while (ExprEvaluator.OP_MAP.arithmetic[this.tokens[this.pos]] && ['*', '/', '%'].includes(this.tokens[this.pos])) {
|
|
302
|
+
const op = this.tokens[this.pos]
|
|
303
|
+
this.pos++
|
|
304
|
+
res = this.operators[op].fn(res, this.parsePrimary())
|
|
296
305
|
}
|
|
306
|
+
return res
|
|
307
|
+
}
|
|
297
308
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
309
|
+
/**
|
|
310
|
+
* 解析加/减运算
|
|
311
|
+
* @private
|
|
312
|
+
* @returns {number} 运算结果
|
|
313
|
+
*/
|
|
314
|
+
parseAdditive() {
|
|
315
|
+
let res = this.parseMultiplicative()
|
|
316
|
+
while (ExprEvaluator.OP_MAP.arithmetic[this.tokens[this.pos]] && ['+', '-'].includes(this.tokens[this.pos])) {
|
|
317
|
+
const op = this.tokens[this.pos]
|
|
318
|
+
this.pos++
|
|
319
|
+
res = this.operators[op].fn(res, this.parseMultiplicative())
|
|
307
320
|
}
|
|
321
|
+
return res
|
|
322
|
+
}
|
|
308
323
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
324
|
+
/**
|
|
325
|
+
* 解析关系运算(<、>、<=、>=)
|
|
326
|
+
* @private
|
|
327
|
+
* @returns {boolean} 运算结果
|
|
328
|
+
*/
|
|
329
|
+
parseRelational() {
|
|
330
|
+
let res = this.parseAdditive()
|
|
331
|
+
while (ExprEvaluator.OP_MAP.relational[this.tokens[this.pos]]) {
|
|
332
|
+
const op = this.tokens[this.pos]
|
|
333
|
+
this.pos++
|
|
334
|
+
res = this.operators[op].fn(res, this.parseAdditive())
|
|
318
335
|
}
|
|
336
|
+
return res
|
|
337
|
+
}
|
|
319
338
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
339
|
+
/**
|
|
340
|
+
* 解析相等运算(==、!=、===、!==)
|
|
341
|
+
* @private
|
|
342
|
+
* @returns {boolean} 运算结果
|
|
343
|
+
*/
|
|
344
|
+
parseEquality() {
|
|
345
|
+
let res = this.parseRelational()
|
|
346
|
+
while (ExprEvaluator.OP_MAP.equality[this.tokens[this.pos]]) {
|
|
347
|
+
const op = this.tokens[this.pos]
|
|
348
|
+
this.pos++
|
|
349
|
+
res = this.operators[op].fn(res, this.parseRelational())
|
|
329
350
|
}
|
|
351
|
+
return res
|
|
352
|
+
}
|
|
330
353
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
354
|
+
/**
|
|
355
|
+
* 解析按位与运算
|
|
356
|
+
* @private
|
|
357
|
+
* @returns {number} 运算结果
|
|
358
|
+
*/
|
|
359
|
+
parseBitwiseAnd() {
|
|
360
|
+
let res = this.parseEquality()
|
|
361
|
+
while (this.tokens[this.pos] === '&') {
|
|
362
|
+
this.pos++
|
|
363
|
+
res = this.operators['&'].fn(res, this.parseEquality())
|
|
340
364
|
}
|
|
365
|
+
return res
|
|
366
|
+
}
|
|
341
367
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
368
|
+
/**
|
|
369
|
+
* 解析按位异或运算
|
|
370
|
+
* @private
|
|
371
|
+
* @returns {number} 运算结果
|
|
372
|
+
*/
|
|
373
|
+
parseBitwiseXor() {
|
|
374
|
+
let res = this.parseBitwiseAnd()
|
|
375
|
+
while (this.tokens[this.pos] === '^') {
|
|
376
|
+
this.pos++
|
|
377
|
+
res = this.operators['^'].fn(res, this.parseBitwiseAnd())
|
|
350
378
|
}
|
|
379
|
+
return res
|
|
380
|
+
}
|
|
351
381
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
382
|
+
/**
|
|
383
|
+
* 解析按位或运算
|
|
384
|
+
* @private
|
|
385
|
+
* @returns {number} 运算结果
|
|
386
|
+
*/
|
|
387
|
+
parseBitwiseOr() {
|
|
388
|
+
let res = this.parseBitwiseXor()
|
|
389
|
+
while (this.tokens[this.pos] === '|') {
|
|
390
|
+
this.pos++
|
|
391
|
+
res = this.operators['|'].fn(res, this.parseBitwiseXor())
|
|
360
392
|
}
|
|
393
|
+
return res
|
|
394
|
+
}
|
|
361
395
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
396
|
+
/**
|
|
397
|
+
* 解析逻辑与运算
|
|
398
|
+
* @private
|
|
399
|
+
* @returns {boolean} 运算结果
|
|
400
|
+
*/
|
|
401
|
+
parseLogicalAnd() {
|
|
402
|
+
let res = this.parseBitwiseOr()
|
|
403
|
+
while (this.tokens[this.pos] === '&&') {
|
|
404
|
+
this.pos++
|
|
405
|
+
res = this.operators['&&'].fn(res, this.parseBitwiseOr())
|
|
370
406
|
}
|
|
407
|
+
return res
|
|
408
|
+
}
|
|
371
409
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
410
|
+
/**
|
|
411
|
+
* 解析逻辑或运算
|
|
412
|
+
* @private
|
|
413
|
+
* @returns {boolean} 运算结果
|
|
414
|
+
*/
|
|
415
|
+
parseLogicalOr() {
|
|
416
|
+
let res = this.parseLogicalAnd()
|
|
417
|
+
while (this.tokens[this.pos] === '||') {
|
|
418
|
+
this.pos++
|
|
419
|
+
res = this.operators['||'].fn(res, this.parseLogicalAnd())
|
|
380
420
|
}
|
|
421
|
+
return res
|
|
422
|
+
}
|
|
381
423
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
424
|
+
/**
|
|
425
|
+
* 解析三元运算核心逻辑
|
|
426
|
+
* @private
|
|
427
|
+
* @returns {any} 运算结果
|
|
428
|
+
*/
|
|
429
|
+
parseTernaryCore() {
|
|
430
|
+
let res = this.parseLogicalOr()
|
|
431
|
+
if (this.tokens[this.pos] === '?') {
|
|
432
|
+
this.pos++
|
|
433
|
+
const a = this.parseTernaryCore()
|
|
434
|
+
if (this.tokens[this.pos] !== ':') throw new Error('三元运算符缺少冒号')
|
|
435
|
+
this.pos++
|
|
436
|
+
const b = this.parseTernaryCore()
|
|
437
|
+
res = this.operators['?:'].fn(res, a, b)
|
|
390
438
|
}
|
|
439
|
+
return res
|
|
440
|
+
}
|
|
391
441
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
// 第二步:解析if关键字
|
|
401
|
-
pos++ // 跳过if关键字
|
|
402
|
-
|
|
403
|
-
// 第三步:解析if后面的括号条件,比如 if (2>1) 中的 (2>1)
|
|
404
|
-
if (tokens[pos] !== '(') {
|
|
405
|
-
throw new Error(`if语法错误:缺少左括号(当前Token:${tokens[pos] || '空'}, 位置:${pos})`)
|
|
406
|
-
}
|
|
407
|
-
pos++ // 跳过左括号(
|
|
408
|
-
const condition = parseTernaryCore() // 解析条件表达式(支持所有原有运算符)
|
|
409
|
-
if (tokens[pos] !== ')') {
|
|
410
|
-
throw new Error(`if语法错误:缺少右括号(当前Token:${tokens[pos] || '空'}, 位置:${pos})`)
|
|
411
|
-
}
|
|
412
|
-
pos++ // 跳过右括号)
|
|
413
|
-
|
|
414
|
-
// 第四步:解析if的结果(then分支),比如 if (2>1) 9 中的 9
|
|
415
|
-
const thenValue = parseTernaryCore()
|
|
416
|
-
|
|
417
|
-
// 第五步:解析else关键字
|
|
418
|
-
if (tokens[pos] !== 'else') {
|
|
419
|
-
throw new Error(`if语法错误:缺少else关键字(当前Token:${tokens[pos] || '空'}, 位置:${pos})`)
|
|
420
|
-
}
|
|
421
|
-
pos++ // 跳过else关键字
|
|
422
|
-
|
|
423
|
-
// 第六步:解析else的结果(else分支),比如 if (2>1)9 else10 中的10
|
|
424
|
-
const elseValue = parseTernaryCore()
|
|
442
|
+
/**
|
|
443
|
+
* 解析if/else和三元运算
|
|
444
|
+
* @private
|
|
445
|
+
* @returns {any} 运算结果
|
|
446
|
+
*/
|
|
447
|
+
parseIfElse() {
|
|
448
|
+
if (this.tokens[this.pos] !== 'if') return this.parseTernaryCore()
|
|
449
|
+
this.pos++
|
|
425
450
|
|
|
426
|
-
|
|
427
|
-
|
|
451
|
+
if (this.tokens[this.pos] !== '(') {
|
|
452
|
+
throw new Error(`if语法错误:缺少左括号(当前Token:${this.tokens[this.pos] || '空'}, 位置:${this.pos})`)
|
|
428
453
|
}
|
|
454
|
+
this.pos++
|
|
429
455
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
if (tokens[pos] === '?') {
|
|
434
|
-
pos++
|
|
435
|
-
const a = parseTernaryCore()
|
|
436
|
-
if (tokens[pos] !== ':') throw new Error('三元运算符缺少冒号')
|
|
437
|
-
pos++
|
|
438
|
-
const b = parseTernaryCore()
|
|
439
|
-
res = self.operators['?:'].fn(res, a, b)
|
|
440
|
-
}
|
|
441
|
-
return res
|
|
456
|
+
const condition = this.parseTernaryCore()
|
|
457
|
+
if (this.tokens[this.pos] !== ')') {
|
|
458
|
+
throw new Error(`if语法错误:缺少右括号(当前Token:${this.tokens[this.pos] || '空'}, 位置:${this.pos})`)
|
|
442
459
|
}
|
|
460
|
+
this.pos++
|
|
443
461
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
return parseIfElse()
|
|
462
|
+
const thenValue = this.parseTernaryCore()
|
|
463
|
+
if (this.tokens[this.pos] !== 'else') {
|
|
464
|
+
throw new Error(`if语法错误:缺少else关键字(当前Token:${this.tokens[this.pos] || '空'}, 位置:${this.pos})`)
|
|
448
465
|
}
|
|
466
|
+
this.pos++
|
|
449
467
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
468
|
+
const elseValue = this.parseTernaryCore()
|
|
469
|
+
return condition ? thenValue : elseValue
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* 入口解析方法
|
|
474
|
+
* @private
|
|
475
|
+
* @returns {any} 运算结果
|
|
476
|
+
*/
|
|
477
|
+
parseTernary() {
|
|
478
|
+
return this.parseIfElse()
|
|
460
479
|
}
|
|
461
480
|
|
|
462
481
|
/**
|
|
463
|
-
*
|
|
482
|
+
* 分词处理
|
|
483
|
+
* @private
|
|
484
|
+
* @param {string} expr 表达式
|
|
485
|
+
* @returns {Array} 分词结果
|
|
464
486
|
*/
|
|
465
487
|
_tokenize(expr) {
|
|
466
488
|
const tokens = []
|
|
467
|
-
TOKEN_REG.
|
|
489
|
+
const reg = new RegExp(ExprEvaluator.TOKEN_REG.source, 'g')
|
|
468
490
|
let match
|
|
469
|
-
const utils = utilsModule.__esModule ? utilsModule.default : utilsModule
|
|
470
491
|
|
|
471
|
-
while ((match =
|
|
472
|
-
const token = match[
|
|
473
|
-
|
|
474
|
-
|
|
492
|
+
while ((match = reg.exec(expr)) !== null) {
|
|
493
|
+
const token = match[0].trim()
|
|
494
|
+
if (!token) continue
|
|
495
|
+
|
|
496
|
+
if (token.includes('#JSON#')) {
|
|
475
497
|
tokens.push(token)
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
else if (/^\d
|
|
479
|
-
tokens.push(
|
|
480
|
-
}
|
|
481
|
-
// 3. 其他Token(关键字/运算符/标识符):原样保留
|
|
482
|
-
else {
|
|
498
|
+
} else if (token.startsWith('"') || token.startsWith("'")) {
|
|
499
|
+
tokens.push(token)
|
|
500
|
+
} else if (/^\d+\.?\d*|\.\d+$/.test(token)) {
|
|
501
|
+
tokens.push(Number(token))
|
|
502
|
+
} else {
|
|
483
503
|
tokens.push(token)
|
|
484
504
|
}
|
|
485
505
|
}
|
|
@@ -489,22 +509,28 @@ class ExprEvaluator {
|
|
|
489
509
|
}
|
|
490
510
|
|
|
491
511
|
/**
|
|
492
|
-
*
|
|
512
|
+
* 对外求值方法
|
|
513
|
+
* @public
|
|
514
|
+
* @param {string} expr 表达式
|
|
515
|
+
* @returns {any} 结果
|
|
493
516
|
*/
|
|
494
517
|
evaluate(expr) {
|
|
495
518
|
this._lazyInit()
|
|
496
519
|
|
|
497
520
|
if (typeof expr !== 'string') throw new Error('表达式必须是字符串')
|
|
521
|
+
const normalizedExpr = expr.replace(/[\n\r]/g, ' ').trim()
|
|
522
|
+
if (!normalizedExpr) throw new Error('表达式不能为空')
|
|
498
523
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
524
|
+
try {
|
|
525
|
+
this.tokens = this._tokenize(normalizedExpr)
|
|
526
|
+
this.pos = 0
|
|
527
|
+
const result = this.parseTernary()
|
|
502
528
|
|
|
503
|
-
|
|
529
|
+
if (this.pos < this.tokens.length) {
|
|
530
|
+
throw new Error(`表达式多余内容: ${this.tokens.slice(this.pos).join('')}(位置:${this.pos})`)
|
|
531
|
+
}
|
|
504
532
|
|
|
505
|
-
|
|
506
|
-
const tokens = this._tokenize(trimmed)
|
|
507
|
-
return this._parseEntry(tokens)
|
|
533
|
+
return result
|
|
508
534
|
} catch (error) {
|
|
509
535
|
throw new Error(`表达式计算失败: ${error.message}(原表达式:${expr})`)
|
|
510
536
|
}
|
package/packages/index.js
CHANGED
package/packages/utils/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import array from './array'
|
|
|
6
6
|
import number from './number'
|
|
7
7
|
import string from './string'
|
|
8
8
|
import boolean from './boolean'
|
|
9
|
+
import time from './time'
|
|
9
10
|
import color from './color'
|
|
10
11
|
import dom from './dom'
|
|
11
12
|
import other from './other'
|
|
@@ -17,6 +18,7 @@ XEUtils.mixin({
|
|
|
17
18
|
...number,
|
|
18
19
|
...string,
|
|
19
20
|
...boolean,
|
|
21
|
+
...time,
|
|
20
22
|
...color,
|
|
21
23
|
...dom,
|
|
22
24
|
...other,
|
package/packages/utils/number.js
CHANGED
|
@@ -15,7 +15,7 @@ export default {
|
|
|
15
15
|
calculate(formula, data) {
|
|
16
16
|
if (!formula) return ''
|
|
17
17
|
if (data && XEUtils.isPlainObject(data)) {
|
|
18
|
-
formula = stringUtil.format(formula, data
|
|
18
|
+
formula = stringUtil.format(formula, data)
|
|
19
19
|
}
|
|
20
20
|
return formulaParser.evaluate(formula)
|
|
21
21
|
}
|
package/packages/utils/other.js
CHANGED
|
@@ -11,27 +11,6 @@ const endStrs = ['Id', '_id']
|
|
|
11
11
|
const dateReplaceMap = { YYYY: 'yyyy', DD: 'dd', hh: 'HH' }
|
|
12
12
|
|
|
13
13
|
export default {
|
|
14
|
-
toDateDiffText(date) {
|
|
15
|
-
let currDate = 1544407800000 // '2018-12-10 10:10:00'
|
|
16
|
-
let dateDiff = XEUtils.getDateDiff(date, currDate)
|
|
17
|
-
if (dateDiff.done) {
|
|
18
|
-
if (dateDiff.time < 10000) {
|
|
19
|
-
return '刚刚'
|
|
20
|
-
} else if (dateDiff.time < 60000) {
|
|
21
|
-
return `${dateDiff.ss}秒之前`
|
|
22
|
-
} else if (dateDiff.time < 360000) {
|
|
23
|
-
return `${dateDiff.mm}分钟之前`
|
|
24
|
-
} else if (dateDiff.time < 86400000) {
|
|
25
|
-
return `${dateDiff.HH}小时之前`
|
|
26
|
-
} else if (dateDiff.time < 2592000000) {
|
|
27
|
-
return `${dateDiff.dd}天之前`
|
|
28
|
-
} else if (dateDiff.time < 31536000000) {
|
|
29
|
-
return `${dateDiff.MM}个月之前`
|
|
30
|
-
}
|
|
31
|
-
return `${dateDiff.yyyy}年之前`
|
|
32
|
-
}
|
|
33
|
-
return '错误类型'
|
|
34
|
-
},
|
|
35
14
|
ioToFile(io, fileName = 'download', type = 'blob') {
|
|
36
15
|
return new Promise((resolve, reject) => {
|
|
37
16
|
try {
|
package/packages/utils/string.js
CHANGED
|
@@ -4,19 +4,26 @@ import XEUtils from 'xe-utils'
|
|
|
4
4
|
function getFormatKeys(format) {
|
|
5
5
|
let regR = new RegExp('({[a-zA-Z0-9_.]*})', 'ig')
|
|
6
6
|
let formatStr = String(format)
|
|
7
|
-
return (formatStr.match(regR) || []).map((key, keyIndex) => {
|
|
8
|
-
return key.replace(/{|}/gi, '')
|
|
9
|
-
})
|
|
7
|
+
return (formatStr.match(regR) || []).map((key, keyIndex) => key.replace(/{|}/gi, ''))
|
|
10
8
|
}
|
|
11
9
|
|
|
12
10
|
// 格式化数据结构
|
|
13
|
-
function format(format, data
|
|
11
|
+
function format(format, data) {
|
|
14
12
|
let keys = getFormatKeys(format)
|
|
15
13
|
let formatStr = String(format)
|
|
16
14
|
keys.map((key, indexkey) => {
|
|
17
15
|
let value = XEUtils.get(data, key) || ''
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
let replaceValue = ''
|
|
17
|
+
if (value === undefined || value === null) {
|
|
18
|
+
replaceValue = '""'
|
|
19
|
+
} else if (typeof value === 'object' || Array.isArray(value)) {
|
|
20
|
+
replaceValue = `#JSON#${JSON.stringify(value)}#JSON#`
|
|
21
|
+
} else if (typeof value === 'string') {
|
|
22
|
+
replaceValue = `"${value}"`
|
|
23
|
+
} else {
|
|
24
|
+
replaceValue = value
|
|
25
|
+
}
|
|
26
|
+
formatStr = formatStr.replace(new RegExp('{' + key + '}', 'ig'), replaceValue)
|
|
20
27
|
})
|
|
21
28
|
return formatStr
|
|
22
29
|
}
|
package/packages/utils/time.js
CHANGED
|
@@ -1 +1,85 @@
|
|
|
1
|
-
|
|
1
|
+
import XEUtils from 'xe-utils'
|
|
2
|
+
|
|
3
|
+
// 常量定义:各单位对应的毫秒数
|
|
4
|
+
const TIME_UNIT = {
|
|
5
|
+
second: 1000,
|
|
6
|
+
minute: 1000 * 60,
|
|
7
|
+
hour: 1000 * 60 * 60,
|
|
8
|
+
day: 1000 * 60 * 60 * 24
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const getDataDiff = (date1, date2) => {
|
|
12
|
+
let datePre = ''
|
|
13
|
+
let dateDiff = XEUtils.getDateDiff(date1, date2)
|
|
14
|
+
if (!dateDiff.done) {
|
|
15
|
+
dateDiff = XEUtils.getDateDiff(date2, date1)
|
|
16
|
+
datePre = '-'
|
|
17
|
+
}
|
|
18
|
+
return { datePre, dateDiff }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default {
|
|
22
|
+
toDateDiffText(date) {
|
|
23
|
+
let currDate = 1544407800000 // '2018-12-10 10:10:00'
|
|
24
|
+
let dateDiff = XEUtils.getDateDiff(date, currDate)
|
|
25
|
+
if (dateDiff.done) {
|
|
26
|
+
if (dateDiff.time < 10000) {
|
|
27
|
+
return '刚刚'
|
|
28
|
+
} else if (dateDiff.time < 60000) {
|
|
29
|
+
return `${dateDiff.ss}秒之前`
|
|
30
|
+
} else if (dateDiff.time < 360000) {
|
|
31
|
+
return `${dateDiff.mm}分钟之前`
|
|
32
|
+
} else if (dateDiff.time < 86400000) {
|
|
33
|
+
return `${dateDiff.HH}小时之前`
|
|
34
|
+
} else if (dateDiff.time < 2592000000) {
|
|
35
|
+
return `${dateDiff.dd}天之前`
|
|
36
|
+
} else if (dateDiff.time < 31536000000) {
|
|
37
|
+
return `${dateDiff.MM}个月之前`
|
|
38
|
+
}
|
|
39
|
+
return `${dateDiff.yyyy}年之前`
|
|
40
|
+
}
|
|
41
|
+
return '错误类型'
|
|
42
|
+
},
|
|
43
|
+
getYearDiff(date1, date2, isSupply) {
|
|
44
|
+
// isSupply: 为是否补充,即不足一年按整年算
|
|
45
|
+
let { datePre, dateDiff } = getDataDiff(date1, date2)
|
|
46
|
+
let dataValue = dateDiff.yyyy
|
|
47
|
+
if (isSupply && (dateDiff.MM || dateDiff.dd || dateDiff.HH || dateDiff.mm || dateDiff.ss)) {
|
|
48
|
+
dataValue += 1
|
|
49
|
+
}
|
|
50
|
+
return Number(`${datePre}${dataValue}`)
|
|
51
|
+
},
|
|
52
|
+
getMonthDiff(date1, date2, isSupply) {
|
|
53
|
+
// isSupply: 为是否补充,即不足一月按整月算
|
|
54
|
+
let { datePre, dateDiff } = getDataDiff(date1, date2)
|
|
55
|
+
let dataValue = XEUtils.multiply(dateDiff.yyyy, 12) + dateDiff.MM
|
|
56
|
+
if (isSupply && (dateDiff.dd || dateDiff.HH || dateDiff.mm || dateDiff.ss)) {
|
|
57
|
+
dataValue += 1
|
|
58
|
+
}
|
|
59
|
+
return Number(`${datePre}${dataValue}`)
|
|
60
|
+
},
|
|
61
|
+
getDayDiff(date1, date2, isSupply) {
|
|
62
|
+
// isSupply: 为是否补充,即不足一天按整天算
|
|
63
|
+
let { datePre, dateDiff } = getDataDiff(date1, date2)
|
|
64
|
+
let dataValue = XEUtils[isSupply ? 'ceil' : 'floor'](XEUtils.divide(dateDiff.time, TIME_UNIT.day))
|
|
65
|
+
return Number(`${datePre}${dataValue}`)
|
|
66
|
+
},
|
|
67
|
+
getHourDiff(date1, date2, isSupply) {
|
|
68
|
+
// isSupply: 为是否补充,即不足一小时按整小时算
|
|
69
|
+
let { datePre, dateDiff } = getDataDiff(date1, date2)
|
|
70
|
+
let dataValue = XEUtils[isSupply ? 'ceil' : 'floor'](XEUtils.divide(dateDiff.time, TIME_UNIT.hour))
|
|
71
|
+
return Number(`${datePre}${dataValue}`)
|
|
72
|
+
},
|
|
73
|
+
getMinuteDiff(date1, date2, isSupply) {
|
|
74
|
+
// isSupply: 为是否补充,即不足一分钟按整分钟算
|
|
75
|
+
let { datePre, dateDiff } = getDataDiff(date1, date2)
|
|
76
|
+
let dataValue = XEUtils[isSupply ? 'ceil' : 'floor'](XEUtils.divide(dateDiff.time, TIME_UNIT.minute))
|
|
77
|
+
return Number(`${datePre}${dataValue}`)
|
|
78
|
+
},
|
|
79
|
+
getSecondDiff(date1, date2, isSupply) {
|
|
80
|
+
// isSupply: 为是否补充,即不足一分钟按整分钟算
|
|
81
|
+
let { datePre, dateDiff } = getDataDiff(date1, date2)
|
|
82
|
+
let dataValue = XEUtils[isSupply ? 'ceil' : 'floor'](XEUtils.divide(dateDiff.time, TIME_UNIT.second))
|
|
83
|
+
return Number(`${datePre}${dataValue}`)
|
|
84
|
+
}
|
|
85
|
+
}
|