operations-stores.component.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import { DropdownDirective } from '../shared/dropdown.directive';
  2. import { CommonModule } from '@angular/common';
  3. import { ChangeDetectionStrategy, Component, OnInit, inject, signal } from '@angular/core';
  4. import { FormsModule } from '@angular/forms';
  5. import { RouterLink } from '@angular/router';
  6. import { Building2, ChevronLeft, ChevronRight, Eye, Pencil, Plus, RefreshCw, Search, Trash2, Users, X } from 'lucide-angular';
  7. import { LucideAngularModule } from 'lucide-angular';
  8. import { StorePage, StoreSummary } from '../admin.models';
  9. import { OperationsService } from '../operations.service';
  10. @Component({
  11. selector: 'app-operations-stores',
  12. standalone: true,
  13. imports: [DropdownDirective, CommonModule, FormsModule, RouterLink, LucideAngularModule],
  14. templateUrl: './operations-stores.component.html',
  15. styleUrls: ['./operations-professional.scss', './operations-stores.component.scss'],
  16. changeDetection: ChangeDetectionStrategy.OnPush,
  17. })
  18. export class OperationsStoresComponent implements OnInit {
  19. readonly page = signal<StorePage | null>(null);
  20. readonly query = signal('');
  21. readonly status = signal('all');
  22. readonly loading = signal(true);
  23. readonly saving = signal(false);
  24. readonly error = signal('');
  25. readonly message = signal('');
  26. readonly drawerOpen = signal(false);
  27. readonly editing = signal<StoreSummary | null>(null);
  28. readonly deleteArmed = signal(false);
  29. readonly username = signal('');
  30. readonly password = signal('');
  31. readonly storeName = signal('');
  32. readonly contactName = signal('');
  33. readonly mobile = signal('');
  34. readonly parentStoreId = signal(0);
  35. readonly disabled = signal(false);
  36. readonly reason = signal('');
  37. readonly icons = { Building2, ChevronLeft, ChevronRight, Eye, Pencil, Plus, RefreshCw, Search, Trash2, Users, X };
  38. private readonly operations = inject(OperationsService);
  39. ngOnInit(): void { void this.load(1); }
  40. async load(page = 1): Promise<void> {
  41. this.loading.set(true);
  42. this.error.set('');
  43. try {
  44. this.page.set(await this.operations.stores(page, 20, this.query(), { status: this.status() }));
  45. } catch (error) {
  46. this.error.set(error instanceof Error ? error.message : '门店数据加载失败');
  47. } finally {
  48. this.loading.set(false);
  49. }
  50. }
  51. reset(): void {
  52. this.query.set('');
  53. this.status.set('all');
  54. void this.load(1);
  55. }
  56. pageCount(): number { return Math.max(1, Math.ceil((this.page()?.total || 0) / (this.page()?.pageSize || 20))); }
  57. availableParents() {
  58. const ownId = this.editing()?.userId || 0;
  59. return (this.page()?.parents || []).filter((store) => store.userId !== ownId);
  60. }
  61. openCreate(): void {
  62. this.editing.set(null);
  63. this.username.set('');
  64. this.password.set('');
  65. this.storeName.set('');
  66. this.contactName.set('');
  67. this.mobile.set('');
  68. this.parentStoreId.set(0);
  69. this.disabled.set(false);
  70. this.reason.set('新增门店');
  71. this.deleteArmed.set(false);
  72. this.error.set('');
  73. this.drawerOpen.set(true);
  74. }
  75. openEdit(row: StoreSummary, deleteMode = false): void {
  76. if (row.readOnly) {
  77. this.message.set('该门店来自旧系统实时数据,持续同步落库前暂不允许在新后台单边修改。');
  78. return;
  79. }
  80. this.editing.set(row);
  81. this.username.set(row.username);
  82. this.password.set('');
  83. this.storeName.set(row.storeName);
  84. this.contactName.set(row.contactName || '');
  85. this.mobile.set(row.mobile || '');
  86. this.parentStoreId.set(row.parentStoreId || 0);
  87. this.disabled.set(row.status === 'disabled');
  88. this.reason.set('');
  89. this.deleteArmed.set(deleteMode);
  90. this.error.set('');
  91. this.drawerOpen.set(true);
  92. }
  93. closeDrawer(): void {
  94. this.drawerOpen.set(false);
  95. this.deleteArmed.set(false);
  96. }
  97. async save(): Promise<void> {
  98. const editing = this.editing();
  99. if (!this.username().trim() || !this.storeName().trim() || !this.reason().trim()) {
  100. this.error.set('登录账号、门店名称和操作原因必填');
  101. return;
  102. }
  103. if (!editing && !this.password()) {
  104. this.error.set('新增门店必须设置初始密码');
  105. return;
  106. }
  107. this.saving.set(true);
  108. this.error.set('');
  109. try {
  110. await this.operations.mutate(
  111. editing ? 'ops/stores/update' : 'ops/stores/create',
  112. editing?.objectId || '',
  113. this.reason(),
  114. {
  115. username: this.username().trim(),
  116. password: this.password(),
  117. storeName: this.storeName().trim(),
  118. contactName: this.contactName().trim(),
  119. mobile: this.mobile().trim(),
  120. parentStoreId: this.parentStoreId(),
  121. disabled: this.disabled(),
  122. },
  123. editing?.updatedAt || '',
  124. );
  125. this.message.set(editing ? '门店资料已更新' : '门店已新增');
  126. this.closeDrawer();
  127. await this.load(editing ? this.page()?.page || 1 : 1);
  128. } catch (error) {
  129. this.error.set(error instanceof Error ? error.message : '门店资料保存失败');
  130. } finally {
  131. this.saving.set(false);
  132. }
  133. }
  134. async remove(): Promise<void> {
  135. const editing = this.editing();
  136. if (!editing) return;
  137. if (!this.reason().trim()) {
  138. this.error.set('删除前必须填写操作原因');
  139. return;
  140. }
  141. if (!this.deleteArmed()) {
  142. this.deleteArmed.set(true);
  143. return;
  144. }
  145. this.saving.set(true);
  146. this.error.set('');
  147. try {
  148. await this.operations.mutate('ops/stores/delete', editing.objectId, this.reason(), {}, editing.updatedAt);
  149. this.message.set('门店已删除,历史业务数据仍保留');
  150. this.closeDrawer();
  151. await this.load(1);
  152. } catch (error) {
  153. this.error.set(error instanceof Error ? error.message : '门店删除失败');
  154. } finally {
  155. this.saving.set(false);
  156. }
  157. }
  158. }