HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

HbuilderX云编译次数限制

HBuilderX

HbuilderX云编译一天可编译10次。

HbuilderX云编译一天可编译10次。

安卓环境下应用签名校验,解决【应用签名未校验风险】

由于业务需要,公司对App进行了第三方安全评估,发现了【应用签名未校验风险】,而且风险等级为【高】,老板看了说一定要解决,“拿人钱财,替人消灾”,开干!。
以下代码已在安卓6.0和安卓9.0手机上测试通过。

//如果是安卓运行环境,校验应用签名是否正确  
if(plus.os.name=='Android'){  
    if(!checkApkSign('SHA1')){  
        $.alert('签名异常,请到各大应用市场下载安装正版XXXX。','错误',function (e) {  
             plus.runtime.quit();  
        });  
    }  
}                 
/**  
 * 检查安卓APK签名  
 * @param {String} type 签名指纹类型:MD5或SHA1  
 */  
function checkApkSign(type){  
    //获取应用上下文  
    var context = plus.android.runtimeMainActivity();  
    var PackageManager = plus.android.importClass("android.content.pm.PackageManager");  
    var packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(),PackageManager.GET_SIGNING_CERTIFICATES||PackageManager.GET_SIGNATURES)  
    var Build = plus.android.importClass("android.os.Build");  
    var signatures = null;  
    //Android 28以后获取包签名信息方法改了  
    if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.P){  
        var signingInfo = packageInfo.plusGetAttribute('signingInfo');  
        signatures = plus.android.invoke(signingInfo,"getApkContentsSigners")  
    }else{  
        signatures = packageInfo.plusGetAttribute('signatures');  
    }  
    if (signatures != null) {  
        var signature;  
        var byteArr;  
        var currentSignature;  
        var md = plus.android.invoke("java.security.MessageDigest","getInstance",type);  
        for (var i in signatures) {  
            byteArr=plus.android.invoke(signatures[i],"toByteArray");  
            plus.android.invoke(md,"update",byteArr);  
            currentSignature = Bytes2HexString(plus.android.invoke(md,"digest")).toUpperCase();  
            //根据检验类型,与与你签名文件对应的签名值比对  
            if(("SHA1"==type && "你签名的SHA1值"==currentSignature)  
                || ("MD5"==type &&"你签名的MD5值"==currentSignature)){  
                return true;  
            }  

        }  
    } else {  
        console.info("应用未签名");  
    }   
    return false;  
}  
//字节数组转十六进制字符串,对负值填坑  
function Bytes2HexString(arrBytes) {  
  var str = "";  
  for (var i = 0; i < arrBytes.length; i++) {  
    var tmp;  
    var num=arrBytes[i];  
    if (num < 0) {  
    //此处填坑,当byte因为符合位导致数值为负时候,需要对数据进行处理  
      tmp =(255+num+1).toString(16);  
    } else {  
      tmp = num.toString(16);  
    }  
    if (tmp.length == 1) {  
      tmp = "0" + tmp;  
    }  
    str += tmp;  
  }  
  return str;  
}
继续阅读 »

由于业务需要,公司对App进行了第三方安全评估,发现了【应用签名未校验风险】,而且风险等级为【高】,老板看了说一定要解决,“拿人钱财,替人消灾”,开干!。
以下代码已在安卓6.0和安卓9.0手机上测试通过。

//如果是安卓运行环境,校验应用签名是否正确  
if(plus.os.name=='Android'){  
    if(!checkApkSign('SHA1')){  
        $.alert('签名异常,请到各大应用市场下载安装正版XXXX。','错误',function (e) {  
             plus.runtime.quit();  
        });  
    }  
}                 
/**  
 * 检查安卓APK签名  
 * @param {String} type 签名指纹类型:MD5或SHA1  
 */  
function checkApkSign(type){  
    //获取应用上下文  
    var context = plus.android.runtimeMainActivity();  
    var PackageManager = plus.android.importClass("android.content.pm.PackageManager");  
    var packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(),PackageManager.GET_SIGNING_CERTIFICATES||PackageManager.GET_SIGNATURES)  
    var Build = plus.android.importClass("android.os.Build");  
    var signatures = null;  
    //Android 28以后获取包签名信息方法改了  
    if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.P){  
        var signingInfo = packageInfo.plusGetAttribute('signingInfo');  
        signatures = plus.android.invoke(signingInfo,"getApkContentsSigners")  
    }else{  
        signatures = packageInfo.plusGetAttribute('signatures');  
    }  
    if (signatures != null) {  
        var signature;  
        var byteArr;  
        var currentSignature;  
        var md = plus.android.invoke("java.security.MessageDigest","getInstance",type);  
        for (var i in signatures) {  
            byteArr=plus.android.invoke(signatures[i],"toByteArray");  
            plus.android.invoke(md,"update",byteArr);  
            currentSignature = Bytes2HexString(plus.android.invoke(md,"digest")).toUpperCase();  
            //根据检验类型,与与你签名文件对应的签名值比对  
            if(("SHA1"==type && "你签名的SHA1值"==currentSignature)  
                || ("MD5"==type &&"你签名的MD5值"==currentSignature)){  
                return true;  
            }  

        }  
    } else {  
        console.info("应用未签名");  
    }   
    return false;  
}  
//字节数组转十六进制字符串,对负值填坑  
function Bytes2HexString(arrBytes) {  
  var str = "";  
  for (var i = 0; i < arrBytes.length; i++) {  
    var tmp;  
    var num=arrBytes[i];  
    if (num < 0) {  
    //此处填坑,当byte因为符合位导致数值为负时候,需要对数据进行处理  
      tmp =(255+num+1).toString(16);  
    } else {  
      tmp = num.toString(16);  
    }  
    if (tmp.length == 1) {  
      tmp = "0" + tmp;  
    }  
    str += tmp;  
  }  
  return str;  
}
收起阅读 »

关于WAP2APP打包JS混淆加密的方法(防止解压直接查看源码)

加密 wap2app云端打包

加密目的是为了防止别人解压APP文件后既可拿到你的信息和源代码,保护APP信息安全。
第一步,点击WAP2APP项目的manifest.json然后点击源码视图找到plus节点添加如下加密代码配置好后保存:

        "confusion" : {  
            "description" : "js原生混淆配置",  
            "resources" : {  
                "js/share.js" : {}, //列如加密的路径文件地址js/share.js  
                "sitemap.json" : {}  
            }  
        },

如下图:

第二步,发行原生APP-云打包的时候,务必要勾选启用JS原生混淆然后点击打包既可生效。如下图:

这样就大功告成了,别人解压也看不到你的APP代码了,包括sitemap.json文件信息别人也看不到了。

HX的JS混淆加密功能是非常强大的。

继续阅读 »

加密目的是为了防止别人解压APP文件后既可拿到你的信息和源代码,保护APP信息安全。
第一步,点击WAP2APP项目的manifest.json然后点击源码视图找到plus节点添加如下加密代码配置好后保存:

        "confusion" : {  
            "description" : "js原生混淆配置",  
            "resources" : {  
                "js/share.js" : {}, //列如加密的路径文件地址js/share.js  
                "sitemap.json" : {}  
            }  
        },

如下图:

第二步,发行原生APP-云打包的时候,务必要勾选启用JS原生混淆然后点击打包既可生效。如下图:

这样就大功告成了,别人解压也看不到你的APP代码了,包括sitemap.json文件信息别人也看不到了。

HX的JS混淆加密功能是非常强大的。

收起阅读 »

uni-app中使用阿里云MQTT,Dome分享,仅限APP端

var $mqtt = require("@/common/js/mqttws31.js");
var $crypto = require("@/common/js/crypto-js.js");
var instanceId = 'xxxxxxxxxx'; //实例 ID,购买后从控制台获取
var host = 'xxxxxxxxxx.mqtt.aliyuncs.com'; // 设置当前用户的接入点域名,接入点获取方法请参考接入准备章节文档,先在控制台创建实例
var port = 80; //WebSocket 协议服务端口,如果是走 HTTPS,设置443端口
var topic = 'Topic_xxxxxxxxxx'; //需要操作的 Topic,第一级父级 topic 需要在控制台申请
var useTLS = false; //是否走加密 HTTPS,如果走 HTTPS,设置为 true
var accessKey = 'xxxxxxxxxx'; //账号的 AccessKey,在阿里云控制台查看
var secretKey = 'xxxxxxxxxx'; //账号的的 SecretKey,在阿里云控制台查看
var cleansession = true;
var groupId = 'GID_xxxxxxxxxx'; //MQTT GroupID,创建实例后从 MQTT 控制台创建
var clientId = groupId '@@@00001'; //GroupId@@@DeviceId,由控制台创建的 Group ID 和自己指定的 Device ID 组合构成
var mqtt;
var reconnectTimeout = 2000;
var username = 'Signature|' accessKey '|' instanceId; //username和 Password 签名模式下的设置方法,参考文档 https://help.aliyun.com/document_detail/48271.html?spm=a2c4g.11186623.6.553.217831c3BSFry7
var password = "";
export default {
data() {
return {
mecont: []
}
},
onLoad() {
password = $crypto.HmacSHA1(clientId, secretKey).toString($crypto.enc.Base64);
MQTTconnect(this);
},
methods: {

    }  
}  

function MQTTconnect(_this) {  
    mqtt = new $mqtt.Client(  
        host, //MQTT 域名  
        port, //WebSocket 端口,如果使用 HTTPS 加密则配置为443,否则配置80  
        clientId //客户端 ClientId  
    );  
    var options = {  
        userName: username,  
        password: password,  
        useSSL: useTLS, //如果使用 HTTPS 加密则配置为 true  
        onSuccess: function onConnect() { //连接成功  
            _this.mecont.push(new Date()   ": "   "连接成功");  
            // Connection succeeded; subscribe to our topic  
            //订阅消息  
            mqtt.subscribe(topic   "/subDemo1", {  
                qos: 0  
            });  
            //测试消息推送  
            var message = new $mqtt.Message("APP测试消息推送!!"); //set body  
            message.destinationName = topic; // set topic  
            // //发送 P2P 消息,topic 设置方式参考https://help.aliyun.com/document_detail/96176.html?spm=a2c4g.11186623.6.586.694f7cb4oookL7  
            // message = new $mqtt.Message("Hello mqtt P2P Msg!!"); //set body  
            // message.destinationName = topic   "/p2p/"   clientId; // set topic  
            mqtt.send(message);  
        },  
        onFailure: function(message) { //连接失败  
            _this.mecont.push(new Date()   ": "   "连接失败"   JSON.stringify(message));  
            //连接失败,重新连接  
            setTimeout(MQTTconnect, reconnectTimeout);  
        }  
    };  
    mqtt.onConnectionLost = function onConnectionLost(response) {  
        _this.mecont.push(new Date()   ": "   "连接中断"   JSON.stringify(response));  
        //连接失败,重新连接  
        setTimeout(MQTTconnect, reconnectTimeout);  
    };  
    mqtt.onMessageArrived = function onMessageArrived(message) {  
        var topic = message.destinationName;  
        var payload = message.payloadString;  
        _this.mecont.push(new Date()   ": "   "收到新消息"    topic   "   "   payload);  
    };  
    mqtt.connect(options);  
}
继续阅读 »

var $mqtt = require("@/common/js/mqttws31.js");
var $crypto = require("@/common/js/crypto-js.js");
var instanceId = 'xxxxxxxxxx'; //实例 ID,购买后从控制台获取
var host = 'xxxxxxxxxx.mqtt.aliyuncs.com'; // 设置当前用户的接入点域名,接入点获取方法请参考接入准备章节文档,先在控制台创建实例
var port = 80; //WebSocket 协议服务端口,如果是走 HTTPS,设置443端口
var topic = 'Topic_xxxxxxxxxx'; //需要操作的 Topic,第一级父级 topic 需要在控制台申请
var useTLS = false; //是否走加密 HTTPS,如果走 HTTPS,设置为 true
var accessKey = 'xxxxxxxxxx'; //账号的 AccessKey,在阿里云控制台查看
var secretKey = 'xxxxxxxxxx'; //账号的的 SecretKey,在阿里云控制台查看
var cleansession = true;
var groupId = 'GID_xxxxxxxxxx'; //MQTT GroupID,创建实例后从 MQTT 控制台创建
var clientId = groupId '@@@00001'; //GroupId@@@DeviceId,由控制台创建的 Group ID 和自己指定的 Device ID 组合构成
var mqtt;
var reconnectTimeout = 2000;
var username = 'Signature|' accessKey '|' instanceId; //username和 Password 签名模式下的设置方法,参考文档 https://help.aliyun.com/document_detail/48271.html?spm=a2c4g.11186623.6.553.217831c3BSFry7
var password = "";
export default {
data() {
return {
mecont: []
}
},
onLoad() {
password = $crypto.HmacSHA1(clientId, secretKey).toString($crypto.enc.Base64);
MQTTconnect(this);
},
methods: {

    }  
}  

function MQTTconnect(_this) {  
    mqtt = new $mqtt.Client(  
        host, //MQTT 域名  
        port, //WebSocket 端口,如果使用 HTTPS 加密则配置为443,否则配置80  
        clientId //客户端 ClientId  
    );  
    var options = {  
        userName: username,  
        password: password,  
        useSSL: useTLS, //如果使用 HTTPS 加密则配置为 true  
        onSuccess: function onConnect() { //连接成功  
            _this.mecont.push(new Date()   ": "   "连接成功");  
            // Connection succeeded; subscribe to our topic  
            //订阅消息  
            mqtt.subscribe(topic   "/subDemo1", {  
                qos: 0  
            });  
            //测试消息推送  
            var message = new $mqtt.Message("APP测试消息推送!!"); //set body  
            message.destinationName = topic; // set topic  
            // //发送 P2P 消息,topic 设置方式参考https://help.aliyun.com/document_detail/96176.html?spm=a2c4g.11186623.6.586.694f7cb4oookL7  
            // message = new $mqtt.Message("Hello mqtt P2P Msg!!"); //set body  
            // message.destinationName = topic   "/p2p/"   clientId; // set topic  
            mqtt.send(message);  
        },  
        onFailure: function(message) { //连接失败  
            _this.mecont.push(new Date()   ": "   "连接失败"   JSON.stringify(message));  
            //连接失败,重新连接  
            setTimeout(MQTTconnect, reconnectTimeout);  
        }  
    };  
    mqtt.onConnectionLost = function onConnectionLost(response) {  
        _this.mecont.push(new Date()   ": "   "连接中断"   JSON.stringify(response));  
        //连接失败,重新连接  
        setTimeout(MQTTconnect, reconnectTimeout);  
    };  
    mqtt.onMessageArrived = function onMessageArrived(message) {  
        var topic = message.destinationName;  
        var payload = message.payloadString;  
        _this.mecont.push(new Date()   ": "   "收到新消息"    topic   "   "   payload);  
    };  
    mqtt.connect(options);  
}
收起阅读 »

提供一个自定义键盘 可以在uniapp中使用 也可以在h5中使用 非常精简可扩展

主要code

<template>  
    <div class="page_comp">  

        <button type="button" @click="openKeyBoard('s10')">唤起键盘,有小数点</button>  
        <button type="button" @click="openKeyBoard('s20')">唤起键盘,无小数点</button>  
        <button type="button" @click="openKeyBoard('s30')">唤起键盘,替换键值</button>  
        <button type="button" @click="openKeyBoard('s40')">唤起键盘,替换确认</button>  
        <button type="button" @click="openKeyBoard('s50')">无确认键</button>  

        <div style="position: fixed;z-index: 1999;font-size: .3rem;color: #000;background-color: #fff;">  
            {{valList.join("")}}  
        </div>  

        <!-- start -->  
        <div class="md-popup bottom" v-show="ShowKeyBoard">  
            <div class="md-popup-mask" v-show="ShowKeyBoard"></div>  
            <!-- <transition name="cus-slide-up"> -->  
            <div class="cus-num-key-slot" @click="cloKeyBoard" v-if="keyS == 's50'">  
                    收起键盘  
            </div>  
            <div class="cus-key-cont" v-show="ShowKeyBoard">  
                <div class="key-num">  
                    <ul class="key-num-list">  
                        <li @click="getKeyValue(item)" v-if="!(keyS == 's20'&&item == '.')" :class="['key-num-item',item === 0 && keyS == 's20'?'large-item':'']" v-for="(item,index) in keyList" :key="index"><span v-if="item == '.' && keyS == 's30'">{{keyReplce}}</span><span v-else>{{item}}</span></li>  
                        <li @click="delKeyValue" v-if="keyS == 's50'" class="key-num-item no-bg delete"><span></span></li>  
                        <li @click="cloKeyBoard" v-if="!(keyS == 's50')" class="key-num-item slidedown"><span></span></li>  
                    </ul>  
                </div>  
                <div class="keyb-ope" v-if="!(keyS == 's50')">  
                    <ul class="keyb-ope-list">  
                        <li class="keyb-ope-item delete" @click="delKeyValue"><span></span></li>  
                        <li class="keyb-ope-item confirm" @click="entKeyValue"><span>{{keyEntText}}</span></li>  
                    </ul>  
                </div>  
            </div>  
            <!-- </transition> -->  
        </div>  
        <!-- end -->  
    </div>  
</template>
<script>  
export default {  
  name: 'keybord',  
  data () {  
    return {  
        keyList: [1,2,3,4,5,6,7,8,9,'.',0],  
        valList: [],  
        temList: [],  
        keyS: 's10',  
        keyReplce: 'x',  
        keyEntText: '确定',  
        ShowKeyBoard: false  
    }  
  },  
  methods: {  
     getKeyValue(item){  
        this.valList.push(item);  
        //console.log(this.valList);  
     },  
     delKeyValue(){  
       let del_in = this.valList.length-1;   
        this.valList.splice(del_in,1);  
        //console.log(this.valList);  
     },  
     cloKeyBoard(){  
        this.ShowKeyBoard = false;  
     },  
     entKeyValue(){  
        this.temList = this.valList;  
        let resNum = this.temList.join("");  
        console.log(resNum);  
     },  
     openKeyBoard(value){  

       if(!value || value == 's10'){  
            this.ShowKeyBoard = true;  
       }  

       if(value == 's20'){  
            this.keyS = value;  
            this.ShowKeyBoard = true;  
       }  

       if(value == 's30'){  
            this.keyS = value;  
            this.ShowKeyBoard = true;  
       }  

       if(value == 's40'){  
            this.keyEntText = '支付';  
            this.ShowKeyBoard = true;  
       }  

       if(value == 's50'){  
            this.keyS = value;  
            this.ShowKeyBoard = true;  
       }  

     }  
  },  
  watch: {  
        ShowKeyBoard: function(){  
            if(this.ShowKeyBoard){  

            }else{  
                this.keyEntText = '确定';  
                this.keyS = 's10';  
            }  
        }  
  },    
  mounted: function(){  

  },  
  created: function(){  

  }    
}  
</script>
<style>  

.md-popup {  
    position: fixed;  
    top: 0;  
    right: 0;  
    bottom: 0;  
    left: 0;  
    display: -webkit-box;  
    display: flex;  
    z-index: 1000;  
}  
.md-popup.bottom {  
    flex-direction: column;  
    justify-content: flex-end;  
}  
.md-popup-mask {  
    top: 0;  
    right: 0;  
    bottom: 0;  
    left: 0;  
    position: absolute;  
    pointer-events: auto;  
    background-color: rgba(0,0,0,.6);  
    z-index: 1;  
}  
.cus-key-cont{position:relative;-webkit-user-select:none;-webkit-tap-highlight-color:transparent;display:-webkit-box;display:-webkit-flex;display:flex;width:100%;height:4.28rem;z-index:9}  
.cus-key-cont:after{content:"";position:absolute;z-index:2;-webkit-transform-origin:100% 50%;transform-origin:100% 50%;-webkit-transform:scaleY(.5) translateY(-100%);transform:scaleY(.5) translateY(-100%);top:0;left:0;right:auto;bottom:auto;width:100%;border-top:0.02rem solid #e2e4ea;-webkit-transform-origin:50% 0;transform-origin:50% 0}  
@media (-webkit-min-device-pixel-ratio:3),(min-resolution:3dppx){.cus-key-cont:after{-webkit-transform:scaleY(.33) translateY(-100%);transform:scaleY(.33) translateY(-100%)}  
}.cus-key-cont .key-num,.cus-key-cont .keyb-ope{display:-webkit-box;display:-webkit-flex;display:flex}  
.cus-key-cont .key-num{-webkit-box-flex:3;-webkit-flex:3;flex:3;background:#f9fafb}  
.cus-key-cont .key-num .key-num-list{float:left;width:100%}  
.cus-key-cont .key-num .key-num-list .key-num-item{position:relative;float:left;width:33.3%;height:1.07rem;padding-top:0.05rem;text-align:center;line-height:1.07rem;font-size:0.52rem;font-weight:500;font-family:Microsoft YaHei,微软雅黑,Arial,sans-serif;color:#111a34;-webkit-transition:background .3s;transition:background .3s;background:#fff;box-sizing:border-box;cursor: pointer;}  
.cus-key-cont .key-num .key-num-list .key-num-item:before{content:"";position:absolute;z-index:2;-webkit-transform:scaleX(.5) translateX(100%);transform:scaleX(.5) translateX(100%);top:0;right:0;left:auto;bottom:auto;border-right:0.02rem solid #e2e4ea;height:100%;-webkit-transform-origin:100% 50%;transform-origin:100% 50%}  
@media (-webkit-min-device-pixel-ratio:3),(min-resolution:3dppx){.cus-key-cont .key-num .key-num-list .key-num-item:before{-webkit-transform:scaleX(.33) translateX(100%);transform:scaleX(.33) translateX(100%)}  
}.cus-key-cont .key-num .key-num-list .key-num-item:after{content:"";position:absolute;z-index:2;-webkit-transform-origin:100% 50%;transform-origin:100% 50%;-webkit-transform:scaleY(.5) translateY(-100%);transform:scaleY(.5) translateY(-100%);top:0;left:0;right:auto;bottom:auto;width:100%;border-top:0.02rem solid #e2e4ea;-webkit-transform-origin:50% 0;transform-origin:50% 0}  
@media (-webkit-min-device-pixel-ratio:3),(min-resolution:3dppx){.cus-key-cont .key-num .key-num-list .key-num-item:after{-webkit-transform:scaleY(.33) translateY(-100%);transform:scaleY(.33) translateY(-100%)}  
}.cus-key-cont .key-num .key-num-list .key-num-item.no-bg{background:0 0}  
.cus-key-cont .key-num .key-num-list .key-num-item:nth-of-type(-n+3):after{display:none}  
.cus-key-cont .key-num .key-num-list .key-num-item:nth-of-type(3n){width:33.4%}  
.cus-key-cont .key-num .key-num-list .key-num-item.delete{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABECAMAAABJe8AqAAAAdVBMVEUAAABBSF5GTWBBSF5CSmJBSF1BSF1hYXVCSV1CSF5SXGtBSV1BSF5DSV9ESl5FTWRBSV5BSF5BSV1BSF5BSV1CSl5BSV5CSV5CSV5DSl9DS2JOTmNBSF1CSV9CSl5ETGBJSWRCSV5CSV1ES19CSF1CSl5BSF2IZB1/AAAAJnRSTlMA1yffH5T1BfudCuvLWUQW8OTUtamjj4FzNy0QwIxNQBxoZkvCZK9RzSgAAAGQSURBVFjDzNW5jsJQDIXhQ8LNwpKwDfswA4Xf/xEpXLhwbo4AWcJVlEj/V6Q4yN9ePr8J8reUWOBcxQKXUmKBrcQCB4kFbqK3xNtXjAHFSvt7xACzjfavdRCw0/66Rwxw1H5zQgzw1yjwQAzwP9d+ixigXmh/MQsCWu3Pp4gBHtpvOsQAp0qBI2KAfq39HWKA+qr9zYs/uB4F/EauikzoJ2HoppNuDPAbecv1RdJQv5Sq44Bt5CHfV8H1xQQH+I3cZvq9fk9DfZFfAthGlhfYDaUSf+kB28jqDDCB9B3gN5ILvG+A30gukL4D3EZSgfQd4DeSC/bIAb+RXGB9A2wj77CjQmt9AmQ2kgrWJ0B+I7mQwIH8RnIh4SuAZ3VmbgMACMNAL0IF7L8iokposaLYEyDx5OyD3CKBQ6avqcBDKx8VMezW57DrH9fAJIHTj0wa+gKxhQxeCtERGN/hVyW+5wJiWqGAHSXQtMZmgpqqhIegpjonE9RVqZVLQWDWac0gqLlavgQ11/u1HxRBUH6BA3XHDDIvn0o0AAAAAElFTkSuQmCC) 50% no-repeat;background-size:0.46rem}  
.cus-key-cont .key-num .key-num-list .key-num-item.slidedown{background:#fff url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGwAAABeAgMAAAC7uhn4AAAACVBMVEUAAAASHDYRGjQxUFCHAAAAAnRSTlMAgJsrThgAAAB/SURBVEjH7dUxDoAgEETRsbGgtvI0XkdP4xEoaOSUJgQzJMMmlpvAtL94YRuQ7flqEf2F2WazWsjtotvm6WbOWvgO5rx5utlsP5uzP84ehtlaX3xDt9R2oLOrpAe97aUlEySnIDkFySlITkFyCpJTkJyCyT4pOQXJ6U7Y29DuBThrzOxbC3kTAAAAAElFTkSuQmCC) 50% no-repeat;background-size:0.54rem}  
.cus-key-cont .key-num .key-num-list .key-num-item.large-item{width:66.6%}  
.cus-key-cont .key-num .key-num-list .key-num-item.active,.cus-key-cont .key-num .key-num-list .key-num-item:active{background-color:#f9fafb}  
.cus-key-cont .key-num .key-num-list .key-num-item:after,.cus-key-cont .key-num .key-num-list .key-num-item:before{pointer-events:none}  
.cus-key-cont .keyb-ope{-webkit-box-flex:1;-webkit-flex:1;flex:1}  
.cus-key-cont .keyb-ope .keyb-ope-list{position:relative;float:left;width:100%;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column}  
.cus-key-cont .keyb-ope .keyb-ope-list .keyb-ope-item{position:relative;float:left;width:100%;background:#fff;-webkit-transition:background .3s;transition:background .3s;cursor: pointer;}  
.cus-key-cont .keyb-ope .keyb-ope-list .keyb-ope-item.delete{height:1.07rem;background:#fff url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABECAMAAABJe8AqAAAAdVBMVEUAAABBSF5GTWBBSF5CSmJBSF1BSF1hYXVCSV1CSF5SXGtBSV1BSF5DSV9ESl5FTWRBSV5BSF5BSV1BSF5BSV1CSl5BSV5CSV5CSV5DSl9DS2JOTmNBSF1CSV9CSl5ETGBJSWRCSV5CSV1ES19CSF1CSl5BSF2IZB1/AAAAJnRSTlMA1yffH5T1BfudCuvLWUQW8OTUtamjj4FzNy0QwIxNQBxoZkvCZK9RzSgAAAGQSURBVFjDzNW5jsJQDIXhQ8LNwpKwDfswA4Xf/xEpXLhwbo4AWcJVlEj/V6Q4yN9ePr8J8reUWOBcxQKXUmKBrcQCB4kFbqK3xNtXjAHFSvt7xACzjfavdRCw0/66Rwxw1H5zQgzw1yjwQAzwP9d+ixigXmh/MQsCWu3Pp4gBHtpvOsQAp0qBI2KAfq39HWKA+qr9zYs/uB4F/EauikzoJ2HoppNuDPAbecv1RdJQv5Sq44Bt5CHfV8H1xQQH+I3cZvq9fk9DfZFfAthGlhfYDaUSf+kB28jqDDCB9B3gN5ILvG+A30gukL4D3EZSgfQd4DeSC/bIAb+RXGB9A2wj77CjQmt9AmQ2kgrWJ0B+I7mQwIH8RnIh4SuAZ3VmbgMACMNAL0IF7L8iokposaLYEyDx5OyD3CKBQ6avqcBDKx8VMezW57DrH9fAJIHTj0wa+gKxhQxeCtERGN/hVyW+5wJiWqGAHSXQtMZmgpqqhIegpjonE9RVqZVLQWDWac0gqLlavgQ11/u1HxRBUH6BA3XHDDIvn0o0AAAAAElFTkSuQmCC) 50% no-repeat;background-size:0.42rem;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-flex:1;-webkit-flex:1;flex:1}  
.cus-key-cont .keyb-ope .keyb-ope-list .keyb-ope-item.delete:active{background-color:#f9fafb}  
.cus-key-cont .keyb-ope .keyb-ope-list .keyb-ope-item.confirm{padding-top:0.05rem;color:#fff;font-size:0.36rem;background:#2f86f6;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-flex:1;-webkit-flex:1;flex:1;-webkit-box-align:center;-webkit-align-items:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center;}  
.cus-key-cont .keyb-ope .keyb-ope-list .keyb-ope-item.confirm:active{background-color:#2a78dc}  
.cus-key-cont.simple .key-num-item{color:number-keyb-key-color-simple!important}  
.cus-slide-up-enter,.cus-slide-up-leave-to {  
    -webkit-transform: translate3d(0,100%,0);  
    transform: translate3d(0,100%,0)  
}  
.cus-slide-up-enter-active {  
    -webkit-transition: -webkit-transform .3s cubic-bezier(.165,.84,.44,1);  
    transition: -webkit-transform .3s cubic-bezier(.165,.84,.44,1);  
    transition: transform .3s cubic-bezier(.165,.84,.44,1);  
    transition: transform .3s cubic-bezier(.165,.84,.44,1), -webkit-transform .3s cubic-bezier(.165,.84,.44,1)  
}  
.cus-slide-up-leave-active {  
    -webkit-transition: -webkit-transform .25s cubic-bezier(.165,.84,.44,1);  
    transition: -webkit-transform .25s cubic-bezier(.165,.84,.44,1);  
    transition: transform .25s cubic-bezier(.165,.84,.44,1);  
    transition: transform .25s cubic-bezier(.165,.84,.44,1), -webkit-transform .25s cubic-bezier(.165,.84,.44,1)  
}  
.cus-num-key-slot{  
    position: relative;  
    width: 100%;  
    height: 40px;  
    line-height: 40px;  
    background-color: #f8f8f8;  
    color: #666;  
    font-size: 14px;  
    z-index: 9;  
    text-align: center;  
}  

</style>
继续阅读 »

主要code

<template>  
    <div class="page_comp">  

        <button type="button" @click="openKeyBoard('s10')">唤起键盘,有小数点</button>  
        <button type="button" @click="openKeyBoard('s20')">唤起键盘,无小数点</button>  
        <button type="button" @click="openKeyBoard('s30')">唤起键盘,替换键值</button>  
        <button type="button" @click="openKeyBoard('s40')">唤起键盘,替换确认</button>  
        <button type="button" @click="openKeyBoard('s50')">无确认键</button>  

        <div style="position: fixed;z-index: 1999;font-size: .3rem;color: #000;background-color: #fff;">  
            {{valList.join("")}}  
        </div>  

        <!-- start -->  
        <div class="md-popup bottom" v-show="ShowKeyBoard">  
            <div class="md-popup-mask" v-show="ShowKeyBoard"></div>  
            <!-- <transition name="cus-slide-up"> -->  
            <div class="cus-num-key-slot" @click="cloKeyBoard" v-if="keyS == 's50'">  
                    收起键盘  
            </div>  
            <div class="cus-key-cont" v-show="ShowKeyBoard">  
                <div class="key-num">  
                    <ul class="key-num-list">  
                        <li @click="getKeyValue(item)" v-if="!(keyS == 's20'&&item == '.')" :class="['key-num-item',item === 0 && keyS == 's20'?'large-item':'']" v-for="(item,index) in keyList" :key="index"><span v-if="item == '.' && keyS == 's30'">{{keyReplce}}</span><span v-else>{{item}}</span></li>  
                        <li @click="delKeyValue" v-if="keyS == 's50'" class="key-num-item no-bg delete"><span></span></li>  
                        <li @click="cloKeyBoard" v-if="!(keyS == 's50')" class="key-num-item slidedown"><span></span></li>  
                    </ul>  
                </div>  
                <div class="keyb-ope" v-if="!(keyS == 's50')">  
                    <ul class="keyb-ope-list">  
                        <li class="keyb-ope-item delete" @click="delKeyValue"><span></span></li>  
                        <li class="keyb-ope-item confirm" @click="entKeyValue"><span>{{keyEntText}}</span></li>  
                    </ul>  
                </div>  
            </div>  
            <!-- </transition> -->  
        </div>  
        <!-- end -->  
    </div>  
</template>
<script>  
export default {  
  name: 'keybord',  
  data () {  
    return {  
        keyList: [1,2,3,4,5,6,7,8,9,'.',0],  
        valList: [],  
        temList: [],  
        keyS: 's10',  
        keyReplce: 'x',  
        keyEntText: '确定',  
        ShowKeyBoard: false  
    }  
  },  
  methods: {  
     getKeyValue(item){  
        this.valList.push(item);  
        //console.log(this.valList);  
     },  
     delKeyValue(){  
       let del_in = this.valList.length-1;   
        this.valList.splice(del_in,1);  
        //console.log(this.valList);  
     },  
     cloKeyBoard(){  
        this.ShowKeyBoard = false;  
     },  
     entKeyValue(){  
        this.temList = this.valList;  
        let resNum = this.temList.join("");  
        console.log(resNum);  
     },  
     openKeyBoard(value){  

       if(!value || value == 's10'){  
            this.ShowKeyBoard = true;  
       }  

       if(value == 's20'){  
            this.keyS = value;  
            this.ShowKeyBoard = true;  
       }  

       if(value == 's30'){  
            this.keyS = value;  
            this.ShowKeyBoard = true;  
       }  

       if(value == 's40'){  
            this.keyEntText = '支付';  
            this.ShowKeyBoard = true;  
       }  

       if(value == 's50'){  
            this.keyS = value;  
            this.ShowKeyBoard = true;  
       }  

     }  
  },  
  watch: {  
        ShowKeyBoard: function(){  
            if(this.ShowKeyBoard){  

            }else{  
                this.keyEntText = '确定';  
                this.keyS = 's10';  
            }  
        }  
  },    
  mounted: function(){  

  },  
  created: function(){  

  }    
}  
</script>
<style>  

.md-popup {  
    position: fixed;  
    top: 0;  
    right: 0;  
    bottom: 0;  
    left: 0;  
    display: -webkit-box;  
    display: flex;  
    z-index: 1000;  
}  
.md-popup.bottom {  
    flex-direction: column;  
    justify-content: flex-end;  
}  
.md-popup-mask {  
    top: 0;  
    right: 0;  
    bottom: 0;  
    left: 0;  
    position: absolute;  
    pointer-events: auto;  
    background-color: rgba(0,0,0,.6);  
    z-index: 1;  
}  
.cus-key-cont{position:relative;-webkit-user-select:none;-webkit-tap-highlight-color:transparent;display:-webkit-box;display:-webkit-flex;display:flex;width:100%;height:4.28rem;z-index:9}  
.cus-key-cont:after{content:"";position:absolute;z-index:2;-webkit-transform-origin:100% 50%;transform-origin:100% 50%;-webkit-transform:scaleY(.5) translateY(-100%);transform:scaleY(.5) translateY(-100%);top:0;left:0;right:auto;bottom:auto;width:100%;border-top:0.02rem solid #e2e4ea;-webkit-transform-origin:50% 0;transform-origin:50% 0}  
@media (-webkit-min-device-pixel-ratio:3),(min-resolution:3dppx){.cus-key-cont:after{-webkit-transform:scaleY(.33) translateY(-100%);transform:scaleY(.33) translateY(-100%)}  
}.cus-key-cont .key-num,.cus-key-cont .keyb-ope{display:-webkit-box;display:-webkit-flex;display:flex}  
.cus-key-cont .key-num{-webkit-box-flex:3;-webkit-flex:3;flex:3;background:#f9fafb}  
.cus-key-cont .key-num .key-num-list{float:left;width:100%}  
.cus-key-cont .key-num .key-num-list .key-num-item{position:relative;float:left;width:33.3%;height:1.07rem;padding-top:0.05rem;text-align:center;line-height:1.07rem;font-size:0.52rem;font-weight:500;font-family:Microsoft YaHei,微软雅黑,Arial,sans-serif;color:#111a34;-webkit-transition:background .3s;transition:background .3s;background:#fff;box-sizing:border-box;cursor: pointer;}  
.cus-key-cont .key-num .key-num-list .key-num-item:before{content:"";position:absolute;z-index:2;-webkit-transform:scaleX(.5) translateX(100%);transform:scaleX(.5) translateX(100%);top:0;right:0;left:auto;bottom:auto;border-right:0.02rem solid #e2e4ea;height:100%;-webkit-transform-origin:100% 50%;transform-origin:100% 50%}  
@media (-webkit-min-device-pixel-ratio:3),(min-resolution:3dppx){.cus-key-cont .key-num .key-num-list .key-num-item:before{-webkit-transform:scaleX(.33) translateX(100%);transform:scaleX(.33) translateX(100%)}  
}.cus-key-cont .key-num .key-num-list .key-num-item:after{content:"";position:absolute;z-index:2;-webkit-transform-origin:100% 50%;transform-origin:100% 50%;-webkit-transform:scaleY(.5) translateY(-100%);transform:scaleY(.5) translateY(-100%);top:0;left:0;right:auto;bottom:auto;width:100%;border-top:0.02rem solid #e2e4ea;-webkit-transform-origin:50% 0;transform-origin:50% 0}  
@media (-webkit-min-device-pixel-ratio:3),(min-resolution:3dppx){.cus-key-cont .key-num .key-num-list .key-num-item:after{-webkit-transform:scaleY(.33) translateY(-100%);transform:scaleY(.33) translateY(-100%)}  
}.cus-key-cont .key-num .key-num-list .key-num-item.no-bg{background:0 0}  
.cus-key-cont .key-num .key-num-list .key-num-item:nth-of-type(-n+3):after{display:none}  
.cus-key-cont .key-num .key-num-list .key-num-item:nth-of-type(3n){width:33.4%}  
.cus-key-cont .key-num .key-num-list .key-num-item.delete{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABECAMAAABJe8AqAAAAdVBMVEUAAABBSF5GTWBBSF5CSmJBSF1BSF1hYXVCSV1CSF5SXGtBSV1BSF5DSV9ESl5FTWRBSV5BSF5BSV1BSF5BSV1CSl5BSV5CSV5CSV5DSl9DS2JOTmNBSF1CSV9CSl5ETGBJSWRCSV5CSV1ES19CSF1CSl5BSF2IZB1/AAAAJnRSTlMA1yffH5T1BfudCuvLWUQW8OTUtamjj4FzNy0QwIxNQBxoZkvCZK9RzSgAAAGQSURBVFjDzNW5jsJQDIXhQ8LNwpKwDfswA4Xf/xEpXLhwbo4AWcJVlEj/V6Q4yN9ePr8J8reUWOBcxQKXUmKBrcQCB4kFbqK3xNtXjAHFSvt7xACzjfavdRCw0/66Rwxw1H5zQgzw1yjwQAzwP9d+ixigXmh/MQsCWu3Pp4gBHtpvOsQAp0qBI2KAfq39HWKA+qr9zYs/uB4F/EauikzoJ2HoppNuDPAbecv1RdJQv5Sq44Bt5CHfV8H1xQQH+I3cZvq9fk9DfZFfAthGlhfYDaUSf+kB28jqDDCB9B3gN5ILvG+A30gukL4D3EZSgfQd4DeSC/bIAb+RXGB9A2wj77CjQmt9AmQ2kgrWJ0B+I7mQwIH8RnIh4SuAZ3VmbgMACMNAL0IF7L8iokposaLYEyDx5OyD3CKBQ6avqcBDKx8VMezW57DrH9fAJIHTj0wa+gKxhQxeCtERGN/hVyW+5wJiWqGAHSXQtMZmgpqqhIegpjonE9RVqZVLQWDWac0gqLlavgQ11/u1HxRBUH6BA3XHDDIvn0o0AAAAAElFTkSuQmCC) 50% no-repeat;background-size:0.46rem}  
.cus-key-cont .key-num .key-num-list .key-num-item.slidedown{background:#fff url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGwAAABeAgMAAAC7uhn4AAAACVBMVEUAAAASHDYRGjQxUFCHAAAAAnRSTlMAgJsrThgAAAB/SURBVEjH7dUxDoAgEETRsbGgtvI0XkdP4xEoaOSUJgQzJMMmlpvAtL94YRuQ7flqEf2F2WazWsjtotvm6WbOWvgO5rx5utlsP5uzP84ehtlaX3xDt9R2oLOrpAe97aUlEySnIDkFySlITkFyCpJTkJyCyT4pOQXJ6U7Y29DuBThrzOxbC3kTAAAAAElFTkSuQmCC) 50% no-repeat;background-size:0.54rem}  
.cus-key-cont .key-num .key-num-list .key-num-item.large-item{width:66.6%}  
.cus-key-cont .key-num .key-num-list .key-num-item.active,.cus-key-cont .key-num .key-num-list .key-num-item:active{background-color:#f9fafb}  
.cus-key-cont .key-num .key-num-list .key-num-item:after,.cus-key-cont .key-num .key-num-list .key-num-item:before{pointer-events:none}  
.cus-key-cont .keyb-ope{-webkit-box-flex:1;-webkit-flex:1;flex:1}  
.cus-key-cont .keyb-ope .keyb-ope-list{position:relative;float:left;width:100%;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column}  
.cus-key-cont .keyb-ope .keyb-ope-list .keyb-ope-item{position:relative;float:left;width:100%;background:#fff;-webkit-transition:background .3s;transition:background .3s;cursor: pointer;}  
.cus-key-cont .keyb-ope .keyb-ope-list .keyb-ope-item.delete{height:1.07rem;background:#fff url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABECAMAAABJe8AqAAAAdVBMVEUAAABBSF5GTWBBSF5CSmJBSF1BSF1hYXVCSV1CSF5SXGtBSV1BSF5DSV9ESl5FTWRBSV5BSF5BSV1BSF5BSV1CSl5BSV5CSV5CSV5DSl9DS2JOTmNBSF1CSV9CSl5ETGBJSWRCSV5CSV1ES19CSF1CSl5BSF2IZB1/AAAAJnRSTlMA1yffH5T1BfudCuvLWUQW8OTUtamjj4FzNy0QwIxNQBxoZkvCZK9RzSgAAAGQSURBVFjDzNW5jsJQDIXhQ8LNwpKwDfswA4Xf/xEpXLhwbo4AWcJVlEj/V6Q4yN9ePr8J8reUWOBcxQKXUmKBrcQCB4kFbqK3xNtXjAHFSvt7xACzjfavdRCw0/66Rwxw1H5zQgzw1yjwQAzwP9d+ixigXmh/MQsCWu3Pp4gBHtpvOsQAp0qBI2KAfq39HWKA+qr9zYs/uB4F/EauikzoJ2HoppNuDPAbecv1RdJQv5Sq44Bt5CHfV8H1xQQH+I3cZvq9fk9DfZFfAthGlhfYDaUSf+kB28jqDDCB9B3gN5ILvG+A30gukL4D3EZSgfQd4DeSC/bIAb+RXGB9A2wj77CjQmt9AmQ2kgrWJ0B+I7mQwIH8RnIh4SuAZ3VmbgMACMNAL0IF7L8iokposaLYEyDx5OyD3CKBQ6avqcBDKx8VMezW57DrH9fAJIHTj0wa+gKxhQxeCtERGN/hVyW+5wJiWqGAHSXQtMZmgpqqhIegpjonE9RVqZVLQWDWac0gqLlavgQ11/u1HxRBUH6BA3XHDDIvn0o0AAAAAElFTkSuQmCC) 50% no-repeat;background-size:0.42rem;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-flex:1;-webkit-flex:1;flex:1}  
.cus-key-cont .keyb-ope .keyb-ope-list .keyb-ope-item.delete:active{background-color:#f9fafb}  
.cus-key-cont .keyb-ope .keyb-ope-list .keyb-ope-item.confirm{padding-top:0.05rem;color:#fff;font-size:0.36rem;background:#2f86f6;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-flex:1;-webkit-flex:1;flex:1;-webkit-box-align:center;-webkit-align-items:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center;}  
.cus-key-cont .keyb-ope .keyb-ope-list .keyb-ope-item.confirm:active{background-color:#2a78dc}  
.cus-key-cont.simple .key-num-item{color:number-keyb-key-color-simple!important}  
.cus-slide-up-enter,.cus-slide-up-leave-to {  
    -webkit-transform: translate3d(0,100%,0);  
    transform: translate3d(0,100%,0)  
}  
.cus-slide-up-enter-active {  
    -webkit-transition: -webkit-transform .3s cubic-bezier(.165,.84,.44,1);  
    transition: -webkit-transform .3s cubic-bezier(.165,.84,.44,1);  
    transition: transform .3s cubic-bezier(.165,.84,.44,1);  
    transition: transform .3s cubic-bezier(.165,.84,.44,1), -webkit-transform .3s cubic-bezier(.165,.84,.44,1)  
}  
.cus-slide-up-leave-active {  
    -webkit-transition: -webkit-transform .25s cubic-bezier(.165,.84,.44,1);  
    transition: -webkit-transform .25s cubic-bezier(.165,.84,.44,1);  
    transition: transform .25s cubic-bezier(.165,.84,.44,1);  
    transition: transform .25s cubic-bezier(.165,.84,.44,1), -webkit-transform .25s cubic-bezier(.165,.84,.44,1)  
}  
.cus-num-key-slot{  
    position: relative;  
    width: 100%;  
    height: 40px;  
    line-height: 40px;  
    background-color: #f8f8f8;  
    color: #666;  
    font-size: 14px;  
    z-index: 9;  
    text-align: center;  
}  

</style>
收起阅读 »

iOS 离线SDK集成示例

离线SDK 离线集成 iOS离线集成

说明

本示例工程实现了从原生页面跳转 uniapp 页面,然后在 uniapp 页面中通过调用原生扩展 Module 插件的方法在跳转回原生页面,希望对那些想在现有原生工程中集成uni-app的用户有所帮助;

运行方式

  • 下载原生离线sdk,并解压;
  • 将本帖子中的附件下载,并解压放到离线sdk的根目录中;
  • 直接运行 HBuilder-Integrate.xcodeproj 即可;

效果

示例

欢迎讨论

下载工程

继续阅读 »

说明

本示例工程实现了从原生页面跳转 uniapp 页面,然后在 uniapp 页面中通过调用原生扩展 Module 插件的方法在跳转回原生页面,希望对那些想在现有原生工程中集成uni-app的用户有所帮助;

运行方式

  • 下载原生离线sdk,并解压;
  • 将本帖子中的附件下载,并解压放到离线sdk的根目录中;
  • 直接运行 HBuilder-Integrate.xcodeproj 即可;

效果

示例

欢迎讨论

下载工程

收起阅读 »

HBuilderx打包iOS教程

iOS打包

HBuilderx使用自有证书打包打包iOS需要两个iOS证书文件p12和.mobileprovision

如果还没这两个文件先申请!

Windows直接申请iOS证书教程

HBuilder在线打包iOS步骤

1、打开HBuilder工具,选择完工的项目,点击发行,选择发行为原生安装包。

2、选择iOS打包,支持的设备类型(可以选择支持iPhone和支持ipad),选择使用苹果证书

AppID:跟申请证书描述.mobileprovision时选择的要一致(又称套装id,appid,BundleID,应用id,包名)

profile文件:选择上传配置文件.mobileprovision

私钥证书:上传.p12文件

私钥密码:输入创建p12设置的密码。

然后点击打包。

3、打包成功后,下载保存ipa,这个ipa包就能安装到手机测试了。

继续阅读 »

HBuilderx使用自有证书打包打包iOS需要两个iOS证书文件p12和.mobileprovision

如果还没这两个文件先申请!

Windows直接申请iOS证书教程

HBuilder在线打包iOS步骤

1、打开HBuilder工具,选择完工的项目,点击发行,选择发行为原生安装包。

2、选择iOS打包,支持的设备类型(可以选择支持iPhone和支持ipad),选择使用苹果证书

AppID:跟申请证书描述.mobileprovision时选择的要一致(又称套装id,appid,BundleID,应用id,包名)

profile文件:选择上传配置文件.mobileprovision

私钥证书:上传.p12文件

私钥密码:输入创建p12设置的密码。

然后点击打包。

3、打包成功后,下载保存ipa,这个ipa包就能安装到手机测试了。

收起阅读 »

长期兼职小程序和混合app的外包,有意向可加QQ私聊

小程序

长期兼职小程序和混合app的外包,有意向可加QQ私聊

长期兼职小程序和混合app的外包,有意向可加QQ私聊

使用本地iconfont阿里字体图标 无需转换base64

icon

1.首先登陆iconfont官网,创建自己的项目,找到需要的图标加入购物车,然后添加到项目中
2.生成unicode ,点击下载至本地,解压
3.将解压后的文件放入static目录
4.修改为本地引入方式


5.使用时在App.vue中全局引入该文件@import "./common/iconfont.css";(根据该文件路径),即可以在各页面中使用
6.使用方法
(1).使用class渲染图标

(2).使用字体编码

继续阅读 »

1.首先登陆iconfont官网,创建自己的项目,找到需要的图标加入购物车,然后添加到项目中
2.生成unicode ,点击下载至本地,解压
3.将解压后的文件放入static目录
4.修改为本地引入方式


5.使用时在App.vue中全局引入该文件@import "./common/iconfont.css";(根据该文件路径),即可以在各页面中使用
6.使用方法
(1).使用class渲染图标

(2).使用字体编码

收起阅读 »

【分享】苹果App上架App Store注意事项,避免被拒!

上架 iOS打包

iOS APP上架审核比较严格!

这里整理了iOS上架需要注意的事项,上架前尽量避免,争取一次提交审核成功,被拒打回重新修改提交审核会耗费很多时间!

分享Windows申请证书打包iOS app上架详细教程

1、应用内包含检查更新功能iOS 应用的版本更新必须通过 App Store 进行,自身 App 内不能包含提示更新功能。从2015年3月起,所有包含检查更新功能的 App 都会被拒绝上架。

2、使用第三方登录时未做安装检测接入第三方登录要检测是否安装了第三方客户端,未安装时不要显示对应按钮。2015年9月之前,通常可以采用判断未安装则隐藏登录按钮的方式。但目前隐藏按钮的方式也可能被审核拒绝,QQ 和微博提供了 web 登录的方式,如果判断未安装,需要允许用户使用 webview 的登录方式。苹果在条款中有声明不允许 iOS 应用的正常使用需要依赖另外一个 App。

3、采集设备IDFA但应用没有广告功能从2014年2月起,Apple 开始拒绝采集 IDFA (identifier for advertising) 却未集成任何广告服务的应用进入 App Store。如果 App 本身没有广告,ASO100.com 建议可以在审核的时候显示一个 Banner 广告,并且放在比较明显的位置,审核通过后关掉即可。

4、含UGC却未提供用户协议及举报功能如果你的 App 内有发帖等UGC(用户产生内容)功能,必须提供用户协议,并留有内容举报功能,否则就会被审核拒绝。

5、上传时没有使用真实的应用截图应用程序的名称、描述、截图或者预览与应用的内容和功能不相关将会被拒绝。有 App 因为应用截图使用的是自己设计的插画而被审核拒绝。

6、应用必须使用邀请码才能注册使用苹果要求应用不能限制只有部分用户可以使用。

7、应用内出现第三方移动平台的名字或图标一直以来,苹果都不允许iOS开发者在进行软件描述时提到 Android 版本,而自从2015年4月起,在 App 内、截图等任何地方提到安卓、Android 的文字、图标、系统界面都会被拒。曾经有电商 App,因为出现了售卖三星安卓手机而被拒。。。

8、应用内涉及奖励,未声明与苹果无关App 里有实物奖励的话,不能使用苹果产品(例如 iPhone 、iPad 等)作为奖品。另外一定要声明“奖励由本公司提供,与苹果官方无关”。

9、没有提供恢复内购的方法增加一个“恢复购买记录”的按钮即可。

10、未注册时不能使用与账号无关的功能对于资讯等 App,在没有进行与用户信息相关的操作时,却强行让用户登录,甚至不登录就无法看到任何内容,有可能会被拒绝。

11、iPhone 应用在 iPad 上不能正常显示iPhone程序必须不经修改就能以iPhone分辨率和2倍iPhone 3GS的分辨率在iPad上运行。即使你的App 只为 iPhone 用户提供,在 iPad 上也必须能够正常显示,否则审核会被拒绝。

12、侵犯第三方版权对于视频、音乐、图书类的应用很容易因为这一条而被拒。另外 ASO100.com 建议应用内最好不要出现第三方的商标,例如运营商的Logo、影视公司的 Logo 等。13、应用截图、名称、描述等出现不雅词汇在应用截图、名称、描述等任何地方出现例如诸如 牛逼、绿茶婊、无节操、逗比 等词汇,都会被苹果审核拒绝。

14、应用出现 beta版、测试版字样不要过度谦虚地在启动画面或者应用名称上加上”beta”字样,苹果不允许测试版产品上架。

15、注册缺少隐私政策如果应用包含注册功能,注册页面必须提供隐私说明协议按钮或者链接。另外在 iTunes connect 提交新版本的时候,Privacy Policy URL 必须要填写。

16、应用出现崩溃、加载失败等 bug审核期间出现崩溃会导致审核被拒。ASO100.com 建议,在审核期间务必保证服务器稳定,避免审核人员审核时出现内容加载失败的情况,导致被拒。

17、应用描述、截图和应用功能不符如果应用的描述或截图介绍的功能在审核期间没有体现,则会被拒绝,如果介绍文案不够详细也会有一定概率被拒。

18、应用包含应用推荐功能除特殊情况,苹果明令禁止应用内推荐其他APP。

19、应用包含不正确的诊断功能如果你的应用中,包含不真实的系统检测或优化功能,苹果会认为这项功能有误导用户的嫌疑,审核时会被拒绝。

20、应用提交的新版本与上一版差异过大如果你提交的新版本应用与上一版相比,功能上变化过大,比如将游戏升级为工具类应用,或在新版本中完全改掉前一版产品的功能,则会被苹果拒绝。

继续阅读 »

iOS APP上架审核比较严格!

这里整理了iOS上架需要注意的事项,上架前尽量避免,争取一次提交审核成功,被拒打回重新修改提交审核会耗费很多时间!

分享Windows申请证书打包iOS app上架详细教程

1、应用内包含检查更新功能iOS 应用的版本更新必须通过 App Store 进行,自身 App 内不能包含提示更新功能。从2015年3月起,所有包含检查更新功能的 App 都会被拒绝上架。

2、使用第三方登录时未做安装检测接入第三方登录要检测是否安装了第三方客户端,未安装时不要显示对应按钮。2015年9月之前,通常可以采用判断未安装则隐藏登录按钮的方式。但目前隐藏按钮的方式也可能被审核拒绝,QQ 和微博提供了 web 登录的方式,如果判断未安装,需要允许用户使用 webview 的登录方式。苹果在条款中有声明不允许 iOS 应用的正常使用需要依赖另外一个 App。

3、采集设备IDFA但应用没有广告功能从2014年2月起,Apple 开始拒绝采集 IDFA (identifier for advertising) 却未集成任何广告服务的应用进入 App Store。如果 App 本身没有广告,ASO100.com 建议可以在审核的时候显示一个 Banner 广告,并且放在比较明显的位置,审核通过后关掉即可。

4、含UGC却未提供用户协议及举报功能如果你的 App 内有发帖等UGC(用户产生内容)功能,必须提供用户协议,并留有内容举报功能,否则就会被审核拒绝。

5、上传时没有使用真实的应用截图应用程序的名称、描述、截图或者预览与应用的内容和功能不相关将会被拒绝。有 App 因为应用截图使用的是自己设计的插画而被审核拒绝。

6、应用必须使用邀请码才能注册使用苹果要求应用不能限制只有部分用户可以使用。

7、应用内出现第三方移动平台的名字或图标一直以来,苹果都不允许iOS开发者在进行软件描述时提到 Android 版本,而自从2015年4月起,在 App 内、截图等任何地方提到安卓、Android 的文字、图标、系统界面都会被拒。曾经有电商 App,因为出现了售卖三星安卓手机而被拒。。。

8、应用内涉及奖励,未声明与苹果无关App 里有实物奖励的话,不能使用苹果产品(例如 iPhone 、iPad 等)作为奖品。另外一定要声明“奖励由本公司提供,与苹果官方无关”。

9、没有提供恢复内购的方法增加一个“恢复购买记录”的按钮即可。

10、未注册时不能使用与账号无关的功能对于资讯等 App,在没有进行与用户信息相关的操作时,却强行让用户登录,甚至不登录就无法看到任何内容,有可能会被拒绝。

11、iPhone 应用在 iPad 上不能正常显示iPhone程序必须不经修改就能以iPhone分辨率和2倍iPhone 3GS的分辨率在iPad上运行。即使你的App 只为 iPhone 用户提供,在 iPad 上也必须能够正常显示,否则审核会被拒绝。

12、侵犯第三方版权对于视频、音乐、图书类的应用很容易因为这一条而被拒。另外 ASO100.com 建议应用内最好不要出现第三方的商标,例如运营商的Logo、影视公司的 Logo 等。13、应用截图、名称、描述等出现不雅词汇在应用截图、名称、描述等任何地方出现例如诸如 牛逼、绿茶婊、无节操、逗比 等词汇,都会被苹果审核拒绝。

14、应用出现 beta版、测试版字样不要过度谦虚地在启动画面或者应用名称上加上”beta”字样,苹果不允许测试版产品上架。

15、注册缺少隐私政策如果应用包含注册功能,注册页面必须提供隐私说明协议按钮或者链接。另外在 iTunes connect 提交新版本的时候,Privacy Policy URL 必须要填写。

16、应用出现崩溃、加载失败等 bug审核期间出现崩溃会导致审核被拒。ASO100.com 建议,在审核期间务必保证服务器稳定,避免审核人员审核时出现内容加载失败的情况,导致被拒。

17、应用描述、截图和应用功能不符如果应用的描述或截图介绍的功能在审核期间没有体现,则会被拒绝,如果介绍文案不够详细也会有一定概率被拒。

18、应用包含应用推荐功能除特殊情况,苹果明令禁止应用内推荐其他APP。

19、应用包含不正确的诊断功能如果你的应用中,包含不真实的系统检测或优化功能,苹果会认为这项功能有误导用户的嫌疑,审核时会被拒绝。

20、应用提交的新版本与上一版差异过大如果你提交的新版本应用与上一版相比,功能上变化过大,比如将游戏升级为工具类应用,或在新版本中完全改掉前一版产品的功能,则会被苹果拒绝。

收起阅读 »

解决mui.ajax使用FormData方式上传图片无法接收参数的问题

经验分享 图片上传

以前图片上传一直采用的图片压缩后上传base64字符串的方式,没有任何问题,最近的一个项目跟其他端是公用的接口,必须使用input file文件上传图片,结果接口各种接收不到formData append的参数,论坛各种搜都是说放弃file上传方式,最后采用原生XMLHttpRequest上传终于可以了。代码如下:

var xhr = new XMLHttpRequest();  
var formData = new FormData();  
formData.append('file', file);  
formData.append('openId', localStorage.openId);  
formData.append('provider', 'app');  

xhr.open('POST', URL + 'mobileshopapi/Common/PostUploadImage', true);  
xhr.onreadystatechange = function(){  
    if(xhr.readyState == 4 && xhr.status == 200){  
        var res = JSON.parse(xhr.responseText)  
    }  
}  
xhr.send(formData);
继续阅读 »

以前图片上传一直采用的图片压缩后上传base64字符串的方式,没有任何问题,最近的一个项目跟其他端是公用的接口,必须使用input file文件上传图片,结果接口各种接收不到formData append的参数,论坛各种搜都是说放弃file上传方式,最后采用原生XMLHttpRequest上传终于可以了。代码如下:

var xhr = new XMLHttpRequest();  
var formData = new FormData();  
formData.append('file', file);  
formData.append('openId', localStorage.openId);  
formData.append('provider', 'app');  

xhr.open('POST', URL + 'mobileshopapi/Common/PostUploadImage', true);  
xhr.onreadystatechange = function(){  
    if(xhr.readyState == 4 && xhr.status == 200){  
        var res = JSON.parse(xhr.responseText)  
    }  
}  
xhr.send(formData);
收起阅读 »