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