Skip to main content

UI 库

Ant Design of Vue

表单

校验时隐藏 label 前面的红色星号 :hide-required-mark="true"

表单校验

<template>
<a-form :form="form">
<a-form-item label="姓名">
<a-input size="large" placeholder="联系人姓名" v-decorator="['username', personalRegister.username]"></a-input>
</a-form-item>
</a-form>
</template>
<script>
import { personalRegister } from '@/utils/validator';
</script>

在 validator.js 中封装校验规则

/**
* 手机号自定义校验
* @param {*} rule
* @param {*} value
* @param {*} callback
*/
function validatePhone(rule, value, callback) {
const phone = value.replace(/\s/g, '')
const regs = /^((13[0-9])|(17[0-1,6-8])|(15[^4,\\D])|(18[0-9]))\d{8}$/
if (!regs.test(phone)) {
callback('请输入正确的手机号')
}
callback()
}

const personalRegister = {
username: { rules: [{ required: true, message: '请输入姓名' }] },
password: {
rules: [
{ required: true, message: '请输入登录密码', whitespace: true },
{ min: 6, max: 16, message: '密码长度为6-16位' }
]
},
email: {
rules: [
{ type: 'email', message: '邮箱格式不正确!' },
{ required: true, message: '请输入邮箱地址!' }
]
}
phone: { rules: [{ required: true, message: '请输入手机号' }, { validator: validatePhone }] },
authCode: { rules: [{ required: true, message: '请输入验证码' }] },
residence: { rules: [{ type: 'array', required: true, message: '请选择所属地区!' }] }
}

module.exports = { personalRegister }

踩坑

1、表单赋值报错

场景:点击编辑按钮,弹出模态框,给模态框中的表单赋值

错误提示: Warning: You cannot set a form field before rendering a field associated with the value.

原因: 在表单还没渲染完成,就给表单赋值。使用了setFieldsValue方法,例如:this.form.setFieldsValue({ name: 'zgh', age: 23 })

解决:

1)、表单需要什么字段就传什么,可少传,不能多传

2)、使用nextTick()

this.$nextTick(() => {
this.form.setFieldsValue({ name: 'zgh', age: 23 });
});

如果还不行就加一个setTimeout()

this.$nextTick(() => {
setTimeout(() => {
this.form.setFieldsValue({ name: 'zgh', age: 23 });
});
});

2、表单回填数据

  • 下拉框类型,如果显示数字即 value 值,可将回填的字段值改为 string 类型,即toString()

气泡卡片

注册时填写密码,给出气泡卡片提示,根据输入位数更改进度条颜色和标题

<template>
<a-form :form="form" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }" @submit="handleSubmit">
<a-form-item label="登录密码">
<a-popover trigger="focus" placement="right">
<template slot="title">
<span :style="passwordLength < 8 ? 'color:#f00' : 'color:#0f0'">强度: {{ passPower }}</span>
<a-progress
:percent="percent"
size="small"
:showInfo="false"
status="active"
:strokeColor="passwordLength < 8 ? '#f00' : '#0f0'"
/>
</template>
<template slot="content">
<p>请至少输入6个字符。请不要使用容易</p>
<p>被猜到的密码。</p>
</template>
<a-input
size="large"
placeholder="6-16位密码,区分大小写"
@change="showTip"
type="password"
v-decorator="['password', validator]"
></a-input>
</a-popover>
</a-form-item>
</a-form>
</template>

<script>
export default {
data() {
return {
percent: 0,
passPower: '0',
passwordLength: 0,
form: this.$form.createForm(this, { name: 'register' }),
validator: {
rules: [
{ required: true, message: '请输入登录密码', whitespace: true },
{ min: 6, max: 16, message: '密码长度为6-16位' }
]
}
};
},
methods: {
showTip() {
this.$nextTick(() => {
let val = this.form.getFieldValue('password');
this.passwordLength = val.length;
this.percent = (val.length / 16) * 100;
if (val.length >= 6 && val.length < 8) {
this.passPower = '太短';
}
if (val.length >= 8) {
this.passPower = '安全';
}
});
},
handleSubmit(e) {
e.preventDefault();
this.form.validateFields((err, values) => {
if (!err) {
console.log(values);
}
});
}
}
};
</script>

<style scoped lang="less"></style>

表格

设置表格序号

实现表格序号自增

{
"title": "序号",
"dataIndex": "index",
"key": "index",
"width": "20%",
"customRender": (text, record, index) => `${index + 1}`
}

使用customRender函数来渲染序号的数据,在customRender函数中:

  • text 表示是序号一列默认显示的数据
  • record 表示是一行的所有数据
  • index 表示 Table 表格数据的下标,也就是数组的下标

因为数组的下标是从 0 开始的,所以需要 +1

columns 配置

{
title: '名称',
dataIndex: 'name', // 数据字段
key: 'name',
ellipsis: true, // 文字超出显示省略
width: '12%',
scopedSlots: { customRender: 'verifyStatus' }, // 插槽
sorter: (a, b) => a.name.length - b.name.length // 排序
}

表格行点击事件

<a-table :columns="colMain" :data-source="colMainData" :rowKey="row => row.id" :pagination="false" :customRow="clickRow"></a-table>

/**
* 点击表格的某一行
*/
clickRow(record, index) {
return {
on: {
click: () => {
console.log(record, index)
}
}
}
}

分割线

<a-divider orientation="left">请求信息</a-divider>
<a-card :bordered="false" class="showContent"></a-card>
/deep/ .ant-divider-horizontal {
margin: 16px 0 0 0;
font-weight: 600;
}
/deep/ .ant-divider-horizontal.ant-divider-with-text-left::before {
width: 0;
}

折叠面板

折叠面板内容在被隐藏时默认是不会渲染 DOM 结构,设置:forceRender='true'即可。 例如面板内容是表单时,不打开时是无法获取数据的

日期选择器

年度选择器

<template>
<a-date-picker
mode="year"
placeholder="请选择年份"
format="YYYY"
:open="isopen"
@openChange="openChange"
@panelChange="panelChange"
v-decorator="['date']"
/>
</template>

<script>
export default {
data() {
return {
isOpen: false
};
},
methods: {
// 年份选择器
openChange(e) {
this.isOpen = e ? true : false;
},
panelChange(e) {
this.form.setFieldsValue({ date: e });
this.isOpen = false;
}
}
};
</script>

设置范围

例如设置当天以前的 30 天可选,结束时间要大于开始时间

<template>
<div>
<a-date-picker
show-time
v-model="value"
:disabled-date="disabledDate"
format="YYYY-MM-DD HH:mm:ss"
@change="onChange"
/>

<div>
<a-date-picker
v-model="startValue"
:disabled-date="disabledStartDate"
show-time
format="YYYY-MM-DD HH:mm:ss"
placeholder="开始时间"
@openChange="handleStartOpenChange"
/>
<span style="padding: 0 7px;">-</span>
<a-date-picker
v-model="endValue"
:disabled-date="disabledEndDate"
show-time
format="YYYY-MM-DD HH:mm:ss"
placeholder="结束时间"
:open="endOpen"
@openChange="handleEndOpenChange"
/>
</div>
</div>
</template>

<script>
import moment from 'moment';

export default {
data() {
return {
moment,
value: null,
startValue: null,
endValue: null,
endOpen: false
};
},
methods: {
disabledDate(current) {
// 获取本月开始结束时间
// const monthStart = this.moment().startOf('month')
// const monthEnd = this.moment().endOf('month')
// 设置区间之外的日期不可选
return current > this.moment() || current < this.moment().subtract(30, 'days');
},

// 设置日期范围
disabledStartDate(current) {
return current > this.moment() || current < this.moment().subtract(30, 'days');
},
disabledEndDate(current) {
return current > this.moment() || current < this.startValue;
},
handleStartOpenChange(open) {
if (!open) {
this.endOpen = true;
}
},
handleEndOpenChange(open) {
this.endOpen = open;
}
}
};
</script>

描述

当描述信息太多需要独占一行时,可以单独使用描述组件。 若使用一个组件,可以使用一个空的<a-descriptions-item />占位,但是不会占满一行

<template>
<a-descriptions :column="1">
<a-descriptions-item label="泊位中文名称">{{ berthInfo.berthCnName }}</a-descriptions-item>
</a-descriptions>
<a-descriptions :column="2">
<a-descriptions-item label="泊位英文名称">{{ berthInfo.berthEnName }}</a-descriptions-item>
<a-descriptions-item label="泊位缩写">{{ berthInfo.berthSimpleName }}</a-descriptions-item>
<a-descriptions-item label="泊位长度(米)">{{ berthInfo.berthLength }}</a-descriptions-item>
<a-descriptions-item label="泊位结构">{{ berthInfo.berthStructure }}</a-descriptions-item>
</a-descriptions>
</template>

ElementUI

1、手动控制菜单显示隐藏

源码地址,源码里有 show 和 hide 方法,但是官方文档上没有写

hide-on-click 是否在点击菜单项后隐藏菜单,默认 true

<el-dropdown
ref="msgDrop"
trigger="click"
placement="bottom"
:hide-on-click="false"
@command="handleCommand"
></el-dropdown>

this.$refs.msgDrop.show();
this.$refs.msgDrop.hide();

有时可以参考一种思路:把自定义内容写在一个el-dropdown-item中,在el-select中也可使用这种方案

form 表单

@submit.native.prevent,阻止浏览器默认行为和事件冒泡,防止页面刷新

el-tooltip

文字超出显示省略号和提示,没超出时不显示

<el-tooltip effect="light" :content="item.name" placement="right" :disabled="isShowTooltip">
<span @mouseover="mouseOver($event)">{{ item.name }}</span>
</el-tooltip>
isShowTooltip: false

mouseOver(event) {
this.isShowTooltip = event.target.scrollWidth <= event.target.clientWidth
}

el-table

单元格样式错位

给 table 绑上ref="tableRef"属性,在获取数据后执行 doLayout 方法

this.$nextTick(() => {
this.$refs.tableRef.doLayout();
});

el-modal

弹窗无法关闭

场景:弹窗中有 tab 页签,点击弹窗的右上角关闭图标,页面卡死无反应。

解决方案:去掉 el-dialog 的:destroy-on-close="true"。如果保留这个属性,el-tab-pane 里不能有任何内容。

<el-dialog
title="项目总概况"
:visible.sync="proOpen"
width="90%"
append-to-body
:before-close="handleModalClose"
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<div class="wrap">
<el-tabs v-model="activeTab">
<el-tab-pane label="项目概况" name="home"></el-tab-pane>
</el-tabs>
</div>
</el-dialog>