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/src/module/index.js
CHANGED
|
@@ -2,6 +2,7 @@ const figlet = require('figlet'); // 用于输出图形文字
|
|
|
2
2
|
const yargs = require('yargs'); // 命令行工具
|
|
3
3
|
const chalk = require('chalk'); // 带样式的log输出
|
|
4
4
|
const inquirer = require('inquirer'); // 问答式交互
|
|
5
|
+
const ora = require('ora');
|
|
5
6
|
|
|
6
7
|
const neoInit = require('./neoInit.js');
|
|
7
8
|
const neoInitByCopy = require('./neoInitByCopy.js');
|
|
@@ -10,6 +11,7 @@ const neoConfigInit = require('../utils/neoConfigInit.js');
|
|
|
10
11
|
const { validateProjectName } = require('../utils/projectNameValidator.js');
|
|
11
12
|
const mainAction = require('./main.js'); // 入口文件
|
|
12
13
|
const getCmpTypeByDir = require('../cmpUtils/getCmpTypeByDir.js');
|
|
14
|
+
const NeoService = require('../neo/neoService.js');
|
|
13
15
|
|
|
14
16
|
// neo 的 package 文件
|
|
15
17
|
const neoPackage = require('../../package.json');
|
|
@@ -67,6 +69,11 @@ yargs
|
|
|
67
69
|
value: 'react-ts',
|
|
68
70
|
short: 'react-ts'
|
|
69
71
|
},
|
|
72
|
+
{
|
|
73
|
+
name: 'Neo 自定义组件(使用平台实体数据源)',
|
|
74
|
+
value: 'neo',
|
|
75
|
+
short: 'neo'
|
|
76
|
+
},
|
|
70
77
|
{
|
|
71
78
|
name: 'antd 自定义组件',
|
|
72
79
|
value: 'antd',
|
|
@@ -77,11 +84,6 @@ yargs
|
|
|
77
84
|
value: 'echarts',
|
|
78
85
|
short: 'echarts'
|
|
79
86
|
},
|
|
80
|
-
{
|
|
81
|
-
name: 'Neo 自定义组件',
|
|
82
|
-
value: 'neo',
|
|
83
|
-
short: 'neo'
|
|
84
|
-
},
|
|
85
87
|
/*
|
|
86
88
|
// 暂不提供 react js 模板(react js 模板已废弃)
|
|
87
89
|
{
|
|
@@ -227,6 +229,53 @@ yargs
|
|
|
227
229
|
}
|
|
228
230
|
}
|
|
229
231
|
)
|
|
232
|
+
.command(
|
|
233
|
+
'pullCmp [options]',
|
|
234
|
+
'拉取线上自定义组件',
|
|
235
|
+
(yargs) => {
|
|
236
|
+
yargs
|
|
237
|
+
.reset()
|
|
238
|
+
.usage(titleTip('Usage') + ': $0 preview [options]')
|
|
239
|
+
.option('name', {
|
|
240
|
+
alias: 'n',
|
|
241
|
+
describe: '自定义组件名称'
|
|
242
|
+
})
|
|
243
|
+
.alias('h', 'help');
|
|
244
|
+
},
|
|
245
|
+
async (argv) => {
|
|
246
|
+
if (argv.name) {
|
|
247
|
+
mainAction.pullCmp(argv.name);
|
|
248
|
+
} else {
|
|
249
|
+
// 创建 neoService 实例
|
|
250
|
+
const neoService = new NeoService(authConfig);
|
|
251
|
+
const spinner = ora('正在获取自定义组件列表...').start();
|
|
252
|
+
const cmpList = await neoService.getCustomCmpList();
|
|
253
|
+
if (cmpList.length === 0) {
|
|
254
|
+
console.error('当前租户暂无任何自定义组件。');
|
|
255
|
+
process.exit(1);
|
|
256
|
+
}
|
|
257
|
+
spinner.clear();
|
|
258
|
+
const cmpTypeChoices = cmpList.map((cmpType) => ({
|
|
259
|
+
name: cmpType,
|
|
260
|
+
value: cmpType
|
|
261
|
+
}));
|
|
262
|
+
questions.push({
|
|
263
|
+
name: 'cmpType',
|
|
264
|
+
type: 'list',
|
|
265
|
+
message: '请选择要拉取的自定义组件:',
|
|
266
|
+
choices: cmpTypeChoices
|
|
267
|
+
});
|
|
268
|
+
inquirer.prompt(questions).then((ans) => {
|
|
269
|
+
if (!ans.name) {
|
|
270
|
+
console.error('自定义组件名称不能为空。');
|
|
271
|
+
process.exit(1);
|
|
272
|
+
} else {
|
|
273
|
+
mainAction.pullCmp(ans.name, neoService);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
)
|
|
230
279
|
.command(
|
|
231
280
|
'preview [options]',
|
|
232
281
|
'预览指定自定义组件(仅预览组件本身内容)',
|
package/src/module/main.js
CHANGED
|
@@ -17,6 +17,7 @@ const { getExternalsByNeoCommonModules } = require('../neo/neoRequire');
|
|
|
17
17
|
const createCommonModulesCode = require('../cmpUtils/createCommonModulesCode');
|
|
18
18
|
const createCmpByTemplate = require('../cmpUtils/createCmpByTemplate');
|
|
19
19
|
const createCmpProjectByTemplate = require('../projectUtils/createCmpProjectByTemplate');
|
|
20
|
+
const pullCmp = require('../cmpUtils/pullCmp');
|
|
20
21
|
|
|
21
22
|
const getValue = (originValue, defaultValue) => {
|
|
22
23
|
return originValue !== undefined ? originValue : defaultValue;
|
|
@@ -277,8 +278,9 @@ module.exports = {
|
|
|
277
278
|
},
|
|
278
279
|
// 发布组件到 NeoCRM 平台
|
|
279
280
|
pushCmp: (cmpType) => {
|
|
281
|
+
const { neoConfig, pushCmp: _pushCmpConfig } = curConfig;
|
|
282
|
+
const pushCmpConfig = Object.assign({}, _pushCmpConfig, neoConfig);
|
|
280
283
|
// 将 pushCmp 相关配置设置给 build2lib
|
|
281
|
-
const pushCmpConfig = curConfig.pushCmp;
|
|
282
284
|
curConfig.build2lib = Object.assign(curConfig.build2lib, pushCmpConfig);
|
|
283
285
|
|
|
284
286
|
const curEntry = curConfig.build2lib.entry;
|
|
@@ -363,5 +365,9 @@ module.exports = {
|
|
|
363
365
|
pushCmp(pushCmpConfig, cmpType);
|
|
364
366
|
});
|
|
365
367
|
},
|
|
368
|
+
// 从 NeoCRM 平台拉取组件
|
|
369
|
+
pullCmp: (cmpType, neoService) => {
|
|
370
|
+
pullCmp(cmpType, curConfig.neoConfig, neoService);
|
|
371
|
+
},
|
|
366
372
|
build2esm: (fileName) => akfun.build2esm(fileName, curConfig, consoleTag) // 构建esm输出模块
|
|
367
373
|
};
|
package/src/neo/NeoUMDContent.js
CHANGED
package/src/neo/neoService.js
CHANGED
|
@@ -2,17 +2,44 @@ const axios = require('axios');
|
|
|
2
2
|
const FormData = require('form-data');
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const ora = require('ora');
|
|
5
6
|
const _ = require('lodash');
|
|
7
|
+
const { resolve } = require('akfun');
|
|
6
8
|
const updatePublishLog = require('../projectUtils/updatePublishLog');
|
|
9
|
+
const { getFramework } = require('../utils/common');
|
|
7
10
|
|
|
8
11
|
// NeoCRM 平台默认 API 配置
|
|
9
12
|
const NeoCrmAPI = {
|
|
10
13
|
neoBaseURL: 'https://crm.xiaoshouyi.com', // 平台根地址
|
|
11
14
|
tokenAPI: 'https://login.xiaoshouyi.com/auc/oauth2/token', // Token 获取接口地址
|
|
12
15
|
uploadAPI: '/rest/metadata/v3.0/ui/customComponents/actions/upload', // 文件上传接口地址
|
|
16
|
+
delete: '/rest/metadata/v3.0/ui/customComponents',
|
|
17
|
+
query: '/rest/metadata/v3.0/ui/customComponents/actions/queryCustomComponents', // 带分页
|
|
18
|
+
queryAll: '/rest/metadata/v3.0/ui/customComponents/actions/queryAllCustomComponents', // 不带分页
|
|
13
19
|
saveAPI: '/rest/metadata/v3.0/ui/customComponents/actions/saveOrUpdateComponent' // 创建或者保存接口地址
|
|
14
20
|
};
|
|
15
21
|
|
|
22
|
+
const cmpFields = [
|
|
23
|
+
'cmpType',
|
|
24
|
+
'label',
|
|
25
|
+
'componentCategory',
|
|
26
|
+
'description',
|
|
27
|
+
'framework',
|
|
28
|
+
'icon',
|
|
29
|
+
'orderNo',
|
|
30
|
+
'version',
|
|
31
|
+
'propsSchema',
|
|
32
|
+
'defaultProps',
|
|
33
|
+
'previewProps',
|
|
34
|
+
'events',
|
|
35
|
+
'actions',
|
|
36
|
+
'asset',
|
|
37
|
+
'plugin',
|
|
38
|
+
'modelAsset',
|
|
39
|
+
'cssAsset',
|
|
40
|
+
'codeLib',
|
|
41
|
+
];
|
|
42
|
+
|
|
16
43
|
/**
|
|
17
44
|
* Neo 平台服务类
|
|
18
45
|
* 提供 token 管理、文件上传、组件更新等功能
|
|
@@ -23,32 +50,34 @@ class NeoService {
|
|
|
23
50
|
* @param {object} config 配置信息
|
|
24
51
|
* @param {string} config.neoBaseURL Neo 平台根地址
|
|
25
52
|
* @param {string} config.tokenAPI Token 获取接口地址
|
|
26
|
-
* @param {object} config.
|
|
27
|
-
* @param {string} config.
|
|
28
|
-
* @param {string} config.
|
|
29
|
-
* @param {string} config.
|
|
30
|
-
* @param {string} config.
|
|
53
|
+
* @param {object} config.auth 授权信息
|
|
54
|
+
* @param {string} config.auth.client_id 客户端 ID
|
|
55
|
+
* @param {string} config.auth.client_secret 客户端密钥
|
|
56
|
+
* @param {string} config.auth.username 用户名
|
|
57
|
+
* @param {string} config.auth.password 密码
|
|
31
58
|
*/
|
|
32
59
|
constructor(config = {}) {
|
|
33
|
-
const { assetsRoot, neoBaseURL, tokenAPI,
|
|
34
|
-
if (!
|
|
35
|
-
throw new Error('
|
|
60
|
+
const { assetsRoot, neoBaseURL, tokenAPI, auth } = config;
|
|
61
|
+
if (!auth) {
|
|
62
|
+
throw new Error('auth 不能为空');
|
|
36
63
|
}
|
|
37
64
|
if (
|
|
38
|
-
!
|
|
39
|
-
!
|
|
40
|
-
!
|
|
41
|
-
!
|
|
65
|
+
!auth.client_id ||
|
|
66
|
+
!auth.client_secret ||
|
|
67
|
+
!auth.username ||
|
|
68
|
+
!auth.password
|
|
42
69
|
) {
|
|
43
70
|
throw new Error(
|
|
44
|
-
'
|
|
71
|
+
'auth 配置不完整,需要包含 client_id、client_secret、username、password'
|
|
45
72
|
);
|
|
46
73
|
}
|
|
47
74
|
|
|
48
|
-
this.assetsRoot = assetsRoot;
|
|
75
|
+
this.assetsRoot = assetsRoot || resolve('dist');
|
|
49
76
|
this.neoBaseURL = neoBaseURL || NeoCrmAPI.neoBaseURL;
|
|
50
77
|
this.tokenAPI = tokenAPI || NeoCrmAPI.tokenAPI;
|
|
51
|
-
this.
|
|
78
|
+
this.auth = auth;
|
|
79
|
+
this.cmpList = [];
|
|
80
|
+
this.cmpInfoMap = {};
|
|
52
81
|
|
|
53
82
|
// Token 缓存
|
|
54
83
|
this.tokenCache = {
|
|
@@ -96,20 +125,20 @@ class NeoService {
|
|
|
96
125
|
* @returns {Promise<string>} token
|
|
97
126
|
*/
|
|
98
127
|
async getToken() {
|
|
128
|
+
const spinner = ora('获取 token...').start();
|
|
99
129
|
// 检查缓存是否有效
|
|
100
130
|
if (!this.isTokenExpired()) {
|
|
101
|
-
|
|
131
|
+
spinner.succeed('使用缓存的 token。');
|
|
102
132
|
return this.tokenCache.token;
|
|
103
133
|
}
|
|
104
|
-
console.info('获取 token...');
|
|
105
134
|
|
|
106
135
|
// 构建表单数据格式的请求参数
|
|
107
136
|
const formData = new URLSearchParams();
|
|
108
137
|
formData.append('grant_type', 'password');
|
|
109
|
-
formData.append('client_id', this.
|
|
110
|
-
formData.append('client_secret', this.
|
|
111
|
-
formData.append('username', this.
|
|
112
|
-
formData.append('password', this.
|
|
138
|
+
formData.append('client_id', this.auth.client_id);
|
|
139
|
+
formData.append('client_secret', this.auth.client_secret);
|
|
140
|
+
formData.append('username', this.auth.username);
|
|
141
|
+
formData.append('password', this.auth.password);
|
|
113
142
|
|
|
114
143
|
const tokenUrl = this.buildFullUrl(this.tokenAPI);
|
|
115
144
|
|
|
@@ -123,7 +152,7 @@ class NeoService {
|
|
|
123
152
|
const { access_token, expires_in } = response.data || {};
|
|
124
153
|
|
|
125
154
|
if (!access_token) {
|
|
126
|
-
|
|
155
|
+
spinner.fail('获取 token 失败(授权配置错误):响应中未包含 access_token,', response.data);
|
|
127
156
|
process.exit(1);
|
|
128
157
|
}
|
|
129
158
|
|
|
@@ -133,6 +162,7 @@ class NeoService {
|
|
|
133
162
|
token: access_token,
|
|
134
163
|
expiresAt: Date.now() + (expiresIn - 60) * 1000
|
|
135
164
|
};
|
|
165
|
+
spinner.succeed('获取 token 成功。');
|
|
136
166
|
return access_token;
|
|
137
167
|
} catch (error) {
|
|
138
168
|
console.error('\n获取 token 失败:', error.message);
|
|
@@ -167,8 +197,10 @@ class NeoService {
|
|
|
167
197
|
if (!this.tokenCache.token) {
|
|
168
198
|
return await this.getToken();
|
|
169
199
|
} else if (this.isTokenExpired()) {
|
|
170
|
-
|
|
171
|
-
|
|
200
|
+
const spinner = ora('token 已过期,正在刷新...').start();
|
|
201
|
+
const token = await this.refreshToken();
|
|
202
|
+
spinner.succeed('token 刷新成功。');
|
|
203
|
+
return token;
|
|
172
204
|
}
|
|
173
205
|
return this.tokenCache.token;
|
|
174
206
|
}
|
|
@@ -216,13 +248,13 @@ class NeoService {
|
|
|
216
248
|
|
|
217
249
|
const fileName = path.basename(filePath);
|
|
218
250
|
const fileSizeKB = (fileStat.size / 1024).toFixed(2);
|
|
219
|
-
|
|
251
|
+
const spinner = ora(`正在上传文件: ${fileName} (${fileSizeKB}KB)...`).start();
|
|
220
252
|
|
|
221
253
|
try {
|
|
222
254
|
// 创建 FormData
|
|
223
255
|
const formData = new FormData();
|
|
224
256
|
const fieldName = options.fieldName || 'customComponentCode';
|
|
225
|
-
|
|
257
|
+
|
|
226
258
|
// 使用文件流而不是读取整个文件到内存(对大文件更友好)
|
|
227
259
|
const fileContent = fs.createReadStream(filePath);
|
|
228
260
|
|
|
@@ -265,7 +297,11 @@ class NeoService {
|
|
|
265
297
|
resultData = responseData.trim();
|
|
266
298
|
} else if (responseData && typeof responseData === 'object') {
|
|
267
299
|
// 检查是否有错误码
|
|
268
|
-
if (
|
|
300
|
+
if (
|
|
301
|
+
responseData.code !== undefined &&
|
|
302
|
+
responseData.code !== 200 &&
|
|
303
|
+
responseData.code !== 0
|
|
304
|
+
) {
|
|
269
305
|
const errorMsg = responseData.message || responseData.msg || '未知错误';
|
|
270
306
|
throw new Error(`上传失败: ${errorMsg} (code: ${responseData.code})`);
|
|
271
307
|
}
|
|
@@ -296,36 +332,35 @@ class NeoService {
|
|
|
296
332
|
} else if (resultData && typeof resultData === 'object' && resultData.url) {
|
|
297
333
|
fileUrl = resultData.url;
|
|
298
334
|
}
|
|
299
|
-
|
|
335
|
+
spinner.succeed(`文件上传成功: ${fileName} -> ${fileUrl}`);
|
|
300
336
|
return fileUrl;
|
|
301
337
|
} catch (error) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
338
|
+
spinner.fail(`上传文件失败: ${error.message}, 文件路径: ${filePath}`);
|
|
339
|
+
|
|
305
340
|
// 输出详细的错误信息
|
|
306
341
|
if (error.response) {
|
|
307
342
|
const status = error.response.status;
|
|
308
343
|
const statusText = error.response.statusText;
|
|
309
344
|
const responseData = error.response.data;
|
|
310
345
|
const requestUrl = error.config?.url || this.uploadAPI();
|
|
311
|
-
|
|
346
|
+
|
|
312
347
|
console.error(`\n========== 上传请求详情 ==========`);
|
|
313
348
|
console.error(`请求 URL: ${requestUrl}`);
|
|
314
349
|
console.error(`HTTP 状态码: ${status} ${statusText}`);
|
|
315
350
|
console.error(`响应数据:`, responseData);
|
|
316
351
|
console.error(`==================================\n`);
|
|
317
|
-
|
|
352
|
+
|
|
318
353
|
if (status === 404) {
|
|
319
354
|
throw new Error(
|
|
320
355
|
`上传 API 不存在 (404): ${requestUrl}\n` +
|
|
321
|
-
|
|
322
|
-
|
|
356
|
+
`请检查 neo.config.js 中的 neoBaseURL 配置是否正确,或者 API 路径是否存在。\n` +
|
|
357
|
+
`当前配置的 API 路径: ${NeoCrmAPI.uploadAPI}`
|
|
323
358
|
);
|
|
324
359
|
}
|
|
325
360
|
} else if (error.request) {
|
|
326
361
|
console.error('请求已发送但未收到响应,请检查网络连接或代理配置。');
|
|
327
362
|
}
|
|
328
|
-
|
|
363
|
+
|
|
329
364
|
throw error;
|
|
330
365
|
}
|
|
331
366
|
}
|
|
@@ -334,9 +369,9 @@ class NeoService {
|
|
|
334
369
|
* 将构建产物上传到 NeoCRM 平台端
|
|
335
370
|
*
|
|
336
371
|
* @param {object} cmpType 自定义组件名称
|
|
337
|
-
* @param {array} fileExtensions 需要上传的文件类型,默认 ['.js', '.css']
|
|
372
|
+
* @param {array} fileExtensions 需要上传的文件类型,默认 ['.js', '.css', '.zip']
|
|
338
373
|
*/
|
|
339
|
-
async publish2oss(cmpType, fileExtensions = ['.js', '.css']) {
|
|
374
|
+
async publish2oss(cmpType, fileExtensions = ['.js', '.css', '.zip']) {
|
|
340
375
|
if (!cmpType) {
|
|
341
376
|
console.error(`自定义组件名称不能为空: ${cmpType}`);
|
|
342
377
|
return;
|
|
@@ -375,6 +410,8 @@ class NeoService {
|
|
|
375
410
|
curCmpInfo.modelAsset = fileUrl;
|
|
376
411
|
} else if (file.endsWith('.css')) {
|
|
377
412
|
curCmpInfo.cssAsset = fileUrl;
|
|
413
|
+
} else if (file.endsWith('.zip')) {
|
|
414
|
+
curCmpInfo.codeLib = fileUrl;
|
|
378
415
|
} else {
|
|
379
416
|
curCmpInfo.asset = fileUrl;
|
|
380
417
|
}
|
|
@@ -408,7 +445,7 @@ class NeoService {
|
|
|
408
445
|
throw new Error('componentData 不能为空');
|
|
409
446
|
}
|
|
410
447
|
|
|
411
|
-
|
|
448
|
+
const spinner = ora('正在更新自定义组件...').start();
|
|
412
449
|
|
|
413
450
|
try {
|
|
414
451
|
const fullUpdateAPI = this.saveAPI();
|
|
@@ -419,15 +456,15 @@ class NeoService {
|
|
|
419
456
|
'Content-Type': 'application/json'
|
|
420
457
|
}
|
|
421
458
|
});
|
|
422
|
-
const {code, message} = response.data || {};
|
|
459
|
+
const { code, message } = response.data || {};
|
|
423
460
|
|
|
424
461
|
if (code && code !== 200) {
|
|
425
462
|
throw new Error(`更新组件失败: ${response.data.message || '未知错误'}`);
|
|
426
463
|
}
|
|
427
|
-
|
|
428
464
|
|
|
429
|
-
|
|
465
|
+
spinner.succeed(message ? `组件更新成功: ${message}。` : '组件更新成功。');
|
|
430
466
|
} catch (error) {
|
|
467
|
+
spinner.fail('更新组件失败。');
|
|
431
468
|
if (error.message) {
|
|
432
469
|
console.error('更新组件失败:', error.message);
|
|
433
470
|
} else {
|
|
@@ -437,6 +474,79 @@ class NeoService {
|
|
|
437
474
|
}
|
|
438
475
|
}
|
|
439
476
|
|
|
477
|
+
/**
|
|
478
|
+
* 获取线上自定义组件列表
|
|
479
|
+
* @returns {Promise<array>} 自定义组件列表
|
|
480
|
+
*/
|
|
481
|
+
async getCustomCmpList() {
|
|
482
|
+
// 确保 token 有效
|
|
483
|
+
const token = await this.ensureValidToken();
|
|
484
|
+
|
|
485
|
+
/*
|
|
486
|
+
// 如果自定义组件列表已存在,则直接返回
|
|
487
|
+
if (this.cmpList && this.cmpList.length > 0) {
|
|
488
|
+
return this.cmpList;
|
|
489
|
+
}
|
|
490
|
+
*/
|
|
491
|
+
|
|
492
|
+
try {
|
|
493
|
+
let queryAllAPI = this.buildFullUrl(NeoCrmAPI.queryAll);
|
|
494
|
+
queryAllAPI += `?fields=${cmpFields.join(',')}`;
|
|
495
|
+
const response = await axios.post(queryAllAPI, {}, {
|
|
496
|
+
headers: {
|
|
497
|
+
Authorization: `Bearer ${token}`,
|
|
498
|
+
'xsy-inner-source': 'bff',
|
|
499
|
+
'Content-Type': 'application/json'
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
const { code, message } = response.data || {};
|
|
503
|
+
|
|
504
|
+
if (code && code !== 200) {
|
|
505
|
+
throw new Error(`获取自定义组件列表失败: ${message || '未知错误'}`);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
this.updateCustomCmpList(response.data.data || []);
|
|
509
|
+
|
|
510
|
+
} catch (error) {
|
|
511
|
+
if (error.message) {
|
|
512
|
+
console.error('获取自定义组件列表失败:', error.message);
|
|
513
|
+
} else {
|
|
514
|
+
console.error('响应数据:', error);
|
|
515
|
+
}
|
|
516
|
+
process.exit(1);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return this.cmpList || [];
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// 获取指定框架的自定义组件列表
|
|
523
|
+
getCmpListByFramework(framework) {
|
|
524
|
+
if (!framework) {
|
|
525
|
+
return this.cmpList;
|
|
526
|
+
}
|
|
527
|
+
const curFramework = getFramework(framework);
|
|
528
|
+
return this.cmpList.filter((cmp) => cmp.framework === curFramework);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// 获取自定义组件信息
|
|
532
|
+
getCmpInfoByCmpType(cmpType) {
|
|
533
|
+
if (!cmpType) {
|
|
534
|
+
return null;
|
|
535
|
+
}
|
|
536
|
+
return this.cmpInfoMap[cmpType] || null;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// 更新自定义组件 Map
|
|
540
|
+
updateCustomCmpList(cmpList) {
|
|
541
|
+
if (!cmpList || !Array.isArray(cmpList)) {
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
this.cmpList = cmpList;
|
|
545
|
+
cmpList.forEach((cmp) => {
|
|
546
|
+
this.cmpInfoMap[cmp.cmpType] = cmp;
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
|
|
440
550
|
/**
|
|
441
551
|
* 通用请求方法(确保 token 有效)
|
|
442
552
|
* @param {string} method HTTP 方法
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const AdmZip = require('adm-zip');
|
|
4
|
+
const _ = require('lodash');
|
|
5
|
+
const { consoleTag } = require('../utils/neoParams'); // 输出标记
|
|
6
|
+
const hasNeoProject = require('./hasNeoProject');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 创建自定义组件源码 zip 包
|
|
10
|
+
* @param {*} cmpType 自定义组件名称
|
|
11
|
+
* @returns {string} zip 文件路径
|
|
12
|
+
*/
|
|
13
|
+
module.exports = function (cmpType, _projectPath, assetsRoot) {
|
|
14
|
+
const projectRoot = _projectPath || process.cwd();
|
|
15
|
+
const finalAssetsRoot = assetsRoot || path.join(projectRoot, 'dist');
|
|
16
|
+
|
|
17
|
+
if (!hasNeoProject(_projectPath)) {
|
|
18
|
+
console.error(`${consoleTag}当前目录不是自定义组件项目,请在自定义组件项目目录下执行。`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!cmpType) {
|
|
23
|
+
console.error(`${consoleTag}自定义组件名称不能为空`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const zip = new AdmZip();
|
|
28
|
+
|
|
29
|
+
// 需要排除的目录和文件
|
|
30
|
+
const excludeDirs = ['node_modules', '.neo-cli', 'dist'];
|
|
31
|
+
const excludeFiles = ['.eslintcache', 'auth.config.js'];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 判断文件/目录是否应该被排除
|
|
35
|
+
* @param {string} filePath 文件路径
|
|
36
|
+
* @param {string} relativePath 相对路径
|
|
37
|
+
* @returns {boolean} 是否应该排除
|
|
38
|
+
*/
|
|
39
|
+
const shouldExclude = (filePath, relativePath) => {
|
|
40
|
+
const name = path.basename(filePath);
|
|
41
|
+
const stat = fs.statSync(filePath);
|
|
42
|
+
|
|
43
|
+
// 排除指定的目录
|
|
44
|
+
if (stat.isDirectory() && excludeDirs.includes(name)) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 排除指定的文件
|
|
49
|
+
if (stat.isFile() && excludeFiles.includes(name)) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 对于 src/components 目录,只包含 cmpType 子目录
|
|
54
|
+
// 处理逻辑:如果路径是 src/components/xxx,且 xxx 不是 cmpType,则排除
|
|
55
|
+
if (relativePath.startsWith('src/components')) {
|
|
56
|
+
const relativeToComponents = path.relative('src/components', relativePath);
|
|
57
|
+
// 如果相对路径不为空,说明已经在 components 的子目录中
|
|
58
|
+
if (relativeToComponents && relativeToComponents !== '.') {
|
|
59
|
+
const firstPart = relativeToComponents.split(path.sep)[0];
|
|
60
|
+
// 如果第一个部分不是 cmpType,则排除
|
|
61
|
+
if (firstPart !== cmpType) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return false;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 递归遍历目录并添加到 zip
|
|
72
|
+
* @param {string} dirPath 目录路径
|
|
73
|
+
* @param {string} relativePath 相对路径(用于 zip 中的路径)
|
|
74
|
+
*/
|
|
75
|
+
const addDirectoryToZip = (dirPath, relativePath = '') => {
|
|
76
|
+
try {
|
|
77
|
+
const items = fs.readdirSync(dirPath);
|
|
78
|
+
|
|
79
|
+
items.forEach((item) => {
|
|
80
|
+
const itemPath = path.join(dirPath, item);
|
|
81
|
+
const itemRelativePath = relativePath ? path.join(relativePath, item) : item;
|
|
82
|
+
|
|
83
|
+
// 检查是否应该排除
|
|
84
|
+
if (shouldExclude(itemPath, itemRelativePath)) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const stat = fs.statSync(itemPath);
|
|
89
|
+
|
|
90
|
+
if (stat.isDirectory()) {
|
|
91
|
+
// 递归处理子目录
|
|
92
|
+
addDirectoryToZip(itemPath, itemRelativePath);
|
|
93
|
+
} else if (stat.isFile()) {
|
|
94
|
+
// 添加文件到 zip,保持相对路径结构
|
|
95
|
+
const fileContent = fs.readFileSync(itemPath);
|
|
96
|
+
zip.addFile(itemRelativePath, fileContent);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error(`${consoleTag}遍历目录失败 (${dirPath}):`, error.message);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// 开始遍历项目根目录
|
|
105
|
+
addDirectoryToZip(projectRoot);
|
|
106
|
+
|
|
107
|
+
// 生成 zip 文件名
|
|
108
|
+
const zipFileName = `${_.camelCase(cmpType)}Source.zip`;
|
|
109
|
+
const zipPath = path.join(finalAssetsRoot, zipFileName);
|
|
110
|
+
|
|
111
|
+
// 如果已存在同名 zip 文件,先删除
|
|
112
|
+
if (fs.existsSync(zipPath)) {
|
|
113
|
+
fs.removeSync(zipPath);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 写入 zip 文件
|
|
117
|
+
zip.writeZip(zipPath);
|
|
118
|
+
|
|
119
|
+
console.log(`${consoleTag}已创建自定义组件源码 zip 包: ${zipFileName}`);
|
|
120
|
+
|
|
121
|
+
return zipPath;
|
|
122
|
+
};
|
|
@@ -2,12 +2,13 @@ const fs = require('fs-extra');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* 判断当前是否存在
|
|
6
|
-
* 备注:neo.config.js 和 package.json 必须同时存在才算作
|
|
5
|
+
* 判断当前是否存在 自定义组件项目
|
|
6
|
+
* 备注:neo.config.js 和 package.json 必须同时存在才算作 自定义组件项目
|
|
7
7
|
*/
|
|
8
|
-
module.exports = function () {
|
|
9
|
-
const
|
|
10
|
-
const
|
|
8
|
+
module.exports = function (_projectPath) {
|
|
9
|
+
const projectPath = _projectPath || process.cwd();
|
|
10
|
+
const neoConfigPath = path.resolve(projectPath, 'neo.config.js');
|
|
11
|
+
const packagePath = path.resolve(projectPath, 'package.json');
|
|
11
12
|
|
|
12
13
|
if (fs.existsSync(neoConfigPath) && fs.existsSync(packagePath)) {
|
|
13
14
|
return true;
|
|
@@ -97,12 +97,12 @@ module.exports = {
|
|
|
97
97
|
}
|
|
98
98
|
*/
|
|
99
99
|
},
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
// NeoCRM 平台配置
|
|
101
|
+
neoConfig: {
|
|
102
102
|
neoBaseURL: 'https://crm-cd.xiaoshouyi.com', // 平台根地址(默认:https://crm.xiaoshouyi.com)
|
|
103
103
|
tokenAPI: 'https://login-cd.xiaoshouyi.com/auc/oauth2/token', // Token 获取接口地址(默认:https://login.xiaoshouyi.com/auc/oauth2/token)
|
|
104
104
|
// NeoCRM 授权配置
|
|
105
|
-
|
|
105
|
+
auth: {
|
|
106
106
|
client_id: 'xx', // 客户端 ID,从创建连接器的客户端信息中获取(Client_Id)
|
|
107
107
|
client_secret: 'xxx', // 客户端秘钥,从创建连接器的客户端信息中获取(Client_Secret)
|
|
108
108
|
username: 'xx', // 用户在销售易系统中的用户名
|
|
@@ -111,7 +111,10 @@ module.exports = {
|
|
|
111
111
|
* 例如,用户密码为 123456,安全令牌为 ABCDEFGH,则 password 的值应为 123456ABCDEFGH。
|
|
112
112
|
*/
|
|
113
113
|
password: 'xx xx' // 用户账户密码 + 8 位安全令牌
|
|
114
|
-
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
pushCmp: {
|
|
117
|
+
// 用于构建并发布至 NeoCRM 的相关配置
|
|
115
118
|
/*
|
|
116
119
|
【特别说明】以下配置项都自带默认值,非必填。如需自定义请自行配置。
|
|
117
120
|
NODE_ENV: 'production',
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"name": "antd-custom-cmp-template",
|
|
3
3
|
"version": "1.1.9",
|
|
4
4
|
"description": "neo自定义组件模板(react&ts技术栈)",
|
|
5
|
+
"framework": "react-ts",
|
|
5
6
|
"keywords": [
|
|
6
7
|
"自定义组件模板",
|
|
7
8
|
"react&ts技术栈",
|
|
@@ -47,7 +48,7 @@
|
|
|
47
48
|
"@commitlint/config-conventional": "^9.1.1",
|
|
48
49
|
"@types/react": "^16.9.11",
|
|
49
50
|
"@types/react-dom": "^16.9.15",
|
|
50
|
-
"neo-cmp-cli": "^1.5.
|
|
51
|
+
"neo-cmp-cli": "^1.5.6",
|
|
51
52
|
"husky": "^4.2.5",
|
|
52
53
|
"lint-staged": "^10.2.9",
|
|
53
54
|
"prettier": "^2.0.5"
|