feat(admin): implement full day-by-day schedule editor with live preview
## Features ### Admin Schedule Page (`packages/app/src/pages/admin/schedule.vue`) - Interactive date-based slot editor for managing daily schedules - Real-time slot editing: start/end times, capacity adjustments - Slot deletion with conflict warnings when bookings exist - Add new slots with modal dialog - Live booking status display (booked count, people names) - Publish/Save changes with sync feedback - Revert unsaved changes with confirmation - Skeleton loading states and empty state handling - Responsive design with optimized mobile UX ### Backend Enhancements - **New DTO** (`PublishDaySlotsDto`): Structured slot publishing with validation - Date string validation - Slot array with existing slot IDs for updates - Time and capacity validation per slot - **Schedule Preview API** (`getSchedulePreview`): - Check for existing published slots - Fallback to active WeekTemplates for unpublished dates - Unified response format with isPublished flag - **Publish Slots API** (`publishDaySlots`): - Atomic transaction for consistency - Update existing slots with new times/capacity - Create new slots from template data - Delete unpublished slots or set to CLOSED if bookings exist - Prevent capacity reduction below existing bookings - Returns all published slots for feedback ### State Management - Enhanced admin store with schedule state - Support for pending/unsaved slot changes - Optimistic UI updates with server sync ### Documentation - Comprehensive scheduling system architecture docs - Quick reference for admin workflows - Flow diagrams and state transitions - Implementation guide for future maintenance ## Breaking Changes None ## Testing Recommendations - Create slots for future dates via schedule editor - Verify booking prevention for locked/full slots - Test capacity adjustments with existing bookings - Confirm template-based schedule generation - Verify transaction rollback on publish failures Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -163,9 +163,9 @@ const dayOptions = [1, 2, 3, 4, 5, 6, 7].map((d) => ({ label: WEEKDAY_LABELS[d],
|
||||
|
||||
const form = ref({
|
||||
dayIdx: 0,
|
||||
startTime: '09:00',
|
||||
endTime: '10:00',
|
||||
capacityStr: '10',
|
||||
startTime: '08:00',
|
||||
endTime: '09:00',
|
||||
capacityStr: '1',
|
||||
})
|
||||
|
||||
const grouped = computed(() => {
|
||||
@@ -180,11 +180,38 @@ const grouped = computed(() => {
|
||||
)
|
||||
})
|
||||
|
||||
/** 生成默认模板:周一到周日,8:00-22:00 每小时一个时段 */
|
||||
function generateDefaultTemplates(): LocalTemplate[] {
|
||||
const defaults: LocalTemplate[] = []
|
||||
for (let day = 1; day <= 7; day++) {
|
||||
for (let hour = 8; hour < 22; hour++) {
|
||||
const start = String(hour).padStart(2, '0') + ':00'
|
||||
const end = String(hour + 1).padStart(2, '0') + ':00'
|
||||
defaults.push({
|
||||
_key: `default-${day}-${start}`,
|
||||
dayOfWeek: day,
|
||||
startTime: start,
|
||||
endTime: end,
|
||||
capacity: 1,
|
||||
isActive: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
return defaults
|
||||
}
|
||||
|
||||
async function fetchTemplates() {
|
||||
loading.value = true
|
||||
try {
|
||||
templates.value = await adminStore.fetchWeekTemplates()
|
||||
isDirty.value = false
|
||||
const data = await adminStore.fetchWeekTemplates()
|
||||
if (data.length === 0) {
|
||||
// No templates yet — pre-fill with defaults
|
||||
templates.value = generateDefaultTemplates()
|
||||
isDirty.value = true
|
||||
} else {
|
||||
templates.value = data
|
||||
isDirty.value = false
|
||||
}
|
||||
} catch {
|
||||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||||
} finally {
|
||||
@@ -194,7 +221,7 @@ async function fetchTemplates() {
|
||||
|
||||
function openAdd() {
|
||||
editTarget.value = null
|
||||
form.value = { dayIdx: 0, startTime: '09:00', endTime: '10:00', capacityStr: '10' }
|
||||
form.value = { dayIdx: 0, startTime: '08:00', endTime: '09:00', capacityStr: '1' }
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user