fix(hold-history): replace auto-search with explicit query button for date filtering

Date changes no longer auto-trigger Oracle queries. Users can freely adjust
dates and click "查詢" to execute. Hold Type still auto-refreshes from cache.
Record Type checkboxes now use pill-toggle style (hidden native checkbox).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
egg
2026-03-03 08:05:56 +08:00
parent fb92579331
commit f1506787fb
3 changed files with 105 additions and 49 deletions

View File

@@ -352,34 +352,34 @@ const holdTypeLabel = computed(() => {
// ---- Event handlers ----
function handleFilterChange(next) {
function handleApply(next) {
const nextStartDate = next?.startDate || '';
const nextEndDate = next?.endDate || '';
const nextHoldType = next?.holdType || 'quality';
const dateChanged = filterBar.startDate !== nextStartDate || filterBar.endDate !== nextEndDate;
const holdTypeChanged = filterBar.holdType !== nextHoldType;
if (!dateChanged && !holdTypeChanged) {
return;
}
filterBar.startDate = nextStartDate;
filterBar.endDate = nextEndDate;
filterBar.holdType = nextHoldType;
reasonFilter.value = '';
durationFilter.value = '';
recordType.value = ['new'];
page.value = 1;
updateUrlState();
if (dateChanged) {
// Date changed -> new primary query (Oracle)
void executePrimaryQuery();
} else {
// Only hold_type changed -> view refresh (cache only)
}
function handleHoldTypeChange(nextHoldType) {
const holdType = nextHoldType || 'quality';
if (filterBar.holdType === holdType) return;
filterBar.holdType = holdType;
reasonFilter.value = '';
durationFilter.value = '';
recordType.value = ['new'];
page.value = 1;
updateUrlState();
// Hold type only → view refresh (cache read, no Oracle query)
void refreshView();
}
}
function handleRecordTypeChange() {
@@ -502,7 +502,8 @@ onMounted(() => {
:end-date="filterBar.endDate"
:hold-type="filterBar.holdType"
:disabled="loading.global"
@change="handleFilterChange"
@apply="handleApply"
@hold-type-change="handleHoldTypeChange"
/>
<SummaryCards :summary="summary" />

View File

@@ -1,5 +1,5 @@
<script setup>
import { computed } from 'vue';
import { computed, ref, watch } from 'vue';
const props = defineProps({
startDate: {
@@ -20,42 +20,42 @@ const props = defineProps({
},
});
const emit = defineEmits(['change']);
const emit = defineEmits(['apply', 'hold-type-change']);
function emitChange(next) {
emit('change', {
startDate: next.startDate ?? props.startDate,
endDate: next.endDate ?? props.endDate,
holdType: next.holdType ?? props.holdType,
});
}
// Local date state — changes don't auto-trigger queries
const localStartDate = ref(props.startDate);
const localEndDate = ref(props.endDate);
const startDateModel = computed({
get() {
return props.startDate || '';
// Sync from parent when props change (URL restore, programmatic set)
watch(
() => props.startDate,
(v) => {
localStartDate.value = v;
},
set(nextValue) {
emitChange({ startDate: nextValue || '' });
);
watch(
() => props.endDate,
(v) => {
localEndDate.value = v;
},
});
const endDateModel = computed({
get() {
return props.endDate || '';
},
set(nextValue) {
emitChange({ endDate: nextValue || '' });
},
});
);
// Hold type still emits immediately (cache-only refresh, no Oracle query)
const holdTypeModel = computed({
get() {
return props.holdType || 'quality';
},
set(nextValue) {
emitChange({ holdType: nextValue || 'quality' });
emit('hold-type-change', nextValue || 'quality');
},
});
function handleApply() {
emit('apply', {
startDate: localStartDate.value,
endDate: localEndDate.value,
});
}
</script>
<template>
@@ -64,7 +64,7 @@ const holdTypeModel = computed({
<label class="filter-label" for="hold-history-start-date">開始日期</label>
<input
id="hold-history-start-date"
v-model="startDateModel"
v-model="localStartDate"
class="date-input"
type="date"
:disabled="disabled"
@@ -75,7 +75,7 @@ const holdTypeModel = computed({
<label class="filter-label" for="hold-history-end-date">結束日期</label>
<input
id="hold-history-end-date"
v-model="endDateModel"
v-model="localEndDate"
class="date-input"
type="date"
:disabled="disabled"
@@ -95,5 +95,17 @@ const holdTypeModel = computed({
<option value="all">全部</option>
</select>
</div>
<div class="filter-group filter-action-group">
<button
type="button"
class="btn btn-primary btn-query"
:disabled="disabled"
@click="handleApply"
>
<template v-if="disabled"><span class="btn-spinner"></span>查詢中...</template>
<template v-else>查詢</template>
</button>
</div>
</section>
</template>

View File

@@ -100,6 +100,38 @@
box-shadow: 0 0 0 2px rgba(14, 165, 233, 0.18);
}
.filter-action-group {
display: flex;
align-items: flex-end;
}
.btn-query {
white-space: nowrap;
}
.btn-query:disabled {
opacity: 0.7;
cursor: not-allowed;
}
.btn-spinner {
display: inline-block;
width: 12px;
height: 12px;
border: 2px solid rgba(255, 255, 255, 0.4);
border-top-color: #fff;
border-radius: 50%;
animation: spin 0.6s linear infinite;
margin-right: 6px;
vertical-align: middle;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.checkbox-group {
display: inline-flex;
flex-wrap: wrap;
@@ -109,13 +141,24 @@
.checkbox-option {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
border-radius: 8px;
gap: 0;
padding: 6px 12px;
border-radius: 999px;
border: 1px solid var(--border);
background: #ffffff;
background: #f8fafc;
cursor: pointer;
font-size: 13px;
color: #475569;
transition: all 0.15s ease;
user-select: none;
}
.checkbox-option input[type='checkbox'] {
position: absolute;
width: 0;
height: 0;
opacity: 0;
pointer-events: none;
}
.checkbox-option.active {