@rolatech/angular-course 17.5.4 → 17.6.1

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 (52) hide show
  1. package/esm2022/index.mjs +2 -1
  2. package/esm2022/lib/components/course-action/course-action.component.mjs +3 -3
  3. package/esm2022/lib/components/course-details/course-details.component.mjs +3 -3
  4. package/esm2022/lib/components/course-details-dialog/course-details-dialog.component.mjs +3 -3
  5. package/esm2022/lib/components/course-edit-dialog/course-edit-dialog.component.mjs +3 -3
  6. package/esm2022/lib/components/course-info/course-info.component.mjs +7 -6
  7. package/esm2022/lib/components/course-item/course-item.component.mjs +3 -3
  8. package/esm2022/lib/components/course-media/course-media.component.mjs +3 -3
  9. package/esm2022/lib/components/course-media-owner-renderer/course-media-owner-renderer.component.mjs +3 -3
  10. package/esm2022/lib/components/course-preview/course-preview.component.mjs +3 -3
  11. package/esm2022/lib/components/course-pricing/course-pricing.component.mjs +3 -3
  12. package/esm2022/lib/components/course-pricing-add-dialog/course-pricing-add-dialog.component.mjs +3 -3
  13. package/esm2022/lib/components/course-pricing-dialog/course-pricing-dialog.component.mjs +3 -3
  14. package/esm2022/lib/components/course-schedule/course-schedule.component.mjs +3 -3
  15. package/esm2022/lib/components/course-schedule-add-dialog/course-schedule-add-dialog.component.mjs +3 -3
  16. package/esm2022/lib/components/course-schedule-dialog/course-schedule-dialog.component.mjs +3 -3
  17. package/esm2022/lib/components/course-section-item/course-section-item.component.mjs +3 -3
  18. package/esm2022/lib/components/course-section-lecture-item/course-section-lecture-item.component.mjs +4 -6
  19. package/esm2022/lib/components/course-section-lecture-video-dialog/course-section-lecture-video-dialog.component.mjs +4 -5
  20. package/esm2022/lib/components/course-section-lecture-video-item/course-section-lecture-video-item.component.mjs +3 -3
  21. package/esm2022/lib/components/course-sections/course-sections.component.mjs +3 -3
  22. package/esm2022/lib/components/detail-item/detail-item.component.mjs +3 -3
  23. package/esm2022/lib/components/pricing-item/pricing-item.component.mjs +3 -3
  24. package/esm2022/lib/components/schedule-item/schedule-item.component.mjs +3 -3
  25. package/esm2022/lib/pages/course/course-category/course-category.component.mjs +3 -3
  26. package/esm2022/lib/pages/course/course-detail/course-detail.component.mjs +5 -5
  27. package/esm2022/lib/pages/course/course-index/course-index.component.mjs +3 -3
  28. package/esm2022/lib/pages/course/course-layout/course-layout.component.mjs +3 -3
  29. package/esm2022/lib/pages/course/course-section-content/course-section-content.component.mjs +5 -5
  30. package/esm2022/lib/pages/course-manage/course-manage-content/course-manage-content.component.mjs +3 -3
  31. package/esm2022/lib/pages/course-manage/course-manage-details/course-manage-details.component.mjs +3 -3
  32. package/esm2022/lib/pages/course-manage/course-manage-info/course-manage-info.component.mjs +6 -6
  33. package/esm2022/lib/pages/course-manage/course-manage-layout/course-manage-layout.component.mjs +3 -3
  34. package/esm2022/lib/pages/course-manage/course-manage-media/course-manage-media.component.mjs +3 -3
  35. package/esm2022/lib/pages/course-manage/course-manage-pricing/course-manage-pricing.component.mjs +3 -3
  36. package/esm2022/lib/pages/course-manage/course-manage-schedule/course-manage-schedule.component.mjs +3 -3
  37. package/esm2022/lib/pages/course-manage/course-manage-section/course-manage-section.component.mjs +4 -5
  38. package/esm2022/lib/services/booking.service.mjs +3 -3
  39. package/esm2022/lib/services/category.service.mjs +3 -3
  40. package/esm2022/lib/services/course-section.service.mjs +3 -3
  41. package/esm2022/lib/services/course.service.mjs +4 -4
  42. package/esm2022/lib/services/instructor.service.mjs +3 -3
  43. package/fesm2022/{rolatech-angular-course-course-index.component-C89fCBfR.mjs → rolatech-angular-course-course-index.component-DqcW95VP.mjs} +5 -5
  44. package/fesm2022/{rolatech-angular-course-course-index.component-C89fCBfR.mjs.map → rolatech-angular-course-course-index.component-DqcW95VP.mjs.map} +1 -1
  45. package/fesm2022/rolatech-angular-course.mjs +3033 -28
  46. package/fesm2022/rolatech-angular-course.mjs.map +1 -1
  47. package/index.d.ts +1 -0
  48. package/lib/pages/course-manage/course-manage-info/course-manage-info.component.d.ts +1 -1
  49. package/package.json +1 -1
  50. package/themes/_default.scss +1 -1
  51. package/fesm2022/rolatech-angular-course-rolatech-angular-course-DzJibnO6.mjs +0 -3038
  52. package/fesm2022/rolatech-angular-course-rolatech-angular-course-DzJibnO6.mjs.map +0 -1
@@ -1,29 +1,3034 @@
1
- export { n as CourseActionComponent, A as CourseDetailsComponent, y as CourseDetailsDialogComponent, z as CourseEditDialogComponent, k as CourseInfoComponent, b as CourseItemComponent, l as CourseMediaComponent, B as CourseMediaOwnerRendererComponent, j as CoursePreviewComponent, u as CoursePricingAddDialogComponent, m as CoursePricingComponent, w as CoursePricingDialogComponent, f as CourseRequestStatus, h as CourseReviewStatus, v as CourseScheduleAddDialogComponent, o as CourseScheduleComponent, x as CourseScheduleDialogComponent, r as CourseSectionItemComponent, i as CourseSectionLectureContentType, s as CourseSectionLectureItemComponent, D as CourseSectionLectureVideoDialogComponent, t as CourseSectionLectureVideoItemComponent, q as CourseSectionsComponent, e as CourseStatus, g as CourseType, F as DetailItemComponent, P as PricingItemComponent, S as ScheduleDate, E as ScheduleItemComponent, d as courseManageRoutes, c as courseRoutes, p as provideAngularCourse } from './rolatech-angular-course-rolatech-angular-course-DzJibnO6.mjs';
2
- import '@angular/core';
3
- import '@angular/forms';
4
- import '@angular/common';
5
- import '@angular/router';
6
- import '@rolatech/angular-services';
7
- import '@angular/material/icon';
8
- import '@angular/material/dialog';
9
- import '@rolatech/angular-components';
10
- import '@angular/material/button';
11
- import '@angular/material/checkbox';
12
- import '@rolatech/angular-common';
13
- import '@angular/material/divider';
14
- import '@angular/material/stepper';
15
- import '@angular/material-moment-adapter';
16
- import '@angular/material/core';
17
- import 'moment';
18
- import '@angular/material/select';
19
- import '@angular/material/datepicker';
20
- import '@angular/cdk/text-field';
21
- import '@angular/material/input';
22
- import '@angular/material/form-field';
23
- import '@angular/material/snack-bar';
24
- import '@angular/material/progress-bar';
25
- import 'lodash';
26
- import '@rolatech/angular-auth';
27
- import '@rolatech/angular-comment';
28
- import 'rxjs';
1
+ import * as i0 from '@angular/core';
2
+ import { Injectable, importProvidersFrom, makeEnvironmentProviders, Component, input, effect, inject, output, ViewEncapsulation, model, PLATFORM_ID, ElementRef, Inject, viewChild, booleanAttribute } from '@angular/core';
3
+ import * as i4 from '@angular/forms';
4
+ import { ReactiveFormsModule, FormsModule } from '@angular/forms';
5
+ import { CommonModule, NgClass, DatePipe, isPlatformBrowser, ViewportScroller } from '@angular/common';
6
+ import * as i1 from '@angular/router';
7
+ import { RouterModule, ActivatedRoute, Router, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
8
+ import { BaseService, DialogService } from '@rolatech/angular-services';
9
+ import * as i2 from '@angular/material/icon';
10
+ import { MatIconModule } from '@angular/material/icon';
11
+ import * as i1$3 from '@angular/material/dialog';
12
+ import { MatDialog, MAT_DIALOG_DATA, MatDialogClose } from '@angular/material/dialog';
13
+ import { ImagePreviewDialogComponent, ThumbnailComponent, AccordionComponent, PanelComponent, InputComponent, IconButtonComponent, AngularComponentsModule, ImageComponent, MediaListComponent, MediaListItemComponent, BaseComponent, RichViewComponent, RichItemComponent, ContainerComponent, ListComponent, ConfirmationDialogComponent, ToolbarComponent } from '@rolatech/angular-components';
14
+ import * as i1$1 from '@angular/material/button';
15
+ import { MatButtonModule } from '@angular/material/button';
16
+ import * as i3$1 from '@angular/material/checkbox';
17
+ import { MatCheckboxModule } from '@angular/material/checkbox';
18
+ import { DurationPipe, AngularCommonModule } from '@rolatech/angular-common';
19
+ import * as i5$1 from '@angular/material/divider';
20
+ import { MatDividerModule } from '@angular/material/divider';
21
+ import * as i6$2 from '@angular/material/stepper';
22
+ import { MatStepperModule, MatStepper } from '@angular/material/stepper';
23
+ import { MomentDateAdapter } from '@angular/material-moment-adapter';
24
+ import * as i6$1 from '@angular/material/core';
25
+ import { DateAdapter, MAT_DATE_LOCALE, MAT_DATE_FORMATS, MatOptionModule } from '@angular/material/core';
26
+ import moment from 'moment';
27
+ import * as i6 from '@angular/material/select';
28
+ import { MatSelectModule } from '@angular/material/select';
29
+ import * as i5 from '@angular/material/datepicker';
30
+ import { MatDatepickerModule } from '@angular/material/datepicker';
31
+ import * as i3 from '@angular/cdk/text-field';
32
+ import { TextFieldModule } from '@angular/cdk/text-field';
33
+ import * as i2$1 from '@angular/material/input';
34
+ import { MatInputModule } from '@angular/material/input';
35
+ import * as i1$2 from '@angular/material/form-field';
36
+ import { MatFormFieldModule } from '@angular/material/form-field';
37
+ import * as i3$3 from '@angular/material/snack-bar';
38
+ import { MatSnackBar } from '@angular/material/snack-bar';
39
+ import * as i3$2 from '@angular/material/progress-bar';
40
+ import { MatProgressBarModule } from '@angular/material/progress-bar';
41
+ import { first, remove, findLastIndex, findIndex, clone } from 'lodash';
42
+ import { AuthService, AuthUserService } from '@rolatech/angular-auth';
43
+ import { CommentsComponent } from '@rolatech/angular-comment';
44
+ import { Observable, from, concatMap, take } from 'rxjs';
45
+
46
+ var CourseStatus;
47
+ (function (CourseStatus) {
48
+ CourseStatus[CourseStatus["ACTIVE"] = '已发布'] = "ACTIVE";
49
+ CourseStatus[CourseStatus["ACCEPTED"] = '已通过'] = "ACCEPTED";
50
+ CourseStatus[CourseStatus["AWAITING"] = '审核中'] = "AWAITING";
51
+ CourseStatus[CourseStatus["PENDING"] = '审核中'] = "PENDING";
52
+ CourseStatus[CourseStatus["STARTED"] = '已开始'] = "STARTED";
53
+ CourseStatus[CourseStatus["ENDED"] = '已完成'] = "ENDED";
54
+ CourseStatus[CourseStatus["DELETED"] = '已删除'] = "DELETED";
55
+ CourseStatus[CourseStatus["DRAFT"] = '草稿'] = "DRAFT";
56
+ })(CourseStatus || (CourseStatus = {}));
57
+ var CourseRequestStatus;
58
+ (function (CourseRequestStatus) {
59
+ CourseRequestStatus["AWAITING"] = "\u5BA1\u6838\u4E2D";
60
+ CourseRequestStatus["ACTIVE"] = "\u5DF2\u901A\u8FC7";
61
+ CourseRequestStatus["REJECTED"] = "\u5DF2\u62D2\u7EDD";
62
+ })(CourseRequestStatus || (CourseRequestStatus = {}));
63
+ var CourseReviewStatus;
64
+ (function (CourseReviewStatus) {
65
+ CourseReviewStatus["ACCEPTED"] = "\u5DF2\u901A\u8FC7";
66
+ CourseReviewStatus["REJECTED"] = "\u672A\u901A\u8FC7";
67
+ })(CourseReviewStatus || (CourseReviewStatus = {}));
68
+ var CourseSectionLectureContentType;
69
+ (function (CourseSectionLectureContentType) {
70
+ CourseSectionLectureContentType[CourseSectionLectureContentType["VIDEO"] = '视频'] = "VIDEO";
71
+ CourseSectionLectureContentType[CourseSectionLectureContentType["ARTICLE"] = '文章'] = "ARTICLE";
72
+ })(CourseSectionLectureContentType || (CourseSectionLectureContentType = {}));
73
+ // export const CourseType = [
74
+ // {
75
+ // key: 'ONLINE',
76
+ // value: '线上',
77
+ // },
78
+ // {
79
+ // key: 'OFFLINE',
80
+ // value: '线下',
81
+ // },
82
+ // {
83
+ // key: 'MIXED',
84
+ // value: '混合',
85
+ // },
86
+ // ];
87
+ var CourseType;
88
+ (function (CourseType) {
89
+ CourseType[CourseType["ONLINE"] = '线上'] = "ONLINE";
90
+ CourseType[CourseType["OFFLINE"] = '线下'] = "OFFLINE";
91
+ CourseType[CourseType["MIXED"] = '混合'] = "MIXED";
92
+ })(CourseType || (CourseType = {}));
93
+ const ScheduleDate = [
94
+ '00:00',
95
+ '00:15',
96
+ '00:30',
97
+ '00:45',
98
+ '01:00',
99
+ '01:15',
100
+ '01:30',
101
+ '01:45',
102
+ '02:00',
103
+ '02:15',
104
+ '02:30',
105
+ '02:45',
106
+ '03:00',
107
+ '03:15',
108
+ '03:30',
109
+ '03:45',
110
+ '04:00',
111
+ '04:15',
112
+ '04:30',
113
+ '04:45',
114
+ '05:00',
115
+ '05:15',
116
+ '05:30',
117
+ '05:45',
118
+ '06:00',
119
+ '06:15',
120
+ '06:30',
121
+ '06:45',
122
+ '07:00',
123
+ '07:15',
124
+ '07:30',
125
+ '07:45',
126
+ '08:00',
127
+ '08:15',
128
+ '08:30',
129
+ '08:45',
130
+ '09:00',
131
+ '09:15',
132
+ '09:30',
133
+ '09:45',
134
+ '10:00',
135
+ '10:15',
136
+ '10:30',
137
+ '10:45',
138
+ '11:00',
139
+ '11:15',
140
+ '11:30',
141
+ '11:45',
142
+ '12:00',
143
+ '12:15',
144
+ '12:30',
145
+ '12:45',
146
+ '13:00',
147
+ '13:15',
148
+ '13:30',
149
+ '13:45',
150
+ '14:00',
151
+ '14:15',
152
+ '14:30',
153
+ '14:45',
154
+ '15:00',
155
+ '15:15',
156
+ '15:30',
157
+ '15:45',
158
+ '16:00',
159
+ '16:15',
160
+ '16:30',
161
+ '16:45',
162
+ '17:00',
163
+ '17:15',
164
+ '17:30',
165
+ '17:45',
166
+ '18:00',
167
+ '18:15',
168
+ '18:30',
169
+ '18:45',
170
+ '19:00',
171
+ '19:15',
172
+ '19:30',
173
+ '19:45',
174
+ '20:00',
175
+ '20:15',
176
+ '20:30',
177
+ '20:45',
178
+ '21:00',
179
+ '21:15',
180
+ '21:30',
181
+ '21:45',
182
+ '22:00',
183
+ '22:15',
184
+ '22:30',
185
+ '22:45',
186
+ '23:00',
187
+ '23:15',
188
+ '23:30',
189
+ '23:45',
190
+ ];
191
+
192
+ class CourseService extends BaseService {
193
+ init() {
194
+ this.endpoint = 'courses';
195
+ super.init();
196
+ }
197
+ me(options) {
198
+ return this.http.get(`${this.actionUrl}/me`, {
199
+ params: options,
200
+ withCredentials: true,
201
+ });
202
+ }
203
+ findSchedule(id) {
204
+ return this.http.get(`${this.actionUrl}/${id}/schedule`, {
205
+ withCredentials: true,
206
+ });
207
+ }
208
+ findPricing(id) {
209
+ return this.http.get(`${this.actionUrl}/${id}/pricing`, {
210
+ withCredentials: true,
211
+ });
212
+ }
213
+ addDetail(id, data) {
214
+ return this.http.post(`${this.actionUrl}/${id}/details`, data, {
215
+ withCredentials: true,
216
+ });
217
+ }
218
+ addBatchDetails(id, data) {
219
+ return this.http.post(`${this.actionUrl}/${id}/details/batch`, data, {
220
+ withCredentials: true,
221
+ });
222
+ }
223
+ findDetails(id) {
224
+ return this.http.get(`${this.actionUrl}/${id}/details`, {
225
+ withCredentials: true,
226
+ });
227
+ }
228
+ updateDetail(id, detailId, data) {
229
+ return this.http.put(`${this.actionUrl}/${id}/details/${detailId}`, data, {
230
+ withCredentials: true,
231
+ });
232
+ }
233
+ deleteDetail(id, detailId) {
234
+ return this.http.delete(`${this.actionUrl}/${id}/details/${detailId}`, {
235
+ withCredentials: true,
236
+ });
237
+ }
238
+ uploadDetailMedia(id, detailId, data) {
239
+ return this.http.post(`${this.actionUrl}/${id}/details/${detailId}/media`, data, {
240
+ withCredentials: true,
241
+ });
242
+ }
243
+ deleteDetailMedia(id, detailId, mediaId) {
244
+ return this.http.delete(`${this.actionUrl}/${id}/details/${detailId}/media/${mediaId}`, {
245
+ withCredentials: true,
246
+ });
247
+ }
248
+ review(id) {
249
+ return this.http.post(`${this.actionUrl}/${id}/review`, {}, {
250
+ withCredentials: true,
251
+ });
252
+ }
253
+ start(id) {
254
+ return this.http.post(`${this.actionUrl}/${id}/start`, {}, {
255
+ withCredentials: true,
256
+ });
257
+ }
258
+ finish(id) {
259
+ return this.http.post(`${this.actionUrl}/${id}/finish`, {}, {
260
+ withCredentials: true,
261
+ });
262
+ }
263
+ publish(id) {
264
+ return this.http.post(`${this.actionUrl}/${id}/publish`, {}, {
265
+ withCredentials: true,
266
+ });
267
+ }
268
+ upload(data) {
269
+ return this.http.post(`${this.actionUrl}/upload`, data, {
270
+ withCredentials: true,
271
+ });
272
+ }
273
+ uploadMedia(id, data) {
274
+ return this.http.post(`${this.actionUrl}/${id}/media`, data, {
275
+ withCredentials: true,
276
+ });
277
+ }
278
+ deleteMedia(id, mediaId) {
279
+ return this.http.delete(`${this.actionUrl}/${id}/media/${mediaId}`, {
280
+ withCredentials: true,
281
+ });
282
+ }
283
+ importFromExcel(data) {
284
+ return this.http.post(`${this.actionUrl}/excel`, data, {
285
+ withCredentials: true,
286
+ });
287
+ }
288
+ createFromExcel(data) {
289
+ return this.http.post(`${this.actionUrl}/list`, data, {
290
+ withCredentials: true,
291
+ });
292
+ }
293
+ findAllRequests(options) {
294
+ return this.http.get(`${this.actionUrl}/requests`, {
295
+ params: options,
296
+ withCredentials: true,
297
+ });
298
+ }
299
+ findRequestDetail(id) {
300
+ return this.http.get(`${this.actionUrl}/requests/${id}`, {
301
+ withCredentials: true,
302
+ });
303
+ }
304
+ accept(id) {
305
+ return this.http.post(`${this.actionUrl}/${id}/reviews/accept`, {}, {
306
+ withCredentials: true,
307
+ });
308
+ }
309
+ reject(id, body) {
310
+ return this.http.post(`${this.actionUrl}/${id}/reviews/reject`, body, {
311
+ withCredentials: true,
312
+ });
313
+ }
314
+ acceptRequest(id) {
315
+ return this.http.post(`${this.actionUrl}/requests/${id}/reviews/accept`, {}, {
316
+ withCredentials: true,
317
+ });
318
+ }
319
+ rejectRequest(id, body) {
320
+ return this.http.post(`${this.actionUrl}/requests/${id}/reviews/reject`, body, {
321
+ withCredentials: true,
322
+ });
323
+ }
324
+ addPricing(id, data) {
325
+ return this.http.post(`${this.actionUrl}/${id}/pricing`, data, {
326
+ withCredentials: true,
327
+ });
328
+ }
329
+ updatePricing(id, pricingId, data) {
330
+ return this.http.put(`${this.actionUrl}/${id}/pricing/${pricingId}`, data, {
331
+ withCredentials: true,
332
+ });
333
+ }
334
+ deletePricing(id, pricingId) {
335
+ return this.http.delete(`${this.actionUrl}/${id}/pricing/${pricingId}`, {
336
+ withCredentials: true,
337
+ });
338
+ }
339
+ addSchedule(id, data) {
340
+ return this.http.post(`${this.actionUrl}/${id}/schedule`, data, {
341
+ withCredentials: true,
342
+ });
343
+ }
344
+ updateSchedule(id, scheduleId, data) {
345
+ return this.http.put(`${this.actionUrl}/${id}/schedule/${scheduleId}`, data, {
346
+ withCredentials: true,
347
+ });
348
+ }
349
+ deleteSchedule(id, scheduleId) {
350
+ return this.http.delete(`${this.actionUrl}/${id}/schedule/${scheduleId}`, {
351
+ withCredentials: true,
352
+ });
353
+ }
354
+ findAllReviews(options) {
355
+ return this.http.get(`${this.actionUrl}/reviews`, {
356
+ params: options,
357
+ withCredentials: true,
358
+ });
359
+ }
360
+ getReview(id) {
361
+ return this.http.get(`${this.actionUrl}/reviews/${id}`, {
362
+ withCredentials: true,
363
+ });
364
+ }
365
+ findReviews(options) {
366
+ return this.http.get(`${this.actionUrl}/reviews/me`, {
367
+ params: options,
368
+ withCredentials: true,
369
+ });
370
+ }
371
+ countUsers(options) {
372
+ return this.http.get(`${this.actionUrl}/users/count/by`, {
373
+ params: options,
374
+ withCredentials: true,
375
+ });
376
+ }
377
+ findByIds(ids) {
378
+ return this.http.get(`${this.actionUrl}/by`, {
379
+ params: { ids },
380
+ withCredentials: true,
381
+ });
382
+ }
383
+ findWishlist(options) {
384
+ return this.http.get(`${this.actionUrl}/wishlist`, { params: options, withCredentials: true });
385
+ }
386
+ addToWishlist(courseId) {
387
+ return this.http.post(`${this.actionUrl}/${courseId}/wishlist`, {}, { withCredentials: true });
388
+ }
389
+ removeFromWishlist(courseId) {
390
+ return this.http.delete(`${this.actionUrl}/${courseId}/wishlist`, { withCredentials: true });
391
+ }
392
+ findWishlistBy(courseId) {
393
+ return this.http.get(`${this.actionUrl}/${courseId}/wishlist/by`, { withCredentials: true });
394
+ }
395
+ findMyCourses(options) {
396
+ return this.http.get(`${this.actionUrl}/users`, { params: options, withCredentials: true });
397
+ }
398
+ findPurchasedByCourseId(courseId) {
399
+ return this.http.get(`${this.actionUrl}/users/me/by?courseId=${courseId}`, {
400
+ withCredentials: true,
401
+ });
402
+ }
403
+ deleteFromWishlist(sectionId) {
404
+ return this.http.delete(`${this.actionUrl}/sections/${sectionId}`, { withCredentials: true });
405
+ }
406
+ // Course sections
407
+ findCourseSections(options) {
408
+ return this.http.get(`${this.actionUrl}/sections`, { params: options, withCredentials: false });
409
+ }
410
+ addCourseSection(courseId, data) {
411
+ return this.http.post(`${this.actionUrl}/${courseId}/sections`, data, { withCredentials: true });
412
+ }
413
+ updateCourseSection(sectionId, data) {
414
+ return this.http.put(`${this.actionUrl}/sections/${sectionId}`, data, { withCredentials: true });
415
+ }
416
+ deleteCourseSection(sectionId) {
417
+ return this.http.delete(`${this.actionUrl}/sections/${sectionId}`, { withCredentials: true });
418
+ }
419
+ getCourseSection(courseId) {
420
+ return this.http.get(`${this.actionUrl}/${courseId}/sections`, { withCredentials: false });
421
+ }
422
+ addLecture(sectionId, data) {
423
+ return this.http.post(`${this.actionUrl}/sections/${sectionId}/lectures`, data, { withCredentials: true });
424
+ }
425
+ updateLecture(lectureId, data) {
426
+ return this.http.put(`${this.actionUrl}/sections/lectures/${lectureId}`, data, { withCredentials: true });
427
+ }
428
+ deleteLecture(lectureId) {
429
+ return this.http.delete(`${this.actionUrl}/sections/lectures/${lectureId}`, { withCredentials: true });
430
+ }
431
+ uploadVideoInit(lectureId, data) {
432
+ return this.http.post(`${this.actionUrl}/sections/lectures/${lectureId}/videos/init`, data, {
433
+ withCredentials: true,
434
+ });
435
+ }
436
+ uploadVideoPartsToLecture(lectureId, data) {
437
+ return this.http.post(`${this.actionUrl}/sections/lectures/${lectureId}/videos/parts`, data, {
438
+ withCredentials: true,
439
+ });
440
+ }
441
+ completePartUpload(lectureId, data) {
442
+ return this.http.put(`${this.actionUrl}/sections/lectures/${lectureId}/videos/parts`, data, {
443
+ withCredentials: true,
444
+ });
445
+ }
446
+ uploadVideoToLecture(lectureId, data) {
447
+ return this.http.post(`${this.actionUrl}/sections/lectures/${lectureId}/videos`, data, {
448
+ withCredentials: true,
449
+ });
450
+ }
451
+ deleteLectureVideo(videoId) {
452
+ return this.http.delete(`${this.actionUrl}/sections/lectures/videos/${videoId}`, { withCredentials: true });
453
+ }
454
+ uploadLectureVideoThumbnail(videoId, data) {
455
+ return this.http.post(`${this.actionUrl}/sections/lectures/videos/${videoId}/thumbnail`, data, {
456
+ withCredentials: true,
457
+ });
458
+ }
459
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
460
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseService, providedIn: 'root' }); }
461
+ }
462
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseService, decorators: [{
463
+ type: Injectable,
464
+ args: [{
465
+ providedIn: 'root',
466
+ }]
467
+ }] });
468
+
469
+ class CourseSectionService extends BaseService {
470
+ init() {
471
+ this.endpoint = 'courses';
472
+ super.init();
473
+ }
474
+ findSections(id) {
475
+ return this.http.get(`${this.actionUrl}/${id}/sections`);
476
+ }
477
+ canBePreviewed(videoId, data) {
478
+ return this.http.post(`${this.actionUrl}/sections/lectures/videos/${videoId}/preview`, data, {
479
+ withCredentials: true,
480
+ });
481
+ }
482
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseSectionService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
483
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseSectionService, providedIn: 'root' }); }
484
+ }
485
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseSectionService, decorators: [{
486
+ type: Injectable,
487
+ args: [{
488
+ providedIn: 'root',
489
+ }]
490
+ }] });
491
+
492
+ class CategoryService extends BaseService {
493
+ init() {
494
+ this.endpoint = 'categories';
495
+ super.init();
496
+ }
497
+ uploadMedia(id, data) {
498
+ return this.http.post(`${this.actionUrl}/${id}/media`, data, {
499
+ withCredentials: true,
500
+ });
501
+ }
502
+ deleteMedia(id, mediaId) {
503
+ return this.http.delete(`${this.actionUrl}/${id}/media/${mediaId}`, {
504
+ withCredentials: true,
505
+ });
506
+ }
507
+ importFromExcel(data) {
508
+ return this.http.post(`${this.actionUrl}/excel`, data, {
509
+ withCredentials: true,
510
+ });
511
+ }
512
+ createFromExcel(data) {
513
+ return this.http.post(`${this.actionUrl}/list`, data, {
514
+ withCredentials: true,
515
+ });
516
+ }
517
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CategoryService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
518
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CategoryService, providedIn: 'root' }); }
519
+ }
520
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CategoryService, decorators: [{
521
+ type: Injectable,
522
+ args: [{
523
+ providedIn: 'root',
524
+ }]
525
+ }] });
526
+
527
+ const services = [CourseService, CourseSectionService, CategoryService];
528
+
529
+ function provideAngularCourse() {
530
+ const providers = [
531
+ ...services,
532
+ importProvidersFrom(CommonModule, ReactiveFormsModule, FormsModule, RouterModule),
533
+ ];
534
+ return makeEnvironmentProviders(providers);
535
+ }
536
+
537
+ class CoursePreviewComponent {
538
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CoursePreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
539
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.4", type: CoursePreviewComponent, isStandalone: true, selector: "rolatech-course-preview", ngImport: i0, template: "<p>course-preview works!</p>\n", styles: [""] }); }
540
+ }
541
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CoursePreviewComponent, decorators: [{
542
+ type: Component,
543
+ args: [{ selector: 'rolatech-course-preview', standalone: true, imports: [], template: "<p>course-preview works!</p>\n" }]
544
+ }] });
545
+
546
+ class CourseInfoComponent {
547
+ constructor() {
548
+ this.course = input.required();
549
+ this.instructor = input();
550
+ this.username = input();
551
+ this.dataToDisplay = effect(() => {
552
+ return this.course();
553
+ });
554
+ }
555
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseInfoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
556
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseInfoComponent, isStandalone: true, selector: "rolatech-course-info", inputs: { course: { classPropertyName: "course", publicName: "course", isSignal: true, isRequired: true, transformFunction: null }, instructor: { classPropertyName: "instructor", publicName: "instructor", isSignal: true, isRequired: false, transformFunction: null }, username: { classPropertyName: "username", publicName: "username", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div>\n <div>\n <div class=\"text-2xl font-medium\">{{ course().name }}</div>\n @if (course().type && course().type.toString() !== 'ONLINE') {\n <div class=\"flex items-center pt-2\">\n <mat-icon>place</mat-icon>\n <span class=\"text-sm opacity-75\">{{ course().classroom.address }}</span>\n </div>\n }\n <div class=\"py-3 flex justify-between\">\n <div>\n <div class=\"mb-1 flex items-center\">\n <span class=\"font-medium\">\u4E3B\u8BB2\u6559\u5E08: </span>\n <a class=\"ml-2 underline text-orange-600 text-[0.9rem]\" [routerLink]=\"['/@' + username()]\">{{ instructor() }}</a>\n </div>\n <span class=\"text-sm opacity-75\">\u6700\u540E\u7F16\u8F91: {{ course().updatedAt }} </span>\n </div>\n </div>\n </div>\n <!-- \u63CF\u8FF0 -->\n <div class=\"mb-3\">\n {{ course().description }}\n </div>\n</div>\n", styles: ["mat-icon{transform:scale(.8)}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
557
+ }
558
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseInfoComponent, decorators: [{
559
+ type: Component,
560
+ args: [{ selector: 'rolatech-course-info', standalone: true, imports: [MatIconModule, RouterModule], template: "<div>\n <div>\n <div class=\"text-2xl font-medium\">{{ course().name }}</div>\n @if (course().type && course().type.toString() !== 'ONLINE') {\n <div class=\"flex items-center pt-2\">\n <mat-icon>place</mat-icon>\n <span class=\"text-sm opacity-75\">{{ course().classroom.address }}</span>\n </div>\n }\n <div class=\"py-3 flex justify-between\">\n <div>\n <div class=\"mb-1 flex items-center\">\n <span class=\"font-medium\">\u4E3B\u8BB2\u6559\u5E08: </span>\n <a class=\"ml-2 underline text-orange-600 text-[0.9rem]\" [routerLink]=\"['/@' + username()]\">{{ instructor() }}</a>\n </div>\n <span class=\"text-sm opacity-75\">\u6700\u540E\u7F16\u8F91: {{ course().updatedAt }} </span>\n </div>\n </div>\n </div>\n <!-- \u63CF\u8FF0 -->\n <div class=\"mb-3\">\n {{ course().description }}\n </div>\n</div>\n", styles: ["mat-icon{transform:scale(.8)}\n"] }]
561
+ }] });
562
+
563
+ class CourseMediaComponent {
564
+ constructor() {
565
+ this.dialog = inject(MatDialog);
566
+ this.media = input([]);
567
+ this.min = input(false);
568
+ this.mediaIndex = 0;
569
+ }
570
+ onImageClick(i) {
571
+ const dialogRef = this.dialog.open(ImagePreviewDialogComponent, {
572
+ maxWidth: '80vw',
573
+ maxHeight: '80vh',
574
+ height: '80%',
575
+ width: '80%',
576
+ panelClass: 'full-screen-modal',
577
+ data: {
578
+ media: this.media,
579
+ selected: i,
580
+ },
581
+ });
582
+ dialogRef.afterClosed().subscribe((result) => { });
583
+ }
584
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseMediaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
585
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseMediaComponent, isStandalone: true, selector: "rolatech-course-media", inputs: { media: { classPropertyName: "media", publicName: "media", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (media()) {\n @if (min()) {\n <div class=\"md:w-80 object-cover aspect-video bg-[--rt-10-percent-layer] rounded-lg\">\n @if (media()) {\n <rolatech-thumbnail [src]=\"media()[0].url\" size=\"small\"></rolatech-thumbnail>\n }\n </div>\n } @else {\n <div>\n <div class=\"object-cover aspect-video bg-[--rt-10-percent-layer]\">\n <rolatech-thumbnail [src]=\"media()[mediaIndex].url\" size=\"small\"></rolatech-thumbnail>\n </div>\n @for (item of media(); track $index) {\n <div\n class=\"inline-flex flex-row mt-3 mr-3 cursor-pointer rounded-md\"\n (click)=\"mediaIndex = $index\"\n [ngClass]=\"mediaIndex === $index ? '' : 'opacity-30'\"\n >\n <rolatech-thumbnail [src]=\"item.url\" size=\"small\"></rolatech-thumbnail>\n </div>\n }\n </div>\n }\n}\n", styles: [""], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ThumbnailComponent, selector: "rolatech-thumbnail", inputs: ["src", "size", "mode", "ratio", "width", "height"] }] }); }
586
+ }
587
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseMediaComponent, decorators: [{
588
+ type: Component,
589
+ args: [{ selector: 'rolatech-course-media', standalone: true, imports: [NgClass, ThumbnailComponent], template: "@if (media()) {\n @if (min()) {\n <div class=\"md:w-80 object-cover aspect-video bg-[--rt-10-percent-layer] rounded-lg\">\n @if (media()) {\n <rolatech-thumbnail [src]=\"media()[0].url\" size=\"small\"></rolatech-thumbnail>\n }\n </div>\n } @else {\n <div>\n <div class=\"object-cover aspect-video bg-[--rt-10-percent-layer]\">\n <rolatech-thumbnail [src]=\"media()[mediaIndex].url\" size=\"small\"></rolatech-thumbnail>\n </div>\n @for (item of media(); track $index) {\n <div\n class=\"inline-flex flex-row mt-3 mr-3 cursor-pointer rounded-md\"\n (click)=\"mediaIndex = $index\"\n [ngClass]=\"mediaIndex === $index ? '' : 'opacity-30'\"\n >\n <rolatech-thumbnail [src]=\"item.url\" size=\"small\"></rolatech-thumbnail>\n </div>\n }\n </div>\n }\n}\n" }]
590
+ }] });
591
+
592
+ class CoursePricingComponent {
593
+ constructor() {
594
+ this.pricing = input([]);
595
+ }
596
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CoursePricingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
597
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CoursePricingComponent, isStandalone: true, selector: "rolatech-course-pricing", inputs: { pricing: { classPropertyName: "pricing", publicName: "pricing", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (pricing()) {\n <div class=\"py-3\">\n <div class=\"text-lg font-medium py-2\">\u4EF7\u683C</div>\n @for (item of pricing(); track $index) {\n <div>\n <span>{{ item.min }} \u81F3 {{ item.max }}\u4EBA</span>\n <span class=\"ml-3\">{{ item.total / 100 }}\u5143</span>\n </div>\n }\n </div>\n}\n", styles: [""] }); }
598
+ }
599
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CoursePricingComponent, decorators: [{
600
+ type: Component,
601
+ args: [{ selector: 'rolatech-course-pricing', standalone: true, template: "@if (pricing()) {\n <div class=\"py-3\">\n <div class=\"text-lg font-medium py-2\">\u4EF7\u683C</div>\n @for (item of pricing(); track $index) {\n <div>\n <span>{{ item.min }} \u81F3 {{ item.max }}\u4EBA</span>\n <span class=\"ml-3\">{{ item.total / 100 }}\u5143</span>\n </div>\n }\n </div>\n}\n" }]
602
+ }] });
603
+
604
+ class CourseActionComponent {
605
+ constructor() {
606
+ this.course = input.required();
607
+ this.inWishList = input(false);
608
+ this.cart = output();
609
+ this.wish = output();
610
+ this.checkout = output();
611
+ }
612
+ onCart(course) {
613
+ this.cart.emit(course);
614
+ }
615
+ onWish(course) {
616
+ this.wish.emit(course);
617
+ }
618
+ onCheckout(course) {
619
+ this.checkout.emit(course);
620
+ }
621
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseActionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
622
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.1.4", type: CourseActionComponent, isStandalone: true, selector: "rolatech-course-action", inputs: { course: { classPropertyName: "course", publicName: "course", isSignal: true, isRequired: true, transformFunction: null }, inWishList: { classPropertyName: "inWishList", publicName: "inWishList", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cart: "cart", wish: "wish", checkout: "checkout" }, ngImport: i0, template: "<div class=\"flex flex-col\">\n <!-- <a class=\"p-3 bg-orange-600 text-center mt-3\" target=\"_blank\" (click)=\"onCart(course)\">\u52A0\u5165\u8D2D\u7269\u8F66</a> -->\n <a mat-flat-button class=\"mt-3 min-h-11\" target=\"_blank\" (click)=\"onCheckout(course())\">\u7ACB\u5373\u8D2D\u4E70</a>\n <a mat-stroked-button class=\"mt-3 min-h-11\" target=\"_blank\" (click)=\"onWish(course())\">{{\n inWishList() ? '\u79FB\u9664\u6536\u85CF' : '\u52A0\u5165\u6536\u85CF'\n }}</a>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatAnchor, selector: "a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button]", exportAs: ["matButton", "matAnchor"] }] }); }
623
+ }
624
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseActionComponent, decorators: [{
625
+ type: Component,
626
+ args: [{ standalone: true, selector: 'rolatech-course-action', imports: [MatButtonModule], template: "<div class=\"flex flex-col\">\n <!-- <a class=\"p-3 bg-orange-600 text-center mt-3\" target=\"_blank\" (click)=\"onCart(course)\">\u52A0\u5165\u8D2D\u7269\u8F66</a> -->\n <a mat-flat-button class=\"mt-3 min-h-11\" target=\"_blank\" (click)=\"onCheckout(course())\">\u7ACB\u5373\u8D2D\u4E70</a>\n <a mat-stroked-button class=\"mt-3 min-h-11\" target=\"_blank\" (click)=\"onWish(course())\">{{\n inWishList() ? '\u79FB\u9664\u6536\u85CF' : '\u52A0\u5165\u6536\u85CF'\n }}</a>\n</div>\n" }]
627
+ }] });
628
+
629
+ class CourseScheduleComponent {
630
+ constructor() {
631
+ this.schedule = input([]);
632
+ }
633
+ ngOnInit() {
634
+ // this.formatSchedule();
635
+ }
636
+ formatSchedule() { }
637
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseScheduleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
638
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseScheduleComponent, isStandalone: true, selector: "rolatech-course-schedule", inputs: { schedule: { classPropertyName: "schedule", publicName: "schedule", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (schedule()) {\n <div class=\"py-3\" id=\"schedule\">\n <div class=\"text-xl font-medium py-2\">\u8BFE\u8868</div>\n <rolatech-accordion>\n @for (item of schedule(); track $index) {\n <rolatech-panel [title]=\"item.title\" [expanded]=\"$index === 0\">\n <div>\n <div class=\"text-gray-600 pt-2 text-sm\">\n <span>{{ item.startAt }} - {{ item.endAt }}</span>\n </div>\n <div class=\"py-3\">\n <div>{{ item.content }}</div>\n </div>\n </div>\n </rolatech-panel>\n }\n </rolatech-accordion>\n </div>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: AccordionComponent, selector: "rolatech-accordion" }, { kind: "component", type: PanelComponent, selector: "rolatech-panel", inputs: ["border", "divider", "expanded", "title", "subtitle"], outputs: ["expandedChange"] }] }); }
639
+ }
640
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseScheduleComponent, decorators: [{
641
+ type: Component,
642
+ args: [{ selector: 'rolatech-course-schedule', standalone: true, imports: [MatIconModule, AccordionComponent, PanelComponent], template: "@if (schedule()) {\n <div class=\"py-3\" id=\"schedule\">\n <div class=\"text-xl font-medium py-2\">\u8BFE\u8868</div>\n <rolatech-accordion>\n @for (item of schedule(); track $index) {\n <rolatech-panel [title]=\"item.title\" [expanded]=\"$index === 0\">\n <div>\n <div class=\"text-gray-600 pt-2 text-sm\">\n <span>{{ item.startAt }} - {{ item.endAt }}</span>\n </div>\n <div class=\"py-3\">\n <div>{{ item.content }}</div>\n </div>\n </div>\n </rolatech-panel>\n }\n </rolatech-accordion>\n </div>\n}\n" }]
643
+ }] });
644
+
645
+ class CourseSectionsComponent {
646
+ constructor() {
647
+ this.purchased = input(false);
648
+ this.sections = input([]);
649
+ this.section = output();
650
+ this.checkout = output();
651
+ }
652
+ onSection(section, lecture) {
653
+ this.section.emit({ section, lecture });
654
+ }
655
+ onCheckout(section, lecture) {
656
+ this.checkout.emit({ section, lecture });
657
+ }
658
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseSectionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
659
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseSectionsComponent, isStandalone: true, selector: "rolatech-course-sections", inputs: { purchased: { classPropertyName: "purchased", publicName: "purchased", isSignal: true, isRequired: false, transformFunction: null }, sections: { classPropertyName: "sections", publicName: "sections", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { section: "section", checkout: "checkout" }, ngImport: i0, template: "@if (sections()) {\n <div id=\"sections()\">\n <div class=\"text-2xl font-medium py-3 lg:pt-0\">\u8BFE\u7A0B\u5185\u5BB9</div>\n <rolatech-accordion>\n @for (item of sections(); track $index) {\n <rolatech-panel [expanded]=\"$index === 0\" [title]=\"item.title\">\n @for (lecture of item.lectures; track $index) {\n <div class=\"flex items-center justify-between py-1\">\n @if (lecture.item && lecture.type === 'VIDEO') {\n <div class=\"flex items-center gap-3\">\n <mat-icon>live_tv</mat-icon>\n <span class=\"text-md\"> {{ lecture.title }}</span>\n </div>\n <div>\n @if (purchased()) {\n <a\n class=\"text-sm mr-1 underline underline-offset-2 cursor-pointer text-orange-600\"\n (click)=\"onSection(item, lecture)\"\n >\u64AD\u653E</a\n >\n } @else {\n @if (lecture.item.canBePreviewed) {\n <a\n class=\"text-sm mr-1 underline underline-offset-2 cursor-pointer text-orange-600\"\n (click)=\"onSection(item, lecture)\"\n >\u8BD5\u770B</a\n >\n } @else {\n <a\n class=\"text-sm mr-1 underline underline-offset-2 cursor-pointer text-orange-600\"\n (click)=\"onCheckout(item, lecture)\"\n >\u8D2D\u4E70</a\n >\n }\n }\n\n <a class=\"text-gray-500 inline-block w-[60px] text-right\">{{ lecture.item.duration | duration }}</a>\n </div>\n }\n </div>\n }\n </rolatech-panel>\n }\n </rolatech-accordion>\n </div>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: DurationPipe, name: "duration" }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: AccordionComponent, selector: "rolatech-accordion" }, { kind: "component", type: PanelComponent, selector: "rolatech-panel", inputs: ["border", "divider", "expanded", "title", "subtitle"], outputs: ["expandedChange"] }], encapsulation: i0.ViewEncapsulation.None }); }
660
+ }
661
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseSectionsComponent, decorators: [{
662
+ type: Component,
663
+ args: [{ selector: 'rolatech-course-sections', encapsulation: ViewEncapsulation.None, standalone: true, imports: [MatIconModule, DurationPipe, MatCheckboxModule, AccordionComponent, PanelComponent], template: "@if (sections()) {\n <div id=\"sections()\">\n <div class=\"text-2xl font-medium py-3 lg:pt-0\">\u8BFE\u7A0B\u5185\u5BB9</div>\n <rolatech-accordion>\n @for (item of sections(); track $index) {\n <rolatech-panel [expanded]=\"$index === 0\" [title]=\"item.title\">\n @for (lecture of item.lectures; track $index) {\n <div class=\"flex items-center justify-between py-1\">\n @if (lecture.item && lecture.type === 'VIDEO') {\n <div class=\"flex items-center gap-3\">\n <mat-icon>live_tv</mat-icon>\n <span class=\"text-md\"> {{ lecture.title }}</span>\n </div>\n <div>\n @if (purchased()) {\n <a\n class=\"text-sm mr-1 underline underline-offset-2 cursor-pointer text-orange-600\"\n (click)=\"onSection(item, lecture)\"\n >\u64AD\u653E</a\n >\n } @else {\n @if (lecture.item.canBePreviewed) {\n <a\n class=\"text-sm mr-1 underline underline-offset-2 cursor-pointer text-orange-600\"\n (click)=\"onSection(item, lecture)\"\n >\u8BD5\u770B</a\n >\n } @else {\n <a\n class=\"text-sm mr-1 underline underline-offset-2 cursor-pointer text-orange-600\"\n (click)=\"onCheckout(item, lecture)\"\n >\u8D2D\u4E70</a\n >\n }\n }\n\n <a class=\"text-gray-500 inline-block w-[60px] text-right\">{{ lecture.item.duration | duration }}</a>\n </div>\n }\n </div>\n }\n </rolatech-panel>\n }\n </rolatech-accordion>\n </div>\n}\n" }]
664
+ }] });
665
+
666
+ class CourseSectionItemComponent {
667
+ constructor() {
668
+ this.section = model.required();
669
+ this.actions = model();
670
+ this.hasUnsaved = model();
671
+ this.editId = model();
672
+ this.save = output();
673
+ this.cancel = output();
674
+ this.delete = output();
675
+ this.edit = output();
676
+ this.addLecture = output();
677
+ this.editing = false;
678
+ this.isExpand = false;
679
+ }
680
+ ngOnInit() { }
681
+ onSave(section) {
682
+ this.editing = false;
683
+ this.editId.set(null);
684
+ this.save.emit(section);
685
+ }
686
+ onCancel(section) {
687
+ this.editing = false;
688
+ this.editId.set(null);
689
+ this.cancel.emit(section);
690
+ }
691
+ onDelete(section) {
692
+ this.delete.emit(section);
693
+ }
694
+ onEdit(section) {
695
+ this.editId.set(null);
696
+ this.editing = true;
697
+ this.edit.emit(section);
698
+ }
699
+ onAddLecture(section) {
700
+ this.addLecture.emit(section);
701
+ }
702
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseSectionItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
703
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseSectionItemComponent, isStandalone: true, selector: "rolatech-course-section-item", inputs: { section: { classPropertyName: "section", publicName: "section", isSignal: true, isRequired: true, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, hasUnsaved: { classPropertyName: "hasUnsaved", publicName: "hasUnsaved", isSignal: true, isRequired: false, transformFunction: null }, editId: { classPropertyName: "editId", publicName: "editId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { section: "sectionChange", actions: "actionsChange", hasUnsaved: "hasUnsavedChange", editId: "editIdChange", save: "save", cancel: "cancel", delete: "delete", edit: "edit", addLecture: "addLecture" }, ngImport: i0, template: "<div class=\"flex flex-col grow\">\n @if (section().id !== editId()) {\n <div>\n <div class=\"flex justify-between items-center cursor-pointer\" (click)=\"isExpand = !isExpand\">\n <div class=\"w-full flex items-center gap-3 pl-3\">\n <span>{{ section().title }}</span>\n <button mat-icon-button class=\"max-w-8 max-h-8 !p-1\" (click)=\"onEdit(section()); $event.stopPropagation()\">\n <mat-icon>edit</mat-icon>\n </button>\n <button mat-icon-button class=\"max-w-8 max-h-8 !p-1\" (click)=\"onDelete(section()); $event.stopPropagation()\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n <button mat-icon-button class=\"max-w-8 max-h-8 !p-1\">\n <mat-icon>{{ isExpand ? 'expand_less' : 'expand_more' }}</mat-icon>\n </button>\n </div>\n @if (isExpand) {\n <div>\n <ng-content></ng-content>\n </div>\n }\n </div>\n } @else {\n <div class=\"flex flex-col justify-between items-center\">\n <rolatech-input [(title)]=\"section().title\" placeholder=\"\u6807\u9898\"></rolatech-input>\n <div class=\"w-full flex flex-row justify-end p-3 gap-3\">\n <button mat-button class=\"max-h-8\" (click)=\"onCancel(section())\">\u53D6\u6D88</button>\n <button mat-flat-button class=\"max-h-8\" (click)=\"onSave(section())\">\u4FDD\u5B58</button>\n </div>\n </div>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i1$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: InputComponent, selector: "rolatech-input", inputs: ["title", "placeholder"], outputs: ["titleChange"] }] }); }
704
+ }
705
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseSectionItemComponent, decorators: [{
706
+ type: Component,
707
+ args: [{ selector: 'rolatech-course-section-item', standalone: true, imports: [MatIconModule, FormsModule, MatButtonModule, IconButtonComponent, InputComponent], template: "<div class=\"flex flex-col grow\">\n @if (section().id !== editId()) {\n <div>\n <div class=\"flex justify-between items-center cursor-pointer\" (click)=\"isExpand = !isExpand\">\n <div class=\"w-full flex items-center gap-3 pl-3\">\n <span>{{ section().title }}</span>\n <button mat-icon-button class=\"max-w-8 max-h-8 !p-1\" (click)=\"onEdit(section()); $event.stopPropagation()\">\n <mat-icon>edit</mat-icon>\n </button>\n <button mat-icon-button class=\"max-w-8 max-h-8 !p-1\" (click)=\"onDelete(section()); $event.stopPropagation()\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n <button mat-icon-button class=\"max-w-8 max-h-8 !p-1\">\n <mat-icon>{{ isExpand ? 'expand_less' : 'expand_more' }}</mat-icon>\n </button>\n </div>\n @if (isExpand) {\n <div>\n <ng-content></ng-content>\n </div>\n }\n </div>\n } @else {\n <div class=\"flex flex-col justify-between items-center\">\n <rolatech-input [(title)]=\"section().title\" placeholder=\"\u6807\u9898\"></rolatech-input>\n <div class=\"w-full flex flex-row justify-end p-3 gap-3\">\n <button mat-button class=\"max-h-8\" (click)=\"onCancel(section())\">\u53D6\u6D88</button>\n <button mat-flat-button class=\"max-h-8\" (click)=\"onSave(section())\">\u4FDD\u5B58</button>\n </div>\n </div>\n }\n</div>\n" }]
708
+ }] });
709
+
710
+ const COURSE_SCHEDULE_DATE_FORMATS = {
711
+ parse: {
712
+ dateInput: 'YYYY-MM-DD',
713
+ },
714
+ display: {
715
+ dateInput: 'YYYY-MM-DD',
716
+ monthYearLabel: 'MMM YYYY',
717
+ dateA11yLabel: 'YYYY-MM-DD',
718
+ monthYearA11yLabel: 'MMMM YYYY',
719
+ },
720
+ };
721
+ class ScheduleItemComponent {
722
+ constructor() {
723
+ this.value = input();
724
+ this.actions = input(false);
725
+ this.delete = output();
726
+ this.save = output();
727
+ this.date = ScheduleDate;
728
+ this.schedule = effect(() => {
729
+ this.schedule = this.value() || {
730
+ title: '',
731
+ content: '',
732
+ startAt: '',
733
+ endAt: '',
734
+ };
735
+ this.schedule.startDate = moment(this.schedule.startAt).format('YYYY-MM-DD');
736
+ this.schedule.startTime = moment(this.schedule.startAt).format('HH:mm');
737
+ this.schedule.endDate = moment(this.schedule.endAt).format('YYYY-MM-DD');
738
+ this.schedule.endTime = moment(this.schedule.endAt).format('HH:mm');
739
+ });
740
+ }
741
+ // constructor() {
742
+ // this.schedule = this.value();
743
+ // }
744
+ ngOnInit() {
745
+ // this.schedule = computed(() => {
746
+ // return this.value();
747
+ // })();
748
+ // this.schedule = this.value();
749
+ // this.schedule.startDate = moment(this.schedule.startAt).format('YYYY-MM-DD');
750
+ // this.schedule.startTime = moment(this.schedule.startAt).format('HH:mm');
751
+ // this.schedule.endDate = moment(this.schedule.endAt).format('YYYY-MM-DD');
752
+ // this.schedule.endTime = moment(this.schedule.endAt).format('HH:mm');
753
+ }
754
+ ngOnDestroy() {
755
+ const { startAt, endAt, startTime, startDate, endDate, endTime } = this.schedule;
756
+ this.schedule.startAt = this.schedule.startDate + ' ' + this.schedule.startTime;
757
+ this.schedule.endAt = this.schedule.endDate + ' ' + this.schedule.endTime;
758
+ delete this.schedule.startDate;
759
+ delete this.schedule.startTime;
760
+ delete this.schedule.endDate;
761
+ delete this.schedule.endTime;
762
+ }
763
+ startDateChanged(event) { }
764
+ startTimeChanged(event) { }
765
+ endDateChanged(event) { }
766
+ endTimeChanged(event) { }
767
+ onSave(schedule) {
768
+ this.format();
769
+ this.save.emit(schedule);
770
+ }
771
+ onDelete(schedule) {
772
+ this.delete.emit(schedule);
773
+ }
774
+ format() {
775
+ this.schedule.startAt = this.schedule.startDate + ' ' + this.schedule.startTime;
776
+ this.schedule.endAt = this.schedule.endDate + ' ' + this.schedule.endTime;
777
+ }
778
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ScheduleItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
779
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ScheduleItemComponent, isStandalone: true, selector: "rolatech-schedule-item", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { delete: "delete", save: "save" }, providers: [
780
+ {
781
+ provide: DateAdapter,
782
+ useClass: MomentDateAdapter,
783
+ deps: [MAT_DATE_LOCALE],
784
+ },
785
+ { provide: MAT_DATE_FORMATS, useValue: COURSE_SCHEDULE_DATE_FORMATS },
786
+ DatePipe,
787
+ ], ngImport: i0, template: "<div class=\"flex flex-col py-3\">\n <mat-form-field appearance=\"fill\">\n <mat-label> \u6807\u9898 </mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"schedule.title\" name=\"title\" required />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label> \u5185\u5BB9 </mat-label>\n <textarea\n matInput\n type=\"text\"\n [(ngModel)]=\"schedule.content\"\n name=\"content\"\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"\n required\n ></textarea>\n </mat-form-field>\n <div class=\"flex flex-row gap-3\">\n <mat-form-field appearance=\"fill\">\n <mat-label>\u5F00\u59CB\u65E5\u671F</mat-label>\n <input\n matInput\n [matDatepicker]=\"picker\"\n (focus)=\"picker.open()\"\n name=\"startAt\"\n [(ngModel)]=\"schedule.startDate\"\n (ngModelChange)=\"startDateChanged($event)\"\n (dateInput)=\"schedule.startDate = $event.value.format('YYYY-MM-DD')\"\n readonly\n required\n />\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\n <mat-datepicker #picker></mat-datepicker>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label>\u5F00\u59CB\u65F6\u95F4</mat-label>\n <mat-select [(ngModel)]=\"schedule.startTime\" (selectionChange)=\"startTimeChanged($event)\" required readonly>\n @for (d of date; track d) {\n <mat-option [value]=\"d\">\n {{ d }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"flex flex-row gap-3\">\n <mat-form-field appearance=\"fill\">\n <mat-label>\u7ED3\u675F\u65E5\u671F</mat-label>\n <input\n matInput\n [matDatepicker]=\"picker2\"\n name=\"endAt\"\n (focus)=\"picker2.open()\"\n (ngModelChange)=\"endDateChanged($event)\"\n [(ngModel)]=\"schedule.endDate\"\n (dateInput)=\"schedule.endDate = $event.value.format('YYYY-MM-DD')\"\n readonly\n required\n />\n <mat-datepicker-toggle matIconSuffix [for]=\"picker2\"></mat-datepicker-toggle>\n <mat-datepicker #picker2></mat-datepicker>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label>\u7ED3\u675F\u65F6\u95F4</mat-label>\n <mat-select [(ngModel)]=\"schedule.endTime\" (selectionChange)=\"endTimeChanged($event)\" required readonly>\n @for (d of date; track d) {\n <mat-option [value]=\"d\">\n {{ d }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n</div>\n@if (actions()) {\n <div class=\"flex flex-row justify-end p-3 gap-3\">\n <button mat-button class=\"max-h-8\" (click)=\"onDelete(schedule)\">\u5220\u9664</button>\n <button mat-flat-button class=\"max-h-8\" (click)=\"onSave(schedule)\">\u4FDD\u5B58</button>\n </div>\n}\n<mat-divider></mat-divider>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i1$2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "directive", type: i3.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i5$1.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }] }); }
788
+ }
789
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ScheduleItemComponent, decorators: [{
790
+ type: Component,
791
+ args: [{ selector: 'rolatech-schedule-item', providers: [
792
+ {
793
+ provide: DateAdapter,
794
+ useClass: MomentDateAdapter,
795
+ deps: [MAT_DATE_LOCALE],
796
+ },
797
+ { provide: MAT_DATE_FORMATS, useValue: COURSE_SCHEDULE_DATE_FORMATS },
798
+ DatePipe,
799
+ ], standalone: true, imports: [
800
+ NgClass,
801
+ MatFormFieldModule,
802
+ MatInputModule,
803
+ FormsModule,
804
+ TextFieldModule,
805
+ MatDatepickerModule,
806
+ MatSelectModule,
807
+ MatOptionModule,
808
+ MatButtonModule,
809
+ MatDividerModule,
810
+ ], template: "<div class=\"flex flex-col py-3\">\n <mat-form-field appearance=\"fill\">\n <mat-label> \u6807\u9898 </mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"schedule.title\" name=\"title\" required />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label> \u5185\u5BB9 </mat-label>\n <textarea\n matInput\n type=\"text\"\n [(ngModel)]=\"schedule.content\"\n name=\"content\"\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"\n required\n ></textarea>\n </mat-form-field>\n <div class=\"flex flex-row gap-3\">\n <mat-form-field appearance=\"fill\">\n <mat-label>\u5F00\u59CB\u65E5\u671F</mat-label>\n <input\n matInput\n [matDatepicker]=\"picker\"\n (focus)=\"picker.open()\"\n name=\"startAt\"\n [(ngModel)]=\"schedule.startDate\"\n (ngModelChange)=\"startDateChanged($event)\"\n (dateInput)=\"schedule.startDate = $event.value.format('YYYY-MM-DD')\"\n readonly\n required\n />\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\n <mat-datepicker #picker></mat-datepicker>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label>\u5F00\u59CB\u65F6\u95F4</mat-label>\n <mat-select [(ngModel)]=\"schedule.startTime\" (selectionChange)=\"startTimeChanged($event)\" required readonly>\n @for (d of date; track d) {\n <mat-option [value]=\"d\">\n {{ d }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"flex flex-row gap-3\">\n <mat-form-field appearance=\"fill\">\n <mat-label>\u7ED3\u675F\u65E5\u671F</mat-label>\n <input\n matInput\n [matDatepicker]=\"picker2\"\n name=\"endAt\"\n (focus)=\"picker2.open()\"\n (ngModelChange)=\"endDateChanged($event)\"\n [(ngModel)]=\"schedule.endDate\"\n (dateInput)=\"schedule.endDate = $event.value.format('YYYY-MM-DD')\"\n readonly\n required\n />\n <mat-datepicker-toggle matIconSuffix [for]=\"picker2\"></mat-datepicker-toggle>\n <mat-datepicker #picker2></mat-datepicker>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label>\u7ED3\u675F\u65F6\u95F4</mat-label>\n <mat-select [(ngModel)]=\"schedule.endTime\" (selectionChange)=\"endTimeChanged($event)\" required readonly>\n @for (d of date; track d) {\n <mat-option [value]=\"d\">\n {{ d }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n</div>\n@if (actions()) {\n <div class=\"flex flex-row justify-end p-3 gap-3\">\n <button mat-button class=\"max-h-8\" (click)=\"onDelete(schedule)\">\u5220\u9664</button>\n <button mat-flat-button class=\"max-h-8\" (click)=\"onSave(schedule)\">\u4FDD\u5B58</button>\n </div>\n}\n<mat-divider></mat-divider>\n" }]
811
+ }] });
812
+
813
+ class CourseSectionLectureVideoDialogComponent {
814
+ constructor(dialogRef, data) {
815
+ this.dialogRef = dialogRef;
816
+ this.data = data;
817
+ this.thumbnailUpload = output();
818
+ this.courseService = inject(CourseService);
819
+ this.courseSectionService = inject(CourseSectionService);
820
+ this.snackBar = inject(MatSnackBar);
821
+ this.platformId = inject(PLATFORM_ID);
822
+ this.el = inject(ElementRef);
823
+ this.lecture = data.data;
824
+ }
825
+ ngAfterViewInit() {
826
+ this.initPlayer(this.lecture.item.url, this.lecture.item.thumbnail);
827
+ }
828
+ close() {
829
+ this.dialogRef.close();
830
+ }
831
+ initPlayer(url, thumbnail) {
832
+ if (!isPlatformBrowser(this.platformId)) {
833
+ return;
834
+ }
835
+ // 'https://cos-video-1258344699.cos.ap-guangzhou.tencentcos.cn/test.mp4',
836
+ if (!this.player) {
837
+ const playerContainer = this.el.nativeElement.querySelector('#dplayer');
838
+ this.player = new DPlayer({
839
+ container: playerContainer,
840
+ screenshot: true,
841
+ preload: 'metadata',
842
+ video: {
843
+ url: url,
844
+ pic: thumbnail,
845
+ },
846
+ });
847
+ // this.player.on('loadedmetadata', () => {
848
+ // });
849
+ }
850
+ }
851
+ ngOnInit() { }
852
+ ngOnDestroy() {
853
+ if (this.player) {
854
+ this.player.destroy();
855
+ }
856
+ }
857
+ onThumbnailUpload(event) {
858
+ const file = event.target.files[0];
859
+ if (file) {
860
+ const reader = new FileReader();
861
+ reader.readAsDataURL(file);
862
+ reader.onload = () => {
863
+ this.mediaSrc = reader.result;
864
+ const formData = new FormData();
865
+ formData.append('file', file);
866
+ this.courseService.uploadLectureVideoThumbnail(this.lecture.item.id, formData).subscribe({
867
+ next: (res) => {
868
+ this.lecture.item = res.data;
869
+ this.snackBar.open('上传成功');
870
+ this.player.switchVideo({
871
+ url: this.lecture.item.url,
872
+ pic: res.data.thumbnail,
873
+ });
874
+ },
875
+ });
876
+ };
877
+ reader.onerror = (error) => { };
878
+ }
879
+ }
880
+ onPreviewChange(event) {
881
+ this.courseSectionService.canBePreviewed(this.lecture.item.id, { canBePreviewed: event.checked }).subscribe({
882
+ next: (res) => {
883
+ this.lecture.item.canBePreviewed = res.data.canBePreviewed;
884
+ // this.lecture = res.data;
885
+ },
886
+ });
887
+ }
888
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseSectionLectureVideoDialogComponent, deps: [{ token: i1$3.MatDialogRef }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component }); }
889
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseSectionLectureVideoDialogComponent, isStandalone: true, selector: "rolatech-course-section-lecture-video-dialog", outputs: { thumbnailUpload: "thumbnailUpload" }, ngImport: i0, template: "<div class=\"flex flex-col-reverse md:flex-row justify-between h-full gap-3\">\n <div class=\"w-full md:w-1/2\">\n <div>\n <div class=\"py-3 flex flex-col\">\n <span class=\"text-lg font-bold\"> \u5C01\u9762 </span>\n <span class=\"text-sm\">\u4E00\u4E2A\u597D\u7684\u5C01\u9762\u53EF\u4EE5\u5F15\u8D77\u66F4\u591A\u7684\u5173\u6CE8</span>\n </div>\n\n <div class=\"flex gap-3\">\n <div\n class=\"p-3 w-36 flex flex-col items-center justify-center outline-dashed outline-1 rounded cursor-pointer\"\n (click)=\"fileInput.click()\"\n >\n <mat-icon>upload</mat-icon>\n <span class=\"text-sm\">\u4E0A\u4F20\u5C01\u9762</span>\n </div>\n <input type=\"file\" accept=\"image/*\" (change)=\"onThumbnailUpload($event)\" #fileInput style=\"display: none\" />\n @if (lecture.item.thumbnail) {\n <div class=\"aspect-video w-32 h-auto bg-black\">\n <img [src]=\"lecture.item.thumbnail\" class=\"aspect-video object-contain\" />\n </div>\n } @else {\n <div class=\"aspect-video w-32 h-auto\">\n <img [src]=\"mediaSrc\" class=\"aspect-video object-contain\" />\n </div>\n }\n </div>\n </div>\n <div class=\"mt-3\">\n <div class=\"py-3 flex flex-col\">\n <span class=\"text-lg font-bold\"> \u8BD5\u770B\u5185\u5BB9 </span>\n <span class=\"text-sm\">\u5C11\u91CF\u7684\u8BD5\u770B\u5185\u5BB9, \u53EF\u4EE5\u589E\u52A0\u8D2D\u4E70\u7387</span>\n </div>\n <mat-checkbox color=\"primary\" (change)=\"onPreviewChange($event)\" [checked]=\"lecture.item.canBePreviewed\"\n >\u5141\u8BB8\u8BD5\u770B</mat-checkbox\n >\n </div>\n </div>\n <div class=\"w-full md:w-1/2\">\n @if (lecture) {\n <div>\n <div id=\"dplayer\" class=\"w-full h-auto aspect-video md:rounded-xl\"></div>\n </div>\n }\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatStepperModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i3$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }] }); }
890
+ }
891
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseSectionLectureVideoDialogComponent, decorators: [{
892
+ type: Component,
893
+ args: [{ selector: 'rolatech-course-section-lecture-video-dialog', standalone: true, imports: [
894
+ MatIconModule,
895
+ MatDividerModule,
896
+ FormsModule,
897
+ MatStepperModule,
898
+ ScheduleItemComponent,
899
+ MatButtonModule,
900
+ MatDialogClose,
901
+ MatCheckboxModule,
902
+ ], template: "<div class=\"flex flex-col-reverse md:flex-row justify-between h-full gap-3\">\n <div class=\"w-full md:w-1/2\">\n <div>\n <div class=\"py-3 flex flex-col\">\n <span class=\"text-lg font-bold\"> \u5C01\u9762 </span>\n <span class=\"text-sm\">\u4E00\u4E2A\u597D\u7684\u5C01\u9762\u53EF\u4EE5\u5F15\u8D77\u66F4\u591A\u7684\u5173\u6CE8</span>\n </div>\n\n <div class=\"flex gap-3\">\n <div\n class=\"p-3 w-36 flex flex-col items-center justify-center outline-dashed outline-1 rounded cursor-pointer\"\n (click)=\"fileInput.click()\"\n >\n <mat-icon>upload</mat-icon>\n <span class=\"text-sm\">\u4E0A\u4F20\u5C01\u9762</span>\n </div>\n <input type=\"file\" accept=\"image/*\" (change)=\"onThumbnailUpload($event)\" #fileInput style=\"display: none\" />\n @if (lecture.item.thumbnail) {\n <div class=\"aspect-video w-32 h-auto bg-black\">\n <img [src]=\"lecture.item.thumbnail\" class=\"aspect-video object-contain\" />\n </div>\n } @else {\n <div class=\"aspect-video w-32 h-auto\">\n <img [src]=\"mediaSrc\" class=\"aspect-video object-contain\" />\n </div>\n }\n </div>\n </div>\n <div class=\"mt-3\">\n <div class=\"py-3 flex flex-col\">\n <span class=\"text-lg font-bold\"> \u8BD5\u770B\u5185\u5BB9 </span>\n <span class=\"text-sm\">\u5C11\u91CF\u7684\u8BD5\u770B\u5185\u5BB9, \u53EF\u4EE5\u589E\u52A0\u8D2D\u4E70\u7387</span>\n </div>\n <mat-checkbox color=\"primary\" (change)=\"onPreviewChange($event)\" [checked]=\"lecture.item.canBePreviewed\"\n >\u5141\u8BB8\u8BD5\u770B</mat-checkbox\n >\n </div>\n </div>\n <div class=\"w-full md:w-1/2\">\n @if (lecture) {\n <div>\n <div id=\"dplayer\" class=\"w-full h-auto aspect-video md:rounded-xl\"></div>\n </div>\n }\n </div>\n</div>\n" }]
903
+ }], ctorParameters: () => [{ type: i1$3.MatDialogRef }, { type: undefined, decorators: [{
904
+ type: Inject,
905
+ args: [MAT_DIALOG_DATA]
906
+ }] }] });
907
+
908
+ class CourseSectionLectureItemComponent {
909
+ constructor() {
910
+ this.platformId = inject(PLATFORM_ID);
911
+ this.el = inject(ElementRef);
912
+ this.dialogService = inject(DialogService);
913
+ this.myVideo = viewChild('video');
914
+ this.progress = input(0);
915
+ this.lecture = input.required();
916
+ this.actions = input(true);
917
+ this.hasUnsaved = input(false);
918
+ this.editId = model();
919
+ this.save = output();
920
+ this.cancel = output();
921
+ this.delete = output();
922
+ this.edit = output();
923
+ this.mediaEdit = output();
924
+ this.thumbnailUpload = output();
925
+ this.upload = output();
926
+ this.deleteMedia = output();
927
+ this.editing = false;
928
+ }
929
+ ngOnInit() { }
930
+ onSave(lecture) {
931
+ this.editing = false;
932
+ this.editId.set(null);
933
+ this.save.emit(lecture);
934
+ }
935
+ onCancel(lecture) {
936
+ this.editing = false;
937
+ this.editId.set(null);
938
+ this.cancel.emit(lecture);
939
+ }
940
+ onDelete(lecture) {
941
+ this.delete.emit(lecture);
942
+ }
943
+ onEdit(lecture) {
944
+ this.editId.set(lecture.id);
945
+ this.editing = true;
946
+ this.edit.emit(lecture);
947
+ }
948
+ onMediaEdit(lecture) {
949
+ this.editId.set(lecture.id);
950
+ this.editing = true;
951
+ // this.mediaEdit.emit(lecture);
952
+ const options = {
953
+ title: '编辑视频',
954
+ cancelText: '取消',
955
+ confirmText: '确定',
956
+ data: this.lecture(),
957
+ width: '80vw',
958
+ maxWidth: '80vw',
959
+ height: '100%',
960
+ component: CourseSectionLectureVideoDialogComponent,
961
+ };
962
+ this.dialogService.open(options);
963
+ this.dialogService.confirmed().subscribe({
964
+ next: (res) => {
965
+ if (res) {
966
+ const data = {
967
+ name: res,
968
+ };
969
+ }
970
+ },
971
+ });
972
+ }
973
+ onVideoThumbnailUpload(video) {
974
+ this.thumbnailUpload.emit(video);
975
+ }
976
+ onUpload(id, data) {
977
+ this.lecture().isUploading = true;
978
+ this.upload.emit({ id, data });
979
+ }
980
+ onDeleteMedia(data) {
981
+ this.deleteMedia.emit(data);
982
+ }
983
+ onLoadedMetadata(event) {
984
+ if (this.lecture().item.duration) {
985
+ return;
986
+ }
987
+ const videoElement = event.target;
988
+ this.lecture().item.duration = videoElement.duration;
989
+ }
990
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseSectionLectureItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
991
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseSectionLectureItemComponent, isStandalone: true, selector: "rolatech-course-section-lecture-item", inputs: { progress: { classPropertyName: "progress", publicName: "progress", isSignal: true, isRequired: false, transformFunction: null }, lecture: { classPropertyName: "lecture", publicName: "lecture", isSignal: true, isRequired: true, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, hasUnsaved: { classPropertyName: "hasUnsaved", publicName: "hasUnsaved", isSignal: true, isRequired: false, transformFunction: null }, editId: { classPropertyName: "editId", publicName: "editId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { editId: "editIdChange", save: "save", cancel: "cancel", delete: "delete", edit: "edit", mediaEdit: "mediaEdit", thumbnailUpload: "thumbnailUpload", upload: "upload", deleteMedia: "deleteMedia" }, viewQueries: [{ propertyName: "myVideo", first: true, predicate: ["video"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"flex flex-col grow p-2\">\n @if (lecture().id !== editId()) {\n <div class=\"w-full flex gap-3 p-3 items-center\">\n <span>{{ lecture().title }}</span>\n <button mat-icon-button (click)=\"onEdit(lecture()); $event.stopPropagation()\" class=\"max-w-8 max-h-8 !p-1\">\n <mat-icon>edit</mat-icon>\n </button>\n <button mat-icon-button (click)=\"onDelete(lecture()); $event.stopPropagation()\" class=\"max-w-8 max-h-8 !p-1\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n } @else {\n <div class=\"flex flex-col justify-between items-center\">\n <rolatech-input [(title)]=\"lecture().title\" placeholder=\"\u6807\u9898\"></rolatech-input>\n <div class=\"w-full flex flex-row justify-end p-3 gap-3\">\n <button mat-button class=\"max-h-8\" (click)=\"onCancel(lecture())\">\u53D6\u6D88</button>\n <button mat-flat-button class=\"max-h-8\" (click)=\"onSave(lecture())\">\u4FDD\u5B58</button>\n </div>\n </div>\n }\n\n <div>\n @if (lecture().item) {\n <div class=\"flex flex-row p-2 gap-3\">\n <div class=\"bg-black h-fit\">\n <video\n id=\"video\"\n #video\n [src]=\"lecture().item.url\"\n class=\"w-32 aspect-video\"\n (loadedmetadata)=\"onLoadedMetadata($event)\"\n [poster]=\"lecture().item.thumbnail\"\n ></video>\n </div>\n <div class=\"flex flex-col justify-between w-full\">\n <div class=\"flex justify-between items-center w-full\">\n <div class=\"flex justify-between items-center w-full px-2\">\n <span>\u89C6\u9891\u5185\u5BB9</span>\n </div>\n @if (lecture().isUploading) {\n <div>\n <span> {{ lecture().item.progress }}%</span>\n </div>\n }\n </div>\n <div class=\"flex justify-between items-center\">\n <div>\n <button mat-button class=\"max-h-8\" (click)=\"onMediaEdit(lecture().item); $event.stopPropagation()\">\n <mat-icon>edit</mat-icon>\n <span>\u7F16\u8F91</span>\n </button>\n <button mat-button class=\"max-h-8\" (click)=\"onDeleteMedia(lecture().item); $event.stopPropagation()\">\n <mat-icon>delete</mat-icon>\n <span>\u5220\u9664</span>\n </button>\n </div>\n <div>\n <span>{{ lecture().item.duration | duration }}</span>\n </div>\n </div>\n </div>\n </div>\n @if (lecture().isUploading) {\n <div class=\"p-2\">\n <mat-progress-bar color=\"primary\" mode=\"determinate\" [value]=\"lecture().item.progress\"> </mat-progress-bar>\n </div>\n }\n } @else {\n @if (lecture().id !== editId()) {\n <div class=\"px-3\">\n <input\n class=\"ud-sr-only\"\n type=\"file\"\n accept=\".avi,.mpg,.mpeg,.flv,.mov,.m2v,.m4v,.mp4,.rm,.ram,.vob,.ogv,.webm,.wmv\"\n (change)=\"onUpload(lecture().id, $event)\"\n #fileInput\n />\n <div class=\"flex justify-between items-center\">\n <div>\u65E0\u89C6\u9891\u6587\u4EF6</div>\n <button mat-flat-button class=\"max-h-8\" (click)=\"fileInput.click()\">\u4E0A\u4F20\u89C6\u9891</button>\n </div>\n </div>\n }\n }\n </div>\n @if (!hasUnsaved) {\n <ng-content></ng-content>\n }\n</div>\n", styles: [".ud-sr-only{position:absolute!important;height:1px;width:1px;overflow:hidden;clip:rect(1px,1px,1px,1px)}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: AngularCommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i1$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: i3$2.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "pipe", type: DurationPipe, name: "duration" }, { kind: "component", type: InputComponent, selector: "rolatech-input", inputs: ["title", "placeholder"], outputs: ["titleChange"] }] }); }
992
+ }
993
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseSectionLectureItemComponent, decorators: [{
994
+ type: Component,
995
+ args: [{ selector: 'rolatech-course-section-lecture-item', standalone: true, imports: [
996
+ MatIconModule,
997
+ AngularCommonModule,
998
+ FormsModule,
999
+ MatButtonModule,
1000
+ MatDividerModule,
1001
+ AngularComponentsModule,
1002
+ DurationPipe,
1003
+ InputComponent,
1004
+ ], template: "<div class=\"flex flex-col grow p-2\">\n @if (lecture().id !== editId()) {\n <div class=\"w-full flex gap-3 p-3 items-center\">\n <span>{{ lecture().title }}</span>\n <button mat-icon-button (click)=\"onEdit(lecture()); $event.stopPropagation()\" class=\"max-w-8 max-h-8 !p-1\">\n <mat-icon>edit</mat-icon>\n </button>\n <button mat-icon-button (click)=\"onDelete(lecture()); $event.stopPropagation()\" class=\"max-w-8 max-h-8 !p-1\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n } @else {\n <div class=\"flex flex-col justify-between items-center\">\n <rolatech-input [(title)]=\"lecture().title\" placeholder=\"\u6807\u9898\"></rolatech-input>\n <div class=\"w-full flex flex-row justify-end p-3 gap-3\">\n <button mat-button class=\"max-h-8\" (click)=\"onCancel(lecture())\">\u53D6\u6D88</button>\n <button mat-flat-button class=\"max-h-8\" (click)=\"onSave(lecture())\">\u4FDD\u5B58</button>\n </div>\n </div>\n }\n\n <div>\n @if (lecture().item) {\n <div class=\"flex flex-row p-2 gap-3\">\n <div class=\"bg-black h-fit\">\n <video\n id=\"video\"\n #video\n [src]=\"lecture().item.url\"\n class=\"w-32 aspect-video\"\n (loadedmetadata)=\"onLoadedMetadata($event)\"\n [poster]=\"lecture().item.thumbnail\"\n ></video>\n </div>\n <div class=\"flex flex-col justify-between w-full\">\n <div class=\"flex justify-between items-center w-full\">\n <div class=\"flex justify-between items-center w-full px-2\">\n <span>\u89C6\u9891\u5185\u5BB9</span>\n </div>\n @if (lecture().isUploading) {\n <div>\n <span> {{ lecture().item.progress }}%</span>\n </div>\n }\n </div>\n <div class=\"flex justify-between items-center\">\n <div>\n <button mat-button class=\"max-h-8\" (click)=\"onMediaEdit(lecture().item); $event.stopPropagation()\">\n <mat-icon>edit</mat-icon>\n <span>\u7F16\u8F91</span>\n </button>\n <button mat-button class=\"max-h-8\" (click)=\"onDeleteMedia(lecture().item); $event.stopPropagation()\">\n <mat-icon>delete</mat-icon>\n <span>\u5220\u9664</span>\n </button>\n </div>\n <div>\n <span>{{ lecture().item.duration | duration }}</span>\n </div>\n </div>\n </div>\n </div>\n @if (lecture().isUploading) {\n <div class=\"p-2\">\n <mat-progress-bar color=\"primary\" mode=\"determinate\" [value]=\"lecture().item.progress\"> </mat-progress-bar>\n </div>\n }\n } @else {\n @if (lecture().id !== editId()) {\n <div class=\"px-3\">\n <input\n class=\"ud-sr-only\"\n type=\"file\"\n accept=\".avi,.mpg,.mpeg,.flv,.mov,.m2v,.m4v,.mp4,.rm,.ram,.vob,.ogv,.webm,.wmv\"\n (change)=\"onUpload(lecture().id, $event)\"\n #fileInput\n />\n <div class=\"flex justify-between items-center\">\n <div>\u65E0\u89C6\u9891\u6587\u4EF6</div>\n <button mat-flat-button class=\"max-h-8\" (click)=\"fileInput.click()\">\u4E0A\u4F20\u89C6\u9891</button>\n </div>\n </div>\n }\n }\n </div>\n @if (!hasUnsaved) {\n <ng-content></ng-content>\n }\n</div>\n", styles: [".ud-sr-only{position:absolute!important;height:1px;width:1px;overflow:hidden;clip:rect(1px,1px,1px,1px)}\n"] }]
1005
+ }] });
1006
+
1007
+ class CourseSectionLectureVideoItemComponent {
1008
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseSectionLectureVideoItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1009
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.4", type: CourseSectionLectureVideoItemComponent, isStandalone: true, selector: "rolatech-course-section-lecture-video-item", ngImport: i0, template: "<p>course-section-lecture-video-item works!</p>\n", styles: [""] }); }
1010
+ }
1011
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseSectionLectureVideoItemComponent, decorators: [{
1012
+ type: Component,
1013
+ args: [{ selector: 'rolatech-course-section-lecture-video-item', standalone: true, template: "<p>course-section-lecture-video-item works!</p>\n" }]
1014
+ }] });
1015
+
1016
+ class CourseItemComponent {
1017
+ constructor() {
1018
+ this.course = input.required();
1019
+ this.row = input(true);
1020
+ this.inset = input(false, { transform: booleanAttribute });
1021
+ }
1022
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1023
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseItemComponent, isStandalone: true, selector: "rolatech-course-item", inputs: { course: { classPropertyName: "course", publicName: "course", isSignal: true, isRequired: true, transformFunction: null }, row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: false, transformFunction: null }, inset: { classPropertyName: "inset", publicName: "inset", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (row()) {\n <div class=\"flex w-full flex-row\" [style]=\"inset() ? 'padding: 0 16px' : ''\">\n <div class=\"h-fit w-2/5 md:w-1/4 aspect-video bg-[--rt-10-percent-layer] rounded-lg\">\n @if (course().media) {\n @defer (on viewport()) {\n <rolatech-thumbnail [src]=\"course().media[0].url + '!w400'\" size=\"medium\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-[--rt-10-percent-layer] h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n }\n </div>\n <div class=\"w-3/5 md:w-3/4 ml-3 flex flex-col justify-between\">\n <div>\n <div class=\"md:text-xl font-medium break-words line-clamp-1 md:line-clamp-2 whitespace-normal\">\n {{ course().name }}\n </div>\n <div class=\"invisible h-0 md:h-auto md:visible break-words line-clamp-1 whitespace-normal\">\n {{ course().description }}\n </div>\n </div>\n @if (course().pricing) {\n <div class=\"md:text-lg font-medium py-1\">\u00A5{{ course().pricing[0].total / 100 }}</div>\n }\n </div>\n </div>\n} @else {\n <div>\n <div>\n @if (course().media; as media) {\n @defer (on viewport()) {\n <rolatech-thumbnail [src]=\"course().media[0].url + '!w400'\" size=\"medium\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-[--rt-10-percent-layer] w-full h-full object-cover aspect-video rounded-lg\"></div>\n }\n }\n </div>\n <div class=\"mt-2\">\n <div class=\"text-md md:text-xl font-medium break-words line-clamp-2\">{{ course().name }}</div>\n @if (course().pricing) {\n <div class=\"text-md md:text-lg font-medium py-1\">\u00A5{{ course().pricing[0].total / 100 }}</div>\n }\n </div>\n </div>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }], deferBlockDependencies: [() => [ThumbnailComponent], () => [ThumbnailComponent]] }); }
1024
+ }
1025
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseItemComponent, decorators: [{
1026
+ type: Component,
1027
+ args: [{ selector: 'rolatech-course-item', standalone: true, imports: [CommonModule, ImageComponent, ThumbnailComponent], template: "@if (row()) {\n <div class=\"flex w-full flex-row\" [style]=\"inset() ? 'padding: 0 16px' : ''\">\n <div class=\"h-fit w-2/5 md:w-1/4 aspect-video bg-[--rt-10-percent-layer] rounded-lg\">\n @if (course().media) {\n @defer (on viewport()) {\n <rolatech-thumbnail [src]=\"course().media[0].url + '!w400'\" size=\"medium\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-[--rt-10-percent-layer] h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n }\n </div>\n <div class=\"w-3/5 md:w-3/4 ml-3 flex flex-col justify-between\">\n <div>\n <div class=\"md:text-xl font-medium break-words line-clamp-1 md:line-clamp-2 whitespace-normal\">\n {{ course().name }}\n </div>\n <div class=\"invisible h-0 md:h-auto md:visible break-words line-clamp-1 whitespace-normal\">\n {{ course().description }}\n </div>\n </div>\n @if (course().pricing) {\n <div class=\"md:text-lg font-medium py-1\">\u00A5{{ course().pricing[0].total / 100 }}</div>\n }\n </div>\n </div>\n} @else {\n <div>\n <div>\n @if (course().media; as media) {\n @defer (on viewport()) {\n <rolatech-thumbnail [src]=\"course().media[0].url + '!w400'\" size=\"medium\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-[--rt-10-percent-layer] w-full h-full object-cover aspect-video rounded-lg\"></div>\n }\n }\n </div>\n <div class=\"mt-2\">\n <div class=\"text-md md:text-xl font-medium break-words line-clamp-2\">{{ course().name }}</div>\n @if (course().pricing) {\n <div class=\"text-md md:text-lg font-medium py-1\">\u00A5{{ course().pricing[0].total / 100 }}</div>\n }\n </div>\n </div>\n}\n" }]
1028
+ }] });
1029
+
1030
+ class PricingItemComponent {
1031
+ constructor() {
1032
+ this.actions = input(false);
1033
+ this.pricing = model.required();
1034
+ this.delete = output();
1035
+ this.save = output();
1036
+ }
1037
+ ngOnInit() {
1038
+ this.pricing().total = this.pricing().total / 100;
1039
+ }
1040
+ onSave(pricing) {
1041
+ const finalPricing = {
1042
+ ...pricing,
1043
+ total: pricing.total * 100,
1044
+ };
1045
+ this.save.emit(finalPricing);
1046
+ }
1047
+ onDelete(pricing) {
1048
+ this.delete.emit(pricing);
1049
+ }
1050
+ setTwoNumberDecimal(e) {
1051
+ this.pricing().total = e;
1052
+ }
1053
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: PricingItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1054
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: PricingItemComponent, isStandalone: true, selector: "rolatech-pricing-item", inputs: { actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, pricing: { classPropertyName: "pricing", publicName: "pricing", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { pricing: "pricingChange", delete: "delete", save: "save" }, ngImport: i0, template: "<div class=\"flex flex-col py-3\">\n <mat-form-field appearance=\"fill\">\n <mat-label> \u6700\u5C11\u4EBA\u6570 </mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"pricing().min\" name=\"min\" required />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label> \u6700\u591A\u4EBA\u6570 </mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"pricing().max\" name=\"max\" required />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label> \u4EF7\u683C </mat-label>\n <!-- <input matInput type=\"number\" step=\"0.01\" [ngModel]=\"pricing().total\"\n (ngModelChange)=\"setTwoNumberDecimal($event)\" name=\"total\" required placeholder=\"20.23\" /> -->\n <input matInput rolatechDecimal type=\"text\" [(ngModel)]=\"pricing().total\" name=\"total\" required />\n </mat-form-field>\n</div>\n@if (actions()) {\n <div class=\"flex flex-row justify-end p-3 gap-3\">\n <button mat-button class=\"max-h-8\" (click)=\"onDelete(pricing())\">\u5220\u9664</button>\n <button mat-flat-button class=\"max-h-8\" (click)=\"onSave(pricing())\">\u4FDD\u5B58</button>\n </div>\n}\n<mat-divider></mat-divider>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1$2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i5$1.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }] }); }
1055
+ }
1056
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: PricingItemComponent, decorators: [{
1057
+ type: Component,
1058
+ args: [{ selector: 'rolatech-pricing-item', standalone: true, imports: [NgClass, MatFormFieldModule, MatInputModule, FormsModule, MatButtonModule, MatDividerModule], template: "<div class=\"flex flex-col py-3\">\n <mat-form-field appearance=\"fill\">\n <mat-label> \u6700\u5C11\u4EBA\u6570 </mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"pricing().min\" name=\"min\" required />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label> \u6700\u591A\u4EBA\u6570 </mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"pricing().max\" name=\"max\" required />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label> \u4EF7\u683C </mat-label>\n <!-- <input matInput type=\"number\" step=\"0.01\" [ngModel]=\"pricing().total\"\n (ngModelChange)=\"setTwoNumberDecimal($event)\" name=\"total\" required placeholder=\"20.23\" /> -->\n <input matInput rolatechDecimal type=\"text\" [(ngModel)]=\"pricing().total\" name=\"total\" required />\n </mat-form-field>\n</div>\n@if (actions()) {\n <div class=\"flex flex-row justify-end p-3 gap-3\">\n <button mat-button class=\"max-h-8\" (click)=\"onDelete(pricing())\">\u5220\u9664</button>\n <button mat-flat-button class=\"max-h-8\" (click)=\"onSave(pricing())\">\u4FDD\u5B58</button>\n </div>\n}\n<mat-divider></mat-divider>\n" }]
1059
+ }], ctorParameters: () => [] });
1060
+
1061
+ class CoursePricingAddDialogComponent {
1062
+ constructor() {
1063
+ this.output = output();
1064
+ this.pricing = {
1065
+ min: 0,
1066
+ max: 0,
1067
+ total: 0,
1068
+ };
1069
+ }
1070
+ ngDoCheck() {
1071
+ this.output.emit(this.pricing);
1072
+ }
1073
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CoursePricingAddDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1074
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.4", type: CoursePricingAddDialogComponent, isStandalone: true, selector: "rolatech-course-pricing-add-dialog", outputs: { output: "output" }, ngImport: i0, template: "<rolatech-pricing-item [pricing]=\"pricing\"></rolatech-pricing-item>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: PricingItemComponent, selector: "rolatech-pricing-item", inputs: ["actions", "pricing"], outputs: ["pricingChange", "delete", "save"] }, { kind: "ngmodule", type: MatButtonModule }] }); }
1075
+ }
1076
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CoursePricingAddDialogComponent, decorators: [{
1077
+ type: Component,
1078
+ args: [{ selector: 'rolatech-course-pricing-add-dialog', standalone: true, imports: [MatIconModule, PricingItemComponent, MatButtonModule], template: "<rolatech-pricing-item [pricing]=\"pricing\"></rolatech-pricing-item>\n", styles: ["mat-form-field{width:100%}\n"] }]
1079
+ }] });
1080
+
1081
+ class CourseScheduleAddDialogComponent {
1082
+ constructor() {
1083
+ this.output = output();
1084
+ this.schedule = {
1085
+ title: '',
1086
+ content: '',
1087
+ startAt: '',
1088
+ endAt: '',
1089
+ };
1090
+ }
1091
+ ngDoCheck() {
1092
+ this.output.emit(this.schedule);
1093
+ }
1094
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseScheduleAddDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1095
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.4", type: CourseScheduleAddDialogComponent, isStandalone: true, selector: "rolatech-course-schedule-add-dialog", outputs: { output: "output" }, ngImport: i0, template: "<rolatech-schedule-item [value]=\"schedule\"></rolatech-schedule-item>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: ScheduleItemComponent, selector: "rolatech-schedule-item", inputs: ["value", "actions"], outputs: ["delete", "save"] }, { kind: "ngmodule", type: MatButtonModule }] }); }
1096
+ }
1097
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseScheduleAddDialogComponent, decorators: [{
1098
+ type: Component,
1099
+ args: [{ selector: 'rolatech-course-schedule-add-dialog', standalone: true, imports: [MatIconModule, ScheduleItemComponent, MatButtonModule], template: "<rolatech-schedule-item [value]=\"schedule\"></rolatech-schedule-item>\n" }]
1100
+ }] });
1101
+
1102
+ class CoursePricingDialogComponent {
1103
+ constructor(dialogRef, data, courseService, snackBar, dialog) {
1104
+ this.dialogRef = dialogRef;
1105
+ this.data = data;
1106
+ this.courseService = courseService;
1107
+ this.snackBar = snackBar;
1108
+ this.dialog = dialog;
1109
+ this.pricing = [];
1110
+ this.courseId = data.courseId;
1111
+ this.pricing = data.pricing;
1112
+ }
1113
+ close() {
1114
+ this.dialogRef.close();
1115
+ }
1116
+ addPricing() {
1117
+ this.pricing.push({
1118
+ min: 0,
1119
+ max: 0,
1120
+ total: 0,
1121
+ });
1122
+ }
1123
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CoursePricingDialogComponent, deps: [{ token: i1$3.MatDialogRef }, { token: MAT_DIALOG_DATA }, { token: CourseService }, { token: i3$3.MatSnackBar }, { token: i1$3.MatDialog }], target: i0.ɵɵFactoryTarget.Component }); }
1124
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CoursePricingDialogComponent, isStandalone: true, selector: "rolatech-course-pricing-dialog", ngImport: i0, template: "<div class=\"flex flex-col h-full overflow-hidden\">\n <div class=\"h-16 flex justify-between items-center px-5\">\n @if (data.title) {\n <div class=\"text-md font-medium\">{{ data.title }}</div>\n }\n <div class=\"cursor-pointer\" (click)=\"close()\">\n <mat-icon fontIcon=\"close\"></mat-icon>\n </div>\n </div>\n <mat-divider></mat-divider>\n <div class=\"flex-1 overflow-hidden mt-3 overflow-y-auto w-2/3 max-h-[55vh]\">\n <form #detailForm=\"ngForm\" class=\"p-3\">\n <ng-template matStepLabel>\u8BE6\u60C5</ng-template>\n @for (item of pricing; track item) {\n <rolatech-pricing-item [pricing]=\"item\"> </rolatech-pricing-item>\n }\n <div class=\"mb-6\">\n <button mat-button (click)=\"addPricing()\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u4EF7\u683C</span>\n </button>\n </div>\n </form>\n </div>\n <mat-divider></mat-divider>\n <div class=\"h-16 flex justify-end items-center px-5\">\n <button mat-button [mat-dialog-close]=\"pricing\" cdkFocusInitial>\u4FDD\u5B58</button>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i5$1.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatStepperModule }, { kind: "directive", type: i6$2.MatStepLabel, selector: "[matStepLabel]" }, { kind: "component", type: PricingItemComponent, selector: "rolatech-pricing-item", inputs: ["actions", "pricing"], outputs: ["pricingChange", "delete", "save"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }] }); }
1125
+ }
1126
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CoursePricingDialogComponent, decorators: [{
1127
+ type: Component,
1128
+ args: [{ selector: 'rolatech-course-pricing-dialog', standalone: true, imports: [
1129
+ MatIconModule,
1130
+ MatDividerModule,
1131
+ FormsModule,
1132
+ MatStepperModule,
1133
+ PricingItemComponent,
1134
+ MatButtonModule,
1135
+ MatDialogClose,
1136
+ ], template: "<div class=\"flex flex-col h-full overflow-hidden\">\n <div class=\"h-16 flex justify-between items-center px-5\">\n @if (data.title) {\n <div class=\"text-md font-medium\">{{ data.title }}</div>\n }\n <div class=\"cursor-pointer\" (click)=\"close()\">\n <mat-icon fontIcon=\"close\"></mat-icon>\n </div>\n </div>\n <mat-divider></mat-divider>\n <div class=\"flex-1 overflow-hidden mt-3 overflow-y-auto w-2/3 max-h-[55vh]\">\n <form #detailForm=\"ngForm\" class=\"p-3\">\n <ng-template matStepLabel>\u8BE6\u60C5</ng-template>\n @for (item of pricing; track item) {\n <rolatech-pricing-item [pricing]=\"item\"> </rolatech-pricing-item>\n }\n <div class=\"mb-6\">\n <button mat-button (click)=\"addPricing()\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u4EF7\u683C</span>\n </button>\n </div>\n </form>\n </div>\n <mat-divider></mat-divider>\n <div class=\"h-16 flex justify-end items-center px-5\">\n <button mat-button [mat-dialog-close]=\"pricing\" cdkFocusInitial>\u4FDD\u5B58</button>\n </div>\n</div>\n" }]
1137
+ }], ctorParameters: () => [{ type: i1$3.MatDialogRef }, { type: undefined, decorators: [{
1138
+ type: Inject,
1139
+ args: [MAT_DIALOG_DATA]
1140
+ }] }, { type: CourseService }, { type: i3$3.MatSnackBar }, { type: i1$3.MatDialog }] });
1141
+
1142
+ class CourseScheduleDialogComponent {
1143
+ constructor(dialogRef, data) {
1144
+ this.dialogRef = dialogRef;
1145
+ this.data = data;
1146
+ this.schedule = [];
1147
+ this.courseId = data.courseId;
1148
+ this.schedule = data.schedule;
1149
+ }
1150
+ close() {
1151
+ this.dialogRef.close();
1152
+ }
1153
+ addSchedule() {
1154
+ if (!this.schedule) {
1155
+ this.schedule = [];
1156
+ }
1157
+ this.schedule.push({
1158
+ title: '',
1159
+ content: '',
1160
+ startAt: '',
1161
+ endAt: '',
1162
+ });
1163
+ }
1164
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseScheduleDialogComponent, deps: [{ token: i1$3.MatDialogRef }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component }); }
1165
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseScheduleDialogComponent, isStandalone: true, selector: "rolatech-course-schedule-dialog", ngImport: i0, template: "<div class=\"flex flex-col h-full overflow-hidden\">\n <div class=\"h-16 flex justify-between items-center px-5\">\n @if (data.title) {\n <div class=\"text-md font-medium\">{{ data.title }}</div>\n }\n <div class=\"cursor-pointer\" (click)=\"close()\">\n <mat-icon fontIcon=\"close\"></mat-icon>\n </div>\n </div>\n <mat-divider></mat-divider>\n <div class=\"flex-1 overflow-hidden mt-3 overflow-y-auto\">\n <form #detailForm=\"ngForm\" class=\"p-3\">\n <ng-template matStepLabel>\u8BE6\u60C5</ng-template>\n @for (item of schedule; track item) {\n <rolatech-schedule-item [value]=\"item\"></rolatech-schedule-item>\n }\n <div class=\"mb-6\">\n <button mat-button (click)=\"addSchedule()\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u8BFE\u8868</span>\n </button>\n </div>\n </form>\n </div>\n <mat-divider></mat-divider>\n <div class=\"h-16 flex justify-end items-center px-5\">\n <button mat-button [mat-dialog-close]=\"schedule\" cdkFocusInitial>\u4FDD\u5B58</button>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i5$1.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatStepperModule }, { kind: "directive", type: i6$2.MatStepLabel, selector: "[matStepLabel]" }, { kind: "component", type: ScheduleItemComponent, selector: "rolatech-schedule-item", inputs: ["value", "actions"], outputs: ["delete", "save"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }] }); }
1166
+ }
1167
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseScheduleDialogComponent, decorators: [{
1168
+ type: Component,
1169
+ args: [{ selector: 'rolatech-course-schedule-dialog', standalone: true, imports: [
1170
+ MatIconModule,
1171
+ MatDividerModule,
1172
+ FormsModule,
1173
+ MatStepperModule,
1174
+ ScheduleItemComponent,
1175
+ MatButtonModule,
1176
+ MatDialogClose,
1177
+ ], template: "<div class=\"flex flex-col h-full overflow-hidden\">\n <div class=\"h-16 flex justify-between items-center px-5\">\n @if (data.title) {\n <div class=\"text-md font-medium\">{{ data.title }}</div>\n }\n <div class=\"cursor-pointer\" (click)=\"close()\">\n <mat-icon fontIcon=\"close\"></mat-icon>\n </div>\n </div>\n <mat-divider></mat-divider>\n <div class=\"flex-1 overflow-hidden mt-3 overflow-y-auto\">\n <form #detailForm=\"ngForm\" class=\"p-3\">\n <ng-template matStepLabel>\u8BE6\u60C5</ng-template>\n @for (item of schedule; track item) {\n <rolatech-schedule-item [value]=\"item\"></rolatech-schedule-item>\n }\n <div class=\"mb-6\">\n <button mat-button (click)=\"addSchedule()\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u8BFE\u8868</span>\n </button>\n </div>\n </form>\n </div>\n <mat-divider></mat-divider>\n <div class=\"h-16 flex justify-end items-center px-5\">\n <button mat-button [mat-dialog-close]=\"schedule\" cdkFocusInitial>\u4FDD\u5B58</button>\n </div>\n</div>\n" }]
1178
+ }], ctorParameters: () => [{ type: i1$3.MatDialogRef }, { type: undefined, decorators: [{
1179
+ type: Inject,
1180
+ args: [MAT_DIALOG_DATA]
1181
+ }] }] });
1182
+
1183
+ class DetailItemComponent {
1184
+ constructor() {
1185
+ this.isUploading = input();
1186
+ this.detail = input.required();
1187
+ this.actions = input(false);
1188
+ this.selectMedia = input();
1189
+ this.upload = output();
1190
+ this.delete = output();
1191
+ this.save = output();
1192
+ this.deleteMedia = output();
1193
+ }
1194
+ ngOnInit() {
1195
+ this.selectedImg = this.detail().media ? this.detail().media[0] : null;
1196
+ }
1197
+ onUpload(id, data) {
1198
+ this.upload.emit({ id, data });
1199
+ }
1200
+ onMediaItemClick(image) {
1201
+ this.selectedImg = image;
1202
+ }
1203
+ select(item) {
1204
+ this.selectedImg = item;
1205
+ }
1206
+ deleteImage() { }
1207
+ onSave(detail) {
1208
+ this.save.emit(detail);
1209
+ }
1210
+ onDelete(detail) {
1211
+ this.delete.emit(detail);
1212
+ }
1213
+ onDeleteMedia(id, media) {
1214
+ this.deleteMedia.emit({ id, media });
1215
+ this.selectedImg = this.detail().media ? first(this.detail().media) : null;
1216
+ }
1217
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: DetailItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1218
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: DetailItemComponent, isStandalone: true, selector: "rolatech-detail-item", inputs: { isUploading: { classPropertyName: "isUploading", publicName: "isUploading", isSignal: true, isRequired: false, transformFunction: null }, detail: { classPropertyName: "detail", publicName: "detail", isSignal: true, isRequired: true, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, selectMedia: { classPropertyName: "selectMedia", publicName: "selectMedia", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { upload: "upload", delete: "delete", save: "save", deleteMedia: "deleteMedia" }, ngImport: i0, template: "<div class=\"mb-8\">\n <div class=\"flex flex-col lg:flex-row\">\n <div class=\"flex flex-col grow\">\n <mat-form-field appearance=\"fill\">\n <mat-label>\u6807\u9898</mat-label>\n <input matInput placeholder=\"\u6807\u9898\" [(ngModel)]=\"detail().title\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" [hideRequiredMarker]=\"true\">\n <mat-label>\u63CF\u8FF0</mat-label>\n <input matInput placeholder=\"\u63CF\u8FF0\" [(ngModel)]=\"detail().description\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" [hideRequiredMarker]=\"true\">\n <mat-label>\u5185\u5BB9</mat-label>\n <textarea\n matInput\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"\n placeholder=\"\u5185\u5BB9\"\n [(ngModel)]=\"detail().content\"\n ></textarea>\n </mat-form-field>\n </div>\n <!-- image -->\n <div class=\"lg:basis-1/2 px-3\">\n <div class=\"flex flex-row justify-center pb-4 p-1\">\n @if (selectedImg) {\n <img class=\"object-contain w-80 h-80\" [src]=\"selectedImg.url\" [alt]=\"selectedImg.alt\" />\n }\n @if (selectedImg) {\n <button mat-icon-button (click)=\"onDeleteMedia(detail().id, selectedImg)\">\n <mat-icon>delete</mat-icon>\n </button>\n }\n </div>\n <!-- media -->\n <div>\n <div class=\"flex flex-row flex-wrap cursor-pointer relative box-border\" fxLayout=\"row\">\n <div class=\"progress-bar\">\n @if (detail().isUploading) {\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n }\n </div>\n @for (media of detail().media; track media) {\n <div class=\"media-list-item\">\n <img class=\"tile-media\" (click)=\"onMediaItemClick(media)\" [src]=\"media.url\" [alt]=\"media.alt\" />\n </div>\n }\n <input style=\"display: none\" type=\"file\" accept=\"image/*\" (change)=\"onUpload(detail().id, $event)\" #fileInput />\n <div class=\"add-button\">\n <div (click)=\"fileInput.click()\" class=\"tile-media flex justify-center items-center\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"48px\" viewBox=\"0 -960 960 960\" width=\"48px\" fill=\"#5f6368\">\n <path d=\"M444-444H240v-72h204v-204h72v204h204v72H516v204h-72v-204Z\" />\n </svg>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n@if (actions()) {\n <div class=\"flex flex-row justify-end p-3 gap-3\">\n <button mat-button class=\"max-h-8\" (click)=\"onDelete(detail())\">\u5220\u9664</button>\n <button mat-flat-button class=\"max-h-8\" (click)=\"onSave(detail())\">\u4FDD\u5B58</button>\n </div>\n}\n<mat-divider></mat-divider>\n", styles: [".media-list{flex-wrap:wrap;box-sizing:border-box}.progress-bar{display:block;min-height:6px;width:100%}.media-list-item{cursor:pointer;position:relative;box-sizing:border-box;padding:2px}.tile-media{height:80px;width:80px;object-fit:contain;cursor:pointer;border:1px solid grey;padding:6px;position:relative;box-sizing:border-box}.add-button{cursor:pointer;position:relative;box-sizing:border-box;padding:2px}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1$2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "directive", type: i3.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i1$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i3$2.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i5$1.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }] }); }
1219
+ }
1220
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: DetailItemComponent, decorators: [{
1221
+ type: Component,
1222
+ args: [{ selector: 'rolatech-detail-item', standalone: true, imports: [
1223
+ NgClass,
1224
+ MatFormFieldModule,
1225
+ MatInputModule,
1226
+ FormsModule,
1227
+ TextFieldModule,
1228
+ MatButtonModule,
1229
+ MatIconModule,
1230
+ MatProgressBarModule,
1231
+ MatDividerModule,
1232
+ ], template: "<div class=\"mb-8\">\n <div class=\"flex flex-col lg:flex-row\">\n <div class=\"flex flex-col grow\">\n <mat-form-field appearance=\"fill\">\n <mat-label>\u6807\u9898</mat-label>\n <input matInput placeholder=\"\u6807\u9898\" [(ngModel)]=\"detail().title\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" [hideRequiredMarker]=\"true\">\n <mat-label>\u63CF\u8FF0</mat-label>\n <input matInput placeholder=\"\u63CF\u8FF0\" [(ngModel)]=\"detail().description\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" [hideRequiredMarker]=\"true\">\n <mat-label>\u5185\u5BB9</mat-label>\n <textarea\n matInput\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"\n placeholder=\"\u5185\u5BB9\"\n [(ngModel)]=\"detail().content\"\n ></textarea>\n </mat-form-field>\n </div>\n <!-- image -->\n <div class=\"lg:basis-1/2 px-3\">\n <div class=\"flex flex-row justify-center pb-4 p-1\">\n @if (selectedImg) {\n <img class=\"object-contain w-80 h-80\" [src]=\"selectedImg.url\" [alt]=\"selectedImg.alt\" />\n }\n @if (selectedImg) {\n <button mat-icon-button (click)=\"onDeleteMedia(detail().id, selectedImg)\">\n <mat-icon>delete</mat-icon>\n </button>\n }\n </div>\n <!-- media -->\n <div>\n <div class=\"flex flex-row flex-wrap cursor-pointer relative box-border\" fxLayout=\"row\">\n <div class=\"progress-bar\">\n @if (detail().isUploading) {\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n }\n </div>\n @for (media of detail().media; track media) {\n <div class=\"media-list-item\">\n <img class=\"tile-media\" (click)=\"onMediaItemClick(media)\" [src]=\"media.url\" [alt]=\"media.alt\" />\n </div>\n }\n <input style=\"display: none\" type=\"file\" accept=\"image/*\" (change)=\"onUpload(detail().id, $event)\" #fileInput />\n <div class=\"add-button\">\n <div (click)=\"fileInput.click()\" class=\"tile-media flex justify-center items-center\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"48px\" viewBox=\"0 -960 960 960\" width=\"48px\" fill=\"#5f6368\">\n <path d=\"M444-444H240v-72h204v-204h72v204h204v72H516v204h-72v-204Z\" />\n </svg>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n@if (actions()) {\n <div class=\"flex flex-row justify-end p-3 gap-3\">\n <button mat-button class=\"max-h-8\" (click)=\"onDelete(detail())\">\u5220\u9664</button>\n <button mat-flat-button class=\"max-h-8\" (click)=\"onSave(detail())\">\u4FDD\u5B58</button>\n </div>\n}\n<mat-divider></mat-divider>\n", styles: [".media-list{flex-wrap:wrap;box-sizing:border-box}.progress-bar{display:block;min-height:6px;width:100%}.media-list-item{cursor:pointer;position:relative;box-sizing:border-box;padding:2px}.tile-media{height:80px;width:80px;object-fit:contain;cursor:pointer;border:1px solid grey;padding:6px;position:relative;box-sizing:border-box}.add-button{cursor:pointer;position:relative;box-sizing:border-box;padding:2px}\n"] }]
1233
+ }], ctorParameters: () => [] });
1234
+
1235
+ class CourseDetailsDialogComponent {
1236
+ constructor(dialogRef, data, courseService, snackBar, dialog) {
1237
+ this.dialogRef = dialogRef;
1238
+ this.data = data;
1239
+ this.courseService = courseService;
1240
+ this.snackBar = snackBar;
1241
+ this.dialog = dialog;
1242
+ this.details = [];
1243
+ this.courseId = data.courseId;
1244
+ }
1245
+ close() {
1246
+ this.dialogRef.close();
1247
+ }
1248
+ addDetail() {
1249
+ this.courseService
1250
+ .addDetail(this.courseId, {
1251
+ title: '',
1252
+ description: '',
1253
+ content: '',
1254
+ media: [],
1255
+ })
1256
+ .subscribe({
1257
+ next: (res) => {
1258
+ const detail = res.data;
1259
+ this.details.push({
1260
+ id: detail.id,
1261
+ title: '',
1262
+ description: '',
1263
+ content: '',
1264
+ media: [],
1265
+ });
1266
+ },
1267
+ error: (e) => {
1268
+ this.snackBar.open(e.message);
1269
+ },
1270
+ });
1271
+ }
1272
+ onDetailMediaUpload(event) { }
1273
+ onDetailMediaDelete(event) { }
1274
+ onDetailSave(event) { }
1275
+ onDetailDelete(detail) {
1276
+ this.courseService.deleteDetail(this.courseId, detail.id).subscribe({
1277
+ next: (res) => {
1278
+ this.snackBar.open(res.data);
1279
+ remove(this.details, {
1280
+ id: detail.id,
1281
+ });
1282
+ },
1283
+ error: (e) => {
1284
+ this.snackBar.open(e.message);
1285
+ },
1286
+ });
1287
+ }
1288
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseDetailsDialogComponent, deps: [{ token: i1$3.MatDialogRef }, { token: MAT_DIALOG_DATA }, { token: CourseService }, { token: i3$3.MatSnackBar }, { token: i1$3.MatDialog }], target: i0.ɵɵFactoryTarget.Component }); }
1289
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseDetailsDialogComponent, isStandalone: true, selector: "rolatech-course-details-dialog", ngImport: i0, template: "<div class=\"flex flex-col h-full overflow-hidden\">\n <div class=\"h-16 flex justify-between items-center px-5\">\n @if (data.title) {\n <div class=\"text-md font-medium\">{{ data.title }}</div>\n }\n <div class=\"cursor-pointer\" (click)=\"close()\">\n <mat-icon fontIcon=\"close\"></mat-icon>\n </div>\n </div>\n <mat-divider></mat-divider>\n <div class=\"flex-1 overflow-hidden mt-3 overflow-y-auto\">\n <form #detailForm=\"ngForm\" class=\"p-3\">\n <ng-template matStepLabel>\u8BE6\u60C5</ng-template>\n @for (item of details; track item) {\n <rolatech-detail-item\n [detail]=\"item\"\n (upload)=\"onDetailMediaUpload($event)\"\n (deleteMedia)=\"onDetailMediaDelete($event)\"\n (save)=\"onDetailSave($event)\"\n (delete)=\"onDetailDelete($event)\"\n ></rolatech-detail-item>\n }\n <div class=\"mb-6\">\n <button mat-button (click)=\"addDetail()\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u8BE6\u60C5</span>\n </button>\n </div>\n </form>\n </div>\n <mat-divider></mat-divider>\n <div class=\"h-16 flex justify-end items-center px-5\">\n <button mat-button [mat-dialog-close]=\"true\" cdkFocusInitial>\u4FDD\u5B58</button>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i5$1.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatStepperModule }, { kind: "directive", type: i6$2.MatStepLabel, selector: "[matStepLabel]" }, { kind: "component", type: DetailItemComponent, selector: "rolatech-detail-item", inputs: ["isUploading", "detail", "actions", "selectMedia"], outputs: ["upload", "delete", "save", "deleteMedia"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }] }); }
1290
+ }
1291
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseDetailsDialogComponent, decorators: [{
1292
+ type: Component,
1293
+ args: [{ selector: 'rolatech-course-details-dialog', standalone: true, imports: [
1294
+ MatIconModule,
1295
+ MatDividerModule,
1296
+ FormsModule,
1297
+ MatStepperModule,
1298
+ DetailItemComponent,
1299
+ MatButtonModule,
1300
+ MatDialogClose,
1301
+ ], template: "<div class=\"flex flex-col h-full overflow-hidden\">\n <div class=\"h-16 flex justify-between items-center px-5\">\n @if (data.title) {\n <div class=\"text-md font-medium\">{{ data.title }}</div>\n }\n <div class=\"cursor-pointer\" (click)=\"close()\">\n <mat-icon fontIcon=\"close\"></mat-icon>\n </div>\n </div>\n <mat-divider></mat-divider>\n <div class=\"flex-1 overflow-hidden mt-3 overflow-y-auto\">\n <form #detailForm=\"ngForm\" class=\"p-3\">\n <ng-template matStepLabel>\u8BE6\u60C5</ng-template>\n @for (item of details; track item) {\n <rolatech-detail-item\n [detail]=\"item\"\n (upload)=\"onDetailMediaUpload($event)\"\n (deleteMedia)=\"onDetailMediaDelete($event)\"\n (save)=\"onDetailSave($event)\"\n (delete)=\"onDetailDelete($event)\"\n ></rolatech-detail-item>\n }\n <div class=\"mb-6\">\n <button mat-button (click)=\"addDetail()\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u8BE6\u60C5</span>\n </button>\n </div>\n </form>\n </div>\n <mat-divider></mat-divider>\n <div class=\"h-16 flex justify-end items-center px-5\">\n <button mat-button [mat-dialog-close]=\"true\" cdkFocusInitial>\u4FDD\u5B58</button>\n </div>\n</div>\n" }]
1302
+ }], ctorParameters: () => [{ type: i1$3.MatDialogRef }, { type: undefined, decorators: [{
1303
+ type: Inject,
1304
+ args: [MAT_DIALOG_DATA]
1305
+ }] }, { type: CourseService }, { type: i3$3.MatSnackBar }, { type: i1$3.MatDialog }] });
1306
+
1307
+ class CourseEditDialogComponent {
1308
+ constructor(dialogRef, data, categoryService, courseService, snackBar, dialog) {
1309
+ this.dialogRef = dialogRef;
1310
+ this.data = data;
1311
+ this.categoryService = categoryService;
1312
+ this.courseService = courseService;
1313
+ this.snackBar = snackBar;
1314
+ this.dialog = dialog;
1315
+ this.isUploading = false;
1316
+ this.courseType = [
1317
+ {
1318
+ key: 'ONLINE',
1319
+ value: '线上',
1320
+ },
1321
+ {
1322
+ key: 'OFFLINE',
1323
+ value: '线下',
1324
+ },
1325
+ {
1326
+ key: 'MIXED',
1327
+ value: '混合',
1328
+ },
1329
+ ];
1330
+ this.date = ScheduleDate;
1331
+ this.lastStepper = false;
1332
+ this.firstStepper = true;
1333
+ this.stepper = viewChild.required(MatStepper);
1334
+ this.course = data.course;
1335
+ this.classrooms = data.classrooms;
1336
+ }
1337
+ ngOnInit() {
1338
+ this.categoryService.find({}).subscribe({
1339
+ next: (res) => {
1340
+ this.categories = res.data;
1341
+ },
1342
+ });
1343
+ }
1344
+ close() {
1345
+ this.dialogRef.close();
1346
+ }
1347
+ compareFn(o1, o2) {
1348
+ return o1.id === o2.id;
1349
+ }
1350
+ addDetail() {
1351
+ if (!this.course.details) {
1352
+ this.course.details = [];
1353
+ }
1354
+ this.courseService
1355
+ .addDetail(this.course.id, {
1356
+ title: '',
1357
+ description: '',
1358
+ content: '',
1359
+ media: [],
1360
+ })
1361
+ .subscribe({
1362
+ next: (res) => {
1363
+ const detail = res.data;
1364
+ this.course.details.push({
1365
+ id: detail.id,
1366
+ title: '',
1367
+ description: '',
1368
+ content: '',
1369
+ media: [],
1370
+ });
1371
+ },
1372
+ error: (e) => {
1373
+ this.snackBar.open(e.message);
1374
+ },
1375
+ });
1376
+ }
1377
+ addSchedule() {
1378
+ if (!this.course.schedule) {
1379
+ this.course.schedule = [];
1380
+ }
1381
+ this.course.schedule.push({
1382
+ title: '',
1383
+ content: '',
1384
+ });
1385
+ }
1386
+ addPricing() {
1387
+ if (!this.course.pricing) {
1388
+ this.course.pricing = [];
1389
+ }
1390
+ this.course.pricing.push({
1391
+ min: 0,
1392
+ max: 0,
1393
+ total: 0,
1394
+ });
1395
+ }
1396
+ onSelectionChange(event) {
1397
+ this.selectedCategoyIds = event.value;
1398
+ }
1399
+ onStepperChange(event) {
1400
+ this.lastStepper = event.selectedIndex === 3;
1401
+ this.firstStepper = event.selectedIndex === 0;
1402
+ }
1403
+ previous() {
1404
+ this.stepper().previous();
1405
+ }
1406
+ next() {
1407
+ this.stepper().next();
1408
+ }
1409
+ onDetailMediaUpload(event) { }
1410
+ onDetailMediaDelete(event) { }
1411
+ onDetailSave(event) { }
1412
+ onDetailDelete(detail) {
1413
+ this.courseService.deleteDetail(this.course.id, detail.id).subscribe({
1414
+ next: (res) => {
1415
+ this.snackBar.open(res.message);
1416
+ remove(this.details, {
1417
+ id: detail.id,
1418
+ });
1419
+ },
1420
+ error: (e) => {
1421
+ this.snackBar.open(e.message);
1422
+ },
1423
+ });
1424
+ }
1425
+ onImageClick(i) {
1426
+ const dialogRef = this.dialog.open(ImagePreviewDialogComponent, {
1427
+ maxWidth: '80vw',
1428
+ maxHeight: '80vh',
1429
+ height: '80%',
1430
+ width: '80%',
1431
+ panelClass: 'full-screen-modal',
1432
+ data: {
1433
+ media: this.course.media,
1434
+ selected: i,
1435
+ },
1436
+ });
1437
+ }
1438
+ onUpload(event) {
1439
+ const file = event.target.files[0];
1440
+ if (file) {
1441
+ const reader = new FileReader();
1442
+ reader.readAsDataURL(file);
1443
+ reader.onload = () => {
1444
+ if (this.course.media) {
1445
+ this.course.media.push({
1446
+ url: reader.result,
1447
+ alt: 'upload image',
1448
+ });
1449
+ }
1450
+ else {
1451
+ this.course.media = [];
1452
+ this.course.media.push({
1453
+ url: reader.result,
1454
+ alt: 'upload image',
1455
+ });
1456
+ }
1457
+ this.isUploading = true;
1458
+ const formData = new FormData();
1459
+ formData.append('file', file);
1460
+ this.courseService.upload(formData).subscribe({
1461
+ next: (res) => {
1462
+ this.isUploading = false;
1463
+ const index = findLastIndex(this.course.media);
1464
+ // Replace item at index using native splice
1465
+ this.course.media.splice(index, 1, res.data);
1466
+ },
1467
+ error: (e) => {
1468
+ this.isUploading = false;
1469
+ this.snackBar.open('上传失败: ' + e.message);
1470
+ },
1471
+ });
1472
+ };
1473
+ reader.onerror = (error) => {
1474
+ this.isUploading = false;
1475
+ };
1476
+ }
1477
+ }
1478
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseEditDialogComponent, deps: [{ token: i1$3.MatDialogRef }, { token: MAT_DIALOG_DATA }, { token: CategoryService }, { token: CourseService }, { token: i3$3.MatSnackBar }, { token: i1$3.MatDialog }], target: i0.ɵɵFactoryTarget.Component }); }
1479
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseEditDialogComponent, isStandalone: true, selector: "rolatech-course-edit-dialog", viewQueries: [{ propertyName: "stepper", first: true, predicate: MatStepper, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"flex flex-col h-full overflow-hidden\">\n <div class=\"h-16 flex justify-between items-center px-5\">\n @if (data.title) {\n <div class=\"text-md font-medium\">{{ data.title }}</div>\n }\n <div class=\"cursor-pointer\" (click)=\"close()\">\n <mat-icon fontIcon=\"close\"></mat-icon>\n </div>\n </div>\n <mat-divider></mat-divider>\n <div class=\"flex-1 overflow-hidden mt-3 overflow-y-auto\">\n <mat-stepper #stepper (selectionChange)=\"onStepperChange($event)\">\n <mat-step [editable]=\"true\">\n <form #infoForm=\"ngForm\" class=\"py-3\">\n <ng-template matStepLabel>\u57FA\u672C\u4FE1\u606F</ng-template>\n <div class=\"flex flex-col\">\n <mat-form-field appearance=\"fill\">\n <mat-label> \u8BFE\u7A0B\u540D\u79F0 </mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"course.name\" name=\"name\" required />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label> \u8BFE\u7A0B\u63CF\u8FF0 </mat-label>\n <textarea\n matInput\n type=\"text\"\n [(ngModel)]=\"course.description\"\n name=\"description\"\n required\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"\n ></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"fill\" ngModelGroup=\"categories\">\n <mat-label>\u5206\u7C7B</mat-label>\n <mat-select\n multiple\n name=\"id\"\n [compareWith]=\"compareFn\"\n [(ngModel)]=\"course.categories\"\n #select=\"matSelect\"\n (selectionChange)=\"onSelectionChange($event)\"\n >\n @for (item of categories; track item) {\n <mat-option [value]=\"item\">\n {{ item.name }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label>\u7C7B\u578B</mat-label>\n <mat-select name=\"type\" [(ngModel)]=\"course.type\">\n @for (item of courseType; track item) {\n <mat-option [value]=\"item.key\">\n {{ item.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n @if (course.type.toString() !== 'ONLINE') {\n <mat-form-field appearance=\"fill\" ngModelGroup=\"classroom\">\n <mat-label>\u6559\u5BA4</mat-label>\n <mat-select [(ngModel)]=\"course.classroom\" name=\"id\">\n @for (classroom of classrooms; track classroom) {\n <mat-option [value]=\"classroom.id\">\n <div class=\"flex gap-3\">\n <span>{{ classroom.name }}</span>\n <span> {{ classroom.address }}</span>\n </div>\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n </div>\n <div>\n <div class=\"py-3\">\n <h2>\u56FE\u7247\u4FE1\u606F</h2>\n </div>\n <rolatech-media-list (upload)=\"onUpload($event)\" [isUploading]=\"isUploading\">\n @for (item of course.media; track item; let i = $index) {\n <rolatech-media-list-item [media]=\"item\" (mediaItemClick)=\"onImageClick(i)\"></rolatech-media-list-item>\n }\n </rolatech-media-list>\n </div>\n </form>\n </mat-step>\n <mat-step [editable]=\"true\">\n <form #detailForm=\"ngForm\" class=\"py-3\">\n <ng-template matStepLabel>\u8BE6\u60C5</ng-template>\n @for (item of course.details; track item) {\n <rolatech-detail-item\n [detail]=\"item\"\n (upload)=\"onDetailMediaUpload($event)\"\n (deleteMedia)=\"onDetailMediaDelete($event)\"\n (save)=\"onDetailSave($event)\"\n (delete)=\"onDetailDelete($event)\"\n ></rolatech-detail-item>\n }\n <div class=\"mb-6\">\n <button mat-button (click)=\"addDetail()\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u8BE6\u60C5</span>\n </button>\n </div>\n </form>\n </mat-step>\n <mat-step [editable]=\"true\">\n <form #pricingForm=\"ngForm\" class=\"py-3\">\n <ng-template matStepLabel>\u4EF7\u683C</ng-template>\n <div>\n @for (item of course.pricing; track item) {\n <rolatech-pricing-item [pricing]=\"item\"> </rolatech-pricing-item>\n }\n <div class=\"mb-6\">\n <button mat-button (click)=\"addPricing()\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u4EF7\u683C</span>\n </button>\n </div>\n </div>\n </form>\n </mat-step>\n <mat-step>\n <form #scheduleForm=\"ngForm\">\n <ng-template matStepLabel>\u8BFE\u8868</ng-template>\n <div class=\"flex flex-col\">\n @for (item of course.schedule; track item) {\n <rolatech-schedule-item [value]=\"item\"></rolatech-schedule-item>\n }\n <div class=\"mb-6\">\n <button mat-button (click)=\"addSchedule()\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u8BFE\u8868</span>\n </button>\n </div>\n </div>\n </form>\n </mat-step>\n </mat-stepper>\n </div>\n <mat-divider></mat-divider>\n <div class=\"h-16 flex justify-end items-center px-5\">\n @if (!firstStepper) {\n <button mat-button mat-button (click)=\"previous()\">\u4E0A\u4E00\u6B65</button>\n }\n @if (!lastStepper) {\n <button mat-button (click)=\"next()\">\u4E0B\u4E00\u6B65</button>\n }\n @if (lastStepper) {\n <button mat-button [mat-dialog-close]=\"course\" cdkFocusInitial>\u4FDD\u5B58</button>\n }\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i5$1.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatStepperModule }, { kind: "component", type: i6$2.MatStep, selector: "mat-step", inputs: ["color"], exportAs: ["matStep"] }, { kind: "directive", type: i6$2.MatStepLabel, selector: "[matStepLabel]" }, { kind: "component", type: i6$2.MatStepper, selector: "mat-stepper, mat-vertical-stepper, mat-horizontal-stepper, [matStepper]", inputs: ["disableRipple", "color", "labelPosition", "headerPosition", "animationDuration"], outputs: ["animationDone"], exportAs: ["matStepper", "matVerticalStepper", "matHorizontalStepper"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i4.NgModelGroup, selector: "[ngModelGroup]", inputs: ["ngModelGroup"], exportAs: ["ngModelGroup"] }, { kind: "directive", type: i4.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1$2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "directive", type: i3.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "component", type: MediaListComponent, selector: "rolatech-media-list", inputs: ["isUploading", "media", "showAdd"], outputs: ["mediaItemClick", "upload"] }, { kind: "component", type: MediaListItemComponent, selector: "rolatech-media-list-item", inputs: ["media", "uploadProgress"], outputs: ["mediaItemClick", "deleteMedia"] }, { kind: "component", type: DetailItemComponent, selector: "rolatech-detail-item", inputs: ["isUploading", "detail", "actions", "selectMedia"], outputs: ["upload", "delete", "save", "deleteMedia"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: PricingItemComponent, selector: "rolatech-pricing-item", inputs: ["actions", "pricing"], outputs: ["pricingChange", "delete", "save"] }, { kind: "component", type: ScheduleItemComponent, selector: "rolatech-schedule-item", inputs: ["value", "actions"], outputs: ["delete", "save"] }, { kind: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }] }); }
1480
+ }
1481
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseEditDialogComponent, decorators: [{
1482
+ type: Component,
1483
+ args: [{ selector: 'rolatech-course-edit-dialog', standalone: true, imports: [
1484
+ MatIconModule,
1485
+ MatDividerModule,
1486
+ MatStepperModule,
1487
+ FormsModule,
1488
+ MatFormFieldModule,
1489
+ MatInputModule,
1490
+ TextFieldModule,
1491
+ MatSelectModule,
1492
+ MatOptionModule,
1493
+ MediaListComponent,
1494
+ MediaListItemComponent,
1495
+ DetailItemComponent,
1496
+ MatButtonModule,
1497
+ PricingItemComponent,
1498
+ ScheduleItemComponent,
1499
+ MatDialogClose,
1500
+ ], template: "<div class=\"flex flex-col h-full overflow-hidden\">\n <div class=\"h-16 flex justify-between items-center px-5\">\n @if (data.title) {\n <div class=\"text-md font-medium\">{{ data.title }}</div>\n }\n <div class=\"cursor-pointer\" (click)=\"close()\">\n <mat-icon fontIcon=\"close\"></mat-icon>\n </div>\n </div>\n <mat-divider></mat-divider>\n <div class=\"flex-1 overflow-hidden mt-3 overflow-y-auto\">\n <mat-stepper #stepper (selectionChange)=\"onStepperChange($event)\">\n <mat-step [editable]=\"true\">\n <form #infoForm=\"ngForm\" class=\"py-3\">\n <ng-template matStepLabel>\u57FA\u672C\u4FE1\u606F</ng-template>\n <div class=\"flex flex-col\">\n <mat-form-field appearance=\"fill\">\n <mat-label> \u8BFE\u7A0B\u540D\u79F0 </mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"course.name\" name=\"name\" required />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label> \u8BFE\u7A0B\u63CF\u8FF0 </mat-label>\n <textarea\n matInput\n type=\"text\"\n [(ngModel)]=\"course.description\"\n name=\"description\"\n required\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"\n ></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"fill\" ngModelGroup=\"categories\">\n <mat-label>\u5206\u7C7B</mat-label>\n <mat-select\n multiple\n name=\"id\"\n [compareWith]=\"compareFn\"\n [(ngModel)]=\"course.categories\"\n #select=\"matSelect\"\n (selectionChange)=\"onSelectionChange($event)\"\n >\n @for (item of categories; track item) {\n <mat-option [value]=\"item\">\n {{ item.name }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label>\u7C7B\u578B</mat-label>\n <mat-select name=\"type\" [(ngModel)]=\"course.type\">\n @for (item of courseType; track item) {\n <mat-option [value]=\"item.key\">\n {{ item.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n @if (course.type.toString() !== 'ONLINE') {\n <mat-form-field appearance=\"fill\" ngModelGroup=\"classroom\">\n <mat-label>\u6559\u5BA4</mat-label>\n <mat-select [(ngModel)]=\"course.classroom\" name=\"id\">\n @for (classroom of classrooms; track classroom) {\n <mat-option [value]=\"classroom.id\">\n <div class=\"flex gap-3\">\n <span>{{ classroom.name }}</span>\n <span> {{ classroom.address }}</span>\n </div>\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n </div>\n <div>\n <div class=\"py-3\">\n <h2>\u56FE\u7247\u4FE1\u606F</h2>\n </div>\n <rolatech-media-list (upload)=\"onUpload($event)\" [isUploading]=\"isUploading\">\n @for (item of course.media; track item; let i = $index) {\n <rolatech-media-list-item [media]=\"item\" (mediaItemClick)=\"onImageClick(i)\"></rolatech-media-list-item>\n }\n </rolatech-media-list>\n </div>\n </form>\n </mat-step>\n <mat-step [editable]=\"true\">\n <form #detailForm=\"ngForm\" class=\"py-3\">\n <ng-template matStepLabel>\u8BE6\u60C5</ng-template>\n @for (item of course.details; track item) {\n <rolatech-detail-item\n [detail]=\"item\"\n (upload)=\"onDetailMediaUpload($event)\"\n (deleteMedia)=\"onDetailMediaDelete($event)\"\n (save)=\"onDetailSave($event)\"\n (delete)=\"onDetailDelete($event)\"\n ></rolatech-detail-item>\n }\n <div class=\"mb-6\">\n <button mat-button (click)=\"addDetail()\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u8BE6\u60C5</span>\n </button>\n </div>\n </form>\n </mat-step>\n <mat-step [editable]=\"true\">\n <form #pricingForm=\"ngForm\" class=\"py-3\">\n <ng-template matStepLabel>\u4EF7\u683C</ng-template>\n <div>\n @for (item of course.pricing; track item) {\n <rolatech-pricing-item [pricing]=\"item\"> </rolatech-pricing-item>\n }\n <div class=\"mb-6\">\n <button mat-button (click)=\"addPricing()\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u4EF7\u683C</span>\n </button>\n </div>\n </div>\n </form>\n </mat-step>\n <mat-step>\n <form #scheduleForm=\"ngForm\">\n <ng-template matStepLabel>\u8BFE\u8868</ng-template>\n <div class=\"flex flex-col\">\n @for (item of course.schedule; track item) {\n <rolatech-schedule-item [value]=\"item\"></rolatech-schedule-item>\n }\n <div class=\"mb-6\">\n <button mat-button (click)=\"addSchedule()\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u8BFE\u8868</span>\n </button>\n </div>\n </div>\n </form>\n </mat-step>\n </mat-stepper>\n </div>\n <mat-divider></mat-divider>\n <div class=\"h-16 flex justify-end items-center px-5\">\n @if (!firstStepper) {\n <button mat-button mat-button (click)=\"previous()\">\u4E0A\u4E00\u6B65</button>\n }\n @if (!lastStepper) {\n <button mat-button (click)=\"next()\">\u4E0B\u4E00\u6B65</button>\n }\n @if (lastStepper) {\n <button mat-button [mat-dialog-close]=\"course\" cdkFocusInitial>\u4FDD\u5B58</button>\n }\n </div>\n</div>\n" }]
1501
+ }], ctorParameters: () => [{ type: i1$3.MatDialogRef }, { type: undefined, decorators: [{
1502
+ type: Inject,
1503
+ args: [MAT_DIALOG_DATA]
1504
+ }] }, { type: CategoryService }, { type: CourseService }, { type: i3$3.MatSnackBar }, { type: i1$3.MatDialog }] });
1505
+
1506
+ class CourseDetailsComponent {
1507
+ constructor() {
1508
+ this.details = input.required();
1509
+ this.instructor = input();
1510
+ this.username = input();
1511
+ this.el = inject(ElementRef);
1512
+ this.viewportScroller = inject(ViewportScroller);
1513
+ this.platformId = inject(PLATFORM_ID);
1514
+ this.show = false;
1515
+ this.mediaIndex = 0;
1516
+ this.finalSchedule = [];
1517
+ }
1518
+ ngOnInit() { }
1519
+ onClickScroller(id) {
1520
+ this.viewportScroller.scrollToAnchor(id);
1521
+ }
1522
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseDetailsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1523
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseDetailsComponent, isStandalone: true, selector: "rolatech-course-details", inputs: { details: { classPropertyName: "details", publicName: "details", isSignal: true, isRequired: true, transformFunction: null }, instructor: { classPropertyName: "instructor", publicName: "instructor", isSignal: true, isRequired: false, transformFunction: null }, username: { classPropertyName: "username", publicName: "username", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"py-3\" id=\"details\">\n <div class=\"text-xl font-medium py-2\">\u8BE6\u60C5</div>\n @if (details()) {\n @for (detail of details(); track $index) {\n <div>\n @if (detail.title) {\n <div class=\"py-3 flex items-center gap-3\">\n <span class=\"h-4 w-1 bg-orange-500 inline-block\"></span>\n <span class=\"text-lg font-medium py-1\"> {{ detail.title }}</span>\n </div>\n }\n @if (detail.content) {\n <div>\n {{ detail.content }}\n </div>\n }\n @if (detail.media) {\n <div class=\"w-80%\">\n @for (item of detail.media; track item) {\n <div class=\"py-3\">\n <rolatech-thumbnail\n [src]=\"item.url\"\n size=\"small\"\n mode=\"full\"\n [width]=\"item.width\"\n [height]=\"item.height\"\n ></rolatech-thumbnail>\n </div>\n }\n </div>\n }\n </div>\n }\n }\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: ThumbnailComponent, selector: "rolatech-thumbnail", inputs: ["src", "size", "mode", "ratio", "width", "height"] }] }); }
1524
+ }
1525
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseDetailsComponent, decorators: [{
1526
+ type: Component,
1527
+ args: [{ selector: 'rolatech-course-details', standalone: true, imports: [ThumbnailComponent], template: "<div class=\"py-3\" id=\"details\">\n <div class=\"text-xl font-medium py-2\">\u8BE6\u60C5</div>\n @if (details()) {\n @for (detail of details(); track $index) {\n <div>\n @if (detail.title) {\n <div class=\"py-3 flex items-center gap-3\">\n <span class=\"h-4 w-1 bg-orange-500 inline-block\"></span>\n <span class=\"text-lg font-medium py-1\"> {{ detail.title }}</span>\n </div>\n }\n @if (detail.content) {\n <div>\n {{ detail.content }}\n </div>\n }\n @if (detail.media) {\n <div class=\"w-80%\">\n @for (item of detail.media; track item) {\n <div class=\"py-3\">\n <rolatech-thumbnail\n [src]=\"item.url\"\n size=\"small\"\n mode=\"full\"\n [width]=\"item.width\"\n [height]=\"item.height\"\n ></rolatech-thumbnail>\n </div>\n }\n </div>\n }\n </div>\n }\n }\n</div>\n" }]
1528
+ }] });
1529
+
1530
+ class CourseMediaOwnerRendererComponent {
1531
+ constructor() {
1532
+ this.name = input();
1533
+ this.avatar = input();
1534
+ this.username = input();
1535
+ }
1536
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseMediaOwnerRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1537
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseMediaOwnerRendererComponent, isStandalone: true, selector: "rolatech-course-media-owner-renderer", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, avatar: { classPropertyName: "avatar", publicName: "avatar", isSignal: true, isRequired: false, transformFunction: null }, username: { classPropertyName: "username", publicName: "username", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"flex flex-row items-center\">\n <div class=\"flex mr-3 gap-2 items-center\">\n @if (avatar()) {\n <div class=\"cursor-pointer\" [routerLink]=\"['/', '@' + username()]\">\n <img [src]=\"avatar()\" class=\"w-11 h-11 rounded-full\" />\n </div>\n } @else {\n <div class=\"w-11 h-11 rounded-full bg-orange-600\"></div>\n }\n <div class=\"flex items-center text-lg font-bold cursor-pointer\">\n <a [routerLink]=\"['/', '@' + username()]\">\n <span>{{ name() }}</span>\n </a>\n <mat-icon color=\"primary\">verified</mat-icon>\n </div>\n </div>\n</div>\n", styles: ["mat-icon{transform:scale(.8)}\n"], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] }); }
1538
+ }
1539
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseMediaOwnerRendererComponent, decorators: [{
1540
+ type: Component,
1541
+ args: [{ selector: 'rolatech-course-media-owner-renderer', standalone: true, imports: [AngularCommonModule, MatIconModule], template: "<div class=\"flex flex-row items-center\">\n <div class=\"flex mr-3 gap-2 items-center\">\n @if (avatar()) {\n <div class=\"cursor-pointer\" [routerLink]=\"['/', '@' + username()]\">\n <img [src]=\"avatar()\" class=\"w-11 h-11 rounded-full\" />\n </div>\n } @else {\n <div class=\"w-11 h-11 rounded-full bg-orange-600\"></div>\n }\n <div class=\"flex items-center text-lg font-bold cursor-pointer\">\n <a [routerLink]=\"['/', '@' + username()]\">\n <span>{{ name() }}</span>\n </a>\n <mat-icon color=\"primary\">verified</mat-icon>\n </div>\n </div>\n</div>\n", styles: ["mat-icon{transform:scale(.8)}\n"] }]
1542
+ }] });
1543
+
1544
+ class CourseCategoryComponent extends BaseComponent {
1545
+ constructor() {
1546
+ super(...arguments);
1547
+ this.categoryService = inject(CategoryService);
1548
+ this.courseService = inject(CourseService);
1549
+ this.courses = [];
1550
+ this.categories = [];
1551
+ this.loading = false;
1552
+ }
1553
+ ngOnInit() {
1554
+ this.findAllCategories();
1555
+ this.route.paramMap.subscribe((params) => {
1556
+ const id = params.get('id');
1557
+ this.findCategoryById(id);
1558
+ this.findCoursesByCategoryId(id);
1559
+ });
1560
+ }
1561
+ findAllCategories() {
1562
+ this.categoryService.find({}).subscribe({
1563
+ next: (res) => {
1564
+ this.categories = res.data;
1565
+ },
1566
+ });
1567
+ }
1568
+ findCategoryById(id) {
1569
+ this.categoryService.get(id).subscribe({
1570
+ next: (res) => {
1571
+ this.category = res.data;
1572
+ },
1573
+ });
1574
+ }
1575
+ findAllCourses() {
1576
+ this.courseService.find({}).subscribe({
1577
+ next: (res) => {
1578
+ this.courses = res.data;
1579
+ },
1580
+ });
1581
+ }
1582
+ findCoursesByCategoryId(id) {
1583
+ this.loading = true;
1584
+ const options = {
1585
+ filter: `categories:${id},published:true`,
1586
+ };
1587
+ this.courseService.find(options).subscribe({
1588
+ next: (res) => {
1589
+ this.courses = res.data;
1590
+ this.loading = false;
1591
+ },
1592
+ error: (error) => {
1593
+ this.loading = false;
1594
+ },
1595
+ });
1596
+ }
1597
+ loadCoursesByCategory(item) {
1598
+ // [routerLink] = "['../../', item.id]";
1599
+ this.router.navigate([`../../${item.id}`], {
1600
+ relativeTo: this.route,
1601
+ });
1602
+ }
1603
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseCategoryComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1604
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseCategoryComponent, isStandalone: true, selector: "rolatech-course-category", usesInheritance: true, ngImport: i0, template: "<div>\n @if (category) {\n <div class=\"p-3 hidden md:block text-2xl font-medium\">{{ category.name }}</div>\n } @else {\n <div class=\"flex flex-row animate-pulse w-full\">\n <div class=\"h-4 bg-[--rt-base-background] rounded col-span-2\"></div>\n </div>\n }\n @if (loading) {\n @for (number of [0, 1, 2, 3, 4, 5]; track number) {\n <div class=\"flex flex-row animate-pulse mt-3 mr-4 cursor-pointer w-full\">\n <div class=\"h-fit w-2/5 md:w-1/4 aspect-video bg-[--rt-base-background] rounded-lg\"></div>\n <div class=\"w-3/5 md:w-3/4 ml-3 py-1 flex flex-col justify-between\">\n <div class=\"space-y-3\">\n <div class=\"h-4 bg-[--rt-base-background] rounded col-span-2\"></div>\n <div class=\"h-2 bg-[--rt-base-background] rounded col-span-1\"></div>\n <div class=\"h-2 bg-[--rt-base-background] rounded col-span-1 py-1\"></div>\n </div>\n <div>\n <div class=\"h-2 bg-[--rt-base-background] rounded col-span-2 py-1\"></div>\n </div>\n </div>\n </div>\n }\n } @else {\n <rolatech-rich-view list>\n @for (item of courses; track item) {\n @defer {\n <rolatech-rich-item\n [routerLink]=\"['../../', item.id]\"\n [thumbnail]=\"item.media[0].url + '!w400'\"\n [title]=\"item.name\"\n [subtitle]=\"item.description\"\n [price]=\"item.pricing[0].total / 100\"\n ></rolatech-rich-item>\n }\n }\n </rolatech-rich-view>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: RichViewComponent, selector: "rolatech-rich-view", inputs: ["list", "wrap"] }], deferBlockDependencies: [() => [i1.RouterLink, RichItemComponent]] }); }
1605
+ }
1606
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseCategoryComponent, decorators: [{
1607
+ type: Component,
1608
+ args: [{ standalone: true, imports: [AngularCommonModule, AngularComponentsModule, CourseItemComponent, RichViewComponent, RichItemComponent], selector: 'rolatech-course-category', template: "<div>\n @if (category) {\n <div class=\"p-3 hidden md:block text-2xl font-medium\">{{ category.name }}</div>\n } @else {\n <div class=\"flex flex-row animate-pulse w-full\">\n <div class=\"h-4 bg-[--rt-base-background] rounded col-span-2\"></div>\n </div>\n }\n @if (loading) {\n @for (number of [0, 1, 2, 3, 4, 5]; track number) {\n <div class=\"flex flex-row animate-pulse mt-3 mr-4 cursor-pointer w-full\">\n <div class=\"h-fit w-2/5 md:w-1/4 aspect-video bg-[--rt-base-background] rounded-lg\"></div>\n <div class=\"w-3/5 md:w-3/4 ml-3 py-1 flex flex-col justify-between\">\n <div class=\"space-y-3\">\n <div class=\"h-4 bg-[--rt-base-background] rounded col-span-2\"></div>\n <div class=\"h-2 bg-[--rt-base-background] rounded col-span-1\"></div>\n <div class=\"h-2 bg-[--rt-base-background] rounded col-span-1 py-1\"></div>\n </div>\n <div>\n <div class=\"h-2 bg-[--rt-base-background] rounded col-span-2 py-1\"></div>\n </div>\n </div>\n </div>\n }\n } @else {\n <rolatech-rich-view list>\n @for (item of courses; track item) {\n @defer {\n <rolatech-rich-item\n [routerLink]=\"['../../', item.id]\"\n [thumbnail]=\"item.media[0].url + '!w400'\"\n [title]=\"item.name\"\n [subtitle]=\"item.description\"\n [price]=\"item.pricing[0].total / 100\"\n ></rolatech-rich-item>\n }\n }\n </rolatech-rich-view>\n }\n</div>\n" }]
1609
+ }] });
1610
+
1611
+ class CourseDetailComponent extends BaseComponent {
1612
+ constructor() {
1613
+ super();
1614
+ this.status = CourseStatus;
1615
+ this.authService = inject(AuthService);
1616
+ this.authUserService = inject(AuthUserService);
1617
+ this.courseService = inject(CourseService);
1618
+ this.viewportScroller = inject(ViewportScroller);
1619
+ this.courseSectionService = inject(CourseSectionService);
1620
+ this.authenticated = this.authService.authenticated;
1621
+ this.instructorName = '';
1622
+ this.username = '';
1623
+ this.inWishList = false;
1624
+ this.purchased = false;
1625
+ this.sections = [];
1626
+ this.type = CourseSectionLectureContentType;
1627
+ effect(() => {
1628
+ if (this.authenticated()) {
1629
+ this.findPurchasedByCourseId();
1630
+ this.findWishlistBy();
1631
+ }
1632
+ });
1633
+ }
1634
+ ngOnInit() {
1635
+ this.findOne();
1636
+ this.findSections();
1637
+ }
1638
+ scrollToAnchor(anchorId) {
1639
+ // this.viewportScroller.setOffset([100, 100]);
1640
+ document.getElementById(anchorId)?.scrollIntoView({
1641
+ behavior: 'smooth',
1642
+ block: 'start',
1643
+ inline: 'nearest',
1644
+ });
1645
+ }
1646
+ findOne() {
1647
+ this.courseService.get(this.id).subscribe({
1648
+ next: (res) => {
1649
+ this.course = res.data;
1650
+ this.findUserBaseInfo(this.course.instructorId);
1651
+ this.titleService.setTitle(`${this.course.name} - 拼小课`);
1652
+ },
1653
+ });
1654
+ }
1655
+ findSections() {
1656
+ this.courseSectionService.findSections(this.id).subscribe({
1657
+ next: (res) => {
1658
+ this.sections = res.data;
1659
+ },
1660
+ });
1661
+ }
1662
+ findUserBaseInfo(userId) {
1663
+ this.authUserService.getPublicUserInfo(userId).subscribe({
1664
+ next: (res) => {
1665
+ this.instructorName = res.name;
1666
+ this.username = res.username;
1667
+ },
1668
+ });
1669
+ }
1670
+ onWish(e) {
1671
+ if (this.authenticated()) {
1672
+ if (this.inWishList) {
1673
+ this.removeFromWishlist();
1674
+ }
1675
+ else {
1676
+ this.addToWishlist();
1677
+ }
1678
+ }
1679
+ else {
1680
+ this.snackBarService.open('请先登录');
1681
+ }
1682
+ }
1683
+ checkout(e) {
1684
+ if (this.authenticated()) {
1685
+ this.router.navigateByUrl(`/cart/checkout/courses/${this.id}`);
1686
+ }
1687
+ else {
1688
+ this.snackBarService.open('请先登录');
1689
+ }
1690
+ }
1691
+ removeFromWishlist() {
1692
+ this.courseService.removeFromWishlist(this.id).subscribe({
1693
+ next: (res) => {
1694
+ this.inWishList = false;
1695
+ },
1696
+ });
1697
+ }
1698
+ addToWishlist() {
1699
+ this.courseService.addToWishlist(this.id).subscribe({
1700
+ next: (res) => {
1701
+ this.inWishList = true;
1702
+ },
1703
+ });
1704
+ }
1705
+ onSection(event) {
1706
+ const { section, lecture } = event;
1707
+ this.router.navigate(['/courses', this.course.id, 'sections', section.id, 'lectures', lecture.id]);
1708
+ }
1709
+ findWishlistBy() {
1710
+ this.courseService.findWishlistBy(this.id).subscribe({
1711
+ next: (res) => {
1712
+ this.inWishList = res.data;
1713
+ },
1714
+ });
1715
+ }
1716
+ findPurchasedByCourseId() {
1717
+ this.courseService.findPurchasedByCourseId(this.id).subscribe({
1718
+ next: (res) => {
1719
+ this.purchased = res.data ? true : false;
1720
+ },
1721
+ });
1722
+ }
1723
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1724
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseDetailComponent, isStandalone: true, selector: "rolatech-course-detail", usesInheritance: true, ngImport: i0, template: "@if (course) {\n <rolatech-container>\n <div class=\"flex flex-col-reverse gap-3 md:flex-row w-full justify-between\">\n <div class=\"md:w-3/4\">\n <rolatech-course-info [course]=\"course\" [instructor]=\"instructorName\" [username]=\"username\"> </rolatech-course-info>\n <rolatech-course-sections\n [sections]=\"sections\"\n (section)=\"onSection($event)\"\n (checkout)=\"checkout($event)\"\n [purchased]=\"purchased\"\n ></rolatech-course-sections>\n <rolatech-course-schedule [schedule]=\"course.schedule\"></rolatech-course-schedule>\n <rolatech-course-details [details]=\"course.details\"></rolatech-course-details>\n <rolatech-comments [itemId]=\"course.id\"></rolatech-comments>\n </div>\n <div class=\"md:px-3\">\n <!-- <rolatech-course-media [min]=\"!purchased\" [media]=\"course.media\" [min]=\"true\"></rolatech-course-media> -->\n <rolatech-course-media [media]=\"course.media\" [min]=\"true\"></rolatech-course-media>\n @if (!purchased) {\n <rolatech-course-pricing [pricing]=\"course.pricing\"></rolatech-course-pricing>\n <rolatech-course-action\n [course]=\"course\"\n (checkout)=\"checkout($event)\"\n (wish)=\"onWish($event)\"\n [inWishList]=\"inWishList\"\n ></rolatech-course-action>\n }\n </div>\n </div>\n </rolatech-container>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: CourseInfoComponent, selector: "rolatech-course-info", inputs: ["course", "instructor", "username"] }, { kind: "component", type: CourseSectionsComponent, selector: "rolatech-course-sections", inputs: ["purchased", "sections"], outputs: ["section", "checkout"] }, { kind: "component", type: CourseScheduleComponent, selector: "rolatech-course-schedule", inputs: ["schedule"] }, { kind: "component", type: CourseDetailsComponent, selector: "rolatech-course-details", inputs: ["details", "instructor", "username"] }, { kind: "component", type: CourseMediaComponent, selector: "rolatech-course-media", inputs: ["media", "min"] }, { kind: "component", type: CoursePricingComponent, selector: "rolatech-course-pricing", inputs: ["pricing"] }, { kind: "component", type: CourseActionComponent, selector: "rolatech-course-action", inputs: ["course", "inWishList"], outputs: ["cart", "wish", "checkout"] }, { kind: "component", type: CommentsComponent, selector: "rolatech-comments", inputs: ["itemId"] }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }] }); }
1725
+ }
1726
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseDetailComponent, decorators: [{
1727
+ type: Component,
1728
+ args: [{ standalone: true, imports: [
1729
+ AngularCommonModule,
1730
+ AngularComponentsModule,
1731
+ CourseInfoComponent,
1732
+ CourseSectionsComponent,
1733
+ CourseScheduleComponent,
1734
+ CourseDetailsComponent,
1735
+ CourseMediaComponent,
1736
+ CoursePricingComponent,
1737
+ CourseActionComponent,
1738
+ CommentsComponent,
1739
+ ContainerComponent,
1740
+ ], selector: 'rolatech-course-detail', template: "@if (course) {\n <rolatech-container>\n <div class=\"flex flex-col-reverse gap-3 md:flex-row w-full justify-between\">\n <div class=\"md:w-3/4\">\n <rolatech-course-info [course]=\"course\" [instructor]=\"instructorName\" [username]=\"username\"> </rolatech-course-info>\n <rolatech-course-sections\n [sections]=\"sections\"\n (section)=\"onSection($event)\"\n (checkout)=\"checkout($event)\"\n [purchased]=\"purchased\"\n ></rolatech-course-sections>\n <rolatech-course-schedule [schedule]=\"course.schedule\"></rolatech-course-schedule>\n <rolatech-course-details [details]=\"course.details\"></rolatech-course-details>\n <rolatech-comments [itemId]=\"course.id\"></rolatech-comments>\n </div>\n <div class=\"md:px-3\">\n <!-- <rolatech-course-media [min]=\"!purchased\" [media]=\"course.media\" [min]=\"true\"></rolatech-course-media> -->\n <rolatech-course-media [media]=\"course.media\" [min]=\"true\"></rolatech-course-media>\n @if (!purchased) {\n <rolatech-course-pricing [pricing]=\"course.pricing\"></rolatech-course-pricing>\n <rolatech-course-action\n [course]=\"course\"\n (checkout)=\"checkout($event)\"\n (wish)=\"onWish($event)\"\n [inWishList]=\"inWishList\"\n ></rolatech-course-action>\n }\n </div>\n </div>\n </rolatech-container>\n}\n" }]
1741
+ }], ctorParameters: () => [] });
1742
+
1743
+ class CourseLayoutComponent extends BaseComponent {
1744
+ constructor() {
1745
+ super(...arguments);
1746
+ this.categories = [];
1747
+ this.categoryService = inject(CategoryService);
1748
+ this.courseService = inject(CourseService);
1749
+ this.selectIndex = 0;
1750
+ }
1751
+ ngOnInit() {
1752
+ this.findCategories();
1753
+ }
1754
+ findCategories() {
1755
+ this.categoryService.find({}).subscribe({
1756
+ next: (res) => {
1757
+ this.categories = res.data;
1758
+ },
1759
+ });
1760
+ }
1761
+ loadCourseByCategoryIndex(index) {
1762
+ this.selectIndex = index;
1763
+ const id = this.categories[index].id;
1764
+ if (index === 0) {
1765
+ this.router.navigate([`/courses`]);
1766
+ // this.courseService.find({}).subscribe({
1767
+ // next: res => {
1768
+ // }
1769
+ // })
1770
+ }
1771
+ else {
1772
+ this.router.navigate([`../courses/categories/${id}`], {
1773
+ relativeTo: this.route,
1774
+ });
1775
+ }
1776
+ }
1777
+ nextCategory() { }
1778
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseLayoutComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1779
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseLayoutComponent, isStandalone: true, selector: "rolatech-course-layout", usesInheritance: true, ngImport: i0, template: "@if (categories) {\n <rolatech-container>\n <div class=\"flex flex-col md:flex-row min-w-[320px] max-w-[1280px] m-auto\">\n <div class=\"h-14 md:h-auto min-w-[240px]\">\n <div class=\"hidden md:block text-2xl font-medium py-3 md:px-1 px-3\">\u5206\u7C7B</div>\n <div\n class=\"flex flex-row md:flex-col md:h-full items-center md:items-start h-14 overflow-x-scroll overflow-y-hidden scrollbar-hide whitespace-pre\"\n >\n <a\n class=\"cursor-pointer h-8 bg-[--rt-badge-chip-background] md:bg-[--rt-base-background] rounded-md flex items-center md:mb-1 mr-2 md:mr-0\"\n routerLinkActive=\"course-layout-active\"\n routerLink=\"/courses\"\n [routerLinkActiveOptions]=\"{ exact: true }\"\n >\n <span class=\"md:px-1 px-3 text-md md:text-lg\">\u5168\u90E8</span>\n </a>\n @for (category of categories; track category; let index = $index) {\n <a\n class=\"cursor-pointer h-8 bg-[--rt-badge-chip-background] md:bg-[--rt-base-background] rounded-md flex items-center mr-2 md:mr-0 md:mb-1\"\n routerLinkActive=\"course-layout-active\"\n [routerLink]=\"['../courses/categories/', category.id]\"\n [routerLinkActiveOptions]=\"{ exact: true }\"\n >\n <span class=\"md:px-1 px-3 text-md md:text-lg\">{{ category.name }}</span>\n </a>\n }\n </div>\n </div>\n <div class=\"w-full\">\n <router-outlet></router-outlet>\n </div>\n </div>\n <!-- <rolatech-list>\n <router-outlet></router-outlet>\n </rolatech-list> -->\n </rolatech-container>\n}\n", styles: [".course-layout-active{border-radius:var(--rt-rounded-base, 8px);background-color:var(-rt-base-background, #fff);color:var(--rt-brand-color, #000)}@media (max-width: 768px){.course-layout-active{background-color:var(--rt-text-primary, #000);color:var(--rt-text-primary-inverse, #000)}}.scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}\n"], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "directive", type: i1.RouterOutlet, selector: "router-outlet", inputs: ["name"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }] }); }
1780
+ }
1781
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseLayoutComponent, decorators: [{
1782
+ type: Component,
1783
+ args: [{ standalone: true, imports: [AngularCommonModule, AngularComponentsModule, ContainerComponent, ListComponent], selector: 'rolatech-course-layout', template: "@if (categories) {\n <rolatech-container>\n <div class=\"flex flex-col md:flex-row min-w-[320px] max-w-[1280px] m-auto\">\n <div class=\"h-14 md:h-auto min-w-[240px]\">\n <div class=\"hidden md:block text-2xl font-medium py-3 md:px-1 px-3\">\u5206\u7C7B</div>\n <div\n class=\"flex flex-row md:flex-col md:h-full items-center md:items-start h-14 overflow-x-scroll overflow-y-hidden scrollbar-hide whitespace-pre\"\n >\n <a\n class=\"cursor-pointer h-8 bg-[--rt-badge-chip-background] md:bg-[--rt-base-background] rounded-md flex items-center md:mb-1 mr-2 md:mr-0\"\n routerLinkActive=\"course-layout-active\"\n routerLink=\"/courses\"\n [routerLinkActiveOptions]=\"{ exact: true }\"\n >\n <span class=\"md:px-1 px-3 text-md md:text-lg\">\u5168\u90E8</span>\n </a>\n @for (category of categories; track category; let index = $index) {\n <a\n class=\"cursor-pointer h-8 bg-[--rt-badge-chip-background] md:bg-[--rt-base-background] rounded-md flex items-center mr-2 md:mr-0 md:mb-1\"\n routerLinkActive=\"course-layout-active\"\n [routerLink]=\"['../courses/categories/', category.id]\"\n [routerLinkActiveOptions]=\"{ exact: true }\"\n >\n <span class=\"md:px-1 px-3 text-md md:text-lg\">{{ category.name }}</span>\n </a>\n }\n </div>\n </div>\n <div class=\"w-full\">\n <router-outlet></router-outlet>\n </div>\n </div>\n <!-- <rolatech-list>\n <router-outlet></router-outlet>\n </rolatech-list> -->\n </rolatech-container>\n}\n", styles: [".course-layout-active{border-radius:var(--rt-rounded-base, 8px);background-color:var(-rt-base-background, #fff);color:var(--rt-brand-color, #000)}@media (max-width: 768px){.course-layout-active{background-color:var(--rt-text-primary, #000);color:var(--rt-text-primary-inverse, #000)}}.scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}\n"] }]
1784
+ }] });
1785
+
1786
+ class CourseSectionContentComponent {
1787
+ constructor() {
1788
+ this.route = inject(ActivatedRoute);
1789
+ this.router = inject(Router);
1790
+ this.courseSectionService = inject(CourseSectionService);
1791
+ this.courseService = inject(CourseService);
1792
+ this.authService = inject(AuthService);
1793
+ this.authUserService = inject(AuthUserService);
1794
+ this.el = inject(ElementRef);
1795
+ this.platformId = inject(PLATFORM_ID);
1796
+ this.authenticated = this.authService.authenticated;
1797
+ this.sections = [];
1798
+ this.courseId = '';
1799
+ this.sectionId = '';
1800
+ this.lectureId = '';
1801
+ this.videoUrl = '';
1802
+ this.instructorName = '';
1803
+ this.instructorAvatar = '';
1804
+ this.instructorUsername = '';
1805
+ this.purchased = false;
1806
+ effect(() => {
1807
+ if (this.authenticated()) {
1808
+ this.findPurchasedByCourseId();
1809
+ }
1810
+ });
1811
+ }
1812
+ ngOnInit() {
1813
+ this.route.params.subscribe((params) => {
1814
+ this.courseId = params['id'];
1815
+ this.sectionId = params['sectionId'];
1816
+ this.lectureId = params['lectureId'];
1817
+ this.findCourse(this.courseId);
1818
+ this.findSections(this.courseId);
1819
+ });
1820
+ }
1821
+ ngOnDestroy() {
1822
+ if (this.player) {
1823
+ this.player.destroy();
1824
+ }
1825
+ }
1826
+ ngAfterViewInit() { }
1827
+ initPlayer(url, thumbnail) {
1828
+ if (!isPlatformBrowser(this.platformId)) {
1829
+ return;
1830
+ }
1831
+ // 'https://cos-video-1258344699.cos.ap-guangzhou.tencentcos.cn/test.mp4',
1832
+ if (!this.player) {
1833
+ const playerContainer = this.el.nativeElement.querySelector('#dplayer');
1834
+ this.player = new DPlayer({
1835
+ container: playerContainer,
1836
+ screenshot: true,
1837
+ preload: 'metadata',
1838
+ video: {
1839
+ url: url,
1840
+ pic: thumbnail,
1841
+ },
1842
+ });
1843
+ }
1844
+ else {
1845
+ this.player.switchVideo({
1846
+ url: url,
1847
+ pic: thumbnail,
1848
+ });
1849
+ }
1850
+ }
1851
+ findSections(courseId) {
1852
+ this.courseSectionService.findSections(courseId).subscribe({
1853
+ next: (res) => {
1854
+ this.sections = res.data;
1855
+ const section = this.sections.find((item) => item.id === this.sectionId);
1856
+ const letcture = section?.lectures.find((item) => item.id === this.lectureId);
1857
+ this.videoUrl = letcture?.item.url;
1858
+ const thumbnail = letcture?.item.thumbnail;
1859
+ this.initPlayer(this.videoUrl, thumbnail);
1860
+ },
1861
+ });
1862
+ }
1863
+ findCourse(courseId) {
1864
+ this.courseService.get(courseId).subscribe({
1865
+ next: (res) => {
1866
+ this.course = res.data;
1867
+ this.findUserBaseInfo(this.course.instructorId);
1868
+ },
1869
+ });
1870
+ }
1871
+ findUserBaseInfo(userId) {
1872
+ this.authUserService.getPublicUserInfo(userId).subscribe({
1873
+ next: (res) => {
1874
+ this.instructorName = res.name;
1875
+ this.instructorAvatar = res.avatar;
1876
+ this.instructorUsername = res.username;
1877
+ },
1878
+ });
1879
+ }
1880
+ findPurchasedByCourseId() {
1881
+ this.courseService.findPurchasedByCourseId(this.courseId).subscribe({
1882
+ next: (res) => {
1883
+ this.purchased = res.data ? true : false;
1884
+ },
1885
+ });
1886
+ }
1887
+ onSection(event) {
1888
+ const { section, lecture } = event;
1889
+ this.router.navigate(['/courses', this.courseId, 'sections', section.id, 'lectures', lecture.id]);
1890
+ }
1891
+ checkout(e) {
1892
+ this.router.navigateByUrl(`/cart/checkout/courses/${this.courseId}`);
1893
+ }
1894
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseSectionContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1895
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseSectionContentComponent, isStandalone: true, selector: "rolatech-course-section-content", ngImport: i0, template: "<div class=\"flex lg:flex-row flex-col justify-between w-full mt-2 lg:mt-4\">\n <div class=\"lg:w-[70%] flex flex-col items-start justify-center px-4 lg:px-6 h-fit\">\n <div id=\"dplayer\" class=\"w-full h-auto aspect-video rounded-xl\"></div>\n <div class=\"py-3 w-full\">\n @if (course) {\n <div class=\"text-xl font-medium py-3\">\n {{ course.name }}\n </div>\n <div>\n <rolatech-course-media-owner-renderer\n [name]=\"instructorName\"\n [avatar]=\"instructorAvatar\"\n [username]=\"instructorUsername\"\n ></rolatech-course-media-owner-renderer>\n </div>\n <div class=\"hidden lg:block\">\n <rolatech-comments [itemId]=\"lectureId\"></rolatech-comments>\n </div>\n }\n </div>\n </div>\n <div class=\"lg:w-[30%] px-4 lg:px-0 lg:pr-6\">\n <rolatech-course-sections\n [sections]=\"sections\"\n [purchased]=\"purchased\"\n (section)=\"onSection($event)\"\n (checkout)=\"checkout($event)\"\n ></rolatech-course-sections>\n </div>\n <div class=\"lg:hidden block px-4 lg:px-6\">\n <rolatech-comments [itemId]=\"lectureId\"></rolatech-comments>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: CourseSectionsComponent, selector: "rolatech-course-sections", inputs: ["purchased", "sections"], outputs: ["section", "checkout"] }, { kind: "component", type: CourseMediaOwnerRendererComponent, selector: "rolatech-course-media-owner-renderer", inputs: ["name", "avatar", "username"] }, { kind: "component", type: CommentsComponent, selector: "rolatech-comments", inputs: ["itemId"] }] }); }
1896
+ }
1897
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseSectionContentComponent, decorators: [{
1898
+ type: Component,
1899
+ args: [{ standalone: true, imports: [
1900
+ AngularCommonModule,
1901
+ AngularComponentsModule,
1902
+ CourseSectionsComponent,
1903
+ CourseMediaOwnerRendererComponent,
1904
+ CommentsComponent,
1905
+ ], selector: 'rolatech-course-section-content', template: "<div class=\"flex lg:flex-row flex-col justify-between w-full mt-2 lg:mt-4\">\n <div class=\"lg:w-[70%] flex flex-col items-start justify-center px-4 lg:px-6 h-fit\">\n <div id=\"dplayer\" class=\"w-full h-auto aspect-video rounded-xl\"></div>\n <div class=\"py-3 w-full\">\n @if (course) {\n <div class=\"text-xl font-medium py-3\">\n {{ course.name }}\n </div>\n <div>\n <rolatech-course-media-owner-renderer\n [name]=\"instructorName\"\n [avatar]=\"instructorAvatar\"\n [username]=\"instructorUsername\"\n ></rolatech-course-media-owner-renderer>\n </div>\n <div class=\"hidden lg:block\">\n <rolatech-comments [itemId]=\"lectureId\"></rolatech-comments>\n </div>\n }\n </div>\n </div>\n <div class=\"lg:w-[30%] px-4 lg:px-0 lg:pr-6\">\n <rolatech-course-sections\n [sections]=\"sections\"\n [purchased]=\"purchased\"\n (section)=\"onSection($event)\"\n (checkout)=\"checkout($event)\"\n ></rolatech-course-sections>\n </div>\n <div class=\"lg:hidden block px-4 lg:px-6\">\n <rolatech-comments [itemId]=\"lectureId\"></rolatech-comments>\n </div>\n</div>\n" }]
1906
+ }], ctorParameters: () => [] });
1907
+
1908
+ const courseRoutes = [
1909
+ {
1910
+ path: '',
1911
+ component: CourseLayoutComponent,
1912
+ children: [
1913
+ {
1914
+ path: '',
1915
+ loadComponent: () => import('./rolatech-angular-course-course-index.component-DqcW95VP.mjs').then((x) => x.CourseIndexComponent),
1916
+ },
1917
+ {
1918
+ path: 'categories/:id',
1919
+ component: CourseCategoryComponent,
1920
+ },
1921
+ ],
1922
+ },
1923
+ {
1924
+ path: ':id',
1925
+ component: CourseDetailComponent,
1926
+ },
1927
+ {
1928
+ path: ':id/sections/:sectionId/lectures/:lectureId',
1929
+ component: CourseSectionContentComponent,
1930
+ },
1931
+ {
1932
+ path: 'my-courses',
1933
+ component: CourseDetailComponent,
1934
+ },
1935
+ ];
1936
+
1937
+ class CourseManageContentComponent {
1938
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseManageContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1939
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.4", type: CourseManageContentComponent, isStandalone: true, selector: "rolatech-course-manage-content", ngImport: i0, template: "<ng-content select=\"rolatech-toolbar\"></ng-content>\n<div class=\"p-3\">\n <ng-content></ng-content>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
1940
+ }
1941
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseManageContentComponent, decorators: [{
1942
+ type: Component,
1943
+ args: [{ selector: 'rolatech-course-manage-content', standalone: true, imports: [CommonModule], template: "<ng-content select=\"rolatech-toolbar\"></ng-content>\n<div class=\"p-3\">\n <ng-content></ng-content>\n</div>\n" }]
1944
+ }] });
1945
+
1946
+ class CourseManageDetailsComponent {
1947
+ constructor() {
1948
+ this.route = inject(ActivatedRoute);
1949
+ this.courseService = inject(CourseService);
1950
+ this.dialog = inject(MatDialog);
1951
+ this.snackBar = inject(MatSnackBar);
1952
+ this.isLoading = false;
1953
+ this.isUploading = false;
1954
+ this.details = [];
1955
+ this.status = CourseStatus;
1956
+ this.courseType = CourseType;
1957
+ this.id = this.route.parent?.snapshot.paramMap.get('id');
1958
+ }
1959
+ ngOnInit() {
1960
+ this.find();
1961
+ }
1962
+ find() {
1963
+ this.courseService.findDetails(this.id).subscribe({
1964
+ next: (res) => {
1965
+ if (res.data) {
1966
+ this.details = res.data;
1967
+ }
1968
+ },
1969
+ });
1970
+ }
1971
+ addDetail() {
1972
+ if (!this.details) {
1973
+ this.details = [];
1974
+ }
1975
+ this.courseService
1976
+ .addDetail(this.id, {
1977
+ title: '',
1978
+ description: '',
1979
+ content: '',
1980
+ media: [],
1981
+ })
1982
+ .subscribe({
1983
+ next: (res) => {
1984
+ const detail = res.data;
1985
+ this.details.push({
1986
+ id: detail.id,
1987
+ title: '',
1988
+ description: '',
1989
+ content: '',
1990
+ media: [],
1991
+ });
1992
+ },
1993
+ error: (e) => {
1994
+ this.snackBar.open(e.message);
1995
+ },
1996
+ });
1997
+ }
1998
+ onDetailMediaUpload(event) {
1999
+ const detailId = event.id;
2000
+ const index = findIndex(this.details, (item) => item.id === detailId);
2001
+ const file = event.data.target.files[0];
2002
+ if (file) {
2003
+ const reader = new FileReader();
2004
+ reader.readAsDataURL(file);
2005
+ reader.onload = () => {
2006
+ if (!this.details[index].media) {
2007
+ this.details[index].media = [];
2008
+ }
2009
+ this.details[index].media.push({
2010
+ url: reader.result,
2011
+ alt: 'upload image',
2012
+ });
2013
+ this.details[index].isUploading = true;
2014
+ const formData = new FormData();
2015
+ formData.append('file', file);
2016
+ this.courseService.uploadDetailMedia(this.id, detailId, formData).subscribe({
2017
+ next: (res) => {
2018
+ this.isUploading = false;
2019
+ this.details[index].isUploading = false;
2020
+ const tmp = this.details[index].media;
2021
+ delete res.data.productDetail;
2022
+ this.details[index].media[this.details[index].media.length - 1] = res.data;
2023
+ },
2024
+ error: (e) => {
2025
+ this.isUploading = false;
2026
+ this.snackBar.open('上传失败: ' + e.message);
2027
+ },
2028
+ });
2029
+ };
2030
+ reader.onerror = (error) => {
2031
+ this.isUploading = false;
2032
+ };
2033
+ }
2034
+ }
2035
+ onDetailMediaDelete(event) {
2036
+ const detailId = event.id;
2037
+ const mediaId = event.media.id;
2038
+ this.courseService.deleteDetailMedia(this.id, detailId, mediaId).subscribe({
2039
+ next: (res) => {
2040
+ const detail = this.details.find((item) => item.id === detailId);
2041
+ remove(detail.media, {
2042
+ id: mediaId,
2043
+ });
2044
+ },
2045
+ });
2046
+ }
2047
+ onDetailSave(detail) {
2048
+ delete detail.isUploading;
2049
+ this.courseService.updateDetail(this.id, detail.id, detail).subscribe({
2050
+ next: (res) => {
2051
+ this.snackBar.open('保存成功');
2052
+ },
2053
+ error: (e) => {
2054
+ this.snackBar.open(e.message);
2055
+ },
2056
+ });
2057
+ }
2058
+ onDetailDelete(detail) {
2059
+ const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
2060
+ width: '400px',
2061
+ data: {
2062
+ title: '删除详情',
2063
+ message: '确定删除此详情吗?',
2064
+ },
2065
+ });
2066
+ dialogRef.afterClosed().subscribe((result) => {
2067
+ if (result) {
2068
+ this.courseService.deleteDetail(this.id, detail.id).subscribe({
2069
+ next: (res) => {
2070
+ this.snackBar.open(res.data);
2071
+ remove(this.details, {
2072
+ id: detail.id,
2073
+ });
2074
+ },
2075
+ error: (e) => {
2076
+ this.snackBar.open(e.message);
2077
+ },
2078
+ });
2079
+ }
2080
+ });
2081
+ }
2082
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseManageDetailsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2083
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseManageDetailsComponent, isStandalone: true, selector: "rolatech-course-manage-details", ngImport: i0, template: "<rolatech-course-manage-content>\n <rolatech-toolbar title=\"\u8BE6\u60C5\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <div>\n <div>\n @for (detail of details; track detail) {\n <rolatech-detail-item\n [detail]=\"detail\"\n (upload)=\"onDetailMediaUpload($event)\"\n (deleteMedia)=\"onDetailMediaDelete($event)\"\n (save)=\"onDetailSave($event)\"\n (delete)=\"onDetailDelete($event)\"\n [actions]=\"true\"\n ></rolatech-detail-item>\n }\n </div>\n <button mat-stroked-button (click)=\"addDetail()\" class=\"mt-3\">\n <mat-icon>add</mat-icon>\n <span>\u6DFB\u52A0\u8BE6\u60C5</span>\n </button>\n </div>\n</rolatech-course-manage-content>\n", styles: [""], dependencies: [{ kind: "component", type: DetailItemComponent, selector: "rolatech-detail-item", inputs: ["isUploading", "detail", "actions", "selectMedia"], outputs: ["upload", "delete", "save", "deleteMedia"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: CourseManageContentComponent, selector: "rolatech-course-manage-content" }] }); }
2084
+ }
2085
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseManageDetailsComponent, decorators: [{
2086
+ type: Component,
2087
+ args: [{ selector: 'rolatech-course-manage-details', standalone: true, imports: [DetailItemComponent, MatButtonModule, MatIconModule, ToolbarComponent, CourseManageContentComponent], template: "<rolatech-course-manage-content>\n <rolatech-toolbar title=\"\u8BE6\u60C5\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <div>\n <div>\n @for (detail of details; track detail) {\n <rolatech-detail-item\n [detail]=\"detail\"\n (upload)=\"onDetailMediaUpload($event)\"\n (deleteMedia)=\"onDetailMediaDelete($event)\"\n (save)=\"onDetailSave($event)\"\n (delete)=\"onDetailDelete($event)\"\n [actions]=\"true\"\n ></rolatech-detail-item>\n }\n </div>\n <button mat-stroked-button (click)=\"addDetail()\" class=\"mt-3\">\n <mat-icon>add</mat-icon>\n <span>\u6DFB\u52A0\u8BE6\u60C5</span>\n </button>\n </div>\n</rolatech-course-manage-content>\n" }]
2088
+ }], ctorParameters: () => [] });
2089
+
2090
+ class CourseManageScheduleComponent extends BaseComponent {
2091
+ constructor() {
2092
+ super(...arguments);
2093
+ this.courseService = inject(CourseService);
2094
+ this.isLoading = false;
2095
+ this.schedule = [];
2096
+ this.status = CourseStatus;
2097
+ this.courseType = CourseType;
2098
+ }
2099
+ ngOnInit() {
2100
+ this.id = this.route.parent?.snapshot.paramMap.get('id');
2101
+ this.find();
2102
+ }
2103
+ find() {
2104
+ this.courseService.findSchedule(this.id).subscribe({
2105
+ next: (res) => {
2106
+ if (res.data) {
2107
+ this.schedule = res.data;
2108
+ }
2109
+ },
2110
+ });
2111
+ }
2112
+ addSchedule() {
2113
+ const options = {
2114
+ width: '500px',
2115
+ title: '添加课表',
2116
+ cancelText: '取消',
2117
+ confirmText: '确认',
2118
+ component: CourseScheduleAddDialogComponent,
2119
+ data: {},
2120
+ };
2121
+ this.dialogService.open(options);
2122
+ this.dialogService.confirmed().subscribe({
2123
+ next: (schedule) => {
2124
+ if (schedule) {
2125
+ this.courseService.addSchedule(this.id, schedule).subscribe({
2126
+ next: (res) => {
2127
+ this.schedule.push(res.data);
2128
+ this.snackBarService.open('添加成功');
2129
+ },
2130
+ error: (error) => {
2131
+ this.snackBarService.open(error.message);
2132
+ },
2133
+ });
2134
+ }
2135
+ },
2136
+ });
2137
+ }
2138
+ editSchedule() {
2139
+ const options = {
2140
+ width: '500px',
2141
+ height: '90%',
2142
+ title: '编辑课表',
2143
+ cancelText: '取消',
2144
+ confirmText: '确认',
2145
+ component: ScheduleItemComponent,
2146
+ data: {
2147
+ courseId: this.id,
2148
+ schedule: this.schedule,
2149
+ },
2150
+ };
2151
+ this.dialogService.open(options);
2152
+ this.dialogService.confirmed().subscribe({
2153
+ next: (result) => {
2154
+ if (result) {
2155
+ this.courseService.addSchedule(this.id, { schedule: result }).subscribe({
2156
+ next: (res) => {
2157
+ this.snackBarService.open('编辑成功');
2158
+ },
2159
+ error: (error) => {
2160
+ this.snackBarService.open(error.message);
2161
+ },
2162
+ });
2163
+ }
2164
+ },
2165
+ });
2166
+ }
2167
+ onScheduleSave(schedule) {
2168
+ this.courseService.updateSchedule(this.id, schedule.id, schedule).subscribe({
2169
+ next: (res) => {
2170
+ this.snackBarService.open('保存成功');
2171
+ },
2172
+ error: (e) => {
2173
+ this.snackBarService.open(e.message);
2174
+ },
2175
+ });
2176
+ }
2177
+ onScheduleDelete(schedule) {
2178
+ const options = {
2179
+ title: '删除课表',
2180
+ message: '确定删除此课表吗?',
2181
+ cancelText: '取消',
2182
+ confirmText: '确认',
2183
+ };
2184
+ this.dialogService.open(options);
2185
+ this.dialogService.confirmed().subscribe((result) => {
2186
+ if (result) {
2187
+ this.courseService.deleteSchedule(this.id, schedule.id).subscribe({
2188
+ next: (res) => {
2189
+ remove(this.schedule, {
2190
+ id: schedule.id,
2191
+ });
2192
+ this.snackBarService.open(res.data);
2193
+ },
2194
+ error: (e) => {
2195
+ this.snackBarService.open(e.message);
2196
+ },
2197
+ });
2198
+ }
2199
+ });
2200
+ }
2201
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseManageScheduleComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2202
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseManageScheduleComponent, isStandalone: true, selector: "rolatech-course-manage-schedule", usesInheritance: true, ngImport: i0, template: "<rolatech-course-manage-content>\n <rolatech-toolbar title=\"\u8BFE\u7A0B\u8868\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <div>\n <div>\n @for (item of schedule; track item) {\n <rolatech-schedule-item\n [value]=\"item\"\n [actions]=\"true\"\n (save)=\"onScheduleSave($event)\"\n (delete)=\"onScheduleDelete($event)\"\n >\n </rolatech-schedule-item>\n }\n </div>\n <button mat-stroked-button (click)=\"addSchedule()\" class=\"mt-3\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u8BFE\u8868</span>\n </button>\n </div>\n</rolatech-course-manage-content>\n", styles: [""], dependencies: [{ kind: "component", type: ScheduleItemComponent, selector: "rolatech-schedule-item", inputs: ["value", "actions"], outputs: ["delete", "save"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: CourseManageContentComponent, selector: "rolatech-course-manage-content" }] }); }
2203
+ }
2204
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseManageScheduleComponent, decorators: [{
2205
+ type: Component,
2206
+ args: [{ selector: 'rolatech-course-manage-schedule', standalone: true, imports: [ScheduleItemComponent, MatButtonModule, MatIconModule, ToolbarComponent, CourseManageContentComponent], template: "<rolatech-course-manage-content>\n <rolatech-toolbar title=\"\u8BFE\u7A0B\u8868\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <div>\n <div>\n @for (item of schedule; track item) {\n <rolatech-schedule-item\n [value]=\"item\"\n [actions]=\"true\"\n (save)=\"onScheduleSave($event)\"\n (delete)=\"onScheduleDelete($event)\"\n >\n </rolatech-schedule-item>\n }\n </div>\n <button mat-stroked-button (click)=\"addSchedule()\" class=\"mt-3\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u8BFE\u8868</span>\n </button>\n </div>\n</rolatech-course-manage-content>\n" }]
2207
+ }] });
2208
+
2209
+ class CourseManagePricingComponent extends BaseComponent {
2210
+ constructor() {
2211
+ super(...arguments);
2212
+ this.courseService = inject(CourseService);
2213
+ this.isLoading = false;
2214
+ this.pricing = [];
2215
+ this.status = CourseStatus;
2216
+ this.courseType = CourseType;
2217
+ }
2218
+ ngOnInit() {
2219
+ this.id = this.route.parent?.snapshot.paramMap.get('id');
2220
+ this.find();
2221
+ }
2222
+ find() {
2223
+ this.courseService.findPricing(this.id).subscribe({
2224
+ next: (res) => {
2225
+ if (res.data) {
2226
+ this.pricing = res.data;
2227
+ }
2228
+ },
2229
+ });
2230
+ }
2231
+ editPricing() {
2232
+ const options = {
2233
+ width: '500px',
2234
+ title: '编辑价格',
2235
+ height: '90%',
2236
+ cancelText: '取消',
2237
+ confirmText: '确认',
2238
+ component: CoursePricingAddDialogComponent,
2239
+ data: {
2240
+ courseId: this.id,
2241
+ pricing: this.pricing,
2242
+ },
2243
+ };
2244
+ this.dialogService.open(options);
2245
+ this.dialogService.confirmed().subscribe((result) => {
2246
+ if (result) {
2247
+ this.courseService.addPricing(this.id, { pricing: result }).subscribe({
2248
+ next: (res) => {
2249
+ this.snackBarService.open('编辑成功');
2250
+ },
2251
+ error: (error) => {
2252
+ this.snackBarService.open(error.message);
2253
+ },
2254
+ });
2255
+ }
2256
+ });
2257
+ }
2258
+ addPricing() {
2259
+ const options = {
2260
+ width: '500px',
2261
+ title: '添加价格',
2262
+ cancelText: '取消',
2263
+ confirmText: '确认',
2264
+ component: CoursePricingAddDialogComponent,
2265
+ data: {},
2266
+ };
2267
+ this.dialogService.open(options);
2268
+ this.dialogService.confirmed().subscribe((pricing) => {
2269
+ if (pricing) {
2270
+ this.courseService.addPricing(this.id, { ...pricing, total: pricing.total * 100 }).subscribe({
2271
+ next: (res) => {
2272
+ this.pricing.push(res.data);
2273
+ this.snackBarService.open('添加成功');
2274
+ },
2275
+ error: (error) => {
2276
+ this.snackBarService.open(error.message);
2277
+ },
2278
+ });
2279
+ }
2280
+ });
2281
+ }
2282
+ onPricingSave(pricing) {
2283
+ this.courseService.updatePricing(this.id, pricing.id, pricing).subscribe({
2284
+ next: (res) => {
2285
+ this.snackBarService.open('保存成功');
2286
+ },
2287
+ error: (e) => {
2288
+ this.snackBarService.open(e.message);
2289
+ },
2290
+ });
2291
+ }
2292
+ onPricingDelete(pricing) {
2293
+ const options = {
2294
+ title: '删除价格',
2295
+ message: '确定删除此价格吗?',
2296
+ cancelText: '取消',
2297
+ confirmText: '确认',
2298
+ };
2299
+ this.dialogService.open(options);
2300
+ this.dialogService.confirmed().subscribe((result) => {
2301
+ if (result) {
2302
+ this.courseService.deletePricing(this.id, pricing.id).subscribe({
2303
+ next: (res) => {
2304
+ remove(this.pricing, {
2305
+ id: pricing.id,
2306
+ });
2307
+ this.snackBarService.open(res.data);
2308
+ },
2309
+ error: (e) => {
2310
+ this.snackBarService.open(e.message);
2311
+ },
2312
+ });
2313
+ }
2314
+ });
2315
+ }
2316
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseManagePricingComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2317
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseManagePricingComponent, isStandalone: true, selector: "rolatech-course-manage-pricing", usesInheritance: true, ngImport: i0, template: "<rolatech-course-manage-content>\n <rolatech-toolbar title=\"\u4EF7\u683C\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <div>\n <div>\n @for (item of pricing; track item) {\n <rolatech-pricing-item\n [pricing]=\"item\"\n [actions]=\"true\"\n (save)=\"onPricingSave($event)\"\n (delete)=\"onPricingDelete($event)\"\n >\n </rolatech-pricing-item>\n }\n </div>\n <button mat-stroked-button (click)=\"addPricing()\" class=\"mt-3\">\n <mat-icon>add</mat-icon>\n <span>\u6DFB\u52A0\u4EF7\u683C</span>\n </button>\n </div>\n</rolatech-course-manage-content>\n", styles: [""], dependencies: [{ kind: "component", type: PricingItemComponent, selector: "rolatech-pricing-item", inputs: ["actions", "pricing"], outputs: ["pricingChange", "delete", "save"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: CourseManageContentComponent, selector: "rolatech-course-manage-content" }] }); }
2318
+ }
2319
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseManagePricingComponent, decorators: [{
2320
+ type: Component,
2321
+ args: [{ selector: 'rolatech-course-manage-pricing', standalone: true, imports: [PricingItemComponent, MatButtonModule, MatIconModule, ToolbarComponent, CourseManageContentComponent], template: "<rolatech-course-manage-content>\n <rolatech-toolbar title=\"\u4EF7\u683C\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <div>\n <div>\n @for (item of pricing; track item) {\n <rolatech-pricing-item\n [pricing]=\"item\"\n [actions]=\"true\"\n (save)=\"onPricingSave($event)\"\n (delete)=\"onPricingDelete($event)\"\n >\n </rolatech-pricing-item>\n }\n </div>\n <button mat-stroked-button (click)=\"addPricing()\" class=\"mt-3\">\n <mat-icon>add</mat-icon>\n <span>\u6DFB\u52A0\u4EF7\u683C</span>\n </button>\n </div>\n</rolatech-course-manage-content>\n" }]
2322
+ }] });
2323
+
2324
+ class BookingService extends BaseService {
2325
+ init() {
2326
+ this.endpoint = 'partners/bookings';
2327
+ super.init();
2328
+ }
2329
+ items(options) {
2330
+ return this.http.get(`${this.actionUrl}/items`, {
2331
+ params: options,
2332
+ withCredentials: true,
2333
+ });
2334
+ }
2335
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: BookingService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
2336
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: BookingService, providedIn: 'root' }); }
2337
+ }
2338
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: BookingService, decorators: [{
2339
+ type: Injectable,
2340
+ args: [{
2341
+ providedIn: 'root',
2342
+ }]
2343
+ }] });
2344
+
2345
+ class InstructorService extends BaseService {
2346
+ init() {
2347
+ this.endpoint = 'instructors';
2348
+ super.init();
2349
+ }
2350
+ findMyClassrooms(options) {
2351
+ return this.http.get(`${this.actionUrl}/classrooms/me`, {
2352
+ params: options,
2353
+ withCredentials: true,
2354
+ });
2355
+ }
2356
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: InstructorService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
2357
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: InstructorService, providedIn: 'root' }); }
2358
+ }
2359
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: InstructorService, decorators: [{
2360
+ type: Injectable,
2361
+ args: [{
2362
+ providedIn: 'root',
2363
+ }]
2364
+ }] });
2365
+
2366
+ class CourseManageInfoComponent extends BaseComponent {
2367
+ constructor() {
2368
+ super(...arguments);
2369
+ this.courseService = inject(CourseService);
2370
+ this.categoryService = inject(CategoryService);
2371
+ this.instructorService = inject(InstructorService);
2372
+ this.bookingService = inject(BookingService);
2373
+ this.isLoading = false;
2374
+ this.status = CourseStatus;
2375
+ this.courseType = [
2376
+ {
2377
+ key: 'ONLINE',
2378
+ value: '线上',
2379
+ },
2380
+ {
2381
+ key: 'OFFLINE',
2382
+ value: '线下',
2383
+ },
2384
+ {
2385
+ key: 'MIXED',
2386
+ value: '混合',
2387
+ },
2388
+ ];
2389
+ }
2390
+ ngOnInit() {
2391
+ this.id = this.route.parent?.snapshot.paramMap.get('id');
2392
+ this.find();
2393
+ this.categoryService.find({}).subscribe({
2394
+ next: (res) => {
2395
+ this.categories = res.data;
2396
+ },
2397
+ });
2398
+ this.findClassrooms();
2399
+ this.findBookedClassrooms();
2400
+ }
2401
+ findBookedClassrooms() {
2402
+ this.bookingService.items({ status: 'paid' }).subscribe({
2403
+ next: (res) => {
2404
+ this.bookedClassrooms = res.data;
2405
+ },
2406
+ });
2407
+ }
2408
+ findClassrooms() {
2409
+ this.instructorService.findMyClassrooms({}).subscribe({
2410
+ next: (res) => {
2411
+ this.classrooms = res.data;
2412
+ },
2413
+ });
2414
+ }
2415
+ find() {
2416
+ this.courseService.get(this.id).subscribe({
2417
+ next: (res) => {
2418
+ this.course = res.data;
2419
+ },
2420
+ });
2421
+ }
2422
+ compareFn(o1, o2) {
2423
+ return o1.id === o2?.id;
2424
+ }
2425
+ compareClassroom(o1, o2) {
2426
+ if (o1.room) {
2427
+ return (o1.latitude === o2?.latitude &&
2428
+ o1.longitude === o2?.longitude &&
2429
+ o1.room === o2.room &&
2430
+ o1.startAt === o2.startAt &&
2431
+ o1.endAt === o2.endAt);
2432
+ }
2433
+ else {
2434
+ return o1.latitude === o2?.latitude && o1.longitude === o2?.longitude;
2435
+ }
2436
+ }
2437
+ onSelectionChange(event) {
2438
+ this.selectedCategoyIds = event.value;
2439
+ }
2440
+ onClassroomSelected(event) {
2441
+ this.selectedClassroom = event.value;
2442
+ }
2443
+ update() {
2444
+ const { name, description, categories, type, classroom } = this.course;
2445
+ const finalClassroom = clone(classroom);
2446
+ finalClassroom.address = classroom.room ? classroom.address + ' ' + classroom.room + '室' : classroom.address;
2447
+ const data = {
2448
+ name,
2449
+ description,
2450
+ categories,
2451
+ type,
2452
+ classroom: finalClassroom,
2453
+ };
2454
+ this.courseService.update(this.id, data).subscribe({
2455
+ next: (res) => {
2456
+ this.snackBarService.open('保存成功');
2457
+ // this.router.navigate([`../${res.data.id}`], { relativeTo: this.route });
2458
+ },
2459
+ error: (e) => {
2460
+ this.snackBarService.open(e.message);
2461
+ },
2462
+ });
2463
+ }
2464
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseManageInfoComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2465
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseManageInfoComponent, isStandalone: true, selector: "rolatech-course-manage-info", usesInheritance: true, ngImport: i0, template: "<rolatech-course-manage-content>\n <rolatech-toolbar title=\"\u57FA\u672C\u4FE1\u606F\" class=\"hidden md:block\" divider></rolatech-toolbar>\n @if (course) {\n <div class=\"flex flex-col\">\n <form #courseForm=\"ngForm\" (ngSubmit)=\"update()\">\n <mat-form-field appearance=\"fill\">\n <mat-label> \u8BFE\u7A0B\u540D\u79F0 </mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"course.name\" name=\"name\" required />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label> \u8BFE\u7A0B\u63CF\u8FF0 </mat-label>\n <textarea\n matInput\n type=\"text\"\n [(ngModel)]=\"course.description\"\n name=\"description\"\n required\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"\n ></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"fill\" ngModelGroup=\"categories\" required>\n <mat-label>\u5206\u7C7B</mat-label>\n <mat-select\n multiple\n name=\"id\"\n [compareWith]=\"compareFn\"\n [(ngModel)]=\"course.categories\"\n #select=\"matSelect\"\n (selectionChange)=\"onSelectionChange($event)\"\n >\n @for (item of categories; track item) {\n <mat-option [value]=\"item\">\n {{ item.name }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label>\u7C7B\u578B</mat-label>\n <mat-select name=\"type\" [(ngModel)]=\"course.type\" required>\n @for (item of courseType; track item) {\n <mat-option [value]=\"item.key\">\n {{ item.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n @if (course.type && course.type.toString() !== 'ONLINE') {\n <mat-form-field appearance=\"fill\" ngModelGroup=\"classroom\">\n <mat-label>\u6559\u5BA4</mat-label>\n <mat-select\n name=\"id\"\n [(ngModel)]=\"course.classroom\"\n [compareWith]=\"compareClassroom\"\n (selectionChange)=\"onClassroomSelected($event)\"\n required\n >\n <mat-optgroup label=\"\u81EA\u6709\u6559\u5BA4\">\n @for (classroom of classrooms; track classroom) {\n <mat-option [value]=\"classroom\">\n <div class=\"flex gap-3\">\n <span>{{ classroom.name }}</span>\n <span> {{ classroom.address }}</span>\n </div>\n </mat-option>\n }\n </mat-optgroup>\n <mat-optgroup label=\"\u5DF2\u8D2D\u6559\u5BA4\">\n @for (item of bookedClassrooms; track item) {\n <mat-option [value]=\"item\">\n <div class=\"flex flex-col py-3\">\n <span class=\"font-bold\"> {{ item.address }} {{ item.room }}\u5BA4</span>\n <span class=\"text-md\"> {{ item.startAt }} - {{ item.endAt }}</span>\n </div>\n </mat-option>\n }\n </mat-optgroup>\n </mat-select>\n </mat-form-field>\n }\n </form>\n </div>\n <div>\n <button mat-flat-button type=\"primary\" (click)=\"update()\">\u4FDD\u5B58</button>\n </div>\n }\n</rolatech-course-manage-content>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i4.NgModelGroup, selector: "[ngModelGroup]", inputs: ["ngModelGroup"], exportAs: ["ngModelGroup"] }, { kind: "directive", type: i4.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1$2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "directive", type: i3.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i6$1.MatOptgroup, selector: "mat-optgroup", inputs: ["label", "disabled"], exportAs: ["matOptgroup"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: CourseManageContentComponent, selector: "rolatech-course-manage-content" }] }); }
2466
+ }
2467
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseManageInfoComponent, decorators: [{
2468
+ type: Component,
2469
+ args: [{ selector: 'rolatech-course-manage-info', standalone: true, imports: [
2470
+ FormsModule,
2471
+ MatFormFieldModule,
2472
+ MatInputModule,
2473
+ TextFieldModule,
2474
+ MatSelectModule,
2475
+ MatOptionModule,
2476
+ MatButtonModule,
2477
+ ToolbarComponent,
2478
+ CourseManageContentComponent,
2479
+ ], template: "<rolatech-course-manage-content>\n <rolatech-toolbar title=\"\u57FA\u672C\u4FE1\u606F\" class=\"hidden md:block\" divider></rolatech-toolbar>\n @if (course) {\n <div class=\"flex flex-col\">\n <form #courseForm=\"ngForm\" (ngSubmit)=\"update()\">\n <mat-form-field appearance=\"fill\">\n <mat-label> \u8BFE\u7A0B\u540D\u79F0 </mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"course.name\" name=\"name\" required />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label> \u8BFE\u7A0B\u63CF\u8FF0 </mat-label>\n <textarea\n matInput\n type=\"text\"\n [(ngModel)]=\"course.description\"\n name=\"description\"\n required\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"\n ></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"fill\" ngModelGroup=\"categories\" required>\n <mat-label>\u5206\u7C7B</mat-label>\n <mat-select\n multiple\n name=\"id\"\n [compareWith]=\"compareFn\"\n [(ngModel)]=\"course.categories\"\n #select=\"matSelect\"\n (selectionChange)=\"onSelectionChange($event)\"\n >\n @for (item of categories; track item) {\n <mat-option [value]=\"item\">\n {{ item.name }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label>\u7C7B\u578B</mat-label>\n <mat-select name=\"type\" [(ngModel)]=\"course.type\" required>\n @for (item of courseType; track item) {\n <mat-option [value]=\"item.key\">\n {{ item.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n @if (course.type && course.type.toString() !== 'ONLINE') {\n <mat-form-field appearance=\"fill\" ngModelGroup=\"classroom\">\n <mat-label>\u6559\u5BA4</mat-label>\n <mat-select\n name=\"id\"\n [(ngModel)]=\"course.classroom\"\n [compareWith]=\"compareClassroom\"\n (selectionChange)=\"onClassroomSelected($event)\"\n required\n >\n <mat-optgroup label=\"\u81EA\u6709\u6559\u5BA4\">\n @for (classroom of classrooms; track classroom) {\n <mat-option [value]=\"classroom\">\n <div class=\"flex gap-3\">\n <span>{{ classroom.name }}</span>\n <span> {{ classroom.address }}</span>\n </div>\n </mat-option>\n }\n </mat-optgroup>\n <mat-optgroup label=\"\u5DF2\u8D2D\u6559\u5BA4\">\n @for (item of bookedClassrooms; track item) {\n <mat-option [value]=\"item\">\n <div class=\"flex flex-col py-3\">\n <span class=\"font-bold\"> {{ item.address }} {{ item.room }}\u5BA4</span>\n <span class=\"text-md\"> {{ item.startAt }} - {{ item.endAt }}</span>\n </div>\n </mat-option>\n }\n </mat-optgroup>\n </mat-select>\n </mat-form-field>\n }\n </form>\n </div>\n <div>\n <button mat-flat-button type=\"primary\" (click)=\"update()\">\u4FDD\u5B58</button>\n </div>\n }\n</rolatech-course-manage-content>\n", styles: ["mat-form-field{width:100%}\n"] }]
2480
+ }] });
2481
+
2482
+ const SIZE = 10 * 1024 * 1024; // file slice size 10MB
2483
+ class CourseManageMediaComponent {
2484
+ constructor() {
2485
+ this.route = inject(ActivatedRoute);
2486
+ this.courseService = inject(CourseService);
2487
+ this.dialog = inject(MatDialog);
2488
+ this.snackBar = inject(MatSnackBar);
2489
+ this.isUploading = false;
2490
+ this.isLoading = false;
2491
+ this.media = [];
2492
+ this.status = CourseStatus;
2493
+ this.courseType = CourseType;
2494
+ this.id = this.route.parent?.snapshot.paramMap.get('id');
2495
+ }
2496
+ ngOnInit() {
2497
+ this.find();
2498
+ }
2499
+ find() {
2500
+ this.courseService.get(this.id).subscribe({
2501
+ next: (res) => {
2502
+ this.media = res.data.media || [];
2503
+ },
2504
+ });
2505
+ }
2506
+ onImageClick(i) {
2507
+ const dialogRef = this.dialog.open(ImagePreviewDialogComponent, {
2508
+ maxWidth: '80vw',
2509
+ maxHeight: '80vh',
2510
+ height: '80%',
2511
+ width: '80%',
2512
+ panelClass: 'full-screen-modal',
2513
+ data: {
2514
+ media: this.media,
2515
+ selected: i,
2516
+ },
2517
+ });
2518
+ dialogRef.afterClosed().subscribe((result) => { });
2519
+ }
2520
+ createFileChunk(file, size = SIZE) {
2521
+ const fileChunkList = [];
2522
+ let cur = 0;
2523
+ while (cur < file.size) {
2524
+ fileChunkList.push({ file: file.slice(cur, cur + size) });
2525
+ cur += size;
2526
+ }
2527
+ return fileChunkList;
2528
+ }
2529
+ onUploadMedia2(event) {
2530
+ const file = event.target.files[0];
2531
+ const reader = new FileReader();
2532
+ reader.onload = (e) => { };
2533
+ const fileChunkList = this.createFileChunk(file);
2534
+ fileChunkList.forEach((item) => { });
2535
+ }
2536
+ onUploadMedia(event) {
2537
+ const file = event.target.files[0];
2538
+ // 5MB * 1024 * 1024 = 5242880
2539
+ // if (file?.size > 5242880) {
2540
+ // this.snackBar.open('尺寸过大, 请修改后上传');
2541
+ // this.isUploading = false;
2542
+ // return;
2543
+ // }
2544
+ if (file) {
2545
+ const reader = new FileReader();
2546
+ const formData = new FormData();
2547
+ formData.append('file', file);
2548
+ reader.readAsDataURL(file);
2549
+ reader.onload = () => {
2550
+ const img = new Image();
2551
+ img.onload = () => {
2552
+ this.media.push({
2553
+ url: img.src,
2554
+ alt: 'upload image',
2555
+ width: img.width,
2556
+ height: img.height,
2557
+ });
2558
+ this.isUploading = true;
2559
+ };
2560
+ img.src = reader.result; // This is the data URL
2561
+ };
2562
+ this.courseService.uploadMedia(this.id, formData).subscribe({
2563
+ next: (res) => {
2564
+ this.isUploading = false;
2565
+ const index = findLastIndex(this.media);
2566
+ // Replace item at index using native splice
2567
+ this.media.splice(index, 1, res.data);
2568
+ },
2569
+ error: (e) => {
2570
+ this.isUploading = false;
2571
+ this.snackBar.open('上传失败: ' + e.message);
2572
+ },
2573
+ });
2574
+ reader.onerror = (error) => {
2575
+ this.isUploading = false;
2576
+ };
2577
+ }
2578
+ }
2579
+ onMediaDelete(item) {
2580
+ const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
2581
+ width: '400px',
2582
+ data: {
2583
+ title: '删除图片',
2584
+ message: '确定删除这张课程图片吗?',
2585
+ },
2586
+ });
2587
+ dialogRef.afterClosed().subscribe((result) => {
2588
+ if (result) {
2589
+ this.courseService.deleteMedia(this.id, item.id).subscribe({
2590
+ next: (res) => {
2591
+ this.media = this.media.filter((m) => m.id !== item.id);
2592
+ this.snackBar.open('删除成功');
2593
+ },
2594
+ error: (e) => {
2595
+ this.snackBar.open(e.message);
2596
+ },
2597
+ });
2598
+ }
2599
+ });
2600
+ }
2601
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseManageMediaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2602
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseManageMediaComponent, isStandalone: true, selector: "rolatech-course-manage-media", ngImport: i0, template: "<rolatech-course-manage-content>\n <rolatech-toolbar title=\"\u56FE\u7247\u4FE1\u606F\" class=\"hidden md:block\" divider></rolatech-toolbar>\n <div>\n <p>*\u56FE\u7247\u6587\u4EF6\u5927\u5C0F\u9650\u5236\u57285MB\u4EE5\u4E0B*</p>\n <rolatech-media-list (upload)=\"onUploadMedia($event)\" [isUploading]=\"isUploading\">\n @for (item of media; track item; let i = $index) {\n <rolatech-media-list-item\n [media]=\"item\"\n (mediaItemClick)=\"onImageClick(i)\"\n (deleteMedia)=\"onMediaDelete(item)\"\n ></rolatech-media-list-item>\n }\n </rolatech-media-list>\n </div>\n</rolatech-course-manage-content>\n", styles: [""], dependencies: [{ kind: "component", type: MediaListComponent, selector: "rolatech-media-list", inputs: ["isUploading", "media", "showAdd"], outputs: ["mediaItemClick", "upload"] }, { kind: "component", type: MediaListItemComponent, selector: "rolatech-media-list-item", inputs: ["media", "uploadProgress"], outputs: ["mediaItemClick", "deleteMedia"] }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: CourseManageContentComponent, selector: "rolatech-course-manage-content" }] }); }
2603
+ }
2604
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseManageMediaComponent, decorators: [{
2605
+ type: Component,
2606
+ args: [{ selector: 'rolatech-course-manage-media', standalone: true, imports: [MediaListComponent, MediaListItemComponent, ToolbarComponent, CourseManageContentComponent], template: "<rolatech-course-manage-content>\n <rolatech-toolbar title=\"\u56FE\u7247\u4FE1\u606F\" class=\"hidden md:block\" divider></rolatech-toolbar>\n <div>\n <p>*\u56FE\u7247\u6587\u4EF6\u5927\u5C0F\u9650\u5236\u57285MB\u4EE5\u4E0B*</p>\n <rolatech-media-list (upload)=\"onUploadMedia($event)\" [isUploading]=\"isUploading\">\n @for (item of media; track item; let i = $index) {\n <rolatech-media-list-item\n [media]=\"item\"\n (mediaItemClick)=\"onImageClick(i)\"\n (deleteMedia)=\"onMediaDelete(item)\"\n ></rolatech-media-list-item>\n }\n </rolatech-media-list>\n </div>\n</rolatech-course-manage-content>\n" }]
2607
+ }], ctorParameters: () => [] });
2608
+
2609
+ class CourseManageSectionComponent extends BaseComponent {
2610
+ constructor() {
2611
+ super(...arguments);
2612
+ this.courseService = inject(CourseService);
2613
+ this.sections = [];
2614
+ this.hasUnsaved = false;
2615
+ this.editId = '';
2616
+ this.lectureEditId = '';
2617
+ this.chunkSize = 5 * 1024 * 1024; // 切片大小
2618
+ this.fileIndex = 0; // 当前正在被遍历的文件下标
2619
+ }
2620
+ ngOnInit() {
2621
+ this.id = this.route.parent?.snapshot.paramMap.get('id');
2622
+ this.find();
2623
+ }
2624
+ find() {
2625
+ this.courseService.getCourseSection(this.id).subscribe({
2626
+ next: (res) => {
2627
+ if (res.data) {
2628
+ this.sections = res.data;
2629
+ }
2630
+ },
2631
+ });
2632
+ }
2633
+ addSection() {
2634
+ const section = {
2635
+ title: '',
2636
+ description: '',
2637
+ };
2638
+ this.courseService.addCourseSection(this.id, section).subscribe({
2639
+ next: (res) => {
2640
+ this.sections.push(res.data);
2641
+ this.editId = res.data.id;
2642
+ },
2643
+ });
2644
+ this.hasUnsaved = false;
2645
+ }
2646
+ onCancel(event) {
2647
+ this.sections.pop();
2648
+ this.hasUnsaved = false;
2649
+ }
2650
+ onSectionSave(event) {
2651
+ const { id } = event;
2652
+ this.courseService.updateCourseSection(id, event).subscribe({
2653
+ next: (res) => {
2654
+ this.hasUnsaved = false;
2655
+ this.snackBarService.open('保存成功');
2656
+ },
2657
+ });
2658
+ this.hasUnsaved = false;
2659
+ }
2660
+ onSectionEdit(event) {
2661
+ this.editId = event.id;
2662
+ // this.editIndex = this.sections.find((item: any) => item.id === event.id);
2663
+ }
2664
+ onSectionDelete(event) {
2665
+ const { id } = event;
2666
+ const options = {
2667
+ title: '确认删除吗',
2668
+ message: '删除这个章节吗?',
2669
+ cancelText: '取消',
2670
+ confirmText: '确认',
2671
+ };
2672
+ this.dialogService.open(options);
2673
+ this.dialogService.confirmed().subscribe({
2674
+ next: (res) => {
2675
+ if (res) {
2676
+ const data = {
2677
+ name: res,
2678
+ };
2679
+ this.courseService.deleteCourseSection(id).subscribe({
2680
+ next: (res) => {
2681
+ this.sections.pop();
2682
+ this.hasUnsaved = false;
2683
+ this.snackBarService.open(res.data);
2684
+ },
2685
+ error: (error) => {
2686
+ this.snackBarService.open(error.message);
2687
+ },
2688
+ });
2689
+ }
2690
+ },
2691
+ });
2692
+ }
2693
+ addLecture(section) {
2694
+ const lecture = {
2695
+ title: '',
2696
+ type: 'VIDEO',
2697
+ };
2698
+ this.courseService.addLecture(section.id, lecture).subscribe({
2699
+ next: (res) => {
2700
+ this.lectureEditId = res.data.id;
2701
+ if (section.lectures) {
2702
+ section.lectures.push(res.data);
2703
+ }
2704
+ else {
2705
+ const lectures = [];
2706
+ lectures.push(res.data);
2707
+ section.lectures = lectures;
2708
+ }
2709
+ },
2710
+ });
2711
+ }
2712
+ onAddLecture(event) { }
2713
+ onLectureSave(lecture) {
2714
+ this.courseService.updateLecture(lecture.id, lecture).subscribe({
2715
+ next: (res) => {
2716
+ this.snackBarService.open('保存成功');
2717
+ },
2718
+ });
2719
+ }
2720
+ onLectureEdit(lecture) { }
2721
+ onMediaEdit(lecture, video) {
2722
+ const { id } = lecture;
2723
+ const options = {
2724
+ title: lecture.title,
2725
+ cancelText: '取消',
2726
+ confirmText: '保存',
2727
+ data: lecture,
2728
+ width: '80vw',
2729
+ height: '100%',
2730
+ component: CourseSectionLectureVideoDialogComponent,
2731
+ };
2732
+ this.dialogService.open(options);
2733
+ this.dialogService.confirmed().subscribe({
2734
+ next: (res) => {
2735
+ if (res) {
2736
+ // this.courseService.deleteLecture(id).subscribe({
2737
+ // next: (res) => {},
2738
+ // error: (error) => {
2739
+ // this.snackBarService.open(error.message);
2740
+ // },
2741
+ // });
2742
+ }
2743
+ },
2744
+ });
2745
+ }
2746
+ onLectureDelete(section, lecture) {
2747
+ const { id } = lecture;
2748
+ const options = {
2749
+ title: '确认删除吗',
2750
+ message: '删除这一小节吗?',
2751
+ cancelText: '取消',
2752
+ confirmText: '确认',
2753
+ };
2754
+ this.dialogService.open(options);
2755
+ this.dialogService.confirmed().subscribe({
2756
+ next: (res) => {
2757
+ if (res) {
2758
+ this.courseService.deleteLecture(id).subscribe({
2759
+ next: (res) => {
2760
+ const index = section.lectures.findIndex((item) => item.id === id);
2761
+ section.lectures.splice(index, 1);
2762
+ this.hasUnsaved = false;
2763
+ this.snackBarService.open(res.data);
2764
+ },
2765
+ error: (error) => {
2766
+ this.snackBarService.open(error.message);
2767
+ },
2768
+ });
2769
+ }
2770
+ },
2771
+ });
2772
+ }
2773
+ onMediaUploadInit(lecture, event) {
2774
+ const file = event.data.target.files[0];
2775
+ if (!file) {
2776
+ return;
2777
+ }
2778
+ const item = {
2779
+ url: URL.createObjectURL(file),
2780
+ };
2781
+ lecture.item = item;
2782
+ lecture.isUploading = true;
2783
+ lecture.item.progress = 0;
2784
+ const reader = new FileReader();
2785
+ if (reader.readyState === FileReader.EMPTY) {
2786
+ reader.onload = async (e) => {
2787
+ const fileBuffer = e.target.result;
2788
+ crypto.subtle.digest('SHA-256', fileBuffer).then((res) => {
2789
+ const hashArray = Array.from(new Uint8Array(res));
2790
+ const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, '0')).join('');
2791
+ const data = { hash: hashHex, filename: file.name, fileType: file.type };
2792
+ this.courseService.uploadVideoInit(lecture.id, data).subscribe({
2793
+ next: (res) => {
2794
+ this.uploadPart(lecture, res.data.uploadId, file);
2795
+ },
2796
+ error: (e) => { },
2797
+ });
2798
+ });
2799
+ };
2800
+ reader.onerror = (error) => { };
2801
+ reader.readAsArrayBuffer(file);
2802
+ }
2803
+ }
2804
+ uploadFile(lecture, file, index, uploadId) {
2805
+ const reader = new FileReader();
2806
+ return new Observable((observer) => {
2807
+ reader.readAsArrayBuffer(file);
2808
+ reader.onload = (e) => {
2809
+ const fileBuffer = e.target.result;
2810
+ crypto.subtle.digest('SHA-256', fileBuffer).then((res) => {
2811
+ const hashArray = Array.from(new Uint8Array(res));
2812
+ const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, '0')).join('');
2813
+ const formData = new FormData();
2814
+ formData.append('file', file);
2815
+ formData.append('uploadId', uploadId);
2816
+ formData.append('number', index + 1);
2817
+ formData.append('hash', hashHex);
2818
+ this.courseService.uploadVideoPartsToLecture(lecture.id, formData).subscribe({
2819
+ next: (res) => {
2820
+ observer.next(res);
2821
+ observer.complete();
2822
+ },
2823
+ error: (e) => {
2824
+ this.snackBarService.open('上传失败: ' + e.message);
2825
+ },
2826
+ });
2827
+ });
2828
+ };
2829
+ reader.onerror = (error) => {
2830
+ this.snackBarService.open('上传失败: ' + error);
2831
+ };
2832
+ });
2833
+ }
2834
+ uploadPart(lecture, uploadId, file) {
2835
+ let uploadedCount = 0;
2836
+ const fileChunkList = this.createFileChunk(file);
2837
+ const numberOfChunks = fileChunkList.length;
2838
+ from(fileChunkList)
2839
+ .pipe(concatMap((val, index) => this.uploadFile(lecture, val.file, index, uploadId)), take(numberOfChunks))
2840
+ .subscribe((res) => {
2841
+ uploadedCount++;
2842
+ const p = ((uploadedCount / numberOfChunks) * 100).toFixed(0);
2843
+ lecture.item.progress = parseFloat(p);
2844
+ if (uploadedCount === numberOfChunks) {
2845
+ this.completePartUpload(lecture, { uploadId });
2846
+ }
2847
+ });
2848
+ }
2849
+ completePartUpload(lecture, data) {
2850
+ data.duration = lecture.item.duration;
2851
+ this.courseService.completePartUpload(lecture.id, data).subscribe({
2852
+ next: (res) => {
2853
+ lecture.isUploading = false;
2854
+ lecture.item.id = res.data.id;
2855
+ },
2856
+ error: (e) => {
2857
+ this.snackBarService.open('上传失败: ' + e.message);
2858
+ },
2859
+ });
2860
+ }
2861
+ onMediaUpload(lecture, event) {
2862
+ const lectureId = event.id;
2863
+ const file = event.data.target.files[0];
2864
+ if (!file) {
2865
+ return;
2866
+ }
2867
+ const reader = new FileReader();
2868
+ reader.readAsDataURL(file);
2869
+ reader.onload = () => {
2870
+ const formData = new FormData();
2871
+ formData.append('file', file);
2872
+ this.courseService.uploadVideoToLecture(lectureId, formData).subscribe({
2873
+ next: (res) => {
2874
+ lecture.isUploading = false;
2875
+ lecture.item = res.data;
2876
+ },
2877
+ error: (e) => {
2878
+ lecture.isUploading = false;
2879
+ this.snackBarService.open('上传失败: ' + e.message);
2880
+ },
2881
+ });
2882
+ };
2883
+ reader.onerror = (error) => { };
2884
+ }
2885
+ onMediaDelete(lecture, event) {
2886
+ // lecture.item = null;
2887
+ const { id } = event;
2888
+ const options = {
2889
+ title: '确认删除吗',
2890
+ message: '删除这个视频吗?',
2891
+ cancelText: '取消',
2892
+ confirmText: '确认',
2893
+ };
2894
+ this.dialogService.open(options);
2895
+ this.dialogService.confirmed().subscribe({
2896
+ next: (res) => {
2897
+ if (res) {
2898
+ this.courseService.deleteLectureVideo(id).subscribe({
2899
+ next: (res) => {
2900
+ lecture.item = null;
2901
+ this.snackBarService.open(res.data);
2902
+ },
2903
+ error: (error) => {
2904
+ this.snackBarService.open(error.message);
2905
+ },
2906
+ });
2907
+ }
2908
+ },
2909
+ });
2910
+ }
2911
+ createFileChunk(file, size = this.chunkSize) {
2912
+ const fileChunkList = [];
2913
+ let count = 0;
2914
+ while (count < file.size) {
2915
+ fileChunkList.push({
2916
+ file: file.slice(count, count + size),
2917
+ });
2918
+ count += size;
2919
+ }
2920
+ return fileChunkList;
2921
+ }
2922
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseManageSectionComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2923
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseManageSectionComponent, isStandalone: true, selector: "rolatech-course-manage-section", usesInheritance: true, ngImport: i0, template: "<rolatech-course-manage-content>\n <rolatech-toolbar title=\"\u7AE0\u8282\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <div>\n <div>\n @for (section of sections; track $index) {\n <div class=\"bg-[--rt-raised-background] p-2 mb-2 rounded-lg\">\n <rolatech-course-section-item\n [section]=\"section\"\n (edit)=\"onSectionEdit($event)\"\n (save)=\"onSectionSave($event)\"\n (delete)=\"onSectionDelete($event)\"\n [editId]=\"editId\"\n >\n <div class=\"bg-[--rt-base-background] md:ml-8 mt-3 rounded-md divide-y divide-dashed\">\n @if (section?.lectures) {\n @for (lecture of section.lectures; track $index) {\n <div>\n <rolatech-course-section-lecture-item\n [lecture]=\"lecture\"\n (save)=\"onLectureSave($event)\"\n (delete)=\"onLectureDelete(section, $event)\"\n (edit)=\"onLectureEdit($event)\"\n (upload)=\"onMediaUploadInit(lecture, $event)\"\n (mediaEdit)=\"onMediaEdit(lecture, $event)\"\n (deleteMedia)=\"onMediaDelete(lecture, $event)\"\n [editId]=\"lectureEditId\"\n ></rolatech-course-section-lecture-item>\n </div>\n }\n }\n <div class=\"p-3\">\n <button mat-flat-button class=\"max-h-8\" (click)=\"addLecture(section)\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u5C0F\u8282</span>\n </button>\n </div>\n </div>\n </rolatech-course-section-item>\n </div>\n }\n </div>\n @if (!hasUnsaved) {\n <button mat-stroked-button (click)=\"addSection()\" class=\"mt-3\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u7AE0\u8282</span>\n </button>\n }\n </div>\n</rolatech-course-manage-content>\n", styles: [""], dependencies: [{ kind: "component", type: CourseSectionItemComponent, selector: "rolatech-course-section-item", inputs: ["section", "actions", "hasUnsaved", "editId"], outputs: ["sectionChange", "actionsChange", "hasUnsavedChange", "editIdChange", "save", "cancel", "delete", "edit", "addLecture"] }, { kind: "component", type: CourseSectionLectureItemComponent, selector: "rolatech-course-section-lecture-item", inputs: ["progress", "lecture", "actions", "hasUnsaved", "editId"], outputs: ["editIdChange", "save", "cancel", "delete", "edit", "mediaEdit", "thumbnailUpload", "upload", "deleteMedia"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: CourseManageContentComponent, selector: "rolatech-course-manage-content" }] }); }
2924
+ }
2925
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseManageSectionComponent, decorators: [{
2926
+ type: Component,
2927
+ args: [{ selector: 'rolatech-course-manage-section', standalone: true, imports: [
2928
+ CourseSectionItemComponent,
2929
+ CourseSectionLectureItemComponent,
2930
+ MatButtonModule,
2931
+ MatIconModule,
2932
+ MatProgressBarModule,
2933
+ ToolbarComponent,
2934
+ CourseManageContentComponent,
2935
+ ], template: "<rolatech-course-manage-content>\n <rolatech-toolbar title=\"\u7AE0\u8282\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <div>\n <div>\n @for (section of sections; track $index) {\n <div class=\"bg-[--rt-raised-background] p-2 mb-2 rounded-lg\">\n <rolatech-course-section-item\n [section]=\"section\"\n (edit)=\"onSectionEdit($event)\"\n (save)=\"onSectionSave($event)\"\n (delete)=\"onSectionDelete($event)\"\n [editId]=\"editId\"\n >\n <div class=\"bg-[--rt-base-background] md:ml-8 mt-3 rounded-md divide-y divide-dashed\">\n @if (section?.lectures) {\n @for (lecture of section.lectures; track $index) {\n <div>\n <rolatech-course-section-lecture-item\n [lecture]=\"lecture\"\n (save)=\"onLectureSave($event)\"\n (delete)=\"onLectureDelete(section, $event)\"\n (edit)=\"onLectureEdit($event)\"\n (upload)=\"onMediaUploadInit(lecture, $event)\"\n (mediaEdit)=\"onMediaEdit(lecture, $event)\"\n (deleteMedia)=\"onMediaDelete(lecture, $event)\"\n [editId]=\"lectureEditId\"\n ></rolatech-course-section-lecture-item>\n </div>\n }\n }\n <div class=\"p-3\">\n <button mat-flat-button class=\"max-h-8\" (click)=\"addLecture(section)\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u5C0F\u8282</span>\n </button>\n </div>\n </div>\n </rolatech-course-section-item>\n </div>\n }\n </div>\n @if (!hasUnsaved) {\n <button mat-stroked-button (click)=\"addSection()\" class=\"mt-3\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0\u7AE0\u8282</span>\n </button>\n }\n </div>\n</rolatech-course-manage-content>\n" }]
2936
+ }] });
2937
+
2938
+ class CourseManageLayoutComponent {
2939
+ constructor() {
2940
+ this.submitted = false;
2941
+ this.accepted = false;
2942
+ this.isDraft = false;
2943
+ this.route = inject(ActivatedRoute);
2944
+ this.courseService = inject(CourseService);
2945
+ this.dialog = inject(MatDialog);
2946
+ this.snackBar = inject(MatSnackBar);
2947
+ this.id = this.route.parent?.snapshot.paramMap.get('id');
2948
+ }
2949
+ ngOnInit() {
2950
+ this.get();
2951
+ }
2952
+ get() {
2953
+ this.courseService.get(this.id).subscribe({
2954
+ next: (res) => {
2955
+ this.course = res.data;
2956
+ this.updateStatus();
2957
+ },
2958
+ });
2959
+ }
2960
+ updateStatus() {
2961
+ this.isDraft = this.course.status.toString() === 'DRAFT' || this.course.status.toString() === 'PENDING';
2962
+ this.accepted = this.course.status.toString() === 'ACCEPTED';
2963
+ }
2964
+ submitForReview() {
2965
+ this.courseService.review(this.id).subscribe({
2966
+ next: (res) => {
2967
+ this.snackBar.open('提交成功, 等待审核');
2968
+ this.course.status = CourseStatus['审核中'];
2969
+ this.updateStatus();
2970
+ },
2971
+ error: (error) => {
2972
+ this.snackBar.open(error.message);
2973
+ },
2974
+ });
2975
+ }
2976
+ publish() {
2977
+ this.courseService.publish(this.id).subscribe({
2978
+ next: (res) => {
2979
+ this.snackBar.open('发布成功');
2980
+ this.course.status = CourseStatus['已发布'];
2981
+ this.updateStatus();
2982
+ },
2983
+ error: (error) => {
2984
+ this.snackBar.open(error.message);
2985
+ },
2986
+ });
2987
+ }
2988
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseManageLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2989
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: CourseManageLayoutComponent, isStandalone: true, selector: "rolatech-course-manage-layout", ngImport: i0, template: "<div class=\"flex flex-col md:flex-row m-auto\">\n <div\n class=\"min-w-[256px] px-3 flex flex-row md:flex-col md:h-full items-center md:items-start h-16 overflow-x-scroll overflow-y-hidden scrollbar-hide whitespace-pre\"\n >\n <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-2\">\u8BFE\u7A0B\u4FE1\u606F</div>\n <a routerLink=\"./info\" routerLinkActive=\"manage-active\" class=\"p-2\">\u57FA\u672C\u4FE1\u606F</a>\n <a routerLink=\"./media\" routerLinkActive=\"manage-active\" class=\"p-2\">\u56FE\u7247</a>\n <a routerLink=\"./details\" routerLinkActive=\"manage-active\" class=\"p-2\">\u8BE6\u60C5</a>\n </div>\n <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-2\">\u8BFE\u7A0B\u5185\u5BB9</div>\n <a routerLink=\"./section\" routerLinkActive=\"manage-active\" class=\"p-2\">\u7AE0\u8282</a>\n <a routerLink=\"./schedule\" routerLinkActive=\"manage-active\" class=\"p-2\">\u8BFE\u8868</a>\n </div>\n <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-2\">\u5176\u4ED6\u4FE1\u606F</div>\n <a routerLink=\"./pricing\" routerLinkActive=\"manage-active\" class=\"p-2\">\u4EF7\u683C</a>\n </div>\n\n @if (isDraft) {\n <div class=\"md:mt-6 ml-3 md:ml-2\">\n <!-- <button mat-stroked-button (click)=\"publish()\">\u9884\u89C8</button> -->\n <button mat-flat-button (click)=\"submitForReview()\">\u63D0\u4EA4\u5BA1\u6838</button>\n </div>\n }\n\n @if (accepted) {\n <div class=\"md:mt-6 md:ml-2\">\n <button mat-flat-button color=\"primary\" (click)=\"publish()\">\u53D1\u5E03\u8BFE\u7A0B</button>\n </div>\n }\n </div>\n <div class=\"w-full\">\n <router-outlet></router-outlet>\n </div>\n</div>\n", styles: [".manage-active{background-color:var(--rt-10-percent-layer, rgba(0, 0, 0, .05));box-shadow:4px 0 var(--rt-base-background-inverse, #000) inset;font-weight:600}@media (max-width: 768px){.manage-active{box-shadow:inset 0 -4px 0 0 var(--rt-base-background-inverse, #000)}}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] }); }
2990
+ }
2991
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: CourseManageLayoutComponent, decorators: [{
2992
+ type: Component,
2993
+ args: [{ selector: 'rolatech-course-manage-layout', standalone: true, imports: [RouterLink, RouterLinkActive, MatButtonModule, RouterOutlet, CourseManageContentComponent], template: "<div class=\"flex flex-col md:flex-row m-auto\">\n <div\n class=\"min-w-[256px] px-3 flex flex-row md:flex-col md:h-full items-center md:items-start h-16 overflow-x-scroll overflow-y-hidden scrollbar-hide whitespace-pre\"\n >\n <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-2\">\u8BFE\u7A0B\u4FE1\u606F</div>\n <a routerLink=\"./info\" routerLinkActive=\"manage-active\" class=\"p-2\">\u57FA\u672C\u4FE1\u606F</a>\n <a routerLink=\"./media\" routerLinkActive=\"manage-active\" class=\"p-2\">\u56FE\u7247</a>\n <a routerLink=\"./details\" routerLinkActive=\"manage-active\" class=\"p-2\">\u8BE6\u60C5</a>\n </div>\n <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-2\">\u8BFE\u7A0B\u5185\u5BB9</div>\n <a routerLink=\"./section\" routerLinkActive=\"manage-active\" class=\"p-2\">\u7AE0\u8282</a>\n <a routerLink=\"./schedule\" routerLinkActive=\"manage-active\" class=\"p-2\">\u8BFE\u8868</a>\n </div>\n <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-2\">\u5176\u4ED6\u4FE1\u606F</div>\n <a routerLink=\"./pricing\" routerLinkActive=\"manage-active\" class=\"p-2\">\u4EF7\u683C</a>\n </div>\n\n @if (isDraft) {\n <div class=\"md:mt-6 ml-3 md:ml-2\">\n <!-- <button mat-stroked-button (click)=\"publish()\">\u9884\u89C8</button> -->\n <button mat-flat-button (click)=\"submitForReview()\">\u63D0\u4EA4\u5BA1\u6838</button>\n </div>\n }\n\n @if (accepted) {\n <div class=\"md:mt-6 md:ml-2\">\n <button mat-flat-button color=\"primary\" (click)=\"publish()\">\u53D1\u5E03\u8BFE\u7A0B</button>\n </div>\n }\n </div>\n <div class=\"w-full\">\n <router-outlet></router-outlet>\n </div>\n</div>\n", styles: [".manage-active{background-color:var(--rt-10-percent-layer, rgba(0, 0, 0, .05));box-shadow:4px 0 var(--rt-base-background-inverse, #000) inset;font-weight:600}@media (max-width: 768px){.manage-active{box-shadow:inset 0 -4px 0 0 var(--rt-base-background-inverse, #000)}}\n"] }]
2994
+ }], ctorParameters: () => [] });
2995
+
2996
+ const courseManageRoutes = [
2997
+ {
2998
+ path: '',
2999
+ component: CourseManageLayoutComponent,
3000
+ children: [
3001
+ {
3002
+ path: 'info',
3003
+ component: CourseManageInfoComponent,
3004
+ },
3005
+ {
3006
+ path: 'media',
3007
+ component: CourseManageMediaComponent,
3008
+ },
3009
+ {
3010
+ path: 'details',
3011
+ component: CourseManageDetailsComponent,
3012
+ },
3013
+ {
3014
+ path: 'pricing',
3015
+ component: CourseManagePricingComponent,
3016
+ },
3017
+ {
3018
+ path: 'schedule',
3019
+ component: CourseManageScheduleComponent,
3020
+ },
3021
+ {
3022
+ path: 'section',
3023
+ component: CourseManageSectionComponent,
3024
+ },
3025
+ ],
3026
+ },
3027
+ ];
3028
+
3029
+ /**
3030
+ * Generated bundle index. Do not edit.
3031
+ */
3032
+
3033
+ export { CategoryService, CourseActionComponent, CourseDetailsComponent, CourseDetailsDialogComponent, CourseEditDialogComponent, CourseInfoComponent, CourseItemComponent, CourseMediaComponent, CourseMediaOwnerRendererComponent, CoursePreviewComponent, CoursePricingAddDialogComponent, CoursePricingComponent, CoursePricingDialogComponent, CourseRequestStatus, CourseReviewStatus, CourseScheduleAddDialogComponent, CourseScheduleComponent, CourseScheduleDialogComponent, CourseSectionItemComponent, CourseSectionLectureContentType, CourseSectionLectureItemComponent, CourseSectionLectureVideoDialogComponent, CourseSectionLectureVideoItemComponent, CourseSectionService, CourseSectionsComponent, CourseService, CourseStatus, CourseType, DetailItemComponent, PricingItemComponent, ScheduleDate, ScheduleItemComponent, courseManageRoutes, courseRoutes, provideAngularCourse, services };
29
3034
  //# sourceMappingURL=rolatech-angular-course.mjs.map