UI改版测试V0
This commit is contained in:
@@ -3,155 +3,146 @@
|
||||
{% block title %}消息列表{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- 消息列表 -->
|
||||
<div>
|
||||
<!-- 筛选条件 -->
|
||||
<el-card class="filter-card">
|
||||
<el-form {% raw %}:inline="true"{% endraw %} size="small">
|
||||
<div class="page-shell message-page">
|
||||
<div class="page-hero">
|
||||
<div class="page-hero-copy">
|
||||
<div class="page-eyebrow">Messages Center</div>
|
||||
<h1>消息列表</h1>
|
||||
<p>按群组、时间与内容快速回溯消息记录,把查看明细、媒体预览与分页统一到一个工作台里。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-card class="filter-card workspace-card" shadow="hover">
|
||||
<div slot="header" class="workspace-header">
|
||||
<div>
|
||||
<h3>筛选条件</h3>
|
||||
<p>组合筛选群组、日期与关键词,快速定位你要看的消息。</p>
|
||||
</div>
|
||||
<div class="filter-summary">
|
||||
当前共 {% raw %}{{ pagination.total }}{% endraw %} 条消息
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-form :inline="true" size="small" class="filter-form">
|
||||
<el-form-item label="群组">
|
||||
<el-select {% raw %}v-model="filter.groupId"{% endraw %} placeholder="选择群组" clearable>
|
||||
<el-select v-model="filter.groupId" placeholder="选择群组" clearable>
|
||||
<el-option
|
||||
{% raw %}v-for="group in groups"
|
||||
v-for="group in groups"
|
||||
:key="group.id"
|
||||
:label="group.name"
|
||||
:value="group.id"{% endraw %}>
|
||||
:value="group.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 其他筛选条件保持不变 -->
|
||||
|
||||
<el-form-item label="日期范围">
|
||||
<el-date-picker
|
||||
{% raw %}v-model="dateRange"
|
||||
v-model="dateRange"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="yyyy-MM-dd"
|
||||
:picker-options="pickerOptions"{% endraw %}>
|
||||
:picker-options="pickerOptions">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="搜索内容">
|
||||
<el-input {% raw %}v-model="filter.searchText"{% endraw %} placeholder="搜索消息内容" clearable></el-input>
|
||||
<el-input v-model="filter.searchText" placeholder="搜索消息内容" clearable></el-input>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" {% raw %}@click="searchMessages"{% endraw %}>搜索</el-button>
|
||||
<el-button {% raw %}@click="resetFilter"{% endraw %}>重置</el-button>
|
||||
<el-button type="primary" @click="searchMessages">搜索</el-button>
|
||||
<el-button @click="resetFilter">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 消息列表内容保持不变 -->
|
||||
<el-card class="message-card">
|
||||
|
||||
<el-card class="message-card workspace-card" shadow="hover">
|
||||
<div slot="header" class="workspace-header">
|
||||
<div>
|
||||
<h3>消息记录</h3>
|
||||
<p>支持文本、图片、视频等多类型消息的统一查看。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
{% raw %}:data="messages"
|
||||
:data="messages"
|
||||
style="width: 100%"
|
||||
size="small"
|
||||
v-loading="loading"{% endraw %}
|
||||
border>
|
||||
<!-- 表格列保持不变 -->
|
||||
<el-table-column
|
||||
prop="timestamp"
|
||||
label="时间"
|
||||
width="150">
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="group_name"
|
||||
label="群组"
|
||||
width="150">
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="sender_name"
|
||||
label="发送者"
|
||||
width="120">
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="content"
|
||||
label="内容">
|
||||
v-loading="loading">
|
||||
<el-table-column prop="timestamp" label="时间" width="165"></el-table-column>
|
||||
<el-table-column prop="group_name" label="群组" width="160"></el-table-column>
|
||||
<el-table-column prop="sender_name" label="发送者" width="140"></el-table-column>
|
||||
<el-table-column prop="content" label="内容" min-width="420">
|
||||
<template slot-scope="scope">
|
||||
<!-- 文本消息 -->
|
||||
<div v-if="scope.row.message_type == 1">
|
||||
{% raw %}{{ scope.row.content }}{% endraw %}
|
||||
</div>
|
||||
|
||||
<!-- 图片消息 -->
|
||||
<div v-else-if="scope.row.message_type == 3">
|
||||
<div>【图片消息】</div>
|
||||
<!-- 优先使用image_path显示图片,如果没有则回退到message_thumb -->
|
||||
<img v-if="scope.row.image_path" :src="getImageUrl(scope.row.image_path)" class="message-thumb" @click="showImage(scope.row)">
|
||||
<img v-else-if="scope.row.message_thumb" :src="scope.row.message_thumb" class="message-thumb" @click="showImage(scope.row)">
|
||||
</div>
|
||||
|
||||
<!-- 视频消息 -->
|
||||
<div v-else-if="scope.row.message_type == 43">
|
||||
<div>【视频消息】</div>
|
||||
<img v-if="scope.row.message_thumb" :src="scope.row.message_thumb" class="message-thumb" @click="showVideo(scope.row)">
|
||||
</div>
|
||||
|
||||
<!-- 其他类型消息 -->
|
||||
<div v-else>
|
||||
{% raw %}{{ scope.row.content || `【消息类型: ${scope.row.message_type}】` }}{% endraw %}
|
||||
<div class="message-preview">
|
||||
<div v-if="scope.row.message_type == 1" class="message-text-preview">
|
||||
{% raw %}{{ scope.row.content }}{% endraw %}
|
||||
</div>
|
||||
|
||||
<div v-else-if="scope.row.message_type == 3" class="message-media-preview">
|
||||
<div class="message-media-label">【图片消息】</div>
|
||||
<img v-if="scope.row.image_path" :src="getImageUrl(scope.row.image_path)" class="message-thumb" @click="showImage(scope.row)">
|
||||
<img v-else-if="scope.row.message_thumb" :src="scope.row.message_thumb" class="message-thumb" @click="showImage(scope.row)">
|
||||
</div>
|
||||
|
||||
<div v-else-if="scope.row.message_type == 43" class="message-media-preview">
|
||||
<div class="message-media-label">【视频消息】</div>
|
||||
<img v-if="scope.row.message_thumb" :src="scope.row.message_thumb" class="message-thumb" @click="showVideo(scope.row)">
|
||||
</div>
|
||||
|
||||
<div v-else class="message-text-preview is-muted">
|
||||
{% raw %}{{ scope.row.content || `【消息类型: ${scope.row.message_type}】` }}{% endraw %}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
label="操作"
|
||||
width="100">
|
||||
<el-table-column label="操作" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="small" @click="showMessageDetail(scope.row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
{% raw %}@size-change="handleSizeChange"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="pagination.page"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:page-size="pagination.pageSize"{% endraw %}
|
||||
:page-size="pagination.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
{% raw %}:total="pagination.total"{% endraw %}>
|
||||
:total="pagination.total">
|
||||
</el-pagination>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 对话框保持不变 -->
|
||||
<!-- 消息详情对话框 -->
|
||||
<el-dialog title="消息详情" {% raw %}:visible.sync="detailDialogVisible"{% endraw %} width="60%">
|
||||
<div {% raw %}v-if="selectedMessage"{% endraw %}>
|
||||
<el-descriptions {% raw %}:column="1"{% endraw %} border>
|
||||
|
||||
<el-dialog title="消息详情" :visible.sync="detailDialogVisible" width="60%">
|
||||
<div v-if="selectedMessage">
|
||||
<el-descriptions :column="1" border>
|
||||
<el-descriptions-item label="时间">{% raw %}{{ selectedMessage.timestamp }}{% endraw %}</el-descriptions-item>
|
||||
<el-descriptions-item label="群组">{% raw %}{{ selectedMessage.group_name }}{% endraw %}</el-descriptions-item>
|
||||
<el-descriptions-item label="发送者">{% raw %}{{ selectedMessage.sender_name }}{% endraw %}</el-descriptions-item>
|
||||
<el-descriptions-item label="消息类型">{% raw %}{{ getMessageTypeName(selectedMessage.message_type) }}{% endraw %}</el-descriptions-item>
|
||||
<el-descriptions-item label="内容">{% raw %}{{ selectedMessage.content }}{% endraw %}</el-descriptions-item>
|
||||
|
||||
<!-- 图片或视频消息 -->
|
||||
<el-descriptions-item {% raw %}v-if="selectedMessage.message_type == 3 || selectedMessage.message_type == 43"{% endraw %} label="媒体内容">
|
||||
<img {% raw %}v-if="selectedMessage.message_type == 3 && selectedMessage.image_path" :src="getImageUrl(selectedMessage.image_path)"{% endraw %} style="max-width: 100%;">
|
||||
<img {% raw %}v-else-if="selectedMessage.message_type == 3 && selectedMessage.message_thumb" :src="selectedMessage.message_thumb"{% endraw %} style="max-width: 100%;">
|
||||
<video {% raw %}v-if="selectedMessage.message_type == 43 && selectedMessage.attachment_url" :src="selectedMessage.attachment_url"{% endraw %} controls style="max-width: 100%;"></video>
|
||||
|
||||
<el-descriptions-item v-if="selectedMessage.message_type == 3 || selectedMessage.message_type == 43" label="媒体内容">
|
||||
<img v-if="selectedMessage.message_type == 3 && selectedMessage.image_path" :src="getImageUrl(selectedMessage.image_path)" style="max-width: 100%; border-radius: 16px;">
|
||||
<img v-else-if="selectedMessage.message_type == 3 && selectedMessage.message_thumb" :src="selectedMessage.message_thumb" style="max-width: 100%; border-radius: 16px;">
|
||||
<video v-if="selectedMessage.message_type == 43 && selectedMessage.attachment_url" :src="selectedMessage.attachment_url" controls style="max-width: 100%; border-radius: 16px;"></video>
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="原始XML" {% raw %}v-if="selectedMessage.message_xml"{% endraw %}>
|
||||
<pre style="white-space: pre-wrap; word-break: break-all;">{% raw %}{{ selectedMessage.message_xml }}{% endraw %}</pre>
|
||||
|
||||
<el-descriptions-item label="原始XML" v-if="selectedMessage.message_xml">
|
||||
<pre class="message-xml">{% raw %}{{ selectedMessage.message_xml }}{% endraw %}</pre>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 图片预览 -->
|
||||
<el-dialog {% raw %}:visible.sync="imageDialogVisible"{% endraw %} append-to-body width="80%" class="image-dialog">
|
||||
<img {% raw %}v-if="selectedMessage && selectedMessage.image_path" :src="getImageUrl(selectedMessage.image_path)"{% endraw %} style="max-width: 100%;">
|
||||
<img {% raw %}v-else-if="selectedMessage && selectedMessage.message_thumb" :src="selectedMessage.message_thumb"{% endraw %} style="max-width: 100%;">
|
||||
|
||||
<el-dialog :visible.sync="imageDialogVisible" append-to-body width="80%" class="image-dialog">
|
||||
<img v-if="selectedMessage && selectedMessage.image_path" :src="getImageUrl(selectedMessage.image_path)" style="max-width: 100%; border-radius: 18px;">
|
||||
<img v-else-if="selectedMessage && selectedMessage.message_thumb" :src="selectedMessage.message_thumb" style="max-width: 100%; border-radius: 18px;">
|
||||
</el-dialog>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -211,18 +202,14 @@
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.currentView = '7'; // 设置当前菜单项
|
||||
|
||||
// 设置默认日期为今天
|
||||
this.currentView = '7';
|
||||
|
||||
const today = new Date();
|
||||
this.dateRange = [this.formatDate(today), this.formatDate(today)];
|
||||
this.filter.startDate = this.formatDate(today);
|
||||
this.filter.endDate = this.formatDate(today);
|
||||
|
||||
// 加载群组列表
|
||||
|
||||
this.loadGroups();
|
||||
|
||||
// 加载消息列表
|
||||
this.loadMessages();
|
||||
},
|
||||
methods: {
|
||||
@@ -249,29 +236,28 @@
|
||||
},
|
||||
loadMessages() {
|
||||
this.loading = true;
|
||||
|
||||
// 构建查询参数
|
||||
|
||||
const params = {
|
||||
page: this.pagination.page,
|
||||
page_size: this.pagination.pageSize
|
||||
};
|
||||
|
||||
|
||||
if (this.filter.groupId) {
|
||||
params.group_id = this.filter.groupId;
|
||||
}
|
||||
|
||||
|
||||
if (this.filter.startDate) {
|
||||
params.start_date = this.filter.startDate;
|
||||
}
|
||||
|
||||
|
||||
if (this.filter.endDate) {
|
||||
params.end_date = this.filter.endDate;
|
||||
}
|
||||
|
||||
|
||||
if (this.filter.searchText) {
|
||||
params.search_text = this.filter.searchText;
|
||||
}
|
||||
|
||||
|
||||
axios.get('/api/messages', { params })
|
||||
.then(response => {
|
||||
this.messages = response.data.messages || [];
|
||||
@@ -287,28 +273,23 @@
|
||||
});
|
||||
},
|
||||
searchMessages() {
|
||||
// 更新日期范围
|
||||
if (this.dateRange && this.dateRange.length === 2) {
|
||||
this.filter.startDate = this.dateRange[0];
|
||||
this.filter.endDate = this.dateRange[1];
|
||||
}
|
||||
|
||||
// 重置页码并加载消息
|
||||
|
||||
this.pagination.page = 1;
|
||||
this.loadMessages();
|
||||
},
|
||||
resetFilter() {
|
||||
// 重置筛选条件
|
||||
this.filter.groupId = '';
|
||||
this.filter.searchText = '';
|
||||
|
||||
// 设置日期为今天
|
||||
|
||||
const today = new Date();
|
||||
this.dateRange = [this.formatDate(today), this.formatDate(today)];
|
||||
this.filter.startDate = this.formatDate(today);
|
||||
this.filter.endDate = this.formatDate(today);
|
||||
|
||||
// 重置页码并加载消息
|
||||
|
||||
this.pagination.page = 1;
|
||||
this.loadMessages();
|
||||
},
|
||||
@@ -342,37 +323,29 @@
|
||||
return typeMap[type] || `未知类型(${type})`;
|
||||
},
|
||||
getImageUrl(imagePath) {
|
||||
// 如果路径为空,返回空字符串
|
||||
if (!imagePath) return '';
|
||||
|
||||
// 如果已经是完整URL,直接返回
|
||||
|
||||
if (imagePath.startsWith('http://') || imagePath.startsWith('https://')) {
|
||||
return imagePath;
|
||||
}
|
||||
|
||||
// 提取文件名和路径
|
||||
|
||||
const pathParts = imagePath.split(/[\/\\]/);
|
||||
const fileName = pathParts[pathParts.length - 1];
|
||||
|
||||
// 如果路径包含群ID(通常是倒数第二个部分)
|
||||
|
||||
if (pathParts.length >= 2) {
|
||||
const groupId = pathParts[pathParts.length - 2];
|
||||
// 检查是否是群ID格式(通常以@chatroom结尾)
|
||||
if (groupId.includes('@chatroom')) {
|
||||
return `/static/images/${groupId}/${fileName}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查路径中是否包含static/images
|
||||
|
||||
if (imagePath.includes('static/images') || imagePath.includes('static\\images')) {
|
||||
// 提取static/images后面的部分
|
||||
const parts = imagePath.split(/static[\/\\]images[\/\\]/);
|
||||
if (parts.length > 1) {
|
||||
return `/static/images/${parts[1]}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果以上都不匹配,则直接使用文件名
|
||||
|
||||
return `/static/images/${fileName}`;
|
||||
}
|
||||
},
|
||||
@@ -388,27 +361,119 @@
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.filter-card {
|
||||
margin-bottom: 20px;
|
||||
.page-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.message-card {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.page-hero {
|
||||
padding: 24px 26px;
|
||||
border-radius: 24px;
|
||||
background: linear-gradient(135deg, rgba(79,70,229,0.10), rgba(59,130,246,0.08), rgba(255,255,255,0.9));
|
||||
border: 1px solid rgba(148, 163, 184, 0.16);
|
||||
box-shadow: 0 18px 40px rgba(15, 23, 42, 0.06);
|
||||
}
|
||||
|
||||
|
||||
.page-eyebrow {
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .08em;
|
||||
color: #6366f1;
|
||||
font-weight: 700;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.page-hero-copy h1 {
|
||||
font-size: 30px;
|
||||
line-height: 1.1;
|
||||
margin-bottom: 10px;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.page-hero-copy p {
|
||||
color: #64748b;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.workspace-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.workspace-header h3 {
|
||||
font-size: 18px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.workspace-header p,
|
||||
.filter-summary {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.filter-form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px 10px;
|
||||
}
|
||||
|
||||
.message-preview {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.message-text-preview {
|
||||
color: #0f172a;
|
||||
line-height: 1.6;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.message-text-preview.is-muted {
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.message-media-preview {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.message-media-label {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.message-thumb {
|
||||
max-width: 120px;
|
||||
max-height: 120px;
|
||||
cursor: pointer;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(148,163,184,0.16);
|
||||
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.message-thumb {
|
||||
max-width: 100px;
|
||||
max-height: 100px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.image-dialog .el-dialog__body {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.message-xml {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
background: rgba(248,250,252,0.85);
|
||||
border: 1px solid rgba(148,163,184,0.12);
|
||||
border-radius: 14px;
|
||||
padding: 14px;
|
||||
color: #334155;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user