@cpzxrobot/sdk 1.2.91 → 1.2.93

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/index.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { DeviceGateway } from "./device_gateway";
2
+ import { PlatformInterface } from "./platform_interface";
3
+ import { WindwosMiniAppPlatform } from "./windows_platform";
2
4
  import { UserGateway } from "./user_gateway";
3
5
  import { FactoryGateway } from "./factory_gateway";
4
6
  import { TransportGateway } from "./transport_gateway";
@@ -22,6 +24,8 @@ import { QuotationGateway } from "./quotation_gateway";
22
24
  import { AiGateway } from "./ai_gateway";
23
25
  import { ConstructionGateway } from "./construction_gateway";
24
26
  import { SystemGateway } from "./system_gateway";
27
+ import { MobilePlatform } from "./mobile_platform";
28
+ import { WebPlatform } from "./web_platform";
25
29
 
26
30
  export class Cpzxrobot {
27
31
  private static factorySelectorLoaded = false;
@@ -44,24 +48,7 @@ export class Cpzxrobot {
44
48
  transport: TransportGateway;
45
49
  platformReady!: Promise<boolean>;
46
50
  messagePort!: MessagePort;
47
- _getSelectedFarmFromMiniApp!: () => any;
48
- _getSelectedUnitFromMiniApp!: () => any;
49
- _jumpToMiniApp!: (url: string) => any;
50
- setTitle!: (title: string) => void;
51
- saveBase64!: (base64: string, filename: string) => Promise<void>;
52
- saveBlob!: (blob: Blob, filename: string) => Promise<void>;
53
- scanQrcode!: () => Promise<string>;
54
- vibrate!: (time?: number) => void;
55
- showInDeviceManager!: (deviceId: any, type: string) => Promise<void>;
56
- reloadGroup!: (key: string) => void;
57
- getGeo!: () => Promise<{
58
- lat: number;
59
- lng: number;
60
- }>;
61
- setResult!: (name: string, value: any) => void;
62
- setResultAs!: (name: string, value: any, type: string) => void;
63
- setError!: (message: string) => void;
64
- setProgress!: (precentage: number) => void;
51
+ platform!: PlatformInterface;
65
52
  assistant: AssistantGateway;
66
53
  energy: EnergyGateway;
67
54
  camera: CameraGateway;
@@ -79,16 +66,18 @@ export class Cpzxrobot {
79
66
  ai: AiGateway = new AiGateway(this);
80
67
  construction: ConstructionGateway = new ConstructionGateway(this);
81
68
  system: SystemGateway = new SystemGateway(this);
82
- sseCallbacks: any;
69
+ sseCallbacks: {
70
+ [key: string]: (data: any) => void;
71
+ } = {};
83
72
 
84
73
 
85
- constructor(appCode: string) {
74
+ constructor(appCode: string, baseUrl: string, initSelectedFactory: Factory | undefined, initSelectedUnit: Unit | undefined) {
86
75
  //获取当前浏览器的域名
87
76
  this.ready = new Promise<MyAxiosInstance>((resolve, reject) => {
88
77
  this.resolveReady = resolve;
89
78
  this.rejectReady = reject;
90
79
  });
91
- this.getDomain();
80
+ this.getDomain(baseUrl, initSelectedFactory, initSelectedUnit);
92
81
  this.appCode = appCode;
93
82
  this.user = new UserGateway(this);
94
83
  this.factory = new FactoryGateway(this);
@@ -115,388 +104,29 @@ export class Cpzxrobot {
115
104
 
116
105
  initAxios(baseURL: string) {
117
106
  if (this.mode !== "miniapp_in_app") {
118
- const fetchWithAuth = async (input: RequestInfo, init?: RequestInit) => {
119
- const headers = new Headers(init?.headers);
120
- headers.set('Authorization', this.token);
121
- headers.set('Miniapp-Code', this.appCode);
122
- const response = await fetch(baseURL + input.toString(), {
123
- ...init,
124
- headers
125
- });
126
-
127
- if (!response.ok) {
128
- throw new Error(`HTTP error! status: ${response.status}`);
129
- }
130
- return response;
131
- };
132
-
133
- const streamWithAuth = async (input: RequestInfo, init?: RequestInit, fn?: Function) => {
134
- const headers = new Headers(init?.headers);
135
- headers.set('Authorization', this.token);
136
- headers.set('Miniapp-Code', this.appCode);
137
- const response = await fetch(baseURL + input.toString(), {
138
- ...init,
139
- headers
140
- });
141
-
142
- var reader = response.body!.getReader();
143
- const decoder = new TextDecoder("utf-8");
144
- let done = false;
145
- var buf: string = "";
146
- while (!done) {
147
- const { value, done } = await reader.read();
148
- if (done) {
149
- break;
150
- }
151
- const chunkValue = decoder.decode(value);
152
-
153
- //split value to lines
154
- // 将值拆分为行
155
- buf = buf + chunkValue;
156
- var i = buf.indexOf("\n");
157
- while (i > -1) {
158
- //find first \n
159
- var line = buf.substring(0, i);
160
- try {
161
- if (line === "[DONE]") {
162
- break;
163
- } else {
164
- // remove prefix 'data: '
165
- // 移除前缀 'data: '
166
- var data = JSON.parse(line.split(": ")[1]);
167
- //{"choices":[{"delta":{"content":" </"},"index":0}],"created":1741749068,"id":"chatcmpl-67ccb889154043f5874cbf3a64ec163e","model":"DeepSeek-R1","object":"chat.completion.chunk"}
168
- fn?.call(data);
169
- }
170
- } catch (e) {
171
- console.error("解析失败", e);
172
- }
173
- buf = buf.substring(i + 1);
174
- i = buf.indexOf("\n");
175
- }
176
- };
177
- return true;
178
- };
179
-
180
-
181
- const processQueryParams = (url: string, config?: any) => {
182
- if (config && config.params) {
183
- const flattenParams = (params: any, prefix = '') => {
184
- const result: Record<string, string> = {};
185
- Object.keys(params).forEach(key => {
186
- const value = params[key];
187
- const fullKey = prefix ? `${prefix}[${key}]` : key;
188
- if (value && typeof value === 'object' && !Array.isArray(value)) {
189
- Object.assign(result, flattenParams(value, fullKey));
190
- } else if (value) {
191
- result[fullKey] = value.toString();
192
- }
193
- });
194
- return result;
195
- };
196
- const flattened = flattenParams(config.params);
197
- url += "?" + new URLSearchParams(flattened).toString();
198
- delete config.params;
199
- }
200
- return url;
201
- }
202
-
203
- const instance = {
204
-
205
- get: async (url: string, config?: any) => {
206
- url = processQueryParams(url, config);
207
- const response = await fetchWithAuth(url, {
208
- method: 'GET',
209
- headers: config?.headers
210
- });
211
- return { data: await response.json() };
212
- },
213
- post: async (url: string, data?: any, config?: any) => {
214
- const response = await fetchWithAuth(url, {
215
- method: 'POST',
216
- headers: {
217
- 'Content-Type': 'application/json',
218
- ...config?.headers
219
- },
220
- body: JSON.stringify(data)
221
- });
222
- return { data: await response.json() };
223
- },
224
- getAndSave: async (url: string, config?: any) => {
225
- url = processQueryParams(url, config);
226
- const response = await fetchWithAuth(url, {
227
- method: 'GET',
228
- headers: config?.headers
229
- });
230
- const blob = await response.blob();
231
-
232
- // 文件名解析逻辑
233
- const contentDisposition = response.headers.get('Content-Disposition');
234
- let filename = config?.fileName ?? ""; // 默认文件名
235
-
236
- // 1. 优先使用响应头中的文件名
237
- if (filename == "") {
238
- if (contentDisposition) {
239
- const utf8FilenameMatch = contentDisposition.match(/filename\*=UTF-8''(.+)/i);
240
- if (utf8FilenameMatch) {
241
- filename = decodeURIComponent(utf8FilenameMatch[1]);
242
- } else {
243
- const filenameMatch = contentDisposition.match(/filename="?(.+?)"?(;|$)/i);
244
- if (filenameMatch) filename = filenameMatch[1];
245
- }
246
- } else {
247
- filename = "file"
248
- }
249
- }
250
107
 
251
- await this.saveBlob(blob, filename);
252
- return { data: blob };
253
- },
254
- getAndPreview: async (url: string, config?: {
255
- fileName?: string;
256
- params?: any;
257
- preview?: boolean;
258
- }) => {
259
- url = processQueryParams(url, config);
260
- return instance.getAndSave(url, config);
261
- },
262
- upload: async (url: string, option?: any) => {
263
- return new Promise<any>((resolve, reject) => {
264
- const button = document.createElement("input");
265
- button.type = "file";
266
- button.style.display = "none";
267
- button.multiple = option?.multiple || false;
268
- document.body.appendChild(button);
269
- button.click(); // 手动触发点击
270
- button.onchange = async (e: Event) => {
271
- const files = (e.target as HTMLInputElement).files;
272
- if (files && files.length > 0) {
273
- const formData = new FormData();
274
- if (option?.multiple) {
275
- for (let i = 0; i < files.length; i++) {
276
- formData.append("files[]", files[i]);
277
- }
278
- } else {
279
- formData.append(option?.["fileField"] || "file", files[0]);
280
- }
281
- for (let key in option?.["data"]) {
282
- formData.append(key, option!["data"][key]);
283
- }
284
- const response = await fetchWithAuth(url, {
285
- method: 'POST',
286
- body: formData
287
- });
288
- button.remove();
289
- resolve({ data: await response.json() });
290
- } else {
291
- button.remove();
292
- resolve({ data: { Error: "没有选择文件" } });
293
- }
294
- };
295
- });
296
- },
297
- getAsSse: (url: string, fn: any, config?: {
298
- fileName?: string;
299
- params?: any;
300
- preview?: boolean;
301
- }) => {
302
- return new Promise<any>((resolve, reject) => {
303
- streamWithAuth(url, {
304
- method: 'GET',
305
- }, fn);
306
- });
307
- }
308
- };
309
108
  // @ts-ignore
310
109
  this.axios = instance;
311
110
  }
312
111
  }
313
112
 
314
113
  _saveBlobAsBase64(blob: Blob, filename: string): Promise<void> {
315
- var that = this;
316
- return new Promise<void>((resolve, reject) => {
317
- const reader = new FileReader();
318
- reader.readAsDataURL(blob);
319
- reader.onloadend = async () => {
320
- // @ts-ignoreconst reader = new FileReader();
321
- const base64 = reader.result as string;
322
- // @ts-ignore
323
- await that.saveBase64(base64, filename);
324
- resolve();
325
- };
326
- reader.onerror = (err) => {
327
- reject(err);
328
- };
329
- });
114
+ return this.platform.saveBlob(blob, filename);
330
115
  }
331
116
 
332
117
  //获取当前浏览器的域名
333
- getDomain() {
118
+ getDomain(baseURL: string, initSelectedFactory: Factory | undefined, initSelectedUnit: Unit | undefined) {
334
119
  const domain = window.location.hostname;
335
120
  console.log("load domain ", domain);
336
121
  if (domain == "miniapp") {
337
122
  this.mode = "miniapp_in_app";
338
- // @ts-ignore
339
- this.axios = getAxiosFromMiniApp();
340
- this.platformReady = Promise.resolve(true);
341
- // @ts-ignore
342
- this._getSelectedFarmFromMiniApp = window.getSelectedFarmFromMiniApp;
343
- // @ts-ignore
344
- this._getSelectedUnitFromMiniApp = window.getSelectedUnitFromMiniApp;
345
- // @ts-ignore
346
- this._jumpToMiniApp = window.miniapp.open;
347
- // @ts-ignore
348
- this.scanQrcode = window.miniapp.scanQrcode;
349
- // @ts-ignore
350
- this.setTitle = window.miniapp.setTitle;
351
- // @ts-ignore
352
- this.saveBase64 = window.miniapp.saveBase64;
353
- // @ts-ignore
354
- this.vibrate = window.miniapp.vibrate;
355
- // @ts-ignore
356
- this.reloadGroup = window.miniapp.reloadGroup;
357
- this.saveBlob = this._saveBlobAsBase64;
358
- // @ts-ignore
359
- this.getGeo = window.miniapp.getGeo;
360
- // @ts-ignore
361
- this.setResult = window.miniapp.setResult;
362
- // @ts-ignore
363
- this.setResultAs = window.miniapp.setResultAs;
364
- // @ts-ignore
365
- this.setError = window.miniapp.setError;
366
- // @ts-ignore
367
- this.setProgress = window.miniapp.setProgress;
368
- // @ts-ignore
369
- this.showInDeviceManager = window.miniapp.showInDeviceManager;
123
+ this.platform = new WindwosMiniAppPlatform();
370
124
  } else if (
371
125
  domain == "appassets.androidplatform.net" || domain == "webc.cpzxrobot.com" ||
372
126
  this.isIosMiniApp(window.location)
373
127
  ) {
374
128
  this.mode = "miniapp_in_app";
375
- const platform: {
376
- callHandler: Function;
377
- } =
378
- // @ts-ignore
379
- window.flutter_inappwebview;
380
- var that = this;
381
- this._getSelectedFarmFromMiniApp = function () {
382
- return platform.callHandler("getSelectedFarmFromMiniApp");
383
- };
384
- this._getSelectedUnitFromMiniApp = function () {
385
- return platform.callHandler("getSelectedUnitFromMiniApp");
386
- };
387
- this._jumpToMiniApp = function (url: string) {
388
- return platform.callHandler("app.openMiniapp", url);
389
- };
390
- this.scanQrcode = function () {
391
- return platform.callHandler("app.scanQrcode");
392
- }
393
- this.getGeo = async function () {
394
- var result = await platform.callHandler("app.getGeo");
395
- if (result.error) {
396
- throw result.error;
397
- }
398
- return {
399
- lat: result.lat,
400
- lng: result.lng
401
- }
402
- };
403
- this.setTitle = function (title: string) {
404
- platform.callHandler("app.setTitle", title);
405
- };
406
- this.saveBlob = this._saveBlobAsBase64;
407
- this.saveBase64 = function (base64: string, filename: string) {
408
- return platform.callHandler("app.saveBase64", base64, filename);
409
- };
410
- this.vibrate = function (time?: number) {
411
- return platform.callHandler("app.vibrate", time);
412
- };
413
- this.showInDeviceManager = function (deviceId: number, type: string) {
414
- return platform.callHandler("app.showInDeviceManager", deviceId, type);
415
- };
416
- this.reloadGroup = function (group: string) {
417
- return platform.callHandler("app.reloadGroup", group);
418
- };
419
- this.setResult = function (name: string, value: any) {
420
- return platform.callHandler("app.setResult", name, value);
421
- };
422
- this.setResultAs = function (name: string, value: any, type: string) {
423
- return platform.callHandler("app.setResultAs", name, value, type);
424
- };
425
- this.setError = function (message: string) {
426
- return platform.callHandler("app.setError", message);
427
- };
428
- this.setProgress = function (precentage: number) {
429
- return platform.callHandler("app.setProgress", precentage);
430
- };
431
- this.axios = {
432
- get: function (url, data) {
433
- return platform.callHandler("axios_get", url, data);
434
- },
435
- post: function (url, data) {
436
- return platform.callHandler("axios_post", url, data);
437
- },
438
- getAndSave: function (url, config) {
439
- return platform.callHandler("axios_getAndSave", url, config);
440
- },
441
- getAndPreview: function (url, config) {
442
- if (config == undefined) {
443
- config = {};
444
- }
445
- if (config.preview == undefined) {
446
- config.preview = true;
447
- }
448
- return platform.callHandler("axios_getAndSave", url, config);
449
- },
450
- upload: function (url, option) {
451
- return platform.callHandler("axios_upload", url, option);
452
- },
453
- getAsSse: async function (url, fn, config?) {
454
- console.log("getAsSse", url, fn);
455
- var randomId = Math.random().toString(36).substring(2);
456
- that.sseCallbacks[randomId] = fn;
457
- await platform.callHandler("axios_getAsSse", url, config, randomId);
458
- delete that.sseCallbacks[randomId];
459
- },
460
- };
461
- if (
462
- // @ts-ignore
463
- window.flutter_inappwebview != null &&
464
- // @ts-ignore
465
- window.flutter_inappwebview.callHandler != null
466
- ) {
467
- this.platformReady = Promise.resolve(true);
468
- } else {
469
- this.platformReady = new Promise<boolean>((resolve, reject) => {
470
- window.addEventListener(
471
- "flutterInAppWebViewPlatformReady",
472
- function () {
473
- console.log("flutter android/ios platform is ready");
474
- resolve(true);
475
- }
476
- );
477
- setTimeout(() => {
478
- console.log("flutter android/ios platform is timeout");
479
- reject("timeout");
480
- }, 1500);
481
- });
482
- }
483
- window.addEventListener('message', (event) => {
484
- if (event.data == 'capturePort') {
485
- console.log("capturePort", event);
486
- if (event.ports[0] != null) {
487
- this.messagePort = event.ports[0];
488
- this.messagePort.onmessage = (event) => {
489
- if (event.data.id != undefined) {
490
- var callback = this.sseCallbacks[event.data.id];
491
- callback(event.data.data);
492
- }
493
- };
494
- }
495
- }else{
496
- console.log("message", event);
497
- }
498
-
499
- }, false);
129
+ this.platform = new MobilePlatform();
500
130
  } else {
501
131
  if (this.isLocalDomain(domain)) {
502
132
  this.mode = "dev";
@@ -506,68 +136,11 @@ export class Cpzxrobot {
506
136
  } else {
507
137
  this.mode = "miniapp_in_web";
508
138
  }
509
- this.setTitle = function (title: string) {
510
- document.title = title;
511
- };
512
- this.saveBase64 = function (base64: string, filename: string) {
513
- //decode base64 to blob
514
- const byteCharacters = atob(base64);
515
- const byteNumbers = new Array(byteCharacters.length);
516
- for (let i = 0; i < byteCharacters.length; i++) {
517
- byteNumbers[i] = byteCharacters.charCodeAt(i);
518
- }
519
- const byteArray = new Uint8Array(byteNumbers);
520
- const blob = new Blob([byteArray], {
521
- type: "application/octet-stream",
522
- });
523
- return this.saveBlob(blob, filename);
524
- };
525
- this.saveBlob = function (blob: Blob, filename: string): Promise<void> {
526
- return new Promise((resolve, reject) => {
527
- try {
528
- const url = URL.createObjectURL(blob);
529
- const a = document.createElement("a");
530
- a.href = url;
531
- a.download = filename;
532
- document.body.appendChild(a);
533
- a.click();
534
- setTimeout(() => {
535
- URL.revokeObjectURL(url);
536
- document.body.removeChild(a);
537
- resolve();
538
- }, 0);
539
- } catch (error) {
540
- reject(error);
541
- }
542
- });
543
- };
544
- this.getGeo = async function () {
545
- return new Promise((resolve, reject) => {
546
- if (navigator.geolocation) {
547
- navigator.geolocation.getCurrentPosition(
548
- (position) => {
549
- const lat = position.coords.latitude; // 获取纬度
550
- const lng = position.coords.longitude; // 获取经度
551
- // 完成位置获取,解析 Promise
552
- resolve({ lat, lng });
553
- },
554
- (error) => {
555
- console.log("获取位置信息失败:" + error.message);
556
- // 位置获取失败,拒绝 Promise
557
- reject(error);
558
- }
559
- );
560
- } else {
561
- console.log("浏览器不支持获取位置信息");
562
- // 浏览器不支持位置服务,拒绝 Promise
563
- reject(new Error("浏览器不支持获取位置信息"));
564
- }
565
- });
566
- };
567
- this.showInDeviceManager = function (deviceId: number, type: string) {
568
- return this.axios.get(`/api/v1/${type}/share/` + deviceId);
569
- };
139
+ this.platform = new WebPlatform(this.token, this.appCode, baseURL, initSelectedFactory, initSelectedUnit);
570
140
  }
141
+
142
+ this.platformReady = this.platform.ready();
143
+ this.axios = this.platform.getAxiosFromMiniApp();
571
144
  }
572
145
  isIosMiniApp(location: Location): boolean {
573
146
  //hostname is 'localhost' and has search params starting with 'mode=ios'
@@ -588,13 +161,14 @@ export class Cpzxrobot {
588
161
  break;
589
162
  }
590
163
  }
164
+
591
165
  dict(dictName: string) {
592
166
  return this.axios.get(`/api/v1/dict/${dictName}`);
593
167
  }
594
168
 
595
169
  //打开其他小程序
596
170
  openMiniApp(url: string) {
597
- this._jumpToMiniApp(url);
171
+ this.platform.jumpToMiniApp(url);
598
172
  }
599
173
 
600
174
  setAuth(auth: string, args: {
@@ -755,8 +329,8 @@ export default function (
755
329
  devAuth: string;
756
330
  baseURL: string;
757
331
  appCode: string;
758
- selectedFarm: Factory | null | undefined;
759
- selectedUnit: Unit | null | undefined;
332
+ selectedFarm: Factory | undefined;
333
+ selectedUnit: Unit | undefined;
760
334
  verbose?: boolean;
761
335
  disableFactorySelector?: boolean;
762
336
  } = {
@@ -779,7 +353,7 @@ export default function (
779
353
  // @ts-ignore
780
354
  var instance = window.single_cpzxrobot_instance;
781
355
  if (!instance) {
782
- instance = new Cpzxrobot(args.appCode);
356
+ instance = new Cpzxrobot(args.appCode, args.baseURL, args.selectedFarm, args.selectedUnit);
783
357
  // @ts-ignore
784
358
  window.single_cpzxrobot_instance = instance;
785
359
  instance.setAuth(args.devAuth, {
@@ -787,12 +361,6 @@ export default function (
787
361
  verbose: args.verbose,
788
362
  disableFactorySelector: args.disableFactorySelector ?? false,
789
363
  });
790
- if (args.selectedFarm) {
791
- instance.user.selectedFarm = args.selectedFarm;
792
- }
793
- if (args.selectedUnit) {
794
- instance.user.selectedUnit = args.selectedUnit;
795
- }
796
364
  }
797
365
  return instance;
798
366
  }