都2022年了还没用过CMS数据可视化吗?04

101 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情

接CMS数据可视化03

一个项目的剩下内容,主要是ajax内容

成绩管理

获取成绩并展示

这个功能有对应的接口,而且得到的数据不需要处理,直接可以使用。认真阅读接口文档

// -------------- 获取分数,展示列表 ------------
function renderScore() {
  axios.get('/score/list').then(({ data: res }) => {
    // console.log(res);
    let { code, data } = res;
    if (code === 0) {
      let arr = [];
      for (let key in data) {
        arr.push(`
          <tr>
              <th scope="row">${key}</th>
              <td>${data[key].name}</td>
              <td class="score">${data[key].score[0]}</td>
              <td class="score">${data[key].score[1]}</td>
              <td class="score">${data[key].score[2]}</td>
              <td class="score">${data[key].score[3]}</td>
              <td class="score">${data[key].score[4]}</td>
          </tr>
          `);
      }
​
      $('tbody').html(arr.join(''))
    }
  })
}
renderScore();

可编辑的表格效果

添加和修改成绩,并没有使用弹出层等效果。而是采用可编辑的表格。

可编辑的表格,需要用到的技术就是DOM操作,没有其他。

思路如下:

  • 点击单元格 td.score ,获取单元格的值。
  • 创建input
  • 设置input的样式和td的样式一致,或者自行修改样式。
  • 将 单元格的值 设置为 input 的 value
  • 将 input 放入 td 中,并获取焦点

后续补充,输入框中按回车,表示确定,失去焦点表示取消

  • 给input注册 keyup 和 blur 事件。

完整代码:

// 可编辑的表格
$('tbody').on('click', '.score', function () {
  let td = $(this);
  if (td.children().length > 0) return; // 判断如果已经有input了,就不要在重复添加input了
  let text = td.text(); // 保存td中的分数
  td.html(''); // 设置td的内容为空
  let input = $('<input />'); // 创建input
  input // 设置input的样式
    .css('color', td.css('color')) // 设置input的color和td的color相同
    .css('background-color', td.css('background-color'))
    .css('font-size', td.css('font-size'))
    .css('width', '100%')
    .css('height', '100%')
    .css('outline', 'none')
    .css('border', 'none')
    .val(text) // 将原来td中的值,设置为input的value
    .appendTo(td) // 将input添加到td中
    .focus(); // 让input获取焦点,即有光标闪烁效果
​
  // 为input注册 keyup 事件
  input.on('keyup', function (e) {
    if (e.keyCode === 13) {
      // console.log('按下回车键,这里调用接口,修改或录入成绩');
    }
  })
  // 为input注册blur事件
  input.on('blur', function () {
    td.html(text); // 失去焦点后,将原来td的值设置回去
  })
})

修改或录入成绩

接口分析:

  • 需要学员id 参数
  • 需要batch次数 参数
  • 需要score成绩 参数

所以,在循环遍历数据时,将需要的 学员idbatch次数,用自定义属性 data-xx 存储。

arr.push(`
  <tr>
      <th scope="row">${key}</th>
      <td>${data[key].name}</td>
      <td data-batch="1" data-id="${key}" class="score">${data[key].score[0]}</td>
      <td data-batch="2" data-id="${key}" class="score">${data[key].score[1]}</td>
      <td data-batch="3" data-id="${key}" class="score">${data[key].score[2]}</td>
      <td data-batch="4" data-id="${key}" class="score">${data[key].score[3]}</td>
      <td data-batch="5" data-id="${key}" class="score">${data[key].score[4]}</td>
  </tr>

当按下回车键后,获取接口所需参数,调用接口发送请求,进行成绩的修改和录入

input.on('keyup', function (e) {
  if (e.keyCode === 13) {
    // console.log('按下回车键,这里调用接口,修改或录入成绩');
    let score = $(this).val();
    if (isNaN(score) || score < 0 || score > 100) {
      return toastr.warning('请输入0~100之间的数字');
    }
    let stu_id = td.data('id');
    let batch = td.data('batch');
    // console.log(score, stu_id, batch);
    axios.post('/score/entry', { score, stu_id, batch }).then(({ data: res }) => {
      if (res.code === 0) {
        toastr.success(res.message);
        td.html(score) // 修改成功后,将新的成绩 放到 td 中
      }
    })
  }
})

学员管理

获取数据展示

这也是一个常规操作,按照接口文档获取数据,然后循环遍历到表格中即可。

// 获取学员数据
function renderStudent() {
  axios.get('/student/list').then(({ data: res }) => {
    // console.log(res);
    if (res.code === 0) {
      let arr = [];
      res.data.forEach(item => {
        arr.push(`
          <tr>
              <th scope="row">${item.id}</th>
              <td>${item.name}</td>
              <td>${item.age}</td>
              <td>${item.sex}</td>
              <td>${item.group}</td>
              <td>${item.phone}</td>
              <td>${item.salary}</td>
              <td>${item.truesalary}</td>
              <td>${item.province}${item.city}${item.county}</td>
              <td>
              <button type="button" class="btn btn-primary btn-sm update">修改</button>
              <button type="button" class="btn btn-danger btn-sm del">删除</button>
              </td>
          </tr>
          `);
      });
      $('tbody').html(arr.join(''))
    }
  })
}
renderStudent();

添加 -- 分析

  • 添加使用了 bootstrap4 的模态框(弹出框),这个只需按 bootstrap 的语法写即可。

  • 表单中有“省市县”联动效果。当然也有对应的接口。

    • 开始时,先获取所有的省,放到第一个下拉框中
    • 当“省”切换的时候,获取对应的市,并放入第二个下拉框中。
    • 当“市”切换的时候,获取对应的县,并放入第三个下拉框中
  • 需要有表单验证,这个安装表单验证插件的语法写即可。

  • 一切准备就绪,提交表单数据,完成添加即可。

添加 -- 省市县联动

// --------------------- 获取省 ---------------------
axios.get('/geo/province').then(({ data: res }) => {
  // console.log(res);
  let arr = ['<option selected value="">--省--</option>'];
  res.forEach(item => {
    arr.push(`<option value="${item}">${item}</option>`)
  });
  $('select[name=province]').html(arr.join(''))
})
​
// --------------------- 省切换的时候,选择市 ---------------------
$('select[name=province]').on('change', function () {
  // 重置县
  $('select[name=county]').html(`<option selected value="">--县--</option>`);
  let pname = $(this).val();
  axios.get('/geo/city', { params: { pname } }).then(({ data: res }) => {
    // console.log(res);
    let arr = ['<option selected value="">--市--</option>'];
    res.forEach(item => {
      arr.push(`<option value="${item}">${item}</option>`)
    });
    $('select[name=city]').html(arr.join(''))
  })
})
​
// --------------------- 市切换的时候,选择县 ---------------------
$('select[name=city]').on('change', function () {
  let pname = $(this).parents('.col-sm-3').prev().find('select').val();
  let cname = $(this).val();
  axios.get('/geo/county', { params: { pname, cname } }).then(({ data: res }) => {
    // console.log(res);
    let arr = ['<option selected value="">--县--</option>'];
    res.forEach(item => {
      arr.push(`<option value="${item}">${item}</option>`)
    });
    $('select[name=county]').html(arr.join(''))
  })
})

添加 -- 表单验证

function student() {
  return {
    fields: {
      name: {
        validators: {
          notEmpty: {
            message: '姓名不能为空',
          },
          stringLength: {
            min: 2,
            max: 10,
            message: '姓名长度2~10位'
          }
        }
      },
      age: {
        validators: {
          notEmpty: {
            message: '年龄不能为空',
          },
          greaterThan: {
            value: 18,
            message: '年龄不能小于18岁'
          },
          lessThan: {
            value: 60,
            message: '年龄不能超过60岁'
          }
        }
      },
      sex: {
        validators: {
          choice: {
            min: 1,
            max: 1,
            message: '请选择性别'
          }
        }
      },
      group: {
        validators: {
          notEmpty: {
            message: '组号不能为空',
          },
          regexp: {
            regexp: /^\d{1,2}$/,
            message: '请选择有效的组号'
          }
        }
      },
      phone: {
        validators: {
          notEmpty: {
            message: '手机号不能为空',
          },
          regexp: {
            regexp: /^1\d{10}$/,
            message: '请填写有效的手机号'
          }
        }
      },
      salary: {
        validators: {
          notEmpty: {
            message: '实际薪资不能为空',
          },
          greaterThan: {
            value: 800,
            message: '期望薪资不能低于800'
          },
          lessThan: {
            value: 100000,
            message: '期望薪资不能高于100000'
          }
        }
      },
      truesalary: {
        validators: {
          notEmpty: {
            message: '实际薪资不能为空',
          },
          greaterThan: {
            value: 800,
            message: '实际薪资不能低于800'
          },
          lessThan: {
            value: 100000,
            message: '实际薪资不能高于100000'
          }
        }
      },
      province: {
        validators: {
          notEmpty: {
            message: '省份必填',
          },
        }
      },
      city: {
        validators: {
          notEmpty: {
            message: '市必填',
          },
        }
      },
      county: {
        validators: {
          notEmpty: {
            message: '县必填',
          },
        }
      },
    }
  }
}

添加 -- 完成添加

// 添加学生
$('.add-form').bootstrapValidator(student()).on('success.form.bv', function (e) {
  e.preventDefault();
  //提交逻辑
  // console.log(222);
  let data = $(this).serialize();
  // console.log(data);
  axios.post('/student/add', data).then(({ data: res }) => {
    // console.log(res);
    if (res.code === 0) {
      toastr.success(res.message);
      renderStudent();
      $('#addModal').modal('hide') // 这个是关闭模态框(弹出框)的意思
    }
  })
});

修改 -- 分析

  • 修改中,也会用到省市县联动。但是不用再写代码了,前面添加的时候已经写过了。

  • 修改时,表单(每个输入框)不能为空,必须设置好默认的value值,这个操作叫做数据回填。所有的修改操作都需要数据回填

    • 点击 修改 按钮
    • 发送请求,获取该学员的信息。
    • 得到数据后,设置每个输入框的value值。下拉框和单项按钮需设置选中状态。
  • 修改接口,需要学员id,所以在修改表单中,加入 <input type="hidden" name="id" />

  • 最后,按照接口文档,完成修改即可。

修改 -- 数据回填

遍历数据时,用 data-id 属性,存储该学员的 id

arr.push(`
      <tr>
          <th scope="row">${item.id}</th>
          <td>${item.name}</td>
          <td>${item.age}</td>
          <td>${item.sex}</td>
          <td>${item.group}</td>
          <td>${item.phone}</td>
          <td>${item.salary}</td>
          <td>${item.truesalary}</td>
          <td>${item.province}${item.city}${item.county}</td>
          <td>
+          <button data-id="${item.id}" type="button" class="btn btn-primary btn-sm update">修改</button>
          <button type="button" class="btn btn-danger btn-sm del">删除</button>
          </td>
      </tr>
      `);

JS代码如下:

// 修改学员 --- 数据回填
$('tbody').on('click', '.update', function () {
  let id = $(this).data('id');
  axios.get('/student/one', { params: { id } }).then(({ data: res }) => {
    if (res.code === 0) {
      // console.log(res.data);
      let { name, age, sex, group, phone, id, salary, truesalary, province, city, county } = res.data;
      $('#updateModal input[name=id]').val(id);
      $('#updateModal input[name=name]').val(name);
      $('#updateModal input[name=age]').val(age);
      $('#updateModal input[name=sex][value=' + sex + ']').prop('checked', true);
      $('#updateModal input[name=phone]').val(phone);
      $('#updateModal input[name=salary]').val(salary);
      $('#updateModal input[name=truesalary]').val(truesalary);
      $('#updateModal select[name=group]').children('[value=' + group + ']').prop('selected', true);
      $('#updateModal select[name=province]').children('[value=' + province + ']').prop('selected', true);
      $('#updateModal select[name=city]').html(`<option value="${city}">${city}</option>`);
      $('#updateModal select[name=county]').html(`<option value="${county}">${county}</option>`);
​
      $('#updateModal').modal('show'); // 这行的意思是让模态框显示
    }
  })
})

修改 -- 完成修改

// 修改学员 --- 确认修改
$('.update-form').bootstrapValidator(student()).on('success.form.bv', function (e) {
  e.preventDefault();
  //提交逻辑
  // console.log(222);
  let data = $(this).serialize();
  // console.log(data);
  axios.put('/student/update', data).then(({ data: res }) => {
    // console.log(res);
    if (res.code === 0) {
      toastr.success(res.message);
      renderStudent();
      $('#updateModal').modal('hide')
    }
  })
});

删除操作

遍历数据时,用 data-id 属性,存储该学员的 id

arr.push(`
      <tr>
          <th scope="row">${item.id}</th>
          <td>${item.name}</td>
          <td>${item.age}</td>
          <td>${item.sex}</td>
          <td>${item.group}</td>
          <td>${item.phone}</td>
          <td>${item.salary}</td>
          <td>${item.truesalary}</td>
          <td>${item.province}${item.city}${item.county}</td>
          <td>
          <button data-id="${item.id}" type="button" class="btn btn-primary btn-sm update">修改</button>
+          <button data-id="${item.id}" type="button" class="btn btn-danger btn-sm del">删除</button>
          </td>
      </tr>
      `);

JS代码如下:

// 删除学员
$('tbody').on('click', '.del', function () {
  if (!confirm('你确定要删除吗?')) return;
  let id = $(this).data('id');
  axios.delete('/student/delete', { params: { id } }).then(({ data: res }) => {
    if (res.code === 0) toastr.success(res.message); renderStudent();
  })
})

最后是原图

wallhaven-kw6pzd.jpg