表单校验参考了https://blog.csdn.net/iamlujingtao/article/details/105186117
效果如图所示,主要做了一些美化和解决一些看着难受的点
代码如下,有时间再介绍
<template>
<div class="app-container">
<div class="filter-container">
<el-input
v-model="listQuery.car"
placeholder="车辆id"
style="width: 200px"
class="filter-item"
clearable
@keyup.enter.native="handleFilter"
/>
<el-autocomplete
v-model="listQuery.project"
:fetch-suggestions="querySearchAsync"
placeholder="项目名称"
class="filter-item"
clearable
@clear="setBlur()"
@keyup.enter.native="handleFilter"
/>
<el-date-picker
v-model="listQuery.startTime"
type="datetime"
placeholder="选择开始日期时间"
class="filter-item"
:picker-options="pickerOptions"
value-format="yyyy-MM-dd HH:mm:ss"
/>
<el-date-picker
v-model="listQuery.endTime"
type="datetime"
placeholder="选择结束日期时间"
class="filter-item"
:picker-options="pickerOptions"
value-format="yyyy-MM-dd HH:mm:ss"
/>
<el-button-group>
<el-button
class="filter-item"
style="margin-left: 20px"
icon="el-icon-search"
plain
round
@click="handleFilter"
> 搜索
</el-button>
<el-button
class="filter-item"
style="margin-left: 10px"
icon="el-icon-circle-plus-outline"
plain
round
@click="add"
> 新增
</el-button>
</el-button-group>
</div>
<el-form ref="form" :model="form" :rules="rules">
<el-table v-loading="loading" :data="form.tableData" style="width: 100%" border fit>
<el-table-column label="车辆" min-width="15%" prop="car" align="center">
<template v-slot="{row,$index}">
<template v-if="row.edit">
<el-form-item :prop="'tableData.'+$index+'.car'" :rules="rules.car">
<el-input v-model="row.car" clearable size="small" />
</el-form-item>
</template>
<span v-else>{{ row.car }}</span>
</template>
</el-table-column>
<el-table-column label="项目" min-width="15%" prop="project" align="center">
<template v-slot="{row,$index}">
<template v-if="row.edit">
<el-form-item :prop="'tableData.'+$index+'.project'" :rules="rules.project">
<el-input v-model="row.project" clearable size="small" />
</el-form-item>
</template>
<span v-else>{{ row.project }}</span>
</template>
</el-table-column>
<el-table-column label="开始日期" min-width="20%" align="center">
<template v-slot="{row,$index}">
<template v-if="row.edit">
<el-form-item :prop="'tableData.'+$index+'.startTime'" :rules="rules.startTime">
<el-date-picker
v-model="row.startTime"
type="datetime"
placeholder="请选择日期"
align="center"
size="small"
:picker-options="pickerOptions"
value-format="yyyy-MM-dd HH:mm:ss"
/>
</el-form-item>
</template>
<template v-else>
<i class="el-icon-time" />
<span>{{ row.startTime }}</span>
</template>
</template>
</el-table-column>
<el-table-column label="结束日期" min-width="20%" align="center">
<template v-slot="{row,$index}">
<template v-if="row.edit">
<el-form-item :prop="'tableData.'+$index+'.endTime'" :rules="rules.endTime">
<el-date-picker
v-model="row.endTime"
type="datetime"
placeholder="请选择日期"
align="center"
size="small"
:picker-options="pickerOptions"
value-format="yyyy-MM-dd HH:mm:ss"
/>
</el-form-item>
</template>
<template v-else>
<i class="el-icon-time" />
<span>{{ row.endTime }}</span>
</template>
</template>
</el-table-column>
<el-table-column label="操作" min-width="20%" align="center">
<template slot-scope="{row,$index}">
<template v-if="!row.edit">
<!--加div是为了和下面else中的button不一样,可以避免点击按钮后不失焦-->
<div>
<el-button
id="edit-button"
size="small"
plain
round
icon="el-icon-edit"
type="primary"
@click="handleEdit(row)"
>
修改
</el-button>
<el-button
:id="'deleteButton'+$index"
size="small"
style="margin-left: 15px"
plain
round
icon="el-icon-delete"
type="danger"
@click="confirmDelete($index,row)"
>
删除
</el-button>
</div>
</template>
<template v-else>
<el-button
id="confirm-save-button"
size="small"
plain
round
icon="el-icon-circle-check"
type="success"
@click="confirmSave($index,row)"
>{{
'保存'
}}
</el-button>
<el-button
id="cancel-save-button"
style="margin-left: 15px"
size="small"
plain
round
icon="el-icon-circle-close"
type="info"
@click="cancelSave($index,row)"
>{{
'取消保存'
}}
</el-button>
</template>
</template>
</el-table-column>
</el-table>
</el-form>
<pagination
v-show="total > 0"
:total="total"
:page.sync="listQuery.page"
:limit.sync="listQuery.limit"
:page-sizes.sync="pageSizes"
@pagination="getTableData"
/>
</div>
</template>
<script>
import Pagination from '@/components/Pagination'
// import api8899 from '@/api/api8899'
export default {
components: { Pagination },
data() {
return {
// 输入建议
restaurants: [],
// 加载状态
loading: true,
list: null,
total: 0,
listQuery: {
page: 1,
limit: 12,
car: undefined,
project: undefined,
startTime: undefined,
endTime: undefined
},
pageSizes: [12, 24, 36],
form: {
tableData: []
},
rules: {
car: [{
type: 'string',
required: true,
trigger: 'change',
message: '车辆不能为空'
}],
project: [{
type: 'string',
required: true,
trigger: 'change',
message: '项目不能为空'
}],
startTime: [{
type: 'string',
required: true,
trigger: 'change',
message: '开始时间不能为空'
}]
},
// 时间选项
pickerOptions: {
shortcuts: [
// {
// text: '现在',
// onClick (picker) {
// picker.$emit('pick', new Date())
// }
// },
{
text: '昨天',
onClick(picker) {
const date = new Date()
date.setTime(date.getTime() - 3600 * 1000 * 24)
picker.$emit('pick', date)
}
}, {
text: '三天前',
onClick(picker) {
const date = new Date()
date.setTime(date.getTime() - 3600 * 1000 * 24 * 3)
picker.$emit('pick', date)
}
}, {
text: '七天前',
onClick(picker) {
const date = new Date()
date.setTime(date.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit('pick', date)
}
}]
}
}
},
mounted() {
this.getTableData()
this.inputSuggest()
},
methods: {
async getTableData() {
// const res = await api8899.search_car(this.listQuery)
// this.total = res.data.code === 200 ? res.data.data.count : 0
// this.form.tableData = res.data.data.data
this.form.tableData = [
{
car: 'Aaa012',
projectId: 'aaa',
project: '某个项目',
startTime: '2021-09-21 13:51:43',
endTime: '2021-09-26 13:51:43'
},
{
car: 'Aaa234',
projectId: 'aaa',
project: '某个项目',
startTime: '2021-08-26 13:51:43',
endTime: ''
},
{
car: 'Aaa345',
projectId: 'bbb',
project: '其他项目',
startTime: '2021-07-26 13:51:43',
endTime: '2021-07-26 13:51:43'
},
{
car: 'Aaa222',
projectId: 'bbb',
project: '其他项目',
startTime: '2021-09-09 13:51:43',
endTime: ''
}
]
this.form.tableData.map((v) => {
this.$set(v, 'edit', false)
// v.edit = false // 不能被v-if监听到
return v
})
this.loading = false
},
handleFilter() {
this.loading = true
this.getTableData()
this.loading = false
},
handleEdit(row) {
if (row.temp === undefined) {
row.temp = Object.assign({}, row) // 复制对象,为了取消修改后还原数据
} else if (row.temp.car === '') {
delete row.temp
row.temp = Object.assign({}, row) // 复制对象,为了取消修改后还原数据
}
row.edit = !row.edit
},
validateField(form, index) {
let result = true
for (const item of this.$refs[form].fields) {
if (parseInt(item.prop.split('.')[1]) === index) {
this.$refs.form.validateField(item.prop, (error) => {
if (error) {
result = false
}
})
}
if (!result) break
}
return result
},
async confirmSave(index, row) {
if (!this.validateField('form', index)) {
this.$message({
type: 'info',
message: '保存修改失败,请检查是否有未填字段',
center: true
})
return
}
// 判断值是否有改变,如果没有则不做下面的操作
const tmp = Object.assign({}, row)
const rowTmp = Object.assign({}, tmp.temp)
delete rowTmp.edit
delete tmp.temp
delete tmp.edit
if (JSON.stringify(rowTmp) === JSON.stringify(tmp)) {
row.edit = !row.edit
return
}
// 配置发送的请求参数
// const params = {
// 'car': row.car,
// 'project': row.project,
// 'startTime': row.startTime,
// 'endTime': row.endTime,
// 'oCar': row.temp.car,
// 'oProject': row.temp.project,
// 'oStartTime': row.temp.startTime,
// 'oEndTime': row.temp.endTime
// }
this.loading = true
// const response = await api8899.upsert_car(params)
const response = { 'data': { 'code': 200 }}
// const response = { 'data': { 'code': 23423,'message':'自定义提示' }}
this.loading = false
let type, message
if (response.data.code === 500) {
type = 'error'
message = '保存出错,请联系开发人员修复'
} else if (response.data.code === 200) {
type = 'success'
message = '保存成功'
} else {
type = 'info'
message = response.data.message
}
this.$message({
type: type,
message: message,
center: true
})
// info为保存数据重复等问题,直接返回
if (type === 'info') {
return
}
row.edit = !row.edit
delete row.temp
row.temp = Object.assign({}, row) // 复制对象,为了取消修改后还原数据
},
cancelSave(index, row) {
if (row.temp.car === '') {
this.$delete(this.form.tableData, index)
return
}
this.$set(this.form.tableData, index, row.temp) // 取消后还原数据到修改前状态
},
// 删除数据
async confirmDelete(index, row) {
// 让按钮失焦防止在提示时按多次enter键导致多次提交
document.getElementById('deleteButton' + index).blur()
// 设置弹窗样式
const h = this.$createElement
const confirmText1 = ['车辆 : ', '项目 : ', '开始时间 : ', '结束时间 : ']
const confirmText2 = [row.car, row.project, row.startTime, row.endTime]
const newData = []
for (const i in confirmText1) {
newData.push(h('span', null, confirmText1[i]))
newData.push(h('i', { style: 'color: teal' }, confirmText2[i]))
newData.push(h('div', null, null))
}
const confirm = await this.$msgbox({
title: '确定删除该条数据吗',
message: h('p', null, newData),
showCancelButton: true,
confirmButtonText: '确定',
cancelButtonText: '取消'
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除',
center: true
})
})
if (confirm === 'confirm') {
this.loading = true
// const response = await api8899.delete_car({ 'car': row.car, 'project': row.project, 'startTime': row.startTime })
const response = { 'data': { 'code': 200 }}
const type = response.data.code === 200 ? 'success' : 'error'
const message = response.data.code === 200 ? '成功删除数据' : '删除数据失败,请联系开发人员修复'
this.$delete(this.form.tableData, index)
this.loading = false
this.$message({
type: type,
message: message,
center: true
})
}
},
// 新增时重新初始化一个插入第一行
add() {
const temp = {
car: '',
project: '',
projectId: '',
startTime: '',
endTime: '',
edit: true,
temp: {
car: '',
project: '',
startTime: '',
endTime: ''
}
}
this.form.tableData.unshift(temp)
},
// 获取项目输入框建议
async inputSuggest() {
// const tmp = await api8899.search_project()
const tmp = { data: { data: [{ value: '某个项目b' }, { value: '某个项目a' }] }}
this.restaurants = tmp.data.data
},
// 处理输入框建议,restaurants必须是包含value属性的对象 [ {value:'xxx'} , ...]
querySearchAsync(queryString, cb) {
const restaurants = this.restaurants
const results = queryString ? restaurants.filter(this.createStateFilter(queryString)) : restaurants
cb(results)
},
// 输入框建议过滤规则
createStateFilter(queryString) {
return (state) => {
return (state.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0)
}
},
// 清空输入框时取消聚焦 使用户重新点击来展示建议下拉框
setBlur() {
document.activeElement.blur()
}
}
}
</script>
<style scoped>
.filter-container {
text-align: left;
padding-bottom: 10px;
}
.filter-item {
display: inline-block;
vertical-align: middle;
margin-bottom: 10px;
}
.app-container {
padding: 20px;
}
/deep/ .el-form-item {
margin-bottom: 0
}
/deep/ .el-input__inner {
text-align: center;
}
/deep/ .el-button--text {
display: none;
}
</style>