下载工作台
jQuery 与 Layui 实战

jQuery + Layui 后台 CRUD 全流程

试读上半部分 · 解锁后可读全文

第 11 章 · jQuery + Layui 后台 CRUD 全流程

本章目标:串联列表页 table + 搜索 form、新增/编辑 layer 弹窗 + form Ajax、删除 confirm + POST/DELETE;实现权限菜单、loading 与统一错误提示;完成 admin-demo 商品管理模块完整 walkthrough,对标行业常见 Layui 商户后台。

学时建议:5 小时(含 2 小时跟练)

前置:ch07~ch10 全部内容;ch04 Ajax;建议对照 admin-demo 的 product-list.html


11.1 后台 CRUD 页面模式

示例电商后台 admin-demo 一条业务线(商品)典型拆分为:

商品管理
├── product-list.html    列表 + 搜索 + table + 行操作
├── product-edit.html    表单(iframe 子页或独立页)
├── product-list.js      列表逻辑
├── api/product.js       接口函数(可选)
└── mock/products.json   联调前数据

CRUD 与 Layui 组件映射

操作Layui / jQuery
C reatetoolbar「新增」→ layer.open 空表单
R eadtable.render + 详情 open
U pdate行「编辑」→ 带 id 打开表单 → PUT
D eletelayer.confirmDELETE
搜索form.on('submit')table.reload

行业常见做法无独立 api.js,逻辑写在页面内联脚本或模板 {% block JS %};admin-demo 建议逐步抽到 js/ 便于 ch12 迁移对照。


11.2 列表页:table + 搜索 form

11.2.1 HTML 骨架

product-list.html:页头说明 + layui-form 搜索(keywordstatus)+ #productTable;模板 #productToolbar(新增)、#productBar(编辑/删除)。布局同 ch09 订单列表,字段换为商品维度。

11.2.2 初始化 table

var searchCache = {};

function initProductTable() {
  table.render({
    id: 'productTable',
    elem: '#productTable',
    url: API_BASE + '/api/products',
    method: 'get',
    height: 'full-140',
    toolbar: '#productToolbar',
    defaultToolbar: ['filter', 'exports'],
    where: searchCache,
    request: { pageName: 'page', limitName: 'limit' },
    parseData: parseListResponse,
    cols: [[
      { type: 'numbers', title: '序号', width: 60 },
      { field: 'id', title: 'ID', width: 80 },
      { field: 'name', title: '商品名', minWidth: 180 },
      { field: 'price', title: '价格', width: 100,
        templet: function (d) { return '¥' + d.price; }
      },
      { field: 'stock', title: '库存', width: 80 },
      { field: 'status', title: '状态', width: 90, templet: '#statusTpl' },
      { fixed: 'right', title: '操作', toolbar: '#productBar', width: 140 }
    ]],
    page: { limit: 10, limits: [10, 20, 50] },
    done: onTableDone
  });
}
form.on('submit(searchProduct)', function (data) {
  searchCache = data.field;
  table.reload('productTable', {
    where: searchCache,
    page: { curr: 1 }
  });
  return false;
});

product-list.html 的搜索表单、table.reload 完全一致。


11.3 新增/编辑:layer 弹窗 + form 提交

11.3.1 打开弹窗

function openProductForm(id) {
  var title = id ? '编辑商品' : '新增商品';
  var url = 'product-edit.html' + (id ? '?id=' + id : '');
  layer.open({
    type: 2,
    title: title,
    shade: 0.3,
    maxmin: true,
    area: ['1000px', '750px'],
    content: url,
    end: function () {
      table.reload('productTable');
    }
  });
}

table.on('toolbar(productTable)', function (obj) {
  if (obj.event === 'add') openProductForm();
});

table.on('tool(productTable)', function (obj) {
  if (obj.event === 'edit') openProductForm(obj.data.id);
});

11.3.2 子页提交(ch08 扩展)

// product-edit.html 内
form.on('submit(saveProduct)', function (data) {
  var field = normalizeProductField(data.field);
  var isEdit = !!field.id;
  $.ajax({
    url: API_BASE + '/api/products' + (isEdit ? '/' + field.id : ''),
    type: isEdit ? 'PUT' : 'POST',
    contentType: 'application/json',
    data: JSON.stringify(field),
    headers: authHeaders(),
    success: function (res) {
      if (res.code === 0) {
        XZ.ok('保存成功', function () {
          closeSelfLayer();
        });
      } else {
        XZ.fail(res.msg);
      }
    },
    error: XZ.onHttpError
  });
  return false;
});

父页传参替代 URL 查询(行业常见:弹窗内操作 iframe DOM):

success: function (layero, index) {
  var iframe = layero.find('iframe')[0];
  var child = iframe.contentWindow;
  if (child && child.setProductId) child.setProductId(rowData);
}

两种任选,团队统一即可。


11.4 删除:confirm + Ajax

table.on('tool(productTable)', function (obj) {
  if (obj.event === 'del') {
    XZ.confirm({
      title: '删除商品',
      html: '<p>确定删除 <b>' + obj.data.name + '</b>?</p>' +
            '<p style="color:#999;font-size:12px;">有未完成订单时请勿删除</p>',
      onOk: function () {
        var load = layer.load(1);
        $.ajax({
          url: API_BASE + '/api/products/' + obj.data.id,
          type: 'DELETE',
          headers: authHeaders(),
          success: function (res) {
            layer.close(load);
            if (res.code === 0) {
              XZ.ok('已删除');
              obj.del();
            } else {
              XZ.fail(res.msg);
            }
          },
          error: function () {
            layer.close(load);
            XZ.onHttpError();
          }
        });
      }
    });
  }
});

行业常见删除后 location.reload();教学项目优先 obj.del()table.reload 减少闪烁。


11.5 权限菜单

11.5.1 菜单配置

// js/menu-config.js

以下内容需解锁后阅读

试读已结束。解锁本章 ¥5.00,或开通年度会员畅读全部教程。
年度会员 ¥199.00/年; 小紫 AI 工作台有效会员 ¥99.00/年

正文仅在服务端鉴权后下发,未付费无法获取下半部分内容。