@yoooloo42/joker 1.0.143 → 1.0.145
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/dist/index.cjs.js +1834 -1559
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +770 -495
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useRouter } from 'vue-router';
|
|
2
|
-
import { ref, createElementBlock, openBlock, Fragment, createCommentVNode, createElementVNode, normalizeStyle, toDisplayString, unref, reactive, computed, resolveComponent, createBlock, withCtx, renderList, createTextVNode, createVNode, isRef, defineComponent, h, onMounted, onBeforeUnmount, watch, nextTick as nextTick$1, resolveDirective, withDirectives } from 'vue';
|
|
2
|
+
import { getCurrentInstance, ref, createElementBlock, openBlock, Fragment, createCommentVNode, createElementVNode, normalizeStyle, toDisplayString, unref, reactive, computed, resolveComponent, createBlock, withCtx, renderList, createTextVNode, createVNode, isRef, defineComponent, h, onMounted, onBeforeUnmount, watch, nextTick as nextTick$1, resolveDirective, withDirectives } from 'vue';
|
|
3
3
|
import { ElMessage } from 'element-plus';
|
|
4
4
|
|
|
5
5
|
function _mergeNamespaces(n, m) {
|
|
@@ -16869,7 +16869,7 @@ var lookup = [];
|
|
|
16869
16869
|
var revLookup = [];
|
|
16870
16870
|
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array;
|
|
16871
16871
|
var inited = false;
|
|
16872
|
-
function init () {
|
|
16872
|
+
function init$1 () {
|
|
16873
16873
|
inited = true;
|
|
16874
16874
|
var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
16875
16875
|
for (var i = 0, len = code.length; i < len; ++i) {
|
|
@@ -16883,7 +16883,7 @@ function init () {
|
|
|
16883
16883
|
|
|
16884
16884
|
function toByteArray (b64) {
|
|
16885
16885
|
if (!inited) {
|
|
16886
|
-
init();
|
|
16886
|
+
init$1();
|
|
16887
16887
|
}
|
|
16888
16888
|
var i, j, l, tmp, placeHolders, arr;
|
|
16889
16889
|
var len = b64.length;
|
|
@@ -16942,7 +16942,7 @@ function encodeChunk (uint8, start, end) {
|
|
|
16942
16942
|
|
|
16943
16943
|
function fromByteArray (uint8) {
|
|
16944
16944
|
if (!inited) {
|
|
16945
|
-
init();
|
|
16945
|
+
init$1();
|
|
16946
16946
|
}
|
|
16947
16947
|
var tmp;
|
|
16948
16948
|
var len = uint8.length;
|
|
@@ -22136,6 +22136,770 @@ var request = {
|
|
|
22136
22136
|
ly0: ly0request
|
|
22137
22137
|
};
|
|
22138
22138
|
|
|
22139
|
+
// 引用标准:GB/T 2260
|
|
22140
|
+
|
|
22141
|
+
/**
|
|
22142
|
+
* 深度拷贝函数
|
|
22143
|
+
*
|
|
22144
|
+
* @param {any} obj 需要拷贝的对象、数组或基本类型值
|
|
22145
|
+
* @param {WeakMap} [cache=new WeakMap()] 用于处理循环引用的缓存
|
|
22146
|
+
* @returns {any} 深度拷贝后的新对象/新值
|
|
22147
|
+
*/
|
|
22148
|
+
function deepClone$1(obj, cache = new WeakMap()) {
|
|
22149
|
+
// 1. 基本类型值(包括 null)和函数,直接返回
|
|
22150
|
+
if (obj === null || typeof obj !== 'object') {
|
|
22151
|
+
return obj;
|
|
22152
|
+
}
|
|
22153
|
+
// 处理函数(尽管技术上函数是对象,但我们通常不克隆它,而是直接引用)
|
|
22154
|
+
if (typeof obj === 'function') {
|
|
22155
|
+
return obj;
|
|
22156
|
+
}
|
|
22157
|
+
|
|
22158
|
+
// 2. 检查循环引用
|
|
22159
|
+
// 如果缓存中已存在该对象,说明遇到了循环引用,直接返回缓存中的克隆对象
|
|
22160
|
+
if (cache.has(obj)) {
|
|
22161
|
+
return cache.get(obj);
|
|
22162
|
+
}
|
|
22163
|
+
|
|
22164
|
+
// 3. 处理特定内置对象(Date 和 RegExp)
|
|
22165
|
+
if (obj instanceof Date) {
|
|
22166
|
+
return new Date(obj.getTime());
|
|
22167
|
+
}
|
|
22168
|
+
if (obj instanceof RegExp) {
|
|
22169
|
+
// g: global, i: ignoreCase, m: multiline, u: unicode, y: sticky
|
|
22170
|
+
const flags = obj.global ? 'g' : ''
|
|
22171
|
+
+ obj.ignoreCase ? 'i' : ''
|
|
22172
|
+
+ obj.multiline ? 'm' : ''
|
|
22173
|
+
+ obj.unicode ? 'u' : ''
|
|
22174
|
+
+ obj.sticky ? 'y' : '';
|
|
22175
|
+
return new RegExp(obj.source, flags);
|
|
22176
|
+
}
|
|
22177
|
+
|
|
22178
|
+
// 4. 初始化克隆对象
|
|
22179
|
+
// 如果是数组,则初始化为空数组;否则初始化为空对象
|
|
22180
|
+
const clone = Array.isArray(obj) ? [] : {};
|
|
22181
|
+
|
|
22182
|
+
// 将克隆对象放入缓存,以处理接下来的递归调用中可能遇到的循环引用
|
|
22183
|
+
cache.set(obj, clone);
|
|
22184
|
+
|
|
22185
|
+
// 5. 递归拷贝属性
|
|
22186
|
+
for (const key in obj) {
|
|
22187
|
+
// 确保只处理对象自身的属性,排除原型链上的属性
|
|
22188
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
22189
|
+
clone[key] = deepClone$1(obj[key], cache);
|
|
22190
|
+
}
|
|
22191
|
+
}
|
|
22192
|
+
|
|
22193
|
+
// 6. 拷贝 Symbol 属性 (ES6/ES2015+)
|
|
22194
|
+
if (typeof Object.getOwnPropertySymbols === 'function') {
|
|
22195
|
+
Object.getOwnPropertySymbols(obj).forEach(sym => {
|
|
22196
|
+
clone[sym] = deepClone$1(obj[sym], cache);
|
|
22197
|
+
});
|
|
22198
|
+
}
|
|
22199
|
+
|
|
22200
|
+
return clone;
|
|
22201
|
+
}
|
|
22202
|
+
|
|
22203
|
+
/**
|
|
22204
|
+
* 深度拷贝函数,并在拷贝叶节点值时应用一个转换函数。
|
|
22205
|
+
*
|
|
22206
|
+
* @param {any} obj - 需要拷贝的对象、数组或基本类型值。
|
|
22207
|
+
* @param {function} valueMapper - 接收叶节点值作为参数,并返回新值的转换函数。
|
|
22208
|
+
* @param {WeakMap} [cache=new WeakMap()] - 用于处理循环引用的缓存。
|
|
22209
|
+
* @returns {any} 深度拷贝并转换后的新对象/新值。
|
|
22210
|
+
*/
|
|
22211
|
+
function deepCloneAndMap(obj, valueMapper, cache = new WeakMap()) {
|
|
22212
|
+
// 1. 基本类型值(包括 null)和函数,**应用 valueMapper**
|
|
22213
|
+
|
|
22214
|
+
// 如果是基本类型值(包括 null),则认为是叶节点,应用 valueMapper
|
|
22215
|
+
if (obj === null || typeof obj !== 'object') {
|
|
22216
|
+
// 对基本类型值应用转换函数
|
|
22217
|
+
return valueMapper ? valueMapper(obj) : obj;
|
|
22218
|
+
}
|
|
22219
|
+
|
|
22220
|
+
// 如果是函数,通常我们不克隆它,也不转换它的值,直接返回
|
|
22221
|
+
if (typeof obj === 'function') {
|
|
22222
|
+
return obj;
|
|
22223
|
+
}
|
|
22224
|
+
|
|
22225
|
+
// 2. 检查循环引用 (与原函数逻辑相同)
|
|
22226
|
+
if (cache.has(obj)) {
|
|
22227
|
+
return cache.get(obj);
|
|
22228
|
+
}
|
|
22229
|
+
|
|
22230
|
+
// 3. 处理特定内置对象(Date 和 RegExp),**应用 valueMapper**
|
|
22231
|
+
|
|
22232
|
+
// 对于 Date 和 RegExp 这种对象实例,我们通常将它们的**值**视为叶节点,
|
|
22233
|
+
// 克隆实例本身后,再对这个克隆实例应用 valueMapper。
|
|
22234
|
+
let clone;
|
|
22235
|
+
|
|
22236
|
+
if (obj instanceof Date) {
|
|
22237
|
+
clone = new Date(obj.getTime());
|
|
22238
|
+
} else if (obj instanceof RegExp) {
|
|
22239
|
+
// 提取 flags
|
|
22240
|
+
const flags = (obj.global ? 'g' : '')
|
|
22241
|
+
+ (obj.ignoreCase ? 'i' : '')
|
|
22242
|
+
+ (obj.multiline ? 'm' : '')
|
|
22243
|
+
+ (obj.unicode ? 'u' : '')
|
|
22244
|
+
+ (obj.sticky ? 'y' : '');
|
|
22245
|
+
clone = new RegExp(obj.source, flags);
|
|
22246
|
+
}
|
|
22247
|
+
|
|
22248
|
+
// 检查是否是内置对象(Date/RegExp),如果是,对克隆后的实例应用转换
|
|
22249
|
+
if (clone) {
|
|
22250
|
+
return valueMapper ? valueMapper(clone) : clone;
|
|
22251
|
+
}
|
|
22252
|
+
|
|
22253
|
+
// 4. 初始化克隆对象 (普通对象和数组)
|
|
22254
|
+
clone = Array.isArray(obj) ? [] : {};
|
|
22255
|
+
|
|
22256
|
+
// 将克隆对象放入缓存
|
|
22257
|
+
cache.set(obj, clone);
|
|
22258
|
+
|
|
22259
|
+
// 5. 递归拷贝属性
|
|
22260
|
+
for (const key in obj) {
|
|
22261
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
22262
|
+
// 递归调用自身,对子属性进行深拷贝和转换
|
|
22263
|
+
clone[key] = deepCloneAndMap(obj[key], valueMapper, cache);
|
|
22264
|
+
}
|
|
22265
|
+
}
|
|
22266
|
+
|
|
22267
|
+
// 6. 拷贝 Symbol 属性
|
|
22268
|
+
if (typeof Object.getOwnPropertySymbols === 'function') {
|
|
22269
|
+
Object.getOwnPropertySymbols(obj).forEach(sym => {
|
|
22270
|
+
// 递归调用自身,对 Symbol 属性的值进行深拷贝和转换
|
|
22271
|
+
clone[sym] = deepCloneAndMap(obj[sym], valueMapper, cache);
|
|
22272
|
+
});
|
|
22273
|
+
}
|
|
22274
|
+
|
|
22275
|
+
// 7. 返回深度克隆并转换后的对象/数组
|
|
22276
|
+
return clone;
|
|
22277
|
+
}
|
|
22278
|
+
|
|
22279
|
+
/**
|
|
22280
|
+
* 健壮的数据类型判断函数
|
|
22281
|
+
*
|
|
22282
|
+
* @param {any} value 需要判断类型的值
|
|
22283
|
+
* @returns {string} 小写的类型字符串,例如:'string', 'number', 'array', 'function', 'object', 'null', 'undefined'
|
|
22284
|
+
*/
|
|
22285
|
+
function typeOfValue(value) {
|
|
22286
|
+
// 1. 处理基本类型值(typeof 准确的部分)
|
|
22287
|
+
const type = typeof value;
|
|
22288
|
+
|
|
22289
|
+
// 'string', 'number', 'boolean', 'symbol', 'bigint', 'function', 'undefined'
|
|
22290
|
+
if (type !== 'object') {
|
|
22291
|
+
return type;
|
|
22292
|
+
}
|
|
22293
|
+
|
|
22294
|
+
// 2. 处理 null (typeof 的缺陷:typeof null === 'object')
|
|
22295
|
+
if (value === null) {
|
|
22296
|
+
return 'null';
|
|
22297
|
+
}
|
|
22298
|
+
|
|
22299
|
+
// 3. 处理对象类型值 (使用 Object.prototype.toString.call() 获取内部 [[Class]] 属性)
|
|
22300
|
+
// 结果格式为:[object Class]
|
|
22301
|
+
const classString = Object.prototype.toString.call(value);
|
|
22302
|
+
|
|
22303
|
+
// 提取 'Array', 'Date', 'RegExp', 'Map', 'Set', 'Object' 等 Class 名称
|
|
22304
|
+
const className = classString.slice(8, -1);
|
|
22305
|
+
|
|
22306
|
+
// 4. 特殊处理 Array.isArray()
|
|
22307
|
+
// 虽然 className 已经是 'Array',但 Array.isArray 更快且明确
|
|
22308
|
+
if (className === 'Array') {
|
|
22309
|
+
return 'array';
|
|
22310
|
+
}
|
|
22311
|
+
|
|
22312
|
+
// 5. 将其他内置对象和普通对象名称转为小写返回
|
|
22313
|
+
return className.toLowerCase();
|
|
22314
|
+
}
|
|
22315
|
+
|
|
22316
|
+
/**
|
|
22317
|
+
* 判断一个字符串是否是有效的 JSON 格式
|
|
22318
|
+
*
|
|
22319
|
+
* @param {string} strValue - 要检查的字符串值
|
|
22320
|
+
* @returns {boolean} 如果字符串是有效的 JSON 格式则返回 true,否则返回 false
|
|
22321
|
+
*/
|
|
22322
|
+
function isJsonString(strValue) {
|
|
22323
|
+
// 1. 确保输入是一个字符串,如果不是,则直接返回 false
|
|
22324
|
+
// JSON 格式只能是字符串
|
|
22325
|
+
if (typeof strValue !== 'string') {
|
|
22326
|
+
return false;
|
|
22327
|
+
}
|
|
22328
|
+
|
|
22329
|
+
// 2. 尝试解析字符串
|
|
22330
|
+
try {
|
|
22331
|
+
// 使用 JSON.parse() 尝试解析。
|
|
22332
|
+
// 如果解析成功,它就会返回解析后的对象或值。
|
|
22333
|
+
// 如果解析失败,就会抛出 SyntaxError 错误。
|
|
22334
|
+
JSON.parse(strValue);
|
|
22335
|
+
|
|
22336
|
+
// 3. 额外的检查 (可选但推荐):
|
|
22337
|
+
// 确保解析结果不是原始类型值,例如 "123" 或 "true"
|
|
22338
|
+
// 某些场景下,用户希望 JSON 字符串必须是对象或数组的字符串表示
|
|
22339
|
+
// const parsed = JSON.parse(strValue);
|
|
22340
|
+
// if (typeof parsed !== 'object' || parsed === null) {
|
|
22341
|
+
// // 如果你想排除 "123", "null", "true" 这些原始值的 JSON 字符串,可以取消注释
|
|
22342
|
+
// // return false;
|
|
22343
|
+
// }
|
|
22344
|
+
|
|
22345
|
+
} catch (e) {
|
|
22346
|
+
// 捕获任何解析错误(通常是 SyntaxError),表示格式不符合 JSON 规范
|
|
22347
|
+
return false;
|
|
22348
|
+
}
|
|
22349
|
+
|
|
22350
|
+
// 4. 解析成功,返回 true
|
|
22351
|
+
return true;
|
|
22352
|
+
}
|
|
22353
|
+
|
|
22354
|
+
/**
|
|
22355
|
+
* 遍历树形结构(对象或数组)的所有叶节点,将叶节点的值收集到数组中。
|
|
22356
|
+
*
|
|
22357
|
+
* 叶节点被定义为:值不是普通对象或数组的节点。
|
|
22358
|
+
*
|
|
22359
|
+
* @param {object|Array} tree - 要扁平化处理的树形结构对象或数组。
|
|
22360
|
+
* @param {Array<any>} [result=[]] - 存储叶节点值的数组(递归内部使用)。
|
|
22361
|
+
* @returns {Array<any>} 包含所有叶节点值的数组。
|
|
22362
|
+
*/
|
|
22363
|
+
function flattenTreeValues(tree, result = []) {
|
|
22364
|
+
// 确保输入是对象或数组。如果不是,或者为 null,则直接返回结果数组。
|
|
22365
|
+
if (typeof tree !== 'object' || tree === null) {
|
|
22366
|
+
return result;
|
|
22367
|
+
}
|
|
22368
|
+
|
|
22369
|
+
// 遍历对象的键(如果是数组,键就是索引)
|
|
22370
|
+
for (const key in tree) {
|
|
22371
|
+
// 确保只处理对象自身的属性,排除原型链上的属性
|
|
22372
|
+
if (Object.prototype.hasOwnProperty.call(tree, key)) {
|
|
22373
|
+
const value = tree[key];
|
|
22374
|
+
|
|
22375
|
+
// 判断当前值是否为“叶节点”
|
|
22376
|
+
// 检查:值不是对象,且值不是 null
|
|
22377
|
+
if (typeof value !== 'object' || value === null) {
|
|
22378
|
+
// 1. 如果是基本类型值(叶节点),则将其压入结果数组
|
|
22379
|
+
result.push(value);
|
|
22380
|
+
} else {
|
|
22381
|
+
// 2. 如果是对象或数组(非叶节点),则进行递归调用
|
|
22382
|
+
// 注意:这里没有处理 Date, RegExp, Set, Map 等特殊对象,
|
|
22383
|
+
// 默认将它们视为非叶节点,直到它们内部的值被遍历完(这通常是不对的)
|
|
22384
|
+
//
|
|
22385
|
+
// 为了更精确地实现“叶节点”概念,我们将 Date, RegExp 等内置对象视为叶节点:
|
|
22386
|
+
const isArray = Array.isArray(value);
|
|
22387
|
+
const isPlainObject = !isArray && Object.prototype.toString.call(value) === '[object Object]';
|
|
22388
|
+
|
|
22389
|
+
if (isArray || isPlainObject) {
|
|
22390
|
+
// 如果是普通数组或普通对象,则递归
|
|
22391
|
+
flattenTreeValues(value, result);
|
|
22392
|
+
} else {
|
|
22393
|
+
// 如果是像 Date, RegExp, Map, Set 等特殊内置对象,
|
|
22394
|
+
// 我们将其视为叶节点的值(而不是继续遍历其内部结构)
|
|
22395
|
+
result.push(value);
|
|
22396
|
+
}
|
|
22397
|
+
}
|
|
22398
|
+
}
|
|
22399
|
+
}
|
|
22400
|
+
|
|
22401
|
+
return result;
|
|
22402
|
+
}
|
|
22403
|
+
|
|
22404
|
+
/**
|
|
22405
|
+
* 将扁平化的对象数组转换为树形结构数据,并在每个节点中保留原始数据。
|
|
22406
|
+
*
|
|
22407
|
+
* @param {Object[]} flatArray 待转化的扁平数组。
|
|
22408
|
+
* @param {Object} inputFields 描述输入数组中字段名的对象。
|
|
22409
|
+
* @param {string} inputFields.idField 节点代码字段名。
|
|
22410
|
+
* @param {string} inputFields.textField 节点文本字段名。
|
|
22411
|
+
* @param {string} inputFields.parentField 父节点代码字段名。
|
|
22412
|
+
* @param {Object} outputFields 描述输出树形结构中字段名的对象。
|
|
22413
|
+
* @param {string} outputFields.idField 输出节点代码字段名 (接收 inputFields.idField 的值)。
|
|
22414
|
+
* @param {string} outputFields.textField 输出节点文本字段名 (接收 inputFields.textField 的值)。
|
|
22415
|
+
* @param {string} outputFields.childrenField 子节点数组字段名。
|
|
22416
|
+
* @param {string} outputFields.originDataField 原始数据字段名 (用于存储原始的 item 对象)。
|
|
22417
|
+
* @returns {Object[]} 转换后的树形结构数组。
|
|
22418
|
+
*/
|
|
22419
|
+
function arrayToTree(flatArray, inputFields, outputFields) {
|
|
22420
|
+
// 1. 参数校验和字段提取
|
|
22421
|
+
if (!Array.isArray(flatArray) || flatArray.length === 0) {
|
|
22422
|
+
return [];
|
|
22423
|
+
}
|
|
22424
|
+
|
|
22425
|
+
// 提取输入字段名
|
|
22426
|
+
const {
|
|
22427
|
+
idField: inputId,
|
|
22428
|
+
textField: inputText,
|
|
22429
|
+
parentField: inputParent
|
|
22430
|
+
} = inputFields;
|
|
22431
|
+
|
|
22432
|
+
// 提取输出字段名
|
|
22433
|
+
const {
|
|
22434
|
+
idField: outputId,
|
|
22435
|
+
textField: outputText,
|
|
22436
|
+
childrenField: outputChildren,
|
|
22437
|
+
originDataField: outputRawData // 新增:用于存储原始数据的字段名
|
|
22438
|
+
} = outputFields;
|
|
22439
|
+
|
|
22440
|
+
// 2. 初始化辅助数据结构
|
|
22441
|
+
const tree = [];
|
|
22442
|
+
const childrenMap = new Map(); // 存储所有节点,键为节点ID
|
|
22443
|
+
const rootCandidates = new Set(); // 存储所有可能的根节点ID
|
|
22444
|
+
|
|
22445
|
+
// 3. 第一次遍历:创建所有节点并建立 ID-Node 映射
|
|
22446
|
+
for (const item of flatArray) {
|
|
22447
|
+
// 关键:创建一个只包含转换后字段和子节点的结构
|
|
22448
|
+
const newNode = {
|
|
22449
|
+
// 映射输入字段到输出字段
|
|
22450
|
+
[outputId]: item[inputId],
|
|
22451
|
+
[outputText]: item[inputText],
|
|
22452
|
+
[outputChildren]: [], // 初始化子节点数组
|
|
22453
|
+
[outputRawData]: item // 新增:存储原始数据对象
|
|
22454
|
+
};
|
|
22455
|
+
|
|
22456
|
+
childrenMap.set(newNode[outputId], newNode);
|
|
22457
|
+
rootCandidates.add(newNode[outputId]); // 假设所有节点都是根节点
|
|
22458
|
+
}
|
|
22459
|
+
|
|
22460
|
+
// 4. 第二次遍历:连接父子关系
|
|
22461
|
+
for (const node of childrenMap.values()) {
|
|
22462
|
+
const parentId = node[outputRawData][inputParent]; // 从原始数据中获取父ID
|
|
22463
|
+
|
|
22464
|
+
// 查找父节点
|
|
22465
|
+
const parentNode = childrenMap.get(parentId);
|
|
22466
|
+
|
|
22467
|
+
if (parentNode) {
|
|
22468
|
+
// 如果找到了父节点,则将当前节点添加到父节点的子节点列表中
|
|
22469
|
+
parentNode[outputChildren].push(node);
|
|
22470
|
+
|
|
22471
|
+
// 如果当前节点有父节点,它就不是根节点,从根节点候选中移除
|
|
22472
|
+
rootCandidates.delete(node[outputId]);
|
|
22473
|
+
}
|
|
22474
|
+
}
|
|
22475
|
+
|
|
22476
|
+
// 5. 组装最终树结构
|
|
22477
|
+
// 最终的树结构由所有仍在 rootCandidates 列表中的节点组成
|
|
22478
|
+
for (const rootId of rootCandidates) {
|
|
22479
|
+
const rootNode = childrenMap.get(rootId);
|
|
22480
|
+
if (rootNode) {
|
|
22481
|
+
tree.push(rootNode);
|
|
22482
|
+
}
|
|
22483
|
+
}
|
|
22484
|
+
|
|
22485
|
+
return tree;
|
|
22486
|
+
}
|
|
22487
|
+
|
|
22488
|
+
/**
|
|
22489
|
+
* 在树形对象中查找指定名称的叶子节点值
|
|
22490
|
+
* @param {Object} tree - 被查找的对象(树形结构)
|
|
22491
|
+
* @param {String} leafName - 要查找的属性名称
|
|
22492
|
+
* @returns {any} - 查找到的叶节点内容,未找到返回 undefined
|
|
22493
|
+
*/
|
|
22494
|
+
function getLeafValue(tree, leafName) {
|
|
22495
|
+
// 边界检查:如果 tree 不是对象或者是 null,无法查找
|
|
22496
|
+
if (typeof tree !== 'object' || tree === null) {
|
|
22497
|
+
return undefined;
|
|
22498
|
+
}
|
|
22499
|
+
|
|
22500
|
+
// 遍历当前层级的键
|
|
22501
|
+
for (const key in tree) {
|
|
22502
|
+
const value = tree[key];
|
|
22503
|
+
|
|
22504
|
+
// 判断当前值是否为“结构节点”(即中间节点:非数组的非空对象)
|
|
22505
|
+
// 这里假设数组是数据(叶子),普通对象是结构(中间节点)
|
|
22506
|
+
const isStructuralNode = typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
22507
|
+
|
|
22508
|
+
// 1. 如果 key 匹配
|
|
22509
|
+
if (key === leafName) {
|
|
22510
|
+
// 且它不是中间节点(即它是叶子节点),则直接返回
|
|
22511
|
+
if (!isStructuralNode) {
|
|
22512
|
+
return value;
|
|
22513
|
+
}
|
|
22514
|
+
// 如果 key 匹配但是它是中间节点(Object),根据要求“不包括中间节点”,
|
|
22515
|
+
// 我们不返回它,而是继续在这个对象内部递归查找(进入下方的递归逻辑)
|
|
22516
|
+
}
|
|
22517
|
+
|
|
22518
|
+
// 2. 如果当前值是对象(中间节点),则递归查找
|
|
22519
|
+
if (isStructuralNode) {
|
|
22520
|
+
const result = getLeafValue(value, leafName);
|
|
22521
|
+
// 如果在深层找到了结果,直接返回(冒泡上来)
|
|
22522
|
+
if (result !== undefined) {
|
|
22523
|
+
return result;
|
|
22524
|
+
}
|
|
22525
|
+
}
|
|
22526
|
+
}
|
|
22527
|
+
|
|
22528
|
+
return undefined; // 遍历完未找到
|
|
22529
|
+
}
|
|
22530
|
+
|
|
22531
|
+
/**
|
|
22532
|
+
* 在树形对象中查找任意指定名称的节点(包含中间节点和叶子节点)
|
|
22533
|
+
* @param {Object} tree - 被查找的对象
|
|
22534
|
+
* @param {String} nodeName - 要查找的属性名称
|
|
22535
|
+
* @returns {any} - 查找到的节点内容(可能是值,也可能是对象),未找到返回 undefined
|
|
22536
|
+
*/
|
|
22537
|
+
function getNodeValue(tree, nodeName) {
|
|
22538
|
+
// 边界检查
|
|
22539
|
+
if (typeof tree !== 'object' || tree === null) {
|
|
22540
|
+
return undefined;
|
|
22541
|
+
}
|
|
22542
|
+
|
|
22543
|
+
// 1. 优先检查当前层级是否存在该 key (广度优先视角的微调,可选)
|
|
22544
|
+
// 如果你希望优先匹配当前层级,而不是先钻入第一个子对象里找,可以取消下面这行的注释:
|
|
22545
|
+
// if (tree.hasOwnProperty(nodeName)) return tree[nodeName];
|
|
22546
|
+
|
|
22547
|
+
// 遍历当前对象
|
|
22548
|
+
for (const key in tree) {
|
|
22549
|
+
const value = tree[key];
|
|
22550
|
+
|
|
22551
|
+
// 1. 只要 Key 匹配,立刻返回 Value
|
|
22552
|
+
// 无论 value 是字符串、数字、还是另一个对象,都视为找到了
|
|
22553
|
+
if (key === nodeName) {
|
|
22554
|
+
return value;
|
|
22555
|
+
}
|
|
22556
|
+
|
|
22557
|
+
// 2. 如果不匹配,且 value 是对象(具备子结构),则递归查找
|
|
22558
|
+
// 注意:这里通常建议排除数组,除非你确定数组里也包含带 key 的对象
|
|
22559
|
+
if (typeof value === 'object' && value !== null) {
|
|
22560
|
+
const result = getNodeValue(value, nodeName);
|
|
22561
|
+
if (result !== undefined) {
|
|
22562
|
+
return result;
|
|
22563
|
+
}
|
|
22564
|
+
}
|
|
22565
|
+
}
|
|
22566
|
+
|
|
22567
|
+
return undefined;
|
|
22568
|
+
}
|
|
22569
|
+
|
|
22570
|
+
/**
|
|
22571
|
+
* 深度非侵入式合并函数 (Deep Non-Destructive Merge)
|
|
22572
|
+
* * 特点:
|
|
22573
|
+
* 1. 深度递归地合并对象属性。
|
|
22574
|
+
* 2. 源对象中缺失的属性不会删除或覆盖目标对象中已存在的对应属性。
|
|
22575
|
+
* 3. 数组会被源对象中的数组完全覆盖。
|
|
22576
|
+
* * @param {Object} target 目标对象(将被修改)
|
|
22577
|
+
* @param {Object} source 源对象
|
|
22578
|
+
* @returns {Object} 修改后的目标对象
|
|
22579
|
+
*/
|
|
22580
|
+
function deepMerge(target, source) {
|
|
22581
|
+
// 确保源对象是一个有效的对象,如果不是则直接返回目标对象
|
|
22582
|
+
if (typeof source !== 'object' || source === null) {
|
|
22583
|
+
return target;
|
|
22584
|
+
}
|
|
22585
|
+
|
|
22586
|
+
for (const key in source) {
|
|
22587
|
+
// 确保只处理源对象自身的属性
|
|
22588
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
22589
|
+
const sourceValue = source[key];
|
|
22590
|
+
const targetValue = target[key];
|
|
22591
|
+
|
|
22592
|
+
// 1. 如果源属性的值是对象且目标属性的值也是对象,则递归合并
|
|
22593
|
+
if (
|
|
22594
|
+
typeof sourceValue === 'object' && sourceValue !== null &&
|
|
22595
|
+
!Array.isArray(sourceValue) &&
|
|
22596
|
+
typeof targetValue === 'object' && targetValue !== null &&
|
|
22597
|
+
!Array.isArray(targetValue)
|
|
22598
|
+
) {
|
|
22599
|
+
// 递归调用自身进行深度合并
|
|
22600
|
+
target[key] = deepMerge(targetValue, sourceValue);
|
|
22601
|
+
}
|
|
22602
|
+
// 2. 对于其他类型(基本类型、数组、null),直接覆盖
|
|
22603
|
+
else {
|
|
22604
|
+
// 这里的关键是:只有源对象中存在的属性,才会覆盖目标对象的属性。
|
|
22605
|
+
// 如果源对象中不存在某个键(key),则不会进入此循环,
|
|
22606
|
+
// 从而目标对象中已有的该属性得以保留。
|
|
22607
|
+
target[key] = sourceValue;
|
|
22608
|
+
}
|
|
22609
|
+
}
|
|
22610
|
+
}
|
|
22611
|
+
|
|
22612
|
+
return target;
|
|
22613
|
+
}
|
|
22614
|
+
var deepClone = {
|
|
22615
|
+
deepClone: deepClone$1,
|
|
22616
|
+
deepCloneAndMap,
|
|
22617
|
+
typeOfValue,
|
|
22618
|
+
isJsonString,
|
|
22619
|
+
flattenTreeValues,
|
|
22620
|
+
arrayToTree,
|
|
22621
|
+
getLeafValue,
|
|
22622
|
+
getNodeValue,
|
|
22623
|
+
deepMerge
|
|
22624
|
+
};
|
|
22625
|
+
|
|
22626
|
+
var unclassified = {
|
|
22627
|
+
deepClone};
|
|
22628
|
+
|
|
22629
|
+
// with-table数据模板
|
|
22630
|
+
|
|
22631
|
+
const {
|
|
22632
|
+
proxy
|
|
22633
|
+
} = getCurrentInstance();
|
|
22634
|
+
const ly0default$4 = {
|
|
22635
|
+
pageSize: 10
|
|
22636
|
+
};
|
|
22637
|
+
|
|
22638
|
+
// 数据刷新
|
|
22639
|
+
const refresh = async _ref => {
|
|
22640
|
+
let {
|
|
22641
|
+
scopeThis
|
|
22642
|
+
} = _ref;
|
|
22643
|
+
const result = await ly0request.ly0.storpro({
|
|
22644
|
+
storproName: scopeThis.storpro.refresh,
|
|
22645
|
+
data: {
|
|
22646
|
+
query: scopeThis.query && scopeThis.query.formData ? scopeThis.query.formData : null,
|
|
22647
|
+
sort: scopeThis.query && scopeThis.query.sort ? scopeThis.query.sort : null,
|
|
22648
|
+
limit: scopeThis.query && scopeThis.query.pageSize ? scopeThis.query.pageSize : ly0default$4.pageSize,
|
|
22649
|
+
page: scopeThis.query && scopeThis.query.currentPage ? scopeThis.query.currentPage : 1
|
|
22650
|
+
}
|
|
22651
|
+
});
|
|
22652
|
+
if (result.code === 0) {
|
|
22653
|
+
scopeThis.tableData = {
|
|
22654
|
+
data: result.data,
|
|
22655
|
+
total: result.total
|
|
22656
|
+
};
|
|
22657
|
+
}
|
|
22658
|
+
return {
|
|
22659
|
+
code: result.code,
|
|
22660
|
+
message: result.message
|
|
22661
|
+
};
|
|
22662
|
+
};
|
|
22663
|
+
|
|
22664
|
+
// 数据重载
|
|
22665
|
+
const reload = async _ref2 => {
|
|
22666
|
+
let {
|
|
22667
|
+
scopeThis
|
|
22668
|
+
} = _ref2;
|
|
22669
|
+
scopeThis.query = scopeThis.queryInit ? unclassified.deepClone.deepClone(scopeThis.queryInit) : null;
|
|
22670
|
+
const result = await refresh({
|
|
22671
|
+
scopeThis
|
|
22672
|
+
});
|
|
22673
|
+
proxy.$message(result.code === 0 ? '数据已重载' : '数据重载错误');
|
|
22674
|
+
};
|
|
22675
|
+
|
|
22676
|
+
// 获取页面数据附加
|
|
22677
|
+
const getPgData = async _ref3 => {
|
|
22678
|
+
let {
|
|
22679
|
+
scopeThis
|
|
22680
|
+
} = _ref3;
|
|
22681
|
+
const result = await ly0request.ly0.storpro({
|
|
22682
|
+
storproName: scopeThis.storpro.getPgData,
|
|
22683
|
+
data: scopeThis.pgData && scopeThis.pgData.query ? scopeThis.pgData.query : null
|
|
22684
|
+
});
|
|
22685
|
+
if (result.code === 0) {
|
|
22686
|
+
scopeThis.pgData = unclassified.deepClone.deepMerge(scopeThis.pgData, {
|
|
22687
|
+
data: result.data
|
|
22688
|
+
});
|
|
22689
|
+
proxy.$message('已获取页面数据');
|
|
22690
|
+
return;
|
|
22691
|
+
}
|
|
22692
|
+
proxy.$message('获取页面数据错误');
|
|
22693
|
+
};
|
|
22694
|
+
|
|
22695
|
+
// 初始化
|
|
22696
|
+
const init = async _ref4 => {
|
|
22697
|
+
let {
|
|
22698
|
+
scopeThis
|
|
22699
|
+
} = _ref4;
|
|
22700
|
+
if (scopeThis.pgData) {
|
|
22701
|
+
await getPgData({
|
|
22702
|
+
scopeThis
|
|
22703
|
+
});
|
|
22704
|
+
}
|
|
22705
|
+
await reload({
|
|
22706
|
+
scopeThis
|
|
22707
|
+
});
|
|
22708
|
+
};
|
|
22709
|
+
|
|
22710
|
+
// 弹出 - 查询
|
|
22711
|
+
const popupFind = async _ref5 => {
|
|
22712
|
+
let {
|
|
22713
|
+
scopeThis
|
|
22714
|
+
} = _ref5;
|
|
22715
|
+
scopeThis.formData = scopeThis.query && scopeThis.query.formData ? unclassified.deepClone.deepClone(scopeThis.query.formData) : null;
|
|
22716
|
+
scopeThis.TableProps.query.sort = scopeThis.query && scopeThis.query.sort ? JSON.parse(JSON.stringify(scopeThis.query.sort)) : null;
|
|
22717
|
+
scopeThis.TableProps.query.pageSize = scopeThis.query && scopeThis.query.pageSize ? scopeThis.query.pageSize : ly0default$4.pageSize;
|
|
22718
|
+
scopeThis.TableProps.query.currentPage = scopeThis.query && scopeThis.query.currentPage ? scopeThis.query.currentPage : 1;
|
|
22719
|
+
scopeThis.formProps = unclassified.deepClone.deepClone(scopeThis.find.formProps);
|
|
22720
|
+
// 弹出窗口
|
|
22721
|
+
scopeThis.formProps.popup = unclassified.deepClone.deepMerge(scopeThis.formProps.popup, {
|
|
22722
|
+
visible: true
|
|
22723
|
+
});
|
|
22724
|
+
};
|
|
22725
|
+
|
|
22726
|
+
// 弹出 - 新增一条记录
|
|
22727
|
+
const popupInsertOne = async _ref6 => {
|
|
22728
|
+
let {
|
|
22729
|
+
scopeThis
|
|
22730
|
+
} = _ref6;
|
|
22731
|
+
scopeThis.formData = unclassified.deepClone.deepClone(scopeThis.insertOne.formData);
|
|
22732
|
+
scopeThis.formProps = unclassified.deepClone.deepClone(scopeThis.insertOne.formProps);
|
|
22733
|
+
// 弹出窗口
|
|
22734
|
+
scopeThis.formProps.popup = unclassified.deepClone.deepMerge(scopeThis.formProps.popup, {
|
|
22735
|
+
visible: true
|
|
22736
|
+
});
|
|
22737
|
+
};
|
|
22738
|
+
|
|
22739
|
+
// 弹出 - 修改一条记录
|
|
22740
|
+
const popupUpdateOne = async _ref7 => {
|
|
22741
|
+
let {
|
|
22742
|
+
scopeThis,
|
|
22743
|
+
formData
|
|
22744
|
+
} = _ref7;
|
|
22745
|
+
scopeThis.formData = unclassified.deepClone.deepClone(formData); // 继承行记录的值
|
|
22746
|
+
scopeThis.formProps = unclassified.deepClone.deepClone(scopeThis.UpdateOne.formProps);
|
|
22747
|
+
// 弹出窗口
|
|
22748
|
+
scopeThis.formProps.popup = unclassified.deepClone.deepMerge(scopeThis.formProps.popup, {
|
|
22749
|
+
visible: true
|
|
22750
|
+
});
|
|
22751
|
+
};
|
|
22752
|
+
|
|
22753
|
+
// 弹出 - 详细信息
|
|
22754
|
+
const popupDoc = async _ref8 => {
|
|
22755
|
+
let {
|
|
22756
|
+
scopeThis,
|
|
22757
|
+
formData
|
|
22758
|
+
} = _ref8;
|
|
22759
|
+
scopeThis.formData = unclassified.deepClone.deepClone(formData); // 继承行记录的值
|
|
22760
|
+
scopeThis.formProps = unclassified.deepClone.deepClone(scopeThis.doc.formProps);
|
|
22761
|
+
// 弹出窗口
|
|
22762
|
+
scopeThis.formProps.popup = unclassified.deepClone.deepMerge(scopeThis.formProps.popup, {
|
|
22763
|
+
visible: true
|
|
22764
|
+
});
|
|
22765
|
+
};
|
|
22766
|
+
|
|
22767
|
+
// 提交 - 查询
|
|
22768
|
+
const submitFind = async _ref9 => {
|
|
22769
|
+
let {
|
|
22770
|
+
scopeThis
|
|
22771
|
+
} = _ref9;
|
|
22772
|
+
scopeThis.query.formData = scopeThis.formData ? unclassified.deepClone.deepClone(scopeThis.formData) : null;
|
|
22773
|
+
scopeThis.query.sort = scopeThis.tableProps.query && scopeThis.tableProps.query.sort ? JSON.parse(JSON.stringify(scopeThis.tableProps.query.sort)) : null;
|
|
22774
|
+
scopeThis.query.pageSize = scopeThis.tableProps.query && scopeThis.tableProps.query.pageSize ? scopeThis.tableProps.query.pageSize : ly0default$4.pageSize;
|
|
22775
|
+
scopeThis.query.currentPage = scopeThis.tableProps.query && scopeThis.tableProps.query.currentPage ? scopeThis.tableProps.query.currentPage : 1;
|
|
22776
|
+
const result = await refresh({
|
|
22777
|
+
scopeThis
|
|
22778
|
+
});
|
|
22779
|
+
if (result.code === 0) {
|
|
22780
|
+
// 关闭表单窗口
|
|
22781
|
+
scopeThis.formProps.popup.visible = false;
|
|
22782
|
+
proxy.$message('查询已提交并刷新数据');
|
|
22783
|
+
} else {
|
|
22784
|
+
proxy.$message('查询错误');
|
|
22785
|
+
}
|
|
22786
|
+
};
|
|
22787
|
+
|
|
22788
|
+
// 提交 - 新增一条记录
|
|
22789
|
+
const submitInsertOne = async _ref0 => {
|
|
22790
|
+
let {
|
|
22791
|
+
scopeThis
|
|
22792
|
+
} = _ref0;
|
|
22793
|
+
try {
|
|
22794
|
+
await proxy.$confirm('新增一条记录, 提交?', '提示', {
|
|
22795
|
+
confirmButtonText: '确认',
|
|
22796
|
+
cancelButtonText: '取消',
|
|
22797
|
+
type: 'warning' // 警告图标
|
|
22798
|
+
});
|
|
22799
|
+
const result = await ly0request.ly0.storpro({
|
|
22800
|
+
storproName: scopeThis.storpro.insertOne,
|
|
22801
|
+
data: scopeThis.formData
|
|
22802
|
+
});
|
|
22803
|
+
if (result.code === 0) {
|
|
22804
|
+
// 关闭表单窗口
|
|
22805
|
+
scopeThis.formProps.popup.visible = false;
|
|
22806
|
+
proxy.$message('新增一条记录成功');
|
|
22807
|
+
scopeThis.query.currentPage = 1;
|
|
22808
|
+
scopeThis.tableData = {
|
|
22809
|
+
data: result.dataNew,
|
|
22810
|
+
total: 1
|
|
22811
|
+
};
|
|
22812
|
+
} else {
|
|
22813
|
+
proxy.$message('新增一条记录失败');
|
|
22814
|
+
}
|
|
22815
|
+
} catch (error) {
|
|
22816
|
+
proxy.$message('已取消');
|
|
22817
|
+
}
|
|
22818
|
+
};
|
|
22819
|
+
|
|
22820
|
+
// 提交 - 修改一条记录
|
|
22821
|
+
const submitUpdateOne = async _ref1 => {
|
|
22822
|
+
let {
|
|
22823
|
+
scopeThis
|
|
22824
|
+
} = _ref1;
|
|
22825
|
+
try {
|
|
22826
|
+
await proxy.$confirm('修改一条记录, 提交?', '提示', {
|
|
22827
|
+
confirmButtonText: '确认',
|
|
22828
|
+
cancelButtonText: '取消',
|
|
22829
|
+
type: 'warning' // 警告图标
|
|
22830
|
+
});
|
|
22831
|
+
const result = await ly0request.ly0.storpro({
|
|
22832
|
+
storproName: scopeThis.storpro.updateOne,
|
|
22833
|
+
data: scopeThis.formData
|
|
22834
|
+
});
|
|
22835
|
+
if (result.code === 0) {
|
|
22836
|
+
// 关闭表单窗口
|
|
22837
|
+
scopeThis.formProps.popup.visible = false;
|
|
22838
|
+
proxy.$message('修改一条记录成功');
|
|
22839
|
+
const resultRefresh = await refresh({
|
|
22840
|
+
scopeThis
|
|
22841
|
+
});
|
|
22842
|
+
if (resultRefresh.code === 0) {
|
|
22843
|
+
proxy.$message('已刷新数据');
|
|
22844
|
+
} else {
|
|
22845
|
+
proxy.$message('刷新错误');
|
|
22846
|
+
}
|
|
22847
|
+
} else {
|
|
22848
|
+
proxy.$message('修改一条记录失败');
|
|
22849
|
+
}
|
|
22850
|
+
} catch (error) {
|
|
22851
|
+
proxy.$message('已取消');
|
|
22852
|
+
}
|
|
22853
|
+
};
|
|
22854
|
+
|
|
22855
|
+
// 提交 - 删除一条记录
|
|
22856
|
+
const submitDeleteOne = async _ref10 => {
|
|
22857
|
+
let {
|
|
22858
|
+
scopeThis,
|
|
22859
|
+
formData
|
|
22860
|
+
} = _ref10;
|
|
22861
|
+
try {
|
|
22862
|
+
await proxy.$confirm('删除一条记录, 提交?', '警告', {
|
|
22863
|
+
confirmButtonText: '确认',
|
|
22864
|
+
cancelButtonText: '取消',
|
|
22865
|
+
type: 'warning' // 警告图标
|
|
22866
|
+
});
|
|
22867
|
+
const result = await ly0request.ly0.storpro({
|
|
22868
|
+
storproName: scopeThis.storpro.deleteOne,
|
|
22869
|
+
data: formData // 继承行记录的值
|
|
22870
|
+
});
|
|
22871
|
+
if (result.code === 0) {
|
|
22872
|
+
proxy.$message('删除一条记录成功');
|
|
22873
|
+
const resultRefresh = await refresh({
|
|
22874
|
+
scopeThis
|
|
22875
|
+
});
|
|
22876
|
+
if (resultRefresh.code === 0) {
|
|
22877
|
+
proxy.$message('已刷新数据');
|
|
22878
|
+
} else {
|
|
22879
|
+
proxy.$message('刷新错误');
|
|
22880
|
+
}
|
|
22881
|
+
} else {
|
|
22882
|
+
proxy.$message('删除一条记录失败');
|
|
22883
|
+
}
|
|
22884
|
+
} catch (error) {
|
|
22885
|
+
proxy.$message('已取消');
|
|
22886
|
+
}
|
|
22887
|
+
};
|
|
22888
|
+
var withTable = {
|
|
22889
|
+
refresh,
|
|
22890
|
+
reload,
|
|
22891
|
+
getPgData,
|
|
22892
|
+
init,
|
|
22893
|
+
popupFind,
|
|
22894
|
+
popupInsertOne,
|
|
22895
|
+
popupUpdateOne,
|
|
22896
|
+
popupDoc,
|
|
22897
|
+
submitFind,
|
|
22898
|
+
submitInsertOne,
|
|
22899
|
+
submitUpdateOne,
|
|
22900
|
+
submitDeleteOne
|
|
22901
|
+
};
|
|
22902
|
+
|
|
22139
22903
|
// 默认值
|
|
22140
22904
|
|
|
22141
22905
|
var ly0default$3 = {
|
|
@@ -23574,496 +24338,6 @@ return (_ctx, _cache) => {
|
|
|
23574
24338
|
|
|
23575
24339
|
script$i.__file = "src/form/Form.vue";
|
|
23576
24340
|
|
|
23577
|
-
// 引用标准:GB/T 2260
|
|
23578
|
-
|
|
23579
|
-
/**
|
|
23580
|
-
* 深度拷贝函数
|
|
23581
|
-
*
|
|
23582
|
-
* @param {any} obj 需要拷贝的对象、数组或基本类型值
|
|
23583
|
-
* @param {WeakMap} [cache=new WeakMap()] 用于处理循环引用的缓存
|
|
23584
|
-
* @returns {any} 深度拷贝后的新对象/新值
|
|
23585
|
-
*/
|
|
23586
|
-
function deepClone$1(obj, cache = new WeakMap()) {
|
|
23587
|
-
// 1. 基本类型值(包括 null)和函数,直接返回
|
|
23588
|
-
if (obj === null || typeof obj !== 'object') {
|
|
23589
|
-
return obj;
|
|
23590
|
-
}
|
|
23591
|
-
// 处理函数(尽管技术上函数是对象,但我们通常不克隆它,而是直接引用)
|
|
23592
|
-
if (typeof obj === 'function') {
|
|
23593
|
-
return obj;
|
|
23594
|
-
}
|
|
23595
|
-
|
|
23596
|
-
// 2. 检查循环引用
|
|
23597
|
-
// 如果缓存中已存在该对象,说明遇到了循环引用,直接返回缓存中的克隆对象
|
|
23598
|
-
if (cache.has(obj)) {
|
|
23599
|
-
return cache.get(obj);
|
|
23600
|
-
}
|
|
23601
|
-
|
|
23602
|
-
// 3. 处理特定内置对象(Date 和 RegExp)
|
|
23603
|
-
if (obj instanceof Date) {
|
|
23604
|
-
return new Date(obj.getTime());
|
|
23605
|
-
}
|
|
23606
|
-
if (obj instanceof RegExp) {
|
|
23607
|
-
// g: global, i: ignoreCase, m: multiline, u: unicode, y: sticky
|
|
23608
|
-
const flags = obj.global ? 'g' : ''
|
|
23609
|
-
+ obj.ignoreCase ? 'i' : ''
|
|
23610
|
-
+ obj.multiline ? 'm' : ''
|
|
23611
|
-
+ obj.unicode ? 'u' : ''
|
|
23612
|
-
+ obj.sticky ? 'y' : '';
|
|
23613
|
-
return new RegExp(obj.source, flags);
|
|
23614
|
-
}
|
|
23615
|
-
|
|
23616
|
-
// 4. 初始化克隆对象
|
|
23617
|
-
// 如果是数组,则初始化为空数组;否则初始化为空对象
|
|
23618
|
-
const clone = Array.isArray(obj) ? [] : {};
|
|
23619
|
-
|
|
23620
|
-
// 将克隆对象放入缓存,以处理接下来的递归调用中可能遇到的循环引用
|
|
23621
|
-
cache.set(obj, clone);
|
|
23622
|
-
|
|
23623
|
-
// 5. 递归拷贝属性
|
|
23624
|
-
for (const key in obj) {
|
|
23625
|
-
// 确保只处理对象自身的属性,排除原型链上的属性
|
|
23626
|
-
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
23627
|
-
clone[key] = deepClone$1(obj[key], cache);
|
|
23628
|
-
}
|
|
23629
|
-
}
|
|
23630
|
-
|
|
23631
|
-
// 6. 拷贝 Symbol 属性 (ES6/ES2015+)
|
|
23632
|
-
if (typeof Object.getOwnPropertySymbols === 'function') {
|
|
23633
|
-
Object.getOwnPropertySymbols(obj).forEach(sym => {
|
|
23634
|
-
clone[sym] = deepClone$1(obj[sym], cache);
|
|
23635
|
-
});
|
|
23636
|
-
}
|
|
23637
|
-
|
|
23638
|
-
return clone;
|
|
23639
|
-
}
|
|
23640
|
-
|
|
23641
|
-
/**
|
|
23642
|
-
* 深度拷贝函数,并在拷贝叶节点值时应用一个转换函数。
|
|
23643
|
-
*
|
|
23644
|
-
* @param {any} obj - 需要拷贝的对象、数组或基本类型值。
|
|
23645
|
-
* @param {function} valueMapper - 接收叶节点值作为参数,并返回新值的转换函数。
|
|
23646
|
-
* @param {WeakMap} [cache=new WeakMap()] - 用于处理循环引用的缓存。
|
|
23647
|
-
* @returns {any} 深度拷贝并转换后的新对象/新值。
|
|
23648
|
-
*/
|
|
23649
|
-
function deepCloneAndMap(obj, valueMapper, cache = new WeakMap()) {
|
|
23650
|
-
// 1. 基本类型值(包括 null)和函数,**应用 valueMapper**
|
|
23651
|
-
|
|
23652
|
-
// 如果是基本类型值(包括 null),则认为是叶节点,应用 valueMapper
|
|
23653
|
-
if (obj === null || typeof obj !== 'object') {
|
|
23654
|
-
// 对基本类型值应用转换函数
|
|
23655
|
-
return valueMapper ? valueMapper(obj) : obj;
|
|
23656
|
-
}
|
|
23657
|
-
|
|
23658
|
-
// 如果是函数,通常我们不克隆它,也不转换它的值,直接返回
|
|
23659
|
-
if (typeof obj === 'function') {
|
|
23660
|
-
return obj;
|
|
23661
|
-
}
|
|
23662
|
-
|
|
23663
|
-
// 2. 检查循环引用 (与原函数逻辑相同)
|
|
23664
|
-
if (cache.has(obj)) {
|
|
23665
|
-
return cache.get(obj);
|
|
23666
|
-
}
|
|
23667
|
-
|
|
23668
|
-
// 3. 处理特定内置对象(Date 和 RegExp),**应用 valueMapper**
|
|
23669
|
-
|
|
23670
|
-
// 对于 Date 和 RegExp 这种对象实例,我们通常将它们的**值**视为叶节点,
|
|
23671
|
-
// 克隆实例本身后,再对这个克隆实例应用 valueMapper。
|
|
23672
|
-
let clone;
|
|
23673
|
-
|
|
23674
|
-
if (obj instanceof Date) {
|
|
23675
|
-
clone = new Date(obj.getTime());
|
|
23676
|
-
} else if (obj instanceof RegExp) {
|
|
23677
|
-
// 提取 flags
|
|
23678
|
-
const flags = (obj.global ? 'g' : '')
|
|
23679
|
-
+ (obj.ignoreCase ? 'i' : '')
|
|
23680
|
-
+ (obj.multiline ? 'm' : '')
|
|
23681
|
-
+ (obj.unicode ? 'u' : '')
|
|
23682
|
-
+ (obj.sticky ? 'y' : '');
|
|
23683
|
-
clone = new RegExp(obj.source, flags);
|
|
23684
|
-
}
|
|
23685
|
-
|
|
23686
|
-
// 检查是否是内置对象(Date/RegExp),如果是,对克隆后的实例应用转换
|
|
23687
|
-
if (clone) {
|
|
23688
|
-
return valueMapper ? valueMapper(clone) : clone;
|
|
23689
|
-
}
|
|
23690
|
-
|
|
23691
|
-
// 4. 初始化克隆对象 (普通对象和数组)
|
|
23692
|
-
clone = Array.isArray(obj) ? [] : {};
|
|
23693
|
-
|
|
23694
|
-
// 将克隆对象放入缓存
|
|
23695
|
-
cache.set(obj, clone);
|
|
23696
|
-
|
|
23697
|
-
// 5. 递归拷贝属性
|
|
23698
|
-
for (const key in obj) {
|
|
23699
|
-
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
23700
|
-
// 递归调用自身,对子属性进行深拷贝和转换
|
|
23701
|
-
clone[key] = deepCloneAndMap(obj[key], valueMapper, cache);
|
|
23702
|
-
}
|
|
23703
|
-
}
|
|
23704
|
-
|
|
23705
|
-
// 6. 拷贝 Symbol 属性
|
|
23706
|
-
if (typeof Object.getOwnPropertySymbols === 'function') {
|
|
23707
|
-
Object.getOwnPropertySymbols(obj).forEach(sym => {
|
|
23708
|
-
// 递归调用自身,对 Symbol 属性的值进行深拷贝和转换
|
|
23709
|
-
clone[sym] = deepCloneAndMap(obj[sym], valueMapper, cache);
|
|
23710
|
-
});
|
|
23711
|
-
}
|
|
23712
|
-
|
|
23713
|
-
// 7. 返回深度克隆并转换后的对象/数组
|
|
23714
|
-
return clone;
|
|
23715
|
-
}
|
|
23716
|
-
|
|
23717
|
-
/**
|
|
23718
|
-
* 健壮的数据类型判断函数
|
|
23719
|
-
*
|
|
23720
|
-
* @param {any} value 需要判断类型的值
|
|
23721
|
-
* @returns {string} 小写的类型字符串,例如:'string', 'number', 'array', 'function', 'object', 'null', 'undefined'
|
|
23722
|
-
*/
|
|
23723
|
-
function typeOfValue(value) {
|
|
23724
|
-
// 1. 处理基本类型值(typeof 准确的部分)
|
|
23725
|
-
const type = typeof value;
|
|
23726
|
-
|
|
23727
|
-
// 'string', 'number', 'boolean', 'symbol', 'bigint', 'function', 'undefined'
|
|
23728
|
-
if (type !== 'object') {
|
|
23729
|
-
return type;
|
|
23730
|
-
}
|
|
23731
|
-
|
|
23732
|
-
// 2. 处理 null (typeof 的缺陷:typeof null === 'object')
|
|
23733
|
-
if (value === null) {
|
|
23734
|
-
return 'null';
|
|
23735
|
-
}
|
|
23736
|
-
|
|
23737
|
-
// 3. 处理对象类型值 (使用 Object.prototype.toString.call() 获取内部 [[Class]] 属性)
|
|
23738
|
-
// 结果格式为:[object Class]
|
|
23739
|
-
const classString = Object.prototype.toString.call(value);
|
|
23740
|
-
|
|
23741
|
-
// 提取 'Array', 'Date', 'RegExp', 'Map', 'Set', 'Object' 等 Class 名称
|
|
23742
|
-
const className = classString.slice(8, -1);
|
|
23743
|
-
|
|
23744
|
-
// 4. 特殊处理 Array.isArray()
|
|
23745
|
-
// 虽然 className 已经是 'Array',但 Array.isArray 更快且明确
|
|
23746
|
-
if (className === 'Array') {
|
|
23747
|
-
return 'array';
|
|
23748
|
-
}
|
|
23749
|
-
|
|
23750
|
-
// 5. 将其他内置对象和普通对象名称转为小写返回
|
|
23751
|
-
return className.toLowerCase();
|
|
23752
|
-
}
|
|
23753
|
-
|
|
23754
|
-
/**
|
|
23755
|
-
* 判断一个字符串是否是有效的 JSON 格式
|
|
23756
|
-
*
|
|
23757
|
-
* @param {string} strValue - 要检查的字符串值
|
|
23758
|
-
* @returns {boolean} 如果字符串是有效的 JSON 格式则返回 true,否则返回 false
|
|
23759
|
-
*/
|
|
23760
|
-
function isJsonString(strValue) {
|
|
23761
|
-
// 1. 确保输入是一个字符串,如果不是,则直接返回 false
|
|
23762
|
-
// JSON 格式只能是字符串
|
|
23763
|
-
if (typeof strValue !== 'string') {
|
|
23764
|
-
return false;
|
|
23765
|
-
}
|
|
23766
|
-
|
|
23767
|
-
// 2. 尝试解析字符串
|
|
23768
|
-
try {
|
|
23769
|
-
// 使用 JSON.parse() 尝试解析。
|
|
23770
|
-
// 如果解析成功,它就会返回解析后的对象或值。
|
|
23771
|
-
// 如果解析失败,就会抛出 SyntaxError 错误。
|
|
23772
|
-
JSON.parse(strValue);
|
|
23773
|
-
|
|
23774
|
-
// 3. 额外的检查 (可选但推荐):
|
|
23775
|
-
// 确保解析结果不是原始类型值,例如 "123" 或 "true"
|
|
23776
|
-
// 某些场景下,用户希望 JSON 字符串必须是对象或数组的字符串表示
|
|
23777
|
-
// const parsed = JSON.parse(strValue);
|
|
23778
|
-
// if (typeof parsed !== 'object' || parsed === null) {
|
|
23779
|
-
// // 如果你想排除 "123", "null", "true" 这些原始值的 JSON 字符串,可以取消注释
|
|
23780
|
-
// // return false;
|
|
23781
|
-
// }
|
|
23782
|
-
|
|
23783
|
-
} catch (e) {
|
|
23784
|
-
// 捕获任何解析错误(通常是 SyntaxError),表示格式不符合 JSON 规范
|
|
23785
|
-
return false;
|
|
23786
|
-
}
|
|
23787
|
-
|
|
23788
|
-
// 4. 解析成功,返回 true
|
|
23789
|
-
return true;
|
|
23790
|
-
}
|
|
23791
|
-
|
|
23792
|
-
/**
|
|
23793
|
-
* 遍历树形结构(对象或数组)的所有叶节点,将叶节点的值收集到数组中。
|
|
23794
|
-
*
|
|
23795
|
-
* 叶节点被定义为:值不是普通对象或数组的节点。
|
|
23796
|
-
*
|
|
23797
|
-
* @param {object|Array} tree - 要扁平化处理的树形结构对象或数组。
|
|
23798
|
-
* @param {Array<any>} [result=[]] - 存储叶节点值的数组(递归内部使用)。
|
|
23799
|
-
* @returns {Array<any>} 包含所有叶节点值的数组。
|
|
23800
|
-
*/
|
|
23801
|
-
function flattenTreeValues(tree, result = []) {
|
|
23802
|
-
// 确保输入是对象或数组。如果不是,或者为 null,则直接返回结果数组。
|
|
23803
|
-
if (typeof tree !== 'object' || tree === null) {
|
|
23804
|
-
return result;
|
|
23805
|
-
}
|
|
23806
|
-
|
|
23807
|
-
// 遍历对象的键(如果是数组,键就是索引)
|
|
23808
|
-
for (const key in tree) {
|
|
23809
|
-
// 确保只处理对象自身的属性,排除原型链上的属性
|
|
23810
|
-
if (Object.prototype.hasOwnProperty.call(tree, key)) {
|
|
23811
|
-
const value = tree[key];
|
|
23812
|
-
|
|
23813
|
-
// 判断当前值是否为“叶节点”
|
|
23814
|
-
// 检查:值不是对象,且值不是 null
|
|
23815
|
-
if (typeof value !== 'object' || value === null) {
|
|
23816
|
-
// 1. 如果是基本类型值(叶节点),则将其压入结果数组
|
|
23817
|
-
result.push(value);
|
|
23818
|
-
} else {
|
|
23819
|
-
// 2. 如果是对象或数组(非叶节点),则进行递归调用
|
|
23820
|
-
// 注意:这里没有处理 Date, RegExp, Set, Map 等特殊对象,
|
|
23821
|
-
// 默认将它们视为非叶节点,直到它们内部的值被遍历完(这通常是不对的)
|
|
23822
|
-
//
|
|
23823
|
-
// 为了更精确地实现“叶节点”概念,我们将 Date, RegExp 等内置对象视为叶节点:
|
|
23824
|
-
const isArray = Array.isArray(value);
|
|
23825
|
-
const isPlainObject = !isArray && Object.prototype.toString.call(value) === '[object Object]';
|
|
23826
|
-
|
|
23827
|
-
if (isArray || isPlainObject) {
|
|
23828
|
-
// 如果是普通数组或普通对象,则递归
|
|
23829
|
-
flattenTreeValues(value, result);
|
|
23830
|
-
} else {
|
|
23831
|
-
// 如果是像 Date, RegExp, Map, Set 等特殊内置对象,
|
|
23832
|
-
// 我们将其视为叶节点的值(而不是继续遍历其内部结构)
|
|
23833
|
-
result.push(value);
|
|
23834
|
-
}
|
|
23835
|
-
}
|
|
23836
|
-
}
|
|
23837
|
-
}
|
|
23838
|
-
|
|
23839
|
-
return result;
|
|
23840
|
-
}
|
|
23841
|
-
|
|
23842
|
-
/**
|
|
23843
|
-
* 将扁平化的对象数组转换为树形结构数据,并在每个节点中保留原始数据。
|
|
23844
|
-
*
|
|
23845
|
-
* @param {Object[]} flatArray 待转化的扁平数组。
|
|
23846
|
-
* @param {Object} inputFields 描述输入数组中字段名的对象。
|
|
23847
|
-
* @param {string} inputFields.idField 节点代码字段名。
|
|
23848
|
-
* @param {string} inputFields.textField 节点文本字段名。
|
|
23849
|
-
* @param {string} inputFields.parentField 父节点代码字段名。
|
|
23850
|
-
* @param {Object} outputFields 描述输出树形结构中字段名的对象。
|
|
23851
|
-
* @param {string} outputFields.idField 输出节点代码字段名 (接收 inputFields.idField 的值)。
|
|
23852
|
-
* @param {string} outputFields.textField 输出节点文本字段名 (接收 inputFields.textField 的值)。
|
|
23853
|
-
* @param {string} outputFields.childrenField 子节点数组字段名。
|
|
23854
|
-
* @param {string} outputFields.originDataField 原始数据字段名 (用于存储原始的 item 对象)。
|
|
23855
|
-
* @returns {Object[]} 转换后的树形结构数组。
|
|
23856
|
-
*/
|
|
23857
|
-
function arrayToTree(flatArray, inputFields, outputFields) {
|
|
23858
|
-
// 1. 参数校验和字段提取
|
|
23859
|
-
if (!Array.isArray(flatArray) || flatArray.length === 0) {
|
|
23860
|
-
return [];
|
|
23861
|
-
}
|
|
23862
|
-
|
|
23863
|
-
// 提取输入字段名
|
|
23864
|
-
const {
|
|
23865
|
-
idField: inputId,
|
|
23866
|
-
textField: inputText,
|
|
23867
|
-
parentField: inputParent
|
|
23868
|
-
} = inputFields;
|
|
23869
|
-
|
|
23870
|
-
// 提取输出字段名
|
|
23871
|
-
const {
|
|
23872
|
-
idField: outputId,
|
|
23873
|
-
textField: outputText,
|
|
23874
|
-
childrenField: outputChildren,
|
|
23875
|
-
originDataField: outputRawData // 新增:用于存储原始数据的字段名
|
|
23876
|
-
} = outputFields;
|
|
23877
|
-
|
|
23878
|
-
// 2. 初始化辅助数据结构
|
|
23879
|
-
const tree = [];
|
|
23880
|
-
const childrenMap = new Map(); // 存储所有节点,键为节点ID
|
|
23881
|
-
const rootCandidates = new Set(); // 存储所有可能的根节点ID
|
|
23882
|
-
|
|
23883
|
-
// 3. 第一次遍历:创建所有节点并建立 ID-Node 映射
|
|
23884
|
-
for (const item of flatArray) {
|
|
23885
|
-
// 关键:创建一个只包含转换后字段和子节点的结构
|
|
23886
|
-
const newNode = {
|
|
23887
|
-
// 映射输入字段到输出字段
|
|
23888
|
-
[outputId]: item[inputId],
|
|
23889
|
-
[outputText]: item[inputText],
|
|
23890
|
-
[outputChildren]: [], // 初始化子节点数组
|
|
23891
|
-
[outputRawData]: item // 新增:存储原始数据对象
|
|
23892
|
-
};
|
|
23893
|
-
|
|
23894
|
-
childrenMap.set(newNode[outputId], newNode);
|
|
23895
|
-
rootCandidates.add(newNode[outputId]); // 假设所有节点都是根节点
|
|
23896
|
-
}
|
|
23897
|
-
|
|
23898
|
-
// 4. 第二次遍历:连接父子关系
|
|
23899
|
-
for (const node of childrenMap.values()) {
|
|
23900
|
-
const parentId = node[outputRawData][inputParent]; // 从原始数据中获取父ID
|
|
23901
|
-
|
|
23902
|
-
// 查找父节点
|
|
23903
|
-
const parentNode = childrenMap.get(parentId);
|
|
23904
|
-
|
|
23905
|
-
if (parentNode) {
|
|
23906
|
-
// 如果找到了父节点,则将当前节点添加到父节点的子节点列表中
|
|
23907
|
-
parentNode[outputChildren].push(node);
|
|
23908
|
-
|
|
23909
|
-
// 如果当前节点有父节点,它就不是根节点,从根节点候选中移除
|
|
23910
|
-
rootCandidates.delete(node[outputId]);
|
|
23911
|
-
}
|
|
23912
|
-
}
|
|
23913
|
-
|
|
23914
|
-
// 5. 组装最终树结构
|
|
23915
|
-
// 最终的树结构由所有仍在 rootCandidates 列表中的节点组成
|
|
23916
|
-
for (const rootId of rootCandidates) {
|
|
23917
|
-
const rootNode = childrenMap.get(rootId);
|
|
23918
|
-
if (rootNode) {
|
|
23919
|
-
tree.push(rootNode);
|
|
23920
|
-
}
|
|
23921
|
-
}
|
|
23922
|
-
|
|
23923
|
-
return tree;
|
|
23924
|
-
}
|
|
23925
|
-
|
|
23926
|
-
/**
|
|
23927
|
-
* 在树形对象中查找指定名称的叶子节点值
|
|
23928
|
-
* @param {Object} tree - 被查找的对象(树形结构)
|
|
23929
|
-
* @param {String} leafName - 要查找的属性名称
|
|
23930
|
-
* @returns {any} - 查找到的叶节点内容,未找到返回 undefined
|
|
23931
|
-
*/
|
|
23932
|
-
function getLeafValue(tree, leafName) {
|
|
23933
|
-
// 边界检查:如果 tree 不是对象或者是 null,无法查找
|
|
23934
|
-
if (typeof tree !== 'object' || tree === null) {
|
|
23935
|
-
return undefined;
|
|
23936
|
-
}
|
|
23937
|
-
|
|
23938
|
-
// 遍历当前层级的键
|
|
23939
|
-
for (const key in tree) {
|
|
23940
|
-
const value = tree[key];
|
|
23941
|
-
|
|
23942
|
-
// 判断当前值是否为“结构节点”(即中间节点:非数组的非空对象)
|
|
23943
|
-
// 这里假设数组是数据(叶子),普通对象是结构(中间节点)
|
|
23944
|
-
const isStructuralNode = typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
23945
|
-
|
|
23946
|
-
// 1. 如果 key 匹配
|
|
23947
|
-
if (key === leafName) {
|
|
23948
|
-
// 且它不是中间节点(即它是叶子节点),则直接返回
|
|
23949
|
-
if (!isStructuralNode) {
|
|
23950
|
-
return value;
|
|
23951
|
-
}
|
|
23952
|
-
// 如果 key 匹配但是它是中间节点(Object),根据要求“不包括中间节点”,
|
|
23953
|
-
// 我们不返回它,而是继续在这个对象内部递归查找(进入下方的递归逻辑)
|
|
23954
|
-
}
|
|
23955
|
-
|
|
23956
|
-
// 2. 如果当前值是对象(中间节点),则递归查找
|
|
23957
|
-
if (isStructuralNode) {
|
|
23958
|
-
const result = getLeafValue(value, leafName);
|
|
23959
|
-
// 如果在深层找到了结果,直接返回(冒泡上来)
|
|
23960
|
-
if (result !== undefined) {
|
|
23961
|
-
return result;
|
|
23962
|
-
}
|
|
23963
|
-
}
|
|
23964
|
-
}
|
|
23965
|
-
|
|
23966
|
-
return undefined; // 遍历完未找到
|
|
23967
|
-
}
|
|
23968
|
-
|
|
23969
|
-
/**
|
|
23970
|
-
* 在树形对象中查找任意指定名称的节点(包含中间节点和叶子节点)
|
|
23971
|
-
* @param {Object} tree - 被查找的对象
|
|
23972
|
-
* @param {String} nodeName - 要查找的属性名称
|
|
23973
|
-
* @returns {any} - 查找到的节点内容(可能是值,也可能是对象),未找到返回 undefined
|
|
23974
|
-
*/
|
|
23975
|
-
function getNodeValue(tree, nodeName) {
|
|
23976
|
-
// 边界检查
|
|
23977
|
-
if (typeof tree !== 'object' || tree === null) {
|
|
23978
|
-
return undefined;
|
|
23979
|
-
}
|
|
23980
|
-
|
|
23981
|
-
// 1. 优先检查当前层级是否存在该 key (广度优先视角的微调,可选)
|
|
23982
|
-
// 如果你希望优先匹配当前层级,而不是先钻入第一个子对象里找,可以取消下面这行的注释:
|
|
23983
|
-
// if (tree.hasOwnProperty(nodeName)) return tree[nodeName];
|
|
23984
|
-
|
|
23985
|
-
// 遍历当前对象
|
|
23986
|
-
for (const key in tree) {
|
|
23987
|
-
const value = tree[key];
|
|
23988
|
-
|
|
23989
|
-
// 1. 只要 Key 匹配,立刻返回 Value
|
|
23990
|
-
// 无论 value 是字符串、数字、还是另一个对象,都视为找到了
|
|
23991
|
-
if (key === nodeName) {
|
|
23992
|
-
return value;
|
|
23993
|
-
}
|
|
23994
|
-
|
|
23995
|
-
// 2. 如果不匹配,且 value 是对象(具备子结构),则递归查找
|
|
23996
|
-
// 注意:这里通常建议排除数组,除非你确定数组里也包含带 key 的对象
|
|
23997
|
-
if (typeof value === 'object' && value !== null) {
|
|
23998
|
-
const result = getNodeValue(value, nodeName);
|
|
23999
|
-
if (result !== undefined) {
|
|
24000
|
-
return result;
|
|
24001
|
-
}
|
|
24002
|
-
}
|
|
24003
|
-
}
|
|
24004
|
-
|
|
24005
|
-
return undefined;
|
|
24006
|
-
}
|
|
24007
|
-
|
|
24008
|
-
/**
|
|
24009
|
-
* 深度非侵入式合并函数 (Deep Non-Destructive Merge)
|
|
24010
|
-
* * 特点:
|
|
24011
|
-
* 1. 深度递归地合并对象属性。
|
|
24012
|
-
* 2. 源对象中缺失的属性不会删除或覆盖目标对象中已存在的对应属性。
|
|
24013
|
-
* 3. 数组会被源对象中的数组完全覆盖。
|
|
24014
|
-
* * @param {Object} target 目标对象(将被修改)
|
|
24015
|
-
* @param {Object} source 源对象
|
|
24016
|
-
* @returns {Object} 修改后的目标对象
|
|
24017
|
-
*/
|
|
24018
|
-
function deepMerge(target, source) {
|
|
24019
|
-
// 确保源对象是一个有效的对象,如果不是则直接返回目标对象
|
|
24020
|
-
if (typeof source !== 'object' || source === null) {
|
|
24021
|
-
return target;
|
|
24022
|
-
}
|
|
24023
|
-
|
|
24024
|
-
for (const key in source) {
|
|
24025
|
-
// 确保只处理源对象自身的属性
|
|
24026
|
-
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
24027
|
-
const sourceValue = source[key];
|
|
24028
|
-
const targetValue = target[key];
|
|
24029
|
-
|
|
24030
|
-
// 1. 如果源属性的值是对象且目标属性的值也是对象,则递归合并
|
|
24031
|
-
if (
|
|
24032
|
-
typeof sourceValue === 'object' && sourceValue !== null &&
|
|
24033
|
-
!Array.isArray(sourceValue) &&
|
|
24034
|
-
typeof targetValue === 'object' && targetValue !== null &&
|
|
24035
|
-
!Array.isArray(targetValue)
|
|
24036
|
-
) {
|
|
24037
|
-
// 递归调用自身进行深度合并
|
|
24038
|
-
target[key] = deepMerge(targetValue, sourceValue);
|
|
24039
|
-
}
|
|
24040
|
-
// 2. 对于其他类型(基本类型、数组、null),直接覆盖
|
|
24041
|
-
else {
|
|
24042
|
-
// 这里的关键是:只有源对象中存在的属性,才会覆盖目标对象的属性。
|
|
24043
|
-
// 如果源对象中不存在某个键(key),则不会进入此循环,
|
|
24044
|
-
// 从而目标对象中已有的该属性得以保留。
|
|
24045
|
-
target[key] = sourceValue;
|
|
24046
|
-
}
|
|
24047
|
-
}
|
|
24048
|
-
}
|
|
24049
|
-
|
|
24050
|
-
return target;
|
|
24051
|
-
}
|
|
24052
|
-
var deepClone = {
|
|
24053
|
-
deepClone: deepClone$1,
|
|
24054
|
-
deepCloneAndMap,
|
|
24055
|
-
typeOfValue,
|
|
24056
|
-
isJsonString,
|
|
24057
|
-
flattenTreeValues,
|
|
24058
|
-
arrayToTree,
|
|
24059
|
-
getLeafValue,
|
|
24060
|
-
getNodeValue,
|
|
24061
|
-
deepMerge
|
|
24062
|
-
};
|
|
24063
|
-
|
|
24064
|
-
var unclassified = {
|
|
24065
|
-
deepClone};
|
|
24066
|
-
|
|
24067
24341
|
var script$h = {
|
|
24068
24342
|
__name: 'index',
|
|
24069
24343
|
props: {
|
|
@@ -45132,7 +45406,8 @@ var index = {
|
|
|
45132
45406
|
app.component('ly0d7thumb', script);
|
|
45133
45407
|
},
|
|
45134
45408
|
FileSaver,
|
|
45135
|
-
request
|
|
45409
|
+
request,
|
|
45410
|
+
withTable
|
|
45136
45411
|
};
|
|
45137
45412
|
|
|
45138
45413
|
export { FileSaver, index as default, request };
|