封装一个可编辑的表单,用于统计月度计划
在正式开始封装之前我们要简单理解一下什么叫做公共组件,根据名字可以得知,这东西相当于合作社的老黄牛,谁家需要用打声招呼拿过去用,所以在封装公共组件的时候我们需要注意的是三个问题:
- 谁去用:将公共组件引入到哪个组件中
- 能不能用:拿来的公共组件是否可以搭配我的组件完成逻辑
- 用来干什么:完成相应的项目需求
等搞清楚了这三个问题我们就大致了解到了有一个公共组件必须具备的条件
- 公共组件必须是所有组件都可以使用的,因此我们需要创建一个components文件夹用于存放公共组件。
- 公共组件必须满足拿来即用,因此需要对于父组件传过来的情况作不同的考量,根据不同的数据书写不同的逻辑。
接下来就给大家写一个基于css、html完成的表格。
- 首先创建一个components文件夹
- 创建EditTable.vue文件
template部分
<template>
<div class="table_box">
<div class="monthName">
<span class="unit_text">单位:万吨</span>
{{ props.title }}
</div>
<div class="table_header">
<div class="header_box" v-for="item in tableData.columns" :key="item.dataIndex">
{{ item.monthName }}
</div>
</div>
<div class="table_body">
<div class="body_box" :style="{ pointerEvents: lastYear ? 'none' : 'all' }" v-for="item in tableData.data" :key="item.monthName">
<input type="text" v-if="item.editable" v-model="item.monthValue" @blur="save(item)" />
<span v-else @click="edit(item)">{{ item.monthValue }}</span>
</div>
</div>
</div>
</template>
script部分
<script setup>
import { reactive, watch, ref } from 'vue';
const props = defineProps({
title: { type: String, default: '' },
});
// 接受父级组件传过来的值
const emit = defineEmits(['getTableData']);
const tableData = reactive({
//因为后端要求所以将表头与表数据分开了,可以自行修改
data: [
{
monthName: 'total',
monthValue: '0',
editable: false,
},
{
monthName: '01',
monthValue: '0',
editable: false,
},
{
monthName: '02',
monthValue: '0',
editable: false,
},
{
monthName: '03',
monthValue: '0',
editable: false,
},
{
monthName: '04',
monthValue: '0',
editable: false,
},
{
monthName: '05',
monthValue: '0',
editable: false,
},
{
monthName: '06',
monthValue: '0',
editable: false,
},
{
monthName: '07',
monthValue: '0',
editable: false,
},
{
monthName: '08',
monthValue: '0',
editable: false,
},
{
monthName: '09',
monthValue: '0',
editable: false,
},
{
monthName: '10',
monthValue: '0',
editable: false,
},
{
monthName: '11',
monthValue: '0',
editable: false,
},
{
monthName: '12',
monthValue: '0',
editable: false,
},
],
columns: [
{ monthName: '总计', dataIndex: 'total', editable: false },
{ monthName: '1月', dataIndex: 'january', editable: true },
{ monthName: '2月', dataIndex: 'february', editable: true },
{ monthName: '3月', dataIndex: 'march', editable: true },
{ monthName: '4月', dataIndex: 'april', editable: true },
{ monthName: '5月', dataIndex: 'may', editable: true },
{ monthName: '6月', dataIndex: 'june', editable: true },
{ monthName: '7月', dataIndex: 'july', editable: true },
{ monthName: '8月', dataIndex: 'august', editable: true },
{ monthName: '9月', dataIndex: 'september', editable: true },
{ monthName: '10月', dataIndex: 'october', editable: true },
{ monthName: '11月', dataIndex: 'november', editable: true },
{ monthName: '12月', dataIndex: 'december', editable: true },
],
});
// 可编辑
const edit = (row) => {
row.editable = true;
};
// 保存数据
const save = (row) => {
row.editable = false;
const monthArr = tableData.data.map((ele) => {
if (ele.monthName === 'total') return 0;
else return Number(ele.monthValue);
});
if (row.monthName !== 'total') {
tableData.data[0].monthValue = monthArr.reduce((per, cur) => per + cur, 0);
}
};
// 判断是否是去年计划,是的话不可编辑表格
const lastYear = ref(false);
watch(
() => [props.title, lastYearList],
(newData) => {
lastYear.value = newData[0] === '去年月度计划时间及数量' ? true : false;
},
{
immediate: true,
deep: true,
}
);
// 发送数据
function sendTableData() {
emit('getTableData', tableData.data);
}
// 改变数据
function changeTableData(data) {
if (data) {
tableData.data = data;
const monthArr = tableData.data.map((ele) => {
if (ele.monthName === 'total') return 0;
else return Number(ele.monthValue);
});
// 计算总数
tableData.data[0].monthValue = monthArr.reduce((per, cur) => per + cur, 0);
}
}
// 清楚数据,可以自行修改
function clearTable() {
tableData.data = [
{
monthName: 'total',
monthValue: '0',
editable: false,
},
{
monthName: '01',
monthValue: '0',
editable: false,
},
{
monthName: '02',
monthValue: '0',
editable: false,
},
{
monthName: '03',
monthValue: '0',
editable: false,
},
{
monthName: '04',
monthValue: '0',
editable: false,
},
{
monthName: '05',
monthValue: '0',
editable: false,
},
{
monthName: '06',
monthValue: '0',
editable: false,
},
{
monthName: '07',
monthValue: '0',
editable: false,
},
{
monthName: '08',
monthValue: '0',
editable: false,
},
{
monthName: '09',
monthValue: '0',
editable: false,
},
{
monthName: '10',
monthValue: '0',
editable: false,
},
{
monthName: '11',
monthValue: '0',
editable: false,
},
{
monthName: '12',
monthValue: '0',
editable: false,
},
];
}
// 因为使用了setup语法糖,所以想要在父级组件中调用方法需要单独暴露,不然拿不到,如果你使用setup函数就不需要这样做
defineExpose({
sendTableData,
changeTableData,
clearTable,
});
</script>
Css部分
<style lang="less" scoped>
.table_box {
display: flex;
flex-direction: column;
margin-bottom: 20px;
.monthName {
width: 100%;
height: 25px;
font-size: 18px;
font-weight: bold;
color: #000;
text-align: center;
margin: 10px 0;
.unit_text {
float: left;
font-size: 16px;
font-weight: normal;
}
}
.table_header {
width: 100%;
height: 35px;
display: grid;
grid-template-columns: repeat(13, 1fr);
grid-template-rows: repeat(1, 1fr);
.header_box {
border: 1px solid #000;
text-align: center;
line-height: 35px;
border-right: none;
}
.header_box:nth-last-child(1) {
border-right: 1px solid #000;
}
}
.table_body {
width: 100%;
height: 35px;
display: grid;
grid-template-columns: repeat(13, 1fr);
grid-template-rows: repeat(1, 1fr);
.body_box {
border: 1px solid #000;
text-align: center;
line-height: 35px;
border-right: none;
border-top: none;
input {
width: 100%;
height: 98%;
text-align: center;
border-top: none;
border: 1px solid #000;
outline: none;
}
span {
display: inline-block;
width: 100%;
height: 100%;
}
}
.body_box:nth-last-child(1) {
border-right: 1px solid #000;
}
}
}
.box {
background-color: #ddd;
padding: 20px;
text-align: center;
}
</style>