HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

关于解决【HBuilderX 内置浏览器空白】无法调试的问题

HBuilderX技巧 Mac版

> 记录Mac系统中HBuilderX问题

当内置的浏览器空白 无法调适时试试下面这个重启工具的命令吧

lsof -i:9777 | awk '{print $2}' | tail -n +2 | xargs kill -9

> 记录Mac系统中HBuilderX问题

当内置的浏览器空白 无法调适时试试下面这个重启工具的命令吧

lsof -i:9777 | awk '{print $2}' | tail -n +2 | xargs kill -9

完整好用的uniapp 录音调用百度语音转文字代码

经历三天时间各种遇到困难 之后终于实现了这个功能,参照网上了许多文章 才找到一个能正常实现的方法,网上能找到的例子都不起作用,相信很多人困惑在这,为了避免别人出现这种情况,我分享我的代码,绝对可用,包括 uniapp前端使用了recorderManager和java端调用百度语音转文字,我相信很多人都很需要我写的东西
我只试验了安卓手机
plus.speech这个方式就不要用了,不好用调用百度的语音识别时在百度的管理控制台会看到dev_id这个参数没传的错误也没找到添加这个参数的地方
所以在hbuildx里面当前项目的app模块配置这个地方不需要选

1.非常重要的一个步骤 manifest.json里面添加android.permission.RECORD_AUDIO这个权限,无论是想真机调试,由其是使用了自定义基座一定要在线打个包,让基座包含这个权限然后在手机系统 里面应用管理当前这个app的权限管理里面必须要看到有录音权限这一项。

  1. 为了能够在使用recorderManager在进入要使用录音功能的页面会询问是否允许录音这样的权限,必须要使用recorderManager之前要调用一次硬件权限申请。这个在插件市场里面有一个https://ext.dcloud.net.cn/plugin?id=594 这个地址 App权限判断和提示这样的js,用于判断或申请某个硬件权限使用的是native.js的功能。这个插件引入项目之后会在当前项目目录的js_sdk这样一个文件夹。在里面会出现wa-permission文件 夹下面会有一个permission.js,附件中会把上传前后端代码

3.相关代码
uniapp端代码


  <view class="popup-content" >  
                    <view>{{msg}}</view>  
                    <view>你在说{{voicetext}}</view>  
                    <button class="uni-btn"  type="warn" @touchstart="startvoice" @touchend="endvoice">按说语话松开停止</button>  
                    <button class="uni-btn"  type="warn" @tap="playvoice" >播放录音</button>  
    </view>  
<script>  
import permision from "@/js_sdk/wa-permission/permission.js"  
    const recorderManager = uni.getRecorderManager();  
    const innerAudioContext = uni.createInnerAudioContext();  
export default {  
     data() {  
    return {  
            voicetext:"",  
        msg:"",  
        voicepath:""  
       }  
   },  
  onLoad() {  
      this.initaudio()  

   },  
methods: {  
   async initaudio(){  
                           //注意此处必须为 await 因为会触发异步事件,手机上会弹出权限申请对话框处理完才能走下一步录音  
                 let recordauth =  await permision.requestAndroidPermission("android.permission.RECORD_AUDIO")  
                 console.log("判断有没有录音权限>>>>>>"+recordauth)  
                  if(recordauth==1){  
                               recorderManager.onStart((res)=>{  
                                        console.log("开始 录音>>>>>>>>>...")  
                                    });  
                               recorderManager.onStop((res)=>{  
                                    console.log("recorderstop....res.tempFilePath>>>"+res.tempFilePath)  
                                    this.voicepath = res.tempFilePath  
                                    this.uploadvoicefile()  
                                    // 使用uni.uploadFile上传到服务器上,此时是mp3格式  
                                });  

                                recorderManager.onError( (res)=> {  

                                 console.log('onError'+JSON.stringify(res));  
                                });  
                  }   
            }, //initaudio 方法结束  
          startvoice(){  
                console.log("开始录音")  
                recorderManager.start({  
                    format:"mp3",  
                    sampleRate: 16000 // 必须设置是后台设置的参数,不然百度语音识别不了  
                });  
         },  
        endvoice(){  

              console.log("结束录音")  
              //注意为了避免说话时间太短导致这个api出现bug要加一些延时  
             setTimeout(()=>{  
                recorderManager.stop()  
             },1000)  

            },  
            playvoice(){  
                console.log("点击playvoice")  
                if (this.voicepath) {  
                    console.log("播放声音")  
                    innerAudioContext.src = this.voicepath;  
                    innerAudioContext.play();  
                }  
            },  
            uploadvoicefile(){  
                // this.msg = "调用java端服务文件路径"+this.voicepath  
                 uni.uploadFile({  
                url: 'http://ip:端口/uploadFile(java端接收文件接口名)',   
                filePath: this.voicepath,//录音结束后返回的临时路径,  
                name: 'file',  
                formData: {  
                   dev_id:1537 //中文带标点  
                 },  
                success: (uploadFileRes) => {  
                    let word = uploadFileRes.data  
                    console.log("上传音频成功"+word);  
                },  
                fail: (res) => {  

                    console.log("上传音频失败"+JSON.stringify(res));  
                }  
                });  
            }  
    }  
}  
</script> ```   
 //注意uploadFile 的url属性 这个地方ip不能是localhost或127,如果自已电脑启动java服务必须 是本机的真实ip如192.xxx这种,或者域名什么的,并且java端接口一定要支持跨域,很多人卡到这个ip上,我也是网上很难找到解决问题的贴子  
注意filePath这个路径就是recorderManager的onStop事件就得到的_doc这种开头的路径,不需要加什么file:不是像网上某些人说的加这种东西  

java端  
pom里面需要引用两个包  

            <dependency>  
            <groupId>com.baidu.aip</groupId>  
            <artifactId>java-sdk</artifactId>  
            <version>4.16.3</version>  
        </dependency>  

        <dependency>  
            <groupId>com.googlecode.soundlibs</groupId>  
            <artifactId>mp3spi</artifactId>  
            <version>1.9.5.4</version>  
        </dependency>  

 ``` import com.baidu.aip.speech.AipSpeech;  
import javazoom.spi.mpeg.sampled.file.MpegAudioFileReader;  
import org.apache.commons.io.IOUtils;  
import org.json.JSONArray;  
import org.json.JSONObject;  

import org.springframework.web.bind.annotation.*;  
import org.springframework.web.multipart.MultipartFile;  

import javax.sound.sampled.AudioFormat;  
import javax.sound.sampled.AudioInputStream;  
import javax.sound.sampled.AudioSystem;  
import java.io.IOException;  
import java.io.InputStream;  
import java.util.HashMap;  

@RestController  
@CrossOrigin(origins = "*")  
public class BaiduSpeech {  
    //设置APPID/AK/SK  
    public static final String APP_ID = ""; //去百度语音服务申请  
    public static final String API_KEY = "";//去百度语音服务申请  
    public static final String SECRET_KEY = "";//去百度语音服务申请  

    @RequestMapping(value = "/uploadFile")  
    public String uploadFile( @RequestParam("dev_id") int dev_id, @RequestParam("file") MultipartFile file) throws Exception {  
        byte[] pcmbytedata = mp3Convert2pcm(file.getInputStream());  
        HashMap<String,Object> options = new HashMap<String,Object>();  
        options.put("dev_pid",dev_id);//  
        JSONObject jsonfrombaidu =  basicBydata(pcmbytedata,"pcm",options);  
        JSONArray jsonArray =  jsonfrombaidu.getJSONArray("result");  
        String result =  jsonArray.getString(0);  
        System.out.println(result); //解析完的结果   
        return result;  
    }  
    // 获取AipSpeech对象,建议单例使用  
    public static AipSpeech getClient() {  
        AipSpeech client = new AipSpeech(APP_ID, API_KEY, SECRET_KEY);  
        // 可选:设置网络连接参数  
        client.setConnectionTimeoutInMillis(2000);  
        client.setSocketTimeoutInMillis(60000);  
        return client;  
    }  

    // 语音识别(来自文件)  
    public static JSONObject basicBydata(byte[] voicedata, String fileType,HashMap<String,Object> options) {  
        AipSpeech client = getClient();  

        return client.asr(voicedata, fileType, 16000, options);  
    }  
    /**  
     * MP3转换PCM  
     * @param inputStream MP3输入流  
     * @throws Exception  
     */  
    public static byte[] mp3Convert2pcm(InputStream inputStream) throws Exception {  
        //转换PCM audioInputStream 数据  
        AudioInputStream audioInputStream = getPcmAudioInputStream(inputStream);  
        byte[] pcmBytes = IOUtils.toByteArray(audioInputStream);  
        return pcmBytes;  
    }  

    /**  
     * 获取PCM AudioInputStream 数据  
     * @param inputStream MP3输入流  
     * @return AudioInputStream PCM输入流  
     */  
    private static AudioInputStream getPcmAudioInputStream(InputStream inputStream) {  
        AudioInputStream audioInputStream = null;  
        AudioFormat targetFormat = null;  
        try {  
            AudioInputStream in = null;  
            MpegAudioFileReader mp = new MpegAudioFileReader();  
            in = mp.getAudioInputStream(inputStream);  
            AudioFormat baseFormat = in.getFormat();  
            targetFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, baseFormat.getSampleRate(), 16,  
                    baseFormat.getChannels(), baseFormat.getChannels() * 2, baseFormat.getSampleRate(), false);  
            audioInputStream = AudioSystem.getAudioInputStream(targetFormat, in);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return audioInputStream;  
    }  

} ``` 
继续阅读 »

经历三天时间各种遇到困难 之后终于实现了这个功能,参照网上了许多文章 才找到一个能正常实现的方法,网上能找到的例子都不起作用,相信很多人困惑在这,为了避免别人出现这种情况,我分享我的代码,绝对可用,包括 uniapp前端使用了recorderManager和java端调用百度语音转文字,我相信很多人都很需要我写的东西
我只试验了安卓手机
plus.speech这个方式就不要用了,不好用调用百度的语音识别时在百度的管理控制台会看到dev_id这个参数没传的错误也没找到添加这个参数的地方
所以在hbuildx里面当前项目的app模块配置这个地方不需要选

1.非常重要的一个步骤 manifest.json里面添加android.permission.RECORD_AUDIO这个权限,无论是想真机调试,由其是使用了自定义基座一定要在线打个包,让基座包含这个权限然后在手机系统 里面应用管理当前这个app的权限管理里面必须要看到有录音权限这一项。

  1. 为了能够在使用recorderManager在进入要使用录音功能的页面会询问是否允许录音这样的权限,必须要使用recorderManager之前要调用一次硬件权限申请。这个在插件市场里面有一个https://ext.dcloud.net.cn/plugin?id=594 这个地址 App权限判断和提示这样的js,用于判断或申请某个硬件权限使用的是native.js的功能。这个插件引入项目之后会在当前项目目录的js_sdk这样一个文件夹。在里面会出现wa-permission文件 夹下面会有一个permission.js,附件中会把上传前后端代码

3.相关代码
uniapp端代码


  <view class="popup-content" >  
                    <view>{{msg}}</view>  
                    <view>你在说{{voicetext}}</view>  
                    <button class="uni-btn"  type="warn" @touchstart="startvoice" @touchend="endvoice">按说语话松开停止</button>  
                    <button class="uni-btn"  type="warn" @tap="playvoice" >播放录音</button>  
    </view>  
<script>  
import permision from "@/js_sdk/wa-permission/permission.js"  
    const recorderManager = uni.getRecorderManager();  
    const innerAudioContext = uni.createInnerAudioContext();  
export default {  
     data() {  
    return {  
            voicetext:"",  
        msg:"",  
        voicepath:""  
       }  
   },  
  onLoad() {  
      this.initaudio()  

   },  
methods: {  
   async initaudio(){  
                           //注意此处必须为 await 因为会触发异步事件,手机上会弹出权限申请对话框处理完才能走下一步录音  
                 let recordauth =  await permision.requestAndroidPermission("android.permission.RECORD_AUDIO")  
                 console.log("判断有没有录音权限>>>>>>"+recordauth)  
                  if(recordauth==1){  
                               recorderManager.onStart((res)=>{  
                                        console.log("开始 录音>>>>>>>>>...")  
                                    });  
                               recorderManager.onStop((res)=>{  
                                    console.log("recorderstop....res.tempFilePath>>>"+res.tempFilePath)  
                                    this.voicepath = res.tempFilePath  
                                    this.uploadvoicefile()  
                                    // 使用uni.uploadFile上传到服务器上,此时是mp3格式  
                                });  

                                recorderManager.onError( (res)=> {  

                                 console.log('onError'+JSON.stringify(res));  
                                });  
                  }   
            }, //initaudio 方法结束  
          startvoice(){  
                console.log("开始录音")  
                recorderManager.start({  
                    format:"mp3",  
                    sampleRate: 16000 // 必须设置是后台设置的参数,不然百度语音识别不了  
                });  
         },  
        endvoice(){  

              console.log("结束录音")  
              //注意为了避免说话时间太短导致这个api出现bug要加一些延时  
             setTimeout(()=>{  
                recorderManager.stop()  
             },1000)  

            },  
            playvoice(){  
                console.log("点击playvoice")  
                if (this.voicepath) {  
                    console.log("播放声音")  
                    innerAudioContext.src = this.voicepath;  
                    innerAudioContext.play();  
                }  
            },  
            uploadvoicefile(){  
                // this.msg = "调用java端服务文件路径"+this.voicepath  
                 uni.uploadFile({  
                url: 'http://ip:端口/uploadFile(java端接收文件接口名)',   
                filePath: this.voicepath,//录音结束后返回的临时路径,  
                name: 'file',  
                formData: {  
                   dev_id:1537 //中文带标点  
                 },  
                success: (uploadFileRes) => {  
                    let word = uploadFileRes.data  
                    console.log("上传音频成功"+word);  
                },  
                fail: (res) => {  

                    console.log("上传音频失败"+JSON.stringify(res));  
                }  
                });  
            }  
    }  
}  
</script> ```   
 //注意uploadFile 的url属性 这个地方ip不能是localhost或127,如果自已电脑启动java服务必须 是本机的真实ip如192.xxx这种,或者域名什么的,并且java端接口一定要支持跨域,很多人卡到这个ip上,我也是网上很难找到解决问题的贴子  
注意filePath这个路径就是recorderManager的onStop事件就得到的_doc这种开头的路径,不需要加什么file:不是像网上某些人说的加这种东西  

java端  
pom里面需要引用两个包  

            <dependency>  
            <groupId>com.baidu.aip</groupId>  
            <artifactId>java-sdk</artifactId>  
            <version>4.16.3</version>  
        </dependency>  

        <dependency>  
            <groupId>com.googlecode.soundlibs</groupId>  
            <artifactId>mp3spi</artifactId>  
            <version>1.9.5.4</version>  
        </dependency>  

 ``` import com.baidu.aip.speech.AipSpeech;  
import javazoom.spi.mpeg.sampled.file.MpegAudioFileReader;  
import org.apache.commons.io.IOUtils;  
import org.json.JSONArray;  
import org.json.JSONObject;  

import org.springframework.web.bind.annotation.*;  
import org.springframework.web.multipart.MultipartFile;  

import javax.sound.sampled.AudioFormat;  
import javax.sound.sampled.AudioInputStream;  
import javax.sound.sampled.AudioSystem;  
import java.io.IOException;  
import java.io.InputStream;  
import java.util.HashMap;  

@RestController  
@CrossOrigin(origins = "*")  
public class BaiduSpeech {  
    //设置APPID/AK/SK  
    public static final String APP_ID = ""; //去百度语音服务申请  
    public static final String API_KEY = "";//去百度语音服务申请  
    public static final String SECRET_KEY = "";//去百度语音服务申请  

    @RequestMapping(value = "/uploadFile")  
    public String uploadFile( @RequestParam("dev_id") int dev_id, @RequestParam("file") MultipartFile file) throws Exception {  
        byte[] pcmbytedata = mp3Convert2pcm(file.getInputStream());  
        HashMap<String,Object> options = new HashMap<String,Object>();  
        options.put("dev_pid",dev_id);//  
        JSONObject jsonfrombaidu =  basicBydata(pcmbytedata,"pcm",options);  
        JSONArray jsonArray =  jsonfrombaidu.getJSONArray("result");  
        String result =  jsonArray.getString(0);  
        System.out.println(result); //解析完的结果   
        return result;  
    }  
    // 获取AipSpeech对象,建议单例使用  
    public static AipSpeech getClient() {  
        AipSpeech client = new AipSpeech(APP_ID, API_KEY, SECRET_KEY);  
        // 可选:设置网络连接参数  
        client.setConnectionTimeoutInMillis(2000);  
        client.setSocketTimeoutInMillis(60000);  
        return client;  
    }  

    // 语音识别(来自文件)  
    public static JSONObject basicBydata(byte[] voicedata, String fileType,HashMap<String,Object> options) {  
        AipSpeech client = getClient();  

        return client.asr(voicedata, fileType, 16000, options);  
    }  
    /**  
     * MP3转换PCM  
     * @param inputStream MP3输入流  
     * @throws Exception  
     */  
    public static byte[] mp3Convert2pcm(InputStream inputStream) throws Exception {  
        //转换PCM audioInputStream 数据  
        AudioInputStream audioInputStream = getPcmAudioInputStream(inputStream);  
        byte[] pcmBytes = IOUtils.toByteArray(audioInputStream);  
        return pcmBytes;  
    }  

    /**  
     * 获取PCM AudioInputStream 数据  
     * @param inputStream MP3输入流  
     * @return AudioInputStream PCM输入流  
     */  
    private static AudioInputStream getPcmAudioInputStream(InputStream inputStream) {  
        AudioInputStream audioInputStream = null;  
        AudioFormat targetFormat = null;  
        try {  
            AudioInputStream in = null;  
            MpegAudioFileReader mp = new MpegAudioFileReader();  
            in = mp.getAudioInputStream(inputStream);  
            AudioFormat baseFormat = in.getFormat();  
            targetFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, baseFormat.getSampleRate(), 16,  
                    baseFormat.getChannels(), baseFormat.getChannels() * 2, baseFormat.getSampleRate(), false);  
            audioInputStream = AudioSystem.getAudioInputStream(targetFormat, in);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return audioInputStream;  
    }  

} ``` 
收起阅读 »

php curl 请求https接口证书问题

php 在windows下面使用curl请求一个https需要配置证书,具体请看代码:

php.ini配置

证书下载

http://curl.haxx.se/ca/cacert.pem

php 在windows下面使用curl请求一个https需要配置证书,具体请看代码:

php.ini配置

证书下载

http://curl.haxx.se/ca/cacert.pem

基于uni-admin的模糊查询和筛选查询(针对用户查询自己商品/产品使用)

admin uni_app项目 云开发

说明

1.程序基于uni-admin管理系统(用户)的模糊查询和筛选查询
2.程序由schema2code自动生成,添加几行代码即可
3.如果内容对各位有帮助请关注一下,创作不易

模糊查询

    let newWheres =this.getWhere();  
    let newWhere=newWheres.length>0?'('+newWheres+') && u_id == $cloudEnv_uid':'u_id == $cloudEnv_uid'

筛选查询

    newWhere.u_id=db.getCloudEnv('$cloudEnv_uid')

示例

    const dbOrderBy = 'create_date desc' // 排序字段,asc(升序)、desc(降序)  
    const dbSearchFields = ["title","description","reject_description"] // 模糊搜索字段,支持模糊搜索的字段列表。联表查询格式: 主表字段名.副表字段名,例如用户表关联角色表 role.role_name  
    export default {  
        methods: {  
            // 模糊查询  
            search() {  
                let newWheres =this.getWhere();  
                let newWhere=newWheres.length>0?'('+newWheres+') && u_id == $cloudEnv_uid':'u_id == $cloudEnv_uid'  
                this.where = newWhere  
                this.$nextTick(() => {  
                    this.loadData()  
                })  
            },  
            // 筛选查询  
            filterChange(e, name) {  
                this._filter[name] = {  
                type: e.filterType,  
                    value: e.filter  
                }  
                let newWhere = filterToWhere(this._filter, db.command)   
                newWhere.u_id=db.getCloudEnv('$cloudEnv_uid')  
                if (Object.keys(newWhere).length) {  
                    this.where = newWhere  
                } else {  
                    this.where = ''  
                }  
                this.$nextTick(() => {  
                    this.$refs.udb.loadData()  
                })  
            }  
        }  
    }
继续阅读 »

说明

1.程序基于uni-admin管理系统(用户)的模糊查询和筛选查询
2.程序由schema2code自动生成,添加几行代码即可
3.如果内容对各位有帮助请关注一下,创作不易

模糊查询

    let newWheres =this.getWhere();  
    let newWhere=newWheres.length>0?'('+newWheres+') && u_id == $cloudEnv_uid':'u_id == $cloudEnv_uid'

筛选查询

    newWhere.u_id=db.getCloudEnv('$cloudEnv_uid')

示例

    const dbOrderBy = 'create_date desc' // 排序字段,asc(升序)、desc(降序)  
    const dbSearchFields = ["title","description","reject_description"] // 模糊搜索字段,支持模糊搜索的字段列表。联表查询格式: 主表字段名.副表字段名,例如用户表关联角色表 role.role_name  
    export default {  
        methods: {  
            // 模糊查询  
            search() {  
                let newWheres =this.getWhere();  
                let newWhere=newWheres.length>0?'('+newWheres+') && u_id == $cloudEnv_uid':'u_id == $cloudEnv_uid'  
                this.where = newWhere  
                this.$nextTick(() => {  
                    this.loadData()  
                })  
            },  
            // 筛选查询  
            filterChange(e, name) {  
                this._filter[name] = {  
                type: e.filterType,  
                    value: e.filter  
                }  
                let newWhere = filterToWhere(this._filter, db.command)   
                newWhere.u_id=db.getCloudEnv('$cloudEnv_uid')  
                if (Object.keys(newWhere).length) {  
                    this.where = newWhere  
                } else {  
                    this.where = ''  
                }  
                this.$nextTick(() => {  
                    this.$refs.udb.loadData()  
                })  
            }  
        }  
    }
收起阅读 »

getBluetoothAdapterState api "discovering": false, 无法搜索到蓝牙设备

蓝牙打印

uni.getBluetoothAdapterState 无法搜索蓝牙连接打印机
安卓版本 6.0,定位权限 与蓝牙权限开启后,还是无法搜索到蓝牙
数据返回

{  
    "discovering": false,  
    "available": true,  
    "errMsg": "getBluetoothAdapterState:ok"  
}

经过检查 ,原因是 手机 定位没有开启,开启手机定位后 能够正常运行

继续阅读 »

uni.getBluetoothAdapterState 无法搜索蓝牙连接打印机
安卓版本 6.0,定位权限 与蓝牙权限开启后,还是无法搜索到蓝牙
数据返回

{  
    "discovering": false,  
    "available": true,  
    "errMsg": "getBluetoothAdapterState:ok"  
}

经过检查 ,原因是 手机 定位没有开启,开启手机定位后 能够正常运行

收起阅读 »

Mac OS HBuilderX 更换Dock栏图标

HBuidlerX 在Mac OS Dock栏图标太大,显的很不协调。
修改前


修改后

修改方法

解压 :hx_icon@2x.icns.zip
右键 -> 显示简介 -> 拖动 hx_icon@2x.icns 到上图位置

继续阅读 »

HBuidlerX 在Mac OS Dock栏图标太大,显的很不协调。
修改前


修改后

修改方法

解压 :hx_icon@2x.icns.zip
右键 -> 显示简介 -> 拖动 hx_icon@2x.icns 到上图位置

收起阅读 »

自荐,uniapp开发新选择!丰富、强大、优雅的全新组件库FirstUI!

自定义组件 小程序组件 ui组件 组件

一、产品介绍

给大家推荐下我们做的FirstUI(https://www.firstui.cn/),它是基于uni-app开发的一款轻量、全面可靠的跨平台移动端组件库。包括框架、组件、模板、功能插件几个部分。我们前后花了大概6个月的时间,目前组件、布局部分基本完成了,还做了几款简单的模板,后续会着力做模板这一块,多提供一些成熟常用的场景模板。可以实现快速上手,更好的节约使用者的时间与成本。

FirstUI我们倾注了大量的精力,很期待大家的关注试用,谢谢!

1、产品特征:

  • 多端支持。一套代码,多端适用,支持iOS(vue和Nvue)、Android(vue和Nvue)、微信小程序、支付宝小程序、QQ小程序、百度小程序、字节跳动小程序、H5平台
  • 完善的组件。目前共规划118款,已上线70款,涵盖基础组件、表单组件、导航组件、布局组件、常用布局、扩展组件、操作反馈、数据组件、JS、图表、画布。
  • 丰富实用的布局、模板。基于FirstUI提供的组件,针对常用场景、行业,提供丰富实用的布局和模板。
  • 专属社区。我们用flarum(ps:这个大家也可以试下,设计与功能方面好用,感觉用来做社区非常棒)打造了FirstU专属社区(https://forum.firstui.cn/),用户可以在社区交流分享FirstUI的使用经验、提问。有其他组件、模板需求,也可以在社区中反馈。

2、源码及文档:

3、扫码体验:

大部分组件是操作性的,大家扫码操作可以有更真切的感受。目前发布了6个平台版本,大家可以扫码体验下。考虑快速预览,所以暂未上架App应用,后续待功能完善再进行上架。

4、部分效果:

5、进展与已上线功能清单:

进展情况:

已上线功能清单:

  • 框架
    • FirstUI组件库(uni-app版)
  • 模板
    • 登录(A)模板
    • 登录(B)模板
    • 登录(C)模板
    • 登录(D)模板
    • 门户模板
  • 组件
    • 基础组件
    • Color 色彩
    • Icon 图标
    • Button 按钮
    • Footer 页脚
    • Animation 动画
    • 导航组件
    • NavBar 导航栏
    • Pagination 分页器
    • SegmentedControl 分段器
    • Tabbar 标签栏
    • Tabs 标签页
    • IndexList 索引列表
    • NoticeBar 通告栏
    • Sticky 吸顶容器
    • Steps 步骤条
    • Fab 浮动按钮
    • Drawer 抽屉
    • BottomPopup 底部弹出层
    • TopPopup 顶部弹出层
    • TimeAxis 时间轴
    • 布局组件
    • Layout 栅格布局
    • Panel 面板
    • Preview 表单预览
    • List 列表
    • Card 卡片
    • 扩展组件
    • ShareSheet 分享面板
    • Result 结果页
    • FilterBar 筛选栏
    • DigitalRoller 数字滚轮
    • DigitalKeyboard 数字键盘
    • CountdownVerify 验证码倒计时
    • SwiperDot 轮播图指示点
    • LicensePlate 车牌号键盘
    • SingleInput 单输入框
    • 操作反馈
    • ActionSheet 上拉菜单
    • Toast 轻提示
    • BackDrop 遮罩层
    • Dialog 对话框
    • DropdownMenu 下拉菜单
    • Modal 模态框
    • Landscape 压屏窗
    • Loading 加载
    • Message 消息提示
    • SwipeAction 滑动菜单
    • DropdownList 下拉菜单
    • LoadAni 加载动画
    • 数据组件
    • Badge 徽章
    • Alert 警告框
    • Avatar 头像
    • Tag 标签
    • Progress 进度条
    • Collapse 折叠面板
    • Divider 分割线
    • LoadMore 加载更多
    • Empty 暂无数据
    • 画布
    • Poster 绘制分享海报
    • 表单组件
    • Picker 选择器
    • DatePicker 日期时间选择器
    • Form 表单校验
    • Input 输入框
    • InputNumber 数字输入框
    • Radio 单选框
    • Checkbox 复选框
    • Switch 开关
    • Textarea 多行输入框
    • SearchBar 搜索栏
    • JS
    • Clipboard 复制文本
    • Request 网络请求
    • Utils 工具类
    • Validator 表单验证

二、商业版与开源版

FirstUI分为开源版与商业版,部分组件为商业版可用,目前商业版的费用是¥150元,一次性费用,支持后续更新。

VIP会员权益:

  • 完整版源码
  • 全部组件
  • 物料商城享VIP折扣
  • 专属会员群指导、答疑
  • 新特性优先体验
  • VIP专属文档

会员权益详情: https://www.firstui.cn/right

三、使用体验与建议

FirstUI刚刚开始成长,非常期待大家的体验使用和宝贵的建议!大家可以在文后留言,让我们有新的认识,谢谢!

继续阅读 »

一、产品介绍

给大家推荐下我们做的FirstUI(https://www.firstui.cn/),它是基于uni-app开发的一款轻量、全面可靠的跨平台移动端组件库。包括框架、组件、模板、功能插件几个部分。我们前后花了大概6个月的时间,目前组件、布局部分基本完成了,还做了几款简单的模板,后续会着力做模板这一块,多提供一些成熟常用的场景模板。可以实现快速上手,更好的节约使用者的时间与成本。

FirstUI我们倾注了大量的精力,很期待大家的关注试用,谢谢!

1、产品特征:

  • 多端支持。一套代码,多端适用,支持iOS(vue和Nvue)、Android(vue和Nvue)、微信小程序、支付宝小程序、QQ小程序、百度小程序、字节跳动小程序、H5平台
  • 完善的组件。目前共规划118款,已上线70款,涵盖基础组件、表单组件、导航组件、布局组件、常用布局、扩展组件、操作反馈、数据组件、JS、图表、画布。
  • 丰富实用的布局、模板。基于FirstUI提供的组件,针对常用场景、行业,提供丰富实用的布局和模板。
  • 专属社区。我们用flarum(ps:这个大家也可以试下,设计与功能方面好用,感觉用来做社区非常棒)打造了FirstU专属社区(https://forum.firstui.cn/),用户可以在社区交流分享FirstUI的使用经验、提问。有其他组件、模板需求,也可以在社区中反馈。

2、源码及文档:

3、扫码体验:

大部分组件是操作性的,大家扫码操作可以有更真切的感受。目前发布了6个平台版本,大家可以扫码体验下。考虑快速预览,所以暂未上架App应用,后续待功能完善再进行上架。

4、部分效果:

5、进展与已上线功能清单:

进展情况:

已上线功能清单:

  • 框架
    • FirstUI组件库(uni-app版)
  • 模板
    • 登录(A)模板
    • 登录(B)模板
    • 登录(C)模板
    • 登录(D)模板
    • 门户模板
  • 组件
    • 基础组件
    • Color 色彩
    • Icon 图标
    • Button 按钮
    • Footer 页脚
    • Animation 动画
    • 导航组件
    • NavBar 导航栏
    • Pagination 分页器
    • SegmentedControl 分段器
    • Tabbar 标签栏
    • Tabs 标签页
    • IndexList 索引列表
    • NoticeBar 通告栏
    • Sticky 吸顶容器
    • Steps 步骤条
    • Fab 浮动按钮
    • Drawer 抽屉
    • BottomPopup 底部弹出层
    • TopPopup 顶部弹出层
    • TimeAxis 时间轴
    • 布局组件
    • Layout 栅格布局
    • Panel 面板
    • Preview 表单预览
    • List 列表
    • Card 卡片
    • 扩展组件
    • ShareSheet 分享面板
    • Result 结果页
    • FilterBar 筛选栏
    • DigitalRoller 数字滚轮
    • DigitalKeyboard 数字键盘
    • CountdownVerify 验证码倒计时
    • SwiperDot 轮播图指示点
    • LicensePlate 车牌号键盘
    • SingleInput 单输入框
    • 操作反馈
    • ActionSheet 上拉菜单
    • Toast 轻提示
    • BackDrop 遮罩层
    • Dialog 对话框
    • DropdownMenu 下拉菜单
    • Modal 模态框
    • Landscape 压屏窗
    • Loading 加载
    • Message 消息提示
    • SwipeAction 滑动菜单
    • DropdownList 下拉菜单
    • LoadAni 加载动画
    • 数据组件
    • Badge 徽章
    • Alert 警告框
    • Avatar 头像
    • Tag 标签
    • Progress 进度条
    • Collapse 折叠面板
    • Divider 分割线
    • LoadMore 加载更多
    • Empty 暂无数据
    • 画布
    • Poster 绘制分享海报
    • 表单组件
    • Picker 选择器
    • DatePicker 日期时间选择器
    • Form 表单校验
    • Input 输入框
    • InputNumber 数字输入框
    • Radio 单选框
    • Checkbox 复选框
    • Switch 开关
    • Textarea 多行输入框
    • SearchBar 搜索栏
    • JS
    • Clipboard 复制文本
    • Request 网络请求
    • Utils 工具类
    • Validator 表单验证

二、商业版与开源版

FirstUI分为开源版与商业版,部分组件为商业版可用,目前商业版的费用是¥150元,一次性费用,支持后续更新。

VIP会员权益:

  • 完整版源码
  • 全部组件
  • 物料商城享VIP折扣
  • 专属会员群指导、答疑
  • 新特性优先体验
  • VIP专属文档

会员权益详情: https://www.firstui.cn/right

三、使用体验与建议

FirstUI刚刚开始成长,非常期待大家的体验使用和宝贵的建议!大家可以在文后留言,让我们有新的认识,谢谢!

收起阅读 »

应用外唤起地图并标注地点

源码分享 技术分享

map.js

/*   
*   功能:应用外唤起地图并标注地点  
*   特点:uni.openLocation()的简易替代品,唤起指定地图的某个标点。  
*   优点:不依赖uniapi,无须集成sdk,无须声明位置权限,兼容多个地图。  
*   缺点:非应用内置地图,依赖文档等等等。  
*   日期:2021年12月15日  
*   支付宝:\u004e\u0054\u0041\u0079\u004d\u0054\u0067\u0077\u004e\u007a\u0051\u0035\u0051\u0048\u0046\u0078\u004c\u006d\u004e\u0076\u0062\u0051\u003d\u003d  
*/  
/*  
#   用法  
    import mapTool from 'xxx.js'  
    mapTool.navTo(point, map)  
#   提示  
    1.未加入Google Map,未使用WGS84坐标系,需要唤起后直接导航 或 地址逆解析,请自行解决。  
    2.(重要)使用腾讯地图scheme需要开发者Key,请自行注册。  
    3.(重要)使用查询地图是否安装的方法mapsExist(),iOS9以后需要添加白名单才可查询。即在manifest.json文件plus->distribute->apple->urlschemewhitelist节点下添加(如urlschemewhitelist:["xxx"]),名称请参考下方MapsInfo对象各属性的i_wlname。  
    4.Document:  
    <https://lbsyun.baidu.com/index.php?title=uri>  
    <https://lbs.amap.com/api/amap-mobile/summary>  
    <https://lbs.qq.com/webApi/uriV1/uriGuide/uriOverview>  
    <https://developer.apple.com/library/ios/featuredarticles/iPhoneURLScheme_Reference/MapLinks/MapLinks.html>  
    <https://developers.google.com/maps/documentation/ios/urlscheme>  
*/  

// 腾讯地图开发者Key  
const QQMapKey = "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX"  

const Platform = plus.os.name // Android;iOS;...  
const MapsInfo = {  
    baidu: {  
        url: 'http://map.baidu.com',  
        CN: '百度地图',  
        EN: 'bdmap',  
        w_uri: 'http://api.map.baidu.com',  
        a_intent: 'baidumap://',  
        a_pname: 'com.baidu.BaiduMap', //package name  
        i_scheme: 'baidumap://', // action  
        i_wlname: 'baidumap' // white list name  
    },  
    gaode: {  
        url: 'https://m.amap.com',  
        CN: '高德地图',  
        EN: 'amap',  
        w_uri: 'https://uri.amap.com',  
        a_intent: 'androidamap://',  
        a_pname: 'com.autonavi.minimap', //package name  
        i_scheme: 'iosamap://', // action  
        i_wlname: 'iosamap' // white list name  
    },  
    tengxun: {  
        url: 'https://map.qq.com',  
        CN: '腾讯地图',  
        EN: 'qqmap',  
        w_uri: 'https://apis.map.qq.com',  
        a_intent: 'qqmap://',  
        a_pname: 'com.tencent.map', //package name  
        i_scheme: 'qqmap://', // action  
        i_wlname: 'qqmap' // white list name  
    },  
    pingguo: { // 白名单需要增加maps,才可检测到系统地图  
        url: 'http://maps.apple.com',  
        CN: '系统地图',  
        EN: 'maps',  
        w_uri: 'http://maps.apple.com',  
        a_intent: 'androidamap://', // 安卓高德地图,理论上用不到  
        a_pname: 'com.autonavi.minimap', // 安卓高德地图,理论上用不到  
        i_scheme: 'maps://', // action  
        i_wlname: 'maps' // white list name  
    }  
}  
const DefaultMap = MapsInfo.tengxun // 据自身业务采用【腾讯地图】作为默认地图,腾讯地图scheme需要开发者Key  
const DefaultPoint = { // 默认标注,【找不到所在位置】意为lat、lng缺失;【未知位置】意为lbl缺失;  
    lat: 22.517007, // gcj02  
    lng: 113.392532, // gcj02  
    lbl: '找不到所在位置', // label  
    dtl: '未知地址' // detail  
}  
const DefaultService = { // 来源信息(可设为包名),百度地图、高德地图必填,不可空  
    web: 'web.xxx.app',  
    ios: 'ios.xxx.app',  
    android: 'android.xxx.app'  
}  

// GCJ-02(腾讯、高德等火星坐标系) To BD-09(百度坐标系)  
const gcj2bd = function(lat, lng) {  
    var point = new Object();  
    var x_pi = 3.14159265358979324 * 3000.0 / 180.0;  
    var x = new Number(lng);  
    var y = new Number(lat);  
    var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);  
    var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);  
    var bd_lng = z * Math.cos(theta) + 0.0065;  
    var bd_lat = z * Math.sin(theta) + 0.006;  
    point.lng = bd_lng;  
    point.lat = bd_lat;  
    return point;  
}  

// BD-09(百度坐标系) To GCJ-02(腾讯、高德等火星坐标系)  
const bd2gcj = function(lat, lng) {  
    var point = new Object();  
    var x_pi = 3.14159265358979324 * 3000.0 / 180.0;  
    var x = new Number(lng - 0.0065);  
    var y = new Number(lat - 0.006);  
    var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);  
    var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);  
    var gcj_lng = z * Math.cos(theta);  
    var gcj_lat = z * Math.sin(theta);  
    point.lng = gcj_lng;  
    point.lat = gcj_lat;  
    return point;  
}  

// URL参数 To JSON对象  
const query2json = function(query) {  
    let param = {}; // 存储最终JSON结果对象  
    query.replace(/([^?&]+)=([^?&]+)/g, function(s, v, k) {  
        param[v] = decodeURIComponent(k); //解析字符为中文(据业务处理)  
        return k + '=' + v;  
    });  
    return param;  
}  

// JSON对象 To URL参数  
const json2query = function(json) {  
    let param = []  
    Object.keys(json).forEach(e => {  
        param.push(e + '=' + encodeURIComponent(json[e]))  
    })  
    return param.join('&')  
}  

// 深拷贝  
function _isArray (arr) {  
    return Object.prototype.toString.call(arr) === '[object Array]';  
}  
function _deepClone(obj) {  
    // 对常见的“非”值,直接返回原来值  
    if([null, undefined, NaN, false].includes(obj)) return obj;  
    if(typeof obj !== "object" && typeof obj !== 'function') {  
        //原始类型直接返回  
        return obj;  
    }  
    var o = _isArray(obj) ? [] : {};  
    for(let i in obj) {  
        if(obj.hasOwnProperty(i)){  
            o[i] = typeof obj[i] === "object" ? _deepClone(obj[i]) : obj[i];  
        }  
    }  
    return o;  
}  

// 判断是否存在这四个应用(百度地图、高德地图、腾讯地图、苹果地图),返回对象数组  
const mapsExist = function() {  
    let res = []  
    if (plus.runtime.isApplicationExist({  
            pname: MapsInfo.baidu.a_pname,  
            action: MapsInfo.baidu.i_scheme  
        })) {  
        console.log("[已安装百度地图]");  
        res.push(MapsInfo.baidu)  
    }  
    if (plus.runtime.isApplicationExist({  
            pname: MapsInfo.gaode.a_pname,  
            action: MapsInfo.gaode.i_scheme  
        })) {  
        console.log("[已安装高德地图]");  
        res.push(MapsInfo.gaode)  
    }  
    if (plus.runtime.isApplicationExist({  
            pname: MapsInfo.tengxun.a_pname,  
            action: MapsInfo.tengxun.i_scheme  
        })) {  
        console.log("[已安装腾讯地图]");  
        res.push(MapsInfo.tengxun)  
    }  
    if (Platform == 'iOS' && plus.runtime.isApplicationExist({  
            pname: MapsInfo.pingguo.a_pname,  
            action: MapsInfo.pingguo.i_scheme  
        })) {  
        console.log("[已安装系统地图]");  
        res.push(MapsInfo.pingguo)  
    }  
    return res  
}  

// 仅仅是打开地图应用(不荐)  
const openApp = function(map) {  
    if (Platform == "Android") {  
        plus.runtime.launchApplication({  
            pname: map.a_pname,  
        }, function(e) {  
            alert("Open system default browser failed: " + e.message);  
        });  
    } else if (Platform == "iOS") {  
        plus.runtime.launchApplication({  
            action: map.i_scheme  
        }, function(e) {  
            alert("Open system default browser failed: " + e.message);  
        });  
    } else {  
        alert("Operating system is not supported");  
    }  
}  

// 坐标参数处理  
const argHandle = function(point) {  
    if (!point) {  
        return DefaultPoint  
    }  
    let pot = _deepClone(point)  
    if (!pot.lat || !pot.lng) {  
        pot = DefaultPoint  
    }  
    if (!pot.lbl) {  
        pot['lbl'] = '未知位置'  
    }  
    if (!pot.dtl) {  
        pot['dtl'] = '未知地址'  
    }  
    return pot  
}  

// 应用内唤起地图应用,并标注位置,失败则使用浏览器唤起       C2C     对应导航path请查阅api文档  
const openByApp = function(map, point) {  
    return new Promise((resolve) => {  
        let pot = argHandle(point)  
        let url = ''  
        if (map.EN == 'bdmap') {  
            // 腾讯坐标系转换为百度坐标  
            let p = gcj2bd(pot.lat, pot.lng)  
            pot = {  
                ...pot,  
                ...p  
            }  

            if (Platform == 'Android') {  
                url =  
                    `bdapp://map/marker?location=${pot.lat},${pot.lng}&title=${pot.lbl}&content=${pot.dtl}&src=${DefaultService.android}`  
            } else if (Platform == 'iOS') {  
                url =  
                    `baidumap://map/marker?location=${pot.lat},${pot.lng}&title=${pot.lbl}&content=${pot.dtl}&src=${DefaultService.ios}`  
            }  
        } else if (map.EN == 'amap') {  
            if (Platform == 'Android') {  
                url =  
                    `androidamap://viewMap?lat=${pot.lat}&lon=${pot.lng}&poiname=${pot.lbl}&sourceApplication=${DefaultService.android}&dev=0`  
            } else if (Platform == 'iOS') {  
                url =  
                    `iosamap://viewMap?lat=${pot.lat}&lon=${pot.lng}&poiname=${pot.lbl}&sourceApplication=${DefaultService.ios}&dev=0`  
            }  
        } else if (map.EN == 'qqmap') {  
            if (Platform == 'Android') {  
                url =  
                    `qqmap://map/marker?marker=coord:${pot.lat},${pot.lng};title:${pot.lbl};addr:${pot.dtl}&referer=${QQMapKey}`  
            } else if (Platform == 'iOS') {  
                url =  
                    `qqmap://map/marker?marker=coord:${pot.lat},${pot.lng};title:${pot.lbl};addr:${pot.dtl}&referer=${QQMapKey}`  
            }  
        } else if (map.EN == 'maps') {  
            if (Platform == 'Android') {  
                url =  
                    `androidamap://viewMap?lat=${pot.lat}&lon=${pot.lng}&poiname=${pot.lbl}&sourceApplication=${DefaultService.android}&dev=0`  
            } else if (Platform == 'iOS') {  
                // Unlike some schemes, map URLs do not start with a “maps” scheme identifier. Instead, map links are specified as regular http links and are opened either in Safari or the Maps app on the target platform.  
                url = `http://maps.apple.com?ll=${pot.lat},${pot.lng}&q=${pot.lbl}`  
            }  
        }  

        if (url != '') {  
            plus.runtime.openURL(encodeURI(url), async function(e) {  
                console.error(e)  
                let result = await openByBrowser(map, point)  
                resolve(result)  
            });  
            resolve(true)  
        } else {  
            console.error('应用内唤起失败')  
            resolve(false)  
        }  

    })  
}  

// 浏览器唤起地图应用,并标注位置      C2B2C   对应导航path请查阅api文档  
const openByBrowser = function(map, point) {  
    return new Promise((resolve) => {  
        let pot = argHandle(point)  
        let url = ''  
        if (map.EN == 'bdmap') {  
            // 腾讯坐标系转换为百度坐标  
            let p = gcj2bd(pot.lat, pot.lng)  
            pot = {  
                ...pot,  
                ...p  
            }  
            url =  
                `http://api.map.baidu.com/marker?location=${pot.lat},${pot.lng}&title=${pot.lbl}&content=${pot.dtl}&src=${DefaultService.web}&output=html`  
        } else if (map.EN == 'amap') {  
            url =  
                `https://uri.amap.com/marker?position=${pot.lng},${pot.lat}&name=${pot.lbl}&src=${DefaultService.web}&callnative=1`  
        } else if (map.EN == 'qqmap') {  
            url =  
                `https://apis.map.qq.com/uri/v1/marker?marker=coord:${pot.lat},${pot.lng};title:${pot.lbl};addr:${pot.dtl}&referer=${DefaultService.web}`  
        } else if (map.EN == 'maps') {  
            url = `http://maps.apple.com?ll=${pot.lat},${pot.lng}&q=${pot.lbl}`  
        }  

        if (url != '') {  
            plus.runtime.openURL(encodeURI(url), function(e) {  
                console.error(e)  
                resolve(false)  
            });  
            resolve(true)  
        } else {  
            console.error('浏览器唤起失败')  
            resolve(false)  
        }  

    })  
}  

// 地图标注,map指定唤起地图(string,['qqmap','amap','bdmap','maps']之一),point坐标对象(object,DefaultPoint)  
const navTo = async function(point, map) {  
    // test  
    // let result = openByBrowser(MapsInfo.pingguo, point)  
    // let result = await openByApp(MapsInfo.pingguo, point)  
    // console.log('打开结果', result)  
    // return false  

    let result = false  
    let res = mapsExist()  
    if (res.length > 0) { // 无地图应用  
        let index = res.findIndex(e => { // 参数有指定地图应用  
            return e.EN == map  
        })  
        if (index != -1) {  
            result = await openByApp(res[index], point)  
        } else if (res.findIndex(e => { // 如果有安装默认地图应用,优先使用默认地图Api  
                return e.EN == DefaultMap.EN  
            }) != -1) {  
            result = await openByApp(DefaultMap, point)  
        } else { // 默认用第一个地图导航  
            result = await openByApp(res[0], point)  
        }  
    } else { // 无地图应用,浏览器打开  
        let arr = Object.values(MapsInfo)  
        let index = Object.values(MapsInfo).findIndex(e => { // 参数有指定地图应用  
            return e.EN == map  
        })  
        if (index != -1) {  
            result = await openByBrowser(arr[index], point)  
        } else {  
            result = await openByBrowser(DefaultMap, point)  
        }  
    }  
    return result  
}  

module.exports = {  
    navTo  
}  
继续阅读 »

map.js

/*   
*   功能:应用外唤起地图并标注地点  
*   特点:uni.openLocation()的简易替代品,唤起指定地图的某个标点。  
*   优点:不依赖uniapi,无须集成sdk,无须声明位置权限,兼容多个地图。  
*   缺点:非应用内置地图,依赖文档等等等。  
*   日期:2021年12月15日  
*   支付宝:\u004e\u0054\u0041\u0079\u004d\u0054\u0067\u0077\u004e\u007a\u0051\u0035\u0051\u0048\u0046\u0078\u004c\u006d\u004e\u0076\u0062\u0051\u003d\u003d  
*/  
/*  
#   用法  
    import mapTool from 'xxx.js'  
    mapTool.navTo(point, map)  
#   提示  
    1.未加入Google Map,未使用WGS84坐标系,需要唤起后直接导航 或 地址逆解析,请自行解决。  
    2.(重要)使用腾讯地图scheme需要开发者Key,请自行注册。  
    3.(重要)使用查询地图是否安装的方法mapsExist(),iOS9以后需要添加白名单才可查询。即在manifest.json文件plus->distribute->apple->urlschemewhitelist节点下添加(如urlschemewhitelist:["xxx"]),名称请参考下方MapsInfo对象各属性的i_wlname。  
    4.Document:  
    <https://lbsyun.baidu.com/index.php?title=uri>  
    <https://lbs.amap.com/api/amap-mobile/summary>  
    <https://lbs.qq.com/webApi/uriV1/uriGuide/uriOverview>  
    <https://developer.apple.com/library/ios/featuredarticles/iPhoneURLScheme_Reference/MapLinks/MapLinks.html>  
    <https://developers.google.com/maps/documentation/ios/urlscheme>  
*/  

// 腾讯地图开发者Key  
const QQMapKey = "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX"  

const Platform = plus.os.name // Android;iOS;...  
const MapsInfo = {  
    baidu: {  
        url: 'http://map.baidu.com',  
        CN: '百度地图',  
        EN: 'bdmap',  
        w_uri: 'http://api.map.baidu.com',  
        a_intent: 'baidumap://',  
        a_pname: 'com.baidu.BaiduMap', //package name  
        i_scheme: 'baidumap://', // action  
        i_wlname: 'baidumap' // white list name  
    },  
    gaode: {  
        url: 'https://m.amap.com',  
        CN: '高德地图',  
        EN: 'amap',  
        w_uri: 'https://uri.amap.com',  
        a_intent: 'androidamap://',  
        a_pname: 'com.autonavi.minimap', //package name  
        i_scheme: 'iosamap://', // action  
        i_wlname: 'iosamap' // white list name  
    },  
    tengxun: {  
        url: 'https://map.qq.com',  
        CN: '腾讯地图',  
        EN: 'qqmap',  
        w_uri: 'https://apis.map.qq.com',  
        a_intent: 'qqmap://',  
        a_pname: 'com.tencent.map', //package name  
        i_scheme: 'qqmap://', // action  
        i_wlname: 'qqmap' // white list name  
    },  
    pingguo: { // 白名单需要增加maps,才可检测到系统地图  
        url: 'http://maps.apple.com',  
        CN: '系统地图',  
        EN: 'maps',  
        w_uri: 'http://maps.apple.com',  
        a_intent: 'androidamap://', // 安卓高德地图,理论上用不到  
        a_pname: 'com.autonavi.minimap', // 安卓高德地图,理论上用不到  
        i_scheme: 'maps://', // action  
        i_wlname: 'maps' // white list name  
    }  
}  
const DefaultMap = MapsInfo.tengxun // 据自身业务采用【腾讯地图】作为默认地图,腾讯地图scheme需要开发者Key  
const DefaultPoint = { // 默认标注,【找不到所在位置】意为lat、lng缺失;【未知位置】意为lbl缺失;  
    lat: 22.517007, // gcj02  
    lng: 113.392532, // gcj02  
    lbl: '找不到所在位置', // label  
    dtl: '未知地址' // detail  
}  
const DefaultService = { // 来源信息(可设为包名),百度地图、高德地图必填,不可空  
    web: 'web.xxx.app',  
    ios: 'ios.xxx.app',  
    android: 'android.xxx.app'  
}  

// GCJ-02(腾讯、高德等火星坐标系) To BD-09(百度坐标系)  
const gcj2bd = function(lat, lng) {  
    var point = new Object();  
    var x_pi = 3.14159265358979324 * 3000.0 / 180.0;  
    var x = new Number(lng);  
    var y = new Number(lat);  
    var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);  
    var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);  
    var bd_lng = z * Math.cos(theta) + 0.0065;  
    var bd_lat = z * Math.sin(theta) + 0.006;  
    point.lng = bd_lng;  
    point.lat = bd_lat;  
    return point;  
}  

// BD-09(百度坐标系) To GCJ-02(腾讯、高德等火星坐标系)  
const bd2gcj = function(lat, lng) {  
    var point = new Object();  
    var x_pi = 3.14159265358979324 * 3000.0 / 180.0;  
    var x = new Number(lng - 0.0065);  
    var y = new Number(lat - 0.006);  
    var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);  
    var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);  
    var gcj_lng = z * Math.cos(theta);  
    var gcj_lat = z * Math.sin(theta);  
    point.lng = gcj_lng;  
    point.lat = gcj_lat;  
    return point;  
}  

// URL参数 To JSON对象  
const query2json = function(query) {  
    let param = {}; // 存储最终JSON结果对象  
    query.replace(/([^?&]+)=([^?&]+)/g, function(s, v, k) {  
        param[v] = decodeURIComponent(k); //解析字符为中文(据业务处理)  
        return k + '=' + v;  
    });  
    return param;  
}  

// JSON对象 To URL参数  
const json2query = function(json) {  
    let param = []  
    Object.keys(json).forEach(e => {  
        param.push(e + '=' + encodeURIComponent(json[e]))  
    })  
    return param.join('&')  
}  

// 深拷贝  
function _isArray (arr) {  
    return Object.prototype.toString.call(arr) === '[object Array]';  
}  
function _deepClone(obj) {  
    // 对常见的“非”值,直接返回原来值  
    if([null, undefined, NaN, false].includes(obj)) return obj;  
    if(typeof obj !== "object" && typeof obj !== 'function') {  
        //原始类型直接返回  
        return obj;  
    }  
    var o = _isArray(obj) ? [] : {};  
    for(let i in obj) {  
        if(obj.hasOwnProperty(i)){  
            o[i] = typeof obj[i] === "object" ? _deepClone(obj[i]) : obj[i];  
        }  
    }  
    return o;  
}  

// 判断是否存在这四个应用(百度地图、高德地图、腾讯地图、苹果地图),返回对象数组  
const mapsExist = function() {  
    let res = []  
    if (plus.runtime.isApplicationExist({  
            pname: MapsInfo.baidu.a_pname,  
            action: MapsInfo.baidu.i_scheme  
        })) {  
        console.log("[已安装百度地图]");  
        res.push(MapsInfo.baidu)  
    }  
    if (plus.runtime.isApplicationExist({  
            pname: MapsInfo.gaode.a_pname,  
            action: MapsInfo.gaode.i_scheme  
        })) {  
        console.log("[已安装高德地图]");  
        res.push(MapsInfo.gaode)  
    }  
    if (plus.runtime.isApplicationExist({  
            pname: MapsInfo.tengxun.a_pname,  
            action: MapsInfo.tengxun.i_scheme  
        })) {  
        console.log("[已安装腾讯地图]");  
        res.push(MapsInfo.tengxun)  
    }  
    if (Platform == 'iOS' && plus.runtime.isApplicationExist({  
            pname: MapsInfo.pingguo.a_pname,  
            action: MapsInfo.pingguo.i_scheme  
        })) {  
        console.log("[已安装系统地图]");  
        res.push(MapsInfo.pingguo)  
    }  
    return res  
}  

// 仅仅是打开地图应用(不荐)  
const openApp = function(map) {  
    if (Platform == "Android") {  
        plus.runtime.launchApplication({  
            pname: map.a_pname,  
        }, function(e) {  
            alert("Open system default browser failed: " + e.message);  
        });  
    } else if (Platform == "iOS") {  
        plus.runtime.launchApplication({  
            action: map.i_scheme  
        }, function(e) {  
            alert("Open system default browser failed: " + e.message);  
        });  
    } else {  
        alert("Operating system is not supported");  
    }  
}  

// 坐标参数处理  
const argHandle = function(point) {  
    if (!point) {  
        return DefaultPoint  
    }  
    let pot = _deepClone(point)  
    if (!pot.lat || !pot.lng) {  
        pot = DefaultPoint  
    }  
    if (!pot.lbl) {  
        pot['lbl'] = '未知位置'  
    }  
    if (!pot.dtl) {  
        pot['dtl'] = '未知地址'  
    }  
    return pot  
}  

// 应用内唤起地图应用,并标注位置,失败则使用浏览器唤起       C2C     对应导航path请查阅api文档  
const openByApp = function(map, point) {  
    return new Promise((resolve) => {  
        let pot = argHandle(point)  
        let url = ''  
        if (map.EN == 'bdmap') {  
            // 腾讯坐标系转换为百度坐标  
            let p = gcj2bd(pot.lat, pot.lng)  
            pot = {  
                ...pot,  
                ...p  
            }  

            if (Platform == 'Android') {  
                url =  
                    `bdapp://map/marker?location=${pot.lat},${pot.lng}&title=${pot.lbl}&content=${pot.dtl}&src=${DefaultService.android}`  
            } else if (Platform == 'iOS') {  
                url =  
                    `baidumap://map/marker?location=${pot.lat},${pot.lng}&title=${pot.lbl}&content=${pot.dtl}&src=${DefaultService.ios}`  
            }  
        } else if (map.EN == 'amap') {  
            if (Platform == 'Android') {  
                url =  
                    `androidamap://viewMap?lat=${pot.lat}&lon=${pot.lng}&poiname=${pot.lbl}&sourceApplication=${DefaultService.android}&dev=0`  
            } else if (Platform == 'iOS') {  
                url =  
                    `iosamap://viewMap?lat=${pot.lat}&lon=${pot.lng}&poiname=${pot.lbl}&sourceApplication=${DefaultService.ios}&dev=0`  
            }  
        } else if (map.EN == 'qqmap') {  
            if (Platform == 'Android') {  
                url =  
                    `qqmap://map/marker?marker=coord:${pot.lat},${pot.lng};title:${pot.lbl};addr:${pot.dtl}&referer=${QQMapKey}`  
            } else if (Platform == 'iOS') {  
                url =  
                    `qqmap://map/marker?marker=coord:${pot.lat},${pot.lng};title:${pot.lbl};addr:${pot.dtl}&referer=${QQMapKey}`  
            }  
        } else if (map.EN == 'maps') {  
            if (Platform == 'Android') {  
                url =  
                    `androidamap://viewMap?lat=${pot.lat}&lon=${pot.lng}&poiname=${pot.lbl}&sourceApplication=${DefaultService.android}&dev=0`  
            } else if (Platform == 'iOS') {  
                // Unlike some schemes, map URLs do not start with a “maps” scheme identifier. Instead, map links are specified as regular http links and are opened either in Safari or the Maps app on the target platform.  
                url = `http://maps.apple.com?ll=${pot.lat},${pot.lng}&q=${pot.lbl}`  
            }  
        }  

        if (url != '') {  
            plus.runtime.openURL(encodeURI(url), async function(e) {  
                console.error(e)  
                let result = await openByBrowser(map, point)  
                resolve(result)  
            });  
            resolve(true)  
        } else {  
            console.error('应用内唤起失败')  
            resolve(false)  
        }  

    })  
}  

// 浏览器唤起地图应用,并标注位置      C2B2C   对应导航path请查阅api文档  
const openByBrowser = function(map, point) {  
    return new Promise((resolve) => {  
        let pot = argHandle(point)  
        let url = ''  
        if (map.EN == 'bdmap') {  
            // 腾讯坐标系转换为百度坐标  
            let p = gcj2bd(pot.lat, pot.lng)  
            pot = {  
                ...pot,  
                ...p  
            }  
            url =  
                `http://api.map.baidu.com/marker?location=${pot.lat},${pot.lng}&title=${pot.lbl}&content=${pot.dtl}&src=${DefaultService.web}&output=html`  
        } else if (map.EN == 'amap') {  
            url =  
                `https://uri.amap.com/marker?position=${pot.lng},${pot.lat}&name=${pot.lbl}&src=${DefaultService.web}&callnative=1`  
        } else if (map.EN == 'qqmap') {  
            url =  
                `https://apis.map.qq.com/uri/v1/marker?marker=coord:${pot.lat},${pot.lng};title:${pot.lbl};addr:${pot.dtl}&referer=${DefaultService.web}`  
        } else if (map.EN == 'maps') {  
            url = `http://maps.apple.com?ll=${pot.lat},${pot.lng}&q=${pot.lbl}`  
        }  

        if (url != '') {  
            plus.runtime.openURL(encodeURI(url), function(e) {  
                console.error(e)  
                resolve(false)  
            });  
            resolve(true)  
        } else {  
            console.error('浏览器唤起失败')  
            resolve(false)  
        }  

    })  
}  

// 地图标注,map指定唤起地图(string,['qqmap','amap','bdmap','maps']之一),point坐标对象(object,DefaultPoint)  
const navTo = async function(point, map) {  
    // test  
    // let result = openByBrowser(MapsInfo.pingguo, point)  
    // let result = await openByApp(MapsInfo.pingguo, point)  
    // console.log('打开结果', result)  
    // return false  

    let result = false  
    let res = mapsExist()  
    if (res.length > 0) { // 无地图应用  
        let index = res.findIndex(e => { // 参数有指定地图应用  
            return e.EN == map  
        })  
        if (index != -1) {  
            result = await openByApp(res[index], point)  
        } else if (res.findIndex(e => { // 如果有安装默认地图应用,优先使用默认地图Api  
                return e.EN == DefaultMap.EN  
            }) != -1) {  
            result = await openByApp(DefaultMap, point)  
        } else { // 默认用第一个地图导航  
            result = await openByApp(res[0], point)  
        }  
    } else { // 无地图应用,浏览器打开  
        let arr = Object.values(MapsInfo)  
        let index = Object.values(MapsInfo).findIndex(e => { // 参数有指定地图应用  
            return e.EN == map  
        })  
        if (index != -1) {  
            result = await openByBrowser(arr[index], point)  
        } else {  
            result = await openByBrowser(DefaultMap, point)  
        }  
    }  
    return result  
}  

module.exports = {  
    navTo  
}  
收起阅读 »

企业账号 iOS证书(.p12)和描述文件(.mobileprovision)申请

云打包、自定义基座时,因没有个人开发者账号,使用的是企业版账号,按照https://ask.dcloud.net.cn/article/152的教程生成证书没问题,但是添加设备、Identifiers、profiles的时候没有自动添加选项,给我造成了一定的困扰,现分享下经验。

Identifiers
在xcode——target——Signing&Capabilities中志杰创建后,会自动创建到企业账号的Identifiers中

设备
插入真机调试时,会自动创建到企业账号的Devices中

Profiles
这个耗费了半天时间,百度、Google也没找到指导教程,直到在官方文档中看到一句话

原来已经由Xcode自动接管了。百度下:provisioning profile 本地路径,找到了~/Library/MobileDevice/Provisioning Profiles,在这个目录下找到项目的.mobileprovision文件即为描述文件。

另一个问题

需要企业账号的管理员权限去创建profiles(未验证,因为没有管理员权限);

继续阅读 »

云打包、自定义基座时,因没有个人开发者账号,使用的是企业版账号,按照https://ask.dcloud.net.cn/article/152的教程生成证书没问题,但是添加设备、Identifiers、profiles的时候没有自动添加选项,给我造成了一定的困扰,现分享下经验。

Identifiers
在xcode——target——Signing&Capabilities中志杰创建后,会自动创建到企业账号的Identifiers中

设备
插入真机调试时,会自动创建到企业账号的Devices中

Profiles
这个耗费了半天时间,百度、Google也没找到指导教程,直到在官方文档中看到一句话

原来已经由Xcode自动接管了。百度下:provisioning profile 本地路径,找到了~/Library/MobileDevice/Provisioning Profiles,在这个目录下找到项目的.mobileprovision文件即为描述文件。

另一个问题

需要企业账号的管理员权限去创建profiles(未验证,因为没有管理员权限);

收起阅读 »

直播预告 | 个推分享从治数到用数的企业数据智能实践

如今,数智化转型成为企业的必选战略。作为一家数据智能上市企业,每日互动(个推)面向行业客户以及政府单位提供专业的数据智能产品和解决方案,用“数据让产业更智能”。结合自身数据智能实践,每日互动(个推)重磅打造“行业数智化实战”系列直播,旨在与行业伙伴们交流企业数智化升级过程中的创新方案和宝贵经验,助力产业进入创新增长的快车道。

12月16日(周四)19:30-20:30,“行业数智化实战”系列直播第一期火热来袭!我们邀请到了个推数据中台“每日治数平台”产品负责人袁凯,为大家带来个推在品牌营销、智慧高速等垂直行业的生动实践。

直播亮点:

解读每日互动治理和挖掘万亿级数据的“高能公式”
分享每日互动在不同垂直行业的数智化“成果样板”
总结企业落地数字化升级战略过程中的“避坑经验”

继续阅读 »

如今,数智化转型成为企业的必选战略。作为一家数据智能上市企业,每日互动(个推)面向行业客户以及政府单位提供专业的数据智能产品和解决方案,用“数据让产业更智能”。结合自身数据智能实践,每日互动(个推)重磅打造“行业数智化实战”系列直播,旨在与行业伙伴们交流企业数智化升级过程中的创新方案和宝贵经验,助力产业进入创新增长的快车道。

12月16日(周四)19:30-20:30,“行业数智化实战”系列直播第一期火热来袭!我们邀请到了个推数据中台“每日治数平台”产品负责人袁凯,为大家带来个推在品牌营销、智慧高速等垂直行业的生动实践。

直播亮点:

解读每日互动治理和挖掘万亿级数据的“高能公式”
分享每日互动在不同垂直行业的数智化“成果样板”
总结企业落地数字化升级战略过程中的“避坑经验”

收起阅读 »

系统分享显示本App、获取分享的文件、选择文件以其它应用打开列表(ios)

系统分享显示本App、获取分享的文件、选择文件以其它应用打开列表(ios):https://ext.dcloud.net.cn/plugin?id=6901

系统分享显示本App、获取分享的文件、选择文件以其它应用打开列表(ios):https://ext.dcloud.net.cn/plugin?id=6901

热更新 WGT安装包中manifest.json文件的version版本不匹配

热更新

app 热更新后 小米手机清除全部数据,然后重启app ,app 重新执行热更新 这是后就会提示 WGT安装包中manifest.json文件的version版本不匹配,查看plus.runtime.version 果然变成了 基座包版本号,然后就会一直包错 Cannot read property 'screen' of undefined

解决:热更新参数WidgetOptions改为force: true(Boolean 类型 )是否强制安装 就好了。原因暂时未知
true表示强制安装,不进行版本号的校验;false则需要版本号校验,如果将要安装应用的版本号不高于现有应用的版本号则终止安装,并返回安装失败。 仅安装wgt和wgtu时生效,默认值 false。
plus.runtime.install(res.tempFilePath, {
force: true
}

继续阅读 »

app 热更新后 小米手机清除全部数据,然后重启app ,app 重新执行热更新 这是后就会提示 WGT安装包中manifest.json文件的version版本不匹配,查看plus.runtime.version 果然变成了 基座包版本号,然后就会一直包错 Cannot read property 'screen' of undefined

解决:热更新参数WidgetOptions改为force: true(Boolean 类型 )是否强制安装 就好了。原因暂时未知
true表示强制安装,不进行版本号的校验;false则需要版本号校验,如果将要安装应用的版本号不高于现有应用的版本号则终止安装,并返回安装失败。 仅安装wgt和wgtu时生效,默认值 false。
plus.runtime.install(res.tempFilePath, {
force: true
}

收起阅读 »