@tarojs/plugin-platform-harmony-ets 4.0.0-beta.111 → 4.0.0-beta.112

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.
@@ -9,11 +9,13 @@
9
9
  // ❌ wx.saveImageToPhotosAlbum(Object object) api 9+ HarmonyOS不支持
10
10
  // ❌ wx.previewImage(Object object) api 9+ HarmonyOS不支持
11
11
 
12
+ import fs from '@ohos.file.fs'
12
13
  import picker from '@ohos.file.picker'
13
14
  import image from '@ohos.multimedia.image'
14
15
  import { isNull } from '@tarojs/shared'
15
16
 
16
- import { callAsyncFail, callAsyncSuccess, temporarilyNotSupport, validateParams } from '../../utils'
17
+ import { callAsyncFail, callAsyncSuccess, requestPermissions, temporarilyNotSupport, validateParams } from '../../utils'
18
+ import { READ_IMAGEVIDEO_PERMISSIONS } from '../../utils/permissions'
17
19
 
18
20
  import type Taro from '@tarojs/taro/types'
19
21
 
@@ -22,12 +24,21 @@ interface IPackingOptionOHOS {
22
24
  quality: number
23
25
  }
24
26
 
27
+ interface IChooseImageData {
28
+ tempFilePaths?: string[]
29
+
30
+ tempFiles?: {
31
+ path: string
32
+ size: number
33
+ }
34
+ }
35
+
25
36
  const getImageInfoSchema = {
26
- url: 'String'
37
+ src: 'String'
27
38
  }
28
39
 
29
40
  const compressImageSchema = {
30
- url: 'String'
41
+ src: 'String'
31
42
  }
32
43
 
33
44
  const chooseImageSchema = {
@@ -60,61 +71,155 @@ export const getImageInfo: typeof Taro.getImageInfo = function (options) {
60
71
  })
61
72
  }
62
73
 
63
- export const compressImage: typeof Taro.compressImage = function (options) {
64
- return new Promise((resolve, reject) => {
65
- try {
66
- validateParams('compressImage', options, compressImageSchema)
67
- } catch (error) {
68
- const res = { errMsg: error.message }
69
- return callAsyncFail(reject, res, options)
70
- }
71
- const { src, quality = 80 } = options
72
74
 
73
- const source = image.createImageSource(src)
74
- if (isNull(source)) {
75
- const createImageSourceError = { errMsg: 'compressImage fail: createImageSource has failed.' }
76
- callAsyncFail(reject, createImageSourceError, options)
77
- return
78
- }
75
+ class CompressedImageInfo {
76
+ imageUri = '' // 压缩后图片保存位置的uri
77
+ imageByteLength = 0 // 压缩后图片字节长度
78
+ }
79
79
 
80
- const packer = image.createImagePacker(src)
81
- if (isNull(packer)) {
82
- const createImagePackerError = { errMsg: 'compressImage fail: createImagePacker has failed.' }
83
- callAsyncFail(reject, createImagePackerError, options)
80
+ async function saveImage(compressedImageData, compressedImageUri) {
81
+ // 定义要保存的压缩图片uri。afterCompressiona.jpeg表示压缩后的图片。
82
+ try {
83
+ const res = fs.accessSync(compressedImageUri)
84
+ if (res) {
85
+ // 如果图片afterCompressiona.jpeg已存在,则删除
86
+ fs.unlinkSync(compressedImageUri)
84
87
  }
88
+ } catch (err) {
89
+ console.error(`[Taro] saveImage Error: AccessSync failed with error message: ${err.message}, error code: ${err.code}`)
90
+ }
91
+
92
+ // 知识点:保存图片。获取最终图片压缩数据compressedImageData,保存图片。
93
+ // 压缩图片数据写入文件
94
+ const file = fs.openSync(compressedImageUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
95
+ fs.writeSync(file.fd, compressedImageData)
96
+ fs.closeSync(file)
97
+
98
+ // 获取压缩图片信息
99
+ const compressedImageInfo = new CompressedImageInfo()
100
+ compressedImageInfo.imageUri = compressedImageUri
101
+ compressedImageInfo.imageByteLength = compressedImageData.byteLength
102
+
103
+ return compressedImageInfo
104
+ }
85
105
 
86
- const packingOptionsOHOS: IPackingOptionOHOS = {
87
- // TODO:需要获取文件名后缀
88
- format: 'image/jpeg',
89
- quality: quality
90
- }
91
- packer.packing(source, packingOptionsOHOS).then((value) => {
92
- callAsyncSuccess(resolve, value, options)
93
- }).catch((error) => {
94
- callAsyncFail(reject, error, options)
106
+ export const compressImage: typeof Taro.compressImage = function (options) {
107
+ return new Promise((resolve, reject) => {
108
+ requestPermissions([READ_IMAGEVIDEO_PERMISSIONS]).then(() => {
109
+ try {
110
+ validateParams('compressImage', options, compressImageSchema)
111
+ } catch (error) {
112
+ const res = { errMsg: error.message }
113
+ return callAsyncFail(reject, res, options)
114
+ }
115
+ const { src, quality = 80 } = options
116
+ const srcAfterCompress = src.split('.').join('_after_compress.')
117
+ const file = fs.openSync(src, fs.OpenMode.READ_ONLY)
118
+
119
+ // const stat = fs.statSync(file.fd)
120
+ // console.log('[Taro] 压缩前图片的大小为:', stat.size)
121
+
122
+ const source = image.createImageSource(file.fd)
123
+ if (isNull(source)) {
124
+ const createImageSourceError = { errMsg: 'compressImage fail: createImageSource has failed.' }
125
+ callAsyncFail(reject, createImageSourceError, options)
126
+ return
127
+ }
128
+
129
+ const packer = image.createImagePacker(file.fd)
130
+ if (isNull(packer)) {
131
+ const createImagePackerError = { errMsg: 'compressImage fail: createImagePacker has failed.' }
132
+ callAsyncFail(reject, createImagePackerError, options)
133
+ }
134
+
135
+ const packingOptionsOHOS: IPackingOptionOHOS = {
136
+ // TODO:需要获取文件名后缀
137
+ format: 'image/jpeg',
138
+ quality: quality
139
+ }
140
+ packer.packing(source, packingOptionsOHOS).then((value) => {
141
+ saveImage(value, srcAfterCompress).then(result => {
142
+ callAsyncSuccess(resolve, { tempFilePath: result.imageUri }, options)
143
+ })
144
+ }).catch((error) => {
145
+ callAsyncFail(reject, error, options)
146
+ })
147
+
148
+ fs.closeSync(file)
149
+ }, (error: string) => {
150
+ const res = { errMsg: error }
151
+ return callAsyncFail(reject, res, options)
95
152
  })
96
153
  })
97
154
  }
98
155
 
99
156
  export const chooseImage: typeof Taro.chooseImage = function (options) {
100
157
  return new Promise((resolve, reject) => {
101
- try {
102
- validateParams('chooseImage', options, chooseImageSchema)
103
- } catch (error) {
104
- const res = { errMsg: error.message }
158
+ requestPermissions([READ_IMAGEVIDEO_PERMISSIONS]).then(() => {
159
+ try {
160
+ validateParams('chooseImage', options, chooseImageSchema)
161
+ } catch (error) {
162
+ const res = { errMsg: error.message }
163
+ return callAsyncFail(reject, res, options)
164
+ }
165
+
166
+ const { count = 9 } = options
167
+ const photoViewPicker = new picker.PhotoViewPicker()
168
+ let sizeType = options.sizeType
169
+
170
+ if (!sizeType || !sizeType.length) {
171
+ sizeType = ['compressed', 'original']
172
+ }
173
+
174
+ photoSelectOptions.maxSelectNumber = count // 选择媒体文件的最大数目
175
+ photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE // 过滤选择媒体文件类型为IMAGE
176
+
177
+ photoViewPicker.select(photoSelectOptions).then((photoSelectResult) => {
178
+ const result: IChooseImageData = {}
179
+
180
+ if (sizeType.includes('original')) {
181
+ const tempFiles = photoSelectResult.photoUris.map(uri => {
182
+ const file = fs.openSync(uri, fs.OpenMode.READ_ONLY)
183
+ const stat = fs.statSync(file.fd)
184
+ const size = stat.size
185
+
186
+ return {
187
+ size,
188
+ path: uri,
189
+ }
190
+ })
191
+
192
+ result.tempFiles = tempFiles
193
+ }
194
+
195
+ if (sizeType.includes('compressed')) {
196
+ const actions: Promise<string>[] = photoSelectResult.photoUris.map(uri => {
197
+ return new Promise<string>(resolve => {
198
+ compressImage({
199
+ src: uri,
200
+ success: (compressResult) => {
201
+ resolve(compressResult.tempFilePath)
202
+ }
203
+ })
204
+ })
205
+ })
206
+
207
+ Promise.all(actions).then(tempFilePaths => {
208
+ result.tempFilePaths = tempFilePaths
209
+ callAsyncSuccess(resolve, result, options)
210
+ }).catch(error => {
211
+ const res = { errMsg: error }
212
+ return callAsyncFail(reject, res, options)
213
+ })
214
+ } else {
215
+ callAsyncSuccess(resolve, result, options)
216
+ }
217
+ }).catch((error) => {
218
+ callAsyncFail(reject, error, options)
219
+ })
220
+ }, (error: string) => {
221
+ const res = { errMsg: error }
105
222
  return callAsyncFail(reject, res, options)
106
- }
107
-
108
- const { count = 9 } = options
109
- const photoViewPicker = new picker.PhotoViewPicker()
110
-
111
- photoSelectOptions.maxSelectNumber = count // 选择媒体文件的最大数目
112
- photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE // 过滤选择媒体文件类型为IMAGE
113
-
114
- photoViewPicker.select(photoSelectOptions).then((photoSelectResult) => {
115
- callAsyncSuccess(resolve, { tempFilePaths: photoSelectResult.photoUris }, options)
116
- }).catch((error) => {
117
- callAsyncFail(reject, error, options)
118
223
  })
119
224
  })
120
225
  }
@@ -1,3 +1,5 @@
1
+ import abilityAccessCtrl from '@ohos.abilityAccessCtrl'
2
+ import { Current } from '@tarojs/runtime'
1
3
  import { eventCenter } from '@tarojs/runtime/dist/runtime.esm'
2
4
 
3
5
  import { ICallbackResult, MethodHandler } from './handler'
@@ -8,6 +10,22 @@ export * from './validate'
8
10
  export { MethodHandler }
9
11
  export { noop } from '@tarojs/shared'
10
12
 
13
+ export function requestPermissions (permissions: string[]) {
14
+ return new Promise<void>((resolve, reject) => {
15
+ const context = getContext(Current?.page)
16
+ const atManager = abilityAccessCtrl.createAtManager()
17
+
18
+ atManager.requestPermissionsFromUser(context, permissions, (err, _) => {
19
+ if (err) {
20
+ // eslint-disable-next-line prefer-promise-reject-errors
21
+ reject(`[Taro] 请求用户授权 ${permissions.join('、')} 失败:${JSON.stringify(err)}`)
22
+ } else {
23
+ resolve()
24
+ }
25
+ })
26
+ })
27
+ }
28
+
11
29
  export function object2String (obj) {
12
30
  let str = ''
13
31
  for (const item in obj) {
@@ -0,0 +1 @@
1
+ export const READ_IMAGEVIDEO_PERMISSIONS = 'ohos.permission.READ_IMAGEVIDEO'
@@ -89,6 +89,8 @@ export function isMaxWidthView (node: TaroElement) {
89
89
  }
90
90
 
91
91
  export function getNormalAttributes (node: TaroElement, initStyle?: HarmonyStyle): HarmonyStyle {
92
+ if (!node) return {}
93
+
92
94
  const hmStyle = node.hmStyle
93
95
 
94
96
  if (!hmStyle) return {}
@@ -1,6 +1,7 @@
1
1
  import { isFunction, isString, isArray, isObject, isNull, isNumber, isUndefined, queryToJson, PLATFORM_TYPE, singleQuote, internalComponents } from '@tarojs/shared';
2
2
  import _display from '@ohos.display';
3
3
  import { Current, window, eventSource, hooks, document as document$1, getPageScrollerOrNode, findChildNodeWithDFS, setNodeEventCallbackAndTriggerComponentUpdate, AREA_CHANGE_EVENT_NAME, disconnectEvent, VISIBLE_CHANGE_EVENT_NAME, getCurrentInstance } from '@tarojs/runtime';
4
+ import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
4
5
  import { eventCenter, Events, History } from '@tarojs/runtime/dist/runtime.esm';
5
6
  import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant';
6
7
  import deviceInfo from '@ohos.deviceInfo';
@@ -22,6 +23,7 @@ import app from '@system.app';
22
23
  import file from '@system.file';
23
24
  import geoLocationManager from '@ohos.geoLocationManager';
24
25
  import mediaLibrary from '@ohos.multimedia.mediaLibrary';
26
+ import fs from '@ohos.file.fs';
25
27
  import picker from '@ohos.file.picker';
26
28
  import image from '@ohos.multimedia.image';
27
29
  import request$1 from '@ohos.request';
@@ -174,6 +176,21 @@ const validateParams = function (name, params, schema) {
174
176
  }
175
177
  };
176
178
 
179
+ function requestPermissions(permissions) {
180
+ return new Promise((resolve, reject) => {
181
+ const context = getContext(Current === null || Current === void 0 ? void 0 : Current.page);
182
+ const atManager = abilityAccessCtrl.createAtManager();
183
+ atManager.requestPermissionsFromUser(context, permissions, (err, _) => {
184
+ if (err) {
185
+ // eslint-disable-next-line prefer-promise-reject-errors
186
+ reject(`[Taro] 请求用户授权 ${permissions.join('、')} 失败:${JSON.stringify(err)}`);
187
+ }
188
+ else {
189
+ resolve();
190
+ }
191
+ });
192
+ });
193
+ }
177
194
  function object2String(obj) {
178
195
  let str = '';
179
196
  for (const item in obj) {
@@ -2262,20 +2279,16 @@ const chooseMedia = function (options) {
2262
2279
  });
2263
2280
  };
2264
2281
 
2282
+ const READ_IMAGEVIDEO_PERMISSIONS = 'ohos.permission.READ_IMAGEVIDEO';
2283
+
2265
2284
  // HarmonyOS 图片模块首批接口从API version 7开始支持。
2266
2285
  // HarmonyOS 文档链接:https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-image-0000001122977382
2267
2286
  // WX 文档链接:https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.saveImageToPhotosAlbum.html
2268
- // ✅ wx.getImageInfo(Object object) API7以上支持
2269
- // ✅ wx.compressImage(Object object) API7以上支持
2270
- // ✅ wx.chooseImage(Object object)
2271
- // ❌ wx.chooseMessageFile(Object object) HarmonyOS不支持
2272
- // ❌ wx.saveImageToPhotosAlbum(Object object) api 9+ HarmonyOS不支持
2273
- // ❌ wx.previewImage(Object object) api 9+ HarmonyOS不支持
2274
2287
  const getImageInfoSchema = {
2275
- url: 'String'
2288
+ src: 'String'
2276
2289
  };
2277
2290
  const compressImageSchema = {
2278
- url: 'String'
2291
+ src: 'String'
2279
2292
  };
2280
2293
  const chooseImageSchema = {
2281
2294
  count: 'Number'
@@ -2305,56 +2318,142 @@ const getImageInfo = function (options) {
2305
2318
  });
2306
2319
  });
2307
2320
  };
2308
- const compressImage = function (options) {
2309
- return new Promise((resolve, reject) => {
2321
+ class CompressedImageInfo {
2322
+ constructor() {
2323
+ this.imageUri = ''; // 压缩后图片保存位置的uri
2324
+ this.imageByteLength = 0; // 压缩后图片字节长度
2325
+ }
2326
+ }
2327
+ function saveImage(compressedImageData, compressedImageUri) {
2328
+ return __awaiter(this, void 0, void 0, function* () {
2329
+ // 定义要保存的压缩图片uri。afterCompressiona.jpeg表示压缩后的图片。
2310
2330
  try {
2311
- validateParams('compressImage', options, compressImageSchema);
2331
+ const res = fs.accessSync(compressedImageUri);
2332
+ if (res) {
2333
+ // 如果图片afterCompressiona.jpeg已存在,则删除
2334
+ fs.unlinkSync(compressedImageUri);
2335
+ }
2312
2336
  }
2313
- catch (error) {
2314
- const res = { errMsg: error.message };
2337
+ catch (err) {
2338
+ console.error(`[Taro] saveImage Error: AccessSync failed with error message: ${err.message}, error code: ${err.code}`);
2339
+ }
2340
+ // 知识点:保存图片。获取最终图片压缩数据compressedImageData,保存图片。
2341
+ // 压缩图片数据写入文件
2342
+ const file = fs.openSync(compressedImageUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
2343
+ fs.writeSync(file.fd, compressedImageData);
2344
+ fs.closeSync(file);
2345
+ // 获取压缩图片信息
2346
+ const compressedImageInfo = new CompressedImageInfo();
2347
+ compressedImageInfo.imageUri = compressedImageUri;
2348
+ compressedImageInfo.imageByteLength = compressedImageData.byteLength;
2349
+ return compressedImageInfo;
2350
+ });
2351
+ }
2352
+ const compressImage = function (options) {
2353
+ return new Promise((resolve, reject) => {
2354
+ requestPermissions([READ_IMAGEVIDEO_PERMISSIONS]).then(() => {
2355
+ try {
2356
+ validateParams('compressImage', options, compressImageSchema);
2357
+ }
2358
+ catch (error) {
2359
+ const res = { errMsg: error.message };
2360
+ return callAsyncFail(reject, res, options);
2361
+ }
2362
+ const { src, quality = 80 } = options;
2363
+ const srcAfterCompress = src.split('.').join('_after_compress.');
2364
+ const file = fs.openSync(src, fs.OpenMode.READ_ONLY);
2365
+ // const stat = fs.statSync(file.fd)
2366
+ // console.log('[Taro] 压缩前图片的大小为:', stat.size)
2367
+ const source = image.createImageSource(file.fd);
2368
+ if (isNull(source)) {
2369
+ const createImageSourceError = { errMsg: 'compressImage fail: createImageSource has failed.' };
2370
+ callAsyncFail(reject, createImageSourceError, options);
2371
+ return;
2372
+ }
2373
+ const packer = image.createImagePacker(file.fd);
2374
+ if (isNull(packer)) {
2375
+ const createImagePackerError = { errMsg: 'compressImage fail: createImagePacker has failed.' };
2376
+ callAsyncFail(reject, createImagePackerError, options);
2377
+ }
2378
+ const packingOptionsOHOS = {
2379
+ // TODO:需要获取文件名后缀
2380
+ format: 'image/jpeg',
2381
+ quality: quality
2382
+ };
2383
+ packer.packing(source, packingOptionsOHOS).then((value) => {
2384
+ saveImage(value, srcAfterCompress).then(result => {
2385
+ callAsyncSuccess(resolve, { tempFilePath: result.imageUri }, options);
2386
+ });
2387
+ }).catch((error) => {
2388
+ callAsyncFail(reject, error, options);
2389
+ });
2390
+ fs.closeSync(file);
2391
+ }, (error) => {
2392
+ const res = { errMsg: error };
2315
2393
  return callAsyncFail(reject, res, options);
2316
- }
2317
- const { src, quality = 80 } = options;
2318
- const source = image.createImageSource(src);
2319
- if (isNull(source)) {
2320
- const createImageSourceError = { errMsg: 'compressImage fail: createImageSource has failed.' };
2321
- callAsyncFail(reject, createImageSourceError, options);
2322
- return;
2323
- }
2324
- const packer = image.createImagePacker(src);
2325
- if (isNull(packer)) {
2326
- const createImagePackerError = { errMsg: 'compressImage fail: createImagePacker has failed.' };
2327
- callAsyncFail(reject, createImagePackerError, options);
2328
- }
2329
- const packingOptionsOHOS = {
2330
- // TODO:需要获取文件名后缀
2331
- format: 'image/jpeg',
2332
- quality: quality
2333
- };
2334
- packer.packing(source, packingOptionsOHOS).then((value) => {
2335
- callAsyncSuccess(resolve, value, options);
2336
- }).catch((error) => {
2337
- callAsyncFail(reject, error, options);
2338
2394
  });
2339
2395
  });
2340
2396
  };
2341
2397
  const chooseImage = function (options) {
2342
2398
  return new Promise((resolve, reject) => {
2343
- try {
2344
- validateParams('chooseImage', options, chooseImageSchema);
2345
- }
2346
- catch (error) {
2347
- const res = { errMsg: error.message };
2399
+ requestPermissions([READ_IMAGEVIDEO_PERMISSIONS]).then(() => {
2400
+ try {
2401
+ validateParams('chooseImage', options, chooseImageSchema);
2402
+ }
2403
+ catch (error) {
2404
+ const res = { errMsg: error.message };
2405
+ return callAsyncFail(reject, res, options);
2406
+ }
2407
+ const { count = 9 } = options;
2408
+ const photoViewPicker = new picker.PhotoViewPicker();
2409
+ let sizeType = options.sizeType;
2410
+ if (!sizeType || !sizeType.length) {
2411
+ sizeType = ['compressed', 'original'];
2412
+ }
2413
+ photoSelectOptions.maxSelectNumber = count; // 选择媒体文件的最大数目
2414
+ photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE; // 过滤选择媒体文件类型为IMAGE
2415
+ photoViewPicker.select(photoSelectOptions).then((photoSelectResult) => {
2416
+ const result = {};
2417
+ if (sizeType.includes('original')) {
2418
+ const tempFiles = photoSelectResult.photoUris.map(uri => {
2419
+ const file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
2420
+ const stat = fs.statSync(file.fd);
2421
+ const size = stat.size;
2422
+ return {
2423
+ size,
2424
+ path: uri,
2425
+ };
2426
+ });
2427
+ result.tempFiles = tempFiles;
2428
+ }
2429
+ if (sizeType.includes('compressed')) {
2430
+ const actions = photoSelectResult.photoUris.map(uri => {
2431
+ return new Promise(resolve => {
2432
+ compressImage({
2433
+ src: uri,
2434
+ success: (compressResult) => {
2435
+ resolve(compressResult.tempFilePath);
2436
+ }
2437
+ });
2438
+ });
2439
+ });
2440
+ Promise.all(actions).then(tempFilePaths => {
2441
+ result.tempFilePaths = tempFilePaths;
2442
+ callAsyncSuccess(resolve, result, options);
2443
+ }).catch(error => {
2444
+ const res = { errMsg: error };
2445
+ return callAsyncFail(reject, res, options);
2446
+ });
2447
+ }
2448
+ else {
2449
+ callAsyncSuccess(resolve, result, options);
2450
+ }
2451
+ }).catch((error) => {
2452
+ callAsyncFail(reject, error, options);
2453
+ });
2454
+ }, (error) => {
2455
+ const res = { errMsg: error };
2348
2456
  return callAsyncFail(reject, res, options);
2349
- }
2350
- const { count = 9 } = options;
2351
- const photoViewPicker = new picker.PhotoViewPicker();
2352
- photoSelectOptions.maxSelectNumber = count; // 选择媒体文件的最大数目
2353
- photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE; // 过滤选择媒体文件类型为IMAGE
2354
- photoViewPicker.select(photoSelectOptions).then((photoSelectResult) => {
2355
- callAsyncSuccess(resolve, { tempFilePaths: photoSelectResult.photoUris }, options);
2356
- }).catch((error) => {
2357
- callAsyncFail(reject, error, options);
2358
2457
  });
2359
2458
  });
2360
2459
  };