settlement-card.html 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. <div class="settlement-card">
  2. <!-- 自动化控制头部 -->
  3. @if (showAutomationControls) {
  4. <div class="automation-header">
  5. <div class="automation-title">
  6. <mat-icon>auto_awesome</mat-icon>
  7. <span>自动化结算处理</span>
  8. </div>
  9. <div class="automation-actions">
  10. <button
  11. mat-raised-button
  12. color="primary"
  13. [disabled]="isProcessing()"
  14. (click)="processAllAutomation()"
  15. matTooltip="批量处理所有待结算项">
  16. @if (isProcessing()) {
  17. <mat-spinner diameter="16"></mat-spinner>
  18. <span>处理中...</span>
  19. } @else {
  20. <ng-container>
  21. <mat-icon>play_arrow</mat-icon>
  22. <span>批量处理</span>
  23. </ng-container>
  24. }
  25. </button>
  26. </div>
  27. </div>
  28. }
  29. <div class="settlement-card">
  30. <!-- 统计概览 -->
  31. <div class="stats-overview">
  32. <div class="stats-title">
  33. <mat-icon>account_balance_wallet</mat-icon>
  34. <span>尾款结算统计</span>
  35. </div>
  36. <div class="stats-grid">
  37. <div class="stat-item total">
  38. <div class="stat-value">{{ formatAmount(stats().totalAmount) }}</div>
  39. <div class="stat-label">总金额 ({{ stats().totalCount }}项)</div>
  40. </div>
  41. <div class="stat-item pending">
  42. <div class="stat-value">{{ formatAmount(stats().pendingAmount) }}</div>
  43. <div class="stat-label">待结算 ({{ stats().pendingCount }}项)</div>
  44. </div>
  45. <div class="stat-item overdue">
  46. <div class="stat-value">{{ formatAmount(stats().overdueAmount) }}</div>
  47. <div class="stat-label">逾期 ({{ stats().overdueCount }}项)</div>
  48. </div>
  49. <div class="stat-item completed">
  50. <div class="stat-value">{{ formatAmount(stats().completedAmount) }}</div>
  51. <div class="stat-label">已结算 ({{ stats().completedCount }}项)</div>
  52. </div>
  53. </div>
  54. </div>
  55. <!-- 筛选区域 -->
  56. <div class="filter-section">
  57. <div class="search-box">
  58. <mat-icon>search</mat-icon>
  59. <input
  60. type="text"
  61. placeholder="搜索项目名称或阶段..."
  62. [value]="searchKeyword()"
  63. (input)="updateSearchKeyword($event.target.value)">
  64. </div>
  65. <div class="filter-buttons">
  66. <button
  67. class="filter-btn"
  68. [class.active]="statusFilter() === 'all'"
  69. (click)="updateStatusFilter('all')">
  70. 全部
  71. </button>
  72. <button
  73. class="filter-btn"
  74. [class.active]="statusFilter() === '待结算'"
  75. (click)="updateStatusFilter('待结算')">
  76. 待结算
  77. </button>
  78. <button
  79. class="filter-btn"
  80. [class.active]="statusFilter() === '已结算'"
  81. (click)="updateStatusFilter('已结算')">
  82. 已结算
  83. </button>
  84. <button
  85. class="filter-btn overdue"
  86. [class.active]="statusFilter() === 'overdue'"
  87. (click)="updateStatusFilter('overdue')">
  88. 逾期
  89. </button>
  90. </div>
  91. </div>
  92. <!-- 结算列表 -->
  93. <div class="settlement-list">
  94. @if (filteredSettlements().length > 0) {
  95. <div class="list-header">
  96. <span class="col-project">项目信息</span>
  97. <span class="col-amount">金额</span>
  98. <span class="col-status">状态</span>
  99. <span class="col-actions">操作</span>
  100. <span class="col-date">日期</span>
  101. </div>
  102. <div class="list-body">
  103. @for (settlement of filteredSettlements(); track settlement.id) {
  104. <div class="settlement-item" [class]="getStatusClass(settlement)">
  105. <div class="col-project">
  106. <div class="project-name">{{ settlement.projectName || '结算项' }}</div>
  107. <div class="stage-name">{{ settlement.stage || '阶段' }}</div>
  108. </div>
  109. <div class="col-amount">
  110. <div class="amount">{{ formatAmount(settlement.amount || 0) }}</div>
  111. <div class="percentage">{{ settlement.percentage }}%</div>
  112. </div>
  113. <div class="col-status">
  114. <span class="status-badge" [class]="getStatusClass(settlement)">
  115. {{ settlement.status }}
  116. </span>
  117. @if (isOverdue(settlement)) {
  118. <div class="overdue-days">逾期 {{ getDaysOverdue(settlement) }} 天</div>
  119. }
  120. <!-- 自动化处理按钮 -->
  121. @if (showAutomationControls && settlement.status === '待结算' && !isOverdue(settlement)) {
  122. <button
  123. class="auto-process-btn"
  124. mat-icon-button
  125. color="primary"
  126. [disabled]="isProcessing() && processingSettlementId() !== settlement.id"
  127. (click)="processSettlementAutomation(settlement); $event.stopPropagation()"
  128. matTooltip="自动处理此结算">
  129. @if (processingSettlementId() === settlement.id) {
  130. <mat-spinner diameter="16"></mat-spinner>
  131. } @else {
  132. <mat-icon>auto_awesome</mat-icon>
  133. }
  134. </button>
  135. }
  136. </div>
  137. <!-- 新增操作列 -->
  138. <div class="col-actions">
  139. <div class="action-buttons">
  140. <!-- 项目验收确认按钮 -->
  141. @if (settlement.status === '待结算') {
  142. <button
  143. mat-stroked-button
  144. [color]="getAcceptanceStatus(settlement.projectId) === 'confirmed' ? 'primary' : 'accent'"
  145. [disabled]="getAcceptanceStatus(settlement.projectId) === 'confirming'"
  146. (click)="confirmProjectAcceptance(settlement)"
  147. matTooltip="技术确认项目验收"
  148. class="acceptance-btn">
  149. @if (getAcceptanceStatus(settlement.projectId) === 'confirming') {
  150. <mat-spinner diameter="16"></mat-spinner>
  151. <span>确认中</span>
  152. } @else if (getAcceptanceStatus(settlement.projectId) === 'confirmed') {
  153. <ng-container>
  154. <mat-icon>check_circle</mat-icon>
  155. <span>已验收</span>
  156. </ng-container>
  157. } @else {
  158. <ng-container>
  159. <mat-icon>task_alt</mat-icon>
  160. <span>确认验收</span>
  161. </ng-container>
  162. }
  163. </button>
  164. }
  165. <!-- 客服跟进提醒按钮 -->
  166. @if (settlement.status === '待结算' && getAcceptanceStatus(settlement.projectId) === 'confirmed') {
  167. <button
  168. mat-stroked-button
  169. color="warn"
  170. [disabled]="getReminderStatus(settlement.projectId) === 'creating' || getReminderStatus(settlement.projectId) === 'created'"
  171. (click)="createCustomerServiceReminder(settlement, 'payment_follow_up')"
  172. matTooltip="创建客服跟进尾款提醒"
  173. class="reminder-btn">
  174. @if (getReminderStatus(settlement.projectId) === 'creating') {
  175. <mat-spinner diameter="16"></mat-spinner>
  176. <span>创建中</span>
  177. } @else if (getReminderStatus(settlement.projectId) === 'created') {
  178. <ng-container>
  179. <mat-icon>notifications_active</mat-icon>
  180. <span>已提醒</span>
  181. </ng-container>
  182. } @else {
  183. <ng-container>
  184. <mat-icon>notification_add</mat-icon>
  185. <span>跟进尾款</span>
  186. </ng-container>
  187. }
  188. </button>
  189. }
  190. <!-- 一键发图到企业微信群按钮 -->
  191. @if (canSendImages(settlement)) {
  192. <button
  193. mat-raised-button
  194. color="primary"
  195. [disabled]="isSendingImagesForProject(settlement.projectId)"
  196. (click)="sendImagesToWeChatGroup(settlement)"
  197. matTooltip="收款后一键发送大图到企业微信群"
  198. class="wechat-send-btn">
  199. @if (isSendingImagesForProject(settlement.projectId)) {
  200. <mat-spinner diameter="16"></mat-spinner>
  201. <span>发送中</span>
  202. } @else {
  203. <ng-container>
  204. <mat-icon>send</mat-icon>
  205. <span>发图到群</span>
  206. </ng-container>
  207. }
  208. </button>
  209. }
  210. <!-- 原有的处理结算按钮 -->
  211. <button
  212. mat-raised-button
  213. color="primary"
  214. [disabled]="settlement.status === '已结算'"
  215. (click)="processSettlement(settlement.id)"
  216. class="process-btn">
  217. @if (settlement.status === '待结算') {
  218. 处理结算
  219. } @else if (settlement.status === '逾期') {
  220. 逾期处理
  221. } @else {
  222. 已完成
  223. }
  224. </button>
  225. @if (isOverdue(settlement)) {
  226. <button
  227. mat-stroked-button
  228. color="warn"
  229. (click)="sendReminder(settlement.id)"
  230. class="reminder-btn">
  231. 发送催款
  232. </button>
  233. }
  234. </div>
  235. </div>
  236. <div class="col-date">
  237. @if (settlement.settledAt) {
  238. <div class="settled-date">{{ settlement.settledAt | date:'yyyy-MM-dd' }}</div>
  239. <div class="date-label">结算日期</div>
  240. } @else {
  241. <div class="due-date">{{ settlement.createdAt | date:'yyyy-MM-dd' }}</div>
  242. <div class="date-label">创建日期</div>
  243. }
  244. </div>
  245. </div>
  246. }
  247. </div>
  248. } @else {
  249. <div class="empty-state">
  250. <div class="empty-icon">💰</div>
  251. <div class="empty-title">暂无结算信息</div>
  252. <div class="empty-desc">当前筛选条件下没有找到相关的结算记录</div>
  253. </div>
  254. }
  255. </div>
  256. </div>
  257. </div>