upload-avatar.ts 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. import { Component, input, model, output, signal } from '@angular/core';
  2. import { UiButton } from '../button/button';
  3. /**
  4. * 上传头像:圆形头像预览 + 上传/删除(操作按钮复用 ui-button),
  5. * 读取本地文件为 dataURL;文件输入为浏览器原生能力,组件内部持有。
  6. */
  7. @Component({
  8. selector: 'ui-upload-avatar',
  9. imports: [UiButton],
  10. templateUrl: './upload-avatar.html',
  11. styleUrl: './upload-avatar.scss',
  12. })
  13. export class UiUploadAvatar {
  14. /** 头像地址(dataURL 或 URL,双向绑定) */
  15. readonly src = model<string>('');
  16. /** 头像直径(px) */
  17. readonly size = input(80);
  18. /** 是否圆形 */
  19. readonly circle = input(true, { transform: (v: unknown) => v === true || v === 'true' });
  20. /** 空态图标 */
  21. readonly icon = input('fa-solid fa-camera');
  22. /** 上传成功(拿到 dataURL)时触发 */
  23. readonly change = output<string>();
  24. /** 校验通过后输出原始 File(供业务方走真实上传链路) */
  25. readonly changeFile = output<File>();
  26. protected readonly uploading = signal(false);
  27. protected onPick(e: Event): void {
  28. const input = e.currentTarget as HTMLInputElement;
  29. const file = input.files?.[0];
  30. input.value = '';
  31. if (!file || !file.type.startsWith('image/')) return;
  32. this.uploading.set(true);
  33. this.changeFile.emit(file);
  34. const reader = new FileReader();
  35. reader.onload = () => {
  36. this.uploading.set(false);
  37. if (typeof reader.result === 'string') {
  38. this.src.set(reader.result);
  39. this.change.emit(reader.result);
  40. }
  41. };
  42. reader.onerror = () => this.uploading.set(false);
  43. reader.readAsDataURL(file);
  44. }
  45. protected remove(): void {
  46. this.src.set('');
  47. }
  48. }