手动实现一个循环可编辑表格(vue)

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


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' : '' ]"
 >
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。