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:
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user