@opendesign-plus/components 0.0.1-rc.2 → 0.0.1-rc.20

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