complaint-card.html 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. <div class="complaint-card">
  2. <!-- 统计数据概览 -->
  3. <div class="stats-overview">
  4. <h4>投诉处理概览</h4>
  5. <div class="stats-grid">
  6. <div class="stat-item total">
  7. <div class="stat-value">{{ stats().totalCount }}</div>
  8. <div class="stat-label">总投诉数</div>
  9. </div>
  10. <div class="stat-item pending">
  11. <div class="stat-value">{{ stats().pendingCount }}</div>
  12. <div class="stat-label">待处理</div>
  13. </div>
  14. <div class="stat-item processing">
  15. <div class="stat-value">{{ stats().processingCount }}</div>
  16. <div class="stat-label">处理中</div>
  17. </div>
  18. <div class="stat-item resolved">
  19. <div class="stat-value">{{ stats().resolvedCount }}</div>
  20. <div class="stat-label">已解决</div>
  21. </div>
  22. <div class="stat-item high-priority">
  23. <div class="stat-value">{{ stats().highPriorityCount }}</div>
  24. <div class="stat-label">高优先级</div>
  25. </div>
  26. <div class="stat-item resolution-time">
  27. <div class="stat-value">{{ stats().averageResolutionTime }}<span class="time-suffix">天</span></div>
  28. <div class="stat-label">平均解决时间</div>
  29. </div>
  30. </div>
  31. </div>
  32. <!-- 优先级统计 -->
  33. <div class="priority-stats">
  34. <h5>优先级分布</h5>
  35. <div class="priority-cards-grid">
  36. @for (priority of priorities; track priority.value) {
  37. <div class="priority-card" [style.border-color]="priority.color">
  38. <div class="priority-card-header" [style.background-color]="priority.color">
  39. <span class="priority-card-label">{{ priority.label }}</span>
  40. </div>
  41. <div class="priority-card-body">
  42. <div class="priority-card-count">{{ stats().priorityStats[priority.value] || 0 }}</div>
  43. <div class="priority-card-suffix">个</div>
  44. </div>
  45. </div>
  46. }
  47. </div>
  48. </div>
  49. <!-- 类型统计 -->
  50. <div class="type-stats">
  51. <h5>问题类型统计</h5>
  52. <div class="type-grid">
  53. @for (type of complaintTypes; track type.value) {
  54. <div class="type-item">
  55. <span class="type-label">{{ type.label }}</span>
  56. <span class="type-count">{{ stats().typeStats[type.value] || 0 }}</span>
  57. </div>
  58. }
  59. </div>
  60. </div>
  61. <!-- 筛选区域 -->
  62. <div class="filter-section">
  63. <div class="search-row">
  64. <div class="search-group">
  65. <label>搜索:</label>
  66. <input
  67. type="text"
  68. class="search-input"
  69. placeholder="搜索投诉内容、客户姓名..."
  70. [value]="searchKeyword()"
  71. (input)="updateSearchKeyword($event.target.value)">
  72. </div>
  73. </div>
  74. <div class="filter-row">
  75. <div class="filter-group">
  76. <label>状态筛选:</label>
  77. <div class="filter-buttons">
  78. <button
  79. class="filter-btn"
  80. [class.active]="statusFilter() === 'all'"
  81. (click)="updateStatusFilter('all')">
  82. 全部
  83. </button>
  84. <button
  85. class="filter-btn pending"
  86. [class.active]="statusFilter() === 'pending'"
  87. (click)="updateStatusFilter('pending')">
  88. 待处理
  89. </button>
  90. <button
  91. class="filter-btn processing"
  92. [class.active]="statusFilter() === 'processing'"
  93. (click)="updateStatusFilter('processing')">
  94. 处理中
  95. </button>
  96. <button
  97. class="filter-btn resolved"
  98. [class.active]="statusFilter() === 'resolved'"
  99. (click)="updateStatusFilter('resolved')">
  100. 已解决
  101. </button>
  102. </div>
  103. </div>
  104. </div>
  105. <div class="filter-row">
  106. <div class="filter-group">
  107. <label>优先级筛选:</label>
  108. <select
  109. class="filter-select"
  110. [value]="priorityFilter()"
  111. (change)="updatePriorityFilter($event)">
  112. <option value="all">全部优先级</option>
  113. @for (priority of priorities; track priority.value) {
  114. <option [value]="priority.value">{{ priority.label }}</option>
  115. }
  116. </select>
  117. </div>
  118. <div class="filter-group">
  119. <label>类型筛选:</label>
  120. <select
  121. class="filter-select"
  122. [value]="typeFilter()"
  123. (change)="updateTypeFilter($event)">
  124. <option value="all">全部类型</option>
  125. @for (type of complaintTypes; track type.value) {
  126. <option [value]="type.value">{{ type.label }}</option>
  127. }
  128. </select>
  129. </div>
  130. </div>
  131. </div>
  132. <!-- 投诉列表 -->
  133. <div class="complaints-list">
  134. @if (filteredComplaints() && filteredComplaints().length > 0) {
  135. <div class="complaints-grid">
  136. @for (complaint of filteredComplaints(); track complaint.id) {
  137. <div class="complaint-card-item" [class]="getStatusClass(complaint)" [class.overdue]="isOverdue(complaint)">
  138. <!-- 卡片头部 -->
  139. <div class="card-header">
  140. <div class="header-left">
  141. <span class="type-tag" [class]="getComplaintType(complaint)">{{ getTypeLabel(getComplaintType(complaint)) }}</span>
  142. <div class="priority-badge" [class]="getPriorityClass(complaint.priority || 'low')" [style.background-color]="getPriorityInfo(complaint.priority || 'low').color">
  143. {{ getPriorityInfo(complaint.priority || 'low').label }}优先级
  144. </div>
  145. </div>
  146. <div class="header-right">
  147. <span class="status-badge" [class]="getStatusClass(complaint)">
  148. {{ complaint.status }}
  149. </span>
  150. @if (isOverdue(complaint)) {
  151. <span class="overdue-badge">超时</span>
  152. }
  153. </div>
  154. </div>
  155. <!-- 卡片主体 -->
  156. <div class="card-body">
  157. @if (complaint.customerName) {
  158. <div class="customer-info">
  159. <span class="customer-label">客户:</span>
  160. <span class="customer-name">{{ complaint.customerName }}</span>
  161. </div>
  162. }
  163. <div class="complaint-description">
  164. <h4>投诉内容</h4>
  165. <p>{{ complaint.description }}</p>
  166. </div>
  167. @if (complaint.images && complaint.images.length > 0) {
  168. <div class="complaint-images">
  169. <h5>相关图片</h5>
  170. <div class="images-grid">
  171. @for (image of complaint.images; track $index) {
  172. <img [src]="image" [alt]="'投诉图片' + ($index + 1)" class="complaint-image">
  173. }
  174. </div>
  175. </div>
  176. }
  177. <div class="time-section">
  178. <div class="time-item">
  179. <span class="time-label">提交时间:</span>
  180. <span class="time-value">{{ complaint.submittedAt | date:'yyyy-MM-dd HH:mm' }}</span>
  181. </div>
  182. <div class="time-item">
  183. <span class="time-label">处理天数:</span>
  184. <span class="time-value">{{ getDaysInProgress(complaint) }} 天</span>
  185. </div>
  186. @if (complaint.resolvedAt) {
  187. <div class="time-item">
  188. <span class="time-label">解决时间:</span>
  189. <span class="time-value">{{ complaint.resolvedAt | date:'yyyy-MM-dd HH:mm' }}</span>
  190. </div>
  191. }
  192. </div>
  193. @if (complaint.handlerComment) {
  194. <div class="handler-section">
  195. <h5>处理意见</h5>
  196. <p class="handler-comment">{{ complaint.handlerComment }}</p>
  197. @if (complaint.handlerName) {
  198. <div class="handler-info">
  199. <span class="handler-label">处理人:</span>
  200. <span class="handler-name">{{ complaint.handlerName }}</span>
  201. </div>
  202. }
  203. </div>
  204. }
  205. @if (complaint.solution) {
  206. <div class="solution-section">
  207. <h5>解决方案</h5>
  208. <p class="solution-text">{{ complaint.solution }}</p>
  209. </div>
  210. }
  211. </div>
  212. <!-- 卡片底部操作按钮 -->
  213. <div class="card-footer">
  214. @if (complaint.status === '待处理') {
  215. <button class="action-btn process-btn" (click)="startProcessing(complaint)">
  216. <span class="btn-icon">🔧</span>
  217. 开始处理
  218. </button>
  219. } @else if (complaint.status === '处理中') {
  220. <button class="action-btn complete-btn" (click)="completeProcessing(complaint)">
  221. <span class="btn-icon">✅</span>
  222. 完成处理
  223. </button>
  224. } @else if (complaint.status === '已解决') {
  225. <div class="completed-status">
  226. <span class="completed-icon">✓</span>
  227. <span class="completed-text">处理完成</span>
  228. </div>
  229. }
  230. @if (complaint.status !== '已解决') {
  231. <button class="action-btn detail-btn" (click)="viewDetails(complaint)">
  232. <span class="btn-icon">👁️</span>
  233. 查看详情
  234. </button>
  235. }
  236. </div>
  237. </div>
  238. }
  239. </div>
  240. } @else {
  241. <div class="empty-state">
  242. <div class="empty-icon">📋</div>
  243. <div class="empty-title">暂无投诉记录</div>
  244. <div class="empty-description">当前没有符合筛选条件的投诉记录</div>
  245. </div>
  246. }
  247. </div>
  248. </div>