| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- <div class="chat-activation-page">
- <!-- 加载状态 -->
- @if (loading) {
- <div class="loading-container">
- <div class="spinner"></div>
- <p>加载中...</p>
- </div>
- } @else if (error) {
- <!-- 错误状态 -->
- <div class="error-container">
- <svg class="icon-error" viewBox="0 0 512 512">
- <path fill="currentColor" d="M256 48C141.31 48 48 141.31 48 256s93.31 208 208 208 208-93.31 208-208S370.69 48 256 48zm0 62.5a52.5 52.5 0 1152.5 52.5A52.5 52.5 0 01256 110.5zm21.72 206.41l-5.74 122a16 16 0 01-32 0l-5.74-122a21.74 21.74 0 1143.44 0z"/>
- </svg>
- <p>{{ error }}</p>
- <button class="btn-retry" (click)="refresh()">重试</button>
- </div>
- } @else {
- <!-- 主内容 -->
- <div class="page-content">
- <!-- 头部 -->
- <div class="page-header">
- <button class="btn-back" (click)="goBack()">
- <svg viewBox="0 0 512 512">
- <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="48" d="M244 400L100 256l144-144M120 256h292"/>
- </svg>
- </button>
- <div class="header-info">
- <h1 class="group-name">{{ groupChat?.get('name') || '群聊' }}</h1>
- <p class="group-meta">{{ messages.length }}条消息</p>
- </div>
- <button class="btn-refresh" (click)="refresh()">
- <svg viewBox="0 0 512 512">
- <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="32" d="M320 146s24.36-12-64-12a160 160 0 10160 160"/>
- <path fill="currentColor" d="M256 58l80 80-80 80"/>
- </svg>
- </button>
- </div>
- <!-- 统计卡片 -->
- <div class="stats-cards">
- <div class="stat-card">
- <div class="stat-icon messages">
- <svg viewBox="0 0 512 512">
- <path fill="currentColor" d="M431 320.6c-1-3.6 1.2-8.6 3.3-12.2a33.68 33.68 0 012.1-3.1A162 162 0 00464 215c.3-92.2-77.5-167-173.7-167-83.9 0-153.9 57.1-170.3 132.9a160.7 160.7 0 00-3.7 34.2c0 92.3 74.8 169.1 171 169.1 15.3 0 35.9-4.6 47.2-7.7s22.5-7.2 25.4-8.3a26.44 26.44 0 019.3-1.7 26 26 0 0110.1 2l56.7 20.1a13.52 13.52 0 003.9 1 8 8 0 008-8 12.85 12.85 0 00-.5-2.7z"/>
- </svg>
- </div>
- <div class="stat-info">
- <div class="stat-value">{{ messages.length }}</div>
- <div class="stat-label">总消息</div>
- </div>
- </div>
- <div class="stat-card">
- <div class="stat-icon customers">
- <svg viewBox="0 0 512 512">
- <path fill="currentColor" d="M258.9 48C141.92 46.42 46.42 141.92 48 258.9c1.56 112.19 92.91 203.54 205.1 205.1 117 1.6 212.48-93.9 210.88-210.88C462.44 140.91 371.09 49.56 258.9 48z"/>
- </svg>
- </div>
- <div class="stat-info">
- <div class="stat-value">{{ customerMessageCount }}</div>
- <div class="stat-label">客户消息</div>
- </div>
- </div>
- <div class="stat-card" [class.alert]="unreadCount > 0">
- <div class="stat-icon unread">
- <svg viewBox="0 0 512 512">
- <path fill="currentColor" d="M256 48C141.31 48 48 141.31 48 256s93.31 208 208 208 208-93.31 208-208S370.69 48 256 48zm0 62.5a52.5 52.5 0 1152.5 52.5A52.5 52.5 0 01256 110.5zm21.72 206.41l-5.74 122a16 16 0 01-32 0l-5.74-122a21.74 21.74 0 1143.44 0z"/>
- </svg>
- </div>
- <div class="stat-info">
- <div class="stat-value">{{ unreadCount }}</div>
- <div class="stat-label">未回复</div>
- </div>
- </div>
- </div>
- <!-- 入群方式卡片 -->
- <div class="section-card join-methods-card">
- <div class="card-header" (click)="showJoinMethods = !showJoinMethods">
- <div class="header-left">
- <svg class="icon" viewBox="0 0 512 512">
- <path fill="currentColor" d="M336 208v-95a80 80 0 00-160 0v95" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"/>
- <rect x="96" y="208" width="320" height="272" rx="48" ry="48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"/>
- </svg>
- <span>入群方式</span>
- </div>
- <svg class="chevron" [class.open]="showJoinMethods" viewBox="0 0 512 512">
- <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="48" d="M112 184l144 144 144-144"/>
- </svg>
- </div>
- @if (showJoinMethods) {
- <div class="card-content">
- <!-- 介绍文案 -->
- <div class="intro-section">
- <div class="intro-header">
- <svg class="icon" viewBox="0 0 512 512">
- <path fill="currentColor" d="M256 48C141.31 48 48 141.31 48 256s93.31 208 208 208 208-93.31 208-208S370.69 48 256 48zm0 62.5a52.5 52.5 0 1152.5 52.5A52.5 52.5 0 01256 110.5zm80 291.5H176a16 16 0 010-32h28v-88h-16a16 16 0 010-32h40a16 16 0 0116 16v104h28a16 16 0 010 32z"/>
- </svg>
- <span>群介绍文案</span>
- </div>
- @if (introSent) {
- <div class="intro-sent">
- <svg class="icon" viewBox="0 0 512 512">
- <path fill="currentColor" d="M448 256c0-106-86-192-192-192S64 150 64 256s86 192 192 192 192-86 192-192z"/>
- <path fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="M352 176L217.6 336 160 272"/>
- </svg>
- <span>介绍文案已发送</span>
- </div>
- } @else {
- <button class="btn-primary" (click)="sendIntroMessage()">
- <svg class="icon" viewBox="0 0 512 512">
- <path fill="currentColor" d="M476.59 227.05l-.16-.07L49.35 49.84A23.56 23.56 0 0027.14 52 24.65 24.65 0 0016 72.59v113.29a24 24 0 0019.52 23.57l232.93 43.07a4 4 0 010 7.86L35.53 303.45A24 24 0 0016 327v113.31A23.57 23.57 0 0026.59 460a23.94 23.94 0 0013.22 4 24.55 24.55 0 009.52-1.93L476.4 285.94l.19-.09a32 32 0 000-58.8z"/>
- </svg>
- <span>自动发送群介绍</span>
- </button>
- }
- </div>
- <!-- 入群二维码和链接 -->
- <div class="join-ways">
- @if (joinQrCode) {
- <div class="join-way-item">
- <div class="join-way-label">
- <svg class="icon" viewBox="0 0 512 512">
- <rect x="336" y="336" width="80" height="80" rx="8" ry="8" fill="currentColor"/>
- <rect x="272" y="272" width="64" height="64" rx="8" ry="8" fill="currentColor"/>
- <rect x="416" y="416" width="64" height="64" rx="8" ry="8" fill="currentColor"/>
- <rect x="432" y="272" width="48" height="48" rx="8" ry="8" fill="currentColor"/>
- <rect x="272" y="432" width="48" height="48" rx="8" ry="8" fill="currentColor"/>
- <rect x="336" y="96" width="80" height="80" rx="8" ry="8" fill="currentColor"/>
- <rect x="288" y="48" width="176" height="176" rx="16" ry="16" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"/>
- <rect x="96" y="96" width="80" height="80" rx="8" ry="8" fill="currentColor"/>
- <rect x="48" y="48" width="176" height="176" rx="16" ry="16" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"/>
- <rect x="96" y="336" width="80" height="80" rx="8" ry="8" fill="currentColor"/>
- <rect x="48" y="288" width="176" height="176" rx="16" ry="16" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"/>
- </svg>
- <span>入群二维码</span>
- </div>
- <div class="qrcode-container">
- <img [src]="joinQrCode" alt="入群二维码" />
- </div>
- </div>
- }
- @if (joinLink) {
- <div class="join-way-item">
- <div class="join-way-label">
- <svg class="icon" viewBox="0 0 512 512">
- <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="48" d="M200.66 352H144a96 96 0 010-192h55.41M312.59 160H368a96 96 0 010 192h-56.66M169.07 256h175.86"/>
- </svg>
- <span>入群链接</span>
- </div>
- <div class="link-container">
- <input type="text" [value]="joinLink" readonly />
- <button class="btn-copy" (click)="copyJoinLink()">
- <svg viewBox="0 0 512 512">
- <rect x="128" y="128" width="336" height="336" rx="57" ry="57" fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="32"/>
- <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="M383.5 128l.5-24a56.16 56.16 0 00-56-56H112a64.19 64.19 0 00-64 64v216a56.16 56.16 0 0056 56h24"/>
- </svg>
- 复制
- </button>
- </div>
- </div>
- }
- </div>
- </div>
- }
- </div>
- <!-- 聊天记录卡片 -->
- <div class="section-card chat-history-card">
- <div class="card-header" (click)="showChatHistory = !showChatHistory">
- <div class="header-left">
- <svg class="icon" viewBox="0 0 512 512">
- <path fill="currentColor" d="M408 64H104a56.16 56.16 0 00-56 56v192a56.16 56.16 0 0056 56h40v80l93.72-78.14a8 8 0 015.13-1.86H408a56.16 56.16 0 0056-56V120a56.16 56.16 0 00-56-56z"/>
- </svg>
- <span>往期聊天记录</span>
- @if (unreadCount > 0) {
- <span class="unread-badge">{{ unreadCount }}</span>
- }
- </div>
- <svg class="chevron" [class.open]="showChatHistory" viewBox="0 0 512 512">
- <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="48" d="M112 184l144 144 144-144"/>
- </svg>
- </div>
- @if (showChatHistory) {
- <div class="card-content">
- <!-- 筛选按钮 -->
- <div class="filter-bar">
- <button
- class="filter-btn"
- [class.active]="filterType === 'all'"
- (click)="setFilter('all')">
- <svg class="icon" viewBox="0 0 512 512">
- <rect x="48" y="80" width="416" height="352" rx="48" ry="48" fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="32"/>
- </svg>
- <span>全部 ({{ messages.length }})</span>
- </button>
- <button
- class="filter-btn"
- [class.active]="filterType === 'customer'"
- (click)="setFilter('customer')">
- <svg class="icon" viewBox="0 0 512 512">
- <path fill="currentColor" d="M258.9 48C141.92 46.42 46.42 141.92 48 258.9c1.56 112.19 92.91 203.54 205.1 205.1 117 1.6 212.48-93.9 210.88-210.88C462.44 140.91 371.09 49.56 258.9 48z"/>
- </svg>
- <span>客户 ({{ customerMessageCount }})</span>
- </button>
- <button
- class="filter-btn"
- [class.active]="filterType === 'unread'"
- [class.alert]="unreadCount > 0"
- (click)="setFilter('unread')">
- <svg class="icon" viewBox="0 0 512 512">
- <path fill="currentColor" d="M256 48C141.31 48 48 141.31 48 256s93.31 208 208 208 208-93.31 208-208S370.69 48 256 48zm0 62.5a52.5 52.5 0 1152.5 52.5A52.5 52.5 0 01256 110.5zm21.72 206.41l-5.74 122a16 16 0 01-32 0l-5.74-122a21.74 21.74 0 1143.44 0z"/>
- </svg>
- <span>未回复 ({{ unreadCount }})</span>
- </button>
- </div>
- <!-- 消息列表 -->
- <div class="message-list">
- @if (getFilteredMessages().length === 0) {
- <div class="empty-state">
- <svg class="icon" viewBox="0 0 512 512">
- <path fill="currentColor" d="M448 341.37V170.61A32 32 0 00416 138.61H96a32 32 0 00-32 32v170.76a32 32 0 0032 32h320a32 32 0 0032-32z"/>
- </svg>
- <span>暂无消息</span>
- </div>
- } @else {
- @for (message of getFilteredMessages(); track message.id) {
- <div
- class="message-item"
- [class.customer]="message.isCustomer"
- [class.needs-reply]="message.needsReply"
- (click)="message.isCustomer && message.needsReply ? selectMessageForReply(message) : null">
- <div class="message-header">
- <div class="sender-info">
- <span class="sender-name">{{ message.senderName }}</span>
- @if (message.isCustomer) {
- <span class="customer-badge">客户</span>
- }
- </div>
- <span class="message-time">{{ formatTime(message.time) }}</span>
- </div>
- <div class="message-content">
- {{ message.content }}
- </div>
- @if (message.needsReply) {
- <div class="reply-warning">
- <svg class="icon" viewBox="0 0 512 512">
- <path fill="currentColor" d="M256 48C141.31 48 48 141.31 48 256s93.31 208 208 208 208-93.31 208-208S370.69 48 256 48zm0 62.5a52.5 52.5 0 1152.5 52.5A52.5 52.5 0 01256 110.5zm21.72 206.41l-5.74 122a16 16 0 01-32 0l-5.74-122a21.74 21.74 0 1143.44 0z"/>
- </svg>
- <span>{{ getUnreadDuration(message.time) }}未回复,点击获取AI回复建议</span>
- </div>
- }
- </div>
- }
- }
- </div>
- <!-- 快捷操作 -->
- <div class="quick-actions">
- <button class="action-btn" (click)="openGroupChat()">
- <svg class="icon" viewBox="0 0 512 512">
- <path fill="currentColor" d="M408 64H104a56.16 56.16 0 00-56 56v192a56.16 56.16 0 0056 56h40v80l93.72-78.14a8 8 0 015.13-1.86H408a56.16 56.16 0 0056-56V120a56.16 56.16 0 00-56-56z"/>
- </svg>
- <span>打开群聊</span>
- </button>
- </div>
- </div>
- }
- </div>
- </div>
- <!-- AI回复建议弹窗 -->
- @if (selectedMessage) {
- <div class="modal-overlay" (click)="selectedMessage = null; replySuggestions = []">
- <div class="modal-content" (click)="$event.stopPropagation()">
- <div class="modal-header">
- <h3>AI回复建议</h3>
- <button class="btn-close" (click)="selectedMessage = null; replySuggestions = []">
- <svg viewBox="0 0 512 512">
- <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="M368 368L144 144M368 144L144 368"/>
- </svg>
- </button>
- </div>
- <div class="modal-body">
- <!-- 原消息 -->
- <div class="original-message">
- <div class="label">客户消息:</div>
- <div class="content">{{ selectedMessage.content }}</div>
- </div>
- <!-- AI生成中 -->
- @if (generatingAI) {
- <div class="generating-state">
- <div class="spinner"></div>
- <p>AI正在生成回复建议...</p>
- </div>
- }
- <!-- 回复建议 -->
- @if (!generatingAI && replySuggestions.length > 0) {
- <div class="suggestions-list">
- <div class="label">选择一个回复:</div>
- @for (suggestion of replySuggestions; track $index) {
- <button class="suggestion-item" (click)="useSuggestion(suggestion)">
- <span class="suggestion-icon">{{ suggestion.icon }}</span>
- <span class="suggestion-text">{{ suggestion.text }}</span>
- <svg class="icon-send" viewBox="0 0 512 512">
- <path fill="currentColor" d="M476.59 227.05l-.16-.07L49.35 49.84A23.56 23.56 0 0027.14 52 24.65 24.65 0 0016 72.59v113.29a24 24 0 0019.52 23.57l232.93 43.07a4 4 0 010 7.86L35.53 303.45A24 24 0 0016 327v113.31A23.57 23.57 0 0026.59 460a23.94 23.94 0 0013.22 4 24.55 24.55 0 009.52-1.93L476.4 285.94l.19-.09a32 32 0 000-58.8z"/>
- </svg>
- </button>
- }
- </div>
- }
- </div>
- </div>
- </div>
- }
- }
- </div>
|