前端项目框架搭建随笔---input组件的编写

4,295 阅读4分钟

接上篇文章,费心劳神好几天的项目框架终于可以用了。现在可以开始写页面了吧?

既然上司说,UI框架我们自己来写,那我们就自己写吧。


虽然答应的时候挺痛快。真到写的时候,首先就不知道从哪里开始下手了

那我们就一点点来。先从组件框架开始一点点做。


首先先排布一下UI框架目录。在于上司聊了许久后,最后决定用这种目录架构:


红色箭头代表业务组件(business components) 存放项目中业务类组件的地方。如头部导航,个人信息卡等

绿色箭头代表业务组件(framework components) 存放项目中基础框架组件的地方。如按钮,输入框,开关等

蓝色箭头是导出文件。统一导出所有组件方便调用(文章后面会讲到)


好的,那我们先从input开始


input组件的编写

我们先看看效果图:


大致就是这个样子。非常简洁的UI,功能也算是够用了。(这里给UI大哥的作品点个赞)

我把它拆分成了这个样子(如图) 每个颜色框内都是不同的slot(具名插槽)


大致代码就是这个样子的存在:

<template>
  <div class="input-wrapper">
    <div class="input-content">
      <div class="input__left">
        <slot name="left"></slot> //红色框插槽
      </div>
      <div class="input__center">
        <input type="text" title="">
        <div class="input__center__tools">
          <i class="iconfont icon-qingchu" v-show="inputValue" @click="clearInputValue"></i> 
         //清除value的地方
        </div>
      </div>
      <div class="input__right">
        <slot name="right"></slot> //input右侧的自定义区。可以放置“获取验证码”之类的操作
      </div>
    </div>
    <div class="input-tip">
      <slot name="tip"></slot> //下方提示区域插槽
    </div>
  </div>
</template>

css方面选用flex布局。字体/图标大小,元素间距使用rem布局 class命名使用bem方式

CSS:

<style scoped>
  .input__left .iconfont {
    font-size: 2rem;
  }

  .input-content {
    display: flex;
    flex-direction: row;
  }

  .input__left {
    padding-bottom: 0.4rem;
  }

  .input__left > span {
    font-size: 1.5rem;
  }

  .input__center {
    margin-left: .5rem;
    flex: 1;
    display: flex;
    flex-direction: row;
    padding-bottom: .3rem;
    border-bottom: 1px solid #E9E9E9;
  }

  .input__center > .input__center__tools > .iconfont {
    color: #141414;
    cursor: pointer;
    font-size: 2rem;
  }

  .input__center > input {
    text-indent: .3rem;
    flex: 1;
    border: 0;
    width: 100%;
    height: 100%;
    font-size: 1.3rem;
    color: #3b3b3b;
    outline: none;
  }

  .input__right {
    padding-left: .5rem;
    padding-bottom: .3rem;
    border-bottom: 1px solid #E9E9E9;
  }

  .icon-qingchu {
    color: #E9E9E9 !important;
  }

  .input-tip {
    margin-top: .3rem;
    margin-left: 2.45rem;
    font-size: 1rem;
  }

  .input-tip i {
    font-size: 1rem;
  }

  .input-tip--info {
    color: #BFBFBF;
  }

  .input-tip--success {
    color: #5CD18B;
  }

  .input-tip--warning {
    color: #FFC53D;
  }

  .input-tip--error {
    color: #FF7875;
  }
</style>


OK,这时候我们的UI画完了。还能输入文字...不错哦

这时候遇到了一个问题:之前我们直接v-model就可以双向数据绑定input的value。现在input在组件内包着。那我们如何在父组件绑定子组件内的input呢??

找了半天教程,找到了这样一个操作:

给组件添加 v-model 属性时,默认会把 value 作为组件的属性,然后把 'input' 值作为给组件绑定事件时的事件名

啊哈,这样就好说了。那我们可以这样去写:

<input :type="textType" title="" v-model="inputValue"
       @input="$emit('input', inputValue)">

export default{
  data() {
    return { 
     inputValue: "" //子组件内绑定一遍。等会要用
     }
   },
  }

外部调用:

<zb-input v-model="phoneLogin.phone.value">

这样就大功告成了。这样父组件调用方就可以绑定到输入框的值了




OK,接下来我们开始做“一键清空”功能

由于我们传出去的值,是走的子组件内的双向绑定的data。所以理论上我们只需要把绑定在data内的变量清空就行了

this.inputValue = '';

但是这样会有问题,子组件内已经清空,父组件仍然保留着值。

那我们就模仿上面的做法,再$emit一次~~~

clearInputValue() {
  this.inputValue = ''; //清空输入框的值
  this.$nextTick(function () { //在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOM
    this.$emit('input', this.inputValue); //执行传出操作
  });
},

这样功能就实现了。如下图( 清除按钮的颜色太浅。动图录制软件看不见~~~ 抱歉)



好的,那接下来实现一下密码“显示”“隐藏“的功能




这个功能也比较有意思。不只是把输入框的type换成password那么简单。还能还原之前传入的input type

首先我们先定义一个props:

inputType: { //输入框类型
  default: 'text'
}
canHide: {  //是否支持隐藏值
  required: false,
  default: false
},

这个props从父组件接收想要的input类型。如果没有就默认text

然后copy一份同义的变量到data里。保持以prop做参考,data负责内部更改~~~

textType: this.inputType //从props里接收初始值
isHideValue: false //现在是否隐藏输入框值

然后清除事件:

hideInputValue() {
  if (this.textType === this.inputType) { //如果props内和data内相等。说明是没隐藏状态
    this.textType = "password"; //让他变为password类型
    this.isHideValue = true; //控制隐藏按钮的类名。更换icon
  } else {
    this.textType = this.inputType; //替换为初始化的type
    this.isHideValue = false;
  }
}

按钮方面:

<i class="iconfont"
   :class="[{'icon-yanjing_yincang_o':!isHideValue},{'icon-yanjing_xianshi_o':isHideValue}]"
   @click="hideInputValue" v-show="canHide && inputValue"></i>


这样就大功告成啦!!


既然我们做input了,那就做到底呗~~

让他支持一下自定义规则验证


通过和上司商定,暂选了这几个规则支持:


1. lengthRange: Object 支持max,min最大最小值

2. regular: 标准正则表达式

3. required: Boolean 是否必填


数据格式大概是这样的:

regex: {
  required:false,
  lengthRange: {
    max: 11,
    min: 1
  },
  regular: /^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/
}

然后通过props传进去:

regexObject: { //校验对象
  required: false
}

:regexObject="regex"


然后准备工作做好了,开始准备校验工作了

校验顺序按照队列校验。分别各自返回校验结果。没有指定的放行

是否必填校验:

reg_required(value) {
  if (this.regexObject.required) { //如果有required这个字段
     return !!value //返回value是否有值
  } else {
    return null; //代表没填required这个字段
  }
},

Tips:" !! " 常常用来做类型判断,在第一步!(变量)之后再做逻辑取反运算。简单理解就是判断这个变量是否存在值。

等价 " value!=null&&typeof(value)!=undefined&&value!='' "

正则表达式校验:

reg_regular(value) {
  if (this.regexObject.regular) { //如果有regular这个字段
    return this.regexObject.regular.test(value); //返回校验的结果
  } else {
    return null; //代表没填regular这个字段
  }
},

长度校验:

reg_lengthRange(value) {
  if (this.regexObject.lengthRange) { //如果有lengthRange这个字段
    return value.length >= this.regexObject.lengthRange.min //如果value的长度大于等于预定最小值
      && value.length <= this.regexObject.lengthRange.max //如果value的长度小于等于预定最大值
  } else {
    return null; //代表没填lengthRange这个字段
  }
},


主入口方法

regex(value) {
  let val = value.toString(); //统一转成字符串。防止没有length属性
  let info = {}; //空对象
  info.value = val; //一块把输入框的值传出去
  if (this.regexObject) { //如果props传了校验对象
    info.required = this.reg_required(val);
    info.regular = this.reg_regular(val);
    info.lengthRange = this.reg_lengthRange(val);
  }
  return info;
},


最后在输入框失焦事件(blur)内调用了一下:

inputReg() {
  console.log(this.regex(this.inputValue));
},


控制台:



自己体会~~~

然后发现了个bug,虽然定义了最大输入范围,但超出只是提示不禁止输入


于是watch监听一下inputValue

inputValue(){
  if (this.regexObject && this.regexObject.lengthRange) {
    if (this.inputValue.length > this.regexObject.lengthRange.max) { //如果输入框长度大于了既定最大长度
      this.inputValue = this.inputValue.slice(0, this.regexObject.lengthRange.max);//从0开始截取。截到最大长度
      return false;
    }
  } else {
    return false;
  }
}

因为html5把maxlength属性去掉了.....所以只能字符串截取


这样一个入门级别的Input就做好了!!功能还算是比较实用


下一篇写Tab组件的实现方式。各位周末愉快~~~周日晚继续写