【流莺书签】基础组件(Form,Input)

1,181 阅读5分钟

写在前面

项目地址

👉👉项目预览地址,可以直接设置为浏览器主页或者桌面快捷方式进行使用

源码地址

完全开源,大家可以随意研究,二次开发。当然还是十分欢迎大家点个Star⭐⭐⭐
👉👉源码链接(gitee)       👉👉源码链接(github)

简介

本文记录了流莺书签封装的部分基础组件,包括

Input

Form

由于本项目是为了练手,所以在某些组件中可能也添加了一些实际并没有用到的功能,接下来将逐个介绍这些组件的属性,方法,以及一些设计思路.

除了一些特殊的样式,文章不会大量贴出CSS代码,由于我SASS功底不是很好,CSS代码看起来也是比较臃肿😣😣😣,感兴趣的同学可以自行查看源码

目录结构

基本就是一个组件的.vue文件和一个对应的index.ts,index.ts存放一些基础数据,类型声明等

├── src 
     ├── assets      // 存放静态资源
     ├── baseComponents  // 基础组件
         └──Form    // 表单组件
         │    ├──Form.vue
              └──index.ts
         └── Input    // 输入框组件
              ├──Input.vue
              └──index.ts
         ......

Input

展示

先来看效果

16.gif

属性

  • rules 验证规则
  • width 宽度
  • padding 内边距
  • modelValue 输入框的绑定值

设计思路/亮点

🌝paddingwidth属性主要是为了样式,其实也可以在组件引用的时候通过类名去修改,至于哪种更好用就仁者见仁,智者见智了

🌝vue3中给组件绑定值使用的是modelValue,具体的用法请看代码或者移步vue3官网

🌝比较复杂的就是内容校验,我使用的是策略模式进行封装,易于扩展.验证的时候只需要调用handlerValidateInput函数,传入值和验证规则即可,返回一个boolen值,如果有多条规则,只需循环调用handlerValidateInput,并结合every方法,当有一个验证没有通过的时候即返回false,那么组件中就可以根据这个返回值去做一些事情了

🌝我这里是结合message组件,当验证不通过的时候就提示错误信息,也可以在组件中加一个标签结合v-show在输入框下方进行错误提示,就如同一些大型的组件库那样,但是我试了不是很好看,就没有采用这种方式

🌝由于vue3中的$on,$off等指令的移除,为了能结合Form组件进行整体验证,我使用了mitt这个插件,它的作用和$on,$off是一样的,只不过不内置在vue3中了.具体的使用方法请查看mitt.js官网,超级简单就不讲了.在Input组件初始化的时候触发一个方法,向Form组件添加验证函数,Form组件有一个对应的方法收集所有的验证函数,用于统一验证

🌝可以看到我这里是套了一层div的,所以为了让我在使用组件时绑定的属性(例如placeholder)不继承在这一层div上,使用v-bind="$attrs"inheritAttrs: false来让input继承属性

全部代码

//Input.vue
<template>
  <div class="wh-input-box" :style='{width:width,padding:padding}'>
    <input class="wh-input" v-model="value" @change='validateInput' v-bind='$attrs'>
  </div>
</template>

<script lang='ts'>
//此处省略文件引用,详情请查看源码
export default defineComponent({
  name: 'Wh-input',
  inheritAttrs: false, // 根元素不继承$attrs
  props: {
    rules: {
      type: Array as PropType<RulesProp>,
      default: () => [],
    },
    modelValue: String,
    width: {
      type: String,
      default: '100%',
    },
    padding: {
      type: String,
      default: '30px',
    },
  },
  emits: ['update:modelValue'],
  setup(props, { emit }) {
    // 定义一个数据类型 包括值 是否错误 错误信息
    const inputRef = reactive({
      // 使用计算属性的get,set方法来实现修改input值
      value: computed({
        get: () => props.modelValue || '',
        set: (val) => {
          emit('update:modelValue', val);
        },
      }),
      error: false,
      message: '',
    });
    // 验证函数
    const validateInput = () => {
      // 循环rules数组 返回结果  every若有一个没通过返回false  全通过返回true
      const isAllPass = props.rules?.every((rule) => {
        // 赋值错误信息
        inputRef.message = rule.message;
        // 执行验证函数
        return handlerValidateInput(inputRef.value, rule);
      });
      // 如果有没通过的验证,则激活error错误开关
      inputRef.error = !isAllPass;
      if (!isAllPass) {
        createMessage(inputRef.message);
      }
      return isAllPass;
    };
    // 初始化的时候执行form-item-created,向form组件添加验证函数
    onMounted(() => {
      emitter.emit('form-item-created', validateInput);
    });
    return {
      ...toRefs(inputRef),
      validateInput,
    };
  },
});
</script>
//此处省略部分CSS代码,详情请查看源码
//index.ts
// 单个验证规则
export type RuleType = 'required' | 'maxlength' | 'custom';
export interface RuleProp {
  type: RuleType;
  message: string;
  maxlength?: number;
  validator?: (value: string) => boolean;
}

// 验证规则组合
export type RulesProp = RuleProp[];

// 单个input的验证器
export const validateInput = {
  // 是否必填
  required: (value: string, rule?: RuleProp) => {
    return value.trim() !== '';
  },
  // 最大长度
  maxlength: (value: string, rule: RuleProp) => {
    if (rule.maxlength) {
      return value.length <= rule.maxlength;
    }
    return true;
  },
  // 自定义验证器
  custom: (value: string, rule: RuleProp) => {
    if (rule.validator) {
      return rule.validator(value);
    }
    return true;
  },
};

// 验证器处理程序
export const handlerValidateInput = (value: string, rule: RuleProp) => {
  return validateInput[rule.type](value, rule);
};

组件中使用

正常引入传参即可使用

 <wh-input :rules='rules' type="text" v-model="labelTitle" placeholder="请输入标签名称" padding='50px 30px' maxlength='10'></wh-input>

Form

设计思路/亮点

🌝通过mitt插件使用一个数组来接受所有Input组件的验证函数,就可以在表单提交的时候进行整体的验证了,因为每一个验证函数返回一个boolen值,所以结合every函数就很容易做到当某一条没有通过的时候进行错误提示

🌝因为流莺书签暂时用到的表单组件几乎都是Input,样式比较容易管理,所以就没有设计form-item,以后随着功能的扩展可能会考虑这样做

代码

//Form.vue
<template>
  <form class='valid-form-container'>
    <slot name='default'></slot>
  </form>
</template>

<script lang='ts'>
import { defineComponent, onUnmounted } from 'vue';
import { emitter, ValidateFunc } from 'base/Form/index';

export default defineComponent({
  name: 'ValidateForm',
  setup() {
    // 声明一个空数组  用来存放所有的验证函数
    let funcArr: ValidateFunc[] = [];
    // 验证函数
    const submitForm = () => {
      // 获取所有item的验证结果,有一个没有通过返回false 否则返回true
      const result = funcArr.every((func) => func());
      return result;
    };
    // 把item的验证函数添加到数组中
    const addValidater = (func: ValidateFunc | undefined) => {
      funcArr.push(func as ValidateFunc);
    };
    // 监听form-item-created事件,当有item触发的时候把验证函数添加到数组中
    emitter.on('form-item-created', addValidater);
    // 组件卸载的时候移除监听事件,数组重置为空
    onUnmounted(() => {
      emitter.off('form-item-created', addValidater);
      funcArr = [];
    });
    return {
      submitForm,
    };
  },
});
</script>
//index.ts
import mitt, { Emitter } from 'mitt';
export const emitter: Emitter = mitt();
export type ValidateFunc = () => boolean;

组件中使用

需要在组件中绑定ref属性,然后调用form.value.submitForm()来进行验证,这个ref属性一定要在return中返回,不然不会生效,这里也是栽过一个跟头

<wh-from ref='form'>
  <wh-input :rules='rules' type="text" v-model="labelTitle" placeholder="请输入标签名称" ></wh-input>
</wh-from>

<script lang='ts'>
//此处省略文件引用,详情请查看源码
export default defineComponent({
  components: {
    WhInput,
    WhFrom,
  },
  setup(props, { emit }) {
    const form = ref<any>(null);
    const submitAll = () => {
      const result = form.value.submitForm();
      if (result) {
        createMessage({ type: 'success', message: '验证通过!' });
      }
    };
    return {
      form,
      submitAll
    };
  },
});
</script>

写在最后

请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.

✅认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式

✅认为我部分代码过于老旧,可以提供新的API或最新语法

✅对于文章中部分内容不理解

✅解答我文章中一些疑问

✅认为某些交互,功能需要优化,发现BUG

✅想要添加新功能,对于整体的设计,外观有更好的建议

最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧

链接整合

🔊项目预览地址(GitHub Pages):👉👉alanhzw.github.io

🔊项目预览备用地址(自己的服务器):👉👉warbler.duwanyu.com

🔊源码地址(gitee):👉👉gitee.com/hzw_0174/Wa…

🔊源码地址(github):👉👉github.com/alanhzw/War…

🔊流莺书签-从零搭建一个Vite+Vue3+Ts项目:👉👉juejin.cn/post/695130…

🔊流莺书签-基础组件介绍(Form,Input):👉👉juejin.cn/post/696393…

🔊流莺书签-基础组件(Button,Overlay,Dialog,Message):👉👉juejin.cn/post/696394…

🔊流莺书签-业务组件介绍:👉👉暂无

🔊我的博客:👉👉www.duwanyu.com