[FEAT] 強化遠端指令中心的操作備註功能
1. 新增 remote_commands 資料表的 remark 欄位以儲存操作備註。 2. 更新 RemoteCommand 模型,將 note 與 remark 加入可批量賦值名單。 3. 更新 MachineService 的 dispatchDispense 函式,支援接收與記錄操作備註。 4. 更新 RemoteController 的 storeCommand 邏輯,將前端輸入的備註存入 remark 並支援遠端出貨指令。 5. 優化操作紀錄搜尋邏輯,支援搜尋備註欄位。 6. 重構指令中心操作紀錄介面,桌面版新增「操作備註」欄位,並優化手機版卡片顯示。 7. 調整表格欄位寬度與標頭樣式,解決「指令類型」寬度過大的問題。
This commit is contained in:
parent
ad921011f0
commit
426253b1c3
@ -53,7 +53,8 @@ class RemoteController extends Controller
|
||||
->orWhere('serial_no', 'like', "%{$search}%");
|
||||
})->orWhereHas('user', function ($uq) use ($search) {
|
||||
$uq->where('name', 'like', "%{$search}%");
|
||||
});
|
||||
})->orWhere('remark', 'like', "%{$search}%")
|
||||
->orWhere('note', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
@ -145,7 +146,8 @@ class RemoteController extends Controller
|
||||
$this->machineService->dispatchDispense(
|
||||
Machine::findOrFail($validated['machine_id']),
|
||||
$validated['slot_no'],
|
||||
auth()->id()
|
||||
auth()->id(),
|
||||
$validated['note'] ?? null
|
||||
);
|
||||
} else {
|
||||
$payload = [];
|
||||
@ -169,7 +171,7 @@ class RemoteController extends Controller
|
||||
'command_type' => $validated['command_type'],
|
||||
'payload' => $payload,
|
||||
'status' => 'pending',
|
||||
'note' => $validated['note'] ?? null,
|
||||
'remark' => $validated['note'] ?? null,
|
||||
]);
|
||||
|
||||
// 推播 MQTT 指令
|
||||
|
||||
@ -16,6 +16,8 @@ class RemoteCommand extends Model
|
||||
'payload',
|
||||
'status',
|
||||
'ttl',
|
||||
'note',
|
||||
'remark',
|
||||
'executed_at',
|
||||
];
|
||||
|
||||
|
||||
@ -403,9 +403,9 @@ class MachineService
|
||||
/**
|
||||
* 遠端出貨指令下發 (指令模式,不再預扣庫存)
|
||||
*/
|
||||
public function dispatchDispense(Machine $machine, string $slotNo, ?int $userId = null): RemoteCommand
|
||||
public function dispatchDispense(Machine $machine, string $slotNo, ?int $userId = null, ?string $remark = null): RemoteCommand
|
||||
{
|
||||
return DB::transaction(function () use ($machine, $slotNo, $userId) {
|
||||
return DB::transaction(function () use ($machine, $slotNo, $userId, $remark) {
|
||||
$slot = $machine->slots()->where('slot_no', $slotNo)->lockForUpdate()->firstOrFail();
|
||||
|
||||
// 併行檢查:若有 pending 指令,超過 1 分鐘視為逾時可覆蓋
|
||||
@ -440,6 +440,7 @@ class MachineService
|
||||
'user_id' => $userId,
|
||||
'command_type' => 'dispense',
|
||||
'status' => 'pending',
|
||||
'remark' => $remark,
|
||||
'payload' => [
|
||||
'slot_no' => (string)$slotNo,
|
||||
'current_stock' => $slot->stock,
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('remote_commands', function (Blueprint $table) {
|
||||
$table->string('remark')->nullable()->after('note')->comment('操作備註');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('remote_commands', function (Blueprint $table) {
|
||||
$table->dropColumn('remark');
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -129,10 +129,13 @@
|
||||
class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800 text-center whitespace-nowrap">
|
||||
{{ __('Picked up Time') }}</th>
|
||||
<th
|
||||
class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800">
|
||||
class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800 whitespace-nowrap">
|
||||
{{ __('Command Type') }}</th>
|
||||
<th
|
||||
class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800">
|
||||
class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800 whitespace-nowrap">
|
||||
{{ __('Operation Note') }}</th>
|
||||
<th
|
||||
class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800 whitespace-nowrap">
|
||||
{{ __('Operator') }}</th>
|
||||
<th
|
||||
class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800 text-center">
|
||||
@ -190,22 +193,39 @@
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-6 py-6">
|
||||
<div class="flex flex-col min-w-[200px]">
|
||||
<div class="flex flex-col min-w-[140px]">
|
||||
<span
|
||||
class="text-sm font-black text-slate-700 dark:text-slate-300 tracking-tight"
|
||||
class="text-sm font-black text-slate-700 dark:text-slate-300 tracking-tight whitespace-nowrap"
|
||||
x-text="getCommandName({{ Js::from($item->command_type) }})"></span>
|
||||
<div class="flex flex-col gap-0.5 mt-1">
|
||||
<span x-show="getPayloadDetails({{ Js::from($item) }})"
|
||||
class="text-[11px] font-bold text-cyan-600 dark:text-cyan-400/80 bg-cyan-500/5 px-2 py-0.5 rounded-md border border-cyan-500/10 w-fit"
|
||||
:class="{'line-through opacity-60': {{ Js::from($item->status) }} === 'failed'}"
|
||||
x-text="getPayloadDetails({{ Js::from($item) }})"></span>
|
||||
@if($item->note)
|
||||
<span class="text-[10px] text-slate-400 italic pl-1"
|
||||
x-text="translateNote({{ Js::from($item->note) }})"></span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-6">
|
||||
<div class="flex flex-col min-w-[200px]">
|
||||
@if($item->remark)
|
||||
<span class="text-sm font-bold text-slate-600 dark:text-slate-300 break-words leading-tight">{{ $item->remark }}</span>
|
||||
@endif
|
||||
|
||||
@if($item->note)
|
||||
<div class="flex items-center gap-1 mt-1 opacity-60">
|
||||
<svg class="w-3 h-3 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<span class="text-[10px] text-slate-400 italic font-medium"
|
||||
x-text="translateNote({{ Js::from($item->note) }})"></span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(!$item->remark && !$item->note)
|
||||
<span class="text-slate-300 dark:text-slate-700">-</span>
|
||||
@endif
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-6 whitespace-nowrap">
|
||||
<div class="flex items-center gap-2">
|
||||
<div
|
||||
@ -301,12 +321,27 @@
|
||||
class="text-[10px] font-bold text-cyan-600 dark:text-cyan-400/80 bg-cyan-500/5 px-2 py-0.5 rounded-md border border-cyan-500/10 w-fit"
|
||||
:class="{'line-through opacity-60': {{ Js::from($item->status) }} === 'failed'}"
|
||||
x-text="getPayloadDetails({{ Js::from($item) }})"></span>
|
||||
@if($item->note)
|
||||
<span class="text-[10px] text-slate-400 italic" x-text="translateNote({{ Js::from($item->note) }})"></span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if($item->remark || $item->note)
|
||||
<div>
|
||||
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-1">{{ __('Operation Note') }}</p>
|
||||
<div class="flex flex-col gap-1.5 p-3 rounded-2xl bg-slate-50 dark:bg-slate-800/50 border border-slate-100 dark:border-slate-800/50">
|
||||
@if($item->remark)
|
||||
<span class="text-sm font-bold text-slate-700 dark:text-slate-200 leading-tight">{{ $item->remark }}</span>
|
||||
@endif
|
||||
@if($item->note)
|
||||
<div class="flex items-center gap-1.5 opacity-60">
|
||||
<svg class="w-3.5 h-3.5 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<span class="text-[10px] text-slate-400 italic font-medium" x-text="translateNote({{ Js::from($item->note) }})"></span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-1">{{ __('Creation Time') }}</p>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user