wangxia 11 months ago
commit 9b1266abd7

@ -39,7 +39,7 @@
let that = this;
if(that.text == '请登录'){
uni.reLaunch({
url: '/pages/login/index?path=' + that.G.getPath().path + '&level=' + that.G.getPath().level
url: '/root/person/loginIndex?path=' + that.G.getPath().path + '&level=' + that.G.getPath().level
});
}
}

@ -145,7 +145,7 @@ export default {
},
goLogin() {
uni.reLaunch({
url: "/pages/login/index",
url: "/root/person/loginIndex",
});
},
goTel($item) {

@ -545,7 +545,7 @@ export default {
},
goLogin() {
uni.reLaunch({
url: "/pages/login/index",
url: "/root/person/loginIndex",
});
},
},

@ -362,7 +362,7 @@ export default {
},
goLogin() {
uni.reLaunch({
url: "/pages/login/index",
url: "/root/person/loginIndex",
});
},
openApplyPop($item) {
@ -450,7 +450,7 @@ export default {
success(res) {
if (res.confirm) {
uni.navigateTo({
url: "/pages/login/index",
url: "/root/person/loginIndex",
});
} else {
}

@ -45,20 +45,6 @@
customIcon: false,
pagePath:"/pages/home/index",
},
{
iconPath: this.cdnBaseImg + 'bill.png',
selectedIconPath: this.cdnBaseImg + 'billActive.png',
text: '发单',
customIcon: false,
pagePath:"/pages/bill/index",
},
{
iconPath: this.cdnBaseImg + 'apply.png',
selectedIconPath: this.cdnBaseImg + 'applyActive.png',
text: '工单',
customIcon: false,
pagePath:"/pages/apply/index",
},
{
iconPath: this.cdnBaseImg + 'person.png',
selectedIconPath: this.cdnBaseImg + 'personActive.png',
@ -78,13 +64,6 @@
customIcon: false,
pagePath:"/pages/home/index",
},
{
iconPath: this.cdnBaseImg + 'apply.png',
selectedIconPath: this.cdnBaseImg + 'applyActive.png',
text: '工单',
customIcon: false,
pagePath:"/pages/apply/index",
},
{
iconPath: this.cdnBaseImg + 'person.png',
selectedIconPath: this.cdnBaseImg + 'personActive.png',

1571
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -10,15 +10,13 @@
"author": "",
"license": "ISC",
"dependencies": {
"html2canvas": "^1.4.1",
"@xkit-yx/im-store-v2": "^0.4.0",
"@xkit-yx/utils": "^0.5.6",
"dayjs": "^1.11.7",
"fetch-event-source": "^1.0.0-alpha.2",
"mobx": "^6.6.1",
"nim-web-sdk-ng": "^10.4.0",
"pinyin": "^3.1.0",
"recorder-core": "^1.3.25011100"
"pinyin": "^3.1.0"
},
"devDependencies": {
"file-saver": "^2.0.5",

@ -8,15 +8,6 @@
"backgroundColorTop": "#caf1e0"
}
},
{
"path": "pages/apply/index",
"style": {
"navigationBarTitleText": "工单",
"backgroundColor": "#ededed",
"backgroundColorTop": "#ededed",
"backgroundColorBottom": "#ededed"
}
},
{
"path": "pages/message/index",
"style": {
@ -35,26 +26,11 @@
"backgroundColorBottom": "#ededed"
}
},
{
"path": "pages/bill/index",
"style": {
"navigationBarTitleText": "我的职位",
"backgroundColor": "#ededed",
"backgroundColorTop": "#ededed",
"backgroundColorBottom": "#ededed"
}
},
{
"path": "pages/person/index",
"style": {
"navigationBarTitleText": "我的"
}
},
{
"path": "pages/login/index",
"style": {
"navigationBarTitleText": "登录"
}
}
],
"subPackages": [
@ -103,6 +79,30 @@
{ //
"root": "root/person",
"pages": [
{
"path": "billIndex",
"style": {
"navigationBarTitleText": "我的职位",
"backgroundColor": "#ededed",
"backgroundColorTop": "#ededed",
"backgroundColorBottom": "#ededed"
}
},
{
"path": "applyIndex",
"style": {
"navigationBarTitleText": "工单",
"backgroundColor": "#ededed",
"backgroundColorTop": "#ededed",
"backgroundColorBottom": "#ededed"
}
},
{
"path": "loginIndex",
"style": {
"navigationBarTitleText": "登录"
}
},
{
"path": "set",
"style": {

@ -43,7 +43,7 @@ export default {
} else {
// im
customRedirectTo({
url: isWxApp ? "/pages/index/index" : "/pages/Login/index",
url: isWxApp ? "/pages/index/index" : "/root/person/loginIndex",
});
}
},
@ -296,7 +296,7 @@ export default {
uni.$UIKitNIM.V2NIMLoginService.logout().then(() => {
uni.$UIKitStore.destroy();
customReLaunch({
url: "/pages/Login/index",
url: "/root/person/loginIndex",
});
});
},

@ -141,7 +141,7 @@ onShow(() => {
});
const goLogin = () => {
uni.reLaunch({
url: "/pages/login/index",
url: "/root/person/loginIndex",
});
};
const goPage = ($path) => {

@ -188,7 +188,7 @@ export default {
{
icon: "icon-zhiweiguanli",
name: "我的职位",
path: "/pages/bill/index",
path: "/root/person/billIndex",
},
{
icon: "icon-fazhandaili1",
@ -468,12 +468,12 @@ export default {
switch (item.name) {
case "全部职位":
uni.navigateTo({
url: "/pages/bill/index",
url: "/root/person/billIndex",
});
break;
case "在招职位":
uni.navigateTo({
url: "/pages/bill/index?active=1",
url: "/root/person/billIndex?active=1",
});
break;
case "关注":

@ -36,7 +36,7 @@
<view class="g_mt_10">
<!-- 报名工单 -->
<g-panel-card-num :list="billDataList" titleNav="/pages/apply/index" :border="true" :speed="1" :marginBottom="16" cusType="num" :height="26" :num="5" cusTitle="报名工单" @clickItem="handleClickNum" />
<g-panel-card-num :list="billDataList" titleNav="/root/person/applyIndex" :border="true" :speed="1" :marginBottom="16" cusType="num" :height="26" :num="5" cusTitle="报名工单" @clickItem="handleClickNum" />
</view>
<!-- wx:if="{{configInfo.record}}" -->
<!-- titleNav="/root/detail/applyTob" -->
@ -158,27 +158,27 @@ export default {
{
name: "审核中",
num: 0,
path: "/pages/apply/index?status=1",
path: "/root/person/applyIndex?status=1",
},
{
name: "待接待",
num: 0,
path: "/pages/apply/index?status=2",
path: "/root/person/applyIndex?status=2",
},
{
name: "待面试",
num: 0,
path: "/pages/apply/index?status=3",
path: "/root/person/applyIndex?status=3",
},
{
name: "待入职",
num: 0,
path: "/pages/apply/index?status=4",
path: "/root/person/applyIndex?status=4",
},
{
name: "在职中",
num: 0,
path: "/pages/apply/index?status=5",
path: "/root/person/applyIndex?status=5",
},
],
query: {
@ -269,27 +269,27 @@ export default {
{
name: "审核中",
num: that.map.classify2num.num10 || 0,
path: "/pages/apply/index?status=1",
path: "/root/person/applyIndex?status=1",
},
{
name: "待接待",
num: that.map.classify2num.num20 || 0,
path: "/pages/apply/index?status=2",
path: "/root/person/applyIndex?status=2",
},
{
name: "待面试",
num: that.map.classify2num.num25 || 0,
path: "/pages/apply/index?status=3",
path: "/root/person/applyIndex?status=3",
},
{
name: "待入职",
num: that.map.classify2num.num30 || 0,
path: "/pages/apply/index?status=4",
path: "/root/person/applyIndex?status=4",
},
{
name: "在职中",
num: that.map.classify2num.num40 || 0,
path: "/pages/apply/index?status=5",
path: "/root/person/applyIndex?status=5",
},
];
});
@ -342,7 +342,7 @@ export default {
goLogin() {
console.log("123");
uni.reLaunch({
url: "/pages/login/index",
url: "/root/person/loginIndex",
});
},
goPage(url) {

@ -43,7 +43,7 @@ export default {
} else {
// im
customRedirectTo({
url: isWxApp ? "/pages/index/index" : "/pages/Login/index",
url: isWxApp ? "/pages/index/index" : "/root/person/loginIndex",
});
}
},
@ -296,7 +296,7 @@ export default {
uni.$UIKitNIM.V2NIMLoginService.logout().then(() => {
uni.$UIKitStore.destroy();
customReLaunch({
url: "/pages/Login/index",
url: "/root/person/loginIndex",
});
});
},

@ -25,7 +25,7 @@
import { customRedirectTo } from '../../utils/customNavigate'
const onLogin = () => {
customRedirectTo({ url: '/pages/Login/index' })
customRedirectTo({ url: '/root/person/loginIndex' })
}
</script>

@ -18,7 +18,7 @@
></canvas>
<!-- #endif -->
<!-- #ifdef H5 -->
<view class="canvas-card g_position_rela" id="canvas-card" style="background-image: url('/static/image/bg.png')">
<view class="canvas-card g_position_rela" id="canvas-card" style="background-image: url('https://matripe-cms.oss-cn-beijing.aliyuncs.com/dailibaoming/v3/bg.png')">
<view class="m-card g_position_abso" style="left: 50%; transform: translateX(-50%)">
<view class="g_w_all g_h_all g_position_rela">
<view class="g_pl_20 g_pt_18 g_pr_24">
@ -98,9 +98,6 @@
</template>
<script>
// #ifdef H5 || APP-PLUS
import html2canvas from "html2canvas";
// #endif
// #ifdef H5
import { saveAs } from "file-saver";
// #endif
@ -415,12 +412,6 @@ export default {
saveH5ImageCanvas() {
let that = this;
// #ifdef H5
const container = document.querySelector("#canvas-card"); //
html2canvas(container).then((canvas) => {
canvas.toBlob((blob) => {
saveAs(blob, that.agencyName + ".png");
}, "image/png");
});
// #endif
},
// APP

@ -489,7 +489,7 @@ export default {
if (!uni.getStorageSync("apply-token")) {
console.log("apply-token");
uni.navigateTo({
url: "/pages/login/index?type=detail&id=" + options.id,
url: "/root/person/loginIndex?type=detail&id=" + options.id,
});
return;
}

@ -654,7 +654,7 @@ export default {
if (!uni.getStorageSync("apply-token")) {
console.log("apply-token");
uni.navigateTo({
url: "/pages/login/index?type=detail&id=" + options.id,
url: "/root/person/loginIndex?type=detail&id=" + options.id,
});
return;
}

@ -35,7 +35,7 @@ export default {
goList() {
uni.setStorageSync("apply-tab-active", 1);
uni.navigateTo({
url: "/pages/apply/index",
url: "/root/person/applyIndex",
});
},
},

@ -2,7 +2,7 @@
<view class="p-person-about g_w_all g_h_all g_bg_f_5 g_flex_column_between g_kuaishou">
<view class="g_flex_1">
<view class="g_mb_24 g_flex_row_center" style="padding-top: 130px;">
<img :src="localBaseImg + 'boss.png'" class="g_w_63 g_h_63 g_radius_20">
<img :src="v3BaseImg + 'boss.png'" class="g_w_63 g_h_63 g_radius_20">
</view>
<view class="g_fs_24 g_c_3 g_flex_row_center g_mb_8 g_fw_600">报名助手</view>
<view class="g_fs_16 g_c_3 g_flex_row_center">Version {{version}}</view>
@ -24,6 +24,7 @@
},
data(){
return {
v3BaseImg:this.G.store().v3BaseImg,
year:new Date().getFullYear(),
localBaseImg:this.G.store().localBaseImg,
version:this.G.store().version

@ -350,7 +350,7 @@ export default {
},
goLogin() {
uni.reLaunch({
url: "/pages/login/index",
url: "/root/person/loginIndex",
});
},
toMessage() {

@ -38,7 +38,7 @@ export default {
level: "",
};
uni.reLaunch({
url: "/pages/login/index?" + that.G.objToStr(params),
url: "/root/person/loginIndex?" + that.G.objToStr(params),
});
});
// });

@ -3,7 +3,7 @@
<!-- 15936360682 -->
<view class="g_h_70"></view>
<view class="g_flex_row_center g_mb_10">
<img :src="localBaseImg + 'boss.png'" alt="" class="g_w_56 g_h_56 g_radius_8" />
<img :src="v3BaseImg + 'boss.png'" alt="" class="g_w_56 g_h_56 g_radius_8" />
</view>
<view style="font-size: 18px; font-weight: 400; color: #919191; line-height: 25px; margin-bottom: 60px; text-align: center">劳务上下游收发单工具</view>
<view class="m-form g_mb_50 g_pl_40 g_pr_40">
@ -56,6 +56,7 @@ export default {
data() {
return {
localBaseImg: this.G.store().localBaseImg,
v3BaseImg:this.G.store().v3BaseImg,
isLoginCode: true,
form: {
tel: "",

@ -64,7 +64,7 @@ export default {
uni.removeStorageSync("scene");
} else {
// uni.reLaunch({
// url: `/pages/login/index?path='/root1/person/memberApplyAdd'&scene=${options.scene}`,
// url: `/root/person/loginIndex?path='/root1/person/memberApplyAdd'&scene=${options.scene}`,
// });
uni.setStorageSync("scene", options.scene);
}

@ -16,7 +16,7 @@
></canvas>
<!-- #endif -->
<!-- #ifdef H5 -->
<view class="canvas-card g_position_rela" id="canvas-card" style="background-image: url('/static/image/bg.png')">
<view class="canvas-card g_position_rela" id="canvas-card" style="background-image: url('https://matripe-cms.oss-cn-beijing.aliyuncs.com/dailibaoming/v3/bg.png')">
<view class="m-card g_position_abso" style="left: 50%; transform: translateX(-50%)">
<view class="g_w_all g_h_all g_position_rela">
<view class="g_pl_20 g_pt_18 g_pr_24">
@ -96,9 +96,6 @@
</template>
<script>
// #ifdef H5 || APP-PLUS
import html2canvas from "html2canvas";
// #endif
// #ifdef H5
import { saveAs } from "file-saver";
// #endif
@ -401,12 +398,6 @@ export default {
saveH5ImageCanvas() {
let that = this;
// #ifdef H5
const container = document.querySelector("#canvas-card"); //
html2canvas(container).then((canvas) => {
canvas.toBlob((blob) => {
saveAs(blob, that.agencyName + ".png");
}, "image/png");
});
// #endif
},
// APP

@ -135,7 +135,7 @@
level:"",
}
uni.reLaunch({
url: '/pages/login/index?' + that.G.objToStr(params)
url: '/root/person/loginIndex?' + that.G.objToStr(params)
});
})
},1500)

@ -135,7 +135,7 @@
level:"",
}
uni.reLaunch({
url: '/pages/login/index?' + that.G.objToStr(params)
url: '/root/person/loginIndex?' + that.G.objToStr(params)
});
})
},1500)

@ -135,7 +135,7 @@ export default {
level: "",
};
uni.reLaunch({
url: "/pages/login/index?" + that.G.objToStr(params),
url: "/root/person/loginIndex?" + that.G.objToStr(params),
});
});
}, 1500);

@ -155,7 +155,7 @@ export default {
level: "",
};
uni.reLaunch({
url: "/pages/login/index?" + that.G.objToStr(params),
url: "/root/person/loginIndex?" + that.G.objToStr(params),
});
});
}

@ -508,7 +508,7 @@ scroll-view::-webkit-scrollbar {
//
$min-font-size: 1;
$max-font-size: 60;
$max-font-size: 28;
@for $i from $min-font-size through $max-font-size {
.g_fs_#{$i} {
font-size: #{$i}px;
@ -528,23 +528,38 @@ $max-font-size: 60;
//
$min-size: 0;
$max-size: 300;
$max-size: 200;
@for $i from $min-size through $max-size {
.g_w_#{$i} {
width: #{$i}px !important;
}
}
$min-size: 0;
$max-size: 140;
@for $i from $min-size through $max-size {
.g_h_#{$i} {
height: #{$i}px !important;
}
}
.g_w_260{
width: 260px !important;
}
.g_w_250{
width: 250px !important;
}
.g_w_240{
width: 240px !important;
}
.g_w_220{
width: 240px !important;
}
.g_w_220{
width: 240px !important;
}
//
$min-mp: 0;
$max-mp: 200;
$max-mp: 27;
@for $i from $min-mp through $max-mp {
.g_p_#{$i} {
padding: #{$i}px;
}
.g_pt_#{$i} {
padding-top: #{$i}px;
}
@ -557,9 +572,6 @@ $max-mp: 200;
.g_pl_#{$i} {
padding-left: #{$i}px;
}
.g_m_#{$i} {
margin: #{$i}px;
}
.g_mt_#{$i} {
margin-top: #{$i}px;
}
@ -569,11 +581,63 @@ $max-mp: 200;
.g_mb_#{$i} {
margin-bottom: #{$i}px;
}
}
$min-mp: 0;
$max-mp: 20;
@for $i from $min-mp through $max-mp {
.g_ml_#{$i} {
margin-left: #{$i}px;
}
.g_p_#{$i} {
padding: #{$i}px;
}
}
.g_pt_32 {
padding-top: 32px;
}
.g_pt_40 {
padding-top: 40px;
}
.g_pr_40 {
padding-right: 40px;
}
.g_pr_32 {
padding-right: 32px;
}
.g_pb_40 {
padding-bottom: 40px;
}
.g_pb_32 {
padding-bottom: 32px;
}
.g_pb_42 {
padding-bottom: 42px;
}
.g_pb_48 {
padding-bottom: 48px;
}
.g_pl_32 {
padding-left: 32px;
}
.g_pl_40 {
padding-left: 40px;
}
.g_mb_32{
margin-bottom: 32px;
}
.g_mb_40{
margin-bottom: 40px;
}
.g_mt_42 {
margin-top: 42px;
}
.g_mt_40 {
margin-top: 40px;
}
.g_m_10 {
margin: 10px;
}
//
$min-radius: 0;
$max-radius: 49;
@ -586,16 +650,12 @@ $max-radius: 49;
}
}
/* dot点生成 */
$min-radius: 0;
$max-radius: 50;
@for $i from $min-radius through $max-radius {
.g_dot_#{$i} {
width: #{$i}px;
height: #{$i}px;
.g_dot_18{
width: 18px;
height: 18px;
border-radius: 50%;
}
}
.flex_center {
align-items: center;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 552 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 498 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 508 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 775 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 964 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -1,38 +1,28 @@
// uViewuni.scss
// uni.scss
// uni.scssscssmain.jsApp.vue
$u-main-color: #303133;
$u-content-color: #606266;
$u-tips-color: #909399;
$u-light-color: #c0c4cc;
$u-border-color: #e4e7ed;
$u-bg-color: #f3f4f6;
$u-type-primary: #2979ff;
$u-type-primary-light: #ecf5ff;
$u-type-primary-disabled: #a0cfff;
$u-type-primary-dark: #2b85e4;
$u-type-warning: #ff9900;
$u-type-warning-disabled: #fcbd71;
$u-type-warning-dark: #f29100;
$u-type-warning-light: #fdf6ec;
$u-type-success: #19be6b;
$u-type-success-disabled: #71d5a1;
$u-type-success-dark: #18b566;
$u-type-success-light: #dbf1e1;
$u-type-error: #fa3534;
$u-type-error-disabled: #fab6b6;
$u-type-error-dark: #dd6161;
$u-type-error-light: #fef0f0;
$u-type-info: #909399;
$u-type-info-disabled: #c8c9cc;
$u-type-info-dark: #82848a;
$u-type-info-light: #f4f4f5;
$u-form-item-height: 70rpx;
$u-form-item-border-color: #dcdfe6;

File diff suppressed because one or more lines are too long

@ -1,19 +0,0 @@
## 1.0.2503312025-03-31
增加RecordApp.UniNativeUtsPlugin_OnJsCall接口App端搭配原生插件使用时可绑定接收配套原生录音插件事件原生插件新增PcmPlayer播放器支持流式播放、完整播放App端边录音边播放更流畅
## 1.0.2501112025-01-11
修复vue3 Fragments(multi-root 多个根节点)的兼容性问题修复uniapp Android自带的XXPermissions库在后台无法请求权限的问题仅限搭配原生录音插件可用
## 1.0.2410202024-10-20
适配HBuilder4.28 vue3 setup编译环境下$root.$scope无法读取的bugHBuilder4.29已修复此编译bug但似乎还是有不能使用的问题。如果setup内不能使用可尝试新建个vue组件然后使用选项式api来调用录音功能页面的setup内使用此vue组件
## 1.0.2409102024-09-10
- 新增RecordApp.UniMainCallBack_Register接口允许App renderjs层多次回调数据给逻辑层
- iOS App请求权限时会预先检查NSMicrophoneUsageDescription是否声明避免无声明时调用录音会崩溃
- 新增appNativePlugin_sampleRate原生插件录音选项
- Android App已提供后台录音保活功能启用后App在后台或锁屏后可继续正常录音
## 1.0.2406252024-06-25
调整UniWebViewCallAsync调用失败时返回更详细信息。android_audioSource默认值由1改成0新增ios_categoryOptions原生插件录音选项
## 1.0.2404092024-04-09
增加功能调用完善demo项目
## 1.0.2312082023-12-08
完善文档增加asr语音识别示例
## 1.0.2312012023-12-04
第一次发布

@ -1,6 +0,0 @@
<template>
<view>
<view style="font-weight: bold;">Recorder-UniCore Vue Component</view>
<view style="font-size:14px; color:#f60">无需手动显示本UI组件只需在script中正常引入 RecordApp + app-uni-support.js 即可实现 H5iOS Android App微信小程序 多端录音</view>
</view>
</template>

@ -1,19 +0,0 @@
《许可及服务协议》
**您以下称“用户”下载、使用我以下称“作者”提供的Recorder-UniCore组件含原生录音插件、uts插件以下统称“本组件”应当阅读并遵守本许可协议。请用户务必审慎阅读、充分理解各条款内容特别是免除或者限制责任的条款并选择接受或不接受。除非用户已阅读并接受本协议所有条款否则用户无权下载、使用本组件及相关服务用户的下载、使用等行为即视为用户已阅读并同意本许可协议的约束。**
1. 用户应当直接从作者许可的途径如作者的GitHub、Gitee仓库、已上架的DCloud插件市场、QQ群等途径中获取本组件其他途径获取到的组件代码是未经过作者授权的存在安全隐患可能会导致你的程序、资产受到侵害作者对因此给用户造成的损失不予负责。
2. 作者将积极并采取措施保护用户的信息和隐私;组件本身不会搜集存储任何用户信息。
3. 除法律法规有明确规定外,作者将尽最大努力确保本组件及其所涉及的技术及信息安全、有效、准确、可靠,但受限于现有技术,用户理解作者不能对此进行担保。
4. 用户理解,对于不可抗力及第三方原因导致的您的直接或间接损失,作者无法承担责任。
5. 用户因使用本组件进行生成、处理数据,由此引起或与有关的包括但不限于利润损失、资料损失、业务中断的损害赔偿或其它商业损害赔偿或损失,需由用户自行承担。
6. 如若发生赔偿、退款等行为,赔偿、退款等累计金额不得超过用户实际支付给作者的总金额。
7. 已授予的授权许可包括免费授权和已购买的原生录音插件、uts插件均仅限在授权指定的uni-app的应用标识AppID对应的项目上使用不可在其他项目上使用用户不得对本组件及其中的相关信息擅自出租、出借、销售、逆向工程、破解不得在未取得作者授权的情况下借助本组件发展与本组件有关联的衍生软件产品、服务、插件、外挂等。
8. 用户不得使用本组件从事违反法律法规政策、破坏公序良俗、损害公共利益的行为。

@ -1,88 +0,0 @@
{
"id": "Recorder-UniCore",
"displayName": "跨平台Recorder录音插件支持多种格式、音频可视化、实时上传、语音识别",
"version": "1.0.250331",
"description": "支持H5、Android iOS App、微信小程序mp3 wav pcm g711a g711u ogg amr 录音格式;实时帧回调处理 音频转码 波形动画显示 ASR语音转文字 无录制时长限制",
"keywords": [
"Recorder-UniCore",
"recorder-core",
"RecordApp",
"record",
"recording"
],
"repository": "https://github.com/xiangyuecn/Recorder",
"engines": {
"HBuilderX": "^3.6.11"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": "753610399"
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "录音权限"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y",
"app-uvue": "n",
"app-harmony": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "n",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "n",
"百度": "n",
"字节跳动": "n",
"QQ": "n",
"钉钉": "n",
"快手": "n",
"飞书": "n",
"京东": "n"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

@ -1,425 +0,0 @@
**
**
# Recorder-UniCore组件uni-app内使用RecordApp录音
本组件使用`Recorder`开源库来进行录音和音频数据处理,使用`RecordApp`和本组件内的`app-uni-support.js`来适配到不同平台环境下进行录音。
- 支持vue2、vue3、nvue
- 支持编译成H5、Android App、iOS App、微信小程序
- 支持已有的大部分录音格式mp3、wav、pcm、amr、ogg、g711a、g711u等
- 支持实时处理包括变速变调、实时上传、ASR语音转文字
- 支持可视化波形显示;可配置回声消除、降噪;**注意:不支持通话时录音**
- 支持PCM音频流式播放、完整播放App端用原生插件边录音边播放更流畅
- 支持离线使用,本组件和配套原生插件均不依赖网络
- App端有配套的[原生录音插件](https://ext.dcloud.net.cn/plugin?name=Recorder-NativePlugin)可供搭配使用,兼容性和体验更好
**详细文档(含Demo项目)** [https://github.com/xiangyuecn/Recorder/tree/master/app-support-sample/demo_UniApp](https://github.com/xiangyuecn/Recorder/tree/master/app-support-sample/demo_UniApp)
**Recorder开源库地址** [https://github.com/xiangyuecn/Recorder](https://github.com/xiangyuecn/Recorder)
如果github打不开可以[点此访问Gitee仓库地址](https://gitee.com/xiangyuecn/Recorder/tree/master/app-support-sample/demo_UniApp) 。
**
## 测试方法
**示例项目如果在HBuilder中编译失败请删掉node_modules目录重新手动执行npm install偶尔出现HBuilder自动创建项目依赖包不完整导致无法编译**
1. 在本插件市场页面右侧下载或导入示例项目或打开上面详细文档链接中的Demo源码
2. 在测试项目根目录执行 `npm install --registry=https://registry.npmmirror.com/` ,完成`recorder-core`依赖的安装
3. 在HBuilder中打开本测试项目文件夹
4. 在HBuilder中运行到浏览器、手机、微信小程序即可在不同环境下测试
5. 测试中提供了基础录音、播放、上传、WebSocket实时语音通话对讲、ASR语音识别等功能
**
**
# 集成到自己项目中
你可以直接参考上面的测试示例项目源码,里面的`main_recTest.vue`更容易入门示例项目中已经实现了很多功能简单使用可直接照抄Demo代码到你的项目中。
## 一、引入js文件
1. 在你的项目根目录安装`recorder-core``npm install recorder-core --registry=https://registry.npmmirror.com/`
2. 导入Recorder-UniCore组件插件市场下载本组件然后添加到你的项目中 `/uni_modules/Recorder-UniCore`
3. 项目配置好录音权限,参考下面的录音权限配置章节,**特别注意App后台录音配置、小程序权限声明**
4. 在需要录音的vue文件script内编写以下代码按需引入需要的js
``` html
<template>
<view>
... 建议template下只有一个根节点最外面套一层view如果不小心踩到了vue3的Fragments(multi-root 多个根节点)特性vue2编译会报错vue3不会可能会出现奇奇怪怪的兼容性问题
</view>
</template>
<script> /****/
//必须引入的Recorder核心文件路径是 /src/recorder-core.js 下同使用import、require都行
import Recorder from 'recorder-core' //注意如果未引用Recorder变量可能编译时会被优化删除如vue3 tree-shaking请改成 import 'recorder-core',或随便调用一下 Recorder.a=1 保证强引用
//必须引入的RecordApp核心文件文件路径是 /src/app-support/app.js
import RecordApp from 'recorder-core/src/app-support/app'
//所有平台必须引入的uni-app支持文件如果编译出现路径错误请把@换成 ../../ 这种)
<!-- import '@/uni_modules/Recorder-UniCore/app-uni-support.js' -->
/** 需要编译成微信小程序时,引入微信小程序支持文件 **/
// #ifdef MP-WEIXIN
import 'recorder-core/src/app-support/app-miniProgram-wx-support.js'
// #endif
/** H5、小程序环境中引入需要的格式编码器、可视化插件App环境中在renderjs中引入 **/
// 注意如果App中需要在逻辑层中调用Recorder的编码/转码功能,需要去掉此条件编译,否则会报未加载编码器的错误
// #ifdef H5 || MP-WEIXIN
//按需引入你需要的录音格式支持文件如果需要多个格式支持把这些格式的编码引擎js文件统统引入进来即可
import 'recorder-core/src/engine/mp3'
import 'recorder-core/src/engine/mp3-engine' //如果此格式有额外的编码引擎(*-engine.js的话必须要加上
//可选的插件支持项,把需要的插件按需引入进来即可
import 'recorder-core/src/extensions/waveview'
// #endif
// ... 这后面写页面代码用选项式API风格vue2、vue3、setup组合式API风格仅vue3都可以
</script>
```
5. 编译成app时默认需要额外提供一个renderjs模块请照抄下面这段代码放到vue文件末尾
``` html
<!-- #ifdef APP -->
<script module="yourModuleName" lang="renderjs"> //APIvue2vue3setupAPIimport vue
/**需要编译成App时你需要添加一个renderjs模块然后一模一样的import上面那些js微信的js除外
因为App中默认是在renderjsWebView中进行录音和音频编码
。如果配置了 RecordApp.UniWithoutAppRenderjs=true 且未调用依赖renderjs的功能时如nvue、可视化、仅H5中可用的插件
可不提供此renderjs模块同时逻辑层中需要将相关import的条件编译去掉**/
import 'recorder-core'
import RecordApp from 'recorder-core/src/app-support/app'
// import '../../uni_modules/Recorder-UniCore/app-uni-support.js' //renderjs中似乎不支持"@/"打头的路径,如果编译路径错误请改正路径即可
//按需引入你需要的录音格式支持文件,和插件
import 'recorder-core/src/engine/mp3'
import 'recorder-core/src/engine/mp3-engine'
import 'recorder-core/src/extensions/waveview'
export default {
mounted(){
//App的renderjs必须调用的函数传入当前模块this
RecordApp.UniRenderjsRegister(this);
},
methods: {
//这里定义的方法,在逻辑层中可通过 RecordApp.UniWebViewVueCall(this,'this.xxxFunc()') 直接调用
//调用逻辑层的方法,请直接用 this.$ownerInstance.callMethod("xxxFunc",{args}) 调用二进制数据需转成base64来传递
}
}
</script>
<!-- #endif -->
```
**
**
## 二、调用录音
``` javascript
/**在逻辑层中编写**/
//import ... 上面那些import代码
//var vue3This=getCurrentInstance().proxy; //当用vue3 setup组合式 API (Composition API) 编写时直接在import后面取到当前实例this在需要this的地方传vue3This变量即可其他的和选项式 API (Options API) 没有任何区别import {getCurrentInstance} from 'vue'详细可以参考Demo项目中的 page_vue3____composition_api.vue
//RecordApp.UniNativeUtsPlugin={ nativePlugin:true }; //App中启用配套的原生录音插件支持配置后会使用原生插件进行录音没有原生插件时依旧使用renderjs H5录音
//App中提升后台录音的稳定性配置了原生插件后可配置 `RecordApp.UniWithoutAppRenderjs=true` 禁用renderjs层音频编码WebWorker加速变成逻辑层中直接编码但会降低逻辑层性能后台运行时可避免部分手机WebView运行受限的影响
//App中提升后台录音的稳定性需要启用后台录音保活服务iOS不需要参考录音权限配置Android 9开始锁屏或进入后台一段时间后App可能会被禁止访问麦克风导致录音静音、无法录音renderjs中H5录音也受影响请调用配套原生插件的`androidNotifyService`接口,或使用第三方保活插件
export default {
data() { return {} } //视图没有引用到的变量无需放data里直接this.xxx使用
,mounted() {
this.isMounted=true;
//页面onShow时【必须调用】的函数传入当前组件this
RecordApp.UniPageOnShow(this);
}
,onShow(){ //onShow可能比mounted先执行页面可能还未准备好
if(this.isMounted) RecordApp.UniPageOnShow(this);
}
,methods:{
//请求录音权限
recReq(){
//编译成App时提供的授权许可编译成H5、小程序为免费授权可不填写如果未填写授权许可将会在App打开后第一次调用请求录音权限时弹出“未获得商用授权时App上仅供测试”提示框
//RecordApp.UniAppUseLicense='我已获得UniAppID=*****的商用授权';
//RecordApp.RequestPermission_H5OpenSet={ audioTrackSet:{ noiseSuppression:true,echoCancellation:true,autoGainControl:true } }; //这个是Start中的audioTrackSet配置在h5H5、App+renderjs中必须提前配置因为h5中RequestPermission会直接打开录音
RecordApp.UniWebViewActivate(this); //App环境下必须先切换成当前页面WebView
RecordApp.RequestPermission(()=>{
console.log("已获得录音权限,可以开始录音了");
},(msg,isUserNotAllow)=>{
if(isUserNotAllow){//用户拒绝了录音权限
//这里你应当编写代码进行引导用户给录音权限,不同平台分别进行编写
}
console.error("请求录音权限失败:"+msg);
});
}
//开始录音
,recStart(){
//Android App如果要后台录音需要启用后台录音保活服务iOS不需要需使用配套原生插件、或使用第三方保活插件
//RecordApp.UniNativeUtsPluginCallAsync("androidNotifyService",{ title:"正在录音" ,content:"正在录音中请勿关闭App运行" }).then(()=>{...}).catch((e)=>{...}) 注意必须RecordApp.RequestPermission得到权限后调用
//录音配置信息
var set={
type:"mp3",sampleRate:16000,bitRate:16 //mp3格式指定采样率hz、比特率kbps其他参数使用默认配置注意是数字的参数必须提供数字不要用字符串需要使用的type类型需提前把格式支持文件加载进来比如使用wav格式需要提前加载wav.js编码引擎
/*,audioTrackSet:{ //可选如果需要同时播放声音比如语音通话需要打开回声消除并不一定会生效打开后声音可能会从听筒播放部分环境下如小程序、App原生插件可调用接口切换成扬声器外放
//注意H5、App+renderjs中需要在请求录音权限前进行相同配置RecordApp.RequestPermission_H5OpenSet后此配置才会生效
echoCancellation:true,noiseSuppression:true,autoGainControl:true} */
,onProcess:(buffers,powerLevel,duration,sampleRate,newBufferIdx,asyncEnd)=>{
//全平台通用可实时上传发送数据配合Recorder.SampleData方法将buffers中的新数据连续的转换成pcm上传或使用mock方法将新数据连续的转码成其他格式上传可以参考Recorder文档里面的Demo片段列表 -> 实时转码并上传-通用版基于本功能可以做到实时转发数据、实时保存数据、实时语音识别ASR
//注意App里面是在renderjs中进行实际的音频格式编码操作此处的buffers数据是renderjs实时转发过来的修改此处的buffers数据不会改变renderjs中buffers所以不会改变生成的音频文件可在onProcess_renderjs中进行修改操作就没有此问题了如需清理buffers内存此处和onProcess_renderjs中均需要进行清理H5、小程序中无此限制
//注意如果你要用只支持在浏览器中使用的Recorder扩展插件App里面请在renderjs中引入此扩展插件然后在onProcess_renderjs中调用这个插件H5可直接在这里进行调用小程序不支持这类插件如果调用插件的逻辑比较复杂建议封装成js文件这样逻辑层、renderjs中直接import不需要重复编写
//H5、小程序等可视化图形绘制直接运行在逻辑层App里面需要在onProcess_renderjs中进行这些操作
// #ifdef H5 || MP-WEIXIN
if(this.waveView) this.waveView.input(buffers[buffers.length-1],powerLevel,sampleRate);
// #endif
/*实时释放清理内存用于支持长时间录音在指定了有效的type时编码器内部可能还会有其他缓冲必须同时提供takeoffEncodeChunk才能清理内存否则type需要提供unknown格式来阻止编码器内部缓冲App的onProcess_renderjs中需要进行相同操作
if(this.clearBufferIdx>newBufferIdx){ this.clearBufferIdx=0 } //重新录音了就重置
for(var i=this.clearBufferIdx||0;i<newBufferIdx;i++) buffers[i]=null;
this.clearBufferIdx=newBufferIdx; */
}
,onProcess_renderjs:`function(buffers,powerLevel,duration,sampleRate,newBufferIdx,asyncEnd){
//App中在这里修改buffers会改变生成的音频文件但注意buffers会先转发到逻辑层onProcess后才会调用本方法因此在逻辑层的onProcess中需要重新修改一遍
//本方法可以返回truerenderjs中的onProcess将开启异步模式处理完后调用asyncEnd结束异步注意这里异步修改的buffers一样的不会在逻辑层的onProcess中生效
//App中是在renderjs中进行的可视化图形绘制因此需要写在这里this是renderjs模块的this也可以用This变量如果代码比较复杂请直接在renderjs的methods里面放个方法xxxFunc这里直接使用this.xxxFunc(args)进行调用
if(this.waveView) this.waveView.input(buffers[buffers.length-1],powerLevel,sampleRate);
/*和onProcess中一样进行释放清理内存用于支持长时间录音
if(this.clearBufferIdx>newBufferIdx){ this.clearBufferIdx=0 } //重新录音了就重置
for(var i=this.clearBufferIdx||0;i<newBufferIdx;i++) buffers[i]=null;
this.clearBufferIdx=newBufferIdx; */
}`
,onProcessBefore_renderjs:`function(buffers,powerLevel,duration,sampleRate,newBufferIdx){
//App中本方法会在逻辑层onProcess之前调用因此修改的buffers会转发给逻辑层onProcess本方法没有asyncEnd参数不支持异步处理
//一般无需提供本方法只用onProcess_renderjs就行renderjs的onProcess内部调用过程onProcessBefore_renderjs -> 转发给逻辑层onProcess -> onProcess_renderjs
}`
,takeoffEncodeChunk:true?null:(chunkBytes)=>{
//全平台通用实时接收到编码器编码出来的音频片段数据chunkBytes是Uint8Array二进制数据可以实时上传发送出去
//App中如果未配置RecordApp.UniWithoutAppRenderjs时建议提供此回调因为录音结束后会将整个录音文件从renderjs传回逻辑层由于uni-app的逻辑层和renderjs层数据交互性能实在太拉跨了大点的文件传输会比较慢提供此回调后可避免Stop时产生超大数据回传
//App中使用原生插件时可方便的将数据实时保存到同一文件第一帧时append:false新建文件后面的append:true追加到文件
//RecordApp.UniNativeUtsPluginCallAsync("writeFile",{path:"xxx.mp3",append:回调次数!=1, dataBase64:RecordApp.UniBtoa(chunkBytes.buffer)}).then(...).catch(...)
}
,takeoffEncodeChunk_renderjs:true?null:`function(chunkBytes){
//App中这里可以做一些仅在renderjs中才生效的事情不提供也行this是renderjs模块的this也可以用This变量
}`
,start_renderjs:`function(){
//App中可以放一个函数在Start成功时renderjs中会先调用这里的代码this是renderjs模块的this也可以用This变量
//放一些仅在renderjs中才生效的事情比如初始化不提供也行
}`
,stop_renderjs:`function(arrayBuffer,duration,mime){
//App中可以放一个函数在Stop成功时renderjs中会先调用这里的代码this是renderjs模块的this也可以用This变量
//放一些仅在renderjs中才生效的事情不提供也行
}`
};
RecordApp.UniWebViewActivate(this); //App环境下必须先切换成当前页面WebView
RecordApp.Start(set,()=>{
console.log("已开始录音");
//【稳如老狗WDT】可选的监控是否在正常录音有onProcess回调如果长时间没有回调就代表录音不正常
//var wdt=this.watchDogTimer=setInterval ... 请参考示例Demo的main_recTest.vue中的watchDogTimer实现
//创建音频可视化图形绘制App环境下是在renderjs中绘制H5、小程序等是在逻辑层中绘制因此需要提供两段相同的代码
//view里面放一个canvascanvas需要指定宽高下面style里指定了300*100
//<canvas type="2d" class="recwave-WaveView" style="width:300px;height:100px"></canvas>
RecordApp.UniFindCanvas(this,[".recwave-WaveView"],`
this.waveView=Recorder.WaveView({compatibleCanvas:canvas1, width:300, height:100});
`,(canvas1)=>{
this.waveView=Recorder.WaveView({compatibleCanvas:canvas1, width:300, height:100});
});
},(msg)=>{
console.error("开始录音失败:"+msg);
});
}
//暂停录音
,recPause(){
if(RecordApp.GetCurrentRecOrNull()){
RecordApp.Pause();
console.log("已暂停");
}
}
//继续录音
,recResume(){
if(RecordApp.GetCurrentRecOrNull()){
RecordApp.Resume();
console.log("继续录音中...");
}
}
//停止录音
,recStop(){
//RecordApp.UniNativeUtsPluginCallAsync("androidNotifyService",{ close:true }) //关闭Android App后台录音保活服务
RecordApp.Stop((arrayBuffer,duration,mime)=>{
//全平台通用arrayBuffer是音频文件二进制数据可以保存成文件或者发送给服务器
//App中如果在Start参数中提供了stop_renderjsrenderjs中的函数会比这个函数先执行
//注意当Start时提供了takeoffEncodeChunk后你需要自行实时保存录音文件数据因此Stop时返回的arrayBuffer的长度将为0字节
//如果是H5环境也可以直接构造成Blob/File文件对象和Recorder使用一致
// #ifdef H5
var blob=new Blob([arrayBuffer],{type:mime});
console.log(blob, (window.URL||webkitURL).createObjectURL(blob));
var file=new File([arrayBuffer],"recorder.mp3");
//uni.uploadFile({file:file, ...}) //参考demo中的test_upload_saveFile.vue
// #endif
//如果是App、小程序环境可以直接保存到本地文件然后调用相关网络接口上传
// #ifdef APP || MP-WEIXIN
RecordApp.UniSaveLocalFile("recorder.mp3",arrayBuffer,(savePath)=>{
console.log(savePath); //app保存的文件夹为`plus.io.PUBLIC_DOWNLOADS`,小程序为 `wx.env.USER_DATA_PATH` 路径
//uni.uploadFile({filePath:savePath, ...}) //参考demo中的test_upload_saveFile.vue
},(errMsg)=>{ console.error(errMsg) });
// #endif
},(msg)=>{
console.error("结束录音失败:"+msg);
});
}
}
}
```
**
**
**
**
# 录音权限配置、需要注意的细节
## 编译成H5时录音和权限
编译成H5时录音功能由Recorder H5提供无需额外处理录音权限。
**
## 编译成微信小程序时录音和权限
编译成微信小程序时,录音功能由小程序的`RecorderManager`提供,屏蔽了微信原有的底层细节(无录音时长限制)。
小程序录音需要用户授予录音权限,调用`RecordApp.RequestPermission`的时候会检查是否能正常录音,如果用户拒绝了录音权限,会进入错误回调,回调里面你应当编写代码检查`wx.getSetting`中的`scope.record`录音权限,然后引导用户进行授权(可调用`wx.openSetting`打开设置页面,方便用户给权限)。
**注意:上架小程序需要到小程序管理后台《[用户隐私保护指引](https://developers.weixin.qq.com/miniprogram/dev/framework/user-privacy/miniprogram-intro.html)》中声明录音权限,否则正式版将无法调用录音功能(请求权限时会直接走错误回调)。**
更多细节请参考 [miniProgram-wx](https://gitee.com/xiangyuecn/Recorder/tree/master/app-support-sample/miniProgram-wx) 测试项目文档。
**
## 编译成App时录音和权限
编译成App录音时分两种情况
1. 默认未配置`RecordApp.UniNativeUtsPlugin`未使用原生录音插件和uts插件会在renderjs中使用Recorder H5进行录音录音数据会实时回传到逻辑层。
2. 配置了`RecordApp.UniNativeUtsPlugin`使用原生录音插件或uts插件时会直接调用原生插件进行录音录音数据默认会传递到renderjs中进行音频编码处理WebWorker加速然后再实时回传到逻辑层如果配置了`RecordApp.UniWithoutAppRenderjs=true`时,音频编码处理将会在逻辑层中直接处理。
当App是在renderjs中使用H5进行录音时未使用原生录音插件和uts插件iOS上只支持14.3以上版本,**且iOS上每次进入页面后第一次请求录音权限时、或长时间无操作再请求录音权限时WebView均会弹出录音权限对话框**不同旧iOS版本低于iOS17下H5录音可能存在的问题在App中同样会存在使用配套的[原生录音插件](https://ext.dcloud.net.cn/plugin?name=Recorder-NativePlugin)或uts插件时无以上问题和版本限制uts插件开发中暂不可用Android也无以上问题。
当音频编码是在renderjs中进行处理时录音结束后会将整个录音文件传回逻辑层由于uni-app的逻辑层和renderjs层大点的文件传输会比较慢**建议Start时使用takeoffEncodeChunk实时获取音频文件数据可避免Stop时产生超大数据回传**;配置了`RecordApp.UniWithoutAppRenderjs=true`后,因为音频编码直接是在逻辑层中进行,将不存在传输性能损耗,但会影响逻辑层的性能(正常情况轻微不明显),需要配套使用原生录音插件才可以进行此项配置。
在调用`RecordApp.RequestPermission`的时候,`Recorder-UniCore`组件会自动处理好App的系统录音权限只需要在uni-app项目的 `manifest.json` 中配置好Android和iOS的录音权限声明。
```
//Android需要勾选的权限第二个也必须勾选
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
【注意】Android如果需要在后台录音需要启用后台录音保活服务Android 9开始锁屏或进入后台一段时间后App可能会被禁止访问麦克风导致录音静音、无法录音renderjs中H5录音、原生插件录音均受影响请调用配套原生插件的`androidNotifyService`接口,或使用第三方保活插件
//iOS需要声明的权限
NSMicrophoneUsageDescription
【注意】iOS需要在 `App常用其它设置`->`后台运行能力`中提供`audio`配置不然App切到后台后立马会停止录音
```
**
## PCM音频流式播放、语音通话、回声消除、声音外放
在App、H5中均可使用H5版的[BufferStreamPlayer](https://gitee.com/xiangyuecn/Recorder/blob/master/src/extensions/buffer_stream.player.js)来实时流式播放语音其中App中需要在renderjs中加载BufferStreamPlayer在逻辑层中调用`RecordApp.UniWebViewVueCall`等方法将逻辑层中接收到的实时语音数据发送到renderjs中播放播放声音的同时进行录音声音可能会被录进去产生回声因此一般需要打开回声消除调用代码参考demo中的[test_realtime_voice.vue](https://gitee.com/xiangyuecn/Recorder/blob/master/app-support-sample/demo_UniApp/pages/recTest/test_realtime_voice.vue)。
App中如果搭配使用了配套的[原生录音插件](https://ext.dcloud.net.cn/plugin?name=Recorder-NativePlugin)可以调用原生实现的PcmPlayer播放器实时流式播放PCM音频边录音边播放更流畅同时也支持完整播放比如AI语音合成的播放调用代码参考demo中的[test_player_nativePlugin_pcmPlayer.vue](https://gitee.com/xiangyuecn/Recorder/blob/master/app-support-sample/demo_UniApp/pages/recTest/test_player_nativePlugin_pcmPlayer.vue)。
微信小程序请参考 [miniProgram-wx](https://gitee.com/xiangyuecn/Recorder/tree/master/app-support-sample/miniProgram-wx) 文档里面的同名章节使用WebAudioContext播放。
配置audioTrackSet可尝试打开回声消除或者切换听筒播放或外放打开回声消除时一般会转为听筒播放显著降低回声。
``` js
//打开回声消除
RecordApp.Start({
... 更多配置参数请参考RecordApp文档
//此配置App、H5、小程序均可打开回声消除注意H5、App+renderjs中需要在请求录音权限前进行相同配置RecordApp.RequestPermission_H5OpenSet后此配置才会生效
,audioTrackSet:{echoCancellation:true,noiseSuppression:true,autoGainControl:true}
//Android指定麦克风源App搭配原生插件、小程序可用0 DEFAULT 默认音频源1 MIC 主麦克风5 CAMCORDER 相机方向的麦6 VOICE_RECOGNITION 语音识别7 VOICE_COMMUNICATION 语音通信(带回声消除)
,android_audioSource:7 //提供此配置时优先级比audioTrackSet更高默认值为0
//iOS的AVAudioSession setCategory的withOptions参数值App搭配原生插件可用取值请参考配套原生插件文档中的iosSetDefault_categoryOptions
//,ios_categoryOptions:0x1|0x4 //默认值为5(0x1|0x4)
});
//App搭配原生插件时尝试切换听筒播放或外放
await RecordApp.UniNativeUtsPluginCallAsync("setSpeakerOff",{off:true或false});
//小程序尝试切换
wx.setInnerAudioOption({ speakerOn:false或true })
//H5不支持切换
```
**
**
**
# 详细文档、RecordApp方法、属性文档
请先阅读 [demo_UniApp文档](https://gitee.com/xiangyuecn/Recorder/tree/master/app-support-sample/demo_UniApp)含Demo项目更高级使用还需深入阅读 [Recorder文档](https://gitee.com/xiangyuecn/Recorder)、[RecordApp文档](https://gitee.com/xiangyuecn/Recorder/tree/master/app-support-sample) 均为完整的一个README.md文档Recorder文档中包含了更丰富的示例代码基础录音、实时处理、格式转码、音频分析、音频混音、音频生成 等等大部分能在uniapp中直接使用。
**
**
**
# 本组件的授权许可限制
**本组件内的app-uni-support.js文件在uni-app中编译到App平台时仅供测试用App平台包括Android App、iOS App不可用于正式发布或商用正式发布或商用需先到DCloud插件市场购买[此带授权的插件](https://ext.dcloud.net.cn/plugin?name=Recorder-NativePlugin-Android)费用为¥199元赠送Android版原生插件即可获得授权许可**编译到其他平台时无此授权限制比如H5、小程序均为免费授权。
在App中如果未获得授权许可将会在App打开后第一次调用`RecordApp.RequestPermission`请求录音权限时弹出“未获得商用授权时App上仅供测试”提示框。
在DCloud插件市场购买了[带授权的插件](https://ext.dcloud.net.cn/plugin?name=Recorder-NativePlugin-Android)获得了授权后,请在调用`RecordApp.RequestPermission`请求录音权限前,赋值`RecordApp.UniAppUseLicense="我已获得UniAppID=***的商用授权"`星号为你项目的uni-app应用标识就不会弹提示框了或者直接使用配套的[原生录音插件](https://ext.dcloud.net.cn/plugin?name=Recorder-NativePlugin),设置`RecordApp.UniNativeUtsPlugin`参数后,也不会弹提示框;其他情况请联系作者咨询,更多细节请参考[本组件的GitHub文档](https://gitee.com/xiangyuecn/Recorder/tree/master/app-support-sample/demo_UniApp)。
获取授权、需要技术支持、或有不清楚的地方可以联系我们客服联系方式QQ 1251654593 或者直接联系作者QQ 753610399 (回复可能没有客服及时)。
插件开发维护不易,感谢支持~
**
**

@ -1,13 +0,0 @@
## 1.5.02025-04-01
返回完整的message、小程序解析逻辑优化改为使用fetch-event-source的解析逻辑
## 1.4.12025-03-28
onOepn回调方法返回sse请求的response对象、及返回err错误信息
## 1.4.02025-03-24
解析微信小程序返回的数据
## 1.3.22025-03-16
1. 示例项目添加了sse server供调试。
2. 发生了错误接口会无限运行的问题解决现在发生了错误会调用stop方法停止。
## 1.3.12025-03-10
修复了get请求无法stop的bug
## 1.3.02025-03-06
插件修改为 uni_modules 模式

@ -1,126 +0,0 @@
<script>
export default {
props: {},
data() {
return {
stopCount: 0,
renderjsData: {
url: "",
key: 0,
body: "",
method: ""
}
}
},
methods: {
stopChat() {
this.stopCount += 1
},
/**
* 开始chat对话
*/
startChat(config) {
const { body } = config;
this.renderjsData = Object.assign({}, this.renderjsData, {
key: this.renderjsData.key + 1,
...config,
body: body ? JSON.stringify(body) : 0,
});
},
open(...args) {
this.$emit("onInnerOpen", ...args)
},
message(msg) {
this.$emit("onInnerMessage", msg)
},
error(...args) {
this.$emit("onInnerError", ...args)
this.stopChat();
},
finish() {
this.$emit("onInnerFinish")
}
},
}
</script>
<script module="chat" lang="renderjs">
import { fetchEventSource } from '../fetch-event-source';
export default {
data() {
return {
ctrl: null,
}
},
methods: {
objToJson(obj) {
const json = {};
for (const key in obj) {
const val = obj[key];
if (typeof val === "string" || typeof val === 'number' || typeof val === 'boolean') {
json[key] = val;
}
}
return json;
},
/**
* 停止生成
*/
stopChatCore() {
this.ctrl?.abort();
},
/**
* 开始对话
*/
startChatCore({ url, body, headers, method }) {
if (!url) return;
try {
this.ctrl = new AbortController();
fetchEventSource(
url,
{
readJson: true,
method,
openWhenHidden: true,
signal: this.ctrl.signal,
headers: {
"Content-Type": "application/json",
...headers,
},
body: body ? body : undefined,
onopen: (response) => {
this.$ownerInstance.callMethod('open', this.objToJson(response));
},
onmessage: (data) => {
this.$ownerInstance.callMethod('message', data);
},
onerror: (err) => {
console.log(err)
this.$ownerInstance.callMethod('error', JSON.stringify(err));
},
}).then(() => {
this.$ownerInstance.callMethod('finish');
}).catch(err => {
console.log(err)
this.$ownerInstance.callMethod('error', err);
})
} catch (e) {
console.log(e);
}
}
}
}
</script>
<template>
<view
:renderjsData="renderjsData"
:change:renderjsData="chat.startChatCore"
:stopCount="stopCount"
:change:stopCount="chat.stopChatCore"
/>
</template>

@ -1,76 +0,0 @@
<script>
import {getLines, getMessages} from "../fetch-event-source/parse"
let requestTask;
export default {
props: {},
data() {
return {
onChunk: undefined
}
},
mounted() {
const onLine = getMessages(() => {}, () => {}, (line) => {
this.$emit("onInnerMessage", line)
})
this.onChunk = getLines(onLine);
},
methods: {
stopChat() {
requestTask.offChunkReceived(this.listener)
requestTask.abort();
},
decode(data) {
return decodeURIComponent(escape(String.fromCharCode(...data)));
},
/**
* 开始chat对话
* @param body
* @param url
* @param headers
* @param method
*/
startChat({ body, url, headers, method }) {
requestTask = uni.request({
url: url,
method,
header: {
Accept: 'text/event-stream',
...headers,
},
data: body,
enableChunked: true,
responseType: 'arraybuffer',
success: (res) => {
console.log('0000000 success',res,' url:',url)
},
fail: (error) => {
console.log('0000000 fail',error,' url:',url)
this.$emit("onInnerError", error)
},
complete: () => {
this.$emit("onInnerFinish")
},
});
requestTask.onChunkReceived(this.listener)
this.$emit("onInnerOpen", requestTask)
},
listener({ data }) {
const type = Object.prototype.toString.call(data);
if (type ==="[object Uint8Array]") {
} else if (data instanceof ArrayBuffer) {
data = new Uint8Array(data);
}
this.onChunk(data)
},
},
}
</script>
<template>
<view />
</template>

@ -1,89 +0,0 @@
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import { getBytes, getLines, getMessages } from './parse';
export const EventStreamContentType = 'text/event-stream';
const DefaultRetryInterval = 1000;
const LastEventId = 'last-event-id';
export function fetchEventSource(input, _a) {
var { signal: inputSignal, headers: inputHeaders, onopen: inputOnOpen, onmessage, onclose, onerror, openWhenHidden, fetch: inputFetch } = _a, rest = __rest(_a, ["signal", "headers", "onopen", "onmessage", "onclose", "onerror", "openWhenHidden", "fetch"]);
return new Promise((resolve, reject) => {
const headers = Object.assign({}, inputHeaders);
if (!headers.accept) {
headers.accept = EventStreamContentType;
}
let curRequestController;
function onVisibilityChange() {
curRequestController.abort();
if (!document.hidden) {
create();
}
}
if (!openWhenHidden) {
document.addEventListener('visibilitychange', onVisibilityChange);
}
let retryInterval = DefaultRetryInterval;
let retryTimer = 0;
function dispose() {
document.removeEventListener('visibilitychange', onVisibilityChange);
window.clearTimeout(retryTimer);
curRequestController.abort();
}
inputSignal === null || inputSignal === void 0 ? void 0 : inputSignal.addEventListener('abort', () => {
dispose();
resolve();
});
const fetch = inputFetch !== null && inputFetch !== void 0 ? inputFetch : window.fetch;
const onopen = inputOnOpen !== null && inputOnOpen !== void 0 ? inputOnOpen : defaultOnOpen;
async function create() {
var _a;
curRequestController = new AbortController();
try {
const response = await fetch(input, Object.assign(Object.assign({}, rest), { headers, signal: curRequestController.signal }));
await onopen(response);
await getBytes(response.body, getLines(getMessages(id => {
if (id) {
headers[LastEventId] = id;
}
else {
delete headers[LastEventId];
}
}, retry => {
retryInterval = retry;
}, onmessage)));
onclose === null || onclose === void 0 ? void 0 : onclose();
dispose();
resolve();
}
catch (err) {
if (!curRequestController.signal.aborted) {
try {
const interval = (_a = onerror === null || onerror === void 0 ? void 0 : onerror(err)) !== null && _a !== void 0 ? _a : retryInterval;
window.clearTimeout(retryTimer);
retryTimer = window.setTimeout(create, interval);
}
catch (innerErr) {
dispose();
reject(innerErr);
}
}
}
}
create();
});
}
function defaultOnOpen(response) {
const contentType = response.headers.get('content-type');
if (!(contentType === null || contentType === void 0 ? void 0 : contentType.startsWith(EventStreamContentType))) {
throw new Error(`Expected content-type to be ${EventStreamContentType}, Actual: ${contentType}`);
}
}
//# sourceMappingURL=fetch.js.map

@ -1,2 +0,0 @@
export { fetchEventSource, EventStreamContentType } from './fetch';
//# sourceMappingURL=index.js.map

@ -1,128 +0,0 @@
export async function getBytes(stream, onChunk) {
const reader = stream.getReader();
let result;
while (!(result = await reader.read()).done) {
onChunk(result.value);
}
}
export function getLines(onLine) {
let buffer;
let position;
let fieldLength;
let discardTrailingNewline = false;
return function onChunk(arr) {
if (buffer === undefined) {
buffer = arr;
position = 0;
fieldLength = -1;
}
else {
buffer = concat(buffer, arr);
}
const bufLength = buffer.length;
let lineStart = 0;
while (position < bufLength) {
if (discardTrailingNewline) {
if (buffer[position] === 10) {
lineStart = ++position;
}
discardTrailingNewline = false;
}
let lineEnd = -1;
for (; position < bufLength && lineEnd === -1; ++position) {
switch (buffer[position]) {
case 58:
if (fieldLength === -1) {
fieldLength = position - lineStart;
}
break;
case 13:
discardTrailingNewline = true;
case 10:
lineEnd = position;
break;
}
}
if (lineEnd === -1) {
break;
}
onLine(buffer.subarray(lineStart, lineEnd), fieldLength);
lineStart = position;
fieldLength = -1;
}
if (lineStart === bufLength) {
buffer = undefined;
}
else if (lineStart !== 0) {
buffer = buffer.subarray(lineStart);
position -= lineStart;
}
};
}
export function getMessages(onId, onRetry, onMessage) {
let message = newMessage();
let decoder;
// #ifdef MP-WEIXIN
decoder = {
decode(arraybuffer) {
return decodeURIComponent(escape(String.fromCharCode(...arraybuffer)))
}
};
// #endif
// #ifdef APP-PLUS || H5
decoder = new TextDecoder();
// #endif
return function onLine(line, fieldLength) {
if (line.length === 0) {
onMessage === null || onMessage === void 0 ? void 0 : onMessage(message);
message = newMessage();
}
else if (fieldLength > 0) {
const field = decoder.decode(line.subarray(0, fieldLength));
const valueOffset = fieldLength + (line[fieldLength + 1] === 32 ? 2 : 1);
const value = decoder.decode(line.subarray(valueOffset));
switch (field) {
case 'data':
message.data = message.data
? message.data + '\n' + value
: value;
break;
case 'event':
message.event = value;
break;
case 'id':
onId(message.id = value);
break;
case 'retry':
const retry = parseInt(value, 10);
if (!isNaN(retry)) {
onRetry(message.retry = retry);
}
break;
default:
const msg = decoder.decode(line, { stream: true });
message.data = msg
onMessage(message);
break;
}
}
};
}
function concat(a, b) {
const res = new Uint8Array(a.length + b.length);
res.set(a);
res.set(b, a.length);
return res;
}
function newMessage() {
return {
data: '',
event: '',
id: '',
retry: undefined,
};
}
//# sourceMappingURL=parse.js.map

@ -1,58 +0,0 @@
<template>
<!-- #ifdef MP-WEIXIN-->
<ChatWxApplet ref="chatRef" @onInnerOpen="open" @onInnerError="error" @onInnerMessage="message" @onInnerFinish="finish" />
<!-- #endif-->
<!-- #ifdef APP-PLUS || H5-->
<ChatAppAndWeb ref="chatRef" @onInnerOpen="open" @onInnerError="error" @onInnerMessage="message" @onInnerFinish="finish" />
<!-- #endif-->
</template>
<script>
// #ifdef MP-WEIXIN
import ChatWxApplet from "./children/ChatWxApplet.vue";
// #endif
// #ifdef APP-PLUS || H5
import ChatAppAndWeb from "./children/ChatAppAndWeb.vue";
// #endif
export default {
components: {
// #ifdef MP-WEIXIN
ChatWxApplet,
// #endif
// #ifdef APP-PLUS || H5
ChatAppAndWeb,
// #endif
},
methods: {
startChat(config) {
config["method"] = (config["method"] || "post").toUpperCase();
config["headers"] = config["headers"] || {};
console.log("this.$refs['chatRef']", this.$refs["chatRef"]);
this.$refs["chatRef"].startChat(config);
},
stopChat(...args) {
console.log("this.$refs['chatRef']stopChat", this.$refs["chatRef"]);
this.$refs["chatRef"].stopChat(...args);
},
open(...args) {
this.$emit("onOpen", ...args);
},
message(msg) {
this.$emit("onMessage", msg);
},
error(...args) {
this.$emit("onError", ...args);
},
finish() {
this.$emit("onFinish");
},
},
};
</script>

@ -1,45 +0,0 @@
// 检索字符,并且加上该字符的长度
const indexOfLen = (str) => {
const startIndex = str.indexOf(str);
if (startIndex !== -1) {
return startIndex + str.length
}
return -1;
}
/**
* 解析SSE数据
* sse返回的数据可能会有多个消息相连这里处理字符串返回数组
* @param data sse字符串数据
* @returns {*} 处理过后的数组
*/
export const parseSseData = (data) => {
try {
let lines = data.split("\n");
lines = lines.map(v => {
if (!v) return null;
let startInd = -1;
for (const ind of [indexOfLen("data: "), v.indexOf("{")]) {
if (ind !== -1) {
startInd = ind;
break;
}
}
if (startInd === -1) {
return v;
} else {
return v.substring(startInd, v.length).trim();
}
}).filter(Boolean);
return lines;
} catch (e) {
console.warn("解析失败:", e);
return data;
}
}

@ -1,65 +0,0 @@
{
"id": "gao-ChatSSEClient",
"name": "sse 客户端组件支持兼容v2、v3、安卓、ios、浏览器、微信小程序",
"displayName": "sse 客户端组件支持兼容v2、v3、安卓、ios、浏览器、微信小程序",
"version": "1.5.0",
"description": "sse 客户端组件支持兼容v2、v3、安卓、ios、浏览器、微信小程序",
"repository": "https://github.com/gaozhenqiang/uniapp-chatSSEClient",
"keywords": [
"sse",
"chat",
"微信小程序sse",
"流式接口",
"流式输出"
],
"dcloudext": {
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"contact": {
"qq": "1933669775"
},
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
}
},
"uni_modules": {
"platforms": {
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-harmony": "u",
"app-nvue": "u",
"app-uvue": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y"
}
}
}
}
}

@ -1,125 +0,0 @@
# sse 客户端组件支持v2、v3、安卓、ios、浏览器、微信小程序
## 使用说明
### 导入组件
点击右上角 `下载插件并导入HBuilderX`
uniapp插件地址https://ext.dcloud.net.cn/plugin?id=20971
或者你可以参考我的示例
### 示例代码
```javascript
<template>
<button @click="start">开始</button>
<button @click="stop">停止</button>
<template v-if="loading">
<view>{{ openLoading ? "正在连接sse..." : '连接完成!' }}</view>
<view>{{ loading ? "加载中..." : '' }}</view>
</template>
<view>
{{ responseText }}
</view>
<gao-ChatSSEClient
ref="chatSSEClientRef"
@onOpen="openCore"
@onError="errorCore"
@onMessage="messageCore"
@onFinish="finishCore"
/>
</template>
<script setup>
import { ref } from 'vue'
const chatSSEClientRef = ref(null);
const responseText = ref("");
const loading = ref(false);
const openLoading = ref(false);
const openCore = (response) => {
openLoading.value = false;
console.log("open sse", response);
}
const errorCore = (err) => {
console.log("error sse", err);
}
const messageCore = (msg) => {
console.log("message sse", msg);
responseText.value += `${msg}\n`
}
const finishCore = () => {
console.log("finish sse")
loading.value = false;
}
const start = () => {
if (loading.value) return;
openLoading.value = true;
loading.value = true;
responseText.value = "";
chatSSEClientRef.value.startChat({
/**
* 将它换成你的地址
* 注意:
* 如果使用 sse-server.js 要在手机端使用的话请确保你的手机和电脑处在一个局域网下并且是正常的ip地址
*/
url: import.meta.env.VITE_CHAT_URL || 'http://localhost:3000/sse',
// 请求头
headers: {
Authorization: import.meta.env.VITE_CHAT_AUTHORIZATION,
},
// 默认为 post
method: 'post',
body: {
"stream":true,
"model": "deepseek-chat",
"messages": [
{"role": "system", "content": "你是来自艺咖科技的数字员工,你的名字叫小咖。"}]
}
})
}
const stop = () => {
chatSSEClientRef.value.stopChat()
console.log("stop");
}
</script>
```
# 温馨提示
示例项目根目录的`sse-server.js`文件提供了一个简单的sse测试服务使用 `node sse-server.js`运行
**提出问题之前请先确保你的接口没有问题**
---
**请仔细阅读我提供的示例代码。**
**如果你的程序有问题请先下载我提供的示例项目调试!**
---
如果想了解原理请看我掘金的文章: [点击前往](https://juejin.cn/post/7435632766375084082)
本插件依赖于 `fetch-event-source`将编辑后的js集成因为我修改了原来库解析的逻辑使其更适用于中国宝宝体质。
---
**如果这个组件解决了你的问题,麻烦去[github](https://github.com/gaozhenqiang/uniapp-chatSSEClient/) 帮我点个赞,谢谢大家**
有新需求或者bug可以在github上提issues或者加我q `1933669775`
# 常见问题
## ios报错TypeError: Load failed
后端接口处理一下跨域即可解决。

@ -126,7 +126,7 @@ let data = {
failback(resData);
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/index?path=/' + that.getPathCopy().path + '&level=' + that.getPathCopy().level
url: '/root/person/loginIndex?path=/' + that.getPathCopy().path + '&level=' + that.getPathCopy().level
});
}, 1500);
} else if (resData.status == 500 || resData.status == 502) {// 某种原因导致接口提示该状态码
@ -148,7 +148,7 @@ let data = {
});
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/index?path=/pages/home/index&level=' + that.getPathCopy().level
url: '/root/person/loginIndex?path=/pages/home/index&level=' + that.getPathCopy().level
});
}, 1500);
} else {// 其他异常

@ -6,6 +6,7 @@ let data = {
appid: 'wxa7b4864efbcff191', //
localBaseImg: "https://matripe-cms.oss-cn-beijing.aliyuncs.com/dailibaoming/APP/", // app图片前缀
cdnBaseImg: "https://matripe-cms.oss-cn-beijing.aliyuncs.com/dailibaoming/", // cdn图片公共前缀路径
v3BaseImg: "https://matripe-cms.oss-cn-beijing.aliyuncs.com/dailibaoming/v3", // cdn图片公共前缀路径
loginText: '请登录',
// #ifdef MP-WEIXIN
version: uni.getAccountInfoSync().miniProgram.version || "1.0.16",
@ -69,7 +70,7 @@ let data = {
let that = this;
if (!uni.getStorageSync("apply-token")) {
uni.reLaunch({
url: '/pages/login/index?path=/' + that.getPath().path + '&level=' + that.getPath().level
url: '/root/person/loginIndex?path=/' + that.getPath().path + '&level=' + that.getPath().level
});
return false;
} else {

@ -135,7 +135,7 @@ let data = {
failback(resData);
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/index?path=/' + that.wyyxgetPathCopy().path + '&level=' + that.wyyxgetPathCopy().level
url: '/root/person/loginIndex?path=/' + that.wyyxgetPathCopy().path + '&level=' + that.wyyxgetPathCopy().level
});
}, 1500);
} else if (resData.status == 500 || resData.status == 502) {// 某种原因导致接口提示该状态码
@ -157,7 +157,7 @@ let data = {
});
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/index?path=/pages/home/index&level=' + that.wyyxgetPathCopy().level
url: '/root/person/loginIndex?path=/pages/home/index&level=' + that.wyyxgetPathCopy().level
});
}, 1500);
} else {// 其他异常

Loading…
Cancel
Save