HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

ios审核一周搞定,分享下过程

正好遇到国外圣诞节,中间有一周的空档,所以这里说的一周是扣除圣诞节放假的,我的app提交赶在他们的节前。累计被驳回5回,每次一天(23号他们给我驳回了2次)。

这里就说下需要注意的地方:
1、必须要隐私条款连接;
2、6.5英寸的预览和截屏得有留海(这个是看到别人说的,就我们修改了下,所以没因为截屏被驳回);
3、标题和副标题这个要特别注意。看appstore中很多标题都是带附加说明的,但是现在应该是不允许的。我们提交的app被驳回3次都是因为标题和副标题的原因:
a、第一次提交时,副标题添加了 一段说明他们认为是有关键词堆砌嫌疑,所以驳回了;
b、第二次我们将副标题的说明修改了,然后在增加了app名称的附加说明 “ app名称 - abc”,在appstore上可以看见很多类似的写法,不过那是以前现在貌似不行(反正被驳回了2次就不想再试了);
c、第三次我们去除了app名称上的附加说明“ - 附加说明”只留下单纯的app名称,然后附加标题采用“「app名称」期货交易数据分析与排名”,并说明为啥要用这个,接着跟他们商量我是否可以保留这个;
d、又一次被驳回,不过这次他们让我保留副标题说明了,驳回的原因是因为用户上传头像要权限,这个权限我没说明。在manifest.json中的“APP模块权限配置”中的“ios隐私信息访问的许可描述”添加说明后重新编译一个版本提交了上去。
e、最后一次被驳回,说我的副标题“「app名称」期货交易数据分析与排名”不符合政策。我在回复中把上次说明的情况再跟他说了一遍,并询问是否是同一个人审核的。最后问他,如果这个说明不符合政策,能否给个建议。这次提交上去后他们给通过了。

我感觉最主要的还是要尊重他们,我每次回复总是心平气和,因为这个其实是他们的工作,没必要耍小脾气。但是并不是说要低三下气,只是陈述事实即可。其实看下我们的副标题“期货交易数据分析与排名”,这个本身确实很难说没关键词堆砌嫌疑,因为含有“期货交易”,但是跟他们说明情况后他们放行了。

继续阅读 »

正好遇到国外圣诞节,中间有一周的空档,所以这里说的一周是扣除圣诞节放假的,我的app提交赶在他们的节前。累计被驳回5回,每次一天(23号他们给我驳回了2次)。

这里就说下需要注意的地方:
1、必须要隐私条款连接;
2、6.5英寸的预览和截屏得有留海(这个是看到别人说的,就我们修改了下,所以没因为截屏被驳回);
3、标题和副标题这个要特别注意。看appstore中很多标题都是带附加说明的,但是现在应该是不允许的。我们提交的app被驳回3次都是因为标题和副标题的原因:
a、第一次提交时,副标题添加了 一段说明他们认为是有关键词堆砌嫌疑,所以驳回了;
b、第二次我们将副标题的说明修改了,然后在增加了app名称的附加说明 “ app名称 - abc”,在appstore上可以看见很多类似的写法,不过那是以前现在貌似不行(反正被驳回了2次就不想再试了);
c、第三次我们去除了app名称上的附加说明“ - 附加说明”只留下单纯的app名称,然后附加标题采用“「app名称」期货交易数据分析与排名”,并说明为啥要用这个,接着跟他们商量我是否可以保留这个;
d、又一次被驳回,不过这次他们让我保留副标题说明了,驳回的原因是因为用户上传头像要权限,这个权限我没说明。在manifest.json中的“APP模块权限配置”中的“ios隐私信息访问的许可描述”添加说明后重新编译一个版本提交了上去。
e、最后一次被驳回,说我的副标题“「app名称」期货交易数据分析与排名”不符合政策。我在回复中把上次说明的情况再跟他说了一遍,并询问是否是同一个人审核的。最后问他,如果这个说明不符合政策,能否给个建议。这次提交上去后他们给通过了。

我感觉最主要的还是要尊重他们,我每次回复总是心平气和,因为这个其实是他们的工作,没必要耍小脾气。但是并不是说要低三下气,只是陈述事实即可。其实看下我们的副标题“期货交易数据分析与排名”,这个本身确实很难说没关键词堆砌嫌疑,因为含有“期货交易”,但是跟他们说明情况后他们放行了。

收起阅读 »

【数位云】开放平台上线,共建位置服务生态~欢迎体验

开发者中心

12月13日,数位宣布即将上线位置服务开发者平台,面向行业开发者赋能采集、定位等基础能力,支持安卓、iOS、小程序等不同平台,帮助行业缩短开发周期,节省开发成本,以更高效的方式获取领先的精准室内位置识别能力。

定 位

基于数位自主研发的核心专利技术,赋能开发者店铺级精准室内位置识别能力,可以在不依赖硬件铺设的前提下,精准区分楼层、店铺、店内及店外。目前,这一定位能力已覆盖全国209座城市超4000万高价值室内场景并不断扩张,为开发者搭建精准室内定位能力通用平台。

采 集

针对不同行业面向的场景差异,数位开发者平台为开发者提供目标场景自采集功能,将原本专业采集团队才能完成的系统性采集工作,面向行业零门槛开放。开发者只需到达符合采集标准的目标采集地点,通过APP进行2步操作,10秒即可获取目标场景的精准定位能力。

目前,数位定位+采集能力已经为本地生活、新闻资讯、视频娱乐、电商母婴、考勤办公等诸多领域的服务类、工具类、娱乐类APP提供服务,赋能人员管理、定位打卡、用户画像、场景服务、数据分析、精细化运营等多种能力,打造从采集到定位,从定位到场景服务,从服务到数据分析的一整套位置服务系统。

即日起,开发者可联系数位工作人员获取免费体验工具。

未来,数位还将陆续开放地图导航、近场服务、数据分析等能力,丰富位置服务形态,构建位置服务生态,与行业共建全域感知的智能世界。

开发者平台链接地址:https://cloud.papakaka.com/flash/#/dashboard

体验中如有疑问
扫描或识别下方二维码
添加数位小助手
随时在线解答您的问题

继续阅读 »

12月13日,数位宣布即将上线位置服务开发者平台,面向行业开发者赋能采集、定位等基础能力,支持安卓、iOS、小程序等不同平台,帮助行业缩短开发周期,节省开发成本,以更高效的方式获取领先的精准室内位置识别能力。

定 位

基于数位自主研发的核心专利技术,赋能开发者店铺级精准室内位置识别能力,可以在不依赖硬件铺设的前提下,精准区分楼层、店铺、店内及店外。目前,这一定位能力已覆盖全国209座城市超4000万高价值室内场景并不断扩张,为开发者搭建精准室内定位能力通用平台。

采 集

针对不同行业面向的场景差异,数位开发者平台为开发者提供目标场景自采集功能,将原本专业采集团队才能完成的系统性采集工作,面向行业零门槛开放。开发者只需到达符合采集标准的目标采集地点,通过APP进行2步操作,10秒即可获取目标场景的精准定位能力。

目前,数位定位+采集能力已经为本地生活、新闻资讯、视频娱乐、电商母婴、考勤办公等诸多领域的服务类、工具类、娱乐类APP提供服务,赋能人员管理、定位打卡、用户画像、场景服务、数据分析、精细化运营等多种能力,打造从采集到定位,从定位到场景服务,从服务到数据分析的一整套位置服务系统。

即日起,开发者可联系数位工作人员获取免费体验工具。

未来,数位还将陆续开放地图导航、近场服务、数据分析等能力,丰富位置服务形态,构建位置服务生态,与行业共建全域感知的智能世界。

开发者平台链接地址:https://cloud.papakaka.com/flash/#/dashboard

体验中如有疑问
扫描或识别下方二维码
添加数位小助手
随时在线解答您的问题

收起阅读 »

【建议】ctrl+f搜索结果总是在画面最下面或最上面,很不明显不便于查看代码上下文。

搜索

ctrl+f搜索结果总是在画面最下面或最上面,很不明显不便于查看代码上下文。
能否将结果高亮显示在中间,这样比较明显,且方便用户查看关键词的上下文,以便用户分别是否是想要的结果。
感谢!

ctrl+f搜索结果总是在画面最下面或最上面,很不明显不便于查看代码上下文。
能否将结果高亮显示在中间,这样比较明显,且方便用户查看关键词的上下文,以便用户分别是否是想要的结果。
感谢!

【建议】需要回到上个编辑位置的功能。感谢

alt+方向 通常要按好多次效率非常低。希望增加编辑位置的跳转,类似目前光标位置的跳转,只不过是历史编辑位置的跳转。感谢!

alt+方向 通常要按好多次效率非常低。希望增加编辑位置的跳转,类似目前光标位置的跳转,只不过是历史编辑位置的跳转。感谢!

Nvue 首页快速启动模式

v3

v3编译模式(HBuilderX 2.5.0+),支持 nvue 首页的快速启动,视图层层不等逻辑层初始化完毕,即可同时进行渲染。

启用方式

在 manifest.json 可视化视图,app其他常用设置里,勾上fast启动模式。

或者在 manifest.json 源码视图里 app-plus 节点下配置 nvueLaunchMode 为 fast。

注意事项

启用快速启动模式后,app启动时,逻辑层和首页视图层是同时初始化的。(非fast模式时,是逻辑层先初始化,然后才开始加载视图层)
由于视图层渲染时,逻辑层不一定初始化完毕,此时首页的nvue页面内不可以依赖逻辑层的状态,具体为:

  • 纯nvue + fast,vuex可用
  • V3 + fast,vuex不可用
  • getApp() 使用存在限制,具体参考:getApp()
继续阅读 »

v3编译模式(HBuilderX 2.5.0+),支持 nvue 首页的快速启动,视图层层不等逻辑层初始化完毕,即可同时进行渲染。

启用方式

在 manifest.json 可视化视图,app其他常用设置里,勾上fast启动模式。

或者在 manifest.json 源码视图里 app-plus 节点下配置 nvueLaunchMode 为 fast。

注意事项

启用快速启动模式后,app启动时,逻辑层和首页视图层是同时初始化的。(非fast模式时,是逻辑层先初始化,然后才开始加载视图层)
由于视图层渲染时,逻辑层不一定初始化完毕,此时首页的nvue页面内不可以依赖逻辑层的状态,具体为:

  • 纯nvue + fast,vuex可用
  • V3 + fast,vuex不可用
  • getApp() 使用存在限制,具体参考:getApp()
收起阅读 »

开启 optional chaining 语法

我是使用 uni-cli 构建的,所以开启 optional-chaining 语法只需要安装了@babel/plugin-proposal-optional-chaining 包后在项目下的 babel.config.js 中配置

const plugins = ["@babel/plugin-proposal-optional-chaining"];

就是在 plugins 中加上@babel/plugin-proposal-optional-chaining 就好了,但是有些玄学问题,昨天搞了半天这个语法都不能用各种报错,今天啥也没改重新开机就能用了。。。。

也就是说这样配置是没问题的,还报错就重启试试?

原文地址

继续阅读 »

我是使用 uni-cli 构建的,所以开启 optional-chaining 语法只需要安装了@babel/plugin-proposal-optional-chaining 包后在项目下的 babel.config.js 中配置

const plugins = ["@babel/plugin-proposal-optional-chaining"];

就是在 plugins 中加上@babel/plugin-proposal-optional-chaining 就好了,但是有些玄学问题,昨天搞了半天这个语法都不能用各种报错,今天啥也没改重新开机就能用了。。。。

也就是说这样配置是没问题的,还报错就重启试试?

原文地址

收起阅读 »

社区被恶意攻击了吧?

社区被恶意攻击了吧,某些平台是真小人啊! 鄙视
截图留个证据!
2019-12-27

社区被恶意攻击了吧,某些平台是真小人啊! 鄙视
截图留个证据!
2019-12-27

APP读取本地文件夹内视频播放

缓存 视频 App

需要用到的几个方法:

// 从本地缓存中同步获取指定 key 对应的内容。  
uni.getStorageSync(KEY)  

// 下载文件资源到本地,客户端直接发起一个 HTTP GET 请求,返回文件的本地临时路径。  
uni.downloadFile(OBJECT)  

// 保存视频到本地  
uni.saveFile(OBJECT)  

// 将 data 存储在本地缓存中指定的 key 中,会覆盖掉原来该 key 对应的内容,这是一个同步接口。  
uni.setStorageSync(KEY,DATA)

首先定义一个变量:获取本地缓存数据,看本地视频是存在。
获取已下载视频目录中的视频列表,删除无关视频。

const videoData = uni.getStorageSync('videoData')  
// 获取本地视频列表  
uni.getSavedFileList({  
    success: function (res) {  
        let fileList = res.fileList  
        console.log(fileList);  
        let needArr = videoData.map(item => item.videoAddress)  
        fileList.forEach((item, index) => {  
            if(needArr.includes(item.filePath)){  
                item.text = "需要这个视频"   
            }else{  
                // 删除视频列表中无关视频  
                uni.removeSavedFile({  
                    filePath: item.filePath,  
                    complete: function (res) {  
                        console.log(res);  
                    }  
                });  
            }  
        });  
        console.log(fileList)  

    }  
});  

写个条件判断:如果存在直接将视频地址插入页面进行播放,如果不存在则进行下载视频-保存视频-缓存本地链接。

const _this = this;  
// 判断是否有缓存  
if(videoData){  
    console.log('我是视频-有缓存')  
    _this.videoSrc = videoData  
}else{  
    console.log('我是视频-没有有缓存')  
    _this.videoSrc = url // 我是在线链接  
    // 下载视频  
    _this.downFile(videoUrlYi,'videoData');  
}  

// 下载视频  
downFile(url,name){  
    const _this = this;  
    uni.downloadFile({  
        url: url,   
        success: (res) => {  
            if (res.statusCode === 200) {  
                console.log('下载成功');  
                console.log(res.tempFilePath)  
                //保存视频到本地  
                uni.saveFile({  
                    tempFilePath: res.tempFilePath,  
                    success: function (res) {  
                        console.log('保存成功');  
                        var savedFilePath = res.savedFilePath;  
                        console.log(savedFilePath)  
                        _this.videoSrc = savedFilePath  
                        console.log('当前连接'+_this.videoSrc)  
                        uni.setStorageSync(name, savedFilePath);  
                    }  
                });  
            }  
        }  
    });  
}  

小编推荐:程序员网址导航

作为一名码农,随着平时工作的需要,这里收集了国内外很多优秀网站,这其中包括在线工具、在线运行、免费接口、在线资源、在线学习、技术论坛、技术博客等等,满足一般程序员日常需求。

地址:https://code-elf.cn/

继续阅读 »

需要用到的几个方法:

// 从本地缓存中同步获取指定 key 对应的内容。  
uni.getStorageSync(KEY)  

// 下载文件资源到本地,客户端直接发起一个 HTTP GET 请求,返回文件的本地临时路径。  
uni.downloadFile(OBJECT)  

// 保存视频到本地  
uni.saveFile(OBJECT)  

// 将 data 存储在本地缓存中指定的 key 中,会覆盖掉原来该 key 对应的内容,这是一个同步接口。  
uni.setStorageSync(KEY,DATA)

首先定义一个变量:获取本地缓存数据,看本地视频是存在。
获取已下载视频目录中的视频列表,删除无关视频。

const videoData = uni.getStorageSync('videoData')  
// 获取本地视频列表  
uni.getSavedFileList({  
    success: function (res) {  
        let fileList = res.fileList  
        console.log(fileList);  
        let needArr = videoData.map(item => item.videoAddress)  
        fileList.forEach((item, index) => {  
            if(needArr.includes(item.filePath)){  
                item.text = "需要这个视频"   
            }else{  
                // 删除视频列表中无关视频  
                uni.removeSavedFile({  
                    filePath: item.filePath,  
                    complete: function (res) {  
                        console.log(res);  
                    }  
                });  
            }  
        });  
        console.log(fileList)  

    }  
});  

写个条件判断:如果存在直接将视频地址插入页面进行播放,如果不存在则进行下载视频-保存视频-缓存本地链接。

const _this = this;  
// 判断是否有缓存  
if(videoData){  
    console.log('我是视频-有缓存')  
    _this.videoSrc = videoData  
}else{  
    console.log('我是视频-没有有缓存')  
    _this.videoSrc = url // 我是在线链接  
    // 下载视频  
    _this.downFile(videoUrlYi,'videoData');  
}  

// 下载视频  
downFile(url,name){  
    const _this = this;  
    uni.downloadFile({  
        url: url,   
        success: (res) => {  
            if (res.statusCode === 200) {  
                console.log('下载成功');  
                console.log(res.tempFilePath)  
                //保存视频到本地  
                uni.saveFile({  
                    tempFilePath: res.tempFilePath,  
                    success: function (res) {  
                        console.log('保存成功');  
                        var savedFilePath = res.savedFilePath;  
                        console.log(savedFilePath)  
                        _this.videoSrc = savedFilePath  
                        console.log('当前连接'+_this.videoSrc)  
                        uni.setStorageSync(name, savedFilePath);  
                    }  
                });  
            }  
        }  
    });  
}  

小编推荐:程序员网址导航

作为一名码农,随着平时工作的需要,这里收集了国内外很多优秀网站,这其中包括在线工具、在线运行、免费接口、在线资源、在线学习、技术论坛、技术博客等等,满足一般程序员日常需求。

地址:https://code-elf.cn/

收起阅读 »

授权弹出误点或者测试点击拒绝授权后无法在弹出授权窗口auth deny终极解决方案

小程序 uniapp

先贴代码
视图层部分

<template>  
    <view>  
        <button @tap="startRecord">开始录音</button>  
        <button @tap="endRecord">停止录音</button>  
        <button @tap="playVoice">播放录音</button>  
        <button @tap="uploadvoice">上传文件</button>  
    </view>  
</template>

js部分

<script>  
    let recorderManager = uni.getRecorderManager();  
    let innerAudioContext = uni.createInnerAudioContext();  

    innerAudioContext.autoplay = true;  

    export default {  
        data: {  
            text: 'uni-app',  
            voicePath: ''  
        },  
        onLoad() {  

        },  
        methods: {  
            uploadvoice() {  
                var target = this  
                let haslogin = false  
                let accesstoken = ''  
                try {  
                    accesstoken = uni.getStorageSync('accesstoken');  
                    if (accesstoken) {  

                        haslogin = true  
                    } else {  
                        uni.showToast({  
                            title: '你还没有登录',  
                            icon: 'none'  
                        })  
                        return  
                    }  

                } catch (e) {  
                    // error  
                }  
                if (target.voicePath == '') {  
                    uni.showToast({  
                        title: '你还没录音',  
                        icon: 'none'  
                    })  
                    return  
                }  
                //上传音频文件  
                uni.uploadFile({  
                    url: target.$api.UploadvoiceFile, //接口地址  
                    filePath: target.voicePath, //临时音频文件  
                    name: 'appfile',  
                    formData: {  
                        'accesstoken': accesstoken,  
                        'viewfee': 5 //付费金额  
                    },  
                    success: (uploadFileRes) => {  
                        console.log(uploadFileRes.data);  
                    }  
                });  
            },  
            startRecord() {  
                //录音先判断是否有录音权限  
                uni.getSetting({  
                    success(res) {  
                        //获取设置成功  
                        console.log(res.authSetting)  
                        if (!res.authSetting['scope.record']) {  
                            //如果没开启录音麦克风权限提示打开  
                            uni.openSetting({  
                                success(res) {  
                                    //打开成功,提示获取录音权限  
                                    console.log(res.authSetting)  
                                    uni.authorize({  
                                        scope: 'scope.record',  
                                        success() {  
                                            //录音权限获取成功开始录音  
                                            console.log('开始录音2');  

                                            recorderManager.start({  
                                                format: 'mp3',  
                                                duration: 600000  
                                            });  
                                        },  
                                        complete() {  
                                            console.log('调用接口完成');  
                                        },  
                                        fail(res) {  
                                            uni.showToast({  
                                                title: '您拒绝了应用获取麦克风权限',  
                                                icon: 'none'  
                                            })  
                                            console.log(res);  
                                        }  
                                    })  
                                }  
                            });  
                        } else {  
                            recorderManager.start({  
                                format: 'mp3',  
                                duration: 600000  
                            });  
                        }  
                    }  
                })  
            },  
            endRecord() {  
                //默认官方把此onStop方法写onload里,但是如果被拒绝的状态后续录音成功也会不执行停止回调,onload只在加载状态执行一次  
                //保险起见,用户后续开启麦克风权限还能正常回调写停止方法里  
                let self = this;  
                recorderManager.onStop(function(res) {  
                    console.log('recorder stop' + JSON.stringify(res));  
                    self.voicePath = res.tempFilePath;  
                });  
                console.log('录音结束');  
                recorderManager.stop();  

            },  
            playVoice() {  
                console.log('播放录音');  

                if (this.voicePath) {  
                    innerAudioContext.src = this.voicePath;  
                    innerAudioContext.play();  
                }  
            }  
        }  
    }  
</script>

音频文件设置了mp3格式,默认10分钟
上面demo是根据官方录音功能改进的,主要是小程序端,startRecord()录音方法里写了如何解决不弹出授权问题,demo可以直接使用测试,包含上传音频文件方法

php接收音频文件
function uploavoice() {
$message = array ();
$accesstoken = $_POST ['accesstoken'];

    $user = $this->check_token ( $accesstoken );  
    if ($user) {  
        if (! empty ( $_FILES ['appfile'] )) {  
            // 获取扩展名  
            $pathinfo = pathinfo ( $_FILES ['appfile'] ['name'] );  

            $exename = strtolower ( $pathinfo ['extension'] );  
            if ($exename != 'mp3' ) {  
                $message ['code'] = 2001;  
                $message ['data'] = null;  
                $message ['message'] = "音频扩展不支持";  
                echo json_encode ( $message );  
                exit ();  
            }  
            $imageSavePath = 'data/weixinrecord/' . uniqid () . '.' . $exename;  
            if (move_uploaded_file ( $_FILES ['appfile'] ['tmp_name'], FCPATH . $imageSavePath )) {  
                $message ['code'] = 2000;  
                $message ['data'] = SITE_URL . $imageSavePath;  
                $message ['message'] = "音频上传成功";  
                echo json_encode ( $message );  
                exit ();  
            }  
        } else {  
            $message ['code'] = 2002;  
            $message ['data'] = null;  
            $message ['message'] = "请上传音频文件";  
            echo json_encode ( $message );  
            exit ();  
        }  
    } else {  
        $message ['code'] = 2088;  
        $message ['data'] = null;  
        $message ['message'] = "用户信息过期";  
        echo json_encode ( $message );  
        exit ();  
    }  
}  
继续阅读 »

先贴代码
视图层部分

<template>  
    <view>  
        <button @tap="startRecord">开始录音</button>  
        <button @tap="endRecord">停止录音</button>  
        <button @tap="playVoice">播放录音</button>  
        <button @tap="uploadvoice">上传文件</button>  
    </view>  
</template>

js部分

<script>  
    let recorderManager = uni.getRecorderManager();  
    let innerAudioContext = uni.createInnerAudioContext();  

    innerAudioContext.autoplay = true;  

    export default {  
        data: {  
            text: 'uni-app',  
            voicePath: ''  
        },  
        onLoad() {  

        },  
        methods: {  
            uploadvoice() {  
                var target = this  
                let haslogin = false  
                let accesstoken = ''  
                try {  
                    accesstoken = uni.getStorageSync('accesstoken');  
                    if (accesstoken) {  

                        haslogin = true  
                    } else {  
                        uni.showToast({  
                            title: '你还没有登录',  
                            icon: 'none'  
                        })  
                        return  
                    }  

                } catch (e) {  
                    // error  
                }  
                if (target.voicePath == '') {  
                    uni.showToast({  
                        title: '你还没录音',  
                        icon: 'none'  
                    })  
                    return  
                }  
                //上传音频文件  
                uni.uploadFile({  
                    url: target.$api.UploadvoiceFile, //接口地址  
                    filePath: target.voicePath, //临时音频文件  
                    name: 'appfile',  
                    formData: {  
                        'accesstoken': accesstoken,  
                        'viewfee': 5 //付费金额  
                    },  
                    success: (uploadFileRes) => {  
                        console.log(uploadFileRes.data);  
                    }  
                });  
            },  
            startRecord() {  
                //录音先判断是否有录音权限  
                uni.getSetting({  
                    success(res) {  
                        //获取设置成功  
                        console.log(res.authSetting)  
                        if (!res.authSetting['scope.record']) {  
                            //如果没开启录音麦克风权限提示打开  
                            uni.openSetting({  
                                success(res) {  
                                    //打开成功,提示获取录音权限  
                                    console.log(res.authSetting)  
                                    uni.authorize({  
                                        scope: 'scope.record',  
                                        success() {  
                                            //录音权限获取成功开始录音  
                                            console.log('开始录音2');  

                                            recorderManager.start({  
                                                format: 'mp3',  
                                                duration: 600000  
                                            });  
                                        },  
                                        complete() {  
                                            console.log('调用接口完成');  
                                        },  
                                        fail(res) {  
                                            uni.showToast({  
                                                title: '您拒绝了应用获取麦克风权限',  
                                                icon: 'none'  
                                            })  
                                            console.log(res);  
                                        }  
                                    })  
                                }  
                            });  
                        } else {  
                            recorderManager.start({  
                                format: 'mp3',  
                                duration: 600000  
                            });  
                        }  
                    }  
                })  
            },  
            endRecord() {  
                //默认官方把此onStop方法写onload里,但是如果被拒绝的状态后续录音成功也会不执行停止回调,onload只在加载状态执行一次  
                //保险起见,用户后续开启麦克风权限还能正常回调写停止方法里  
                let self = this;  
                recorderManager.onStop(function(res) {  
                    console.log('recorder stop' + JSON.stringify(res));  
                    self.voicePath = res.tempFilePath;  
                });  
                console.log('录音结束');  
                recorderManager.stop();  

            },  
            playVoice() {  
                console.log('播放录音');  

                if (this.voicePath) {  
                    innerAudioContext.src = this.voicePath;  
                    innerAudioContext.play();  
                }  
            }  
        }  
    }  
</script>

音频文件设置了mp3格式,默认10分钟
上面demo是根据官方录音功能改进的,主要是小程序端,startRecord()录音方法里写了如何解决不弹出授权问题,demo可以直接使用测试,包含上传音频文件方法

php接收音频文件
function uploavoice() {
$message = array ();
$accesstoken = $_POST ['accesstoken'];

    $user = $this->check_token ( $accesstoken );  
    if ($user) {  
        if (! empty ( $_FILES ['appfile'] )) {  
            // 获取扩展名  
            $pathinfo = pathinfo ( $_FILES ['appfile'] ['name'] );  

            $exename = strtolower ( $pathinfo ['extension'] );  
            if ($exename != 'mp3' ) {  
                $message ['code'] = 2001;  
                $message ['data'] = null;  
                $message ['message'] = "音频扩展不支持";  
                echo json_encode ( $message );  
                exit ();  
            }  
            $imageSavePath = 'data/weixinrecord/' . uniqid () . '.' . $exename;  
            if (move_uploaded_file ( $_FILES ['appfile'] ['tmp_name'], FCPATH . $imageSavePath )) {  
                $message ['code'] = 2000;  
                $message ['data'] = SITE_URL . $imageSavePath;  
                $message ['message'] = "音频上传成功";  
                echo json_encode ( $message );  
                exit ();  
            }  
        } else {  
            $message ['code'] = 2002;  
            $message ['data'] = null;  
            $message ['message'] = "请上传音频文件";  
            echo json_encode ( $message );  
            exit ();  
        }  
    } else {  
        $message ['code'] = 2088;  
        $message ['data'] = null;  
        $message ['message'] = "用户信息过期";  
        echo json_encode ( $message );  
        exit ();  
    }  
}  
收起阅读 »

无条件支持Dcloud,希望Dcloud越走越远

uniapp DCloud

在客户端产品开发没选型之前,北京一个客户建议用apicloud开发,我当时建议是uni-app,也算是眼缘吧,然后试着把老的vue项目重构成uni-app,并编译了百度小程序,头条小程序,微信小程序,体验流畅不卡,唯独app端还没做适配,估计改起来也不是很大工作量,uni-app开发文档写的还可以的,国产应该支持,本人就是做国产开源社区的,Dcloud是我唯一见过能快速编译到多平台且兼容性不错的开发工具,确实提升了开发速度。
有个小建议:
插件市场里的有些作者有点不负责,导致项目会开发可能会返工,他们有的开发完成没标注是否适合app端或者兼容到哪个平台小程序或者哪些机型有问题。
希望增加一个选择项,把已知问题让作者列出来,或者在插件入口给此插件单独开通一个话题讨论,这样利于知识沉淀,后来者也可以少踩坑,直接可以找到现成问题并解决。

继续阅读 »

在客户端产品开发没选型之前,北京一个客户建议用apicloud开发,我当时建议是uni-app,也算是眼缘吧,然后试着把老的vue项目重构成uni-app,并编译了百度小程序,头条小程序,微信小程序,体验流畅不卡,唯独app端还没做适配,估计改起来也不是很大工作量,uni-app开发文档写的还可以的,国产应该支持,本人就是做国产开源社区的,Dcloud是我唯一见过能快速编译到多平台且兼容性不错的开发工具,确实提升了开发速度。
有个小建议:
插件市场里的有些作者有点不负责,导致项目会开发可能会返工,他们有的开发完成没标注是否适合app端或者兼容到哪个平台小程序或者哪些机型有问题。
希望增加一个选择项,把已知问题让作者列出来,或者在插件入口给此插件单独开通一个话题讨论,这样利于知识沉淀,后来者也可以少踩坑,直接可以找到现成问题并解决。

收起阅读 »

hbuilderX编辑器貌似有点low

HBuilder

HBuilderX编辑器下载后,点击运行,出现卡死的界面。

我的是win10 64位机器。且发现官方给的windows下载版本是32位的。。

官方是不是有点太草率了。连一个编辑器安装都出现各种奇葩问题。。。

HBuilderX编辑器下载后,点击运行,出现卡死的界面。

我的是win10 64位机器。且发现官方给的windows下载版本是32位的。。

官方是不是有点太草率了。连一个编辑器安装都出现各种奇葩问题。。。