Skip to content

flask + layui 实现学员信息案例

视频地址: https://www.bilibili.com/video/BV1jX4y1t7EH/

接下来演示用 flask + layui 搭建一个学员信息管理的案例

这个案例将会利用 flask 做后端,layui table 组件做前端,基于 restful api 完成一个学员信息管理的完整案例。 案例内容涉及的知识点会比较多,但是对于基础,我在案例中也不会过多赘述,当看不懂时可以查阅一下官方文档或者补一下对应的基础知识。

项目搭建

  1. 新建 flask 项目
  2. 下载 layui 文件,解压之后复制到指定文件
  3. 配置 flask-sqlalchemy 插件
  4. 完成数据库建模,数据表创建

创建项目

创建项目的方式有很多种,只要有任意一种方式创建即刻。在这里我直接选择使用 pycharm 进行创建。

创建成功后目录如下

X:\flask-student>tree /f
X:.
│  app.py

├─static
└─templates

访问 https://layui.dev/ ,点击直接下载之后就能得到 layui 的静态文件,将其复制到 static 目录下,完成之后的目录结构如下

txt
X:\flask-student>tree /f
X:.
│  app.py

├─static
│  │  layui.js
│  │
│  ├─css
│  │      layui.css
│  │
│  └─font
│          iconfont.eot
│          iconfont.svg
│          iconfont.ttf
│          iconfont.woff
│          iconfont.woff2

└─templates

配置插件

1、安装插件

shell
pip install flask-sqlalchemy

2、添加配置文件

python
# 数据库链接配置
SQLALCHEMY_DATABASE_URI = 'sqlite:///flask-student.sqlite'
SQLALCHEMY_TRACK_MODIFICATIONS = False

3、创建实例对象

python
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()


def register_extension(app):
    db.init_app(app)

4、绑定到 flask app

python
import config
from extensions import register_extension, db


app.config.from_object(config)
register_extension(app)

数据库建模

1、创建数据模型

python
from extensions import db
from datetime import datetime


class StudentORM(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), nullable=False)
    gender = db.Column(db.Enum('M', 'F'))
    mobile = db.Column(db.String(11), nullable=False, unique=True)
    class_name = db.Column(db.String(10), nullable=True)
    address = db.Column(db.String(255))
    disable = db.Column(db.Boolean, default=False)
    is_del = db.Column(db.Boolean, default=False)

    create_at = db.Column(db.DateTime, default=datetime.now)  # 记录的创建时间
    update_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)  # 记录的更新时间

    def save(self):
        db.session.add(self)
        db.session.commit()

2、生成数据表

python
from orms import StudentORM

@app.cli.command()
def create():
    db.drop_all()
    db.create_all()
    from faker import Faker
    import random

    faker = Faker(locale="zh-CN")

    for i in range(100):
        student = orms.StudentORM()
        info = faker.simple_profile()
        student.name = info['name']
        student.gender = info['sex']
        student.mobile = faker.phone_number()
        student.address = info['address']
        student.class_name = random.choice(['一班', '二班', '三班'])
        student.save()

学员信息渲染

用户
用户
浏览器
浏览器
后端服务器
后端服务器
访问信息页面
访问信息页面
MySql
MySql
返回页面
返回页面
渲染数据
渲染数据
请求接口数据
请求接口数据
返回接口数据
返回接口数据
显示到页面上
显示到页面上
查询后端数据
查询后端数据
返回原始数据
返回原始数据
Text is not SVG - cannot display

需要完成的工作如下:

  1. 返回学员信息页面,在前端请求后端接口数据
  2. 返回学员信息接口,并且支持分页、查询(后续待用)

后端部分

1、编写后端接口返回前端数据,返回数据

python
@app.route('/api/student')
def student_view():
    page = request.args.get('page', type=int, default=1)
    per_page = request.args.get('per_page', type=int, default=10)
    paginate = StudentORM.query.paginate(page=page, per_page=per_page, error_out=False)
    items: [StudentORM] = paginate.items
    return {
        'code': 0,
        'msg': '信息查询成功',
        'count': paginate.total,
        'data': [
            {
                'id': item.id,
                'name': item.name,
                'gender': item.gender,
                'mobile': item.mobile,
                'class_name': item.class_name,
                'address': item.address,
                'disable': item.disable,
                'is_del': item.is_del,
                'create_at': item.create_at.strftime('%Y-%m-%d %H:%M:%S'),
                'update_at': item.update_at.strftime('%Y-%m-%d %H:%M:%S'),
            } for item in items
        ]
    }

2、后端返回静态页面

python
@app.route('/')
def hello_world():
    return render_template('index.html')

前端部分

  1. 表格获取,数据渲染
html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>学生信息管理系统</title>
    <link rel="stylesheet" href="/static/css/layui.css">
</head>
<body>
<div class="layui-container">
    <table class="layui-hide" id="student"></table>
</div>
<script src="/static/layui.js"></script>
<script>
    layui.use(function () {
        var table = layui.table;

        // 已知数据渲染
        var inst = table.render({
            elem: '#student',
            id: 'student',
            url: '/api/student',
            cols: [
                [   // 标题栏
                    {field: 'id', title: 'ID', width: 60, sort: true},
                    {field: 'name', title: '姓名', width: 100},
                    {field: 'gender', title: '性别', width: 60},
                    {field: 'class_name', title: '班级', width: 80},
                    {field: 'mobile', title: '手机', width: 120},
                    {field: 'address', title: '地址', minWidth: 160},
                    {field: 'create_at', title: '创建时间', width: 200},
                    {field: 'disable', title: '禁用', width: 100},
                    {field: 'is_del', title: '删除', width: 80},
                ]],
            page: true,
        });
    });
</script>
</body>
</html>

2、性别渲染

先定义渲染方法

javascript
function render_gender(d) {
    if (d.gender === 'M') {
        return '<span style="color: blue">♂</span>';
    } else {
        return '<span style="color: pink">♀</span>';
    }
}

再进行渲染

{field: 'gender', title: '性别', width: 60, templet: render_gender},

后面还有好几个字段需要进行渲染与修改,例如班级字段需要实现点击下拉选择,地址信息直接编辑修改,创建时间点击之后选择新的日期进行修改,禁用、删除用开关进行点击切换。关于这部分的内容,将放到最后进行修改。

新增学员信息

用户
用户
浏览器
浏览器
后端服务器
后端服务器
请求新增页面
请求新增页面
MySql
MySql
返回新增页面
返回新增页面
校验通过提交后端
校验通过提交后端
校验学员信息
校验学员信息
录入学员信息
录入学员信息
校验失败提示错误
校验失败提示错误
校验通过保存数据库
校验通过保存数据库
校验学员信息
校验学员信息
将校验结果返回给前端
将校验结果返回给前端
校验返回结果
成功需要关闭当前弹窗
校验返回结果 成功需要关闭当前弹窗
返回信息
返回信息
以 iframe 的形式弹窗展示
以 iframe 的形式弹窗展示
返回新增页面
返回新增页面
Text is not SVG - cannot display

请求新增页面

  1. 添加表格头部栏新增按钮
  2. 添加新增案例的点击事件
  3. 前端发起请求,用 iframe 显示到新的页面
  4. 后端返回 html 模板

1、新增表格头部工具栏

html
<!-- 工具栏模板 -->
<script type="text/html" id="toolbar">
    <div class="layui-btn-container">
        <button class="layui-btn layui-btn-sm" lay-event="add">添加</button>
        <button class="layui-btn layui-btn-sm" lay-event="delete_many">删除</button>
    </div>
</script>

2、添加表格工具栏事件

js
// 已知数据渲染
var inst = table.render({
    elem: '#student',
    id: 'student',
    url: '/api/student',
    toolbar: '#toolbar',
    cols: [[/*...*/]],
    page: true,
});

3、添加点击事件

js
// 头部工具栏事件
table.on('toolbar(student)', function (obj) {
    var options = obj.config; // 获取当前表格属性配置项
    var checkStatus = table.checkStatus(options.id); // 获取选中行相关数据

    // 根据不同的事件名进行相应的操作
    switch (obj.event) { // 对应模板元素中的 lay-event 属性值
        case 'add':
            window.show_add()
            break;
        case 'delete_many':
            window.delete_many(obj)
            break;
    }
});

window.show_add = function () {
    layer.msg('添加');
}

window.delete_many = function (obj) {
    layer.msg('批量删除');
}

4、显示新增表单

4.1、新建子页 html

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/css/layui.css">
</head>
<body>

新增表单

<script src="/static/layui.js"></script>
<script>
    layui.use(function () {
        var $ = layui.$;
        var form = layui.form;

    })
</script>
</body>
</html>

4.2、后端返回模板

python
@app.get('/student_add')
def student_add():
    return render_template('student_add.html')

4.3、前端发起请求

js
window.show_add = function () {
    layer.open({
        type: 2,
        title: '新增学员',
        shadeClose: true,
        maxmin: true, //开启最大化最小化按钮
        area: ['900px', '600px'],
        content: '/student_add'
    });
}

新增逻辑

  1. 添加新增表单
  2. 前端录入数据之后进行校验
  3. 校验成功之后提交到后端
  4. 后端接收数据添加到数据库
  5. 前端关闭添加页面

1、添加新增表单

html

<form class="layui-form" action="" lay-filter="form_add" style="margin-top: 20px">
    <div class="layui-form-item">
        <label class="layui-form-label">姓名</label>
        <div class="layui-input-block">
            <input type="text" name="name" lay-verify="required" autocomplete="off" placeholder="请输入"
                   class="layui-input">
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">性别</label>
        <div class="layui-input-block">
            <select name="gender" lay-verify="required">
                <option value=""></option>
                <option value="M">男</option>
                <option value="F">女</option>
            </select>
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">班级</label>
        <div class="layui-input-block">
            <select name="class_name" lay-verify="required">
                <option value=""></option>
                <option value="一班">一班</option>
                <option value="二班">二班</option>
                <option value="三班">三班</option>
            </select>
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">手机号</label>
        <div class="layui-input-block">
            <input type="text" name="mobile" lay-verify="required|phone" autocomplete="off" placeholder="请输入"
                   class="layui-input">
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">地址</label>
        <div class="layui-input-block">
            <textarea name="address" class="layui-textarea" placeholder="请输入地址"></textarea>
        </div>
    </div>
    <div class="layui-form-item">
        <div class="layui-inline">
            <label class="layui-form-label">创建时间</label>
            <div class="layui-input-inline">
                <input type="text" class="layui-input" name="create_at" id="create-at"
                       placeholder="" autocomplete="off" lay-verify="required">
            </div>
        </div>
    </div>
    <div class="layui-form-item" pane>
        <label class="layui-form-label">禁用</label>
        <div class="layui-input-block">
            <input type="checkbox" name="disable" lay-skin="switch" lay-filter="switchTest" title="是|否">
        </div>
    </div>
    <div class="layui-form-item">
        <div class="layui-input-block">
            <button type="submit" class="layui-btn" lay-submit lay-filter="add-commit">立即提交</button>
            <button type="reset" class="layui-btn layui-btn-primary">重置</button>
        </div>
    </div>
</form>

2、监控提交事件

html

<script>
    layui.use(function () {
        var $ = layui.$;
        var form = layui.form;
        var laydate = layui.laydate;

        // 提交事件
        form.on('submit(add-commit)', function (data) {
            var field = data.field; // 获取表单字段值
            console.log(field)
            // 此处可执行 Ajax 等操作
            // …
            return false; // 阻止默认 form 跳转
        });

        // 日期时间选择器
        laydate.render({
            elem: '#create-at',
            type: 'datetime'
        });
    })
</script>

3、检测数据并进行提交

javascript
// 新增提交的方法
const add_student = async (data) => {
    const options = {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify(data)
    }
    const response = await fetch('/api/student', options)
    return await response.json()
}

// 提交事件
form.on('submit(add-commit)', function (data) {
    var field = data.field; // 获取表单字段值
    console.log(field)
    // 此处可执行 Ajax 等操作
    field.disable = field?.disable ? true : false

    add_student(field).then(function (ret) {

        // 提交成功之后的回调
        if (!ret.code) {
            layer.msg(ret.msg, {
                icon: 1,
                time: 1000,
            }, function () {
                parent.layer.close(parent.layer.getFrameIndex(window.name)); //关闭当前页
                parent.layui.table.reload('student');
            });
        } else {
            layer.msg(ret.msg, {
                icon: 2,
                time: 1000,
            });
        }
    })

    // …
    return false; // 阻止默认 form 跳转
});

4、后端模型添加方法

python
class StudentORM(db.Model):
    ...

    def save(self):
        db.session.add(self)
        db.session.commit()

    def update(self, data):
        for key, value in data.items():
            setattr(self, key, value)

5、接口完成新增逻辑

python
@app.post('/api/student')
def api_student_post():
    data = request.get_json()
    data['create_at'] = datetime.strptime(data['create_at'], '%Y-%m-%d %H:%M:%S')
    student = StudentORM()
    student.update(data)
    try:
        student.save()
    except Exception as e:
        return {
            'code': -1,
            'msg': '新增数据失败'
        }
    return {
        'code': 0,
        'msg': '新增数据成功'
    }

修改学员信息

修改学员信息,因为表格中会加载完整的学员信息,所以在修改的时候可以在新的 iframe 页面请求数据加载,或者是利用原本已经加载的数据进行修改。

iframe 版本

这个版本与添加的逻辑差不多,只是需要多一次请求。因为大部分逻辑与新增差不多,在这里我就不写了,如果自己实现看下方的时序图即可。

用户
用户
浏览器
浏览器
后端服务器
后端服务器
请求修改页面
请求修改页面
MySql
MySql
返回修改页面(可选 jinja2 渲染)
返回修改页面(可选 jinja2 渲染)
请求学员信息(jinja2跳过)
请求学员信息(jinja2跳过)
返回渲染之后的页面
返回渲染之后的页面
查询数据库
查询数据库
返回学员信息
返回学员信息
将数据渲染到修改页面
将数据渲染到修改页面
以 iframe 的形式弹窗展示
以 iframe 的形式弹窗展示
返回修改页面
返回修改页面
请求学员信息
请求学员信息
校验通过提交后端
校验通过提交后端
校验学员信息
校验学员信息
修改学员信息
修改学员信息
校验失败提示错误
校验失败提示错误
校验通过保存数据库
校验通过保存数据库
校验学员信息
校验学员信息
将校验结果返回给前端
将校验结果返回给前端
校验返回结果
成功需要关闭当前弹窗
校验返回结果 成功需要关闭当前弹窗
返回信息
返回信息
返回学员数据
返回学员数据
Text is not SVG - cannot display

单页版

单页面是利用原来表格中已有的数据,将其设置到之前已经定义好的表单中,然后将表单显示出来,修改完之后再将表单设置为空,然后影藏。

用户
用户
浏览器
浏览器
后端服务器
后端服务器
请求修改页面
请求修改页面
MySql
MySql
渲染已有数据,弹窗展示
渲染已有数据,弹窗展示
返回修改页面
返回修改页面
校验通过提交后端
校验通过提交后端
校验学员信息
校验学员信息
修改学员信息
修改学员信息
校验失败提示错误
校验失败提示错误
校验通过保存数据库
校验通过保存数据库
校验学员信息
校验学员信息
将校验结果返回给前端
将校验结果返回给前端
校验返回结果
成功需要关闭当前弹窗
校验返回结果 成功需要关闭当前弹窗
返回信息
返回信息
Text is not SVG - cannot display

1、新增表格单元格事件

html
<!-- 表头某列 templet 属性指向的模板 -->
<script type="text/html" id="tools">
    <a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
    <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>

挂载表格栏事件

{title: '操作', width: 120, templet: '#tools'}

2、添加编辑、删除事件

js
// 单元格工具事件
table.on('tool(student)', function (obj) {
    var layEvent = obj.event; // 获得元素对应的 lay-event 属性值
    if (layEvent === 'edit') { //编辑
        window.show_edit(obj)
    } else if (layEvent === 'del') { //删除
        layer.confirm('确定删除吗?', function (index) {
            layer.close(index);
            window.del_student(obj)
        });
    }
});

window.show_edit = (obj) => {
    layer.msg('显示编辑表单')
}

window.del_student = (obj) => {
    layer.msg('删除学生数据')
}

3、新增修改表单

html

<form class="layui-form" action="" id="form_edit" lay-filter="form_edit" style="margin-top: 20px;display: none">
    <div class="layui-form-item" style="display: none">
        <label class="layui-form-label">ID</label>
        <div class="layui-input-block">
            <input type="text" name="id" lay-verify="required" autocomplete="off" placeholder="请输入"
                   class="layui-input">
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">姓名</label>
        <div class="layui-input-block">
            <input type="text" name="name" lay-verify="required" autocomplete="off" placeholder="请输入"
                   class="layui-input">
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">性别</label>
        <div class="layui-input-block">
            <select name="gender" lay-verify="required">
                <option value=""></option>
                <option value="M">男</option>
                <option value="F">女</option>
            </select>
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">班级</label>
        <div class="layui-input-block">
            <select name="class_name" lay-verify="required">
                <option value=""></option>
                <option value="一班">一班</option>
                <option value="二班">二班</option>
                <option value="三班">三班</option>
            </select>
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">手机号</label>
        <div class="layui-input-block">
            <input type="text" name="mobile" lay-verify="required|phone" autocomplete="off" placeholder="请输入"
                   class="layui-input">
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">地址</label>
        <div class="layui-input-block">
            <textarea name="address" class="layui-textarea" placeholder="请输入地址"></textarea>
        </div>
    </div>
    <div class="layui-form-item">
        <div class="layui-inline">
            <label class="layui-form-label">创建时间</label>
            <div class="layui-input-inline">
                <input type="text" class="layui-input" name="create_at" id="create-at"
                       placeholder="" autocomplete="off" lay-verify="required">
            </div>
        </div>
    </div>
    <div class="layui-form-item" pane>
        <label class="layui-form-label">禁用</label>
        <div class="layui-input-block">
            <input type="checkbox" name="disable" lay-skin="switch" lay-filter="switchTest" title="是|否">
        </div>
    </div>
    <div class="layui-form-item">
        <div class="layui-input-block">
            <button type="submit" class="layui-btn" lay-submit lay-filter="edit-commit">立即提交</button>
            <button type="reset" class="layui-btn layui-btn-primary">重置</button>
        </div>
    </div>
</form>

4、加载数据并显示表单

js
// 日期时间选择器
laydate.render({
    elem: '#create-at',
    type: 'datetime'
});

window.show_edit = (obj) => {
    console.log(obj.data)

    form.val('form_edit', obj.data);

    layer.open({
        type: 1,
        area: ['900px', '600px'],
        content: $('#form_edit'), // 捕获的元素
    });
}

5、校验数据并完成提交

js
// 新增提交的方法
const change_student = async (id, data) => {
    const options = {
        method: 'PUT',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify(data)
    }
    const response = await fetch(`/api/student/${id}`, options)
    return await response.json()
}

// 提交事件
form.on('submit(edit-commit)', function (data) {
    var field = data.field; // 获取表单字段值
    // console.log(field)
    // 此处可执行 Ajax 等操作
    field.disable = field?.disable ? true : false

    change_student(field.id, field).then(function (ret) {

        // 提交成功之后的回调
        if (!ret.code) {
            layer.msg(ret.msg, {
                icon: 1,
                time: 1000,
            }, function () {
                layer.closeAll('page');
                table.reload('student');
            });
        } else {
            layer.msg(ret.msg, {
                icon: 2,
                time: 1000,
            });
        }
    })

    return false; // 阻止默认 form 跳转
});

6、后端完成修改

python
class StudentORM(db.Model):
    ...

    def update(self, data):
        for key, value in data.items():
            setattr(self, key, value)


@app.put('/api/student/<int:sid>')
def api_student_put(sid):
    data = request.get_json()
    data['create_at'] = datetime.strptime(data['create_at'], '%Y-%m-%d %H:%M:%S')
    # student = StudentORM.query.get(sid)
    student = db.get_or_404(StudentORM, sid)
    student.update(data)
    try:
        student.save()
    except Exception as e:
        return {
            'code': -1,
            'msg': '修改数据失败'
        }
    return {
        'code': 0,
        'msg': '修改数据成功'
    }

7、表单恢复初始状态

这个可以做可以不做,因为下次显示的时候会再次覆盖

删除学员信息

删除数据时有两种方式,一种是物理删除,另一种是逻辑删除。物理删除就是从数据库中彻底删除,而逻辑删除则是在数据库中保留,但是不再显示给用户。就像被 404 的微信公众号文章,虽然在公众号上无法找到也无法查看,但是在腾讯的服务器中还保留着,说不定未来某一天还能再次看到。

1、前端请求删除

js
// 新增提交的方法
const del_student_api = async (id) => {
    const options = {
        method: 'DELETE',
        headers: {'Content-Type': 'application/json'},
    }
    const response = await fetch(`/api/student/${id}`, options)
    return await response.json()
}

window.del_student = (obj) => {
    // console.log(obj)
    del_student_api(obj.data.id).then(function (ret) {
        // 提交成功之后的回调
        if (!ret.code) {
            layer.msg(ret.msg, {
                icon: 1,
                time: 1000,
            }, function () {
                table.reload('student');
            });
        } else {
            layer.msg(ret.msg, {
                icon: 2,
                time: 1000,
            });
        }
    })
}

2、后端实现逻辑删除

python
@app.delete('/api/student/<int:sid>')
def api_student_del(sid):
    student: StudentORM = db.get_or_404(StudentORM, sid)
    try:
        # db.session.delete(student)
        student.is_del = True
        db.session.commit()
    except Exception as e:
        return {
            'code': -1,
            'msg': '删除数据失败'
        }
    return {
        'code': 0,
        'msg': '删除数据成功'
    }

3、查询学员的时候过滤以删除

在获取学员列表的方法里面过滤已经删除的数据,不再返回给前端。

修改学员单个信息

默认修改信息需要打开一个新的编辑页面进行操作,如果能够在表格上像 excel 一样修改,则会变得非常方便。而 layui 就提供了这么一套机制,可以直接在表格上进行修改。

修改班级

1、渲染班级列

html
<!-- 推荐 -->
<script type="text/html" id="TPL-dropdpwn-demo">
    <button class="layui-btn layui-btn-sm layui-btn-primary dropdpwn-demo">
        <span>{{ "{{= d.class_name }}"|safe }}</span>
        <i class="layui-icon layui-icon-down layui-font-12"></i>
    </button>
</script>
{field: 'class_name', title: '班级', width: 120, templet: '#TPL-dropdpwn-demo'},

2、渲染下拉组件

js
// 已知数据渲染
var inst = table.render({
    elem: '#student',
    // 表格渲染完之后再在里面渲染下拉框
    done: function (res, curr, count) {
        var options = this;
        // 获取当前行数据
        table.getRowData = function (elem) {
            var index = $(elem).closest('tr').data('index');
            return table.cache[options.id][index] || {};
        };
        // dropdown 方式的下拉选择
        dropdown.render({
            elem: '.dropdpwn-demo',
            // trigger: 'hover',
            // 此处的 data 值,可根据 done 返回的 res 遍历来赋值
            data: [{
                title: '一班',
                id: 100
            }, {
                title: '二班',
                id: 101
            }, {
                title: '三班',
                id: 102
            }],
            click: function (obj) {
                var data = table.getRowData(this.elem); // 获取当前行数据(如 id 等字段,以作为数据修改的索引)

                this.elem.find('span').html(obj.title);
                // 更新数据中对应的字段
                data.class_name = obj.title;
                // 显示 - 仅用于演示
                layer.msg('选中值: ' + obj.title + '<br>当前行数据:' + JSON.stringify(data));

            }
        });
    }
});

3、添加修改请求

js
 const change_student_class_name = async (id, data) => {
    const options = {
        method: 'PUT',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify(data)
    }
    const response = await fetch(`/api/student/${id}/class_name`, options)
    return await response.json()
}

change_student_class_name(data.id, {'class_name': data.class_name}).then(function (ret) {
    if (!ret.code) {
        layer.msg(ret.msg, {
            icon: 1,
            time: 1000,
        });
    } else {
        layer.msg(ret.msg, {
            icon: 2,
            time: 1000,
        }, function () {
            table.reload('student');
        });
    }
})

4、后端完成修改

python
@app.put('/api/student/<int:sid>/class_name')
def api_student_class_name(sid):
    student: StudentORM = db.get_or_404(StudentORM, sid)
    data = request.get_json()
    try:
        student.class_name = data['class_name']
        student.save()
    except Exception as e:
        return {
            'code': -1,
            'msg': '修改班级失败'
        }
    return {
        'code': 0,
        'msg': '修改班级成功'
    }

修改地址

1、地址列设置为可以编辑

{field: 'address', title: '地址', minWidth: 160, edit: 'textarea'}

2、前端编辑事件

内容下载 table 的 done 方法里面

js
// 单元格普通编辑事件
table.on('edit(student)', function (obj) {
    var value = obj.value // 得到修改后的值
    var data = obj.data // 得到所在行所有键值
    var field = obj.field; // 得到字段

    var send_data = {}
    send_data[field] = value

    const change_student_class_name = async (id, data) => {
        const options = {
            method: 'PUT',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify(data)
        }
        const response = await fetch(`/api/student/${id}/address`, options)
        return await response.json()
    }

    change_student_class_name(data.id, send_data).then(function (ret) {
        if (!ret.code) {
            layer.msg(ret.msg, {
                icon: 1,
                time: 1000,
            });
        } else {
            layer.msg(ret.msg, {
                icon: 2,
                time: 1000,
            }, function () {
                table.reload('student');
            });
        }
    })
});

3、后端完成修改

python
@app.put('/api/student/<int:sid>/address')
def api_student_address(sid):
    student: StudentORM = db.get_or_404(StudentORM, sid)
    data = request.get_json()
    try:
        student.address = data['address']
        student.save()
    except Exception as e:
        return {
            'code': -1,
            'msg': '修改地址失败'
        }
    return {
        'code': 0,
        'msg': '修改地址成功'
    }

修改禁用状态

1、完成禁用列的渲染

js
function render_disable(d) {
    return `<input type="checkbox" name="disable" value="${d.id}" title="是|否" lay-skin="switch" ${d.disable ? 'checked' : ''} lay-filter="switch-disable"/>`
}
{field: 'disable', title: '禁用', width: 100, templet: render_disable},

2、前端事件处理

js
form.on('switch(switch-disable)', function (obj) {
    var value = this.value;
    var name = this.name;

    const send_data = {}
    send_data[name] = !!obj.elem.checked

    const change_student_disable = async (id, data) => {
        const options = {
            method: 'PUT',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify(data)
        }
        const response = await fetch(`/api/student/${id}/disable`, options)
        return await response.json()
    }

    change_student_disable(value, send_data).then(function (ret) {
        if (!ret.code) {
            layer.msg(ret.msg, {
                icon: 1,
                time: 1000,
            });
        } else {
            layer.msg(ret.msg, {
                icon: 2,
                time: 1000,
            }, function () {
                table.reload('student');
            });
        }
    })
})

3、后端完成修改

python
@app.put('/api/student/<int:sid>/disable')
def api_student_disable(sid):
    student: StudentORM = db.get_or_404(StudentORM, sid)
    data = request.get_json()
    try:
        student.disable = data['disable']
        student.save()
    except Exception as e:
        return {
            'code': -1,
            'msg': '修改禁用失败'
        }
    return {
        'code': 0,
        'msg': '修改禁用成功'
    }

表单查询

1、新建查询表单

html

<div class="layui-container">
    <form class="layui-form layui-row layui-col-space16" style="margin: 20px 10px 20px">
        <div class="layui-col-md4">
            <div class="layui-input-wrap">
                <div class="layui-input-prefix">
                    <i class="layui-icon layui-icon-username"></i>
                </div>
                <input type="text" name="name" value="" placeholder="请输入姓名" class="layui-input" lay-affix="clear">
            </div>
        </div>
        <div class="layui-col-md4">
            <div class="layui-input-wrap">
                <input type="text" name="mobile" placeholder="请输入电话" lay-affix="clear" class="layui-input">
            </div>
        </div>
        <div class="layui-btn-container layui-col-xs12">
            <button class="layui-btn layui-btn-sm" lay-submit lay-filter="table-search">查询</button>
            <button type="reset" class="layui-btn layui-btn-primary layui-btn-sm">重置</button>
        </div>
    </form>
</div>

2、追加表单搜索事件

js
// 搜索提交
form.on('submit(table-search)', function (data) {
    var field = data.field; // 获得表单字段
    // 执行搜索重载
    table.reload('student', {
        page: {
            curr: 1, // 重新从第 1 页开始
        },
        where: field, // 搜索的字段
    });
    return false; // 阻止默认 form 跳转
});

3、改造后端逻辑,实现查询

python
@app.route('/api/student')
def student_view():
    page = request.args.get('page', type=int, default=1)
    per_page = request.args.get('per_page', type=int, default=10)

    # paginate = StudentORM.query.paginate(page=page, per_page=per_page, error_out=False)
    q = db.select(StudentORM)
    name = request.args.get('name')
    if name:
        q = q.where(StudentORM.name == name)
    paginate = db.paginate(q, page=page, per_page=per_page, error_out=False)
    items: [StudentORM] = paginate.items
    return {
        'code': 0,
        'msg': '信息查询成功',
        'count': paginate.total,
        'data': [
            {
                'id': item.id,
                'name': item.name,
                'gender': item.gender,
                'mobile': item.mobile,
                'class_name': item.class_name,
                'address': item.address,
                'disable': item.disable,
                'is_del': item.is_del,
                'create_at': item.create_at.strftime('%Y-%m-%d %H:%M:%S'),
                'update_at': item.update_at.strftime('%Y-%m-%d %H:%M:%S'),
            } for item in items
        ]
    }