工作中写过这样一个表格,每一个模块都是数据循环出来的,且每个模块样式有所不同,某些模块可以编辑。简单记录一下,有类似需求的可以参考!效果图如下:

1677812321430.png

image.png
文中使用的是elementui的input输入框配合实现,以财务表格来举例,首先定义好能需要的字段,
//财务
financeData: [
{
label: '货币资金',//左侧文字
value: 'capital',//字段值
canEdit: true,//是否可编辑
edit: false,//是否聚焦
type: 'number',//数字类型的文本框
min: 0,//最小值
max: 9999.99,//最大值
precision: 2,//保留几位小数
require: false,//是否*号必填
},
{
label: '征信负债',
value: 'creditDebt',
canEdit: true,
edit: false,
type: 'number',
min: 0,
max: 9999.99,
precision: 2,
require: false,
},
{
label: '应收账款',
value: 'accountsReceivable',
canEdit: true,
edit: false,
type: 'number',
min: 0,
max: 9999.99,
precision: 2,
require: false,
},
{
label: '其他负债(征信未体现)',
value: 'outCreditDebt',
canEdit: true,
edit: false,
type: 'number',
min: 0,
max: 9999.99,
precision: 2,
require: false,
},
{
label: '存货',
value: 'stock',
canEdit: true,
edit: false,
type: 'number',
min: 0,
max: 9999.99,
precision: 2,
require: false,
},
{
label: '对外担保',
value: 'guarantee',
canEdit: true,
edit: false,
type: 'number',
min: 0,
max: 9999.99,
precision: 2,
require: false,
},
{
label: '预付账款',
value: 'accountsPrepaid',
canEdit: true,
edit: false,
type: 'number',
min: 0,
max: 9999.99,
precision: 2,
require: false,
},
{
label: '民间融资(私人欠款)',
value: 'privateFinancing',
canEdit: true,
edit: false,
type: 'number',
min: 0,
max: 9999.99,
precision: 2,
require: false,
},
{
label: '固定资产',
value: 'fixedAssets',
canEdit: true,
edit: false,
type: 'number',
min: 0,
max: 9999.99,
precision: 2,
require: false,
},
{
label: '应付账款',
value: 'financing',
canEdit: true,
edit: false,
type: 'number',
min: 0,
max: 9999.99,
precision: 2,
require: false,
},
{
label: '对外投资',
value: 'investment',
canEdit: true,
edit: false,
type: 'number',
min: 0,
max: 9999.99,
precision: 2,
require: false,
},
{
label: '其他应付款',
value: 'otherShdPay',
canEdit: true,
edit: false,
type: 'number',
min: 0,
max: 9999.99,
precision: 2,
require: false,
},
{
label: '对外出借',
value: 'outerLend',
canEdit: true,
edit: false,
type: 'number',
min: 0,
max: 9999.99,
precision: 2,
require: false,
},
{
label: '预收账款',
value: 'accountsPayable',
canEdit: true,
edit: false,
type: 'number',
min: 0,
max: 9999.99,
precision: 2,
require: false,
},
{
label: '其他流动性资产',
value: 'otherAssets',
canEdit: true,
edit: false,
type: 'number',
min: 0,
max: 9999.99,
precision: 2,
require: false,
},
{
label: '其他负债',
value: 'otherDebt',
canEdit: true,
edit: false,
type: 'number',
min: 0,
max: 9999.99,
precision: 2,
require: false,
},
{
label: '资产合计',
value: 'assetsTotal',
canEdit: false,
edit: false,
type: 'leftSum',//求合计
precision: 2,
require: false,
},
{
label: '负债合计',
value: 'debtTotal',
canEdit: false,
edit: false,
type: 'rightSum',//求合计
precision: 2,
require: false,
},
{
label: '资产负债率(%)',
value: 'assetLiabilityRatio',
canEdit: false,
edit: false,
type: 'ratio',//求百分比
precision: 2,
require: false,
},
{
label: '所有者权益',
value: 'ownerEquity',
canEdit: false,
edit: false,
type: 'reduce',
require: false,
},
{
label: '其他披露(如有)',
value: 'other',
default:
'如针对以上资产负债表中未披露,需要披露的,可在此处做分析解释,若无,请备注“无”',
canEdit: true,
edit: false,
type: 'textarea',//文本域
require: true,
},
],
一、不可剪辑的单元格
不可编辑的单元格涉及到求和、百分比,代码如下:
//各种计算举例子
computed: {
//资产合计
getAssetsTotal() {
return function () {
const assetDebtInfo = this.reportData.assetDebtInfo
let sum = this.getSum(
assetDebtInfo.capital,
assetDebtInfo.accountsReceivable,
assetDebtInfo.stock,
assetDebtInfo.accountsPrepaid,
assetDebtInfo.fixedAssets,
assetDebtInfo.investment,
assetDebtInfo.outerLend,
assetDebtInfo.otherAssets
)
if (sum >= 0) return sum.toFixed(2)
}
},
//资产负债率
getRatio() {
return function (assetsTotal, debtTotal) {
if (Number(assetsTotal) == 0 || Number(debtTotal) == 0) {
return '0.00'
} else {
return ((debtTotal / assetsTotal) * 100).toFixed(2)
}
}
}
}
//精度加法
getSum() {
let arr = [...arguments]
var sum = arr.reduce(function (total, currentValue) {
return (total * 100 + currentValue * 100) / 100
})
return sum
},
二、可编辑单元格
可编辑单元格有数字、文字两种类型,如下:
<!-- 可编辑 -->
<template v-if="item.canEdit && item.edit">
<!-- 金额 -->
<el-input-number
v-if="item.type == 'number'"
v-model="reportData.assetDebtInfo[item.value]"
v-focus
:max="item.max"
:min="item.min"
:placeholder="'请输入' + item.label"
:precision="item.precision"
@blur="
blurRow('financeData', index, $event, 'assetDebtInfo')
"
/>
<!-- 文字 -->
<el-input
v-if="item.type == 'textarea'"
v-model="reportData.assetDebtInfo[item.value]"
v-focus="{ type: 'textarea' }"
:autosize="{ minRows: 2 }"
maxlength="1000"
:placeholder="item.default"
show-word-limit
type="textarea"
@blur="blurRow('financeData', index)"
/>
</template>
首先是点击聚焦功能,写了个自定义指令,可以获取当前input焦点,如下:
//获取input焦点
directives: {
focus: {
inserted: function (el, binding) {
if (binding && binding.value && binding.value.type == 'textarea') {
el.querySelector('textarea').focus()
} else {
el.querySelector('input').focus()
}
},
},
},
修改完成之后赋值,如下:
//单元格聚焦 编辑
clickRow(type, index) {
this[type][index].edit = true //该单元格聚焦
},
// 单元格失焦:type为财务数据这个对象的字段名
blurRow(type, index, e) {
this[type][index].edit = false//该单元格失焦
this.reportData[this[type][index].value] = String(e.target.value)//赋值,reportData为编辑传回的对象
}
三、完整代码块如下:
<div class="new_tab row_two row_textarea">
<div class="table">
<el-row class="title" justify="center" type="flex">
<article>财务情况(单位:万元)</article>
</el-row>
<el-row class="title" justify="center" type="flex">
<article>资产</article>
<article>负债</article>
</el-row>
<div class="content">
<div
v-for="(item, index) in financeData"
:key="index + 4"
:class="[
'item',
index == financeData.length - 1 ? 'big_item' : '',
]"
>
<div class="left">
<span v-if="item.require" class="require">*</span>
{{ item.label }}
</div>
<div
:class="['right', item.canEdit == false ? 'dis_right' : '']"
@click="clickRow('financeData', index)"
>
<!-- 可编辑 -->
<template v-if="item.canEdit && item.edit">
<!-- 金额 -->
<el-input-number
v-if="item.type == 'number'"
v-model="reportData.assetDebtInfo[item.value]"
v-focus
:max="item.max"
:min="item.min"
:placeholder="'请输入' + item.label"
:precision="item.precision"
@blur="
blurRow('financeData', index, $event, 'assetDebtInfo')
"/>
<!-- 文字 -->
<el-input
v-if="item.type == 'textarea'"
v-model="reportData.assetDebtInfo[item.value]"
v-focus="{ type: 'textarea' }"
:autosize="{ minRows: 2 }"
maxlength="1000"
:placeholder="item.default"
show-word-limit
type="textarea"
@blur="blurRow('financeData', index)"/>
</template>
<!-- 不可编辑 -->
<template v-else>
<!-- 1.资产合计 -->
<div v-if="item.type == 'leftSum'">
{{ getAssetsTotal() }}
</div>
<!--2.负债合计 -->
<div v-else-if="item.type == 'rightSum'">
{{ getDebtTotal() }}
</div>
<!-- 3.利率 -->
<div v-else-if="item.type == 'ratio'">
{{ getRatio(getAssetsTotal(), getDebtTotal()) }}
</div>
<!-- 所有者权益 -->
<div v-else-if="item.type == 'reduce'">
{{ getReduce(getAssetsTotal(), getDebtTotal()) }}
</div>
<div v-else>
{{
reportData.assetDebtInfo[item.value] ||
(item.type == 'textarea' ? '/' : '0.00')
}}
</div>
</template>
</div>
</div>
</div>
</div>
</div>
四、单元格样式
定义好基本的样式,然后改变item的宽度,匹配不同的类名,big_item是最后一行最大的单元格(比如财务情况里的其他披露),用下标判断显示在哪个位置
//一排两个可输入框
.row_two {
.item {
width: 50%;
}
.big_item {
width: 100% !important;
.left {
width: 25% !important;
}
.right {
width: 75% !important;
}
}
}
//一排三个可输入框
.row_three {
.item {
width: 33.33%;
}
.big_item {
width: 100% !important;
.left {
width: 16.66% !important;
}
.right {
width: 83.34% !important;
}
}
}
//一排四个可输入框
.row_four {
.item {
width: 25%;
}
.big_item {
width: 100% !important;
.left {
width: 12.6% !important;
}
.right {
width: 88.4% !important;
}
}
}
//一排五个可输入框
.row_five {
.item {
width: 20%;
}
.big_item {
width: 100% !important;
.left {
width: 16.66% !important;
}
.right {
width: 83.34% !important;
}
}
}
//如其他批量是最后一行的大单元格子,动态添加big_item类名
<div
v-for="(item, index) in financeData"
:key="index + 4"
:class="['item',index == financeData.length - 1 ? 'big_item' : '' ]"
>