neo-cmp-cli 1.8.7 → 1.8.9
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/bin/index.js +2 -2
- package/dist/_virtual/_commonjsHelpers.js +12 -0
- package/dist/_virtual/array-set.js +7 -0
- package/dist/_virtual/base64-vlq.js +7 -0
- package/dist/_virtual/base64.js +7 -0
- package/dist/_virtual/binary-search.js +7 -0
- package/dist/_virtual/mapping-list.js +7 -0
- package/dist/_virtual/quick-sort.js +7 -0
- package/dist/_virtual/source-map-consumer.js +7 -0
- package/dist/_virtual/source-map-generator.js +7 -0
- package/dist/_virtual/source-map-support.js +7 -0
- package/dist/_virtual/source-map.js +7 -0
- package/dist/_virtual/source-node.js +7 -0
- package/dist/_virtual/typescript.js +7 -0
- package/dist/_virtual/util.js +7 -0
- package/dist/config/auth.config.js +43 -0
- package/dist/config/default.config.js +192 -0
- package/dist/config/index.js +26 -0
- package/dist/main.js +9 -0
- package/dist/main2.js +259 -0
- package/dist/module/inspect.js +63 -0
- package/dist/module/neoInit.js +75 -0
- package/dist/module/neoInitByCopy.js +83 -0
- package/dist/neo/neoLogin.js +590 -0
- package/dist/neo/neoRequire.js +123 -0
- package/dist/neo/neoService.js +898 -0
- package/dist/node_modules/buffer-from/index.js +86 -0
- package/dist/node_modules/source-map/lib/array-set.js +138 -0
- package/dist/node_modules/source-map/lib/base64-vlq.js +157 -0
- package/dist/node_modules/source-map/lib/base64.js +83 -0
- package/dist/node_modules/source-map/lib/binary-search.js +129 -0
- package/dist/node_modules/source-map/lib/mapping-list.js +96 -0
- package/dist/node_modules/source-map/lib/quick-sort.js +130 -0
- package/dist/node_modules/source-map/lib/source-map-consumer.js +1166 -0
- package/dist/node_modules/source-map/lib/source-map-generator.js +445 -0
- package/dist/node_modules/source-map/lib/source-node.js +431 -0
- package/dist/node_modules/source-map/lib/util.js +506 -0
- package/dist/node_modules/source-map/source-map.js +27 -0
- package/dist/node_modules/source-map-support/source-map-support.js +646 -0
- package/dist/node_modules/typescript/lib/typescript.js +174130 -0
- package/dist/oss/publish2oss.js +331 -0
- package/dist/plugins/AddNeoRequirePlugin.js +195 -0
- package/dist/utils/autoEntryRootDir.js +103 -0
- package/dist/utils/cmpUtils/createCmpByTemplate.js +82 -0
- package/dist/utils/cmpUtils/createCmpByZip.js +433 -0
- package/dist/utils/cmpUtils/createCommonModulesCode.js +139 -0
- package/dist/utils/cmpUtils/deleteCmp.js +81 -0
- package/dist/utils/cmpUtils/getCmpModelRegisterCode.js +47 -0
- package/dist/utils/cmpUtils/getCmpPreviewCode.js +60 -0
- package/dist/utils/cmpUtils/getCmpRegisterCode.js +47 -0
- package/dist/utils/cmpUtils/getCmpTypeByDir.js +59 -0
- package/dist/utils/cmpUtils/hasCmpTypeByDir.js +27 -0
- package/dist/utils/cmpUtils/previewCmp.js +75 -0
- package/dist/utils/cmpUtils/pullCmp.js +126 -0
- package/dist/utils/cmpUtils/pushCmp.js +254 -0
- package/dist/utils/common.js +125 -0
- package/dist/utils/configureNeoBuild.js +129 -0
- package/dist/utils/generateEntries.js +80 -0
- package/dist/utils/neoConfigInit.js +30 -0
- package/dist/utils/neoParams.js +26 -0
- package/dist/utils/pathUtils.js +40 -0
- package/dist/utils/projectNameValidator.js +90 -0
- package/dist/utils/projectUtils/createCmpProjectByTemplate.js +81 -0
- package/dist/utils/projectUtils/createCmpProjectZip.js +141 -0
- package/dist/utils/projectUtils/getEntries.js +99 -0
- package/dist/utils/projectUtils/getEntriesWithAutoRegister.js +129 -0
- package/dist/utils/projectUtils/hasNeoProject.js +34 -0
- package/dist/utils/projectUtils/openProject.js +117 -0
- package/dist/utils/projectUtils/updatePublishLog.js +47 -0
- package/dist/utils/replaceInFilesByMap.js +71 -0
- package/dist/utils/replaceInPackage.js +151 -0
- package/dist/utils/resetPackageVersion.js +132 -0
- package/package.json +6 -8
- package/test/demo.js +0 -2
- package/test/deprecate-versions.js +1 -1
- package/src/config/auth.config.js +0 -27
- package/src/config/default.config.js +0 -176
- package/src/config/index.js +0 -9
- package/src/main.js +0 -221
- package/src/module/inspect.js +0 -41
- package/src/module/neoInit.js +0 -55
- package/src/module/neoInitByCopy.js +0 -61
- package/src/neo/NeoUMDContent.js +0 -30
- package/src/neo/neoLogin.js +0 -565
- package/src/neo/neoRequire.js +0 -125
- package/src/neo/neoService.js +0 -874
- package/src/neo/webpack.mf.js +0 -60
- package/src/neo/wrapperContent.js +0 -16
- package/src/oss/publish2oss.js +0 -348
- package/src/plugins/AddNeoRequirePlugin-v1.js +0 -47
- package/src/plugins/AddNeoRequirePlugin.js +0 -179
- package/src/plugins/README.md +0 -109
- package/src/utils/autoEntryRootDir.js +0 -85
- package/src/utils/cmpUtils/createCmpByTemplate.js +0 -60
- package/src/utils/cmpUtils/createCmpByZip.js +0 -408
- package/src/utils/cmpUtils/createCommonModulesCode.js +0 -121
- package/src/utils/cmpUtils/deleteCmp.js +0 -63
- package/src/utils/cmpUtils/getCmpModelRegisterCode.js +0 -31
- package/src/utils/cmpUtils/getCmpPreviewCode.js +0 -43
- package/src/utils/cmpUtils/getCmpRegisterCode.js +0 -31
- package/src/utils/cmpUtils/getCmpTypeByDir.js +0 -41
- package/src/utils/cmpUtils/hasCmpTypeByDir.js +0 -11
- package/src/utils/cmpUtils/previewCmp.js +0 -55
- package/src/utils/cmpUtils/pullCmp.js +0 -104
- package/src/utils/cmpUtils/pushCmp.js +0 -230
- package/src/utils/common.js +0 -107
- package/src/utils/configureNeoBuild.js +0 -109
- package/src/utils/generateEntries.js +0 -63
- package/src/utils/neoConfigInit.js +0 -13
- package/src/utils/neoParams.js +0 -12
- package/src/utils/pathUtils.js +0 -23
- package/src/utils/projectNameValidator.js +0 -76
- package/src/utils/projectUtils/createCmpProjectByTemplate.js +0 -59
- package/src/utils/projectUtils/createCmpProjectZip.js +0 -120
- package/src/utils/projectUtils/getEntries.js +0 -80
- package/src/utils/projectUtils/getEntriesWithAutoRegister.js +0 -108
- package/src/utils/projectUtils/hasNeoProject.js +0 -17
- package/src/utils/projectUtils/openProject.js +0 -96
- package/src/utils/projectUtils/updatePublishLog.js +0 -30
- package/src/utils/replaceInFiles.js +0 -47
- package/src/utils/replaceInFilesByMap.js +0 -54
- package/src/utils/replaceInPackage.js +0 -134
- package/src/utils/resetPackageVersion.js +0 -115
- /package/{src → template}/initData/defaultTemplate.html +0 -0
- /package/{src → template}/initData/neo.config.js +0 -0
|
@@ -0,0 +1,898 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
4
|
+
|
|
5
|
+
const require$$0$1 = require('axios');
|
|
6
|
+
const require$$1$1 = require('form-data');
|
|
7
|
+
const require$$0$2 = require('node:fs');
|
|
8
|
+
const require$$1 = require('node:path');
|
|
9
|
+
const require$$3 = require('ora');
|
|
10
|
+
const require$$2 = require('lodash');
|
|
11
|
+
const require$$0 = require('akfun');
|
|
12
|
+
const neoLogin = require('./neoLogin.js');
|
|
13
|
+
const updatePublishLog = require('../utils/projectUtils/updatePublishLog.js');
|
|
14
|
+
const common = require('../utils/common.js');
|
|
15
|
+
|
|
16
|
+
var neoService;
|
|
17
|
+
var hasRequiredNeoService;
|
|
18
|
+
|
|
19
|
+
function requireNeoService () {
|
|
20
|
+
if (hasRequiredNeoService) return neoService;
|
|
21
|
+
hasRequiredNeoService = 1;
|
|
22
|
+
const axios = require$$0$1;
|
|
23
|
+
const FormData = require$$1$1;
|
|
24
|
+
const fs = require$$0$2;
|
|
25
|
+
const path = require$$1;
|
|
26
|
+
const ora = require$$3;
|
|
27
|
+
const _ = require$$2;
|
|
28
|
+
const { resolve } = require$$0;
|
|
29
|
+
const NeoLoginService = neoLogin.__require();
|
|
30
|
+
const updatePublishLog$1 = updatePublishLog.__require();
|
|
31
|
+
const { getFramework, errorLog, successLog } = common.__require();
|
|
32
|
+
|
|
33
|
+
// NeoCRM 平台默认 API 配置
|
|
34
|
+
const NeoCrmAPI = {
|
|
35
|
+
neoBaseURL: 'https://crm.xiaoshouyi.com', // 平台根地址
|
|
36
|
+
loginAPI: 'https://login-cd.xiaoshouyi.com/auc/oauth2/auth', // code 获取接口地址
|
|
37
|
+
tokenAPI: 'https://login.xiaoshouyi.com/auc/oauth2/token', // Token 获取接口地址
|
|
38
|
+
delete: '/rest/metadata/v3.0/ui/customComponents',
|
|
39
|
+
saveAPI: '/rest/metadata/v3.0/ui/customComponents/actions/saveOrUpdateComponent', // 创建或者保存接口地址
|
|
40
|
+
queryAll: '/rest/metadata/v3.0/ui/components/filter?custom=true', // 不带分页
|
|
41
|
+
getCodeLibAPI: (cmpType) => `/rest/metadata/v3.0/ui/customComponents/${cmpType}/codeLib`, // 组件源码下载
|
|
42
|
+
|
|
43
|
+
uploadAPI: '/rest/metadata/v3.0/ui/customComponents/actions/upload', // 文件上传接口地址(已废弃)
|
|
44
|
+
query: '/rest/metadata/v3.0/ui/customComponents/actions/queryCustomComponents' // 带分页(暂未使用)
|
|
45
|
+
// queryAll_v1: '/rest/metadata/v3.0/ui/customComponents/actions/queryAllCustomComponents', // 不带分页(已废弃)
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const cmpFields = [
|
|
49
|
+
'cmpType',
|
|
50
|
+
'label',
|
|
51
|
+
'componentCategory',
|
|
52
|
+
'description',
|
|
53
|
+
'framework',
|
|
54
|
+
'icon',
|
|
55
|
+
'iconUrl',
|
|
56
|
+
'orderNo',
|
|
57
|
+
'version',
|
|
58
|
+
'propsSchema',
|
|
59
|
+
'defaultProps',
|
|
60
|
+
'previewProps',
|
|
61
|
+
'events',
|
|
62
|
+
'functions',
|
|
63
|
+
'asset',
|
|
64
|
+
'modelAsset',
|
|
65
|
+
'cssAsset',
|
|
66
|
+
'codeLib'
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Neo 平台服务类
|
|
71
|
+
* 提供 token 管理、文件上传、组件更新等功能
|
|
72
|
+
*/
|
|
73
|
+
class NeoService {
|
|
74
|
+
/**
|
|
75
|
+
* 初始化 Neo 服务
|
|
76
|
+
* @param {object} config 配置信息
|
|
77
|
+
* @param {string} config.authType 授权类型,可选值:oauth2(默认)、password
|
|
78
|
+
* @param {string} config.neoBaseURL Neo 平台根地址
|
|
79
|
+
* @param {string} config.tokenAPI Token 获取接口地址
|
|
80
|
+
* @param {string} config.loginURL OAuth2 登录授权 URL(OAuth2 模式需要)
|
|
81
|
+
* @param {object} config.auth 授权信息
|
|
82
|
+
* @param {string} config.auth.client_id 客户端 ID
|
|
83
|
+
* @param {string} config.auth.client_secret 客户端密钥
|
|
84
|
+
* @param {string} config.auth.username 用户名(password 模式需要)
|
|
85
|
+
* @param {string} config.auth.password 密码(password 模式需要)
|
|
86
|
+
*/
|
|
87
|
+
constructor(config = {}) {
|
|
88
|
+
const { assetsRoot, neoBaseURL, tokenAPI, loginURL, auth, authType } = config || {};
|
|
89
|
+
|
|
90
|
+
// 设置授权类型,默认为 oauth2
|
|
91
|
+
this.authType = authType || 'oauth2';
|
|
92
|
+
|
|
93
|
+
if (this.authType === 'password' && !auth) {
|
|
94
|
+
errorLog('密码授权模式时,neo.config.js / neoConfig / auth 配置不能为空');
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 根据授权类型验证必需的配置项
|
|
99
|
+
if (this.authType === 'password') {
|
|
100
|
+
if (!auth.client_id || !auth.client_secret || !auth.username || !auth.password) {
|
|
101
|
+
errorLog(
|
|
102
|
+
'neo.config.js / neoConfig / auth 配置不完整(password 模式),需要包含 client_id、client_secret、username、password'
|
|
103
|
+
);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
} else if (this.authType === 'oauth2') {
|
|
107
|
+
if (!loginURL || !tokenAPI) {
|
|
108
|
+
errorLog(
|
|
109
|
+
'neo.config.js / neoConfig 配置不完整(oauth2 模式),需要包含 loginURL、tokenAPI 配置。'
|
|
110
|
+
);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
errorLog(`不支持的授权类型: ${this.authType},可选值:oauth2、password`);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
this.assetsRoot = assetsRoot || resolve('dist');
|
|
119
|
+
this.neoBaseURL = neoBaseURL || NeoCrmAPI.neoBaseURL;
|
|
120
|
+
this.tokenAPI = tokenAPI || NeoCrmAPI.tokenAPI;
|
|
121
|
+
this.loginURL = loginURL || NeoCrmAPI.loginAPI;
|
|
122
|
+
this.auth = auth;
|
|
123
|
+
this.cmpList = [];
|
|
124
|
+
this.cmpInfoMap = {};
|
|
125
|
+
|
|
126
|
+
// Token 缓存
|
|
127
|
+
this.tokenCache = {
|
|
128
|
+
token: null,
|
|
129
|
+
expiresAt: null
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 构建完整的 API URL
|
|
135
|
+
* @param {string} url 相对或绝对 URL
|
|
136
|
+
* @returns {string} 完整的 URL
|
|
137
|
+
*/
|
|
138
|
+
buildFullUrl(url) {
|
|
139
|
+
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
140
|
+
return url;
|
|
141
|
+
}
|
|
142
|
+
return `${this.neoBaseURL}${url}`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// 获取文件上传接口地址
|
|
146
|
+
uploadAPI() {
|
|
147
|
+
return this.buildFullUrl(NeoCrmAPI.uploadAPI);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 获取组件信息保存接口地址
|
|
151
|
+
saveAPI() {
|
|
152
|
+
return this.buildFullUrl(NeoCrmAPI.saveAPI);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 检查 token 是否过期
|
|
157
|
+
* @returns {boolean} true 表示已过期,false 表示未过期
|
|
158
|
+
*/
|
|
159
|
+
isTokenExpired() {
|
|
160
|
+
if (!this.tokenCache.token || !this.tokenCache.expiresAt) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
// 提前 60 秒判断为过期,避免边缘情况
|
|
164
|
+
return Date.now() >= this.tokenCache.expiresAt;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* 获取 token(含授权信息、租户信息)
|
|
169
|
+
* 根据 authType 自动选择授权模式:oauth2 或 password
|
|
170
|
+
* @returns {Promise<string>} token
|
|
171
|
+
*/
|
|
172
|
+
async getToken() {
|
|
173
|
+
// 检查缓存是否有效
|
|
174
|
+
if (!this.isTokenExpired()) {
|
|
175
|
+
return this.tokenCache.token;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 根据授权类型选择对应的获取方式
|
|
179
|
+
if (this.authType === 'oauth2') {
|
|
180
|
+
return await this.getTokenByOAuth2();
|
|
181
|
+
} else {
|
|
182
|
+
return await this.getTokenByPassword();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* 使用密码模式获取 token
|
|
188
|
+
* @returns {Promise<string>} token
|
|
189
|
+
*/
|
|
190
|
+
async getTokenByPassword() {
|
|
191
|
+
const spinner = ora('获取 token(密码模式)...').start();
|
|
192
|
+
|
|
193
|
+
// 检查缓存是否有效
|
|
194
|
+
if (!this.isTokenExpired()) {
|
|
195
|
+
successLog('使用缓存的 token。', spinner);
|
|
196
|
+
return this.tokenCache.token;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 构建表单数据格式的请求参数
|
|
200
|
+
const formData = new URLSearchParams();
|
|
201
|
+
formData.append('grant_type', 'password');
|
|
202
|
+
formData.append('client_id', this.auth.client_id);
|
|
203
|
+
formData.append('client_secret', this.auth.client_secret);
|
|
204
|
+
formData.append('username', this.auth.username);
|
|
205
|
+
formData.append('password', this.auth.password);
|
|
206
|
+
|
|
207
|
+
const tokenUrl = this.buildFullUrl(this.tokenAPI);
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
const response = await axios.post(tokenUrl, formData.toString(), {
|
|
211
|
+
headers: {
|
|
212
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const { access_token, expires_in } = response.data || {};
|
|
217
|
+
|
|
218
|
+
if (!access_token) {
|
|
219
|
+
errorLog(
|
|
220
|
+
'获取 token 失败(授权配置错误):响应中未包含 access_token,' +
|
|
221
|
+
JSON.stringify(response.data),
|
|
222
|
+
spinner
|
|
223
|
+
);
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// 缓存 token(提前 60 秒过期,避免边缘情况)
|
|
228
|
+
const expiresIn = parseInt(expires_in) || 3600; // 默认 1 小时
|
|
229
|
+
this.tokenCache = {
|
|
230
|
+
token: access_token,
|
|
231
|
+
expiresAt: Date.now() + (expiresIn - 60) * 1000
|
|
232
|
+
};
|
|
233
|
+
spinner.clear();
|
|
234
|
+
spinner.stop();
|
|
235
|
+
return access_token;
|
|
236
|
+
} catch (error) {
|
|
237
|
+
errorLog('获取 token 失败', spinner);
|
|
238
|
+
errorLog(`\n获取 token 失败: ${error.message}`);
|
|
239
|
+
errorLog(`\ntoken 授权地址: ${tokenUrl}`);
|
|
240
|
+
errorLog(`\ntoken 请求参数: ${formData}`);
|
|
241
|
+
if (error.response) {
|
|
242
|
+
errorLog(`响应数据: ${JSON.stringify(error.response.data)}`);
|
|
243
|
+
}
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* 获取 OAuth2 授权码模式的 token
|
|
250
|
+
* @returns {Promise<string>} token
|
|
251
|
+
*/
|
|
252
|
+
async getTokenByOAuth2() {
|
|
253
|
+
const spinner = ora('获取 token(OAuth2 模式)...').start();
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
// 构建 OAuth2 配置
|
|
257
|
+
const oauth2Config = {
|
|
258
|
+
loginURL: this.loginURL,
|
|
259
|
+
tokenAPI: this.tokenAPI
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const loginService = new NeoLoginService(oauth2Config);
|
|
263
|
+
const accessToken = await loginService.getAccessToken();
|
|
264
|
+
|
|
265
|
+
// 缓存 token(OAuth2 模式下的 token 有效期由 NeoLoginService 管理)
|
|
266
|
+
// 这里我们设置一个较长的过期时间,实际过期检查由 NeoLoginService 内部处理
|
|
267
|
+
this.tokenCache = {
|
|
268
|
+
token: accessToken,
|
|
269
|
+
expiresAt: Date.now() + 7200 * 1000 // 默认 2 小时,实际由 NeoLoginService 管理
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
spinner.clear();
|
|
273
|
+
spinner.stop();
|
|
274
|
+
return accessToken;
|
|
275
|
+
} catch (error) {
|
|
276
|
+
errorLog('获取 token 失败(OAuth2 模式)', spinner);
|
|
277
|
+
errorLog(`\n获取 token 失败: ${error.message}`);
|
|
278
|
+
if (error.response) {
|
|
279
|
+
errorLog(`响应数据: ${JSON.stringify(error.response.data)}`);
|
|
280
|
+
}
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* 刷新 token
|
|
287
|
+
* @returns {Promise<string>} 新的 token
|
|
288
|
+
*/
|
|
289
|
+
async refreshToken() {
|
|
290
|
+
// 清除缓存,强制获取新 token
|
|
291
|
+
this.tokenCache = {
|
|
292
|
+
token: null,
|
|
293
|
+
expiresAt: null
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
return await this.getToken();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* 确保 token 有效,如果过期则自动刷新
|
|
301
|
+
* @returns {Promise<string>} 有效的 token
|
|
302
|
+
*/
|
|
303
|
+
async ensureValidToken() {
|
|
304
|
+
if (!this.tokenCache.token) {
|
|
305
|
+
return await this.getToken();
|
|
306
|
+
} else if (this.isTokenExpired()) {
|
|
307
|
+
const spinner = ora('token 已过期,正在刷新...').start();
|
|
308
|
+
try {
|
|
309
|
+
const token = await this.refreshToken();
|
|
310
|
+
successLog('token 刷新成功。', spinner);
|
|
311
|
+
return token;
|
|
312
|
+
} catch (error) {
|
|
313
|
+
errorLog('token 刷新失败。', spinner);
|
|
314
|
+
throw error;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return this.tokenCache.token;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* 上传文件到 Neo 平台(已废弃)
|
|
322
|
+
* 备注:已废弃,改为 保存时直接上传组件资源文件
|
|
323
|
+
* @param {string} filePath 文件路径
|
|
324
|
+
* @param {object} options 可选配置
|
|
325
|
+
* @param {string} options.fieldName 表单字段名,默认为 'customComponentCode'
|
|
326
|
+
* @param {number} options.maxSize 最大文件大小(字节),默认 50MB
|
|
327
|
+
* @param {number} options.timeout 超时时间(毫秒),默认 60000
|
|
328
|
+
* @returns {Promise<string>} CDN 地址或文件 URL
|
|
329
|
+
*/
|
|
330
|
+
async uploadFile(filePath, options = {}) {
|
|
331
|
+
// 确保 token 有效
|
|
332
|
+
const token = await this.ensureValidToken();
|
|
333
|
+
|
|
334
|
+
// 验证文件路径
|
|
335
|
+
if (!filePath || typeof filePath !== 'string') {
|
|
336
|
+
throw new Error(`文件路径无效: ${filePath}`);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// 检查文件是否存在
|
|
340
|
+
if (!fs.existsSync(filePath)) {
|
|
341
|
+
throw new Error(`文件不存在: ${filePath}`);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// 检查文件状态
|
|
345
|
+
const fileStat = fs.statSync(filePath);
|
|
346
|
+
if (!fileStat.isFile()) {
|
|
347
|
+
throw new Error(`路径不是文件: ${filePath}`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// 检查文件大小
|
|
351
|
+
const maxSize = options.maxSize || 5 * 1024 * 1024; // 默认 5MB
|
|
352
|
+
if (fileStat.size > maxSize) {
|
|
353
|
+
const sizeMB = (fileStat.size / 1024 / 1024).toFixed(2);
|
|
354
|
+
const maxSizeMB = (maxSize / 1024 / 1024).toFixed(2);
|
|
355
|
+
throw new Error(`文件大小超过限制: ${sizeMB}MB > ${maxSizeMB}MB`);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (fileStat.size === 0) {
|
|
359
|
+
throw new Error(`文件为空: ${filePath}`);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const fileName = path.basename(filePath);
|
|
363
|
+
const fileSizeKB = (fileStat.size / 1024).toFixed(2);
|
|
364
|
+
const spinner = ora(`正在上传文件: ${fileName} (${fileSizeKB}KB)...`).start();
|
|
365
|
+
|
|
366
|
+
try {
|
|
367
|
+
// 创建 FormData
|
|
368
|
+
const formData = new FormData();
|
|
369
|
+
const fieldName = options.fieldName || 'customComponentCode';
|
|
370
|
+
|
|
371
|
+
// 使用文件流而不是读取整个文件到内存(对大文件更友好)
|
|
372
|
+
const fileContent = fs.createReadStream(filePath);
|
|
373
|
+
|
|
374
|
+
// 追加文件到 FormData,第三个参数指定文件名
|
|
375
|
+
formData.append(fieldName, fileContent, fileName);
|
|
376
|
+
|
|
377
|
+
// 构建完整的上传 API 地址
|
|
378
|
+
const fullUploadAPI = this.uploadAPI();
|
|
379
|
+
|
|
380
|
+
// 配置请求选项
|
|
381
|
+
const timeout = options.timeout || 60000; // 默认 60 秒
|
|
382
|
+
const requestConfig = {
|
|
383
|
+
headers: {
|
|
384
|
+
Authorization: `Bearer ${token}`,
|
|
385
|
+
'xsy-inner-source': 'bff',
|
|
386
|
+
// 无需手动设置 Content-Type,formData.getHeaders() 会自动设置正确的 multipart/form-data 和 boundary
|
|
387
|
+
...formData.getHeaders()
|
|
388
|
+
},
|
|
389
|
+
timeout,
|
|
390
|
+
// 确保 axios 正确处理大文件和流
|
|
391
|
+
maxContentLength: Infinity, // 不限制响应内容长度
|
|
392
|
+
maxBodyLength: Infinity // 不限制请求体长度(适用于文件上传)
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
// 发送上传请求
|
|
396
|
+
const response = await axios.post(fullUploadAPI, formData, requestConfig);
|
|
397
|
+
|
|
398
|
+
// 处理响应数据
|
|
399
|
+
let resultData;
|
|
400
|
+
const responseData = response.data;
|
|
401
|
+
|
|
402
|
+
// 检查响应状态码
|
|
403
|
+
if (response.status !== 200 && response.status !== 201) {
|
|
404
|
+
throw new Error(`上传失败: HTTP ${response.status}`);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// 处理不同的响应格式
|
|
408
|
+
if (typeof responseData === 'string') {
|
|
409
|
+
// 如果响应是字符串,直接使用
|
|
410
|
+
resultData = responseData.trim();
|
|
411
|
+
} else if (responseData && typeof responseData === 'object') {
|
|
412
|
+
// 检查是否有错误码
|
|
413
|
+
if (
|
|
414
|
+
responseData.code !== undefined &&
|
|
415
|
+
responseData.code !== 200 &&
|
|
416
|
+
responseData.code !== 0
|
|
417
|
+
) {
|
|
418
|
+
const errorMsg = responseData.message || responseData.msg || '未知错误';
|
|
419
|
+
throw new Error(`上传失败: ${errorMsg} (code: ${responseData.code})`);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// 提取数据
|
|
423
|
+
if (responseData.data !== undefined) {
|
|
424
|
+
resultData = responseData.data;
|
|
425
|
+
} else if (responseData.url !== undefined) {
|
|
426
|
+
resultData = responseData.url;
|
|
427
|
+
} else if (responseData.fileUrl !== undefined) {
|
|
428
|
+
resultData = responseData.fileUrl;
|
|
429
|
+
} else {
|
|
430
|
+
resultData = responseData;
|
|
431
|
+
}
|
|
432
|
+
} else {
|
|
433
|
+
throw new Error(`响应数据格式不正确: ${typeof responseData}`);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// 验证返回的文件地址
|
|
437
|
+
if (!resultData) {
|
|
438
|
+
throw new Error(`返回的文件地址为空`);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// 格式化文件 URL
|
|
442
|
+
let fileUrl;
|
|
443
|
+
if (typeof resultData === 'string') {
|
|
444
|
+
fileUrl = resultData;
|
|
445
|
+
} else if (resultData && typeof resultData === 'object' && resultData.url) {
|
|
446
|
+
fileUrl = resultData.url;
|
|
447
|
+
}
|
|
448
|
+
successLog(`文件上传成功: ${fileName} -> ${fileUrl}`, spinner);
|
|
449
|
+
return fileUrl;
|
|
450
|
+
} catch (error) {
|
|
451
|
+
errorLog(`上传文件失败: ${error.message}, 文件路径: ${filePath}`, spinner);
|
|
452
|
+
|
|
453
|
+
// 输出详细的错误信息
|
|
454
|
+
if (error.response) {
|
|
455
|
+
const status = error.response.status;
|
|
456
|
+
const statusText = error.response.statusText;
|
|
457
|
+
const responseData = error.response.data;
|
|
458
|
+
const requestUrl = error.config?.url || this.uploadAPI();
|
|
459
|
+
|
|
460
|
+
errorLog(`\n========== 上传请求详情 ==========`);
|
|
461
|
+
errorLog(`请求 URL: ${requestUrl}`);
|
|
462
|
+
errorLog(`HTTP 状态码: ${status} ${statusText}`);
|
|
463
|
+
errorLog(`响应数据: ${JSON.stringify(responseData)}`);
|
|
464
|
+
errorLog(`==================================\n`);
|
|
465
|
+
|
|
466
|
+
if (status === 404) {
|
|
467
|
+
throw new Error(
|
|
468
|
+
`上传 API 不存在 (404): ${requestUrl}\n` +
|
|
469
|
+
`请检查 neo.config.js 中的 neoBaseURL 配置是否正确,或者 API 路径是否存在。\n` +
|
|
470
|
+
`当前配置的 API 路径: ${NeoCrmAPI.uploadAPI}`
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
} else if (error.request) {
|
|
474
|
+
errorLog('请求已发送但未收到响应,请检查网络连接或代理配置。');
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
throw error;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* 将构建产物上传到 NeoCRM 平台端(已废弃)
|
|
483
|
+
* 备注:已废弃,改为 保存时直接上传组件资源文件
|
|
484
|
+
*
|
|
485
|
+
* @param {object} cmpType 自定义组件名称
|
|
486
|
+
* @param {array} fileExtensions 需要上传的文件类型,默认 ['.js', '.css', '.zip']
|
|
487
|
+
*/
|
|
488
|
+
async publish2oss(cmpType, fileExtensions = ['.js', '.css', '.zip']) {
|
|
489
|
+
if (!cmpType) {
|
|
490
|
+
errorLog(`自定义组件名称不能为空: ${cmpType}`);
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
if (!fs.existsSync(this.assetsRoot)) {
|
|
494
|
+
errorLog(`未找到自定义组件资源目录: ${this.assetsRoot}`);
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// 当前组件信息
|
|
499
|
+
const curCmpInfo = {
|
|
500
|
+
cmpType
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
const files = fs.readdirSync(this.assetsRoot); // 读取构建目录下的所有文件
|
|
504
|
+
|
|
505
|
+
// 并行上传所有指定类型的文件
|
|
506
|
+
const uploadPromises = files.map(async (file) => {
|
|
507
|
+
const filePath = path.join(this.assetsRoot, file);
|
|
508
|
+
// 获取文件状态
|
|
509
|
+
const fileStat = fs.statSync(filePath);
|
|
510
|
+
// 检查文件扩展名
|
|
511
|
+
// const fileExt = path.extname(file);
|
|
512
|
+
const fileInfo = path.parse(filePath);
|
|
513
|
+
if (fileStat.isFile() && fileExtensions.includes(fileInfo.ext)) {
|
|
514
|
+
let widgetName = _.camelCase(cmpType);
|
|
515
|
+
|
|
516
|
+
if (file.indexOf(widgetName) < 0) {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
try {
|
|
521
|
+
// 上传文件
|
|
522
|
+
const fileUrl = await this.uploadFile(filePath);
|
|
523
|
+
|
|
524
|
+
if (file.indexOf('Model') > -1) {
|
|
525
|
+
curCmpInfo.modelAsset = fileUrl;
|
|
526
|
+
} else if (file.endsWith('.css')) {
|
|
527
|
+
curCmpInfo.cssAsset = fileUrl;
|
|
528
|
+
} else if (file.endsWith('.zip')) {
|
|
529
|
+
curCmpInfo.codeLib = fileUrl;
|
|
530
|
+
} else {
|
|
531
|
+
curCmpInfo.asset = fileUrl;
|
|
532
|
+
}
|
|
533
|
+
} catch (error) {
|
|
534
|
+
errorLog(`文件上传失败(${file}):\n`);
|
|
535
|
+
process.exit(1);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
await Promise.all(uploadPromises);
|
|
541
|
+
|
|
542
|
+
if (curCmpInfo && curCmpInfo.cmpType) {
|
|
543
|
+
console.info('上传至 OSS 的文件信息:\n', curCmpInfo);
|
|
544
|
+
// 更新发布日志
|
|
545
|
+
updatePublishLog$1(curCmpInfo);
|
|
546
|
+
}
|
|
547
|
+
return curCmpInfo;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* 处理组件构建产物
|
|
552
|
+
*
|
|
553
|
+
* @param {object} cmpType 自定义组件名称
|
|
554
|
+
* @param {array} fileExtensions 需要处理的文件类型,默认 ['.js', '.css', '.zip']
|
|
555
|
+
*/
|
|
556
|
+
async getCmpAssets(cmpType, fileExtensions = ['.js', '.css', '.zip']) {
|
|
557
|
+
if (!cmpType) {
|
|
558
|
+
errorLog(`自定义组件名称不能为空: ${cmpType}`);
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
if (!fs.existsSync(this.assetsRoot)) {
|
|
562
|
+
errorLog(`未找到自定义组件资源目录: ${this.assetsRoot}`);
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// 当前组件信息
|
|
567
|
+
const curCmpInfo = {
|
|
568
|
+
cmpType
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
const files = fs.readdirSync(this.assetsRoot); // 读取构建目录下的所有文件
|
|
572
|
+
|
|
573
|
+
// 处理构建产物目录下所有指定类型的文件
|
|
574
|
+
files.forEach((file) => {
|
|
575
|
+
const filePath = path.join(this.assetsRoot, file);
|
|
576
|
+
// 获取文件状态
|
|
577
|
+
const fileStat = fs.statSync(filePath);
|
|
578
|
+
// 检查文件扩展名
|
|
579
|
+
// const fileExt = path.extname(file);
|
|
580
|
+
const fileInfo = path.parse(filePath);
|
|
581
|
+
if (fileStat.isFile() && fileExtensions.includes(fileInfo.ext)) {
|
|
582
|
+
let widgetName = _.camelCase(cmpType);
|
|
583
|
+
|
|
584
|
+
if (file.indexOf(widgetName) < 0) {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// 检查文件大小
|
|
589
|
+
const maxSize = 5 * 1024 * 1024; // 默认 5MB
|
|
590
|
+
if (fileStat.size > maxSize) {
|
|
591
|
+
const sizeMB = (fileStat.size / 1024 / 1024).toFixed(2);
|
|
592
|
+
const maxSizeMB = (maxSize / 1024 / 1024).toFixed(2);
|
|
593
|
+
throw new Error(`${file} 文件大小超过限制: ${sizeMB}MB > ${maxSizeMB}MB`);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const fileContent = fs.createReadStream(filePath);
|
|
597
|
+
const fileInfo = {
|
|
598
|
+
fileContent,
|
|
599
|
+
fileName: file,
|
|
600
|
+
fileSize: fileStat.size,
|
|
601
|
+
};
|
|
602
|
+
if (file.indexOf('Model') > -1) {
|
|
603
|
+
// 使用文件流而不是读取整个文件到内存(对大文件更友好)
|
|
604
|
+
curCmpInfo.modelAssetFile = fileInfo;
|
|
605
|
+
} else if (file.endsWith('.css')) {
|
|
606
|
+
curCmpInfo.cssAssetFile = fileInfo;
|
|
607
|
+
} else if (file.endsWith('.zip')) {
|
|
608
|
+
curCmpInfo.codeLibFile = fileInfo;
|
|
609
|
+
} else {
|
|
610
|
+
curCmpInfo.assetFile = fileInfo;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
return curCmpInfo;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* 更新自定义组件
|
|
620
|
+
* @param {object} componentData 组件数据
|
|
621
|
+
* @returns {Promise<object>} 更新结果
|
|
622
|
+
*/
|
|
623
|
+
async updateCustomComponent(componentData) {
|
|
624
|
+
// 确保 token 有效
|
|
625
|
+
const token = await this.ensureValidToken();
|
|
626
|
+
|
|
627
|
+
if (!componentData) {
|
|
628
|
+
throw new Error('componentData 不能为空');
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const spinner = ora('正在保存自定义组件信息...').start();
|
|
632
|
+
|
|
633
|
+
try {
|
|
634
|
+
const fullUpdateAPI = this.saveAPI();
|
|
635
|
+
|
|
636
|
+
// 创建 FormData
|
|
637
|
+
const formData = new FormData();
|
|
638
|
+
// 处理 componentData 中的 fileInfo 文件
|
|
639
|
+
if (componentData.assetFile) {
|
|
640
|
+
formData.append('assetFile', componentData.assetFile.fileContent, componentData.assetFile.fileName);
|
|
641
|
+
}
|
|
642
|
+
if (componentData.modelAssetFile) {
|
|
643
|
+
formData.append('modelAssetFile', componentData.modelAssetFile.fileContent, componentData.modelAssetFile.fileName);
|
|
644
|
+
}
|
|
645
|
+
if (componentData.cssAssetFile) {
|
|
646
|
+
formData.append('cssAssetFile', componentData.cssAssetFile.fileContent, componentData.cssAssetFile.fileName);
|
|
647
|
+
}
|
|
648
|
+
if (componentData.codeLibFile) {
|
|
649
|
+
formData.append('codeLibFile', componentData.codeLibFile.fileContent, componentData.codeLibFile.fileName);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// 将 componentData 中组件基本信息
|
|
653
|
+
formData.append('component', JSON.stringify(_.omit(componentData, ['assetFile', 'modelAssetFile', 'cssAssetFile', 'codeLibFile'])));
|
|
654
|
+
|
|
655
|
+
const response = await axios.post(fullUpdateAPI, formData, {
|
|
656
|
+
headers: {
|
|
657
|
+
Authorization: `Bearer ${token}`,
|
|
658
|
+
'xsy-inner-source': 'bff',
|
|
659
|
+
// 无需手动设置 Content-Type,formData.getHeaders() 会自动设置正确的 multipart/form-data 和 boundary
|
|
660
|
+
...formData.getHeaders()
|
|
661
|
+
},
|
|
662
|
+
timeout: 120000, // 默认 60 秒
|
|
663
|
+
// 确保 axios 正确处理大文件和流
|
|
664
|
+
maxContentLength: Infinity, // 不限制响应内容长度
|
|
665
|
+
maxBodyLength: Infinity // 不限制请求体长度(适用于文件上传)
|
|
666
|
+
});
|
|
667
|
+
const { code, message } = response.data || {};
|
|
668
|
+
|
|
669
|
+
if (code && code !== 200) {
|
|
670
|
+
errorLog(`保存自定义组件信息失败: ${response.data.message || '未知错误'}`);
|
|
671
|
+
process.exit(1);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
spinner.clear();
|
|
675
|
+
spinner.stop();
|
|
676
|
+
} catch (error) {
|
|
677
|
+
if (error.message) {
|
|
678
|
+
errorLog(`保存自定义组件信息失败: ${error.message}`, spinner);
|
|
679
|
+
} else {
|
|
680
|
+
errorLog(`保存自定义组件信息失败: ${JSON.stringify(error)}`, spinner);
|
|
681
|
+
}
|
|
682
|
+
process.exit(1);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* 获取线上自定义组件列表
|
|
688
|
+
* @returns {Promise<array>} 自定义组件列表
|
|
689
|
+
*/
|
|
690
|
+
async getCustomCmpList() {
|
|
691
|
+
// 确保 token 有效
|
|
692
|
+
const token = await this.ensureValidToken();
|
|
693
|
+
|
|
694
|
+
/*
|
|
695
|
+
// 如果自定义组件列表已存在,则直接返回
|
|
696
|
+
if (this.cmpList && this.cmpList.length > 0) {
|
|
697
|
+
return this.cmpList;
|
|
698
|
+
}
|
|
699
|
+
*/
|
|
700
|
+
|
|
701
|
+
try {
|
|
702
|
+
let queryAllAPI = this.buildFullUrl(NeoCrmAPI.queryAll);
|
|
703
|
+
queryAllAPI += `&fields=${cmpFields.join(',')}`;
|
|
704
|
+
const response = await axios.get(
|
|
705
|
+
queryAllAPI,
|
|
706
|
+
{
|
|
707
|
+
headers: {
|
|
708
|
+
Authorization: `Bearer ${token}`,
|
|
709
|
+
'xsy-inner-source': 'bff',
|
|
710
|
+
'Content-Type': 'application/json'
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
);
|
|
714
|
+
const { code, message } = response.data || {};
|
|
715
|
+
|
|
716
|
+
if (code && code !== 200) {
|
|
717
|
+
errorLog(`获取自定义组件列表失败: ${message || '未知错误'}`);
|
|
718
|
+
process.exit(1);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
this.updateCustomCmpList(response.data.data || []);
|
|
722
|
+
} catch (error) {
|
|
723
|
+
if (error.message) {
|
|
724
|
+
errorLog(`获取自定义组件列表失败: ${error.message}`);
|
|
725
|
+
} else {
|
|
726
|
+
errorLog(`响应数据: ${JSON.stringify(error)}`);
|
|
727
|
+
}
|
|
728
|
+
process.exit(1);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
return this.cmpList || [];
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* 获取自定义组件源码文件地址
|
|
736
|
+
* @param {string} cmpType 自定义组件名称
|
|
737
|
+
* @returns {string} 源码文件地址
|
|
738
|
+
*/
|
|
739
|
+
getCodeLibByCmpType(cmpType) {
|
|
740
|
+
if (!cmpType) {
|
|
741
|
+
return null;
|
|
742
|
+
} return this.buildFullUrl(NeoCrmAPI.getCodeLibAPI(cmpType));
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* 删除自定义组件
|
|
749
|
+
* @param {*} cmpType 自定义组件类型
|
|
750
|
+
* @returns {Promise<object>} 删除结果
|
|
751
|
+
*/
|
|
752
|
+
async deleteCmp(cmpType) {
|
|
753
|
+
// 确保 token 有效
|
|
754
|
+
const token = await this.ensureValidToken();
|
|
755
|
+
|
|
756
|
+
if (!cmpType) {
|
|
757
|
+
errorLog('自定义组件名称不能为空。');
|
|
758
|
+
process.exit(1);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
let fullDeleteAPI = this.buildFullUrl(NeoCrmAPI.delete);
|
|
762
|
+
fullDeleteAPI += `/${cmpType}`;
|
|
763
|
+
|
|
764
|
+
const response = await axios.delete(fullDeleteAPI, {
|
|
765
|
+
headers: {
|
|
766
|
+
Authorization: `Bearer ${token}`,
|
|
767
|
+
'xsy-inner-source': 'bff',
|
|
768
|
+
'Content-Type': 'application/json'
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
const { code, message } = response.data || {};
|
|
772
|
+
|
|
773
|
+
if (code && code !== 200) {
|
|
774
|
+
errorLog(`删除自定义组件失败: ${message || '未知错误'}`);
|
|
775
|
+
process.exit(1);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
return response.data;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// 获取指定框架的自定义组件列表
|
|
782
|
+
getCmpListByFramework(framework) {
|
|
783
|
+
if (!framework) {
|
|
784
|
+
return this.cmpList;
|
|
785
|
+
}
|
|
786
|
+
const curFramework = getFramework(framework);
|
|
787
|
+
return this.cmpList.filter((cmp) => cmp.framework === curFramework);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// 获取自定义组件信息
|
|
791
|
+
getCmpInfoByCmpType(cmpType) {
|
|
792
|
+
if (!cmpType) {
|
|
793
|
+
return null;
|
|
794
|
+
}
|
|
795
|
+
return this.cmpInfoMap[cmpType] || null;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// 更新自定义组件 Map
|
|
799
|
+
updateCustomCmpList(cmpList) {
|
|
800
|
+
if (!cmpList || !Array.isArray(cmpList)) {
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
this.cmpList = cmpList;
|
|
804
|
+
cmpList.forEach((cmp) => {
|
|
805
|
+
this.cmpInfoMap[cmp.cmpType] = cmp;
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* 通用请求方法(确保 token 有效)
|
|
811
|
+
* @param {string} method HTTP 方法
|
|
812
|
+
* @param {string} api API 地址(相对或绝对路径)
|
|
813
|
+
* @param {object} options 请求选项
|
|
814
|
+
* @param {object} options.data 请求数据
|
|
815
|
+
* @param {object} options.headers 额外的请求头
|
|
816
|
+
* @param {object} options.params 查询参数
|
|
817
|
+
* @returns {Promise<object>} 响应数据
|
|
818
|
+
*/
|
|
819
|
+
async request(method, api, options = {}) {
|
|
820
|
+
// 确保 token 有效
|
|
821
|
+
const token = await this.ensureValidToken();
|
|
822
|
+
|
|
823
|
+
const { data, headers = {}, params } = options;
|
|
824
|
+
const fullAPI = this.buildFullUrl(api);
|
|
825
|
+
|
|
826
|
+
try {
|
|
827
|
+
const response = await axios({
|
|
828
|
+
method,
|
|
829
|
+
url: fullAPI,
|
|
830
|
+
data,
|
|
831
|
+
params,
|
|
832
|
+
headers: {
|
|
833
|
+
Authorization: `Bearer ${token}`,
|
|
834
|
+
'xsy-inner-source': 'bff',
|
|
835
|
+
...headers
|
|
836
|
+
}
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
if (response.data && response.data.code && response.data.code !== 200) {
|
|
840
|
+
throw new Error(`请求失败: ${response.data.message || '未知错误'}`);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
return response.data;
|
|
844
|
+
} catch (error) {
|
|
845
|
+
errorLog(`请求失败 [${method} ${api}]: ${error.message}`);
|
|
846
|
+
if (error.response) {
|
|
847
|
+
errorLog(`响应数据: ${JSON.stringify(error.response.data)}`);
|
|
848
|
+
}
|
|
849
|
+
throw error;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* GET 请求
|
|
855
|
+
* @param {string} api API 地址
|
|
856
|
+
* @param {object} options 请求选项
|
|
857
|
+
* @returns {Promise<object>} 响应数据
|
|
858
|
+
*/
|
|
859
|
+
async get(api, options = {}) {
|
|
860
|
+
return await this.request('GET', api, options);
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* POST 请求
|
|
865
|
+
* @param {string} api API 地址
|
|
866
|
+
* @param {object} options 请求选项
|
|
867
|
+
* @returns {Promise<object>} 响应数据
|
|
868
|
+
*/
|
|
869
|
+
async post(api, options = {}) {
|
|
870
|
+
return await this.request('POST', api, options);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* PUT 请求
|
|
875
|
+
* @param {string} api API 地址
|
|
876
|
+
* @param {object} options 请求选项
|
|
877
|
+
* @returns {Promise<object>} 响应数据
|
|
878
|
+
*/
|
|
879
|
+
async put(api, options = {}) {
|
|
880
|
+
return await this.request('PUT', api, options);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* DELETE 请求
|
|
885
|
+
* @param {string} api API 地址
|
|
886
|
+
* @param {object} options 请求选项
|
|
887
|
+
* @returns {Promise<object>} 响应数据
|
|
888
|
+
*/
|
|
889
|
+
async delete(api, options = {}) {
|
|
890
|
+
return await this.request('DELETE', api, options);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
neoService = NeoService;
|
|
895
|
+
return neoService;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
exports.__require = requireNeoService;
|