组件化的可编辑数据表格

在开发Web应用程序时,数据表格是常见的界面元素之一。然而,传统的数据表格在编辑和更新数据方面存在一些限制。为了解决这个问题,本文提出了一种基于组件化的可编辑数据表格的设计和实现方法。

本文的实现基于前端技术栈,使用了JSON-Server作为后端数据源,并借助JavaScript和HTML来创建交互性的数据表格界面。同时,通过模块化的设计思想,将功能划分为不同的组件,提高了代码的可维护性和可扩展性。

json-server data.json

首先,有两个关键的组件:Tips和Good。Tips组件用于显示提示信息,包括错误提示和成功提示。Good组件表示商品数据,包含商品的ID、名称、价格和数量等信息,并提供了删除和更新商品的方法。

export default class Tips {
    static TipsClassName = { "error": "err movedown", "success": "success movedown" }
    constructor(text = "提示框", classname = Tips.TipsClassName.error) {
        this.text = text;
        this.classname = classname;
    }
    //错误提示
    showTips() {
        var that = this;
        var thetips = document.getElementById("tips");
        thetips.innerHTML = this.text;
        thetips.className = this.classname;
        thetips.style.display = "block";
        setTimeout(function () {
            that.noneTips();
        }, 3000);
    }
    //隐藏错误提示
    noneTips() {
        var thetips = document.getElementById("tips");
        thetips.style.display = "none";
    }
}
import Tips from "./tips.mjs";
export default class Good {
  constructor({ id, name, price, num, src }) {
    this.id = id;
    this.name = name;
    this.src = src;
    this.price = parseFloat(price);
    this.num = parseFloat(num);
  }
  geteAllPrice() {
    return this.price * this.num;
  }

  async del() {
    var url = "http://localhost:3000/good/" + this.id;
    const xhr = new XMLHttpRequest();
    xhr.open("DELETE", url);
    xhr.addEventListener("load", () => {
      if (xhr.status == 200) {
        new Tips("删除成功!!", Tips.TipsClassName.success).showTips();
      } else {
        new Tips("删除失败!!", Tips.TipsClassName.error).showTips();
      }
    });
    xhr.send();
  }

  async upd(newGood) {
    Object.assign(this, newGood);
    var url = "http://localhost:3000/good/" + this.id;
    const xhr = new XMLHttpRequest();
    xhr.open("PUT", url);
    xhr.setRequestHeader("Content-Type", "application/json");
    xhr.addEventListener("load", () => {
      if (xhr.status == 200) {
        new Tips("修改成功!!", Tips.TipsClassName.success).showTips();
      } else {
        new Tips("修改失败!!", Tips.TipsClassName.error).showTips();
      }
    });
    xhr.send(JSON.stringify(this));
  }
}

接下来,引入了Table组件作为整个数据表格的核心。Table组件包含一个存储商品数据的数组goods,并提供了获取商品数据和加载数据表格的方法。其中,getUserData方法通过发送HTTP请求从JSON-Server获取商品数据,并将返回的数据解析为商品对象并存储到goods数组中。loadUserTable方法根据goods数组的数据动态生成表格的HTML内容,并将其插入到相应的DOM元素中。

import Good from "./good.mjs";
import Tips from "./tips.mjs";

export default class Table {
  constructor() {
    this.goods = [];
  }

  //获取学生数据
  async getUserData() {
    var url = "http://localhost:3000/good";
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url, false);
    xhr.send();
    if (xhr.status === 200) {
      var goodsData = JSON.parse(xhr.responseText);
      var good;
      new Tips("加载成功!!", Tips.TipsClassName.success).showTips();
      for (good of goodsData) {
        this.goods.push(new Good(good));
      }
    } else {
      new Tips("加载失败!!", Tips.TipsClassName.error).showTips();
      console.error("Error: " + xhr.status);
    }
  }

  //加载数据,显示在表格上
  async loadUserTable() {
    var that = this;
    var userData = this.goods;
    var tbody = document.getElementById("tbody");
    var innerHtml = "";
    var good;
    for (good of userData) {
      innerHtml += `
                <tr>
                    <td class='check' data-id="${good.id
        }"><lable><input type="checkbox" class="onecheck"/></lable></td>
                    <td  data-id="${good.id}" ><img src="${good.src
        }" width=50px></img>${good.name}</td>
                    <td class="price" data-id="${good.id}" data-name="price">${good.price
        }</td>
                    <td class="count" name="num" data-id="${good.id
        }" data-name="num"> ${good.num}</td>
                    <td class="subtotal" rname="allprice" data-id="${good.id
        }">${good.geteAllPrice()}</td>
                    <td rname="allgrade"><button id="goodDelBtn${good.id
        }" data-id="${good.id}">删除</button></td>
                </tr>`;
    }
    tbody.innerHTML = innerHtml;
    var tfoot = document.getElementById("tfoot");
    var innerHtml2 = "";
    innerHtml2 = `<tr>总计:<em id="totalPrice" >0</em>¥</tr>`;
    tfoot.innerHTML = innerHtml2;
    var thead = document.getElementById("thead");
    var innerHtml3 = "";
    innerHtml3 = `  <tr>
                    <th>
                        <lable><input type="checkbox" id="selectAll" />全选</lable>
                    </th>
                    <th>商品</th>
                    <th>单价</th>
                    <th>数量</th>
                    <th>小计</th>
                    <th>操作</th>
                </tr>`;
    thead.innerHTML = innerHtml3;
    //全选框
    var selectAll = document.getElementById("selectAll");
    var select = document.getElementsByClassName("onecheck");
    var subtotals = document.getElementsByClassName("subtotal");
    // var addBut = document.getElementsByClassName("addBut");
    // var jianBut = document.getElementsByClassName("jianBut");

    // //添加
    // for (var i = 0; i < addBut.length; i++) {
    //   addBut[i].onclick = function (ele) {
    //     // console.info(this);
    //     // 找到+按钮的下一个兄弟节点
    //     var counts = this.nextElementSibling;
    //     var count = parseInt(counts.value);
    //     counts.value = ++count;
    //   };
    // }
    // //减少
    // for (var i = 0; i < jianBut.length; i++) {
    //   jianBut[i].onclick = function () {
    //     // console.info(this);
    //     var counts = this.previousElementSibling;
    //     var count = parseInt(counts.value);
    //     if (count > 1) {
    //       counts.value = --count;
    //     }
    //   };
    // }

    //全选
    selectAll.onchange = function () {
      var checked = this.checked;
      var number = 0;
      if (checked) {
        for (var i = 0; i < select.length; i++) {
          select[i].checked = true;
          number += parseInt(subtotals[i].innerHTML);
        }
        document.getElementById("totalPrice").innerHTML = number;
      } else {
        for (var i = 0; i < select.length; i++) {
          select[i].checked = false;
        }
        document.getElementById("totalPrice").innerHTML = 0;
      }
    };
    // 未点击全选框根据每个复选框来判断是否全选
    for (var i = 0; i < select.length; i++) {
      select[i].onclick = function () {
        // 默认全部没有选中
        var temp = true;
        // 选中个数
        var number = 0;
        for (var j = 0; j < select.length; j++) {
          if (select[j].checked) {
            number += parseInt(subtotals[j].innerHTML);
          } else {
            temp = false;
          }
        }
        selectAll.checked = temp;
        document.getElementById("totalPrice").innerHTML = number;
      };
    }

    //事件绑定
    for (good of userData) {
      document.getElementById(`goodDelBtn${good.id}`).onclick = function () {
        that.delGood(this.dataset.id);
      };
    }
    this.setCellCilck();
  }

  // 给单元格添加点击事件
  async setCellCilck() {
    var that = this;
    var nums = document.getElementsByName("num");

    for (let i = 0; i < nums.length; i++) {
      nums[i].onclick = function () {
        that.updateCell(this);
      };
    }
  }

  // 更新单元格内容
  async updateCell(ele) {
    var that = this;
    if (document.getElementsByClassName("active-input").length == 0) {
      var oldhtml = ele.innerHTML;
      ele.innerHTML = "";
      // 通过DOM API 创建input元素,设置属性,值,方法
      var newInput = document.createElement("input");
      newInput.setAttribute("class", "active-input");
      newInput.value = oldhtml;
      newInput.onblur = function () {
        var name = ele.getAttribute("name");
        // 修改商品数量
        if (name == "num") {
          // 正则表达式,限制数字的范围是0-100。
          const isValid = /^(\d|[1-9]\d|100)(\.\d+)?$/.test(this.value);
          if (isValid) {
            //修改数据
            ele.innerHTML = this.value;
            //修改对象
            const id = ele.dataset.id;
            var data = {};
            data[ele.dataset.name] = this.value;
            //按断文案
            that.updGood(id, data);
          } else {
            new Tips(
              "商品数量不能低于0,请重新输入!",
              Tips.TipsClassName.error
            ).showTips();
            ele.innerHTML = oldhtml;
          }
        }
      };
      newInput.select();
      ele.appendChild(newInput);
      newInput.focus();
    } else {
      return;
    }
  }

  // 总价
  async total() {
    // 1.获取全部单价
    var prices = document.getElementsByClassName("price");
    // 2.获取全部的数量
    var amounts = document.getElementsByClassName("amount");
    // var subtotals = document.getElementsByClassName("subtotal");
    // 3.计算商品总价
    var sum = 0;
    for (var i = 0; i < amounts.length; i++) {
      sum += parseFloat(prices[i].innerHTML) * parseInt(amounts[i].value);
      // sum += parseFloat(subtotals[i].innerHTML);
    }
    document.getElementById("totalPrice").innerHTML = sum;
  }

  async delGood(id) {
    for (var good of this.goods) {
      if (id == good.id) {
        good.del();
      }
    }
  }
  // 更新商品数据
  async updGood(id, newGood) {
    for (var good of this.goods) {
      if (id == good.id) {
        good.upd(newGood);
      }
    }
  }
}

在加载数据表格时,Table组件还实现了一些交互功能。首先,我们可以通过全选框实现全选和取消全选的功能,该功能会自动更新选中商品的总价。其次,通过给每个单元格添加点击事件,我们可以实现对数量进行编辑的功能。当用户点击某个单元格时,单元格的内容会变成可编辑状态,用户可以直接修改数量,并在失去焦点时保存修改,并更新对应的商品对象和总价。

此外,Table组件还提供了删除商品和更新商品数据的方法。通过点击表格中的删除按钮,可以调用delGood方法删除对应的商品。而通过编辑数量并保存后,会调用updGood方法更新对应商品的数量信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值