@rolatech/angular-course 17.3.0 → 17.3.2

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