vue+antd实现可编辑表格

1、新建一个custom_table的的组件

<template>
	<a-card :title="title">
		<template #extra>
			<div class="submitBtn">
				<a v-if="!edit" @click="editHandle">编辑</a>
				<div v-else>
					<a-button @click="saveHandle" class="mr-2" type="primary">保存</a-button>
					<a-button @click="edit = false">取消</a-button>
				</div>
			</div>
		</template>

		<a-table :columns="columns" :data-source="edit ? editData : data" bordered :pagination="false"
			:scroll="{y: 400}">
			<template #chargeName="{ text,record, index  }">
                <a-input @change="getAmountHandle" v-if="edit" v-model:value="record.chargeName" placeholder="请输入收费名称" />
				<div v-else>
					{{ text || ' ' }}
				</div>
			</template>
			<template #currency="{ text,record,index }">
                <a-input @change="getAmountHandle" v-if="edit" v-model:value="record.prohibitEditing" placeholder="请输入币种" />
				<div v-else>
					{{ text || ' ' }}
				</div>
			</template>
			<template #unitPrice="{ text,record,index }">
				<a-input @change="getAmountHandle" v-if="edit" v-model:value="record.unitPrice" placeholder="请输入单价" />
				<div v-else>
					{{ text || ' ' }}
				</div>
			</template>
			<template #billingVolume="{ text,record,index }">
				<a-input @change="getAmountHandle" v-if="edit" v-model:value="record.billingVolume" placeholder="请输入计费量" />
				<div v-else>
					{{ text || ' ' }}
				</div>
			</template>
			<template #exchangeRate="{ text,record,index }">
                <a-input @change="getAmountHandle" v-if="edit" v-model:value="record.prohibitEditing" placeholder="请输入汇率" />
				<div v-else>
					{{ text || ' ' }}
				</div>
			</template>
            <-- 自动计算 -->
			<template #amount="{ text,record }">
				<div>
					{{ text || 0 }}
				</div>
			</template>
            <-- 自动计算 -->
			<template #totalAmount="{ text,record }">
				<div>
					{{ text }}
				</div>
			</template>
		</a-table>
        <-- 从最后一项开始删除 -->
		<div style="float: right;" v-if="edit" class="mt-2">
			<a-popconfirm title="确认删除最后一项吗?" ok-text="确定" cancel-text="取消" @confirm="delHandle">
				<a-button type="primary" danger shape="circle">
					<template #icon>
						<MinusOutlined />
					</template>
				</a-button>
			</a-popconfirm>
			<a-button @click="addHandle" type="primary" shape="circle" class="ml-2" style="float: right;">
				<template #icon>
					<PlusOutlined />
				</template>
			</a-button>

		</div>
	</a-card>
</template>

<script lang="ts">
	import {
		defineComponent,
		ref,
		onMounted,
		reactive,
		toRaw,
		watch
	} from 'vue';
	import {
		message
	} from 'ant-design-vue';
	import {
		cloneDeep
	} from 'lodash-es';
	import {
		PlusOutlined,
		MinusOutlined
	} from '@ant-design/icons-vue';
	export default defineComponent({
		props: ['data', 'title'],
		components: {
			PlusOutlined,
			MinusOutlined,
		},
		setup(props, context) {
			// 表格数据
			const data = ref(props.data)
			// 表格名称
			const title = ref(props.title)
			// 是否编辑
			let edit = ref(false)
			// 编辑的数据
			let editData = ref([])
			// 需要删除的数据
			let delArr = ref([])
			// 总金额
			let total = ref(0)
			// 表头
			const columns = reactive([{
					title: '收费名称',
					dataIndex: 'chargeName',
					slots: {
						customRender: 'chargeName'
					},
				},
				{
					title: '币种',
					dataIndex: 'currency',
					slots: {
						customRender: 'currency'
					},
				},
				{
					title: '收费单价',
					dataIndex: 'unitPrice',
					slots: {
						customRender: 'unitPrice'
					},
				},
				{
					title: '计费量',
					dataIndex: 'billingVolume',
					slots: {
						customRender: 'billingVolume'
					},
				},
				{
					title: '汇率',
					dataIndex: 'exchangeRate',
					slots: {
						customRender: 'exchangeRate'
					},
				},
				{
					title: '金额(RMB)',
					dataIndex: 'amount',
					slots: {
						customRender: 'amount'
					},
				},
				{
					title: '总计金额(RMB)',
					dataIndex: 'totalAmount',
					slots: {
						customRender: 'totalAmount'
					},
					align: 'center',
					customRender: ({
						text,
						index
					}) => {
						// 动态设置单元格
						const obj = {
							children: text,
							props: {},
						};
						if (index === 0) {
							obj.props.rowSpan = !edit.value ? data.value.length : editData.value.length
						}
						if (index >= 1) {
							obj.props.rowSpan = 0;
						}
						return obj;
					},
				},
			])
			// 计算总金额
			const getTotal = () => {
				let total = 0
				editData.value.map(item => {
					total += Number(item.amount)
				})
				editData.value.map(item => {
					item.totalAmount = total
				})
			}
			// 计算单条金额
			const getAmountHandle = () => {
				editData.value.map((item, i) => {
					let unitPrice = parseFloat(item.unitPrice) ? parseFloat(item.unitPrice) : 0
					let billingVolume = parseFloat(item.billingVolume) ? parseFloat(item.billingVolume) : 0
					item.amount = (unitPrice * billingVolume * item
						.exchangeRate).toFixed(2)
				})
				getTotal()
			}

			// 添加按钮
			const addHandle = () => {
				editData.value.push({
					chargeName: "",
					currency: "",
					unitPrice: '',
					billingVolume: "",
					exchangeRate: "",
					amount: "",
					totalAmount: "",
				})
			}
			// 编辑按钮
			const editHandle = () => {
				edit.value = true
				// 深度拷贝表格数据
				editData.value = cloneDeep(data.value)
			}
			// 删除按钮
			const delHandle = () => {
				editData.value.pop()
				getTotal()
			}
			// 保存
			const saveHandle = () => {
				// 用于判断是否有数据未填
				let flag = true
				// 验证表格数据必填项
				editData.value.map((item, i) => {
					item.sort = i + 1
					if (!item.chargeName || !item.currency || !item
						.unitPrice || !item.billingVolume || !item.exchangeRate) {
						flag = false
						return;
					}
					item.amount = ((parseFloat(item.unitPrice) * parseFloat(item.billingVolume)) * item
						.exchangeRate).toFixed(2)
				})
				if (!flag) {
					message.warning('请完善表格');
					return
				}
				data.value = editData.value
				edit.value = false
			}
			return {
				title,
				data,
				columns,
				edit,
				editData,
				addHandle,
				editHandle,
				delHandle,
				saveHandle,
				getAmountHandle
			};
		}
	})
</script>

<style scoped lang="less">
	.submitBtn {
		/deep/.ant-select-selector,
		/deep/.ant-input,
		/deep/ .ant-picker,
		/deep/ .ant-btn {
			height: 30px !important;
			line-height: 30px;
			padding: 0 10px;
		}
	}
</style>

2、在父组件中引入

<template>
	<CostTable title="费用明细" :data="data" />
</template>

<script lang="ts">
	import {
		defineComponent,
		ref,
	} from 'vue';
	import CostTable from './costTable.vue'

	export default defineComponent({
		components: {
			CostTable
		},
		setup(props, context) {
			let data= ref([
                {
                    chargeName: '人工费',
                    currency: 'RMB',
                    unitPrice: '2',
                    billingVolume: '3',
                    exchangeRate: '4',
                    amount: '12',
                }
            ])
			return {
				data
			};
		}
	})
</script>

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值