|
|
@@ -3,7 +3,7 @@ import { ChangeDetectionStrategy, Component, DestroyRef, OnInit, computed, injec
|
|
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
|
import { FormsModule } from '@angular/forms';
|
|
|
import { ActivatedRoute } from '@angular/router';
|
|
|
-import { ChevronLeft, ChevronRight, Eye, LoaderCircle, LucideAngularModule, Pencil, Plus, RefreshCw, Search, Trash2, X } from 'lucide-angular';
|
|
|
+import { ChevronLeft, ChevronRight, Eye, KeyRound, LoaderCircle, LucideAngularModule, Pencil, Plus, RefreshCw, Search, Trash2, X } from 'lucide-angular';
|
|
|
import { AdminClassSchema, AdminFieldSchema, ResourcePage } from '../admin.models';
|
|
|
import { CloudFunctionsService } from '../cloud-functions.service';
|
|
|
|
|
|
@@ -21,6 +21,7 @@ export class AdminResourceComponent implements OnInit {
|
|
|
readonly page = signal<ResourcePage | null>(null);
|
|
|
readonly loading = signal(true);
|
|
|
readonly saving = signal(false);
|
|
|
+ readonly resettingPassword = signal(false);
|
|
|
readonly error = signal('');
|
|
|
readonly search = signal('');
|
|
|
readonly editorOpen = signal(false);
|
|
|
@@ -28,7 +29,11 @@ export class AdminResourceComponent implements OnInit {
|
|
|
readonly draft = signal<Record<string, unknown>>({});
|
|
|
readonly fieldErrors = signal<Record<string, string>>({});
|
|
|
readonly fieldInputs = signal<Record<string, string>>({});
|
|
|
- readonly icons = { ChevronLeft, ChevronRight, Eye, LoaderCircle, Pencil, Plus, RefreshCw, Search, Trash2, X };
|
|
|
+ readonly newPassword = signal('');
|
|
|
+ readonly confirmPassword = signal('');
|
|
|
+ readonly passwordMessage = signal('');
|
|
|
+ readonly passwordError = signal('');
|
|
|
+ readonly icons = { ChevronLeft, ChevronRight, Eye, KeyRound, 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'];
|
|
|
@@ -57,16 +62,16 @@ export class AdminResourceComponent implements OnInit {
|
|
|
finally { this.loading.set(false); }
|
|
|
}
|
|
|
|
|
|
- openCreate(): void { this.draftId.set(''); this.draft.set({}); this.fieldErrors.set({}); this.fieldInputs.set({}); this.editorOpen.set(true); }
|
|
|
+ openCreate(): void { this.draftId.set(''); this.draft.set({}); this.fieldErrors.set({}); this.fieldInputs.set({}); this.resetPasswordFields(); 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);
|
|
|
+ this.draftId.set(objectId); this.draft.set(detail); this.fieldErrors.set({}); this.fieldInputs.set({}); this.resetPasswordFields(); 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({}); }
|
|
|
+ closeEditor(): void { this.editorOpen.set(false); this.draft.set({}); this.draftId.set(''); this.fieldErrors.set({}); this.fieldInputs.set({}); this.resetPasswordFields(); }
|
|
|
setField(field: AdminFieldSchema, raw: unknown): void {
|
|
|
let value = raw;
|
|
|
if (field.type === 'Number') value = raw === '' ? null : Number(raw);
|
|
|
@@ -102,6 +107,18 @@ export class AdminResourceComponent implements OnInit {
|
|
|
} catch (error) { this.error.set(error instanceof Error ? error.message : '保存失败'); }
|
|
|
finally { this.saving.set(false); }
|
|
|
}
|
|
|
+ async resetPassword(): Promise<void> {
|
|
|
+ this.passwordError.set(''); this.passwordMessage.set('');
|
|
|
+ if (this.className() !== '_User' || !this.draftId()) { this.passwordError.set('只能重置现有用户密码'); return; }
|
|
|
+ if (this.newPassword().length < 8) { this.passwordError.set('新密码至少 8 位'); return; }
|
|
|
+ if (this.newPassword() !== this.confirmPassword()) { this.passwordError.set('两次输入的密码不一致'); return; }
|
|
|
+ this.resettingPassword.set(true);
|
|
|
+ try {
|
|
|
+ await this.functions.admin('resetPassword', { className: '_User', objectId: this.draftId(), newPassword: this.newPassword() });
|
|
|
+ this.newPassword.set(''); this.confirmPassword.set(''); this.passwordMessage.set('密码已安全重置');
|
|
|
+ } catch (error) { this.passwordError.set(error instanceof Error ? error.message : '密码重置失败'); }
|
|
|
+ finally { this.resettingPassword.set(false); }
|
|
|
+ }
|
|
|
async remove(row: Record<string, unknown>): Promise<void> {
|
|
|
const objectId = String(row['objectId'] ?? '');
|
|
|
if (!objectId || !confirm(`确认删除 ${this.className()} / ${objectId}?`)) return;
|
|
|
@@ -116,4 +133,5 @@ export class AdminResourceComponent implements OnInit {
|
|
|
}
|
|
|
return String(value);
|
|
|
}
|
|
|
+ private resetPasswordFields(): void { this.newPassword.set(''); this.confirmPassword.set(''); this.passwordMessage.set(''); this.passwordError.set(''); }
|
|
|
}
|