@rolatech/angular-product 17.0.0 → 17.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/esm2022/index.mjs +5 -12
  2. package/esm2022/lib/components/index.mjs +12 -0
  3. package/esm2022/lib/components/product-action/product-action.component.mjs +25 -0
  4. package/esm2022/lib/components/product-info/product-info.component.mjs +17 -0
  5. package/esm2022/lib/components/product-item/product-item.component.mjs +17 -0
  6. package/esm2022/lib/components/product-manage-option-add/product-manage-option-add.component.mjs +68 -0
  7. package/esm2022/lib/components/product-manage-option-item/product-manage-option-item.component.mjs +49 -0
  8. package/esm2022/lib/components/product-manage-variant/product-manage-variant.component.mjs +115 -0
  9. package/esm2022/lib/components/product-mange-option-action/product-manage-option-action.component.mjs +22 -0
  10. package/esm2022/lib/components/product-mange-section-item/product-mange-section-item.component.mjs +89 -0
  11. package/esm2022/lib/components/product-media/product-media.component.mjs +34 -0
  12. package/esm2022/lib/components/product-option/product-option.component.mjs +23 -0
  13. package/esm2022/lib/components/product-owner-renderer/product-owner-renderer.component.mjs +20 -0
  14. package/esm2022/lib/components/product-pricing/product-pricing.component.mjs +31 -0
  15. package/esm2022/lib/components/product-section/product-section.component.mjs +26 -0
  16. package/esm2022/lib/interfaces/category.mjs +2 -0
  17. package/esm2022/lib/interfaces/index.mjs +2 -0
  18. package/esm2022/lib/interfaces/product.mjs +32 -0
  19. package/esm2022/lib/pages/product/product-category/product-category.component.mjs +70 -8
  20. package/esm2022/lib/pages/product/product-details/product-details.component.mjs +193 -0
  21. package/esm2022/lib/pages/product/product-details/selected.pipe.mjs +18 -0
  22. package/esm2022/lib/pages/product/product-index/product-index.component.mjs +39 -8
  23. package/esm2022/lib/pages/product/product-layout/product-layout.component.mjs +46 -8
  24. package/esm2022/lib/pages/product/product.routes.mjs +3 -3
  25. package/esm2022/lib/pages/product-manage/product-manage-content/product-manage-content.component.mjs +5 -5
  26. package/esm2022/lib/pages/product-manage/product-manage-create/product-manage-create.component.mjs +61 -0
  27. package/esm2022/lib/pages/product-manage/product-manage-index/product-manage-index.component.mjs +155 -0
  28. package/esm2022/lib/pages/product-manage/product-manage-info/product-manage-info.component.mjs +98 -8
  29. package/esm2022/lib/pages/product-manage/product-manage-inventory/product-manage-inventory.component.mjs +14 -0
  30. package/esm2022/lib/pages/product-manage/product-manage-item/product-manage-item.component.mjs +27 -0
  31. package/esm2022/lib/pages/product-manage/product-manage-item-header/product-manage-item-header.component.mjs +12 -0
  32. package/esm2022/lib/pages/product-manage/product-manage-layout/product-manage-layout.component.mjs +51 -8
  33. package/esm2022/lib/pages/product-manage/product-manage-media/product-manage-media.component.mjs +133 -8
  34. package/esm2022/lib/pages/product-manage/product-manage-options/product-manage-options.component.mjs +102 -0
  35. package/esm2022/lib/pages/product-manage/product-manage-pricing/product-manage-pricing.component.mjs +5 -5
  36. package/esm2022/lib/pages/product-manage/product-manage-schedule/product-manage-schedule.component.mjs +14 -0
  37. package/esm2022/lib/pages/product-manage/product-manage-sections/product-manage-sections.component.mjs +170 -0
  38. package/esm2022/lib/pages/product-manage/product-manage-variants/product-manage-variants.component.mjs +133 -0
  39. package/esm2022/lib/pages/product-manage/product-manage.routes.mjs +34 -4
  40. package/esm2022/lib/services/index.mjs +4 -0
  41. package/esm2022/lib/services/product-category.service.mjs +38 -0
  42. package/esm2022/provider.mjs +13 -0
  43. package/fesm2022/rolatech-angular-product-product-index.component-C_cbg95R.mjs +70 -0
  44. package/fesm2022/rolatech-angular-product-product-index.component-C_cbg95R.mjs.map +1 -0
  45. package/fesm2022/rolatech-angular-product.mjs +1697 -76
  46. package/fesm2022/rolatech-angular-product.mjs.map +1 -1
  47. package/index.d.ts +4 -11
  48. package/lib/components/index.d.ts +11 -0
  49. package/lib/components/product-action/product-action.component.d.ts +11 -0
  50. package/lib/components/product-info/product-info.component.d.ts +7 -0
  51. package/lib/components/product-item/product-item.component.d.ts +7 -0
  52. package/lib/components/product-manage-option-add/product-manage-option-add.component.d.ts +20 -0
  53. package/lib/components/product-manage-option-item/product-manage-option-item.component.d.ts +14 -0
  54. package/lib/components/product-manage-variant/product-manage-variant.component.d.ts +18 -0
  55. package/lib/components/product-mange-option-action/product-manage-option-action.component.d.ts +10 -0
  56. package/lib/components/product-mange-section-item/product-mange-section-item.component.d.ts +33 -0
  57. package/lib/components/product-media/product-media.component.d.ts +12 -0
  58. package/lib/components/product-option/product-option.component.d.ts +14 -0
  59. package/lib/components/product-owner-renderer/product-owner-renderer.component.d.ts +8 -0
  60. package/lib/components/product-pricing/product-pricing.component.d.ts +12 -0
  61. package/lib/components/product-section/product-section.component.d.ts +16 -0
  62. package/lib/interfaces/category.d.ts +16 -0
  63. package/lib/interfaces/index.d.ts +2 -0
  64. package/lib/interfaces/product.d.ts +91 -0
  65. package/lib/pages/product/product-category/product-category.component.d.ts +19 -2
  66. package/lib/pages/product/product-details/product-details.component.d.ts +39 -0
  67. package/lib/pages/product/product-details/selected.pipe.d.ts +8 -0
  68. package/lib/pages/product/product-index/product-index.component.d.ts +12 -2
  69. package/lib/pages/product/product-layout/product-layout.component.d.ts +15 -2
  70. package/lib/pages/product-manage/product-manage-content/product-manage-content.component.d.ts +1 -1
  71. package/lib/pages/product-manage/product-manage-create/product-manage-create.component.d.ts +11 -0
  72. package/lib/pages/product-manage/product-manage-index/product-manage-index.component.d.ts +38 -0
  73. package/lib/pages/product-manage/product-manage-info/product-manage-info.component.d.ts +26 -2
  74. package/lib/pages/product-manage/product-manage-inventory/product-manage-inventory.component.d.ts +5 -0
  75. package/lib/pages/product-manage/product-manage-item/product-manage-item.component.d.ts +12 -0
  76. package/lib/pages/product-manage/product-manage-item-header/product-manage-item-header.component.d.ts +5 -0
  77. package/lib/pages/product-manage/product-manage-layout/product-manage-layout.component.d.ts +22 -2
  78. package/lib/pages/product-manage/product-manage-media/product-manage-media.component.d.ts +26 -2
  79. package/lib/pages/product-manage/product-manage-options/product-manage-options.component.d.ts +17 -0
  80. package/lib/pages/product-manage/product-manage-pricing/product-manage-pricing.component.d.ts +1 -1
  81. package/lib/pages/product-manage/product-manage-schedule/product-manage-schedule.component.d.ts +5 -0
  82. package/lib/pages/product-manage/product-manage-sections/product-manage-sections.component.d.ts +29 -0
  83. package/lib/pages/product-manage/product-manage-variants/product-manage-variants.component.d.ts +27 -0
  84. package/lib/services/index.d.ts +3 -0
  85. package/lib/services/product-category.service.d.ts +12 -0
  86. package/package.json +1 -1
  87. package/provider.d.ts +2 -0
  88. package/themes/_default.scss +1 -1
  89. package/esm2022/lib/angular-product/angular-product.component.mjs +0 -12
  90. package/esm2022/lib/pages/product/product-detail/product-detail.component.mjs +0 -12
  91. package/esm2022/lib/pages/product-manage/product-manage-details/product-manage-details.component.mjs +0 -12
  92. package/lib/angular-product/angular-product.component.d.ts +0 -5
  93. package/lib/pages/product/product-detail/product-detail.component.d.ts +0 -5
  94. package/lib/pages/product-manage/product-manage-details/product-manage-details.component.d.ts +0 -5
@@ -1,34 +1,716 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Component } from '@angular/core';
3
- import { CommonModule } from '@angular/common';
2
+ import { Injectable, inject, Component, input, ViewEncapsulation, booleanAttribute, output, computed, model, Pipe, signal, effect, viewChild, ChangeDetectorRef, importProvidersFrom, makeEnvironmentProviders } from '@angular/core';
3
+ import { AngularCommonModule, OptionsFormatPipe, DecimalDirective } from '@rolatech/angular-common';
4
+ import { BaseComponent, AngularComponentsModule, ContainerComponent, ListComponent, RichViewComponent, RichItemComponent, ImagePreviewDialogComponent, ThumbnailComponent, ToolbarComponent, ConfirmationDialogComponent, MediaListComponent, MediaListItemComponent, ImagePlaceholderComponent, SpinnerComponent, AccordionComponent, PanelComponent, ImageComponent } from '@rolatech/angular-components';
5
+ import { BaseService, ProductService, CartService } from '@rolatech/angular-services';
6
+ import * as i1 from '@angular/router';
7
+ import { RouterModule, ActivatedRoute, RouterLink, RouterLinkActive, RouterOutlet, Router } from '@angular/router';
8
+ import * as i2$1 from '@angular/material/icon';
9
+ import { MatIconModule, MatIcon } from '@angular/material/icon';
10
+ import * as i1$1 from '@angular/common';
11
+ import { NgClass, ViewportScroller, CommonModule } from '@angular/common';
12
+ import { MatDialog } from '@angular/material/dialog';
13
+ import * as i1$2 from '@angular/material/button';
14
+ import { MatButtonModule } from '@angular/material/button';
15
+ import * as i9 from '@angular/material/divider';
16
+ import { MatDivider, MatDividerModule } from '@angular/material/divider';
17
+ import * as i4 from '@angular/cdk/text-field';
18
+ import { TextFieldModule } from '@angular/cdk/text-field';
19
+ import * as i1$3 from '@angular/forms';
20
+ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
21
+ import * as i2 from '@angular/material/form-field';
22
+ import { MatFormFieldModule } from '@angular/material/form-field';
23
+ import * as i3 from '@angular/material/input';
24
+ import { MatInputModule } from '@angular/material/input';
25
+ import * as i8 from '@angular/material/progress-bar';
26
+ import { MatProgressBarModule } from '@angular/material/progress-bar';
27
+ import _, { first, remove, findLastIndex } from 'lodash';
28
+ import { trigger, state, style, transition, animate } from '@angular/animations';
29
+ import * as i7 from '@angular/material/menu';
30
+ import { MatMenuModule } from '@angular/material/menu';
31
+ import { ENTER, COMMA } from '@angular/cdk/keycodes';
32
+ import * as i1$4 from '@angular/material/chips';
33
+ import { MatChipsModule } from '@angular/material/chips';
34
+ import { CommentsComponent } from '@rolatech/angular-comment';
35
+ import { AuthService, AuthUserService } from '@rolatech/angular-auth';
36
+ import { MatSnackBar } from '@angular/material/snack-bar';
37
+ import * as i6 from '@angular/material/core';
38
+ import { MatOptionModule } from '@angular/material/core';
39
+ import * as i5 from '@angular/material/select';
40
+ import { MatSelectModule } from '@angular/material/select';
41
+ import * as i3$1 from '@angular/material/paginator';
42
+ import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
43
+ import { MatTableDataSource, MatTableModule } from '@angular/material/table';
44
+ import * as i8$1 from '@angular/material/progress-spinner';
45
+ import * as i2$2 from '@angular/material/slide-toggle';
4
46
 
5
- class ProductLayoutComponent {
6
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.0", type: ProductLayoutComponent, isStandalone: true, selector: "lib-product-layout", ngImport: i0, template: "<p>product-layout works!</p>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
47
+ class ProductCategoryService extends BaseService {
48
+ init() {
49
+ this.endpoint = 'products/categories';
50
+ super.init();
51
+ }
52
+ uploadMedia(id, data) {
53
+ return this.http.post(`${this.actionUrl}/${id}/media`, data, {
54
+ withCredentials: true,
55
+ });
56
+ }
57
+ deleteMedia(id, mediaId) {
58
+ return this.http.delete(`${this.actionUrl}/${id}/media/${mediaId}`, {
59
+ withCredentials: true,
60
+ });
61
+ }
62
+ importFromExcel(data) {
63
+ return this.http.post(`${this.actionUrl}/excel`, data, {
64
+ withCredentials: true,
65
+ });
66
+ }
67
+ createFromExcel(data) {
68
+ return this.http.post(`${this.actionUrl}/list`, data, {
69
+ withCredentials: true,
70
+ });
71
+ }
72
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductCategoryService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
73
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductCategoryService, providedIn: 'root' }); }
8
74
  }
9
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductLayoutComponent, decorators: [{
75
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductCategoryService, decorators: [{
76
+ type: Injectable,
77
+ args: [{
78
+ providedIn: 'root',
79
+ }]
80
+ }] });
81
+
82
+ const services = [ProductCategoryService];
83
+
84
+ class ProductLayoutComponent extends BaseComponent {
85
+ constructor() {
86
+ super(...arguments);
87
+ this.categories = [];
88
+ this.categoryService = inject(ProductCategoryService);
89
+ this.productService = inject(ProductService);
90
+ this.selectIndex = 0;
91
+ }
92
+ ngOnInit() {
93
+ this.findCategories();
94
+ }
95
+ findCategories() {
96
+ this.categoryService.find({}).subscribe({
97
+ next: (res) => {
98
+ this.categories = res.data;
99
+ },
100
+ });
101
+ }
102
+ loadProductByCategoryIndex(index) {
103
+ this.selectIndex = index;
104
+ const id = this.categories[index].id;
105
+ if (index === 0) {
106
+ this.router.navigate([`/products`]);
107
+ // this.productService.find({}).subscribe({
108
+ // next: res => {
109
+ // }
110
+ // })
111
+ }
112
+ else {
113
+ this.router.navigate([`../products/categories/${id}`], {
114
+ relativeTo: this.route,
115
+ });
116
+ }
117
+ }
118
+ nextCategory() { }
119
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductLayoutComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
120
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductLayoutComponent, isStandalone: true, selector: "rolatech-product-layout", usesInheritance: true, ngImport: i0, template: "@if (categories) {\n <rolatech-container>\n <div class=\"flex flex-col md:flex-row min-w-[320px] max-w-[1280px] m-auto\">\n <div class=\"h-14 md:h-auto min-w-[240px]\">\n <div class=\"hidden md:block text-2xl font-medium py-3 md:px-1 px-3\">\u5206\u7C7B</div>\n <div\n class=\"flex flex-row md:flex-col md:h-full items-center md:items-start h-14 overflow-x-scroll overflow-y-hidden scrollbar-hide whitespace-pre\"\n >\n <a\n class=\"cursor-pointer h-8 bg-[--rt-badge-chip-background] md:bg-[--rt-base-background] rounded-md flex items-center md:mb-1 mr-2 md:mr-0\"\n routerLinkActive=\"product-layout-active\"\n routerLink=\"/products\"\n [routerLinkActiveOptions]=\"{ exact: true }\"\n >\n <span class=\"md:px-1 px-3 text-md md:text-lg\">\u5168\u90E8</span>\n </a>\n @for (category of categories; track category; let index = $index) {\n <a\n class=\"cursor-pointer h-8 bg-[--rt-badge-chip-background] md:bg-[--rt-base-background] rounded-md flex items-center mr-2 md:mr-0 md:mb-1\"\n routerLinkActive=\"product-layout-active\"\n [routerLink]=\"['../products/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-container>\n}\n", styles: [".product-layout-active{border-radius:var(--rt-rounded-base, 8px);background-color:var(-rt-base-background, #fff);color:var(--rt-brand-color, #000)}@media (max-width: 768px){.product-layout-active{background-color:var(--rt-text-primary, #000);color:var(--rt-text-primary-inverse, #000)}}.scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}\n"], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "directive", type: i1.RouterOutlet, selector: "router-outlet", inputs: ["name"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }] }); }
121
+ }
122
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductLayoutComponent, decorators: [{
123
+ type: Component,
124
+ args: [{ selector: 'rolatech-product-layout', standalone: true, imports: [AngularCommonModule, AngularComponentsModule, ContainerComponent, ListComponent], template: "@if (categories) {\n <rolatech-container>\n <div class=\"flex flex-col md:flex-row min-w-[320px] max-w-[1280px] m-auto\">\n <div class=\"h-14 md:h-auto min-w-[240px]\">\n <div class=\"hidden md:block text-2xl font-medium py-3 md:px-1 px-3\">\u5206\u7C7B</div>\n <div\n class=\"flex flex-row md:flex-col md:h-full items-center md:items-start h-14 overflow-x-scroll overflow-y-hidden scrollbar-hide whitespace-pre\"\n >\n <a\n class=\"cursor-pointer h-8 bg-[--rt-badge-chip-background] md:bg-[--rt-base-background] rounded-md flex items-center md:mb-1 mr-2 md:mr-0\"\n routerLinkActive=\"product-layout-active\"\n routerLink=\"/products\"\n [routerLinkActiveOptions]=\"{ exact: true }\"\n >\n <span class=\"md:px-1 px-3 text-md md:text-lg\">\u5168\u90E8</span>\n </a>\n @for (category of categories; track category; let index = $index) {\n <a\n class=\"cursor-pointer h-8 bg-[--rt-badge-chip-background] md:bg-[--rt-base-background] rounded-md flex items-center mr-2 md:mr-0 md:mb-1\"\n routerLinkActive=\"product-layout-active\"\n [routerLink]=\"['../products/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-container>\n}\n", styles: [".product-layout-active{border-radius:var(--rt-rounded-base, 8px);background-color:var(-rt-base-background, #fff);color:var(--rt-brand-color, #000)}@media (max-width: 768px){.product-layout-active{background-color:var(--rt-text-primary, #000);color:var(--rt-text-primary-inverse, #000)}}.scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}\n"] }]
125
+ }] });
126
+
127
+ class ProductCategoryComponent extends BaseComponent {
128
+ constructor() {
129
+ super(...arguments);
130
+ this.categoryService = inject(ProductCategoryService);
131
+ this.productService = inject(ProductService);
132
+ this.products = [];
133
+ this.categories = [];
134
+ this.loading = false;
135
+ }
136
+ ngOnInit() {
137
+ this.findAllCategories();
138
+ this.route.paramMap.subscribe((params) => {
139
+ const id = params.get('id');
140
+ this.findCategoryById(id);
141
+ this.findProductsByCategoryId(id);
142
+ });
143
+ }
144
+ findAllCategories() {
145
+ this.categoryService.find({}).subscribe({
146
+ next: (res) => {
147
+ this.categories = res.data;
148
+ },
149
+ });
150
+ }
151
+ findCategoryById(id) {
152
+ this.categoryService.get(id).subscribe({
153
+ next: (res) => {
154
+ this.category = res.data;
155
+ },
156
+ });
157
+ }
158
+ findAllProducts() {
159
+ this.productService.find({}).subscribe({
160
+ next: (res) => {
161
+ this.products = res.data;
162
+ },
163
+ });
164
+ }
165
+ findProductsByCategoryId(id) {
166
+ this.loading = true;
167
+ const options = {
168
+ filter: `categories:${id},published:true`,
169
+ };
170
+ this.productService.find(options).subscribe({
171
+ next: (res) => {
172
+ this.products = res.data;
173
+ this.loading = false;
174
+ },
175
+ error: (error) => {
176
+ this.loading = false;
177
+ },
178
+ });
179
+ }
180
+ loadProductsByCategory(item) {
181
+ // [routerLink] = "['../../', item.id]";
182
+ this.router.navigate([`../../${item.id}`], {
183
+ relativeTo: this.route,
184
+ });
185
+ }
186
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductCategoryComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
187
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductCategoryComponent, isStandalone: true, selector: "rolatech-product-category", usesInheritance: true, ngImport: i0, template: "<div>\n @if (category) {\n <div class=\"p-3 hidden md:block text-2xl font-medium\">{{ category.name }}</div>\n } @else {\n <div class=\"flex flex-row animate-pulse w-full\">\n <div class=\"h-4 bg-[--rt-base-background] rounded col-span-2\"></div>\n </div>\n }\n @if (loading) {\n @for (number of [0, 1, 2, 3, 4, 5]; track number) {\n <div class=\"flex flex-row animate-pulse mt-3 mr-4 cursor-pointer w-full\">\n <div class=\"h-fit w-2/5 md:w-1/4 aspect-video bg-[--rt-base-background] rounded-lg\"></div>\n <div class=\"w-3/5 md:w-3/4 ml-3 py-1 flex flex-col justify-between\">\n <div class=\"space-y-3\">\n <div class=\"h-4 bg-[--rt-base-background] rounded col-span-2\"></div>\n <div class=\"h-2 bg-[--rt-base-background] rounded col-span-1\"></div>\n <div class=\"h-2 bg-[--rt-base-background] rounded col-span-1 py-1\"></div>\n </div>\n <div>\n <div class=\"h-2 bg-[--rt-base-background] rounded col-span-2 py-1\"></div>\n </div>\n </div>\n </div>\n }\n } @else {\n <rolatech-rich-view list>\n @for (item of products; track item) {\n @defer {\n <rolatech-rich-item\n [routerLink]=\"['../../', item.id]\"\n [thumbnail]=\"item.media[0].url + '!w400'\"\n [title]=\"item.name\"\n [subtitle]=\"item.description\"\n [price]=\"item.price / 100\"\n ></rolatech-rich-item>\n }\n }\n </rolatech-rich-view>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: RichViewComponent, selector: "rolatech-rich-view", inputs: ["list", "wrap"] }], deferBlockDependencies: [() => [i1.RouterLink, RichItemComponent]] }); }
188
+ }
189
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductCategoryComponent, decorators: [{
190
+ type: Component,
191
+ args: [{ selector: 'rolatech-product-category', standalone: true, imports: [AngularCommonModule, AngularComponentsModule, RichViewComponent, RichItemComponent], template: "<div>\n @if (category) {\n <div class=\"p-3 hidden md:block text-2xl font-medium\">{{ category.name }}</div>\n } @else {\n <div class=\"flex flex-row animate-pulse w-full\">\n <div class=\"h-4 bg-[--rt-base-background] rounded col-span-2\"></div>\n </div>\n }\n @if (loading) {\n @for (number of [0, 1, 2, 3, 4, 5]; track number) {\n <div class=\"flex flex-row animate-pulse mt-3 mr-4 cursor-pointer w-full\">\n <div class=\"h-fit w-2/5 md:w-1/4 aspect-video bg-[--rt-base-background] rounded-lg\"></div>\n <div class=\"w-3/5 md:w-3/4 ml-3 py-1 flex flex-col justify-between\">\n <div class=\"space-y-3\">\n <div class=\"h-4 bg-[--rt-base-background] rounded col-span-2\"></div>\n <div class=\"h-2 bg-[--rt-base-background] rounded col-span-1\"></div>\n <div class=\"h-2 bg-[--rt-base-background] rounded col-span-1 py-1\"></div>\n </div>\n <div>\n <div class=\"h-2 bg-[--rt-base-background] rounded col-span-2 py-1\"></div>\n </div>\n </div>\n </div>\n }\n } @else {\n <rolatech-rich-view list>\n @for (item of products; track item) {\n @defer {\n <rolatech-rich-item\n [routerLink]=\"['../../', item.id]\"\n [thumbnail]=\"item.media[0].url + '!w400'\"\n [title]=\"item.name\"\n [subtitle]=\"item.description\"\n [price]=\"item.price / 100\"\n ></rolatech-rich-item>\n }\n }\n </rolatech-rich-view>\n }\n</div>\n" }]
192
+ }] });
193
+
194
+ class ProductInfoComponent {
195
+ constructor() {
196
+ this.name = input.required();
197
+ this.description = input();
198
+ }
199
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductInfoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
200
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.1.4", type: ProductInfoComponent, isStandalone: true, selector: "rolatech-product-info", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div>\n <div class=\"text-2xl font-medium\">{{ name() }}</div>\n <div>\n {{ description() }}\n </div>\n</div>\n", styles: ["mat-icon{transform:scale(.8)}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: RouterModule }], encapsulation: i0.ViewEncapsulation.None }); }
201
+ }
202
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductInfoComponent, decorators: [{
203
+ type: Component,
204
+ args: [{ selector: 'rolatech-product-info', standalone: true, imports: [MatIconModule, RouterModule], encapsulation: ViewEncapsulation.None, template: "<div>\n <div class=\"text-2xl font-medium\">{{ name() }}</div>\n <div>\n {{ description() }}\n </div>\n</div>\n", styles: ["mat-icon{transform:scale(.8)}\n"] }]
205
+ }] });
206
+
207
+ class ProductMediaComponent {
208
+ constructor() {
209
+ this.dialog = inject(MatDialog);
210
+ this.media = input([]);
211
+ this.min = input(false);
212
+ this.mediaIndex = 0;
213
+ }
214
+ onImageClick(i) {
215
+ const dialogRef = this.dialog.open(ImagePreviewDialogComponent, {
216
+ maxWidth: '80vw',
217
+ maxHeight: '80vh',
218
+ height: '80%',
219
+ width: '80%',
220
+ panelClass: 'full-screen-modal',
221
+ data: {
222
+ media: this.media,
223
+ selected: i,
224
+ },
225
+ });
226
+ dialogRef.afterClosed().subscribe((result) => { });
227
+ }
228
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductMediaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
229
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductMediaComponent, isStandalone: true, selector: "rolatech-product-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 <div>\n <div class=\"object-cover aspect-video bg-[--rt-raised-background]\">\n <rolatech-thumbnail [src]=\"media()[mediaIndex].url\" size=\"small\" mode=\"full\"></rolatech-thumbnail>\n </div>\n @for (media of media(); track media; let index = $index) {\n <div\n class=\"inline-flex flex-row mt-3 mr-3 cursor-pointer w-28 p-2 bg-[--rt-10-percent-layer]\"\n (click)=\"mediaIndex = index\"\n [ngClass]=\"mediaIndex === index ? 'border border-black' : 'opacity-70'\"\n >\n <rolatech-thumbnail [src]=\"media.url\" mode=\"full\"></rolatech-thumbnail>\n </div>\n }\n </div>\n}\n", styles: [""], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ThumbnailComponent, selector: "rolatech-thumbnail", inputs: ["src", "size", "mode", "ratio", "width", "height"] }], encapsulation: i0.ViewEncapsulation.None }); }
230
+ }
231
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductMediaComponent, decorators: [{
232
+ type: Component,
233
+ args: [{ selector: 'rolatech-product-media', standalone: true, imports: [NgClass, ThumbnailComponent], encapsulation: ViewEncapsulation.None, template: "@if (media()) {\n <div>\n <div class=\"object-cover aspect-video bg-[--rt-raised-background]\">\n <rolatech-thumbnail [src]=\"media()[mediaIndex].url\" size=\"small\" mode=\"full\"></rolatech-thumbnail>\n </div>\n @for (media of media(); track media; let index = $index) {\n <div\n class=\"inline-flex flex-row mt-3 mr-3 cursor-pointer w-28 p-2 bg-[--rt-10-percent-layer]\"\n (click)=\"mediaIndex = index\"\n [ngClass]=\"mediaIndex === index ? 'border border-black' : 'opacity-70'\"\n >\n <rolatech-thumbnail [src]=\"media.url\" mode=\"full\"></rolatech-thumbnail>\n </div>\n }\n </div>\n}\n" }]
234
+ }] });
235
+
236
+ class ProductSectionComponent {
237
+ constructor() {
238
+ this.section = input.required();
239
+ this.user = input();
240
+ this.username = input();
241
+ this.viewportScroller = inject(ViewportScroller);
242
+ this.row = input(false, { transform: booleanAttribute });
243
+ this.reverse = input(false, { transform: booleanAttribute });
244
+ }
245
+ ngOnInit() { }
246
+ onClickScroller(id) {
247
+ this.viewportScroller.scrollToAnchor(id);
248
+ }
249
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductSectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
250
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductSectionComponent, isStandalone: true, selector: "rolatech-product-section", inputs: { section: { classPropertyName: "section", publicName: "section", isSignal: true, isRequired: true, transformFunction: null }, user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null }, username: { classPropertyName: "username", publicName: "username", isSignal: true, isRequired: false, transformFunction: null }, row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: false, transformFunction: null }, reverse: { classPropertyName: "reverse", publicName: "reverse", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (section(); as section) {\n @if (row()) {\n <div>\n @if (section.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\"> {{ section.title }}</span>\n </div>\n }\n @if (section.description) {\n <div>\n {{ section.description }}\n </div>\n }\n @if (section.media) {\n <div class=\"w-80%\">\n @for (item of section.media; track item) {\n <div class=\"py-3\">\n <rolatech-thumbnail\n [src]=\"item.url\"\n size=\"small\"\n mode=\"full\"\n [width]=\"item.width\"\n [height]=\"item.height\"\n ></rolatech-thumbnail>\n </div>\n }\n </div>\n }\n </div>\n } @else {\n <div\n class=\"flex w-full p-3\"\n [ngClass]=\"reverse() ? 'flex-col-reverse md:flex-row-reverse' : 'flex-col-reverse md:flex-row'\"\n >\n <div\n class=\"w-full md:w-1/2 py-3 flex flex-col justify-center items-center gap-3\"\n [ngClass]=\"reverse() ? ' md:ml-16' : 'md:mr-16'\"\n >\n <span class=\"text-3xl font-bold py-1\"> {{ section.title }}</span>\n <span>\n {{ section.description }}\n </span>\n </div>\n <div class=\"w-full md:w-1/2 py-11\">\n @for (item of section.media; track item) {\n <div class=\"py-3\">\n <rolatech-thumbnail\n [src]=\"item.url\"\n size=\"small\"\n mode=\"full\"\n [width]=\"item.width\"\n [height]=\"item.height\"\n ></rolatech-thumbnail>\n </div>\n }\n </div>\n </div>\n }\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ThumbnailComponent, selector: "rolatech-thumbnail", inputs: ["src", "size", "mode", "ratio", "width", "height"] }], encapsulation: i0.ViewEncapsulation.None }); }
251
+ }
252
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductSectionComponent, decorators: [{
253
+ type: Component,
254
+ args: [{ selector: 'rolatech-product-section', standalone: true, imports: [CommonModule, ThumbnailComponent], encapsulation: ViewEncapsulation.None, template: "@if (section(); as section) {\n @if (row()) {\n <div>\n @if (section.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\"> {{ section.title }}</span>\n </div>\n }\n @if (section.description) {\n <div>\n {{ section.description }}\n </div>\n }\n @if (section.media) {\n <div class=\"w-80%\">\n @for (item of section.media; track item) {\n <div class=\"py-3\">\n <rolatech-thumbnail\n [src]=\"item.url\"\n size=\"small\"\n mode=\"full\"\n [width]=\"item.width\"\n [height]=\"item.height\"\n ></rolatech-thumbnail>\n </div>\n }\n </div>\n }\n </div>\n } @else {\n <div\n class=\"flex w-full p-3\"\n [ngClass]=\"reverse() ? 'flex-col-reverse md:flex-row-reverse' : 'flex-col-reverse md:flex-row'\"\n >\n <div\n class=\"w-full md:w-1/2 py-3 flex flex-col justify-center items-center gap-3\"\n [ngClass]=\"reverse() ? ' md:ml-16' : 'md:mr-16'\"\n >\n <span class=\"text-3xl font-bold py-1\"> {{ section.title }}</span>\n <span>\n {{ section.description }}\n </span>\n </div>\n <div class=\"w-full md:w-1/2 py-11\">\n @for (item of section.media; track item) {\n <div class=\"py-3\">\n <rolatech-thumbnail\n [src]=\"item.url\"\n size=\"small\"\n mode=\"full\"\n [width]=\"item.width\"\n [height]=\"item.height\"\n ></rolatech-thumbnail>\n </div>\n }\n </div>\n </div>\n }\n}\n" }]
255
+ }] });
256
+
257
+ class ProductItemComponent {
258
+ constructor() {
259
+ this.product = input.required();
260
+ }
261
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
262
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.1.4", type: ProductItemComponent, isStandalone: true, selector: "rolatech-product-item", inputs: { product: { classPropertyName: "product", publicName: "product", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<rolatech-rich-item\n [routerLink]=\"['./', product().id]\"\n [thumbnail]=\"product().media[0].url + '!w400'\"\n [title]=\"product().name\"\n [subtitle]=\"product().description\"\n [price]=\"product().price / 100\"\n></rolatech-rich-item>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: RichItemComponent, selector: "rolatech-rich-item", inputs: ["list", "thumbnail", "avatar", "title", "subtitle", "price"] }], encapsulation: i0.ViewEncapsulation.None }); }
263
+ }
264
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductItemComponent, decorators: [{
265
+ type: Component,
266
+ args: [{ selector: 'rolatech-product-item', standalone: true, imports: [AngularCommonModule, AngularComponentsModule, RichViewComponent, RichItemComponent], encapsulation: ViewEncapsulation.None, template: "<rolatech-rich-item\n [routerLink]=\"['./', product().id]\"\n [thumbnail]=\"product().media[0].url + '!w400'\"\n [title]=\"product().name\"\n [subtitle]=\"product().description\"\n [price]=\"product().price / 100\"\n></rolatech-rich-item>\n" }]
267
+ }] });
268
+
269
+ class ProductActionComponent {
270
+ constructor() {
271
+ this.product = input.required();
272
+ this.cart = output();
273
+ this.checkout = output();
274
+ }
275
+ onCart(product) {
276
+ this.cart.emit(product);
277
+ }
278
+ onCheckout(product) {
279
+ this.checkout.emit(product);
280
+ }
281
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductActionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
282
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.1.4", type: ProductActionComponent, isStandalone: true, selector: "rolatech-product-action", inputs: { product: { classPropertyName: "product", publicName: "product", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { cart: "cart", checkout: "checkout" }, ngImport: i0, template: "<div class=\"flex gap-3\">\n <a mat-stroked-button class=\"\" (click)=\"onCart(product())\">\u52A0\u5165\u8D2D\u7269\u8F66</a>\n <a mat-flat-button class=\"\" (click)=\"onCheckout(product())\">\u7ACB\u5373\u8D2D\u4E70</a>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatAnchor, selector: "a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button]", exportAs: ["matButton", "matAnchor"] }], encapsulation: i0.ViewEncapsulation.None }); }
283
+ }
284
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductActionComponent, decorators: [{
285
+ type: Component,
286
+ args: [{ selector: 'rolatech-product-action', standalone: true, imports: [CommonModule, MatButtonModule], encapsulation: ViewEncapsulation.None, template: "<div class=\"flex gap-3\">\n <a mat-stroked-button class=\"\" (click)=\"onCart(product())\">\u52A0\u5165\u8D2D\u7269\u8F66</a>\n <a mat-flat-button class=\"\" (click)=\"onCheckout(product())\">\u7ACB\u5373\u8D2D\u4E70</a>\n</div>\n" }]
287
+ }] });
288
+
289
+ class ProductPricingComponent {
290
+ constructor() {
291
+ this.product = input.required();
292
+ // price = computed(() => {
293
+ // return (this.product().price / 100).toFixed(2);
294
+ // });
295
+ this.price = input.required();
296
+ this.displayPrice = computed(() => {
297
+ return (this.price() / 100).toFixed(2);
298
+ });
299
+ this.wish = output();
300
+ this.inWishList = input(false);
301
+ }
302
+ onWish(product) {
303
+ this.wish.emit(product);
304
+ }
305
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductPricingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
306
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductPricingComponent, isStandalone: true, selector: "rolatech-product-pricing", inputs: { product: { classPropertyName: "product", publicName: "product", isSignal: true, isRequired: true, transformFunction: null }, price: { classPropertyName: "price", publicName: "price", isSignal: true, isRequired: true, transformFunction: null }, inWishList: { classPropertyName: "inWishList", publicName: "inWishList", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { wish: "wish" }, ngImport: i0, template: "<div class=\"py-3\">\n <div class=\"flex justify-between items-center\">\n <div class=\"text-xl font-bold py-3\">\u00A5 {{ displayPrice() }}</div>\n <button mat-icon-button (click)=\"onWish(product())\">\n @if (inWishList()) {\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"#5f6368\">\n <path\n d=\"m293-203.08 49.62-212.54-164.93-142.84 217.23-18.85L480-777.69l85.08 200.38 217.23 18.85-164.93 142.84L667-203.08 480-315.92 293-203.08Z\"\n />\n </svg>\n } @else {\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"#5f6368\">\n <path\n d=\"m354-287 126-76 126 77-33-144 111-96-146-13-58-136-58 135-146 13 111 97-33 143Zm-61 83.92 49.62-212.54-164.93-142.84 217.23-18.85L480-777.69l85.08 200.38 217.23 18.85-164.93 142.84L667-203.08 480-315.92 293-203.08ZM480-470Z\"\n />\n </svg>\n }\n </button>\n </div>\n <mat-divider></mat-divider>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }], encapsulation: i0.ViewEncapsulation.None }); }
307
+ }
308
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductPricingComponent, decorators: [{
309
+ type: Component,
310
+ args: [{ selector: 'rolatech-product-pricing', standalone: true, imports: [CommonModule, MatButtonModule, MatIcon, MatDivider], encapsulation: ViewEncapsulation.None, template: "<div class=\"py-3\">\n <div class=\"flex justify-between items-center\">\n <div class=\"text-xl font-bold py-3\">\u00A5 {{ displayPrice() }}</div>\n <button mat-icon-button (click)=\"onWish(product())\">\n @if (inWishList()) {\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"#5f6368\">\n <path\n d=\"m293-203.08 49.62-212.54-164.93-142.84 217.23-18.85L480-777.69l85.08 200.38 217.23 18.85-164.93 142.84L667-203.08 480-315.92 293-203.08Z\"\n />\n </svg>\n } @else {\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"#5f6368\">\n <path\n d=\"m354-287 126-76 126 77-33-144 111-96-146-13-58-136-58 135-146 13 111 97-33 143Zm-61 83.92 49.62-212.54-164.93-142.84 217.23-18.85L480-777.69l85.08 200.38 217.23 18.85-164.93 142.84L667-203.08 480-315.92 293-203.08ZM480-470Z\"\n />\n </svg>\n }\n </button>\n </div>\n <mat-divider></mat-divider>\n</div>\n" }]
311
+ }] });
312
+
313
+ class ProductOptionComponent {
314
+ constructor() {
315
+ this.option = input.required();
316
+ this.change = output();
317
+ this.selected = model();
318
+ this.value = model();
319
+ }
320
+ onChange(value) {
321
+ this.value.set(value);
322
+ this.change.emit({ option: this.option(), value: value });
323
+ }
324
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductOptionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
325
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductOptionComponent, isStandalone: true, selector: "rolatech-product-option", inputs: { option: { classPropertyName: "option", publicName: "option", isSignal: true, isRequired: true, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { change: "change", selected: "selectedChange", value: "valueChange" }, ngImport: i0, template: "<div class=\"mb-3\">\n <div class=\"text-xl font-bold py-3\">{{ option().name }}</div>\n <div class=\"flex flex-wrap gap-3\">\n @for (item of option().values; track $index) {\n <div\n class=\"w-24 h-12 border border-[--rt-border-color] rounded flex justify-center items-center hover:text-[--rt-text-primary-inverse] hover:bg-[--rt-base-background-inverse] cursor-pointer\"\n (click)=\"onChange(item)\"\n [ngClass]=\"value()?.id === item.id ? 'bg-[--rt-base-background-inverse] text-[--rt-text-primary-inverse]' : ''\"\n >\n {{ item.name }}\n </div>\n }\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
326
+ }
327
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductOptionComponent, decorators: [{
328
+ type: Component,
329
+ args: [{ selector: 'rolatech-product-option', standalone: true, imports: [CommonModule], template: "<div class=\"mb-3\">\n <div class=\"text-xl font-bold py-3\">{{ option().name }}</div>\n <div class=\"flex flex-wrap gap-3\">\n @for (item of option().values; track $index) {\n <div\n class=\"w-24 h-12 border border-[--rt-border-color] rounded flex justify-center items-center hover:text-[--rt-text-primary-inverse] hover:bg-[--rt-base-background-inverse] cursor-pointer\"\n (click)=\"onChange(item)\"\n [ngClass]=\"value()?.id === item.id ? 'bg-[--rt-base-background-inverse] text-[--rt-text-primary-inverse]' : ''\"\n >\n {{ item.name }}\n </div>\n }\n </div>\n</div>\n" }]
330
+ }] });
331
+
332
+ class ProductManageSectionItemComponent {
333
+ constructor() {
334
+ this.isUploading = input();
335
+ this.section = input.required();
336
+ this.actions = input(false);
337
+ this.selectMedia = input();
338
+ this.upload = output();
339
+ this.delete = output();
340
+ this.save = output();
341
+ this.deleteMedia = output();
342
+ this.expanded = false;
343
+ }
344
+ ngOnInit() {
345
+ this.selectedImg = this.section().media ? this.section().media[0] : null;
346
+ }
347
+ onUpload(data) {
348
+ this.upload.emit({ section: this.section(), data });
349
+ }
350
+ onMediaItemClick(image) {
351
+ this.selectedImg = image;
352
+ }
353
+ select(item) {
354
+ this.selectedImg = item;
355
+ }
356
+ deleteImage() { }
357
+ onSave(section) {
358
+ this.save.emit(section);
359
+ }
360
+ onDelete(section) {
361
+ this.delete.emit(section);
362
+ }
363
+ onDeleteMedia(media) {
364
+ this.deleteMedia.emit({ section: this.section(), media });
365
+ this.selectedImg = this.section().media ? first(this.section().media) : null;
366
+ }
367
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageSectionItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
368
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductManageSectionItemComponent, isStandalone: true, selector: "rolatech-product-manage-section-item", inputs: { isUploading: { classPropertyName: "isUploading", publicName: "isUploading", isSignal: true, isRequired: false, transformFunction: null }, section: { classPropertyName: "section", publicName: "section", 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=\"px-3\">\n <div\n class=\"h-14 py-3 flex items-center justify-between cursor-pointer\"\n (click)=\"expanded = !expanded; $event.stopPropagation()\"\n >\n <div class=\"flex\">\n <div class=\"w-32 line-clamp-1\">{{ section().title }}</div>\n <div class=\"line-clamp-1\">{{ section().description }}</div>\n </div>\n <div>\n <button mat-icon-button aria-label=\"expand row\" (click)=\"expanded = !expanded; $event.stopPropagation()\">\n @if (expanded) {\n <mat-icon>keyboard_arrow_up</mat-icon>\n } @else {\n <mat-icon>keyboard_arrow_down</mat-icon>\n }\n </button>\n </div>\n </div>\n <div class=\"flex flex-col gap-3 w-full overflow-hidden\" [@detailExpand]=\"expanded ? 'expanded' : 'collapsed'\">\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)]=\"section().title\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" [hideRequiredMarker]=\"true\">\n <mat-label>\u63CF\u8FF0</mat-label>\n <textarea\n matInput\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"\n placeholder=\"\u63CF\u8FF0\"\n [(ngModel)]=\"section().description\"\n ></textarea>\n </mat-form-field>\n </div>\n <!-- media -->\n <div class=\"lg:basis-1/2 px-0 lg:px-3\">\n @if (selectedImg) {\n <div class=\"flex flex-row justify-center relative aspect-video\">\n <img class=\"object-contain w-full\" [src]=\"selectedImg.url\" [alt]=\"selectedImg.alt\" />\n <div class=\"absolute z-30 right-0\">\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"beforeMenu\"\n class=\"ml-auto focus:outline-none hover:bg-[--rt-base-background] p-1\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n <mat-menu #beforeMenu=\"matMenu\" xPosition=\"before\">\n <button mat-menu-item (click)=\"onDeleteMedia(selectedImg)\">\n <span>\u5220\u9664</span>\n </button>\n </mat-menu>\n </div>\n </div>\n }\n\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 (section().isUploading) {\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n }\n </div>\n @for (media of section().media; track media) {\n <div class=\"media-list-item\">\n <img class=\"tile-media\" (click)=\"onMediaItemClick(media)\" [src]=\"media.url\" [alt]=\"media.alt\" />\n </div>\n }\n <input style=\"display: none\" type=\"file\" accept=\"image/*\" (change)=\"onUpload($event)\" #fileInput />\n <div class=\"add-button\">\n <div (click)=\"fileInput.click()\" class=\"tile-media flex justify-center items-center\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"48px\" viewBox=\"0 -960 960 960\" width=\"48px\" fill=\"#5f6368\">\n <path d=\"M444-444H240v-72h204v-204h72v204h204v72H516v204h-72v-204Z\" />\n </svg>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n @if (actions()) {\n <div class=\"flex flex-row justify-end p-3 gap-3\">\n <button mat-button class=\"max-h-8\" (click)=\"onDelete(section())\">\u5220\u9664</button>\n <button mat-flat-button class=\"max-h-8\" (click)=\"onSave(section())\">\u4FDD\u5B58</button>\n </div>\n }\n </div>\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:64px;width:64px;object-fit:contain;cursor:pointer;border:1px solid grey;position:relative;box-sizing:border-box;border-radius:8px}.add-button{cursor:pointer;position:relative;box-sizing:border-box;padding:2px}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.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: i4.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.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: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i1$2.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i7.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i7.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i7.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i8.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i9.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }], animations: [
369
+ trigger('detailExpand', [
370
+ state('collapsed', style({ height: '0px' })),
371
+ state('expanded', style({ height: '*' })),
372
+ transition('expanded <=> collapsed', animate('400ms cubic-bezier(0.25, 0.1, 0.25, 1)')),
373
+ ]),
374
+ ] }); }
375
+ }
376
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageSectionItemComponent, decorators: [{
377
+ type: Component,
378
+ args: [{ selector: 'rolatech-product-manage-section-item', standalone: true, imports: [
379
+ NgClass,
380
+ MatFormFieldModule,
381
+ MatInputModule,
382
+ FormsModule,
383
+ TextFieldModule,
384
+ MatButtonModule,
385
+ MatIconModule,
386
+ MatMenuModule,
387
+ MatProgressBarModule,
388
+ MatDividerModule,
389
+ ], animations: [
390
+ trigger('detailExpand', [
391
+ state('collapsed', style({ height: '0px' })),
392
+ state('expanded', style({ height: '*' })),
393
+ transition('expanded <=> collapsed', animate('400ms cubic-bezier(0.25, 0.1, 0.25, 1)')),
394
+ ]),
395
+ ], template: "<div class=\"px-3\">\n <div\n class=\"h-14 py-3 flex items-center justify-between cursor-pointer\"\n (click)=\"expanded = !expanded; $event.stopPropagation()\"\n >\n <div class=\"flex\">\n <div class=\"w-32 line-clamp-1\">{{ section().title }}</div>\n <div class=\"line-clamp-1\">{{ section().description }}</div>\n </div>\n <div>\n <button mat-icon-button aria-label=\"expand row\" (click)=\"expanded = !expanded; $event.stopPropagation()\">\n @if (expanded) {\n <mat-icon>keyboard_arrow_up</mat-icon>\n } @else {\n <mat-icon>keyboard_arrow_down</mat-icon>\n }\n </button>\n </div>\n </div>\n <div class=\"flex flex-col gap-3 w-full overflow-hidden\" [@detailExpand]=\"expanded ? 'expanded' : 'collapsed'\">\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)]=\"section().title\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" [hideRequiredMarker]=\"true\">\n <mat-label>\u63CF\u8FF0</mat-label>\n <textarea\n matInput\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"\n placeholder=\"\u63CF\u8FF0\"\n [(ngModel)]=\"section().description\"\n ></textarea>\n </mat-form-field>\n </div>\n <!-- media -->\n <div class=\"lg:basis-1/2 px-0 lg:px-3\">\n @if (selectedImg) {\n <div class=\"flex flex-row justify-center relative aspect-video\">\n <img class=\"object-contain w-full\" [src]=\"selectedImg.url\" [alt]=\"selectedImg.alt\" />\n <div class=\"absolute z-30 right-0\">\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"beforeMenu\"\n class=\"ml-auto focus:outline-none hover:bg-[--rt-base-background] p-1\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n <mat-menu #beforeMenu=\"matMenu\" xPosition=\"before\">\n <button mat-menu-item (click)=\"onDeleteMedia(selectedImg)\">\n <span>\u5220\u9664</span>\n </button>\n </mat-menu>\n </div>\n </div>\n }\n\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 (section().isUploading) {\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n }\n </div>\n @for (media of section().media; track media) {\n <div class=\"media-list-item\">\n <img class=\"tile-media\" (click)=\"onMediaItemClick(media)\" [src]=\"media.url\" [alt]=\"media.alt\" />\n </div>\n }\n <input style=\"display: none\" type=\"file\" accept=\"image/*\" (change)=\"onUpload($event)\" #fileInput />\n <div class=\"add-button\">\n <div (click)=\"fileInput.click()\" class=\"tile-media flex justify-center items-center\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"48px\" viewBox=\"0 -960 960 960\" width=\"48px\" fill=\"#5f6368\">\n <path d=\"M444-444H240v-72h204v-204h72v204h204v72H516v204h-72v-204Z\" />\n </svg>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n @if (actions()) {\n <div class=\"flex flex-row justify-end p-3 gap-3\">\n <button mat-button class=\"max-h-8\" (click)=\"onDelete(section())\">\u5220\u9664</button>\n <button mat-flat-button class=\"max-h-8\" (click)=\"onSave(section())\">\u4FDD\u5B58</button>\n </div>\n }\n </div>\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:64px;width:64px;object-fit:contain;cursor:pointer;border:1px solid grey;position:relative;box-sizing:border-box;border-radius:8px}.add-button{cursor:pointer;position:relative;box-sizing:border-box;padding:2px}\n"] }]
396
+ }], ctorParameters: () => [] });
397
+
398
+ class ProductManageOptionActionComponent {
399
+ constructor() {
400
+ this.delete = output();
401
+ this.save = output();
402
+ }
403
+ onDelete(option) {
404
+ this.delete.emit(option);
405
+ }
406
+ onSave(option) {
407
+ this.save.emit(option);
408
+ }
409
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageOptionActionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
410
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.4", type: ProductManageOptionActionComponent, isStandalone: true, selector: "rolatech-product-manage-option-action", outputs: { delete: "delete", save: "save" }, ngImport: i0, template: "<div>\n <!-- <button mat-button color=\"primary\" (click)=\"onDelete(item)\">\u5220\u9664</button>\n <button mat-button color=\"primary\" (click)=\"onSave(item)\">\u4FDD\u5B58</button> -->\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatButtonModule }] }); }
411
+ }
412
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageOptionActionComponent, decorators: [{
10
413
  type: Component,
11
- args: [{ selector: 'lib-product-layout', standalone: true, imports: [CommonModule], template: "<p>product-layout works!</p>\n" }]
414
+ args: [{ selector: 'rolatech-product-manage-option-action', standalone: true, imports: [MatButtonModule], template: "<div>\n <!-- <button mat-button color=\"primary\" (click)=\"onDelete(item)\">\u5220\u9664</button>\n <button mat-button color=\"primary\" (click)=\"onSave(item)\">\u4FDD\u5B58</button> -->\n</div>\n" }]
12
415
  }] });
13
416
 
14
- class ProductCategoryComponent {
15
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductCategoryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
16
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.0", type: ProductCategoryComponent, isStandalone: true, selector: "lib-product-category", ngImport: i0, template: "<p>product-category works!</p>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
417
+ class ProductManageOptionAddComponent {
418
+ constructor() {
419
+ this.option = {
420
+ id: '',
421
+ name: '',
422
+ values: [],
423
+ };
424
+ this.addOnBlur = true;
425
+ this.separatorKeysCodes = [ENTER, COMMA];
426
+ this.cancel = output();
427
+ this.save = output();
428
+ this.action = true;
429
+ this.output = output();
430
+ }
431
+ onCancel() {
432
+ this.cancel.emit();
433
+ }
434
+ onSave() {
435
+ this.save.emit(this.option);
436
+ }
437
+ addOptionValues(event) {
438
+ const value = (event.value || '').trim();
439
+ if (value) {
440
+ this.option.values.push({ name: value });
441
+ }
442
+ event.chipInput.clear();
443
+ }
444
+ removeValue(value) {
445
+ if (value) {
446
+ let index = this.option.values.findIndex((item) => item.name === value.name);
447
+ this.option.values?.splice(index, 1);
448
+ }
449
+ }
450
+ ngDoCheck() {
451
+ this.output.emit(this.option);
452
+ }
453
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageOptionAddComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
454
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductManageOptionAddComponent, isStandalone: true, selector: "rolatech-product-manage-option-add", outputs: { cancel: "cancel", save: "save", output: "output" }, ngImport: i0, template: "<div class=\"flex flex-col\">\n <div class=\"p-1\">\u9009\u9879</div>\n <form>\n <mat-form-field appearance=\"fill\">\n <input matInput placeholder=\"\u540D\u79F0\" type=\"text\" [(ngModel)]=\"option.name\" [ngModelOptions]=\"{ standalone: true }\" />\n </mat-form-field>\n <mat-form-field class=\"example-chip-list\">\n <mat-chip-grid #chipGrid aria-label=\"Enter values\">\n @for (value of option.values; track value) {\n <mat-chip-row\n (removed)=\"removeValue(value)\"\n [editable]=\"true\"\n [aria-description]=\"'press enter to edit ' + value.name\"\n >\n {{ value.name }}\n <button matChipRemove [attr.aria-label]=\"'remove ' + value.name\">\n <mat-icon>cancel</mat-icon>\n </button>\n </mat-chip-row>\n }\n <input\n placeholder=\"\u9009\u9879\u503C\"\n [matChipInputFor]=\"chipGrid\"\n [matChipInputSeparatorKeyCodes]=\"separatorKeysCodes\"\n [matChipInputAddOnBlur]=\"addOnBlur\"\n (matChipInputTokenEnd)=\"addOptionValues($event)\"\n />\n </mat-chip-grid>\n </mat-form-field>\n </form>\n</div>\n@if (action) {\n <div class=\"flex items-center justify-end\">\n <button mat-button (click)=\"onCancel()\">\u53D6\u6D88</button>\n <button mat-button color=\"primary\" (click)=\"onSave()\">\u4FDD\u5B58</button>\n </div>\n}\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i1$4.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "directive", type: i1$4.MatChipInput, selector: "input[matChipInputFor]", inputs: ["matChipInputFor", "matChipInputAddOnBlur", "matChipInputSeparatorKeyCodes", "placeholder", "id", "disabled"], outputs: ["matChipInputTokenEnd"], exportAs: ["matChipInput", "matChipInputFor"] }, { kind: "directive", type: i1$4.MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: i1$4.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "ngmodule", type: AngularCommonModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.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: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i3.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: MatIconModule }] }); }
17
455
  }
18
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductCategoryComponent, decorators: [{
456
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageOptionAddComponent, decorators: [{
19
457
  type: Component,
20
- args: [{ selector: 'lib-product-category', standalone: true, imports: [CommonModule], template: "<p>product-category works!</p>\n" }]
458
+ args: [{ selector: 'rolatech-product-manage-option-add', standalone: true, imports: [
459
+ MatChipsModule,
460
+ MatButtonModule,
461
+ MatFormFieldModule,
462
+ AngularCommonModule,
463
+ AngularComponentsModule,
464
+ MatIconModule,
465
+ ProductManageOptionActionComponent,
466
+ ], template: "<div class=\"flex flex-col\">\n <div class=\"p-1\">\u9009\u9879</div>\n <form>\n <mat-form-field appearance=\"fill\">\n <input matInput placeholder=\"\u540D\u79F0\" type=\"text\" [(ngModel)]=\"option.name\" [ngModelOptions]=\"{ standalone: true }\" />\n </mat-form-field>\n <mat-form-field class=\"example-chip-list\">\n <mat-chip-grid #chipGrid aria-label=\"Enter values\">\n @for (value of option.values; track value) {\n <mat-chip-row\n (removed)=\"removeValue(value)\"\n [editable]=\"true\"\n [aria-description]=\"'press enter to edit ' + value.name\"\n >\n {{ value.name }}\n <button matChipRemove [attr.aria-label]=\"'remove ' + value.name\">\n <mat-icon>cancel</mat-icon>\n </button>\n </mat-chip-row>\n }\n <input\n placeholder=\"\u9009\u9879\u503C\"\n [matChipInputFor]=\"chipGrid\"\n [matChipInputSeparatorKeyCodes]=\"separatorKeysCodes\"\n [matChipInputAddOnBlur]=\"addOnBlur\"\n (matChipInputTokenEnd)=\"addOptionValues($event)\"\n />\n </mat-chip-grid>\n </mat-form-field>\n </form>\n</div>\n@if (action) {\n <div class=\"flex items-center justify-end\">\n <button mat-button (click)=\"onCancel()\">\u53D6\u6D88</button>\n <button mat-button color=\"primary\" (click)=\"onSave()\">\u4FDD\u5B58</button>\n </div>\n}\n", styles: ["mat-form-field{width:100%}\n"] }]
467
+ }] });
468
+
469
+ class ProductManageOptionItemComponent {
470
+ constructor() {
471
+ this.option = input.required();
472
+ this.addOnBlur = true;
473
+ this.delete = output();
474
+ this.save = output();
475
+ this.edit = output();
476
+ }
477
+ onDelete() {
478
+ this.delete.emit(this.option());
479
+ }
480
+ onSave() {
481
+ this.save.emit(this.option());
482
+ }
483
+ onEdit() {
484
+ this.edit.emit(this.option());
485
+ }
486
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageOptionItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
487
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.1.4", type: ProductManageOptionItemComponent, isStandalone: true, selector: "rolatech-product-manage-option-item", inputs: { option: { classPropertyName: "option", publicName: "option", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { delete: "delete", save: "save", edit: "edit" }, ngImport: i0, template: "<div class=\"group flex justify-between items-center h-14 hover:bg-[--rt-raised-background] cursor-pointer px-2\">\n <div class=\"flex\">\n <div class=\"min-w-[100px]\">{{ option().name }}</div>\n <div class=\"overflow-hidden line-clamp-1\">\n {{ option().values! | options }}\n </div>\n </div>\n <div class=\"flex justify-end max-w-24 w-24 invisible group-hover:visible\">\n <button mat-icon-button (click)=\"onEdit()\">\n <mat-icon>edit</mat-icon>\n </button>\n <button mat-icon-button (click)=\"onDelete()\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n</div>\n<mat-divider></mat-divider>\n", styles: ["mat-icon{scale:.9}\n"], dependencies: [{ kind: "ngmodule", type: MatChipsModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: AngularCommonModule }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: i9.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "pipe", type: OptionsFormatPipe, name: "options" }] }); }
488
+ }
489
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageOptionItemComponent, decorators: [{
490
+ type: Component,
491
+ args: [{ selector: 'rolatech-product-manage-option-item', standalone: true, imports: [
492
+ MatChipsModule,
493
+ MatButtonModule,
494
+ MatFormFieldModule,
495
+ AngularCommonModule,
496
+ AngularComponentsModule,
497
+ MatIconModule,
498
+ ProductManageOptionActionComponent,
499
+ ProductManageOptionAddComponent,
500
+ OptionsFormatPipe,
501
+ ], template: "<div class=\"group flex justify-between items-center h-14 hover:bg-[--rt-raised-background] cursor-pointer px-2\">\n <div class=\"flex\">\n <div class=\"min-w-[100px]\">{{ option().name }}</div>\n <div class=\"overflow-hidden line-clamp-1\">\n {{ option().values! | options }}\n </div>\n </div>\n <div class=\"flex justify-end max-w-24 w-24 invisible group-hover:visible\">\n <button mat-icon-button (click)=\"onEdit()\">\n <mat-icon>edit</mat-icon>\n </button>\n <button mat-icon-button (click)=\"onDelete()\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n</div>\n<mat-divider></mat-divider>\n", styles: ["mat-icon{scale:.9}\n"] }]
502
+ }] });
503
+
504
+ class SelectedPipe {
505
+ transform(variant, option) {
506
+ const findedOption = variant.options.find((item) => item.option.id === option.id);
507
+ return findedOption?.value;
508
+ }
509
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: SelectedPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
510
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.1.4", ngImport: i0, type: SelectedPipe, isStandalone: true, name: "selected" }); }
511
+ }
512
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: SelectedPipe, decorators: [{
513
+ type: Pipe,
514
+ args: [{
515
+ name: 'selected',
516
+ standalone: true,
517
+ }]
21
518
  }] });
22
519
 
23
- class ProductDetailComponent {
24
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
25
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.0", type: ProductDetailComponent, isStandalone: true, selector: "lib-product-detail", ngImport: i0, template: "<p>product-detail works!</p>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
520
+ class ProductOwnerRendererComponent {
521
+ constructor() {
522
+ this.name = input();
523
+ this.avatar = input();
524
+ this.username = input();
525
+ }
526
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductOwnerRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
527
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductOwnerRendererComponent, isStandalone: true, selector: "rolatech-product-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 py-3\">\n <div class=\"flex mr-3 gap-2 items-center\">\n @if (avatar()) {\n <div class=\"cursor-pointer\" [routerLink]=\"['/', '@' + username()]\">\n <img [src]=\"avatar()\" class=\"w-11 h-11 rounded-full\" />\n </div>\n } @else {\n <div class=\"w-11 h-11 rounded-full bg-orange-600\"></div>\n }\n <div class=\"flex items-center text-lg font-bold cursor-pointer\">\n <a [routerLink]=\"['/', '@' + username()]\">\n <span>{{ name() }}</span>\n </a>\n <mat-icon color=\"primary\">verified</mat-icon>\n </div>\n </div>\n</div>\n", styles: ["mat-icon{transform:scale(.8)}\n"], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] }); }
26
528
  }
27
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductDetailComponent, decorators: [{
529
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductOwnerRendererComponent, decorators: [{
28
530
  type: Component,
29
- args: [{ selector: 'lib-product-detail', standalone: true, imports: [CommonModule], template: "<p>product-detail works!</p>\n" }]
531
+ args: [{ selector: 'rolatech-product-owner-renderer', standalone: true, imports: [AngularCommonModule, MatIconModule], template: "<div class=\"flex flex-row items-center py-3\">\n <div class=\"flex mr-3 gap-2 items-center\">\n @if (avatar()) {\n <div class=\"cursor-pointer\" [routerLink]=\"['/', '@' + username()]\">\n <img [src]=\"avatar()\" class=\"w-11 h-11 rounded-full\" />\n </div>\n } @else {\n <div class=\"w-11 h-11 rounded-full bg-orange-600\"></div>\n }\n <div class=\"flex items-center text-lg font-bold cursor-pointer\">\n <a [routerLink]=\"['/', '@' + username()]\">\n <span>{{ name() }}</span>\n </a>\n <mat-icon color=\"primary\">verified</mat-icon>\n </div>\n </div>\n</div>\n", styles: ["mat-icon{transform:scale(.8)}\n"] }]
30
532
  }] });
31
533
 
534
+ class ProductDetailsComponent extends BaseComponent {
535
+ constructor() {
536
+ super();
537
+ this.authService = inject(AuthService);
538
+ this.authUserService = inject(AuthUserService);
539
+ this.productService = inject(ProductService);
540
+ this.cartService = inject(CartService);
541
+ this.authenticated = this.authService.authenticated;
542
+ this.fullname = '';
543
+ this.username = '';
544
+ this.inWishList = false;
545
+ this.purchased = false;
546
+ this.variants = [];
547
+ this.variantOption = signal(undefined);
548
+ this.variantOptionChanged = computed(() => {
549
+ // this.findVariant(item.option)
550
+ console.log(this.variantOption());
551
+ return this.variantOption();
552
+ });
553
+ this.results = [];
554
+ effect(() => {
555
+ if (this.authenticated()) {
556
+ this.findWishlistBy();
557
+ }
558
+ });
559
+ }
560
+ ngOnInit() {
561
+ this.getProduct();
562
+ }
563
+ getProduct() {
564
+ this.productService.get(this.id).subscribe({
565
+ next: (res) => {
566
+ this.product = res.data;
567
+ if (this.product.variants) {
568
+ this.variants = this.product.variants;
569
+ this.selectedVariant = this.variants[0];
570
+ this.selectedVariant.options.forEach((item) => {
571
+ this.results.push([item.option.id, item.value.id]);
572
+ });
573
+ }
574
+ this.findUserBaseInfo(this.product.userId);
575
+ this.titleService.setTitle(`${this.product.name} - 拼小课`);
576
+ },
577
+ });
578
+ }
579
+ findVariant(optionId, valueId) {
580
+ return this.variants.find((item) => {
581
+ const exist = item.options.some((i) => i.option.id === optionId && i.value.id === valueId);
582
+ if (exist) {
583
+ return item;
584
+ }
585
+ return null;
586
+ });
587
+ }
588
+ findUserBaseInfo(userId) {
589
+ this.authUserService.getPublicUserInfo(userId).subscribe({
590
+ next: (res) => {
591
+ this.fullname = res.name;
592
+ this.username = res.username;
593
+ this.user = res;
594
+ },
595
+ });
596
+ }
597
+ findWishlistBy() {
598
+ this.productService.findWishlistBy(this.id).subscribe({
599
+ next: (res) => {
600
+ this.inWishList = res.data;
601
+ },
602
+ });
603
+ }
604
+ onWish(e) {
605
+ if (this.authenticated()) {
606
+ if (this.inWishList) {
607
+ this.removeFromWishlist();
608
+ }
609
+ else {
610
+ this.addToWishlist();
611
+ }
612
+ }
613
+ else {
614
+ this.snackBarService.open('请先登录');
615
+ }
616
+ }
617
+ onCart(e) {
618
+ if (this.authenticated()) {
619
+ const data = { productId: this.id, quantity: 1 };
620
+ if (this.selectedVariant) {
621
+ data['variantId'] = this.selectedVariant.id;
622
+ }
623
+ this.cartService.create(data).subscribe({
624
+ next: (res) => {
625
+ console.log(res);
626
+ },
627
+ });
628
+ }
629
+ else {
630
+ this.snackBarService.open('请先登录');
631
+ }
632
+ }
633
+ removeFromWishlist() {
634
+ this.productService.removeFromWishlist(this.id).subscribe({
635
+ next: (res) => {
636
+ this.inWishList = false;
637
+ },
638
+ });
639
+ }
640
+ addToWishlist() {
641
+ this.productService.addToWishlist(this.id).subscribe({
642
+ next: (res) => {
643
+ this.inWishList = true;
644
+ },
645
+ });
646
+ }
647
+ checkout(e) {
648
+ if (this.authenticated()) {
649
+ if (this.selectedVariant) {
650
+ // this.router.navigateByUrl(`/cart/checkout/products/${this.id}?variant-id=${this.selectedVariant.id}`);
651
+ // this.router.navigate([`/cart/checkout/products/${this.id}`], { queryParams: { variant: this.selectedVariant.id } });
652
+ this.router.navigate([`/cart/checkout/products/${this.id}/variants/${this.selectedVariant.id}`]);
653
+ }
654
+ else {
655
+ this.router.navigateByUrl(`/cart/checkout/products/${this.id}`);
656
+ }
657
+ }
658
+ else {
659
+ this.snackBarService.open('请先登录');
660
+ }
661
+ }
662
+ onOptionChange(e) {
663
+ let criteria = [];
664
+ this.selectedVariant.options.map((item) => {
665
+ criteria.push({
666
+ optionId: item.option.id === e.option.id ? e.option.id : item.option.id,
667
+ valueId: item.option.id === e.option.id ? e.value.id : item.value.id,
668
+ });
669
+ });
670
+ // const criteria = [
671
+ // { optionId: '249008855294545920', valueId: '249008855298740224' }, // color: red
672
+ // { optionId: '248661715955355648', valueId: '249001130061860865' }, // size: 256GB
673
+ // ];
674
+ // Function to check if a variant matches all the criteria
675
+ const matchesAllCriteria = (variant, criteria) => {
676
+ return _.every(criteria, (criterion) => {
677
+ return _.some(variant.options, (o) => o.option.id === criterion.optionId && o.value.id === criterion.valueId);
678
+ });
679
+ };
680
+ // Function to update the criteria based on a matching variant
681
+ const updateCriteriaFromVariant = (variant) => {
682
+ return _.map(variant.options, (o) => ({
683
+ optionId: o.option.id,
684
+ valueId: o.value.id,
685
+ }));
686
+ };
687
+ // Find the variant that matches all the criteria
688
+ const matchingVariant = _.find(this.variants, (variant) => matchesAllCriteria(variant, criteria));
689
+ this.selectedVariant = matchingVariant;
690
+ // const updatedCriteria = matchingVariant ? updateCriteriaFromVariant(matchingVariant) : [];
691
+ console.log(matchingVariant);
692
+ }
693
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductDetailsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
694
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductDetailsComponent, isStandalone: true, selector: "rolatech-product-details", usesInheritance: true, ngImport: i0, template: "@if (product) {\n <rolatech-container>\n <div class=\"flex flex-col md:flex-row w-full\">\n <div class=\"md:w-1/2 md:p-3 py-3\">\n <rolatech-product-media [media]=\"product.media\"></rolatech-product-media>\n </div>\n <div class=\"md:w-1/2 md:p-3 py-3\">\n <rolatech-product-info [name]=\"product.name\" [description]=\"product.description\"> </rolatech-product-info>\n @for (option of product.options; track $index) {\n <rolatech-product-option\n [option]=\"option\"\n [value]=\"selectedVariant | selected: option\"\n (change)=\"onOptionChange($event)\"\n ></rolatech-product-option>\n }\n <rolatech-product-pricing\n (wish)=\"onWish($event)\"\n [inWishList]=\"inWishList\"\n [product]=\"product\"\n [price]=\"selectedVariant ? selectedVariant.price : product.price\"\n ></rolatech-product-pricing>\n <rolatech-product-action\n [product]=\"product\"\n (checkout)=\"checkout($event)\"\n (cart)=\"onCart($event)\"\n ></rolatech-product-action>\n </div>\n </div>\n <div class=\"py-3\">\n <mat-divider></mat-divider>\n <rolatech-product-owner-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n ></rolatech-product-owner-renderer>\n <mat-divider></mat-divider>\n </div>\n <div class=\"flex flex-col\">\n @for (section of product.sections; track $index) {\n <rolatech-product-section [section]=\"section\" [reverse]=\"$index % 2 !== 0\"></rolatech-product-section>\n }\n\n <rolatech-comments [itemId]=\"product.id\"></rolatech-comments>\n </div>\n </rolatech-container>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: i9.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { kind: "component", type: ProductInfoComponent, selector: "rolatech-product-info", inputs: ["name", "description"] }, { kind: "component", type: ProductMediaComponent, selector: "rolatech-product-media", inputs: ["media", "min"] }, { kind: "component", type: ProductPricingComponent, selector: "rolatech-product-pricing", inputs: ["product", "price", "inWishList"], outputs: ["wish"] }, { kind: "component", type: ProductSectionComponent, selector: "rolatech-product-section", inputs: ["section", "user", "username", "row", "reverse"] }, { kind: "component", type: ProductActionComponent, selector: "rolatech-product-action", inputs: ["product"], outputs: ["cart", "checkout"] }, { kind: "component", type: CommentsComponent, selector: "rolatech-comments", inputs: ["itemId"] }, { kind: "component", type: ProductOptionComponent, selector: "rolatech-product-option", inputs: ["option", "selected", "value"], outputs: ["change", "selectedChange", "valueChange"] }, { kind: "component", type: ProductOwnerRendererComponent, selector: "rolatech-product-owner-renderer", inputs: ["name", "avatar", "username"] }, { kind: "pipe", type: SelectedPipe, name: "selected" }] }); }
695
+ }
696
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductDetailsComponent, decorators: [{
697
+ type: Component,
698
+ args: [{ selector: 'rolatech-product-details', standalone: true, imports: [
699
+ AngularCommonModule,
700
+ AngularComponentsModule,
701
+ ContainerComponent,
702
+ ProductInfoComponent,
703
+ ProductMediaComponent,
704
+ ProductPricingComponent,
705
+ ProductSectionComponent,
706
+ ProductActionComponent,
707
+ CommentsComponent,
708
+ ProductOptionComponent,
709
+ ProductOwnerRendererComponent,
710
+ SelectedPipe,
711
+ ], template: "@if (product) {\n <rolatech-container>\n <div class=\"flex flex-col md:flex-row w-full\">\n <div class=\"md:w-1/2 md:p-3 py-3\">\n <rolatech-product-media [media]=\"product.media\"></rolatech-product-media>\n </div>\n <div class=\"md:w-1/2 md:p-3 py-3\">\n <rolatech-product-info [name]=\"product.name\" [description]=\"product.description\"> </rolatech-product-info>\n @for (option of product.options; track $index) {\n <rolatech-product-option\n [option]=\"option\"\n [value]=\"selectedVariant | selected: option\"\n (change)=\"onOptionChange($event)\"\n ></rolatech-product-option>\n }\n <rolatech-product-pricing\n (wish)=\"onWish($event)\"\n [inWishList]=\"inWishList\"\n [product]=\"product\"\n [price]=\"selectedVariant ? selectedVariant.price : product.price\"\n ></rolatech-product-pricing>\n <rolatech-product-action\n [product]=\"product\"\n (checkout)=\"checkout($event)\"\n (cart)=\"onCart($event)\"\n ></rolatech-product-action>\n </div>\n </div>\n <div class=\"py-3\">\n <mat-divider></mat-divider>\n <rolatech-product-owner-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n ></rolatech-product-owner-renderer>\n <mat-divider></mat-divider>\n </div>\n <div class=\"flex flex-col\">\n @for (section of product.sections; track $index) {\n <rolatech-product-section [section]=\"section\" [reverse]=\"$index % 2 !== 0\"></rolatech-product-section>\n }\n\n <rolatech-comments [itemId]=\"product.id\"></rolatech-comments>\n </div>\n </rolatech-container>\n}\n" }]
712
+ }], ctorParameters: () => [] });
713
+
32
714
  const productRoutes = [
33
715
  {
34
716
  path: '',
@@ -36,7 +718,7 @@ const productRoutes = [
36
718
  children: [
37
719
  {
38
720
  path: '',
39
- loadComponent: () => Promise.resolve().then(function () { return productIndex_component; }).then((x) => x.ProductIndexComponent),
721
+ loadComponent: () => import('./rolatech-angular-product-product-index.component-C_cbg95R.mjs').then((x) => x.ProductIndexComponent),
40
722
  },
41
723
  {
42
724
  path: 'categories/:id',
@@ -46,58 +728,1005 @@ const productRoutes = [
46
728
  },
47
729
  {
48
730
  path: ':id',
49
- component: ProductDetailComponent,
731
+ component: ProductDetailsComponent,
50
732
  },
51
733
  ];
52
734
 
735
+ var ProductStatus;
736
+ (function (ProductStatus) {
737
+ ProductStatus[ProductStatus["DRAFT"] = '草稿'] = "DRAFT";
738
+ ProductStatus[ProductStatus["AWAITING"] = '审核中'] = "AWAITING";
739
+ ProductStatus[ProductStatus["PENDING"] = '审核中'] = "PENDING";
740
+ ProductStatus[ProductStatus["ACCEPTED"] = '已通过'] = "ACCEPTED";
741
+ ProductStatus[ProductStatus["ACTIVE"] = '已发布'] = "ACTIVE";
742
+ ProductStatus[ProductStatus["DELETED"] = '已删除'] = "DELETED";
743
+ ProductStatus[ProductStatus["ARCHIVED"] = '已下架'] = "ARCHIVED";
744
+ })(ProductStatus || (ProductStatus = {}));
745
+ var ProductType;
746
+ (function (ProductType) {
747
+ // DIGITAL = <any>'虚拟商品',
748
+ // PHYSICAL = <any>'实体商品',
749
+ // OTHER = <any>'其他',
750
+ ProductType["DIGITAL"] = "\u865A\u62DF\u5546\u54C1";
751
+ ProductType["PHYSICAL"] = "\u5B9E\u4F53\u5546\u54C1";
752
+ ProductType["OTHER"] = "\u5176\u4ED6";
753
+ })(ProductType || (ProductType = {}));
754
+ var ProductInventoryStatus;
755
+ (function (ProductInventoryStatus) {
756
+ ProductInventoryStatus["IN_STOCK"] = "\u6709\u8D27";
757
+ ProductInventoryStatus["LOW_STOCK"] = "\u5E93\u5B58\u4E0D\u8DB3";
758
+ ProductInventoryStatus["OUT_OF_STOCK"] = "\u552E\u7F44";
759
+ ProductInventoryStatus["DISCONTINUED"] = "\u5DF2\u505C\u4EA7";
760
+ })(ProductInventoryStatus || (ProductInventoryStatus = {}));
761
+ var ProductScope;
762
+ (function (ProductScope) {
763
+ ProductScope[ProductScope["WEB"] = '网站'] = "WEB";
764
+ ProductScope[ProductScope["GLOBAL"] = '全域'] = "GLOBAL";
765
+ })(ProductScope || (ProductScope = {}));
766
+
767
+ class ProductManageContentComponent {
768
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
769
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.4", type: ProductManageContentComponent, isStandalone: true, selector: "rolatech-product-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 }] }); }
770
+ }
771
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageContentComponent, decorators: [{
772
+ type: Component,
773
+ args: [{ selector: 'rolatech-product-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" }]
774
+ }] });
775
+
53
776
  class ProductManageLayoutComponent {
54
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductManageLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
55
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.0", type: ProductManageLayoutComponent, isStandalone: true, selector: "lib-product-manage-layout", ngImport: i0, template: "<p>product-manage-layout works!</p>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
777
+ constructor() {
778
+ this.submitted = false;
779
+ this.accepted = false;
780
+ this.isDraft = false;
781
+ this.route = inject(ActivatedRoute);
782
+ this.productService = inject(ProductService);
783
+ this.dialog = inject(MatDialog);
784
+ this.snackBar = inject(MatSnackBar);
785
+ this.id = this.route.snapshot.paramMap.get('id');
786
+ }
787
+ ngOnInit() {
788
+ this.get();
789
+ }
790
+ get() {
791
+ this.productService.get(this.id).subscribe({
792
+ next: (res) => {
793
+ this.product = res.data;
794
+ },
795
+ });
796
+ }
797
+ updateStatus() {
798
+ this.isDraft = this.product.status.toString() === 'DRAFT' || this.product.status.toString() === 'PENDING';
799
+ this.accepted = this.product.status.toString() === 'ACCEPTED';
800
+ }
801
+ publish() {
802
+ this.productService.publish(this.id).subscribe({
803
+ next: (res) => {
804
+ this.snackBar.open('发布成功');
805
+ this.product.status = ProductStatus['已发布'];
806
+ this.updateStatus();
807
+ },
808
+ error: (error) => {
809
+ this.snackBar.open(error.message);
810
+ },
811
+ });
812
+ }
813
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
814
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductManageLayoutComponent, isStandalone: true, selector: "rolatech-product-manage-layout", ngImport: i0, template: "<div class=\"flex flex-col md:flex-row m-auto\">\n <div\n class=\"min-w-[256px] px-3 md:px-0 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\">\u5546\u54C1\u4FE1\u606F</div>\n <a routerLink=\"./info\" routerLinkActive=\"manage-active\" class=\"p-2\">\u57FA\u672C\u4FE1\u606F</a>\n <a routerLink=\"./media\" routerLinkActive=\"manage-active\" class=\"p-2\">\u56FE\u7247</a>\n <a routerLink=\"./sections\" routerLinkActive=\"manage-active\" class=\"p-2\">\u8BE6\u60C5</a>\n <a routerLink=\"./options\" routerLinkActive=\"manage-active\" class=\"p-2\">\u9009\u9879</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\">\u5546\u54C1\u8BBE\u7F6E</div>\n <a routerLink=\"./variants\" routerLinkActive=\"manage-active\" class=\"p-2\">\u4EF7\u683C\u7B56\u7565</a>\n <a routerLink=\"./inventory\" routerLinkActive=\"manage-active\" class=\"p-2\">\u5E93\u5B58\u8BBE\u7F6E</a>\n <a routerLink=\"./schedule\" routerLinkActive=\"manage-active\" class=\"p-2\">\u53D1\u5E03\u8BBE\u7F6E</a>\n </div>\n <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-2\">\u5176\u4ED6\u4FE1\u606F</div>\n <a routerLink=\"./pricing\" routerLinkActive=\"manage-active\" class=\"p-2\">\u4EF7\u683C</a>\n </div>\n\n @if (isDraft) {\n <div class=\"md:mt-6 md:ml-2\">\n <button mat-flat-button color=\"primary\" (click)=\"publish()\">\u53D1\u5E03\u5546\u54C1</button>\n </div>\n }\n </div>\n <div class=\"w-full\">\n <router-outlet></router-outlet>\n </div>\n</div>\n", styles: [".manage-active{background-color:var(--rt-10-percent-layer, rgba(0, 0, 0, .05));box-shadow:4px 0 var(--rt-base-background-inverse, #000) inset;font-weight:600}@media (max-width: 768px){.manage-active{box-shadow:inset 0 -4px 0 0 var(--rt-base-background-inverse, #000)}}.scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.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"] }] }); }
815
+ }
816
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageLayoutComponent, decorators: [{
817
+ type: Component,
818
+ args: [{ selector: 'rolatech-product-manage-layout', standalone: true, imports: [RouterLink, RouterLinkActive, MatButtonModule, RouterOutlet, ProductManageContentComponent], template: "<div class=\"flex flex-col md:flex-row m-auto\">\n <div\n class=\"min-w-[256px] px-3 md:px-0 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\">\u5546\u54C1\u4FE1\u606F</div>\n <a routerLink=\"./info\" routerLinkActive=\"manage-active\" class=\"p-2\">\u57FA\u672C\u4FE1\u606F</a>\n <a routerLink=\"./media\" routerLinkActive=\"manage-active\" class=\"p-2\">\u56FE\u7247</a>\n <a routerLink=\"./sections\" routerLinkActive=\"manage-active\" class=\"p-2\">\u8BE6\u60C5</a>\n <a routerLink=\"./options\" routerLinkActive=\"manage-active\" class=\"p-2\">\u9009\u9879</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\">\u5546\u54C1\u8BBE\u7F6E</div>\n <a routerLink=\"./variants\" routerLinkActive=\"manage-active\" class=\"p-2\">\u4EF7\u683C\u7B56\u7565</a>\n <a routerLink=\"./inventory\" routerLinkActive=\"manage-active\" class=\"p-2\">\u5E93\u5B58\u8BBE\u7F6E</a>\n <a routerLink=\"./schedule\" routerLinkActive=\"manage-active\" class=\"p-2\">\u53D1\u5E03\u8BBE\u7F6E</a>\n </div>\n <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-2\">\u5176\u4ED6\u4FE1\u606F</div>\n <a routerLink=\"./pricing\" routerLinkActive=\"manage-active\" class=\"p-2\">\u4EF7\u683C</a>\n </div>\n\n @if (isDraft) {\n <div class=\"md:mt-6 md:ml-2\">\n <button mat-flat-button color=\"primary\" (click)=\"publish()\">\u53D1\u5E03\u5546\u54C1</button>\n </div>\n }\n </div>\n <div class=\"w-full\">\n <router-outlet></router-outlet>\n </div>\n</div>\n", styles: [".manage-active{background-color:var(--rt-10-percent-layer, rgba(0, 0, 0, .05));box-shadow:4px 0 var(--rt-base-background-inverse, #000) inset;font-weight:600}@media (max-width: 768px){.manage-active{box-shadow:inset 0 -4px 0 0 var(--rt-base-background-inverse, #000)}}.scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}\n"] }]
819
+ }], ctorParameters: () => [] });
820
+
821
+ class ProductManageInfoComponent extends BaseComponent {
822
+ constructor() {
823
+ super(...arguments);
824
+ this.productService = inject(ProductService);
825
+ this.categoryService = inject(ProductCategoryService);
826
+ this.status = ProductStatus;
827
+ this.productType = ProductType;
828
+ this.selectedType = '';
829
+ this.enumKeys = Object.keys(this.productType).filter((key) => isNaN(Number(key)));
830
+ this.enumValues = Object.values(this.productType);
831
+ this.price = model();
832
+ }
833
+ ngOnInit() {
834
+ this.id = this.route.parent?.snapshot.paramMap.get('id');
835
+ this.find();
836
+ this.categoryService.find({}).subscribe({
837
+ next: (res) => {
838
+ this.categories = res.data;
839
+ },
840
+ });
841
+ }
842
+ find() {
843
+ this.productService.get(this.id).subscribe({
844
+ next: (res) => {
845
+ this.product = res.data;
846
+ let price = (this.product.price / 100).toFixed(2);
847
+ this.price.set(price);
848
+ },
849
+ });
850
+ }
851
+ typeCompareFn(t1, t2) {
852
+ return t1 === ProductType[t2];
853
+ }
854
+ compareFn(o1, o2) {
855
+ return o1.id === o2?.id;
856
+ }
857
+ onTypeChange(event) {
858
+ this.product.type = Object.keys(this.productType).find((key) => this.productType[key] === event.value);
859
+ }
860
+ onCategoryChange(event) {
861
+ this.selectedCategoyIds = event.value;
862
+ }
863
+ update() {
864
+ const { name, description, categories } = this.product;
865
+ const data = {
866
+ name,
867
+ description,
868
+ categories,
869
+ price: Number(this.price()) * 100,
870
+ type: this.product.type,
871
+ };
872
+ this.productService.update(this.id, data).subscribe({
873
+ next: (res) => {
874
+ this.snackBarService.open('保存成功');
875
+ },
876
+ error: (e) => {
877
+ this.snackBarService.open(e.message);
878
+ },
879
+ });
880
+ }
881
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageInfoComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
882
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductManageInfoComponent, isStandalone: true, selector: "rolatech-product-manage-info", inputs: { price: { classPropertyName: "price", publicName: "price", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { price: "priceChange" }, usesInheritance: true, ngImport: i0, template: "<rolatech-product-manage-content>\n <rolatech-toolbar title=\"\u57FA\u672C\u4FE1\u606F\" class=\"hidden md:block\" divider></rolatech-toolbar>\n @if (product) {\n <div class=\"flex flex-col\">\n <form #productForm=\"ngForm\">\n <mat-form-field appearance=\"fill\">\n <mat-label> \u5546\u54C1\u540D\u79F0 </mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"product.name\" name=\"name\" required />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label> \u5546\u54C1\u63CF\u8FF0 </mat-label>\n <textarea\n matInput\n type=\"text\"\n [(ngModel)]=\"product.description\"\n name=\"description\"\n required\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"\n ></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"fill\" required>\n <mat-label>\u7C7B\u578B</mat-label>\n <mat-select\n name=\"type\"\n [compareWith]=\"typeCompareFn\"\n (selectionChange)=\"onTypeChange($event)\"\n [(ngModel)]=\"product.type\"\n >\n @for (key of enumKeys; track key) {\n <mat-option [value]=\"productType[key]\">\n {{ productType[key] }}\n </mat-option>\n }\n </mat-select>\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)]=\"product.categories\"\n #select=\"matSelect\"\n (selectionChange)=\"onCategoryChange($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> \u4EF7\u683C </mat-label>\n <input matInput rolatechDecimal type=\"text\" [(ngModel)]=\"price\" name=\"price\" required />\n </mat-form-field>\n </form>\n </div>\n <div>\n <button mat-flat-button type=\"primary\" (click)=\"update()\">\u4FDD\u5B58</button>\n </div>\n }\n</rolatech-product-manage-content>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.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: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$3.NgModelGroup, selector: "[ngModelGroup]", inputs: ["ngModelGroup"], exportAs: ["ngModelGroup"] }, { kind: "directive", type: i1$3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.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: i4.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.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.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.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: ProductManageContentComponent, selector: "rolatech-product-manage-content" }, { kind: "directive", type: DecimalDirective, selector: "[rolatechDecimal]" }] }); }
56
883
  }
57
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductManageLayoutComponent, decorators: [{
884
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageInfoComponent, decorators: [{
58
885
  type: Component,
59
- args: [{ selector: 'lib-product-manage-layout', standalone: true, imports: [CommonModule], template: "<p>product-manage-layout works!</p>\n" }]
886
+ args: [{ selector: 'rolatech-product-manage-info', standalone: true, imports: [
887
+ FormsModule,
888
+ MatFormFieldModule,
889
+ MatInputModule,
890
+ TextFieldModule,
891
+ MatSelectModule,
892
+ MatOptionModule,
893
+ MatButtonModule,
894
+ ToolbarComponent,
895
+ ProductManageContentComponent,
896
+ DecimalDirective,
897
+ MatSelectModule,
898
+ ], template: "<rolatech-product-manage-content>\n <rolatech-toolbar title=\"\u57FA\u672C\u4FE1\u606F\" class=\"hidden md:block\" divider></rolatech-toolbar>\n @if (product) {\n <div class=\"flex flex-col\">\n <form #productForm=\"ngForm\">\n <mat-form-field appearance=\"fill\">\n <mat-label> \u5546\u54C1\u540D\u79F0 </mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"product.name\" name=\"name\" required />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label> \u5546\u54C1\u63CF\u8FF0 </mat-label>\n <textarea\n matInput\n type=\"text\"\n [(ngModel)]=\"product.description\"\n name=\"description\"\n required\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"\n ></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"fill\" required>\n <mat-label>\u7C7B\u578B</mat-label>\n <mat-select\n name=\"type\"\n [compareWith]=\"typeCompareFn\"\n (selectionChange)=\"onTypeChange($event)\"\n [(ngModel)]=\"product.type\"\n >\n @for (key of enumKeys; track key) {\n <mat-option [value]=\"productType[key]\">\n {{ productType[key] }}\n </mat-option>\n }\n </mat-select>\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)]=\"product.categories\"\n #select=\"matSelect\"\n (selectionChange)=\"onCategoryChange($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> \u4EF7\u683C </mat-label>\n <input matInput rolatechDecimal type=\"text\" [(ngModel)]=\"price\" name=\"price\" required />\n </mat-form-field>\n </form>\n </div>\n <div>\n <button mat-flat-button type=\"primary\" (click)=\"update()\">\u4FDD\u5B58</button>\n </div>\n }\n</rolatech-product-manage-content>\n", styles: ["mat-form-field{width:100%}\n"] }]
60
899
  }] });
61
900
 
62
- class ProductManageInfoComponent {
63
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductManageInfoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
64
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.0", type: ProductManageInfoComponent, isStandalone: true, selector: "lib-product-manage-info", ngImport: i0, template: "<p class=\"text-lg\">product-manage-info works!</p>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
901
+ class ProductManageSectionsComponent {
902
+ constructor() {
903
+ this.route = inject(ActivatedRoute);
904
+ this.productService = inject(ProductService);
905
+ this.dialog = inject(MatDialog);
906
+ this.snackBar = inject(MatSnackBar);
907
+ this.isLoading = false;
908
+ this.isUploading = false;
909
+ this.sections = [];
910
+ this.status = ProductStatus;
911
+ this.productType = ProductType;
912
+ this.id = this.route.parent?.snapshot.paramMap.get('id');
913
+ }
914
+ ngOnInit() {
915
+ this.find();
916
+ }
917
+ find() {
918
+ this.productService.findSections(this.id).subscribe({
919
+ next: (res) => {
920
+ if (res.data) {
921
+ this.sections = res.data;
922
+ }
923
+ },
924
+ });
925
+ }
926
+ addSection() {
927
+ if (!this.sections) {
928
+ this.sections = [];
929
+ }
930
+ this.productService
931
+ .addSection(this.id, {
932
+ title: '',
933
+ description: '',
934
+ content: '',
935
+ media: [],
936
+ })
937
+ .subscribe({
938
+ next: (res) => {
939
+ const section = res.data;
940
+ this.sections.push({
941
+ id: section.id,
942
+ title: '',
943
+ description: '',
944
+ media: [],
945
+ });
946
+ },
947
+ error: (e) => {
948
+ this.snackBar.open(e.message);
949
+ },
950
+ });
951
+ }
952
+ onUploadSectionMedia(event) {
953
+ const section = event.section;
954
+ const sectionId = section.id;
955
+ const file = event.data.target.files[0];
956
+ if (file) {
957
+ const reader = new FileReader();
958
+ reader.readAsDataURL(file);
959
+ reader.onload = () => {
960
+ if (!section.media) {
961
+ section.media = [];
962
+ }
963
+ section.media.push({
964
+ url: reader.result,
965
+ alt: 'upload image',
966
+ });
967
+ section.isUploading = true;
968
+ const formData = new FormData();
969
+ formData.append('file', file);
970
+ this.productService.uploadSectionMedia(sectionId, formData).subscribe({
971
+ next: (res) => {
972
+ this.isUploading = false;
973
+ section.isUploading = false;
974
+ delete res.data.productSection;
975
+ section.media[section.media.length - 1].id = res.data.id;
976
+ },
977
+ error: (e) => {
978
+ this.isUploading = false;
979
+ this.snackBar.open('上传失败: ' + e.message);
980
+ },
981
+ });
982
+ };
983
+ reader.onerror = (error) => {
984
+ this.isUploading = false;
985
+ };
986
+ }
987
+ }
988
+ onDeleteSectionMedia(event) {
989
+ const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
990
+ width: '400px',
991
+ data: {
992
+ title: '删除图片',
993
+ message: `确定删除吗?`,
994
+ },
995
+ });
996
+ dialogRef.afterClosed().subscribe((result) => {
997
+ if (result) {
998
+ const section = event.section;
999
+ const mediaId = event.media.id;
1000
+ remove(section.media, {
1001
+ id: mediaId,
1002
+ });
1003
+ this.productService.deleteSectionMedia(section.id, mediaId).subscribe({
1004
+ next: (res) => {
1005
+ // remove(section.media, {
1006
+ // id: mediaId,
1007
+ // });
1008
+ },
1009
+ });
1010
+ }
1011
+ });
1012
+ }
1013
+ onSectionSave(section) {
1014
+ delete section.isUploading;
1015
+ this.productService.updateSection(section.id, section).subscribe({
1016
+ next: (res) => {
1017
+ this.snackBar.open('保存成功');
1018
+ },
1019
+ error: (e) => {
1020
+ this.snackBar.open(e.message);
1021
+ },
1022
+ });
1023
+ }
1024
+ onSectionDelete(section) {
1025
+ const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
1026
+ width: '400px',
1027
+ data: {
1028
+ title: '删除详情',
1029
+ message: '确定删除此详情吗?',
1030
+ },
1031
+ });
1032
+ dialogRef.afterClosed().subscribe((result) => {
1033
+ if (result) {
1034
+ this.productService.deleteSection(section.id).subscribe({
1035
+ next: (res) => {
1036
+ this.snackBar.open(res.data);
1037
+ remove(this.sections, {
1038
+ id: section.id,
1039
+ });
1040
+ },
1041
+ error: (e) => {
1042
+ this.snackBar.open(e.message);
1043
+ },
1044
+ });
1045
+ }
1046
+ });
1047
+ }
1048
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageSectionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1049
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductManageSectionsComponent, isStandalone: true, selector: "rolatech-product-manage-sections", ngImport: i0, template: "<rolatech-product-manage-content>\n <rolatech-toolbar title=\"\u8BE6\u60C5\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <div>\n <div>\n @for (section of sections; track section) {\n <rolatech-product-manage-section-item\n [section]=\"section\"\n (upload)=\"onUploadSectionMedia($event)\"\n (deleteMedia)=\"onDeleteSectionMedia($event)\"\n (save)=\"onSectionSave($event)\"\n (delete)=\"onSectionDelete($event)\"\n [actions]=\"true\"\n ></rolatech-product-manage-section-item>\n }\n </div>\n <button mat-stroked-button (click)=\"addSection()\" class=\"mt-3\">\n <mat-icon>add</mat-icon>\n <span>\u6DFB\u52A0\u8BE6\u60C5</span>\n </button>\n </div>\n</rolatech-product-manage-content>\n", styles: [""], dependencies: [{ kind: "component", type: ProductManageSectionItemComponent, selector: "rolatech-product-manage-section-item", inputs: ["isUploading", "section", "actions", "selectMedia"], outputs: ["upload", "delete", "save", "deleteMedia"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.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$1.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: ProductManageContentComponent, selector: "rolatech-product-manage-content" }] }); }
65
1050
  }
66
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductManageInfoComponent, decorators: [{
1051
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageSectionsComponent, decorators: [{
67
1052
  type: Component,
68
- args: [{ selector: 'lib-product-manage-info', standalone: true, imports: [CommonModule], template: "<p class=\"text-lg\">product-manage-info works!</p>\n" }]
1053
+ args: [{ selector: 'rolatech-product-manage-sections', standalone: true, imports: [ProductManageSectionItemComponent, MatButtonModule, MatIconModule, ToolbarComponent, ProductManageContentComponent], template: "<rolatech-product-manage-content>\n <rolatech-toolbar title=\"\u8BE6\u60C5\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <div>\n <div>\n @for (section of sections; track section) {\n <rolatech-product-manage-section-item\n [section]=\"section\"\n (upload)=\"onUploadSectionMedia($event)\"\n (deleteMedia)=\"onDeleteSectionMedia($event)\"\n (save)=\"onSectionSave($event)\"\n (delete)=\"onSectionDelete($event)\"\n [actions]=\"true\"\n ></rolatech-product-manage-section-item>\n }\n </div>\n <button mat-stroked-button (click)=\"addSection()\" class=\"mt-3\">\n <mat-icon>add</mat-icon>\n <span>\u6DFB\u52A0\u8BE6\u60C5</span>\n </button>\n </div>\n</rolatech-product-manage-content>\n" }]
1054
+ }], ctorParameters: () => [] });
1055
+
1056
+ const SIZE = 10 * 1024 * 1024; // file slice size 10MB
1057
+ class ProductManageMediaComponent {
1058
+ // productType: any = ProductType;
1059
+ constructor() {
1060
+ this.route = inject(ActivatedRoute);
1061
+ this.productService = inject(ProductService);
1062
+ this.dialog = inject(MatDialog);
1063
+ this.snackBar = inject(MatSnackBar);
1064
+ this.isUploading = false;
1065
+ this.isLoading = false;
1066
+ this.media = [];
1067
+ this.status = ProductStatus;
1068
+ this.id = this.route.parent?.snapshot.paramMap.get('id');
1069
+ }
1070
+ ngOnInit() {
1071
+ this.find();
1072
+ }
1073
+ find() {
1074
+ this.productService.get(this.id).subscribe({
1075
+ next: (res) => {
1076
+ this.media = res.data.media || [];
1077
+ },
1078
+ });
1079
+ }
1080
+ onImageClick(i) {
1081
+ const dialogRef = this.dialog.open(ImagePreviewDialogComponent, {
1082
+ maxWidth: '80vw',
1083
+ maxHeight: '80vh',
1084
+ height: '80%',
1085
+ width: '80%',
1086
+ panelClass: 'full-screen-modal',
1087
+ data: {
1088
+ media: this.media,
1089
+ selected: i,
1090
+ },
1091
+ });
1092
+ dialogRef.afterClosed().subscribe((result) => { });
1093
+ }
1094
+ createFileChunk(file, size = SIZE) {
1095
+ const fileChunkList = [];
1096
+ let cur = 0;
1097
+ while (cur < file.size) {
1098
+ fileChunkList.push({ file: file.slice(cur, cur + size) });
1099
+ cur += size;
1100
+ }
1101
+ return fileChunkList;
1102
+ }
1103
+ onUploadMedia2(event) {
1104
+ const file = event.target.files[0];
1105
+ const reader = new FileReader();
1106
+ reader.onload = (e) => { };
1107
+ const fileChunkList = this.createFileChunk(file);
1108
+ fileChunkList.forEach((item) => { });
1109
+ }
1110
+ onUploadMedia(event) {
1111
+ const file = event.target.files[0];
1112
+ // 5MB * 1024 * 1024 = 5242880
1113
+ // if (file?.size > 5242880) {
1114
+ // this.snackBar.open('尺寸过大, 请修改后上传');
1115
+ // this.isUploading = false;
1116
+ // return;
1117
+ // }
1118
+ if (file) {
1119
+ const reader = new FileReader();
1120
+ const formData = new FormData();
1121
+ formData.append('file', file);
1122
+ reader.readAsDataURL(file);
1123
+ reader.onload = () => {
1124
+ const img = new Image();
1125
+ img.onload = () => {
1126
+ this.media.push({
1127
+ url: img.src,
1128
+ alt: 'upload image',
1129
+ width: img.width,
1130
+ height: img.height,
1131
+ });
1132
+ this.isUploading = true;
1133
+ };
1134
+ img.src = reader.result; // This is the data URL
1135
+ };
1136
+ this.productService.uploadMedia(this.id, formData).subscribe({
1137
+ next: (res) => {
1138
+ this.isUploading = false;
1139
+ const index = findLastIndex(this.media);
1140
+ // Replace item at index using native splice
1141
+ this.media.splice(index, 1, res.data);
1142
+ },
1143
+ error: (e) => {
1144
+ this.isUploading = false;
1145
+ this.snackBar.open('上传失败: ' + e.message);
1146
+ },
1147
+ });
1148
+ reader.onerror = (error) => {
1149
+ this.isUploading = false;
1150
+ };
1151
+ }
1152
+ }
1153
+ onMediaDelete(item) {
1154
+ const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
1155
+ width: '400px',
1156
+ data: {
1157
+ title: '删除图片',
1158
+ message: '确定删除这张商品图片吗?',
1159
+ },
1160
+ });
1161
+ dialogRef.afterClosed().subscribe((result) => {
1162
+ if (result) {
1163
+ this.productService.deleteMedia(this.id, item.id).subscribe({
1164
+ next: (res) => {
1165
+ this.media = this.media.filter((m) => m.id !== item.id);
1166
+ this.snackBar.open('删除成功');
1167
+ },
1168
+ error: (e) => {
1169
+ this.snackBar.open(e.message);
1170
+ },
1171
+ });
1172
+ }
1173
+ });
1174
+ }
1175
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageMediaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1176
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductManageMediaComponent, isStandalone: true, selector: "rolatech-product-manage-media", ngImport: i0, template: "<rolatech-product-manage-content>\n <rolatech-toolbar title=\"\u56FE\u7247\u4FE1\u606F\" class=\"hidden md:block\" divider></rolatech-toolbar>\n <div>\n <p>*\u56FE\u7247\u6587\u4EF6\u5927\u5C0F\u9650\u5236\u57285MB\u4EE5\u4E0B*</p>\n <rolatech-media-list (upload)=\"onUploadMedia($event)\" [isUploading]=\"isUploading\">\n @for (item of media; track item; let i = $index) {\n <rolatech-media-list-item\n [media]=\"item\"\n (mediaItemClick)=\"onImageClick(i)\"\n (deleteMedia)=\"onMediaDelete(item)\"\n ></rolatech-media-list-item>\n }\n </rolatech-media-list>\n </div>\n</rolatech-product-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: ProductManageContentComponent, selector: "rolatech-product-manage-content" }] }); }
1177
+ }
1178
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageMediaComponent, decorators: [{
1179
+ type: Component,
1180
+ args: [{ selector: 'rolatech-product-manage-media', standalone: true, imports: [MediaListComponent, MediaListItemComponent, ToolbarComponent, ProductManageContentComponent], template: "<rolatech-product-manage-content>\n <rolatech-toolbar title=\"\u56FE\u7247\u4FE1\u606F\" class=\"hidden md:block\" divider></rolatech-toolbar>\n <div>\n <p>*\u56FE\u7247\u6587\u4EF6\u5927\u5C0F\u9650\u5236\u57285MB\u4EE5\u4E0B*</p>\n <rolatech-media-list (upload)=\"onUploadMedia($event)\" [isUploading]=\"isUploading\">\n @for (item of media; track item; let i = $index) {\n <rolatech-media-list-item\n [media]=\"item\"\n (mediaItemClick)=\"onImageClick(i)\"\n (deleteMedia)=\"onMediaDelete(item)\"\n ></rolatech-media-list-item>\n }\n </rolatech-media-list>\n </div>\n</rolatech-product-manage-content>\n" }]
1181
+ }], ctorParameters: () => [] });
1182
+
1183
+ class ProductManagePricingComponent {
1184
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManagePricingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1185
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.4", type: ProductManagePricingComponent, isStandalone: true, selector: "rolatech-product-manage-pricing", ngImport: i0, template: "<p>product-manage-pricing works!</p>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
1186
+ }
1187
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManagePricingComponent, decorators: [{
1188
+ type: Component,
1189
+ args: [{ selector: 'rolatech-product-manage-pricing', standalone: true, imports: [CommonModule], template: "<p>product-manage-pricing works!</p>\n" }]
69
1190
  }] });
70
1191
 
71
- class ProductManageDetailsComponent {
72
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductManageDetailsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
73
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.0", type: ProductManageDetailsComponent, isStandalone: true, selector: "lib-product-manage-details", ngImport: i0, template: "<p>product-manage-details works!</p>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
1192
+ class ProductManageItemComponent {
1193
+ constructor() {
1194
+ this.product = input.required();
1195
+ this.thumbnail = input();
1196
+ this.status = ProductStatus;
1197
+ }
1198
+ publish() { }
1199
+ archived() { }
1200
+ delete() { }
1201
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1202
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductManageItemComponent, isStandalone: true, selector: "rolatech-product-manage-item", inputs: { product: { classPropertyName: "product", publicName: "product", isSignal: true, isRequired: true, transformFunction: null }, thumbnail: { classPropertyName: "thumbnail", publicName: "thumbnail", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"flex p-3 items-center hover:bg-[--rt-raised-background] cursor-pointer\">\n <div>\n @if (product().media) {\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n @defer (on viewport()) {\n <rolatech-thumbnail [src]=\"product().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 </div>\n } @else {\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n <rolatech-image-placeholder></rolatech-image-placeholder>\n </div>\n }\n </div>\n <div class=\"px-3\">\n {{ product().name }}\n </div>\n <div class=\"px-3\">\n {{ status[product().status] }}\n </div>\n <div class=\"px-3\">\n {{ product().price }}\n </div>\n <div class=\"flex-1\"></div>\n <div class=\"pr-4\">\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"menu\"\n aria-label=\"Example icon-button with a menu\"\n (click)=\"$event.stopPropagation()\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n <mat-menu #menu=\"matMenu\" xPosition=\"before\">\n @if (product().status === 'DRAFT') {\n <button mat-menu-item (click)=\"publish()\">\n <span> \u4E0A\u67B6 </span>\n </button>\n }\n @if (product().status === 'STARTED') {\n <button mat-menu-item (click)=\"archived()\">\n <span> \u7ED3\u675F </span>\n </button>\n }\n @if (product().status !== 'ACTIVE') {\n <button mat-menu-item (click)=\"delete()\">\n <span> \u5220\u9664 </span>\n </button>\n }\n </mat-menu>\n </div>\n</div>\n<div class=\"h-[1px] px-3 bg-[--rt-raised-background]\"></div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i7.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i7.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i7.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], encapsulation: i0.ViewEncapsulation.None, deferBlockDependencies: [() => [ThumbnailComponent]] }); }
74
1203
  }
75
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductManageDetailsComponent, decorators: [{
1204
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageItemComponent, decorators: [{
76
1205
  type: Component,
77
- args: [{ selector: 'lib-product-manage-details', standalone: true, imports: [CommonModule], template: "<p>product-manage-details works!</p>\n" }]
1206
+ args: [{ selector: 'rolatech-product-manage-item', standalone: true, imports: [CommonModule, ThumbnailComponent, ImagePlaceholderComponent, MatButtonModule, MatMenuModule, MatIcon], encapsulation: ViewEncapsulation.None, template: "<div class=\"flex p-3 items-center hover:bg-[--rt-raised-background] cursor-pointer\">\n <div>\n @if (product().media) {\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n @defer (on viewport()) {\n <rolatech-thumbnail [src]=\"product().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 </div>\n } @else {\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n <rolatech-image-placeholder></rolatech-image-placeholder>\n </div>\n }\n </div>\n <div class=\"px-3\">\n {{ product().name }}\n </div>\n <div class=\"px-3\">\n {{ status[product().status] }}\n </div>\n <div class=\"px-3\">\n {{ product().price }}\n </div>\n <div class=\"flex-1\"></div>\n <div class=\"pr-4\">\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"menu\"\n aria-label=\"Example icon-button with a menu\"\n (click)=\"$event.stopPropagation()\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n <mat-menu #menu=\"matMenu\" xPosition=\"before\">\n @if (product().status === 'DRAFT') {\n <button mat-menu-item (click)=\"publish()\">\n <span> \u4E0A\u67B6 </span>\n </button>\n }\n @if (product().status === 'STARTED') {\n <button mat-menu-item (click)=\"archived()\">\n <span> \u7ED3\u675F </span>\n </button>\n }\n @if (product().status !== 'ACTIVE') {\n <button mat-menu-item (click)=\"delete()\">\n <span> \u5220\u9664 </span>\n </button>\n }\n </mat-menu>\n </div>\n</div>\n<div class=\"h-[1px] px-3 bg-[--rt-raised-background]\"></div>\n" }]
78
1207
  }] });
79
1208
 
80
- class ProductManageMediaComponent {
81
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductManageMediaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
82
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.0", type: ProductManageMediaComponent, isStandalone: true, selector: "lib-product-manage-media", ngImport: i0, template: "<p>product-manage-media works!</p>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
1209
+ class ProductManageItemHeaderComponent {
1210
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageItemHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1211
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.4", type: ProductManageItemHeaderComponent, isStandalone: true, selector: "rolatech-product-manage-item-header", ngImport: i0, template: "<div class=\"flex h-11 items-center\">\n <div class=\"w-36 mr-3 px-3 min-w-[320px]\">\u5546\u54C1</div>\n <div class=\"text-md font-bold px-3\">Status</div>\n <div class=\"flex-1\"></div>\n <div class=\"text-md px-3\"></div>\n</div>\n<div class=\"h-[1px] px-3 bg-[--rt-raised-background]\"></div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
83
1212
  }
84
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductManageMediaComponent, decorators: [{
1213
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageItemHeaderComponent, decorators: [{
85
1214
  type: Component,
86
- args: [{ selector: 'lib-product-manage-media', standalone: true, imports: [CommonModule], template: "<p>product-manage-media works!</p>\n" }]
1215
+ args: [{ selector: 'rolatech-product-manage-item-header', standalone: true, imports: [CommonModule], template: "<div class=\"flex h-11 items-center\">\n <div class=\"w-36 mr-3 px-3 min-w-[320px]\">\u5546\u54C1</div>\n <div class=\"text-md font-bold px-3\">Status</div>\n <div class=\"flex-1\"></div>\n <div class=\"text-md px-3\"></div>\n</div>\n<div class=\"h-[1px] px-3 bg-[--rt-raised-background]\"></div>\n" }]
87
1216
  }] });
88
1217
 
89
- class ProductManagePricingComponent {
90
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductManagePricingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
91
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.0", type: ProductManagePricingComponent, isStandalone: true, selector: "lib-product-manage-pricing", ngImport: i0, template: "<p>product-manage-pricing works!</p>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
1218
+ class ProductManageIndexComponent {
1219
+ constructor() {
1220
+ this.paginator = viewChild(MatPaginator);
1221
+ this.dialog = inject(MatDialog);
1222
+ this.snackBar = inject(MatSnackBar);
1223
+ this.productService = inject(ProductService);
1224
+ this.isLoading = false;
1225
+ this.isSearch = false;
1226
+ this.length = 100;
1227
+ this.pageSize = 15;
1228
+ this.pageSizeOptions = [5, 10, 25, 100];
1229
+ this.dataSource = new MatTableDataSource();
1230
+ this.products = [];
1231
+ this.displayedColumns = ['name', 'status', 'createdAt', 'actions'];
1232
+ this.orderOptions = [
1233
+ {
1234
+ key: 'createdAt',
1235
+ value: '创建时间',
1236
+ icon: 'arrow_upward',
1237
+ sort: 'asc',
1238
+ },
1239
+ {
1240
+ key: 'createdAt',
1241
+ value: '创建时间',
1242
+ icon: 'arrow_downward',
1243
+ sort: 'desc',
1244
+ },
1245
+ {
1246
+ key: 'status',
1247
+ value: '状态',
1248
+ icon: 'arrow_upward',
1249
+ sort: 'asc',
1250
+ },
1251
+ {
1252
+ key: 'status',
1253
+ value: '状态',
1254
+ icon: 'arrow_downward',
1255
+ sort: 'desc',
1256
+ },
1257
+ ];
1258
+ this.status = ProductStatus;
1259
+ this.orderString = 'createdAt desc';
1260
+ }
1261
+ ngOnInit() {
1262
+ this.find(null);
1263
+ }
1264
+ find(event) {
1265
+ this.isLoading = true;
1266
+ const page = event ? event.pageIndex + 1 : 1;
1267
+ const limit = event ? event.pageSize : 15;
1268
+ const sort = this.orderString;
1269
+ const options = {
1270
+ page,
1271
+ limit,
1272
+ sort,
1273
+ };
1274
+ this.productService.me(options).subscribe({
1275
+ next: (res) => {
1276
+ this.products = res.data;
1277
+ // this.dataSource.data = this.products;
1278
+ this.length = res.meta.pagination.count;
1279
+ this.isLoading = false;
1280
+ },
1281
+ error: (e) => {
1282
+ this.isLoading = false;
1283
+ this.snackBar.open(e.message);
1284
+ },
1285
+ });
1286
+ }
1287
+ archived(product) {
1288
+ const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
1289
+ width: '400px',
1290
+ data: {
1291
+ title: '商品下架',
1292
+ message: `确定下架 ${product.name} 吗?`,
1293
+ },
1294
+ });
1295
+ dialogRef.afterClosed().subscribe((result) => {
1296
+ if (result) {
1297
+ this.productService.archived(product.id).subscribe({
1298
+ next: (res) => {
1299
+ const index = this.products.findIndex((item) => item.id === product.id);
1300
+ const status = ProductStatus[ProductStatus.ARCHIVED];
1301
+ this.products[index].status = status;
1302
+ this.snackBar.open('结束成功');
1303
+ },
1304
+ error: (e) => {
1305
+ this.snackBar.open(e.message);
1306
+ },
1307
+ });
1308
+ }
1309
+ });
1310
+ }
1311
+ delete(product) {
1312
+ const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
1313
+ width: '400px',
1314
+ data: {
1315
+ title: '删除商品',
1316
+ message: `确定删除 ${product.name} 吗?`,
1317
+ },
1318
+ });
1319
+ dialogRef.afterClosed().subscribe((result) => {
1320
+ if (result) {
1321
+ this.productService.delete(product.id).subscribe({
1322
+ next: (res) => {
1323
+ const index = this.products.findIndex((item) => item.id === product.id);
1324
+ this.products.splice(index, 1);
1325
+ this.dataSource.data = this.products;
1326
+ this.length--;
1327
+ this.snackBar.open('删除成功');
1328
+ },
1329
+ error: (e) => {
1330
+ this.snackBar.open(e.message);
1331
+ },
1332
+ });
1333
+ }
1334
+ });
1335
+ }
1336
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageIndexComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1337
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductManageIndexComponent, isStandalone: true, selector: "rolatech-product-manage-index", viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true, isSignal: true }], ngImport: i0, template: "<rolatech-toolbar title=\"\u5546\u54C1\">\n <div class=\"flex items-center gap-2\">\n <button mat-button routerLink=\"./create\">\n <mat-icon>add</mat-icon>\n \u521B\u5EFA\u5546\u54C1\n </button>\n </div>\n</rolatech-toolbar>\n@if (isLoading) {\n <div class=\"flex justify-center items-center\">\n <rolatech-spinner></rolatech-spinner>\n </div>\n} @else {\n <rolatech-product-manage-item-header></rolatech-product-manage-item-header>\n @for (item of products; track $index) {\n <rolatech-product-manage-item\n [product]=\"item\"\n routerLink=\"/products/{{ item.id }}/manage/info\"\n ></rolatech-product-manage-item>\n }\n <mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"pageEvent = find($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n}\n", styles: ["mat-form-field{width:100%}table{width:100%}td.mat-column-actions{text-align:right;max-width:64px;font-size:.8rem;padding:0 8px}.mat-mdc-header-cell.actions{text-align:right;max-width:64px;width:64px}.mat-mdc-cell:nth-last-child(2),.mat-mdc-header-cell:nth-last-child(2),.mat-mdc-footer-cell:nth-last-child(2){text-align:right;max-width:180px;width:180px}mat-cell:last-of-type,mat-header-cell:last-of-type,mat-footer-cell:last-of-type{text-align:right;padding-right:8px!important}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTableModule }, { kind: "ngmodule", type: MatMenuModule }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i3$1.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: SpinnerComponent, selector: "rolatech-spinner", inputs: ["title"] }, { kind: "component", type: ProductManageItemComponent, selector: "rolatech-product-manage-item", inputs: ["product", "thumbnail"] }, { kind: "component", type: ProductManageItemHeaderComponent, selector: "rolatech-product-manage-item-header" }] }); }
1338
+ }
1339
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageIndexComponent, decorators: [{
1340
+ type: Component,
1341
+ args: [{ selector: 'rolatech-product-manage-index', standalone: true, imports: [
1342
+ MatButtonModule,
1343
+ RouterLink,
1344
+ MatIconModule,
1345
+ MatTableModule,
1346
+ MatMenuModule,
1347
+ MatPaginatorModule,
1348
+ ToolbarComponent,
1349
+ SpinnerComponent,
1350
+ ProductManageItemComponent,
1351
+ ProductManageItemHeaderComponent,
1352
+ ], template: "<rolatech-toolbar title=\"\u5546\u54C1\">\n <div class=\"flex items-center gap-2\">\n <button mat-button routerLink=\"./create\">\n <mat-icon>add</mat-icon>\n \u521B\u5EFA\u5546\u54C1\n </button>\n </div>\n</rolatech-toolbar>\n@if (isLoading) {\n <div class=\"flex justify-center items-center\">\n <rolatech-spinner></rolatech-spinner>\n </div>\n} @else {\n <rolatech-product-manage-item-header></rolatech-product-manage-item-header>\n @for (item of products; track $index) {\n <rolatech-product-manage-item\n [product]=\"item\"\n routerLink=\"/products/{{ item.id }}/manage/info\"\n ></rolatech-product-manage-item>\n }\n <mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"pageEvent = find($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n}\n", styles: ["mat-form-field{width:100%}table{width:100%}td.mat-column-actions{text-align:right;max-width:64px;font-size:.8rem;padding:0 8px}.mat-mdc-header-cell.actions{text-align:right;max-width:64px;width:64px}.mat-mdc-cell:nth-last-child(2),.mat-mdc-header-cell:nth-last-child(2),.mat-mdc-footer-cell:nth-last-child(2){text-align:right;max-width:180px;width:180px}mat-cell:last-of-type,mat-header-cell:last-of-type,mat-footer-cell:last-of-type{text-align:right;padding-right:8px!important}\n"] }]
1353
+ }] });
1354
+
1355
+ class ProductManageCreateComponent {
1356
+ constructor() {
1357
+ this.productService = inject(ProductService);
1358
+ this.router = inject(Router);
1359
+ this.route = inject(ActivatedRoute);
1360
+ this.snackBar = inject(MatSnackBar);
1361
+ this.product = {};
1362
+ }
1363
+ create() {
1364
+ const data = {
1365
+ ...this.product,
1366
+ // classroom: this.selectedClassroom,
1367
+ };
1368
+ this.productService.create(data).subscribe({
1369
+ next: (res) => {
1370
+ this.router.navigate([`../${res.data.id}/manage/info`], {
1371
+ relativeTo: this.route,
1372
+ });
1373
+ },
1374
+ error: (e) => {
1375
+ this.snackBar.open(e.message);
1376
+ },
1377
+ });
1378
+ }
1379
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageCreateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1380
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.4", type: ProductManageCreateComponent, isStandalone: true, selector: "rolatech-product-manage-create", ngImport: i0, template: "<rolatech-toolbar title=\"\u521B\u5EFA\u5546\u54C1\" link=\"../\"> </rolatech-toolbar>\n<div class=\"p-3\">\n <section>\n <form #productForm=\"ngForm\">\n <div class=\"flex flex-col\">\n <mat-form-field appearance=\"fill\">\n <mat-label> \u5546\u54C1\u540D\u79F0 </mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"product.name\" name=\"name\" required />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label> \u5546\u54C1\u63CF\u8FF0 </mat-label>\n <textarea\n matInput\n type=\"text\"\n [(ngModel)]=\"product.description\"\n name=\"description\"\n required\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"\n ></textarea>\n </mat-form-field>\n </div>\n </form>\n </section>\n <div class=\"mt-3\">\n <button mat-flat-button [disabled]=\"!productForm.valid\" class=\"w-28\" (click)=\"create()\">\u521B\u5EFA</button>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.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: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.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: i4.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }] }); }
1381
+ }
1382
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageCreateComponent, decorators: [{
1383
+ type: Component,
1384
+ args: [{ selector: 'rolatech-product-manage-create', standalone: true, imports: [
1385
+ ToolbarComponent,
1386
+ FormsModule,
1387
+ MatFormFieldModule,
1388
+ MatInputModule,
1389
+ TextFieldModule,
1390
+ MatSelectModule,
1391
+ MatOptionModule,
1392
+ MediaListComponent,
1393
+ MediaListItemComponent,
1394
+ MatButtonModule,
1395
+ ], template: "<rolatech-toolbar title=\"\u521B\u5EFA\u5546\u54C1\" link=\"../\"> </rolatech-toolbar>\n<div class=\"p-3\">\n <section>\n <form #productForm=\"ngForm\">\n <div class=\"flex flex-col\">\n <mat-form-field appearance=\"fill\">\n <mat-label> \u5546\u54C1\u540D\u79F0 </mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"product.name\" name=\"name\" required />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label> \u5546\u54C1\u63CF\u8FF0 </mat-label>\n <textarea\n matInput\n type=\"text\"\n [(ngModel)]=\"product.description\"\n name=\"description\"\n required\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"\n ></textarea>\n </mat-form-field>\n </div>\n </form>\n </section>\n <div class=\"mt-3\">\n <button mat-flat-button [disabled]=\"!productForm.valid\" class=\"w-28\" (click)=\"create()\">\u521B\u5EFA</button>\n </div>\n</div>\n" }]
1396
+ }] });
1397
+
1398
+ class ProductManageOptionsComponent extends BaseComponent {
1399
+ constructor() {
1400
+ super(...arguments);
1401
+ this.productService = inject(ProductService);
1402
+ this.options = [];
1403
+ this.add = false;
1404
+ }
1405
+ ngOnInit() {
1406
+ this.id = this.route.parent?.snapshot.paramMap.get('id');
1407
+ this.find();
1408
+ }
1409
+ find() {
1410
+ this.productService.findOptions(this.id).subscribe({
1411
+ next: (res) => {
1412
+ this.options = res.data || [];
1413
+ },
1414
+ });
1415
+ }
1416
+ saveOption(option) {
1417
+ this.productService.addOption(this.id, option).subscribe({
1418
+ next: (res) => {
1419
+ this.options.push(res.data);
1420
+ this.add = false;
1421
+ },
1422
+ });
1423
+ }
1424
+ onUpdateOption(option) {
1425
+ const options = {
1426
+ title: '编辑选项',
1427
+ cancelText: '取消',
1428
+ confirmText: '确认',
1429
+ data: {
1430
+ option: option,
1431
+ action: false,
1432
+ },
1433
+ component: ProductManageOptionAddComponent,
1434
+ };
1435
+ this.dialogService.open(options);
1436
+ this.dialogService.confirmed().subscribe({
1437
+ next: (res) => {
1438
+ if (res) {
1439
+ this.productService.updateOption(option.id, res).subscribe({
1440
+ next: (res) => {
1441
+ option.values = res.data.values;
1442
+ },
1443
+ });
1444
+ }
1445
+ },
1446
+ });
1447
+ }
1448
+ onDeleteOption(option) {
1449
+ const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
1450
+ width: '400px',
1451
+ data: {
1452
+ title: '删除选项',
1453
+ message: `确定删除 ${option.name} 吗?`,
1454
+ },
1455
+ });
1456
+ dialogRef.afterClosed().subscribe((result) => {
1457
+ if (result) {
1458
+ this.productService.deleteOption(option.id).subscribe({
1459
+ next: (res) => {
1460
+ const index = this.options.findIndex((item) => item.id === option.id);
1461
+ this.options.splice(index, 1);
1462
+ this.snackBarService.open('删除成功');
1463
+ },
1464
+ });
1465
+ }
1466
+ });
1467
+ }
1468
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageOptionsComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1469
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductManageOptionsComponent, isStandalone: true, selector: "rolatech-product-manage-options", usesInheritance: true, ngImport: i0, template: "<rolatech-product-manage-content>\n <rolatech-toolbar title=\"\u9009\u9879\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <div>\n @for (item of options; track $index) {\n <rolatech-product-manage-option-item\n [option]=\"item\"\n (delete)=\"onDeleteOption($event)\"\n (edit)=\"onUpdateOption($event)\"\n ></rolatech-product-manage-option-item>\n }\n <div class=\"mt-3\">\n @if (add) {\n <rolatech-product-manage-option-add\n (save)=\"saveOption($event)\"\n (cancel)=\"add = false\"\n ></rolatech-product-manage-option-add>\n } @else {\n <button mat-stroked-button (click)=\"add = true\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0</span>\n </button>\n }\n </div>\n </div>\n</rolatech-product-manage-content>\n", styles: [""], dependencies: [{ kind: "component", type: ProductManageContentComponent, selector: "rolatech-product-manage-content" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: AngularCommonModule }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: ProductManageOptionItemComponent, selector: "rolatech-product-manage-option-item", inputs: ["option"], outputs: ["delete", "save", "edit"] }, { kind: "component", type: ProductManageOptionAddComponent, selector: "rolatech-product-manage-option-add", outputs: ["cancel", "save", "output"] }] }); }
92
1470
  }
93
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductManagePricingComponent, decorators: [{
1471
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageOptionsComponent, decorators: [{
94
1472
  type: Component,
95
- args: [{ selector: 'lib-product-manage-pricing', standalone: true, imports: [CommonModule], template: "<p>product-manage-pricing works!</p>\n" }]
1473
+ args: [{ selector: 'rolatech-product-manage-options', standalone: true, imports: [
1474
+ ProductManageContentComponent,
1475
+ ToolbarComponent,
1476
+ MatButtonModule,
1477
+ MatFormFieldModule,
1478
+ AngularCommonModule,
1479
+ AngularComponentsModule,
1480
+ MatIconModule,
1481
+ AccordionComponent,
1482
+ PanelComponent,
1483
+ ProductManageOptionItemComponent,
1484
+ ProductManageOptionAddComponent,
1485
+ ], template: "<rolatech-product-manage-content>\n <rolatech-toolbar title=\"\u9009\u9879\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <div>\n @for (item of options; track $index) {\n <rolatech-product-manage-option-item\n [option]=\"item\"\n (delete)=\"onDeleteOption($event)\"\n (edit)=\"onUpdateOption($event)\"\n ></rolatech-product-manage-option-item>\n }\n <div class=\"mt-3\">\n @if (add) {\n <rolatech-product-manage-option-add\n (save)=\"saveOption($event)\"\n (cancel)=\"add = false\"\n ></rolatech-product-manage-option-add>\n } @else {\n <button mat-stroked-button (click)=\"add = true\">\n <mat-icon>add</mat-icon>\n <span>\u589E\u52A0</span>\n </button>\n }\n </div>\n </div>\n</rolatech-product-manage-content>\n" }]
1486
+ }] });
1487
+
1488
+ class ProductManageVariantComponent {
1489
+ constructor() {
1490
+ this.dialog = inject(MatDialog);
1491
+ this.variant = model.required();
1492
+ this.expanded = false;
1493
+ this.upload = output();
1494
+ this.variantStatus = ProductInventoryStatus;
1495
+ this.enumKeys = Object.keys(this.variantStatus).filter((key) => isNaN(Number(key)));
1496
+ }
1497
+ onUpload(event) {
1498
+ const file = event.target.files[0];
1499
+ if (file) {
1500
+ const reader = new FileReader();
1501
+ const formData = new FormData();
1502
+ formData.append('file', file);
1503
+ reader.readAsDataURL(file);
1504
+ reader.onload = () => {
1505
+ const img = new Image();
1506
+ img.onload = () => {
1507
+ this.variant.update((item) => {
1508
+ item.media = item.media ? item.media : [];
1509
+ item.media.push({
1510
+ url: img.src,
1511
+ alt: 'upload image',
1512
+ uploading: true,
1513
+ });
1514
+ return item;
1515
+ });
1516
+ };
1517
+ img.src = reader.result;
1518
+ this.upload.emit(formData);
1519
+ };
1520
+ reader.onerror = (error) => { };
1521
+ }
1522
+ }
1523
+ onMediaClick(i) {
1524
+ const dialogRef = this.dialog.open(ImagePreviewDialogComponent, {
1525
+ maxWidth: '80vw',
1526
+ maxHeight: '80vh',
1527
+ height: '80%',
1528
+ width: '80%',
1529
+ panelClass: 'full-screen-modal',
1530
+ data: {
1531
+ media: this.variant().media,
1532
+ selected: i,
1533
+ },
1534
+ });
1535
+ dialogRef.afterClosed().subscribe((result) => { });
1536
+ }
1537
+ statusCompareFn(t1, t2) {
1538
+ return t1 === ProductInventoryStatus[t2];
1539
+ }
1540
+ onStatusChange(event) {
1541
+ this.variant.update((item) => {
1542
+ item.status = Object.keys(this.variantStatus).find((key) => this.variantStatus[key] === event.value);
1543
+ return item;
1544
+ });
1545
+ }
1546
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageVariantComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1547
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductManageVariantComponent, isStandalone: true, selector: "rolatech-product-manage-variant", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { variant: "variantChange", upload: "upload" }, ngImport: i0, template: "<div class=\"flex items-center h-[72px] cursor-pointer px-2\" (click)=\"expanded = !expanded; $event.stopPropagation()\">\n <div>{{ variant().options | options }}</div>\n <div class=\"flex-1\"></div>\n <div class=\"px-3 dense-2\" (click)=\"$event.stopPropagation()\">\n <!-- <input [(ngModel)]=\"variant().price\" /> -->\n <mat-form-field subscriptSizing=\"dynamic\">\n <input matInput [(ngModel)]=\"variant().price\" />\n </mat-form-field>\n </div>\n <div class=\"px-3\">\n <mat-form-field class=\"dense-2\" (click)=\"$event.stopPropagation()\" subscriptSizing=\"dynamic\">\n <mat-select\n name=\"type\"\n [compareWith]=\"statusCompareFn\"\n (selectionChange)=\"onStatusChange($event)\"\n [(ngModel)]=\"variant().status\"\n >\n @for (key of enumKeys; track key) {\n <mat-option [value]=\"variantStatus[key]\">\n {{ variantStatus[key] }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"block w-20 text-right\">\n <button mat-icon-button aria-label=\"expand row\" (click)=\"expanded = !expanded; $event.stopPropagation()\">\n @if (expanded) {\n <mat-icon>keyboard_arrow_up</mat-icon>\n } @else {\n <mat-icon>keyboard_arrow_down</mat-icon>\n }\n </button>\n </div>\n</div>\n<div>\n <div class=\"flex flex-col gap-3 w-full overflow-hidden\" [@detailExpand]=\"expanded ? 'expanded' : 'collapsed'\">\n <div class=\"flex flex-col my-3 md:w-1/2\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <span matTextPrefix> \u00A5 &nbsp;</span>\n <input matInput rolatechDecimal type=\"text\" [(ngModel)]=\"variant().price\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>\u5E93\u5B58</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"variant().price\" />\n </mat-form-field>\n </div>\n <div class=\"flex flex-col items-start my-3 md:w-1/2\">\n <div class=\"flex flex-wrap\">\n @for (media of variant().media; track $index) {\n <div class=\"media-list-item\">\n <div class=\"inner-media\">\n @defer (on viewport()) {\n <img (click)=\"onMediaClick($index)\" [src]=\"media.url\" [alt]=\"media.alt\" />\n } @placeholder {\n <div class=\"bg-gray-200 h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n @if (media.uploading) {\n <div class=\"absolute flex items-center justify-center p-4 w-full h-full bg-[--rt-20-percent-layer]\">\n <mat-spinner></mat-spinner>\n <!--\n <svg\n viewBox=\"0 0 24 24\"\n preserveAspectRatio=\"xMidYMid meet\"\n focusable=\"false\"\n class=\"style-scope tp-yt-iron-icon\"\n style=\"pointer-events: none; display: block; width: 100%; height: 100%\"\n >\n <g class=\"style-scope tp-yt-iron-icon\">\n <path\n d=\"M17,18v1H6V18ZM6.49,9l.71.71L11,5.91V16h1V5.91l3.8,3.81L16.51,9l-5-5Z\"\n class=\"style-scope tp-yt-iron-icon\"\n ></path>\n </g>\n </svg>\n <span>Uploading\u2026</span> -->\n </div>\n }\n </div>\n </div>\n }\n <div class=\"media-list-item\">\n <div class=\"inner-media\" (click)=\"fileInput.click()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"48px\" viewBox=\"0 -960 960 960\" width=\"48px\" fill=\"#5f6368\">\n <path d=\"M444-444H240v-72h204v-204h72v204h204v72H516v204h-72v-204Z\" />\n </svg>\n </div>\n </div>\n <input style=\"display: none\" type=\"file\" accept=\"image/*\" (change)=\"onUpload($event)\" #fileInput />\n </div>\n </div>\n </div>\n</div>\n<mat-divider></mat-divider>\n", styles: [".media-list-item{cursor:pointer;position:relative;padding:2px}.inner-media{display:flex;align-items:center;justify-content:center;width:64px;height:64px;border-radius:4px;border:1px solid grey;position:relative;cursor:pointer;overflow:hidden}mat-form-field{width:160px}\n"], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "directive", type: i1$3.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: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i1$2.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i9.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i3.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: "component", type: i8$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: i5.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: "pipe", type: OptionsFormatPipe, name: "options" }, { kind: "ngmodule", type: MatTableModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "directive", type: DecimalDirective, selector: "[rolatechDecimal]" }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "ngmodule", type: MatSelectModule }], animations: [
1548
+ trigger('detailExpand', [
1549
+ state('collapsed', style({ height: '0px' })),
1550
+ state('expanded', style({ height: '*' })),
1551
+ transition('expanded <=> collapsed', animate('220ms cubic-bezier(0.25, 0.1, 0.25, 1)')),
1552
+ ]),
1553
+ ] }); }
1554
+ }
1555
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageVariantComponent, decorators: [{
1556
+ type: Component,
1557
+ args: [{ selector: 'rolatech-product-manage-variant', standalone: true, imports: [
1558
+ AngularCommonModule,
1559
+ AngularComponentsModule,
1560
+ OptionsFormatPipe,
1561
+ ProductManageContentComponent,
1562
+ ToolbarComponent,
1563
+ MatTableModule,
1564
+ MatIcon,
1565
+ MatFormFieldModule,
1566
+ ThumbnailComponent,
1567
+ ImageComponent,
1568
+ DecimalDirective,
1569
+ MatProgressBarModule,
1570
+ MatSelectModule,
1571
+ ], animations: [
1572
+ trigger('detailExpand', [
1573
+ state('collapsed', style({ height: '0px' })),
1574
+ state('expanded', style({ height: '*' })),
1575
+ transition('expanded <=> collapsed', animate('220ms cubic-bezier(0.25, 0.1, 0.25, 1)')),
1576
+ ]),
1577
+ ], template: "<div class=\"flex items-center h-[72px] cursor-pointer px-2\" (click)=\"expanded = !expanded; $event.stopPropagation()\">\n <div>{{ variant().options | options }}</div>\n <div class=\"flex-1\"></div>\n <div class=\"px-3 dense-2\" (click)=\"$event.stopPropagation()\">\n <!-- <input [(ngModel)]=\"variant().price\" /> -->\n <mat-form-field subscriptSizing=\"dynamic\">\n <input matInput [(ngModel)]=\"variant().price\" />\n </mat-form-field>\n </div>\n <div class=\"px-3\">\n <mat-form-field class=\"dense-2\" (click)=\"$event.stopPropagation()\" subscriptSizing=\"dynamic\">\n <mat-select\n name=\"type\"\n [compareWith]=\"statusCompareFn\"\n (selectionChange)=\"onStatusChange($event)\"\n [(ngModel)]=\"variant().status\"\n >\n @for (key of enumKeys; track key) {\n <mat-option [value]=\"variantStatus[key]\">\n {{ variantStatus[key] }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"block w-20 text-right\">\n <button mat-icon-button aria-label=\"expand row\" (click)=\"expanded = !expanded; $event.stopPropagation()\">\n @if (expanded) {\n <mat-icon>keyboard_arrow_up</mat-icon>\n } @else {\n <mat-icon>keyboard_arrow_down</mat-icon>\n }\n </button>\n </div>\n</div>\n<div>\n <div class=\"flex flex-col gap-3 w-full overflow-hidden\" [@detailExpand]=\"expanded ? 'expanded' : 'collapsed'\">\n <div class=\"flex flex-col my-3 md:w-1/2\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <span matTextPrefix> \u00A5 &nbsp;</span>\n <input matInput rolatechDecimal type=\"text\" [(ngModel)]=\"variant().price\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>\u5E93\u5B58</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"variant().price\" />\n </mat-form-field>\n </div>\n <div class=\"flex flex-col items-start my-3 md:w-1/2\">\n <div class=\"flex flex-wrap\">\n @for (media of variant().media; track $index) {\n <div class=\"media-list-item\">\n <div class=\"inner-media\">\n @defer (on viewport()) {\n <img (click)=\"onMediaClick($index)\" [src]=\"media.url\" [alt]=\"media.alt\" />\n } @placeholder {\n <div class=\"bg-gray-200 h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n @if (media.uploading) {\n <div class=\"absolute flex items-center justify-center p-4 w-full h-full bg-[--rt-20-percent-layer]\">\n <mat-spinner></mat-spinner>\n <!--\n <svg\n viewBox=\"0 0 24 24\"\n preserveAspectRatio=\"xMidYMid meet\"\n focusable=\"false\"\n class=\"style-scope tp-yt-iron-icon\"\n style=\"pointer-events: none; display: block; width: 100%; height: 100%\"\n >\n <g class=\"style-scope tp-yt-iron-icon\">\n <path\n d=\"M17,18v1H6V18ZM6.49,9l.71.71L11,5.91V16h1V5.91l3.8,3.81L16.51,9l-5-5Z\"\n class=\"style-scope tp-yt-iron-icon\"\n ></path>\n </g>\n </svg>\n <span>Uploading\u2026</span> -->\n </div>\n }\n </div>\n </div>\n }\n <div class=\"media-list-item\">\n <div class=\"inner-media\" (click)=\"fileInput.click()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"48px\" viewBox=\"0 -960 960 960\" width=\"48px\" fill=\"#5f6368\">\n <path d=\"M444-444H240v-72h204v-204h72v204h204v72H516v204h-72v-204Z\" />\n </svg>\n </div>\n </div>\n <input style=\"display: none\" type=\"file\" accept=\"image/*\" (change)=\"onUpload($event)\" #fileInput />\n </div>\n </div>\n </div>\n</div>\n<mat-divider></mat-divider>\n", styles: [".media-list-item{cursor:pointer;position:relative;padding:2px}.inner-media{display:flex;align-items:center;justify-content:center;width:64px;height:64px;border-radius:4px;border:1px solid grey;position:relative;cursor:pointer;overflow:hidden}mat-form-field{width:160px}\n"] }]
1578
+ }] });
1579
+
1580
+ class ProductManageVariantsComponent extends BaseComponent {
1581
+ constructor() {
1582
+ super(...arguments);
1583
+ this.productService = inject(ProductService);
1584
+ this.changeDetectorRefs = inject(ChangeDetectorRef);
1585
+ this.options = signal([]);
1586
+ this.snackBar = inject(MatSnackBar);
1587
+ this.productPrice = model();
1588
+ this.variantPriceChecked = false;
1589
+ this.loading = false;
1590
+ }
1591
+ ngOnInit() {
1592
+ this.id = this.route.parent?.snapshot.paramMap.get('id');
1593
+ this.getProduct();
1594
+ }
1595
+ onVariantSlideToggle(event) {
1596
+ const newState = event.checked;
1597
+ if (newState) {
1598
+ this.productService.updateVariantPrice(this.id, true).subscribe({
1599
+ next: (res) => {
1600
+ this.variants = res.data;
1601
+ this.variantPriceChecked = newState;
1602
+ },
1603
+ });
1604
+ }
1605
+ else {
1606
+ const options = {
1607
+ title: '确认删除吗',
1608
+ message: '删除这个章节吗?',
1609
+ cancelText: '取消',
1610
+ confirmText: '确认',
1611
+ };
1612
+ this.dialogService.open(options);
1613
+ this.dialogService.confirmed().subscribe({
1614
+ next: (res) => {
1615
+ if (res) {
1616
+ this.productService.updateVariantPrice(this.id, false).subscribe({
1617
+ next: (res) => {
1618
+ this.variantPriceChecked = false;
1619
+ this.variants = res.data;
1620
+ },
1621
+ });
1622
+ }
1623
+ else {
1624
+ event.source.checked = !newState;
1625
+ }
1626
+ },
1627
+ });
1628
+ }
1629
+ }
1630
+ onInventorySlideToggle(e) {
1631
+ if (e.checked) {
1632
+ }
1633
+ }
1634
+ saveVariants() {
1635
+ this.productService.updateVariants(this.id, this.variants).subscribe({
1636
+ next: (res) => {
1637
+ this.snackBarService.open('保存成功');
1638
+ },
1639
+ error: (error) => {
1640
+ this.snackBarService.open(error.message);
1641
+ },
1642
+ });
1643
+ }
1644
+ onUpload(variant, data) {
1645
+ this.productService.uploadVariantMedia(variant.id, data).subscribe({
1646
+ next: (res) => {
1647
+ variant.media[variant.media.length - 1] = {
1648
+ id: res.data.id,
1649
+ url: res.data.url + '!w200',
1650
+ uploading: false,
1651
+ };
1652
+ },
1653
+ error: (e) => {
1654
+ this.snackBar.open('上传失败: ' + e.message);
1655
+ },
1656
+ });
1657
+ }
1658
+ findVariants() {
1659
+ this.productService.findVariants(this.id).subscribe({
1660
+ next: (res) => {
1661
+ if (res.data) {
1662
+ this.variantPriceChecked = true;
1663
+ }
1664
+ this.variants = res.data;
1665
+ },
1666
+ });
1667
+ }
1668
+ getProduct() {
1669
+ this.loading = true;
1670
+ this.productService.get(this.id).subscribe({
1671
+ next: (res) => {
1672
+ this.product = res.data;
1673
+ this.variants = this.product.variants;
1674
+ this.variantPriceChecked = this.variants ? this.variants.length > 0 : false;
1675
+ this.options.set(res.data.options || []);
1676
+ let price = (res.data.price / 100).toFixed(2);
1677
+ this.productPrice.set(price);
1678
+ this.loading = false;
1679
+ },
1680
+ });
1681
+ }
1682
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageVariantsComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1683
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.4", type: ProductManageVariantsComponent, isStandalone: true, selector: "rolatech-product-manage-variants", inputs: { productPrice: { classPropertyName: "productPrice", publicName: "productPrice", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { productPrice: "productPriceChange" }, usesInheritance: true, ngImport: i0, template: "<rolatech-product-manage-content>\n <rolatech-toolbar title=\"\u4EF7\u683C\u7B56\u7565\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n @if (loading) {\n <rolatech-spinner></rolatech-spinner>\n } @else {\n <div class=\"flex items-center\">\n <mat-slide-toggle\n #slide\n [checked]=\"variantPriceChecked\"\n (change)=\"onVariantSlideToggle($event)\"\n [disabled]=\"options().length <= 0\"\n ></mat-slide-toggle>\n <div>\u6839\u636E\u5546\u54C1\u9009\u9879\u8BBE\u7F6E\u4E0D\u540C\u7684\u4EF7\u683C\u548C\u5E93\u5B58</div>\n </div>\n <div class=\"flex items-center\">\n <mat-slide-toggle #inventorySlide (change)=\"onInventorySlideToggle($event)\"></mat-slide-toggle>\n <div>\u8DDF\u8E2A\u5E93\u5B58</div>\n </div>\n\n @if (slide.checked) {\n <div>\n <div class=\"header flex gap-3 h-14 items-center\">\n <div class=\"px-3\">\u540D\u79F0</div>\n <div class=\"flex-1\"></div>\n <div class=\"w-[160px]\">\u4EF7\u683C</div>\n <div class=\"w-[160px]\">\u72B6\u6001</div>\n @if (inventorySlide.checked) {\n <div class=\"w-[160px]\">\u5E93\u5B58</div>\n }\n\n <div class=\"block w-20 text-right\">\u5C55\u5F00/\u6298\u53E0</div>\n </div>\n @for (item of variants; track $index) {\n <rolatech-product-manage-variant [variant]=\"item\" (upload)=\"onUpload(item, $event)\"></rolatech-product-manage-variant>\n }\n </div>\n <div class=\"mt-3\">\n <button mat-button (click)=\"saveVariants()\">\u4FDD\u5B58</button>\n </div>\n }\n }\n</rolatech-product-manage-content>\n", styles: ["mat-slide-toggle{scale:.8}mat-form-field{width:72px}\n"], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: i1$2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i2$2.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "component", type: ProductManageContentComponent, selector: "rolatech-product-manage-content" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: ProductManageVariantComponent, selector: "rolatech-product-manage-variant", inputs: ["variant"], outputs: ["variantChange", "upload"] }, { kind: "component", type: SpinnerComponent, selector: "rolatech-spinner", inputs: ["title"] }] }); }
1684
+ }
1685
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageVariantsComponent, decorators: [{
1686
+ type: Component,
1687
+ args: [{ selector: 'rolatech-product-manage-variants', standalone: true, imports: [
1688
+ AngularCommonModule,
1689
+ AngularComponentsModule,
1690
+ ProductManageContentComponent,
1691
+ ToolbarComponent,
1692
+ MatIcon,
1693
+ MatFormFieldModule,
1694
+ ThumbnailComponent,
1695
+ ImageComponent,
1696
+ ProductManageVariantComponent,
1697
+ SpinnerComponent,
1698
+ ], template: "<rolatech-product-manage-content>\n <rolatech-toolbar title=\"\u4EF7\u683C\u7B56\u7565\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n @if (loading) {\n <rolatech-spinner></rolatech-spinner>\n } @else {\n <div class=\"flex items-center\">\n <mat-slide-toggle\n #slide\n [checked]=\"variantPriceChecked\"\n (change)=\"onVariantSlideToggle($event)\"\n [disabled]=\"options().length <= 0\"\n ></mat-slide-toggle>\n <div>\u6839\u636E\u5546\u54C1\u9009\u9879\u8BBE\u7F6E\u4E0D\u540C\u7684\u4EF7\u683C\u548C\u5E93\u5B58</div>\n </div>\n <div class=\"flex items-center\">\n <mat-slide-toggle #inventorySlide (change)=\"onInventorySlideToggle($event)\"></mat-slide-toggle>\n <div>\u8DDF\u8E2A\u5E93\u5B58</div>\n </div>\n\n @if (slide.checked) {\n <div>\n <div class=\"header flex gap-3 h-14 items-center\">\n <div class=\"px-3\">\u540D\u79F0</div>\n <div class=\"flex-1\"></div>\n <div class=\"w-[160px]\">\u4EF7\u683C</div>\n <div class=\"w-[160px]\">\u72B6\u6001</div>\n @if (inventorySlide.checked) {\n <div class=\"w-[160px]\">\u5E93\u5B58</div>\n }\n\n <div class=\"block w-20 text-right\">\u5C55\u5F00/\u6298\u53E0</div>\n </div>\n @for (item of variants; track $index) {\n <rolatech-product-manage-variant [variant]=\"item\" (upload)=\"onUpload(item, $event)\"></rolatech-product-manage-variant>\n }\n </div>\n <div class=\"mt-3\">\n <button mat-button (click)=\"saveVariants()\">\u4FDD\u5B58</button>\n </div>\n }\n }\n</rolatech-product-manage-content>\n", styles: ["mat-slide-toggle{scale:.8}mat-form-field{width:72px}\n"] }]
1699
+ }] });
1700
+
1701
+ class ProductManageScheduleComponent {
1702
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageScheduleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1703
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.4", type: ProductManageScheduleComponent, isStandalone: true, selector: "rolatech-product-manage-schedule", ngImport: i0, template: "<rolatech-product-manage-content>\n <rolatech-toolbar title=\"\u53D1\u5E03\u8BBE\u7F6E\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <div></div>\n</rolatech-product-manage-content>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: ProductManageContentComponent, selector: "rolatech-product-manage-content" }] }); }
1704
+ }
1705
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageScheduleComponent, decorators: [{
1706
+ type: Component,
1707
+ args: [{ selector: 'rolatech-product-manage-schedule', standalone: true, imports: [AngularCommonModule, AngularComponentsModule, ToolbarComponent, ProductManageContentComponent], template: "<rolatech-product-manage-content>\n <rolatech-toolbar title=\"\u53D1\u5E03\u8BBE\u7F6E\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <div></div>\n</rolatech-product-manage-content>\n" }]
1708
+ }] });
1709
+
1710
+ class ProductManageInventoryComponent {
1711
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageInventoryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1712
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.4", type: ProductManageInventoryComponent, isStandalone: true, selector: "rolatech-product-manage-inventory", ngImport: i0, template: "<rolatech-product-manage-content>\n <rolatech-toolbar title=\"\u5E93\u5B58\u8BBE\u7F6E\" class=\"hidden md:block\" divider></rolatech-toolbar>\n</rolatech-product-manage-content>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: ProductManageContentComponent, selector: "rolatech-product-manage-content" }] }); }
1713
+ }
1714
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.4", ngImport: i0, type: ProductManageInventoryComponent, decorators: [{
1715
+ type: Component,
1716
+ args: [{ selector: 'rolatech-product-manage-inventory', standalone: true, imports: [AngularCommonModule, AngularComponentsModule, ToolbarComponent, ProductManageContentComponent], template: "<rolatech-product-manage-content>\n <rolatech-toolbar title=\"\u5E93\u5B58\u8BBE\u7F6E\" class=\"hidden md:block\" divider></rolatech-toolbar>\n</rolatech-product-manage-content>\n" }]
96
1717
  }] });
97
1718
 
98
1719
  const productManageRoutes = [
99
1720
  {
100
1721
  path: '',
1722
+ component: ProductManageIndexComponent,
1723
+ },
1724
+ {
1725
+ path: 'create',
1726
+ component: ProductManageCreateComponent,
1727
+ },
1728
+ {
1729
+ path: ':id/manage',
101
1730
  component: ProductManageLayoutComponent,
102
1731
  children: [
103
1732
  {
@@ -109,52 +1738,44 @@ const productManageRoutes = [
109
1738
  component: ProductManageMediaComponent,
110
1739
  },
111
1740
  {
112
- path: 'details',
113
- component: ProductManageDetailsComponent,
1741
+ path: 'sections',
1742
+ component: ProductManageSectionsComponent,
1743
+ },
1744
+ {
1745
+ path: 'options',
1746
+ component: ProductManageOptionsComponent,
1747
+ },
1748
+ {
1749
+ path: 'variants',
1750
+ component: ProductManageVariantsComponent,
1751
+ },
1752
+ {
1753
+ path: 'inventory',
1754
+ component: ProductManageInventoryComponent,
114
1755
  },
115
1756
  {
116
1757
  path: 'pricing',
117
1758
  component: ProductManagePricingComponent,
118
1759
  },
1760
+ {
1761
+ path: 'schedule',
1762
+ component: ProductManageScheduleComponent,
1763
+ },
119
1764
  ],
120
1765
  },
121
1766
  ];
122
1767
 
123
- class AngularProductComponent {
124
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: AngularProductComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
125
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.0", type: AngularProductComponent, isStandalone: true, selector: "lib-angular-product", ngImport: i0, template: "<p>angular-product works!</p>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
1768
+ function provideAngularProduct() {
1769
+ const providers = [
1770
+ ...services,
1771
+ importProvidersFrom(CommonModule, ReactiveFormsModule, FormsModule, RouterModule),
1772
+ ];
1773
+ return makeEnvironmentProviders(providers);
126
1774
  }
127
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: AngularProductComponent, decorators: [{
128
- type: Component,
129
- args: [{ selector: 'lib-angular-product', standalone: true, imports: [CommonModule], template: "<p>angular-product works!</p>\n" }]
130
- }] });
131
-
132
- class ProductManageContentComponent {
133
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductManageContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
134
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.0", type: ProductManageContentComponent, isStandalone: true, selector: "lib-product-manage-content", ngImport: i0, template: "<p>product-manage-content works!</p>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
135
- }
136
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductManageContentComponent, decorators: [{
137
- type: Component,
138
- args: [{ selector: 'lib-product-manage-content', standalone: true, imports: [CommonModule], template: "<p>product-manage-content works!</p>\n" }]
139
- }] });
140
-
141
- class ProductIndexComponent {
142
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductIndexComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
143
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.0", type: ProductIndexComponent, isStandalone: true, selector: "lib-product-index", ngImport: i0, template: "<p>product-index works!</p>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
144
- }
145
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ProductIndexComponent, decorators: [{
146
- type: Component,
147
- args: [{ selector: 'lib-product-index', standalone: true, imports: [CommonModule], template: "<p>product-index works!</p>\n" }]
148
- }] });
149
-
150
- var productIndex_component = /*#__PURE__*/Object.freeze({
151
- __proto__: null,
152
- ProductIndexComponent: ProductIndexComponent
153
- });
154
1775
 
155
1776
  /**
156
1777
  * Generated bundle index. Do not edit.
157
1778
  */
158
1779
 
159
- export { AngularProductComponent, ProductCategoryComponent, ProductDetailComponent, ProductIndexComponent, ProductLayoutComponent, ProductManageContentComponent, ProductManageDetailsComponent, ProductManageInfoComponent, ProductManageLayoutComponent, ProductManageMediaComponent, ProductManagePricingComponent, productManageRoutes, productRoutes };
1780
+ export { ProductActionComponent, ProductCategoryService, ProductInfoComponent, ProductInventoryStatus, ProductItemComponent, ProductManageOptionAddComponent, ProductManageOptionItemComponent, ProductManageSectionItemComponent, ProductMediaComponent, ProductOptionComponent, ProductPricingComponent, ProductScope, ProductSectionComponent, ProductStatus, ProductType, productManageRoutes, productRoutes, provideAngularProduct, services };
160
1781
  //# sourceMappingURL=rolatech-angular-product.mjs.map