coda-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +9 -0
- package/README.md +76 -0
- package/package.json +45 -0
- package/src/bin/index.js +4 -0
- package/src/bin/index.ts +2 -0
- package/src/cli-api/cmd.js +86 -0
- package/src/cli-api/cmd.ts +104 -0
- package/src/cli-api/data.js +135 -0
- package/src/cli-api/data.ts +146 -0
- package/src/cli-api/date.js +23 -0
- package/src/cli-api/date.ts +22 -0
- package/src/cli-api/event.js +6 -0
- package/src/cli-api/event.ts +6 -0
- package/src/cli-api/fs.js +236 -0
- package/src/cli-api/fs.ts +213 -0
- package/src/cli-api/git.js +328 -0
- package/src/cli-api/git.ts +397 -0
- package/src/cli-api/index.js +22 -0
- package/src/cli-api/index.ts +10 -0
- package/src/cli-api/packages.js +69 -0
- package/src/cli-api/packages.ts +73 -0
- package/src/cli-api/request.js +55 -0
- package/src/cli-api/request.ts +67 -0
- package/src/cli-core/cli.js +135 -0
- package/src/cli-core/cli.ts +145 -0
- package/src/cli-core/index.js +51 -0
- package/src/cli-core/index.ts +58 -0
- package/src/cli-core/queue.js +60 -0
- package/src/cli-core/queue.ts +68 -0
- package/src/cli-core/watcher.js +31 -0
- package/src/cli-core/watcher.ts +31 -0
- package/src/config/commands.json +20 -0
- package/src/generator/cmd-create/index.js +170 -0
- package/src/generator/cmd-create/index.ts +164 -0
- package/src/generator/cmd-create/tmpl/index.js +39 -0
- package/src/generator/cmd-create/tmpl/index.ts +46 -0
- package/src/generator/cmd-delete/index.js +129 -0
- package/src/generator/cmd-delete/index.ts +122 -0
- package/src/generator/startup.js +253 -0
- package/src/generator/startup.ts +225 -0
- package/src/service/lang.js +73 -0
- package/src/service/lang.ts +71 -0
- package/src/service/util.js +120 -0
- package/src/service/util.ts +91 -0
- package/src/types/index.js +2 -0
- package/src/types/index.ts +29 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright © 2026 Lasncod
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Code-Agent
|
|
2
|
+
|
|
3
|
+
一款命令行工具,可以调用本地AI模型的编程助手,通过工程化、智能化集成方案,推动前端项目在架构、性能、安全、体验、效率、质量全面提升。
|
|
4
|
+
|
|
5
|
+
# 🛠 技术栈
|
|
6
|
+
|
|
7
|
+
- 核心语言: TypeScript (Target: ES2022, ModuleResolution: NodeNext)
|
|
8
|
+
- 编译与路径处理: tsc + tsc-alias(支持同级目录 src 编译与路径别名 @/\*)
|
|
9
|
+
- 交互与终端UI: inquirer(交互式问答)、ora(加载动画)、chalk(终端色彩)
|
|
10
|
+
- 文件操作: fs-extra、glob
|
|
11
|
+
- 执行模型: 基于生命周期的编排架构
|
|
12
|
+
|
|
13
|
+
# 🏗 核心架构与生命周期
|
|
14
|
+
|
|
15
|
+
本项目采用严谨的生命周期驱动模型,每个命令的执行流程均遵循以下阶段:
|
|
16
|
+
|
|
17
|
+
1. init: 初始化全局状态与基础参数。
|
|
18
|
+
2. prompt: 调动 Inquirer 收集用户交互输入。
|
|
19
|
+
3. default: 核心计算与数据处理流程。
|
|
20
|
+
4. writing: 文件模板生成、写入及配置文件更新。
|
|
21
|
+
5. install: 自动拉取或安装所需依赖库。
|
|
22
|
+
6. end: 执行收尾工作并输出成功或错误日志。
|
|
23
|
+
|
|
24
|
+
# 📁 目录结构概览
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
code-agent/
|
|
28
|
+
├── src/
|
|
29
|
+
│ ├── bin/ # code-agent执行命令的入口文件
|
|
30
|
+
│ ├── cli-core/ # code-agent命令的核心生命周期、基类及状态管理
|
|
31
|
+
│ ├── cli-api/ # code-agent底层工具封装(文件系统、git处理、子进程任务等)
|
|
32
|
+
│ ├── config/ # 命令及全局配置文件
|
|
33
|
+
│ ├── extensions/ # 实际业务命令(通过coda cmd create来生成命令模板并实现具体业务)
|
|
34
|
+
│ ├── generator/ # 命令模板管理器(如 cmd-create生成新的命令模板, cmd-delete删除命令模板)
|
|
35
|
+
│ ├── service/ # 公共服务(语言包、工具函数等)
|
|
36
|
+
│ └── types/ # 公用的类型接口定义文件
|
|
37
|
+
├── tsconfig.json # TypeScript 配置文件
|
|
38
|
+
├── tsc-alias.json # 路径别名重写配置
|
|
39
|
+
└── package.json # 项目配置与依赖管理
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
# 🚀 快速开始与开发指南
|
|
43
|
+
|
|
44
|
+
1. 安装依赖
|
|
45
|
+
确保已安装 Node.js(推荐 22+ 或最新 LTS 版本),然后安装项目依赖:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
Bash
|
|
49
|
+
npm install
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
2. 构建与本地链接本项目支持 TypeScript 源码同级编译(src/ 到 src/),并配置了路径别名映射:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
Bash
|
|
56
|
+
# 编译 TypeScript 并重写路径别名
|
|
57
|
+
npm run build
|
|
58
|
+
|
|
59
|
+
# 将 CLI 工具链接到全局以便本地联调 (coda 命令)
|
|
60
|
+
npm run link
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
# 🐞 VS Code 调试配置
|
|
64
|
+
|
|
65
|
+
点击vscode左侧栏“运行和调试”按钮,选中需要调试的命令再点运行即可。
|
|
66
|
+
|
|
67
|
+
# 📜 可用脚本命令
|
|
68
|
+
|
|
69
|
+
| 命令 | 描述 |
|
|
70
|
+
| :------------------------- | :------------------------------------- |
|
|
71
|
+
| `npm run build` | 编译 TypeScript 文件并自动转换路径别名 |
|
|
72
|
+
| `npm run link` | 构建并注册全局 coda 命令 |
|
|
73
|
+
| `npm run debug_cmd-create` | 调试创建自定义命令流程 |
|
|
74
|
+
| `npm run debug_cmd-delete` | 调试删除自定义命令流程 |
|
|
75
|
+
|
|
76
|
+
📄**License**: [ISC](LICENSE)
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "coda-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "一款命令行工具,可以调用本地AI模型的编程助手,通过工程化、智能化集成方案,推动前端项目在架构、性能、安全、体验、效率、质量全面提升。",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "tsc && tsc-alias --dir src",
|
|
7
|
+
"link": "npm run build && npm link"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/Lanscod88/code-agent.git"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"command line tools",
|
|
16
|
+
"AI agent"
|
|
17
|
+
],
|
|
18
|
+
"author": "Lanscod",
|
|
19
|
+
"bin": {
|
|
20
|
+
"coda": "./src/bin/index.js"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"src"
|
|
24
|
+
],
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"axios": "^1.7.3",
|
|
27
|
+
"chalk": "^6.0.1",
|
|
28
|
+
"del": "^9.0.1",
|
|
29
|
+
"fs-extra": "^11.4.1",
|
|
30
|
+
"@types/fs-extra": "^11.0.4",
|
|
31
|
+
"glob": "^13.0.6",
|
|
32
|
+
"npm-check-updates": "^23.1.1",
|
|
33
|
+
"ora": "^9.4.1",
|
|
34
|
+
"semver": "^7.8.5",
|
|
35
|
+
"@types/semver": "^7.8.0",
|
|
36
|
+
"inquirer": "^14.2.3",
|
|
37
|
+
"@types/inquirer": "^9.0.10"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^26.6.5",
|
|
41
|
+
"cross-env": "^7.0.3",
|
|
42
|
+
"typescript": "^7.0.2",
|
|
43
|
+
"tsc-alias": "^1.9.7"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/bin/index.js
ADDED
package/src/bin/index.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.childProcess = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
exports.childProcess = {
|
|
6
|
+
/**
|
|
7
|
+
* 执行命令
|
|
8
|
+
* @param cmd 命令
|
|
9
|
+
* @param dir 运行命令的目录
|
|
10
|
+
* @param showOutput 是否在控制台打印标准输出
|
|
11
|
+
*/
|
|
12
|
+
cmdExecute(cmd, dir = process.cwd(), showOutput = false) {
|
|
13
|
+
(0, child_process_1.spawn)(cmd, {
|
|
14
|
+
cwd: dir,
|
|
15
|
+
stdio: showOutput === true ? 'inherit' : 'pipe',
|
|
16
|
+
shell: true,
|
|
17
|
+
});
|
|
18
|
+
},
|
|
19
|
+
/**
|
|
20
|
+
* 执行命令并返回异步结果
|
|
21
|
+
* @param cmd 命令
|
|
22
|
+
* @param dir 运行命令的目录
|
|
23
|
+
* @returns Promise 对象
|
|
24
|
+
*/
|
|
25
|
+
asyncCmdExecuteResult(cmd, dir = process.cwd()) {
|
|
26
|
+
return new Promise(resolve => {
|
|
27
|
+
const cp = (0, child_process_1.spawn)(cmd, {
|
|
28
|
+
cwd: dir,
|
|
29
|
+
stdio: 'pipe',
|
|
30
|
+
shell: true,
|
|
31
|
+
});
|
|
32
|
+
let dataOk = '';
|
|
33
|
+
let dataError = '';
|
|
34
|
+
cp.stdout?.on('data', (data) => {
|
|
35
|
+
dataOk += data.toString().trim();
|
|
36
|
+
});
|
|
37
|
+
cp.stderr?.on('data', (data) => {
|
|
38
|
+
dataError += data.toString().trim();
|
|
39
|
+
});
|
|
40
|
+
cp.on('close', () => {
|
|
41
|
+
resolve({
|
|
42
|
+
status: dataOk.length !== 0 ? 'success' : 'error',
|
|
43
|
+
data: dataOk.length !== 0 ? dataOk : dataError,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
/**
|
|
49
|
+
* 执行命令并同步返回结果
|
|
50
|
+
* @param cmd 命令
|
|
51
|
+
* @param dir 运行命令的目录
|
|
52
|
+
* @returns 执行结果字符串
|
|
53
|
+
*/
|
|
54
|
+
syncCmdExecuteResult(cmd, dir = process.cwd()) {
|
|
55
|
+
const cp = (0, child_process_1.spawnSync)(cmd, {
|
|
56
|
+
cwd: dir,
|
|
57
|
+
stdio: 'pipe',
|
|
58
|
+
encoding: 'utf-8',
|
|
59
|
+
shell: true,
|
|
60
|
+
});
|
|
61
|
+
// 安全处理标准输出或错误输出,避免空指针异常
|
|
62
|
+
const message = cp.stdout && cp.stdout.length > 0 ? cp.stdout : cp.stderr || '';
|
|
63
|
+
return message.toString().trim();
|
|
64
|
+
},
|
|
65
|
+
/**
|
|
66
|
+
* 在新的控制台窗口中执行任务
|
|
67
|
+
* @param task 命令
|
|
68
|
+
* @param dir 运行命令的目录
|
|
69
|
+
* @param autoClose 执行完毕后控制台窗口是否自动关闭
|
|
70
|
+
*/
|
|
71
|
+
taskInNewWindow(task, dir, autoClose) {
|
|
72
|
+
let cmd = autoClose ? `cmd /c ${task}` : `cmd /k ${task}`;
|
|
73
|
+
if (process.platform !== 'win32') {
|
|
74
|
+
cmd = `gnome-terminal -e 'bash -c "${task}; exec bash"'`;
|
|
75
|
+
}
|
|
76
|
+
(0, child_process_1.spawn)(cmd, {
|
|
77
|
+
cwd: dir,
|
|
78
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
79
|
+
shell: true,
|
|
80
|
+
detached: true,
|
|
81
|
+
windowsHide: true,
|
|
82
|
+
});
|
|
83
|
+
process.exit(0);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
exports.default = exports.childProcess;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {spawn, spawnSync} from 'child_process';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 异步命令执行结果接口
|
|
5
|
+
*/
|
|
6
|
+
export interface CmdResult {
|
|
7
|
+
status: 'success' | 'error';
|
|
8
|
+
data: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const childProcess = {
|
|
12
|
+
/**
|
|
13
|
+
* 执行命令
|
|
14
|
+
* @param cmd 命令
|
|
15
|
+
* @param dir 运行命令的目录
|
|
16
|
+
* @param showOutput 是否在控制台打印标准输出
|
|
17
|
+
*/
|
|
18
|
+
cmdExecute(cmd: string, dir: string = process.cwd(), showOutput: boolean = false): void {
|
|
19
|
+
spawn(cmd, {
|
|
20
|
+
cwd: dir,
|
|
21
|
+
stdio: showOutput === true ? 'inherit' : 'pipe',
|
|
22
|
+
shell: true,
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 执行命令并返回异步结果
|
|
28
|
+
* @param cmd 命令
|
|
29
|
+
* @param dir 运行命令的目录
|
|
30
|
+
* @returns Promise 对象
|
|
31
|
+
*/
|
|
32
|
+
asyncCmdExecuteResult(cmd: string, dir: string = process.cwd()): Promise<CmdResult> {
|
|
33
|
+
return new Promise(resolve => {
|
|
34
|
+
const cp = spawn(cmd, {
|
|
35
|
+
cwd: dir,
|
|
36
|
+
stdio: 'pipe',
|
|
37
|
+
shell: true,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
let dataOk = '';
|
|
41
|
+
let dataError = '';
|
|
42
|
+
|
|
43
|
+
cp.stdout?.on('data', (data: Buffer) => {
|
|
44
|
+
dataOk += data.toString().trim();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
cp.stderr?.on('data', (data: Buffer) => {
|
|
48
|
+
dataError += data.toString().trim();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
cp.on('close', () => {
|
|
52
|
+
resolve({
|
|
53
|
+
status: dataOk.length !== 0 ? 'success' : 'error',
|
|
54
|
+
data: dataOk.length !== 0 ? dataOk : dataError,
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 执行命令并同步返回结果
|
|
62
|
+
* @param cmd 命令
|
|
63
|
+
* @param dir 运行命令的目录
|
|
64
|
+
* @returns 执行结果字符串
|
|
65
|
+
*/
|
|
66
|
+
syncCmdExecuteResult(cmd: string, dir: string = process.cwd()): string {
|
|
67
|
+
const cp = spawnSync(cmd, {
|
|
68
|
+
cwd: dir,
|
|
69
|
+
stdio: 'pipe',
|
|
70
|
+
encoding: 'utf-8',
|
|
71
|
+
shell: true,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// 安全处理标准输出或错误输出,避免空指针异常
|
|
75
|
+
const message = cp.stdout && cp.stdout.length > 0 ? cp.stdout : cp.stderr || '';
|
|
76
|
+
return message.toString().trim();
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 在新的控制台窗口中执行任务
|
|
81
|
+
* @param task 命令
|
|
82
|
+
* @param dir 运行命令的目录
|
|
83
|
+
* @param autoClose 执行完毕后控制台窗口是否自动关闭
|
|
84
|
+
*/
|
|
85
|
+
taskInNewWindow(task: string, dir: string, autoClose: boolean): void {
|
|
86
|
+
let cmd = autoClose ? `cmd /c ${task}` : `cmd /k ${task}`;
|
|
87
|
+
|
|
88
|
+
if (process.platform !== 'win32') {
|
|
89
|
+
cmd = `gnome-terminal -e 'bash -c "${task}; exec bash"'`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
spawn(cmd, {
|
|
93
|
+
cwd: dir,
|
|
94
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
95
|
+
shell: true,
|
|
96
|
+
detached: true,
|
|
97
|
+
windowsHide: true,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
process.exit(0);
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export default childProcess;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Data = void 0;
|
|
4
|
+
exports.Data = {
|
|
5
|
+
/**
|
|
6
|
+
* 将数组转换为 Set
|
|
7
|
+
* @param arr 任意数组
|
|
8
|
+
* @returns Set 对象
|
|
9
|
+
*/
|
|
10
|
+
arrayToSet(arr) {
|
|
11
|
+
return new Set(arr);
|
|
12
|
+
},
|
|
13
|
+
/**
|
|
14
|
+
* 将 Set 转换为数组
|
|
15
|
+
* @param set Set 对象
|
|
16
|
+
* @returns 数组对象
|
|
17
|
+
*/
|
|
18
|
+
setToArray(set) {
|
|
19
|
+
return Array.from(set);
|
|
20
|
+
},
|
|
21
|
+
/**
|
|
22
|
+
* 获取两个数组的差集 (arrA 中有但 arrB 中没有的元素)
|
|
23
|
+
* @param arrA 数组 A
|
|
24
|
+
* @param arrB 数组 B
|
|
25
|
+
* @returns 差集数组
|
|
26
|
+
*/
|
|
27
|
+
getDifferenceSet(arrA, arrB) {
|
|
28
|
+
const setA = new Set(arrA);
|
|
29
|
+
const setB = new Set(arrB);
|
|
30
|
+
const difference = new Set([...setA].filter(e => !setB.has(e)));
|
|
31
|
+
return Array.from(difference);
|
|
32
|
+
},
|
|
33
|
+
/**
|
|
34
|
+
* 获取两个数组的交集
|
|
35
|
+
* @param arrA 数组 A
|
|
36
|
+
* @param arrB 数组 B
|
|
37
|
+
* @returns 交集数组
|
|
38
|
+
*/
|
|
39
|
+
getIntersectionSet(arrA, arrB) {
|
|
40
|
+
const setA = new Set(arrA);
|
|
41
|
+
const setB = new Set(arrB);
|
|
42
|
+
const intersection = new Set([...setA].filter(e => setB.has(e)));
|
|
43
|
+
return Array.from(intersection);
|
|
44
|
+
},
|
|
45
|
+
/**
|
|
46
|
+
* 获取两个数组的并集(合并并去重)
|
|
47
|
+
* @param arrA 数组 A
|
|
48
|
+
* @param arrB 数组 B
|
|
49
|
+
* @returns 并集数组
|
|
50
|
+
*/
|
|
51
|
+
getUnionSet(arrA, arrB) {
|
|
52
|
+
const set = new Set([...arrA, ...arrB]);
|
|
53
|
+
return Array.from(set);
|
|
54
|
+
},
|
|
55
|
+
/**
|
|
56
|
+
* 获取数据的真实类型
|
|
57
|
+
* @param data 任意数据
|
|
58
|
+
* @returns 类型字符串 (如 String, Number, Boolean, Object 等)
|
|
59
|
+
*/
|
|
60
|
+
getType(data) {
|
|
61
|
+
const temp = Object.prototype.toString.call(data);
|
|
62
|
+
return temp.substring(8, temp.length - 1);
|
|
63
|
+
},
|
|
64
|
+
/**
|
|
65
|
+
* 验证元素是否存在于数组中
|
|
66
|
+
* @param element 目标元素
|
|
67
|
+
* @param source 源数组
|
|
68
|
+
* @returns 是否存在
|
|
69
|
+
*/
|
|
70
|
+
hasElement(element, source) {
|
|
71
|
+
let exist = false;
|
|
72
|
+
for (const e in source) {
|
|
73
|
+
if (source[e] === element) {
|
|
74
|
+
exist = true;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return exist;
|
|
79
|
+
},
|
|
80
|
+
/**
|
|
81
|
+
* 向数组中添加元素(若不存在则添加)
|
|
82
|
+
* @param element 目标元素
|
|
83
|
+
* @param source 源数组
|
|
84
|
+
* @returns 更新后的数组
|
|
85
|
+
*/
|
|
86
|
+
addElement(element, source) {
|
|
87
|
+
const newArr = source;
|
|
88
|
+
if (newArr.indexOf(element) === -1) {
|
|
89
|
+
newArr.push(element);
|
|
90
|
+
}
|
|
91
|
+
return newArr;
|
|
92
|
+
},
|
|
93
|
+
/**
|
|
94
|
+
* 数组排序(支持基础类型,或对象按指定唯一属性排序)
|
|
95
|
+
* @param array 源数组
|
|
96
|
+
* @param uniqueProp 对象用于排序的唯一属性名
|
|
97
|
+
* @returns 排序后的数组
|
|
98
|
+
*/
|
|
99
|
+
arraySort(array, uniqueProp) {
|
|
100
|
+
const res = [];
|
|
101
|
+
const map = new Map();
|
|
102
|
+
array.forEach(item => {
|
|
103
|
+
const type = exports.Data.getType(item);
|
|
104
|
+
if (type === 'Object' && uniqueProp !== undefined) {
|
|
105
|
+
map.set(item[uniqueProp], item);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
res.push(item);
|
|
109
|
+
res.sort();
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
if (map.size !== 0 && uniqueProp !== undefined) {
|
|
113
|
+
const keys = [];
|
|
114
|
+
for (const key of map.keys()) {
|
|
115
|
+
keys.push(key);
|
|
116
|
+
}
|
|
117
|
+
keys.sort();
|
|
118
|
+
const newCmds = [];
|
|
119
|
+
keys.forEach(key => {
|
|
120
|
+
for (const index in array) {
|
|
121
|
+
const uniquePropVal = array[index][uniqueProp];
|
|
122
|
+
if (uniquePropVal === key) {
|
|
123
|
+
newCmds.push(array[index]);
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
return newCmds;
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
return res;
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
exports.default = exports.Data;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
export const Data = {
|
|
2
|
+
/**
|
|
3
|
+
* 将数组转换为 Set
|
|
4
|
+
* @param arr 任意数组
|
|
5
|
+
* @returns Set 对象
|
|
6
|
+
*/
|
|
7
|
+
arrayToSet<T>(arr: T[]): Set<T> {
|
|
8
|
+
return new Set(arr);
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 将 Set 转换为数组
|
|
13
|
+
* @param set Set 对象
|
|
14
|
+
* @returns 数组对象
|
|
15
|
+
*/
|
|
16
|
+
setToArray<T>(set: Set<T>): T[] {
|
|
17
|
+
return Array.from(set);
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 获取两个数组的差集 (arrA 中有但 arrB 中没有的元素)
|
|
22
|
+
* @param arrA 数组 A
|
|
23
|
+
* @param arrB 数组 B
|
|
24
|
+
* @returns 差集数组
|
|
25
|
+
*/
|
|
26
|
+
getDifferenceSet<T>(arrA: T[], arrB: T[]): T[] {
|
|
27
|
+
const setA = new Set(arrA);
|
|
28
|
+
const setB = new Set(arrB);
|
|
29
|
+
const difference = new Set([...setA].filter(e => !setB.has(e)));
|
|
30
|
+
|
|
31
|
+
return Array.from(difference);
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 获取两个数组的交集
|
|
36
|
+
* @param arrA 数组 A
|
|
37
|
+
* @param arrB 数组 B
|
|
38
|
+
* @returns 交集数组
|
|
39
|
+
*/
|
|
40
|
+
getIntersectionSet<T>(arrA: T[], arrB: T[]): T[] {
|
|
41
|
+
const setA = new Set(arrA);
|
|
42
|
+
const setB = new Set(arrB);
|
|
43
|
+
const intersection = new Set([...setA].filter(e => setB.has(e)));
|
|
44
|
+
|
|
45
|
+
return Array.from(intersection);
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 获取两个数组的并集(合并并去重)
|
|
50
|
+
* @param arrA 数组 A
|
|
51
|
+
* @param arrB 数组 B
|
|
52
|
+
* @returns 并集数组
|
|
53
|
+
*/
|
|
54
|
+
getUnionSet<T>(arrA: T[], arrB: T[]): T[] {
|
|
55
|
+
const set = new Set([...arrA, ...arrB]);
|
|
56
|
+
return Array.from(set);
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 获取数据的真实类型
|
|
61
|
+
* @param data 任意数据
|
|
62
|
+
* @returns 类型字符串 (如 String, Number, Boolean, Object 等)
|
|
63
|
+
*/
|
|
64
|
+
getType(data: unknown): string {
|
|
65
|
+
const temp = Object.prototype.toString.call(data);
|
|
66
|
+
return temp.substring(8, temp.length - 1);
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 验证元素是否存在于数组中
|
|
71
|
+
* @param element 目标元素
|
|
72
|
+
* @param source 源数组
|
|
73
|
+
* @returns 是否存在
|
|
74
|
+
*/
|
|
75
|
+
hasElement<T>(element: T, source: T[]): boolean {
|
|
76
|
+
let exist = false;
|
|
77
|
+
|
|
78
|
+
for (const e in source) {
|
|
79
|
+
if (source[e as unknown as keyof T[]] === element) {
|
|
80
|
+
exist = true;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return exist;
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 向数组中添加元素(若不存在则添加)
|
|
90
|
+
* @param element 目标元素
|
|
91
|
+
* @param source 源数组
|
|
92
|
+
* @returns 更新后的数组
|
|
93
|
+
*/
|
|
94
|
+
addElement<T>(element: T, source: T[]): T[] {
|
|
95
|
+
const newArr = source;
|
|
96
|
+
if (newArr.indexOf(element) === -1) {
|
|
97
|
+
newArr.push(element);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return newArr;
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 数组排序(支持基础类型,或对象按指定唯一属性排序)
|
|
105
|
+
* @param array 源数组
|
|
106
|
+
* @param uniqueProp 对象用于排序的唯一属性名
|
|
107
|
+
* @returns 排序后的数组
|
|
108
|
+
*/
|
|
109
|
+
arraySort<T>(array: T[], uniqueProp?: keyof T): T[] {
|
|
110
|
+
const res: T[] = [];
|
|
111
|
+
const map = new Map<any, T>();
|
|
112
|
+
|
|
113
|
+
array.forEach(item => {
|
|
114
|
+
const type = Data.getType(item);
|
|
115
|
+
if (type === 'Object' && uniqueProp !== undefined) {
|
|
116
|
+
map.set(item[uniqueProp], item);
|
|
117
|
+
} else {
|
|
118
|
+
res.push(item);
|
|
119
|
+
res.sort();
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
if (map.size !== 0 && uniqueProp !== undefined) {
|
|
124
|
+
const keys: any[] = [];
|
|
125
|
+
for (const key of map.keys()) {
|
|
126
|
+
keys.push(key);
|
|
127
|
+
}
|
|
128
|
+
keys.sort();
|
|
129
|
+
const newCmds: T[] = [];
|
|
130
|
+
keys.forEach(key => {
|
|
131
|
+
for (const index in array) {
|
|
132
|
+
const uniquePropVal = array[index][uniqueProp];
|
|
133
|
+
if (uniquePropVal === key) {
|
|
134
|
+
newCmds.push(array[index]);
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
return newCmds;
|
|
140
|
+
} else {
|
|
141
|
+
return res;
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export default Data;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BDate = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* 日期工具类
|
|
6
|
+
*/
|
|
7
|
+
exports.BDate = {
|
|
8
|
+
/**
|
|
9
|
+
* 获取格式化后的日期字符串
|
|
10
|
+
* @param date Date 对象
|
|
11
|
+
* @returns 格式化后的日期字符串 (YYYY-MM-DD HH:mm:ss)
|
|
12
|
+
*/
|
|
13
|
+
getFormatDate(date) {
|
|
14
|
+
const year = date.getFullYear();
|
|
15
|
+
const month = date.getMonth() >= 9 ? date.getMonth() + 1 : `0${date.getMonth() + 1}`;
|
|
16
|
+
const day = date.getDate() > 9 ? date.getDate() : `0${date.getDate()}`;
|
|
17
|
+
const hour = date.getHours() > 9 ? date.getHours() : `0${date.getHours()}`;
|
|
18
|
+
const minute = date.getMinutes() > 9 ? date.getMinutes() : `0${date.getMinutes()}`;
|
|
19
|
+
const second = date.getSeconds() > 9 ? date.getSeconds() : `0${date.getSeconds()}`;
|
|
20
|
+
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
exports.default = exports.BDate;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 日期工具类
|
|
3
|
+
*/
|
|
4
|
+
export const BDate = {
|
|
5
|
+
/**
|
|
6
|
+
* 获取格式化后的日期字符串
|
|
7
|
+
* @param date Date 对象
|
|
8
|
+
* @returns 格式化后的日期字符串 (YYYY-MM-DD HH:mm:ss)
|
|
9
|
+
*/
|
|
10
|
+
getFormatDate(date: Date): string {
|
|
11
|
+
const year = date.getFullYear();
|
|
12
|
+
const month = date.getMonth() >= 9 ? date.getMonth() + 1 : `0${date.getMonth() + 1}`;
|
|
13
|
+
const day = date.getDate() > 9 ? date.getDate() : `0${date.getDate()}`;
|
|
14
|
+
const hour = date.getHours() > 9 ? date.getHours() : `0${date.getHours()}`;
|
|
15
|
+
const minute = date.getMinutes() > 9 ? date.getMinutes() : `0${date.getMinutes()}`;
|
|
16
|
+
const second = date.getSeconds() > 9 ? date.getSeconds() : `0${date.getSeconds()}`;
|
|
17
|
+
|
|
18
|
+
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default BDate;
|