Laravel Livewire动态可加行工时表失焦自动更新合计行实现方法
Livewire实现工时表失焦逐列自动合计方案
核心逻辑说明
全程不需要引入jQuery,依托Livewire原生的双向绑定、事件钩子机制即可实现「输入框失焦→同步数据到组件→自动逐列累加→视图自动更新合计值」的完整流程,和现有代码结构完全兼容。
组件层(ShowTimesheet.php)改造步骤
- 统一
$entries数组的字段结构,确保每行的周一到周日字段键名固定(如mon/tue/wed/thu/fri/sat/sun),方便循环累加。 - 新增独立属性存储每日列的合计值,避免临时计算导致的视图刷新问题。
- 封装通用的列合计计算方法,统一处理空值转数值、累加逻辑。
- 利用Livewire的更新钩子,在任意行的工时数据变化时自动触发计算,无需在每个输入框单独绑定事件。
namespace App\Http\Controllers\Livewire; use Livewire\Component; class ShowTimesheet extends Component { // 动态工时行数组 public array $entries = []; // 每日列合计值 public array $dayTotals = [ 'mon' => 0, 'tue' => 0, 'wed' => 0, 'thu' => 0, 'fri' => 0, 'sat' => 0, 'sun' => 0, ]; // 初始化页面加载时的逻辑 public function mount() { // 从数据库读取已存的工时行时,赋值给$entries后直接计算一次合计 $this->calculateColumnTotals(); } // 新增动态行 public function addEntry() { $this->entries[] = [ 'job_id' => null, 'worktype_id' => null, 'mon' => 0, 'tue' => 0, 'wed' => 0, 'thu' => 0, 'fri' => 0, 'sat' => 0, 'sun' => 0, ]; } // 逐列计算所有行的工时总和 protected function calculateColumnTotals() { // 先重置合计值,避免旧数据残留 $this->dayTotals = array_map(fn() => 0, $this->dayTotals); foreach ($this->entries as $row) { foreach ($this->dayTotals as $dayKey => $originalValue) { // 兼容空字符串、非数值类型的输入,统一转成浮点型累加 $this->dayTotals[$dayKey] += (float)($row[$dayKey] ?? 0); } } } // 钩子:只要$entries数组内的任意字段被更新,就自动触发合计计算 public function updatedEntries() { $this->calculateColumnTotals(); } public function render() { return view('livewire.timesheet'); } }
视图层(timesheet.blade.php)改造步骤
- 调整每个动态行的工时输入框的绑定规则,使用*.blur*修饰符实现「输入完成、点击离开输入框时才同步数据到后端」的触发逻辑,避免每敲一个字符就发起不必要的请求。
- 在表格底部的合计区域直接输出预先计算好的
$dayTotals值,不需要手动操作DOM更新文本。
<table> <thead> <tr> <th>关联任务</th> <th>工作类型</th> <th>周一</th> <th>周二</th> <th>周三</th> <th>周四</th> <th>周五</th> <th>周六</th> <th>周日</th> </tr> </thead> <tbody> @foreach($entries as $index => $entry) <tr> <td> <select wire:model="entries.{{ $index }}.job_id"> <!-- 任务选项 --> </select> </td> <td> <select wire:model="entries.{{ $index }}.worktype_id"> <!-- 工作类型选项 --> </select> </td> <!-- 天工时输入框,用.blur修饰符实现失焦才同步 --> <td><input type="number" step="0.5" wire:model.blur="entries.{{ $index }}.mon"></td> <td><input type="number" step="0.5" wire:model.blur="entries.{{ $index }}.tue"></td> <td><input type="number" step="0.5" wire:model.blur="entries.{{ $index }}.wed"></td> <td><input type="number" step="0.5" wire:model.blur="entries.{{ $index }}.thu"></td> <td><input type="number" step="0.5" wire:model.blur="entries.{{ $index }}.fri"></td> <td><input type="number" step="0.5" wire:model.blur="entries.{{ $index }}.sat"></td> <td><input type="number" step="0.5" wire:model.blur="entries.{{ $index }}.sun"></td> </tr> @endforeach <!-- 底部合计行,对应标注的红色区域 --> <tr class="total-row"> <td colspan="2" class="text-right font-bold">列合计</td> <td>{{ number_format($dayTotals['mon'], 1) }}</td> <td>{{ number_format($dayTotals['tue'], 1) }}</td> <td>{{ number_format($dayTotals['wed'], 1) }}</td> <td>{{ number_format($dayTotals['thu'], 1) }}</td> <td>{{ number_format($dayTotals['fri'], 1) }}</td> <td>{{ number_format($dayTotals['sat'], 1) }}</td> <td>{{ number_format($dayTotals['sun'], 1) }}</td> </tr> </tbody> </table> <button wire:click="addEntry">新增工时行</button>
关键注意点
- 触发逻辑完全匹配需求:.wire:model.blur是Livewire提供的原生修饰符,专门在输入框失焦时才将最新值同步到组件,和“输入完成、点击离开才计算”的逻辑完全一致,不会出现输入过程中频繁请求的问题。
- 无需手动操作DOM:Livewire的响应式渲染机制会在后端合计值更新后,自动把视图里对应位置的数字替换成最新值,不需要写任何jQuery或原生JS的DOM操作代码。
- 边界兼容:计算环节统一将空值、字符串类型的输入转成浮点型,避免出现累加结果为
NaN的问题。 - 若后续新增删除行的逻辑,只要删除操作修改了
$entries数组,updatedEntries钩子会自动触发重新计算,不需要额外修改合计逻辑。 - 相比jQuery实现方式,这套逻辑不需要手动绑定多个输入框的事件、不需要手动遍历DOM取单元格值、不需要手动修改合计文本,和动态新增/删除行的逻辑完全打通。
内容的提问来源于stack exchange,提问作者Pokeycam
相关产品推荐
相关产品推荐

