1. Ajax
1.1 Ajax 常用请求示例
$.ajax({
type: "post", // 请求方式,默认为 get。可以省略
url: "url", // 请求路径。不能省略
data: { "id": id }, // 请求数据。可以省略
async: true, // 是否为异步请求,默认为 true。可以省略
cache: false, // 是否缓存页面,设置为 false 不缓存。可以省略
dataType: 'json', // 设置返回数据格式。可以省略
success: function(data) {
// 请求成功执行的操作
console.log(data);
},
error: function(data) {
// 请求失败执行的操作
console.log(data);
}
});
综上,一个最简单的 Ajax 请求要求写上 请求路径,其他请求参数均可省略不写。
2. Vue + ElementUI
2.1 Vue 校验
<el-form :inline="true" ref="studentForm" :model="studentFormData" :rules="rules" style="display: flex;flex-wrap: wrap;">
<el-form-item class="my-line" label="姓名:" prop="name">
<el-input type="text" v-model="studentFormData.name" placeholder="请填写姓名" size="mini"></el-input>
</el-form-item>
<el-form-item class="my-line" label="联系电话:" prop="phoneNo">
<el-input type="text" v-model="studentFormData.phoneNo" placeholder="请填写联系方式" size="mini"></el-input>
</el-form-item>
<el-form-item class="my-line" label="是否已完成报到:" prop="isArrival">
<el-select v-model="studentFormData.isArrival" placeholder="请选择是否已完成报到" class="item" size="small" clearable filterable default-first-option>
<el-option v-for="item in isArrivalOptions" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item class="my-line" label="报道时间:" prop="arrivalTime" v-show="studentFormData.isArrival == '1'">
<el-date-picker size="mini" v-model="studentFormData.arrivalTime" type="datetime" format="yyyy-MM-dd" value-format="yyyy-MM-dd" placeholder="请选择报道时间" style="width: 180px;"></el-date-picker>
</el-form-item>
<el-form-item class="my-line" label="报到方式:" prop="arrivalType" v-show="studentFormData.isArrival == '1'">
<el-input type="text" v-model="studentFormData.arrivalType" placeholder="请填写报道方式" size="mini"></el-input>
</el-form-item>
</el-form>
<div style="display: flex;align-items: center;justify-content: center;">
<button type="button" class="btn btn-primary" @click="saveStudentInfo('studentForm')">
<span>保存</span>
</button>
</div>
data() {
// 特殊字符校验规则
function checkSpecialKey(str) {
var specialKey = "`~@$^&=¥……&\\";
if (str != null)
for (var i = 0; i < str.length; i++) {
if (specialKey.indexOf(str.substr(i, 1)) != -1) {
return false;
}
}
return true;
}
// 手机号码校验规则
function checkPhone(str) {
const reg = /^1[3|4|5|7|8|6|9][0-9]\d{8}$/;
if (str != "") {
if (reg.test(str)) {
return true;
} else {
return false;
}
} else {
return true;
}
}
const validateInput = (rule, value, callback) => {
if (!checkSpecialKey(value)) {
callback(new Error(rule.message));
} else {
callback();
}
}
const validatePhone = (rule, value, callback) => {
if (!value) {
callback();
} else {
let result = checkPhone(value);
if (result) {
callback();
} else {
callback(new Error(rule.message));
}
}
}
// v-show, v-if 校验
const validateSelect = (rule, value, callback) => {
if (rule.field === 'arrivalTime' && this.studentFormData.isArrival == '1' && !value) {
callback(new Error(rule.message))
} else if (rule.field === 'arrivalType' && this.studentFormData.isArrival == '1' && !value) {
callback(new Error(rule.message))
} else {
callback()
}
}
return {
rules: {
name: [{required: true, message: '请填写姓名', trigger: 'blur'}, {validator: validateInput, trigger: 'blur'}],
phoneNo: [{required: true, message: '请填写联系人电话', trigger: 'blur'}, {validator: validatePhone, trigger: 'blur'}],
isArrival: [{required: true, message: '请选择是否已完成报到', trigger: 'change'}, {validator: validateInput, trigger: 'change'}],
arrivalTime: [{required: true, validator: validateSelect, message:'请选择报道时间'}],
arrivalType: [{required: true, validator: validateSelect, message:'请选择报道方式'}],
},
}
}
methods: {
saveStudentInfo: function (formName) {
var that = this;
that.$refs[formName].validate(function (valid) {
if (valid) {
// 校验通过要做的操作
}
});
},
}
注意:
- 如果有多个
<el-form>
标签,而且多个 <el-form> 标签都要做校验,要求这多个 <el-form> 标签的 ref 属性值必须不同,否则会导致只有最后一个 <el-form> 标签校验生效。 - @click 点击事件中必须传入 ref 属性值,否则校验不生效(
@click="saveStudentInfo('studentForm')"
)。
2.2 隐藏 Upload 组件 picture-card 上传框
也就是隐藏下图圈起来的上传框
<style>
<!-- 隐藏上传框 -->
.el-icon-plus-hide .el-upload--picture-card {
display: none;
}
</style>
<el-upload
action="https://jsonplaceholder.typicode.com/posts/"
list-type="picture-card"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove"
class="el-icon-plus-hide">
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
<script>
export default {
data() {
return {
dialogImageUrl: '',
dialogVisible: false
};
},
methods: {
// 图片移除钩子函数
handleRemove(file, fileList) {
console.log(file, fileList);
},
// 图片预览钩子函数
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
}
}
}
</script>
2.3 数组或对象属性复制赋值
为了减少代码量的编写,可以通过数组或对象属性复制的方式给某一个变量赋值。
<script>
const
searchItems = {
sName: '',
class: '',
startTime: '',
endTime: '',
currentPage: 0,
pageSize: 0,
};
var vm = new Vue({
data: function () {
return {
searchItems: this.cpObjArr(searchItems), // 复制对象属性赋值
}
},
methods: {
cpObjArr(data) {
let cpData = '';
if (data) {
if (Array.isArray(data)) (cpData = [...data]);
else if (typeof data == 'object') cpData = {...data};
}
return cpData;
},
},
});
</script>
2.4 日期时间格式化
<script>
var vm = new Vue({
data: function () {
return {
now: '',
}
},
mounted() {
this.now = this.formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss');
},
methods: {
formatDate(time, fmt) {
time = new Date(time);
var splitTime = {
"y+": time.getFullYear().toString(), // 年
"M+": time.getMonth() + 1, //月份
"d+": time.getDate(), //日
"h+": time.getHours(), //小时
"m+": time.getMinutes(), //分
"s+": time.getSeconds(), //秒
"q+": Math.floor((time.getMonth() + 3) / 3), //季度
"S": time.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt))
fmt = fmt.replace(RegExp.$1, (time.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var key in splitTime) {
if (new RegExp("(" + key + ")").test(fmt))
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (splitTime[key]) : (("00" + splitTime[key]).substr(("" + splitTime[key]).length)));
}
return fmt;
},
},
});
</script>
2.5 多级表头及导出
导出指的是 前端导出,适用于不需要分页显示的表格。
<div class="table table-box">
<div style="margin:10px;">
<el-button type="primary" @click="tableToExcel">导出</el-button>
</div>
<!-- header-cell-style: 设置表头样式 -->
<el-table :data="tableData" stripe style="width: 50%"
:header-cell-style="{textAlign: 'center', background:'#00B050', color:'#000'}" :cell-style="{ textAlign: 'center' }">
<el-table-column prop="sno" label="学号"></el-table-column>
<el-table-column label="基本信息">
<el-table-column v-for="(item, index) in titleArr[0]" :prop="item[0]" :label="item[1]" :formatter="showNull"></el-table-column>
</el-table-column>
<el-table-column label="班级信息">
<el-table-column v-for="(item, index) in titleArr[1]" :prop="item[0]" :label="item[1]"></el-table-column>
</el-table-column>
</el-table>
</div>
const
// 表头对应字段
tPArr = [
[
'sName', 'height', 'weight',
'cNo', 'cName',
],
],
// 表头
titleArr = [
[
[
[tPArr[0][0], '姓名'],
[tPArr[0][1], '身高(CM)'],
[tPArr[0][2], '体重(KG)'],
],
[
[tPArr[0][3], '班级号'],
[tPArr[0][4], '班级名'],
],
],
];
const vm = new Vue({
el: '#index',
data: function () {
return {
titleArr: this.cpObjArr(titleArr[0]), // 初始化表头数据
tableData: [
{sno: '1', sName: '张三', height: 170, weight: 60, cNo: 201, cName: '201班'},
{sno: '2', sName: '李四', height: 168, weight: 65, cNo: 202, cName: '202班'},
{sno: '3', sName: '小红', height: 160, weight: null, cNo: 201, cName: '201班'},
],
}
},
methods: {
// 复制对象属性
cpObjArr (data) {
let cpData = '';
if (data) {
if (Array.isArray(data)) (cpData = [...data]);
else if (typeof data == 'object') cpData = {...data};
}
return cpData;
},
// 空数据显示 0
showNull(row, column) {
return row[column.property] ? row[column.property] : 0;
},
// 导出表格 (前端导出)
tableToExcel() {
setTimeout(() => {
var excelHeader = $(".table .el-table__header").html();
var excelContent = $(".table .el-table__body").html();
excelHeader = excelHeader.replaceAll('<th class="gutter" style="width: 0px; display: none;"></th>', '');
var excelFile = "<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:x='urn:schemas-microsoft-com:office:excel' xmlns='http://www.w3.org/TR/REC-html40'>";
excelFile += "<head><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name></x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head>";
excelFile += "<body><table width='10%' border='1'>";
excelFile += excelHeader + excelContent;
excelFile += "</table></body>";
excelFile += "</html>";
var link = "data:application/vnd.ms-excel;base64," + this.base64(excelFile);
var a = document.createElement("a");
a.download = `学生信息表.xls`; // 导出文件名
a.href = link;
a.click();
}, 500);
},
base64(content) {
return window.btoa(unescape(encodeURIComponent(content)));
},
},
});
2.6 重复调用一个接口,进行封装调用(如,获取数据字典)
const
codeObject = {
/**
typeCode: 数据字典编码
typeData: 封装下拉选项数据
value: 获取指定 value 的数据字典值
label: 获取指定 label 的数据字典值
callback: 请求数据字典后的回调函数(自定义)
*/
classCode: {typeCode: 'classCode', typeData: 'classOptions', value: '', label: '', callback: ''}, // 所属班级选项
isAdvClass: {typeCode: 'advClass', typeData: 'advClassOptions', value: '', label: '', callback: ''}, // 是否文明班选项
};
const vm = new Vue({
el: '#index',
data: function () {
return {
classOptions: [], // 数据项名称必须和 typeData 的属性值保持一致
advClassOptions: [], // 数据项名称必须和 typeData 的属性值保持一致
}
},
mounted() {
let _codeObject = this.cpObjArr(codeObject);
this.initDataDictionary(_codeObject); // 初始化数据字典
},
methods: {
// 复制对象属性
cpObjArr (data) {
let cpData = '';
if (data) {
if (Array.isArray(data)) (cpData = [...data]);
else if (typeof data == 'object') cpData = {...data};
}
return cpData;
},
// 加载数据字典
initDataDictionary(codeObject, typeCode) {
let _this = this;
// 需要优先加载的数据字典
let code = typeCode ? codeObject[typeCode] : '';
if (code) {
delete codeObject[typeCode];
_this[code.typeData] && _this[code.typeData].length <= 1 && _this.getDataDictionaryByTypeCode(code);
}
// 加载所有数据字典
for (let key in codeObject) {
_this[codeObject[key].typeData] && _this[codeObject[key].typeData].length <= 1 && _this.getDataDictionaryByTypeCode(codeObject[key]);
}
},
// 调用数据字典接口
getDataDictionaryByTypeCode(codeObject = {}) {
let _this = this;
// 如果没有指定 value 和 label,也就是没有指定获取特定的数据字典值,则把接口返回数据中的 typeName 属性作为 value 和 label
let value = codeObject.value ? codeObject.value : 'typeName', label = codeObject.label ? codeObject.label : 'typeName';
$.ajax({
url: 'xxx',
data: {typeCode: codeObject.typeCode},
success: (data) => {
let typeData = [];
for (let i = 0; i < data.length; i++) {
typeData.push({value: data[i][value], label: data[i][label]});
}
codeObject.typeData && _this.$set(_this, codeObject.typeData, typeData);
typeof codeObject.callback == 'function' && codeObject.callback();
},
error: () => {
_this.$message({message: '数据字典获取失败', type: 'warning'});
}
});
},
},
});
<el-form-item class="my-line" label="所属班级:" prop="class">
<el-select v-model="studentFormData.class" size="small" class="item" placeholder="请选择所属班级" clearable>
<el-option
v-for="(item, index) in classOptions"
:key="index"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item class="my-line" label="是否文明班:" prop="advClass">
<el-select v-model="studentFormData.advClass" size="small" class="item" placeholder="请选择是否文明班" clearable>
<el-option
v-for="(item, index) in advClassOptions"
:key="index"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
2.7 多个 Tab 页
实现效果如下图所示
<div id="index" v-cloak>
<el-tabs type="border-card" style="width: 12%">
<el-tab-pane label="学生信息">
<template>
<el-tabs type="border-card">
<el-tab-pane
v-for="(item, index) in tabInfo"
:key="item.index"
:label="item.tabName">
</el-tab-pane>
</el-tabs>
</template>
</el-tab-pane>
</el-tabs>
</div>
<script>
var vm = new Vue({
el: '#index',
data: function () {
return {
tabInfo: [
{tabName: '基本信息'},
{tabName: '班级信息'},
],
}
},
});
</script>
2.8 封装 el-table-column 或 el-form-item
<div id="index" v-cloak>
<el-table :data="tableData" stripe style="width: 50%"
:cell-style="{ textAlign: 'center' }">
<el-table-column
v-for="(item, index) in columnItems"
:key="index"
:type="item.type"
:prop="item.prop"
:width="item.width"
:label="item.label"
:align="item.align"
:formatter="item.formatter">
</el-table-column>
</el-table>
</div>
<script>
var vm = new Vue({
el: '#index',
data: function () {
return {
tableData: [
{sno: '1', sName: '张三', height: 170, weight: 60},
{sno: '2', sName: '李四', height: 168, weight: 65},
{sno: '3', sName: '小红', height: null, weight: null},
],
columnItems: [
{type: 'selection'},
{prop: 'sno', width: '50px', label: '学号', align: 'center'},
{prop: 'sName', width: '100px', label: '姓名', align: 'center'},
{
prop: 'height', width: '100px', label: '身高(CM)', align: 'center',
formatter: function (row, column) {
return row[column.property] ? row[column.property] : 0;
}
},
{
prop: 'weight', width: '100px', label: '体重(KG)', align: 'center',
formatter: function (row, column) {
return row[column.property] ? row[column.property] : 0;
}
},
],
}
},
});
</script>
<div id="index" v-cloak>
<el-form :inline="true" ref="studentForm" :model="studentFormData" style="display: flex;flex-wrap: wrap;">
<el-form-item class="my-line"
v-for="(item, index) in formItems"
:key="index"
:label="item.label"
:prop="item.prop">
<el-select
v-if="item.selecItems"
v-model="item.value"
size="small"
class="item"
clearable
placeholder="请选择">
<el-option
v-for="(option, key) in item.optionItems"
:key="key"
:label="option.label"
:value="option.value"
:disabled="option.disabled">
</el-option>
</el-select>
<el-input v-else type="text" v-model="item.value" size="mini" placeholder="请填写信息" :disabled="item.disabled"></el-input>
</el-form-item>
</el-form>
</div>
<script>
var vm = new Vue({
el: '#index',
data: function () {
return {
studentFormData: {sno: '1', sName: '张三', class: '202班'},
classOption: [],
formItems: [],
}
},
mounted() {
this.classOption.push({label: '201班', value: '201班'}, {label: '202班', value: '202班'});
this.formItems.push({
label: '学号',
prop: 'sno',
value: this.studentFormData.sno,
disabled: true,
}, {
label: '姓名',
prop: 'sName',
value: this.studentFormData.sName,
}, {
label: '所属班级',
prop: 'class',
value: this.studentFormData.class,
selecItems: true,
optionItems: this.classOption
}
);
},
});
</script>
2.9 时间日期选择器给定默认开始时间和结束时间
default-time
属性可以设置默认的开始时间和结束时间
<el-date-picker style="width: 250px" size="small" class="item" v-model="startAndEndTime" type="daterange" :default-time="['00:00:00', '23:59:59']" align="right" unlink-panels start-placeholder="入学开始日期" end-placeholder="入学结束日期"></el-date-picker>
2.10 封装查询条件
对于查询条件过多的接口,我们可以使用对象复制的方式进行查询条件的封装。
<style>
.tools {
margin: 15px 0;
border: 1px solid #ebedf2;
padding: 20px 20px 0 0;
}
.tools .searchBox {
width: 100%;
display: flex;
align-items: center;
flex-wrap: wrap;
}
.tools .searchBox .search-item {
display: flex;
align-items: center;
justify-content: space-between;
margin: 10px 10px;
}
.tools .searchBox .search-item .item {
flex: 1;
}
.btns-box {
width: 100%;
margin-left: 5px;
display: flex;
justify-content: flex-end;
padding-bottom: 10px;
}
</style>
<div id="index" v-cloak>
<d
iv class="tools">
<div class="searchBox">
<div class="search-item">
<span class="search-title"> 姓名:</span>
<el-input placeholder="请输入姓名" v-model="searchItems.sName" size="small" class="item" clearable>
<i slot="suffix"></i>
</el-input>
</div>
<div class="search-item">
<span class="search-title"> 班级:</span>
<el-select v-model="searchItems.class" size="small" class="item" placeholder="请选择所属班级" clearable>
<el-option
v-for="(item, index) in classOption"
:key="index"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<div class="search-item">
<span class="search-title"> 入学日期:</span>
<el-date-picker style="width: 250px" size="small" class="item" v-model="startAndEndTime"
type="daterange" :default-time="['00:00:00', '23:59:59']" align="right" unlink-panels
start-placeholder="入学开始日期" end-placeholder="入学结束日期"></el-date-picker>
</div>
<div class="btns-box">
<el-button type="primary" size="small" @click="searchTableData">查询</el-button>
<el-button type="warning" size="small" @click="clean">重置</el-button>
</div>
</div>
</div>
</div>
<script>
const
searchItems = { // 查询条件
sName: '',
class: '',
startTime: '',
endTime: '',
currentPage: 0,
pageSize: 0,
};
var vm = new Vue({
el: '#index',
data: function () {
return {
searchItems: this.cpObjArr(searchItems),
classOption: [{label: '201班', value: '201班'}, {label: '202班', value: '202班'}],
startAndEndTime: [],
currentPage: 1, // 当前页码
pageSize: 10, // 页面大小
}
},
methods: {
searchTableData() {
this.searchItems.currentPage = 1; // 重置当前页码
this.searchItems.pageSize = 1; // 重置当前页面大小
this.searchItems.startTime = this.startAndEndTime[0] && this.formatDate(this.startAndEndTime[0], 'yyyy-MM-dd hh:mm:ss');
this.searchItems.endTime = this.startAndEndTime[1] && this.formatDate(this.startAndEndTime[1], 'yyyy-MM-dd hh:mm:ss');
debugger
this.getTableData(this.cpObjArr(this.searchItems));
},
getTableData(data) {
$.ajax({
url: 'xxx',
data: data,
success: (data) => {
},
error: () => {
}
});
},
clean() {
// 使用空对象重置查询条件
this.searchItems = cpObjArr(searchItems);
this.startAndEndTime = [];
},
cpObjArr(data) {
let cpData = '';
if (data) {
if (Array.isArray(data)) (cpData = [...data]);
else if (typeof data == 'object') cpData = {...data};
}
return cpData;
},
// 格式化时间
formatDate(time, fmt) {
time = new Date(time);
var splitTime = {
"y+": time.getFullYear().toString(), // 年
"M+": time.getMonth() + 1, //月份
"d+": time.getDate(), //日
"h+": time.getHours(), //小时
"m+": time.getMinutes(), //分
"s+": time.getSeconds(), //秒
"q+": Math.floor((time.getMonth() + 3) / 3), //季度
"S": time.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt))
fmt = fmt.replace(RegExp.$1, (time.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var key in splitTime) {
if (new RegExp("(" + key + ")").test(fmt))
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (splitTime[key]) : (("00" + splitTime[key]).substr(("" + splitTime[key]).length)));
}
return fmt;
},
},
});
</script>
2.11 数组字符串转换为数组、数组转换为字符串并用逗号分隔
<script>
var vm = new Vue({
data: function () {
return {
class: '["201班","202班"]', // 数组字符串
classArr: [],
classStr: '',
}
},
mounted() {
// 数组字符串转换为数组
this.classArr = JSON.parse(this.class); // ['201班', '202班']
// 数组转换为字符串并用逗号分隔
this.classStr = this.classArr.join(","); // '201班,202班'
},
});
</script>
2.12 双向数据绑定的三种写法
<div id="index" v-cloak>
<div>
<!-- 写法一 -->
<span>{{tips}}</span>
<el-table border stripe :header-cell-style="{textAlign: 'center',fontSize: '12px'}"
:cell-style="{textAlign: 'center',fontSize: '12px'}">
<!-- 写法二 -->
<el-table-column :label=" '姓名' + info "></el-table-column>
<!-- 写法三 -->
<el-table-column :label="info"></el-table-column>
</el-table>
</div>
</div>
<script>
var vm = new Vue({
el: '#index',
data: function () {
return {
tips: '学生信息表',
info: '班级',
}
},
});
</script>
2.13 el-table-column 内容格式化
转载自 【vue】el-table格式化el-table-column内容(主要的三种方法)。
<el-table-column prop="cyxb" label="性别">
<template slot-scope="scope">
<span v-if="scope.row.cyxb == 0">男</span>
<span v-if="scope.row.cyxb == 1">女</span>
</template>
</el-table-column>
<el-table-column prop="xb1" label="成员性别1" width="120" :formatter="Formatter">
<script>
Formatter(row, column){
if(row.xb == 0){
return "男"
}else if(row.xb == 1){
return "女"
}
}
</script>
<el-table-column
v-for="column in cbdksTableColumns"
:prop="column.field"
:label="column.label"
sortable="custom"
:key="column.field"
min-width="200"
>
<template slot-scope="scope">
<div v-if="column.field == 'cyxb'">
<span v-html="xbFormatter(scope.row.cyxb, scope.column.property)"></span>
//将表格数据格式化后,再用 template + v-html 展示出来
</div>
//<div v-else-if="column.field == 'qqfs'">中间还可以加好多判断,从此针对某列的值进行格式化。
<div v-else>
{{ scope.row[scope.column.property] }}//千万不要忘啦!!!
</div>
</template>
</el-table-column>
<script>
//之前的代码取数据比较复杂,简化代码,便于理解。
xbFormatter(value, row) {
//性别
let cyxbvalue = value;
if (cyxbvalue == null || cyxbvalue == "" || cyxbvalue == undefined) {
return cyxbvalue;
} else {
let dycyxb = this.xbOptions.filter((item) => item.value === cyxbvalue);//filter过滤方法(看自己的情况、需求)
return dycyxb[0].label;//rerun的内容即为要在表格中显示的内容
}
},
</script>