HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

React Native基础+进阶+高级+实战视频教程

React Native

分享一套React Native基础+进阶+高级+实战视频教程,React Native零基础到入门实战,IOS+安卓移动应用开发,贯穿全栈React Native开发App 实战教程,比较全的React Native教学课程

教程目录列表:

教程下载:https://www.sucaihuo.com/video/398.html

继续阅读 »

分享一套React Native基础+进阶+高级+实战视频教程,React Native零基础到入门实战,IOS+安卓移动应用开发,贯穿全栈React Native开发App 实战教程,比较全的React Native教学课程

教程目录列表:

教程下载:https://www.sucaihuo.com/video/398.html

收起阅读 »

数据可视化之下发图实践

作者:个推前端工程师 东风

随着互联网的快速发展,数据维度越来越广,呈现形式也越发丰富,具有多维度数据特点的相关业务实践都能通过可视化图表来展示,比如个推的下发图,从时间和区域两个维度,可以即时、直观地展现个推数据下发的过程。

一、下发图的由来
个推下发图主要用于呈现个推为APP提供推送服务时数据的下发过程,可以直观显示个推推送触达到的城市,有利于开发者对下发数据进行分析。

个推下发图运用了迁徙图的原理,再通过自主设计开发出的一套可视化展示图像。这一类型的可视化可以广泛应用于拥有地理位置信息和数据转移特征的数据展示。

二、下发图的构成
下发图主要由地图、地理位置信息,以及飞线组成。如下图所示:

三、下发图的技术要点

1.地图
地图可以利用第三方地图服务,也可以自主绘制地图,本文以后者为例。自主绘制的地图主要利用了墨卡托投影原理,将地球正轴圆柱投影,由经纬度信息转化到画布上对应的位置。

本文案例中用了 d3.js 中的 geoMercator 进行墨卡托投影转换。

然后我们可以在阿里云的 datav 中获取地图的 geojson 数据,具体地址可参见括号内链接,(https://datav.aliyun.com/tools/atlas )再通过 canvas 原生 Api,添加背景色、边框等,就可以画出想要的地图了。

注意:下图中的地图角度透视主要应用了 css 中的 transform,perspective、rotateX、rotateY、rotateZ 等。

遵循上述步骤,一个透视角度的静态地图就绘制完成了。

2.贝塞尔曲线
贝塞尔曲线是计算机图形学中相当重要的参数曲线,它通过一个方程来描述一条曲线,根据方程的最高阶数,又分为线性贝塞尔曲线、二次贝塞尔曲线、三次贝塞尔曲线和更高阶的贝塞尔曲线。

本案例中主要应用了二次贝塞尔曲线,二次贝塞尔曲线的函数如下:
B(t) = (1-t) ²P0 + 2t(1-t)P1 + t²P2, t ∈ [0,1]

上图为本文案例中飞线的贝塞尔曲线应用,其中 from 为起点,to 为终点,curveness 为曲线的曲率,取值-1 ~ 1,曲率的绝对值越大,曲线越弯曲,percent为飞线位置占比。

3.动画
在 canvas 中,动画效果的实现通常是由 window.requestAnimationFrame 循环执行,因此,飞线需要算出每一帧中飞线的状态,以及飞线的入场和离场形态。

4.发光效果
那么下发图的特效具体如何实现呢?首先我们来介绍一下头部发光效果的实现过程:

我们以工业中的HSL色彩模式为颜色标准,通过对色相(H)、饱和度(S)、明度(L)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色,HSL即代表色相、饱和度、明度三个通道的颜色,这一标准几乎包括了人类视力所能感知的所有颜色。以此为依据,可以发现光源的发光规律,即光源中心的明亮度最高,由内向外,明亮度依次递减。

所以根据配置的基础颜色,就能获取到顶点发光处颜色的明亮度,大致方法如下:

完成头部发光步骤后,接下来需要打造一个酷炫的形状。发光的头部是一个类似棉签棒的形状,该形状可以用一个半圆和一个三角形来绘制,再根据曲线的切线,获取三角形以及半圆的旋转角度。

完成下发图头部制作后,接下来需要进行尾部的操作,因为canvas自带线性渐变,所以具体代码如下:

canvas 的落地效果呈圆形渐变样式,当飞线到达终点后,完整的落地效果就开始展示,整个画面类似雨滴降落到地面。

5.透视
如果不调整透视角度,贝塞尔曲线的样式如下图所示:

当曲线与下发方向的角度呈90度时,曲率最大;角度为0度或者180度时,曲率最小,与余弦定律相似。

其中 from 是起始位置,to 是终止位置,curveness 是曲线的曲率,angel 是视线的角度。

最终效果如下:

四、技术选型
在进行下发图的技术选型时,个推技术团队对比了 svg 和 canvas 两种技术栈,最后选择了 canvas,然后配合 requestAnimationFrame 画出下发轨迹的帧动画。两款技术栈的具体性能对比如下:

五、总结
随着数据维度的扩展和丰富,数据可视化的形态日渐丰富。作为地理位置信息和数据转移特征的数据可视化图表,下发图可以更直观地展现个推为APP提供推送服务时的下发量、下发区域等数据,对APP的行业分析以及战略调整有着指导性意义。

继续阅读 »

作者:个推前端工程师 东风

随着互联网的快速发展,数据维度越来越广,呈现形式也越发丰富,具有多维度数据特点的相关业务实践都能通过可视化图表来展示,比如个推的下发图,从时间和区域两个维度,可以即时、直观地展现个推数据下发的过程。

一、下发图的由来
个推下发图主要用于呈现个推为APP提供推送服务时数据的下发过程,可以直观显示个推推送触达到的城市,有利于开发者对下发数据进行分析。

个推下发图运用了迁徙图的原理,再通过自主设计开发出的一套可视化展示图像。这一类型的可视化可以广泛应用于拥有地理位置信息和数据转移特征的数据展示。

二、下发图的构成
下发图主要由地图、地理位置信息,以及飞线组成。如下图所示:

三、下发图的技术要点

1.地图
地图可以利用第三方地图服务,也可以自主绘制地图,本文以后者为例。自主绘制的地图主要利用了墨卡托投影原理,将地球正轴圆柱投影,由经纬度信息转化到画布上对应的位置。

本文案例中用了 d3.js 中的 geoMercator 进行墨卡托投影转换。

然后我们可以在阿里云的 datav 中获取地图的 geojson 数据,具体地址可参见括号内链接,(https://datav.aliyun.com/tools/atlas )再通过 canvas 原生 Api,添加背景色、边框等,就可以画出想要的地图了。

注意:下图中的地图角度透视主要应用了 css 中的 transform,perspective、rotateX、rotateY、rotateZ 等。

遵循上述步骤,一个透视角度的静态地图就绘制完成了。

2.贝塞尔曲线
贝塞尔曲线是计算机图形学中相当重要的参数曲线,它通过一个方程来描述一条曲线,根据方程的最高阶数,又分为线性贝塞尔曲线、二次贝塞尔曲线、三次贝塞尔曲线和更高阶的贝塞尔曲线。

本案例中主要应用了二次贝塞尔曲线,二次贝塞尔曲线的函数如下:
B(t) = (1-t) ²P0 + 2t(1-t)P1 + t²P2, t ∈ [0,1]

上图为本文案例中飞线的贝塞尔曲线应用,其中 from 为起点,to 为终点,curveness 为曲线的曲率,取值-1 ~ 1,曲率的绝对值越大,曲线越弯曲,percent为飞线位置占比。

3.动画
在 canvas 中,动画效果的实现通常是由 window.requestAnimationFrame 循环执行,因此,飞线需要算出每一帧中飞线的状态,以及飞线的入场和离场形态。

4.发光效果
那么下发图的特效具体如何实现呢?首先我们来介绍一下头部发光效果的实现过程:

我们以工业中的HSL色彩模式为颜色标准,通过对色相(H)、饱和度(S)、明度(L)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色,HSL即代表色相、饱和度、明度三个通道的颜色,这一标准几乎包括了人类视力所能感知的所有颜色。以此为依据,可以发现光源的发光规律,即光源中心的明亮度最高,由内向外,明亮度依次递减。

所以根据配置的基础颜色,就能获取到顶点发光处颜色的明亮度,大致方法如下:

完成头部发光步骤后,接下来需要打造一个酷炫的形状。发光的头部是一个类似棉签棒的形状,该形状可以用一个半圆和一个三角形来绘制,再根据曲线的切线,获取三角形以及半圆的旋转角度。

完成下发图头部制作后,接下来需要进行尾部的操作,因为canvas自带线性渐变,所以具体代码如下:

canvas 的落地效果呈圆形渐变样式,当飞线到达终点后,完整的落地效果就开始展示,整个画面类似雨滴降落到地面。

5.透视
如果不调整透视角度,贝塞尔曲线的样式如下图所示:

当曲线与下发方向的角度呈90度时,曲率最大;角度为0度或者180度时,曲率最小,与余弦定律相似。

其中 from 是起始位置,to 是终止位置,curveness 是曲线的曲率,angel 是视线的角度。

最终效果如下:

四、技术选型
在进行下发图的技术选型时,个推技术团队对比了 svg 和 canvas 两种技术栈,最后选择了 canvas,然后配合 requestAnimationFrame 画出下发轨迹的帧动画。两款技术栈的具体性能对比如下:

五、总结
随着数据维度的扩展和丰富,数据可视化的形态日渐丰富。作为地理位置信息和数据转移特征的数据可视化图表,下发图可以更直观地展现个推为APP提供推送服务时的下发量、下发区域等数据,对APP的行业分析以及战略调整有着指导性意义。

收起阅读 »

最大资源网列表及剧集数据采集

zuidazy_list(page){
var pattern = new RegExp('-' + "(.?)" + '.html' +"."+ '_blank">' + "(.?)" + '</a' +"."+ '<span class="xing_vb5">' + "(.?)" + '</span>' +"."+ '<span class="xing_vb(6|7)">' + "(.?)" + '</span>',"g");
let data=[];
uni.request({
method:"GET",
url:'http://zuidazy.net/?m='+page+'.html',
success: (result) => {
if(result.statusCode==200){
// var data = result.data.match(/\/>(.
)\$(.*)<\/li>/g)
while (pattern.exec(result.data) != null){

                        data.push({"id":RegExp.$1,  
                                    "title":RegExp.$2,  
                                    "type":RegExp.$3,  
                                    "times":RegExp.$5});  
                        this.zuidazy_info(RegExp.$1)      
                        // result.push(RegExp.$1);  
                    }  

                }  

            },  
            fail: (e) => {  
                console.log(JSON.stringify(e))  
            }  
        });  
        console.log(data)  
    },  
    zuidazy_info(id){  
        let d1=[];  
        let d2=[];  
        let d3=[];  
        uni.request({  
            method:"GET",  
            url:'http://zuidazy.net/?m='+id+'.html',  
            success: (result) => {  
                if(result.statusCode==200){  
                    var data = result.data.match(/\/>(.*)\$(.*)<\/li>/g)  
                    for(var i=0;i<data.length;i++){  
                        if(data[i].indexOf('.m3u8')>-1){  
                            d1.push(this.getdata(data[i]))  
                        }else if(data[i].indexOf('.mp4')>-1){  
                            d2.push(this.getdata(data[i]))  
                        }else{  
                            d3.push(this.getdata(data[i]))  
                        }  
                    }  

                }  

            },  
            fail: (e) => {  
                console.log(JSON.stringify(e))  
            }  
        });  

        console.log([d1,d2,d3])  
    },  

继续阅读 »

zuidazy_list(page){
var pattern = new RegExp('-' + "(.?)" + '.html' +"."+ '_blank">' + "(.?)" + '</a' +"."+ '<span class="xing_vb5">' + "(.?)" + '</span>' +"."+ '<span class="xing_vb(6|7)">' + "(.?)" + '</span>',"g");
let data=[];
uni.request({
method:"GET",
url:'http://zuidazy.net/?m='+page+'.html',
success: (result) => {
if(result.statusCode==200){
// var data = result.data.match(/\/>(.
)\$(.*)<\/li>/g)
while (pattern.exec(result.data) != null){

                        data.push({"id":RegExp.$1,  
                                    "title":RegExp.$2,  
                                    "type":RegExp.$3,  
                                    "times":RegExp.$5});  
                        this.zuidazy_info(RegExp.$1)      
                        // result.push(RegExp.$1);  
                    }  

                }  

            },  
            fail: (e) => {  
                console.log(JSON.stringify(e))  
            }  
        });  
        console.log(data)  
    },  
    zuidazy_info(id){  
        let d1=[];  
        let d2=[];  
        let d3=[];  
        uni.request({  
            method:"GET",  
            url:'http://zuidazy.net/?m='+id+'.html',  
            success: (result) => {  
                if(result.statusCode==200){  
                    var data = result.data.match(/\/>(.*)\$(.*)<\/li>/g)  
                    for(var i=0;i<data.length;i++){  
                        if(data[i].indexOf('.m3u8')>-1){  
                            d1.push(this.getdata(data[i]))  
                        }else if(data[i].indexOf('.mp4')>-1){  
                            d2.push(this.getdata(data[i]))  
                        }else{  
                            d3.push(this.getdata(data[i]))  
                        }  
                    }  

                }  

            },  
            fail: (e) => {  
                console.log(JSON.stringify(e))  
            }  
        });  

        console.log([d1,d2,d3])  
    },  

收起阅读 »

uni-app加载外网地址会全屏把手机状态栏也覆盖住了

uniapp 5+App开发


场景是这样的:我们以前使用mui和5+开发过的项目已经部署在微信端可以外网访问的。现在想通过uni-app里面的web-view组件加载以前的微信端页面地址。但是在加载页面成功后,点击页面跳转重新打开页面会把手机状态栏覆盖住。

继续阅读 »


场景是这样的:我们以前使用mui和5+开发过的项目已经部署在微信端可以外网访问的。现在想通过uni-app里面的web-view组件加载以前的微信端页面地址。但是在加载页面成功后,点击页面跳转重新打开页面会把手机状态栏覆盖住。

收起阅读 »

蓝牙小票打印机 代码片段分享

蓝牙
var main = plus.android.runtimeMainActivity()  
var BluetoothAdapter = plus.android.importClass(  
  'android.bluetooth.BluetoothAdapter'  
)  
var IntentFilter = plus.android.importClass('android.content.IntentFilter')  
var BluetoothDevice = plus.android.importClass(  
  'android.bluetooth.BluetoothDevice'  
)  
var BAdapter = new BluetoothAdapter.getDefaultAdapter()  

BAdapter.isEnabled()  
BAdapter.enable()  

plus.bluetooth.openBluetoothAdapter({  
  success: function(e) {  
    console.log('open success: ' + JSON.stringify(e))  
    plus.bluetooth.startBluetoothDevicesDiscovery({  
      success: function(e) {  
        console.log('start discovery success: ' + JSON.stringify(e))  
      },  
      fail: function(e) {  
        console.log('start discovery failed: ' + JSON.stringify(e))  
      }  
    })  
  },  
  fail: function(e) {  
    console.log('open failed: ' + JSON.stringify(e))  
  }  
})  

plus.bluetooth.getBluetoothDevices({  
  success: function(e) {  
    var devices = e.devices  
    console.log('get devices success: ' + e.length)  
    for (var i in devices) {  
      console.log(i + ': ' + JSON.stringify(devices[i]))  
    }  
  },  
  fail: function(e) {  
    console.log('get devices failed: ' + JSON.stringify(e))  
  }  
})  

plus.bluetooth.stopBluetoothDevicesDiscovery({  
  success: function(e) {  
    console.log('stop discovery success: ' + JSON.stringify(e))  
    plus.bluetooth.closeBluetoothAdapter({  
      success: function(e) {  
        console.log('close success: ' + JSON.stringify(e))  
      },  
      fail: function(e) {  
        console.log('close failed: ' + JSON.stringify(e))  
      }  
    })  
  },  
  fail: function(e) {  
    console.log('stop discovery failed: ' + JSON.stringify(e))  
  }  
})  
UUID = plus.android.importClass('java.util.UUID')  
uuid = UUID.fromString('00001101-0000-1000-8000-00805F9B34FB') //不需要更改  

device = BAdapter.getRemoteDevice('DC:1D:30:34:62:A6') //这里是蓝牙打印机的蓝牙地址  
plus.android.importClass(device)  
bluetoothSocket = device.createInsecureRfcommSocketToServiceRecord(uuid)  
plus.android.importClass(bluetoothSocket)  

bluetoothSocket.connect() //连接  
bluetoothSocket.isConnected()  

var outputStream = bluetoothSocket.getOutputStream()  
plus.android.importClass(outputStream)  

// 打印字符串  
var s = plus.android.importClass('java.lang.String')  
var string = new s('测试数据' + new Date().getMilliseconds() + '\n\n\n\n') //必须以创建字符串对象的形式创建对象,否则返回NULL  
var bytes = string.getBytes('gbk')  
outputStream.write(bytes)  
outputStream.flush()  

let Util = {  
  RESET: [27, 64], // 复位  
  ALIGN_LEFT: [27, 97, 0], // 左对齐  
  ALIGN_CENTER: [27, 97, 1], // 居中  
  ALIGN_RIGHT: [27, 97, 2], // 右对齐  
  BOLD: [27, 69, 1], // 加粗  
  BOLD_CANCEL: [27, 69, 0], //取消加粗  
  DOUBLE_HEIGHT_WIDTH: [29, 33, 17], //宽高加倍  
  DOUBLE_WIDTH: [29, 33, 16], //宽加倍  
  DOUBLE_HEIGHT: [29, 33, 1], //高加倍  
  NORMAL: [29, 33, 0], //字体不放大  
  LINE_SPACING_DEFAULT: [27, 50] // 设置默认间距  
}  
function setFormat(command) {  
  outputStream.write(command)  
  outputStream.flush()  
}  
function PrintText(text) {  
  var s = plus.android.importClass('java.lang.String')  
  var string = new s(text)  
  var bytes = string.getBytes('gbk')  
  outputStream.write(bytes)  
  outputStream.flush()  
}  
// 获取字符长度  
function GetLength(str) {  
  return str.replace(/[\u0391-\uFFE5]/g, 'aa').length //先把中文替换成两个字节的英文,在计算长度  
}  
// 输出两列,不能超过32字符  
function printTowData(text1, text2) {  
  let textLength1 = GetLength(text1)  
  let textLength2 = GetLength(text2)  
  let blankLength = 32 - textLength1 - textLength2  
  let blank = ''  
  for (let i = 0; i < blankLength; i++) {  
    blank = blank + ' '  
  }  
  return text1 + blank + text2  
}  

PrintText(printTowData('哈哈哈', '嘿嘿嘿'))  
继续阅读 »
var main = plus.android.runtimeMainActivity()  
var BluetoothAdapter = plus.android.importClass(  
  'android.bluetooth.BluetoothAdapter'  
)  
var IntentFilter = plus.android.importClass('android.content.IntentFilter')  
var BluetoothDevice = plus.android.importClass(  
  'android.bluetooth.BluetoothDevice'  
)  
var BAdapter = new BluetoothAdapter.getDefaultAdapter()  

BAdapter.isEnabled()  
BAdapter.enable()  

plus.bluetooth.openBluetoothAdapter({  
  success: function(e) {  
    console.log('open success: ' + JSON.stringify(e))  
    plus.bluetooth.startBluetoothDevicesDiscovery({  
      success: function(e) {  
        console.log('start discovery success: ' + JSON.stringify(e))  
      },  
      fail: function(e) {  
        console.log('start discovery failed: ' + JSON.stringify(e))  
      }  
    })  
  },  
  fail: function(e) {  
    console.log('open failed: ' + JSON.stringify(e))  
  }  
})  

plus.bluetooth.getBluetoothDevices({  
  success: function(e) {  
    var devices = e.devices  
    console.log('get devices success: ' + e.length)  
    for (var i in devices) {  
      console.log(i + ': ' + JSON.stringify(devices[i]))  
    }  
  },  
  fail: function(e) {  
    console.log('get devices failed: ' + JSON.stringify(e))  
  }  
})  

plus.bluetooth.stopBluetoothDevicesDiscovery({  
  success: function(e) {  
    console.log('stop discovery success: ' + JSON.stringify(e))  
    plus.bluetooth.closeBluetoothAdapter({  
      success: function(e) {  
        console.log('close success: ' + JSON.stringify(e))  
      },  
      fail: function(e) {  
        console.log('close failed: ' + JSON.stringify(e))  
      }  
    })  
  },  
  fail: function(e) {  
    console.log('stop discovery failed: ' + JSON.stringify(e))  
  }  
})  
UUID = plus.android.importClass('java.util.UUID')  
uuid = UUID.fromString('00001101-0000-1000-8000-00805F9B34FB') //不需要更改  

device = BAdapter.getRemoteDevice('DC:1D:30:34:62:A6') //这里是蓝牙打印机的蓝牙地址  
plus.android.importClass(device)  
bluetoothSocket = device.createInsecureRfcommSocketToServiceRecord(uuid)  
plus.android.importClass(bluetoothSocket)  

bluetoothSocket.connect() //连接  
bluetoothSocket.isConnected()  

var outputStream = bluetoothSocket.getOutputStream()  
plus.android.importClass(outputStream)  

// 打印字符串  
var s = plus.android.importClass('java.lang.String')  
var string = new s('测试数据' + new Date().getMilliseconds() + '\n\n\n\n') //必须以创建字符串对象的形式创建对象,否则返回NULL  
var bytes = string.getBytes('gbk')  
outputStream.write(bytes)  
outputStream.flush()  

let Util = {  
  RESET: [27, 64], // 复位  
  ALIGN_LEFT: [27, 97, 0], // 左对齐  
  ALIGN_CENTER: [27, 97, 1], // 居中  
  ALIGN_RIGHT: [27, 97, 2], // 右对齐  
  BOLD: [27, 69, 1], // 加粗  
  BOLD_CANCEL: [27, 69, 0], //取消加粗  
  DOUBLE_HEIGHT_WIDTH: [29, 33, 17], //宽高加倍  
  DOUBLE_WIDTH: [29, 33, 16], //宽加倍  
  DOUBLE_HEIGHT: [29, 33, 1], //高加倍  
  NORMAL: [29, 33, 0], //字体不放大  
  LINE_SPACING_DEFAULT: [27, 50] // 设置默认间距  
}  
function setFormat(command) {  
  outputStream.write(command)  
  outputStream.flush()  
}  
function PrintText(text) {  
  var s = plus.android.importClass('java.lang.String')  
  var string = new s(text)  
  var bytes = string.getBytes('gbk')  
  outputStream.write(bytes)  
  outputStream.flush()  
}  
// 获取字符长度  
function GetLength(str) {  
  return str.replace(/[\u0391-\uFFE5]/g, 'aa').length //先把中文替换成两个字节的英文,在计算长度  
}  
// 输出两列,不能超过32字符  
function printTowData(text1, text2) {  
  let textLength1 = GetLength(text1)  
  let textLength2 = GetLength(text2)  
  let blankLength = 32 - textLength1 - textLength2  
  let blank = ''  
  for (let i = 0; i < blankLength; i++) {  
    blank = blank + ' '  
  }  
  return text1 + blank + text2  
}  

PrintText(printTowData('哈哈哈', '嘿嘿嘿'))  
收起阅读 »

uni-app各环节版本兼容性说明

兼容性 版本

此文档停止维护,最新的文档见:uni版本说明

uni-app是一个跨度很大的产品,支持多种开发ide、HBuilderX有正式版和alpha版、支持cli和普通项目、支持很多端、有云打包app和本地打包app、编译器模式支持老的模板模式和新的自定义组件模式。
它们都会升级,如果出现一个升级、另一个未升级的情况,可能会运行异常。

正常情况,HBuilderX升级后,其自带的app运行基座、uni-app编译器、云打包配套引擎会同步升级。但在开发者使用cli创建项目、使用自定义基座、使用5+sdk离线打包时,就需要手动维护版本更新。

我们经常遇到的误报问题是:“我的HBuilderX已经是最新版了,升级日志里提到的已解决问题,为什么没有仍然存在?”,其实有的是因为cli项目下编译器是旧的、有的是因为本地打包的sdk是旧的,有的是因为自定义基座的版本是旧的。

  • 如果你使用cli创建项目(即项目根目录是package.json),不管用什么ide,即便是用HBuilderX,切记cli项目的编译器是在项目下的,HBuilderX不管怎么升级都不会影响编译器版本。你需要手动npm update来升级编译器。以及如果你想要安装less、scss等预编译器,也需要自己npm安装在项目下,而不是在HBuilderX的插件管理里安装。

  • 如果你使用离线打包,请注意HBuilderX升级后,真机运行基座和云打包对应引擎跟随HBuilderX升级,而你的sdk需要手动升级。sdk的版本升级一般滞后HBuilderX正式版升级一两天。在这里下载最新版

  • 如果你使用自定义基座,之前制作的自定义基座是不会跟随HBuilderX升级的,升级HBuilderX后你应该重新制作新版自定义基座。

  • 如果你使用wgt升级,新版HBuilderX编译的wgt,运行到之前的runtime上,一定要先测试好,看有没有兼容性问题。如果有问题,就不要wgt升级,整包升级。

  • 考虑到向下兼容,uni-app编译器在升级为新的自定义组件模式后,同时保留了对老编译模式的向下兼容。
    在HBuilderX alpha版中,App端一定会使用新编译器,不理会manifest配置。
    在HBuilderX 正式版中,新创建的项目会使用新编译器,老项目不会强制使用,而是开发者自己在manifest里配置开启。

  • 如果你使用其他ide开发uni-app,会经常因为拼错单词而运行失败,因为经过webpack编译一道,很多错误反应的不够直观,排错时间很长,不如从开始就依赖有良好提示的HBuilderX,避免敲错单词。

  • 云打包的引擎版本说明
    自HBuilderX 3.9起,云打包机版本保留了多个可用的版本,具体可用的云端打包版本参考下方的版本对应表。
    打包时,服务端会根据用户使用的HBuilderX或cli版本去匹配最合适的打包机,规则如下(匹配优先级从高到低):

    1. HBuilderX或cli版本与云端版本完全一致;
    2. HBuilderX或cli版本的大版本号与云端一致时,使用该大版本的最新版本;如打包机上有[3.92, 3.93, 3.99, 4.01, 4.10],则3.91使用3.99打包机。
    3. 当以上规则无法匹配时,使用云端最新版本。

举个例子:
当云打包机有以下版本[3.7.11,3.8.12,3.92, 3.93, 3.99, 4.01, 4.15]可用时:

  • 如果HBuilderX使用3.7.11,3.8.12,3.92, 3.93, 3.99, 4.08, 4.15这些版本时,版本号完全匹配,直接使用同版本号打包机;
  • 如果HBuilderX使用3.7.3,则云端打包机使用3.7.11版本;HBuilderX使用3.91,则云端打包机使用3.99版本;HBuilderX使用4.01版本,则云端打包机使用4.08版本;
  • 如果HBuilderX使用3.7以下的版本,则云端打包机使用最新版本4.15。

很多人在报问题时不说清自己的情况,导致别人给出错误的回答。
很多人在搜问题时没注意看条件,导致使用了并不适用于自己的错误方案。

要想问题少,推荐使用HBuilderX完成一切工作,包括创建项目、运行编译、云打包app。
因为在这套体系里,官方会对很多常见的问题做出提醒和引导,减少问题的概率。随着HBuilderX的升级,uni-app编译器、真机运行基座、云打包引擎都会升级。

版本列表

下面提供 uni-app 开发中各产品的版本对应表:
<iframe src="https://dev.dcloud.net.cn/product/versions/view" width="100%" height="760px" frameborder="0" scrolling="no" style="border: 1px solid #eee; border-radius: 4px;"> </iframe>

继续阅读 »

此文档停止维护,最新的文档见:uni版本说明

uni-app是一个跨度很大的产品,支持多种开发ide、HBuilderX有正式版和alpha版、支持cli和普通项目、支持很多端、有云打包app和本地打包app、编译器模式支持老的模板模式和新的自定义组件模式。
它们都会升级,如果出现一个升级、另一个未升级的情况,可能会运行异常。

正常情况,HBuilderX升级后,其自带的app运行基座、uni-app编译器、云打包配套引擎会同步升级。但在开发者使用cli创建项目、使用自定义基座、使用5+sdk离线打包时,就需要手动维护版本更新。

我们经常遇到的误报问题是:“我的HBuilderX已经是最新版了,升级日志里提到的已解决问题,为什么没有仍然存在?”,其实有的是因为cli项目下编译器是旧的、有的是因为本地打包的sdk是旧的,有的是因为自定义基座的版本是旧的。

  • 如果你使用cli创建项目(即项目根目录是package.json),不管用什么ide,即便是用HBuilderX,切记cli项目的编译器是在项目下的,HBuilderX不管怎么升级都不会影响编译器版本。你需要手动npm update来升级编译器。以及如果你想要安装less、scss等预编译器,也需要自己npm安装在项目下,而不是在HBuilderX的插件管理里安装。

  • 如果你使用离线打包,请注意HBuilderX升级后,真机运行基座和云打包对应引擎跟随HBuilderX升级,而你的sdk需要手动升级。sdk的版本升级一般滞后HBuilderX正式版升级一两天。在这里下载最新版

  • 如果你使用自定义基座,之前制作的自定义基座是不会跟随HBuilderX升级的,升级HBuilderX后你应该重新制作新版自定义基座。

  • 如果你使用wgt升级,新版HBuilderX编译的wgt,运行到之前的runtime上,一定要先测试好,看有没有兼容性问题。如果有问题,就不要wgt升级,整包升级。

  • 考虑到向下兼容,uni-app编译器在升级为新的自定义组件模式后,同时保留了对老编译模式的向下兼容。
    在HBuilderX alpha版中,App端一定会使用新编译器,不理会manifest配置。
    在HBuilderX 正式版中,新创建的项目会使用新编译器,老项目不会强制使用,而是开发者自己在manifest里配置开启。

  • 如果你使用其他ide开发uni-app,会经常因为拼错单词而运行失败,因为经过webpack编译一道,很多错误反应的不够直观,排错时间很长,不如从开始就依赖有良好提示的HBuilderX,避免敲错单词。

  • 云打包的引擎版本说明
    自HBuilderX 3.9起,云打包机版本保留了多个可用的版本,具体可用的云端打包版本参考下方的版本对应表。
    打包时,服务端会根据用户使用的HBuilderX或cli版本去匹配最合适的打包机,规则如下(匹配优先级从高到低):

    1. HBuilderX或cli版本与云端版本完全一致;
    2. HBuilderX或cli版本的大版本号与云端一致时,使用该大版本的最新版本;如打包机上有[3.92, 3.93, 3.99, 4.01, 4.10],则3.91使用3.99打包机。
    3. 当以上规则无法匹配时,使用云端最新版本。

举个例子:
当云打包机有以下版本[3.7.11,3.8.12,3.92, 3.93, 3.99, 4.01, 4.15]可用时:

  • 如果HBuilderX使用3.7.11,3.8.12,3.92, 3.93, 3.99, 4.08, 4.15这些版本时,版本号完全匹配,直接使用同版本号打包机;
  • 如果HBuilderX使用3.7.3,则云端打包机使用3.7.11版本;HBuilderX使用3.91,则云端打包机使用3.99版本;HBuilderX使用4.01版本,则云端打包机使用4.08版本;
  • 如果HBuilderX使用3.7以下的版本,则云端打包机使用最新版本4.15。

很多人在报问题时不说清自己的情况,导致别人给出错误的回答。
很多人在搜问题时没注意看条件,导致使用了并不适用于自己的错误方案。

要想问题少,推荐使用HBuilderX完成一切工作,包括创建项目、运行编译、云打包app。
因为在这套体系里,官方会对很多常见的问题做出提醒和引导,减少问题的概率。随着HBuilderX的升级,uni-app编译器、真机运行基座、云打包引擎都会升级。

版本列表

下面提供 uni-app 开发中各产品的版本对应表:
<iframe src="https://dev.dcloud.net.cn/product/versions/view" width="100%" height="760px" frameborder="0" scrolling="no" style="border: 1px solid #eee; border-radius: 4px;"> </iframe>

收起阅读 »

本地uni-app原生插件提交云端打包

uniapp原生插件 uniPlugins

从HBuilderX1.9.0开始uni-app原生插件可以直接在插件市场绑定应用后,直接云端打包生效,避免下载uni-app原生插件到本地后再提交云端打包。
同时也继续支持将插件下载到本地后再提交云端打包,通常在以下情况使用这种方式:

  • uni-app原生插件开发者,开发后配置提交云端打包验证插件的包格式是否正确
  • uni-app原生插件使用者,需要对插件自定义修改(如插件的资源等)

获取本地uni-app原生插件

插件市场下载免费uni-app原生插件

可以登录uni原生插件市场,在免费的插件详情页中点击“下载for离线打包”下载原生插件(zip格式),解压到HBuilderX的uni-app项目下的“nativeplugins”目录(如不存在则创建),以下是“DCloud-RichAlert”插件举例,它的下载地址是:https://ext.dcloud.net.cn/plugin?id=36
下载解压后目录结构如下:

开发者自己开发uni-app原生插件

原生插件开发完成后按指定格式压缩为zip包,参考uni-app原生插件格式说明文档
按上图的格式配置到uni-app项目下的“nativeplugins”目录。

uni-app原生插件本地配置

将原生插件配置到uni-app项目的“nativeplugins”下,还需要在manifest.json文件的“App原生插件配置”项下点击“选择本地插件”,在列表中选择需要打包生效的插件:

保存后,重新提交云端打包生效

关于云端打包资源大小超限的说明

云端打包资源大小限制40M,如果超限每次打包需支付一定费用。40M—100M,每次打包10元,每增加100M费用增加10元。请登录开发者中心(https://dev.dcloud.net.cn),选择“打包服务”- “App大小超限充值”进行自助充值后,再提交打包。
如果是为了开发uni-app原生插件提交插件市场前的测试打包,大小超限也需付费才能云端打包,插件上线通过审核以后可以申请退还相关打包费用。
提供开发者账号及插件链接地址发邮件到 bd@dcloud.io 申请,谢谢!

使用 Windows 系统提交 iOS 本地插件打包报错的解决方法

如果使用 mac 提交本地插件打包正常,而使用 Windows 系统提交 iOS 本地插件打包时报错,是因为插件包使用的某个 .framework 库中存现软链接,在 Windows 系统上解压后导致软链接失效,打包时导致这个库缺失,所以会报错
报错如下图:

解决办法:
1.依次查看插件iOS目录下面的 .framework 库文件,下面以 UTDID.framework 为例,打开后发现 UTDID、Resources、Headers 大小都为 1kb,说明这几个文件既是软链(在Windows上加压后被转换为文本文件,无法链接到真实文件)


2.用记事本打开这几个文件,里面写的就是链接的真实文件目录,(链接的是/Versions/A/目录中的文件)

3.把真实文件拷贝到这个库的根目录,将其他文件全部删除即可,最终的库文件

然后重新提交打包即可;

使用uni-app原生插件参考:uni-app原生插件(native plugin)使用说明
更多uni-app原生插件文档参考:uni-app原生插件开发指南

继续阅读 »

从HBuilderX1.9.0开始uni-app原生插件可以直接在插件市场绑定应用后,直接云端打包生效,避免下载uni-app原生插件到本地后再提交云端打包。
同时也继续支持将插件下载到本地后再提交云端打包,通常在以下情况使用这种方式:

  • uni-app原生插件开发者,开发后配置提交云端打包验证插件的包格式是否正确
  • uni-app原生插件使用者,需要对插件自定义修改(如插件的资源等)

获取本地uni-app原生插件

插件市场下载免费uni-app原生插件

可以登录uni原生插件市场,在免费的插件详情页中点击“下载for离线打包”下载原生插件(zip格式),解压到HBuilderX的uni-app项目下的“nativeplugins”目录(如不存在则创建),以下是“DCloud-RichAlert”插件举例,它的下载地址是:https://ext.dcloud.net.cn/plugin?id=36
下载解压后目录结构如下:

开发者自己开发uni-app原生插件

原生插件开发完成后按指定格式压缩为zip包,参考uni-app原生插件格式说明文档
按上图的格式配置到uni-app项目下的“nativeplugins”目录。

uni-app原生插件本地配置

将原生插件配置到uni-app项目的“nativeplugins”下,还需要在manifest.json文件的“App原生插件配置”项下点击“选择本地插件”,在列表中选择需要打包生效的插件:

保存后,重新提交云端打包生效

关于云端打包资源大小超限的说明

云端打包资源大小限制40M,如果超限每次打包需支付一定费用。40M—100M,每次打包10元,每增加100M费用增加10元。请登录开发者中心(https://dev.dcloud.net.cn),选择“打包服务”- “App大小超限充值”进行自助充值后,再提交打包。
如果是为了开发uni-app原生插件提交插件市场前的测试打包,大小超限也需付费才能云端打包,插件上线通过审核以后可以申请退还相关打包费用。
提供开发者账号及插件链接地址发邮件到 bd@dcloud.io 申请,谢谢!

使用 Windows 系统提交 iOS 本地插件打包报错的解决方法

如果使用 mac 提交本地插件打包正常,而使用 Windows 系统提交 iOS 本地插件打包时报错,是因为插件包使用的某个 .framework 库中存现软链接,在 Windows 系统上解压后导致软链接失效,打包时导致这个库缺失,所以会报错
报错如下图:

解决办法:
1.依次查看插件iOS目录下面的 .framework 库文件,下面以 UTDID.framework 为例,打开后发现 UTDID、Resources、Headers 大小都为 1kb,说明这几个文件既是软链(在Windows上加压后被转换为文本文件,无法链接到真实文件)


2.用记事本打开这几个文件,里面写的就是链接的真实文件目录,(链接的是/Versions/A/目录中的文件)

3.把真实文件拷贝到这个库的根目录,将其他文件全部删除即可,最终的库文件

然后重新提交打包即可;

使用uni-app原生插件参考:uni-app原生插件(native plugin)使用说明
更多uni-app原生插件文档参考:uni-app原生插件开发指南

收起阅读 »

uni-app模板编译模式和自定义组件编译模式差异说明

编译模式

更新:这2种编译模式均已下线。本文已过期

uni-app 自 1.8版本开始,同时支持两种编译模式:

  • template模板模式:老框架模式,借鉴自mpvue,将用户编写的Vue组件,编译为WXML中的模板(template),变相实现组件化开发,性能差,支持 Vue 语法少,比如不支持过滤器。
  • 自定义组件模式:新框架模式,DCloud自研,将用户编写的Vue组件,编译为微信小程序自定义组件,实现了更高的性能更完善的 Vue 语法支持。同时在App端提供了独立的js引擎,大幅提升性能

如何切换编译模式

manifest.json 的源码视图里配置是否启用新编译器, manifest.json -> %platform% -> usingComponents 切换编译模式。

%platform% 是指平台名称

// manifest.json    
{    
    // ...    
     /* App平台特有配置 */    
    "app-plus": {    
        "usingComponents":true //是否启用`自定义组件模式`,为true表示新的`自定义组件模式` ,否则为`template模板模式`   
    }    
    /* 微信小程序特有配置 */    
    "mp-weixin": {    
        "usingComponents":true //是否启用`自定义组件模式`,为true表示新的`自定义组件模式`,否则为`template模板模式`   
    }    
  // ...  
} 

在HBuilderX的manifest的可视化界面,每个平台的也都有配置勾选。

manifest.json配置文件中,未明确指定编译模式(即未配置%platform% -> usingComponents),则uni-app默认策略如下:

  • HbuilderX 2.3.0 正式版之前的版本为保证历史项目兼容,默认使用老编译器(template模板模式),即不配置的情况下效果等同于 usingComponents: false,而 HbuilderX 2.3.0 正式版之后,默认会启用新编译器(自定义组件模式),即不配置的情况下效果等同于 usingComponents: true,从 HbuilderX 2.3.8 正式版开始将停止支持非自定义组件模式,届时,新版HBuilderX真机运行和云打包,都不再支持非自定义组件模式。HBuilderX的云打包,只向下保留2个版本。再升级1个版本后,老版打包机也不再支持非自定义组件模式。详见https://ask.dcloud.net.cn/article/36385
  • HBuilderX Alpha 为开发者提供最新技术尝鲜,默认会启用新编译器(自定义组件模式),即不配置的情况下效果等同于 usingComponents: true
  • CLI 开发模式下的,默认编译策略同 HBuidlerX 正式版策略

Tips:

  • 切换编译环境之后,请重新运行项目

非自定义组件模式升级为自定义组件模式注意

自定义组件模式虽然兼容大部分老模式的写法,但也有部分不兼容。老项目升级时,可能需要修改部分代码,请详细参考自定义组件模式开发注意事项:https://ask.dcloud.net.cn/article/35851

不同编译模式支持的Vue语法差异

自定义组件模式模板模式都不支持的 vue 语法:

  • class不支持绑定Obejct变量(使用字符串的形式绑定)
  • 不支持事件修饰符:prevent、passive(在App与小程序平台,使用stop修饰符,既可以阻止冒泡也能阻止默认行为)
  • 不支持render、inline-template、X-Templates、keep-alive、transition
  • 不支持使用 Vue.use 的方式注册全局组件(在main.js使用Vue.component的方式引入)

template模板模式除了不支持如上Vue语法外,额外还不支持如下语法:

  • 不支持过滤器 filter
  • 不支持比较复杂的 JavaScript 渲染表达式
  • 不支持在 template 内使用 methods 中的函数
  • 不支持 v-html
  • 不支持 v-slot 新语法及后备内容
  • 不支持解构插槽 Prop 设置默认值
  • 组件不支持原生事件绑定,如:@tap.native
  • 组件不支持通过class绑定样式

问题反馈

  • 如果在使用 uni-app 的过程中遇到问题,先认真阅读下 如何准确地反馈 uni-app 的问题 再发帖咨询。
  • 在不要在本文章下留言报bug,请发布新帖,并将帖子地址复制到回复栏,并简短说明bug。

扩展阅读

继续阅读 »

更新:这2种编译模式均已下线。本文已过期

uni-app 自 1.8版本开始,同时支持两种编译模式:

  • template模板模式:老框架模式,借鉴自mpvue,将用户编写的Vue组件,编译为WXML中的模板(template),变相实现组件化开发,性能差,支持 Vue 语法少,比如不支持过滤器。
  • 自定义组件模式:新框架模式,DCloud自研,将用户编写的Vue组件,编译为微信小程序自定义组件,实现了更高的性能更完善的 Vue 语法支持。同时在App端提供了独立的js引擎,大幅提升性能

如何切换编译模式

manifest.json 的源码视图里配置是否启用新编译器, manifest.json -> %platform% -> usingComponents 切换编译模式。

%platform% 是指平台名称

// manifest.json    
{    
    // ...    
     /* App平台特有配置 */    
    "app-plus": {    
        "usingComponents":true //是否启用`自定义组件模式`,为true表示新的`自定义组件模式` ,否则为`template模板模式`   
    }    
    /* 微信小程序特有配置 */    
    "mp-weixin": {    
        "usingComponents":true //是否启用`自定义组件模式`,为true表示新的`自定义组件模式`,否则为`template模板模式`   
    }    
  // ...  
} 

在HBuilderX的manifest的可视化界面,每个平台的也都有配置勾选。

manifest.json配置文件中,未明确指定编译模式(即未配置%platform% -> usingComponents),则uni-app默认策略如下:

  • HbuilderX 2.3.0 正式版之前的版本为保证历史项目兼容,默认使用老编译器(template模板模式),即不配置的情况下效果等同于 usingComponents: false,而 HbuilderX 2.3.0 正式版之后,默认会启用新编译器(自定义组件模式),即不配置的情况下效果等同于 usingComponents: true,从 HbuilderX 2.3.8 正式版开始将停止支持非自定义组件模式,届时,新版HBuilderX真机运行和云打包,都不再支持非自定义组件模式。HBuilderX的云打包,只向下保留2个版本。再升级1个版本后,老版打包机也不再支持非自定义组件模式。详见https://ask.dcloud.net.cn/article/36385
  • HBuilderX Alpha 为开发者提供最新技术尝鲜,默认会启用新编译器(自定义组件模式),即不配置的情况下效果等同于 usingComponents: true
  • CLI 开发模式下的,默认编译策略同 HBuidlerX 正式版策略

Tips:

  • 切换编译环境之后,请重新运行项目

非自定义组件模式升级为自定义组件模式注意

自定义组件模式虽然兼容大部分老模式的写法,但也有部分不兼容。老项目升级时,可能需要修改部分代码,请详细参考自定义组件模式开发注意事项:https://ask.dcloud.net.cn/article/35851

不同编译模式支持的Vue语法差异

自定义组件模式模板模式都不支持的 vue 语法:

  • class不支持绑定Obejct变量(使用字符串的形式绑定)
  • 不支持事件修饰符:prevent、passive(在App与小程序平台,使用stop修饰符,既可以阻止冒泡也能阻止默认行为)
  • 不支持render、inline-template、X-Templates、keep-alive、transition
  • 不支持使用 Vue.use 的方式注册全局组件(在main.js使用Vue.component的方式引入)

template模板模式除了不支持如上Vue语法外,额外还不支持如下语法:

  • 不支持过滤器 filter
  • 不支持比较复杂的 JavaScript 渲染表达式
  • 不支持在 template 内使用 methods 中的函数
  • 不支持 v-html
  • 不支持 v-slot 新语法及后备内容
  • 不支持解构插槽 Prop 设置默认值
  • 组件不支持原生事件绑定,如:@tap.native
  • 组件不支持通过class绑定样式

问题反馈

  • 如果在使用 uni-app 的过程中遇到问题,先认真阅读下 如何准确地反馈 uni-app 的问题 再发帖咨询。
  • 在不要在本文章下留言报bug,请发布新帖,并将帖子地址复制到回复栏,并简短说明bug。

扩展阅读

收起阅读 »

求助,uni-app按钮点击没反应

<template>
<!--orderinfo{{cartIds}}{{amount}}-->

<view class="parent">  
    <view class="view-address">  
        <view class="view-addressleft">  
            <image src="/images/white_loc.png"></image>  
            <view class="view-addressleft-info">  
                <text class="text-name">收货人:{{ address.consignee }}</text>  
                <text class="text-address">收货地址:{{ address.address }}</text>  
            </view>  
        </view>  

        <view class="view-addressright">  
            <text class="text-mobile">{{ address.mobile }}</text>  
            <image src="/images/white_hint.png"></image>  
        </view>  
    </view>  

    <view v-for="(item,index) in cartList" :key="index"  class="container carts-list">  
        <view v-if="item.show == 1" class="view-title">  
            <image src="/images/icon_order.png"></image>  
            <text>供应商:{{ item.store_name }}</text>  
        </view>  

        <view v-if="item.show == 1" class="class-line"></view>  
        <view class="carts-item">  
            <view>  
                <!-- 缩略图 -->  
                <image class="carts-image" src="{{item.image}}" mode="aspectFill" />  
            </view>  
            <view class="carts-text">  
                <!-- 商品标题 -->  
                <text class="carts-title">{{ item.goods_name }}</text>  
                <text class="carts-key-name">{{ item.spec_key_name }}</text>  
                <view class="carts-subtitle">  
                    <!-- 价格 -->  

                    <!--<text class="carts-price">{{item.goods_price}}</text>  

-->

                    <!-- 数量加减 -->  
                </view>  
            </view>  
            <view class="carts-right">  
                <text class="text-red">¥{{ item.member_goods_price }}</text>  
                <text class="text-price">x{{ item.goods_num }}</text>  
            </view>  
        </view>  
    </view>  

    <view class="class-line1"></view>  

    <view class="view-freemoney">  
        <!--  

<text class="text-hint">优 惠 劵:</text>

<input bindchange="bindChange" placeholder-style="color:#999999" class="input-money" placeholder="输入余额"/>
-->

        <radio color="red" class="radio-style" value="0" checked="{{check[0]}}" />  

        <picker  value="{{index}}" range="{{coupon}}">  
            <view class="view-picker">{{ cv }}</view>  
        </picker>  

        <text class="text-hint">或者</text>  

        <radio color="red" class="radio-style" value="1" checked="{{check[1]}}" />  
        <input placeholder-style="color:#999999"  class="input-money" placeholder="直接输入优惠劵" style="width:200rpx" />  
    </view>  

    <view class="view-freemoney">  
        <text class="text-hint">使用余额:</text>  

        <input placeholder-style="color:#999999"  class="input-money" placeholder="输入余额" />  
        <text class="btn-use" >使用</text>  
        <text class="text-hint1">您的可用余额{{ freemoney }}</text>  
    </view>  

    <view class="view-freemoney">  
        <text class="text-hint">使用积分:</text>  

        <input placeholder-style="color:#999999"  class="input-money" placeholder="输入积分" />  
        <text class="btn-use">使用</text>  
        <text class="text-hint1">您的可用积分{{ pay_points }}</text>  
    </view>  

    <view class="class-line1"></view>  

    <view class="view-price">  
        <view class="view-price1">  
            <text class="text-price1">商品总额:</text>  
            <text class="text-red">¥{{ totalPrice.total_fee }}元</text>  
        </view>  
        <view hidden class="view-price1">  
            <text class="text-price1">邮费:</text>  
            <text class="text-red">¥20元</text>  
        </view>  

        <view class="view-price1">  
            <text class="text-price1">应付总金额:</text>  
            <text class="text-red">¥{{ totalPrice.total_fee }}元</text>  
        </view>  
    </view>  

    <view class="text-save" @longtap="longtap">提交订单</view>  
</view>  

</template>
<script>
export default {
methods:{
clickTest: function(e){
console.log(e);
console.log('click');
},
longtap: function(e){
console.log(e);
console.log('longtap');
}
}
}
</script>
<style lang="scss">
/ pages/order/ordersubmit/index.wxss /

.view-address {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
height: 150rpx;
background: #3aa0b7;
}
.view-addressleft {
display: flex;
flex-direction: row;
padding: 20rpx 0 0 3;
justify-content: flex-start;
align-items: center;
width: 450rpx;
}

.view-addressleft image {
width: 50rpx;
height: 50rpx;
}

.view-addressleft-info {
display: flex;
flex-direction: column;
margin-left: 10rpx;
justify-content: space-around;
align-items: flex-start;
height: 85rpx;
}
.text-name {
font-size: 29rpx;
color: #ffffff;
}
.text-address {
font-size: 29rpx;
color: #ffffff;
}
.view-addressright {
height: 50rpx;
margin-right: 20rpx;
display: flex;
flex-direction: row;
align-items: center;
}

.text-mobile {
font-size: 29rpx;
color: #ffffff;
}

.view-addressright image {
width: 30rpx;
height: 30rpx;
margin-left: 15rpx;
}

.view-title {
display: flex;
flex-direction: row;
height: 75rpx;

align-items: center;  

}

.view-title image {
width: 50rpx;
height: 50rpx;
}

.view-title text {
font-size: 29rpx;
color: black;
}

.class-line {
width: 670rpx;
margin-left: 0rpx;
height: 2rpx;
background: #f4f4f4;
}

/外部容器/
.container {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: space-between;
box-sizing: border-box;
}

/整体列表/
.carts-list {
display: flex;
flex-direction: column;
padding: 0 40rpx;
}

/每行单元格/
.carts-item {
display: flex;
flex-direction: row;
height: 150rpx;
/width属性解决标题文字太短而缩略图偏移/
width: 100%;
border-bottom: 1px solid #eee;
padding: 30rpx 0;

justify-content: space-between;  

}

/左部图片/
.carts-image {
width: 130rpx;
height: 150rpx;
border-width: 3rpx;

border-style: solid;  
border-color: #ccc;  

}

/右部描述/
.carts-text {
margin-left: 10rpx;
width: 370rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.carts-right {
height: 100%;
width: 110rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
/右上部分标题/
.carts-title {
font-size: 27rpx;
color: #444;
line-height: 38rpx;
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.carts-key-name {
margin-top: 10rpx;
font-size: 27rpx;
color: #999999;
}
/右下部分价格与数量/
.carts-subtitle {
font-size: 25rpx;
color: darkgray;
padding: 0 20rpx;
display: flex;
flex-direction: row;
justify-content: space-between;
}

/价格/
.carts-price {
color: #f60;
font-size: 14px;
line-height: 22px;
margin-top: 12rpx;
}

/商品加减按钮/
/stepper容器/
.stepper {
border: 1px solid #ccc;
border-radius: 3px;
width: 80px;
height: 26px;
margin: 0 auto;
}

/加号与减号/
.stepper text {
width: 19px;
line-height: 26px;
text-align: center;
float: left;
}

/数值/
.stepper input {
color: black;
float: left;
margin: 0 auto;
width: 40px;
height: 26px;
text-align: center;
font-size: 12px;
border-left: 1px solid #ccc;
border-right: 1px solid #ccc;
}

/普通样式/
.stepper .normal {
color: black;
}

/禁用样式/
.stepper .disabled {
color: #ccc;
}

/复选框样式/
.carts-list icon {
margin-top: 60rpx;
margin-right: 20rpx;
}

/底部按钮/
.carts-footer {
width: 100%;
height: 80rpx;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
position: fixed;
bottom: 0;
border-top: 1px solid #eee;
background: white;
}

/复选框/
.carts-footer icon {
margin-left: 20rpx;
margin-top: 20rpx;
}

/全选字样/
.carts-footer text {
font-size: 30rpx;
margin-left: 8rpx;
line-height: 10rpx;
}

/立即结算按钮/
.carts-footer .button {
line-height: 80rpx;
text-align: center;
width: 220rpx;
height: 80rpx;
background-color: #f60;
color: white;
font-size: 36rpx;
border-radius: 0;
border: 0;
}
.carts-footer-left {
display: flex;
flex-direction: row;
align-items: center;
}

.carts-footer-left text {
margin-top: 20rpx;
}

.text-price {
font-size: 32rpx;
color: #999999;
}
.text-red {
font-size: 32rpx;
color: orangered;
}
.image-delete {
width: 50rpx;
height: 60rpx;
}

.class-line1 {
width: 750rpx;

height: 20rpx;  
background: #f4f4f4;  

}

.view-price {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-end;
}

.view-price1 {
margin-top: 30rpx;
margin-right: 20rpx;

display: flex;  
flex-direction: row;  
justify-content: flex-end;  
align-items: center;  

}

.text-price1 {
font-size: 29rpx;
color: grey;
}

.text-save {
width: 680rpx;
align-self: center;
font-size: 32rpx;
margin-top: 30rpx;
color: #ffffff;
text-align: center;
height: 70rpx;
border-radius: 9rpx;
background: orange;
line-height: 70rpx;
margin-bottom: 40rpx;
}

.parent {
display: flex;
flex-direction: column;
width: 100%;
}

.view-freemoney {
width: 100%;
height: 100rpx;
background: #ffffff;
display: flex;
flex-direction: row;
align-items: center;
border-bottom: 1px solid #eee;
}

.text-hint {
font-size: 28rpx;
color: #000000;
margin-left: 40rpx;
}

.text-hint1 {
font-size: 28rpx;
color: #000000;
margin-left: 20rpx;
flex-shrink: 0;
flex-grow: 0;
}

.input-money {
font-size: 26rpx;
color: #999999;
margin-left: 6rpx;
border-style: solid;
border-width: 1rpx;
padding-left: 15rpx;
width: 140rpx;
}
.btn-use {
line-height: 55rpx;
background: red;
color: #ffffff;
text-align: center;
font-size: 28rpx;
padding-left: 10rpx;
margin-left: 5rpx;
padding-right: 10rpx;
}
.radio-style {
font-size: 28rpx;
color: red;
margin-left: 40rpx;
}
.text-redhint {
font-size: 28rpx;
color: red;
}
.text-select {
font-size: 28rpx;
color: #999999;
width: 200rpx;

height: 40rpx;  

line-height: 28rpx;  

}
.view-picker {
border-style: solid;
border-color: #999999;
padding-left: 20rpx;
display: flex;
flex: row;
height: 60rpx;
justify-content: flex-start;
align-items: center;
width: 200rpx;
border-width: 1rpx;
font-size: 28rpx;
color: #999999;
}
</style>

继续阅读 »

<template>
<!--orderinfo{{cartIds}}{{amount}}-->

<view class="parent">  
    <view class="view-address">  
        <view class="view-addressleft">  
            <image src="/images/white_loc.png"></image>  
            <view class="view-addressleft-info">  
                <text class="text-name">收货人:{{ address.consignee }}</text>  
                <text class="text-address">收货地址:{{ address.address }}</text>  
            </view>  
        </view>  

        <view class="view-addressright">  
            <text class="text-mobile">{{ address.mobile }}</text>  
            <image src="/images/white_hint.png"></image>  
        </view>  
    </view>  

    <view v-for="(item,index) in cartList" :key="index"  class="container carts-list">  
        <view v-if="item.show == 1" class="view-title">  
            <image src="/images/icon_order.png"></image>  
            <text>供应商:{{ item.store_name }}</text>  
        </view>  

        <view v-if="item.show == 1" class="class-line"></view>  
        <view class="carts-item">  
            <view>  
                <!-- 缩略图 -->  
                <image class="carts-image" src="{{item.image}}" mode="aspectFill" />  
            </view>  
            <view class="carts-text">  
                <!-- 商品标题 -->  
                <text class="carts-title">{{ item.goods_name }}</text>  
                <text class="carts-key-name">{{ item.spec_key_name }}</text>  
                <view class="carts-subtitle">  
                    <!-- 价格 -->  

                    <!--<text class="carts-price">{{item.goods_price}}</text>  

-->

                    <!-- 数量加减 -->  
                </view>  
            </view>  
            <view class="carts-right">  
                <text class="text-red">¥{{ item.member_goods_price }}</text>  
                <text class="text-price">x{{ item.goods_num }}</text>  
            </view>  
        </view>  
    </view>  

    <view class="class-line1"></view>  

    <view class="view-freemoney">  
        <!--  

<text class="text-hint">优 惠 劵:</text>

<input bindchange="bindChange" placeholder-style="color:#999999" class="input-money" placeholder="输入余额"/>
-->

        <radio color="red" class="radio-style" value="0" checked="{{check[0]}}" />  

        <picker  value="{{index}}" range="{{coupon}}">  
            <view class="view-picker">{{ cv }}</view>  
        </picker>  

        <text class="text-hint">或者</text>  

        <radio color="red" class="radio-style" value="1" checked="{{check[1]}}" />  
        <input placeholder-style="color:#999999"  class="input-money" placeholder="直接输入优惠劵" style="width:200rpx" />  
    </view>  

    <view class="view-freemoney">  
        <text class="text-hint">使用余额:</text>  

        <input placeholder-style="color:#999999"  class="input-money" placeholder="输入余额" />  
        <text class="btn-use" >使用</text>  
        <text class="text-hint1">您的可用余额{{ freemoney }}</text>  
    </view>  

    <view class="view-freemoney">  
        <text class="text-hint">使用积分:</text>  

        <input placeholder-style="color:#999999"  class="input-money" placeholder="输入积分" />  
        <text class="btn-use">使用</text>  
        <text class="text-hint1">您的可用积分{{ pay_points }}</text>  
    </view>  

    <view class="class-line1"></view>  

    <view class="view-price">  
        <view class="view-price1">  
            <text class="text-price1">商品总额:</text>  
            <text class="text-red">¥{{ totalPrice.total_fee }}元</text>  
        </view>  
        <view hidden class="view-price1">  
            <text class="text-price1">邮费:</text>  
            <text class="text-red">¥20元</text>  
        </view>  

        <view class="view-price1">  
            <text class="text-price1">应付总金额:</text>  
            <text class="text-red">¥{{ totalPrice.total_fee }}元</text>  
        </view>  
    </view>  

    <view class="text-save" @longtap="longtap">提交订单</view>  
</view>  

</template>
<script>
export default {
methods:{
clickTest: function(e){
console.log(e);
console.log('click');
},
longtap: function(e){
console.log(e);
console.log('longtap');
}
}
}
</script>
<style lang="scss">
/ pages/order/ordersubmit/index.wxss /

.view-address {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
height: 150rpx;
background: #3aa0b7;
}
.view-addressleft {
display: flex;
flex-direction: row;
padding: 20rpx 0 0 3;
justify-content: flex-start;
align-items: center;
width: 450rpx;
}

.view-addressleft image {
width: 50rpx;
height: 50rpx;
}

.view-addressleft-info {
display: flex;
flex-direction: column;
margin-left: 10rpx;
justify-content: space-around;
align-items: flex-start;
height: 85rpx;
}
.text-name {
font-size: 29rpx;
color: #ffffff;
}
.text-address {
font-size: 29rpx;
color: #ffffff;
}
.view-addressright {
height: 50rpx;
margin-right: 20rpx;
display: flex;
flex-direction: row;
align-items: center;
}

.text-mobile {
font-size: 29rpx;
color: #ffffff;
}

.view-addressright image {
width: 30rpx;
height: 30rpx;
margin-left: 15rpx;
}

.view-title {
display: flex;
flex-direction: row;
height: 75rpx;

align-items: center;  

}

.view-title image {
width: 50rpx;
height: 50rpx;
}

.view-title text {
font-size: 29rpx;
color: black;
}

.class-line {
width: 670rpx;
margin-left: 0rpx;
height: 2rpx;
background: #f4f4f4;
}

/外部容器/
.container {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: space-between;
box-sizing: border-box;
}

/整体列表/
.carts-list {
display: flex;
flex-direction: column;
padding: 0 40rpx;
}

/每行单元格/
.carts-item {
display: flex;
flex-direction: row;
height: 150rpx;
/width属性解决标题文字太短而缩略图偏移/
width: 100%;
border-bottom: 1px solid #eee;
padding: 30rpx 0;

justify-content: space-between;  

}

/左部图片/
.carts-image {
width: 130rpx;
height: 150rpx;
border-width: 3rpx;

border-style: solid;  
border-color: #ccc;  

}

/右部描述/
.carts-text {
margin-left: 10rpx;
width: 370rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.carts-right {
height: 100%;
width: 110rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
/右上部分标题/
.carts-title {
font-size: 27rpx;
color: #444;
line-height: 38rpx;
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.carts-key-name {
margin-top: 10rpx;
font-size: 27rpx;
color: #999999;
}
/右下部分价格与数量/
.carts-subtitle {
font-size: 25rpx;
color: darkgray;
padding: 0 20rpx;
display: flex;
flex-direction: row;
justify-content: space-between;
}

/价格/
.carts-price {
color: #f60;
font-size: 14px;
line-height: 22px;
margin-top: 12rpx;
}

/商品加减按钮/
/stepper容器/
.stepper {
border: 1px solid #ccc;
border-radius: 3px;
width: 80px;
height: 26px;
margin: 0 auto;
}

/加号与减号/
.stepper text {
width: 19px;
line-height: 26px;
text-align: center;
float: left;
}

/数值/
.stepper input {
color: black;
float: left;
margin: 0 auto;
width: 40px;
height: 26px;
text-align: center;
font-size: 12px;
border-left: 1px solid #ccc;
border-right: 1px solid #ccc;
}

/普通样式/
.stepper .normal {
color: black;
}

/禁用样式/
.stepper .disabled {
color: #ccc;
}

/复选框样式/
.carts-list icon {
margin-top: 60rpx;
margin-right: 20rpx;
}

/底部按钮/
.carts-footer {
width: 100%;
height: 80rpx;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
position: fixed;
bottom: 0;
border-top: 1px solid #eee;
background: white;
}

/复选框/
.carts-footer icon {
margin-left: 20rpx;
margin-top: 20rpx;
}

/全选字样/
.carts-footer text {
font-size: 30rpx;
margin-left: 8rpx;
line-height: 10rpx;
}

/立即结算按钮/
.carts-footer .button {
line-height: 80rpx;
text-align: center;
width: 220rpx;
height: 80rpx;
background-color: #f60;
color: white;
font-size: 36rpx;
border-radius: 0;
border: 0;
}
.carts-footer-left {
display: flex;
flex-direction: row;
align-items: center;
}

.carts-footer-left text {
margin-top: 20rpx;
}

.text-price {
font-size: 32rpx;
color: #999999;
}
.text-red {
font-size: 32rpx;
color: orangered;
}
.image-delete {
width: 50rpx;
height: 60rpx;
}

.class-line1 {
width: 750rpx;

height: 20rpx;  
background: #f4f4f4;  

}

.view-price {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-end;
}

.view-price1 {
margin-top: 30rpx;
margin-right: 20rpx;

display: flex;  
flex-direction: row;  
justify-content: flex-end;  
align-items: center;  

}

.text-price1 {
font-size: 29rpx;
color: grey;
}

.text-save {
width: 680rpx;
align-self: center;
font-size: 32rpx;
margin-top: 30rpx;
color: #ffffff;
text-align: center;
height: 70rpx;
border-radius: 9rpx;
background: orange;
line-height: 70rpx;
margin-bottom: 40rpx;
}

.parent {
display: flex;
flex-direction: column;
width: 100%;
}

.view-freemoney {
width: 100%;
height: 100rpx;
background: #ffffff;
display: flex;
flex-direction: row;
align-items: center;
border-bottom: 1px solid #eee;
}

.text-hint {
font-size: 28rpx;
color: #000000;
margin-left: 40rpx;
}

.text-hint1 {
font-size: 28rpx;
color: #000000;
margin-left: 20rpx;
flex-shrink: 0;
flex-grow: 0;
}

.input-money {
font-size: 26rpx;
color: #999999;
margin-left: 6rpx;
border-style: solid;
border-width: 1rpx;
padding-left: 15rpx;
width: 140rpx;
}
.btn-use {
line-height: 55rpx;
background: red;
color: #ffffff;
text-align: center;
font-size: 28rpx;
padding-left: 10rpx;
margin-left: 5rpx;
padding-right: 10rpx;
}
.radio-style {
font-size: 28rpx;
color: red;
margin-left: 40rpx;
}
.text-redhint {
font-size: 28rpx;
color: red;
}
.text-select {
font-size: 28rpx;
color: #999999;
width: 200rpx;

height: 40rpx;  

line-height: 28rpx;  

}
.view-picker {
border-style: solid;
border-color: #999999;
padding-left: 20rpx;
display: flex;
flex: row;
height: 60rpx;
justify-content: flex-start;
align-items: center;
width: 200rpx;
border-width: 1rpx;
font-size: 28rpx;
color: #999999;
}
</style>

收起阅读 »

hbuilder快速换行及查询单词代码快捷键

快捷键

图一是未换行的代码,然后按ctrl + enter就会变成图二,轻轻松松换行不用动鼠标。
图三是按ctrl + F会跳出下面的框,可以查找也可以替换。

图一是未换行的代码,然后按ctrl + enter就会变成图二,轻轻松松换行不用动鼠标。
图三是按ctrl + F会跳出下面的框,可以查找也可以替换。

关于 1.9.0 调用 API 返回 Promise 对象不正确的临时解决方案

uniapp

更新:此问题已于1.9.2版本修复,更新新版即可。

=============以下为历史内容==============

首先对于本次 1.9.0 更新引发的返回 Promise 对象不正确的问题表示抱歉,同时也感谢及时反馈问题的小伙伴。

解决方案

HBuilderX

首先,找到 HBuilderX 的安装目录,并访问至 HBuilderX/plugins/uniapp-cli/node_modules/@dcloudio/ 目录。
然后,下载附件压缩包解压。
最后,使用解压出来的资源,覆盖 HBuilderX 插件目录下的同名资源,即 uni-app-plus、uni-mp-weixin 目录。

cli

cli 创建的项目,访问 项目根目录/node_modules/@dcloudio/ 并替换即可。

也可以通过 npm update 更新依赖来解决此问题。

参考文档

关于 uni 对于 Promise 的封装策略,详细请参考:Promise 封装

1.9.1

若更新至 1.9.1 后,在 H5 平台依旧存在问题,请下载附件中的文件,仅覆盖 uni-h5 目录的资源即可。

最后

使用 HBuilderX 请先使用此方案临时解决下问题,今日会更新一个版本来彻底修复此问题。

继续阅读 »

更新:此问题已于1.9.2版本修复,更新新版即可。

=============以下为历史内容==============

首先对于本次 1.9.0 更新引发的返回 Promise 对象不正确的问题表示抱歉,同时也感谢及时反馈问题的小伙伴。

解决方案

HBuilderX

首先,找到 HBuilderX 的安装目录,并访问至 HBuilderX/plugins/uniapp-cli/node_modules/@dcloudio/ 目录。
然后,下载附件压缩包解压。
最后,使用解压出来的资源,覆盖 HBuilderX 插件目录下的同名资源,即 uni-app-plus、uni-mp-weixin 目录。

cli

cli 创建的项目,访问 项目根目录/node_modules/@dcloudio/ 并替换即可。

也可以通过 npm update 更新依赖来解决此问题。

参考文档

关于 uni 对于 Promise 的封装策略,详细请参考:Promise 封装

1.9.1

若更新至 1.9.1 后,在 H5 平台依旧存在问题,请下载附件中的文件,仅覆盖 uni-h5 目录的资源即可。

最后

使用 HBuilderX 请先使用此方案临时解决下问题,今日会更新一个版本来彻底修复此问题。

收起阅读 »

HBuilderX的几点萌新问题[是我使用姿势不对吗]

HBuilderX HBuilder X

我用的是Mac版最新HBuilderX 1.9.0.20190412。

刚开始使用,体验如下:

  1. 文件改动没有任何的差异化标记。因为以前使用的编辑器里面都有文件是否改动,哪一行改动的标记,能看出这一次与上一次git提交后的差异。现在编辑器里面无法看,无法快速定位到改动过的地方,不得不通过git的gui工具去查看哪里改动了;
  2. 规范和错误提示并不是很友好的感觉。我写的代码,可能存在很多不规范的地方,虽然可能代码正确,但是不太符合规范,比如命名/空格/过时的语法等,但是没有提示我。然后我在编辑器里面随便敲错误的代码,并不能提示我错误,比如变量未定义就使用。编辑器里面也没有任何错误标记。而是需要我保存的时候进行代码检测,然后告诉我F4提示定位到下一出错误。虽然一般也不会写出什么神级错误,但是很显然有个时候会出现拼写错误的时候,这种情况似乎检测不出。if (plkj) {hello},这种东西在js文件里面没有任何的提示。我保存也不提示错误。plkj hello都没有定义;
  3. 书写vue文件的时候,我随便敲一个未引入的组建<hello-k></hello-k>也没有给我任何提示,我ctrl+s保存也没有触发什么提示。也没有那种比较便捷的<hello-k>然后输入</就自动补全的功能。因为我以前用WebStorm,各方面都体验很好。

因为刚刚入手,不知道这些问题是我使用的姿势不对,还是编辑器就是这么用的。不管怎么样,希望越做越好。
谢谢。

继续阅读 »

我用的是Mac版最新HBuilderX 1.9.0.20190412。

刚开始使用,体验如下:

  1. 文件改动没有任何的差异化标记。因为以前使用的编辑器里面都有文件是否改动,哪一行改动的标记,能看出这一次与上一次git提交后的差异。现在编辑器里面无法看,无法快速定位到改动过的地方,不得不通过git的gui工具去查看哪里改动了;
  2. 规范和错误提示并不是很友好的感觉。我写的代码,可能存在很多不规范的地方,虽然可能代码正确,但是不太符合规范,比如命名/空格/过时的语法等,但是没有提示我。然后我在编辑器里面随便敲错误的代码,并不能提示我错误,比如变量未定义就使用。编辑器里面也没有任何错误标记。而是需要我保存的时候进行代码检测,然后告诉我F4提示定位到下一出错误。虽然一般也不会写出什么神级错误,但是很显然有个时候会出现拼写错误的时候,这种情况似乎检测不出。if (plkj) {hello},这种东西在js文件里面没有任何的提示。我保存也不提示错误。plkj hello都没有定义;
  3. 书写vue文件的时候,我随便敲一个未引入的组建<hello-k></hello-k>也没有给我任何提示,我ctrl+s保存也没有触发什么提示。也没有那种比较便捷的<hello-k>然后输入</就自动补全的功能。因为我以前用WebStorm,各方面都体验很好。

因为刚刚入手,不知道这些问题是我使用的姿势不对,还是编辑器就是这么用的。不管怎么样,希望越做越好。
谢谢。

收起阅读 »