| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- import { CommonModule } from '@angular/common';
- import { ChangeDetectionStrategy, Component, DestroyRef, OnInit, computed, inject, signal } from '@angular/core';
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
- import { FormsModule } from '@angular/forms';
- import { ActivatedRoute } from '@angular/router';
- import { ChevronLeft, ChevronRight, LoaderCircle, LucideAngularModule, Pencil, Plus, RefreshCw, Search, Trash2, X } from 'lucide-angular';
- import { AdminClassSchema, AdminFieldSchema, ResourcePage } from '../admin.models';
- import { CloudFunctionsService } from '../cloud-functions.service';
- @Component({
- selector: 'app-admin-resource',
- standalone: true,
- imports: [CommonModule, FormsModule, LucideAngularModule],
- templateUrl: './admin-resource.component.html',
- styleUrl: './admin-resource.component.scss',
- changeDetection: ChangeDetectionStrategy.OnPush,
- })
- export class AdminResourceComponent implements OnInit {
- readonly className = signal('');
- readonly schema = signal<AdminClassSchema | null>(null);
- readonly page = signal<ResourcePage | null>(null);
- readonly loading = signal(true);
- readonly saving = signal(false);
- readonly error = signal('');
- readonly search = signal('');
- readonly editorOpen = signal(false);
- readonly draftId = signal('');
- readonly draft = signal<Record<string, unknown>>({});
- readonly fieldErrors = signal<Record<string, string>>({});
- readonly fieldInputs = signal<Record<string, string>>({});
- readonly icons = { ChevronLeft, ChevronRight, LoaderCircle, Pencil, Plus, RefreshCw, Search, Trash2, X };
- readonly columns = computed(() => {
- const fields = this.schema()?.fields ?? [];
- const preferred = ['objectId', 'name', 'title', 'username', 'realName', 'mobile', 'sourceKey', 'status', 'createdAt', 'updatedAt'];
- return [...fields].sort((a, b) => preferred.indexOf(a.name) - preferred.indexOf(b.name)).slice(0, 7);
- });
- private readonly functions = inject(CloudFunctionsService);
- private readonly route = inject(ActivatedRoute);
- private readonly destroyRef = inject(DestroyRef);
- ngOnInit(): void {
- this.route.paramMap.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => {
- this.className.set(params.get('className') ?? '');
- this.search.set('');
- void this.load(1, true);
- });
- }
- async load(pageNumber = this.page()?.page ?? 1, reloadSchema = false): Promise<void> {
- this.loading.set(true); this.error.set('');
- try {
- if (reloadSchema || !this.schema()) this.schema.set(await this.functions.admin<AdminClassSchema>('schema', { className: this.className() }));
- this.page.set(await this.functions.admin<ResourcePage>('list', { className: this.className(), page: pageNumber, pageSize: 20, search: this.search().trim() }));
- } catch (error) { this.error.set(error instanceof Error ? error.message : '数据加载失败'); }
- finally { this.loading.set(false); }
- }
- openCreate(): void { this.draftId.set(''); this.draft.set({}); this.fieldErrors.set({}); this.fieldInputs.set({}); this.editorOpen.set(true); }
- async openEdit(row: Record<string, unknown>): Promise<void> {
- const objectId = String(row['objectId'] ?? '');
- this.error.set('');
- try {
- const detail = await this.functions.admin<Record<string, unknown>>('get', { className: this.className(), objectId });
- this.draftId.set(objectId); this.draft.set(detail); this.fieldErrors.set({}); this.fieldInputs.set({}); this.editorOpen.set(true);
- } catch (error) { this.error.set(error instanceof Error ? error.message : '详情加载失败'); }
- }
- closeEditor(): void { this.editorOpen.set(false); this.draft.set({}); this.draftId.set(''); this.fieldErrors.set({}); this.fieldInputs.set({}); }
- setField(field: AdminFieldSchema, raw: unknown): void {
- let value = raw;
- if (field.type === 'Number') value = raw === '' ? null : Number(raw);
- if (field.type === 'Pointer') value = raw ? { __type: 'Pointer', className: field.targetClass, objectId: String(raw) } : null;
- if ((field.type === 'Object' || field.type === 'Array') && typeof raw === 'string') {
- this.fieldInputs.update((current) => ({ ...current, [field.name]: raw }));
- try {
- value = raw.trim() ? JSON.parse(raw) : field.type === 'Array' ? [] : {};
- const valid = field.type === 'Array' ? Array.isArray(value) : Boolean(value) && typeof value === 'object' && !Array.isArray(value);
- if (!valid) throw new Error();
- this.fieldErrors.update((current) => { const next = { ...current }; delete next[field.name]; return next; });
- } catch {
- this.fieldErrors.update((current) => ({ ...current, [field.name]: field.type === 'Array' ? '请输入有效的 JSON 数组' : '请输入有效的 JSON 对象' }));
- return;
- }
- }
- this.draft.update((current) => ({ ...current, [field.name]: value }));
- }
- inputValue(field: AdminFieldSchema): string | number {
- const value = this.draft()[field.name];
- if (field.type === 'Pointer') return String((value as { objectId?: string } | null)?.objectId ?? '');
- if (field.type === 'Date') return String((value as { iso?: string } | string | null)?.hasOwnProperty?.('iso') ? (value as { iso: string }).iso.slice(0, 16) : String(value ?? '').slice(0, 16));
- if (field.type === 'Object' || field.type === 'Array') return this.fieldInputs()[field.name] ?? (value == null ? '' : JSON.stringify(value, null, 2));
- return typeof value === 'number' ? value : String(value ?? '');
- }
- async save(): Promise<void> {
- const invalidFields = Object.keys(this.fieldErrors());
- if (invalidFields.length) { this.error.set(`请先修正字段格式:${invalidFields.join('、')}`); return; }
- this.saving.set(true); this.error.set('');
- try {
- await this.functions.admin('save', { className: this.className(), objectId: this.draftId() || undefined, fields: this.draft() });
- this.closeEditor(); await this.load();
- } catch (error) { this.error.set(error instanceof Error ? error.message : '保存失败'); }
- finally { this.saving.set(false); }
- }
- async remove(row: Record<string, unknown>): Promise<void> {
- const objectId = String(row['objectId'] ?? '');
- if (!objectId || !confirm(`确认删除 ${this.className()} / ${objectId}?`)) return;
- try { await this.functions.admin('delete', { className: this.className(), objectId }); await this.load(); }
- catch (error) { this.error.set(error instanceof Error ? error.message : '删除失败'); }
- }
- display(value: unknown): string {
- if (value == null || value === '') return '—';
- if (typeof value === 'object') {
- const data = value as Record<string, unknown>;
- return String(data['name'] ?? data['title'] ?? data['username'] ?? data['objectId'] ?? JSON.stringify(value));
- }
- return String(value);
- }
- }
|