diff --git a/admin/dashboard/blueprints/robot.py b/admin/dashboard/blueprints/robot.py index 064dbbb..f6a50fa 100644 --- a/admin/dashboard/blueprints/robot.py +++ b/admin/dashboard/blueprints/robot.py @@ -97,8 +97,9 @@ def _load_recent_group_summaries(server, group_id: str, limit: int = 3) -> list: 设计说明: 1. 群画像适合给“结论”,但运营者往往还想看到最近几期总结到底说了什么; - 2. 这里不把整段长文原样吐给前端,而是裁成可扫读的短摘要,避免弹窗过重; - 3. 若没有总结记录,返回空列表,由前端优雅降级。 + 2. 默认卡片仍使用短摘要,避免详情页被大段文本挤占; + 3. 同时补充完整总结正文,供前端弹窗按需展开查看; + 4. 若没有总结记录,返回空列表,由前端优雅降级。 """ summary_db = getattr(server, "message_summary_db", None) if summary_db is None: @@ -124,6 +125,9 @@ def _load_recent_group_summaries(server, group_id: str, limit: int = 3) -> list: "summary_type": str(row.get("summary_type") or "").strip(), "source_message_count": int(row.get("source_message_count") or 0), "last_generated_at": str(row.get("last_generated_at") or "").strip(), + # 详情弹窗需要查看完整总结正文;这里保留原文,不做截断。 + "summary_text": raw_summary, + # 卡片区域继续展示压缩后的摘要,避免详情首屏信息密度过高。 "summary_excerpt": normalized_summary[:180] + ("..." if len(normalized_summary) > 180 else ""), }) return result diff --git a/admin/dashboard/templates/contacts_management.html b/admin/dashboard/templates/contacts_management.html index 5122f6e..0ef67fe 100644 --- a/admin/dashboard/templates/contacts_management.html +++ b/admin/dashboard/templates/contacts_management.html @@ -349,10 +349,24 @@
- {% raw %}{{ item.period_key || '-' }}{% endraw %} - {% raw %}{{ item.source_message_count || 0 }}{% endraw %} 条消息 +
+ {% raw %}{{ item.period_key || '-' }}{% endraw %} + {% raw %}{{ formatSummaryTypeLabel(item.summary_type) }}{% endraw %} +
+
+ {% raw %}{{ item.source_message_count || 0 }}{% endraw %} 条消息 + + 查看全文 + +
{% raw %}{{ item.summary_excerpt || '暂无摘要内容' }}{% endraw %}
+
{% raw %}{{ item.last_generated_at || '暂无生成时间' }}{% endraw %}
当前还没有可展示的群总结记录
@@ -850,6 +864,21 @@ + +
+ + {% raw %}{{ currentSummaryRecord.period_key || '-' }}{% endraw %} + {% raw %}{{ formatSummaryTypeLabel(currentSummaryRecord.summary_type) }}{% endraw %} + {% raw %}{{ currentSummaryRecord.source_message_count || 0 }}{% endraw %} 条 + {% raw %}{{ currentSummaryRecord.last_generated_at || '-' }}{% endraw %} + +
+
完整总结
+
{% raw %}{{ currentSummaryRecord.summary_text || '暂无可展示的总结正文' }}{% endraw %}
+
+
+
+
@@ -1053,6 +1082,9 @@ groupAnnouncementSyncing: false, groupInsight: null, groupInsightLoading: false, + // 群总结正文采用弹窗承载,避免详情区被长文本撑满。 + summaryDetailDialogVisible: false, + currentSummaryRecord: null, groupMembersList: [], groupMembersCurrentPage: 1, groupMembersPageSize: 10, groupMemberSearchQuery: '', groupMembersLoading: false, memberContextDialogVisible: false, memberContextLoading: false, memberContext: null, currentContextMember: {}, memberContextEnabled: false, @@ -1217,6 +1249,9 @@ // 切换群时恢复默认折叠态,保持“手动展开再看”的交互习惯。 this.groupPermissionsCollapsed = true; this.groupWelcomeCollapsed = true; + // 群总结详情挂在当前群上下文里,因此切群时要清空旧弹窗与旧正文。 + this.summaryDetailDialogVisible = false; + this.currentSummaryRecord = null; this.groupDetailDialogVisible = true; // 进入群详情时先加载群资料,保证群主/公告/管理员信息第一时间可见。 this.loadGroupProfile(group.wxid); @@ -1225,6 +1260,27 @@ this.loadGroupInsights(group.wxid); this.loadGroupWelcomeConfig(group.wxid); }, + formatSummaryTypeLabel(summaryType) { + const normalizedType = String(summaryType || '').trim().toLowerCase(); + // 这里把后端枚举名映射成更像运营视角的文案,避免把内部字段直接暴露给页面。 + if (!normalizedType) return '常规总结'; + const labelMap = { + daily: '日总结', + weekly: '周总结', + monthly: '月总结', + manual: '手动总结' + }; + return labelMap[normalizedType] || summaryType; + }, + openSummaryDetailDialog(summaryItem) { + if (!summaryItem || !summaryItem.summary_text) { + this.$message.info('当前总结还没有完整内容可查看'); + return; + } + // 只读弹窗复制一份当前记录,避免后续列表刷新时把弹窗内容一并抖掉。 + this.currentSummaryRecord = { ...summaryItem }; + this.summaryDetailDialogVisible = true; + }, viewUserDetails(user) { this.currentUser = user; this.userDetailDialogVisible = true; }, viewOfficialDetails(official) { this.currentOfficial = official; this.officialDetailDialogVisible = true; }, viewPublicDetails(publicFriend) { this.currentPublic = publicFriend; this.publicDetailDialogVisible = true; }, @@ -2106,11 +2162,33 @@ gap: 10px; margin-bottom: 8px; } + .ops-summary-head-main { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; + } + .ops-summary-head-side { + display: flex; + align-items: center; + gap: 10px; + flex-shrink: 0; + } .ops-summary-period { font-size: 13px; font-weight: 700; color: #0f172a; } + .ops-summary-type { + display: inline-flex; + align-items: center; + padding: 2px 8px; + border-radius: 999px; + background: rgba(37,99,235,0.08); + color: #1d4ed8; + font-size: 11px; + line-height: 1.4; + } .ops-summary-meta { font-size: 11px; color: #94a3b8; @@ -2120,6 +2198,33 @@ color: #475569; line-height: 1.7; } + .ops-summary-time { + margin-top: 8px; + font-size: 11px; + color: #94a3b8; + } + .summary-detail-body { + margin-top: 16px; + } + .summary-detail-title { + margin-bottom: 10px; + font-size: 13px; + font-weight: 700; + color: #334155; + } + .summary-detail-text { + padding: 14px 16px; + border-radius: 14px; + border: 1px solid rgba(148,163,184,0.14); + background: rgba(248,250,252,0.92); + color: #334155; + line-height: 1.8; + font-size: 13px; + white-space: pre-wrap; + word-break: break-word; + max-height: 420px; + overflow-y: auto; + } .feature-chip-list { display: flex; gap: 8px; flex-wrap: wrap; min-height: 60px; align-items: flex-start; } .empty-inline, .detail-inline-note { font-size: 12px; color: #64748b; } .group-announcement-wrap { display: flex; gap: 12px; align-items: flex-start; justify-content: space-between; }