这个方案说实话不怎么样,只是能暂时解决问题,复用性比较差。 还是挺多人问这个源码的,所以今天重新去除了业务代码,理出了一个简单的demo, 源码如下
/*
* @Author: Xuuu
* @Last Modified by: Xuuu
* @Last Modified time: 2018-07-16 15:25:17
* @Function: 表格明细模板:由于复杂度特别高,和业务耦合性很高,无法抽象成通用组件,需根据业务更改此模板
*/
<template>
<div class='detailed-table'>
<el-form :rules="rules" :model="tableForm" ref='form' size="small">
<div class='btn-row'>
<el-button type='primary' size="mini" @click="addRow">添加一行</el-button>
<el-button size="mini" @click="deleteRow">删除</el-button>
</div>
<el-table border :data="tableData" show-summary @selection-change="handleSelectionChange" ref="table" :summary-method="getSummaries">
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
prop="leaveDate"
label="出发日期"
min-width="160px"
>
<template slot-scope="scope">
<el-form-item :prop='"leaveDate" + scope.row.id' :rules="rules.leaveDate" class="form-item__margin">
<el-date-picker
v-model="tableForm['leaveDate' + scope.row.id]"
@input="changeValue('leaveDate', scope.$index, scope.row.id)"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择日期"
>
</el-date-picker>
</el-form-item>
</template>
</el-table-column>
<el-table-column
prop="returnDate"
label="到达日期"
min-width="160px"
>
<template slot-scope="scope">
<el-form-item :prop='"returnDate" + scope.row.id' :rules="rules.returnDate" class="form-item__margin">
<el-date-picker
v-model="tableForm['returnDate' + scope.row.id]"
@input="changeValue('returnDate', scope.$index, scope.row.id)"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择日期">
</el-date-picker>
</el-form-item>
</template>
</el-table-column>
<el-table-column
prop="from"
label="出发地"
>
<template slot-scope="scope">
<el-form-item :prop='"from" + scope.row.id' :rules="rules.from" class="form-item__margin">
<el-input
v-model="tableForm['from' + scope.row.id]"
@input="changeValue('from', scope.$index, scope.row.id)"
>
</el-input>
</el-form-item>
</template>
</el-table-column>
<el-table-column
prop="to"
label="到达地"
>
<template slot-scope="scope">
<el-form-item :prop='"to" + scope.row.id' :rules="rules.to" class="form-item__margin">
<el-input
v-model="tableForm['to' + scope.row.id]"
@input="changeValue('to', scope.$index, scope.row.id)"
>
</el-input>
</el-form-item>
</template>
</el-table-column>
<el-table-column
prop="transport"
label="交通工具"
>
<template slot-scope="scope">
<el-form-item :prop='"transport" + scope.row.id' :rules="rules.transport" class="form-item__margin" ref='transport'>
<el-select
v-model="tableForm['transport' + scope.row.id]"
placeholder="请选择"
@input="changeValue('transport', scope.$index, scope.row.id)"
>
<el-option
v-for="item in vehicleList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column
prop="ticketCost"
label="机票"
>
<template slot-scope="scope">
<el-form-item :prop='"ticketCost" + scope.row.id' :rules="rules.ticketCost" class="form-item__margin">
<el-input
@input="changeValue('ticketCost', scope.$index, scope.row.id)"
v-model="tableForm['ticketCost' + scope.row.id]"
placeholder="">
</el-input>
</el-form-item>
</template>
</el-table-column>
<el-table-column
prop="transportCost"
label="交通费"
>
<template slot-scope="scope">
<el-form-item :prop='"transportCost" + scope.row.id' :rules="rules.transportCost" class="form-item__margin">
<el-input
@input="changeValue('transportCost', scope.$index, scope.row.id)"
v-model="tableForm['transportCost' + scope.row.id]"
placeholder="">
</el-input>
</el-form-item>
</template>
</el-table-column>
<el-table-column
prop="hotelCost"
label="住宿费"
>
<template slot-scope="scope">
<el-form-item :prop='"hotelCost" + scope.row.id' :rules="rules.hotelCost" class="form-item__margin">
<el-input
@input="changeValue('hotelCost', scope.$index, scope.row.id)"
v-model="tableForm['hotelCost' + scope.row.id]"
placeholder="">
</el-input>
</el-form-item>
</template>
</el-table-column>
</el-table>
<div class="total-row">
<div class="money-item">合计人民币(大写):<span class="money"> {{formatMoney(total)}}</span></div>
<div class="money-item">合计人民币(小写):<span class="money"> {{total}}</span></div>
</div>
<!-- <div class='result-row'><el-button @click="send" type="primary">发送</el-button></div> -->
</el-form>
<div>
<el-button type="primary" @click="check">提 交</el-button>
</div>
</div>
</template>
<script>
import moment from 'moment'
// 初始化表格数据结构
const initData = {
leaveDate: moment().format('YYYY-MM-DD'),
returnDate: moment().format('YYYY-MM-DD'),
from: '',
to: '',
transport: '',
ticketCost: '',
transportCost: '',
hotelCost: ''
}
// 交通工具列表
const vehicleList = [
{label: '飞机', value: 'airport'},
{label: '火车', value: 'train'},
{label: '汽车', value: 'bus'},
{label: '自驾', value: 'drive'},
{label: '其他', value: 'other'}
]
export default {
// name: 'DetailedTable',
data () {
// 验证规则 --- 出发日期
const validateStart = (rule, value, callback) => {
const field = rule.field
const index = field.slice(-13)
const goalfiled = `returnDate${index}`
const goal = this.tableForm[goalfiled]
if (!goal) {
callback()
} else {
if (moment(value).isAfter(goal)) {
return callback(new Error('出发日期必须小于到达日期'))
} else {
callback()
}
}
}
// 验证规则 --- 到达日期
const validateArrive = (rule, value, callback) => {
const field = rule.field
const index = field.slice(-13)
const goalfiled = `leaveDate${index}`
const goal = this.tableForm[goalfiled]
if (!goal) {
callback()
} else {
if (!moment(value).isBefore(goal)) {
callback()
} else {
return callback(new Error('到达日期必须大于出发日期'))
}
}
}
// 验证规则 --- 是否是数字
const validateNumber = (rule, value, callback) => {
if (value && value !== '0' && !Number(value)) {
callback(new Error('必须是数字'))
}
}
return {
tableData: [], // 实际需要的表格数据
vehicleList: [...vehicleList], // 交通工具列表
initData: {...initData}, // 初始化表单数据
checkedRow: [], // 选中的行
tableForm: {}, // 用于验证字段合法性
total: null, // 合计费用字段
rules: { // 验证规则
leaveDate: [
{required: true, message: '不能为空', trigger: 'blur'},
{validator: validateStart, trigger: ['blur', 'change']}
],
returnDate: [
{required: true, message: '不能为空', trigger: 'blur'},
{validator: validateArrive, trigger: ['blur', 'change']}
],
from: [
{required: true, message: '不能为空', trigger: 'blur'}
],
to: [
{required: true, message: '不能为空', trigger: 'blur'}
],
transport: [
{required: true, message: '不能为空', trigger: 'change'}
],
ticketCost: [
{validator: validateNumber, message: '必须为数字', trigger: 'blur'}
],
transportCost: [
{validator: validateNumber, message: '必须为数字', trigger: 'blur'}
],
hotelCost: [
{validator: validateNumber, message: '必须为数字', trigger: 'blur'}
]
}
}
},
methods: {
debounce (fn, delay) {
let timerId;
return function (...args) {
if (timerId) {
clearTimeout(timerId);
}
timerId = setTimeout(() => {
fn(...args);
timerId = null;
}, delay);
}
},
// 发送回调
check () {
this.$refs.form.validate()
let result = {name: 'detailedTable', value: false}
const promise = new Promise((resolve) => {
this.$nextTick(() => {
const node = document.querySelector('.detailed-table')
const err = node.querySelectorAll('.el-form-item__error')
if (err.length) {
result.value = false
} else {
result.value = true
}
resolve(result)
})
})
promise.then(result => {
this.$emit('check', result)
})
},
// 格式化大写金额
formatMoney (num) {
let strOutput = ""
let strUnit = '仟佰拾亿仟佰拾万仟佰拾元角分'
num += "00"
var intPos = num.indexOf('.')
if (intPos >= 0)
num = num.substring(0, intPos) + num.substr(intPos + 1, 2)
strUnit = strUnit.substr(strUnit.length - num.length)
for (let i=0; i < num.length; i++)
strOutput += '零壹贰叁肆伍陆柒捌玖'.substr(num.substr(i,1),1) + strUnit.substr(i,1)
return strOutput.replace(/零角零分$/, '整').replace(/零[仟佰拾]/g, '零').replace(/零{2,}/g, '零').replace(/零([亿|万])/g, '$1').replace(/零+元/, '元').replace(/亿零{0,3}万/, '亿').replace(/^元/, "零元")
},
// 改变值去给 tableData 赋值
changeValue (val, index, id) {
this.debounce(this.$set(this.tableData[index], val, this.tableForm[val + id]), 300)
},
// 处理合计选项
getSummaries (param) {
const { columns, data } = param
const sums = []
columns.forEach((column, index) => {
if (index === 0) {
sums[index] = ''
return
}
if (index === 1) {
sums[index] = '总价'
return
}
if (index < 6) {
sums[index] = ''
return
}
const values = data.map(item => Number(item[column.property]))
if (!values.every(value => isNaN(value))) {
sums[index] = values.reduce((prev, curr) => {
const value = Number(curr)
if (!isNaN(value)) {
return prev + curr
} else {
return prev
}
}, 0)
sums[index] = sums[index].toFixed(2)
} else {
sums[index] = 'N/A'
}
})
const format = sums.map(item => Number(item))
const result = format.reduce((prev, curr) => {
if (!isNaN(curr)) {
return prev + curr
} else {
return prev
}
}, 0)
this.total = result.toFixed(2)
return sums
},
// 选择选项
handleSelectionChange (val) {
this.checkedRow = val
},
// 添加行
addRow () {
// TODO: 添加行时,数据会重置
const {tableData, initData} = this
const id = +moment()
const data = {...this.initData}
for (let [key, value] of Object.entries(this.initData)) {
this.$set(this.tableForm, `${key}${id}`, value)
}
data.id = id
this.tableData.push(data)
},
// 删除选中行
deleteRow () {
const {checkedRow, tableData} = this
if (!checkedRow.length) {
this.$message({
message: `没有选中项`,
type: 'warning'
})
return
}
const ids = []
checkedRow.forEach(item => {
ids.push(item.id)
})
const newTable = tableData.filter(item => {
return !ids.includes(item.id)
})
this.tableData = newTable
for (let [key, value] of Object.entries(this.tableForm)) {
for (let id of ids) {
if (key.includes(id)) {
delete this.tableForm[key]
}
}
}
},
// 初始化
init () {
const id = +moment()
const data = {...this.initData}
for (let [key, value] of Object.entries(this.initData)) {
this.$set(this.tableForm, `${key}${id}`, value)
}
data.id = id
this.tableData.push(data)
}
},
mounted () {
this.init()
},
}
</script>
<style lang='less'>
.detailed-table {
padding: 20px;
.btn-row {
margin-bottom: 20px;
text-align: right;
}
.result-row {
margin-top: 30px;
text-align: right;
.el-button {
padding: 12px 30px;
}
}
.total-row {
font-size: 14px;
padding: 6px;
display: flex;
justify-content: flex-end;
.money-item {
margin-left: 20px;
font-weight: 700;
.money {
color: #f05050;
}
}
}
.el-date-editor.el-input, .el-date-editor.el-input__inner {
width: auto;
}
.form-item__margin {
margin: 18px 0;
}
.el-table td {
padding: 0;
}
.el-table__footer-wrapper {
td, th {
padding: 12px 0;
color: #f05050;
}
}
}
</style>