fix(project): remove pickup mock preview and sync with api
This commit is contained in:
@@ -22,7 +22,7 @@ const emit = defineEmits<{
|
|||||||
(event: 'selectDate', date: string): void;
|
(event: 'selectDate', date: string): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const previewSubtitle = '根据规则自动生成,以下为预览效果';
|
const previewSubtitle = '来自后端规则计算(未扣减实际预约)';
|
||||||
|
|
||||||
function getPreviewStatusText(slot: PickupPreviewSlotDto) {
|
function getPreviewStatusText(slot: PickupPreviewSlotDto) {
|
||||||
if (slot.status === 'expired') return '已过期';
|
if (slot.status === 'expired') return '已过期';
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import type {
|
|||||||
PickupBasicSettingsDto,
|
PickupBasicSettingsDto,
|
||||||
PickupFineRuleDto,
|
PickupFineRuleDto,
|
||||||
PickupMode,
|
PickupMode,
|
||||||
PickupSlotDto,
|
|
||||||
PickupWeekDay,
|
PickupWeekDay,
|
||||||
} from '#/api/store-pickup';
|
} from '#/api/store-pickup';
|
||||||
import type { PickupWeekDayOption } from '#/views/store/pickup/types';
|
import type { PickupWeekDayOption } from '#/views/store/pickup/types';
|
||||||
@@ -49,64 +48,6 @@ export const DEFAULT_PICKUP_BASIC_SETTINGS: PickupBasicSettingsDto = {
|
|||||||
maxItemsPerOrder: 20,
|
maxItemsPerOrder: 20,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_BIG_SLOTS: PickupSlotDto[] = [
|
|
||||||
{
|
|
||||||
id: 'slot-morning',
|
|
||||||
name: '上午时段',
|
|
||||||
startTime: '09:00',
|
|
||||||
endTime: '11:30',
|
|
||||||
cutoffMinutes: 30,
|
|
||||||
capacity: 20,
|
|
||||||
reservedCount: 5,
|
|
||||||
dayOfWeeks: [...WEEKDAY_ONLY],
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'slot-noon',
|
|
||||||
name: '午间时段',
|
|
||||||
startTime: '11:30',
|
|
||||||
endTime: '14:00',
|
|
||||||
cutoffMinutes: 20,
|
|
||||||
capacity: 30,
|
|
||||||
reservedCount: 12,
|
|
||||||
dayOfWeeks: [...ALL_WEEK_DAYS],
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'slot-afternoon',
|
|
||||||
name: '下午时段',
|
|
||||||
startTime: '14:00',
|
|
||||||
endTime: '17:00',
|
|
||||||
cutoffMinutes: 30,
|
|
||||||
capacity: 15,
|
|
||||||
reservedCount: 3,
|
|
||||||
dayOfWeeks: [...WEEKDAY_ONLY],
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'slot-evening',
|
|
||||||
name: '晚间时段',
|
|
||||||
startTime: '17:00',
|
|
||||||
endTime: '20:30',
|
|
||||||
cutoffMinutes: 30,
|
|
||||||
capacity: 25,
|
|
||||||
reservedCount: 8,
|
|
||||||
dayOfWeeks: [...ALL_WEEK_DAYS],
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'slot-weekend',
|
|
||||||
name: '周末特惠',
|
|
||||||
startTime: '10:00',
|
|
||||||
endTime: '15:00',
|
|
||||||
cutoffMinutes: 45,
|
|
||||||
capacity: 40,
|
|
||||||
reservedCount: 18,
|
|
||||||
dayOfWeeks: [...WEEKEND_ONLY],
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const DEFAULT_FINE_RULE: PickupFineRuleDto = {
|
export const DEFAULT_FINE_RULE: PickupFineRuleDto = {
|
||||||
intervalMinutes: 30,
|
intervalMinutes: 30,
|
||||||
slotCapacity: 5,
|
slotCapacity: 5,
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import {
|
|||||||
} from '#/api/store-pickup';
|
} from '#/api/store-pickup';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DEFAULT_BIG_SLOTS,
|
|
||||||
DEFAULT_FINE_RULE,
|
DEFAULT_FINE_RULE,
|
||||||
DEFAULT_PICKUP_BASIC_SETTINGS,
|
DEFAULT_PICKUP_BASIC_SETTINGS,
|
||||||
DEFAULT_PICKUP_MODE,
|
DEFAULT_PICKUP_MODE,
|
||||||
@@ -37,7 +36,6 @@ import {
|
|||||||
cloneFineRule,
|
cloneFineRule,
|
||||||
clonePreviewDays,
|
clonePreviewDays,
|
||||||
createSettingsSnapshot,
|
createSettingsSnapshot,
|
||||||
generatePreviewDays,
|
|
||||||
sortSlots,
|
sortSlots,
|
||||||
} from './helpers';
|
} from './helpers';
|
||||||
|
|
||||||
@@ -90,9 +88,9 @@ export function createDataActions(options: CreateDataActionsOptions) {
|
|||||||
function applyDefaultSettings() {
|
function applyDefaultSettings() {
|
||||||
options.mode.value = DEFAULT_PICKUP_MODE;
|
options.mode.value = DEFAULT_PICKUP_MODE;
|
||||||
syncBasicSettings(cloneBasicSettings(DEFAULT_PICKUP_BASIC_SETTINGS));
|
syncBasicSettings(cloneBasicSettings(DEFAULT_PICKUP_BASIC_SETTINGS));
|
||||||
options.bigSlots.value = sortSlots(cloneBigSlots(DEFAULT_BIG_SLOTS));
|
options.bigSlots.value = [];
|
||||||
syncFineRule(cloneFineRule(DEFAULT_FINE_RULE));
|
syncFineRule(cloneFineRule(DEFAULT_FINE_RULE));
|
||||||
options.previewDays.value = generatePreviewDays(options.fineRule);
|
options.previewDays.value = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 应用快照到当前页面状态。 */
|
/** 应用快照到当前页面状态。 */
|
||||||
@@ -118,9 +116,7 @@ export function createDataActions(options: CreateDataActionsOptions) {
|
|||||||
...result.basicSettings,
|
...result.basicSettings,
|
||||||
});
|
});
|
||||||
options.bigSlots.value = sortSlots(
|
options.bigSlots.value = sortSlots(
|
||||||
result.bigSlots?.length
|
result.bigSlots?.length ? result.bigSlots : [],
|
||||||
? result.bigSlots
|
|
||||||
: cloneBigSlots(DEFAULT_BIG_SLOTS),
|
|
||||||
);
|
);
|
||||||
syncFineRule({
|
syncFineRule({
|
||||||
...DEFAULT_FINE_RULE,
|
...DEFAULT_FINE_RULE,
|
||||||
@@ -129,7 +125,7 @@ export function createDataActions(options: CreateDataActionsOptions) {
|
|||||||
options.previewDays.value =
|
options.previewDays.value =
|
||||||
result.previewDays?.length > 0
|
result.previewDays?.length > 0
|
||||||
? clonePreviewDays(result.previewDays)
|
? clonePreviewDays(result.previewDays)
|
||||||
: generatePreviewDays(options.fineRule);
|
: [];
|
||||||
|
|
||||||
options.snapshot.value = buildCurrentSnapshot();
|
options.snapshot.value = buildCurrentSnapshot();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -189,12 +185,13 @@ export function createDataActions(options: CreateDataActionsOptions) {
|
|||||||
if (!options.selectedStoreId.value) return;
|
if (!options.selectedStoreId.value) return;
|
||||||
options.isSavingBasic.value = true;
|
options.isSavingBasic.value = true;
|
||||||
try {
|
try {
|
||||||
|
const currentStoreId = options.selectedStoreId.value;
|
||||||
await savePickupBasicSettingsApi({
|
await savePickupBasicSettingsApi({
|
||||||
storeId: options.selectedStoreId.value,
|
storeId: currentStoreId,
|
||||||
mode: options.mode.value,
|
mode: options.mode.value,
|
||||||
basicSettings: cloneBasicSettings(options.basicSettings),
|
basicSettings: cloneBasicSettings(options.basicSettings),
|
||||||
});
|
});
|
||||||
options.snapshot.value = buildCurrentSnapshot();
|
await loadStoreSettings(currentStoreId);
|
||||||
message.success('基本设置已保存');
|
message.success('基本设置已保存');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@@ -208,12 +205,13 @@ export function createDataActions(options: CreateDataActionsOptions) {
|
|||||||
if (!options.selectedStoreId.value) return;
|
if (!options.selectedStoreId.value) return;
|
||||||
options.isSavingSlots.value = true;
|
options.isSavingSlots.value = true;
|
||||||
try {
|
try {
|
||||||
|
const currentStoreId = options.selectedStoreId.value;
|
||||||
await savePickupSlotsApi({
|
await savePickupSlotsApi({
|
||||||
storeId: options.selectedStoreId.value,
|
storeId: currentStoreId,
|
||||||
mode: options.mode.value,
|
mode: options.mode.value,
|
||||||
slots: cloneBigSlots(options.bigSlots.value),
|
slots: cloneBigSlots(options.bigSlots.value),
|
||||||
});
|
});
|
||||||
options.snapshot.value = buildCurrentSnapshot();
|
await loadStoreSettings(currentStoreId);
|
||||||
message.success('大时段配置已保存');
|
message.success('大时段配置已保存');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@@ -227,13 +225,13 @@ export function createDataActions(options: CreateDataActionsOptions) {
|
|||||||
if (!options.selectedStoreId.value) return;
|
if (!options.selectedStoreId.value) return;
|
||||||
options.isSavingFineRule.value = true;
|
options.isSavingFineRule.value = true;
|
||||||
try {
|
try {
|
||||||
|
const currentStoreId = options.selectedStoreId.value;
|
||||||
await savePickupFineRuleApi({
|
await savePickupFineRuleApi({
|
||||||
storeId: options.selectedStoreId.value,
|
storeId: currentStoreId,
|
||||||
mode: options.mode.value,
|
mode: options.mode.value,
|
||||||
fineRule: cloneFineRule(options.fineRule),
|
fineRule: cloneFineRule(options.fineRule),
|
||||||
});
|
});
|
||||||
options.previewDays.value = generatePreviewDays(options.fineRule);
|
await loadStoreSettings(currentStoreId);
|
||||||
options.snapshot.value = buildCurrentSnapshot();
|
|
||||||
message.success('精细规则已保存');
|
message.success('精细规则已保存');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|||||||
@@ -5,20 +5,15 @@ import type { Ref } from 'vue';
|
|||||||
* 1. 管理精细规则字段与适用星期选择。
|
* 1. 管理精细规则字段与适用星期选择。
|
||||||
* 2. 处理规则校验与保存提交流程。
|
* 2. 处理规则校验与保存提交流程。
|
||||||
*/
|
*/
|
||||||
import type {
|
import type { PickupFineRuleDto, PickupWeekDay } from '#/api/store-pickup';
|
||||||
PickupFineRuleDto,
|
|
||||||
PickupPreviewDayDto,
|
|
||||||
PickupWeekDay,
|
|
||||||
} from '#/api/store-pickup';
|
|
||||||
|
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
import { ALL_WEEK_DAYS, WEEKDAY_ONLY, WEEKEND_ONLY } from './constants';
|
import { ALL_WEEK_DAYS, WEEKDAY_ONLY, WEEKEND_ONLY } from './constants';
|
||||||
import { generatePreviewDays, parseTimeToMinutes } from './helpers';
|
import { parseTimeToMinutes } from './helpers';
|
||||||
|
|
||||||
interface CreateFineRuleActionsOptions {
|
interface CreateFineRuleActionsOptions {
|
||||||
fineRule: PickupFineRuleDto;
|
fineRule: PickupFineRuleDto;
|
||||||
previewDays: Ref<PickupPreviewDayDto[]>;
|
|
||||||
saveFineRule: () => Promise<void>;
|
saveFineRule: () => Promise<void>;
|
||||||
selectedPreviewDate: Ref<string>;
|
selectedPreviewDate: Ref<string>;
|
||||||
}
|
}
|
||||||
@@ -29,22 +24,18 @@ export function createFineRuleActions(options: CreateFineRuleActionsOptions) {
|
|||||||
5,
|
5,
|
||||||
Math.floor(Number(value || 5)),
|
Math.floor(Number(value || 5)),
|
||||||
);
|
);
|
||||||
refreshPreviewDays();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFineSlotCapacity(value: number) {
|
function setFineSlotCapacity(value: number) {
|
||||||
options.fineRule.slotCapacity = Math.max(1, Math.floor(Number(value || 1)));
|
options.fineRule.slotCapacity = Math.max(1, Math.floor(Number(value || 1)));
|
||||||
refreshPreviewDays();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFineDayStartTime(value: string) {
|
function setFineDayStartTime(value: string) {
|
||||||
options.fineRule.dayStartTime = value;
|
options.fineRule.dayStartTime = value;
|
||||||
refreshPreviewDays();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFineDayEndTime(value: string) {
|
function setFineDayEndTime(value: string) {
|
||||||
options.fineRule.dayEndTime = value;
|
options.fineRule.dayEndTime = value;
|
||||||
refreshPreviewDays();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFineMinAdvanceHours(value: number) {
|
function setFineMinAdvanceHours(value: number) {
|
||||||
@@ -52,7 +43,6 @@ export function createFineRuleActions(options: CreateFineRuleActionsOptions) {
|
|||||||
0,
|
0,
|
||||||
Math.floor(Number(value || 0)),
|
Math.floor(Number(value || 0)),
|
||||||
);
|
);
|
||||||
refreshPreviewDays();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isFineDaySelected(day: PickupWeekDay) {
|
function isFineDaySelected(day: PickupWeekDay) {
|
||||||
@@ -63,7 +53,6 @@ export function createFineRuleActions(options: CreateFineRuleActionsOptions) {
|
|||||||
options.fineRule.dayOfWeeks = options.fineRule.dayOfWeeks.includes(day)
|
options.fineRule.dayOfWeeks = options.fineRule.dayOfWeeks.includes(day)
|
||||||
? options.fineRule.dayOfWeeks.filter((item) => item !== day)
|
? options.fineRule.dayOfWeeks.filter((item) => item !== day)
|
||||||
: [...options.fineRule.dayOfWeeks, day].toSorted((a, b) => a - b);
|
: [...options.fineRule.dayOfWeeks, day].toSorted((a, b) => a - b);
|
||||||
refreshPreviewDays();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function quickSelectFineDays(mode: 'all' | 'weekday' | 'weekend') {
|
function quickSelectFineDays(mode: 'all' | 'weekday' | 'weekend') {
|
||||||
@@ -74,29 +63,12 @@ export function createFineRuleActions(options: CreateFineRuleActionsOptions) {
|
|||||||
} else {
|
} else {
|
||||||
options.fineRule.dayOfWeeks = [...WEEKEND_ONLY];
|
options.fineRule.dayOfWeeks = [...WEEKEND_ONLY];
|
||||||
}
|
}
|
||||||
refreshPreviewDays();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSelectedPreviewDate(date: string) {
|
function setSelectedPreviewDate(date: string) {
|
||||||
options.selectedPreviewDate.value = date;
|
options.selectedPreviewDate.value = date;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 重新计算预览,保障规则修改后页面即时反馈。 */
|
|
||||||
function refreshPreviewDays() {
|
|
||||||
options.previewDays.value = generatePreviewDays(options.fineRule);
|
|
||||||
if (options.previewDays.value.length === 0) {
|
|
||||||
options.selectedPreviewDate.value = '';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const hasCurrent = options.previewDays.value.some(
|
|
||||||
(day) => day.date === options.selectedPreviewDate.value,
|
|
||||||
);
|
|
||||||
if (!hasCurrent) {
|
|
||||||
const firstDay = options.previewDays.value[0];
|
|
||||||
options.selectedPreviewDate.value = firstDay?.date ?? '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 校验精细规则并提交保存。 */
|
/** 校验精细规则并提交保存。 */
|
||||||
async function handleSaveFineRule() {
|
async function handleSaveFineRule() {
|
||||||
// 1. 核心字段校验。
|
// 1. 核心字段校验。
|
||||||
@@ -128,7 +100,6 @@ export function createFineRuleActions(options: CreateFineRuleActionsOptions) {
|
|||||||
handleSaveFineRule,
|
handleSaveFineRule,
|
||||||
isFineDaySelected,
|
isFineDaySelected,
|
||||||
quickSelectFineDays,
|
quickSelectFineDays,
|
||||||
refreshPreviewDays,
|
|
||||||
setFineDayEndTime,
|
setFineDayEndTime,
|
||||||
setFineDayStartTime,
|
setFineDayStartTime,
|
||||||
setFineIntervalMinutes,
|
setFineIntervalMinutes,
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* 文件职责:自提设置页面纯函数工具。
|
* 文件职责:自提设置页面纯函数工具。
|
||||||
* 1. 负责克隆、格式化、校验、排序等纯逻辑。
|
* 1. 负责克隆、格式化、校验、排序等纯逻辑。
|
||||||
* 2. 负责根据精细规则生成预览数据。
|
* 2. 不直接请求接口,不生成 mock 预览数据。
|
||||||
*/
|
*/
|
||||||
import type {
|
import type {
|
||||||
PickupBasicSettingsDto,
|
PickupBasicSettingsDto,
|
||||||
PickupFineRuleDto,
|
PickupFineRuleDto,
|
||||||
PickupPreviewDayDto,
|
PickupPreviewDayDto,
|
||||||
PickupPreviewSlotDto,
|
|
||||||
PickupPreviewStatus,
|
|
||||||
PickupSlotDto,
|
PickupSlotDto,
|
||||||
PickupWeekDay,
|
PickupWeekDay,
|
||||||
} from '#/api/store-pickup';
|
} from '#/api/store-pickup';
|
||||||
@@ -150,142 +148,7 @@ export function validateSlotForm(payload: {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 生成 3 天预览数据。 */
|
|
||||||
export function generatePreviewDays(
|
|
||||||
fineRule: PickupFineRuleDto,
|
|
||||||
baseDate = new Date(),
|
|
||||||
) {
|
|
||||||
const startMinutes = parseTimeToMinutes(fineRule.dayStartTime);
|
|
||||||
const endMinutes = parseTimeToMinutes(fineRule.dayEndTime);
|
|
||||||
if (!Number.isFinite(startMinutes) || !Number.isFinite(endMinutes)) return [];
|
|
||||||
if (endMinutes <= startMinutes || fineRule.intervalMinutes <= 0) return [];
|
|
||||||
|
|
||||||
return Array.from({ length: 3 }).map((_, index) => {
|
|
||||||
const date = addDays(baseDate, index);
|
|
||||||
const dateKey = toDateOnly(date);
|
|
||||||
const dayOfWeek = toPickupWeekDay(date);
|
|
||||||
const isEnabledDay = fineRule.dayOfWeeks.includes(dayOfWeek);
|
|
||||||
|
|
||||||
const slots = isEnabledDay
|
|
||||||
? generateDaySlots({
|
|
||||||
date,
|
|
||||||
dateKey,
|
|
||||||
fineRule,
|
|
||||||
})
|
|
||||||
: [];
|
|
||||||
|
|
||||||
return {
|
|
||||||
date: dateKey,
|
|
||||||
label: `${date.getMonth() + 1}/${date.getDate()}`,
|
|
||||||
subLabel: resolvePreviewSubLabel(index, dayOfWeek),
|
|
||||||
slots,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 是否为有效自提日索引。 */
|
|
||||||
function toPickupWeekDay(date: Date): PickupWeekDay {
|
|
||||||
const jsDay = date.getDay(); // 0=周日
|
|
||||||
const mapping: PickupWeekDay[] = [6, 0, 1, 2, 3, 4, 5];
|
|
||||||
return mapping[jsDay] ?? 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 生成某日预览时段。 */
|
|
||||||
function generateDaySlots(payload: {
|
|
||||||
date: Date;
|
|
||||||
dateKey: string;
|
|
||||||
fineRule: PickupFineRuleDto;
|
|
||||||
}): PickupPreviewSlotDto[] {
|
|
||||||
const startMinutes = parseTimeToMinutes(payload.fineRule.dayStartTime);
|
|
||||||
const endMinutes = parseTimeToMinutes(payload.fineRule.dayEndTime);
|
|
||||||
const interval = payload.fineRule.intervalMinutes;
|
|
||||||
const total = Math.floor((endMinutes - startMinutes) / interval);
|
|
||||||
|
|
||||||
return Array.from({ length: total + 1 }).map((_, index) => {
|
|
||||||
const minutes = startMinutes + index * interval;
|
|
||||||
const time = `${String(Math.floor(minutes / 60)).padStart(2, '0')}:${String(
|
|
||||||
minutes % 60,
|
|
||||||
).padStart(2, '0')}`;
|
|
||||||
const booked = calcMockBookedCount(
|
|
||||||
`${payload.dateKey}|${time}`,
|
|
||||||
payload.fineRule.slotCapacity,
|
|
||||||
);
|
|
||||||
const remainingCount = Math.max(0, payload.fineRule.slotCapacity - booked);
|
|
||||||
|
|
||||||
const status = resolvePreviewStatus({
|
|
||||||
date: payload.date,
|
|
||||||
fineRule: payload.fineRule,
|
|
||||||
remainingCount,
|
|
||||||
time,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
time,
|
|
||||||
status,
|
|
||||||
remainingCount,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 计算预览时段状态。 */
|
|
||||||
function resolvePreviewStatus(payload: {
|
|
||||||
date: Date;
|
|
||||||
fineRule: PickupFineRuleDto;
|
|
||||||
remainingCount: number;
|
|
||||||
time: string;
|
|
||||||
}): PickupPreviewStatus {
|
|
||||||
const now = new Date();
|
|
||||||
const today = toDateOnly(now);
|
|
||||||
const dateKey = toDateOnly(payload.date);
|
|
||||||
const slotMinutes = parseTimeToMinutes(payload.time);
|
|
||||||
const nowMinutes = now.getHours() * 60 + now.getMinutes();
|
|
||||||
const minAdvanceMinutes = payload.fineRule.minAdvanceHours * 60;
|
|
||||||
|
|
||||||
if (dateKey < today) return 'expired';
|
|
||||||
if (dateKey === today && slotMinutes - nowMinutes <= minAdvanceMinutes) {
|
|
||||||
return 'expired';
|
|
||||||
}
|
|
||||||
if (payload.remainingCount <= 0) return 'full';
|
|
||||||
if (payload.remainingCount <= 1) return 'almost';
|
|
||||||
return 'available';
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 简单哈希,保障预览可复现。 */
|
|
||||||
function calcMockBookedCount(seed: string, capacity: number) {
|
|
||||||
if (capacity <= 0) return 0;
|
|
||||||
let hash = 0;
|
|
||||||
for (const char of seed) {
|
|
||||||
hash = (hash * 31 + (char.codePointAt(0) ?? 0)) >>> 0;
|
|
||||||
}
|
|
||||||
if (hash % 7 === 0) return capacity;
|
|
||||||
if (hash % 5 === 0) return Math.max(0, capacity - 1);
|
|
||||||
return hash % (capacity + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isSameDaySet(a: PickupWeekDay[], b: PickupWeekDay[]) {
|
function isSameDaySet(a: PickupWeekDay[], b: PickupWeekDay[]) {
|
||||||
if (a.length !== b.length) return false;
|
if (a.length !== b.length) return false;
|
||||||
return a.every((day, index) => day === b[index]);
|
return a.every((day, index) => day === b[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toDateOnly(date: Date) {
|
|
||||||
const year = date.getFullYear();
|
|
||||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
||||||
const day = String(date.getDate()).padStart(2, '0');
|
|
||||||
return `${year}-${month}-${day}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addDays(baseDate: Date, days: number) {
|
|
||||||
const next = new Date(baseDate);
|
|
||||||
next.setDate(baseDate.getDate() + days);
|
|
||||||
return next;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolvePreviewSubLabel(offset: number, dayOfWeek: PickupWeekDay) {
|
|
||||||
const dayText = WEEKDAY_OPTIONS.find(
|
|
||||||
(item) => item.value === dayOfWeek,
|
|
||||||
)?.label;
|
|
||||||
if (offset === 0) return `${dayText} 今天`;
|
|
||||||
if (offset === 1) return `${dayText} 明天`;
|
|
||||||
if (offset === 2) return `${dayText} 后天`;
|
|
||||||
return dayText ?? '';
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ import type { StoreListItemDto } from '#/api/store';
|
|||||||
* 2. 组合数据加载、复制、大时段与精细规则动作。
|
* 2. 组合数据加载、复制、大时段与精细规则动作。
|
||||||
* 3. 对外暴露视图层可直接消费的状态与方法。
|
* 3. 对外暴露视图层可直接消费的状态与方法。
|
||||||
*/
|
*/
|
||||||
import type { PickupBasicSettingsDto, PickupSlotDto } from '#/api/store-pickup';
|
import type {
|
||||||
|
PickupBasicSettingsDto,
|
||||||
|
PickupPreviewDayDto,
|
||||||
|
PickupSlotDto,
|
||||||
|
} from '#/api/store-pickup';
|
||||||
import type {
|
import type {
|
||||||
PickupDrawerMode,
|
PickupDrawerMode,
|
||||||
PickupSettingsSnapshot,
|
PickupSettingsSnapshot,
|
||||||
@@ -16,7 +20,6 @@ import { computed, onActivated, onMounted, reactive, ref, watch } from 'vue';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
ALL_WEEK_DAYS,
|
ALL_WEEK_DAYS,
|
||||||
DEFAULT_BIG_SLOTS,
|
|
||||||
DEFAULT_FINE_RULE,
|
DEFAULT_FINE_RULE,
|
||||||
DEFAULT_PICKUP_BASIC_SETTINGS,
|
DEFAULT_PICKUP_BASIC_SETTINGS,
|
||||||
DEFAULT_PICKUP_MODE,
|
DEFAULT_PICKUP_MODE,
|
||||||
@@ -30,13 +33,10 @@ import { createFineRuleActions } from './pickup-page/fine-rule-actions';
|
|||||||
import {
|
import {
|
||||||
calcReservedPercent,
|
calcReservedPercent,
|
||||||
cloneBasicSettings,
|
cloneBasicSettings,
|
||||||
cloneBigSlots,
|
|
||||||
cloneFineRule,
|
cloneFineRule,
|
||||||
clonePreviewDays,
|
clonePreviewDays,
|
||||||
createSlotId,
|
createSlotId,
|
||||||
formatDayOfWeeksText,
|
formatDayOfWeeksText,
|
||||||
generatePreviewDays,
|
|
||||||
sortSlots,
|
|
||||||
} from './pickup-page/helpers';
|
} from './pickup-page/helpers';
|
||||||
import { createSlotActions } from './pickup-page/slot-actions';
|
import { createSlotActions } from './pickup-page/slot-actions';
|
||||||
|
|
||||||
@@ -56,11 +56,9 @@ export function useStorePickupPage() {
|
|||||||
const basicSettings = reactive<PickupBasicSettingsDto>(
|
const basicSettings = reactive<PickupBasicSettingsDto>(
|
||||||
cloneBasicSettings(DEFAULT_PICKUP_BASIC_SETTINGS),
|
cloneBasicSettings(DEFAULT_PICKUP_BASIC_SETTINGS),
|
||||||
);
|
);
|
||||||
const bigSlots = ref<PickupSlotDto[]>(
|
const bigSlots = ref<PickupSlotDto[]>([]);
|
||||||
sortSlots(cloneBigSlots(DEFAULT_BIG_SLOTS)),
|
|
||||||
);
|
|
||||||
const fineRule = reactive(cloneFineRule(DEFAULT_FINE_RULE));
|
const fineRule = reactive(cloneFineRule(DEFAULT_FINE_RULE));
|
||||||
const previewDays = ref(generatePreviewDays(fineRule));
|
const previewDays = ref<PickupPreviewDayDto[]>([]);
|
||||||
const selectedPreviewDate = ref(previewDays.value[0]?.date ?? '');
|
const selectedPreviewDate = ref(previewDays.value[0]?.date ?? '');
|
||||||
const snapshot = ref<null | PickupSettingsSnapshot>(null);
|
const snapshot = ref<null | PickupSettingsSnapshot>(null);
|
||||||
|
|
||||||
@@ -187,7 +185,6 @@ export function useStorePickupPage() {
|
|||||||
handleSaveFineRule,
|
handleSaveFineRule,
|
||||||
isFineDaySelected,
|
isFineDaySelected,
|
||||||
quickSelectFineDays,
|
quickSelectFineDays,
|
||||||
refreshPreviewDays,
|
|
||||||
setFineDayEndTime,
|
setFineDayEndTime,
|
||||||
setFineDayStartTime,
|
setFineDayStartTime,
|
||||||
setFineIntervalMinutes,
|
setFineIntervalMinutes,
|
||||||
@@ -197,7 +194,6 @@ export function useStorePickupPage() {
|
|||||||
toggleFineDay,
|
toggleFineDay,
|
||||||
} = createFineRuleActions({
|
} = createFineRuleActions({
|
||||||
fineRule,
|
fineRule,
|
||||||
previewDays,
|
|
||||||
saveFineRule,
|
saveFineRule,
|
||||||
selectedPreviewDate,
|
selectedPreviewDate,
|
||||||
});
|
});
|
||||||
@@ -252,7 +248,7 @@ export function useStorePickupPage() {
|
|||||||
previewDays.value =
|
previewDays.value =
|
||||||
snapshot.value?.previewDays && snapshot.value.previewDays.length > 0
|
snapshot.value?.previewDays && snapshot.value.previewDays.length > 0
|
||||||
? clonePreviewDays(snapshot.value.previewDays)
|
? clonePreviewDays(snapshot.value.previewDays)
|
||||||
: generatePreviewDays(fineRule);
|
: [];
|
||||||
selectedPreviewDate.value = previewDays.value[0]?.date ?? '';
|
selectedPreviewDate.value = previewDays.value[0]?.date ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,9 +261,9 @@ export function useStorePickupPage() {
|
|||||||
basicSettings.bookingDays = DEFAULT_PICKUP_BASIC_SETTINGS.bookingDays;
|
basicSettings.bookingDays = DEFAULT_PICKUP_BASIC_SETTINGS.bookingDays;
|
||||||
basicSettings.maxItemsPerOrder =
|
basicSettings.maxItemsPerOrder =
|
||||||
DEFAULT_PICKUP_BASIC_SETTINGS.maxItemsPerOrder;
|
DEFAULT_PICKUP_BASIC_SETTINGS.maxItemsPerOrder;
|
||||||
bigSlots.value = sortSlots(cloneBigSlots(DEFAULT_BIG_SLOTS));
|
bigSlots.value = [];
|
||||||
Object.assign(fineRule, cloneFineRule(DEFAULT_FINE_RULE));
|
Object.assign(fineRule, cloneFineRule(DEFAULT_FINE_RULE));
|
||||||
previewDays.value = generatePreviewDays(fineRule);
|
previewDays.value = [];
|
||||||
selectedPreviewDate.value = previewDays.value[0]?.date ?? '';
|
selectedPreviewDate.value = previewDays.value[0]?.date ?? '';
|
||||||
snapshot.value = null;
|
snapshot.value = null;
|
||||||
return;
|
return;
|
||||||
@@ -276,6 +272,20 @@ export function useStorePickupPage() {
|
|||||||
selectedPreviewDate.value = previewDays.value[0]?.date ?? '';
|
selectedPreviewDate.value = previewDays.value[0]?.date ?? '';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(previewDays, (days) => {
|
||||||
|
if (days.length === 0) {
|
||||||
|
selectedPreviewDate.value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasCurrent = days.some(
|
||||||
|
(item) => item.date === selectedPreviewDate.value,
|
||||||
|
);
|
||||||
|
if (!hasCurrent) {
|
||||||
|
selectedPreviewDate.value = days[0]?.date ?? '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 9. 页面首屏初始化。
|
// 9. 页面首屏初始化。
|
||||||
onMounted(loadStores);
|
onMounted(loadStores);
|
||||||
// 10. 路由回到当前页时刷新门店列表,避免使用旧缓存。
|
// 10. 路由回到当前页时刷新门店列表,避免使用旧缓存。
|
||||||
@@ -316,7 +326,6 @@ export function useStorePickupPage() {
|
|||||||
previewDays,
|
previewDays,
|
||||||
quickSelectFineDays,
|
quickSelectFineDays,
|
||||||
quickSelectSlotDays,
|
quickSelectSlotDays,
|
||||||
refreshPreviewDays,
|
|
||||||
resetBasicSettings,
|
resetBasicSettings,
|
||||||
resetFineRule,
|
resetFineRule,
|
||||||
resetFromSnapshot,
|
resetFromSnapshot,
|
||||||
|
|||||||
Reference in New Issue
Block a user