neo-cmp-cli 1.3.10 → 1.5.0-beta.10
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 +52 -15
- package/package.json +4 -1
- package/src/cmpUtils/createCmpByTemplate.js +50 -0
- package/src/cmpUtils/createCommonModulesCode.js +15 -15
- package/src/cmpUtils/{getCmpModelRegister.js → getCmpModelRegisterCode.js} +2 -2
- package/src/cmpUtils/{getCmpPreview.js → getCmpPreviewCode.js} +2 -2
- package/src/cmpUtils/{getCmpRegister.js → getCmpRegisterCode.js} +2 -2
- package/src/cmpUtils/getCmpTypeByDir.js +41 -0
- package/src/cmpUtils/hasCmpTypeByDir.js +11 -0
- package/src/{module → cmpUtils}/previewCmp.js +2 -2
- package/src/cmpUtils/pushCmp.js +232 -0
- package/src/config/default.config.js +14 -2
- package/src/module/index.js +174 -11
- package/src/module/main.js +96 -3
- package/src/module/neoInit.js +3 -0
- package/src/module/neoInitByCopy.js +3 -0
- package/src/neo/NeoUMDContent.js +29 -0
- package/src/neo/neoRequire.js +7 -7
- package/src/neo/neoService.js +525 -0
- package/src/neo/wrapperContent.js +2 -1
- package/src/oss/publish2oss.js +96 -96
- package/src/plugins/AddNeoRequirePlugin.js +5 -2
- package/src/projectUtils/createCmpProjectByTemplate.js +49 -0
- package/src/{cmpUtils → projectUtils}/getEntriesWithAutoRegister.js +4 -4
- package/src/template/antd-custom-cmp-template/README.md +2 -2
- package/src/template/antd-custom-cmp-template/neo.config.js +22 -14
- package/src/template/antd-custom-cmp-template/package.json +2 -2
- package/src/template/develop/neo-custom-cmp-template/neo.config.js +1 -1
- package/src/template/echarts-custom-cmp-template/README.md +2 -2
- package/src/template/echarts-custom-cmp-template/neo.config.js +19 -13
- package/src/template/echarts-custom-cmp-template/package.json +2 -2
- package/src/template/empty-cmp/index.tsx +51 -0
- package/src/template/empty-cmp/model.ts +77 -0
- package/src/template/empty-cmp/style.scss +72 -0
- package/src/template/empty-custom-cmp-template/.prettierrc.js +12 -0
- package/src/template/empty-custom-cmp-template/README.md +45 -0
- package/src/template/empty-custom-cmp-template/commitlint.config.js +59 -0
- package/src/template/empty-custom-cmp-template/neo.config.js +126 -0
- package/src/template/empty-custom-cmp-template/package.json +57 -0
- package/src/template/empty-custom-cmp-template/public/css/base.css +283 -0
- package/src/template/empty-custom-cmp-template/public/scripts/app/bluebird.js +6679 -0
- package/src/template/empty-custom-cmp-template/public/template.html +13 -0
- package/src/template/empty-custom-cmp-template/src/assets/css/common.scss +127 -0
- package/src/template/empty-custom-cmp-template/src/assets/css/mixin.scss +47 -0
- package/src/template/empty-custom-cmp-template/src/assets/img/NeoCRM.jpg +0 -0
- package/src/template/empty-custom-cmp-template/src/assets/img/custom-widget.svg +1 -0
- package/src/template/empty-custom-cmp-template/src/assets/img/favicon.png +0 -0
- package/src/template/empty-custom-cmp-template/src/assets/img/map.svg +1 -0
- package/src/template/empty-custom-cmp-template/src/components/README.md +3 -0
- package/src/template/empty-custom-cmp-template/tsconfig.json +68 -0
- package/src/template/neo-custom-cmp-template/README.md +5 -5
- package/src/template/neo-custom-cmp-template/neo.config.js +18 -29
- package/src/template/neo-custom-cmp-template/package.json +3 -4
- package/src/template/react-custom-cmp-template/.prettierrc.js +1 -1
- package/src/template/react-custom-cmp-template/README.md +2 -2
- package/src/template/react-custom-cmp-template/neo.config.js +20 -15
- package/src/template/react-custom-cmp-template/package.json +2 -2
- package/src/template/react-ts-custom-cmp-template/README.md +2 -2
- package/src/template/react-ts-custom-cmp-template/neo.config.js +19 -14
- package/src/template/react-ts-custom-cmp-template/package.json +2 -2
- package/src/template/vue2-custom-cmp-template/README.md +2 -2
- package/src/template/vue2-custom-cmp-template/neo.config.js +20 -15
- package/src/template/vue2-custom-cmp-template/package.json +2 -2
- package/src/utils/autoEntryRootDir.js +42 -0
- package/src/utils/replaceInFilesByMap.js +54 -0
- package/test/demo.js +2 -2
- /package/src/{cmpUtils → projectUtils}/getEntries.js +0 -0
- /package/src/{cmpUtils → projectUtils}/updatePublishLog.js +0 -0
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const FormData = require('form-data');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const _ = require('lodash');
|
|
6
|
+
const updatePublishLog = require('../projectUtils/updatePublishLog');
|
|
7
|
+
|
|
8
|
+
// NeoCRM 平台默认 API 配置
|
|
9
|
+
const NeoCrmAPI = {
|
|
10
|
+
neoBaseURL: 'https://crm.xiaoshouyi.com', // 平台根地址
|
|
11
|
+
tokenAPI: 'https://login.xiaoshouyi.com/auc/oauth2/token', // Token 获取接口地址
|
|
12
|
+
uploadAPI: '/rest/metadata/v3.0/ui/customComponents/actions/upload', // 文件上传接口地址
|
|
13
|
+
saveAPI: '/rest/metadata/v3.0/ui/customComponents/actions/saveOrUpdateComponent' // 创建或者保存接口地址
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Neo 平台服务类
|
|
18
|
+
* 提供 token 管理、文件上传、组件更新等功能
|
|
19
|
+
*/
|
|
20
|
+
class NeoService {
|
|
21
|
+
/**
|
|
22
|
+
* 初始化 Neo 服务
|
|
23
|
+
* @param {object} config 配置信息
|
|
24
|
+
* @param {string} config.neoBaseURL Neo 平台根地址
|
|
25
|
+
* @param {string} config.tokenAPI Token 获取接口地址
|
|
26
|
+
* @param {object} config.authConfig 授权信息
|
|
27
|
+
* @param {string} config.authConfig.client_id 客户端 ID
|
|
28
|
+
* @param {string} config.authConfig.client_secret 客户端密钥
|
|
29
|
+
* @param {string} config.authConfig.username 用户名
|
|
30
|
+
* @param {string} config.authConfig.password 密码
|
|
31
|
+
*/
|
|
32
|
+
constructor(config = {}) {
|
|
33
|
+
const { assetsRoot, neoBaseURL, tokenAPI, authConfig } = config;
|
|
34
|
+
if (!authConfig) {
|
|
35
|
+
throw new Error('authConfig 不能为空');
|
|
36
|
+
}
|
|
37
|
+
if (
|
|
38
|
+
!authConfig.client_id ||
|
|
39
|
+
!authConfig.client_secret ||
|
|
40
|
+
!authConfig.username ||
|
|
41
|
+
!authConfig.password
|
|
42
|
+
) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
'authConfig 配置不完整,需要包含 client_id、client_secret、username、password'
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this.assetsRoot = assetsRoot;
|
|
49
|
+
this.neoBaseURL = neoBaseURL || NeoCrmAPI.neoBaseURL;
|
|
50
|
+
this.tokenAPI = tokenAPI || NeoCrmAPI.tokenAPI;
|
|
51
|
+
this.authConfig = authConfig;
|
|
52
|
+
|
|
53
|
+
// Token 缓存
|
|
54
|
+
this.tokenCache = {
|
|
55
|
+
token: null,
|
|
56
|
+
expiresAt: null
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 构建完整的 API URL
|
|
62
|
+
* @param {string} url 相对或绝对 URL
|
|
63
|
+
* @returns {string} 完整的 URL
|
|
64
|
+
*/
|
|
65
|
+
buildFullUrl(url) {
|
|
66
|
+
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
67
|
+
return url;
|
|
68
|
+
}
|
|
69
|
+
return `${this.neoBaseURL}${url}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 获取文件上传接口地址
|
|
73
|
+
uploadAPI() {
|
|
74
|
+
return this.buildFullUrl(NeoCrmAPI.uploadAPI);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 获取组件信息保存接口地址
|
|
78
|
+
saveAPI() {
|
|
79
|
+
return this.buildFullUrl(NeoCrmAPI.saveAPI);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 检查 token 是否过期
|
|
84
|
+
* @returns {boolean} true 表示已过期,false 表示未过期
|
|
85
|
+
*/
|
|
86
|
+
isTokenExpired() {
|
|
87
|
+
if (!this.tokenCache.token || !this.tokenCache.expiresAt) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
// 提前 60 秒判断为过期,避免边缘情况
|
|
91
|
+
return Date.now() >= this.tokenCache.expiresAt;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 获取 token(含授权信息、租户信息)
|
|
96
|
+
* @returns {Promise<string>} token
|
|
97
|
+
*/
|
|
98
|
+
async getToken() {
|
|
99
|
+
// 检查缓存是否有效
|
|
100
|
+
if (!this.isTokenExpired()) {
|
|
101
|
+
console.info('使用缓存的 token');
|
|
102
|
+
return this.tokenCache.token;
|
|
103
|
+
}
|
|
104
|
+
console.info('获取 token...');
|
|
105
|
+
|
|
106
|
+
// 构建表单数据格式的请求参数
|
|
107
|
+
const formData = new URLSearchParams();
|
|
108
|
+
formData.append('grant_type', 'password');
|
|
109
|
+
formData.append('client_id', this.authConfig.client_id);
|
|
110
|
+
formData.append('client_secret', this.authConfig.client_secret);
|
|
111
|
+
formData.append('username', this.authConfig.username);
|
|
112
|
+
formData.append('password', this.authConfig.password);
|
|
113
|
+
|
|
114
|
+
const tokenUrl = this.buildFullUrl(this.tokenAPI);
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const response = await axios.post(tokenUrl, formData.toString(), {
|
|
118
|
+
headers: {
|
|
119
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const { access_token, expires_in } = response.data || {};
|
|
124
|
+
|
|
125
|
+
if (!access_token) {
|
|
126
|
+
console.error('\n获取 token 失败:响应中未包含 access_token', response.data);
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 缓存 token(提前 60 秒过期,避免边缘情况)
|
|
131
|
+
const expiresIn = parseInt(expires_in) || 3600; // 默认 1 小时
|
|
132
|
+
this.tokenCache = {
|
|
133
|
+
token: access_token,
|
|
134
|
+
expiresAt: Date.now() + (expiresIn - 60) * 1000
|
|
135
|
+
};
|
|
136
|
+
return access_token;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error('\n获取 token 失败:', error.message);
|
|
139
|
+
console.error('\ntoken 授权地址:', tokenUrl);
|
|
140
|
+
console.error('\ntoken 请求参数:', formData);
|
|
141
|
+
if (error.response) {
|
|
142
|
+
console.error('响应数据:', error.response.data);
|
|
143
|
+
}
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 刷新 token
|
|
150
|
+
* @returns {Promise<string>} 新的 token
|
|
151
|
+
*/
|
|
152
|
+
async refreshToken() {
|
|
153
|
+
// 清除缓存,强制获取新 token
|
|
154
|
+
this.tokenCache = {
|
|
155
|
+
token: null,
|
|
156
|
+
expiresAt: null
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
return await this.getToken();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* 确保 token 有效,如果过期则自动刷新
|
|
164
|
+
* @returns {Promise<string>} 有效的 token
|
|
165
|
+
*/
|
|
166
|
+
async ensureValidToken() {
|
|
167
|
+
if (!this.tokenCache.token) {
|
|
168
|
+
return await this.getToken();
|
|
169
|
+
} else if (this.isTokenExpired()) {
|
|
170
|
+
console.info('token 已过期,正在刷新...');
|
|
171
|
+
return await this.refreshToken();
|
|
172
|
+
}
|
|
173
|
+
return this.tokenCache.token;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* 上传文件到 Neo 平台
|
|
178
|
+
* @param {string} filePath 文件路径
|
|
179
|
+
* @param {object} options 可选配置
|
|
180
|
+
* @param {string} options.fieldName 表单字段名,默认为 'customComponentCode'
|
|
181
|
+
* @param {number} options.maxSize 最大文件大小(字节),默认 50MB
|
|
182
|
+
* @param {number} options.timeout 超时时间(毫秒),默认 60000
|
|
183
|
+
* @returns {Promise<string>} CDN 地址或文件 URL
|
|
184
|
+
*/
|
|
185
|
+
async uploadFile(filePath, options = {}) {
|
|
186
|
+
// 确保 token 有效
|
|
187
|
+
const token = await this.ensureValidToken();
|
|
188
|
+
|
|
189
|
+
// 验证文件路径
|
|
190
|
+
if (!filePath || typeof filePath !== 'string') {
|
|
191
|
+
throw new Error(`文件路径无效: ${filePath}`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 检查文件是否存在
|
|
195
|
+
if (!fs.existsSync(filePath)) {
|
|
196
|
+
throw new Error(`文件不存在: ${filePath}`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 检查文件状态
|
|
200
|
+
const fileStat = fs.statSync(filePath);
|
|
201
|
+
if (!fileStat.isFile()) {
|
|
202
|
+
throw new Error(`路径不是文件: ${filePath}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// 检查文件大小
|
|
206
|
+
const maxSize = options.maxSize || 50 * 1024 * 1024; // 默认 50MB
|
|
207
|
+
if (fileStat.size > maxSize) {
|
|
208
|
+
const sizeMB = (fileStat.size / 1024 / 1024).toFixed(2);
|
|
209
|
+
const maxSizeMB = (maxSize / 1024 / 1024).toFixed(2);
|
|
210
|
+
throw new Error(`文件大小超过限制: ${sizeMB}MB > ${maxSizeMB}MB`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (fileStat.size === 0) {
|
|
214
|
+
throw new Error(`文件为空: ${filePath}`);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const fileName = path.basename(filePath);
|
|
218
|
+
const fileSizeKB = (fileStat.size / 1024).toFixed(2);
|
|
219
|
+
console.info(`正在上传文件: ${fileName} (${fileSizeKB}KB)...`);
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
// 创建 FormData
|
|
223
|
+
const formData = new FormData();
|
|
224
|
+
const fieldName = options.fieldName || 'customComponentCode';
|
|
225
|
+
|
|
226
|
+
// 使用文件流而不是读取整个文件到内存(对大文件更友好)
|
|
227
|
+
const fileContent = fs.createReadStream(filePath);
|
|
228
|
+
|
|
229
|
+
// 追加文件到 FormData,第三个参数指定文件名
|
|
230
|
+
formData.append(fieldName, fileContent, fileName);
|
|
231
|
+
|
|
232
|
+
// 构建完整的上传 API 地址
|
|
233
|
+
const fullUploadAPI = this.uploadAPI();
|
|
234
|
+
|
|
235
|
+
// 配置请求选项
|
|
236
|
+
const timeout = options.timeout || 60000; // 默认 60 秒
|
|
237
|
+
const requestConfig = {
|
|
238
|
+
headers: {
|
|
239
|
+
Authorization: `Bearer ${token}`,
|
|
240
|
+
'xsy-inner-source': 'bff',
|
|
241
|
+
// 无需手动设置 Content-Type,formData.getHeaders() 会自动设置正确的 multipart/form-data 和 boundary
|
|
242
|
+
...formData.getHeaders()
|
|
243
|
+
},
|
|
244
|
+
timeout,
|
|
245
|
+
// 确保 axios 正确处理大文件和流
|
|
246
|
+
maxContentLength: Infinity, // 不限制响应内容长度
|
|
247
|
+
maxBodyLength: Infinity // 不限制请求体长度(适用于文件上传)
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// 发送上传请求
|
|
251
|
+
const response = await axios.post(fullUploadAPI, formData, requestConfig);
|
|
252
|
+
|
|
253
|
+
// 处理响应数据
|
|
254
|
+
let resultData;
|
|
255
|
+
const responseData = response.data;
|
|
256
|
+
|
|
257
|
+
// 检查响应状态码
|
|
258
|
+
if (response.status !== 200 && response.status !== 201) {
|
|
259
|
+
throw new Error(`上传失败: HTTP ${response.status}`);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// 处理不同的响应格式
|
|
263
|
+
if (typeof responseData === 'string') {
|
|
264
|
+
// 如果响应是字符串,直接使用
|
|
265
|
+
resultData = responseData.trim();
|
|
266
|
+
} else if (responseData && typeof responseData === 'object') {
|
|
267
|
+
// 检查是否有错误码
|
|
268
|
+
if (responseData.code !== undefined && responseData.code !== 200 && responseData.code !== 0) {
|
|
269
|
+
const errorMsg = responseData.message || responseData.msg || '未知错误';
|
|
270
|
+
throw new Error(`上传失败: ${errorMsg} (code: ${responseData.code})`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// 提取数据
|
|
274
|
+
if (responseData.data !== undefined) {
|
|
275
|
+
resultData = responseData.data;
|
|
276
|
+
} else if (responseData.url !== undefined) {
|
|
277
|
+
resultData = responseData.url;
|
|
278
|
+
} else if (responseData.fileUrl !== undefined) {
|
|
279
|
+
resultData = responseData.fileUrl;
|
|
280
|
+
} else {
|
|
281
|
+
resultData = responseData;
|
|
282
|
+
}
|
|
283
|
+
} else {
|
|
284
|
+
throw new Error(`响应数据格式不正确: ${typeof responseData}`);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// 验证返回的文件地址
|
|
288
|
+
if (!resultData) {
|
|
289
|
+
throw new Error(`返回的文件地址为空`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// 格式化文件 URL
|
|
293
|
+
let fileUrl;
|
|
294
|
+
if (typeof resultData === 'string') {
|
|
295
|
+
fileUrl = resultData;
|
|
296
|
+
} else if (resultData && typeof resultData === 'object' && resultData.url) {
|
|
297
|
+
fileUrl = resultData.url;
|
|
298
|
+
}
|
|
299
|
+
console.info(`\n文件上传成功: ${fileName} -> ${fileUrl}`);
|
|
300
|
+
return fileUrl;
|
|
301
|
+
} catch (error) {
|
|
302
|
+
console.error(`\n上传文件失败: ${error.message},`);
|
|
303
|
+
console.error(`文件路径: ${filePath}。\n`);
|
|
304
|
+
|
|
305
|
+
// 输出详细的错误信息
|
|
306
|
+
if (error.response) {
|
|
307
|
+
const status = error.response.status;
|
|
308
|
+
const statusText = error.response.statusText;
|
|
309
|
+
const responseData = error.response.data;
|
|
310
|
+
const requestUrl = error.config?.url || this.uploadAPI();
|
|
311
|
+
|
|
312
|
+
console.error(`\n========== 上传请求详情 ==========`);
|
|
313
|
+
console.error(`请求 URL: ${requestUrl}`);
|
|
314
|
+
console.error(`HTTP 状态码: ${status} ${statusText}`);
|
|
315
|
+
console.error(`响应数据:`, responseData);
|
|
316
|
+
console.error(`==================================\n`);
|
|
317
|
+
|
|
318
|
+
if (status === 404) {
|
|
319
|
+
throw new Error(
|
|
320
|
+
`上传 API 不存在 (404): ${requestUrl}\n` +
|
|
321
|
+
`请检查 neo.config.js 中的 neoBaseURL 配置是否正确,或者 API 路径是否存在。\n` +
|
|
322
|
+
`当前配置的 API 路径: ${NeoCrmAPI.uploadAPI}`
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
} else if (error.request) {
|
|
326
|
+
console.error('请求已发送但未收到响应,请检查网络连接或代理配置。');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
throw error;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* 将构建产物上传到 NeoCRM 平台端
|
|
335
|
+
*
|
|
336
|
+
* @param {object} cmpType 自定义组件名称
|
|
337
|
+
* @param {array} fileExtensions 需要上传的文件类型,默认 ['.js', '.css']
|
|
338
|
+
*/
|
|
339
|
+
async publish2oss(cmpType, fileExtensions = ['.js', '.css']) {
|
|
340
|
+
if (!cmpType) {
|
|
341
|
+
console.error(`自定义组件名称不能为空: ${cmpType}`);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
if (!fs.existsSync(this.assetsRoot)) {
|
|
345
|
+
console.error(`未找到自定义组件资源目录: ${this.assetsRoot}`);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
// 当前组件信息
|
|
349
|
+
const curCmpInfo = {
|
|
350
|
+
cmpType
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
const files = fs.readdirSync(this.assetsRoot); // 读取构建目录下的所有文件
|
|
354
|
+
|
|
355
|
+
// 并行上传所有指定类型的文件
|
|
356
|
+
const uploadPromises = files.map(async (file) => {
|
|
357
|
+
const filePath = path.join(this.assetsRoot, file);
|
|
358
|
+
// 获取文件状态
|
|
359
|
+
const fileStat = fs.statSync(filePath);
|
|
360
|
+
// 检查文件扩展名
|
|
361
|
+
// const fileExt = path.extname(file);
|
|
362
|
+
const fileInfo = path.parse(filePath);
|
|
363
|
+
if (fileStat.isFile() && fileExtensions.includes(fileInfo.ext)) {
|
|
364
|
+
let widgetName = _.camelCase(cmpType);
|
|
365
|
+
|
|
366
|
+
if (file.indexOf(widgetName) < 0) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
try {
|
|
371
|
+
// 上传文件
|
|
372
|
+
const fileUrl = await this.uploadFile(filePath);
|
|
373
|
+
|
|
374
|
+
if (file.indexOf('Model') > -1) {
|
|
375
|
+
curCmpInfo.modelAsset = fileUrl;
|
|
376
|
+
} else if (file.endsWith('.css')) {
|
|
377
|
+
curCmpInfo.cssAsset = fileUrl;
|
|
378
|
+
} else {
|
|
379
|
+
curCmpInfo.asset = fileUrl;
|
|
380
|
+
}
|
|
381
|
+
} catch (error) {
|
|
382
|
+
console.error(`文件上传失败(${file}):\n`);
|
|
383
|
+
process.exit(1);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
await Promise.all(uploadPromises);
|
|
389
|
+
|
|
390
|
+
if (curCmpInfo && curCmpInfo.cmpType) {
|
|
391
|
+
console.info('上传至 OSS 的文件信息:\n', curCmpInfo);
|
|
392
|
+
// 更新发布日志
|
|
393
|
+
updatePublishLog(curCmpInfo);
|
|
394
|
+
}
|
|
395
|
+
return curCmpInfo;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* 更新自定义组件
|
|
400
|
+
* @param {object} componentData 组件数据
|
|
401
|
+
* @returns {Promise<object>} 更新结果
|
|
402
|
+
*/
|
|
403
|
+
async updateCustomComponent(componentData) {
|
|
404
|
+
// 确保 token 有效
|
|
405
|
+
const token = await this.ensureValidToken();
|
|
406
|
+
|
|
407
|
+
if (!componentData) {
|
|
408
|
+
throw new Error('componentData 不能为空');
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
console.info('正在更新自定义组件...');
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
const fullUpdateAPI = this.saveAPI();
|
|
415
|
+
const response = await axios.post(fullUpdateAPI, componentData, {
|
|
416
|
+
headers: {
|
|
417
|
+
Authorization: `Bearer ${token}`,
|
|
418
|
+
'xsy-inner-source': 'bff',
|
|
419
|
+
'Content-Type': 'application/json'
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
const {code, message} = response.data || {};
|
|
423
|
+
|
|
424
|
+
if (code && code !== 200) {
|
|
425
|
+
throw new Error(`更新组件失败: ${response.data.message || '未知错误'}`);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
console.info(message ? `组件更新成功: ${message}。` : '组件更新成功。');
|
|
430
|
+
} catch (error) {
|
|
431
|
+
if (error.message) {
|
|
432
|
+
console.error('更新组件失败:', error.message);
|
|
433
|
+
} else {
|
|
434
|
+
console.error('响应数据:', error);
|
|
435
|
+
}
|
|
436
|
+
process.exit(1);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* 通用请求方法(确保 token 有效)
|
|
442
|
+
* @param {string} method HTTP 方法
|
|
443
|
+
* @param {string} api API 地址(相对或绝对路径)
|
|
444
|
+
* @param {object} options 请求选项
|
|
445
|
+
* @param {object} options.data 请求数据
|
|
446
|
+
* @param {object} options.headers 额外的请求头
|
|
447
|
+
* @param {object} options.params 查询参数
|
|
448
|
+
* @returns {Promise<object>} 响应数据
|
|
449
|
+
*/
|
|
450
|
+
async request(method, api, options = {}) {
|
|
451
|
+
// 确保 token 有效
|
|
452
|
+
const token = await this.ensureValidToken();
|
|
453
|
+
|
|
454
|
+
const { data, headers = {}, params } = options;
|
|
455
|
+
const fullAPI = this.buildFullUrl(api);
|
|
456
|
+
|
|
457
|
+
try {
|
|
458
|
+
const response = await axios({
|
|
459
|
+
method,
|
|
460
|
+
url: fullAPI,
|
|
461
|
+
data,
|
|
462
|
+
params,
|
|
463
|
+
headers: {
|
|
464
|
+
Authorization: `Bearer ${token}`,
|
|
465
|
+
'xsy-inner-source': 'bff',
|
|
466
|
+
...headers
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
if (response.data && response.data.code && response.data.code !== 200) {
|
|
471
|
+
throw new Error(`请求失败: ${response.data.message || '未知错误'}`);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
return response.data;
|
|
475
|
+
} catch (error) {
|
|
476
|
+
console.error(`请求失败 [${method} ${api}]:`, error.message);
|
|
477
|
+
if (error.response) {
|
|
478
|
+
console.error('响应数据:', error.response.data);
|
|
479
|
+
}
|
|
480
|
+
throw error;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* GET 请求
|
|
486
|
+
* @param {string} api API 地址
|
|
487
|
+
* @param {object} options 请求选项
|
|
488
|
+
* @returns {Promise<object>} 响应数据
|
|
489
|
+
*/
|
|
490
|
+
async get(api, options = {}) {
|
|
491
|
+
return await this.request('GET', api, options);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* POST 请求
|
|
496
|
+
* @param {string} api API 地址
|
|
497
|
+
* @param {object} options 请求选项
|
|
498
|
+
* @returns {Promise<object>} 响应数据
|
|
499
|
+
*/
|
|
500
|
+
async post(api, options = {}) {
|
|
501
|
+
return await this.request('POST', api, options);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* PUT 请求
|
|
506
|
+
* @param {string} api API 地址
|
|
507
|
+
* @param {object} options 请求选项
|
|
508
|
+
* @returns {Promise<object>} 响应数据
|
|
509
|
+
*/
|
|
510
|
+
async put(api, options = {}) {
|
|
511
|
+
return await this.request('PUT', api, options);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* DELETE 请求
|
|
516
|
+
* @param {string} api API 地址
|
|
517
|
+
* @param {object} options 请求选项
|
|
518
|
+
* @returns {Promise<object>} 响应数据
|
|
519
|
+
*/
|
|
520
|
+
async delete(api, options = {}) {
|
|
521
|
+
return await this.request('DELETE', api, options);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
module.exports = NeoService;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 注入 neoRequire 函数
|
|
3
3
|
* 备注:用于实现和 Neo 平台共享依赖
|
|
4
|
+
* 特别说明:这个文件只是用于说明 AddNeoRequirePlugin 中会插入的代码内容,实际内容内置在 AddNeoRequirePlugin 方法中。
|
|
4
5
|
*/
|
|
5
6
|
(function(NeoCustomCmpFileFactory) {
|
|
6
7
|
if (!window.neoRequire) {
|
|
@@ -12,4 +13,4 @@
|
|
|
12
13
|
* 这里放自定义组件相关内容代码
|
|
13
14
|
* 备注: 自定义组件代码中的 require 函数 已被替换成 neoRequire 函数(require === neoRequire)。
|
|
14
15
|
*/
|
|
15
|
-
});
|
|
16
|
+
});
|