message-activity-detail.component.ts 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. import { Component, EventEmitter, Input, Output } from '@angular/core';
  2. import { CommonModule, DatePipe } from '@angular/common';
  3. // NG-ZORRO 组件
  4. import { NzModalModule } from 'ng-zorro-antd/modal';
  5. import { NzDescriptionsModule } from 'ng-zorro-antd/descriptions';
  6. import { NzCardModule } from 'ng-zorro-antd/card';
  7. import { NzTagModule } from 'ng-zorro-antd/tag';
  8. import { NzSwitchModule } from 'ng-zorro-antd/switch';
  9. import { NzCollapseModule } from 'ng-zorro-antd/collapse';
  10. // 类型
  11. import { IMessageActivity, FilterCondition } from '../services/message.service';
  12. import { FormsModule } from '@angular/forms';
  13. @Component({
  14. selector: 'app-activity-detail',
  15. standalone: true,
  16. imports: [
  17. CommonModule,
  18. NzModalModule,
  19. FormsModule,
  20. NzDescriptionsModule,
  21. NzCardModule,
  22. NzTagModule,
  23. NzSwitchModule,
  24. NzCollapseModule,
  25. DatePipe,
  26. ],
  27. template: `
  28. <nz-modal
  29. [(nzVisible)]="isVisible"
  30. [nzTitle]="'活动详情 - ' + (activity?.name || '')"
  31. [nzFooter]="null"
  32. (nzOnCancel)="onCancel.emit()"
  33. [nzWidth]="800"
  34. >
  35. <ng-container *nzModalContent>
  36. <nz-descriptions nzBordered [nzColumn]="1">
  37. <nz-descriptions-item nzTitle="活动名称">{{
  38. activity?.name
  39. }}</nz-descriptions-item>
  40. <nz-descriptions-item nzTitle="关联模板">{{
  41. getTemplateName(activity?.templateId)
  42. }}</nz-descriptions-item>
  43. <nz-descriptions-item nzTitle="推送策略">
  44. <nz-tag [nzColor]="getStrategyColor(activity?.strategy || 0)">
  45. {{ getStrategyName(activity?.strategy || 0) }}
  46. </nz-tag>
  47. </nz-descriptions-item>
  48. <nz-descriptions-item nzTitle="计划时间">{{
  49. activity?.scheduleAt | date : 'yyyy-MM-dd HH:mm:ss'
  50. }}</nz-descriptions-item>
  51. <nz-descriptions-item nzTitle="每日推送">
  52. <nz-switch
  53. [ngModel]="activity?.everyday"
  54. [nzDisabled]="true"
  55. ></nz-switch>
  56. </nz-descriptions-item>
  57. <nz-descriptions-item nzTitle="状态">
  58. <nz-tag [nzColor]="getStatusColor(activity?.status || 0)">
  59. {{ getStatusName(activity?.status || 0) }}
  60. </nz-tag>
  61. </nz-descriptions-item>
  62. <nz-descriptions-item nzTitle="创建时间">{{
  63. activity?.createdAt | date : 'yyyy-MM-dd HH:mm:ss'
  64. }}</nz-descriptions-item>
  65. <nz-descriptions-item nzTitle="更新时间">{{
  66. activity?.updatedAt | date : 'yyyy-MM-dd HH:mm:ss'
  67. }}</nz-descriptions-item>
  68. </nz-descriptions>
  69. <nz-card nzTitle="其他信息" style="margin-top: 16px;">
  70. <nz-descriptions nzBordered [nzColumn]="2">
  71. <nz-descriptions-item nzTitle="通知图片">
  72. <img
  73. [src]="activity?.image"
  74. style="max-width: 100px; max-height: 100px"
  75. onerror="this.style.display='none'"
  76. />
  77. </nz-descriptions-item>
  78. <nz-descriptions-item nzTitle="允许展开">
  79. <nz-switch
  80. [ngModel]="activity?.bigger"
  81. [nzDisabled]="true"
  82. ></nz-switch>
  83. </nz-descriptions-item>
  84. <nz-descriptions-item nzTitle="客户端行为">{{
  85. activity?.action || '无'
  86. }}</nz-descriptions-item>
  87. <nz-descriptions-item nzTitle="参数">{{
  88. activity?.param || '无'
  89. }}</nz-descriptions-item>
  90. <nz-descriptions-item nzTitle="扩展参数">{{
  91. activity?.extend || '无'
  92. }}</nz-descriptions-item>
  93. </nz-descriptions>
  94. <!-- 目标用户筛选条件展示 -->
  95. <nz-collapse style="margin-top: 16px;">
  96. <nz-collapse-panel nzHeader="目标用户筛选条件">
  97. @if (activity && activity.filter && activity.filter.length > 0) {
  98. <div class="filter-conditions">
  99. @for (condition of activity.filter; track $index) {
  100. <div class="filter-condition">
  101. <div class="condition-field">
  102. {{ getFieldLabel(condition.field) }}
  103. </div>
  104. <div class="condition-operator">
  105. {{ getOperatorLabel(condition.operator) }}
  106. </div>
  107. <div class="condition-value">
  108. {{ formatConditionValue(condition) }}
  109. </div>
  110. </div>
  111. }
  112. </div>
  113. } @else {
  114. <div>无筛选条件</div>
  115. }
  116. </nz-collapse-panel>
  117. </nz-collapse>
  118. <nz-descriptions nzBordered [nzColumn]="1">
  119. <nz-descriptions-item nzTitle="活动描述">{{
  120. activity?.description
  121. }}</nz-descriptions-item>
  122. </nz-descriptions>
  123. </nz-card>
  124. </ng-container>
  125. </nz-modal>
  126. `,
  127. styles: [
  128. `
  129. nz-card {
  130. margin-bottom: 16px;
  131. }
  132. .filter-conditions {
  133. display: flex;
  134. flex-direction: column;
  135. gap: 8px;
  136. }
  137. .filter-condition {
  138. display: flex;
  139. align-items: center;
  140. gap: 8px;
  141. padding: 8px;
  142. background: #f5f5f5;
  143. border-radius: 4px;
  144. }
  145. .condition-field {
  146. font-weight: 500;
  147. min-width: 100px;
  148. }
  149. .condition-operator {
  150. color: #666;
  151. min-width: 80px;
  152. }
  153. .condition-value {
  154. flex: 1;
  155. }
  156. `,
  157. ],
  158. })
  159. export class ActivityDetailComponent {
  160. @Input() activity: IMessageActivity | null = null;
  161. @Input() isVisible = false;
  162. @Input() templates: { _id: string; templateName: string }[] = [];
  163. @Output() onCancel = new EventEmitter<void>();
  164. getTemplateName(templateId: string | undefined): string {
  165. if (!templateId) return '-';
  166. const template = this.templates.find((t) => t._id === templateId);
  167. return template ? template.templateName : '-';
  168. }
  169. getStrategyName(strategy: number): string {
  170. const strategies = {
  171. 0: '自定义策略',
  172. 1: '活跃用户推送',
  173. 2: '新用户推送',
  174. 3: '沉默用户唤醒',
  175. };
  176. return strategies[strategy as keyof typeof strategies] || '未知策略';
  177. }
  178. getStrategyColor(strategy: number): string {
  179. const colors = {
  180. 0: 'blue',
  181. 1: 'green',
  182. 2: 'orange',
  183. 3: 'purple',
  184. };
  185. return colors[strategy as keyof typeof colors] || 'default';
  186. }
  187. getStatusName(status: number): string {
  188. const statuses = {
  189. 0: '未发布',
  190. 1: '已发布',
  191. 2: '已完成',
  192. };
  193. return statuses[status as keyof typeof statuses] || '未知状态';
  194. }
  195. getStatusColor(status: number): string {
  196. const colors = {
  197. 0: 'default',
  198. 1: 'processing',
  199. 2: 'success',
  200. };
  201. return colors[status as keyof typeof colors] || 'default';
  202. }
  203. /**
  204. * 获取字段的显示标签
  205. */
  206. getFieldLabel(field: string): string {
  207. const fieldLabels: { [key: string]: string } = {
  208. cc: '国家',
  209. firstLoginAt: '首次登录时间',
  210. lastActiveAt: '最后活跃时间',
  211. // 可以添加更多字段映射
  212. };
  213. return fieldLabels[field] || field;
  214. }
  215. /**
  216. * 获取运算符的显示标签
  217. */
  218. getOperatorLabel(operator: string): string {
  219. const operatorLabels: { [key: string]: string } = {
  220. $in: '包含',
  221. $nin: '不包含',
  222. $gt: '大于',
  223. $lt: '小于',
  224. $gte: '大于等于',
  225. $lte: '小于等于',
  226. };
  227. return operatorLabels[operator] || operator;
  228. }
  229. /**
  230. * 格式化条件值的显示
  231. */
  232. formatConditionValue(condition: FilterCondition): string {
  233. if (condition.field === 'cc' && Array.isArray(condition.value)) {
  234. return condition.value.join(', ');
  235. }
  236. if (
  237. condition.field === 'firstLoginAt' ||
  238. condition.field === 'lastActiveAt'
  239. ) {
  240. if (condition.operator === '$in' && Array.isArray(condition.value)) {
  241. return `${condition.value[0]} 至 ${condition.value[1]} 天前`;
  242. } else if (typeof condition.value === 'number') {
  243. return `${condition.value} 天前`;
  244. }
  245. }
  246. return JSON.stringify(condition.value);
  247. }
  248. }