delivery-page.component.html 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. <section class="plain-view delivery-view">
  2. <header class="page-hero">
  3. <div class="page-hero-copy">
  4. <p class="eyebrow">名单交付</p>
  5. <h2>对客输出讲价值,内部复核看风险</h2>
  6. <small>同一份名单分客户版和内部复核版,避免把供应商、风险和规则细节直接暴露给客户。</small>
  7. <div class="page-guide">
  8. <span><b>目标</b>生成可发客户的推荐表</span>
  9. <span><b>操作</b>切换版本、检查字段、导出名单</span>
  10. <span><b>产出</b>客户版 / 内部版 Excel</span>
  11. </div>
  12. </div>
  13. <div class="page-actions segmented-actions">
  14. <button type="button" class="ghost-button" [class.active]="state.previewAudience === '内部复核版'" (click)="state.setPreviewAudience('内部复核版')">内部复核版</button>
  15. <button type="button" class="ghost-button" [class.active]="state.previewAudience === '客户版'" (click)="state.setPreviewAudience('客户版')">客户版</button>
  16. <button type="button" class="ghost-button" (click)="state.toggleAdvancedWorkspace()">
  17. {{ state.showAdvancedWorkspace ? '收起细节' : '展开细节' }}
  18. </button>
  19. <button type="button" class="primary-button compact" (click)="state.exportRecommendations()">导出名单</button>
  20. </div>
  21. </header>
  22. <section class="delivery-top" [class.no-version]="state.simpleMode && !state.showAdvancedWorkspace">
  23. <article class="readiness-panel delivery-readiness">
  24. <div class="section-mini-head">
  25. <div>
  26. <strong>交付准备度</strong>
  27. <small>字段映射、规则、候选和采样共同计算</small>
  28. </div>
  29. <b>{{ state.exportReadiness }}%</b>
  30. </div>
  31. <div class="progress large"><i [style.width.%]="state.exportReadiness"></i></div>
  32. <div class="gap-list" *ngIf="state.deliveryGaps.length > 0; else ready">
  33. <span *ngFor="let gap of state.deliveryGaps">{{ gap }}</span>
  34. </div>
  35. <ng-template #ready>
  36. <p class="empty-state">当前名单已经可以作为客户版导出。</p>
  37. </ng-template>
  38. </article>
  39. <article class="field-config">
  40. <div class="section-mini-head">
  41. <strong>导出字段</strong>
  42. <small>{{ state.previewAudience }} · {{ state.selectedDeliveryFields.length }} 个字段</small>
  43. </div>
  44. <div class="field-chip-grid">
  45. <label *ngFor="let field of state.deliveryFields" [class.disabled]="state.previewAudience === '客户版' && !field.customer">
  46. <input type="checkbox" [(ngModel)]="field.selected" [disabled]="state.previewAudience === '客户版' && !field.customer" />
  47. <span>{{ field.label }}</span>
  48. </label>
  49. </div>
  50. </article>
  51. <article class="version-list" *ngIf="!state.simpleMode || state.showAdvancedWorkspace">
  52. <div class="section-mini-head">
  53. <strong>版本历史</strong>
  54. <small>需求、名单、反馈可追溯</small>
  55. </div>
  56. <section *ngFor="let version of state.deliveryVersions">
  57. <span>{{ version.version }}</span>
  58. <div>
  59. <strong>{{ version.title }}</strong>
  60. <p>{{ version.summary }}</p>
  61. <small>{{ version.owner }} · {{ version.time }} · {{ version.status }}</small>
  62. </div>
  63. </section>
  64. </article>
  65. </section>
  66. <section class="delivery-control-grid">
  67. <article class="delivery-checklist">
  68. <div class="section-mini-head">
  69. <div>
  70. <strong>交付检查清单</strong>
  71. <small>{{ state.deliveryChecklistDoneCount }}/{{ state.deliveryChecklist.length }} 已完成</small>
  72. </div>
  73. </div>
  74. <button
  75. type="button"
  76. *ngFor="let item of state.deliveryChecklist"
  77. [class.done]="item.done"
  78. (click)="state.toggleDeliveryChecklist(item)"
  79. >
  80. <span>{{ item.done ? '✓' : '·' }}</span>
  81. <div>
  82. <strong>{{ item.label }}</strong>
  83. <small>{{ item.detail }}</small>
  84. </div>
  85. </button>
  86. </article>
  87. <article class="delivery-copy-panel">
  88. <div class="section-mini-head">
  89. <strong>客户版摘要</strong>
  90. <small>可直接用于导出前口径确认</small>
  91. </div>
  92. <p>
  93. 本次名单保留 {{ state.selectedCandidateList.length }} 个账号,主推 {{ state.strongRecommendationCount }} 个;
  94. 推荐理由聚焦真实测评、通勤生活和轻专业成分表达。
  95. </p>
  96. <div class="tag-row">
  97. <span *ngFor="let field of state.selectedDeliveryFields.slice(0, 6)">{{ field }}</span>
  98. </div>
  99. </article>
  100. <article class="delivery-review-queue">
  101. <div class="section-mini-head">
  102. <strong>内部待处理</strong>
  103. <small>{{ state.reviewQueueCandidates.length }} 个账号</small>
  104. </div>
  105. <button type="button" *ngFor="let candidate of state.reviewQueueCandidates" (click)="state.openRecommendationReview(candidate)">
  106. <span>{{ candidate.status }}</span>
  107. <div>
  108. <strong>{{ candidate.name }}</strong>
  109. <small>{{ candidate.risk }}</small>
  110. </div>
  111. </button>
  112. </article>
  113. </section>
  114. <section class="delivery-preview">
  115. <div class="section-mini-head">
  116. <div>
  117. <strong>{{ state.previewAudience }}预览</strong>
  118. <small>展示当前已选账号,客户版隐藏内部风险字段</small>
  119. </div>
  120. <em>{{ state.selectedCandidateList.length }} 个账号</em>
  121. </div>
  122. <div class="delivery-table">
  123. <div class="delivery-head">
  124. <span>账号</span>
  125. <span>报价/粉丝</span>
  126. <span>推荐理由</span>
  127. <span>内容证据</span>
  128. <span *ngIf="state.previewAudience === '内部复核版'">内部风险</span>
  129. </div>
  130. <article *ngFor="let candidate of state.selectedCandidateList">
  131. <div>
  132. <strong>{{ candidate.name }}</strong>
  133. <small>{{ candidate.platform }} · {{ candidate.category || candidate.tags[0] }}</small>
  134. </div>
  135. <div>
  136. <strong>{{ candidate.price | currency: 'CNY' : 'symbol-narrow' : '1.0-0' }}</strong>
  137. <small>{{ candidate.fans }}粉丝</small>
  138. </div>
  139. <p>{{ candidate.reason }}</p>
  140. <div>
  141. <strong>{{ candidate.styleMatch }}% 风格匹配</strong>
  142. <small>{{ candidate.recentNotes[0].title }} · {{ candidate.recentNotes[0].match }}%</small>
  143. </div>
  144. <p *ngIf="state.previewAudience === '内部复核版'" class="risk">{{ candidate.risk }}</p>
  145. <div class="delivery-row-actions" *ngIf="state.previewAudience === '内部复核版'">
  146. <button type="button" class="text-button" (click)="state.updateCandidateStatus(candidate, '备选')">转备选</button>
  147. <button type="button" class="text-button" (click)="state.openRecommendationReview(candidate)">看证据</button>
  148. </div>
  149. </article>
  150. </div>
  151. </section>
  152. <section class="handoff-grid" *ngIf="!state.simpleMode || state.showAdvancedWorkspace">
  153. <article>
  154. <strong>客户版话术</strong>
  155. <p>推荐理由聚焦“为什么适合品牌”,使用真实测评、通勤生活、成分科普等可理解证据,不展示内部剔除逻辑。</p>
  156. </article>
  157. <article>
  158. <strong>内部复核动作</strong>
  159. <p>媒介在导出前确认报价、档期、供应商、风险和近 10 条内容样本,所有修改进入决策日志。</p>
  160. </article>
  161. <article>
  162. <strong>复盘沉淀</strong>
  163. <p>客户反馈进入反馈学习页,先生成待确认规则,再决定是本次任务生效、单客户生效还是长期入库。</p>
  164. </article>
  165. </section>
  166. </section>