[FEAT] 優化遠端管理與庫存歷史紀錄之日期篩選介面

1. 將單一日期區間選擇器拆分為獨立的「開始時間」與「結束時間」輸入框,提升手機版適應性。
2. 修正後端解析邏輯,移除強制 startOfDay() 以支援使用者選擇的精確分鐘數。
3. 修復庫存管理頁面中「截止日」欄位的 HTML 標籤閉合錯誤 (r">" 亂碼)。
4. 導入 Flatpickr defaultDate 與 position 參數,解決時間選擇器 UI 不對齊與邊緣裁切問題。
5. 更新標籤翻譯為「開始時間」與「結束時間」,並補齊庫存分頁的 Alpine.js 預設日期狀態。
This commit is contained in:
sky121113 2026-05-16 21:25:11 +08:00
parent 9fce030f5c
commit 080412d2b3
4 changed files with 87 additions and 71 deletions

View File

@ -72,6 +72,9 @@ class RemoteController extends Controller
} catch (\Exception $e) {
// 忽略解析錯誤
}
} else {
// 預設過濾當天紀錄
$historyQuery->whereDate('created_at', now()->toDateString());
}
// 指令類型過濾
@ -259,6 +262,9 @@ class RemoteController extends Controller
} catch (\Exception $e) {
// 忽略解析錯誤
}
} else {
// 預設過濾當天紀錄
$historyQuery->whereDate('created_at', now()->toDateString());
}
// 狀態過濾

View File

@ -36,41 +36,47 @@
class="luxury-input py-2.5 pl-12 pr-6 block w-full sm:w-72 text-sm font-bold">
</div>
{{-- 日期範圍 --}}
<div class="relative group w-full sm:w-72 sm:flex-none" x-data="{
fp: null,
startDate: '{{ request('start_date') }}',
endDate: '{{ request('end_date') }}'
}" x-init="fp = flatpickr($refs.dateRange, {
mode: 'range',
dateFormat: 'Y-m-d H:i', enableTime: true, time_24hr: true,
defaultHour: 0,
defaultMinute: 0,
locale: 'zh_tw',
defaultDate: startDate && endDate ? [startDate, endDate] : (startDate ? [startDate] : []),
onChange: function(selectedDates, dateStr, instance) {
if (selectedDates.length === 2) {
$refs.startDate.value = instance.formatDate(selectedDates[0], 'Y-m-d H:i');
$refs.endDate.value = instance.formatDate(selectedDates[1], 'Y-m-d H:i');
} else if (selectedDates.length === 0) {
$refs.startDate.value = '';
$refs.endDate.value = '';
}
}
})">
{{-- 起始時間 --}}
<div class="relative group w-full sm:w-48 sm:flex-none">
<span class="absolute inset-y-0 left-0 flex items-center pl-4 pointer-events-none z-10 text-slate-400 group-focus-within:text-cyan-500 transition-colors">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
</span>
<input type="hidden" name="start_date" x-ref="startDate"
value="{{ request('start_date') }}">
<input type="hidden" name="end_date" x-ref="endDate" value="{{ request('end_date') }}">
<input type="text" x-ref="dateRange"
value="{{ request('start_date') && request('end_date') ? request('start_date') . ' ' . __('to') . ' ' . request('end_date') : (request('start_date') ?: '') }}"
placeholder="{{ __('Select Date Range') }}"
class="luxury-input py-2.5 pl-12 pr-6 block w-full text-sm font-bold cursor-pointer">
<input type="text" name="start_date"
x-init="flatpickr($el, {
dateFormat: 'Y-m-d H:i',
enableTime: true,
time_24hr: true,
locale: 'zh_tw',
defaultDate: $el.value
})"
value="{{ request('start_date', now()->startOfDay()->format('Y-m-d H:i')) }}"
placeholder="{{ __('Start Date') }}"
class="luxury-input py-2.5 pl-12 pr-4 block w-full text-sm font-bold cursor-pointer">
<div class="absolute -top-2 left-4 px-1 bg-white dark:bg-slate-900 text-[9px] font-black text-slate-400 uppercase tracking-widest opacity-0 group-focus-within:opacity-100 transition-opacity">{{ __('Start Time') }}</div>
</div>
{{-- 結束時間 --}}
<div class="relative group w-full sm:w-48 sm:flex-none">
<span class="absolute inset-y-0 left-0 flex items-center pl-4 pointer-events-none z-10 text-slate-400 group-focus-within:text-cyan-500 transition-colors">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
</span>
<input type="text" name="end_date"
x-init="flatpickr($el, {
dateFormat: 'Y-m-d H:i',
enableTime: true,
time_24hr: true,
locale: 'zh_tw',
defaultDate: $el.value,
position: 'auto right'
})"
value="{{ request('end_date', now()->endOfDay()->format('Y-m-d H:i')) }}"
placeholder="{{ __('End Date') }}"
class="luxury-input py-2.5 pl-12 pr-4 block w-full text-sm font-bold cursor-pointer">
<div class="absolute -top-2 left-4 px-1 bg-white dark:bg-slate-900 text-[9px] font-black text-slate-400 uppercase tracking-widest opacity-0 group-focus-within:opacity-100 transition-opacity">{{ __('End Time') }}</div>
</div>
{{-- 指令類型 --}}

View File

@ -17,45 +17,49 @@
placeholder="{{ __('Search machines...') }}" class="luxury-input py-2.5 pl-12 pr-6 block w-full">
</div>
<!-- Date Range -->
<div class="relative group flex-[2] min-w-[340px]" x-data="{
fp: null,
startDate: '{{ request('start_date') }}',
endDate: '{{ request('end_date') }}'
}" x-init="fp = flatpickr($refs.dateRange, {
mode: 'range',
dateFormat: 'Y-m-d H:i', enableTime: true, time_24hr: true,
defaultHour: 0,
defaultMinute: 0,
locale: 'zh_tw',
defaultDate: startDate && endDate ? [startDate, endDate] : (startDate ? [startDate] : []),
onChange: function(selectedDates, dateStr, instance) {
if (selectedDates.length === 2) {
$refs.startDate.value = instance.formatDate(selectedDates[0], 'Y-m-d H:i');
$refs.endDate.value = instance.formatDate(selectedDates[1], 'Y-m-d H:i');
historyStartDate = $refs.startDate.value;
historyEndDate = $refs.endDate.value;
} else if (selectedDates.length === 0) {
$refs.startDate.value = '';
$refs.endDate.value = '';
historyStartDate = '';
historyEndDate = '';
}
}
})">
<span
class="absolute inset-y-0 left-0 flex items-center pl-4 pointer-events-none z-10 text-slate-400 group-focus-within:text-cyan-500 transition-colors">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
<!-- Start Date -->
<div class="relative group flex-1 min-w-[180px]">
<span class="absolute inset-y-0 left-0 flex items-center pl-4 pointer-events-none z-10 text-slate-400 group-focus-within:text-cyan-500 transition-colors">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
</span>
<input type="hidden" name="start_date" x-ref="startDate" value="{{ request('start_date') }}">
<input type="hidden" name="end_date" x-ref="endDate" value="{{ request('end_date') }}">
<input type="text" x-ref="dateRange"
value="{{ request('start_date') && request('end_date') ? request('start_date') . ' 至 ' . request('end_date') : (request('start_date') ?: '') }}"
placeholder="{{ __('Select Date Range') }}"
class="luxury-input py-2.5 pl-12 pr-6 block w-full cursor-pointer">
<input type="text" name="start_date"
x-model="historyStartDate"
x-init="flatpickr($el, {
dateFormat: 'Y-m-d H:i',
enableTime: true,
time_24hr: true,
locale: 'zh_tw',
defaultDate: historyStartDate || '{{ now()->startOfDay()->format('Y-m-d H:i') }}',
onChange: (selectedDates, dateStr) => { historyStartDate = dateStr }
})"
placeholder="{{ __('Start Date') }}"
class="luxury-input py-2.5 pl-12 pr-4 block w-full text-sm font-bold cursor-pointer">
<div class="absolute -top-2 left-4 px-1 bg-white dark:bg-slate-900 text-[9px] font-black text-slate-400 uppercase tracking-widest opacity-0 group-focus-within:opacity-100 transition-opacity">{{ __('Start Time') }}</div>
</div>
<!-- End Date -->
<div class="relative group flex-1 min-w-[180px]">
<span class="absolute inset-y-0 left-0 flex items-center pl-4 pointer-events-none z-10 text-slate-400 group-focus-within:text-cyan-500 transition-colors">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
</span>
<input type="text" name="end_date"
x-model="historyEndDate"
x-init="flatpickr($el, {
dateFormat: 'Y-m-d H:i',
enableTime: true,
time_24hr: true,
locale: 'zh_tw',
defaultDate: historyEndDate || '{{ now()->endOfDay()->format('Y-m-d H:i') }}',
position: 'auto right',
onChange: (selectedDates, dateStr) => { historyEndDate = dateStr }
})"
placeholder="{{ __('End Date') }}"
class="luxury-input py-2.5 pl-12 pr-4 block w-full text-sm font-bold cursor-pointer">
<div class="absolute -top-2 left-4 px-1 bg-white dark:bg-slate-900 text-[9px] font-black text-slate-400 uppercase tracking-widest opacity-0 group-focus-within:opacity-100 transition-opacity">{{ __('End Time') }}</div>
</div>
<!-- Status -->

View File

@ -25,8 +25,8 @@
// 機器搜尋狀態
machineSearch: '{{ request('machine_search') }}',
historySearch: '{{ request('search') }}',
historyStartDate: '{{ request('start_date') }}',
historyEndDate: '{{ request('end_date') }}',
historyStartDate: '{{ request('start_date', now()->startOfDay()->format('Y-m-d H:i')) }}',
historyEndDate: '{{ request('end_date', now()->endOfDay()->format('Y-m-d H:i')) }}',
historyStatus: '{{ request('status') }}',
async init() {