vue/iview的table单元格可编辑,可上下键切换,小键盘enter可选中下一个

在开发过程中,前后至今遇到好几次的编辑输入框编辑情况,4.24之前的版本,虽然改进好几次操作,但是都是用render函数实现,发现有时并不是很好操作,而且隐藏好几个bug,今天(2019-7-19)发布一个新版,目前无发现bug,而且监听键盘更灵活。废话不多说,上代码

 

2019-04-24版本

因为需要多列可编辑表格,直接用render函数,isNum代表这个数是否需要只允许输入小数点

有一点要特别注意,表格展示的数据用tableData,然后深拷贝一组数据,copyTableData,不然实际过程中发现赋值的时候,容易失去焦点

columns的代码如下:

storeColumns: [
  {title: '序号', type: 'index', align: 'center', fixed: 'left', width: 60},
  {title: '供应商', key: 'VenderName', align: 'center', fixed: 'left', width: 180},
  {title: '商品名称', key: 'GoodsName', align: 'center', fixed: 'left', width: 180},
  {title: '商品条码', key: 'BarCode', align: 'center', width: 180},
  {title: '品类', key: 'CategoryName', align: 'center', width: 180},
  {title: '单位/规格', key: 'SpecUnit', align: 'center', width: 180},
  {title: '保质期', key: 'ShelfLife', align: 'center', width: 180},
  {title: '销售量', key: 'Qty', align: 'center', width: 180},
  {title: '同期总销售额', key: 'SaleValue', align: 'center', width: 180},
  {title: '同期毛利额', key: 'GrossProfit', align: 'center', width: 180},
  {title: '同期毛利率(%)', key: 'RateOfMargin', align: 'center', width: 180},
  {title: '昨日库存总量', key: 'CloseQty', align: 'center', width: 180},
  {title: '建议补货量', key: 'SRQ', align: 'center', width: 180},
  {title: '订货周期', key: 'OrderCycle', align: 'center', width: 180},
  {title: '门店订货量', key: 'OrderQtyShop', align: 'center', width: 180, render: (h, params) => {return this.renderInput(h, params, 'OrderQtyShop', 1)}},
  {title: '预计售完时间', key: 'SellOutDate', align: 'center', width: 180, render: (h, params) => {return this.renderInput(h, params, 'SellOutDate')}},
  {title: '陈列位置', key: 'POD', align: 'center', width: 180, render: (h, params) => {return this.renderInput(h, params, 'POD')}},
  {title: '进价', key: 'CostPrice', align: 'center', width: 180},
  {title: '平均售价', key: 'AvgPrice', align: 'center', width: 180},
  {title: '计划促销价', key: 'PlanPromPrice', align: 'center', width: 180, render: (h, params) => {return this.renderInput(h, params, 'PlanPromPrice', 1)}},
  {title: '备注', key: 'Notes', align: 'center', width: 280, render: (h, params) => {return this.renderInput(h, params, 'Notes')}}
],

 renderInput的代码如下,如果有一些功能不需要刻意自己删除,代码有一点乱,可以取自己需求的

renderInput (h, params, values, isNum) {
// 设置一个class,方便待会进行操作dom
  let ref = values + params.row._index + 'n'
  return h('Input', {
    class: [
      `${ref}`
    ],
    props: {
      // 给予初始值
      value: params.row[values],
    },
    on: {
      'on-blur': (e) => {
        // 失去焦点时赋值
        this.$set(this.copyTableData[params.row._index], values, this.changeValue)
      },
      'on-focus': () =>{
        // 获取焦点时,给changeValue中间变量赋值,且选中当前值
        this.changeValue = this.copyTableData[params.row._index][values]
        setTimeout(()=> {
          document.getElementsByClassName(`${ref}`)[0].children[1].select()
        })
      }
    },
    nativeOn: {
      keydown: (event) => {
        // 监听键盘变化,input上下框可以通过上下键切换
        if (event.keyCode === 38) {
          if (this.cando) {
            return
          }
          this.cando = true
          setTimeout(() => {
            this.cando = false
            let name = values + (params.row._index - 1) + 'n'
            document.getElementsByClassName(`${name}`)[0].children[1].focus()
          }, 50)
        }
        if (event.keyCode === 40) {
          if (this.cando) {
            return
          }
          this.cando = true
          setTimeout(() => {
            this.cando = false
            let name = values + (params.row._index + 1) + 'n'
            document.getElementsByClassName(`${name}`)[0].children[1].focus()
          }, 50)
        }
      },
      input: (e) => {
        // 项目要求只允许输入三位小数点,直接操作dom赋值
        let val = e.target.value
        if (isNum) {
          val = val.replace(/[^\d.]/g, "");  //清除“数字”和“.”以外的字符  
          val = val.replace(/\.{2,}/g, "."); //只保留第一个. 清除多余的  
          val = val.replace(".", "$#$").replace(/\./g, "").replace("$#$", ".");
          val = val.replace(/^(\-)*(\d+)\.(\d\d\d).*$/, '$1$2.$3');
          console.log(val)
          setTimeout(() =>{
            document.getElementsByClassName(`${ref}`)[0].children[1].value = val
          }, 2)
          this.changeValue = val
        } else {
          this.changeValue = val
        }
      }
    },
    key: ref
  })
},

2019-7-19效果如图

 1.HTML主要用slot进行操作,需要添加一个class,方便后续dom操作

// 主要利用iview 的slot进行操作
// keyup.native 监听按钮事件
// inputName 监听输入事件,主要限制数字等输入

2.表头和表格数据

// 表头数据格式定义,咳咳,忽略拼音的key
tableColumns: [
  { type: 'selection', align: 'center', width: 50, fixed: 'left' },
  { title: '序号', type: 'index', width: 80, align: 'center', fixed: 'left' },
  { title: '编码', key: 'num', align: 'center', fixed: 'left', width: 100 },
  { title: '品名', key: 'pinming', align: 'center', fixed: 'left', width: 100 },
  { title: '单位', key: 'danwei', align: 'center', width: 80 },
  { title: '单价', key: 'danjia', width: 100, slot: 'danjia', renderHeader: (h, params) => this.renderHeader(h, params, '单价(元)', 'danjia') },
  { title: '数量', key: 'shuliang', slot: 'shuliang', width: 100, align: 'center' },
  { title: '金额(元)', key: 'jine', align: 'center', width: 100, renderHeader: (h, params) => this.renderHeader(h, params, '金额(元)', 'jine') },
  { title: '赠送数量', key: 'zengsong', slot: 'zengsong', width: 100, align: 'center' },
  { title: '计数', key: 'jishu', align: 'center', width: 100, slot: 'jishu', renderHeader: (h, params) => this.renderHeader(h, params, '计数', 'jishu') },
  { title: '计重', key: 'jizhong', align: 'center', width: 100, slot: 'jizhong', renderHeader: (h, params) => this.renderHeader(h, params, '计重', 'jizhong') },
  { title: '件数', key: 'jianshu', align: 'center', width: 100, slot: 'jianshu', renderHeader: (h, params) => this.renderHeader(h, params, '件数', 'jianshu') },
  { title: '原产地', key: 'yuanchandi', align: 'center', width: 150, slot: 'yuanchandi' },
  { title: '生产日期', key: 'shengcanriqi', align: 'center', width: 180, slot: 'shengcanriqi' },
  { title: '保质期', key: 'baozhiqi', align: 'center', width: 300, slot: 'baozhiqi' }
],
// 当然是假数据,哈哈哈,后端数据没来,key全部用拼音代替
tableData: [
  {danwei: '头', num: 1, pinming: '品名1', danjia: 2, shuliang: 2, jine: 2, jishu: 2, jizhong: 3, jianshu: 5},
  {danwei: '个', num: 1, pinming: '品名2', danjia: 2, shuliang: 2, jine: 2, jishu: 2, jizhong: 3, jianshu: 5},
  {danwei: '个', num: 1, pinming: '品名2', danjia: 2, shuliang: 2, jine: 2, jishu: 2, jizhong: 3, jianshu: 5},
  {danwei: '个', num: 1, pinming: '品名2', danjia: 2, shuliang: 2, jine: 2, jishu: 2, jizhong: 3, jianshu: 5},
  {danwei: 'kg', num: 1, pinming: '品名3', danjia: 2, shuliang: 2, jine: 2, jishu: 2, jizhong: 3, jianshu: 5}
],

3. methods方法的定义,直接操作dom,小键盘enter键向右既下一个,注意需要按顺序写一个对象

// 表头方法定义,由于个别列需要进行输入合计
renderHeader (h, params, name, key) {
  return h('div', [
    h('div', name),
    h('div', {
      style: {
        'text-align': 'right'
      }
    }, this.addNum[key])
  ])
},

// 按键切换
handleKeyup (event, index, key) {
  // 向上键
  if (event.keyCode === 38) {
    // 获取到所有class为[key]的dom列表
    let doms = document.getElementsByClassName(key)
    if (!index) {
      index = this.copyTableData.length
    }
    // 选中类表中第index个,向上故而减1,dom里头包含的input,获取焦点以及选中input里的内容
    let dom = doms[index - 1].getElementsByTagName('input')[0]
    dom.focus()
    dom.select()
  }
  // 向下键
  if (event.keyCode === 40) {
    let doms = document.getElementsByClassName(key)
    if (+index === this.copyTableData.length - 1) {
      index = -1
    }
    let dom = doms[index + 1].getElementsByTagName('input')[0]
    dom.focus()
    dom.select()
  }
  // 小键盘回车键
  if (event.keyCode === 13) {
   // 小键盘回车因为需要根据class判断下一个,故而有input输入框的全部需要在这按顺序声明
    let keyName = ['danjia', 'shuliang', 'zengsong', 'jishu', 'jizhong', 'jianshu', 'yuanchandi', 'shengcanriqi', 'baozhiqi']
    let num = 0
    if (key === keyName[keyName.length - 1]) {
      if (index === this.copyTableData.length - 1) {
        index = 0
      } else {
        ++index
      }
    } else {
      keyName.map((v, i) => {
        if (v === key) {
          num = i + 1
        }
      })
    }
    let doms = document.getElementsByClassName(keyName[num])
    let dom = doms[index].getElementsByTagName('input')[0]
    dom.focus()
    dom.select()
  }
},
// 输入赋值,记得不直接给tableData赋值,而是深度拷贝一个,不然从新渲染时会失去焦点
inputName (value, row, index, key) {
  let ret = this.regular(value)
  let dom = document.getElementsByClassName(key)
  setTimeout(() => {
    dom[index].children[1].value = ret
  })
  this.copyTableData[index][key] = value
},
// 正则验证,限定输入数字,到时可根据参数选择要不要验证,这里自己看是否需要
regular (value) {
  value = value.replace(/[^\d.]/g, '') // 清除"数字"和"."以外的字符
  value = value.replace(/[^\d.]/g, '') // 清除"数字"和"."以外的字符
  value = value.replace(/^\./g, '') // 验证第一个字符是数字而不是
  value = value.replace(/\.{2,}/g, '.') // 只保留第一个. 清除多余的
  value = value.replace('.', '$#$').replace(/\./g, '').replace('$#$', '.')
  console.log('value2:' + value)
  value = value.replace(/^(-)*(\d+)\.(\d{3}).*$/, '$1$2.$3') // 只能输入两个小数
  return value
}

4.computed计算属性监听头部数字变化

computed: {
  addNum () {
    let nums = {
      jine: 0,
      jishu: 0,
      jizhong: 0,
      jianshu: 0
    }
    this.copyTableData.map(v => {
      nums.jine += +v.jine
      nums.jishu += +v.jishu
      nums.jizhong += +v.jizhong
      nums.jianshu += +v.jianshu
    })
    return nums
  }
},

 

你可能感兴趣的:(vue)