feat: add pluginized member context profiling
This commit is contained in:
@@ -185,7 +185,10 @@
|
||||
<div class="group-members-section">
|
||||
<div class="section-title">
|
||||
<h3>群成员列表</h3>
|
||||
<el-input placeholder="搜索群成员..." v-model="groupMemberSearchQuery" class="group-search" clearable></el-input>
|
||||
<div class="section-actions">
|
||||
<el-input placeholder="搜索群成员..." v-model="groupMemberSearchQuery" class="group-search" clearable></el-input>
|
||||
<el-button size="mini" type="success" @click="refreshCurrentGroupContexts">刷新本群摘要</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-table :data="filteredGroupMembers" style="width: 100%" v-loading="groupMembersLoading">
|
||||
<el-table-column type="index" width="54"></el-table-column>
|
||||
@@ -213,6 +216,15 @@
|
||||
<el-table-column prop="latest_active_time" label="活跃时间">
|
||||
<template slot-scope="scope">{% raw %}{{ scope.row.latest_active_time || '-' }}{% endraw %}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="activity_level" label="互动强度" width="110"></el-table-column>
|
||||
<el-table-column label="回复建议" min-width="220" show-overflow-tooltip>
|
||||
<template slot-scope="scope">{% raw %}{{ scope.row.response_style_hint || '-' }}{% endraw %}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="后台摘要" width="130" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="primary" plain @click="openMemberContextDialog(scope.row)">查看</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container" v-if="groupMembersList.length > 10">
|
||||
@@ -233,6 +245,51 @@
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="成员交互摘要" :visible.sync="memberContextDialogVisible" width="52%">
|
||||
<div v-loading="memberContextLoading">
|
||||
<el-alert
|
||||
title="该摘要仅供后台调优参考,不会在群内对用户显式展示。"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon>
|
||||
</el-alert>
|
||||
|
||||
<div class="member-context-toolbar">
|
||||
<div class="member-context-title">
|
||||
<strong>{% raw %}{{ currentContextMember.name || currentContextMember.wxid || '成员' }}{% endraw %}</strong>
|
||||
<span>{% raw %}{{ currentContextMember.wxid || '' }}{% endraw %}</span>
|
||||
</div>
|
||||
<el-button size="mini" type="success" @click="refreshMemberContext">刷新摘要</el-button>
|
||||
</div>
|
||||
|
||||
<el-descriptions :column="1" border v-if="memberContext">
|
||||
<el-descriptions-item label="互动强度">{% raw %}{{ memberContext.activity_level || '-' }}{% endraw %}</el-descriptions-item>
|
||||
<el-descriptions-item label="表达特征">{% raw %}{{ memberContext.message_pattern || '-' }}{% endraw %}</el-descriptions-item>
|
||||
<el-descriptions-item label="互动风格">{% raw %}{{ memberContext.interaction_style || '-' }}{% endraw %}</el-descriptions-item>
|
||||
<el-descriptions-item label="回复建议">{% raw %}{{ memberContext.response_style_hint || '-' }}{% endraw %}</el-descriptions-item>
|
||||
<el-descriptions-item label="长期关注">
|
||||
<el-tag v-for="item in (memberContext.topics_of_interest || [])" :key="item" size="mini" class="context-tag">{% raw %}{{ item }}{% endraw %}</el-tag>
|
||||
<span v-if="!(memberContext.topics_of_interest || []).length">-</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="近期话题">
|
||||
<el-tag v-for="item in (memberContext.recent_focus || [])" :key="item" size="mini" type="success" class="context-tag">{% raw %}{{ item }}{% endraw %}</el-tag>
|
||||
<span v-if="!(memberContext.recent_focus || []).length">-</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="显著特征">
|
||||
<el-tag v-for="item in ((memberContext.meta || {}).engagement_traits || [])" :key="item" size="mini" type="warning" class="context-tag">{% raw %}{{ item }}{% endraw %}</el-tag>
|
||||
<span v-if="!(((memberContext.meta || {}).engagement_traits || []).length)">-</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="回复避坑">
|
||||
<el-tag v-for="item in ((memberContext.meta || {}).reply_taboos || [])" :key="item" size="mini" type="danger" class="context-tag">{% raw %}{{ item }}{% endraw %}</el-tag>
|
||||
<span v-if="!(((memberContext.meta || {}).reply_taboos || []).length)">-</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="摘要说明">{% raw %}{{ memberContext.summary_text || '-' }}{% endraw %}</el-descriptions-item>
|
||||
<el-descriptions-item label="样本消息">{% raw %}{{ memberContext.source_message_count || 0 }}{% endraw %}</el-descriptions-item>
|
||||
<el-descriptions-item label="最后更新">{% raw %}{{ memberContext.last_profiled_at || '-' }}{% endraw %}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="公众号详情" :visible.sync="officialDetailDialogVisible" width="50%">
|
||||
<div class="detail-avatar-wrap">
|
||||
<el-avatar size="large" :src="getHeadImage(currentOfficial.wxid)" @error="() => true" class="detail-avatar">
|
||||
@@ -323,6 +380,8 @@
|
||||
publicDetailDialogVisible: false,
|
||||
currentGroup: {}, currentUser: {}, currentOfficial: {}, currentPublic: {},
|
||||
groupMembersList: [], groupMembersCurrentPage: 1, groupMembersPageSize: 10, groupMemberSearchQuery: '', groupMembersLoading: false,
|
||||
memberContextDialogVisible: false, memberContextLoading: false, memberContext: null, currentContextMember: {},
|
||||
memberContextEnabled: false,
|
||||
chatDialogVisible: false, currentChatUser: null, messageInput: '', chatMessages: [],
|
||||
linkDialogVisible: false,
|
||||
linkForm: { url: '', title: '', description: '' }
|
||||
@@ -386,12 +445,80 @@
|
||||
axios.get(`/contacts/api/group_members/${roomid}`).then(response => {
|
||||
if (response.data.success) {
|
||||
const members = response.data.data.members;
|
||||
this.groupMembersList = members.map(item => ({ wxid: item.wxid, name: item.nick_name, display_name: item.display_name, status: item.status, latest_active_time: item.latest_active_time, small_head_img_url: item.small_head_img_url }));
|
||||
this.memberContextEnabled = !!response.data.data.member_context_enabled;
|
||||
this.groupMembersList = members.map(item => ({ wxid: item.wxid, name: item.nick_name, display_name: item.display_name, status: item.status, latest_active_time: item.latest_active_time, small_head_img_url: item.small_head_img_url, activity_level: item.activity_level, response_style_hint: item.response_style_hint, summary_text: item.summary_text, last_profiled_at: item.last_profiled_at }));
|
||||
} else { this.$message.error('获取群成员失败'); }
|
||||
}).catch(error => { console.error('加载群成员数据失败:', error); this.$message.error('加载群成员数据失败'); }).finally(() => { this.groupMembersLoading = false; });
|
||||
},
|
||||
handleGroupMembersSizeChange(size) { this.groupMembersPageSize = size; },
|
||||
handleGroupMembersCurrentChange(page) { this.groupMembersCurrentPage = page; },
|
||||
refreshCurrentGroupContexts() {
|
||||
if (!this.currentGroup.wxid) return;
|
||||
if (!this.memberContextEnabled) {
|
||||
this.$message.warning('该群未启用成员交互摘要功能');
|
||||
return;
|
||||
}
|
||||
this.groupMembersLoading = true;
|
||||
axios.post('/contacts/api/group_member_context/refresh', { roomid: this.currentGroup.wxid })
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
this.$message.success('本群成员交互摘要已刷新');
|
||||
this.loadGroupMembers(this.currentGroup.wxid);
|
||||
} else {
|
||||
this.$message.error('刷新本群成员交互摘要失败');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('刷新本群成员交互摘要失败:', error);
|
||||
this.$message.error('刷新本群成员交互摘要失败');
|
||||
})
|
||||
.finally(() => { this.groupMembersLoading = false; });
|
||||
},
|
||||
openMemberContextDialog(member) {
|
||||
if (!this.memberContextEnabled) {
|
||||
this.$message.warning('该群未启用成员交互摘要功能');
|
||||
return;
|
||||
}
|
||||
this.currentContextMember = member;
|
||||
this.memberContextDialogVisible = true;
|
||||
this.loadMemberContext();
|
||||
},
|
||||
loadMemberContext() {
|
||||
if (!this.currentGroup.wxid || !this.currentContextMember.wxid) return;
|
||||
this.memberContextLoading = true;
|
||||
this.memberContext = null;
|
||||
axios.get(`/contacts/api/group_member_context/${this.currentGroup.wxid}/${this.currentContextMember.wxid}`)
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
this.memberContext = response.data.data.context;
|
||||
} else {
|
||||
this.$message.error('加载成员交互摘要失败');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('加载成员交互摘要失败:', error);
|
||||
this.$message.error('加载成员交互摘要失败');
|
||||
})
|
||||
.finally(() => { this.memberContextLoading = false; });
|
||||
},
|
||||
refreshMemberContext() {
|
||||
if (!this.currentGroup.wxid || !this.currentContextMember.wxid) return;
|
||||
this.memberContextLoading = true;
|
||||
axios.post('/contacts/api/group_member_context/refresh', {
|
||||
roomid: this.currentGroup.wxid,
|
||||
wxid: this.currentContextMember.wxid
|
||||
}).then(response => {
|
||||
if (response.data.success) {
|
||||
this.memberContext = response.data.data.context;
|
||||
this.$message.success('成员交互摘要已刷新');
|
||||
} else {
|
||||
this.$message.error('刷新成员交互摘要失败');
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('刷新成员交互摘要失败:', error);
|
||||
this.$message.error('刷新成员交互摘要失败');
|
||||
}).finally(() => { this.memberContextLoading = false; });
|
||||
},
|
||||
openChatDialog(user) { this.currentChatUser = user; this.chatDialogVisible = true; this.chatMessages = []; },
|
||||
async sendTextMessage() {
|
||||
if (!this.messageInput.trim()) return;
|
||||
@@ -462,10 +589,16 @@
|
||||
margin: 20px 0 15px 0; border-bottom: 1px solid rgba(148,163,184,0.12); padding-bottom: 10px;
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
}
|
||||
.section-actions { display: flex; align-items: center; gap: 10px; }
|
||||
.section-title h3 { margin: 0; font-size: 18px; color: #0f172a; }
|
||||
.group-search { width: 220px; }
|
||||
.detail-avatar-wrap { text-align: center; margin-bottom: 20px; }
|
||||
.detail-avatar { width: 100px; height: 100px; }
|
||||
.member-context-toolbar {
|
||||
display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 16px 0;
|
||||
}
|
||||
.member-context-title { display: flex; flex-direction: column; gap: 4px; color: #475569; }
|
||||
.context-tag { margin-right: 8px; margin-bottom: 8px; }
|
||||
.chat-container { display: flex; flex-direction: column; height: 500px; }
|
||||
.message-list {
|
||||
flex: 1; overflow-y: auto; padding: 20px; background: rgba(248,250,252,0.82); border: 1px solid rgba(148,163,184,0.12);
|
||||
|
||||
Reference in New Issue
Block a user