i18-fe-automator-beta 2.0.7 → 2.1.1
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/commonjs/index.js +2035 -1513
- package/dist/esm/index.mjs +2035 -1513
- package/package.json +50 -50
package/dist/commonjs/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var fs = require('fs');
|
|
@@ -25,7 +25,6 @@ var stripAnsi = require('strip-ansi');
|
|
|
25
25
|
require('shelljs');
|
|
26
26
|
var process$1 = require('child_process');
|
|
27
27
|
var _ = require('lodash');
|
|
28
|
-
require('nanoid');
|
|
29
28
|
var request$1 = require('request-promise');
|
|
30
29
|
require('os');
|
|
31
30
|
require('console');
|
|
@@ -291,267 +290,367 @@ function readCache(key) {
|
|
|
291
290
|
return fs.readFileSync(getRunCliPath({ root: true }) + "/.cache/" + key);
|
|
292
291
|
}
|
|
293
292
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
.
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
293
|
+
// 确保 .cache 目录存在
|
|
294
|
+
function ensureCacheDir() {
|
|
295
|
+
const cacheDir = getRunCliPath({ root: true }) + "/.cache";
|
|
296
|
+
if (!isExistPath(cacheDir)) {
|
|
297
|
+
fs.mkdirSync(cacheDir);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// 读取凭据缓存
|
|
302
|
+
function readSecretCache(cacheKey) {
|
|
303
|
+
const cacheFilePath = getRunCliPath({ root: true }) + "/.cache/" + cacheKey;
|
|
304
|
+
if (isExistPath(cacheFilePath)) {
|
|
305
|
+
try {
|
|
306
|
+
return JSON.parse(readCache(cacheKey));
|
|
307
|
+
} catch (error) {
|
|
308
|
+
return {};
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return {};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// 校验参数值, 不合法则红字提示合法值并退出
|
|
315
|
+
function validateChoice(value, validList, optionName) {
|
|
316
|
+
if (!validList.includes(value)) {
|
|
317
|
+
console.log(
|
|
318
|
+
chalk.red(
|
|
319
|
+
`参数 ${optionName} 的值 "${value}" 不合法, 可选值: ${validList.join(
|
|
320
|
+
" | "
|
|
321
|
+
)}`
|
|
322
|
+
)
|
|
323
|
+
);
|
|
324
|
+
process.exit(1);
|
|
325
|
+
}
|
|
326
|
+
return value;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// CLI 参数 > 交互询问
|
|
330
|
+
async function askOrUse(cliValue, question) {
|
|
331
|
+
if (cliValue !== undefined && cliValue !== "") {
|
|
332
|
+
return cliValue;
|
|
333
|
+
}
|
|
334
|
+
const answers = await inquirer.prompt([question]);
|
|
335
|
+
return answers[question.name];
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* 凭据解析: CLI 参数 > --cache 缓存 > 交互(展示缓存默认值)
|
|
340
|
+
* 解析成功后统一回写缓存(与现状"输入即缓存"一致)
|
|
341
|
+
* @param questions inquirer 问题数组(含 default/validate), default 会被缓存值覆盖
|
|
342
|
+
* @param cliValues CLI 参数值 { 字段名: 值 }
|
|
343
|
+
* @param useCache 是否走 --cache
|
|
344
|
+
* @param cacheKey 缓存键(baidu/sass/sass_pro)
|
|
345
|
+
* @param extraCache 回写缓存时额外携带的字段(如 baidu 的 type)
|
|
346
|
+
*/
|
|
347
|
+
async function resolveSecretQuestions({
|
|
348
|
+
questions,
|
|
349
|
+
cliValues = {},
|
|
350
|
+
useCache = false,
|
|
351
|
+
cacheKey,
|
|
352
|
+
extraCache = {},
|
|
353
|
+
}) {
|
|
354
|
+
ensureCacheDir();
|
|
355
|
+
const cacheSecret = readSecretCache(cacheKey);
|
|
356
|
+
const resolved = {};
|
|
357
|
+
const pendingQuestions = [];
|
|
358
|
+
questions.forEach((question) => {
|
|
359
|
+
const { name } = question;
|
|
360
|
+
const cliValue = cliValues[name];
|
|
361
|
+
if (cliValue !== undefined && cliValue !== "") {
|
|
362
|
+
resolved[name] = cliValue;
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (useCache && cacheSecret[name]) {
|
|
366
|
+
resolved[name] = cacheSecret[name];
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
// 未解析的字段进入交互, 默认值优先用缓存
|
|
370
|
+
pendingQuestions.push({
|
|
371
|
+
...question,
|
|
372
|
+
default: cacheSecret[name] ?? question.default,
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
if (pendingQuestions.length) {
|
|
376
|
+
const answers = await inquirer.prompt(pendingQuestions);
|
|
377
|
+
Object.assign(resolved, answers);
|
|
378
|
+
}
|
|
379
|
+
writeCache(
|
|
380
|
+
cacheKey,
|
|
381
|
+
JSON.stringify({
|
|
382
|
+
...extraCache,
|
|
383
|
+
...resolved,
|
|
384
|
+
})
|
|
385
|
+
);
|
|
386
|
+
return resolved;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const sleep = (time) =>
|
|
390
|
+
new Promise((resolve = 2000) => {
|
|
391
|
+
setTimeout(() => resolve, time);
|
|
392
|
+
});
|
|
393
|
+
// 上传翻译包(支持 --env/--files/--username/--password/--cache 一键, 参数齐全时跳过确认/选择)
|
|
394
|
+
async function upload$1(cliOpts = {}) {
|
|
395
|
+
// 空字符串视为未传(与askOrUse语义一致)
|
|
396
|
+
const cliEnv = cliOpts.env || undefined;
|
|
397
|
+
const cliFiles = cliOpts.files || undefined;
|
|
398
|
+
const cliUsername = cliOpts.username || undefined;
|
|
399
|
+
const cliPassword = cliOpts.password || undefined;
|
|
400
|
+
const cache = Boolean(cliOpts.cache);
|
|
401
|
+
// 传了任一CLI参数即视为一键意图, 跳过"是否上传"确认(传参即意图)
|
|
402
|
+
const oneClick =
|
|
403
|
+
cliEnv !== undefined ||
|
|
404
|
+
cliFiles !== undefined ||
|
|
405
|
+
cliUsername !== undefined ||
|
|
406
|
+
cliPassword !== undefined ||
|
|
407
|
+
cache;
|
|
408
|
+
// CLI参数校验前置(快速失败, 不进交互)
|
|
409
|
+
if (cliEnv !== undefined) {
|
|
410
|
+
const envArr = cliEnv
|
|
411
|
+
.split(",")
|
|
412
|
+
.map((item) => item.trim())
|
|
413
|
+
.filter(Boolean);
|
|
414
|
+
if (envArr.length === 1) {
|
|
415
|
+
if (!["mit", "sit", "uat", "pro"].includes(envArr[0])) {
|
|
416
|
+
console.log(
|
|
417
|
+
chalk.red(
|
|
418
|
+
`参数 --env 的值 "${cliEnv}" 不合法, 可选值: mit | sit | uat | pro, 多环境逗号分隔(仅mit/sit/uat, pro须单独传)`
|
|
419
|
+
)
|
|
420
|
+
);
|
|
421
|
+
process.exit(1);
|
|
422
|
+
}
|
|
423
|
+
} else {
|
|
424
|
+
const invalid = envArr.filter(
|
|
425
|
+
(item) => !["mit", "sit", "uat"].includes(item)
|
|
426
|
+
);
|
|
427
|
+
if (invalid.length) {
|
|
428
|
+
console.log(
|
|
429
|
+
chalk.red(
|
|
430
|
+
`多环境仅支持 mit | sit | uat 逗号分隔, 不支持: ${invalid.join(
|
|
431
|
+
" | "
|
|
432
|
+
)} (pro须单独传)`
|
|
433
|
+
)
|
|
434
|
+
);
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
const excelChoices = fs
|
|
440
|
+
.readdirSync("./")
|
|
441
|
+
.filter((item) => item.indexOf(".xlsx") > -1 || item.indexOf(".xls") > -1);
|
|
442
|
+
if (excelChoices.length === 0) {
|
|
443
|
+
console.log(chalk.red("当前目录下没有excel文件(.xlsx/.xls)"));
|
|
444
|
+
process.exit(1);
|
|
445
|
+
}
|
|
446
|
+
let cliFileArr;
|
|
447
|
+
if (cliFiles !== undefined) {
|
|
448
|
+
if (cliFiles === "all") {
|
|
449
|
+
cliFileArr = excelChoices;
|
|
450
|
+
} else {
|
|
451
|
+
cliFileArr = cliFiles
|
|
452
|
+
.split(",")
|
|
453
|
+
.map((item) => item.trim())
|
|
454
|
+
.filter(Boolean);
|
|
455
|
+
const invalid = cliFileArr.filter((item) => !excelChoices.includes(item));
|
|
456
|
+
if (invalid.length) {
|
|
457
|
+
console.log(
|
|
458
|
+
chalk.red(`当前目录不存在excel文件: ${invalid.join(", ")}`)
|
|
459
|
+
);
|
|
460
|
+
process.exit(1);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
// 1.是否上传(一键模式跳过, 选否属用户主动取消, exit 0)
|
|
465
|
+
if (!oneClick) {
|
|
466
|
+
const { isUpload } = await inquirer.prompt([
|
|
467
|
+
{
|
|
468
|
+
message: "是否上传到sass平台",
|
|
469
|
+
name: "isUpload",
|
|
470
|
+
type: "confirm",
|
|
471
|
+
default: true,
|
|
472
|
+
},
|
|
473
|
+
]);
|
|
474
|
+
if (!isUpload) {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
// 2.选择环境(CLI参数 > 交互)
|
|
479
|
+
let envValue;
|
|
480
|
+
if (cliEnv !== undefined) {
|
|
481
|
+
envValue = cliEnv
|
|
482
|
+
.split(",")
|
|
483
|
+
.map((item) => item.trim())
|
|
484
|
+
.filter(Boolean)
|
|
485
|
+
.join(",");
|
|
486
|
+
} else {
|
|
487
|
+
const res = await inquirer.prompt([
|
|
488
|
+
{
|
|
489
|
+
message: "请选择环境",
|
|
490
|
+
name: "env",
|
|
491
|
+
type: "rawlist",
|
|
492
|
+
choices: [
|
|
493
|
+
{
|
|
494
|
+
name: "mit",
|
|
495
|
+
value: "mit",
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
name: "sit",
|
|
499
|
+
value: "sit",
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
name: "uat",
|
|
503
|
+
value: "uat",
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
name: "pro",
|
|
507
|
+
value: "pro",
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
name: "一键发布uat(mit->sit->uat)",
|
|
511
|
+
value: "to-mit,sit,uat",
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
name: "一键发布sit(mit->sit)",
|
|
515
|
+
value: "to-mit,sit",
|
|
516
|
+
},
|
|
517
|
+
],
|
|
518
|
+
validate: function (val) {
|
|
519
|
+
if (val.length > 0) {
|
|
520
|
+
return true;
|
|
521
|
+
}
|
|
522
|
+
return "请选择环境";
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
]);
|
|
526
|
+
envValue = res.env;
|
|
527
|
+
}
|
|
528
|
+
// 统一解析: 去掉to-前缀(兼容交互选择值) → 按逗号拆分
|
|
529
|
+
const envList = envValue
|
|
530
|
+
.replace(/^to-/, "")
|
|
531
|
+
.split(",")
|
|
532
|
+
.map((item) => item.trim())
|
|
533
|
+
.filter(Boolean);
|
|
534
|
+
const isPro = envList.length === 1 && envList[0] === "pro";
|
|
535
|
+
// 3.凭据: CLI参数 > --cache缓存 > 交互(展示缓存默认值), 解析后回写缓存
|
|
536
|
+
const secret = await resolveSecretQuestions({
|
|
537
|
+
questions: [
|
|
538
|
+
{
|
|
539
|
+
message: isPro ? "请输入生产域账号" : "请输入域账号",
|
|
540
|
+
name: "username",
|
|
541
|
+
// 必填
|
|
542
|
+
validate: function (val) {
|
|
543
|
+
if (val) {
|
|
544
|
+
return true;
|
|
545
|
+
}
|
|
546
|
+
return isPro ? "请输入生产域账号" : "请输入域账号";
|
|
547
|
+
},
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
message: isPro ? "请输入生产域密码" : "请输入域密码",
|
|
551
|
+
name: "password",
|
|
552
|
+
// 必填
|
|
553
|
+
validate: function (val) {
|
|
554
|
+
if (val) {
|
|
555
|
+
return true;
|
|
556
|
+
}
|
|
557
|
+
return isPro ? "请输入生产域密码" : "请输入域密码";
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
],
|
|
561
|
+
cliValues: { username: cliUsername, password: cliPassword },
|
|
562
|
+
useCache: cache,
|
|
563
|
+
cacheKey: isPro ? "sass_pro" : "sass",
|
|
564
|
+
});
|
|
565
|
+
const { username, password } = secret;
|
|
566
|
+
// 4.选择上传文件(CLI参数 > 交互, 交互带全选联动)
|
|
567
|
+
let excelFileNames;
|
|
568
|
+
if (cliFileArr !== undefined) {
|
|
569
|
+
excelFileNames = cliFileArr;
|
|
570
|
+
} else {
|
|
571
|
+
inquirer.registerPrompt("checkbox-all", CheckboxAllPrompt);
|
|
572
|
+
// 选项拼接序号,与数字键选择对应(按1选全选,按2选第一个文件...)
|
|
573
|
+
const promptChoices = [
|
|
574
|
+
{ name: "全选", value: SELECT_ALL, short: "全选" },
|
|
575
|
+
...excelChoices.map((file) => ({ name: file, value: file, short: file })),
|
|
576
|
+
].map((choice, index) => ({
|
|
577
|
+
...choice,
|
|
578
|
+
name: `${index + 1}. ${choice.name}`,
|
|
579
|
+
}));
|
|
580
|
+
const res = await inquirer.prompt([
|
|
581
|
+
{
|
|
582
|
+
type: "checkbox-all",
|
|
583
|
+
name: "excelFileNames",
|
|
584
|
+
message: "请选择要上传的excel文件(可多选)",
|
|
585
|
+
choices: promptChoices,
|
|
586
|
+
validate: function (val) {
|
|
587
|
+
if (val.length > 0) {
|
|
588
|
+
return true;
|
|
589
|
+
}
|
|
590
|
+
return "请选择要上传的excel文件";
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
]);
|
|
594
|
+
excelFileNames = res.excelFileNames;
|
|
595
|
+
// 勾选了全选则上传所有文件
|
|
596
|
+
if (excelFileNames.includes(SELECT_ALL)) {
|
|
597
|
+
excelFileNames = excelChoices;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
// 环境外层循环(每个环境只登录一次),文件内层循环批量上传
|
|
601
|
+
const failList = [];
|
|
602
|
+
for (const env of envList) {
|
|
603
|
+
// 1.登录
|
|
604
|
+
let token;
|
|
605
|
+
try {
|
|
606
|
+
token = await login({
|
|
607
|
+
env,
|
|
608
|
+
username,
|
|
609
|
+
password,
|
|
610
|
+
});
|
|
611
|
+
} catch (e) {
|
|
612
|
+
console.log(chalk.red(`${env} 登录失败: ${e.message || e}`));
|
|
613
|
+
failList.push(`${env} 登录`);
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
for (const excelFileName of excelFileNames) {
|
|
617
|
+
const uploadFilePath = "./" + excelFileName;
|
|
618
|
+
try {
|
|
619
|
+
// 2.读取excel
|
|
620
|
+
const projectList = await readExcel({ uploadFilePath });
|
|
621
|
+
// 3.导入excel
|
|
622
|
+
await importExcel({
|
|
623
|
+
uploadFilePath,
|
|
624
|
+
token,
|
|
625
|
+
env,
|
|
626
|
+
});
|
|
627
|
+
await sleep(2000);
|
|
628
|
+
// 4.上传
|
|
629
|
+
await uploadExcel({
|
|
630
|
+
data: {
|
|
631
|
+
projectList,
|
|
632
|
+
},
|
|
633
|
+
token,
|
|
634
|
+
env,
|
|
635
|
+
});
|
|
636
|
+
console.log(
|
|
637
|
+
`🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀${env} ${excelFileName} 成功`
|
|
638
|
+
);
|
|
639
|
+
} catch (e) {
|
|
640
|
+
console.log(
|
|
641
|
+
chalk.red(`${env} ${excelFileName} 失败: ${e.message || e}`)
|
|
642
|
+
);
|
|
643
|
+
failList.push(`${env} ${excelFileName}`);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
// 汇总失败项(登录失败也计入, 统一失败退出码1)
|
|
648
|
+
if (failList.length > 0) {
|
|
649
|
+
console.log(
|
|
650
|
+
chalk.red(`\n以下 ${failList.length} 项上传失败,请检查:\n- ${failList.join("\n- ")}`)
|
|
651
|
+
);
|
|
652
|
+
process.exit(1);
|
|
653
|
+
}
|
|
555
654
|
}
|
|
556
655
|
|
|
557
656
|
// 百度翻译接口
|
|
@@ -757,429 +856,762 @@ function convertHTMLToPlaceholders(html, prefix = "N") {
|
|
|
757
856
|
// let str = `<div class="translate">apple<p class="aa test">test</p>word</div>`;
|
|
758
857
|
// console.log(convertHTMLToPlaceholders(str));
|
|
759
858
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
859
|
+
// 翻译excel指定列语言(支持 --file/--lang/--appid/--key/--cache 一键, file+lang齐全时跳过选择)
|
|
860
|
+
async function excelFn(cliOpts = {}) {
|
|
861
|
+
// 空字符串视为未传(与askOrUse语义一致)
|
|
862
|
+
const cliFile = cliOpts.file || undefined;
|
|
863
|
+
const cliLang = cliOpts.lang || undefined;
|
|
864
|
+
const cliAppid = cliOpts.appid || undefined;
|
|
865
|
+
const cliKey = cliOpts.key || undefined;
|
|
866
|
+
const cache = Boolean(cliOpts.cache);
|
|
867
|
+
// 1.获取excel文件列表, CLI传入file先校验存在性(快速失败, 不进交互)
|
|
868
|
+
const choices = fs
|
|
869
|
+
.readdirSync("./")
|
|
870
|
+
.filter((item) => item.indexOf(".xlsx") > -1 || item.indexOf(".xls") > -1);
|
|
871
|
+
if (choices.length === 0) {
|
|
872
|
+
console.log(chalk.red("当前目录下没有excel文件(.xlsx/.xls)"));
|
|
873
|
+
process.exit(1);
|
|
874
|
+
}
|
|
875
|
+
if (cliFile !== undefined && !choices.includes(cliFile)) {
|
|
876
|
+
console.log(chalk.red(`当前目录不存在excel文件: ${cliFile}`));
|
|
877
|
+
process.exit(1);
|
|
878
|
+
}
|
|
879
|
+
// 2.选择excel文件(CLI参数 > 交互)
|
|
880
|
+
let excelFileName;
|
|
881
|
+
if (cliFile !== undefined) {
|
|
882
|
+
excelFileName = cliFile;
|
|
883
|
+
} else {
|
|
884
|
+
const res = await inquirer.prompt([
|
|
885
|
+
{
|
|
886
|
+
type: "rawlist",
|
|
887
|
+
name: "excelFileName",
|
|
888
|
+
message: "请选择要上传的excel文件",
|
|
889
|
+
choices,
|
|
890
|
+
validate: function (val) {
|
|
891
|
+
if (val) {
|
|
892
|
+
return true;
|
|
893
|
+
}
|
|
894
|
+
return "请选择项目";
|
|
895
|
+
},
|
|
896
|
+
},
|
|
897
|
+
]);
|
|
898
|
+
excelFileName = res.excelFileName;
|
|
899
|
+
}
|
|
900
|
+
// 3.读取excel
|
|
901
|
+
const uploadFilePath = "./" + excelFileName;
|
|
902
|
+
const workSheetsFromFile = xlsx.parse(uploadFilePath);
|
|
903
|
+
const data = workSheetsFromFile[0].data;
|
|
904
|
+
// 获取第一行
|
|
905
|
+
const titleList = data[0];
|
|
906
|
+
const zhIndex = titleList.findIndex((item) => item === "zhCn");
|
|
907
|
+
const enIndex = titleList.findIndex((item) => item === "en");
|
|
908
|
+
const languageList = titleList.slice(enIndex);
|
|
909
|
+
// 4.选择语言列(CLI参数 > 交互), CLI传入先校验在该excel语言列中(快速失败)
|
|
910
|
+
let language;
|
|
911
|
+
if (cliLang !== undefined) {
|
|
912
|
+
if (!languageList.includes(cliLang)) {
|
|
913
|
+
console.log(
|
|
914
|
+
chalk.red(
|
|
915
|
+
`参数 --lang 的值 "${cliLang}" 不在该excel的语言列中, 可选值: ${languageList.join(
|
|
916
|
+
" | "
|
|
917
|
+
)}`
|
|
918
|
+
)
|
|
919
|
+
);
|
|
920
|
+
process.exit(1);
|
|
921
|
+
}
|
|
922
|
+
language = cliLang;
|
|
923
|
+
} else {
|
|
924
|
+
const res = await inquirer.prompt([
|
|
925
|
+
{
|
|
926
|
+
type: "rawlist",
|
|
927
|
+
name: "language",
|
|
928
|
+
message: `请选择要翻译的语言\n${chalk.red("注意:\n")}${chalk.green(
|
|
929
|
+
"1. 英文基于中文翻译\n2. 非英文基于英文翻译"
|
|
930
|
+
)}`,
|
|
931
|
+
choices: languageList,
|
|
932
|
+
validate: function (val) {
|
|
933
|
+
if (val) {
|
|
934
|
+
return true;
|
|
935
|
+
}
|
|
936
|
+
return "请选择";
|
|
937
|
+
},
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
type: "confirm", // 获取上一个选中结果
|
|
941
|
+
name: "isTranslate",
|
|
942
|
+
message: ({ language }) => {
|
|
943
|
+
return `是否翻译${chalk.red(language)}`;
|
|
944
|
+
},
|
|
945
|
+
default: true,
|
|
946
|
+
prefix: "Y", // 添加前缀符号
|
|
947
|
+
},
|
|
948
|
+
]);
|
|
949
|
+
language = res.language;
|
|
950
|
+
// 用户选择不翻译则直接退出(一键模式传 --lang 视为翻译, 不进入该询问)
|
|
951
|
+
if (!res.isTranslate) {
|
|
952
|
+
console.log(chalk.green(`已跳过${language}列翻译`));
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
const content = data.slice(1);
|
|
957
|
+
const zhArr = content.map((row) => {
|
|
958
|
+
return {
|
|
959
|
+
from: row[zhIndex],
|
|
960
|
+
};
|
|
961
|
+
});
|
|
962
|
+
const enArr = content.map((row) => {
|
|
963
|
+
return {
|
|
964
|
+
from: row[enIndex],
|
|
965
|
+
};
|
|
966
|
+
});
|
|
967
|
+
let transArr = language === "en" ? zhArr : enArr;
|
|
968
|
+
// 5.凭据: CLI参数 > --cache缓存 > 交互(展示缓存默认值), 翻译接口当前仅百度, 一键时自动选择
|
|
969
|
+
const secret = await resolveSecretQuestions({
|
|
970
|
+
questions: [
|
|
971
|
+
{
|
|
972
|
+
message: "请输入百度翻译的appid",
|
|
973
|
+
name: "appid",
|
|
974
|
+
// 必填
|
|
975
|
+
validate: function (val) {
|
|
976
|
+
if (val) {
|
|
977
|
+
return true;
|
|
978
|
+
}
|
|
979
|
+
return "请输入百度翻译的appid";
|
|
980
|
+
},
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
message: "请输入百度翻译的key",
|
|
984
|
+
name: "key",
|
|
985
|
+
// 必填
|
|
986
|
+
validate: function (val) {
|
|
987
|
+
if (val) {
|
|
988
|
+
return true;
|
|
989
|
+
}
|
|
990
|
+
return "请输入百度翻译的key";
|
|
991
|
+
},
|
|
992
|
+
},
|
|
993
|
+
],
|
|
994
|
+
cliValues: { appid: cliAppid, key: cliKey },
|
|
995
|
+
useCache: cache,
|
|
996
|
+
cacheKey: "baidu",
|
|
997
|
+
extraCache: { type: "baidu" },
|
|
998
|
+
});
|
|
999
|
+
const { appid, key } = secret;
|
|
1000
|
+
const baiduLang = {
|
|
1001
|
+
vi: "vie", // 越南语
|
|
1002
|
+
};
|
|
1003
|
+
translate(transArr, {
|
|
1004
|
+
originData: data,
|
|
1005
|
+
excelFileName,
|
|
1006
|
+
appid,
|
|
1007
|
+
key,
|
|
1008
|
+
from: language === "en" ? "zh" : "en",
|
|
1009
|
+
to: baiduLang[language] || language,
|
|
1010
|
+
toIndex: titleList.findIndex((item) => item === language),
|
|
1011
|
+
});
|
|
905
1012
|
}
|
|
906
1013
|
|
|
907
|
-
// 获取应用list
|
|
908
|
-
function getApplicationList({ appName, env, token }) {
|
|
909
|
-
return new Promise((resolve, reject) => {
|
|
910
|
-
request(
|
|
911
|
-
{
|
|
912
|
-
url: `https://${
|
|
913
|
-
env === "pro" ? "" : `${env}-`
|
|
914
|
-
}hxjf.hongxinshop.com/api-u/api/saas/app/nameList`,
|
|
915
|
-
method: "POST",
|
|
916
|
-
json: true,
|
|
917
|
-
body: { name: appName, pageNo: 1, pageSize: 50 },
|
|
918
|
-
headers: {
|
|
919
|
-
Authorization: `Bearer ${token}`,
|
|
920
|
-
"x-request-vaildate": "open",
|
|
921
|
-
},
|
|
922
|
-
},
|
|
923
|
-
function (error, response, body) {
|
|
924
|
-
const res = body;
|
|
925
|
-
if (!error && res.code == 200) {
|
|
926
|
-
resolve(
|
|
927
|
-
res.data.map((item) => {
|
|
928
|
-
// return {
|
|
929
|
-
// name: item.name,
|
|
930
|
-
// value: item.id,
|
|
931
|
-
// };
|
|
932
|
-
return {
|
|
933
|
-
name: item.name,
|
|
934
|
-
value: item.name,
|
|
935
|
-
id: item.id,
|
|
936
|
-
code: item.code,
|
|
937
|
-
};
|
|
938
|
-
})
|
|
939
|
-
);
|
|
940
|
-
} else {
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1014
|
+
// 获取应用list
|
|
1015
|
+
function getApplicationList({ appName, env, token }) {
|
|
1016
|
+
return new Promise((resolve, reject) => {
|
|
1017
|
+
request(
|
|
1018
|
+
{
|
|
1019
|
+
url: `https://${
|
|
1020
|
+
env === "pro" ? "" : `${env}-`
|
|
1021
|
+
}hxjf.hongxinshop.com/api-u/api/saas/app/nameList`,
|
|
1022
|
+
method: "POST",
|
|
1023
|
+
json: true,
|
|
1024
|
+
body: { name: appName, pageNo: 1, pageSize: 50 },
|
|
1025
|
+
headers: {
|
|
1026
|
+
Authorization: `Bearer ${token}`,
|
|
1027
|
+
"x-request-vaildate": "open",
|
|
1028
|
+
},
|
|
1029
|
+
},
|
|
1030
|
+
function (error, response, body) {
|
|
1031
|
+
const res = body;
|
|
1032
|
+
if (!error && res.code == 200) {
|
|
1033
|
+
resolve(
|
|
1034
|
+
res.data.map((item) => {
|
|
1035
|
+
// return {
|
|
1036
|
+
// name: item.name,
|
|
1037
|
+
// value: item.id,
|
|
1038
|
+
// };
|
|
1039
|
+
return {
|
|
1040
|
+
name: item.name,
|
|
1041
|
+
value: item.name,
|
|
1042
|
+
id: item.id,
|
|
1043
|
+
code: item.code,
|
|
1044
|
+
};
|
|
1045
|
+
})
|
|
1046
|
+
);
|
|
1047
|
+
} else {
|
|
1048
|
+
const errorData = error || JSON.stringify(res.data);
|
|
1049
|
+
console.log(chalk.red(errorData));
|
|
1050
|
+
reject(errorData);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
);
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
// 获取应用配置
|
|
1057
|
+
function getApplicationConfig({ appId, env, token }) {
|
|
1058
|
+
Loading$1.start(`获取应用配置中...`);
|
|
1059
|
+
return new Promise((resolve, reject) => {
|
|
1060
|
+
request(
|
|
1061
|
+
{
|
|
1062
|
+
url: `https://${
|
|
1063
|
+
env === "pro" ? "" : `${env}-`
|
|
1064
|
+
}hxjf.hongxinshop.com/api-u/api/saas/app/info`,
|
|
1065
|
+
method: "POST",
|
|
1066
|
+
json: true,
|
|
1067
|
+
body: { id: appId },
|
|
1068
|
+
headers: {
|
|
1069
|
+
Authorization: `Bearer ${token}`,
|
|
1070
|
+
"x-request-vaildate": "open",
|
|
1071
|
+
},
|
|
1072
|
+
},
|
|
1073
|
+
function (error, response, body) {
|
|
1074
|
+
const res = body;
|
|
1075
|
+
if (!error && res.code == 200) {
|
|
1076
|
+
Loading$1.succeed(`获取应用配置成功`);
|
|
1077
|
+
resolve(res.data);
|
|
1078
|
+
} else {
|
|
1079
|
+
Loading$1.fail(`获取应用配置失败`);
|
|
1080
|
+
const errorData = error || JSON.stringify(res.data);
|
|
1081
|
+
console.log(chalk.red(errorData));
|
|
1082
|
+
reject(errorData);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
);
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
// 获取应用按钮权限配置
|
|
1089
|
+
function getApplicationButtonConfig({ appId, env, token }) {
|
|
1090
|
+
Loading$1.start(`获取按钮权限配置中...`);
|
|
1091
|
+
return new Promise((resolve, reject) => {
|
|
1092
|
+
request(
|
|
1093
|
+
{
|
|
1094
|
+
url: `https://${
|
|
1095
|
+
env === "pro" ? "" : `${env}-`
|
|
1096
|
+
}hxjf.hongxinshop.com/api-u/api/saas/permission/list`,
|
|
1097
|
+
method: "POST",
|
|
1098
|
+
json: true,
|
|
1099
|
+
body: { id: appId },
|
|
1100
|
+
headers: {
|
|
1101
|
+
Authorization: `Bearer ${token}`,
|
|
1102
|
+
"x-request-vaildate": "open",
|
|
1103
|
+
},
|
|
1104
|
+
},
|
|
1105
|
+
function (error, response, body) {
|
|
1106
|
+
const res = body;
|
|
1107
|
+
if (!error && res.code == 200) {
|
|
1108
|
+
Loading$1.succeed(`获取${env}环境按钮权限配置成功`);
|
|
1109
|
+
resolve(res.data);
|
|
1110
|
+
} else {
|
|
1111
|
+
Loading$1.fail(`获取${env}环境按钮权限配置失败`);
|
|
1112
|
+
const errorData = error || JSON.stringify(res.data);
|
|
1113
|
+
console.log(chalk.red(errorData));
|
|
1114
|
+
reject(errorData);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
);
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
// 删除应用按钮权限
|
|
1121
|
+
function deleteApplicationButtonConfig({ data, env, token }) {
|
|
1122
|
+
return new Promise((resolve, reject) => {
|
|
1123
|
+
request(
|
|
1124
|
+
{
|
|
1125
|
+
url: `https://${
|
|
1126
|
+
env === "pro" ? "" : `${env}-`
|
|
1127
|
+
}hxjf.hongxinshop.com/api-u/api/saas/permission/del`,
|
|
1128
|
+
method: "POST",
|
|
1129
|
+
json: true,
|
|
1130
|
+
body: data,
|
|
1131
|
+
headers: {
|
|
1132
|
+
Authorization: `Bearer ${token}`,
|
|
1133
|
+
"x-request-vaildate": "open",
|
|
1134
|
+
},
|
|
1135
|
+
},
|
|
1136
|
+
function (error, response, body) {
|
|
1137
|
+
const res = body;
|
|
1138
|
+
if (!error && res.code == 200) {
|
|
1139
|
+
resolve(res.data);
|
|
1140
|
+
} else {
|
|
1141
|
+
const errorData = error || JSON.stringify(res.data);
|
|
1142
|
+
console.log(chalk.red(errorData));
|
|
1143
|
+
reject(errorData);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
);
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
// 新增应用按钮权限
|
|
1150
|
+
function addApplicationButtonConfig({ data, env, token }) {
|
|
1151
|
+
Loading$1.start(`新增按钮权限 ${data.code} 中...`);
|
|
1152
|
+
return new Promise((resolve, reject) => {
|
|
1153
|
+
request(
|
|
1154
|
+
{
|
|
1155
|
+
url: `https://${
|
|
1156
|
+
env === "pro" ? "" : `${env}-`
|
|
1157
|
+
}hxjf.hongxinshop.com/api-u/api/saas/permission/add`,
|
|
1158
|
+
method: "POST",
|
|
1159
|
+
json: true,
|
|
1160
|
+
body: data,
|
|
1161
|
+
headers: {
|
|
1162
|
+
Authorization: `Bearer ${token}`,
|
|
1163
|
+
"x-request-vaildate": "open",
|
|
1164
|
+
},
|
|
1165
|
+
},
|
|
1166
|
+
function (error, response, body) {
|
|
1167
|
+
const res = body;
|
|
1168
|
+
if (!error && res.code == 200) {
|
|
1169
|
+
Loading$1.succeed(`${data.code}`);
|
|
1170
|
+
resolve(true);
|
|
1171
|
+
} else {
|
|
1172
|
+
Loading$1.fail(`${data.code}`);
|
|
1173
|
+
const errorData = error || JSON.stringify(res.data);
|
|
1174
|
+
console.log(chalk.red(errorData));
|
|
1175
|
+
reject(errorData);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
);
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
// -sass 入口: 功能路由(--action 显式指定 > --from/--to 推断同步 > --permission-* 推断新增 > 菜单)
|
|
1182
|
+
async function sassMain(rawCliOpts = {}) {
|
|
1183
|
+
// 空字符串视为未传(与askOrUse语义一致)
|
|
1184
|
+
const sassOpts = {
|
|
1185
|
+
...rawCliOpts,
|
|
1186
|
+
action: rawCliOpts.action || undefined,
|
|
1187
|
+
from: rawCliOpts.from || undefined,
|
|
1188
|
+
to: rawCliOpts.to || undefined,
|
|
1189
|
+
app: rawCliOpts.app || undefined,
|
|
1190
|
+
env: rawCliOpts.env || undefined,
|
|
1191
|
+
permissionName: rawCliOpts.permissionName || undefined,
|
|
1192
|
+
permissionCode: rawCliOpts.permissionCode || undefined,
|
|
1193
|
+
};
|
|
1194
|
+
const { action, from, to, permissionName, permissionCode } = sassOpts;
|
|
1195
|
+
if (action !== undefined) {
|
|
1196
|
+
validateChoice(action, ["sync", "permission"], "--action");
|
|
1197
|
+
return action === "permission"
|
|
1198
|
+
? addSassPermission(sassOpts)
|
|
1199
|
+
: syncSassFlow(sassOpts);
|
|
1200
|
+
}
|
|
1201
|
+
if (from !== undefined || to !== undefined) {
|
|
1202
|
+
return syncSassFlow(sassOpts);
|
|
1203
|
+
}
|
|
1204
|
+
if (permissionName !== undefined || permissionCode !== undefined) {
|
|
1205
|
+
return addSassPermission(sassOpts);
|
|
1206
|
+
}
|
|
1207
|
+
const { menuAction } = await inquirer.prompt([
|
|
1208
|
+
{
|
|
1209
|
+
message: "请选择功能",
|
|
1210
|
+
name: "menuAction",
|
|
1211
|
+
type: "list",
|
|
1212
|
+
choices: [
|
|
1213
|
+
{ name: "同步按钮权限配置", value: "sync" },
|
|
1214
|
+
{ name: "新增按钮权限", value: "permission" },
|
|
1215
|
+
],
|
|
1216
|
+
},
|
|
1217
|
+
]);
|
|
1218
|
+
return menuAction === "permission"
|
|
1219
|
+
? addSassPermission(sassOpts)
|
|
1220
|
+
: syncSassFlow(sassOpts);
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
// 同步流程(支持 --from/--to/--app 一键, 参数齐全时跳过使用须知)
|
|
1224
|
+
async function syncSassFlow(cliOpts) {
|
|
1225
|
+
const { from: cliFrom, to: cliTo, app: cliApp } = cliOpts;
|
|
1226
|
+
const envList = ["mit", "sit", "uat"];
|
|
1227
|
+
// 传了任一同步参数即视为一键意图, 自动同意使用须知(传参即意图)
|
|
1228
|
+
const oneClick =
|
|
1229
|
+
cliFrom !== undefined || cliTo !== undefined || cliApp !== undefined;
|
|
1230
|
+
if (!oneClick) {
|
|
1231
|
+
const { notice } = await inquirer.prompt([
|
|
1232
|
+
{
|
|
1233
|
+
message: `使用须知:
|
|
1234
|
+
${chalk.green(`
|
|
1235
|
+
1.此命令会${chalk.red('强制覆盖')}[from]->[to]环境配置,${chalk.red('不会合并')}!!! \n
|
|
1236
|
+
2.请确保同步前已经在目标环境创建了应用 \n
|
|
1237
|
+
3.从mit->sit/uat,建议先同步sit->mit以保证mit环境的配置是最新的
|
|
1238
|
+
`)}
|
|
1239
|
+
`,
|
|
1240
|
+
name: "notice",
|
|
1241
|
+
type: "confirm",
|
|
1242
|
+
default: false,
|
|
1243
|
+
},
|
|
1244
|
+
]);
|
|
1245
|
+
if (!notice) {
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
if (cliFrom !== undefined) {
|
|
1250
|
+
validateChoice(cliFrom, envList, "--from");
|
|
1251
|
+
}
|
|
1252
|
+
const from = await askOrUse(cliFrom, {
|
|
1253
|
+
message: "请选择从哪个环境同步配置",
|
|
1254
|
+
name: "from",
|
|
1255
|
+
type: "list",
|
|
1256
|
+
choices: envList,
|
|
1257
|
+
});
|
|
1258
|
+
if (cliTo !== undefined) {
|
|
1259
|
+
validateChoice(cliTo, envList, "--to");
|
|
1260
|
+
if (cliTo === from) {
|
|
1261
|
+
console.log(chalk.red("参数 --to 的值须不同于 --from"));
|
|
1262
|
+
process.exit(1);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
const to = await askOrUse(cliTo, {
|
|
1266
|
+
message: "请选择同步至哪个环境",
|
|
1267
|
+
name: "to",
|
|
1268
|
+
type: "list",
|
|
1269
|
+
choices: envList.filter((item) => item !== from),
|
|
1270
|
+
});
|
|
1271
|
+
let token;
|
|
1272
|
+
try {
|
|
1273
|
+
token = await login({
|
|
1274
|
+
env: from,
|
|
1275
|
+
username: "superAdmin",
|
|
1276
|
+
password: "admin1",
|
|
1277
|
+
});
|
|
1278
|
+
} catch (e) {
|
|
1279
|
+
console.log(chalk.red(`${from} 登录失败: ${e.message || e}`));
|
|
1280
|
+
process.exit(1);
|
|
1281
|
+
}
|
|
1282
|
+
const inputAppName = await askOrUse(cliApp, {
|
|
1283
|
+
message: "请输入应用名称关键字",
|
|
1284
|
+
name: "inputAppName",
|
|
1285
|
+
type: "input",
|
|
1286
|
+
validate: function (value) {
|
|
1287
|
+
if (value) {
|
|
1288
|
+
return true;
|
|
1289
|
+
}
|
|
1290
|
+
return "请输入应用名称";
|
|
1291
|
+
},
|
|
1292
|
+
});
|
|
1293
|
+
let applicationList;
|
|
1294
|
+
try {
|
|
1295
|
+
applicationList = await getApplicationList({
|
|
1296
|
+
env: from,
|
|
1297
|
+
token,
|
|
1298
|
+
appName: inputAppName,
|
|
1299
|
+
});
|
|
1300
|
+
} catch (e) {
|
|
1301
|
+
console.log(chalk.red(`查询${from}环境应用列表失败: ${e.message || e}`));
|
|
1302
|
+
process.exit(1);
|
|
1303
|
+
}
|
|
1304
|
+
if (!applicationList.length) {
|
|
1305
|
+
console.log(chalk.red(`${from}环境未找到该应用`));
|
|
1306
|
+
process.exit(1);
|
|
1307
|
+
}
|
|
1308
|
+
// 一键模式: 唯一匹配或精确匹配CLI传入的应用名时自动选中(与新增按钮权限流程一致)
|
|
1309
|
+
let appName;
|
|
1310
|
+
if (applicationList.length === 1) {
|
|
1311
|
+
appName = applicationList[0].name;
|
|
1312
|
+
console.log(chalk.gray(`唯一匹配,已自动选中: ${appName}`));
|
|
1313
|
+
} else if (oneClick) {
|
|
1314
|
+
const exactMatch = applicationList.find((item) => item.name === cliApp);
|
|
1315
|
+
if (!exactMatch) {
|
|
1316
|
+
console.log(
|
|
1317
|
+
chalk.red(`${from}环境未精确匹配到应用: ${cliApp}, 请更换关键字`)
|
|
1318
|
+
);
|
|
1319
|
+
process.exit(1);
|
|
1320
|
+
}
|
|
1321
|
+
appName = exactMatch.name;
|
|
1322
|
+
console.log(chalk.gray(`精确匹配,已自动选中: ${appName}`));
|
|
1323
|
+
} else {
|
|
1324
|
+
const res = await inquirer.prompt([
|
|
1325
|
+
{
|
|
1326
|
+
message: "请选择应用名称",
|
|
1327
|
+
name: "appName",
|
|
1328
|
+
type: "rawlist",
|
|
1329
|
+
choices: applicationList,
|
|
1330
|
+
},
|
|
1331
|
+
]);
|
|
1332
|
+
appName = res.appName;
|
|
1333
|
+
}
|
|
1334
|
+
const appId = applicationList.find((item) => item.name === appName).id;
|
|
1335
|
+
let fromButtonList;
|
|
1336
|
+
try {
|
|
1337
|
+
fromButtonList = await getApplicationButtonConfig({
|
|
1338
|
+
appId,
|
|
1339
|
+
env: from,
|
|
1340
|
+
token,
|
|
1341
|
+
});
|
|
1342
|
+
} catch (e) {
|
|
1343
|
+
console.log(
|
|
1344
|
+
chalk.red(`获取${from}环境按钮权限配置失败: ${e.message || e}`)
|
|
1345
|
+
);
|
|
1346
|
+
process.exit(1);
|
|
1347
|
+
}
|
|
1348
|
+
if (!fromButtonList.length) {
|
|
1349
|
+
console.log(chalk.red("按钮权限配置为空,无法同步"));
|
|
1350
|
+
process.exit(1);
|
|
1351
|
+
}
|
|
1352
|
+
console.log(`开始同步到${to}🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀`);
|
|
1353
|
+
let toToken;
|
|
1354
|
+
try {
|
|
1355
|
+
toToken = await login({
|
|
1356
|
+
env: to,
|
|
1357
|
+
username: "superAdmin",
|
|
1358
|
+
password: "admin1",
|
|
1359
|
+
});
|
|
1360
|
+
} catch (e) {
|
|
1361
|
+
console.log(chalk.red(`${to} 登录失败: ${e.message || e}`));
|
|
1362
|
+
process.exit(1);
|
|
1363
|
+
}
|
|
1364
|
+
let toApplicationList;
|
|
1365
|
+
try {
|
|
1366
|
+
toApplicationList = await getApplicationList({
|
|
1367
|
+
env: to,
|
|
1368
|
+
token: toToken,
|
|
1369
|
+
appName,
|
|
1370
|
+
});
|
|
1371
|
+
} catch (e) {
|
|
1372
|
+
console.log(chalk.red(`查询${to}环境应用列表失败: ${e.message || e}`));
|
|
1373
|
+
process.exit(1);
|
|
1374
|
+
}
|
|
1375
|
+
if (!toApplicationList.length) {
|
|
1376
|
+
console.log(chalk.red(`${to}环境不存在该应用`));
|
|
1377
|
+
process.exit(1);
|
|
1378
|
+
}
|
|
1379
|
+
const toAppId = toApplicationList[0].id;
|
|
1380
|
+
let toButtonList;
|
|
1381
|
+
try {
|
|
1382
|
+
toButtonList = await getApplicationButtonConfig({
|
|
1383
|
+
appId: toAppId,
|
|
1384
|
+
env: to,
|
|
1385
|
+
token: toToken,
|
|
1386
|
+
});
|
|
1387
|
+
} catch (e) {
|
|
1388
|
+
console.log(chalk.red(`获取${to}环境按钮权限配置失败: ${e.message || e}`));
|
|
1389
|
+
process.exit(1);
|
|
1390
|
+
}
|
|
1391
|
+
try {
|
|
1392
|
+
if (toButtonList.length) {
|
|
1393
|
+
const deleteButtonPromiseList = toButtonList.map((item) => {
|
|
1394
|
+
return deleteApplicationButtonConfig({
|
|
1395
|
+
data: {
|
|
1396
|
+
id: item.id,
|
|
1397
|
+
ver: item.ver,
|
|
1398
|
+
},
|
|
1399
|
+
env: to,
|
|
1400
|
+
token: toToken,
|
|
1401
|
+
});
|
|
1402
|
+
});
|
|
1403
|
+
Loading$1.start(`删除${to}环境按钮权限配置中...`);
|
|
1404
|
+
await Promise.all(deleteButtonPromiseList);
|
|
1405
|
+
Loading$1.succeed(`删除${to}环境按钮权限配置成功`);
|
|
1406
|
+
}
|
|
1407
|
+
} catch (error) {
|
|
1408
|
+
console.log(chalk.red(`删除${to}环境按钮权限配置失败`));
|
|
1409
|
+
} finally {
|
|
1410
|
+
try {
|
|
1411
|
+
for (const item of fromButtonList) {
|
|
1412
|
+
const { code, name, visitConf } = item;
|
|
1413
|
+
await addApplicationButtonConfig({
|
|
1414
|
+
env: to,
|
|
1415
|
+
token: toToken,
|
|
1416
|
+
data: {
|
|
1417
|
+
code1: code.split(":")[0],
|
|
1418
|
+
code,
|
|
1419
|
+
name,
|
|
1420
|
+
visitConf,
|
|
1421
|
+
ascriptionApp: toAppId,
|
|
1422
|
+
},
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
console.log(chalk.green("同步成功"));
|
|
1426
|
+
} catch (e) {
|
|
1427
|
+
console.log(chalk.red(`同步失败: ${e.message || e}`));
|
|
1428
|
+
process.exit(1);
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
// 新增按钮权限流程(支持 --env/--app/--permission-name/--permission-code 一键, 参数齐全时免确认)
|
|
1434
|
+
async function addSassPermission(cliOpts = {}) {
|
|
1435
|
+
const {
|
|
1436
|
+
env: cliEnv,
|
|
1437
|
+
app: cliApp,
|
|
1438
|
+
permissionName: cliPermissionName,
|
|
1439
|
+
permissionCode: cliPermissionCode,
|
|
1440
|
+
} = cliOpts;
|
|
1441
|
+
const envList = ["mit", "sit", "uat"];
|
|
1442
|
+
const oneClick =
|
|
1443
|
+
cliEnv !== undefined &&
|
|
1444
|
+
cliApp !== undefined &&
|
|
1445
|
+
cliPermissionName !== undefined &&
|
|
1446
|
+
cliPermissionCode !== undefined;
|
|
1447
|
+
// CLI传入的权限编码含:时快速失败(code以:为分隔符,含:会产生歧义,且避免登录查询后才报错)
|
|
1448
|
+
if (cliPermissionCode !== undefined && cliPermissionCode.includes(":")) {
|
|
1449
|
+
console.log(chalk.red("参数 --permission-code 的值不允许包含 :"));
|
|
1450
|
+
process.exit(1);
|
|
1451
|
+
}
|
|
1452
|
+
if (cliEnv !== undefined) {
|
|
1453
|
+
validateChoice(cliEnv, envList, "--env");
|
|
1454
|
+
}
|
|
1455
|
+
const env = await askOrUse(cliEnv, {
|
|
1456
|
+
message: "请选择环境",
|
|
1457
|
+
name: "env",
|
|
1458
|
+
type: "list",
|
|
1459
|
+
choices: envList,
|
|
1460
|
+
});
|
|
1461
|
+
let token;
|
|
1462
|
+
try {
|
|
1463
|
+
token = await login({
|
|
1464
|
+
env,
|
|
1465
|
+
username: "superAdmin",
|
|
1466
|
+
password: "admin1",
|
|
1467
|
+
});
|
|
1468
|
+
} catch (e) {
|
|
1469
|
+
console.log(chalk.red(`${env} 登录失败: ${e.message || e}`));
|
|
1470
|
+
process.exit(1);
|
|
1471
|
+
}
|
|
1472
|
+
const inputAppName = await askOrUse(cliApp, {
|
|
1473
|
+
message: "请输入要新增按钮权限的应用名称",
|
|
1474
|
+
name: "inputAppName",
|
|
1475
|
+
type: "input",
|
|
1476
|
+
validate: function (value) {
|
|
1477
|
+
if (value) {
|
|
1478
|
+
return true;
|
|
1479
|
+
}
|
|
1480
|
+
return "请输入应用名称";
|
|
1481
|
+
},
|
|
1482
|
+
});
|
|
1483
|
+
let applicationList;
|
|
1484
|
+
try {
|
|
1485
|
+
applicationList = await getApplicationList({
|
|
1486
|
+
env,
|
|
1487
|
+
token,
|
|
1488
|
+
appName: inputAppName,
|
|
1489
|
+
});
|
|
1490
|
+
} catch (e) {
|
|
1491
|
+
console.log(chalk.red(`查询${env}环境应用列表失败: ${e.message || e}`));
|
|
1492
|
+
process.exit(1);
|
|
1493
|
+
}
|
|
1494
|
+
if (!applicationList.length) {
|
|
1495
|
+
console.log(chalk.red(`${env}环境未找到该应用`));
|
|
1496
|
+
process.exit(1);
|
|
1497
|
+
}
|
|
1498
|
+
let appId;
|
|
1499
|
+
let appName;
|
|
1500
|
+
if (applicationList.length === 1) {
|
|
1501
|
+
appId = applicationList[0].id;
|
|
1502
|
+
appName = applicationList[0].name;
|
|
1503
|
+
console.log(chalk.gray(`唯一匹配,已自动选中: ${appName}`));
|
|
1504
|
+
} else {
|
|
1505
|
+
// 一键模式下优先精确匹配CLI传入的应用名称(应用列表是关键字模糊查询结果)
|
|
1506
|
+
const exactMatch = oneClick
|
|
1507
|
+
? applicationList.find((item) => item.name === cliApp)
|
|
1508
|
+
: undefined;
|
|
1509
|
+
if (exactMatch) {
|
|
1510
|
+
appId = exactMatch.id;
|
|
1511
|
+
appName = exactMatch.name;
|
|
1512
|
+
console.log(chalk.gray(`精确匹配,已自动选中: ${appName}`));
|
|
1513
|
+
} else {
|
|
1514
|
+
const res = await inquirer.prompt([
|
|
1515
|
+
{
|
|
1516
|
+
message: "请选择应用名称",
|
|
1517
|
+
name: "appName",
|
|
1518
|
+
type: "rawlist",
|
|
1519
|
+
choices: applicationList,
|
|
1520
|
+
},
|
|
1521
|
+
]);
|
|
1522
|
+
appId = applicationList.find(
|
|
1523
|
+
(item) => item.name === res.appName
|
|
1524
|
+
).id;
|
|
1525
|
+
appName = res.appName;
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
let appConfig;
|
|
1529
|
+
try {
|
|
1530
|
+
appConfig = await getApplicationConfig({ appId, env, token });
|
|
1531
|
+
} catch (e) {
|
|
1532
|
+
console.log(chalk.red(`获取应用配置失败: ${e.message || e}`));
|
|
1533
|
+
process.exit(1);
|
|
1534
|
+
}
|
|
1535
|
+
// CLI传入的权限名称/编码仅第一圈消费, 后续圈次走交互输入
|
|
1536
|
+
let cliName = cliPermissionName;
|
|
1537
|
+
let cliCode = cliPermissionCode;
|
|
1538
|
+
let goOn = true;
|
|
1539
|
+
while (goOn) {
|
|
1540
|
+
const permissionName = await askOrUse(cliName, {
|
|
1541
|
+
message: "请输入权限名称",
|
|
1542
|
+
name: "permissionName",
|
|
1543
|
+
type: "input",
|
|
1544
|
+
validate: function (value) {
|
|
1545
|
+
if (value) {
|
|
1546
|
+
return true;
|
|
1547
|
+
}
|
|
1548
|
+
return "请输入权限名称";
|
|
1549
|
+
},
|
|
1550
|
+
});
|
|
1551
|
+
const permissionCode = await askOrUse(cliCode, {
|
|
1552
|
+
message: "请输入权限编码",
|
|
1553
|
+
name: "permissionCode",
|
|
1554
|
+
type: "input",
|
|
1555
|
+
validate: function (value) {
|
|
1556
|
+
if (!value) {
|
|
1557
|
+
return "请输入权限编码";
|
|
1558
|
+
}
|
|
1559
|
+
if (value.includes(":")) {
|
|
1560
|
+
return "权限编码不允许包含 :";
|
|
1561
|
+
}
|
|
1562
|
+
return true;
|
|
1563
|
+
},
|
|
1564
|
+
});
|
|
1565
|
+
cliName = undefined;
|
|
1566
|
+
cliCode = undefined;
|
|
1567
|
+
const data = {
|
|
1568
|
+
code1: appConfig.code,
|
|
1569
|
+
name: permissionName,
|
|
1570
|
+
visitConf: permissionCode,
|
|
1571
|
+
ascriptionApp: appConfig.id,
|
|
1572
|
+
code: `${appConfig.code}:${permissionCode}`,
|
|
1573
|
+
};
|
|
1574
|
+
let confirmed = true;
|
|
1575
|
+
if (!oneClick) {
|
|
1576
|
+
const { confirmAdd } = await inquirer.prompt([
|
|
1577
|
+
{
|
|
1578
|
+
message: `确认新增按钮权限? ${chalk.cyan(
|
|
1579
|
+
`${data.name} (${data.code})`
|
|
1580
|
+
)}`,
|
|
1581
|
+
name: "confirmAdd",
|
|
1582
|
+
type: "confirm",
|
|
1583
|
+
default: true,
|
|
1584
|
+
},
|
|
1585
|
+
]);
|
|
1586
|
+
confirmed = confirmAdd;
|
|
1587
|
+
}
|
|
1588
|
+
if (confirmed) {
|
|
1589
|
+
try {
|
|
1590
|
+
await addApplicationButtonConfig({ data, env, token });
|
|
1591
|
+
console.log(
|
|
1592
|
+
chalk.green(
|
|
1593
|
+
`新增按钮权限成功: ${appName} - ${data.name} (${data.code})`
|
|
1594
|
+
)
|
|
1595
|
+
);
|
|
1596
|
+
} catch (e) {
|
|
1597
|
+
console.log(chalk.red(`新增按钮权限失败: ${e.message || e}`));
|
|
1598
|
+
process.exit(1);
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
if (oneClick) {
|
|
1602
|
+
goOn = false;
|
|
1603
|
+
} else {
|
|
1604
|
+
const { continueAdd } = await inquirer.prompt([
|
|
1605
|
+
{
|
|
1606
|
+
message: "是否继续为该应用添加按钮权限?",
|
|
1607
|
+
name: "continueAdd",
|
|
1608
|
+
type: "confirm",
|
|
1609
|
+
default: false,
|
|
1610
|
+
},
|
|
1611
|
+
]);
|
|
1612
|
+
goOn = continueAdd;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1183
1615
|
}
|
|
1184
1616
|
|
|
1185
1617
|
/**
|
|
@@ -2177,839 +2609,929 @@ function stylish(results) {
|
|
|
2177
2609
|
return total > 0 ? chalk.reset(output) : "";
|
|
2178
2610
|
}
|
|
2179
2611
|
|
|
2180
|
-
async function lint$1(patterns, fix, rule, hook = {}) {
|
|
2181
|
-
const rules$1 = {
|
|
2182
|
-
"i18n/no-single-chinese": rule ? "off" : "warn",
|
|
2183
|
-
};
|
|
2184
|
-
rules[0].choices.forEach((item) => {
|
|
2185
|
-
rules$1[`i18n/no-single-${item.value}`] = "off";
|
|
2186
|
-
if (item.children) {
|
|
2187
|
-
item.children.forEach((child) => {
|
|
2188
|
-
child.choices.forEach((item) => {
|
|
2189
|
-
rules$1[`i18n/no-single-${item.value}`] = "off";
|
|
2190
|
-
});
|
|
2191
|
-
});
|
|
2192
|
-
}
|
|
2193
|
-
});
|
|
2194
|
-
if (rule) {
|
|
2195
|
-
rules$1[`i18n/no-single-${rule}`] = "warn";
|
|
2196
|
-
}
|
|
2197
|
-
const eslint$1 = new eslint.ESLint({
|
|
2198
|
-
useEslintrc: false, // 不加载项目中的 .eslintrc 文件, 即不检测执行项目里规则,只检测i18n规则, 不设置为false,则会检测项目中的.eslintrc.js文件规则
|
|
2199
|
-
// allowInlineConfig: false, // 禁止解析文件内的 /* eslint-disable */ 等注释,否则虽然设置了useEslintrc:false, 它也会检测/* eslint-disable */等注释,然后找不到该规则的定义,从而检测出错误.但⚠️该规则会导致eslint-disable 失效
|
|
2200
|
-
// 只检测js,jsx,vue,ts,tsx文件
|
|
2201
|
-
extensions: [".js", ".jsx", ".vue",'ts', 'tsx'],
|
|
2202
|
-
fix: fix || false, // 是否自动修复
|
|
2203
|
-
overrideConfigFile: null, // 所有规则通过下面overrideConfig配置,不使用基于文件的配置方式,如传配指定配置文件路径
|
|
2204
|
-
plugins: { [meta.name]: i18n }, // 必须写,下面的extends才能识别, 和配置文件那种不一样,配置文件那种只写extends就行,不用写plugins
|
|
2205
|
-
overrideConfig: {
|
|
2206
|
-
extends: [
|
|
2207
|
-
"plugin:i18n/recommended", // 表示使用test插件的推荐规则, plugin:后不能有空格,否则不生效
|
|
2208
|
-
],
|
|
2209
|
-
// 此处增加rules是为了动态判断覆盖extends中的rule级别
|
|
2210
|
-
rules: rules$1,
|
|
2211
|
-
},
|
|
2212
|
-
});
|
|
2213
|
-
Loading$1.start(`检测中...`);
|
|
2214
|
-
try {
|
|
2215
|
-
// 检查文件
|
|
2216
|
-
const results = await eslint$1.lintFiles(patterns);
|
|
2217
|
-
results.forEach(item=>{
|
|
2218
|
-
if(item?.messages?.length){
|
|
2219
|
-
// 脚本只会检测脚本自定义的规则,但是在项目中运行发现eslint-disable-line xx规则会报找不到规则的错误,如
|
|
2220
|
-
// error Definition for rule '@typescript-eslint/no-unused-vars' was not found @typescript-eslint/no-unused-vars
|
|
2221
|
-
// 这种报错没有messageId,忽略掉
|
|
2222
|
-
item.messages=item.messages.filter(message=>{
|
|
2223
|
-
return message.messageId
|
|
2224
|
-
});
|
|
2225
|
-
}
|
|
2226
|
-
});
|
|
2227
|
-
hook.lintFilesAfter && (await hook.lintFilesAfter(results, Loading$1));
|
|
2228
|
-
if (!fix) {
|
|
2229
|
-
// 判断是否有匹配到错误
|
|
2230
|
-
const hasErrors = results.some(
|
|
2231
|
-
(result) => result.messages && result.messages.length
|
|
2232
|
-
);
|
|
2233
|
-
if (!hasErrors) {
|
|
2234
|
-
return Loading$1.succeed(`没有要修复的错误`);
|
|
2235
|
-
}
|
|
2236
|
-
Loading$1.succeed(`检测完成`);
|
|
2237
|
-
// 如果没有fix,则直接输出错误
|
|
2238
|
-
return console.log(stylish(results));
|
|
2239
|
-
}
|
|
2240
|
-
// 判断是否有匹配到错误
|
|
2241
|
-
const hasErrors1 = results.some(
|
|
2242
|
-
(result) => result.output || result.warningCount
|
|
2243
|
-
);
|
|
2244
|
-
if (!hasErrors1) {
|
|
2245
|
-
return Loading$1.succeed(`没有要修复的错误`);
|
|
2246
|
-
}
|
|
2247
|
-
// 输出回原文件
|
|
2248
|
-
await eslint.ESLint.outputFixes(results);
|
|
2249
|
-
Loading$1.succeed(`替换空key完成`);
|
|
2250
|
-
inquirer
|
|
2251
|
-
.prompt([
|
|
2252
|
-
{
|
|
2253
|
-
message: `是否开始翻译key`,
|
|
2254
|
-
name: "isTransKey",
|
|
2255
|
-
type: "confirm",
|
|
2256
|
-
default: true,
|
|
2257
|
-
},
|
|
2258
|
-
])
|
|
2259
|
-
.then(({ isTransKey }) => {
|
|
2260
|
-
if (isTransKey) {
|
|
2261
|
-
console.log(chalk.green(`开始准备翻译...`));
|
|
2262
|
-
process$1.execSync(`fe-it-beta --fix -i "${patterns}" `, {
|
|
2263
|
-
stdio: "inherit", // 打印子进程的输出到父进程
|
|
2264
|
-
});
|
|
2265
|
-
}
|
|
2266
|
-
});
|
|
2267
|
-
} catch (error) {
|
|
2268
|
-
Loading$1.fail(`检测失败`);
|
|
2269
|
-
console.error(chalk.red(error));
|
|
2270
|
-
}
|
|
2612
|
+
async function lint$1(patterns, fix, rule, hook = {}) {
|
|
2613
|
+
const rules$1 = {
|
|
2614
|
+
"i18n/no-single-chinese": rule ? "off" : "warn",
|
|
2615
|
+
};
|
|
2616
|
+
rules[0].choices.forEach((item) => {
|
|
2617
|
+
rules$1[`i18n/no-single-${item.value}`] = "off";
|
|
2618
|
+
if (item.children) {
|
|
2619
|
+
item.children.forEach((child) => {
|
|
2620
|
+
child.choices.forEach((item) => {
|
|
2621
|
+
rules$1[`i18n/no-single-${item.value}`] = "off";
|
|
2622
|
+
});
|
|
2623
|
+
});
|
|
2624
|
+
}
|
|
2625
|
+
});
|
|
2626
|
+
if (rule) {
|
|
2627
|
+
rules$1[`i18n/no-single-${rule}`] = "warn";
|
|
2628
|
+
}
|
|
2629
|
+
const eslint$1 = new eslint.ESLint({
|
|
2630
|
+
useEslintrc: false, // 不加载项目中的 .eslintrc 文件, 即不检测执行项目里规则,只检测i18n规则, 不设置为false,则会检测项目中的.eslintrc.js文件规则
|
|
2631
|
+
// allowInlineConfig: false, // 禁止解析文件内的 /* eslint-disable */ 等注释,否则虽然设置了useEslintrc:false, 它也会检测/* eslint-disable */等注释,然后找不到该规则的定义,从而检测出错误.但⚠️该规则会导致eslint-disable 失效
|
|
2632
|
+
// 只检测js,jsx,vue,ts,tsx文件
|
|
2633
|
+
extensions: [".js", ".jsx", ".vue",'ts', 'tsx'],
|
|
2634
|
+
fix: fix || false, // 是否自动修复
|
|
2635
|
+
overrideConfigFile: null, // 所有规则通过下面overrideConfig配置,不使用基于文件的配置方式,如传配指定配置文件路径
|
|
2636
|
+
plugins: { [meta.name]: i18n }, // 必须写,下面的extends才能识别, 和配置文件那种不一样,配置文件那种只写extends就行,不用写plugins
|
|
2637
|
+
overrideConfig: {
|
|
2638
|
+
extends: [
|
|
2639
|
+
"plugin:i18n/recommended", // 表示使用test插件的推荐规则, plugin:后不能有空格,否则不生效
|
|
2640
|
+
],
|
|
2641
|
+
// 此处增加rules是为了动态判断覆盖extends中的rule级别
|
|
2642
|
+
rules: rules$1,
|
|
2643
|
+
},
|
|
2644
|
+
});
|
|
2645
|
+
Loading$1.start(`检测中...`);
|
|
2646
|
+
try {
|
|
2647
|
+
// 检查文件
|
|
2648
|
+
const results = await eslint$1.lintFiles(patterns);
|
|
2649
|
+
results.forEach(item=>{
|
|
2650
|
+
if(item?.messages?.length){
|
|
2651
|
+
// 脚本只会检测脚本自定义的规则,但是在项目中运行发现eslint-disable-line xx规则会报找不到规则的错误,如
|
|
2652
|
+
// error Definition for rule '@typescript-eslint/no-unused-vars' was not found @typescript-eslint/no-unused-vars
|
|
2653
|
+
// 这种报错没有messageId,忽略掉
|
|
2654
|
+
item.messages=item.messages.filter(message=>{
|
|
2655
|
+
return message.messageId
|
|
2656
|
+
});
|
|
2657
|
+
}
|
|
2658
|
+
});
|
|
2659
|
+
hook.lintFilesAfter && (await hook.lintFilesAfter(results, Loading$1));
|
|
2660
|
+
if (!fix) {
|
|
2661
|
+
// 判断是否有匹配到错误
|
|
2662
|
+
const hasErrors = results.some(
|
|
2663
|
+
(result) => result.messages && result.messages.length
|
|
2664
|
+
);
|
|
2665
|
+
if (!hasErrors) {
|
|
2666
|
+
return Loading$1.succeed(`没有要修复的错误`);
|
|
2667
|
+
}
|
|
2668
|
+
Loading$1.succeed(`检测完成`);
|
|
2669
|
+
// 如果没有fix,则直接输出错误
|
|
2670
|
+
return console.log(stylish(results));
|
|
2671
|
+
}
|
|
2672
|
+
// 判断是否有匹配到错误
|
|
2673
|
+
const hasErrors1 = results.some(
|
|
2674
|
+
(result) => result.output || result.warningCount
|
|
2675
|
+
);
|
|
2676
|
+
if (!hasErrors1) {
|
|
2677
|
+
return Loading$1.succeed(`没有要修复的错误`);
|
|
2678
|
+
}
|
|
2679
|
+
// 输出回原文件
|
|
2680
|
+
await eslint.ESLint.outputFixes(results);
|
|
2681
|
+
Loading$1.succeed(`替换空key完成`);
|
|
2682
|
+
inquirer
|
|
2683
|
+
.prompt([
|
|
2684
|
+
{
|
|
2685
|
+
message: `是否开始翻译key`,
|
|
2686
|
+
name: "isTransKey",
|
|
2687
|
+
type: "confirm",
|
|
2688
|
+
default: true,
|
|
2689
|
+
},
|
|
2690
|
+
])
|
|
2691
|
+
.then(({ isTransKey }) => {
|
|
2692
|
+
if (isTransKey) {
|
|
2693
|
+
console.log(chalk.green(`开始准备翻译...`));
|
|
2694
|
+
process$1.execSync(`fe-it-beta --fix -i "${patterns}" `, {
|
|
2695
|
+
stdio: "inherit", // 打印子进程的输出到父进程
|
|
2696
|
+
});
|
|
2697
|
+
}
|
|
2698
|
+
});
|
|
2699
|
+
} catch (error) {
|
|
2700
|
+
Loading$1.fail(`检测失败`);
|
|
2701
|
+
console.error(chalk.red(error));
|
|
2702
|
+
}
|
|
2271
2703
|
}
|
|
2272
2704
|
|
|
2273
|
-
const condition = {
|
|
2274
|
-
currency: hasCurrencyName,
|
|
2275
|
-
};
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
);
|
|
2351
|
-
Promise.allSettled(requestArr).then(async (res) => {
|
|
2352
|
-
const tableData = [];
|
|
2353
|
-
res.forEach(({ value, status }, index) => {
|
|
2354
|
-
const { path, module } = moduleArr[index];
|
|
2355
|
-
if (status === "fulfilled") {
|
|
2356
|
-
const { data } = value;
|
|
2357
|
-
const { showColumns, hiddenColumns } = data;
|
|
2358
|
-
let fieldArr = [];
|
|
2359
|
-
showColumns.concat(hiddenColumns).forEach((item) => {
|
|
2360
|
-
if (condition[parentRule](item.showField)) {
|
|
2361
|
-
fieldArr.push(`${item.showField} ${item.showName}`);
|
|
2362
|
-
}
|
|
2363
|
-
});
|
|
2364
|
-
if (fieldArr.length) {
|
|
2365
|
-
fieldArr.forEach((item, index) => {
|
|
2366
|
-
let obj = {
|
|
2367
|
-
module,
|
|
2368
|
-
field: item,
|
|
2369
|
-
path,
|
|
2370
|
-
};
|
|
2371
|
-
if (index !== 0) {
|
|
2372
|
-
// obj.module = module;
|
|
2373
|
-
// obj.path = path;
|
|
2374
|
-
delete obj.module;
|
|
2375
|
-
delete obj.path;
|
|
2376
|
-
}
|
|
2377
|
-
tableData.push(obj);
|
|
2378
|
-
});
|
|
2379
|
-
} else {
|
|
2380
|
-
tableData.push({
|
|
2381
|
-
module,
|
|
2382
|
-
field: "NA",
|
|
2383
|
-
path,
|
|
2384
|
-
});
|
|
2385
|
-
}
|
|
2386
|
-
} else {
|
|
2387
|
-
tableData.push({
|
|
2388
|
-
module,
|
|
2389
|
-
field: "error",
|
|
2390
|
-
path,
|
|
2391
|
-
});
|
|
2392
|
-
}
|
|
2393
|
-
});
|
|
2394
|
-
// 合并动态模块
|
|
2395
|
-
dynamicsArr.forEach((item) => {
|
|
2396
|
-
tableData.push({
|
|
2397
|
-
module: item.module,
|
|
2398
|
-
field: "error",
|
|
2399
|
-
path: item.path,
|
|
2400
|
-
});
|
|
2401
|
-
});
|
|
2402
|
-
// 排序, field为NA和error的排在后面
|
|
2403
|
-
tableData.sort((a, b) => {
|
|
2404
|
-
if (["NA", "error"].includes(b.field)) {
|
|
2405
|
-
return -1;
|
|
2406
|
-
} else {
|
|
2407
|
-
return 1;
|
|
2408
|
-
}
|
|
2409
|
-
});
|
|
2410
|
-
const successModuleArr = new Set();
|
|
2411
|
-
|
|
2412
|
-
const naTableData = [];
|
|
2413
|
-
const naModuleArr = new Set();
|
|
2414
|
-
|
|
2415
|
-
const noNaTableData = [];
|
|
2416
|
-
const noNaModuleArr = new Set();
|
|
2417
|
-
|
|
2418
|
-
const errorTableData = []; // 失败的数据
|
|
2419
|
-
const errorModuleArr = new Set();
|
|
2420
|
-
|
|
2421
|
-
tableData.forEach((item) => {
|
|
2422
|
-
if (item.field === "error") {
|
|
2423
|
-
errorTableData.push(item);
|
|
2424
|
-
item.module && errorModuleArr.add(item.module);
|
|
2425
|
-
} else {
|
|
2426
|
-
if (item.field === "NA") {
|
|
2427
|
-
naTableData.push(item);
|
|
2428
|
-
item.module &&naModuleArr.add(item.module);
|
|
2429
|
-
} else {
|
|
2430
|
-
noNaTableData.push(item);
|
|
2431
|
-
item.module &&noNaModuleArr.add(item.module);
|
|
2432
|
-
}
|
|
2433
|
-
item.module &&successModuleArr.add(item.module);
|
|
2434
|
-
}
|
|
2435
|
-
});
|
|
2436
|
-
|
|
2437
|
-
console.log(
|
|
2438
|
-
chalk.green(
|
|
2439
|
-
`共${moduleArr.length + dynamicsArr.length}个moduleId, 请求成功${
|
|
2440
|
-
successModuleArr.size
|
|
2441
|
-
}个(其中规则命中${noNaModuleArr.size}个, ${chalk.yellow(
|
|
2442
|
-
`未命中${naModuleArr.size}个`
|
|
2443
|
-
)}),`
|
|
2444
|
-
) + chalk.red(`请求失败${errorModuleArr.size}个`)
|
|
2445
|
-
);
|
|
2446
|
-
console.log(chalk.green(`🚀🚀🚀🚀🚀共匹配到${noNaTableData.length+errorTableData.length}个字段🚀🚀🚀🚀🚀`));
|
|
2447
|
-
|
|
2448
|
-
if (noNaTableData.length) {
|
|
2449
|
-
console.log(chalk.green(`成功命中字段${noNaTableData.length}个`));
|
|
2450
|
-
console.table(noNaTableData);
|
|
2451
|
-
}
|
|
2452
|
-
if (errorTableData.length) {
|
|
2453
|
-
console.log(chalk.red(`失败,需手动处理或重试${errorTableData.length}个`));
|
|
2454
|
-
console.table(errorTableData);
|
|
2455
|
-
}
|
|
2456
|
-
if (naTableData.length) {
|
|
2457
|
-
console.log(
|
|
2458
|
-
chalk.yellow(`成功但未命中字段,无需处理${naTableData.length}个`)
|
|
2459
|
-
);
|
|
2460
|
-
console.table(naTableData);
|
|
2461
|
-
}
|
|
2462
|
-
});
|
|
2705
|
+
const condition = {
|
|
2706
|
+
currency: hasCurrencyName,
|
|
2707
|
+
};
|
|
2708
|
+
// 自定义列检测(支持 --way/--column-id 一键, CLI参数 > 交互)
|
|
2709
|
+
async function customColumnModule (lintc, fix, rule, parentRule, cliOpts = {}) {
|
|
2710
|
+
// 空字符串视为未传(与askOrUse语义一致)
|
|
2711
|
+
const cliWay = cliOpts.way || undefined;
|
|
2712
|
+
const cliColumnId = cliOpts.columnId || undefined;
|
|
2713
|
+
// CLI参数校验前置(快速失败, 不进交互)
|
|
2714
|
+
if (cliWay !== undefined) {
|
|
2715
|
+
validateChoice(cliWay, ["normal", "input"], "--way");
|
|
2716
|
+
}
|
|
2717
|
+
if (cliColumnId !== undefined && cliWay !== "input") {
|
|
2718
|
+
console.log(chalk.red("参数 --column-id 须搭配 --way input 使用"));
|
|
2719
|
+
process.exit(1);
|
|
2720
|
+
}
|
|
2721
|
+
const way = await askOrUse(cliWay, {
|
|
2722
|
+
message: "请选择检测自定义列方式",
|
|
2723
|
+
name: "way",
|
|
2724
|
+
type: "list",
|
|
2725
|
+
choices: [
|
|
2726
|
+
{
|
|
2727
|
+
name: "自动检测HxSearchListPage、this.$customColumnDialog的自定义列ID",
|
|
2728
|
+
value: "normal",
|
|
2729
|
+
},
|
|
2730
|
+
{
|
|
2731
|
+
name: "自定义输入",
|
|
2732
|
+
value: "input",
|
|
2733
|
+
},
|
|
2734
|
+
],
|
|
2735
|
+
});
|
|
2736
|
+
if (way === "normal") {
|
|
2737
|
+
lint$1(lintc, fix, "customColumnModule", {
|
|
2738
|
+
lintFilesAfter: (results, Loading) => {
|
|
2739
|
+
const arr = getModuleArr();
|
|
2740
|
+
const dynamicsArr = arr.filter((item) => item.isDynamics);
|
|
2741
|
+
const normalArr = arr.filter((item) => !item.isDynamics);
|
|
2742
|
+
|
|
2743
|
+
return new Promise((resolve) => {
|
|
2744
|
+
Loading.succeed(`检测完成,${arr.length ? "" : "未"}匹配到规则`);
|
|
2745
|
+
if (arr.length) {
|
|
2746
|
+
getColumn(normalArr, dynamicsArr, parentRule);
|
|
2747
|
+
}
|
|
2748
|
+
});
|
|
2749
|
+
},
|
|
2750
|
+
});
|
|
2751
|
+
} else if (way === "input") {
|
|
2752
|
+
// CLI传入直接用, 否则交互输入
|
|
2753
|
+
let customColumnId;
|
|
2754
|
+
if (cliColumnId !== undefined) {
|
|
2755
|
+
customColumnId = cliColumnId;
|
|
2756
|
+
} else {
|
|
2757
|
+
const res = await inquirer.prompt([
|
|
2758
|
+
{
|
|
2759
|
+
message: "请输入自定义列ID,格式为xx,xx,xx",
|
|
2760
|
+
name: "customColumnId",
|
|
2761
|
+
type: "input",
|
|
2762
|
+
},
|
|
2763
|
+
]);
|
|
2764
|
+
customColumnId = res.customColumnId;
|
|
2765
|
+
}
|
|
2766
|
+
const columnArr = customColumnId
|
|
2767
|
+
.split(",")
|
|
2768
|
+
.map((item) => item.trim())
|
|
2769
|
+
.filter(Boolean);
|
|
2770
|
+
if (!columnArr.length) {
|
|
2771
|
+
console.log(chalk.red("自定义列ID不能为空"));
|
|
2772
|
+
process.exit(1);
|
|
2773
|
+
}
|
|
2774
|
+
const moduleArr = columnArr.map((item) => {
|
|
2775
|
+
return {
|
|
2776
|
+
module: item,
|
|
2777
|
+
path: "NA",
|
|
2778
|
+
};
|
|
2779
|
+
});
|
|
2780
|
+
getColumn(moduleArr, [], parentRule);
|
|
2781
|
+
}
|
|
2463
2782
|
}
|
|
2464
2783
|
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
}
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
//
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
//
|
|
2557
|
-
const
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
{
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
.
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
}
|
|
2803
|
-
console.log(chalk.green(
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
}
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
"
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
2784
|
+
async function getColumn(moduleArr = [], dynamicsArr = [], parentRule) {
|
|
2785
|
+
let token;
|
|
2786
|
+
try {
|
|
2787
|
+
token = await login({
|
|
2788
|
+
env: "sit",
|
|
2789
|
+
username: "superAdmin",
|
|
2790
|
+
password: "admin1",
|
|
2791
|
+
});
|
|
2792
|
+
} catch (e) {
|
|
2793
|
+
console.log(chalk.red(`登录失败: ${e.message || e}`));
|
|
2794
|
+
return;
|
|
2795
|
+
}
|
|
2796
|
+
const requestArr = moduleArr.map(({ module, path }) =>
|
|
2797
|
+
request$1({
|
|
2798
|
+
url: `http://sit-hxjf.hongxinshop.com/api-item/api/customizedColumns/find`,
|
|
2799
|
+
method: "POST",
|
|
2800
|
+
json: true,
|
|
2801
|
+
body: { module },
|
|
2802
|
+
headers: {
|
|
2803
|
+
Authorization: `Bearer ${token}`,
|
|
2804
|
+
"x-request-vaildate": "open",
|
|
2805
|
+
},
|
|
2806
|
+
})
|
|
2807
|
+
);
|
|
2808
|
+
Promise.allSettled(requestArr).then(async (res) => {
|
|
2809
|
+
const tableData = [];
|
|
2810
|
+
res.forEach(({ value, status }, index) => {
|
|
2811
|
+
const { path, module } = moduleArr[index];
|
|
2812
|
+
if (status === "fulfilled") {
|
|
2813
|
+
const { data } = value;
|
|
2814
|
+
const { showColumns, hiddenColumns } = data;
|
|
2815
|
+
let fieldArr = [];
|
|
2816
|
+
showColumns.concat(hiddenColumns).forEach((item) => {
|
|
2817
|
+
if (condition[parentRule](item.showField)) {
|
|
2818
|
+
fieldArr.push(`${item.showField} ${item.showName}`);
|
|
2819
|
+
}
|
|
2820
|
+
});
|
|
2821
|
+
if (fieldArr.length) {
|
|
2822
|
+
fieldArr.forEach((item, index) => {
|
|
2823
|
+
let obj = {
|
|
2824
|
+
module,
|
|
2825
|
+
field: item,
|
|
2826
|
+
path,
|
|
2827
|
+
};
|
|
2828
|
+
if (index !== 0) {
|
|
2829
|
+
// obj.module = module;
|
|
2830
|
+
// obj.path = path;
|
|
2831
|
+
delete obj.module;
|
|
2832
|
+
delete obj.path;
|
|
2833
|
+
}
|
|
2834
|
+
tableData.push(obj);
|
|
2835
|
+
});
|
|
2836
|
+
} else {
|
|
2837
|
+
tableData.push({
|
|
2838
|
+
module,
|
|
2839
|
+
field: "NA",
|
|
2840
|
+
path,
|
|
2841
|
+
});
|
|
2842
|
+
}
|
|
2843
|
+
} else {
|
|
2844
|
+
tableData.push({
|
|
2845
|
+
module,
|
|
2846
|
+
field: "error",
|
|
2847
|
+
path,
|
|
2848
|
+
});
|
|
2849
|
+
}
|
|
2850
|
+
});
|
|
2851
|
+
// 合并动态模块
|
|
2852
|
+
dynamicsArr.forEach((item) => {
|
|
2853
|
+
tableData.push({
|
|
2854
|
+
module: item.module,
|
|
2855
|
+
field: "error",
|
|
2856
|
+
path: item.path,
|
|
2857
|
+
});
|
|
2858
|
+
});
|
|
2859
|
+
// 排序, field为NA和error的排在后面
|
|
2860
|
+
tableData.sort((a, b) => {
|
|
2861
|
+
if (["NA", "error"].includes(b.field)) {
|
|
2862
|
+
return -1;
|
|
2863
|
+
} else {
|
|
2864
|
+
return 1;
|
|
2865
|
+
}
|
|
2866
|
+
});
|
|
2867
|
+
const successModuleArr = new Set();
|
|
2868
|
+
|
|
2869
|
+
const naTableData = [];
|
|
2870
|
+
const naModuleArr = new Set();
|
|
2871
|
+
|
|
2872
|
+
const noNaTableData = [];
|
|
2873
|
+
const noNaModuleArr = new Set();
|
|
2874
|
+
|
|
2875
|
+
const errorTableData = []; // 失败的数据
|
|
2876
|
+
const errorModuleArr = new Set();
|
|
2877
|
+
|
|
2878
|
+
tableData.forEach((item) => {
|
|
2879
|
+
if (item.field === "error") {
|
|
2880
|
+
errorTableData.push(item);
|
|
2881
|
+
item.module && errorModuleArr.add(item.module);
|
|
2882
|
+
} else {
|
|
2883
|
+
if (item.field === "NA") {
|
|
2884
|
+
naTableData.push(item);
|
|
2885
|
+
item.module &&naModuleArr.add(item.module);
|
|
2886
|
+
} else {
|
|
2887
|
+
noNaTableData.push(item);
|
|
2888
|
+
item.module &&noNaModuleArr.add(item.module);
|
|
2889
|
+
}
|
|
2890
|
+
item.module &&successModuleArr.add(item.module);
|
|
2891
|
+
}
|
|
2892
|
+
});
|
|
2893
|
+
|
|
2894
|
+
console.log(
|
|
2895
|
+
chalk.green(
|
|
2896
|
+
`共${moduleArr.length + dynamicsArr.length}个moduleId, 请求成功${
|
|
2897
|
+
successModuleArr.size
|
|
2898
|
+
}个(其中规则命中${noNaModuleArr.size}个, ${chalk.yellow(
|
|
2899
|
+
`未命中${naModuleArr.size}个`
|
|
2900
|
+
)}),`
|
|
2901
|
+
) + chalk.red(`请求失败${errorModuleArr.size}个`)
|
|
2902
|
+
);
|
|
2903
|
+
console.log(chalk.green(`🚀🚀🚀🚀🚀共匹配到${noNaTableData.length+errorTableData.length}个字段🚀🚀🚀🚀🚀`));
|
|
2904
|
+
|
|
2905
|
+
if (noNaTableData.length) {
|
|
2906
|
+
console.log(chalk.green(`成功命中字段${noNaTableData.length}个`));
|
|
2907
|
+
console.table(noNaTableData);
|
|
2908
|
+
}
|
|
2909
|
+
if (errorTableData.length) {
|
|
2910
|
+
console.log(chalk.red(`失败,需手动处理或重试${errorTableData.length}个`));
|
|
2911
|
+
console.table(errorTableData);
|
|
2912
|
+
}
|
|
2913
|
+
if (naTableData.length) {
|
|
2914
|
+
console.log(
|
|
2915
|
+
chalk.yellow(`成功但未命中字段,无需处理${naTableData.length}个`)
|
|
2916
|
+
);
|
|
2917
|
+
console.table(naTableData);
|
|
2918
|
+
}
|
|
2919
|
+
});
|
|
2920
|
+
}
|
|
2921
|
+
|
|
2922
|
+
// import packageJson from "../package.json" assert { type: "json" };
|
|
2923
|
+
const { version } = getPackageJson();
|
|
2924
|
+
|
|
2925
|
+
// 获取git修改的文件以及暂存区的文件(包含未跟踪的新文件)
|
|
2926
|
+
function getGitChangedFiles() {
|
|
2927
|
+
const cwd = process.cwd();
|
|
2928
|
+
try {
|
|
2929
|
+
const repoRoot = process$1.execSync("git rev-parse --show-toplevel", {
|
|
2930
|
+
cwd,
|
|
2931
|
+
encoding: "utf-8",
|
|
2932
|
+
}).trim();
|
|
2933
|
+
// 1. git status --porcelain 获取已跟踪文件的变更(已暂存staged + 未暂存unstaged)
|
|
2934
|
+
// 注意: 不能用 git diff --name-only HEAD, 它不会包含未跟踪(untracked)的新文件
|
|
2935
|
+
const statusOutput = process$1.execSync("git status --porcelain", {
|
|
2936
|
+
cwd: repoRoot,
|
|
2937
|
+
encoding: "utf-8",
|
|
2938
|
+
});
|
|
2939
|
+
const trackedFiles = statusOutput
|
|
2940
|
+
.split("\n")
|
|
2941
|
+
.map((line) => line.replace(/\r$/, ""))
|
|
2942
|
+
.filter(Boolean)
|
|
2943
|
+
.map((line) => {
|
|
2944
|
+
// 输出格式: XY filename, X=暂存区状态, Y=工作区状态
|
|
2945
|
+
const status = line.slice(0, 2);
|
|
2946
|
+
let filePath = line.slice(3);
|
|
2947
|
+
// 重命名(R)/复制(C): "old -> new", 取新文件名
|
|
2948
|
+
if (status[0] === "R" || status[0] === "C") {
|
|
2949
|
+
const idx = filePath.indexOf(" -> ");
|
|
2950
|
+
if (idx > -1) filePath = filePath.slice(idx + 4);
|
|
2951
|
+
}
|
|
2952
|
+
// 含特殊字符的文件名git会加引号, 去除引号
|
|
2953
|
+
if (filePath.startsWith('"') && filePath.endsWith('"')) {
|
|
2954
|
+
filePath = filePath.slice(1, -1);
|
|
2955
|
+
}
|
|
2956
|
+
return { status, filePath };
|
|
2957
|
+
})
|
|
2958
|
+
// 排除删除的文件 (D 在暂存区或工作区)
|
|
2959
|
+
.filter(({ status }) => !status.includes("D"))
|
|
2960
|
+
// 未跟踪文件(??)用 git ls-files 单独处理(因为可能只显示目录,不展开)
|
|
2961
|
+
.filter(({ status }) => status !== "??")
|
|
2962
|
+
.map(({ filePath }) => filePath);
|
|
2963
|
+
|
|
2964
|
+
// 2. git ls-files --others --exclude-standard 获取未跟踪的新文件
|
|
2965
|
+
// 相比 git status --porcelain, 它会自动展开目录, 列出具体文件
|
|
2966
|
+
const untrackedOutput = process$1.execSync(
|
|
2967
|
+
"git ls-files --others --exclude-standard",
|
|
2968
|
+
{ cwd: repoRoot, encoding: "utf-8" }
|
|
2969
|
+
);
|
|
2970
|
+
const untrackedFiles = untrackedOutput
|
|
2971
|
+
.split("\n")
|
|
2972
|
+
.map((f) => f.trim())
|
|
2973
|
+
.filter(Boolean);
|
|
2974
|
+
|
|
2975
|
+
// 合并去重
|
|
2976
|
+
const allFiles = Array.from(
|
|
2977
|
+
new Set([...trackedFiles, ...untrackedFiles])
|
|
2978
|
+
)
|
|
2979
|
+
// 只保留lint支持的文件类型
|
|
2980
|
+
.filter((f) => /\.(js|jsx|vue|ts|tsx)$/.test(f))
|
|
2981
|
+
// git输出的路径相对于仓库根目录, 转为绝对路径
|
|
2982
|
+
.map((f) => path.join(repoRoot, f))
|
|
2983
|
+
// 过滤掉不存在的文件
|
|
2984
|
+
.filter((f) => fs.existsSync(f));
|
|
2985
|
+
return allFiles;
|
|
2986
|
+
} catch (error) {
|
|
2987
|
+
// git命令失败(如不在git仓库中运行)属于使用错误, 以退出码1结束
|
|
2988
|
+
console.log(chalk.red("获取git修改文件失败,请确保在git仓库中运行"));
|
|
2989
|
+
process.exit(1);
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
// const spinner = ora();
|
|
2993
|
+
commander.program.version(version);
|
|
2994
|
+
commander.program.option("-i, --input <type>", "翻译空key");
|
|
2995
|
+
// command('merge')
|
|
2996
|
+
// .option("-i, --input <type>" , "翻译空key")
|
|
2997
|
+
// .option("-m, --merge" , "输出文件").action((options) => {
|
|
2998
|
+
// console.log('merge', options)
|
|
2999
|
+
// })
|
|
3000
|
+
// 上传翻译
|
|
3001
|
+
commander.program.option("-u, --upload", "上传翻译包");
|
|
3002
|
+
// 同步sass应用权限配置
|
|
3003
|
+
commander.program.option("-sass, --sass", "sass按钮权限配置(同步/新增)");
|
|
3004
|
+
// 检测未$hxt中文
|
|
3005
|
+
commander.program.option("-lint, --lint <patterns>", "检测未$hxt的中文");
|
|
3006
|
+
// 检测配置项
|
|
3007
|
+
commander.program.option("-lintc, --lintc <patterns>", "检测指定配置规则");
|
|
3008
|
+
// 检测未$hxt中文自动修复
|
|
3009
|
+
commander.program.option("--fix", "修复lint检出的错误");
|
|
3010
|
+
// 翻译excel指定列语言
|
|
3011
|
+
commander.program.option("-excel, --excel", "翻译excel指定列语言");
|
|
3012
|
+
// 一键执行选择参数(取值优先级: CLI参数 > --cache凭据缓存 > 交互询问)
|
|
3013
|
+
commander.program.option("--cache", "凭据静默走缓存(百度appid/key、域账号密码), 缓存缺失时回退交互");
|
|
3014
|
+
commander.program.option("--env <env>", "上传环境: mit|sit|uat|pro, 多环境逗号分隔(仅mit/sit/uat, pro须单独传); sass新增时单选mit|sit|uat");
|
|
3015
|
+
commander.program.option("--username <name>", "域账号(env为pro时为生产域账号)");
|
|
3016
|
+
commander.program.option("--password <pwd>", "域密码(明文会进shell历史, 日常建议用--cache)");
|
|
3017
|
+
commander.program.option("--files <files>", "上传的excel文件, 逗号分隔或all(全部), 如 --files \"a.xlsx,b.xlsx\"");
|
|
3018
|
+
commander.program.option("--appid <appid>", "百度翻译appid");
|
|
3019
|
+
commander.program.option("--key <key>", "百度翻译key");
|
|
3020
|
+
commander.program.option("--merge", "翻译结果合并至export.xlsx(默认覆盖)");
|
|
3021
|
+
commander.program.option("--from <env>", "sass同步源环境: mit|sit|uat");
|
|
3022
|
+
commander.program.option("--to <env>", "sass同步目标环境: mit|sit|uat(须不同于--from)");
|
|
3023
|
+
commander.program.option("--app <name>", "sass应用名称关键字(同步/新增)");
|
|
3024
|
+
commander.program.option("--action <action>", "sass功能: sync(同步按钮权限) | permission(新增按钮权限)");
|
|
3025
|
+
commander.program.option("--permission-name <name>", "sass按钮权限名称(须搭配 --action permission)");
|
|
3026
|
+
commander.program.option("--permission-code <code>", "sass按钮权限编码(须搭配 --action permission)");
|
|
3027
|
+
commander.program.option("--rule <rule>", "lintc检查规则: currency");
|
|
3028
|
+
commander.program.option("--scene <scene>", "lintc检查场景: currency|customColumnModule");
|
|
3029
|
+
commander.program.option("--way <way>", "自定义列检测方式: normal|input");
|
|
3030
|
+
commander.program.option("--column-id <ids>", "自定义列ID, 逗号分隔(way=input时), 如 --column-id \"a,b,c\"");
|
|
3031
|
+
commander.program.option("--file <name>", "excel翻译的文件名");
|
|
3032
|
+
commander.program.option("--lang <lang>", "excel翻译的语言列(如en、vi)");
|
|
3033
|
+
// help追加的一键执行示例(命令绿色, 说明灰色, 按最长命令对齐)
|
|
3034
|
+
const helpExamples = [
|
|
3035
|
+
["fe-it-beta -u --env sit --files all --cache", "上传全部excel到sit, 账号密码走缓存"],
|
|
3036
|
+
["fe-it-beta -u --env sit --files all --username xxx --cache", "账号用传的, 其余凭据走缓存(可混合)"],
|
|
3037
|
+
['fe-it-beta -u --env "mit,sit,uat" --files export.xlsx --cache', ""],
|
|
3038
|
+
["fe-it-beta -i src --cache", "翻译空key, 凭据走缓存, 覆盖export.xlsx"],
|
|
3039
|
+
["fe-it-beta -i src --appid x --key y --merge", "翻译并合并至export.xlsx"],
|
|
3040
|
+
["fe-it-beta -lint src --fix", "检测并自动修复"],
|
|
3041
|
+
[
|
|
3042
|
+
'fe-it-beta -lintc src --rule currency --scene customColumnModule --way input --column-id "a,b,c"',
|
|
3043
|
+
"",
|
|
3044
|
+
],
|
|
3045
|
+
["fe-it-beta -sass --from mit --to sit --app 应用名", "传同步参数直通同步流程"],
|
|
3046
|
+
[
|
|
3047
|
+
"fe-it-beta -sass --action permission --env mit --app 应用名 --permission-name 权限名 --permission-code 编码",
|
|
3048
|
+
"一键新增按钮权限(参数齐全免确认)",
|
|
3049
|
+
],
|
|
3050
|
+
["fe-it-beta -excel --file export.xlsx --lang vi --cache", ""],
|
|
3051
|
+
];
|
|
3052
|
+
commander.program.addHelpText(
|
|
3053
|
+
"after",
|
|
3054
|
+
"\n" +
|
|
3055
|
+
chalk.bold("一键执行示例") +
|
|
3056
|
+
" " +
|
|
3057
|
+
chalk.gray("(注意: PowerShell下含逗号的参数值需加引号)") +
|
|
3058
|
+
":\n" +
|
|
3059
|
+
helpExamples
|
|
3060
|
+
.map(([cmd, desc]) => {
|
|
3061
|
+
// 说明列固定从62列起, 超长命令后接2空格
|
|
3062
|
+
const pad = " ".repeat(Math.max(2, 62 - cmd.length));
|
|
3063
|
+
return " " + chalk.green(cmd) + (desc ? chalk.gray(pad + desc) : "");
|
|
3064
|
+
})
|
|
3065
|
+
.join("\n")
|
|
3066
|
+
);
|
|
3067
|
+
commander.program.parse(process.argv);
|
|
3068
|
+
|
|
3069
|
+
// 判断命令参数
|
|
3070
|
+
const { input, sass, upload, lint, lintc, fix, excel } = commander.program.opts();
|
|
3071
|
+
const cliOpts = commander.program.opts();
|
|
3072
|
+
const rules = [
|
|
3073
|
+
{
|
|
3074
|
+
message: "请选择检查规则",
|
|
3075
|
+
name: "rule",
|
|
3076
|
+
type: "list",
|
|
3077
|
+
choices: [
|
|
3078
|
+
{
|
|
3079
|
+
name: "币种",
|
|
3080
|
+
value: "currency",
|
|
3081
|
+
children: [
|
|
3082
|
+
{
|
|
3083
|
+
message: "请选择场景",
|
|
3084
|
+
name: "scene",
|
|
3085
|
+
type: "list",
|
|
3086
|
+
choices: [
|
|
3087
|
+
{
|
|
3088
|
+
name: "匹配属性名含有price、total、amount金额字段",
|
|
3089
|
+
value: "currency",
|
|
3090
|
+
},
|
|
3091
|
+
{
|
|
3092
|
+
name: "自定义列",
|
|
3093
|
+
value: "customColumnModule",
|
|
3094
|
+
},
|
|
3095
|
+
],
|
|
3096
|
+
},
|
|
3097
|
+
],
|
|
3098
|
+
},
|
|
3099
|
+
],
|
|
3100
|
+
},
|
|
3101
|
+
];
|
|
3102
|
+
if (sass) {
|
|
3103
|
+
sassMain(cliOpts);
|
|
3104
|
+
} else if (upload) {
|
|
3105
|
+
upload$1(cliOpts);
|
|
3106
|
+
} else if (lint !== undefined) {
|
|
3107
|
+
// 必须传入路径
|
|
3108
|
+
if (!lint || !lint.trim()) {
|
|
3109
|
+
console.log(
|
|
3110
|
+
chalk.red("请传入要检测的路径, 或使用 -lint git 检测git修改的文件")
|
|
3111
|
+
);
|
|
3112
|
+
process.exit(1);
|
|
3113
|
+
}
|
|
3114
|
+
let lintPatterns = lint.trim();
|
|
3115
|
+
// 如果路径写死为 git, 则用git检测修改的文件以及暂存区的文件作为路径
|
|
3116
|
+
if (lintPatterns === "git") {
|
|
3117
|
+
lintPatterns = getGitChangedFiles();
|
|
3118
|
+
if (!lintPatterns.length) {
|
|
3119
|
+
console.log(chalk.green("没有检测到git修改的文件"));
|
|
3120
|
+
process.exit(0);
|
|
3121
|
+
}
|
|
3122
|
+
console.log(chalk.green(`检测到${lintPatterns.length}个git修改文件:`));
|
|
3123
|
+
lintPatterns.forEach((f) => console.log(chalk.gray(` ${f}`)));
|
|
3124
|
+
}
|
|
3125
|
+
lint$1(lintPatterns, fix);
|
|
3126
|
+
} else if (lintc) {
|
|
3127
|
+
runLintc().catch((error) => console.error(error));
|
|
3128
|
+
} else if (excel) {
|
|
3129
|
+
excelFn(cliOpts);
|
|
3130
|
+
} else {
|
|
3131
|
+
runTranslate().catch((error) => console.error(error));
|
|
3132
|
+
}
|
|
3133
|
+
|
|
3134
|
+
// lintc分支(支持 --rule/--scene 一键执行)
|
|
3135
|
+
async function runLintc() {
|
|
3136
|
+
const ruleQuestion = rules.find((item) => item.name === "rule");
|
|
3137
|
+
const ruleValues = ruleQuestion.choices.map((item) => item.value);
|
|
3138
|
+
if (cliOpts.rule !== undefined) {
|
|
3139
|
+
validateChoice(cliOpts.rule, ruleValues, "--rule");
|
|
3140
|
+
}
|
|
3141
|
+
const parentRule = await askOrUse(cliOpts.rule, ruleQuestion);
|
|
3142
|
+
if (parentRule === "currency") {
|
|
3143
|
+
const currencySubChoice = ruleQuestion.choices.find(
|
|
3144
|
+
(item) => item.value === "currency"
|
|
3145
|
+
).children;
|
|
3146
|
+
const sceneValues = currencySubChoice[0].choices.map((item) => item.value);
|
|
3147
|
+
if (cliOpts.scene !== undefined) {
|
|
3148
|
+
validateChoice(cliOpts.scene, sceneValues, "--scene");
|
|
3149
|
+
}
|
|
3150
|
+
const scene = await askOrUse(cliOpts.scene, currencySubChoice[0]);
|
|
3151
|
+
if (scene === "customColumnModule") {
|
|
3152
|
+
return customColumnModule(lintc, fix, scene, parentRule, cliOpts);
|
|
3153
|
+
}
|
|
3154
|
+
lint$1(lintc, fix, scene);
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
|
|
3158
|
+
// 默认翻译分支(支持 --appid/--key/--merge/--cache 一键执行)
|
|
3159
|
+
async function runTranslate() {
|
|
3160
|
+
// 前置守卫: 必须传入 -i 路径
|
|
3161
|
+
if (!input || !String(input).trim()) {
|
|
3162
|
+
console.log(chalk.red("请传入要翻译的路径 -i <path>"));
|
|
3163
|
+
process.exit(1);
|
|
3164
|
+
}
|
|
3165
|
+
const { cache, appid, key, merge } = cliOpts;
|
|
3166
|
+
const oneClick = Boolean(cache || appid || key || merge);
|
|
3167
|
+
let projectName = "请在package.json中配置name字段(项目名称)";
|
|
3168
|
+
const projectPath = "./package.json";
|
|
3169
|
+
const isExist = isExistPath(projectPath);
|
|
3170
|
+
if (isExist) {
|
|
3171
|
+
const str = fs.readFileSync(projectPath, "utf-8").toString();
|
|
3172
|
+
projectName = JSON.parse(str).name;
|
|
3173
|
+
}
|
|
3174
|
+
if (!oneClick) {
|
|
3175
|
+
const { isProjectName } = await inquirer.prompt([
|
|
3176
|
+
{
|
|
3177
|
+
message: `请确认package.json中项目名称是否是${projectName}`,
|
|
3178
|
+
name: "isProjectName",
|
|
3179
|
+
type: "confirm",
|
|
3180
|
+
default: true,
|
|
3181
|
+
},
|
|
3182
|
+
]);
|
|
3183
|
+
if (!isProjectName) {
|
|
3184
|
+
console.log(
|
|
3185
|
+
chalk.red("请在package.json中配置正确的name字段(项目名称)")
|
|
3186
|
+
);
|
|
3187
|
+
process.exit(1);
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
// 翻译接口(当前仅百度, 一键执行时自动选择)
|
|
3191
|
+
let type = "baidu";
|
|
3192
|
+
if (!oneClick) {
|
|
3193
|
+
const res = await inquirer.prompt([
|
|
3194
|
+
{
|
|
3195
|
+
message: "请选择翻译接口",
|
|
3196
|
+
name: "type",
|
|
3197
|
+
type: "list",
|
|
3198
|
+
choices: [
|
|
3199
|
+
{
|
|
3200
|
+
name: "百度翻译",
|
|
3201
|
+
value: "baidu",
|
|
3202
|
+
},
|
|
3203
|
+
],
|
|
3204
|
+
},
|
|
3205
|
+
]);
|
|
3206
|
+
type = res.type;
|
|
3207
|
+
}
|
|
3208
|
+
// 凭据: CLI参数 > --cache缓存 > 交互
|
|
3209
|
+
const secret = await resolveSecretQuestions({
|
|
3210
|
+
questions: [
|
|
3211
|
+
{
|
|
3212
|
+
message: "请输入百度翻译的appid",
|
|
3213
|
+
name: "appid",
|
|
3214
|
+
// 必填
|
|
3215
|
+
validate: function (val) {
|
|
3216
|
+
if (val) {
|
|
3217
|
+
return true;
|
|
3218
|
+
}
|
|
3219
|
+
return "请输入百度翻译的appid";
|
|
3220
|
+
},
|
|
3221
|
+
},
|
|
3222
|
+
{
|
|
3223
|
+
message: "请输入百度翻译的key",
|
|
3224
|
+
name: "key",
|
|
3225
|
+
// 必填
|
|
3226
|
+
validate: function (val) {
|
|
3227
|
+
if (val) {
|
|
3228
|
+
return true;
|
|
3229
|
+
}
|
|
3230
|
+
return "请输入百度翻译的key";
|
|
3231
|
+
},
|
|
3232
|
+
},
|
|
3233
|
+
],
|
|
3234
|
+
cliValues: { appid, key },
|
|
3235
|
+
useCache: Boolean(cache),
|
|
3236
|
+
cacheKey: type,
|
|
3237
|
+
extraCache: { type },
|
|
3238
|
+
});
|
|
3239
|
+
// isMerge预决定: --merge为true, 一键执行默认false(覆盖), 否则翻译完成后交互询问
|
|
3240
|
+
const isMergeDecision = merge ? true : oneClick ? false : null;
|
|
3241
|
+
translateUtil(secret.appid, secret.key, projectName, fix, isMergeDecision);
|
|
3242
|
+
}
|
|
3243
|
+
|
|
3244
|
+
function translateUtil(appid, key, projectName, isLintFix, isMergeDecision) {
|
|
3245
|
+
const { input } = commander.program.opts();
|
|
3246
|
+
// 支持逗号分隔的多个路径(来自 -lint git 的多文件场景)
|
|
3247
|
+
const inputPaths = String(input)
|
|
3248
|
+
.split(",")
|
|
3249
|
+
.map((s) => s.trim())
|
|
3250
|
+
.filter(Boolean);
|
|
3251
|
+
let src = [];
|
|
3252
|
+
inputPaths.forEach((inputPath) => {
|
|
3253
|
+
const normalizedInput = inputPath.replace(/\\/g, "/");
|
|
3254
|
+
if (fs.statSync(inputPath).isDirectory()) {
|
|
3255
|
+
const directory = normalizedInput + "/**/*.{js,vue,jsx,ts,tsx}";
|
|
3256
|
+
const files = glob.glob.sync(directory, {
|
|
3257
|
+
ignore: directory + `/**/node_modules/**`,
|
|
3258
|
+
});
|
|
3259
|
+
src = src.concat(files);
|
|
3260
|
+
} else {
|
|
3261
|
+
src.push(normalizedInput);
|
|
3262
|
+
}
|
|
3263
|
+
});
|
|
3264
|
+
let allArr = []; // 所有文本组成的数组
|
|
3265
|
+
let groupArr = []; // 分组后的数组
|
|
3266
|
+
let newAllArr = []; // 分组后的数组还原成所有数组
|
|
3267
|
+
// 循环遍历文件下的文件
|
|
3268
|
+
function readFolder(isOk) {
|
|
3269
|
+
// spinner.start();
|
|
3270
|
+
src.forEach((item, index) => {
|
|
3271
|
+
const fPath = item;
|
|
3272
|
+
let file = fs.readFileSync(fPath, "utf-8").toString();
|
|
3273
|
+
// 提取file里所有以$hxt({ key: '', desc:为开头 以')}结尾的字符串
|
|
3274
|
+
const reg =
|
|
3275
|
+
/\$hxt\([\r\n\s]*\{[\r\n\s]*key[\r\n\s]*:[\r\n\s]*['|"][\r\n\s]*['|"],[\r\n\s]*desc[\r\n\s]*:[\r\n\s]*['|"|`](.*?)['|"|`],*[\r\n\s]*\}(\)|,[\r\n\s]*\{)/g;
|
|
3276
|
+
file = file.replace(reg, function (match, p1, p2) {
|
|
3277
|
+
if (isOk) {
|
|
3278
|
+
const reg1 = /key[\r\n\s]*:[\r\n\s]*('|")[\r\n\s]*('|")/g;
|
|
3279
|
+
let key = newAllArr.find(
|
|
3280
|
+
(item) =>
|
|
3281
|
+
item.path === fPath &&
|
|
3282
|
+
(item.ZH_CN === p1 || item.ZH_CN === p1.trim()) // 去除前后空格,原[ 执行人: ]翻译变成[执行人:]
|
|
3283
|
+
).key;
|
|
3284
|
+
// 去除{ slotxx }
|
|
3285
|
+
// key = key.replace(/\{[\r\n\s]*slot\d[\r\n\s]*\}/g, "");
|
|
3286
|
+
// 去除空格
|
|
3287
|
+
key = key.replace(/\s/g, "");
|
|
3288
|
+
return match.replace(reg1, `key: '${key}'`);
|
|
3289
|
+
} else {
|
|
3290
|
+
allArr.push({
|
|
3291
|
+
path: fPath,
|
|
3292
|
+
ZH_CN: p1,
|
|
3293
|
+
EN: "",
|
|
3294
|
+
key: "",
|
|
3295
|
+
});
|
|
3296
|
+
}
|
|
3297
|
+
});
|
|
3298
|
+
if (isOk) {
|
|
3299
|
+
fs.writeFileSync(fPath, file, "utf-8");
|
|
3300
|
+
}
|
|
3301
|
+
});
|
|
3302
|
+
if (isOk) {
|
|
3303
|
+
// spinner.stop();
|
|
3304
|
+
return;
|
|
3305
|
+
}
|
|
3306
|
+
// 数组分割,如果str长度大于2000,就分割成多个数组
|
|
3307
|
+
let str = "";
|
|
3308
|
+
let cutArr = [];
|
|
3309
|
+
console.log(chalk.green(`共有${allArr.length}条数据需要翻译`));
|
|
3310
|
+
|
|
3311
|
+
allArr.forEach((item, index) => {
|
|
3312
|
+
if (str.length > 2000) {
|
|
3313
|
+
groupArr.push(cutArr);
|
|
3314
|
+
str = "";
|
|
3315
|
+
cutArr = [];
|
|
3316
|
+
}
|
|
3317
|
+
str += item.ZH_CN + "\n";
|
|
3318
|
+
cutArr.push(item);
|
|
3319
|
+
});
|
|
3320
|
+
if (cutArr.length > 0) {
|
|
3321
|
+
Array.prototype.push.apply(groupArr, [cutArr]);
|
|
3322
|
+
} else {
|
|
3323
|
+
groupArr = allArr;
|
|
3324
|
+
}
|
|
3325
|
+
console.log(chalk.green(`共有${groupArr.length}组数据需要翻译`));
|
|
3326
|
+
if (!groupArr.length) return;
|
|
3327
|
+
const task = groupArr.map(async (item, index) => {
|
|
3328
|
+
let str = "";
|
|
3329
|
+
item.forEach((item1, index1) => {
|
|
3330
|
+
// 去除换行空格
|
|
3331
|
+
const ZH_CN = item1.ZH_CN.replace(/\n/g, "");
|
|
3332
|
+
str += ZH_CN + "\n";
|
|
3333
|
+
});
|
|
3334
|
+
return translate(item, str, index + 1);
|
|
3335
|
+
});
|
|
3336
|
+
Promise.allSettled(task).then(async (res) => {
|
|
3337
|
+
console.log(chalk.green("任务执行完毕"));
|
|
3338
|
+
groupArr.forEach((item, index) => {
|
|
3339
|
+
Array.prototype.push.apply(newAllArr, item);
|
|
3340
|
+
});
|
|
3341
|
+
// 如果中文不同,但翻译的英文相同(比如商品品类和商品类别翻译的英文都是Product Category),对key进行自动+1
|
|
3342
|
+
for (let i = 0; i < newAllArr.length; i++) {
|
|
3343
|
+
let num = 0;
|
|
3344
|
+
for (let j = 0; j < newAllArr.length; j++) {
|
|
3345
|
+
// key存在且不能为空
|
|
3346
|
+
if (newAllArr[j].key && newAllArr[i].key) {
|
|
3347
|
+
if (
|
|
3348
|
+
newAllArr[j].key === newAllArr[i].key &&
|
|
3349
|
+
newAllArr[j].ZH_CN !== newAllArr[i].ZH_CN
|
|
3350
|
+
) {
|
|
3351
|
+
num++;
|
|
3352
|
+
newAllArr[j].key = newAllArr[j].key + num;
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
readFolder(true);
|
|
3358
|
+
// 如果key相等去重
|
|
3359
|
+
const uniqueArr = Array.from(
|
|
3360
|
+
new Map(newAllArr.map((item) => [item.key, item])).values()
|
|
3361
|
+
);
|
|
3362
|
+
let exportData = [];
|
|
3363
|
+
// key中有debug排后面
|
|
3364
|
+
const sortNewAllArr = _.sortBy(uniqueArr, function (item) {
|
|
3365
|
+
return item.key.indexOf("debug") > -1;
|
|
3366
|
+
});
|
|
3367
|
+
sortNewAllArr.forEach((item, index) => {
|
|
3368
|
+
const rowArr = [
|
|
3369
|
+
"GIT", // 系统
|
|
3370
|
+
projectName, // 项目
|
|
3371
|
+
"frontInit", // 模块(路由)
|
|
3372
|
+
"", // 编码
|
|
3373
|
+
1, // 所属(1:前端 2:后端)
|
|
3374
|
+
1, // 数据类型(1:static,2:enum,3:json)
|
|
3375
|
+
"", // 中文值
|
|
3376
|
+
"", // 英文
|
|
3377
|
+
"", // 泰文(TH)
|
|
3378
|
+
"", // 越南(VI)
|
|
3379
|
+
"", // 土耳其(TR)
|
|
3380
|
+
];
|
|
3381
|
+
const cacheIndex = {
|
|
3382
|
+
ZH_CN: 6,
|
|
3383
|
+
EN: 7,
|
|
3384
|
+
key: 3,
|
|
3385
|
+
};
|
|
3386
|
+
Object.entries(item).forEach(([key, value], index1) => {
|
|
3387
|
+
const insertIndex = cacheIndex[key];
|
|
3388
|
+
rowArr[insertIndex] = value;
|
|
3389
|
+
});
|
|
3390
|
+
exportData.push(rowArr);
|
|
3391
|
+
});
|
|
3392
|
+
// isMergeDecision: true/false为预决定值(一键执行), null表示仍需交互询问
|
|
3393
|
+
let isMerge = isMergeDecision;
|
|
3394
|
+
if (isMerge === null || isMerge === undefined) {
|
|
3395
|
+
const res = await inquirer.prompt([
|
|
3396
|
+
{
|
|
3397
|
+
message: `是否合并至export.xlsx,默认${chalk.red("否,直接覆盖")}`,
|
|
3398
|
+
name: "isMerge",
|
|
3399
|
+
type: "confirm",
|
|
3400
|
+
default: false,
|
|
3401
|
+
},
|
|
3402
|
+
]);
|
|
3403
|
+
isMerge = res.isMerge;
|
|
3404
|
+
}
|
|
3405
|
+
try {
|
|
3406
|
+
if (isMerge) {
|
|
3407
|
+
const str = xlsx.parse(fs.readFileSync("./export.xlsx"));
|
|
3408
|
+
if (str[0].data.length) {
|
|
3409
|
+
exportData = str[0].data.concat(exportData);
|
|
3410
|
+
} else {
|
|
3411
|
+
throw new Error();
|
|
3412
|
+
}
|
|
3413
|
+
} else {
|
|
3414
|
+
throw new Error();
|
|
3415
|
+
}
|
|
3416
|
+
} catch (error) {
|
|
3417
|
+
// 第一行插入标题
|
|
3418
|
+
exportData.unshift([
|
|
3419
|
+
"系统",
|
|
3420
|
+
"项目",
|
|
3421
|
+
"模块",
|
|
3422
|
+
"编码",
|
|
3423
|
+
"所属(1:前端 2:后端)",
|
|
3424
|
+
"数据类型(1:static,2:enum,3:json)",
|
|
3425
|
+
"中文值",
|
|
3426
|
+
"英文",
|
|
3427
|
+
"泰文(TH)",
|
|
3428
|
+
"越南(VI)",
|
|
3429
|
+
"土耳其(TR)",
|
|
3430
|
+
]);
|
|
3431
|
+
}
|
|
3432
|
+
const buffer = xlsx.build([{ data: exportData }]);
|
|
3433
|
+
fs.writeFileSync(`./export.xlsx`, buffer, "binary");
|
|
3434
|
+
console.log(chalk.green(`${isMerge ? "合并" : "覆盖"}文件成功`));
|
|
3435
|
+
});
|
|
3436
|
+
}
|
|
3437
|
+
|
|
3438
|
+
// 百度翻译接口
|
|
3439
|
+
async function translate(item, str, groupIndex) {
|
|
3440
|
+
const q = str;
|
|
3441
|
+
const salt = Math.random();
|
|
3442
|
+
const from = "zh";
|
|
3443
|
+
const to = "en";
|
|
3444
|
+
const sign = md5(appid + q + salt + key);
|
|
3445
|
+
const query = querystring.stringify({
|
|
3446
|
+
q,
|
|
3447
|
+
appid,
|
|
3448
|
+
salt,
|
|
3449
|
+
from,
|
|
3450
|
+
to,
|
|
3451
|
+
sign,
|
|
3452
|
+
});
|
|
3453
|
+
const url = "http://api.fanyi.baidu.com/api/trans/vip/translate?" + query;
|
|
3454
|
+
return new Promise((resolve, reject) => {
|
|
3455
|
+
// 自动翻译延时,必须大于 1000 ms,否则调用百度翻译 API 会失败
|
|
3456
|
+
throttle(() => {
|
|
3457
|
+
console.log(chalk.green(`正在翻译第${groupIndex}组数据`));
|
|
3458
|
+
return request(url, function (_error, response, body) {
|
|
3459
|
+
const resBody = JSON.parse(body);
|
|
3460
|
+
if (resBody.trans_result) {
|
|
3461
|
+
console.log(`ok`);
|
|
3462
|
+
let result = resBody.trans_result || [];
|
|
3463
|
+
|
|
3464
|
+
item.forEach((item1, index1) => {
|
|
3465
|
+
const id = uuid.v5(item1.path, uuid.v5.URL).slice(0, 6);
|
|
3466
|
+
if (
|
|
3467
|
+
result[index1] &&
|
|
3468
|
+
(result[index1].src === item1.ZH_CN ||
|
|
3469
|
+
result[index1].src === item1.ZH_CN.trim()) // 去除前后空格,原[ 执行人: ]翻译变成[执行人:]
|
|
3470
|
+
) {
|
|
3471
|
+
// 删除str中的单引号或双引号或斜杠或逗号
|
|
3472
|
+
let str = result[index1].dst
|
|
3473
|
+
.replace(/\'/g, "")
|
|
3474
|
+
.replace(/\"/g, "")
|
|
3475
|
+
.replace(/\//g, "")
|
|
3476
|
+
.replace(/\,/g, "");
|
|
3477
|
+
// 每个单词首首字母转换为大写
|
|
3478
|
+
const s = str.split(" ");
|
|
3479
|
+
let key = s
|
|
3480
|
+
.map((item, index) => {
|
|
3481
|
+
return item ? item[0].toUpperCase() + item.slice(1) : item;
|
|
3482
|
+
})
|
|
3483
|
+
.join("");
|
|
3484
|
+
// 去除key中的标点符号
|
|
3485
|
+
key = key.replace(/[^a-zA-Z0-9{}]/g, "").slice(0, 73);
|
|
3486
|
+
let EN = result[index1].dst;
|
|
3487
|
+
EN = EN[0].toUpperCase() + EN.slice(1);
|
|
3488
|
+
item1.EN = EN;
|
|
3489
|
+
item1.key = id + "-" + key;
|
|
3490
|
+
}
|
|
3491
|
+
});
|
|
3492
|
+
resolve(true);
|
|
3493
|
+
} else {
|
|
3494
|
+
item.forEach((item1, index1) => {
|
|
3495
|
+
const id = uuid.v5(item1.path, uuid.v5.URL).slice(0, 6);
|
|
3496
|
+
item1.key = id + "-" + "debug";
|
|
3497
|
+
});
|
|
3498
|
+
console.log(chalk.red(`error: ${body}`));
|
|
3499
|
+
resolve(true);
|
|
3500
|
+
}
|
|
3501
|
+
});
|
|
3502
|
+
});
|
|
3503
|
+
});
|
|
3504
|
+
}
|
|
3505
|
+
// md5加密
|
|
3506
|
+
function md5(str) {
|
|
3507
|
+
const md5 = crypto.createHash("md5");
|
|
3508
|
+
md5.update(str);
|
|
3509
|
+
return md5.digest("hex");
|
|
3510
|
+
}
|
|
3511
|
+
|
|
3512
|
+
// 节流函数
|
|
3513
|
+
const delay = 2000;
|
|
3514
|
+
const throttle = (function (delay = 1500) {
|
|
3515
|
+
const wait = [];
|
|
3516
|
+
let canCall = true;
|
|
3517
|
+
return function throttle(callback) {
|
|
3518
|
+
if (!canCall) {
|
|
3519
|
+
if (callback) wait.push(callback);
|
|
3520
|
+
return;
|
|
3521
|
+
}
|
|
3522
|
+
|
|
3523
|
+
callback();
|
|
3524
|
+
canCall = false;
|
|
3525
|
+
setTimeout(() => {
|
|
3526
|
+
canCall = true;
|
|
3527
|
+
if (wait.length) {
|
|
3528
|
+
throttle(wait.shift());
|
|
3529
|
+
}
|
|
3530
|
+
}, delay);
|
|
3531
|
+
};
|
|
3532
|
+
})(delay);
|
|
3533
|
+
// 开始方法
|
|
3534
|
+
readFolder();
|
|
3013
3535
|
}
|
|
3014
3536
|
|
|
3015
3537
|
exports.rules = rules;
|