给表单增加筛选功能的通用实践二
模板+JS
筛选UI:直接在表头下方()为每一列加了 <input> 或 <select>,实现了单号、门店、日期、状态、创建人等字段的筛选。
DataTables配置:
serverSide: true,即后端分页、排序、筛选。
通过 table.column(i).search(...) 实现每列的前端筛选交互,DataTables 会自动把这些搜索条件作为参数传给后端。
下拉选项是通过 DataTables 初始化后自动收集当前页数据的唯一值生成的。
优点:无需额外表单,筛选体验和 DataTables 原生风格一致,支持多列组合筛选。
缺点:如果后端API没有正确处理所有列的搜索参数,可能筛选不生效或只筛选当前页。
{% block content %}
<div class="container-fluid">
<!-- <h1 class="h3 mb-4 text-gray-800">补货单列表</h1> -->
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-start">
<h3 class="m-0 font-weight-bold text-primary">补货单列表</h6>
<a href="{% url 'store:store_replenishment_create' %}" class="btn btn-primary btn-sm ms-4">新建补货单</a>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered table-sm align-middle" id="dataTable" style="font-size:14px;">
<thead>
<tr>
<th data-priority="1" style="min-width: 120px; width: 17%;">补货单号</th>
<th data-priority="5" style="min-width: 80px; width: 10%;">门店</th>
<th data-priority="3" style="min-width: 100px; width: 12%;">补货日期</th>
<th data-priority="1" style="min-width: 80px; width: 10%;">状态</th>
<th data-priority="4" style="min-width: 80px; width: 10%;">创建人</th>
<th data-priority="1" style="min-width: 90px; width: 10%;">总金额</th>
<th data-priority="1" style="min-width: 120px; width: 13%;">操作</th>
</tr>
<tr class="filter-row">
<th><input type="text" class="form-control form-control-sm" placeholder="筛选单号"></th>
<th><select class="form-select form-select-sm"><option value="">全部</option></select></th>
<th><input type="text" class="form-control form-control-sm" placeholder="筛选日期"></th>
<th><select class="form-select form-select-sm"><option value="">全部</option></select></th>
<th><input type="text" class="form-control form-control-sm" placeholder="筛选创建人"></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{% for order in replenishment_orders %}
<tr>
<td>{{ order.order_number }}</td>
<td>{{ order.store.name }}</td>
<!-- <td class="d-none d-md-table-cell" style="max-width:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">{{ order.warehouse.name }}</td> -->
<td>{{ order.order_date }}</td>
<td>
<span class="badge bg-{{ order.status|get_replenishment_status_style }}" style="font-size:0.95em;">
{{ order.get_status_display }}
</span>
</td>
<td>{{ order.created_by.username }}</td>
<td>¥{{ order.total_amount|default:"0.00" }}</td>
<td>
<a href="{% url 'store:store_replenishment_detail' order.pk %}" class="btn btn-info btn-sm me-1">详情</a>
{% if order.status == 'draft' or order.status == 'pending_review' %}
<a href="{% url 'store:store_replenishment_update' order.pk %}" class="btn btn-warning btn-sm me-1">编辑</a>
<a href="{% url 'store:store_replenishment_delete' order.pk %}" class="btn btn-danger btn-sm">删除</a>
{% endif %}
</td>
</tr>
{% empty %}
<tr>
<td colspan="7" class="text-center">暂无补货单数据</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
{% endblock %}
{% block store_js %}
<script>
$(document).ready(function() {
var table = $('#dataTable').DataTable({
serverSide: true,
processing: true,
ajax: {
url: "{% url 'store:store_replenishment_list_api' %}",
type: 'POST',
data: function(d) {
d.csrfmiddlewaretoken = '{{ csrf_token }}';
}
},
language: {
url: '/static/js/datatables.zh-cn.json'
},
orderCellsTop: true,
fixedHeader: true,
pageLength: 10,
columns: [
{ title: '补货单号' },
{ title: '门店' },
{ title: '补货日期' },
{ title: '状态' },
{ title: '创建人' },
{ title: '总金额' },
{ title: '操作', orderable: false, searchable: false }
]
});
// 下拉筛选列索引
var selectColumns = [1, 2, 4];
table.on('init', function() {
selectColumns.forEach(function(colIdx) {
var column = table.column(colIdx);
var select = $(column.header()).closest('table').find('thead tr.filter-row th').eq(colIdx).find('select');
var uniqueData = column.data().unique().sort();
select.empty().append('<option value="">全部</option>');
uniqueData.each(function(d) {
var val = $('<div>').html(d).text().trim();
// 对状态列(4)特殊处理,提取纯文本
if (colIdx === 4) {
val = $(d).text().trim();
}
if (val && select.find('option[value="'+val+'"]').length === 0) {
select.append('<option value="'+val+'">'+val+'</option>');
}
});
});
});
// 每列筛选
$('#dataTable thead tr.filter-row th').each(function(i) {
var input = $(this).find('input');
var select = $(this).find('select');
if (input.length) {
input.on('keyup change', function() {
if (table.column(i).search() !== this.value) {
table.column(i).search(this.value).draw();
}
});
}
if (select.length) {
select.on('change', function() {
var val = $(this).val();
if (i === 4) {
// 状态列筛选,匹配纯文本
table.column(i).search(val ? val : '', false, false).draw();
} else {
table.column(i).search(val ? '^'+$.fn.dataTable.util.escapeRegex(val)+'$' : '', true, false).draw();
}
});
}
});
});
</script>
{% endblock %}
模板+JS
- 筛选UI:直接在表头下方(
<tr class="filter-row">)为每一列加了<input>或<select>,实现了“单号、门店、日期、状态、创建人”等字段的筛选。 - DataTables配置:
serverSide: true,即后端分页、排序、筛选。- 通过
table.column(i).search(...)实现每列的前端筛选交互,DataTables 会自动把这些搜索条件作为参数传给后端。 - 下拉选项是通过 DataTables 初始化后自动收集当前页数据的唯一值生成的。
- 优点:无需额外表单,筛选体验和 DataTables 原生风格一致,支持多列组合筛选。
- 缺点:如果后端API没有正确处理所有列的搜索参数,可能筛选不生效或只筛选当前页。
筛选UI
- 筛选UI:在表格上方单独放一个
<form>,每个字段一个输入框或下拉,点击“筛选”按钮后刷新表格。 - DataTables配置:
- 通过
ajax.data显式将表单的值作为参数传递给后端API。 - 可以自定义筛选字段、类型、交互方式。
- 通过
- 优点:筛选参数传递更直观,后端API处理更灵活,适合复杂筛选和自定义表单。
- 缺点:不如表头筛选那样“所见即所得”,但更适合复杂业务。
主要区别
| 表头筛选 | 表单筛选 | |
|---|---|---|
| UI位置 | 表头下方每列 | 表格上方单独表单 |
| 参数传递 | DataTables自动处理每列search | 手动将表单值加到ajax.data |
| 适用场景 | 简单、列筛选为主 | 复杂、多条件、跨列筛选 |
| 后端API | 需支持DataTables的search参数 | 需支持自定义参数名 |
方法是否足够?
- 如果表头筛选已经能满足业务需求(如所有字段都能正确筛选,后端API能正确处理DataTables的search参数),可以继续用你的方法,无需切换。
- 如果需要更复杂的筛选逻辑(如跨列、范围、模糊、组合等),推荐用表单+自定义参数的方式,后端处理更灵活。

浙公网安备 33010602011771号