From 344ebc3910dfca26a2ca1cb277666596518c928e Mon Sep 17 00:00:00 2001 From: MSuMshk <2039814060@qq.com> Date: Fri, 20 Feb 2026 10:09:58 +0800 Subject: [PATCH] feat(project): pickup mode switch with auto-save and rollback --- apps/web-antd/src/api/store-pickup/index.ts | 11 ++++ .../pickup/components/PickupModeSwitch.vue | 63 ++++++++++++++++--- .../pickup/composables/useStorePickupPage.ts | 48 +++++++++++++- .../web-antd/src/views/store/pickup/index.vue | 3 + .../src/views/store/pickup/styles/mode.less | 45 ++++++++++++- .../views/store/pickup/styles/responsive.less | 5 ++ 6 files changed, 161 insertions(+), 14 deletions(-) diff --git a/apps/web-antd/src/api/store-pickup/index.ts b/apps/web-antd/src/api/store-pickup/index.ts index f6e333b..5213d5f 100644 --- a/apps/web-antd/src/api/store-pickup/index.ts +++ b/apps/web-antd/src/api/store-pickup/index.ts @@ -101,6 +101,12 @@ export interface SavePickupFineRuleParams { storeId: string; } +/** 保存自提模式参数 */ +export interface SavePickupModeParams { + mode: PickupMode; + storeId: string; +} + /** 复制自提设置参数 */ export interface CopyStorePickupSettingsParams { sourceStoreId: string; @@ -131,6 +137,11 @@ export async function savePickupFineRuleApi(data: SavePickupFineRuleParams) { return requestClient.post('/store/pickup/fine-rule/save', data); } +/** 保存自提模式 */ +export async function savePickupModeApi(data: SavePickupModeParams) { + return requestClient.post('/store/pickup/mode/save', data); +} + /** 复制到其他门店 */ export async function copyStorePickupSettingsApi( data: CopyStorePickupSettingsParams, diff --git a/apps/web-antd/src/views/store/pickup/components/PickupModeSwitch.vue b/apps/web-antd/src/views/store/pickup/components/PickupModeSwitch.vue index e88f46c..1f9c9ed 100644 --- a/apps/web-antd/src/views/store/pickup/components/PickupModeSwitch.vue +++ b/apps/web-antd/src/views/store/pickup/components/PickupModeSwitch.vue @@ -6,7 +6,11 @@ */ import type { PickupMode } from '#/api/store-pickup'; +import { Switch } from 'ant-design-vue'; + interface Props { + disabled?: boolean; + isSwitching?: boolean; mode: PickupMode; options: Array<{ label: string; value: PickupMode }>; } @@ -16,19 +20,58 @@ const props = defineProps(); const emit = defineEmits<{ (event: 'change', mode: PickupMode): void; }>(); + +function getModeLabel(mode: PickupMode) { + return props.options.find((item) => item.value === mode)?.label ?? mode; +} diff --git a/apps/web-antd/src/views/store/pickup/composables/useStorePickupPage.ts b/apps/web-antd/src/views/store/pickup/composables/useStorePickupPage.ts index e39069b..bac044b 100644 --- a/apps/web-antd/src/views/store/pickup/composables/useStorePickupPage.ts +++ b/apps/web-antd/src/views/store/pickup/composables/useStorePickupPage.ts @@ -18,6 +18,10 @@ import type { import { computed, onActivated, onMounted, reactive, ref, watch } from 'vue'; +import { message } from 'ant-design-vue'; + +import { savePickupModeApi } from '#/api/store-pickup'; + import { ALL_WEEK_DAYS, DEFAULT_FINE_RULE, @@ -35,6 +39,7 @@ import { cloneBasicSettings, cloneFineRule, clonePreviewDays, + createSettingsSnapshot, createSlotId, formatDayOfWeeksText, } from './pickup-page/helpers'; @@ -47,6 +52,7 @@ export function useStorePickupPage() { const isSavingBasic = ref(false); const isSavingSlots = ref(false); const isSavingFineRule = ref(false); + const isModeSwitching = ref(false); const isCopySubmitting = ref(false); const isConfigured = ref(false); const loadedStoreId = ref(''); @@ -134,7 +140,8 @@ export function useStorePickupPage() { !isPageLoading.value && !isSavingBasic.value && !isSavingSlots.value && - !isSavingFineRule.value, + !isSavingFineRule.value && + !isModeSwitching.value, ); function clearSettings() { @@ -236,9 +243,43 @@ export function useStorePickupPage() { selectedStoreId.value = value; } - function setPickupMode(value: 'big' | 'fine') { + async function setPickupMode(value: 'big' | 'fine') { if (!canOperate.value) return; + if (value === pickupMode.value) return; + if (!selectedStoreId.value) return; + + const currentStoreId = selectedStoreId.value; + const previousMode = pickupMode.value; pickupMode.value = value; + isModeSwitching.value = true; + + try { + await savePickupModeApi({ + storeId: currentStoreId, + mode: value, + }); + + if (selectedStoreId.value === currentStoreId) { + isConfigured.value = true; + loadedStoreId.value = currentStoreId; + snapshot.value = createSettingsSnapshot({ + mode: value, + basicSettings, + bigSlots: bigSlots.value, + fineRule, + previewDays: previewDays.value, + }); + message.success('自提模式已切换'); + } + } catch (error) { + console.error(error); + if (selectedStoreId.value === currentStoreId) { + pickupMode.value = previousMode; + message.error('自提模式切换失败,请稍后重试'); + } + } finally { + isModeSwitching.value = false; + } } function setAllowSameDayPickup(value: boolean) { @@ -289,12 +330,14 @@ export function useStorePickupPage() { // 8. 门店切换时自动刷新配置。 watch(selectedStoreId, async (storeId) => { if (!storeId) { + isModeSwitching.value = false; loadedStoreId.value = ''; isConfigured.value = false; clearSettings(); snapshot.value = null; return; } + isModeSwitching.value = false; loadedStoreId.value = ''; isConfigured.value = false; snapshot.value = null; @@ -351,6 +394,7 @@ export function useStorePickupPage() { isPageLoading, isSavingBasic, isSavingFineRule, + isModeSwitching, isSavingSlots, isSlotDaySelected, isSlotDrawerOpen, diff --git a/apps/web-antd/src/views/store/pickup/index.vue b/apps/web-antd/src/views/store/pickup/index.vue index bdb8feb..8531fd5 100644 --- a/apps/web-antd/src/views/store/pickup/index.vue +++ b/apps/web-antd/src/views/store/pickup/index.vue @@ -46,6 +46,7 @@ const { isPageLoading, isSavingBasic, isSavingFineRule, + isModeSwitching, isSavingSlots, isSlotDaySelected, isSlotDrawerOpen, @@ -129,6 +130,8 @@ const { diff --git a/apps/web-antd/src/views/store/pickup/styles/mode.less b/apps/web-antd/src/views/store/pickup/styles/mode.less index 1f5f0c5..88d98c9 100644 --- a/apps/web-antd/src/views/store/pickup/styles/mode.less +++ b/apps/web-antd/src/views/store/pickup/styles/mode.less @@ -1,10 +1,17 @@ /* 文件职责:自提设置页面模式切换样式。 */ .page-store-pickup { + .pickup-mode-switch-wrap { + display: flex; + flex-direction: column; + gap: 10px; + margin-bottom: 16px; + } + .pickup-mode-switch { display: inline-flex; - gap: 2px; + gap: 10px; + align-items: center; padding: 3px; - margin-bottom: 16px; background: #f8f9fb; border-radius: 8px; } @@ -21,10 +28,44 @@ transition: all 0.2s ease; } + .pickup-mode-item:disabled { + cursor: not-allowed; + opacity: 0.6; + } + .pickup-mode-item.active { font-weight: 600; color: #1677ff; background: #fff; box-shadow: 0 1px 2px rgb(15 23 42 / 10%); } + + .pickup-mode-guide { + padding: 10px 12px; + border-radius: 8px; + border: 1px solid transparent; + } + + .pickup-mode-guide-big { + background: #f5f9ff; + border-color: #bfdbfe; + } + + .pickup-mode-guide-fine { + background: #effff7; + border-color: #bbf7d0; + } + + .pickup-mode-guide .guide-title { + margin-bottom: 2px; + font-size: 13px; + font-weight: 600; + color: #1f2937; + } + + .pickup-mode-guide .guide-desc { + font-size: 12px; + line-height: 1.55; + color: #4b5563; + } } diff --git a/apps/web-antd/src/views/store/pickup/styles/responsive.less b/apps/web-antd/src/views/store/pickup/styles/responsive.less index 5894d00..9628aee 100644 --- a/apps/web-antd/src/views/store/pickup/styles/responsive.less +++ b/apps/web-antd/src/views/store/pickup/styles/responsive.less @@ -7,9 +7,14 @@ } @media (max-width: 768px) { + .pickup-mode-switch-wrap { + gap: 8px; + } + .pickup-mode-switch { display: flex; width: 100%; + justify-content: space-between; } .pickup-mode-item {