【笔记】Vue Element+Node.js开发企业通用管理后台系统——Element-UI 入门


Element-UI 入门 | 「小慕读书」管理后台


一、简介

饿了么开源组件库
github 4w+ star
访问官网github

Element,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库

二、基本用法

  • 初始化项目
vue create element-test -d	// 默认创建方式,这步可能会出现vue-cli版本需要更新,或是网络问题
  • 运行一下看看效果
cd element-test
npm run serve
  • 安装
cnpm i element-ui -S
  • 使用 ElementUI 插件(main.js)
import ElementUI from 'element-ui'
Vue.use(ElementUI)
  • 引用样式(main.js)
import 'element-ui/lib/theme-chalk/index.css'
  • element-ui 案例
<template>
  <div id="app">
    <el-button @click="show">点我</el-button>
  </div>
</template>
<script>
export default {
  name: 'app',
  methods: {
    show() {
      this.$message.success('Toast from element-ui')
    }
  }
}
</script>

三、按需加载

  • 对项目进行打包:
npm run build
  • 发现 vendors 库高达 近800KB
-rw-r--r-- 1 onc-virn 197121  801539  4月 10 11:51 chunk-vendors.bf819743.js
  • 这是由于我们未使用按需加载,所以对 element-ui 进行全量打包的结果,按需加载的用法如下:
  • 安装 babel-plugin-component
cnpm i -D babel-plugin-component
  • 修改 babel.config.js:
module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}
  • 按需引入 Button 和 Message
import { Button, Message } from 'element-ui'

Vue.component(Button.name, Button)
Vue.prototype.$message = Message
  • 来代替如下内容
    import ElementUI from ‘element-ui’
    import ‘element-ui/lib/theme-chalk/index.css’
    Vue.use(ElementUI)

重新构建,vendors 体积减小到 113KB

-rw-r--r-- 1 onc-virn 197121 114301  4月 10 12:22 chunk-vendors.c4537f82.js

四、插件引用

  • 创建一个新项目
vue create element-test2 -d
  • 可以通过 element 插件快速集成 element-ui
vue add element
  • 安装过程中的选择如下:
? How do you want to import Element? Fully import
? Do you wish to overwrite Element's SCSS variables? No
? Choose the locale you want to load zh-CN
  • 运行完毕可见新增或修改如下:
  • 新增 src\plugins\element.js
import Vue from 'vue'
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.use(Element)
  • 在main.js中新增如下:
import './plugins/element.js'
  • App.vue新增如下:
<div>
	<p>
		If Element is successfully added to this project, you'll see an
		<code v-text="'<el-button>'"></code>
		below
	</p>
	<el-button>el-button</el-button>
</div>

package.json新增如下:

"dependencies": {
    "element-ui": "^2.4.5",
  },
  "devDependencies": {
    "vue-cli-plugin-element": "~1.0.1",
  },

然后就是yarn.lock中的一些改变,暂时不用理会。

五、表单基本用法

官方文档:https://element.eleme.cn/#/zh-CN/component/form

  • el-form 容器,通过 model 绑定数据(inline保证控件在一行)
  • el-form-item 容器,通过 label 绑定标签
  • 表单组件通过 v-model 绑定 model 中的数据
<template>
  <div id="app">
    <el-form inline :model="data">
      <el-form-item label="审批人">
        <el-input v-model="data.user" placeholder="审批人"></el-input>
      </el-form-item>
      <el-form-item label="活动区域">
        <el-select v-model="data.region" placeholder="活动区域">
          <el-option label="区域一" value="shanghai"></el-option>
          <el-option label="区域二" value="beijing"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="onSubmit">查询</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      data: {
        user: 'sam',
        region: '区域二'
      }
    }
  },
  methods: {
    onSubmit() {
      console.log(this.data)
    }
  }
}
</script>

六、表单校验基本用法

yiminghe / async-validator:验证表单异步

首先修改data部分:

扫描二维码关注公众号,回复: 10747885 查看本文章
data() {
  const userValidator = (rule, value, callback) => {
    if (value.length > 3) {
      callback()
    } else {
      callback(new Error('用户名长度必须大于3'))
    }
  }
  return {
    data: {
      user: '',
      region: ''
    },
    rules: {
      user: [
        { required: true, trigger: 'change', message: '用户名必须录入' },
        { validator: userValidator, trigger: 'change' }
      ]
    }
  }
}

然后绑定校验规则,使用:rules="rules"绑定到 el-form 或 el-form-item

<el-form inline :model="data" :rules="rules">

指定 el-form-item 的 prop 属性:prop="user"

<el-form-item label="审批人" prop="user">
  <el-input v-model="data.user" placeholder="审批人" clearable></el-input>
</el-form-item>

rules

  • required:必填项
  • trigger:触发条件(例如:change)
  • message:错误提示内容
  • validator:绑定其他详细验证规则
    在表单提交时也进行验证,需要在表单后增加ref="form"
<el-form inline :model="data" :rules="rules" ref="form">

然后修改提交表单的方法为如下:

onSubmit() {
	console.log(this.data)
	this.$refs.form.validate((isValid,errors)=>{
		console.log(isValid,errors)
	})
}

表单校验相关属性

  • hide-required-asterisk:隐藏必录标识
  • inline-message:验证消息是否在一行显示

七、表单校验高级用法

用法一:动态改变校验规则

使rules 只包含一个校验规则

{
  rules: {
      user: [
        { required: true, trigger: 'change', message: '用户名必须录入' },
      ]
  }
}

新增按钮触发动态添加 rules

<el-button type="primary" @click="addRule">添加校验规则</el-button>
addRule() {
    const userValidator = (rule, value, callback) => {
      if (value.length > 3) {
        this.inputError = ''
        this.inputValidateStatus = ''
        callback()
      } else {
        callback(new Error('用户名长度必须大于3'))
      }
    }
    const newRule = [
      ...this.rules.user,
      { validator: userValidator, trigger: 'change' }
    ]
    this.rules = Object.assign({}, this.rules, { user: newRule })	// 会将新旧两个rule合并为一个
}

去掉之前的校验规则userValidator

表单中添加属性:validate-on-rule-change就会在新加规则后立即校验(默认为true,不添加也可)

  • 使用:this.rules = Object.assign({}, this.rules, { user: newRule })
  • 不使用:this.rules.user.push(newRule)

新增新规则时必须重新生成一个新的rules对象,因为watch只能监听到rules,而监听不到它内部的变化

<el-form inline :model="data" :rules="rules" ref="form" validate-on-rule-change>

用法二:手动控制校验状态

  • validate-status:验证状态,枚举值,共四种:
    • success:验证成功
    • error:验证失败
    • validating:验证中
    • (空):未验证
  • error:自定义错误提示

data()中新增errorstatus元素:

return {
	error: '',
	status: '',
	data: {
	  user: '',
	  region: ''
	},
	...

设置 el-form-item 属性

<el-form-item
  label="用户名"
  prop="user"
  :error="error"
  :validate-status="status"
>

el-form标签添加status-icon属性

<el-form inline :model="data" :rules="rules" ref="form" validate-on-rule-change status-icon>

新增按钮:

<el-button type="success" @click="showSuccess">校验成功</el-button>
<el-button type="danger" @click="showError">校验失败</el-button>
<el-button type="warning" @click="showValidating">校验中</el-button>

methods中新增自定义 statuserror的方法

showError() {
  this.status = 'error'
  this.error = '用户名输入有误'
},
showSuccess() {
  this.status = 'success'
  this.error = ''
},
showValidating() {
  this.status = 'validating'
  this.error = ''
}

在这里插入图片描述

八、表单属性解析

  • label-position:标签位置,枚举值,left 和 top
  • label-width:标签宽度
  • label-suffix:标签后缀
  • inline:行内表单
  • disabled: 设置整个 form 中的表单组件全部 disabled,优先级低于表单组件自身的

disabled

node_modules\element-ui\packages\form\src下找到form.vue可以看到,里面并没有对disabled进行处理,而是使用provide() {return {elForm: this};}将自身传递给了后代,间接控制,让它们进行处理。
比如:下面el-input中,先判断自身是否含有disabled属性,再判断祖先是否含有。

/* el-input 源码 */
inputDisabled() {
  return this.disabled || (this.elForm || {}).disabled;
}

size:设置表单组件尺寸

size属性也是通过provide()inject:[]实现的

/* el-input 源码 */
inputSize() {
  return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
},
_elFormItemSize() {
  return (this.elFormItem || {}).elFormItemSize;
}
/* el-form-item 源码 */
elFormItemSize() {
  return this.size || this._formSize;
},
_formSize() {
  return this.elForm.size;
}

可以说,el-form-item在这一体系统起到承上启下的作用,而provide()inject:[]功能的强大也是有目共睹的

/* el-form 源码 */
provide() {
 return {
    elForm: this
  };
},
/* el-form-item 源码 */
provide() {
  return {
    elFormItem: this
  };
},
inject: ['elForm'],
/* el-input 源码 */
inject: {
  elForm: {
    default: ''
  },
  elFormItem: {
    default: ''
  }
},

九、分析element-ui源码

1.入口文件分析

node_modules\element-ui\src\index.jselement-ui的入口文件

  • Vue.use(Element)调用element-ui入口文件的install方法
  • install方法中,通过如下方法加载全部组件:
components.forEach(component => {
    Vue.component(component.name, component);
});
  • 而所有的组件都存在于components数组中

因此按需加载使用Vue.component(Button.name, Button)

2.form.vue

  • 容器功能是通过插槽来实现的,它的自带属性只有两个labelPositionel-form--inline
<template>
  <form class="el-form" :class="[
    labelPosition ? 'el-form--label-' + labelPosition : '',
    { 'el-form--inline': inline }
  ]">
    <slot></slot>
  </form>
</template>
  • form组件的入口文件是上级目录的index.js,很简单,完成如下操作:
ElForm.install = function(Vue) {
	Vue.component(ElForm.name, ElForm);
};
  • 在props下面定义了一系列属性;
  • watch主要监听了rules的变化:
watch: {
  rules() {
    // remove then add event listeners on form-item after form rules change
    this.fields.forEach(field => {
      field.removeValidateEvents();
      field.addValidateEvents();
    });

    if (this.validateOnRuleChange) {
      this.validate(() => {});
    }
  }
},
  • computed主要计算label的自动宽度:
computed: {
  autoLabelWidth() {
    if (!this.potentialLabelWidthArr.length) return 0;
    const max = Math.max(...this.potentialLabelWidthArr);
    return max ? `${max}px` : '';
  }
},
  • methods(这里就可以体现出关联组件,解耦代码的思想,每个组件仅仅做自己事,form只做了统一的管理与调度,真正干活的还是小弟):
// 重置所有字段
resetFields() {},
// 清除验证,如果不传prop就清除所有
clearValidate(props = []) {},
// 验证方法
validate(callback) {},
// 指定字段进行验证
validateField(props, cb) {},
// 获取label的下标
getLabelWidthIndex(width) {},
// 重置labelwidth
registerLabelWidth(val, oldVal) {},
// 移除此宽度
deregisterLabelWidth(val) {}
  • 验证方法
// 对整个表单进行验证
validate(callback) {
  // 没有表单数据 抛警告跳出
  if (!this.model) {
    console.warn('[Element Warn][Form]model is required for validate to work!');
    return;
  }

  let promise;
  // 没有callback并且浏览器支持Promise return promise
  if (typeof callback !== 'function' && window.Promise) {
    promise = new window.Promise((resolve, reject) => {
      callback = function(valid) {
        valid ? resolve(valid) : reject(valid);
      };
    });
  }
    
  let valid = true;
  let count = 0;
  // 如果需要验证的fields为空,调用验证时立刻返回callback
  if (this.fields.length === 0 && callback) {
    callback(true);
  }
  let invalidFields = {};
  // 遍历所有实例,一个个验证
  this.fields.forEach(field => {
    // 这里的validate是form-item的方法,实际操作在form-item中执行
    field.validate('', (message, field) => {
      // 如果有返回信息, 则说明验证失败
      if (message) {
        valid = false;
      }
      // 将错误对象复制到invalidFields
      invalidFields = objectAssign({}, invalidFields, field);
      // 调callback
      if (typeof callback === 'function' && ++count === this.fields.length) {
        callback(valid, invalidFields);
      }
    });
  });
  if (promise) {
    return promise;
  }
}

3.form-item.vue

  • 模板结构:
    • 可看出验证提示效果可以自定义
<div class="el-form-item" :class="[{
    'el-form-item--feedback': elForm && elForm.statusIcon,
    'is-error': validateState === 'error',
    'is-validating': validateState === 'validating',
    'is-success': validateState === 'success',
    'is-required': isRequired || required
  },
  sizeClass ? 'el-form-item--' + sizeClass : ''
]">
  <!-- 表单域标签文本 -->
  <label :for="labelFor" class="el-form-item__label" :style="labelStyle" v-if="label || $slots.label">
    <slot name="label">{{label + form.labelSuffix}}</slot>
  </label>
  <div class="el-form-item__content" :style="contentStyle">
    <!-- 插槽接收表单验证的元素,input框单选框多选框之类的 -->
    <slot></slot>
    <!-- 验证不通过时的message -->
    <transition name="el-zoom-in-top">
      <div
        v-if="validateState === 'error' && showMessage && form.showMessage"
        class="el-form-item__error"
        :class="{
          'el-form-item__error--inline': typeof inlineMessage === 'boolean'
            ? inlineMessage
            : (elForm && elForm.inlineMessage || false)
        }"
      >
        {{validateMessage}}
      </div>
    </transition>
  </div>
</div>
  • 监听验证状态和信息
watch: {
  error: {
    immediate: true,	// 立即显示
    handler(value) {
      // 获取验证信息
      this.validateMessage = value;
      // 判断验证状态
      this.validateState = value ? 'error' : '';
    }
  },
  // 验证状态
  validateStatus(value) {
    this.validateState = value;
  }
},
  • 验证方法
validate(trigger, callback = noop) {
  this.validateDisabled = false;
  // 符合规则的trigger
  const rules = this.getFilteredRule(trigger);
  // 没有规则也不是必填 返回true
  if ((!rules || rules.length === 0) && this.required === undefined) {
    // 执行回调
    callback();
    return true;
  }
  // 状态改为验证中
  this.validateState = 'validating';

  const descriptor = {};
  // 为了匹配AsyncValidator插件所需要的格式,需要做规则数据做一些操作
  if (rules && rules.length > 0) {
    rules.forEach(rule => {
      delete rule.trigger;
    });
  }
  // AsyncValidator需要的验证规则
  descriptor[this.prop] = rules;
  // 验证规则AsyncValidator实例对象(element校验使用的第三方库)
  const validator = new AsyncValidator(descriptor);
  const model = {};
  // AsyncValidator需要的验证数据
  model[this.prop] = this.fieldValue;
  // 验证
  validator.validate(model, { firstFields: true }, (errors, invalidFields) => {
    // 验证后的状态(结果)
    this.validateState = !errors ? 'success' : 'error';
    // 验证提示
    this.validateMessage = errors ? errors[0].message : '';
    // 执行回调
    callback(this.validateMessage, invalidFields);
    // form组件发布validate事件
    this.elForm && this.elForm.$emit('validate', this.prop, !errors);
  });
}

拓展:


  • 案例全部代码(App.vue):
<template>
  <div id="app">
    <el-form
        :model="data"
        style="width: 500px"
        label-position="left"
        label-width="100px"
        label-suffix=""
        :inline="false"
        :rules="rules"
        :disabled="false"
        status-icon
        validate-on-rule-change
        hide-required-asterisk
        :inline-message="false"
    >
      <el-form-item
          label="用户名"
          prop="user"
          :error="error"
          :validate-status="status"
      >
        <el-input v-model="data.user" placeholder="用户名" clearable></el-input>
      </el-form-item>
      <el-form-item label="活动区域" prop="region">
        <el-select v-model="data.region" placeholder="活动区域" style="width:100%">
          <el-option label="区域一" value="shanghai"></el-option>
          <el-option label="区域二" value="beijing"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="onSubmit">查询</el-button>
        <el-button type="primary" @click="addRule">添加校验规则</el-button>
        <el-button @click="showError">错误状态</el-button>
        <el-button @click="showSuccess">正确状态</el-button>
        <el-button @click="showValidating">验证状态</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
  export default {
    name: 'app',
    data() {
      return {
        data: {
          user: 'sam',
          region: '区域二'
        },
        error: '',
        status: '',
        rules: {
          user: [
            { required: true, trigger: 'change', message: '用户名必须录入' }
          ]
        }
      }
    },
    methods: {
      /* eslint-disable */
      onSubmit() {
        console.log(this.data)
      },
      addRule() {
        const userValidator = (rule, value, callback) => {
          if (value.length > 3) {
            this.inputError = ''
            this.inputValidateStatus = ''
            callback()
          } else {
            callback(new Error('用户名长度必须大于3'))
          }
        }
        const newRule = [
          ...this.rules.user,
          { validator: userValidator, trigger: 'change' }
        ]
        this.rules = Object.assign({}, this.rules, { user: newRule })
      },
      showError() {
        this.status = 'error'
        this.error = '用户名输入有误'
      },
      showSuccess() {
        this.status = 'success'
        this.error = ''
      },
      showValidating() {
        this.status = 'validating'
        this.error = ''
      }
    }
  }
</script>
发布了28 篇原创文章 · 获赞 53 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_32682301/article/details/105428095