1

github地址:weexSafeKeyboard

效果图:
图片描述

技术依赖:
框架:weex+vue
弹出层:weex-ui
图标:iconfont

说明:
1、如果不想用到weex-ui,可以把inputkey.vue文件里的wxc-popup组件去掉,按自己的弹窗实现即可;
2、删除、大小写、空格图标用的是iconfont;如不想用请自行替换;
本项目是放在本地,以安卓为例:android/app/src/main/assets/iconfont
3、密码可见、不可见图标按钮用的是common.js里的getImageUrl方法获取的路径,请自行替换

声明:
如有需要,请参考实现的思路,消化成自己的东西,勿直接复制,会消化不良。

实际调用页面:index.vue 代码如下:

<template>
    <div>
        <div class="cell">
            <div class="cell_label">
                <text class="label">密码</text>
            </div>
            <div class="cell_input">
                <inputkey @inputVal="inputVal" :inputstyle="inputstyle" :textstyle="textstyle" type=true placeholder="请输入登录密码"></inputkey>
            </div>
        </div>
    </div>
</template>
<style>
.cell {width: 750px;height: 120px;border-bottom-width: 2px;border-bottom-color: #C9C5C4;flex-direction: row;align-items: center;justify-content: center;}
.cell_label {justify-content: center;align-items: center;}
.label{font-size: 34px;font-weight: 600px;color:#000;}
.cell_input {justify-content: center;align-items: center;margin-left: 30px;}
</style>
<script>
import inputkey from './components/inputkey.vue';
export default {
    data: {
        inputstyle:{width:'550px',height:'50px',margin:'5px',fontSize:'36px'},
        textstyle:{fontSize:'34px',fontWeight:'600',color:'#999'},
        password:'',
    },
    components: {
        inputkey
    },
    created: function () {
    },
    methods:{
        inputVal(e){
            this.password = e.inputVal;
        }
    }
}
</script>

组件:components/inputkey.vue 代码如下:

<template>
    <div>
        <div>
            <div style="flex-direction:row;position:relative;" :style="inputstyle" @click="onfocus">
                //placeholder内容
                <text class="placeholder" v-if="blank">{{placeholder}}</text>
                //把输入内容显示为*
                <text v-if="type"  :style="textstyle">{{passwordInput}}</text>
                <text v-if="!type"  :style="textstyle">{{input}}</text>
                //光标“|”
                <text v-if="cursor=='true'" :style="mrTextstyle" style="color:blue;">|</text>
                //可不可见图标
                <div class="imagearea" @click="lookPwd">
                    <image :src="imageUrl" class="image"></image>
                </div>
            </div>
        </div>
        //weex-ui 里的 wxc-popup 弹窗,可改为自己的
        <wxc-popup popup-color="#fff"
               :show="isBottomShow"
               @wxcPopupOverlayClicked="popupMenu"
               pos="bottom"
               :height="popupHeight">
            <div class="title">
                <div class="btn" @click="changeState('abc')">
                    <text>Abc</text>
                </div>
                <div class="btn" @click="changeState('symbol')">
                    <text>符</text>
                </div>
                <div class="btn" @click="changeState('num')">
                    <text>123</text>
                </div>
                <div class="btn2" @click="randomAbc">
                    <text>安全键盘</text>
                </div>
                <div class="btn" @click="popupMenu">
                    <text>完成</text>
                </div>
            </div>
            <div class="content">
                //abc界面
                <div class="row" v-for="item in charList" v-if="state == 'abc'">
                    <div class="button" v-for="ite in item" @click="ite=='top'?lowerToUpper(): btnClick(ite)">
                        <text v-if="ite === 'top'" style="fontFamily:iconfont;color:red;" v-bind:style="{backgroundColor:charState=='lower'?'':'#999999'}">&#xe685;</text>
                        <text v-else-if="ite === 'blank'" style="fontFamily:iconfont;color:red;">&#xe66e;</text>
                        <text v-else-if="ite === 'del'" style="fontFamily:iconfont;color:red;">&#xe629;</text>
                        <text v-else>{{ite}}</text>
                    </div>
                </div>
                //特殊符号界面
                <div class="row" v-for="item in symbolList" v-if="state == 'symbol'">
                    <div class="button" v-for="ite in item" v-bind:style="{flex:ite==='blank'?6:1}" @click="btnClick(ite)">
                        <text v-if="ite === 'blank'" style="fontFamily:iconfont;color:red;">&#xe66e;</text>
                        <text v-else-if="ite === 'del'" style="fontFamily:iconfont;color:red;">&#xe629;</text>
                        <text v-else>{{ite}}</text>
                    </div>
                </div>
                //数字界面
                <div class="row" v-for="item in numList" v-if="state == 'num'">
                    <div class="button" v-for="ite in item" v-bind:style="{flex:ite==='blank'?6:1}" @click="btnClick(ite)">
                        <text v-if="ite === 'blank'" style="fontFamily:iconfont;color:red;">&#xe66e;</text>
                        <text v-else-if="ite === 'del'" style="fontFamily:iconfont;color:red;">&#xe629;</text>
                        <text v-else>{{ite}}</text>
                    </div>
                </div>
            </div>
        </wxc-popup>
        
    </div>
</template>
<style>
.placeholder{color:#999999;font-size:36px;}
.title{flex-direction:row;justify-content:space-between;align-items:center;padding:10px;border-bottom-width:1px;}
.btn{height:70px;flex:1;align-items:center;justify-content:center;}
.btn2{height:70px;flex:4;align-items:center;justify-content:center;}
.content{padding-top:10px;padding-left:10px;}
.row{flex-direction:row;}
.button{height:65px;border-width:1px;flex:1;justify-content:center;align-items:center;margin-right:10px;margin-bottom:10px;padding-top:10px;padding-bottom:10px;}
.button:active{background-color: #999999;}
.image{width:48px;height:32px;}
.imagearea{width:48px;height:32px;position:absolute;top:9px;right:0;}
</style>
  
<script>
import common from '../common/common';
import { WxcPopup } from 'weex-ui';
var domModule = weex.requireModule('dom');
export default {
    props: {
        inputstyle:Object,
        textstyle:Object,
        type:{
            type: Boolean,
            default: true
          },
          placeholder:{
              type: String,
            default: '请输入密码'
          }
    },
    components: {WxcPopup},
    data: function(){
        return {
            input:'',               //实际输入值
            passwordInput:'',       //实际输入值转为*
            char: ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p','a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v', 'b', 'n', 'm'],
            charInit: [],
            symbolList: [
                ['&','"',';','^',',','|','$','*',':','\''],
                ['?','{','[','~','#','}','.',']','\\','!'],
                ['(','%','-','_','+','/',')','=','<','`'],
                ['>','@','blank','del']
            ],
            num:['1','2','3','4','5','6','7','8','9','0'],
            numInit:[],
            charRandom:'0',         //字母随机标记
            numRandom:'0',          //数字随机标记
            state:'abc',
            charState:'lower',
            isBottomShow:false,
            popupHeight:'400',
            cursor:'',
            interval:'',
            blank:true,
            imageUrl:''
        }
    },
    created: function () {
        var self = this;
        //字母初始值深拷贝
        self.charInit = common.copy(self.char,'deep');
        //数字初始值深拷贝
        self.numInit = common.copy(self.num,'deep');
        //iconfont引入
        domModule.addRule('fontFace',{
            'fontFamily':'iconfont',
            //调用本地ttf
            'src':"url('local:///iconfont/iconfont.ttf')" 
        });
        //可不可见图片,可改变自己的
        self.imageUrl=common.getImageUrl(self)+'login/bukejian.png';
    },
    methods:{
        //聚焦事件,控制光标闪烁
        onfocus(){
            var self = this;
            self.blank = false;
            this.interval = setInterval(function() {
                if(self.cursor == 'true'){
                    self.cursor = ''
                }else{
                    self.cursor = 'true'
                }
            },500);
            this.isBottomShow = true;
        },
        //关闭弹窗,取消闪烁
        popupMenu(){
            clearInterval(this.interval);
            this.cursor = '';
            this.isBottomShow = false;
            this.$emit('inputVal', { inputVal: this.input });
            if(!this.input){
                this.blank = true;
            }
        },
        //数组数据随机,恢复
        randomArr(arr, name, init){
            var self = this;
            if(Array.isArray(arr)){
                if(self[name] == '0'){
                    self[name] = '1';
                    return arr.sort(function() {
                        return (0.5-Math.random());
                    });
                }else{
                    self[name] = '0';
                    self.charState = 'lower';
                        return common.copy(self[init],'deep');
                }
            }else{
                return arr;
            }
        },
        //安全键盘点击事件
        randomAbc(){
            var self = this;
            if(this.state == 'abc'){
                this.char = this.randomArr(this.char, 'charRandom', 'charInit');
            }else if(this.state == 'num'){
                this.num = this.randomArr(this.num, 'numRandom', 'numInit');
            }    
        },
        //大小写转换
        lowerToUpper(){
            var self = this;
            var arr = [];
            for(var i=0;i<self.char.length;i++){
                if(self.char[i] >= 'a' && self.char[i] <= 'z'){
                    arr[i] = self.char[i].toUpperCase();
                    self.charState = 'upper';
                }else{
                    arr[i] = self.char[i].toLowerCase();
                    self.charState = 'lower';
                }
            }
            self.char = arr;
        },
        //字母、符号、数字切换事件
        btnClick(ite){
            if(ite == 'blank'){
                this.input += ' '
                this.passwordInput += '*'
            }else if(ite == 'del'){
                this.input = this.input.slice(0,this.input.length -1);
                this.passwordInput = this.passwordInput.slice(0,this.passwordInput.length -1)
            }else{
                this.input += ite;
                this.passwordInput += '*'
            }
        },
        changeState(state){
            this.state = state;
        },
        //密码可不可见切换
        lookPwd(){
            var self = this;
            this.isBottomShow=false;
            this.type=!(this.type);
            self.imageUrl=common.getImageUrl(self)+(self.type?'login/bukejian.png':'login/kejian.png');
        }
    },
    computed: {
        //字母页面动态数据
        charList () {
            var self = this;
            var arr = [];
            arr[0] = ['1','2','3','4','5','6','7','8','9','0',];
            arr[1] = self.char.slice(0,10);
            arr[2] = ['top'].concat(self.char.slice(10,19));
            arr[3] = ['blank'].concat(self.char.slice(19,26)).concat(['del']);
            return arr;
        },
        //数字页面动态数据
        numList(){
            var self = this;
            var arr = [];
            arr[0] = self.num.slice(0,3);
            arr[1] = self.num.slice(3,6);
            arr[2] = self.num.slice(6,9);
            arr[3] = ['.'].concat(self.num.slice(9,10)).concat(['del']);
            return arr;
        },
        //光标的样式同输入字体样式相同,颜色为蓝色
        mrTextstyle(){
            const {textstyle} =this;
            const mrBtnStyle = {
                  ...textstyle, color: "blue"
            };
            return mrBtnStyle;
        }
    }
}
</script>

公用方法:common/common.js 代码如下:

exports.bundleUrl = function (self) {
    var bundleUrl = self.$getConfig().bundleUrl;
    return bundleUrl;
};
//判断系统,安卓返回'android',ios返回'iOS',h5返回'web'
exports.androidOrIos = function (self) {
    return self.$getConfig().env.platform;
};
//获取图片完整路径前缀
exports.getImageUrl = function (self) {
    var androidOrIos = this.androidOrIos(self);
    var bundleUrl = this.bundleUrl(self);
    var isHttp = bundleUrl.indexOf('http://') >= 0;
    var imageUrl;
    if (isHttp) {
        var i = bundleUrl.indexOf('/dist/');
        if (androidOrIos == "android") {
           imageUrl = bundleUrl.slice(0, i) + '/images/'; 
        } else if (androidOrIos == "iOS") {
           imageUrl = bundleUrl.slice(0, i) + '/images.bundle/'; 
        }
    } else {
        if (androidOrIos == "android") {
            imageUrl = 'assets:';
        } else if (androidOrIos == "iOS") {
            var i = bundleUrl.indexOf('XDPT.app');
            //vue语法中读取图片资源放在.bundle文件中
            //不然会出现The requested URL was not found on this server.
            imageUrl = bundleUrl.slice(0, i + 8) + '/images.bundle/';
        }
    }
    return imageUrl;
}

//对象类型判断,下面深,浅拷贝用 
//深浅拷贝来源百度,太懒没自己写
exports.util = (function () {
    var class2type = {};
    ["Null", "Undefined", "Number", "Boolean", "String", "Object", "Function", "Array", "RegExp", "Date"].forEach(function (item) {
        class2type["[object " + item + "]"] = item.toLowerCase();
    })

    function isType(obj, type) {
        return getType(obj) === type;
    }

    function getType(obj) {
        return class2type[Object.prototype.toString.call(obj)] || "object";
    }

    return {
        isType: isType,
        getType: getType
    }
})();
//对象深,浅拷贝
exports.copy = function (obj, deep) {
    if (obj === null || typeof obj !== "object") {
        return obj;
    }
    var i, target = this.util.isType(obj, "array") ? [] : {}, value, valueType;
    for (i in obj) {
        value = obj[i];
        valueType = this.util.getType(value);
        if (deep && (valueType === "array" || valueType === "object")) {
            target[i] = this.copy(value);
        } else {
            target[i] = value;
        }
    }
    return target;
}

kalakalaxyz
123 声望6 粉丝