[REFACTOR] 遷移商品狀態切換邏輯至全頁重新整理模式

1. 重構 index.blade.php 中的 productManager Alpine 元件,將狀態切換由 AJAX 非同步更新改為原生表單提交。
2. 移除 index.blade.php 中不再使用的 deleteItem 函式與重複定義的 confirmDelete 函式。
3. 更新 tab-products.blade.php 的按鈕行為:商品「啟用」改為點擊後直接提交表單刷新;商品「停用」維持確認彈窗,但確認後執行全頁重新整理。
4. 同步更新桌機版表格與手機版卡片視圖中的按鈕邏輯。
This commit is contained in:
sky121113 2026-05-07 11:52:08 +08:00
parent 4bf53db3be
commit 577bf136de
2 changed files with 28 additions and 68 deletions

View File

@ -599,12 +599,28 @@ hover:bg-slate-100 dark:hover:bg-white/5 rounded-lg flex items-center justify-be
},
handleFilterSubmit(tab) {
// Clear page when searching
const url = new URL(window.location.href);
const pageKey = tab === 'products' ? 'product_page' : 'category_page';
url.searchParams.delete(pageKey);
// When submitting the filter form, we want to reset to page 1
const container = document.getElementById('tab-' + tab + '-container');
const form = container?.querySelector('form');
let params = new URLSearchParams();
this.fetchTabData(tab);
if (form) {
const formData = new FormData(form);
for (let [key, value] of formData.entries()) {
if (value) params.set(key, value);
}
}
// Ensure tab and ajax markers are present
params.set('tab', tab);
params.set('_ajax', '1');
// Explicitly remove page parameters to reset to page 1
params.delete('product_page');
params.delete('category_page');
const url = window.location.pathname + '?' + params.toString();
this.fetchTabData(tab, url);
},
bindPaginationLinks(containerSelector, tab) {
@ -749,73 +765,13 @@ hover:bg-slate-100 dark:hover:bg-white/5 rounded-lg flex items-center justify-be
this.isStatusConfirmOpen = true;
},
async submitConfirmedForm() {
submitConfirmedForm() {
if (this.isStatusConfirmOpen) {
this.isStatusConfirmOpen = false;
this.loading = true;
try {
const response = await fetch(this.toggleFormAction, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest'
},
body: new URLSearchParams({
'_method': 'PATCH'
})
});
const data = await response.json();
if (data.success) {
await this.fetchTabData(this.activeTab);
if (window.showToast) window.showToast(data.message, 'success');
} else {
if (window.showToast) window.showToast(data.message || 'Error', 'error');
}
} catch (error) {
console.error('Status toggle error:', error);
if (window.showToast) window.showToast('{{ __("Operation failed") }}', 'error');
} finally {
this.loading = false;
}
this.$refs.statusToggleForm.submit();
}
},
confirmDelete(actionUrl) {
this.deleteFormAction = actionUrl;
this.isDeleteConfirmOpen = true;
},
async deleteItem() {
this.isDeleteConfirmOpen = false;
this.loading = true;
try {
const response = await fetch(this.deleteFormAction, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: new URLSearchParams({
'_method': 'DELETE'
})
});
const data = await response.json();
if (data.success) {
await this.fetchTabData(this.activeTab);
if (window.showToast) window.showToast(data.message, 'success');
} else {
if (window.showToast) window.showToast(data.message || 'Error', 'error');
}
} catch (error) {
console.error('Delete error:', error);
if (window.showToast) window.showToast('{{ __("Operation failed") }}', 'error');
} finally {
this.loading = false;
}
},
getCategoryName(id) {
const category = this.categories.find(c => c.id == id);

View File

@ -183,7 +183,7 @@ $baseRoute = 'admin.data-config.products';
</button>
@else
<button type="button"
@click="toggleStatus('{{ route($baseRoute . '.status.toggle', $product->id) }}')"
@click="toggleFormAction = '{{ route($baseRoute . '.status.toggle', $product->id) }}'; $nextTick(() => $refs.statusToggleForm.submit())"
class="p-2.5 rounded-lg bg-slate-50 dark:bg-slate-800 text-slate-400 hover:text-emerald-500 hover:bg-emerald-500/5 transition-all border border-transparent hover:border-emerald-500/20"
title="{{ __('Enable') }}">
<svg class="w-4 h-4 stroke-[2.5]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@ -311,7 +311,11 @@ $baseRoute = 'admin.data-config.products';
<div class="flex items-center gap-3">
<div class="flex items-center gap-2">
<button type="button"
@if($product->is_active)
@click="toggleStatus('{{ route($baseRoute . '.status.toggle', $product->id) }}')"
@else
@click="toggleFormAction = '{{ route($baseRoute . '.status.toggle', $product->id) }}'; $nextTick(() => $refs.statusToggleForm.submit())"
@endif
class="p-3 rounded-xl bg-slate-50 dark:bg-slate-800 text-slate-400 hover:bg-emerald-500 hover:text-white transition-all duration-300 border border-slate-200/50 dark:border-slate-700/50"
title="{{ __('Status') }}">
@if($product->is_active)