Files
abot/admin/dashboard/templates/contacts_management.html

1029 lines
44 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}通讯录管理 - 机器人管理后台{% endblock %}
{% block content %}
<!-- 通讯录管理 -->
<div>
<el-row {% raw %}:gutter="20" {% endraw %}>
<el-col {% raw %}:span="24" {% endraw %}>
<el-card shadow="hover">
<div slot="header" class="clearfix">
<span>通讯录管理</span>
<el-button
type="primary"
size="small"
style="float: right; margin-left: 10px;"
{% raw %}@click="refreshContacts" {% endraw %}>
刷新数据
</el-button>
<el-button
type="success"
size="small"
style="float: right; margin-left: 10px;"
{% raw %}@click="updateContacts" {% endraw %}>
更新通讯录
</el-button>
<el-input
placeholder="搜索联系人..."
{% raw %}v-model="searchQuery" {% endraw %}
style="width: 200px; float: right"
clearable>
</el-input>
</div>
<!-- 统计卡片 -->
<el-row {% raw %}:gutter="20" {% endraw %} style="margin-bottom: 20px;">
<el-col {% raw %}:span="6" {% endraw %}>
<el-card shadow="hover" class="stat-card">
<div class="stat-title">总联系人数</div>
<div class="stat-value">{% raw %}{{ statistics.total }}{% endraw %}</div>
</el-card>
</el-col>
<el-col {% raw %}:span="6" {% endraw %}>
<el-card shadow="hover" class="stat-card">
<div class="stat-title">群组数</div>
<div class="stat-value">{% raw %}{{ statistics.groups }}{% endraw %}</div>
</el-card>
</el-col>
<el-col {% raw %}:span="6" {% endraw %}>
<el-card shadow="hover" class="stat-card">
<div class="stat-title">个人联系人数</div>
<div class="stat-value">{% raw %}{{ statistics.personal }}{% endraw %}</div>
</el-card>
</el-col>
<el-col {% raw %}:span="6" {% endraw %}>
<el-card shadow="hover" class="stat-card">
<div class="stat-title">公众号数</div>
<div class="stat-value">{% raw %}{{ statistics.official }}{% endraw %}</div>
</el-card>
</el-col>
</el-row>
<!-- 选项卡 -->
<el-tabs {% raw %}v-model="activeTab" @tab-click="handleTabClick" {% endraw %}>
<!-- 群组联系人 -->
<el-tab-pane label="群组" name="groups">
<el-table
{% raw %}:data="filteredGroups" {% endraw %}
style="width: 100%"
border>
<el-table-column type="index" width="50"></el-table-column>
<el-table-column prop="wxid" label="群ID" width="220"></el-table-column>
<el-table-column prop="name" label="群名称"></el-table-column>
<el-table-column label="操作" width="220">
<template slot-scope="scope">
<el-button
size="mini"
type="primary"
{% raw %}@click="viewGroupDetails(scope.row)" {% endraw %}>
查看详情
</el-button>
<el-button
size="mini"
type="success"
{% raw %}@click="openChatDialog(scope.row)" {% endraw %}>
聊天
</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<!-- 个人联系人 -->
<el-tab-pane label="个人联系人" name="personal">
<el-table
{% raw %}:data="filteredPersonal" {% endraw %}
style="width: 100%"
border>
<el-table-column type="index" width="50"></el-table-column>
<el-table-column label="头像" width="70">
<template slot-scope="scope">
<el-avatar
size="small"
{% raw %}:src="getHeadImage(scope.row.wxid)" {% endraw %}
{% raw %}@error="() => true" {% endraw %}>
<img src="/static/logo.png"/>
</el-avatar>
</template>
</el-table-column>
<el-table-column prop="wxid" label="微信ID" width="220"></el-table-column>
<el-table-column prop="name" label="昵称"></el-table-column>
<el-table-column label="操作" width="220">
<template slot-scope="scope">
<el-button
size="mini"
type="primary"
{% raw %}@click="viewUserDetails(scope.row)" {% endraw %}>
查看详情
</el-button>
<el-button
size="mini"
type="success"
{% raw %}@click="openChatDialog(scope.row)" {% endraw %}>
聊天
</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<!-- 公众号 -->
<el-tab-pane label="公众号" name="official">
<el-table
{% raw %}:data="filteredOfficial" {% endraw %}
style="width: 100%"
border>
<el-table-column type="index" width="50"></el-table-column>
<el-table-column label="头像" width="70">
<template slot-scope="scope">
<el-avatar
size="small"
{% raw %}:src="getHeadImage(scope.row.wxid)" {% endraw %}
{% raw %}@error="() => true" {% endraw %}>
<img src="/static/logo.png"/>
</el-avatar>
</template>
</el-table-column>
<el-table-column prop="wxid" label="公众号ID" width="220"></el-table-column>
<el-table-column prop="name" label="公众号名称"></el-table-column>
<el-table-column label="操作" width="150">
<template slot-scope="scope">
<el-button
size="mini"
type="primary"
{% raw %}@click="viewOfficialDetails(scope.row)" {% endraw %}>
查看详情
</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<!-- 公共好友 -->
<el-tab-pane label="公共好友" name="public">
<el-table
{% raw %}:data="filteredPublic" {% endraw %}
style="width: 100%"
border>
<el-table-column type="index" width="50"></el-table-column>
<el-table-column label="头像" width="70">
<template slot-scope="scope">
<el-avatar
size="small"
{% raw %}:src="getHeadImage(scope.row.wxid)" {% endraw %}
{% raw %}@error="() => true" {% endraw %}>
<img src="/static/logo.png"/>
</el-avatar>
</template>
</el-table-column>
<el-table-column prop="wxid" label="ID" width="220"></el-table-column>
<el-table-column prop="name" label="名称"></el-table-column>
<el-table-column label="操作" width="150">
<template slot-scope="scope">
<el-button
size="mini"
type="primary"
{% raw %}@click="viewPublicDetails(scope.row)" {% endraw %}>
查看详情
</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<!-- 分页 -->
<div class="pagination-container" {% raw %}v-if="activeTab === 'groups' && groupsList.length > 10" {%
endraw %}>
<el-pagination
{% raw %}@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize" {% endraw %}
layout="total, sizes, prev, pager, next, jumper"
{% raw %}:total="groupsList.length" {% endraw %}>
</el-pagination>
</div>
<div class="pagination-container" {% raw %}v-if="activeTab === 'personal' && personalList.length > 10"
{% endraw %}>
<el-pagination
{% raw %}@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize" {% endraw %}
layout="total, sizes, prev, pager, next, jumper"
{% raw %}:total="personalList.length" {% endraw %}>
</el-pagination>
</div>
<div class="pagination-container" {% raw %}v-if="activeTab === 'official' && officialList.length > 10"
{% endraw %}>
<el-pagination
{% raw %}@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize" {% endraw %}
layout="total, sizes, prev, pager, next, jumper"
{% raw %}:total="officialList.length" {% endraw %}>
</el-pagination>
</div>
<div class="pagination-container" {% raw %}v-if="activeTab === 'public' && publicList.length > 10" {%
endraw %}>
<el-pagination
{% raw %}@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize" {% endraw %}
layout="total, sizes, prev, pager, next, jumper"
{% raw %}:total="publicList.length" {% endraw %}>
</el-pagination>
</div>
</el-card>
</el-col>
</el-row>
<!-- 群组详情对话框 -->
<el-dialog title="群组详情" {% raw %}:visible.sync="groupDetailDialogVisible" {% endraw %} width="70%">
<el-descriptions {% raw %}:column="1" {% endraw %} border>
<el-descriptions-item label="群ID">{% raw %}{{ currentGroup.wxid }}{% endraw %}</el-descriptions-item>
<el-descriptions-item label="群名称">{% raw %}{{ currentGroup.name }}{% endraw %}</el-descriptions-item>
<!-- 可以添加更多群组相关信息 -->
</el-descriptions>
<!-- 群成员列表 -->
<div style="margin-top: 20px;">
<div class="section-title">
<h3>群成员列表</h3>
<el-input
placeholder="搜索群成员..."
{% raw %}v-model="groupMemberSearchQuery" {% endraw %}
style="width: 200px; float: right; margin-bottom: 10px;"
clearable>
</el-input>
</div>
<el-table
{% raw %}:data="filteredGroupMembers" {% endraw %}
style="width: 100%"
border
{% raw %}v-loading="groupMembersLoading" {% endraw %}>
<el-table-column type="index" width="50"></el-table-column>
<el-table-column label="头像" width="70">
<template slot-scope="scope">
<el-avatar
size="small"
{% raw %}:src="getHeadImage(scope.row.wxid)" {% endraw %}
{% raw %}@error="() => true" {% endraw %}>
<img src="/static/logo.png"/>
</el-avatar>
</template>
</el-table-column>
<el-table-column prop="wxid" label="成员ID" width="220"></el-table-column>
<el-table-column prop="name" label="成员昵称"></el-table-column>
</el-table>
<!-- 群成员分页 -->
<div class="pagination-container" {% raw %}v-if="groupMembersList.length > 10" {% endraw %}>
<el-pagination
{% raw %}@size-change="handleGroupMembersSizeChange"
@current-change="handleGroupMembersCurrentChange"
:current-page="groupMembersCurrentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="groupMembersPageSize" {% endraw %}
layout="total, sizes, prev, pager, next, jumper"
{% raw %}:total="groupMembersList.length" {% endraw %}>
</el-pagination>
</div>
</div>
</el-dialog>
<!-- 用户详情对话框 -->
<el-dialog title="用户详情" {% raw %}:visible.sync="userDetailDialogVisible" {% endraw %} width="50%">
<div style="text-align: center; margin-bottom: 20px;">
<el-avatar
size="large"
{% raw %}:src="getHeadImage(currentUser.wxid)" {% endraw %}
{% raw %}@error="() => true" {% endraw %}
style="width: 100px; height: 100px;">
<img src="/static/logo.png"/>
</el-avatar>
</div>
<el-descriptions {% raw %}:column="1" {% endraw %} border>
<el-descriptions-item label="微信ID">{% raw %}{{ currentUser.wxid }}{% endraw %}</el-descriptions-item>
<el-descriptions-item label="昵称">{% raw %}{{ currentUser.name }}{% endraw %}</el-descriptions-item>
<!-- 可以添加更多用户相关信息 -->
</el-descriptions>
</el-dialog>
<!-- 公众号详情对话框 -->
<el-dialog title="公众号详情" {% raw %}:visible.sync="officialDetailDialogVisible" {% endraw %} width="50%">
<div style="text-align: center; margin-bottom: 20px;">
<el-avatar
size="large"
{% raw %}:src="getHeadImage(currentOfficial.wxid)" {% endraw %}
{% raw %}@error="() => true" {% endraw %}
style="width: 100px; height: 100px;">
<img src="/static/logo.png"/>
</el-avatar>
</div>
<el-descriptions {% raw %}:column="1" {% endraw %} border>
<el-descriptions-item label="公众号ID">{% raw %}{{ currentOfficial.wxid }}{% endraw %}
</el-descriptions-item>
<el-descriptions-item label="公众号名称">{% raw %}{{ currentOfficial.name }}{% endraw %}
</el-descriptions-item>
<!-- 可以添加更多公众号相关信息 -->
</el-descriptions>
</el-dialog>
<!-- 公共好友详情对话框 -->
<el-dialog title="公共好友详情" {% raw %}:visible.sync="publicDetailDialogVisible" {% endraw %} width="50%">
<div style="text-align: center; margin-bottom: 20px;">
<el-avatar
size="large"
{% raw %}:src="getHeadImage(currentPublic.wxid)" {% endraw %}
{% raw %}@error="() => true" {% endraw %}
style="width: 100px; height: 100px;">
<img src="/static/logo.png"/>
</el-avatar>
</div>
<el-descriptions {% raw %}:column="1" {% endraw %} border>
<el-descriptions-item label="ID">{% raw %}{{ currentPublic.wxid }}{% endraw %}</el-descriptions-item>
<el-descriptions-item label="名称">{% raw %}{{ currentPublic.name }}{% endraw %}</el-descriptions-item>
<!-- 可以添加更多公共好友相关信息 -->
</el-descriptions>
</el-dialog>
<!-- 聊天对话框 -->
<el-dialog title="聊天" {% raw %}:visible.sync="chatDialogVisible" {% endraw %} width="60%" :close-on-click-modal="false">
<div class="chat-container">
<!-- 消息列表 -->
<div class="message-list" ref="messageList">
{% raw %}
<div v-for="(msg, index) in chatMessages" :key="index" class="message-item" :class="{'message-self': msg.isSelf}">
<div class="message-content">
<div v-if="msg.type === 'text'" v-text="msg.content"></div>
<div v-else-if="msg.type === 'image'">
<img :src="msg.content" style="max-width: 200px; max-height: 200px;">
</div>
<div v-else-if="msg.type === 'voice'">
<audio controls :src="msg.content"></audio>
</div>
<div v-else-if="msg.type === 'video'">
<video controls :src="msg.content" style="max-width: 200px;"></video>
</div>
<div v-else-if="msg.type === 'link'">
<a :href="msg.content.url" target="_blank" v-text="msg.content.title"></a>
<p v-text="msg.content.description"></p>
</div>
</div>
<div class="message-time" v-text="msg.time"></div>
</div>
{% endraw %}
</div>
<!-- 输入区域 -->
<div class="input-area">
<el-input
type="textarea"
:rows="3"
placeholder="请输入消息..."
{% raw %}v-model="messageInput" {% endraw %}
@keyup.enter.native="sendTextMessage">
</el-input>
<div class="toolbar">
<el-upload
class="upload-demo"
action="#"
{% raw %}:http-request="uploadImage" {% endraw %}
:show-file-list="false">
<el-button size="small" type="primary">图片</el-button>
</el-upload>
<el-upload
class="upload-demo"
action="#"
{% raw %}:http-request="uploadVoice" {% endraw %}
:show-file-list="false">
<el-button size="small" type="primary">语音</el-button>
</el-upload>
<el-upload
class="upload-demo"
action="#"
{% raw %}:http-request="uploadVideo" {% endraw %}
:show-file-list="false">
<el-button size="small" type="primary">视频</el-button>
</el-upload>
<el-button size="small" type="primary" {% raw %}@click="showLinkDialog" {% endraw %}>链接</el-button>
<el-button size="small" type="primary" {% raw %}@click="sendTextMessage" {% endraw %}>发送</el-button>
</div>
</div>
</div>
</el-dialog>
<!-- 链接消息对话框 -->
<el-dialog title="发送链接" {% raw %}:visible.sync="linkDialogVisible" {% endraw %} width="30%">
<el-form :model="linkForm" label-width="80px">
<el-form-item label="链接">
<el-input v-model="linkForm.url" placeholder="请输入链接"></el-input>
</el-form-item>
<el-form-item label="标题">
<el-input v-model="linkForm.title" placeholder="请输入标题"></el-input>
</el-form-item>
<el-form-item label="描述">
<el-input type="textarea" v-model="linkForm.description" placeholder="请输入描述"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="linkDialogVisible = false">取消</el-button>
<el-button type="primary" @click="sendLinkMessage">发送</el-button>
</span>
</el-dialog>
</div>
{% endblock %}
{% block scripts %}
<script>
new Vue({
el: '#app',
mixins: [baseApp],
data() {
return {
activeTab: 'groups',
searchQuery: '',
currentPage: 1,
pageSize: 10,
groupsList: [],
personalList: [],
officialList: [],
publicList: [],
headImages: {}, // 添加头像数据存储
statistics: {
total: 0,
groups: 0,
personal: 0,
official: 0,
public: 0
},
groupDetailDialogVisible: false,
userDetailDialogVisible: false,
officialDetailDialogVisible: false,
publicDetailDialogVisible: false,
currentGroup: {},
currentUser: {},
currentOfficial: {},
currentPublic: {},
// 新增群成员相关数据
groupMembersList: [],
groupMembersCurrentPage: 1,
groupMembersPageSize: 10,
groupMemberSearchQuery: '',
groupMembersLoading: false,
chatDialogVisible: false,
currentChatUser: null,
messageInput: '',
chatMessages: [],
linkDialogVisible: false,
linkForm: {
url: '',
title: '',
description: ''
}
};
},
computed: {
filteredGroups() {
const query = this.searchQuery.toLowerCase();
if (!query) {
return this.groupsList.slice(
(this.currentPage - 1) * this.pageSize,
this.currentPage * this.pageSize
);
}
return this.groupsList.filter(group =>
group.wxid.toLowerCase().includes(query) ||
group.name.toLowerCase().includes(query)
).slice(
(this.currentPage - 1) * this.pageSize,
this.currentPage * this.pageSize
);
},
filteredPersonal() {
const query = this.searchQuery.toLowerCase();
if (!query) {
return this.personalList.slice(
(this.currentPage - 1) * this.pageSize,
this.currentPage * this.pageSize
);
}
return this.personalList.filter(user =>
user.wxid.toLowerCase().includes(query) ||
user.name.toLowerCase().includes(query)
).slice(
(this.currentPage - 1) * this.pageSize,
this.currentPage * this.pageSize
);
},
filteredOfficial() {
const query = this.searchQuery.toLowerCase();
if (!query) {
return this.officialList.slice(
(this.currentPage - 1) * this.pageSize,
this.currentPage * this.pageSize
);
}
return this.officialList.filter(official =>
official.wxid.toLowerCase().includes(query) ||
official.name.toLowerCase().includes(query)
).slice(
(this.currentPage - 1) * this.pageSize,
this.currentPage * this.pageSize
);
},
filteredPublic() {
const query = this.searchQuery.toLowerCase();
if (!query) {
return this.publicList.slice(
(this.currentPage - 1) * this.pageSize,
this.currentPage * this.pageSize
);
}
return this.publicList.filter(item =>
item.wxid.toLowerCase().includes(query) ||
item.name.toLowerCase().includes(query)
).slice(
(this.currentPage - 1) * this.pageSize,
this.currentPage * this.pageSize
);
},
// 添加群成员过滤计算属性
filteredGroupMembers() {
const query = this.groupMemberSearchQuery.toLowerCase();
if (!query) {
return this.groupMembersList.slice(
(this.groupMembersCurrentPage - 1) * this.groupMembersPageSize,
this.groupMembersCurrentPage * this.groupMembersPageSize
);
}
return this.groupMembersList.filter(member =>
member.wxid.toLowerCase().includes(query) ||
(member.name && member.name.toLowerCase().includes(query))
).slice(
(this.groupMembersCurrentPage - 1) * this.groupMembersPageSize,
this.groupMembersCurrentPage * this.groupMembersPageSize
);
}
},
mounted() {
this.currentView = '10'; // 设置当前视图为通讯录管理
this.loadContactsData();
},
methods: {
loadContactsData() {
// 修改API请求路径从 /api/contacts/ 改为 /contacts/api/
// 加载统计数据
axios.get('/contacts/api/statistics')
.then(response => {
if (response.data.success) {
this.statistics = response.data.data;
}
})
.catch(error => {
console.error('加载联系人统计数据失败:', error);
this.$message.error('加载联系人统计数据失败');
});
// 加载群组数据
axios.get('/contacts/api/groups')
.then(response => {
if (response.data.success) {
const groups = response.data.data.groups;
this.groupsList = Object.entries(groups).map(([wxid, name]) => ({
wxid,
name
}));
}
})
.catch(error => {
console.error('加载群组数据失败:', error);
this.$message.error('加载群组数据失败');
});
// 加载个人联系人数据
axios.get('/contacts/api/personal')
.then(response => {
if (response.data.success) {
const personal = response.data.data.personal;
this.personalList = Object.entries(personal).map(([wxid, name]) => ({
wxid,
name
}));
}
})
.catch(error => {
console.error('加载个人联系人数据失败:', error);
this.$message.error('加载个人联系人数据失败');
});
// 加载公众号数据
axios.get('/contacts/api/official')
.then(response => {
if (response.data.success) {
const official = response.data.data.official;
this.officialList = Object.entries(official).map(([wxid, name]) => ({
wxid,
name
}));
}
})
.catch(error => {
console.error('加载公众号数据失败:', error);
this.$message.error('加载公众号数据失败');
});
// 加载公共好友数据
axios.get('/contacts/api/public')
.then(response => {
if (response.data.success) {
const publicFriends = response.data.data.public;
this.publicList = Object.entries(publicFriends).map(([wxid, name]) => ({
wxid,
name
}));
}
})
.catch(error => {
console.error('加载公共好友数据失败:', error);
this.$message.error('加载公共好友数据失败');
});
// 加载头像数据
axios.get('/contacts/api/head_images')
.then(response => {
if (response.data.success) {
this.headImages = response.data.data.head_images;
}
})
.catch(error => {
console.error('加载联系人头像数据失败:', error);
this.$message.error('加载联系人头像数据失败');
});
},
updateContacts() {
this.$message.info('正在更新通讯录...');
axios.post('/contacts/api/update')
.then(res => {
if (res.data.success) {
this.$message.success('通讯录更新成功!');
this.refreshContacts();
} else {
this.$message.error(res.data.message || '通讯录更新失败');
}
})
.catch(() => {
this.$message.error('通讯录更新请求失败');
});
},
refreshContacts() {
this.loadContactsData();
this.$message.success('联系人数据已刷新');
},
handleTabClick(tab) {
this.currentPage = 1; // 切换选项卡时重置页码
},
// 添加获取头像的方法
getHeadImage(wxid) {
return this.headImages[wxid] || '';
},
handleSizeChange(size) {
this.pageSize = size;
},
handleCurrentChange(page) {
this.currentPage = page;
},
viewGroupDetails(group) {
this.currentGroup = group;
this.groupDetailDialogVisible = true;
this.loadGroupMembers(group.wxid);
},
viewUserDetails(user) {
this.currentUser = user;
this.userDetailDialogVisible = true;
},
viewOfficialDetails(official) {
this.currentOfficial = official;
this.officialDetailDialogVisible = true;
},
viewPublicDetails(publicFriend) {
this.currentPublic = publicFriend;
this.publicDetailDialogVisible = true;
},
// 添加加载群成员的方法
loadGroupMembers(roomid) {
this.groupMembersLoading = true;
this.groupMembersList = [];
this.groupMembersCurrentPage = 1;
axios.get(`/contacts/api/group_members/${roomid}`)
.then(response => {
if (response.data.success) {
const members = response.data.data.members;
console.log('获取到的群成员数据:', members); // 添加调试日志
this.groupMembersList = members.map(item => ({
wxid: item.wxid,
name: item.display_name
? (item.nick_name && item.display_name !== item.nick_name
? `${item.display_name}${item.nick_name}`
: item.display_name)
: (item.nick_name || item.wxid),
}));
console.log('处理后的群成员列表:', this.groupMembersList); // 添加调试日志
} else {
console.error('获取群成员失败:', response.data.error);
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;
},
// 打开聊天对话框
openChatDialog(user) {
this.currentChatUser = user;
this.chatDialogVisible = true;
this.chatMessages = [];
},
// 发送文本消息
async sendTextMessage() {
if (!this.messageInput.trim()) return;
try {
const response = await axios.post('/contacts/api/send_message', {
wxid: this.currentChatUser.wxid,
type: 'text',
content: this.messageInput
});
if (response.data.success) {
this.chatMessages.push({
type: 'text',
content: this.messageInput,
isSelf: true,
time: new Date().toLocaleTimeString()
});
this.messageInput = '';
this.$nextTick(() => {
this.scrollToBottom();
});
}
} catch (error) {
this.$message.error('发送消息失败');
}
},
// 上传图片
async uploadImage(options) {
const file = options.file;
const formData = new FormData();
formData.append('file', file);
formData.append('wxid', this.currentChatUser.wxid);
formData.append('type', 'image');
try {
const response = await axios.post('/contacts/api/send_message', formData);
if (response.data.success) {
this.chatMessages.push({
type: 'image',
content: response.data.url,
isSelf: true,
time: new Date().toLocaleTimeString()
});
this.$nextTick(() => {
this.scrollToBottom();
});
}
} catch (error) {
this.$message.error('发送图片失败');
}
},
// 上传语音
async uploadVoice(options) {
const file = options.file;
const formData = new FormData();
formData.append('file', file);
formData.append('wxid', this.currentChatUser.wxid);
formData.append('type', 'voice');
try {
const response = await axios.post('/contacts/api/send_message', formData);
if (response.data.success) {
this.chatMessages.push({
type: 'voice',
content: response.data.url,
isSelf: true,
time: new Date().toLocaleTimeString()
});
this.$nextTick(() => {
this.scrollToBottom();
});
}
} catch (error) {
this.$message.error('发送语音失败');
}
},
// 上传视频
async uploadVideo(options) {
const file = options.file;
const formData = new FormData();
formData.append('file', file);
formData.append('wxid', this.currentChatUser.wxid);
formData.append('type', 'video');
try {
const response = await axios.post('/contacts/api/send_message', formData);
if (response.data.success) {
this.chatMessages.push({
type: 'video',
content: response.data.url,
isSelf: true,
time: new Date().toLocaleTimeString()
});
this.$nextTick(() => {
this.scrollToBottom();
});
}
} catch (error) {
this.$message.error('发送视频失败');
}
},
// 显示链接对话框
showLinkDialog() {
this.linkForm = {
url: '',
title: '',
description: ''
};
this.linkDialogVisible = true;
},
// 发送链接消息
async sendLinkMessage() {
if (!this.linkForm.url) {
this.$message.warning('请输入链接');
return;
}
try {
const response = await axios.post('/contacts/api/send_message', {
wxid: this.currentChatUser.wxid,
type: 'link',
content: this.linkForm
});
if (response.data.success) {
this.chatMessages.push({
type: 'link',
content: this.linkForm,
isSelf: true,
time: new Date().toLocaleTimeString()
});
this.linkDialogVisible = false;
this.$nextTick(() => {
this.scrollToBottom();
});
}
} catch (error) {
this.$message.error('发送链接失败');
}
},
// 滚动到底部
scrollToBottom() {
const messageList = this.$refs.messageList;
if (messageList) {
messageList.scrollTop = messageList.scrollHeight;
}
}
},
});
</script>
<style>
.stat-card {
text-align: center;
padding: 10px;
}
.stat-title {
font-size: 16px;
color: #606266;
margin-bottom: 10px;
}
.stat-value {
font-size: 28px;
font-weight: bold;
color: #409EFF;
}
.pagination-container {
margin-top: 20px;
text-align: right;
}
.section-title {
margin: 20px 0 15px 0;
border-bottom: 1px solid #ebeef5;
padding-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
.section-title h3 {
margin: 0;
font-size: 18px;
color: #303133;
}
.chat-container {
display: flex;
flex-direction: column;
height: 500px;
}
.message-list {
flex: 1;
overflow-y: auto;
padding: 20px;
background: #f5f5f5;
}
.message-item {
margin-bottom: 15px;
display: flex;
flex-direction: column;
}
.message-self {
align-items: flex-end;
}
.message-content {
max-width: 70%;
padding: 10px;
border-radius: 5px;
background: #fff;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
.message-self .message-content {
background: #95ec69;
}
.message-time {
font-size: 12px;
color: #999;
margin-top: 5px;
}
.input-area {
padding: 20px;
background: #fff;
border-top: 1px solid #eee;
}
.toolbar {
margin-top: 10px;
display: flex;
gap: 10px;
}
</style>
{% endblock %}