@opendesign-plus/components 0.0.1-rc.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/dist/chunk-OElCookieNotice.cjs.js +1 -0
  2. package/dist/chunk-OElCookieNotice.es.js +833 -0
  3. package/dist/components/OBanner.vue.d.ts +11 -0
  4. package/dist/components/OCookieNotice.vue.d.ts +17 -0
  5. package/dist/components/OFooter.vue.d.ts +46 -0
  6. package/dist/components/OHeaderSearch.vue.d.ts +692 -0
  7. package/dist/components/OHeaderUser.vue.d.ts +38 -0
  8. package/dist/components/OPlusConfigProvider.vue.d.ts +23 -0
  9. package/dist/components/OSection.vue.d.ts +37 -0
  10. package/dist/components/OSourceCode.vue.d.ts +20 -0
  11. package/dist/components/OThemeSwitcher.vue.d.ts +28 -0
  12. package/dist/components/activity/OActivityApproval.vue.d.ts +277 -0
  13. package/dist/components/activity/OActivityForm.vue.d.ts +140 -0
  14. package/dist/components/activity/OMyActivityCalendar.vue.d.ts +578 -0
  15. package/dist/components/activity/config.d.ts +15 -0
  16. package/dist/components/activity/index.d.ts +623 -0
  17. package/dist/components/activity/types.d.ts +81 -0
  18. package/dist/components/element-plus/OElCookieNotice.vue.d.ts +34 -0
  19. package/dist/components/element-plus/index.d.ts +2 -0
  20. package/dist/components/events/OEventsApply.vue.d.ts +16 -0
  21. package/dist/components/events/OEventsCalendar.vue.d.ts +5 -0
  22. package/dist/components/events/OEventsList.vue.d.ts +26 -0
  23. package/dist/components/events/config.d.ts +27 -0
  24. package/dist/components/events/index.d.ts +78 -0
  25. package/dist/components/events/types.d.ts +66 -0
  26. package/dist/components/events/utils.d.ts +7 -0
  27. package/dist/components/header/OHeader.vue.d.ts +24 -0
  28. package/dist/components/header/OHeaderMoblie.vue.d.ts +33 -0
  29. package/dist/components/header/components/HeaderContent.vue.d.ts +6 -0
  30. package/dist/components/header/components/HeaderNav.vue.d.ts +7 -0
  31. package/dist/components/header/components/HeaderNavMoblie.vue.d.ts +17 -0
  32. package/dist/components/header/components/HeaderUbmcNav.vue.d.ts +2 -0
  33. package/dist/components/header/index.d.ts +22 -0
  34. package/dist/components/meeting/OMeetingCalendar.vue.d.ts +298 -0
  35. package/dist/components/meeting/OMeetingForm.vue.d.ts +145 -0
  36. package/dist/components/meeting/OMyMeetingCalendar.vue.d.ts +586 -0
  37. package/dist/components/meeting/OSigMeetingCalendar.vue.d.ts +24 -0
  38. package/dist/components/meeting/components/OMeetingCalendarList.vue.d.ts +28 -0
  39. package/dist/components/meeting/components/OMeetingCalendarSelector.vue.d.ts +664 -0
  40. package/dist/components/meeting/components/OMeetingDetail.vue.d.ts +12 -0
  41. package/dist/components/meeting/components/OMeetingPlaybackSubtitles.vue.d.ts +5 -0
  42. package/dist/components/meeting/components/OMeetingPlaybackVideo.vue.d.ts +17 -0
  43. package/dist/components/meeting/components/OSigMeetingAside.vue.d.ts +16 -0
  44. package/dist/components/meeting/config.d.ts +27 -0
  45. package/dist/components/meeting/types.d.ts +166 -0
  46. package/dist/components/meeting/utils.d.ts +22 -0
  47. package/dist/components.cjs.js +224 -0
  48. package/dist/components.css +1 -0
  49. package/dist/components.element.cjs.js +1 -0
  50. package/dist/components.element.es.js +4 -0
  51. package/dist/components.es.js +45054 -0
  52. package/dist/index.d.ts +19 -0
  53. package/docs/design.md +27 -0
  54. package/docs/design_banner.md +41 -0
  55. package/docs/design_section.md +27 -0
  56. package/package.json +56 -0
  57. package/scripts/generate-components-index.js +104 -0
  58. package/src/assets/events/svg-icons/icon-checked.svg +3 -0
  59. package/src/assets/events/svg-icons/icon-competition.svg +7 -0
  60. package/src/assets/events/svg-icons/icon-events.svg +4 -0
  61. package/src/assets/events/svg-icons/icon-release.svg +4 -0
  62. package/src/assets/events/svg-icons/icon-summit.svg +4 -0
  63. package/src/assets/meeting/svg-icons/icon-all.svg +3 -0
  64. package/src/assets/meeting/svg-icons/icon-backward.svg +4 -0
  65. package/src/assets/meeting/svg-icons/icon-calendar.svg +3 -0
  66. package/src/assets/meeting/svg-icons/icon-cancel.svg +4 -0
  67. package/src/assets/meeting/svg-icons/icon-captions.svg +4 -0
  68. package/src/assets/meeting/svg-icons/icon-close-captions.svg +6 -0
  69. package/src/assets/meeting/svg-icons/icon-close-fullscreen.svg +6 -0
  70. package/src/assets/meeting/svg-icons/icon-copy.svg +3 -0
  71. package/src/assets/meeting/svg-icons/icon-create.svg +5 -0
  72. package/src/assets/meeting/svg-icons/icon-delete.svg +7 -0
  73. package/src/assets/meeting/svg-icons/icon-empty.svg +31 -0
  74. package/src/assets/meeting/svg-icons/icon-empty_dark.svg +49 -0
  75. package/src/assets/meeting/svg-icons/icon-event.svg +3 -0
  76. package/src/assets/meeting/svg-icons/icon-export.svg +3 -0
  77. package/src/assets/meeting/svg-icons/icon-forward.svg +4 -0
  78. package/src/assets/meeting/svg-icons/icon-fullscreen.svg +6 -0
  79. package/src/assets/meeting/svg-icons/icon-help.svg +3 -0
  80. package/src/assets/meeting/svg-icons/icon-important.svg +4 -0
  81. package/src/assets/meeting/svg-icons/icon-info.svg +3 -0
  82. package/src/assets/meeting/svg-icons/icon-meet.svg +3 -0
  83. package/src/assets/meeting/svg-icons/icon-meeting-message.svg +5 -0
  84. package/src/assets/meeting/svg-icons/icon-meeting.svg +4 -0
  85. package/src/assets/meeting/svg-icons/icon-play.svg +5 -0
  86. package/src/assets/meeting/svg-icons/icon-playing-tip.svg +7 -0
  87. package/src/assets/meeting/svg-icons/icon-playing.svg +5 -0
  88. package/src/assets/meeting/svg-icons/icon-question.svg +4 -0
  89. package/src/assets/meeting/svg-icons/icon-sound.svg +5 -0
  90. package/src/assets/meeting/svg-icons/icon-speaker.svg +3 -0
  91. package/src/assets/meeting/svg-icons/icon-summit.svg +3 -0
  92. package/src/assets/meeting/svg-icons/icon-telligent.svg +3 -0
  93. package/src/assets/meeting/svg-icons/icon-tip.svg +3 -0
  94. package/src/assets/meeting/svg-icons/icon-todo.svg +4 -0
  95. package/src/assets/meeting/transparent.png +0 -0
  96. package/src/assets/svg-icons/icon-arrow-left.svg +3 -0
  97. package/src/assets/svg-icons/icon-avatar-line.svg +3 -0
  98. package/src/assets/svg-icons/icon-caret-left.svg +3 -0
  99. package/src/assets/svg-icons/icon-caret-right.svg +3 -0
  100. package/src/assets/svg-icons/icon-chevron-down.svg +3 -0
  101. package/src/assets/svg-icons/icon-chevron-right.svg +3 -0
  102. package/src/assets/svg-icons/icon-chevron-up.svg +3 -0
  103. package/src/assets/svg-icons/icon-close.svg +3 -0
  104. package/src/assets/svg-icons/icon-delete.svg +3 -0
  105. package/src/assets/svg-icons/icon-filter.svg +3 -0
  106. package/src/assets/svg-icons/icon-header-back.svg +3 -0
  107. package/src/assets/svg-icons/icon-header-delete.svg +3 -0
  108. package/src/assets/svg-icons/icon-header-menu.svg +3 -0
  109. package/src/assets/svg-icons/icon-header-person.svg +3 -0
  110. package/src/assets/svg-icons/icon-header-search.svg +4 -0
  111. package/src/assets/svg-icons/icon-loading.svg +4 -0
  112. package/src/assets/svg-icons/icon-locale.svg +3 -0
  113. package/src/assets/svg-icons/icon-log-off.svg +3 -0
  114. package/src/assets/svg-icons/icon-message.svg +3 -0
  115. package/src/assets/svg-icons/icon-moon.svg +3 -0
  116. package/src/assets/svg-icons/icon-outlink.svg +3 -0
  117. package/src/assets/svg-icons/icon-overview.svg +3 -0
  118. package/src/assets/svg-icons/icon-search.svg +3 -0
  119. package/src/assets/svg-icons/icon-setting.svg +3 -0
  120. package/src/assets/svg-icons/icon-sun.svg +3 -0
  121. package/src/assets/svg-icons/icon-tips.svg +3 -0
  122. package/src/components/OBanner.vue +398 -0
  123. package/src/components/OCookieNotice.vue +575 -0
  124. package/src/components/OFooter.vue +576 -0
  125. package/src/components/OHeaderSearch.vue +601 -0
  126. package/src/components/OHeaderUser.vue +237 -0
  127. package/src/components/OPlusConfigProvider.vue +32 -0
  128. package/src/components/OSection.vue +178 -0
  129. package/src/components/OSourceCode.vue +153 -0
  130. package/src/components/OThemeSwitcher.vue +108 -0
  131. package/src/components/activity/OActivityApproval.vue +871 -0
  132. package/src/components/activity/OActivityForm.vue +548 -0
  133. package/src/components/activity/OMyActivityCalendar.vue +1501 -0
  134. package/src/components/activity/config.ts +141 -0
  135. package/src/components/activity/index.ts +24 -0
  136. package/src/components/activity/types.ts +88 -0
  137. package/src/components/common/AppAvatar.vue +83 -0
  138. package/src/components/common/ClientOnlyWrapper.ts +21 -0
  139. package/src/components/common/ContentWrapper.vue +85 -0
  140. package/src/components/common/MoreText.vue +124 -0
  141. package/src/components/common/ThFilter.vue +330 -0
  142. package/src/components/element-plus/OElCookieNotice.vue +603 -0
  143. package/src/components/element-plus/index.ts +3 -0
  144. package/src/components/events/OEventsApply.vue +419 -0
  145. package/src/components/events/OEventsCalendar.vue +588 -0
  146. package/src/components/events/OEventsList.vue +354 -0
  147. package/src/components/events/config.ts +35 -0
  148. package/src/components/events/index.ts +24 -0
  149. package/src/components/events/types.ts +80 -0
  150. package/src/components/events/utils.ts +9 -0
  151. package/src/components/header/OHeader.vue +175 -0
  152. package/src/components/header/OHeaderMoblie.vue +152 -0
  153. package/src/components/header/components/HeaderContent.vue +942 -0
  154. package/src/components/header/components/HeaderNav.vue +280 -0
  155. package/src/components/header/components/HeaderNavMoblie.vue +346 -0
  156. package/src/components/header/components/HeaderUbmcNav.vue +540 -0
  157. package/src/components/header/index.ts +16 -0
  158. package/src/components/meeting/OMeetingCalendar.vue +900 -0
  159. package/src/components/meeting/OMeetingForm.vue +1041 -0
  160. package/src/components/meeting/OMeetingPlayback.vue +439 -0
  161. package/src/components/meeting/OMyMeetingCalendar.vue +1502 -0
  162. package/src/components/meeting/OSigMeetingCalendar.vue +411 -0
  163. package/src/components/meeting/components/OMeetingCalendarList.vue +505 -0
  164. package/src/components/meeting/components/OMeetingCalendarSelector.vue +206 -0
  165. package/src/components/meeting/components/OMeetingDetail.vue +227 -0
  166. package/src/components/meeting/components/OMeetingPlaybackSubtitles.vue +611 -0
  167. package/src/components/meeting/components/OMeetingPlaybackVideo.vue +741 -0
  168. package/src/components/meeting/components/OSigMeetingAside.vue +197 -0
  169. package/src/components/meeting/config.ts +121 -0
  170. package/src/components/meeting/index.ts +45 -0
  171. package/src/components/meeting/types.ts +193 -0
  172. package/src/components/meeting/utils.ts +123 -0
  173. package/src/draft/Banner.vue +265 -0
  174. package/src/draft/ButtonCards.vue +106 -0
  175. package/src/draft/Feature.vue +134 -0
  176. package/src/draft/Footer.vue +512 -0
  177. package/src/draft/HorizontalAnchor.vue +165 -0
  178. package/src/draft/ItemSwiper.vue +133 -0
  179. package/src/draft/Logo.vue +141 -0
  180. package/src/draft/LogoCard.vue +75 -0
  181. package/src/draft/LogoV2.vue +19 -0
  182. package/src/draft/MainCard.vue +38 -0
  183. package/src/draft/MultiCard.vue +95 -0
  184. package/src/draft/MultiIconCard.vue +74 -0
  185. package/src/draft/OInfoCard.vue +176 -0
  186. package/src/draft/Process.vue +81 -0
  187. package/src/draft/Section.vue +167 -0
  188. package/src/draft/SingleTabCard.vue +85 -0
  189. package/src/draft/SliderCard.vue +110 -0
  190. package/src/env.d.ts +16 -0
  191. package/src/i18n/en.ts +261 -0
  192. package/src/i18n/index.ts +56 -0
  193. package/src/i18n/zh.ts +250 -0
  194. package/src/index.ts +45 -0
  195. package/src/shared/provide.ts +6 -0
  196. package/src/shims-vue-dompurify-html.d.ts +17 -0
  197. package/src/vue.d.ts +10 -0
  198. package/tsconfig.json +37 -0
  199. package/vite.config.ts +118 -0
@@ -0,0 +1,1502 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ DialogActionT,
4
+ OCollapse,
5
+ OCollapseItem,
6
+ ODialog,
7
+ ODivider,
8
+ OIcon,
9
+ OIconArrowLeft,
10
+ OIconArrowRight,
11
+ OIconChevronLeft,
12
+ OIconChevronRight,
13
+ OLink,
14
+ ORadio,
15
+ ORadioGroup,
16
+ OScroller,
17
+ OTag,
18
+ useMessage,
19
+ } from '@opensig/opendesign';
20
+ import { ElCalendar } from 'element-plus';
21
+ import { computed, nextTick, onMounted, ref, onUnmounted, watch } from 'vue';
22
+ import OMeetingDetail from './components/OMeetingDetail.vue';
23
+ import { MeetingGroupType, MeetingItemT, PageParamsT } from './types.ts';
24
+ import dayjs from 'dayjs';
25
+ import IconMeeting from '~icons/meeting/icon-meet.svg';
26
+ import { INTERVAL_DAY, INTERVAL_MONTH, INTERVAL_WEEK, WEEKDAY } from './config.ts';
27
+ import { formatDate, getDateNumber } from './utils.ts';
28
+ import { getPointStr } from './utils.ts';
29
+ import { useDebounceFn } from '@vueuse/core';
30
+ import { useScreen } from '@opendesign-plus/composables';
31
+ import { Locales, useI18n } from '@/i18n';
32
+ import IconCopy from '~icons/meeting/icon-copy.svg';
33
+
34
+ const message = useMessage(null);
35
+ const tableLoading = ref(false); // 列表数据加载状态
36
+ const currentPage = ref(1); // 分页-当前页
37
+ const pageSize = ref(50); // 分页-每页数量
38
+ const total = ref<number | null>(null); // 分页-总数
39
+ const list = ref<MeetingItemT[]>([]); // 列表数据
40
+ const originList = ref([]); // 原始数据
41
+ const canLoadMore = computed(() => total.value === null || originList.value.length < total.value);
42
+ const reloadAll = ref(false); // 是否需要清空数据
43
+ const { t, locale } = useI18n();
44
+ const isEn = computed(() => locale.value === Locales.EN);
45
+
46
+ const props = withDefaults(defineProps<{
47
+ cancelSubMeetingRequest: any;
48
+ deleteMeetingRequest: any;
49
+ getMeetingListRequest: any;
50
+ groupType: MeetingGroupType
51
+ }>(), {
52
+ groupType: MeetingGroupType.SIG,
53
+ });
54
+
55
+ const dialogLoading = ref(false); // 弹窗按钮状态
56
+ const { isPhone } = useScreen();
57
+ const expanded = ref([]); // 展开的数据, sub_id 或 id
58
+ const emits = defineEmits(['edit']);
59
+ // -------------------- 自动加载下一页 --------------------
60
+ const nextLoading = ref(false);
61
+ const bottomReached = ref(false);
62
+ const headerRef = ref();
63
+ const headerHeight = ref(0);
64
+ const getHeaderHeight = () => {
65
+ headerHeight.value = headerRef.value?.clientHeight || 0;
66
+ };
67
+
68
+ const getList = async () => {
69
+ if (!props.getMeetingListRequest) {
70
+ return;
71
+ }
72
+ if (nextLoading.value) {
73
+ return;
74
+ }
75
+ try {
76
+ if (total.value > 0 && (currentPage.value - 1) * pageSize.value > total.value) {
77
+ return;
78
+ }
79
+ // 当月数据已加载完,手动切换下一月
80
+ if (!canLoadMore.value && isPhone.value) {
81
+ changeMonth('next-month');
82
+ return;
83
+ }
84
+ tableLoading.value = true;
85
+ nextLoading.value = true;
86
+ const res = await props.getMeetingListRequest({
87
+ page: currentPage.value,
88
+ size: pageSize.value,
89
+ order_by: 'date',
90
+ order_type: 'asc',
91
+ month: dayjs(selectedDate.value).format('YYYY-MM'),
92
+ } as unknown as PageParamsT);
93
+ const tempList = (res.data || [])
94
+ .map((item: MeetingItemT) => {
95
+ const {
96
+ is_cycle,
97
+ date,
98
+ start,
99
+ end,
100
+ cycle_sub,
101
+ cycle_start_date,
102
+ cycle_end_date,
103
+ cycle_start,
104
+ cycle_end,
105
+ cycle_type,
106
+ cycle_interval,
107
+ cycle_point,
108
+ } =
109
+ item;
110
+ if (is_cycle) {
111
+ let timeRange = '';
112
+ let cycleType = '';
113
+ if (cycle_type === INTERVAL_DAY) {
114
+ cycleType = t('meeting.cycleDay');
115
+ }
116
+ if (cycle_type === INTERVAL_WEEK) {
117
+ if (cycle_interval > 1) {
118
+ cycleType = t('meeting.cycleWeek.other', [getPointStr(cycle_type, cycle_point), cycle_interval]);
119
+ } else {
120
+ cycleType = t('meeting.cycleWeek.one', [getPointStr(cycle_type, cycle_point)]);
121
+ }
122
+ }
123
+ if (cycle_type === INTERVAL_MONTH) {
124
+ cycleType = t('meeting.cycleMonth', [getPointStr(cycle_type, cycle_point)]);
125
+ }
126
+ timeRange = t('meeting.cycleMeetingText2', {
127
+ startDate: cycle_start_date,
128
+ endDate: cycle_end_date,
129
+ startTime: cycle_start,
130
+ endTime: cycle_end,
131
+ cycleType,
132
+ });
133
+ const obsData = item.obs_data?.filter((v) => v.text_video_url);
134
+ return cycle_sub
135
+ .filter((v) => {
136
+ return (
137
+ dayjs(v.date).isBefore(dayjs(selectedDate.value).add(1, 'month').format('YYYY-MM-01')) &&
138
+ !dayjs(v.date).isBefore(dayjs(selectedDate.value).format('YYYY-MM-01'))
139
+ );
140
+ })
141
+ .map(({ id: _id, ...sub }) => {
142
+ return {
143
+ ...item,
144
+ ...sub,
145
+ timeRange: timeRange,
146
+ dateRange: `${ sub.start }-${ sub.end }`,
147
+ hasObsData: obsData.find((v) => v.sub_id === sub.sub_id),
148
+ time: `${ sub.start }-${ sub.end }`,
149
+ isExpired: dayjs(`${ sub.date } ${ sub.start }`).isBefore(dayjs()),
150
+ };
151
+ });
152
+ }
153
+ return [
154
+ {
155
+ ...item,
156
+ dateRange: `${ start } - ${ end }`,
157
+ timeRange: `${ start } - ${ end }`,
158
+ hasObsData: item.obs_data?.filter((v) => v.text_video_url)?.length > 0,
159
+ time: `${ start }-${ end }`,
160
+ isExpired: dayjs(`${ date } ${ start }`).isBefore(dayjs()),
161
+ },
162
+ ];
163
+ })
164
+ .flat()
165
+ .filter((v) => v.date.slice(0, 7) === formatDate(selectedDate.value, 'YYYY-MM-DD').slice(0, 7));
166
+ // 如果需要清空,则完全替换
167
+ // 如果页码为1,表示第一次加载
168
+ // 如果是移动端,则一直往里填充数据
169
+ if (reloadAll.value) {
170
+ originList.value = res.data || [];
171
+ list.value = tempList;
172
+ } else {
173
+ if (currentPage.value === 1 && !isPhone.value) {
174
+ originList.value = res.data || [];
175
+ list.value = tempList;
176
+ } else {
177
+ originList.value = [...originList.value, ...(res.data || [])];
178
+ list.value = [...list.value, ...tempList];
179
+ }
180
+ }
181
+ list.value.sort((a, b) => {
182
+ if (a.date === b.date) {
183
+ return getDateNumber(a.start) > getDateNumber(b.start) ? 1 : -1;
184
+ } else {
185
+ return dayjs(a.date).isAfter(dayjs(b.date)) ? 1 : -1;
186
+ }
187
+ });
188
+ total.value = res?.total || 0;
189
+ nextTick(() => {
190
+ getSelectedDate();
191
+ // 监听滚动事件以自动加载下一页
192
+ if (scrollerRef.value && !isPhone.value) {
193
+ updateScroller();
194
+ }
195
+ });
196
+ } finally {
197
+ tableLoading.value = false;
198
+ nextLoading.value = false;
199
+ bottomReached.value = false;
200
+ reloadAll.value = false;
201
+ }
202
+ };
203
+
204
+ const load = useDebounceFn(() => {
205
+ if (!canLoadMore.value) return;
206
+ if (isPhone.value) return;
207
+ bottomReached.value = true;
208
+ currentPage.value++;
209
+ getList();
210
+ }, 200);
211
+ const handleScroll = useDebounceFn(() => {
212
+ if (!canLoadMore.value) return;
213
+ if (!isPhone.value) return;
214
+ const scrollPosition = window.scrollY || window.pageYOffset;
215
+ // 检测是否接近底部
216
+ const windowHeight = window.innerHeight;
217
+ const docHeight = document.documentElement.scrollHeight;
218
+ const distanceToBottom = docHeight - (scrollPosition + windowHeight);
219
+ // 当距离底部300px时开始加载
220
+ if (distanceToBottom <= 300 && !nextLoading.value) {
221
+ bottomReached.value = true;
222
+ // 加载下一页
223
+ currentPage.value++;
224
+ getList();
225
+ }
226
+ }, 200);
227
+ const calcIfAllDeleted = (date) => {
228
+ const meetingsOfDate = list.value.filter((v) => v.date === date);
229
+ return meetingsOfDate.length && meetingsOfDate.every((v) => v.is_delete);
230
+ };
231
+
232
+ const scrollerScroll = (el) => {
233
+ const container = el.target;
234
+ if (!container) return;
235
+ const scrollTop = container.scrollTop; // 已经滚动的距离
236
+ const scrollHeight = container.scrollHeight; // 内容总高度
237
+ const clientHeight = container.clientHeight; // 容器可视高度
238
+ if (scrollTop + clientHeight >= scrollHeight) {
239
+ load();
240
+ }
241
+ };
242
+ const updateScroller = () => {
243
+ const scrollerContainerEl = scrollerRef.value.getContainerEl();
244
+ scrollerContainerEl.addEventListener('scroll', scrollerScroll);
245
+ };
246
+
247
+ const detailRefs = ref({}); // 会议详情组件实例
248
+ const getDetailRefs = (insRef, id) => {
249
+ if (insRef && id) {
250
+ detailRefs.value[id] = insRef;
251
+ }
252
+ };
253
+ const copyInfo = async (idx) => {
254
+ const instance = detailRefs.value[idx];
255
+ await instance.copyInfo();
256
+ message.success({
257
+ content: t('common.copySuccess'),
258
+ });
259
+ };
260
+ // -------------------- 取消 --------------------
261
+ const cancelVisible = ref(false); // 取消弹窗
262
+ const currentRow = ref<MeetingItemT | null>(null); // 当前激活行,用于取消事件
263
+ // 打开编辑会议弹窗
264
+ const editMeeting = (row: MeetingItemT) => {
265
+ emits('edit', row, 'whole');
266
+ };
267
+ // 打开取消会议弹窗
268
+ const cancelMeeting = (row: MeetingItemT) => {
269
+ currentRow.value = row;
270
+ cancelVisible.value = true;
271
+ };
272
+ // 确定取消会议
273
+ const confirmCancel = async () => {
274
+ if (!props.deleteMeetingRequest) {
275
+ return;
276
+ }
277
+ try {
278
+ dialogLoading.value = true;
279
+ await props.deleteMeetingRequest(currentRow.value.id);
280
+ cancelVisible.value = false;
281
+ message.success({
282
+ content: `${ t('meeting.meetingCancel', [currentRow.value.topic]) }`,
283
+ });
284
+ reloadAll.value = true;
285
+ getList();
286
+ } finally {
287
+ dialogLoading.value = false;
288
+ }
289
+ };
290
+ const handleDialogVisible = ref(false);
291
+ const handleDialogType = ref('');
292
+ const handleDialogRow = ref(null);
293
+ const handleOptions = [
294
+ {
295
+ label: t('meeting.meetingSingle'),
296
+ value: 'single',
297
+ },
298
+ {
299
+ label: t('meeting.meetingCycle'),
300
+ value: 'whole',
301
+ },
302
+ ];
303
+
304
+ const handleType = ref('single');
305
+ const handleItem = (row: MeetingItemT, type: 'edit' | 'cancel') => {
306
+ if (row.is_cycle) {
307
+ handleDialogRow.value = row;
308
+ handleDialogType.value = type;
309
+ handleDialogVisible.value = true;
310
+ } else {
311
+ if (type === 'cancel') {
312
+ cancelMeeting(row);
313
+ } else {
314
+ editMeeting(row);
315
+ }
316
+ }
317
+ };
318
+ const cancelHandleItem = () => {
319
+ handleDialogVisible.value = false;
320
+ handleDialogRow.value = null;
321
+ handleDialogType.value = '';
322
+ handleType.value = 'single';
323
+ };
324
+ const confirmHandleItem = async () => {
325
+ const row = handleDialogRow.value;
326
+ if (handleDialogType.value === 'cancel') {
327
+ try {
328
+ dialogLoading.value = true;
329
+ if (handleType.value === 'single' && row.is_cycle) {
330
+ if (!props.cancelSubMeetingRequest) {
331
+ return;
332
+ }
333
+ await props.cancelSubMeetingRequest(row.sub_id);
334
+ message.success({
335
+ content: `${ t('meeting.meetingCancel', [row.topic]) }`,
336
+ });
337
+ } else {
338
+ if (!props.deleteMeetingRequest) {
339
+ return;
340
+ }
341
+ await props.deleteMeetingRequest(row.id);
342
+ message.success({
343
+ content: `${ t('meeting.meetingCancel', [row.topic]) }`,
344
+ });
345
+ }
346
+ cancelHandleItem();
347
+ reloadAll.value = true;
348
+ getList();
349
+ } finally {
350
+ dialogLoading.value = false;
351
+ }
352
+ } else {
353
+ emits('edit', row, handleType.value);
354
+ cancelHandleItem();
355
+ }
356
+ };
357
+
358
+ // -------------------- 日历 --------------------
359
+ const calendarRef = ref();
360
+ const allDateList = computed<string[]>(() => [...new Set(list.value.map((v) => v.date))].sort((a, b) => (dayjs(a).isBefore(dayjs(b)) ? -1 : 1)));
361
+ const dateList = computed<string[]>(() =>
362
+ [...new Set(list.value.filter((v) => !v.isExpired && !v.is_delete).map((v) => v.date))].sort((a, b) => (dayjs(a).isBefore(dayjs(b)) ? -1 : 1)),
363
+ );
364
+ const selectedDate = ref();
365
+
366
+ const getSelectedDate = () => {
367
+ const latest = dateList.value.find((v) => !dayjs(v).isBefore(dayjs(new Date()).format('YYYY-MM-DD')));
368
+ if (latest) {
369
+ selectedDate.value = latest;
370
+ } else if (!selectedDate.value) {
371
+ selectedDate.value = dayjs().format('YYYY-MM-DD');
372
+ }
373
+ calendarRef.value?.pickDay(dayjs(selectedDate.value));
374
+ selectedDate.value = dayjs(selectedDate.value).format('YYYY-MM-DD');
375
+ // 根据天再计算出需要展开的最近的会议
376
+ const needExpand = list.value.find((v) => v.date === selectedDate.value && !v.isExpired && !v.is_delete);
377
+ if (needExpand) {
378
+ expanded.value = [needExpand.sub_id || needExpand.id];
379
+ }
380
+ };
381
+
382
+ const cellClick = (e: PointerEvent & any, clickable: boolean) => {
383
+ if (!clickable || !e.target?.className.includes('date-cell-text')) {
384
+ e.stopPropagation();
385
+ e.preventDefault();
386
+ }
387
+ };
388
+
389
+ const changeMonth = (val) => {
390
+ if (!calendarRef.value) return;
391
+ currentPage.value = 1;
392
+ total.value = null;
393
+ window.scrollTo({
394
+ top: 0,
395
+ behavior: 'smooth',
396
+ });
397
+ calendarRef.value.selectDate(val);
398
+ reloadAll.value = true;
399
+ nextTick(() => {
400
+ selectedDate.value = dayjs(calendarRef.value.selectedDay).format('YYYY-MM-DD');
401
+ getList();
402
+ });
403
+ };
404
+ // -------------------- 列表 --------------------
405
+ const groupList = computed(() => {
406
+ return list.value.reduce((prev, cur) => {
407
+ if (!prev.length) {
408
+ return [
409
+ {
410
+ date: cur.date,
411
+ list: [cur],
412
+ },
413
+ ];
414
+ } else {
415
+ const last = prev.at(-1);
416
+ if (last.date === cur.date) {
417
+ last.list.push(cur);
418
+ } else {
419
+ prev.push({
420
+ date: cur.date,
421
+ list: [cur],
422
+ });
423
+ }
424
+ return prev;
425
+ }
426
+ }, []);
427
+ });
428
+
429
+ const getWeekFromDate = (date) => {
430
+ return WEEKDAY[dayjs(date).day()];
431
+ };
432
+
433
+ // -------------------- 处理滚动事件 --------------------
434
+ const scrollerRef = ref();
435
+ watch(
436
+ () => selectedDate.value,
437
+ () => {
438
+ scrollToSelectedDate(selectedDate.value);
439
+ },
440
+ );
441
+ const scrollToSelectedDate = (date: string) => {
442
+ const key = dayjs(date).format('YYYY-MM-DD');
443
+ const targetEle = document.querySelector(`#group-title-${ key }`);
444
+ if (targetEle) {
445
+ if (isPhone.value) {
446
+ window.scrollTo({
447
+ top: (targetEle.parentElement?.offsetTop || 0) - 52,
448
+ behavior: 'smooth',
449
+ });
450
+ } else {
451
+ scrollerRef.value?.scrollTo({
452
+ top: targetEle.parentElement?.offsetTop || 0,
453
+ behavior: 'smooth',
454
+ });
455
+ }
456
+ }
457
+ };
458
+ onMounted(() => {
459
+ getList();
460
+ // 添加滚动事件监听
461
+ window.addEventListener('scroll', handleScroll);
462
+ // 添加resize监听器
463
+ window.addEventListener('resize', handleScroll);
464
+ getHeaderHeight();
465
+ window.addEventListener('resize', getHeaderHeight);
466
+ });
467
+
468
+ onUnmounted(() => {
469
+ window.removeEventListener('scroll', handleScroll);
470
+ window.removeEventListener('resize', handleScroll);
471
+ window.removeEventListener('resize', getHeaderHeight);
472
+ const scrollerContainerEl = scrollerRef.value?.getContainerEl();
473
+ scrollerContainerEl?.removeEventListener('scroll', scrollerScroll);
474
+ });
475
+
476
+ const formatYearMonth = (date) => {
477
+ date = dayjs(date ? date : undefined);
478
+ if (isEn.value) {
479
+ return date.format('MMMM YYYY');
480
+ } else {
481
+ return date.format('YYYY MM月');
482
+ }
483
+ };
484
+ const handleActions = computed<DialogActionT[]>(() => {
485
+ return [
486
+ {
487
+ id: 'confirm',
488
+ color: 'primary',
489
+ label: t('common.confirm'),
490
+ variant: 'solid',
491
+ size: 'large',
492
+ round: 'pill',
493
+ loading: dialogLoading.value,
494
+ onClick: () => {
495
+ confirmHandleItem();
496
+ },
497
+ },
498
+ {
499
+ id: 'cancel',
500
+ color: 'primary',
501
+ label: t('common.cancel'),
502
+ variant: 'outline',
503
+ size: 'large',
504
+ round: 'pill',
505
+ onClick: () => {
506
+ cancelHandleItem();
507
+ },
508
+ },
509
+ ];
510
+ });
511
+ const cancelActions = computed<DialogActionT[]>(() => {
512
+ return [
513
+ {
514
+ id: 'confirm',
515
+ color: 'primary',
516
+ label: t('common.confirm'),
517
+ variant: 'solid',
518
+ size: 'large',
519
+ round: 'pill',
520
+ loading: dialogLoading.value,
521
+ onClick: () => {
522
+ confirmCancel();
523
+ },
524
+ },
525
+ {
526
+ id: 'cancel',
527
+ color: 'primary',
528
+ label: t('common.cancel'),
529
+ variant: 'outline',
530
+ size: 'large',
531
+ round: 'pill',
532
+ onClick: () => {
533
+ cancelVisible.value = false;
534
+ },
535
+ },
536
+ ];
537
+ });
538
+ </script>
539
+
540
+ <template>
541
+ <div class="o-my-meeting-calendar">
542
+ <div class="meeting-list">
543
+ <div class="list-calendar-mb">
544
+ <span>{{ formatYearMonth(selectedDate) }}</span>
545
+ <span>
546
+ <OIcon @click="changeMonth('prev-month')"><OIconChevronLeft /></OIcon>
547
+ <OIcon @click="changeMonth('next-month')"><OIconChevronRight /></OIcon>
548
+ </span>
549
+ </div>
550
+ <div class="list-calendar">
551
+ <ElCalendar ref="calendarRef" v-model="selectedDate">
552
+ <template #header>
553
+ <span>{{ formatYearMonth(selectedDate) }}</span>
554
+ <div>
555
+ <OIcon @click="changeMonth('prev-month')">
556
+ <OIconChevronLeft />
557
+ </OIcon>
558
+ <OIcon @click="changeMonth('next-month')">
559
+ <OIconChevronRight />
560
+ </OIcon>
561
+ </div>
562
+ </template>
563
+ <template #date-cell="{ data }">
564
+ <div
565
+ @click="(e) => cellClick(e, allDateList.includes(data.day))"
566
+ :class="{
567
+ 'date-cell': true,
568
+ 'is-selected': data.isSelected,
569
+ 'is-today': formatDate(data.day) === formatDate(),
570
+ clickable: allDateList.includes(data.day),
571
+ expired: dayjs(formatDate()).isAfter(dayjs(data.day)),
572
+ 'all-deleted': calcIfAllDeleted(data.day),
573
+ }"
574
+ >
575
+ <div class="date-cell-text">
576
+ {{ Number(data.day.split('-')[2]) }}
577
+ </div>
578
+ </div>
579
+ </template>
580
+ </ElCalendar>
581
+ </div>
582
+ <div class="list-wrapper" :class="!list.length ? 'is-empty' : ''">
583
+ <OScroller
584
+ ref="scrollerRef"
585
+ @scrollend="load"
586
+ v-if="list.length"
587
+ :style="{ '--header-height': headerHeight }"
588
+ class="scroller-container"
589
+ show-type="hover"
590
+ >
591
+ <div class="list-body">
592
+ <OCollapse v-model="expanded" :accordion="isPhone">
593
+ <template v-for="(group, idx) in groupList" :key="group.date">
594
+ <div class="list-month-change prev-month" v-if="idx === 0" @click="changeMonth('prev-month')">
595
+ <OIcon>
596
+ <OIconArrowLeft />
597
+ </OIcon>
598
+ <span>{{ t('meeting.preMonth') }}</span>
599
+ </div>
600
+ <div class="group-item" :class="idx === groupList.length - 1 && 'last-item'">
601
+ <div
602
+ :class="{
603
+ 'group-bar': true,
604
+ 'is-active': dayjs(selectedDate).format('YYYY-MM-DD') === group.date,
605
+ 'is-end': group.list.every((row) => row.isExpired),
606
+ }"
607
+ >
608
+ <div class="group-bar-line"></div>
609
+ <div class="group-bar-dot"></div>
610
+ </div>
611
+ <div
612
+ :class="{
613
+ 'group-title': true,
614
+ 'is-end': group.list.every((row) => row.isExpired),
615
+ }"
616
+ :id="`group-title-${dayjs(new Date(group.date)).format('YYYY-MM-DD')}`"
617
+ >
618
+ {{ dayjs(group.date).format('MM/DD') }} {{ getWeekFromDate(group.date) }}
619
+ </div>
620
+ <OCollapseItem
621
+ v-for="(row, rowIdx) in group.list"
622
+ :key="row.sub_id || row.id"
623
+ :value="row.sub_id || row.id"
624
+ :class="{
625
+ 'last-item': idx === groupList.length - 1 && rowIdx === group.list.length - 1,
626
+ }"
627
+ >
628
+ <template #title>
629
+ <div class="item-header-left">
630
+ <div class="meeting-icon">
631
+ <OIcon>
632
+ <IconMeeting />
633
+ </OIcon>
634
+ </div>
635
+ <div class="header-info">
636
+ <div
637
+ :class="{
638
+ 'meeting-title': true,
639
+ 'is-delete': row.is_delete,
640
+ 'is-end': row.isExpired,
641
+ }"
642
+ >
643
+ <div v-if="row.is_delete">{{ t('meeting.meetingCancelled') }}</div>
644
+ <div class="title-wrapper">
645
+ <div class="title-text">{{ row.topic }}</div>
646
+ </div>
647
+ <div class="tag-wrapper" v-if="row.is_cycle">
648
+ <OTag color="primary" variant="outline">{{ t('meeting.cycle') }}</OTag>
649
+ </div>
650
+ </div>
651
+ <div class="meeting-info">
652
+ <span>{{ row.dateRange }}</span>
653
+ <ODivider direction="v" />
654
+ <span>
655
+ {{ groupType === MeetingGroupType.GROUP ? t('meeting.groups') : t('meeting.sigs')
656
+ }}: {{ row.group_name }}
657
+ </span>
658
+ </div>
659
+ </div>
660
+ </div>
661
+ <div class="item-header-right" v-if="!row.is_delete">
662
+ <OLink
663
+ v-if="!row.isExpired"
664
+ target="_blank"
665
+ :href="row.join_url"
666
+ rel="noopener noreferrer"
667
+ >
668
+ {{ t('meeting.joinMeeting') }}
669
+ <template #suffix>
670
+ <OIcon>
671
+ <OIconChevronRight></OIconChevronRight>
672
+ </OIcon>
673
+ </template>
674
+ </OLink>
675
+ <template v-else>
676
+ <OLink target="_blank" v-if="row.etherpad" :href="row.etherpad">
677
+ {{ t('meeting.meetingSummary') }}
678
+ <template #suffix>
679
+ <OIcon>
680
+ <OIconChevronRight></OIconChevronRight>
681
+ </OIcon>
682
+ </template>
683
+ </OLink>
684
+ <OLink
685
+ target="_blank"
686
+ :href="`/${locale}/video/${row.group_name}/${row.mid}/${row.date}`"
687
+ v-if="row.hasObsData">
688
+ {{ t('meeting.viewRecord') }}
689
+ <template #suffix>
690
+ <OIcon>
691
+ <OIconChevronRight></OIconChevronRight>
692
+ </OIcon>
693
+ </template>
694
+ </OLink>
695
+ </template>
696
+ </div>
697
+ <OIcon @click.stop="() => copyInfo(rowIdx)" class="copy-icon">
698
+ <IconCopy />
699
+ </OIcon>
700
+ </template>
701
+ <div class="meeting-detail">
702
+ <OMeetingDetail
703
+ :show="expanded.includes(row.sub_id || row.id)"
704
+ :data="row"
705
+ :ref="(insRef) => getDetailRefs(insRef, row.id)"
706
+ from="my"
707
+ />
708
+ <div class="meeting-btn" v-if="!row.isExpired && !row.is_delete">
709
+ <OLink @click="handleItem(row, 'edit')">{{ t('meeting.editMeeting') }}
710
+ </OLink>
711
+ <OLink @click="handleItem(row, 'cancel')">{{ t('meeting.cancelMeeting') }}
712
+ </OLink>
713
+ </div>
714
+ </div>
715
+ </OCollapseItem>
716
+ <div class="height-placeholder"></div>
717
+ </div>
718
+ <template v-if="idx === groupList.length - 1">
719
+ <div class="load-text" v-if="bottomReached">{{ t('common.loading') }}</div>
720
+ <div class="list-month-change next-month" @click="changeMonth('next-month')">
721
+ <OIcon>
722
+ <OIconArrowRight />
723
+ </OIcon>
724
+ <span>{{ t('meeting.nextMonth') }}</span>
725
+ </div>
726
+ </template>
727
+ </template>
728
+ </OCollapse>
729
+ </div>
730
+ </OScroller>
731
+ <slot name="empty" v-else-if="!tableLoading"></slot>
732
+ </div>
733
+ <ODialog
734
+ v-model:visible="handleDialogVisible"
735
+ main-class="handle-dialog"
736
+ @close="cancelHandleItem"
737
+ :actions="handleActions"
738
+ >
739
+ <template #header>
740
+ {{
741
+ t('meeting.meetingHandleText', [handleDialogType === 'edit' ? t('meeting.edit') : t('meeting.cancel2')])
742
+ }}
743
+ </template>
744
+ <ORadioGroup v-model="handleType">
745
+ <ORadio v-for="t in handleOptions" :value="t.value" :key="t.value">{{ t.label }}</ORadio>
746
+ </ORadioGroup>
747
+ </ODialog>
748
+ <ODialog v-model:visible="cancelVisible" main-class="cancel-dialog" :actions="cancelActions">
749
+ <template #header>{{ t('meeting.confirmCancel') }}</template>
750
+ <div class="dialog-content">{{ t('meeting.confirmCancelDesc', [currentRow?.topic]) }}</div>
751
+ </ODialog>
752
+ </div>
753
+ </div>
754
+ </template>
755
+
756
+ <style lang="scss">
757
+ .o-my-meeting-calendar {
758
+
759
+ .header {
760
+ display: flex;
761
+ align-items: center;
762
+ justify-content: space-between;
763
+
764
+ .title {
765
+ font-weight: 500;
766
+ color: var(--o-color-info1);
767
+ @include h2;
768
+ }
769
+
770
+ .desc {
771
+ margin-top: 12px;
772
+ color: var(--o-color-info2);
773
+ @include tip1;
774
+ }
775
+ }
776
+
777
+ .meeting-list {
778
+ display: flex;
779
+ flex-wrap: nowrap;
780
+ gap: var(--o-gap-4);
781
+ height: 100%;
782
+ --phone-padding-top: 0;
783
+ @include respond-to('pad_v') {
784
+ flex-direction: column;
785
+ gap: var(--o-gap-4);
786
+ }
787
+ @include respond-to('phone') {
788
+ flex-direction: column;
789
+ gap: var(--o-gap-3);
790
+ --phone-padding-top: calc(var(--o-gap-5) + var(--o-gap-3) + var(--o-gap-3));
791
+ }
792
+
793
+ .o-loading {
794
+ .o-layer-mask {
795
+ background-color: transparent;
796
+ }
797
+
798
+ .o-loading-icon {
799
+ color: var(--layer-mask);
800
+ }
801
+ }
802
+
803
+ .list-calendar-mb {
804
+ display: none;
805
+ align-items: center;
806
+ justify-content: space-between;
807
+ padding: 0 var(--grid--layout-padding);
808
+
809
+ span:first-child {
810
+ @include display2;
811
+ }
812
+
813
+ span:last-child {
814
+ display: flex;
815
+ align-items: center;
816
+ }
817
+
818
+ .o-icon {
819
+ font-size: 24px;
820
+ cursor: pointer;
821
+
822
+ &:last-child {
823
+ margin-left: 24px;
824
+ }
825
+ }
826
+
827
+ @include respond-to('phone') {
828
+ padding-top: var(--o-gap-7);
829
+ background-color: var(--o-color-fill1);
830
+ padding-bottom: var(--o-gap-4);
831
+ display: flex;
832
+ position: fixed;
833
+ height: var(--phone-padding-top);
834
+ z-index: 1;
835
+ top: var(--layout-header-height);
836
+ left: 0;
837
+ right: 0;
838
+ }
839
+ }
840
+
841
+ .list-calendar {
842
+ flex-shrink: 0;
843
+ width: 334px;
844
+ background-color: color-mix(in srgb, var(--o-color-control2-light), transparent 60%);
845
+
846
+ @include respond-to('pad_h') {
847
+ .el-calendar {
848
+ .el-calendar__body {
849
+ padding-left: 12px;
850
+ padding-right: 12px;
851
+
852
+ .el-calendar-table .date-cell {
853
+ height: 40px;
854
+ width: 28px;
855
+
856
+ .date-cell-text {
857
+ line-height: 24px;
858
+ @include tip2;
859
+ }
860
+ }
861
+ }
862
+ }
863
+ }
864
+ @include respond-to('pad_v') {
865
+ width: 100%;
866
+ }
867
+ @include respond-to('phone') {
868
+ display: none;
869
+ }
870
+ .el-calendar {
871
+ min-height: 460px;
872
+ height: calc(100% - 54px);
873
+ border-radius: var(--o-radius-xs);
874
+ background-color: transparent;
875
+
876
+ .el-calendar__header {
877
+ border-bottom: 1px solid var(--o-color-control4);
878
+
879
+ & > span {
880
+ font-weight: 500;
881
+ color: var(--o-color-info1);
882
+ @include h1;
883
+ }
884
+
885
+ & > div {
886
+ display: flex;
887
+ align-items: center;
888
+ gap: var(--o-gap-5);
889
+
890
+ .o-icon {
891
+ font-size: 24px;
892
+ cursor: pointer;
893
+
894
+ &:hover {
895
+ color: var(--o-color-primary1);
896
+ }
897
+ }
898
+ }
899
+ }
900
+
901
+ .el-calendar__body {
902
+ .el-calendar-table {
903
+
904
+ th {
905
+ text-align: center;
906
+ border: none;
907
+ --o-color-control3-light: transparent;
908
+ }
909
+
910
+ tr {
911
+ --o-color-fill2: transparent;
912
+ }
913
+
914
+ td {
915
+ background-color: transparent !important;
916
+ border: none;
917
+ text-align: center;
918
+ transition: none;
919
+ padding: 0;
920
+
921
+ .el-calendar-day {
922
+ padding: 0;
923
+ height: fit-content;
924
+ }
925
+
926
+ div {
927
+ cursor: default !important;
928
+ }
929
+
930
+ &:hover {
931
+ .el-calendar-day {
932
+ background-color: transparent;
933
+ }
934
+ }
935
+ }
936
+
937
+ .date-cell {
938
+ height: 56px;
939
+ width: 42px;
940
+ padding: var(--o-gap-1);
941
+ position: relative;
942
+ cursor: default !important;
943
+
944
+ .date-cell-text {
945
+ font-size: 14px;
946
+ line-height: 36px;
947
+ border-radius: var(--o-radius-xs);
948
+ background-color: var(--o-color-control2-light);
949
+ }
950
+
951
+ .date-cell-text {
952
+ cursor: not-allowed !important;
953
+ }
954
+
955
+ &.clickable {
956
+ .date-cell-text {
957
+ cursor: pointer !important;
958
+ }
959
+
960
+ &:not(.is-selected) {
961
+ .date-cell-text:hover {
962
+ background-color: var(--o-color-control3-light);
963
+ }
964
+ }
965
+ }
966
+
967
+ &::after {
968
+ content: '';
969
+ position: absolute;
970
+ bottom: 2px;
971
+ left: 50%;
972
+ transform: translateX(-50%);
973
+ width: 8px;
974
+ height: 8px;
975
+ border-radius: 50%;
976
+ }
977
+
978
+ &.is-today {
979
+ .date-cell-text {
980
+ color: #000;
981
+ background-color: var(--o-color-control3-light);
982
+ }
983
+ }
984
+
985
+ &.is-selected {
986
+ .date-cell-text {
987
+ color: #fff;
988
+ background-color: var(--o-color-primary1);
989
+ }
990
+ }
991
+
992
+ &.clickable {
993
+ &::after {
994
+ background-color: var(--o-color-primary1);
995
+ }
996
+
997
+ &.expired::after {
998
+ background-color: rgb(var(--o-mixedgray-6));
999
+ }
1000
+
1001
+ &.all-deleted::after {
1002
+ background-color: rgb(var(--o-mixedgray-6));
1003
+ }
1004
+ }
1005
+ }
1006
+
1007
+ .is-today {
1008
+ color: inherit;
1009
+ }
1010
+ }
1011
+ }
1012
+ }
1013
+
1014
+ }
1015
+
1016
+ .list-wrapper {
1017
+ flex-grow: 1;
1018
+ background-color: var(--o-color-fill2);
1019
+
1020
+ @include respond-to('phone') {
1021
+ margin-top: calc(var(--phone-padding-top) - var(--o-gap-4));
1022
+ }
1023
+ &.is-empty {
1024
+ display: flex;
1025
+ align-items: center;
1026
+ justify-content: center;
1027
+ }
1028
+
1029
+
1030
+ .o-scroller {
1031
+ .o-scrollbar-rail {
1032
+ right: -16px;
1033
+ height: 100%;
1034
+ }
1035
+ }
1036
+
1037
+ .scroller-container {
1038
+ height: 100%;
1039
+ max-height: calc(var(--layout-left-height, 900px) - 4 * var(--o-gap-5) - var(--header-height) * 1px);
1040
+
1041
+ @include respond-to('phone') {
1042
+ max-height: fit-content;
1043
+ }
1044
+
1045
+ .group-item {
1046
+ padding-left: 24px;
1047
+ position: relative;
1048
+
1049
+ &.last-item {
1050
+ flex-grow: 1;
1051
+ }
1052
+
1053
+ @include respond-to('phone') {
1054
+ padding-left: var(--o-gap-2);
1055
+ }
1056
+
1057
+ .group-bar {
1058
+ position: absolute;
1059
+ left: 0;
1060
+ width: 16px;
1061
+ top: 0;
1062
+ bottom: 0;
1063
+ overflow: hidden;
1064
+
1065
+ --active-color: var(--o-color-primary1);
1066
+
1067
+ &::before {
1068
+ content: '';
1069
+ width: 2px;
1070
+ position: absolute;
1071
+ top: 0;
1072
+ bottom: 0;
1073
+ left: 50%;
1074
+ transform: translateX(-50%);
1075
+ background-color: var(--o-color-control4);
1076
+ }
1077
+
1078
+ .group-bar-dot {
1079
+ width: 16px;
1080
+ height: 26px;
1081
+ position: relative;
1082
+ @include respond-to('laptop') {
1083
+ height: 24px;
1084
+ }
1085
+ @include respond-to('pad_h') {
1086
+ height: 22px;
1087
+ }
1088
+ @include respond-to('<=pad_v') {
1089
+ height: 22px;
1090
+ }
1091
+
1092
+ &::before,
1093
+ &::after {
1094
+ content: '';
1095
+ border-radius: 50%;
1096
+ position: absolute;
1097
+ top: 50%;
1098
+ left: 50%;
1099
+ transform: translateY(-50%) translateX(-50%);
1100
+ }
1101
+
1102
+ &::before {
1103
+ width: 16px;
1104
+ height: 16px;
1105
+ background-color: transparent;
1106
+ }
1107
+
1108
+ &::after {
1109
+ width: 8px;
1110
+ height: 8px;
1111
+ background-color: var(--active-color);
1112
+ }
1113
+ }
1114
+
1115
+ &.is-active {
1116
+ .group-bar-dot {
1117
+ &::before {
1118
+ background-color: var(--active-color);
1119
+ }
1120
+
1121
+ &::after {
1122
+ background-color: var(--o-color-fill2);
1123
+ }
1124
+ }
1125
+ }
1126
+
1127
+ &.is-end {
1128
+ --active-color: rgb(222, 222, 227);
1129
+ }
1130
+ }
1131
+
1132
+ .group-title {
1133
+ font-weight: 500;
1134
+ margin-bottom: var(--o-gap-2);
1135
+ color: var(--o-color-info1);
1136
+ @include text2;
1137
+ @include respond-to('phone') {
1138
+ padding-left: var(--o-gap-5);
1139
+ }
1140
+
1141
+ &.is-end {
1142
+ color: var(--o-color-info3);
1143
+ }
1144
+ }
1145
+ }
1146
+ }
1147
+ }
1148
+
1149
+ .list-body {
1150
+ height: 100%;
1151
+
1152
+ @include respond-to('phone') {
1153
+ height: fit-content;
1154
+ padding: var(--o-gap-4) !important;
1155
+ }
1156
+
1157
+ .list-month-change {
1158
+ flex-shrink: 0;
1159
+ display: flex;
1160
+ align-items: center;
1161
+ gap: var(--o-gap-5);
1162
+ cursor: pointer;
1163
+ --btn-color: var(--o-color-primary1);
1164
+ @include hover {
1165
+ --btn-color: var(--o-color-primary2);
1166
+ }
1167
+
1168
+ @include respond-to('phone') {
1169
+ display: none;
1170
+ }
1171
+
1172
+ &.prev-month {
1173
+ margin-bottom: var(--o-gap-6);
1174
+ }
1175
+
1176
+ &.next-month {
1177
+ margin-top: var(--o-gap-6);
1178
+ padding-bottom: 32px;
1179
+ }
1180
+
1181
+ .o-icon {
1182
+ font-size: 24px;
1183
+ color: var(--btn-color);
1184
+ }
1185
+
1186
+ span {
1187
+ color: var(--btn-color);
1188
+ @include text1;
1189
+ }
1190
+ }
1191
+
1192
+ .o-collapse {
1193
+ padding: 0;
1194
+ border-radius: 0;
1195
+ height: 100%;
1196
+ display: flex;
1197
+ flex-direction: column;
1198
+
1199
+ .o-collapse-item {
1200
+ &.last-item {
1201
+ .o-collapse-item-header {
1202
+ border-bottom: none;
1203
+ }
1204
+ }
1205
+ }
1206
+
1207
+ .o-collapse-item-expanded + .o-collapse-item-expanded {
1208
+ margin-top: var(--o-gap-4);
1209
+ }
1210
+
1211
+ .height-placeholder {
1212
+ height: 0;
1213
+ transition: margin var(--o-easing-standard) var(--o-duration-s);
1214
+ }
1215
+
1216
+ .o-collapse-item-expanded + .height-placeholder {
1217
+ height: var(--o-gap-4);
1218
+ }
1219
+
1220
+ .o-collapse-item {
1221
+ padding: var(--o-gap-4) var(--o-gap-5);
1222
+ border-top: none;
1223
+ border-radius: var(--o-radius-xs);
1224
+ transition: margin var(--o-easing-standard) var(--o-duration-s);
1225
+ --copy-display: none;
1226
+ --icon-size: 24px;
1227
+ @include respond-to('<=pad_v') {
1228
+ padding: var(--o-gap-3) var(--o-gap-4);
1229
+ }
1230
+
1231
+ &:hover {
1232
+ @include respond-to('>pad_v') {
1233
+ --copy-display: inline-flex;
1234
+ }
1235
+ }
1236
+
1237
+ @include respond-to('phone') {
1238
+ --icon-size: 20px;
1239
+ }
1240
+ &.o-collapse-item-expanded {
1241
+ @include respond-to('<=pad_v') {
1242
+ --copy-display: inline-flex;
1243
+ }
1244
+ }
1245
+
1246
+ &.o-collapse-item-expanded {
1247
+ background-color: color-mix(in srgb, var(--o-color-control2-light), transparent 60%) !important;
1248
+ }
1249
+ }
1250
+
1251
+ .o-collapse-item-header {
1252
+ border-bottom: 1px solid var(--o-color-control4);
1253
+ padding-top: 0;
1254
+ padding-bottom: var(--o-gap-2);
1255
+ display: flex;
1256
+ align-items: center;
1257
+ gap: var(--o-gap-4);
1258
+ position: relative;
1259
+
1260
+ .o-collapse-item-icon {
1261
+ position: relative;
1262
+ top: 4px;
1263
+ flex-shrink: 0;
1264
+ @include respond-to('phone') {
1265
+ position: absolute;
1266
+ right: 0;
1267
+ width: 20px;
1268
+ height: 20px;
1269
+ font-size: 20px;
1270
+ top: 50%;
1271
+ transform: translateY(-50%);
1272
+ }
1273
+ }
1274
+
1275
+ .o-collapse-item-title {
1276
+ flex: 1;
1277
+ width: 0;
1278
+ display: flex;
1279
+ flex-direction: column;
1280
+ align-items: flex-start;
1281
+ justify-content: space-between;
1282
+ margin-bottom: 0;
1283
+ .item-header-left {
1284
+ display: flex;
1285
+ align-items: flex-start;
1286
+ gap: var(--o-gap-3);
1287
+ width: 100%;
1288
+ margin-bottom: var(--o-gap-2);
1289
+ @include respond-to('phone') {
1290
+ flex-grow: 1;
1291
+ width: 100%;
1292
+ align-self: stretch;
1293
+ flex-shrink: 0;
1294
+ }
1295
+ .meeting-icon {
1296
+ width: var(--icon-size);
1297
+ height: var(--icon-size);
1298
+ border-radius: 50%;
1299
+ background-color: var(--o-color-primary1);
1300
+ color: #fff;
1301
+ display: flex;
1302
+ align-items: center;
1303
+ justify-content: center;
1304
+ flex-shrink: 0;
1305
+
1306
+ .o-icon {
1307
+ svg path {
1308
+ fill: currentColor;
1309
+ }
1310
+ }
1311
+
1312
+ }
1313
+
1314
+ .header-info {
1315
+ width: calc(100% - var(--o-gap-3) - var(--icon-size));
1316
+
1317
+ .meeting-title {
1318
+ font-weight: 500;
1319
+ display: flex;
1320
+ align-items: center;
1321
+ margin-bottom: var(--o-gap-2);
1322
+ @include text2;
1323
+
1324
+ &.is-delete,
1325
+ &.is-end {
1326
+ color: var(--o-color-info3);
1327
+ }
1328
+
1329
+ .tag-wrapper {
1330
+ flex: 10;
1331
+ margin-left: var(--o-gap-2);
1332
+
1333
+ .o-tag {
1334
+ background-color: var(--o-color-control2-light);
1335
+ border: none;
1336
+ }
1337
+ }
1338
+
1339
+ .title-wrapper {
1340
+ flex: 0 1 auto;
1341
+ min-width: 0;
1342
+ max-width: 100%;
1343
+ }
1344
+
1345
+ .title-text {
1346
+ @include text-truncate(1);
1347
+ }
1348
+ }
1349
+
1350
+ .meeting-info {
1351
+ color: var(--o-color-info3);
1352
+ display: flex;
1353
+ align-items: center;
1354
+ font-weight: 400;
1355
+ @include tip1;
1356
+
1357
+ span:last-child {
1358
+ width: 0;
1359
+ flex: 1;
1360
+ @include text-truncate(1);
1361
+ }
1362
+ }
1363
+ }
1364
+ }
1365
+
1366
+ .item-header-right {
1367
+ display: flex;
1368
+ align-items: center;
1369
+ padding-left: calc(var(--o-gap-3) + var(--icon-size));
1370
+ gap: var(--o-gap-4);
1371
+ width: 100%;
1372
+
1373
+ .o-link {
1374
+ font-size: 14px;
1375
+ line-height: 21px;
1376
+ }
1377
+ .o-icon {
1378
+ font-size: 16px;
1379
+ }
1380
+ }
1381
+
1382
+ .copy-icon {
1383
+ position: absolute;
1384
+ top: 50%;
1385
+ transform: translateY(-50%);
1386
+ right: calc(var(--collapse-item-icon-size) + var(--o-gap-4));
1387
+ font-size: 18px;
1388
+ height: 18px;
1389
+ width: 18px;
1390
+ display: var(--copy-display);
1391
+ @include respond-to('phone') {
1392
+ bottom: var(--o-gap-2);
1393
+ right: calc(20px + var(--o-gap-2))
1394
+ }
1395
+
1396
+ &:hover {
1397
+ color: var(--o-color-primary1);
1398
+ }
1399
+
1400
+
1401
+ svg path {
1402
+ fill: currentColor;
1403
+ }
1404
+ }
1405
+ }
1406
+ }
1407
+
1408
+ .o-collapse-item-body {
1409
+ margin-bottom: 0;
1410
+ padding: var(--o-gap-4) 0 0;
1411
+
1412
+ .meeting-detail {
1413
+ padding-left: calc(var(--o-gap-3) + var(--icon-size));
1414
+
1415
+ @include respond-to('phone') {
1416
+ padding-left: 0;
1417
+ }
1418
+
1419
+ .meeting-btn {
1420
+ border-top: 1px solid var(--o-color-control4);
1421
+ margin-top: var(--o-gap-5);
1422
+ padding-top: var(--o-gap-4);
1423
+ display: flex;
1424
+ align-items: center;
1425
+ justify-content: flex-end;
1426
+ gap: var(--o-gap-5);
1427
+ }
1428
+ }
1429
+ }
1430
+
1431
+ .o-btn.o-btn-text {
1432
+ padding-left: 0 !important;
1433
+ padding-right: 0 !important;
1434
+ min-width: auto;
1435
+ }
1436
+ }
1437
+
1438
+ .load-text {
1439
+ text-align: center;
1440
+ color: var(--o-color-info3);
1441
+ @include tip1;
1442
+ }
1443
+ }
1444
+ }
1445
+
1446
+ @include in-dark {
1447
+ .group-bar.is-end {
1448
+ --active-color: rgb(65, 65, 68) !important;
1449
+ }
1450
+ .meeting-list {
1451
+
1452
+ .is-today {
1453
+ .date-cell-text {
1454
+ color: #fff !important;
1455
+ }
1456
+ }
1457
+ }
1458
+ }
1459
+ }
1460
+ </style>
1461
+
1462
+ <style lang="scss">
1463
+ .handle-dialog {
1464
+ width: 450px;
1465
+
1466
+ .o-dlg-header {
1467
+ margin-bottom: var(--o-gap-5);
1468
+ }
1469
+
1470
+ .o-dlg-body-content {
1471
+ display: flex;
1472
+ justify-content: center;
1473
+ }
1474
+
1475
+ .dialog-footer {
1476
+ display: flex;
1477
+ justify-content: center;
1478
+ margin-top: var(--o-gap-4);
1479
+ column-gap: var(--o-gap-4);
1480
+ }
1481
+ }
1482
+
1483
+ .cancel-dialog {
1484
+ .dialog-content {
1485
+ width: 384px;
1486
+ text-align: center;
1487
+ }
1488
+
1489
+ .dialog-footer {
1490
+ display: flex;
1491
+ justify-content: center;
1492
+ margin-top: var(--o-gap-4);
1493
+ column-gap: var(--o-gap-4);
1494
+ }
1495
+ }
1496
+
1497
+ [data-o-theme='dark'] .meeting-list {
1498
+ .o-collapse-item-body {
1499
+ background-color: rgb(43, 43, 47) !important;
1500
+ }
1501
+ }
1502
+ </style>