HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

HQChart使用教程44-uniapp使用条件编译同时支持h5,app,小程序

今天在群里帮一位朋友排查HQChart同时在多端使用的问题。我整理了以下

背景

由于小程序/app很多局限性(无法创建DOM,canvas异步绘图等)导致HQChart开发的时候分成两个版本(小程序版本和H5页面版本)。
使用条件编译我们可以把这2个版本组件同时包含的工程中,并且通过平台判断动态加载对应版本的js.

安装插件

在工程里建2个目录,把HQChart的2个版本分别考入对应的目录里。
版本对应关系看以前的教程
HQChart使用教程35 - 如何在uni-app创建K线图(h5)
HQChart使用教程37 - 如何在uni-app创建k线图(app)
在这里插入图片描述

template 设置

由于小程序/app是无法动态创建dom,所有只能是先在模板里写死一个画布,在初始化的时候绑定到HQChart中。H5是可以直接内部创建dom,所以只需要传入一个div,HQChart自动会创建画布. 我们使用条件编译在不同的平台使用不同的模板
注意 id的名字尽量使用不一样的,如h5如果使用id='kline' 在app/小程序就使用id='kline2'

<!--  #ifdef  H5 -->  
<div>  
    <div class='kline' id="kline" ref='kline'  v-show="KLine.IsShow"></div>  
    <div class='minute' id="minute" ref='minute'  v-show="Minute.IsShow"></div>  
</div>  
<!--  #endif -->  

<!--  #ifndef  H5 -->  
<view>  
    <canvas id="kline2" canvas-id='kline2' class='kline2' v-bind:style="{width: ChartWidth+'px', height: ChartHeight+'px'}"  v-show="KLine.IsShow"  
      @touchstart="KLineTouchStart" @touchmove='KLineTouchMove' @touchend='KLineTouchEnd' ></canvas>    
      <canvas id="minute2" canvas-id='minute2' class='minute' v-bind:style="{width: ChartWidth+'px', height: ChartHeight+'px'}" v-show="Minute.IsShow"  
       @touchstart="MinuteTouchStart" @touchmove='MinuteTouchMove' @touchend='MinuteTouchEnd' ></canvas>  
</view>  
<!--  #endif -->

这样2个模板就同时存在一个页面中了

创建插件设置

为每个平台创建一个创建插件的函数,然后通过一个总的创建函数动态调用对应的创建方法
下面是也创建K线图为例子‘CreateKLineChart_h5()'是h5平台的创建插件方法, 'CreateKLineChart_app()'是app和小程序创建的方法,在"CreateKLineChart()'通过条件编译就可以动态选择使用对应的创建函数
为了代码的可读和可维护性我这边是拆分成2个创建函数,你也可以在CreateKLineChart里面把2个平台的创建都写里面,只过不这样可读性比较差。

CreateKLineChart()  
{  
    // #ifdef H5  
    this.CreateKLineChart_h5();  
    // #endif  

    // #ifndef H5  
    this.CreateKLineChart_app();  
    // #endif  
},

下面是2个平台对应的创建方法,走势图的创建也是一样

CreateKLineChart_h5()  //创建K线图  
{  
    if (g_KLine.JSChart) return;  
    this.KLine.Option.Symbol=this.Symbol;  
    let chart=HQChart.JSChart.Init(this.$refs.kline);  
this.KLine.Option.NetworkFilter=this.NetworkFilter;  
    chart.SetOption(this.KLine.Option);  
    g_KLine.JSChart=chart;  
},  

CreateKLineChart_app()  
{  
    if (this.KLine.JSChart) return;  

    let element = new JSCommon.JSCanvasElement();  
    // #ifdef APP-PLUS  
    element.IsUniApp=true;  //canvas需要指定下 是uniapp的app  
    // #endif  
    element.ID = 'kline2';  
    element.Height = this.ChartHeight;  //高度宽度需要手动绑定!!  
    element.Width = this.ChartWidth;  

    g_KLine.JSChart = JSCommon.JSChart.Init(element);  
    this.KLine.Option.NetworkFilter=this.NetworkFilter;  
    this.KLine.Option.Symbol=this.Symbol;  
    g_KLine.JSChart.SetOption(this.KLine.Option);  
},

HQChart大小调整

由于app/小程序无法获取dom,所以只能是在外部把画布的长宽设置到HQChart中(动态获取只能通过其他的查询元素函数获取,比较麻烦,关键还是异步的,非常搞不懂获取一个元素信息还使用异步,难道以目前的手机配置查询几千几万了dom信息不能在毫秒级处理完),
H5是可以动态获取dom,就不存在这个文件,改变了外层的div调用HQChart的OnSize()方法就可以动态把div的大小绑定画布上。

OnSize()  
{  
    // #ifdef H5  
    this.OnSize_h5();  
    // #endif  
},  

OnSize_h5()  
{  
    var chartHeight = this.ChartHeight;  
    var chartWidth = this.ChartWidth;  

    var kline=this.$refs.kline;  
    kline.style.width=chartWidth+'px';  
    kline.style.height=chartHeight+'px';  
    if (g_KLine.JSChart) g_KLine.JSChart.OnSize();  

    var minute=this.$refs.minute;  
    minute.style.width=chartWidth+'px';  
    minute.style.height=chartHeight+'px';  
    if (g_Minute.JSChart) g_Minute.JSChart.OnSize();  
},

这样多端支持就完成了。点运行,然后喝杯咖啡吧,编译调试真的很慢。

效果图

在这里插入图片描述
在这里插入图片描述

完整代码

<template>  
    <div class='divchart' >  
        <!--  #ifdef  H5 -->  
        <div>  
        <div class='kline' id="kline" ref='kline'  v-show="KLine.IsShow"></div>  
        <div class='minute' id="minute" ref='minute'  v-show="Minute.IsShow"></div>  
        </div>  
        <!--  #endif -->  

        <!--  #ifndef  H5 -->  
        <view>  
            <canvas id="kline2" canvas-id='kline2' class='kline2' v-bind:style="{width: ChartWidth+'px', height: ChartHeight+'px'}"  v-show="KLine.IsShow"  
              @touchstart="KLineTouchStart" @touchmove='KLineTouchMove' @touchend='KLineTouchEnd' ></canvas>    
              <canvas id="minute2" canvas-id='minute2' class='minute' v-bind:style="{width: ChartWidth+'px', height: ChartHeight+'px'}" v-show="Minute.IsShow"  
               @touchstart="MinuteTouchStart" @touchmove='MinuteTouchMove' @touchend='MinuteTouchEnd' ></canvas>  
        </view>  
        <!--  #endif -->  

        <div class="button-sp-area">  
            <button class="mini-btn" type="default" size="mini" @click="ChangeMinutePeriod(MINUTE_PERIOD_ID.MINUTE_ID)">分时</button>  
            <button class="mini-btn" type="default" size="mini" @click="ChangeMinutePeriod(MINUTE_PERIOD_ID.MINUTE_5DAY_ID)">5日</button>  
            <button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(KLINE_PERIOD_ID.KLINE_DAY_ID)">日线</button>  
            <button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(KLINE_PERIOD_ID.KLINE_WEEK_ID)">周线</button>  
            <button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(KLINE_PERIOD_ID.KLINE_MINUTE_ID)">1分钟</button>  
            <button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(KLINE_PERIOD_ID.KLINE_15MINUTE_ID)">15分钟</button>  
        </div>  

    </div>  
</template>  

<script>  
// #ifdef H5      
import HQChart from '../../umychart_uniapp_h5/umychart.uniapp.h5.js'  
// #endif  

// #ifndef H5  
import {JSCommon} from '../../umychart.uniapp/umychart.wechat.3.0.js'  
// #endif  

function DefaultData() { }  

DefaultData.GetKLineOption = function ()   
{  
    let data =   
    {  
        Type: '历史K线图',   

        Windows: //窗口指标  
        [  
            {Index:"MA",Modify: false, Change: false},   
            {Index:"VOL",Modify: false, Change: false}  
        ],   

        IsCorssOnlyDrawKLine:true,  
        CorssCursorTouchEnd:true,  

        Border: //边框  
        {  
            Left:   1,  
            Right:  1, //右边间距  
            Top:    25,  
            Bottom: 25,  
        },  

        KLine:  
        {  
            Right:1,                            //复权 0 不复权 1 前复权 2 后复权  
            Period:0,                           //周期: 0 日线 1 周线 2 月线 3 年线   
            PageSize:30,  
            IsShowTooltip:false  
        },  

        ExtendChart:  
        [  
            {Name:'KLineTooltip' }, //开启手机端tooltip  
        ],   

        Frame:  //子框架设置  
        [  
            {SplitCount:3},  
            {SplitCount:2},  
            {SplitCount:3},  
        ],  

    };  

    return data;  
}  

DefaultData.GetMinuteOption=function()  
{  
    var option=   
    {  
        Type:'分钟走势图',   //创建图形类型  

        Windows: //窗口指标  
        [  

        ],   

        IsAutoUpdate:true,       //是自动更新数据  
        DayCount:1,                 //1 最新交易日数据 >1 多日走势图  
        IsShowRightMenu:false,       //是否显示右键菜单  
        CorssCursorTouchEnd:true,  

        MinuteLine:  
        {  
            //IsDrawAreaPrice:false,      //是否画价格面积图  
        },  

        Border: //边框  
        {  
            Left:1,    //左边间距  
            Right:1,   //右边间距  
            Top:20,  
            Bottom:20  
        },  

        Frame:  //子框架设置  
        [  
            {SplitCount:3},  
            {SplitCount:2},  
            {SplitCount:3},  
        ],  

        ExtendChart:    //扩展图形  
        [  
            {Name:'MinuteTooltip' }  //手机端tooltip  
        ],  
    };  

    return option;  
}  

//周期枚举  
var KLINE_PERIOD_ID=  
{  
    KLINE_DAY_ID:0,  
    KLINE_WEEK_ID:1,  
    KLINE_MONTH_ID:2,  
    KLINE_YEAR_ID:3,  

    KLINE_MINUTE_ID:4,  
    KLINE_5MINUTE_ID:5,  
    KLINE_15MINUTE_ID:6,  
    KLINE_30MINUTE_ID:7,  
    KLINE_60MINUTE_ID:8  
}  

//周期枚举  
var MINUTE_PERIOD_ID=  
{  
    MINUTE_ID:1,  
    MINUTE_2DAY_ID:2,  
    MINUTE_3DAY_ID:3,  
    MINUTE_4DAY_ID:4,  
    MINUTE_5DAY_ID:5,  
}  

var g_KLine={ JSChart:null };  
var g_Minute={ JSChart:null };  
export default   
{  
    data()   
    {  
        let data=  
        {  
            Symbol:'600000.sh',  
            ChartWidth:300,  
            ChartHeight:500,  
            KLine:  
            {  

                Option:DefaultData.GetKLineOption(),   
                IsShow:true,  
            },  
            Minute:  
            {  

                Option:DefaultData.GetMinuteOption(),  
                IsShow:false,  
            },  

            MINUTE_PERIOD_ID:MINUTE_PERIOD_ID,  
            KLINE_PERIOD_ID:KLINE_PERIOD_ID,  
        };  

        return data;  
    },  

    onLoad()   
    {  

    },  

    onReady()  
    {  
        this.OnSize();  
        this.CreateKLineChart();   
    },  

    methods:   
    {  
        OnSize()  
        {  
            // #ifdef H5  
            this.OnSize_h5();  
            // #endif  
        },  

        OnSize_h5()  
        {  
            var chartHeight = this.ChartHeight;  
            var chartWidth = this.ChartWidth;  

            var kline=this.$refs.kline;  
            kline.style.width=chartWidth+'px';  
            kline.style.height=chartHeight+'px';  
            if (g_KLine.JSChart) g_KLine.JSChart.OnSize();  

            var minute=this.$refs.minute;  
            minute.style.width=chartWidth+'px';  
            minute.style.height=chartHeight+'px';  
            if (g_Minute.JSChart) g_Minute.JSChart.OnSize();  
        },  

        CreateKLineChart_h5()  //创建K线图  
        {  
            if (g_KLine.JSChart) return;  
            this.KLine.Option.Symbol=this.Symbol;  
            let chart=HQChart.JSChart.Init(this.$refs.kline);  
            this.KLine.Option.NetworkFilter=this.NetworkFilter;  
            chart.SetOption(this.KLine.Option);  
            g_KLine.JSChart=chart;  
        },  

        CreateKLineChart_app()  
        {  
            if (this.KLine.JSChart) return;  

            let element = new JSCommon.JSCanvasElement();  
            // #ifdef APP-PLUS  
            element.IsUniApp=true;  //canvas需要指定下 是uniapp的app  
            // #endif  
            element.ID = 'kline2';  
            element.Height = this.ChartHeight;  //高度宽度需要手动绑定!!  
            element.Width = this.ChartWidth;  

            g_KLine.JSChart = JSCommon.JSChart.Init(element);  
            this.KLine.Option.NetworkFilter=this.NetworkFilter;  
            this.KLine.Option.Symbol=this.Symbol;  
            g_KLine.JSChart.SetOption(this.KLine.Option);  
        },  

        CreateKLineChart()  
        {  
            // #ifdef H5  
            this.CreateKLineChart_h5();  
            // #endif  

            // #ifndef H5  
            this.CreateKLineChart_app();  
            // #endif  
        },  

        //K线周期切换  
        ChangeKLinePeriod:function(period)  
        {  
            this.Minute.IsShow=false;  
            this.KLine.IsShow=true;  
            if (!g_KLine.JSChart)    //不存在创建  
            {  
                this.KLine.Option.Period=period;  
                this.CreateKLineChart_h5();  
            }  
            else  
            {  
                g_KLine.JSChart.ChangePeriod(period);  
            }  
        },  

        CreateMinuteChart_h5() //创建日线图  
        {  
            if (g_Minute.JSChart) return;  
            this.Minute.Option.Symbol=this.Symbol;  
            let chart=HQChart.JSChart.Init(this.$refs.minute);  
            this.Minute.Option.NetworkFilter=this.NetworkFilter;  
            chart.SetOption(this.Minute.Option);  
            g_Minute.JSChart=chart;  
        },  

        CreateMinuteChart_app()  
        {  
            if (g_Minute.JSChart) return;  

            var element = new JSCommon.JSCanvasElement();  
            // #ifdef APP-PLUS  
            element.IsUniApp=true;  //canvas需要指定下 是uniapp的app  
            // #endif  
            element.ID = 'minute2';  
            element.Height = this.ChartHeight;  //高度宽度需要手动绑定!!  
            element.Width = this.ChartWidth;  

            g_Minute.JSChart = JSCommon.JSChart.Init(element);  
            this.Minute.Option.NetworkFilter=this.NetworkFilter;  
            this.Minute.Option.Symbol=this.Symbol;  
            g_Minute.JSChart.SetOption(this.Minute.Option);  
        },  

        CreateMinuteChart()  
        {  
            // #ifdef H5  
            this.CreateMinuteChart_h5();  
            // #endif  

            // #ifndef H5  
            this.CreateMinuteChart_app();  
            // #endif  
        },  

        //走势图多日切换  
        ChangeMinutePeriod:function(period)  
        {  
            this.Minute.IsShow=true;  
            this.KLine.IsShow=false;  
            if (!g_Minute.JSChart)   //不存在创建  
            {  
                this.Minute.Option.DayCount=period;  
                this.CreateMinuteChart();  
            }  
            else  
            {  
                g_Minute.JSChart.ChangeDayCount(period);  
            }  
        },  

        NetworkFilter:function(data, callback)  
        {  
            console.log(`[HQChart:NetworkFilter] Name=${data.Name} Explain=${data.Explain}` );  
        },  

        ///////////////////////////////////////////////  
        //手势事件 app/小程序才有  
        //KLine事件  
        KLineTouchStart: function (event)   
        {  
          if (g_KLine.JSChart) g_KLine.JSChart.OnTouchStart(event);  
        },  

        KLineTouchMove: function (event)   
        {  
          if (g_KLine.JSChart) g_KLine.JSChart.OnTouchMove(event);  
        },  

        KLineTouchEnd: function (event)   
        {  
          if (g_KLine.JSChart) g_KLine.JSChart.OnTouchEnd(event);  
        },  

        //走势图事件  
        MinuteTouchStart: function (event)   
        {  
          if (g_Minute.JSChart) g_Minute.JSChart.OnTouchStart(event);  
        },  

        MinuteTouchMove: function (event)   
        {  
          if (g_Minute.JSChart) g_Minute.JSChart.OnTouchMove(event);  
        },  

        MinuteTouchEnd: function (event)   
        {  
          if (g_Minute.JSChart) g_Minute.JSChart.OnTouchEnd(event);  
        },  
    }  
}  

</script>  

<style>  

</style>  

如果还有问题可以加交流QQ群: 950092318

HQChart代码地址
地址:github.com/jones2000/HQChart

继续阅读 »

今天在群里帮一位朋友排查HQChart同时在多端使用的问题。我整理了以下

背景

由于小程序/app很多局限性(无法创建DOM,canvas异步绘图等)导致HQChart开发的时候分成两个版本(小程序版本和H5页面版本)。
使用条件编译我们可以把这2个版本组件同时包含的工程中,并且通过平台判断动态加载对应版本的js.

安装插件

在工程里建2个目录,把HQChart的2个版本分别考入对应的目录里。
版本对应关系看以前的教程
HQChart使用教程35 - 如何在uni-app创建K线图(h5)
HQChart使用教程37 - 如何在uni-app创建k线图(app)
在这里插入图片描述

template 设置

由于小程序/app是无法动态创建dom,所有只能是先在模板里写死一个画布,在初始化的时候绑定到HQChart中。H5是可以直接内部创建dom,所以只需要传入一个div,HQChart自动会创建画布. 我们使用条件编译在不同的平台使用不同的模板
注意 id的名字尽量使用不一样的,如h5如果使用id='kline' 在app/小程序就使用id='kline2'

<!--  #ifdef  H5 -->  
<div>  
    <div class='kline' id="kline" ref='kline'  v-show="KLine.IsShow"></div>  
    <div class='minute' id="minute" ref='minute'  v-show="Minute.IsShow"></div>  
</div>  
<!--  #endif -->  

<!--  #ifndef  H5 -->  
<view>  
    <canvas id="kline2" canvas-id='kline2' class='kline2' v-bind:style="{width: ChartWidth+'px', height: ChartHeight+'px'}"  v-show="KLine.IsShow"  
      @touchstart="KLineTouchStart" @touchmove='KLineTouchMove' @touchend='KLineTouchEnd' ></canvas>    
      <canvas id="minute2" canvas-id='minute2' class='minute' v-bind:style="{width: ChartWidth+'px', height: ChartHeight+'px'}" v-show="Minute.IsShow"  
       @touchstart="MinuteTouchStart" @touchmove='MinuteTouchMove' @touchend='MinuteTouchEnd' ></canvas>  
</view>  
<!--  #endif -->

这样2个模板就同时存在一个页面中了

创建插件设置

为每个平台创建一个创建插件的函数,然后通过一个总的创建函数动态调用对应的创建方法
下面是也创建K线图为例子‘CreateKLineChart_h5()'是h5平台的创建插件方法, 'CreateKLineChart_app()'是app和小程序创建的方法,在"CreateKLineChart()'通过条件编译就可以动态选择使用对应的创建函数
为了代码的可读和可维护性我这边是拆分成2个创建函数,你也可以在CreateKLineChart里面把2个平台的创建都写里面,只过不这样可读性比较差。

CreateKLineChart()  
{  
    // #ifdef H5  
    this.CreateKLineChart_h5();  
    // #endif  

    // #ifndef H5  
    this.CreateKLineChart_app();  
    // #endif  
},

下面是2个平台对应的创建方法,走势图的创建也是一样

CreateKLineChart_h5()  //创建K线图  
{  
    if (g_KLine.JSChart) return;  
    this.KLine.Option.Symbol=this.Symbol;  
    let chart=HQChart.JSChart.Init(this.$refs.kline);  
this.KLine.Option.NetworkFilter=this.NetworkFilter;  
    chart.SetOption(this.KLine.Option);  
    g_KLine.JSChart=chart;  
},  

CreateKLineChart_app()  
{  
    if (this.KLine.JSChart) return;  

    let element = new JSCommon.JSCanvasElement();  
    // #ifdef APP-PLUS  
    element.IsUniApp=true;  //canvas需要指定下 是uniapp的app  
    // #endif  
    element.ID = 'kline2';  
    element.Height = this.ChartHeight;  //高度宽度需要手动绑定!!  
    element.Width = this.ChartWidth;  

    g_KLine.JSChart = JSCommon.JSChart.Init(element);  
    this.KLine.Option.NetworkFilter=this.NetworkFilter;  
    this.KLine.Option.Symbol=this.Symbol;  
    g_KLine.JSChart.SetOption(this.KLine.Option);  
},

HQChart大小调整

由于app/小程序无法获取dom,所以只能是在外部把画布的长宽设置到HQChart中(动态获取只能通过其他的查询元素函数获取,比较麻烦,关键还是异步的,非常搞不懂获取一个元素信息还使用异步,难道以目前的手机配置查询几千几万了dom信息不能在毫秒级处理完),
H5是可以动态获取dom,就不存在这个文件,改变了外层的div调用HQChart的OnSize()方法就可以动态把div的大小绑定画布上。

OnSize()  
{  
    // #ifdef H5  
    this.OnSize_h5();  
    // #endif  
},  

OnSize_h5()  
{  
    var chartHeight = this.ChartHeight;  
    var chartWidth = this.ChartWidth;  

    var kline=this.$refs.kline;  
    kline.style.width=chartWidth+'px';  
    kline.style.height=chartHeight+'px';  
    if (g_KLine.JSChart) g_KLine.JSChart.OnSize();  

    var minute=this.$refs.minute;  
    minute.style.width=chartWidth+'px';  
    minute.style.height=chartHeight+'px';  
    if (g_Minute.JSChart) g_Minute.JSChart.OnSize();  
},

这样多端支持就完成了。点运行,然后喝杯咖啡吧,编译调试真的很慢。

效果图

在这里插入图片描述
在这里插入图片描述

完整代码

<template>  
    <div class='divchart' >  
        <!--  #ifdef  H5 -->  
        <div>  
        <div class='kline' id="kline" ref='kline'  v-show="KLine.IsShow"></div>  
        <div class='minute' id="minute" ref='minute'  v-show="Minute.IsShow"></div>  
        </div>  
        <!--  #endif -->  

        <!--  #ifndef  H5 -->  
        <view>  
            <canvas id="kline2" canvas-id='kline2' class='kline2' v-bind:style="{width: ChartWidth+'px', height: ChartHeight+'px'}"  v-show="KLine.IsShow"  
              @touchstart="KLineTouchStart" @touchmove='KLineTouchMove' @touchend='KLineTouchEnd' ></canvas>    
              <canvas id="minute2" canvas-id='minute2' class='minute' v-bind:style="{width: ChartWidth+'px', height: ChartHeight+'px'}" v-show="Minute.IsShow"  
               @touchstart="MinuteTouchStart" @touchmove='MinuteTouchMove' @touchend='MinuteTouchEnd' ></canvas>  
        </view>  
        <!--  #endif -->  

        <div class="button-sp-area">  
            <button class="mini-btn" type="default" size="mini" @click="ChangeMinutePeriod(MINUTE_PERIOD_ID.MINUTE_ID)">分时</button>  
            <button class="mini-btn" type="default" size="mini" @click="ChangeMinutePeriod(MINUTE_PERIOD_ID.MINUTE_5DAY_ID)">5日</button>  
            <button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(KLINE_PERIOD_ID.KLINE_DAY_ID)">日线</button>  
            <button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(KLINE_PERIOD_ID.KLINE_WEEK_ID)">周线</button>  
            <button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(KLINE_PERIOD_ID.KLINE_MINUTE_ID)">1分钟</button>  
            <button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(KLINE_PERIOD_ID.KLINE_15MINUTE_ID)">15分钟</button>  
        </div>  

    </div>  
</template>  

<script>  
// #ifdef H5      
import HQChart from '../../umychart_uniapp_h5/umychart.uniapp.h5.js'  
// #endif  

// #ifndef H5  
import {JSCommon} from '../../umychart.uniapp/umychart.wechat.3.0.js'  
// #endif  

function DefaultData() { }  

DefaultData.GetKLineOption = function ()   
{  
    let data =   
    {  
        Type: '历史K线图',   

        Windows: //窗口指标  
        [  
            {Index:"MA",Modify: false, Change: false},   
            {Index:"VOL",Modify: false, Change: false}  
        ],   

        IsCorssOnlyDrawKLine:true,  
        CorssCursorTouchEnd:true,  

        Border: //边框  
        {  
            Left:   1,  
            Right:  1, //右边间距  
            Top:    25,  
            Bottom: 25,  
        },  

        KLine:  
        {  
            Right:1,                            //复权 0 不复权 1 前复权 2 后复权  
            Period:0,                           //周期: 0 日线 1 周线 2 月线 3 年线   
            PageSize:30,  
            IsShowTooltip:false  
        },  

        ExtendChart:  
        [  
            {Name:'KLineTooltip' }, //开启手机端tooltip  
        ],   

        Frame:  //子框架设置  
        [  
            {SplitCount:3},  
            {SplitCount:2},  
            {SplitCount:3},  
        ],  

    };  

    return data;  
}  

DefaultData.GetMinuteOption=function()  
{  
    var option=   
    {  
        Type:'分钟走势图',   //创建图形类型  

        Windows: //窗口指标  
        [  

        ],   

        IsAutoUpdate:true,       //是自动更新数据  
        DayCount:1,                 //1 最新交易日数据 >1 多日走势图  
        IsShowRightMenu:false,       //是否显示右键菜单  
        CorssCursorTouchEnd:true,  

        MinuteLine:  
        {  
            //IsDrawAreaPrice:false,      //是否画价格面积图  
        },  

        Border: //边框  
        {  
            Left:1,    //左边间距  
            Right:1,   //右边间距  
            Top:20,  
            Bottom:20  
        },  

        Frame:  //子框架设置  
        [  
            {SplitCount:3},  
            {SplitCount:2},  
            {SplitCount:3},  
        ],  

        ExtendChart:    //扩展图形  
        [  
            {Name:'MinuteTooltip' }  //手机端tooltip  
        ],  
    };  

    return option;  
}  

//周期枚举  
var KLINE_PERIOD_ID=  
{  
    KLINE_DAY_ID:0,  
    KLINE_WEEK_ID:1,  
    KLINE_MONTH_ID:2,  
    KLINE_YEAR_ID:3,  

    KLINE_MINUTE_ID:4,  
    KLINE_5MINUTE_ID:5,  
    KLINE_15MINUTE_ID:6,  
    KLINE_30MINUTE_ID:7,  
    KLINE_60MINUTE_ID:8  
}  

//周期枚举  
var MINUTE_PERIOD_ID=  
{  
    MINUTE_ID:1,  
    MINUTE_2DAY_ID:2,  
    MINUTE_3DAY_ID:3,  
    MINUTE_4DAY_ID:4,  
    MINUTE_5DAY_ID:5,  
}  

var g_KLine={ JSChart:null };  
var g_Minute={ JSChart:null };  
export default   
{  
    data()   
    {  
        let data=  
        {  
            Symbol:'600000.sh',  
            ChartWidth:300,  
            ChartHeight:500,  
            KLine:  
            {  

                Option:DefaultData.GetKLineOption(),   
                IsShow:true,  
            },  
            Minute:  
            {  

                Option:DefaultData.GetMinuteOption(),  
                IsShow:false,  
            },  

            MINUTE_PERIOD_ID:MINUTE_PERIOD_ID,  
            KLINE_PERIOD_ID:KLINE_PERIOD_ID,  
        };  

        return data;  
    },  

    onLoad()   
    {  

    },  

    onReady()  
    {  
        this.OnSize();  
        this.CreateKLineChart();   
    },  

    methods:   
    {  
        OnSize()  
        {  
            // #ifdef H5  
            this.OnSize_h5();  
            // #endif  
        },  

        OnSize_h5()  
        {  
            var chartHeight = this.ChartHeight;  
            var chartWidth = this.ChartWidth;  

            var kline=this.$refs.kline;  
            kline.style.width=chartWidth+'px';  
            kline.style.height=chartHeight+'px';  
            if (g_KLine.JSChart) g_KLine.JSChart.OnSize();  

            var minute=this.$refs.minute;  
            minute.style.width=chartWidth+'px';  
            minute.style.height=chartHeight+'px';  
            if (g_Minute.JSChart) g_Minute.JSChart.OnSize();  
        },  

        CreateKLineChart_h5()  //创建K线图  
        {  
            if (g_KLine.JSChart) return;  
            this.KLine.Option.Symbol=this.Symbol;  
            let chart=HQChart.JSChart.Init(this.$refs.kline);  
            this.KLine.Option.NetworkFilter=this.NetworkFilter;  
            chart.SetOption(this.KLine.Option);  
            g_KLine.JSChart=chart;  
        },  

        CreateKLineChart_app()  
        {  
            if (this.KLine.JSChart) return;  

            let element = new JSCommon.JSCanvasElement();  
            // #ifdef APP-PLUS  
            element.IsUniApp=true;  //canvas需要指定下 是uniapp的app  
            // #endif  
            element.ID = 'kline2';  
            element.Height = this.ChartHeight;  //高度宽度需要手动绑定!!  
            element.Width = this.ChartWidth;  

            g_KLine.JSChart = JSCommon.JSChart.Init(element);  
            this.KLine.Option.NetworkFilter=this.NetworkFilter;  
            this.KLine.Option.Symbol=this.Symbol;  
            g_KLine.JSChart.SetOption(this.KLine.Option);  
        },  

        CreateKLineChart()  
        {  
            // #ifdef H5  
            this.CreateKLineChart_h5();  
            // #endif  

            // #ifndef H5  
            this.CreateKLineChart_app();  
            // #endif  
        },  

        //K线周期切换  
        ChangeKLinePeriod:function(period)  
        {  
            this.Minute.IsShow=false;  
            this.KLine.IsShow=true;  
            if (!g_KLine.JSChart)    //不存在创建  
            {  
                this.KLine.Option.Period=period;  
                this.CreateKLineChart_h5();  
            }  
            else  
            {  
                g_KLine.JSChart.ChangePeriod(period);  
            }  
        },  

        CreateMinuteChart_h5() //创建日线图  
        {  
            if (g_Minute.JSChart) return;  
            this.Minute.Option.Symbol=this.Symbol;  
            let chart=HQChart.JSChart.Init(this.$refs.minute);  
            this.Minute.Option.NetworkFilter=this.NetworkFilter;  
            chart.SetOption(this.Minute.Option);  
            g_Minute.JSChart=chart;  
        },  

        CreateMinuteChart_app()  
        {  
            if (g_Minute.JSChart) return;  

            var element = new JSCommon.JSCanvasElement();  
            // #ifdef APP-PLUS  
            element.IsUniApp=true;  //canvas需要指定下 是uniapp的app  
            // #endif  
            element.ID = 'minute2';  
            element.Height = this.ChartHeight;  //高度宽度需要手动绑定!!  
            element.Width = this.ChartWidth;  

            g_Minute.JSChart = JSCommon.JSChart.Init(element);  
            this.Minute.Option.NetworkFilter=this.NetworkFilter;  
            this.Minute.Option.Symbol=this.Symbol;  
            g_Minute.JSChart.SetOption(this.Minute.Option);  
        },  

        CreateMinuteChart()  
        {  
            // #ifdef H5  
            this.CreateMinuteChart_h5();  
            // #endif  

            // #ifndef H5  
            this.CreateMinuteChart_app();  
            // #endif  
        },  

        //走势图多日切换  
        ChangeMinutePeriod:function(period)  
        {  
            this.Minute.IsShow=true;  
            this.KLine.IsShow=false;  
            if (!g_Minute.JSChart)   //不存在创建  
            {  
                this.Minute.Option.DayCount=period;  
                this.CreateMinuteChart();  
            }  
            else  
            {  
                g_Minute.JSChart.ChangeDayCount(period);  
            }  
        },  

        NetworkFilter:function(data, callback)  
        {  
            console.log(`[HQChart:NetworkFilter] Name=${data.Name} Explain=${data.Explain}` );  
        },  

        ///////////////////////////////////////////////  
        //手势事件 app/小程序才有  
        //KLine事件  
        KLineTouchStart: function (event)   
        {  
          if (g_KLine.JSChart) g_KLine.JSChart.OnTouchStart(event);  
        },  

        KLineTouchMove: function (event)   
        {  
          if (g_KLine.JSChart) g_KLine.JSChart.OnTouchMove(event);  
        },  

        KLineTouchEnd: function (event)   
        {  
          if (g_KLine.JSChart) g_KLine.JSChart.OnTouchEnd(event);  
        },  

        //走势图事件  
        MinuteTouchStart: function (event)   
        {  
          if (g_Minute.JSChart) g_Minute.JSChart.OnTouchStart(event);  
        },  

        MinuteTouchMove: function (event)   
        {  
          if (g_Minute.JSChart) g_Minute.JSChart.OnTouchMove(event);  
        },  

        MinuteTouchEnd: function (event)   
        {  
          if (g_Minute.JSChart) g_Minute.JSChart.OnTouchEnd(event);  
        },  
    }  
}  

</script>  

<style>  

</style>  

如果还有问题可以加交流QQ群: 950092318

HQChart代码地址
地址:github.com/jones2000/HQChart

收起阅读 »

Android平台解决使用UniPush和个推推送违反谷歌应用商店(GooglePlay)个人和敏感信息政策无法上架的说明

GooglePlay unipush

HBuilderX2.8.4+版本调整为默认不再弹出隐私政策提示框,建议使用Android平台隐私与政策提示框配置方法,参考:https://ask.dcloud.net.cn/article/36937

最近谷歌应用商店(GooglePlay)加强对应用的审查,如果违反个人和敏感信息政策无法通过审核上架,甚至已经上架的应用也可能被下架。
提示信息如下:

sue: Violation of Personal and Sensitive Information policy  

We’ve identified that your app is using an SDK or library that facilitates the collection and transmission of installed packages information without meeting the prominent disclosure guidelines.  

If necessary, you can consult your SDK provider(s) for further information.  

Next steps: Submit your app for another review  

Read through the Personal and Sensitive Information policy and make the appropriate changes to your app. Your app is using the Igexin SDK, which is uploading users installed packages information to http://sdk.open.phone.igexin.com/api.php without a prominent disclosure. Prior to the collection and transmission, it must prominently highlight how the user data will be used, describe the type of data being collected and have the user provide affirmative consent for such use. Your app must handle user data securely, including transmitting it using modern cryptography (for example, over HTTPS).

这是因为UniPush和个推推送使用的SDK需要获取设备的唯一标识等用于下发推送消息,但没有提示用户确认导致违反谷歌的个人和敏感政策。

HBuilderX2.3.4+版已经更新个推SDK解决此问题
使用UniPush和个推推送时会在应用启动时会弹出隐私政策提示框

以上提示框可能会影响用户的使用体验,因此仅会在打GooglePlay渠道包时生效

提交云端打包时请在渠道包项中选择“GooglePlay”

<a id="noprompt"/>

配置不弹出隐私政策提示框

HBuilder2.6.13+版本支持配置不弹出隐私政策提示框
打开manifest.json文件,切换到源码视图:

  • 5+ App项目
    在 "plus" -> "distribute" -> "plugins" -> "push" -> "unipush" 下添加"__privacy_prompt__"字段
  • uni-app项目
    在 "app-plus" -> "distribute" -> "sdkConfigs" -> "push" -> "unipush" 下添加"__privacy_prompt__"字段
    "push" : {  
          "unipush" : {  
              "__privacy_prompt__" : "none"    //值为"show"则显示隐私政策提示框  
          }  
   },

提交云端打包后生效

离线打包配置

在AndroidManifest.xml文件中添加以下meta-data数据

<meta-data android:name="DCLOUD_PUSH_PRIVACY" android:value="false"/>
继续阅读 »

HBuilderX2.8.4+版本调整为默认不再弹出隐私政策提示框,建议使用Android平台隐私与政策提示框配置方法,参考:https://ask.dcloud.net.cn/article/36937

最近谷歌应用商店(GooglePlay)加强对应用的审查,如果违反个人和敏感信息政策无法通过审核上架,甚至已经上架的应用也可能被下架。
提示信息如下:

sue: Violation of Personal and Sensitive Information policy  

We’ve identified that your app is using an SDK or library that facilitates the collection and transmission of installed packages information without meeting the prominent disclosure guidelines.  

If necessary, you can consult your SDK provider(s) for further information.  

Next steps: Submit your app for another review  

Read through the Personal and Sensitive Information policy and make the appropriate changes to your app. Your app is using the Igexin SDK, which is uploading users installed packages information to http://sdk.open.phone.igexin.com/api.php without a prominent disclosure. Prior to the collection and transmission, it must prominently highlight how the user data will be used, describe the type of data being collected and have the user provide affirmative consent for such use. Your app must handle user data securely, including transmitting it using modern cryptography (for example, over HTTPS).

这是因为UniPush和个推推送使用的SDK需要获取设备的唯一标识等用于下发推送消息,但没有提示用户确认导致违反谷歌的个人和敏感政策。

HBuilderX2.3.4+版已经更新个推SDK解决此问题
使用UniPush和个推推送时会在应用启动时会弹出隐私政策提示框

以上提示框可能会影响用户的使用体验,因此仅会在打GooglePlay渠道包时生效

提交云端打包时请在渠道包项中选择“GooglePlay”

<a id="noprompt"/>

配置不弹出隐私政策提示框

HBuilder2.6.13+版本支持配置不弹出隐私政策提示框
打开manifest.json文件,切换到源码视图:

  • 5+ App项目
    在 "plus" -> "distribute" -> "plugins" -> "push" -> "unipush" 下添加"__privacy_prompt__"字段
  • uni-app项目
    在 "app-plus" -> "distribute" -> "sdkConfigs" -> "push" -> "unipush" 下添加"__privacy_prompt__"字段
    "push" : {  
          "unipush" : {  
              "__privacy_prompt__" : "none"    //值为"show"则显示隐私政策提示框  
          }  
   },

提交云端打包后生效

离线打包配置

在AndroidManifest.xml文件中添加以下meta-data数据

<meta-data android:name="DCLOUD_PUSH_PRIVACY" android:value="false"/>
收起阅读 »

uniapp H5端不能够定位的问题

定位 h5 uniapp
  1. H5 端获取定位信息,需要部署在 https 服务上,本地预览(localhost)仍然可以使用 http 协议。(照搬官方文档)
  2. 在manifest.json 下面需要配置腾讯地图的key(可自己申请)
"sdkConfigs" : {  
       "maps" : {  
            "qqmap" : {  
                  "key" : "xxxxxxxxxx"  
            }  
       }  
}
继续阅读 »
  1. H5 端获取定位信息,需要部署在 https 服务上,本地预览(localhost)仍然可以使用 http 协议。(照搬官方文档)
  2. 在manifest.json 下面需要配置腾讯地图的key(可自己申请)
"sdkConfigs" : {  
       "maps" : {  
            "qqmap" : {  
                  "key" : "xxxxxxxxxx"  
            }  
       }  
}
收起阅读 »

准则2.1-性能、运行Wi-Fi在iPad上一个或多个错误问题

上架 iOS打包

很多开发者上架遇到这个问题,苹果那边打不开APP,加载不出来内容!

很多人以为是没有兼容ipad,其实是苹果审核都用ipad,跟有没有支持兼容没有关系。

如果自己在国内测试加载正常,要看APP服务器是否支持国外访问,因为苹果审核在国外,自己用国外网络测试看下!!

具体反馈翻译内容

准则2.1-性能-应用程序完整性

我们在运行Wi-Fi iOS 12.4.1的iPad上查看您的应用程序时发现一个或多个错误。

具体来说,我们无法在应用程序中加载某些内容。

下一步

若要解决此问题,请在设备上运行应用程序以确定任何问题,然后修订并重新提交应用程序以供审阅。

如果我们误解了您的应用程序的预期行为,请在Resolution Center中回复此消息,以提供有关这些功能的预期工作方式的信息。

对于新应用程序,请从设备中卸载所有以前版本的应用程序,然后安装并按照步骤重现问题。对于更新,请将新版本安装为对以前版本的更新,然后按照步骤重现问题。

资源

有关测试应用程序并准备进行审阅的信息,请参阅技术说明TN2431:应用程序测试指南。

有关网络概述,请查看有关网络的信息

iOS APP上架被拒重新提交审核教程

继续阅读 »

很多开发者上架遇到这个问题,苹果那边打不开APP,加载不出来内容!

很多人以为是没有兼容ipad,其实是苹果审核都用ipad,跟有没有支持兼容没有关系。

如果自己在国内测试加载正常,要看APP服务器是否支持国外访问,因为苹果审核在国外,自己用国外网络测试看下!!

具体反馈翻译内容

准则2.1-性能-应用程序完整性

我们在运行Wi-Fi iOS 12.4.1的iPad上查看您的应用程序时发现一个或多个错误。

具体来说,我们无法在应用程序中加载某些内容。

下一步

若要解决此问题,请在设备上运行应用程序以确定任何问题,然后修订并重新提交应用程序以供审阅。

如果我们误解了您的应用程序的预期行为,请在Resolution Center中回复此消息,以提供有关这些功能的预期工作方式的信息。

对于新应用程序,请从设备中卸载所有以前版本的应用程序,然后安装并按照步骤重现问题。对于更新,请将新版本安装为对以前版本的更新,然后按照步骤重现问题。

资源

有关测试应用程序并准备进行审阅的信息,请参阅技术说明TN2431:应用程序测试指南。

有关网络概述,请查看有关网络的信息

iOS APP上架被拒重新提交审核教程

收起阅读 »

一个页面多个视频,控制只播放一个

视频
vue:  
<video :id="'video'+index" :src="item.photo_list[0]" @play="videoPlayHandle('video'+index)"></video>
data:  
video:null
methods:  
// 视频播放需要暂停其他视频,也支持当前视频播放暂停  
            videoPlayHandle(id) {  
                let newVideo = uni.createVideoContext(id);  
                if (!this.video) {  
                    this.video = newVideo;  
                    this.video.play();  
                    return  
                }  
                if (this.video.id !== newVideo.id) {  
                    newVideo.play();  
                    this.video.pause();  
                    this.video = newVideo;  
                }  
            },

app中无效,待解决

继续阅读 »
vue:  
<video :id="'video'+index" :src="item.photo_list[0]" @play="videoPlayHandle('video'+index)"></video>
data:  
video:null
methods:  
// 视频播放需要暂停其他视频,也支持当前视频播放暂停  
            videoPlayHandle(id) {  
                let newVideo = uni.createVideoContext(id);  
                if (!this.video) {  
                    this.video = newVideo;  
                    this.video.play();  
                    return  
                }  
                if (this.video.id !== newVideo.id) {  
                    newVideo.play();  
                    this.video.pause();  
                    this.video = newVideo;  
                }  
            },

app中无效,待解决

收起阅读 »

Hbuilderx开发uni-app问题及解决方案记录,持续更新~

uniapp

1、组件回调函数中,需要传自定义参数时,可以传arguments或者$event作为回调的参数,后面加上自定义参数。
注:传arguments在app端取不到value值,h5可以取到,所以最终传$event。
vue:
@change="changeFun(arguments,item)"
@change="changeFun($event,item)"

js:
changeFun(value,item){
//value为回调的参数,item为自定义参数
}

2、vue中最好不要写比较复杂的js,最好不要写js,语法类似小程序,小程序不支持html有js语法及函数,打包成app会报错,会显示undefined。
例如:.substring();格式化时间;
解决方案:可以用计算属性代替html中的计算之类的

3、uni.showModal ,app上 content参数为空值,弹窗不显示,但是h5会显示,title可为空。

4、选择图片,需要限制大小,否则上传返回的res.data为空

 uni.chooseImage({  
                    count: 9, //默认9  
                    sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有  
                    sourceType: ['album'], //从相册选择  
                    success: function(res) {  
                        let tempFilePaths = [];  
                        res.tempFiles.forEach(item => {  
                            if (item.size < 1048576) {  
                                tempFilePaths.push(item.path);  
                            }  
                        })  
                        if (tempFilePaths.length !== res.tempFiles.length) {  
                            self.$showToast({  
                                text: '请选择1M内的图片'  
                            })  
                        }  
                        if (tempFilePaths.length > 0) {  
                            uni.setStorageSync('activity_products', '');  
                            uni.setStorageSync('activity_photos', tempFilePaths);  
                            uni.navigateTo({  
                                url: './issue1'  
                            })  
                        }  
                    }  
                });  

                            uni.uploadFile({  
                        url: apiList.host + '/gateway',  
                        filePath: item,  
                        name: 'file',  
                        header: {  
                            'content-type': 'multipart/form-data', // 文件类型    
                            'method': 'file.upload.direct',  
                            'mall_code': self.$store.state.mall_code,  
                            '_app-key': 'MjIyMg==',  
                            '_authorization': self.$store.state.token || '',  
                        },  
                        success: function(res) {  
                            console.log('res.data', res.data)  
                            if (res.data) {  
                                let data = JSON.parse(res.data);  
                                if (data.code == 200) {  
                                    let url = data.data;  
                                    photos.push(url);  
                                    if (photos.length == self.activity_photos.length) {  
                                        self.params.photos = photos.join(',');  
                                        self.post();  
                                    }  
                                } else {  
                                    self.$showToast({  
                                        text: data.error  
                                    })  
                                    uni.hideLoading();  
                                }  
                            } else {  
                                self.$showToast({  
                                    text: '发布失败'  
                                })  
                                uni.hideLoading();  
                            }  
                        },  
                        fail: function(res) {  
                            self.$showToast({  
                                text: res.msg  
                            });  
                            uni.hideLoading();  
                        },  
                    })

5、app中 使用store,需要在main.js中加 Vue.prototype.$store = store;

6、app中 给变量赋值,data中必须要有此变量,否则报错。

7、app运行报错:uni-app unexpected character "
原因:页面中的class=" 之后接的数据换行了

8、app运行报错: [错误] ./pages/member/mobile_modify/mobile_modify.wxml:1:2067: Bad attr data-event-opts with message: unexpected token email.
解决:input标签中写了 v-model="a==1?a:b",这种写法不支持

9、app运行报错:TypeError: $gwx is not a function
网上方法1(无效):是那个美元符号的问题。。加个空格
解决:是语法不兼容有问题就会报这个错,至于哪里不兼容需要自己找
1.组件中的props 不能使用vue原型的全局数据,需要单独引入使用
2.localStorage.getItem()在app中不识别,不能使用,需用uni的自定义api
3.组件内不能使用data作为props
4.data()中定义的变量,若语法有问题,那么onLoad()中打印任何data中的变量,都为undefined
5.vue中使用函数@tap="autFun('btns','editStock','fun',()=>{return editStockShow(item)})",报错bad attr 'data-ecent-opts' with message:unexpected

10、如下图

11、在app端取不到value值,h5可以取到



解决:
将arguments,改为$event

12、在app端会报错。.title为undefined,h5不会


解决:
改为计算属性

13、html中最好不要做太多js操作,例如格式化时间,app中会显示undefined

14、保存图片到相册 app
1、安卓无效

         //利用plus完成保存到相册    
    let filename = Math.random() +".png"    
            plus.downloader.createDownload(this.imageURL, {    
                filename:"_downloads/"+filename    
            }, (download,status)=>{    
                if(status==200){//下载成功    
                    plus.gallery.save( download.filename, ()=>{    
                        uni.hideLoading();    
                        uni.showToast({    
                            title: '保存成功!'    
                        });    
                    })    
                }    
            }).start()  

2、ios有效 安卓无效,保存失败

 uni.downloadFile({  
    url: this.code,  
    success(res) {  
        uni.saveImageToPhotosAlbum({  
            filePath: res.tempFilePath,  
            success: function() {  
                self.$showToast({  
                    text: '保存成功'  
                })  
            },  
            fail: function(err) {  
                console.log(err)  
                self.$showToast({  
                    text: '保存失败,请截图分享'  
                })  
            }  
        });  
    },  
    fail: function(err) {  
        console.log(err)  
        self.$showToast({  
            text: '下载失败,请截图分享'  
        })  
    }  
})  

3、canvas绘图 保存到本地 ios有效 安卓 h5绘图不出来,可能是因为下载的图片是base64的;drawImg()如果是base64图片可以直接canvas绘图,不用downloadfile了,否则安卓不显示;base64直接绘图,h5不显示,安卓ios显示

<canvas style="width: 300px; height: 300px;margin:0 auto" canvas-id="myCanvas" ></canvas>
uni.downloadFile({  
    url: res.data,  
    success(res) {  
        self.drawImg(res.tempFilePath);  
    },  
    fail: function(err) {  
        console.log(err)  
        self.$showToast({  
            text: '下载失败'  
        })  
    }  
})  

drawImg(code) {  
setTimeout(() => {  
    const ctx = uni.createCanvasContext('myCanvas');  
    ctx.drawImage(code, 0, 0, 300, 300);  
    ctx.setFontSize(14);  
    ctx.setFillStyle('#6D7278');  
    ctx.fillText('长按或扫描二维码访问我的门店', 50, 280);  
    ctx.draw();  
    this.code = true;  
}, 500);  
},  
save() {  
let self = this;  
uni.canvasToTempFilePath({  
    x: 0,  
    y: 0,  
    width: 300,  
    height: 300,  
    destWidth: 600,  
    destHeight: 600,  
    canvasId: 'myCanvas',  
    success: function(res) {  
        console.log(res.tempFilePath)  
        uni.saveImageToPhotosAlbum({  
            filePath: res.tempFilePath,  
            success: function() {  
                self.$showToast({  
                    text: '保存成功'  
                })  
            },  
            fail: function(err) {  
                console.log(err)  
                self.$showToast({  
                    text: '保存失败,请截图分享'  
                })  
            }  
        });  
    }  
})  
},

15、app中,组件props为string类型,传值为空串“”,app会显示为true,若默认default为“”,其子组件的props也会显示为true

16、复制api

self.copyText = function(text) {  
        uni.setClipboardData({  
            data: text,  
            success: function(res) {  
                uni.getClipboardData({  
                    success: function(res) {  
                        // app会有默认提示“内容已复制”  
                        // self.$common.Toast({  
                        //  text: self.i18.order.copySuc  
                        // });  
                    }  
                })  
            }  
        })  
    }

解决有默认提示

17、uni.uploadFile() h5 不能写content-type参数,写了会报错,app也可以不写

18、picker:有一个app兼容问题,如果默认值为空,首次手动赋值的时候如果index是0, 页面显示不更新,我看了下文档估计是组件默认值为0的原因,H5没有,我现在处理的是index为0的话就先改为其他值,然后setTimeout赋正确值

19、页面隐藏滚动条,包括横向滚动条

//1.设置全局样式,此方法无效  
 ::-webkit-scrollbar {  
    width: 0;  
    height: 0;  
    color: transparent;  
  }
//2.设置全局样式,此方法h5页面有效,app无效  
 ::-webkit-scrollbar {  
    display: none;  
  }
//3.page.json 页面设置此参数,仅对app有效  
{  
      "path": "pages/home/classifyDetail/classifyDetail",  
      "style": {  
        "navigationBarTitleText": "分类详情",  
        "navigationBarBackgroundColor": "#FFFFFF",  
        "app-plus": {  
          // 禁用原生导航栏  
          "titleNView": false,  
          // 手机端隐藏滚动条,保留滚动效果  
          "scrollIndicator":"none"  
        }  
      }  
  },

20、解决安卓点击两次返回按钮关闭应用问题

// 重写quit方法改为隐藏至后台  
if (!this.$common.isIOS()) {  
  let main = plus.android.runtimeMainActivity();  
  plus.runtime.quit = function () {  
    main.moveTaskToBack(false);  
  }  
}

21、跳转最好都写绝对路径,/pages/……
遇过的问题:h5中,首页写相对路径,./ ../ 首次进页面跳转成功,进入其他tab页面,再回来跳转,无效了
@tap=navigateTo(url)
navigator标签
uni.navigateTo()

继续阅读 »

1、组件回调函数中,需要传自定义参数时,可以传arguments或者$event作为回调的参数,后面加上自定义参数。
注:传arguments在app端取不到value值,h5可以取到,所以最终传$event。
vue:
@change="changeFun(arguments,item)"
@change="changeFun($event,item)"

js:
changeFun(value,item){
//value为回调的参数,item为自定义参数
}

2、vue中最好不要写比较复杂的js,最好不要写js,语法类似小程序,小程序不支持html有js语法及函数,打包成app会报错,会显示undefined。
例如:.substring();格式化时间;
解决方案:可以用计算属性代替html中的计算之类的

3、uni.showModal ,app上 content参数为空值,弹窗不显示,但是h5会显示,title可为空。

4、选择图片,需要限制大小,否则上传返回的res.data为空

 uni.chooseImage({  
                    count: 9, //默认9  
                    sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有  
                    sourceType: ['album'], //从相册选择  
                    success: function(res) {  
                        let tempFilePaths = [];  
                        res.tempFiles.forEach(item => {  
                            if (item.size < 1048576) {  
                                tempFilePaths.push(item.path);  
                            }  
                        })  
                        if (tempFilePaths.length !== res.tempFiles.length) {  
                            self.$showToast({  
                                text: '请选择1M内的图片'  
                            })  
                        }  
                        if (tempFilePaths.length > 0) {  
                            uni.setStorageSync('activity_products', '');  
                            uni.setStorageSync('activity_photos', tempFilePaths);  
                            uni.navigateTo({  
                                url: './issue1'  
                            })  
                        }  
                    }  
                });  

                            uni.uploadFile({  
                        url: apiList.host + '/gateway',  
                        filePath: item,  
                        name: 'file',  
                        header: {  
                            'content-type': 'multipart/form-data', // 文件类型    
                            'method': 'file.upload.direct',  
                            'mall_code': self.$store.state.mall_code,  
                            '_app-key': 'MjIyMg==',  
                            '_authorization': self.$store.state.token || '',  
                        },  
                        success: function(res) {  
                            console.log('res.data', res.data)  
                            if (res.data) {  
                                let data = JSON.parse(res.data);  
                                if (data.code == 200) {  
                                    let url = data.data;  
                                    photos.push(url);  
                                    if (photos.length == self.activity_photos.length) {  
                                        self.params.photos = photos.join(',');  
                                        self.post();  
                                    }  
                                } else {  
                                    self.$showToast({  
                                        text: data.error  
                                    })  
                                    uni.hideLoading();  
                                }  
                            } else {  
                                self.$showToast({  
                                    text: '发布失败'  
                                })  
                                uni.hideLoading();  
                            }  
                        },  
                        fail: function(res) {  
                            self.$showToast({  
                                text: res.msg  
                            });  
                            uni.hideLoading();  
                        },  
                    })

5、app中 使用store,需要在main.js中加 Vue.prototype.$store = store;

6、app中 给变量赋值,data中必须要有此变量,否则报错。

7、app运行报错:uni-app unexpected character "
原因:页面中的class=" 之后接的数据换行了

8、app运行报错: [错误] ./pages/member/mobile_modify/mobile_modify.wxml:1:2067: Bad attr data-event-opts with message: unexpected token email.
解决:input标签中写了 v-model="a==1?a:b",这种写法不支持

9、app运行报错:TypeError: $gwx is not a function
网上方法1(无效):是那个美元符号的问题。。加个空格
解决:是语法不兼容有问题就会报这个错,至于哪里不兼容需要自己找
1.组件中的props 不能使用vue原型的全局数据,需要单独引入使用
2.localStorage.getItem()在app中不识别,不能使用,需用uni的自定义api
3.组件内不能使用data作为props
4.data()中定义的变量,若语法有问题,那么onLoad()中打印任何data中的变量,都为undefined
5.vue中使用函数@tap="autFun('btns','editStock','fun',()=>{return editStockShow(item)})",报错bad attr 'data-ecent-opts' with message:unexpected

10、如下图

11、在app端取不到value值,h5可以取到



解决:
将arguments,改为$event

12、在app端会报错。.title为undefined,h5不会


解决:
改为计算属性

13、html中最好不要做太多js操作,例如格式化时间,app中会显示undefined

14、保存图片到相册 app
1、安卓无效

         //利用plus完成保存到相册    
    let filename = Math.random() +".png"    
            plus.downloader.createDownload(this.imageURL, {    
                filename:"_downloads/"+filename    
            }, (download,status)=>{    
                if(status==200){//下载成功    
                    plus.gallery.save( download.filename, ()=>{    
                        uni.hideLoading();    
                        uni.showToast({    
                            title: '保存成功!'    
                        });    
                    })    
                }    
            }).start()  

2、ios有效 安卓无效,保存失败

 uni.downloadFile({  
    url: this.code,  
    success(res) {  
        uni.saveImageToPhotosAlbum({  
            filePath: res.tempFilePath,  
            success: function() {  
                self.$showToast({  
                    text: '保存成功'  
                })  
            },  
            fail: function(err) {  
                console.log(err)  
                self.$showToast({  
                    text: '保存失败,请截图分享'  
                })  
            }  
        });  
    },  
    fail: function(err) {  
        console.log(err)  
        self.$showToast({  
            text: '下载失败,请截图分享'  
        })  
    }  
})  

3、canvas绘图 保存到本地 ios有效 安卓 h5绘图不出来,可能是因为下载的图片是base64的;drawImg()如果是base64图片可以直接canvas绘图,不用downloadfile了,否则安卓不显示;base64直接绘图,h5不显示,安卓ios显示

<canvas style="width: 300px; height: 300px;margin:0 auto" canvas-id="myCanvas" ></canvas>
uni.downloadFile({  
    url: res.data,  
    success(res) {  
        self.drawImg(res.tempFilePath);  
    },  
    fail: function(err) {  
        console.log(err)  
        self.$showToast({  
            text: '下载失败'  
        })  
    }  
})  

drawImg(code) {  
setTimeout(() => {  
    const ctx = uni.createCanvasContext('myCanvas');  
    ctx.drawImage(code, 0, 0, 300, 300);  
    ctx.setFontSize(14);  
    ctx.setFillStyle('#6D7278');  
    ctx.fillText('长按或扫描二维码访问我的门店', 50, 280);  
    ctx.draw();  
    this.code = true;  
}, 500);  
},  
save() {  
let self = this;  
uni.canvasToTempFilePath({  
    x: 0,  
    y: 0,  
    width: 300,  
    height: 300,  
    destWidth: 600,  
    destHeight: 600,  
    canvasId: 'myCanvas',  
    success: function(res) {  
        console.log(res.tempFilePath)  
        uni.saveImageToPhotosAlbum({  
            filePath: res.tempFilePath,  
            success: function() {  
                self.$showToast({  
                    text: '保存成功'  
                })  
            },  
            fail: function(err) {  
                console.log(err)  
                self.$showToast({  
                    text: '保存失败,请截图分享'  
                })  
            }  
        });  
    }  
})  
},

15、app中,组件props为string类型,传值为空串“”,app会显示为true,若默认default为“”,其子组件的props也会显示为true

16、复制api

self.copyText = function(text) {  
        uni.setClipboardData({  
            data: text,  
            success: function(res) {  
                uni.getClipboardData({  
                    success: function(res) {  
                        // app会有默认提示“内容已复制”  
                        // self.$common.Toast({  
                        //  text: self.i18.order.copySuc  
                        // });  
                    }  
                })  
            }  
        })  
    }

解决有默认提示

17、uni.uploadFile() h5 不能写content-type参数,写了会报错,app也可以不写

18、picker:有一个app兼容问题,如果默认值为空,首次手动赋值的时候如果index是0, 页面显示不更新,我看了下文档估计是组件默认值为0的原因,H5没有,我现在处理的是index为0的话就先改为其他值,然后setTimeout赋正确值

19、页面隐藏滚动条,包括横向滚动条

//1.设置全局样式,此方法无效  
 ::-webkit-scrollbar {  
    width: 0;  
    height: 0;  
    color: transparent;  
  }
//2.设置全局样式,此方法h5页面有效,app无效  
 ::-webkit-scrollbar {  
    display: none;  
  }
//3.page.json 页面设置此参数,仅对app有效  
{  
      "path": "pages/home/classifyDetail/classifyDetail",  
      "style": {  
        "navigationBarTitleText": "分类详情",  
        "navigationBarBackgroundColor": "#FFFFFF",  
        "app-plus": {  
          // 禁用原生导航栏  
          "titleNView": false,  
          // 手机端隐藏滚动条,保留滚动效果  
          "scrollIndicator":"none"  
        }  
      }  
  },

20、解决安卓点击两次返回按钮关闭应用问题

// 重写quit方法改为隐藏至后台  
if (!this.$common.isIOS()) {  
  let main = plus.android.runtimeMainActivity();  
  plus.runtime.quit = function () {  
    main.moveTaskToBack(false);  
  }  
}

21、跳转最好都写绝对路径,/pages/……
遇过的问题:h5中,首页写相对路径,./ ../ 首次进页面跳转成功,进入其他tab页面,再回来跳转,无效了
@tap=navigateTo(url)
navigator标签
uni.navigateTo()

收起阅读 »

获取手机设备信息、app版本信息、ip地址

Device mui html5plus

获取手机设备的相关信息,如IMEI、IMSI、型号、厂商等。通过plus.device获取设备信息管理对象。
获取当前运行环境信息、与其它程序进行通讯等。通过plus.runtime可获取运行环境管理对象。

直接上demo,复制即可用。

<!doctype html>  
<html>  
    <head>  
        <meta charset="utf-8">  
        <title></title>  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <link href="../../css/mui.min.css" rel="stylesheet" />  
        <script src="../../js/mui.js"></script>  
        <script src="../../js/vue.js"></script>  
        <style>  
            body{max-width: 750px; min-width: 320px; margin: 0 auto; background-color: #F5F5F5;overflow-x: hidden;  
                font-family: -apple-system,Helvetica,sans-serif;}  
            div{font-size: .26rem; color: #474747;line-height: 2;}  
            span{font-size: .28rem; color: #D1021F;}  
        </style>  
        <script>  
            (function(doc, win) {  
                var w = document.documentElement.clientWidth;  
                if (w > 750) {  
                    w = 750  
                } else if (w < 320) {  
                    w = 320  
                }  
                var f = w / 750 * 100 + "px";  
                document.documentElement.style.fontSize = f;  
                var docEl = doc.documentElement,  
                    resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',  
                    recalc = function() {  
                        var clientWidth = docEl.clientWidth > 750 ? 750 : docEl.clientWidth;  
                        if (clientWidth > 750) {  
                            clientWidth = 750  
                        } else if (clientWidth < 320) {  
                            clientWidth = 320  
                        }  
                        if (!clientWidth) return;  
                        docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';  
                    };  

                if (!doc.addEventListener) return;  
                win.addEventListener(resizeEvt, recalc, false);  
                doc.addEventListener('DOMContentLoaded', recalc, false);  
            })(document, window);  
        </script>  
    </head>  

    <body>  
        <header class="mui-bar mui-bar-nav">  
            <h1 class="mui-title">我的手机信息</h1>  
        </header>  
        <div id="content" class="mui-content mui-content-padded">  
            <div class="mui-text-left" v-for="item in list">  
                {{item.title}}  
                <span>  
                    {{item.value}}  
                </span>  
            </div>  
        </div>  

        <script type="text/javascript">  
            var spans = document.getElementsByTagName('span');  
            var VM = new Vue({  
                el: ".mui-content",  
                data: {  
                    list:[]  
                },  

            })  
            mui.plusReady(function() {  
                //获取系统名称  
                var name = plus.os.name;  
                VM.list.push({  
                    "title": "系统名称",  
                    "value": name  
                })  
                //获取系统版本  
                var version = plus.os.version;  
                VM.list.push({  
                    "title": "系统版本",  
                    "value": version  
                })  
                //设备型号  
                VM.list.push({  
                    "title": "设备型号",  
                    "value": plus.device.model  
                })  
                //获取生产厂商  
                var vendor2 = plus.device.vendor  
                VM.list.push({  
                    "title": "生产厂商",  
                    "value": vendor2  
                })  
                //获取系统供应商  
                var vendor = plus.os.vendor  
                VM.list.push({  
                    "title": "系统供应商",  
                    "value": vendor  
                })  
                //获取系统语言信息  
                var language = plus.os.language;  
                VM.list.push({  
                    "title": "系统语言信息",  
                    "value": language  
                })  

                var types = {}; //网络类型  
                types[plus.networkinfo.CONNECTION_UNKNOW] = "未知";  
                types[plus.networkinfo.CONNECTION_NONE] = "未连接网络";  
                types[plus.networkinfo.CONNECTION_ETHERNET] = "有线网络";  
                types[plus.networkinfo.CONNECTION_WIFI] = "WiFi网络";  
                types[plus.networkinfo.CONNECTION_CELL2G] = "2G蜂窝网络";  
                types[plus.networkinfo.CONNECTION_CELL3G] = "3G蜂窝网络";  
                types[plus.networkinfo.CONNECTION_CELL4G] = "4G蜂窝网络";  
                var network = types[plus.networkinfo.getCurrentType()];  
                VM.list.push({  
                    "title": "网络类型",  
                    "value": network  
                })  
                //获取设备的唯一标示  
                plus.device.getInfo({  
                    success: function(e) {  
                        VM.list.push({  
                            "title": "国际移动设备身份码imei",  
                            "value": e.imei  
                        })  
                        VM.list.push({  
                            "title": "国际移动用户识别码imsi",  
                            "value": e.imsi  
                        })  
                        VM.list.push({  
                            "title": "设备的唯一标识",  
                            "value": e.uuid  
                        })  
                    },  
                    fail: function(e) {  
                        console.log('getDeviceInfo failed: ' + JSON.stringify(e));  
                    }  
                });  
                //获取APP版本信息  
                plus.runtime.getProperty(plus.runtime.appid, function(inf) {  
                    var ver = inf.version;  
                    VM.list.push({  
                        "title": "APP名称",  
                        "value": inf.name  
                    })  
                    VM.list.push({  
                        "title": "APP版本信息",  
                        "value": "版本:"+inf.version+";版本号:"+inf.versionCode  
                    })  
                    console.log(JSON.stringify(inf))  
                })  

            });  
            //获取IP地址信息  
            function addScriptTag(src) {  
                var script = document.createElement('script');  
                script.setAttribute("type", "text/javascript");  
                script.src = src;  
                document.body.appendChild(script);  
            }  

            function foo(data) {  
                var json = data.data[0];  

                VM.list.push({  
                    "title": "位置",  
                    "value": json.location  
                })  
                VM.list.push({  
                    "title": "IP地址",  
                    "value": json.origip  
                })  
                console.log("IPInfo:"+ JSON.stringify(json));  
            };  
            window.onload = function() {  
                addScriptTag(  
                    'https://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query=ip&co=&resource_id=6006&t=1562124098965&ie=utf8&oe=gbk&cb=foo&format=json&tn=baidu'  
                );  
            }  
        </script>  
    </body>  
</html>  

注意:
获取IP地址和plus.device.getInfo都是异步的,所以在使用的时候要注意时机

运行结果:
Image text

Android和IOS获取imei、imsi、uuid时须知:

imei: (String 类型 )设备的国际移动设备身份码

如果设备不支持或无法获取(如用户未授权)则返回空字符串。 如果设备存在多个身份码,则以“,”字符分割拼接,如“862470039452950,862470039452943”。
平台支持

Android - ALL (支持): 需要用户授权才能获取,如果用户拒绝获取设备信息则返回空字符串。  
iOS - ALL (不支持): 无法获取设备身份码,返回空字符串。  

imsi: (Array[ String ] 类型 )设备的国际移动用户识别码

字符串数组类型,获取设备上插入SIM的国际移动设备身份码。 如果设备支持多卡模式则返回所有SIM身份码。 如果设备不支持或没有插入SIM卡则返回空数组。
平台支持

Android - ALL (支持): 如果无法获取国际移动用户标识(如用户未授权)则返回空数组。  
iOS - ALL (不支持): 无法获取设备移动用户识别码,返回空数组。  

uuid: (String 类型 )设备标识

设备的唯一标识号。
平台支持

Android - ALL (支持): 与设备的imei号一致。 注意:如果无法获取设备imei则使用设备wifi的mac地址,如果无法获取设备mac地址则随机生成设备标识号(不同App在同一台设备上获取的值一致)。  
iOS - ALL (不支持): 根据包名随机生成的设备标识号。 注意:设备重置(刷机)后会重新生成  

其他的属性和方法,参考html5plus官网:
http://www.html5plus.org/doc/zh_cn/device.html

继续阅读 »

获取手机设备的相关信息,如IMEI、IMSI、型号、厂商等。通过plus.device获取设备信息管理对象。
获取当前运行环境信息、与其它程序进行通讯等。通过plus.runtime可获取运行环境管理对象。

直接上demo,复制即可用。

<!doctype html>  
<html>  
    <head>  
        <meta charset="utf-8">  
        <title></title>  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <link href="../../css/mui.min.css" rel="stylesheet" />  
        <script src="../../js/mui.js"></script>  
        <script src="../../js/vue.js"></script>  
        <style>  
            body{max-width: 750px; min-width: 320px; margin: 0 auto; background-color: #F5F5F5;overflow-x: hidden;  
                font-family: -apple-system,Helvetica,sans-serif;}  
            div{font-size: .26rem; color: #474747;line-height: 2;}  
            span{font-size: .28rem; color: #D1021F;}  
        </style>  
        <script>  
            (function(doc, win) {  
                var w = document.documentElement.clientWidth;  
                if (w > 750) {  
                    w = 750  
                } else if (w < 320) {  
                    w = 320  
                }  
                var f = w / 750 * 100 + "px";  
                document.documentElement.style.fontSize = f;  
                var docEl = doc.documentElement,  
                    resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',  
                    recalc = function() {  
                        var clientWidth = docEl.clientWidth > 750 ? 750 : docEl.clientWidth;  
                        if (clientWidth > 750) {  
                            clientWidth = 750  
                        } else if (clientWidth < 320) {  
                            clientWidth = 320  
                        }  
                        if (!clientWidth) return;  
                        docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';  
                    };  

                if (!doc.addEventListener) return;  
                win.addEventListener(resizeEvt, recalc, false);  
                doc.addEventListener('DOMContentLoaded', recalc, false);  
            })(document, window);  
        </script>  
    </head>  

    <body>  
        <header class="mui-bar mui-bar-nav">  
            <h1 class="mui-title">我的手机信息</h1>  
        </header>  
        <div id="content" class="mui-content mui-content-padded">  
            <div class="mui-text-left" v-for="item in list">  
                {{item.title}}  
                <span>  
                    {{item.value}}  
                </span>  
            </div>  
        </div>  

        <script type="text/javascript">  
            var spans = document.getElementsByTagName('span');  
            var VM = new Vue({  
                el: ".mui-content",  
                data: {  
                    list:[]  
                },  

            })  
            mui.plusReady(function() {  
                //获取系统名称  
                var name = plus.os.name;  
                VM.list.push({  
                    "title": "系统名称",  
                    "value": name  
                })  
                //获取系统版本  
                var version = plus.os.version;  
                VM.list.push({  
                    "title": "系统版本",  
                    "value": version  
                })  
                //设备型号  
                VM.list.push({  
                    "title": "设备型号",  
                    "value": plus.device.model  
                })  
                //获取生产厂商  
                var vendor2 = plus.device.vendor  
                VM.list.push({  
                    "title": "生产厂商",  
                    "value": vendor2  
                })  
                //获取系统供应商  
                var vendor = plus.os.vendor  
                VM.list.push({  
                    "title": "系统供应商",  
                    "value": vendor  
                })  
                //获取系统语言信息  
                var language = plus.os.language;  
                VM.list.push({  
                    "title": "系统语言信息",  
                    "value": language  
                })  

                var types = {}; //网络类型  
                types[plus.networkinfo.CONNECTION_UNKNOW] = "未知";  
                types[plus.networkinfo.CONNECTION_NONE] = "未连接网络";  
                types[plus.networkinfo.CONNECTION_ETHERNET] = "有线网络";  
                types[plus.networkinfo.CONNECTION_WIFI] = "WiFi网络";  
                types[plus.networkinfo.CONNECTION_CELL2G] = "2G蜂窝网络";  
                types[plus.networkinfo.CONNECTION_CELL3G] = "3G蜂窝网络";  
                types[plus.networkinfo.CONNECTION_CELL4G] = "4G蜂窝网络";  
                var network = types[plus.networkinfo.getCurrentType()];  
                VM.list.push({  
                    "title": "网络类型",  
                    "value": network  
                })  
                //获取设备的唯一标示  
                plus.device.getInfo({  
                    success: function(e) {  
                        VM.list.push({  
                            "title": "国际移动设备身份码imei",  
                            "value": e.imei  
                        })  
                        VM.list.push({  
                            "title": "国际移动用户识别码imsi",  
                            "value": e.imsi  
                        })  
                        VM.list.push({  
                            "title": "设备的唯一标识",  
                            "value": e.uuid  
                        })  
                    },  
                    fail: function(e) {  
                        console.log('getDeviceInfo failed: ' + JSON.stringify(e));  
                    }  
                });  
                //获取APP版本信息  
                plus.runtime.getProperty(plus.runtime.appid, function(inf) {  
                    var ver = inf.version;  
                    VM.list.push({  
                        "title": "APP名称",  
                        "value": inf.name  
                    })  
                    VM.list.push({  
                        "title": "APP版本信息",  
                        "value": "版本:"+inf.version+";版本号:"+inf.versionCode  
                    })  
                    console.log(JSON.stringify(inf))  
                })  

            });  
            //获取IP地址信息  
            function addScriptTag(src) {  
                var script = document.createElement('script');  
                script.setAttribute("type", "text/javascript");  
                script.src = src;  
                document.body.appendChild(script);  
            }  

            function foo(data) {  
                var json = data.data[0];  

                VM.list.push({  
                    "title": "位置",  
                    "value": json.location  
                })  
                VM.list.push({  
                    "title": "IP地址",  
                    "value": json.origip  
                })  
                console.log("IPInfo:"+ JSON.stringify(json));  
            };  
            window.onload = function() {  
                addScriptTag(  
                    'https://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query=ip&co=&resource_id=6006&t=1562124098965&ie=utf8&oe=gbk&cb=foo&format=json&tn=baidu'  
                );  
            }  
        </script>  
    </body>  
</html>  

注意:
获取IP地址和plus.device.getInfo都是异步的,所以在使用的时候要注意时机

运行结果:
Image text

Android和IOS获取imei、imsi、uuid时须知:

imei: (String 类型 )设备的国际移动设备身份码

如果设备不支持或无法获取(如用户未授权)则返回空字符串。 如果设备存在多个身份码,则以“,”字符分割拼接,如“862470039452950,862470039452943”。
平台支持

Android - ALL (支持): 需要用户授权才能获取,如果用户拒绝获取设备信息则返回空字符串。  
iOS - ALL (不支持): 无法获取设备身份码,返回空字符串。  

imsi: (Array[ String ] 类型 )设备的国际移动用户识别码

字符串数组类型,获取设备上插入SIM的国际移动设备身份码。 如果设备支持多卡模式则返回所有SIM身份码。 如果设备不支持或没有插入SIM卡则返回空数组。
平台支持

Android - ALL (支持): 如果无法获取国际移动用户标识(如用户未授权)则返回空数组。  
iOS - ALL (不支持): 无法获取设备移动用户识别码,返回空数组。  

uuid: (String 类型 )设备标识

设备的唯一标识号。
平台支持

Android - ALL (支持): 与设备的imei号一致。 注意:如果无法获取设备imei则使用设备wifi的mac地址,如果无法获取设备mac地址则随机生成设备标识号(不同App在同一台设备上获取的值一致)。  
iOS - ALL (不支持): 根据包名随机生成的设备标识号。 注意:设备重置(刷机)后会重新生成  

其他的属性和方法,参考html5plus官网:
http://www.html5plus.org/doc/zh_cn/device.html

收起阅读 »

uni-app仿微信聊天uni_chatroom实例|uniapp仿微信

uniapp模板

前几天给大家分享了一个uniapp自定义弹窗组件,今天给大家分享基于uniapp+vue+vuex等技术开发的仿微信界面App聊天室UniChatRoom,实现了发送消息、图片预览、红包、长按菜单、地图位置、仿朋友圈等功能。
uni-app自定义仿微信/ios弹出框:https://ask.dcloud.net.cn/article/36440

三端效果图如下:

使用技术:

  • 编辑器:HBuilder X
  • 技术框架:uni-app + vue
  • 状态管理:Vuex
  • iconfont图标:阿里字体图标库
  • 自定义导航栏 + 底部Tabbar
  • 弹窗组件:uniPop(基于uni-app封装模态弹窗)
  • 测试环境:H5端 + 小程序 + App端(三端均兼容)

引入公共部分组件及样式

import Vue from 'vue'  
import App from './App'  

// >>>引入css  
import './assets/fonts/iconfont.css'  
import './assets/css/reset.css'  
import './assets/css/layout.css'  

// >>>引入状态管理  
import store from './store'  
Vue.prototype.$store = store  

// >>>引入公共组件  
import headerBar from './components/header/header.vue'  
import tabBar from './components/tabbar/tabbar.vue'  
import popupWindow from './components/popupWindow.vue'  
Vue.component('header-bar', headerBar)  
Vue.component('tab-bar', tabBar)  
Vue.component('popup-window', popupWindow)  

// >>>引入uniPop弹窗组件  
import uniPop from './components/uniPop/uniPop.vue'  
Vue.component('uni-pop', uniPop)  

Vue.config.productionTip = false  
App.mpType = 'app'  

const app = new Vue({  
    ...App  
})  
app.$mount()

uniapp朋友圈功能实现

/**  
 * @tpl 朋友圈模板  
 */  

<template>  
    <view class="flexbox flex_col">  
        <header-bar :isBack="true" title="朋友圈" :bgColor="{background: headerBarBackground}" transparent>  
            <text slot="back" class="uni_btnIco iconfont icon-arrL"></text>  
            <text slot="iconfont" class="uni_btnIco iconfont icon-publish mr_5" @tap="handlePublish"></text>  
        </header-bar>  

        <view class="uni__scrollview flex1">  
            <view class="uni-friendZone">  
                ...  
            </view>  
        </view>  
    </view>  
</template>  

<script>  
    export default {  
        data() {  
            return {  
                headerBarBackground: 'transparent'  
            }  
        },  
        onPageScroll : function(e) {  
            // console.log("滚动距离为:" + e.scrollTop);  
            this.headerBarBackground = 'rgba(65,168,99,'+e.scrollTop / 200+')'  
        },  
        methods: {  
            ...  
        }  
    }  
</script>  

<style scoped>  

</style>

uniapp使用scroll-view组件,通过判断聊天内容高度和scroll-view高度的大小设置滚动距离

<scroll-view id="scrollview" scroll-y="true" :scroll-top="scrollTop" style="height: 100%;">  
    <view class="uni-chatMsgCnt" id="msglistview">  
        <view class="msgitem">xxx</view>  
        <view class="msgitem">xxx</view>  
        <view class="msgitem">xxx</view>  
        ...  
    </view>  
</scroll-view>  

export default {  
    data() {  
        return {  
            scrollTop: 0,  
            ...  
        }  
    },  
    mounted() {  
        this.scrollToBottom()  
    },  
    updated() {  
        this.scrollToBottom()  
    },  
    methods: {  
        // 滚动至聊天底部  
        scrollToBottom(t) {  
            let that = this  
            let query = uni.createSelectorQuery()  
            query.select('#scrollview').boundingClientRect()  
            query.select('#msglistview').boundingClientRect()  
            query.exec((res) => {  
                // console.log(res)  
                if(res[1].height > res[0].height){  
                    that.scrollTop = res[1].height - res[0].height  
                }  
            })  
        },  
        ...  
    }  
}

作者:xiaoyan2015
链接: https://segmentfault.com/a/1190000020636455
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

继续阅读 »

前几天给大家分享了一个uniapp自定义弹窗组件,今天给大家分享基于uniapp+vue+vuex等技术开发的仿微信界面App聊天室UniChatRoom,实现了发送消息、图片预览、红包、长按菜单、地图位置、仿朋友圈等功能。
uni-app自定义仿微信/ios弹出框:https://ask.dcloud.net.cn/article/36440

三端效果图如下:

使用技术:

  • 编辑器:HBuilder X
  • 技术框架:uni-app + vue
  • 状态管理:Vuex
  • iconfont图标:阿里字体图标库
  • 自定义导航栏 + 底部Tabbar
  • 弹窗组件:uniPop(基于uni-app封装模态弹窗)
  • 测试环境:H5端 + 小程序 + App端(三端均兼容)

引入公共部分组件及样式

import Vue from 'vue'  
import App from './App'  

// >>>引入css  
import './assets/fonts/iconfont.css'  
import './assets/css/reset.css'  
import './assets/css/layout.css'  

// >>>引入状态管理  
import store from './store'  
Vue.prototype.$store = store  

// >>>引入公共组件  
import headerBar from './components/header/header.vue'  
import tabBar from './components/tabbar/tabbar.vue'  
import popupWindow from './components/popupWindow.vue'  
Vue.component('header-bar', headerBar)  
Vue.component('tab-bar', tabBar)  
Vue.component('popup-window', popupWindow)  

// >>>引入uniPop弹窗组件  
import uniPop from './components/uniPop/uniPop.vue'  
Vue.component('uni-pop', uniPop)  

Vue.config.productionTip = false  
App.mpType = 'app'  

const app = new Vue({  
    ...App  
})  
app.$mount()

uniapp朋友圈功能实现

/**  
 * @tpl 朋友圈模板  
 */  

<template>  
    <view class="flexbox flex_col">  
        <header-bar :isBack="true" title="朋友圈" :bgColor="{background: headerBarBackground}" transparent>  
            <text slot="back" class="uni_btnIco iconfont icon-arrL"></text>  
            <text slot="iconfont" class="uni_btnIco iconfont icon-publish mr_5" @tap="handlePublish"></text>  
        </header-bar>  

        <view class="uni__scrollview flex1">  
            <view class="uni-friendZone">  
                ...  
            </view>  
        </view>  
    </view>  
</template>  

<script>  
    export default {  
        data() {  
            return {  
                headerBarBackground: 'transparent'  
            }  
        },  
        onPageScroll : function(e) {  
            // console.log("滚动距离为:" + e.scrollTop);  
            this.headerBarBackground = 'rgba(65,168,99,'+e.scrollTop / 200+')'  
        },  
        methods: {  
            ...  
        }  
    }  
</script>  

<style scoped>  

</style>

uniapp使用scroll-view组件,通过判断聊天内容高度和scroll-view高度的大小设置滚动距离

<scroll-view id="scrollview" scroll-y="true" :scroll-top="scrollTop" style="height: 100%;">  
    <view class="uni-chatMsgCnt" id="msglistview">  
        <view class="msgitem">xxx</view>  
        <view class="msgitem">xxx</view>  
        <view class="msgitem">xxx</view>  
        ...  
    </view>  
</scroll-view>  

export default {  
    data() {  
        return {  
            scrollTop: 0,  
            ...  
        }  
    },  
    mounted() {  
        this.scrollToBottom()  
    },  
    updated() {  
        this.scrollToBottom()  
    },  
    methods: {  
        // 滚动至聊天底部  
        scrollToBottom(t) {  
            let that = this  
            let query = uni.createSelectorQuery()  
            query.select('#scrollview').boundingClientRect()  
            query.select('#msglistview').boundingClientRect()  
            query.exec((res) => {  
                // console.log(res)  
                if(res[1].height > res[0].height){  
                    that.scrollTop = res[1].height - res[0].height  
                }  
            })  
        },  
        ...  
    }  
}

作者:xiaoyan2015
链接: https://segmentfault.com/a/1190000020636455
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

收起阅读 »

安卓禁止弹出软件盘

虚拟键盘

最近在做一个项目,其中有个功能需要自动输入数据(扫码枪或者是IC读卡器)。用input每次都会弹出虚拟键盘,非常不方便,然后在论坛里面找到加上readonly就行,实测不行。最后发现扫码枪(部分)、IC读卡器是模拟键盘输入的,就想到了另外一个解决方案。

问题:禁止安卓输入框弹出软键盘

解决思路:用jQuery获取按键值,然后把按键值转换回按键,之后累加到自己想要的数位后可以直接填充到相应位置或操作。

代码如下:

            card_id = "";  
            old_card_id = "";  
            $("body").on("keyup", function(event) {//获取键值  
                old_card_id = "";  
                switch(event.which){//键值转换为所按按键(下面为数字0-9键值)  
                    case 48:  
                    old_card_id = 0;  
                    break;  

                    case 49:  
                    old_card_id = 1;  
                    break;  

                    case 50:  
                    old_card_id = 2;  
                    break;  

                    case 51:  
                    old_card_id = 3;  
                    break;  

                    case 52:  
                    old_card_id = 4;  
                    break;  

                    case 53:  
                    old_card_id = 5;  
                    break;  

                    case 54:  
                    old_card_id = 6;  
                    break;  

                    case 55:  
                    old_card_id = 7;  
                    break;  

                    case 56:  
                    old_card_id = 8;  
                    break;  

                    case 57:  
                    old_card_id = 9;  
                    break;  

                    default:  
                    card_id = "";  
                    document.onkeydown=null;  
                    break;  
                };  
                card_id = card_id + old_card_id;//把转换后的按键累计成字符串  
                if (card_id.length == 10) {//需要的字符个数  
                    console.log(card_id);  
                    alert(card_id);  
                    card_id = "";  
                }if(card_id.length > 10){//本处可不需要,因为我的IC读卡器是18位的,我只需要10位数字,所以这里我过滤了超过10位数字的字符串  
                    card_id = "";  
                }  
            });

最后,也许代码有些乱或者冗余。各位需要的小伙伴看一下思路就行。

继续阅读 »

最近在做一个项目,其中有个功能需要自动输入数据(扫码枪或者是IC读卡器)。用input每次都会弹出虚拟键盘,非常不方便,然后在论坛里面找到加上readonly就行,实测不行。最后发现扫码枪(部分)、IC读卡器是模拟键盘输入的,就想到了另外一个解决方案。

问题:禁止安卓输入框弹出软键盘

解决思路:用jQuery获取按键值,然后把按键值转换回按键,之后累加到自己想要的数位后可以直接填充到相应位置或操作。

代码如下:

            card_id = "";  
            old_card_id = "";  
            $("body").on("keyup", function(event) {//获取键值  
                old_card_id = "";  
                switch(event.which){//键值转换为所按按键(下面为数字0-9键值)  
                    case 48:  
                    old_card_id = 0;  
                    break;  

                    case 49:  
                    old_card_id = 1;  
                    break;  

                    case 50:  
                    old_card_id = 2;  
                    break;  

                    case 51:  
                    old_card_id = 3;  
                    break;  

                    case 52:  
                    old_card_id = 4;  
                    break;  

                    case 53:  
                    old_card_id = 5;  
                    break;  

                    case 54:  
                    old_card_id = 6;  
                    break;  

                    case 55:  
                    old_card_id = 7;  
                    break;  

                    case 56:  
                    old_card_id = 8;  
                    break;  

                    case 57:  
                    old_card_id = 9;  
                    break;  

                    default:  
                    card_id = "";  
                    document.onkeydown=null;  
                    break;  
                };  
                card_id = card_id + old_card_id;//把转换后的按键累计成字符串  
                if (card_id.length == 10) {//需要的字符个数  
                    console.log(card_id);  
                    alert(card_id);  
                    card_id = "";  
                }if(card_id.length > 10){//本处可不需要,因为我的IC读卡器是18位的,我只需要10位数字,所以这里我过滤了超过10位数字的字符串  
                    card_id = "";  
                }  
            });

最后,也许代码有些乱或者冗余。各位需要的小伙伴看一下思路就行。

收起阅读 »

Android检查手机是否被root

Native.JS

论坛里找了好久,没找到demo,只好自己摸索,借鉴了一下,自己测试了一下是可以的,分享出来,欢迎补充

                let isRoot = false;  
                const paths = ["/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su",  
                                "/system/bin/failsafe/su", "/data/local/su"];  

                var File = plus.android.importClass("java.io.File");  
                for (let i = 0; i < paths.length; i++) {  
                    let path = paths[i];  
                    var fd = new File(path);  
                    if(fd.exists()){  
                       isRoot = true;  
                       break;  
                    }  
                }
继续阅读 »

论坛里找了好久,没找到demo,只好自己摸索,借鉴了一下,自己测试了一下是可以的,分享出来,欢迎补充

                let isRoot = false;  
                const paths = ["/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su",  
                                "/system/bin/failsafe/su", "/data/local/su"];  

                var File = plus.android.importClass("java.io.File");  
                for (let i = 0; i < paths.length; i++) {  
                    let path = paths[i];  
                    var fd = new File(path);  
                    if(fd.exists()){  
                       isRoot = true;  
                       break;  
                    }  
                }
收起阅读 »

HBuilderX初级安装使用教程

HBuilderX安装使用教程

1. 安装HBuilderX

HBuilderX官网下载地址https://www.dcloud.io/hbuilderx.html

打开网站后,点击Download按钮,弹窗如下:

  • App开发版,包含大部分App开发插件。App开发,指的是手机应用开发。
  • 如果您是初学者开发前端,建议下载 标准版 。后期如果学习App开发,可以到【插件安装】中,安装相关插件

  1. Windows版本,下载的是一个 zip包,需要解压后才能使用。解压软件可以使用:360压缩软件、或7zip压缩软件

  2. 压缩过程,不要中断。不要在压缩软件中打开exe。

  3. 解压完成后,点击HBuilderX.exe ,如下图

注意:如果您无法打开或启动报错,请参考此贴 windows启动问题排查

2. 初识HBuilderX

HBuilderX首次启动后,打开了一个HBuilderX自述文件.md, 如下图:

HBuilderX自述文件.md: 是一个markdown文件,什么是markdown呢?就是个文本语言。 有兴趣的同学,点此了解Markdown

有兴趣的同学,可以读一读这个文件。或者直接点击右上角的x,关闭此文件。

关闭后的页面窗口如下:

3. 创建项目,开始HX旅程

点击【新建项目】、或点击工具栏上第一个图标(就是带有红色+号)、或直接 Ctrl + N ,调出创建项目窗口

新建项目后,如下图:

4. 编写代码

如上图所示,HBuilderX拥有强大的代码助手提示,可以帮您少敲很多代码。

5. 查看代码效果

如下图所示,点击【预览】可以查看代码效果。

如果您首次点击【预览】,又没有安装【内置浏览器】,点击预览的时候,会提示您安装插件,点击确定即可。

6. 安装插件

点击菜单【工具】【插件安装】

7. 文件单击与文件双击

如上图,标红部分,显示效果不一样。

这是因为:单击预览文件,双击打开文件

继续阅读 »

1. 安装HBuilderX

HBuilderX官网下载地址https://www.dcloud.io/hbuilderx.html

打开网站后,点击Download按钮,弹窗如下:

  • App开发版,包含大部分App开发插件。App开发,指的是手机应用开发。
  • 如果您是初学者开发前端,建议下载 标准版 。后期如果学习App开发,可以到【插件安装】中,安装相关插件

  1. Windows版本,下载的是一个 zip包,需要解压后才能使用。解压软件可以使用:360压缩软件、或7zip压缩软件

  2. 压缩过程,不要中断。不要在压缩软件中打开exe。

  3. 解压完成后,点击HBuilderX.exe ,如下图

注意:如果您无法打开或启动报错,请参考此贴 windows启动问题排查

2. 初识HBuilderX

HBuilderX首次启动后,打开了一个HBuilderX自述文件.md, 如下图:

HBuilderX自述文件.md: 是一个markdown文件,什么是markdown呢?就是个文本语言。 有兴趣的同学,点此了解Markdown

有兴趣的同学,可以读一读这个文件。或者直接点击右上角的x,关闭此文件。

关闭后的页面窗口如下:

3. 创建项目,开始HX旅程

点击【新建项目】、或点击工具栏上第一个图标(就是带有红色+号)、或直接 Ctrl + N ,调出创建项目窗口

新建项目后,如下图:

4. 编写代码

如上图所示,HBuilderX拥有强大的代码助手提示,可以帮您少敲很多代码。

5. 查看代码效果

如下图所示,点击【预览】可以查看代码效果。

如果您首次点击【预览】,又没有安装【内置浏览器】,点击预览的时候,会提示您安装插件,点击确定即可。

6. 安装插件

点击菜单【工具】【插件安装】

7. 文件单击与文件双击

如上图,标红部分,显示效果不一样。

这是因为:单击预览文件,双击打开文件

收起阅读 »

关于MUI安卓方向急需改进的方面

mui

安卓项目很多都需要依赖Jar包各方面的扩展,但是MUI这方面支持很不好,需要自定义增加sdk的话,就必须离线打包,处理起来很麻烦

安卓项目很多都需要依赖Jar包各方面的扩展,但是MUI这方面支持很不好,需要自定义增加sdk的话,就必须离线打包,处理起来很麻烦