BookingCalendarDialog.vue
9.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
<template>
<el-dialog
:visible.sync="visibleProxy"
:show-close="false"
width="90%"
:close-on-click-modal="false"
custom-class="booking-calendar-dialog"
append-to-body
>
<div class="dialog-inner">
<div class="dialog-header">
<div class="dialog-title">预约日历</div>
<span class="dialog-close" @click="visibleProxy = false"><i class="el-icon-close"></i></span>
</div>
<div class="dialog-search">
<el-form @submit.native.prevent :inline="true" size="small">
<el-form-item label="预约状态">
<el-select v-model="query.F_Status" placeholder="预约状态" clearable style="width:140px">
<el-option label="已确认" value="已确认" />
<el-option label="已取消" value="已取消" />
<el-option label="已预约" value="已预约" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="search">查询</el-button>
<el-button @click="reset">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="dialog-content" v-loading="loading">
<FullCalendar
ref="fullCalendar"
class="store-calendar"
defaultView="dayGridMonth"
:header="calendarHeader"
:plugins="calendarPlugins"
:weekends="true"
:events="calendarEvents"
locale="zh-cn"
:buttonText="buttonText"
:height="calendarHeight"
:eventLimit="true"
allDayText="全天"
:editable="false"
@datesRender="datesRender"
/>
</div>
</div>
</el-dialog>
</template>
<script>
import FullCalendar from '@fullcalendar/vue'
import dayGridPlugin from '@fullcalendar/daygrid'
import timeGridPlugin from '@fullcalendar/timegrid'
import interactionPlugin from '@fullcalendar/interaction'
const MOCK_BOOKING = [
{ id: '1', storeName: '保利', yyrName: '贾琳', gkxm: '范佳佳', yysj: '2026-02-11T11:00:00', yyjs: '2026-02-11T11:30:00', F_Status: '已预约', yyjksName: '贾琳' },
{ id: '2', storeName: '保利', yyrName: '贾琳', gkxm: '黄仕碧', yysj: '2026-02-11T15:00:00', yyjs: '2026-02-11T15:30:00', F_Status: '已预约', yyjksName: '贾琳' },
{ id: '3', storeName: '保利', yyrName: '贾琳', gkxm: '赵丽', yysj: '2026-02-11T17:00:00', yyjs: '2026-02-11T17:30:00', F_Status: '已确认', yyjksName: '贾琳' },
{ id: '4', storeName: '468', yyrName: '刘恬恬', gkxm: '王英', yysj: '2026-02-11T14:00:00', yyjs: '2026-02-11T14:30:00', F_Status: '已预约', yyjksName: '刘恬恬' },
{ id: '5', storeName: '保利', yyrName: '贾琳', gkxm: '罗建琼', yysj: '2026-02-11T10:30:00', yyjs: '2026-02-11T11:00:00', F_Status: '已确认', yyjksName: '贾琳' },
{ id: '6', storeName: '保利', yyrName: '贾琳', gkxm: '沈丽', yysj: '2026-02-10T17:00:00', yyjs: '2026-02-10T17:30:00', F_Status: '已预约', yyjksName: '贾琳' },
{ id: '7', storeName: '静居寺', yyrName: '董顺秀', gkxm: '陈晴', yysj: '2026-02-11T09:30:00', yyjs: '2026-02-11T10:00:00', F_Status: '已取消', yyjksName: '董顺秀' },
{ id: '8', storeName: '静居寺', yyrName: '董顺秀', gkxm: '胡蝶', yysj: '2026-02-10T17:00:00', yyjs: '2026-02-10T17:30:00', F_Status: '已预约', yyjksName: '董顺秀' },
{ id: '9', storeName: '静居寺', yyrName: '董顺秀', gkxm: '魏海燕', yysj: '2026-02-10T14:00:00', yyjs: '2026-02-10T14:30:00', F_Status: '已确认', yyjksName: '董顺秀' },
{ id: '10', storeName: '静居寺', yyrName: '董顺秀', gkxm: '肖丛娇', yysj: '2026-02-10T11:00:00', yyjs: '2026-02-10T11:30:00', F_Status: '已预约', yyjksName: '董顺秀' }
]
export default {
name: 'BookingCalendarDialog',
components: { FullCalendar },
props: { visible: { type: Boolean, default: false } },
data() {
return {
loading: false,
mockData: MOCK_BOOKING,
query: { F_Status: undefined },
calendarPlugins: [dayGridPlugin, timeGridPlugin, interactionPlugin],
calendarEvents: [],
calendarHeader: { left: 'prev,next today', center: 'title', right: 'dayGridMonth,timeGridWeek,timeGridDay' },
buttonText: { today: '今日', month: '月', week: '周', day: '日' },
startTime: null,
endTime: null,
calendarHeight: 600
}
},
computed: {
visibleProxy: {
get() { return this.visible },
set(v) { this.$emit('update:visible', v) }
}
},
watch: {
visible(v) {
if (v) {
this.$nextTick(() => { this.calcHeight(); this.initData() })
}
}
},
mounted() {
window.addEventListener('resize', this.calcHeight)
},
beforeDestroy() {
window.removeEventListener('resize', this.calcHeight)
},
methods: {
calcHeight() {
this.$nextTick(() => {
const el = this.$el && this.$el.querySelector('.dialog-content')
if (el) {
this.calendarHeight = Math.max(el.clientHeight - 20, 500)
}
})
},
datesRender(info) {
const view = info.view
this.startTime = view.activeStart
this.endTime = view.activeEnd
this.initData()
},
initData() {
this.loading = true
setTimeout(() => {
let filtered = [...this.mockData]
if (this.query.F_Status) filtered = filtered.filter(r => r.F_Status === this.query.F_Status)
if (this.startTime && this.endTime) {
filtered = filtered.filter(r => {
const t = new Date(r.yysj).getTime()
return t >= this.startTime.getTime() && t < this.endTime.getTime()
})
}
this.calendarEvents = filtered.map(item => {
let color = '#409EFF'
if (item.F_Status === '已确认') color = '#67C23A'
else if (item.F_Status === '已取消') color = '#F56C6C'
let title = `${item.gkxm || '无'} - ${item.yyrName || '无'}`
if (item.yyjksName) title += ` (${item.yyjksName})`
return {
id: item.id,
title,
start: item.yysj ? new Date(item.yysj).toISOString() : new Date().toISOString(),
end: item.yyjs ? new Date(item.yyjs).toISOString() : new Date().toISOString(),
color,
editable: false,
allDay: false
}
})
this.loading = false
}, 300)
},
search() { this.initData() },
reset() { this.query.F_Status = undefined; this.initData() }
}
}
</script>
<style lang="scss">
@import '~@fullcalendar/core/main.css';
@import '~@fullcalendar/daygrid/main.css';
@import '~@fullcalendar/timegrid/main.css';
.booking-calendar-dialog .store-calendar {
.fc-toolbar.fc-header-toolbar { padding: 16px 20px; margin-bottom: 0; border-bottom: 2px solid #f1f5f9; background: linear-gradient(135deg, rgba(59,130,246,0.02) 0%, rgba(96,165,250,0.02) 100%); }
.fc-toolbar-title { font-size: 18px; font-weight: 700; color: #1e293b; }
.fc-button-primary { background-color: #3b82f6; border-color: #3b82f6; border-radius: 10px; font-size: 13px; font-weight: 500; height: 34px; line-height: 34px; padding: 0 14px; transition: all 0.2s; &:hover { background: #2563eb; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(59,130,246,0.3); } }
.fc-button-primary:not(:disabled):active, .fc-button-primary:not(:disabled).fc-button-active { background-color: #2563eb; border-color: #2563eb; box-shadow: 0 4px 12px rgba(59,130,246,0.3); }
.fc-day-today { background: linear-gradient(135deg, rgba(59,130,246,0.05) 0%, rgba(96,165,250,0.05) 100%); .fc-daygrid-day-number { background: linear-gradient(135deg, #3b82f6, #2563eb); color: #fff; border-radius: 6px; box-shadow: 0 2px 8px rgba(59,130,246,0.3); } }
.fc-event { cursor: pointer; border-radius: 6px; padding: 3px 6px; font-size: 12px; font-weight: 500; border: none; box-shadow: 0 2px 4px rgba(0,0,0,0.1); transition: all 0.2s; &:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(0,0,0,0.15); } }
.fc-day-header { font-size: 14px !important; color: #64748b !important; font-weight: 700 !important; background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%) !important; border-bottom: 2px solid #e2e8f0 !important; padding: 12px 8px !important; }
.fc-unthemed th, .fc-unthemed td { border-color: #f1f5f9; }
}
</style>
<style lang="scss" scoped>
::v-deep .booking-calendar-dialog { max-width: 1600px; margin-top: 3vh !important; border-radius: 20px; padding: 0; background: radial-gradient(circle at 0 0, rgba(255,255,255,0.96) 0, rgba(248,250,252,0.98) 40%, rgba(241,245,249,0.98) 100%); box-shadow: 0 24px 48px rgba(15,23,42,0.18), 0 0 0 1px rgba(255,255,255,0.9); backdrop-filter: blur(22px); -webkit-backdrop-filter: blur(22px); }
::v-deep .el-dialog__header { display: none; }
::v-deep .el-dialog__body { padding: 0; }
.dialog-inner { display: flex; flex-direction: column; max-height: 92vh; height: 88vh; }
.dialog-header { flex-shrink: 0; display: flex; align-items: center; justify-content: space-between; margin: 18px 22px 0; padding: 10px 14px; border-radius: 14px; background: rgba(219,234,254,0.96); }
.dialog-title { font-size: 17px; font-weight: 600; color: #0f172a; }
.dialog-close { cursor: pointer; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; border-radius: 999px; color: #64748b; transition: all 0.15s; &:hover { background: rgba(0,0,0,0.06); color: #0f172a; } }
.dialog-search { flex-shrink: 0; padding: 12px 22px 4px; }
.dialog-content { flex: 1; min-height: 0; overflow: hidden; padding: 0 22px 14px; }
::v-deep .booking-calendar-dialog .el-input__inner { border-radius: 999px; height: 32px; line-height: 32px; border-color: #e5e7eb; background-color: #f9fafb; &:focus { border-color: #2563eb; } }
::v-deep .booking-calendar-dialog .el-button--primary { border-radius: 999px; background: #2563eb; border-color: #2563eb; box-shadow: 0 4px 10px rgba(37,99,235,0.35); }
::v-deep .booking-calendar-dialog .el-button--default { border-radius: 999px; }
::v-deep .booking-calendar-dialog .el-form-item { margin-bottom: 8px; }
::v-deep .booking-calendar-dialog .el-form-item__label { white-space: nowrap; }
</style>