angular-primeng
25
总安装量
25
周安装量
#14907
全站排名
安装命令
npx skills add https://github.com/seikaikyo/dash-skills --skill angular-primeng
Agent 安装分布
gemini-cli
24
opencode
24
github-copilot
23
codex
22
amp
21
kimi-cli
21
Skill 文档
Angular 21 + PrimeNG éç¼è¦ç¯
é©ç¨å ´æ¯
- MES 製é å·è¡ç³»çµ±
- ERP 伿¥è³æºè¦å
- å¾å°ç®¡ç系統
- è³æå¯éåæç¨
æ ¸å¿åå
1. å°æ¡çµæ§
src/app/
âââ core/ # æ ¸å¿æ¨¡çµï¼å®ä¾æåï¼
â âââ auth/ # èªèç¸é
â âââ guards/ # è·¯ç±å®è¡
â âââ interceptors/ # HTTP ææªå¨
â âââ models/ # è³ææ¨¡å
â âââ services/ # å
±ç¨æå
âââ shared/ # å
±ç¨æ¨¡çµ
â âââ components/ # å¯å¾©ç¨å
ä»¶
â âââ directives/ # èªå®ç¾©æä»¤
â âââ pipes/ # è³æç®¡ç·
â âââ utils/ # å·¥å
·å½æ¸
âââ features/ # åè½æ¨¡çµ
â âââ dashboard/
â âââ production/
â âââ settings/
âââ layout/ # çé¢é
ç½®
2. PrimeNG è¨å® (app.config.ts)
import { ApplicationConfig } from '@angular/core';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { providePrimeNG } from 'primeng/config';
import Aura from '@primeng/themes/aura';
export const appConfig: ApplicationConfig = {
providers: [
provideAnimationsAsync(),
providePrimeNG({
theme: {
preset: Aura,
options: {
darkModeSelector: '.dark-mode',
cssLayer: {
name: 'primeng',
order: 'tailwind-base, primeng, tailwind-utilities'
}
}
},
ripple: true
})
]
};
3. PrimeNG å ä»¶å°ç §è¡¨
| ç¨é | å ä»¶ | ç¯ä¾ |
|---|---|---|
| 表å®/詳æ å´éæ¬ | <p-drawer> |
編輯工å®ãæ°å¢å®¢æ¶ |
| 確èªå°è©±æ¡ | <p-dialog> |
åªé¤ç¢ºèªãè¦åè¨æ¯ |
| è³æè¡¨æ ¼ | <p-table> |
å·¥å®å表ãå ±è¡¨ |
| æé | <p-button> |
æææéæä½ |
| æåè¼¸å ¥ | <input pInputText> |
æé ngModel |
| 䏿é¸å® | <p-select> |
çæ é¸æãåé¡ |
| æ¥æé¸æ | <p-datepicker> |
æ¥æç¯åç¯©é¸ |
| æ¨ç±¤/å¾½ç« | <p-tag> |
çæ æ¨ç¤º |
| Toast éç¥ | <p-toast> |
æä½åé¥ |
| è¼å ¥é®ç½© | <p-blockui> |
é忥æä½ |
4. Signals çæ ç®¡ç (Angular 21)
// æ¨è¦ï¼ä½¿ç¨ Signals
import { signal, computed, effect } from '@angular/core';
@Component({...})
export class WorkOrderListComponent {
// çæ
workOrders = signal<WorkOrder[]>([]);
selectedId = signal<string | null>(null);
isLoading = signal(false);
// è¨ç®å±¬æ§
selectedOrder = computed(() =>
this.workOrders().find(o => o.id === this.selectedId())
);
pendingCount = computed(() =>
this.workOrders().filter(o => o.status === 'pending').length
);
// å¯ä½ç¨
constructor() {
effect(() => {
console.log('é¸ä¸çå·¥å®:', this.selectedOrder());
});
}
}
5. æå層è¨è¨
@Injectable({ providedIn: 'root' })
export class WorkOrderService {
private apiUrl = environment.apiUrl + '/work-orders';
constructor(private http: HttpClient) {}
// å表æ¥è©¢ï¼æ¯æ´ç¯©é¸ï¼
getList(params?: WorkOrderQueryParams): Observable<ApiResponse<WorkOrder[]>> {
return this.http.get<ApiResponse<WorkOrder[]>>(this.apiUrl, { params });
}
// å®çæ¥è©¢
getById(id: string): Observable<ApiResponse<WorkOrder>> {
return this.http.get<ApiResponse<WorkOrder>>(`${this.apiUrl}/${id}`);
}
// æ°å¢
create(data: CreateWorkOrderDto): Observable<ApiResponse<WorkOrder>> {
return this.http.post<ApiResponse<WorkOrder>>(this.apiUrl, data);
}
// æ´æ°
update(id: string, data: UpdateWorkOrderDto): Observable<ApiResponse<WorkOrder>> {
return this.http.put<ApiResponse<WorkOrder>>(`${this.apiUrl}/${id}`, data);
}
// åªé¤
delete(id: string): Observable<ApiResponse<void>> {
return this.http.delete<ApiResponse<void>>(`${this.apiUrl}/${id}`);
}
}
6. API åææ ¼å¼
// çµ±ä¸åææ ¼å¼
interface ApiResponse<T> {
success: boolean;
data?: T;
error?: {
code: string;
message: string;
};
}
// åé åæ
interface PaginatedResponse<T> extends ApiResponse<T[]> {
pagination: {
total: number;
page: number;
limit: number;
};
}
7. 表å®èç
// æ¨è¦ï¼Reactive Forms + PrimeNG
@Component({
template: `
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<div class="field">
<label for="orderNumber">å·¥å®ç·¨è</label>
<input pInputText id="orderNumber" formControlName="orderNumber" />
<small *ngIf="form.get('orderNumber')?.errors?.['required']" class="p-error">
å¿
å¡«æ¬ä½
</small>
</div>
<div class="field">
<label for="customer">客æ¶</label>
<p-select
id="customer"
formControlName="customerId"
[options]="customers()"
optionLabel="name"
optionValue="id"
placeholder="鏿客æ¶"
/>
</div>
<div class="field">
<label for="dueDate">交æ</label>
<p-datepicker
id="dueDate"
formControlName="dueDate"
dateFormat="yy-mm-dd"
/>
</div>
<p-button type="submit" label="å²å" [loading]="isSubmitting()" />
</form>
`
})
export class WorkOrderFormComponent {
form = new FormGroup({
orderNumber: new FormControl('', Validators.required),
customerId: new FormControl('', Validators.required),
dueDate: new FormControl<Date | null>(null)
});
customers = signal<Customer[]>([]);
isSubmitting = signal(false);
}
8. è¡¨æ ¼æä½³å¯¦è¸
@Component({
template: `
<p-table
[value]="workOrders()"
[paginator]="true"
[rows]="20"
[rowsPerPageOptions]="[10, 20, 50]"
[loading]="isLoading()"
[globalFilterFields]="['orderNumber', 'customerName']"
styleClass="p-datatable-sm"
>
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="orderNumber">
å·¥å®ç·¨è <p-sortIcon field="orderNumber" />
</th>
<th pSortableColumn="status">çæ
</th>
<th>æä½</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-order>
<tr>
<td>{{ order.orderNumber }}</td>
<td>
<p-tag [value]="order.status" [severity]="getStatusSeverity(order.status)" />
</td>
<td>
<p-button icon="pi pi-pencil" [text]="true" (click)="edit(order)" />
<p-button icon="pi pi-trash" [text]="true" severity="danger" (click)="confirmDelete(order)" />
</td>
</tr>
</ng-template>
<ng-template pTemplate="emptymessage">
<tr>
<td colspan="3" class="text-center">ç¡è³æ</td>
</tr>
</ng-template>
</p-table>
`
})
export class WorkOrderTableComponent {
workOrders = signal<WorkOrder[]>([]);
isLoading = signal(false);
getStatusSeverity(status: string): 'success' | 'info' | 'warn' | 'danger' {
const map: Record<string, 'success' | 'info' | 'warn' | 'danger'> = {
completed: 'success',
in_progress: 'info',
pending: 'warn',
cancelled: 'danger'
};
return map[status] || 'info';
}
}
9. Drawer å´éæ¬æ¨¡å¼
@Component({
template: `
<p-drawer
[(visible)]="drawerVisible"
[header]="isEditMode() ? '編輯工å®' : 'æ°å¢å·¥å®'"
position="right"
[style]="{ width: '500px' }"
(onHide)="onClose()"
>
<app-work-order-form
[workOrder]="selectedOrder()"
(save)="onSave($event)"
(cancel)="drawerVisible = false"
/>
</p-drawer>
`
})
export class WorkOrderListComponent {
drawerVisible = false;
selectedOrder = signal<WorkOrder | null>(null);
isEditMode = computed(() => this.selectedOrder() !== null);
openCreate() {
this.selectedOrder.set(null);
this.drawerVisible = true;
}
openEdit(order: WorkOrder) {
this.selectedOrder.set(order);
this.drawerVisible = true;
}
}
10. é¯èª¤èç
// HTTP ææªå¨
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
constructor(private messageService: MessageService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError((error: HttpErrorResponse) => {
let message = 'ç¼çé¯èª¤ï¼è«ç¨å¾å試';
if (error.status === 401) {
message = 'è«éæ°ç»å
¥';
} else if (error.status === 403) {
message = 'æ¬éä¸è¶³';
} else if (error.status === 404) {
message = 'è³æä¸åå¨';
} else if (error.status === 503) {
message = 'æåæ«æç¡æ³ä½¿ç¨';
} else if (error.error?.error?.message) {
message = error.error.error.message;
}
this.messageService.add({
severity: 'error',
summary: 'é¯èª¤',
detail: message
});
return throwError(() => error);
})
);
}
}
ç¦æ¢äºé
- ç¦æ¢ Fallback Mock è³æ – API 失ææé¡¯ç¤ºé¯èª¤ï¼ä¸å¾ä½¿ç¨åè³æ
- ç¦æ¢ Emoji – ç¨å¼ç¢¼ã註解ãUI é½ä¸ä½¿ç¨ emoji
- ç¦æ¢ç°¡é«å – å ¨ç¨ä½¿ç¨æ£é«ä¸æï¼å°ç£ç¨èªï¼
- ç¦æ¢å®æªè¶ é 500 è¡ – è¶ ééæå
æè½åªå
- OnPush è®æ´åµæ¸¬ – æé Signals 使ç¨
- trackBy – è¡¨æ ¼åè¡¨å¿ é 使ç¨
- Lazy Loading – åè½æ¨¡çµå»¶é²è¼å ¥
- èæ¬æ²å – 大éè³æä½¿ç¨
<p-scroller>