neo-cmp-cli 1.5.5 → 1.6.0-beta.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/README.md +64 -44
- package/package.json +1 -1
- package/src/cmpUtils/createCmpByTemplate.js +2 -2
- package/src/cmpUtils/createCmpByZip.js +87 -0
- package/src/cmpUtils/pullCmp.js +96 -0
- package/src/cmpUtils/pushCmp.js +77 -101
- package/src/module/index.js +54 -5
- package/src/module/main.js +7 -1
- package/src/neo/NeoUMDContent.js +1 -0
- package/src/neo/neoService.js +152 -42
- package/src/projectUtils/createCmpProjectZip.js +122 -0
- package/src/projectUtils/hasNeoProject.js +6 -5
- package/src/template/antd-custom-cmp-template/neo.config.js +7 -4
- package/src/template/antd-custom-cmp-template/package.json +2 -1
- package/src/template/echarts-custom-cmp-template/neo.config.js +7 -4
- package/src/template/echarts-custom-cmp-template/package.json +2 -1
- package/src/template/empty-custom-cmp-template/neo.config.js +7 -4
- package/src/template/empty-custom-cmp-template/package.json +2 -1
- package/src/template/neo-custom-cmp-template/neo.config.js +12 -9
- package/src/template/neo-custom-cmp-template/package.json +2 -1
- package/src/template/neo-custom-cmp-template/pushCmp.js +166 -0
- package/src/template/react-custom-cmp-template/neo.config.js +7 -4
- package/src/template/react-custom-cmp-template/package.json +2 -1
- package/src/template/react-ts-custom-cmp-template/neo.config.js +7 -4
- package/src/template/react-ts-custom-cmp-template/package.json +2 -1
- package/src/template/vue2-custom-cmp-template/neo.config.js +7 -4
- package/src/template/vue2-custom-cmp-template/package.json +2 -1
- package/src/utils/common.js +48 -0
package/README.md
CHANGED
|
@@ -21,68 +21,83 @@ neo-cmp-cli 是 Neo 自定义组件开发工具,基于 [AKFun](https://github.
|
|
|
21
21
|
|
|
22
22
|
## 快速开始
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
##### 1) 全局安装
|
|
24
|
+
#### 1) 全局安装自定义组件开发工具
|
|
26
25
|
```bash
|
|
27
26
|
yarn global add neo-cmp-cli
|
|
28
27
|
# 或
|
|
29
28
|
npm i -g neo-cmp-cli
|
|
30
29
|
```
|
|
31
|
-
|
|
30
|
+
|
|
31
|
+
#### 2) 创建自定义组件项目
|
|
32
32
|
```bash
|
|
33
|
-
|
|
33
|
+
# 方式一:创建空的自定义组件项目
|
|
34
|
+
neo createProject
|
|
35
|
+
|
|
36
|
+
# 方式二:根据模板创建自定义组件项目(默认 React+TS,可用 -t 指定模板,-n 指定自定义组件名称)
|
|
37
|
+
neo init
|
|
34
38
|
```
|
|
35
|
-
|
|
39
|
+
|
|
40
|
+
#### 3) 进入自定义组件项目根目录,安装依赖并运行
|
|
36
41
|
```bash
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
cd xxCmpProject
|
|
43
|
+
```
|
|
39
44
|
|
|
45
|
+
#### 4) 创建自定义组件
|
|
46
|
+
```bash
|
|
40
47
|
# 在当前项目中创建一个自定义组件
|
|
41
48
|
neo createCmp
|
|
49
|
+
```
|
|
50
|
+
默认在当前项目 src/components/ 目录下新增自定义组件。
|
|
42
51
|
|
|
52
|
+
#### 5) 本地预览自定义组件内容
|
|
53
|
+
```bash
|
|
43
54
|
# 预览自定义组件内容
|
|
44
55
|
neo preview
|
|
45
|
-
|
|
46
|
-
# 外链调试(在平台线上预览与调试)
|
|
47
|
-
neo linkDebug
|
|
48
|
-
|
|
49
|
-
# 构建并发布到 NeoCRM(需自行添加授权配置,并确保 package.json 的 name 唯一、version 不重复)
|
|
50
|
-
neo pushCmp
|
|
51
56
|
```
|
|
57
|
+
执行成功后,默认自动打开本地浏览器预览自定义组件内容。
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
此方式可用于将现有业务组件发布成平台可用自定义组件。
|
|
55
|
-
|
|
56
|
-
##### 1) 安装到当前项目,并添加开发依赖
|
|
57
|
-
使用以下安装命令时会自动将 neo-cmp-cli 添加成当前项目开发依赖
|
|
58
|
-
```bash
|
|
59
|
-
yarn add neo-cmp-cli --dev
|
|
60
|
-
# 或
|
|
61
|
-
npm i neo-cmp-cli --save-dev
|
|
62
|
-
```
|
|
63
|
-
##### 2) 在 package.json 添加可用执行脚本
|
|
59
|
+
#### 6) 外链调试(在 NeoCRM 端预览与调试)
|
|
64
60
|
```bash
|
|
65
|
-
|
|
66
|
-
"linkDebug": "neo linkDebug",
|
|
67
|
-
"pushCmp": "neo pushCmp"
|
|
61
|
+
neo linkDebug
|
|
68
62
|
```
|
|
69
|
-
|
|
63
|
+
需在NeoCRM / 页面设计器端启动 debug 模式,并添加控制台输出的外链脚本地址,方可在页面设计器端使用当前自定义组件。
|
|
64
|
+
|
|
65
|
+
#### 7) 构建并发布到 NeoCRM
|
|
70
66
|
```bash
|
|
71
|
-
neo
|
|
67
|
+
neo pushCmp
|
|
72
68
|
```
|
|
73
|
-
#####
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
69
|
+
##### 需自行添加授权配置,并确保 package.json 的 name 唯一、version 不重复。
|
|
70
|
+
```javascript
|
|
71
|
+
module.exports = {
|
|
72
|
+
neoConfig: {
|
|
73
|
+
neoBaseURL: 'https://crm-cd.xiaoshouyi.com', // 平台根地址(默认:https://crm.xiaoshouyi.com)
|
|
74
|
+
tokenAPI: 'https://login-cd.xiaoshouyi.com/auc/oauth2/token', // Token 获取接口地址(默认:https://login.xiaoshouyi.com/auc/oauth2/token)
|
|
75
|
+
// NeoCRM 授权配置
|
|
76
|
+
auth: {
|
|
77
|
+
client_id: 'xx', // 客户端 ID,从创建连接器的客户端信息中获取(Client_Id)
|
|
78
|
+
client_secret: 'xxx', // 客户端秘钥,从创建连接器的客户端信息中获取(Client_Secret)
|
|
79
|
+
username: 'xx', // 用户在销售易系统中的用户名
|
|
80
|
+
/**
|
|
81
|
+
* password 为 用户在销售易系统中的账号密码加上 8 位安全令牌。
|
|
82
|
+
* 例如,用户密码为 123456,安全令牌为 ABCDEFGH,则 password 的值应为 123456ABCDEFGH。
|
|
83
|
+
*/
|
|
84
|
+
password: 'xx xx' // 用户账户密码 + 8 位安全令牌
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
}
|
|
78
88
|
```
|
|
89
|
+
1、客户端 ID 和 客户端秘钥 需通过 创建连接器 获取,获取方式见:[https://doc.xiaoshouyi.com](https://doc.xiaoshouyi.com) / 创建连接器;
|
|
90
|
+
2、如何获取 安全令牌 见:[https://doc.xiaoshouyi.com](https://doc.xiaoshouyi.com) / OAuth安全认证 / 密码模式 / 获取令牌;
|
|
91
|
+
3、发布成功后即可在 NeoCRM 对应租户环境的页面设计器和表单设计器中使用此自定义组件。
|
|
79
92
|
|
|
80
93
|
## 常用命令说明
|
|
81
|
-
- **neo init**: 交互式创建自定义组件(支持 -t、--name
|
|
82
|
-
- **neo
|
|
83
|
-
- **neo
|
|
84
|
-
- **neo
|
|
85
|
-
- **neo
|
|
94
|
+
- **neo init**: 交互式创建自定义组件(支持 -t、--name);
|
|
95
|
+
- **neo createProject**: 创建自定义组件项目(支持 --name);
|
|
96
|
+
- **neo createCmp**: 在当前项目中创建一个自定义组件(支持 --name);
|
|
97
|
+
- **neo preview**: 本地预览自定义组件内容(支持 --name),默认支持热更新与接口代理;
|
|
98
|
+
- **neo linkDebug**: 外链调试模式,在平台端页面设计器中调试自定义组件;
|
|
99
|
+
- **neo publish2oss**: 构建并上传到对象存储(支持 --name,可自定义配置对象存储);
|
|
100
|
+
- **neo pushCmp**: 构建并发布到NeoCRM平台(支持 --name,需自行添加授权配置)。
|
|
86
101
|
|
|
87
102
|
## 开发须知
|
|
88
103
|
#### 1)默认自动识别自定义组件
|
|
@@ -145,14 +160,13 @@ neo linkDebug
|
|
|
145
160
|
##### 需自行添加授权配置
|
|
146
161
|
```javascript
|
|
147
162
|
module.exports = {
|
|
148
|
-
|
|
163
|
+
neoConfig: {
|
|
149
164
|
neoBaseURL: 'https://crm-cd.xiaoshouyi.com', // 平台根地址(默认:https://crm.xiaoshouyi.com)
|
|
150
165
|
tokenAPI: 'https://login-cd.xiaoshouyi.com/auc/oauth2/token', // Token 获取接口地址(默认:https://login.xiaoshouyi.com/auc/oauth2/token)
|
|
151
166
|
// NeoCRM 授权配置
|
|
152
|
-
|
|
167
|
+
auth: {
|
|
153
168
|
/**
|
|
154
|
-
* 客户端 ID 和 客户端秘钥 需通过 创建连接器
|
|
155
|
-
* 详细见:https://doc.xiaoshouyi.com / 创建连接器。
|
|
169
|
+
* 客户端 ID 和 客户端秘钥 需通过 创建连接器 获取
|
|
156
170
|
*/
|
|
157
171
|
client_id: 'xx', // 客户端 ID,从创建连接器的客户端信息中获取(Client_Id)
|
|
158
172
|
client_secret: 'xxx', // 客户端秘钥,从创建连接器的客户端信息中获取(Client_Secret)
|
|
@@ -160,7 +174,6 @@ module.exports = {
|
|
|
160
174
|
/**
|
|
161
175
|
* password 为 用户在销售易系统中的账号密码加上 8 位安全令牌。
|
|
162
176
|
* 例如,用户密码为 123456,安全令牌为 ABCDEFGH,则 password 的值应为 123456ABCDEFGH。
|
|
163
|
-
* 如何获取 安全令牌请见:https://doc.xiaoshouyi.com / OAuth安全认证 / 密码模式 / 获取令牌。
|
|
164
177
|
*/
|
|
165
178
|
password: 'xx xx' // 用户账户密码 + 8 位安全令牌
|
|
166
179
|
},
|
|
@@ -171,8 +184,12 @@ module.exports = {
|
|
|
171
184
|
##### 支持发布指定自定义组件
|
|
172
185
|
执行 `neo pushCmp --name=xxCmp`
|
|
173
186
|
|
|
187
|
+
##### 线上 NeoCRM 端使用
|
|
188
|
+
发布成功后,即可在对应租户环境下的页面设计器和表单设计器中使用此自定义组件。
|
|
189
|
+
|
|
174
190
|
#### 7)发布自定义组件至CDN
|
|
175
191
|
执行 `neo publish2oss` 即可构建对应自定义组件,并自动将构建后资源上传到对象存储(OSS)中。
|
|
192
|
+
备注:请优先使用 neo pushCmp。
|
|
176
193
|
|
|
177
194
|
##### 发布前请确保
|
|
178
195
|
- **package.json 的 name 唯一**
|
|
@@ -202,6 +219,9 @@ module.exports = {
|
|
|
202
219
|
##### 支持发布指定自定义组件
|
|
203
220
|
执行 `neo publish2oss --name=xxCmp`
|
|
204
221
|
|
|
222
|
+
##### 特别说明
|
|
223
|
+
此发布方式只是将自定义组件构建产物发布到 CDN 上,如要使用自定义组件还需要手动添加到指定租户上(NeoCRM 管理端 / 自定义组件管理页)。
|
|
224
|
+
|
|
205
225
|
## 项目工程配置说明(neo.config.js)
|
|
206
226
|
neo-cmp-cli 默认提供完整配置;
|
|
207
227
|
如需自定义,使用 `neo config init` 生成 `neo.config.js` 并按需修改。
|
package/package.json
CHANGED
|
@@ -29,12 +29,12 @@ module.exports = function (cmpName, componentBaseDir = './src/components') {
|
|
|
29
29
|
const finalCmpPath = path.resolve(process.cwd(), componentBaseDir, finalCmpName);
|
|
30
30
|
|
|
31
31
|
if (!hasNeoProject()) {
|
|
32
|
-
console.error(`${consoleTag}
|
|
32
|
+
console.error(`${consoleTag}当前(${process.cwd()})还不是自定义组件项目,请先创建一个自定义组件项目(neo init / neo createProject)。`);
|
|
33
33
|
process.exit(1);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
if (hasCmpTypeByDir(finalCmpName)) {
|
|
37
|
-
console.error(`${consoleTag}
|
|
37
|
+
console.error(`${consoleTag}创建自定义组件失败,当前项目已经存在${finalCmpName}自定义组件。`);
|
|
38
38
|
process.exit(1);
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const axios = require('axios');
|
|
4
|
+
const ora = require('ora');
|
|
5
|
+
const AdmZip = require('adm-zip');
|
|
6
|
+
const { consoleTag } = require('../utils/neoParams'); // 输出标记
|
|
7
|
+
const hasCmpTypeByDir = require('./hasCmpTypeByDir');
|
|
8
|
+
const hasNeoProject = require('../projectUtils/hasNeoProject');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 从 zip 包中创建自定义组件
|
|
12
|
+
* @param {*} cmpZipUrl 自定义组件源码文件地址(zip包地址)
|
|
13
|
+
* @param {*} cmpName 自定义组件名称
|
|
14
|
+
* @param {*} componentBaseDir 自定义组件目录
|
|
15
|
+
*/
|
|
16
|
+
module.exports = async function (cmpZipUrl, cmpName, componentBaseDir = './src/components') {
|
|
17
|
+
const finalCmpName = cmpName || 'neoCustomCmp';
|
|
18
|
+
|
|
19
|
+
if (!hasNeoProject()) {
|
|
20
|
+
console.error(
|
|
21
|
+
`${consoleTag}当前(${process.cwd()})还不是自定义组件项目,请先创建一个自定义组件项目(neo init / neo createProject)。`
|
|
22
|
+
);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (hasCmpTypeByDir(finalCmpName)) {
|
|
27
|
+
console.error(`${consoleTag}当前项目已经存在${finalCmpName}自定义组件。`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 创建临时目录
|
|
32
|
+
const tempDir = path.join(process.cwd(), '.neo-cli', 'zip-source');
|
|
33
|
+
|
|
34
|
+
// 下载源码文件并解析到 src/components 目录下
|
|
35
|
+
try {
|
|
36
|
+
await fs.ensureDir(tempDir); // 如果目录不存在,会自动创建(包括所有必要的父目录)
|
|
37
|
+
|
|
38
|
+
// 下载 zip 文件
|
|
39
|
+
const spinner = ora(`${consoleTag}正在下载组件源码...`).start();
|
|
40
|
+
const zipFilePath = path.join(tempDir, `${finalCmpName}.zip`);
|
|
41
|
+
|
|
42
|
+
const response = await axios({
|
|
43
|
+
url: cmpZipUrl,
|
|
44
|
+
method: 'GET',
|
|
45
|
+
responseType: 'arraybuffer'
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// 保存 zip 文件到临时目录
|
|
49
|
+
await fs.writeFile(zipFilePath, response.data);
|
|
50
|
+
|
|
51
|
+
// 解压 zip 文件
|
|
52
|
+
spinner.text(`${consoleTag}正在解压组件源码...`);
|
|
53
|
+
const zip = new AdmZip(zipFilePath);
|
|
54
|
+
const extractPath = path.join(tempDir, finalCmpName);
|
|
55
|
+
zip.extractAllTo(extractPath, true);
|
|
56
|
+
|
|
57
|
+
// 查找解压后的 src/components 目录
|
|
58
|
+
const cmpSourcePath = path.join(extractPath, componentBaseDir, finalCmpName);
|
|
59
|
+
|
|
60
|
+
if (!fs.existsSync(cmpSourcePath)) {
|
|
61
|
+
spinner.stopAndFail(`${consoleTag}解压后的 zip 包中未找到 ${finalCmpName} 组件源码目录。`);
|
|
62
|
+
await fs.remove(tempDir);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 确保目标目录存在
|
|
67
|
+
const targetComponentsDir = path.resolve(process.cwd(), componentBaseDir, finalCmpName);
|
|
68
|
+
await fs.ensureDir(targetComponentsDir);
|
|
69
|
+
|
|
70
|
+
// 复制 src/components 目录下的所有内容到目标目录
|
|
71
|
+
await fs
|
|
72
|
+
.copy(cmpSourcePath, targetComponentsDir)
|
|
73
|
+
.catch((err) => spinner.stopAndFail(`${consoleTag}自定义组件模板下载失败:`, err));
|
|
74
|
+
|
|
75
|
+
// 清理临时目录
|
|
76
|
+
await fs.remove(tempDir);
|
|
77
|
+
spinner.succeed(`${consoleTag}已成功从 zip 包解析自定义组件(${finalCmpName})!`);
|
|
78
|
+
|
|
79
|
+
return true;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
spinner.stopAndFail(`${consoleTag}从 zip 包创建自定义组件失败(${finalCmpName}):`, error.message);
|
|
82
|
+
// 清理临时目录
|
|
83
|
+
await fs.remove(tempDir);
|
|
84
|
+
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
const _ = require('lodash');
|
|
2
|
+
const { catchCurPackageJson } = require('../utils/pathUtils');
|
|
3
|
+
const getConfigObj = require('../utils/getConfigObj');
|
|
4
|
+
const ora = require('ora');
|
|
5
|
+
const NeoService = require('../neo/neoService');
|
|
6
|
+
const { getFramework } = require('../utils/common');
|
|
7
|
+
const getCmpTypeByDir = require('../cmpUtils/getCmpTypeByDir.js');
|
|
8
|
+
const createCmpByZip = require('../cmpUtils/createCmpByZip.js');
|
|
9
|
+
|
|
10
|
+
// 获取当前项目的package文件
|
|
11
|
+
const currentPackageJsonDir = catchCurPackageJson();
|
|
12
|
+
const currentPackageJson = getConfigObj(currentPackageJsonDir);
|
|
13
|
+
const framework = getFramework(currentPackageJson.framework);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 从 NeoCRM 拉取自定义组件
|
|
17
|
+
* @param {string} cmpType 自定义组件类型
|
|
18
|
+
* @param {object} authConfig 授权配置
|
|
19
|
+
*/
|
|
20
|
+
const pullCmp = async (cmpType, authConfig, _neoService) => {
|
|
21
|
+
|
|
22
|
+
if (!authConfig) {
|
|
23
|
+
console.error('未找到 NeoCRM 平台授权配置(neo.config.js / authConfig)。');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const cmpTypes = getCmpTypeByDir(); // 获取当前项目目录中已存在的自定义组件类型
|
|
28
|
+
if (cmpTypes.indexOf(cmpType) > -1) {
|
|
29
|
+
console.error(`当前项目目录中已存在${cmpType}自定义组件。(./src/components 目录下)`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const spinner = ora('正在拉取组件...').start();
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
let neoService = _neoService;
|
|
37
|
+
let cmpList = [], cmpInfoMap = {};
|
|
38
|
+
let cmpInfo = null;
|
|
39
|
+
|
|
40
|
+
if (_neoService) {
|
|
41
|
+
// 使用传入的 neoService 实例
|
|
42
|
+
cmpList = _neoService.cmpList || [];
|
|
43
|
+
cmpInfoMap = _neoService.cmpInfoMap || {};
|
|
44
|
+
} else {
|
|
45
|
+
// 创建新的 neoService 实例
|
|
46
|
+
neoService = new NeoService(authConfig);
|
|
47
|
+
|
|
48
|
+
// 获取自定义组件列表
|
|
49
|
+
spinner.text('正在获取自定义组件列表...');
|
|
50
|
+
cmpList = await neoService.getCustomCmpList();
|
|
51
|
+
cmpInfoMap = neoService.cmpInfoMap || {};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (cmpList.length === 0) {
|
|
55
|
+
spinner.stopAndFail('拉取失败,当前租户暂无任何自定义组件。');
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 获取自定义组件信息
|
|
60
|
+
spinner.text(`正在查找 ${cmpType} 组件信息...`);
|
|
61
|
+
cmpInfo = neoService.getCmpInfoByCmpType(cmpType);
|
|
62
|
+
if (!cmpInfo) {
|
|
63
|
+
spinner.stopAndFail(`拉取失败,当前租户不存在${cmpType}自定义组件。`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 判断拉取的组件和当前项目是否为同一技术栈
|
|
68
|
+
if (cmpInfo.framework !== framework) {
|
|
69
|
+
spinner.stopAndFail(`拉取失败,${cmpType}自定义组件与当前项目技术栈不一致。`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 获取当前源码
|
|
74
|
+
if (!cmpInfo.codeLib) {
|
|
75
|
+
spinner.stopAndFail(`拉取失败,${cmpType}自定义组件未记录对应的源码文件。`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 下载源码文件并解析到 src/components 目录下
|
|
80
|
+
const codeLib = cmpInfo.codeLib; // 源码文件地址(zip包地址)
|
|
81
|
+
|
|
82
|
+
// 将zip 包里面的自定义组件源码解析到 src/components 目录下
|
|
83
|
+
const cmpResult = await createCmpByZip(codeLib, cmpType, './src/components');
|
|
84
|
+
if (!cmpResult) {
|
|
85
|
+
spinner.stopAndFail(`拉取失败,${cmpType}自定义组件源码解析失败,请检查源码文件是否正确。`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
spinner.succeed(`已成功拉取${cmpType}自定义组件!\n`);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
spinner.fail(`拉取自定义组件失败: ${error.message}`);
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
module.exports = pullCmp;
|
package/src/cmpUtils/pushCmp.js
CHANGED
|
@@ -1,84 +1,17 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const AdmZip = require('adm-zip');
|
|
4
3
|
const _ = require('lodash');
|
|
5
4
|
const { catchCurPackageJson } = require('../utils/pathUtils');
|
|
6
5
|
const getConfigObj = require('../utils/getConfigObj');
|
|
7
6
|
const ora = require('ora');
|
|
8
7
|
const NeoService = require('../neo/neoService');
|
|
9
|
-
const {
|
|
8
|
+
const { getFramework } = require('../utils/common');
|
|
9
|
+
const createCmpProjectZip = require('../projectUtils/createCmpProjectZip');
|
|
10
10
|
|
|
11
11
|
// 获取当前项目的package文件
|
|
12
12
|
const currentPackageJsonDir = catchCurPackageJson();
|
|
13
13
|
const currentPackageJson = getConfigObj(currentPackageJsonDir);
|
|
14
14
|
|
|
15
|
-
/**
|
|
16
|
-
* 将构建产物打包成 zip 文件
|
|
17
|
-
* @param {string} assetsRoot 构建产物的目录
|
|
18
|
-
* @returns {Promise<string>} zip 文件路径
|
|
19
|
-
*/
|
|
20
|
-
const createZipPackage = async (assetsRoot) => {
|
|
21
|
-
if (!fs.existsSync(assetsRoot)) {
|
|
22
|
-
throw new Error(`assetsRoot 不存在: ${assetsRoot}`);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const files = fs.readdirSync(assetsRoot);
|
|
26
|
-
const zip = new AdmZip();
|
|
27
|
-
|
|
28
|
-
files.forEach((file) => {
|
|
29
|
-
const filePath = path.join(assetsRoot, file);
|
|
30
|
-
const fileStat = fs.statSync(filePath);
|
|
31
|
-
if (fileStat.isFile()) {
|
|
32
|
-
// 只添加 .js 文件
|
|
33
|
-
if (file.endsWith('.js')) {
|
|
34
|
-
zip.addLocalFile(filePath);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
const zipPath = path.join(assetsRoot, `${currentPackageJson.name}.zip`);
|
|
40
|
-
zip.writeZip(zipPath);
|
|
41
|
-
console.info(`已创建 zip 文件: ${zipPath}`);
|
|
42
|
-
|
|
43
|
-
return zipPath;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* 获取技术栈标识
|
|
48
|
-
* 目的:兼容用户非标准写法
|
|
49
|
-
* 0: React, 1: vue2, 2: jQuery, 3: vue3
|
|
50
|
-
*/
|
|
51
|
-
function getFramework(_framework) {
|
|
52
|
-
let defaultFramework = '0'; // 默认 React 技术栈
|
|
53
|
-
if (!_framework) {
|
|
54
|
-
return defaultFramework;
|
|
55
|
-
}
|
|
56
|
-
let curFramework = _framework.toLowerCase().trim();
|
|
57
|
-
switch (curFramework) {
|
|
58
|
-
case 'jquery':
|
|
59
|
-
case 'jq':
|
|
60
|
-
curFramework = '2';
|
|
61
|
-
break;
|
|
62
|
-
case 'vue2':
|
|
63
|
-
case 'vue 2':
|
|
64
|
-
case 'vue2.0':
|
|
65
|
-
case 'vue 2.0':
|
|
66
|
-
curFramework = '1';
|
|
67
|
-
break;
|
|
68
|
-
case 'vue':
|
|
69
|
-
case 'vue3':
|
|
70
|
-
case 'vue 3':
|
|
71
|
-
case 'vue3.0':
|
|
72
|
-
case 'vue 3.0':
|
|
73
|
-
curFramework = '3';
|
|
74
|
-
console.error(`${consoleTag} 暂不支持 vue3.0 技术栈。`);
|
|
75
|
-
break;
|
|
76
|
-
default:
|
|
77
|
-
curFramework = '0';
|
|
78
|
-
}
|
|
79
|
-
return curFramework;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
15
|
/**
|
|
83
16
|
* 构建组件数据映射
|
|
84
17
|
* @param {string} assetsRoot 构建产物的目录
|
|
@@ -92,7 +25,7 @@ const buildComponentData = async (assetsRoot, cmpInfo) => {
|
|
|
92
25
|
}
|
|
93
26
|
|
|
94
27
|
const { cmpType } = cmpInfo;
|
|
95
|
-
|
|
28
|
+
|
|
96
29
|
if (!assetsRoot || !fs.existsSync(assetsRoot)) {
|
|
97
30
|
console.error(`未找到自定义组件目录: ${assetsRoot}`);
|
|
98
31
|
return null;
|
|
@@ -107,7 +40,7 @@ const buildComponentData = async (assetsRoot, cmpInfo) => {
|
|
|
107
40
|
globalThis.window = {
|
|
108
41
|
console: console,
|
|
109
42
|
neoRequire: () => {},
|
|
110
|
-
postMessage: () => {}
|
|
43
|
+
postMessage: () => {}
|
|
111
44
|
// 可以添加其他常用的 window 属性
|
|
112
45
|
};
|
|
113
46
|
}
|
|
@@ -119,13 +52,14 @@ const buildComponentData = async (assetsRoot, cmpInfo) => {
|
|
|
119
52
|
let modelModule = require(modelFile);
|
|
120
53
|
// 获取导出的模型类(可能是 default 导出或命名导出)
|
|
121
54
|
// const CatchCustomCmpModelClass = modelModule.default || modelModule;
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
55
|
+
} else {
|
|
124
56
|
console.error(`未找到自定义组件模型文件,请检查以下路径是否存在:`, modelFile);
|
|
125
57
|
return null;
|
|
126
58
|
}
|
|
127
59
|
if (!window.NEOEditorCustomModels) {
|
|
128
|
-
console.error(
|
|
60
|
+
console.error(
|
|
61
|
+
`模型文件未导出有效模型方法(CatchCustomCmpModelClass),模型文件地址: ${modelFile} `
|
|
62
|
+
);
|
|
129
63
|
return null;
|
|
130
64
|
}
|
|
131
65
|
|
|
@@ -147,6 +81,7 @@ const buildComponentData = async (assetsRoot, cmpInfo) => {
|
|
|
147
81
|
...cmpInfo,
|
|
148
82
|
plugin: cmpInfo.modelAsset,
|
|
149
83
|
version: currentPackageJson.version || '1.0.0',
|
|
84
|
+
// 技术栈标识: 从 package.json / framework 字段获取,没有则默认 为 react ts 技术栈
|
|
150
85
|
framework: currentPackageJson.framework ? getFramework(currentPackageJson.framework) : '0', // 0: React, 1: vue2, 2: jQuery, 3: vue3
|
|
151
86
|
// 从模型实例中提取并设置组件信息
|
|
152
87
|
label: modelInstance.label || cmpType,
|
|
@@ -159,9 +94,11 @@ const buildComponentData = async (assetsRoot, cmpInfo) => {
|
|
|
159
94
|
events: modelInstance.events || [],
|
|
160
95
|
actions: modelInstance.actions || [],
|
|
161
96
|
// 如果模型实例中有其他属性,也可以添加
|
|
162
|
-
exposedToDesigner:
|
|
97
|
+
exposedToDesigner:
|
|
98
|
+
modelInstance.exposedToDesigner !== undefined ? modelInstance.exposedToDesigner : true,
|
|
163
99
|
namespace: modelInstance.namespace || 'neo-cmp-cli',
|
|
164
|
-
enableDuplicate:
|
|
100
|
+
enableDuplicate:
|
|
101
|
+
modelInstance.enableDuplicate !== undefined ? modelInstance.enableDuplicate : true
|
|
165
102
|
};
|
|
166
103
|
|
|
167
104
|
console.log(`自定义组件模型信息(${cmpType}):`, curCmpInfo);
|
|
@@ -183,15 +120,13 @@ const buildComponentData = async (assetsRoot, cmpInfo) => {
|
|
|
183
120
|
/**
|
|
184
121
|
* 发布组件到 NeoCRM
|
|
185
122
|
* @param {object} config 配置信息
|
|
186
|
-
* @param {string}
|
|
123
|
+
* @param {string} cmpType 自定义组件类型
|
|
187
124
|
*/
|
|
188
125
|
const pushCmp = async (config, cmpType) => {
|
|
189
|
-
const {
|
|
190
|
-
authConfig: credentials
|
|
191
|
-
} = config;
|
|
126
|
+
const { auth: credentials } = config;
|
|
192
127
|
|
|
193
128
|
if (!credentials) {
|
|
194
|
-
console.error('未找到 NeoCRM 平台授权配置(neo.config.js /
|
|
129
|
+
console.error('未找到 NeoCRM 平台授权配置(neo.config.js / neoConfig)');
|
|
195
130
|
return;
|
|
196
131
|
}
|
|
197
132
|
|
|
@@ -199,35 +134,76 @@ const pushCmp = async (config, cmpType) => {
|
|
|
199
134
|
|
|
200
135
|
try {
|
|
201
136
|
// 步骤 1: 初始化 NeoService
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
137
|
+
let neoService = new NeoService(config);
|
|
138
|
+
|
|
139
|
+
// 步骤 2: 打包源码文件(打包单个自定义组件源码)
|
|
140
|
+
spinner.start(`[1/4] 打包源码文件(含单个自定义组件源码)...`);
|
|
141
|
+
let zipPath;
|
|
142
|
+
try {
|
|
143
|
+
zipPath = createCmpProjectZip(cmpType, process.cwd(), config.assetsRoot);
|
|
144
|
+
if (!zipPath) {
|
|
145
|
+
spinner.fail('[1/4] 源码文件打包失败,未返回 zip 文件路径。');
|
|
146
|
+
} else {
|
|
147
|
+
spinner.succeed(`[1/4] 源码文件打包完成: ${path.basename(zipPath)}。`);
|
|
148
|
+
}
|
|
149
|
+
} catch (error) {
|
|
150
|
+
spinner.fail(`[1/4] 源码文件打包失败。`);
|
|
151
|
+
}
|
|
208
152
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
153
|
+
// 步骤 3: 上传构建后资源文件
|
|
154
|
+
spinner.start(`[2/4] 上传自定义组件构建产物到 OSS...`);
|
|
155
|
+
let cmpInfo;
|
|
156
|
+
try {
|
|
157
|
+
cmpInfo = await neoService.publish2oss(cmpType);
|
|
158
|
+
if (!cmpInfo || !cmpInfo.cmpType) {
|
|
159
|
+
spinner.fail('[2/4] 上传构建产物失败,未获取到组件信息。');
|
|
160
|
+
} else {
|
|
161
|
+
spinner.succeed(`[2/4] 构建产物上传完成。`);
|
|
162
|
+
}
|
|
163
|
+
} catch (error) {
|
|
164
|
+
spinner.fail(`[2/4] 构建产物上传失败。`);
|
|
165
|
+
}
|
|
214
166
|
|
|
215
167
|
// 步骤 4: 构建组件数据
|
|
216
|
-
spinner.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
168
|
+
spinner.start(`[3/4] 构建组件数据...`);
|
|
169
|
+
let componentInfo;
|
|
170
|
+
try {
|
|
171
|
+
componentInfo = await buildComponentData(config.assetsRoot, cmpInfo);
|
|
172
|
+
if (!componentInfo) {
|
|
173
|
+
spinner.fail(`[3/4] 未获取到自定义组件模型信息(${cmpType})。`);
|
|
174
|
+
} else {
|
|
175
|
+
spinner.succeed(`[3/4] 组件数据构建完成。`);
|
|
176
|
+
}
|
|
177
|
+
} catch (error) {
|
|
178
|
+
spinner.fail(`[3/4] 组件数据构建失败: `, error.message);
|
|
220
179
|
}
|
|
221
180
|
|
|
222
181
|
// 步骤 5: 保存组件信息
|
|
223
|
-
spinner.
|
|
224
|
-
|
|
182
|
+
spinner.start(`[4/4] 保存组件信息到 NeoCRM 平台...`);
|
|
183
|
+
try {
|
|
184
|
+
await neoService.updateCustomComponent(componentInfo);
|
|
185
|
+
spinner.succeed(`[4/4] 组件信息保存成功`);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
spinner.fail(`[4/4] 组件信息保存失败`);
|
|
188
|
+
}
|
|
225
189
|
|
|
226
|
-
|
|
190
|
+
// 最终成功提示
|
|
191
|
+
console.log('\n✅ 自定义组件发布成功!');
|
|
192
|
+
console.log('\n组件信息:', componentInfo);
|
|
227
193
|
} catch (error) {
|
|
228
|
-
spinner
|
|
194
|
+
// 内层 catch 已经处理了步骤错误并显示了 spinner 失败状态
|
|
195
|
+
// 这里处理未预期的错误(如果 spinner 还在运行)
|
|
196
|
+
try {
|
|
197
|
+
// ora 的 spinner 对象可能没有 isSpinning 属性,使用 try-catch 安全访问
|
|
198
|
+
if (spinner && spinner.isSpinning) {
|
|
199
|
+
spinner.fail(`❌ 自定义组件发布失败: ${error.message}`);
|
|
200
|
+
}
|
|
201
|
+
} catch {
|
|
202
|
+
// 如果访问失败或 spinner 已停止,直接输出错误信息
|
|
203
|
+
console.error(`\n❌ 自定义组件发布失败: ${error.message}`);
|
|
204
|
+
}
|
|
229
205
|
throw error;
|
|
230
206
|
}
|
|
231
207
|
};
|
|
232
208
|
|
|
233
|
-
module.exports = pushCmp;
|
|
209
|
+
module.exports = pushCmp;
|