diff --git a/src/modules/appointment-calendar/AppointmentCalendarPanel.test.ts b/src/modules/appointment-calendar/AppointmentCalendarPanel.test.ts
index 4870fa91..141ca57d 100644
--- a/src/modules/appointment-calendar/AppointmentCalendarPanel.test.ts
+++ b/src/modules/appointment-calendar/AppointmentCalendarPanel.test.ts
@@ -25,6 +25,22 @@ describe('appointment calendar interactions', () => {
expect(wrapper.emitted('range')?.at(-1)).toEqual([{ from: '2026-09-28', to: '2026-10-05' }])
})
+ it('keeps busy days to three previews in both views and exposes the full count', async () => {
+ const wrapper = render()
+ await wrapper.setProps({ events: Array.from({ length: 233 }, (_, index) => ({
+ key: `arrival-${index}`, type: 'arrival' as const, date: '2026-09-24', time: '09:00',
+ title: `船舶 ${index}`, fields: [],
+ })) })
+ for (const view of ['month', 'week']) {
+ if (view === 'week') await wrapper.get('[aria-label="预约视图"]').findAll('button')[1]!.trigger('click')
+ const day = wrapper.get('[aria-label="2026-09-24,233 项安排"]')
+ expect(day.findAll('.event-chip')).toHaveLength(3)
+ expect(day.attributes('title')).toContain('共 233 条安排')
+ expect(day.get('.event-more').text()).toBe('还有 230 条')
+ expect(wrapper.findAll('.detail-event')).toHaveLength(233)
+ }
+ })
+
it('uses the selected day for creation and emits edit/delete only from detail actions', async () => {
const wrapper = render()
await wrapper.findAll('button').find(button => button.text() === '+ 新增预约')!.trigger('click')
diff --git a/src/modules/appointment-calendar/AppointmentCalendarPanel.vue b/src/modules/appointment-calendar/AppointmentCalendarPanel.vue
index 108e3896..dfa6a354 100644
--- a/src/modules/appointment-calendar/AppointmentCalendarPanel.vue
+++ b/src/modules/appointment-calendar/AppointmentCalendarPanel.vue
@@ -84,13 +84,13 @@ defineExpose({ selectedDate, mode })
@@ -131,19 +131,21 @@ defineExpose({ selectedDate, mode })
.weekdays, .calendar-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.weekdays { text-align: center; color: #8491a5; background: #f8faff; padding: 12px 14px; font-size: 13px; }
.calendar-grid { gap: 7px; padding: 12px; background: #fbfcff; }
-.calendar-day { display: flex; flex-direction: column; align-items: stretch; justify-content: flex-start; min-height: 100px; padding: 8px; border: 1px solid #e5eaf2; border-radius: 8px; background: #fff; text-align: left; cursor: pointer; color: inherit; font: inherit; overflow: hidden; }
+.calendar-day { display: flex; flex-direction: column; align-items: stretch; justify-content: flex-start; height: 128px; min-width: 0; padding: 8px; border: 1px solid #e5eaf2; border-radius: 8px; background: #fff; text-align: left; cursor: pointer; color: inherit; font: inherit; overflow: hidden; }
.calendar-day.outside { background: #f7f8fa; color: #98a2b3; }
+.calendar-day:hover { border-color: #bac8ed; background: #f8faff; }
.calendar-day.selected { border-color: #617ce9; background: #f5f7ff; }
.calendar-day:focus-visible, .nav-button:focus-visible, .view-toggle button:focus-visible { outline: 2px solid #4167d8; outline-offset: 2px; }
.day-number { display: block; font-size: 12px; font-weight: 600; }
-.day-events { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; }
-.event-chip { display: block; padding: 4px 6px; border-radius: 4px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
+.day-events { display: flex; flex-direction: column; gap: 3px; margin-top: 6px; }
+.event-chip { display: block; padding: 3px 6px; line-height: 16px; border-radius: 4px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
.arrival { --event-bg: #edf3ff; --event-fg: #3765d4; }
.delivery { --event-bg: #e8f8f1; --event-fg: #18845f; }
.event-chip, .event-type { background: var(--event-bg); color: var(--event-fg); }
-.event-chip small { display: block; margin-top: 4px; }
+.event-chip small { display: block; margin-top: 2px; font-size: 11px; opacity: .8; }
+.event-more { padding: 1px 6px; line-height: 16px; }
.event-more, .empty-day { color: #667085; font-size: 12px; }
-.week-grid .calendar-day { min-height: 350px; align-content: start; }
+.week-grid .calendar-day { height: 220px; }
.calendar-detail { align-self: start; }
.calendar-detail > header { padding: 18px; border-bottom: 1px solid #edf0f6; }
.calendar-detail > header > strong { font-size: 17px; }
diff --git a/src/modules/appointment-calendar/PROTOTYPE_MATRIX.md b/src/modules/appointment-calendar/PROTOTYPE_MATRIX.md
index 37f47e98..c5095740 100644
--- a/src/modules/appointment-calendar/PROTOTYPE_MATRIX.md
+++ b/src/modules/appointment-calendar/PROTOTYPE_MATRIX.md
@@ -12,7 +12,7 @@
| 到港详情 | 当天安排右侧只读 | 使用货柜/船节点事实,无逐行详情请求,无模拟到港数据 |
| 删除预约 | 二次确认 | 显式提示全部货物解除预约关联;独立删除权限,已关联有效排车拒绝删除 |
| 月/周切换 | 顶部切换与前后按钮 | 周模式按7天移动,修正原型仍按月移动的问题 |
-| 多条安排 | 日历标签、右侧明细 | 日格限制显示数量并提示剩余条目,右侧全部展示 |
+| 多条安排 | 日历标签、右侧明细 | 月/周日格均最多显示3条,超出显示“还有N条”,悬停显示当天总数;日格固定高度,右侧全部展示并独立滚动 |
预约结束时间未明确时允许为空;不把预计装车时间当结束时间。预约号、目的仓和货物来源遵守服务端合同。日历范围最多42天,每次范围变化查询预约与到港各一次;选择范围内日期不发送请求。候选只在用户点击查询时分页读取,跨页选择保留,重新查询清空。