@lambo-design-mobile/lambo-js-bridge 1.0.0-beta.22 → 1.0.0-beta.24
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/CHANGELOG.md +14 -0
- package/README.md +94 -51
- package/demo/index.vue +79 -32
- package/package.json +1 -1
- package/src/sdk/LamboJsBridge.js +4 -0
- package/src/sdk/YunTuAdapter.js +81 -135
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
|
+
## [1.0.0-beta.24](http://git.inspur.com/ecbh/lambo-design/lambo-design/-/compare/@lambo-design-mobile/lambo-js-bridge@1.0.0-beta.23...@lambo-design-mobile/lambo-js-bridge@1.0.0-beta.24) (2025-03-04)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### ✨ Features | 新功能
|
|
6
|
+
|
|
7
|
+
* **@lambo-design-mobile/js-bridge:** 录音新增base64出参;新增下载接口 ([131e4a6](http://git.inspur.com/ecbh/lambo-design/lambo-design/-/commit/131e4a68228d528badea094d057a3d81c41f359b))
|
|
8
|
+
|
|
9
|
+
## [1.0.0-beta.23](http://git.inspur.com/ecbh/lambo-design/lambo-design/-/compare/@lambo-design-mobile/lambo-js-bridge@1.0.0-beta.22...@lambo-design-mobile/lambo-js-bridge@1.0.0-beta.23) (2025-02-28)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### ✨ Features | 新功能
|
|
13
|
+
|
|
14
|
+
* **@lambo-design-mobile/js-bridge:** 更改录音功能调用方式 ([2aa9dd0](http://git.inspur.com/ecbh/lambo-design/lambo-design/-/commit/2aa9dd033c77b2cdb8f14787f3420f903da7e961))
|
|
15
|
+
|
|
2
16
|
## [1.0.0-beta.22](http://git.inspur.com/ecbh/lambo-design/lambo-design/-/compare/@lambo-design-mobile/lambo-js-bridge@1.0.0-beta.21...@lambo-design-mobile/lambo-js-bridge@1.0.0-beta.22) (2025-02-21)
|
|
3
17
|
|
|
4
18
|
|
package/README.md
CHANGED
|
@@ -324,50 +324,66 @@ async openLocation() {
|
|
|
324
324
|
data() {
|
|
325
325
|
return {
|
|
326
326
|
audioUrl: null, // 用于存储录音文件的 URL
|
|
327
|
-
fileName: '', // 录音文件名
|
|
328
|
-
fileSize: 0, // 录音文件大小
|
|
329
|
-
fileType: '', // 录音文件类型
|
|
330
327
|
recordingStatus: "not started", // 用于记录当前录音状态
|
|
331
|
-
recordingOptions: {
|
|
332
|
-
isAuto: false, // 是否自动录音
|
|
333
|
-
fftSize: 512, // FFT 大小
|
|
334
|
-
fileName: 'audio.ogg', // 默认录音文件名
|
|
335
|
-
silenceThreshold: 100, // 静音检测音量阈值
|
|
336
|
-
silenceDuration: 1000, // 静音持续时长
|
|
337
|
-
silenceDelay: 2000 // 静音检测延迟
|
|
338
|
-
}
|
|
339
328
|
};
|
|
340
329
|
},
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
330
|
+
method:{
|
|
331
|
+
// 开始手动录音
|
|
332
|
+
async startRecording() {
|
|
333
|
+
// 清空之前的录音数据
|
|
334
|
+
this.audioUrl = null;
|
|
335
|
+
this.recordingStatus = 'recording';
|
|
336
|
+
try {
|
|
337
|
+
// 传入 { auto: false } 表示手动录音
|
|
338
|
+
const result = await this.$lamboJsBridge.startRecording({ auto: false });
|
|
339
|
+
console.log("录音开始成功:", result);
|
|
340
|
+
} catch (error) {
|
|
341
|
+
console.error("录音开始失败:", error);
|
|
342
|
+
this.recordingStatus = 'failed';
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
|
|
346
|
+
// 开始自动录音
|
|
347
|
+
async startAutoRecording() {
|
|
348
|
+
// 清空之前的录音数据
|
|
349
|
+
this.audioUrl = null;
|
|
350
|
+
this.recordingStatus = 'recording';
|
|
351
|
+
try {
|
|
352
|
+
// 传入 { auto: true } 表示启用自动录音
|
|
353
|
+
const result = await this.$lamboJsBridge.startRecording({ auto: true });
|
|
354
|
+
console.log("自动录音开始成功:", result);
|
|
355
|
+
} catch (error) {
|
|
356
|
+
console.error("自动录音开始失败:", error);
|
|
357
|
+
this.recordingStatus = 'failed';
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
|
|
361
|
+
// 停止录音
|
|
362
|
+
async stopRecording() {
|
|
363
|
+
try {
|
|
364
|
+
const result = await this.$lamboJsBridge.stopRecording();
|
|
365
|
+
console.log("录音停止成功:", result);
|
|
366
|
+
this.recordingStatus = 'stopped';
|
|
367
|
+
// 更新录音文件信息
|
|
368
|
+
this.audioUrl = result.fileUrl || result.msg;
|
|
369
|
+
// 如果你非要用全局函数更新,也可以调用它
|
|
370
|
+
this.updateAudioDisplay(result);
|
|
371
|
+
} catch (error) {
|
|
372
|
+
console.error("录音停止失败:", error);
|
|
373
|
+
this.recordingStatus = 'failed';
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
|
|
377
|
+
// 如果你希望借助类似全局函数的形式更新页面,可以这样定义:
|
|
378
|
+
updateAudioDisplay(data) {
|
|
379
|
+
// 更新组件中的数据,页面会自动响应
|
|
380
|
+
this.recordingStatus = 'stopped';
|
|
381
|
+
this.audioUrl = data.fileUrl;
|
|
382
|
+
},
|
|
354
383
|
},
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
this.recordingOptions.isAuto = true;
|
|
358
|
-
await this.startRecording(); // 调用startRecording方法进行录音
|
|
384
|
+
mounted() {
|
|
385
|
+
window.updateAudioDisplay = this.updateAudioDisplay;
|
|
359
386
|
},
|
|
360
|
-
async stopRecording() {
|
|
361
|
-
try {
|
|
362
|
-
await this.$lamboJsBridge.stopRecording(); // 调用接口停止录音
|
|
363
|
-
this.recordingStatus = 'stopped'; // 设置录音状态为停止
|
|
364
|
-
console.log('录音已停止');
|
|
365
|
-
} catch (error) {
|
|
366
|
-
console.error('停止录音失败:', error);
|
|
367
|
-
this.recordingStatus = 'failed'; // 设置录音状态为失败
|
|
368
|
-
}
|
|
369
|
-
},
|
|
370
|
-
|
|
371
387
|
```
|
|
372
388
|
|
|
373
389
|
### 参数说明
|
|
@@ -377,24 +393,17 @@ async stopRecording() {
|
|
|
377
393
|
| 属性 | 类型 | 默认值 | 必填 | 支持平台 | 说明 |
|
|
378
394
|
|-----------|--|----------|-----|----------|-------------------------------------------------------------|
|
|
379
395
|
| isAuto | boolean | false | 否 | Yuntu | 是否开启自动录音。若为 true,则自动开始录音。 |
|
|
380
|
-
| fftSize | number | 512 | 否 | Yuntu | FFT大小,用于声音信号处理。 |
|
|
381
|
-
| fileName | string | 录音文件.ogg | 否 | Yuntu | 录音文件的默认文件名。 |
|
|
382
|
-
| silenceThreshold | number | 20 | 否 | Yuntu | 静音检测音量阈值。 |
|
|
383
|
-
| silenceDuration | number | 3000 | 否 | Yuntu | 静音持续时长。|
|
|
384
|
-
| silenceDelay | number | 1000 | 否 | Yuntu | 静音检测延迟。 |
|
|
385
396
|
|
|
386
397
|
|
|
387
398
|
### 返回说明
|
|
388
399
|
|
|
389
400
|
`Promise<Object>`
|
|
390
401
|
|
|
391
|
-
| 属性 | 类型 | 默认值 | 必填 | 支持平台 | 说明
|
|
392
|
-
|
|
393
|
-
|
|
|
394
|
-
|
|
|
395
|
-
|
|
|
396
|
-
| file.size | number | | 是 | Yuntu | 录音文件的大小,单位为字节 |
|
|
397
|
-
| file.type | string | | 是 | Yuntu | 录音文件的类型 |
|
|
402
|
+
| 属性 | 类型 | 默认值 | 必填 | 支持平台 | 说明 |
|
|
403
|
+
|-----------|--|----------|-----|----------|---------------|
|
|
404
|
+
| fileUrl | string | | 是 | Yuntu | 录音文件的URL |
|
|
405
|
+
| filePath | string | | 是 | Yuntu | 录音文件的路径 |
|
|
406
|
+
| fileData | string | | 是 | Yuntu | 录音文件的base64文件 |
|
|
398
407
|
<br>
|
|
399
408
|
|
|
400
409
|
**录音状态** <br>
|
|
@@ -402,3 +411,37 @@ async stopRecording() {
|
|
|
402
411
|
**not started**:录音未开始。<br>
|
|
403
412
|
**stopped**:录音已停止。<br>
|
|
404
413
|
**failed**:录音失败。<br>
|
|
414
|
+
|
|
415
|
+
### 下载文件(downloadFile)
|
|
416
|
+
|
|
417
|
+
调用文件下载功能,通过指定文件 URL 下载文件,并自动触发下载行为。
|
|
418
|
+
``` javascript
|
|
419
|
+
async downloadFile() {
|
|
420
|
+
try {
|
|
421
|
+
const fileUrl = 'http://10.110.34.27/yc_scrm.apk'; // 需要下载的文件 URL
|
|
422
|
+
const fileName = '下载文件'; // 文件保存名称
|
|
423
|
+
await this.$lamboJsBridge.downloadFile({ fileUrl, fileName });
|
|
424
|
+
console.log('Download initiated');
|
|
425
|
+
} catch (error) {
|
|
426
|
+
console.error('Download failed:', error);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### 参数说明
|
|
432
|
+
|
|
433
|
+
`params`: Object
|
|
434
|
+
|
|
435
|
+
| 属性 | 类型 | 默认值 | 必填 | 支持平台 | 说明 |
|
|
436
|
+
|-----------|--|----------|-----|----------|-------------------------------------------------------------|
|
|
437
|
+
| fileUrl | string | 无 | 是 | Yuntu | 要下载文件的 URL 地址 |
|
|
438
|
+
| fileName | string | 'downloadedFile' | 否 | Yuntu | 下载后保存的文件名称,建议不包含文件扩展名(可由调用者自行添加扩展名) |
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
### 返回说明
|
|
442
|
+
|
|
443
|
+
`Promise<Object>`
|
|
444
|
+
|
|
445
|
+
| 属性 | 类型 | 默认值 | 必填 | 支持平台 | 说明 |
|
|
446
|
+
|-----------|--|----------|-----|----------|---------|
|
|
447
|
+
| filePath | string | | 是 | Yuntu | 录音文件的路径 |
|
package/demo/index.vue
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<pre v-show="openLocationResult"><code>{{ openLocationResult }}</code></pre>
|
|
30
30
|
</demo-block>
|
|
31
31
|
<demo-block title="文件预览">
|
|
32
|
-
<a href="javascript:void(0);" @click="
|
|
32
|
+
<a href="javascript:void(0);" @click="filePreview">点击这里预览文件</a>
|
|
33
33
|
</demo-block>
|
|
34
34
|
<demo-block title="录音(仅在Flutter环境下可用)">
|
|
35
35
|
<van-button @click="startRecording">开始录音</van-button>
|
|
@@ -50,12 +50,15 @@
|
|
|
50
50
|
<div v-if="audioUrl">
|
|
51
51
|
<h3>录音已完成</h3>
|
|
52
52
|
<audio :src="audioUrl" controls></audio>
|
|
53
|
-
<p
|
|
54
|
-
<p
|
|
55
|
-
<p
|
|
56
|
-
<p>文件地址:{{ audioUrl }}</p>
|
|
53
|
+
<p>FileUrl:{{ audioUrl }}</p>
|
|
54
|
+
<p>FilePath:{{ audioPath }}</p>
|
|
55
|
+
<p>FileData:{{ audioData }}</p>
|
|
57
56
|
</div>
|
|
58
57
|
</demo-block>
|
|
58
|
+
<demo-block title="下载">
|
|
59
|
+
<van-button @click="downloadFile">下载文件</van-button>
|
|
60
|
+
<p v-if="downloadLink">文件已下载,路径:{{ downloadLink }}</p>
|
|
61
|
+
</demo-block>
|
|
59
62
|
|
|
60
63
|
|
|
61
64
|
|
|
@@ -90,18 +93,10 @@ export default {
|
|
|
90
93
|
photoPreviews: [],
|
|
91
94
|
initKeyMessage: '', // 用于存储 initKey 的返回消息
|
|
92
95
|
audioUrl: null, // 用于存储录音文件的 URL
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
fileType: '', // 录音文件类型
|
|
96
|
+
audioPath: null,
|
|
97
|
+
audioData: null,
|
|
96
98
|
recordingStatus: "not started", // 用于记录当前录音状态
|
|
97
|
-
|
|
98
|
-
isAuto: false, // 是否自动录音
|
|
99
|
-
fftSize: 512, // FFT 大小
|
|
100
|
-
fileName: 'audio.ogg', // 默认录音文件名
|
|
101
|
-
silenceThreshold: 100, // 静音检测音量阈值
|
|
102
|
-
silenceDuration: 1000, // 静音持续时长
|
|
103
|
-
silenceDelay: 2000 // 静音检测延迟
|
|
104
|
-
}
|
|
99
|
+
downloadLink : null,
|
|
105
100
|
};
|
|
106
101
|
},
|
|
107
102
|
computed: {
|
|
@@ -128,7 +123,7 @@ export default {
|
|
|
128
123
|
weComId:this.weComId,
|
|
129
124
|
agentId:this.agentId,
|
|
130
125
|
dingTalkId:this.dingTalkId,
|
|
131
|
-
pluginConfig:["localAuthPlugin","amapPlugin","tabBarPlugin","scanCodePlugin","filePreviewPlugin"],
|
|
126
|
+
pluginConfig:["localAuthPlugin","amapPlugin","tabBarPlugin","scanCodePlugin","filePreviewPlugin","autoRecordPlugin","downloadFilePlugin"],
|
|
132
127
|
};
|
|
133
128
|
this.$lamboJsBridge = new LamboJsBridge(options);
|
|
134
129
|
await this.getPlatform();
|
|
@@ -144,40 +139,89 @@ export default {
|
|
|
144
139
|
console.error('Error getting init info:', error);
|
|
145
140
|
}
|
|
146
141
|
},
|
|
142
|
+
|
|
143
|
+
// 开始手动录音
|
|
147
144
|
async startRecording() {
|
|
145
|
+
// 清空之前的录音数据
|
|
146
|
+
this.audioUrl = null;
|
|
147
|
+
this.recordingStatus = 'recording';
|
|
148
148
|
try {
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
this.fileName = file.name;
|
|
153
|
-
this.fileSize = file.size;
|
|
154
|
-
this.fileType = file.type;
|
|
155
|
-
console.log(this.recordingOptions.isAuto ? '自动录音已开始' : '录音已开始');
|
|
149
|
+
// 传入 { auto: false } 表示手动录音
|
|
150
|
+
const result = await this.$lamboJsBridge.startRecording({ auto: false });
|
|
151
|
+
console.log("录音开始成功:", result);
|
|
156
152
|
} catch (error) {
|
|
157
|
-
console.error(
|
|
153
|
+
console.error("录音开始失败:", error);
|
|
158
154
|
this.recordingStatus = 'failed';
|
|
159
155
|
}
|
|
160
156
|
},
|
|
161
157
|
|
|
162
158
|
// 开始自动录音
|
|
163
159
|
async startAutoRecording() {
|
|
164
|
-
//
|
|
165
|
-
this.
|
|
166
|
-
|
|
160
|
+
// 清空之前的录音数据
|
|
161
|
+
this.audioUrl = null;
|
|
162
|
+
this.recordingStatus = 'recording';
|
|
163
|
+
try {
|
|
164
|
+
// 传入 { auto: true } 表示启用自动录音
|
|
165
|
+
const result = await this.$lamboJsBridge.startRecording({ auto: true });
|
|
166
|
+
console.log("自动录音开始成功:", result);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error("自动录音开始失败:", error);
|
|
169
|
+
this.recordingStatus = 'failed';
|
|
170
|
+
}
|
|
167
171
|
},
|
|
168
172
|
|
|
169
173
|
// 停止录音
|
|
170
174
|
async stopRecording() {
|
|
171
175
|
try {
|
|
172
|
-
await this.$lamboJsBridge.stopRecording();
|
|
176
|
+
const result = await this.$lamboJsBridge.stopRecording();
|
|
177
|
+
console.log("录音停止成功:", result);
|
|
173
178
|
this.recordingStatus = 'stopped';
|
|
174
|
-
|
|
179
|
+
// 更新录音文件信息
|
|
180
|
+
this.audioUrl = result.fileUrl || result.msg;
|
|
181
|
+
// 如果你非要用全局函数更新,也可以调用它
|
|
182
|
+
this.updateAudioDisplay(result);
|
|
175
183
|
} catch (error) {
|
|
176
|
-
console.error(
|
|
184
|
+
console.error("录音停止失败:", error);
|
|
177
185
|
this.recordingStatus = 'failed';
|
|
178
186
|
}
|
|
179
187
|
},
|
|
180
188
|
|
|
189
|
+
// 如果你希望借助类似全局函数的形式更新页面,可以这样定义:
|
|
190
|
+
updateAudioDisplay(data) {
|
|
191
|
+
// 更新组件中的数据,页面会自动响应
|
|
192
|
+
this.recordingStatus = 'stopped';
|
|
193
|
+
this.audioUrl = data.fileUrl;
|
|
194
|
+
this.audioPath = data.filePath;
|
|
195
|
+
this.audioData = data.fileData;
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
async downloadFile(){
|
|
200
|
+
try {
|
|
201
|
+
const fileUrl = 'http://10.110.34.27/sampleFile.txt'; // 要下载的文件 URL
|
|
202
|
+
const fileName = '下载文件.txt'; // 文件名称(建议带上扩展名)
|
|
203
|
+
// 注意传递的参数键需与插件内部代码一致:title 和 path
|
|
204
|
+
let result = await this.$lamboJsBridge.downloadFile({ title: fileName, path: fileUrl });
|
|
205
|
+
console.log('Download initiated, result:', result);
|
|
206
|
+
|
|
207
|
+
// 如果返回的数据为字符串,则尝试解析为对象
|
|
208
|
+
if (typeof result === 'string') {
|
|
209
|
+
try {
|
|
210
|
+
result = JSON.parse(result);
|
|
211
|
+
} catch (e) {
|
|
212
|
+
console.error('解析下载结果失败:', e);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 假设返回的对象中包含 path 属性(如果没有则直接使用 result)
|
|
217
|
+
const filePath = result.path || result;
|
|
218
|
+
// 更新组件数据,显示文件下载路径
|
|
219
|
+
this.downloadLink = filePath;
|
|
220
|
+
} catch (error){
|
|
221
|
+
console.error('Download failed:', error);
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
|
|
181
225
|
async getLocation() {
|
|
182
226
|
try {
|
|
183
227
|
const options ={
|
|
@@ -259,7 +303,7 @@ export default {
|
|
|
259
303
|
console.error('Error opening location:', error);
|
|
260
304
|
}
|
|
261
305
|
},
|
|
262
|
-
async
|
|
306
|
+
async filePreview() {
|
|
263
307
|
try {
|
|
264
308
|
const options = {
|
|
265
309
|
title: '预览文件.txt', // 文件的标题
|
|
@@ -283,6 +327,9 @@ export default {
|
|
|
283
327
|
}
|
|
284
328
|
},
|
|
285
329
|
},
|
|
330
|
+
mounted() {
|
|
331
|
+
window.updateAudioDisplay = this.updateAudioDisplay;
|
|
332
|
+
},
|
|
286
333
|
beforeRouteEnter (to, from, next) {
|
|
287
334
|
next(vm => {
|
|
288
335
|
if (from.name !== null) {
|
package/package.json
CHANGED
package/src/sdk/LamboJsBridge.js
CHANGED
package/src/sdk/YunTuAdapter.js
CHANGED
|
@@ -6,12 +6,7 @@ class YunTuAdapter {
|
|
|
6
6
|
this.isInitialized = false;
|
|
7
7
|
this.audioUrl = null; // 录音文件的 URL
|
|
8
8
|
this.fileName = ''; // 录音文件名称
|
|
9
|
-
this.fileSize = 0; // 录音文件大小
|
|
10
9
|
this.fileType = ''; // 录音文件类型
|
|
11
|
-
this.errorMessage = ''; // 错误信息
|
|
12
|
-
this.recorder = null; // MediaRecorder 实例
|
|
13
|
-
this.streams = null; // 音频流
|
|
14
|
-
this.chunks = []; // 录音数据块
|
|
15
10
|
this.initializePlugin(options.pluginConfig);
|
|
16
11
|
}
|
|
17
12
|
|
|
@@ -33,143 +28,94 @@ class YunTuAdapter {
|
|
|
33
28
|
}
|
|
34
29
|
}
|
|
35
30
|
|
|
36
|
-
//
|
|
37
|
-
async startRecording({
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// 创建音频上下文
|
|
54
|
-
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
55
|
-
console.log("创建音频上下文:", this.audioContext);
|
|
56
|
-
|
|
57
|
-
// 创建音频分析器
|
|
58
|
-
this.analyser = this.audioContext.createAnalyser();
|
|
59
|
-
console.log("创建音频分析器:", this.analyser);
|
|
60
|
-
|
|
61
|
-
const microphone = this.audioContext.createMediaStreamSource(stream);
|
|
62
|
-
microphone.connect(this.analyser);
|
|
63
|
-
this.analyser.fftSize = fftSize;
|
|
64
|
-
console.log("音频分析器配置完成,FFT Size:", this.analyser.fftSize);
|
|
65
|
-
|
|
66
|
-
// 创建 MediaRecorder 实例
|
|
67
|
-
this.recorder = new MediaRecorder(stream);
|
|
68
|
-
console.log("创建 MediaRecorder 实例:", this.recorder);
|
|
69
|
-
|
|
70
|
-
// 监听数据可用事件
|
|
71
|
-
this.recorder.ondataavailable = (e) => {
|
|
72
|
-
console.log("收到录音数据块,大小:", e.data.size, "字节");
|
|
73
|
-
this.chunks.push(e.data);
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
// 返回 Promise
|
|
77
|
-
return new Promise((resolve, reject) => {
|
|
78
|
-
// 监听录音停止事件
|
|
79
|
-
this.recorder.onstop = () => {
|
|
80
|
-
console.log("录音停止,生成音频文件...");
|
|
81
|
-
const blob = new Blob(this.chunks, { type: 'audio/ogg' });
|
|
82
|
-
this.audioUrl = URL.createObjectURL(blob); // 生成录音文件 URL
|
|
83
|
-
const file = new File([blob], fileName, { type: 'audio/ogg' });
|
|
84
|
-
this.fileName = file.name;
|
|
85
|
-
this.fileSize = file.size;
|
|
86
|
-
this.fileType = file.type;
|
|
87
|
-
console.log("生成的音频文件:", file);
|
|
88
|
-
|
|
89
|
-
// 返回包含音频 URL 和文件对象的对象
|
|
90
|
-
resolve({
|
|
91
|
-
audioUrl: this.audioUrl,
|
|
92
|
-
file: file
|
|
93
|
-
});
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
// 启动静音检测
|
|
97
|
-
this.startSilenceDetection({
|
|
98
|
-
silenceThreshold,
|
|
99
|
-
silenceDuration,
|
|
100
|
-
silenceDelay
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// 开始录音
|
|
104
|
-
this.recorder.start();
|
|
105
|
-
console.log("录音已开始");
|
|
106
|
-
});
|
|
107
|
-
} catch (err) {
|
|
108
|
-
console.error("获取麦克风权限失败:", err);
|
|
109
|
-
this.errorMessage = "无法访问麦克风,请检查权限设置";
|
|
110
|
-
return Promise.reject(new Error(this.errorMessage)); // 返回错误
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// 静音检测
|
|
115
|
-
startSilenceDetection({
|
|
116
|
-
silenceThreshold = 20, // 静音检测的音量阈值
|
|
117
|
-
silenceDuration = 3000, // 超过多少秒静音就停止录音
|
|
118
|
-
silenceDelay = 1000 // 延迟多少秒开始静音检测
|
|
119
|
-
}) {
|
|
120
|
-
console.log("启动静音检测...");
|
|
121
|
-
const bufferLength = this.analyser.frequencyBinCount;
|
|
122
|
-
const dataArray = new Uint8Array(bufferLength);
|
|
123
|
-
let silenceStart = Date.now();
|
|
124
|
-
let isSpeaking = false;
|
|
125
|
-
|
|
126
|
-
const checkVolume = () => {
|
|
127
|
-
this.analyser.getByteFrequencyData(dataArray);
|
|
128
|
-
const volume = Math.max(...dataArray);
|
|
129
|
-
console.debug("当前音量:", volume);
|
|
130
|
-
|
|
131
|
-
if (volume > silenceThreshold) { // 音量阈值
|
|
132
|
-
console.debug("检测到声音,重置静音计时器");
|
|
133
|
-
isSpeaking = true;
|
|
134
|
-
silenceStart = Date.now();
|
|
135
|
-
} else if (isSpeaking && Date.now() - silenceStart > silenceDuration) {
|
|
136
|
-
// 超过指定静音时长
|
|
137
|
-
console.log(`检测到持续 ${silenceDuration / 1000} 秒静音,停止录音`);
|
|
138
|
-
this.stopRecording();
|
|
139
|
-
return;
|
|
31
|
+
// 录音:开始录音(支持自动和手动模式)
|
|
32
|
+
async startRecording(options = { auto: false }) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
if (window.autoRecord && typeof window.autoRecord.startRecording === 'function') {
|
|
35
|
+
window.autoRecord.startRecording(
|
|
36
|
+
(result) => {
|
|
37
|
+
console.log("录音开始:", result);
|
|
38
|
+
resolve(result);
|
|
39
|
+
},
|
|
40
|
+
(error) => {
|
|
41
|
+
console.error("录音错误:", error);
|
|
42
|
+
reject(error);
|
|
43
|
+
},
|
|
44
|
+
JSON.stringify(options) // 传入参数:{auto: false} 表示手动录音,{auto: true} 表示自动录音
|
|
45
|
+
);
|
|
46
|
+
} else {
|
|
47
|
+
reject(new Error("autoRecord 接口不可用"));
|
|
140
48
|
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
141
51
|
|
|
142
|
-
|
|
143
|
-
|
|
52
|
+
// 录音:结束录音,并返回录音结果
|
|
53
|
+
async stopRecording() {
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
if (window.autoRecord && typeof window.autoRecord.stopRecording === 'function') {
|
|
56
|
+
window.autoRecord.stopRecording(
|
|
57
|
+
(result) => {
|
|
58
|
+
console.log("录音停止,返回结果:", result);
|
|
59
|
+
// 如果返回数据为字符串,则尝试解析为对象
|
|
60
|
+
try {
|
|
61
|
+
if (typeof result === "string") {
|
|
62
|
+
result = JSON.parse(result);
|
|
63
|
+
}
|
|
64
|
+
} catch (e) {
|
|
65
|
+
console.error("JSON.parse error:", e);
|
|
66
|
+
}
|
|
67
|
+
resolve(result);
|
|
68
|
+
},
|
|
69
|
+
(error) => {
|
|
70
|
+
console.error("录音结束错误:", error);
|
|
71
|
+
// 同样尝试解析错误信息
|
|
72
|
+
try {
|
|
73
|
+
if (typeof error === "string") {
|
|
74
|
+
error = JSON.parse(error);
|
|
75
|
+
}
|
|
76
|
+
} catch (e) {
|
|
77
|
+
console.error("JSON.parse error:", e);
|
|
78
|
+
}
|
|
79
|
+
reject(error);
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
} else {
|
|
83
|
+
reject(new Error("autoRecord 接口不可用"));
|
|
144
84
|
}
|
|
145
|
-
};
|
|
85
|
+
});
|
|
86
|
+
}
|
|
146
87
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
88
|
+
// 新增下载文件接口,options 作为参数传入
|
|
89
|
+
async downloadFile(options) {
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
if (window.fileDownload && typeof window.fileDownload.downloadFile === 'function') {
|
|
92
|
+
window.fileDownload.downloadFile(
|
|
93
|
+
// 成功回调
|
|
94
|
+
(result) => {
|
|
95
|
+
console.log("文件下载成功:", result);
|
|
96
|
+
try {
|
|
97
|
+
if (typeof result === "string") {
|
|
98
|
+
result = JSON.parse(result);
|
|
99
|
+
}
|
|
100
|
+
} catch (e) {
|
|
101
|
+
console.error("JSON.parse error:", e);
|
|
102
|
+
}
|
|
103
|
+
resolve(result);
|
|
104
|
+
},
|
|
105
|
+
// 失败回调
|
|
106
|
+
(error) => {
|
|
107
|
+
console.error("文件下载失败:", error);
|
|
108
|
+
reject(error);
|
|
109
|
+
},
|
|
110
|
+
// 直接将传入的 options 对象转为字符串
|
|
111
|
+
JSON.stringify(options)
|
|
112
|
+
);
|
|
113
|
+
} else {
|
|
114
|
+
reject(new Error("downloadFile 接口不可用"));
|
|
152
115
|
}
|
|
153
|
-
}
|
|
116
|
+
});
|
|
154
117
|
}
|
|
155
118
|
|
|
156
|
-
// 停止录音
|
|
157
|
-
async stopRecording() {
|
|
158
|
-
console.log("停止录音...");
|
|
159
|
-
if (this.recorder && this.recorder.state === 'recording') {
|
|
160
|
-
this.recorder.stop();
|
|
161
|
-
console.log("录音已停止");
|
|
162
|
-
}
|
|
163
|
-
if (this.streams) {
|
|
164
|
-
this.streams.getTracks().forEach((track) => {
|
|
165
|
-
track.stop();
|
|
166
|
-
console.log("停止音频轨道:", track);
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
this.chunks = []; // 清空录音数据块
|
|
170
|
-
this.audioContext.close();
|
|
171
|
-
console.log("音频上下文已关闭");
|
|
172
|
-
}
|
|
173
119
|
|
|
174
120
|
async getPlatform() {
|
|
175
121
|
return {
|