新增查询学生历史对话消息
This commit is contained in:
@@ -1,85 +1,10 @@
|
||||
import request from '@/utils/request';
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 获取会话列表
|
||||
* @param {Object} params - 请求参数
|
||||
* @param {number} params.user - 用户ID
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function getConversationList(params) {
|
||||
// 获取学生AI对话消息列表(管理员查看)
|
||||
export function getMessagesToAdmin(params) {
|
||||
return request({
|
||||
url: '/aitutor/aichat/conversations',
|
||||
url: '/aitutor/aichat/getMessagesToAdmin',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
* @param {Object} data - 请求体
|
||||
* @param {string} data.query - 查询内容
|
||||
* @param {string} [data.conversation_id] - 会话ID
|
||||
* @param {number} data.user - 用户ID
|
||||
* @param {number} data.user_id - 用户ID
|
||||
* @param {string} data.user_name - 用户名
|
||||
* @param {string} data.user_role - 用户角色
|
||||
* @param {string} data.user_token - 用户token
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function sendMessage(data) {
|
||||
return request({
|
||||
url: '/aitutor/aichat/stream',
|
||||
method: 'post',
|
||||
data,
|
||||
headers: {
|
||||
'Accept': 'text/event-stream'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交反馈
|
||||
* @param {Object} data - 请求体
|
||||
* @param {string} data.message_id - 消息ID
|
||||
* @param {number} data.user_id - 用户ID
|
||||
* @param {string} data.rating - 评分('like'或'dislike')
|
||||
* @param {string} [data.content] - 反馈内容
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function submitFeedback(data) {
|
||||
return request({
|
||||
url: '/aitutor/aichat/feedback',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取反馈列表
|
||||
* @param {Object} params - 请求参数
|
||||
* @param {string} params.conversation_id - 会话ID
|
||||
* @param {number} params.user_id - 用户ID
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function getFeedbacks(params) {
|
||||
return request({
|
||||
url: '/aitutor/aichat/app/feedbacks',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取历史消息
|
||||
* @param {Object} params - 请求参数
|
||||
* @param {string} params.conversation_id - 会话ID
|
||||
* @param {number} params.user - 用户ID
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function getHistoryMessages(params) {
|
||||
return request({
|
||||
url: '/aitutor/aichat/history',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
params: params
|
||||
})
|
||||
}
|
||||
@@ -1,273 +1,585 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="search-form">
|
||||
<el-form :inline="true" :model="queryParams" ref="queryForm" label-width="68px">
|
||||
<el-form-item label="用户ID" prop="user">
|
||||
<el-input v-model="queryParams.user" placeholder="请输入用户ID" clearable size="small" />
|
||||
</el-form-item>
|
||||
<el-form-item label="会话ID" prop="conversation_id">
|
||||
<el-input v-model="queryParams.conversation_id" placeholder="请输入会话ID" clearable size="small" />
|
||||
</el-form-item>
|
||||
<el-form-item label="消息内容" prop="content">
|
||||
<el-input v-model="queryParams.content" placeholder="请输入消息内容" clearable size="small" />
|
||||
</el-form-item>
|
||||
<el-form-item label="时间段">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
size="small"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="small" @click="handleQuery">搜索</el-button>
|
||||
<el-button type="success" icon="el-icon-menu" size="small" @click="getConversationsByUser">查询会话</el-button>
|
||||
<el-button icon="el-icon-refresh" size="small" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="app-container">
|
||||
<div class="search-form-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" v-show="showSearch" label-width="80px" class="search-form">
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6">
|
||||
<el-form-item label="学号" prop="stuNo">
|
||||
<el-input v-model="queryParams.stuNo" placeholder="请输入学号" clearable @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6">
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入姓名" clearable @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6">
|
||||
<el-form-item label="所属班级" prop="classId">
|
||||
<el-cascader placeholder="请选择班级" v-model="classVlue1" :show-all-levels="false"
|
||||
:options="ClassNameList" @change="handleChange1" clearable filterable style="width: 100%">
|
||||
<template slot-scope="{ node, data }">
|
||||
<span>{{ data.label }}</span>
|
||||
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
|
||||
</template>
|
||||
</el-cascader>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6">
|
||||
<el-form-item label="学院" prop="deptId">
|
||||
<el-select v-model="queryParams.deptId" filterable clearable placeholder="请选择学院" style="width: 100%">
|
||||
<el-option v-for="item in dept_list" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6">
|
||||
<el-form-item label="辅导员" prop="cphName">
|
||||
<el-input v-model="queryParams.cphName" placeholder="请输入辅导员" clearable @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6">
|
||||
<el-form-item label="年级" prop="gradeId">
|
||||
<el-select v-model="queryParams.gradeId" filterable clearable placeholder="请选择年级" style="width: 100%">
|
||||
<el-option v-for="item in grade_list" :key="item.gradeId" :label="item.gradeName" :value="item.gradeId">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
||||
<el-form-item class="search-buttons">
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="historyList" border fit highlight-current-row style="width: 100%">
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column prop="user" label="用户ID" width="100" align="center" />
|
||||
<el-table-column prop="conversation_id" label="会话ID" width="180" align="center" />
|
||||
<el-table-column prop="message_id" label="消息ID" width="180" align="center" />
|
||||
<el-table-column prop="type" label="消息类型" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.type === 'user' ? 'primary' : 'success'" size="small">
|
||||
{{ scope.row.type === 'user' ? '用户' : 'AI' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="content" label="消息内容" min-width="300">
|
||||
<template slot-scope="scope">
|
||||
<div v-html="scope.row.content"></div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="feedback" label="反馈状态" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<div v-if="scope.row.feedback === 'like'"><i class="el-icon-thumb text-primary"></i> 点赞</div>
|
||||
<div v-else-if="scope.row.feedback === 'dislike'"><i class="el-icon-minus text-danger"></i> 点踩</div>
|
||||
<div v-else>无反馈</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="created_at" label="创建时间" width="180" align="center" />
|
||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="primary" icon="el-icon-view" size="mini" @click="viewMessage(scope.row)">查看详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="studentList" style="width: 100%">
|
||||
<el-table-column label="学号" align="center" prop="stuNo" min-width="120" />
|
||||
<el-table-column label="姓名" align="center" prop="name" min-width="100" />
|
||||
<el-table-column label="性别" align="center" prop="gender" min-width="80" />
|
||||
<el-table-column label="学院名称" align="center" prop="deptId" min-width="120">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.dept.deptName }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="专业名称" align="center" prop="majorId" min-width="200">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.srsMajors.majorName }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="班级名称" align="center" prop="classId" min-width="200">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.srsClass.className }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="辅导员" align="center" prop="cphName" min-width="100" />
|
||||
<el-table-column label="学生状态" align="center" prop="status" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.srs_stu_status" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" min-width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-chat-dot-round" @click="viewConversations(scope.row)">查看会话列表</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
v-show="total > 0"
|
||||
:current-page="queryParams.pageNum"
|
||||
:page-size="queryParams.pageSize"
|
||||
:total="total"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize" @pagination="getList" />
|
||||
|
||||
<!-- AI对话记录弹出对话框 -->
|
||||
<el-dialog
|
||||
title="AI对话记录"
|
||||
:visible.sync="dialogVisible"
|
||||
width="80%"
|
||||
:before-close="closeChatHistory"
|
||||
class="chat-dialog"
|
||||
>
|
||||
<div class="dialog-header" v-if="currentStudent">
|
||||
<span class="student-info">学生:{{ currentStudent.name }}({{ currentStudent.stuNo }})</span>
|
||||
</div>
|
||||
|
||||
<div v-loading="chatLoading" class="chat-content">
|
||||
<div v-if="chatMessages.length === 0 && !chatLoading" class="no-chat">
|
||||
<el-empty description="暂无对话记录"></el-empty>
|
||||
</div>
|
||||
|
||||
<div v-for="message in chatMessages" :key="message.id" class="message-item">
|
||||
<div class="message-header">
|
||||
<span class="message-time">{{ parseTime(message.created_at * 1000, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
<div class="feedback-status" v-if="message.feedback">
|
||||
<el-tag v-if="message.feedback && message.feedback.rating === 'like'" type="success" size="mini">
|
||||
<i class="el-icon-thumb-up"></i> 点赞
|
||||
</el-tag>
|
||||
<el-tag v-else-if="message.feedback && message.feedback.rating === 'dislike'" type="danger" size="mini">
|
||||
<i class="el-icon-thumb-down"></i> 点踩
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message-content">
|
||||
<div class="user-message">
|
||||
<div class="message-label">学生提问:</div>
|
||||
<div class="message-text user-text">{{ message.query }}</div>
|
||||
</div>
|
||||
|
||||
<div class="ai-message">
|
||||
<div class="message-label">AI回答:</div>
|
||||
<div class="message-text ai-text" :class="{
|
||||
'feedback-like': message.feedback && message.feedback.rating === 'like',
|
||||
'feedback-dislike': message.feedback && message.feedback.rating === 'dislike'
|
||||
}">{{ message.answer }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getHistoryMessages, getFeedbacks, getConversationList } from "@/api/aitutor/chat";
|
||||
import { getTokenKeySessionStorage } from "@/utils/auth";
|
||||
import { listStudent, getClassName } from "@/api/stuCQS/basedata/student";
|
||||
import { getMessagesToAdmin } from "@/api/aitutor/chat";
|
||||
import { listGrade } from "@/api/stuCQS/basedata/grade";
|
||||
import { getDeptName } from "@/api/system/dept";
|
||||
|
||||
export default {
|
||||
name: "ChatHistory",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
historyList: [],
|
||||
conversationList: [], // 存储会话列表
|
||||
total: 0,
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
user: undefined,
|
||||
conversation_id: undefined,
|
||||
content: undefined
|
||||
},
|
||||
dateRange: [],
|
||||
userToken: getTokenKeySessionStorage()
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 根据用户ID查询会话列表 */
|
||||
getConversationsByUser() {
|
||||
if (!this.queryParams.user) {
|
||||
this.$message.error('请输入用户ID');
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
const params = {
|
||||
user: this.queryParams.user
|
||||
};
|
||||
|
||||
getConversationList(params).then(response => {
|
||||
if (response.code === 200) {
|
||||
let conversationData = response.data;
|
||||
// 如果返回的是字符串形式的JSON,尝试解析
|
||||
if (typeof conversationData === 'string') {
|
||||
name: "ChatHistory",
|
||||
dicts: ['srs_stu_status'],
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 学生信息表格数据
|
||||
studentList: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
stuNo: null,
|
||||
deptId: null,
|
||||
classId: null,
|
||||
cphName: null,
|
||||
gradeId: null
|
||||
},
|
||||
// 班级名称列表
|
||||
ClassNameList: [],
|
||||
// 班级搜索选择
|
||||
classVlue1: [],
|
||||
// 学院列表
|
||||
dept_list: [],
|
||||
// 年级列表
|
||||
grade_list: [],
|
||||
// AI对话记录相关
|
||||
dialogVisible: false,
|
||||
chatMessages: [],
|
||||
chatLoading: false,
|
||||
currentStudent: null
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
this.getClassNameList();
|
||||
this.listDept();
|
||||
this.listGrade();
|
||||
},
|
||||
methods: {
|
||||
/** 查询学生信息列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listStudent(this.queryParams).then(response => {
|
||||
console.log('学生列表API响应:', response);
|
||||
// 根据实际API响应结构调整数据获取方式
|
||||
if (response.data) {
|
||||
this.studentList = response.data.rows || response.data || [];
|
||||
this.total = response.data.total || response.total || 0;
|
||||
} else {
|
||||
this.studentList = response.rows || [];
|
||||
this.total = response.total || 0;
|
||||
}
|
||||
this.loading = false;
|
||||
}).catch(error => {
|
||||
console.error('获取学生列表失败:', error);
|
||||
this.$message.error('获取学生列表失败');
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 获取班级名称列表 */
|
||||
getClassNameList() {
|
||||
getClassName().then(res => {
|
||||
this.ClassNameList = res.data;
|
||||
});
|
||||
},
|
||||
/** 获取学院列表 */
|
||||
async listDept() {
|
||||
try {
|
||||
conversationData = JSON.parse(conversationData);
|
||||
} catch (e) {
|
||||
console.error('解析会话列表数据失败:', e);
|
||||
let res = await getDeptName();
|
||||
this.dept_list = [...res.data];
|
||||
} catch (error) {
|
||||
console.error('获取学院列表失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化数据
|
||||
if (Array.isArray(conversationData)) {
|
||||
this.conversationList = conversationData;
|
||||
if (conversationData.length > 0) {
|
||||
this.$message.success(`成功获取到${conversationData.length}条会话记录`);
|
||||
// 如果只有一条会话,可以自动填充到会话ID输入框
|
||||
if (conversationData.length === 1) {
|
||||
this.queryParams.conversation_id = conversationData[0].conversation_id;
|
||||
}
|
||||
} else {
|
||||
this.$message.info('未找到该用户的会话记录');
|
||||
}
|
||||
} else if (conversationData && Array.isArray(conversationData.list)) {
|
||||
this.conversationList = conversationData.list;
|
||||
this.$message.success(`成功获取到${conversationData.list.length}条会话记录`);
|
||||
} else {
|
||||
this.conversationList = [];
|
||||
this.$message.info('未找到该用户的会话记录');
|
||||
}
|
||||
} else {
|
||||
this.$message.error("获取会话列表失败:" + (response.msg || response.message));
|
||||
}
|
||||
this.loading = false;
|
||||
}).catch(error => {
|
||||
console.error('获取会话列表失败:', error);
|
||||
this.$message.error("网络错误:" + error.message);
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
|
||||
/** 查询历史消息列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
// 处理日期范围
|
||||
if (this.dateRange && this.dateRange.length > 0) {
|
||||
this.queryParams.start_time = this.dateRange[0];
|
||||
this.queryParams.end_time = this.dateRange[1];
|
||||
} else {
|
||||
this.queryParams.start_time = undefined;
|
||||
this.queryParams.end_time = undefined;
|
||||
}
|
||||
|
||||
// 检查user参数是否存在
|
||||
if (!this.queryParams.user) {
|
||||
this.$message.error('请输入用户ID');
|
||||
this.loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加user参数以匹配API要求
|
||||
const params = {
|
||||
...this.queryParams,
|
||||
user: this.queryParams.user
|
||||
};
|
||||
getHistoryMessages(params).then(response => {
|
||||
// 假设接口返回格式为 { code: 200, data: { list: [], total: 0 } }
|
||||
// 根据实际接口调整
|
||||
if (response.code === 200) {
|
||||
let historyData = response.data;
|
||||
// 如果返回的是字符串形式的JSON,尝试解析
|
||||
if (typeof historyData === 'string') {
|
||||
},
|
||||
/** 获取年级列表 */
|
||||
async listGrade() {
|
||||
try {
|
||||
historyData = JSON.parse(historyData);
|
||||
} catch (e) {
|
||||
console.error('解析历史消息数据失败:', e);
|
||||
let res = await listGrade();
|
||||
if (res.code == 200) {
|
||||
this.grade_list = [...res.rows];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取年级列表失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化数据
|
||||
if (Array.isArray(historyData)) {
|
||||
this.historyList = historyData;
|
||||
this.total = historyData.length;
|
||||
} else if (historyData && Array.isArray(historyData.list)) {
|
||||
this.historyList = historyData.list;
|
||||
this.total = historyData.total;
|
||||
} else {
|
||||
this.historyList = [];
|
||||
this.total = 0;
|
||||
}
|
||||
} else {
|
||||
this.$message.error("获取历史消息失败:" + (response.msg || response.message));
|
||||
},
|
||||
/** 搜索班级选择 */
|
||||
handleChange1(value) {
|
||||
this.queryParams.classId = value[2];
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.classVlue1 = [];
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 查看AI对话记录 */
|
||||
viewConversations(row) {
|
||||
this.currentStudent = row;
|
||||
this.dialogVisible = true;
|
||||
this.chatLoading = true;
|
||||
this.chatMessages = [];
|
||||
|
||||
getMessagesToAdmin({
|
||||
user: row.stuNo
|
||||
}).then(response => {
|
||||
console.log('对话记录API响应:', response);
|
||||
|
||||
if (response.code === 200 && response.data && response.data.data) {
|
||||
this.chatMessages = response.data.data;
|
||||
} else {
|
||||
this.$modal.msgWarning(response.msg || '该学生暂无对话记录');
|
||||
}
|
||||
|
||||
this.chatLoading = false;
|
||||
}).catch(error => {
|
||||
console.error('获取对话记录失败:', error);
|
||||
this.$modal.msgError('获取对话记录失败');
|
||||
this.chatLoading = false;
|
||||
});
|
||||
},
|
||||
/** 关闭对话记录 */
|
||||
closeChatHistory() {
|
||||
this.dialogVisible = false;
|
||||
this.currentStudent = null;
|
||||
this.chatMessages = [];
|
||||
}
|
||||
this.loading = false;
|
||||
}).catch(error => {
|
||||
console.error('获取历史消息失败:', error);
|
||||
this.$message.error("网络错误:" + error.message);
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.$refs.queryForm.resetFields();
|
||||
this.handleQuery();
|
||||
},
|
||||
|
||||
/** 查看消息详情 */
|
||||
viewMessage(row) {
|
||||
// 这里可以实现查看消息详情的逻辑
|
||||
this.$modal.msgDetail('消息详情', {
|
||||
user: row.user,
|
||||
conversation_id: row.conversation_id,
|
||||
message_id: row.message_id,
|
||||
type: row.type,
|
||||
content: row.content,
|
||||
feedback: row.feedback,
|
||||
created_at: row.created_at
|
||||
});
|
||||
// 如果需要更复杂的详情展示,可以打开一个新的弹窗组件
|
||||
// this.$refs.detailDialog.open(row);
|
||||
},
|
||||
|
||||
/** 分页大小改变 */
|
||||
handleSizeChange(val) {
|
||||
this.queryParams.pageSize = val;
|
||||
this.getList();
|
||||
},
|
||||
|
||||
/** 当前页码改变 */
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.pageNum = val;
|
||||
this.getList();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-form {
|
||||
margin-bottom: 20px;
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
.mb8 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.el-table .cell {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* 搜索表单容器样式 */
|
||||
.search-form-container {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 0.5rem;
|
||||
padding: 1.25rem;
|
||||
margin-bottom: 1.25rem;
|
||||
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.05);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.search-form .el-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -0.625rem;
|
||||
}
|
||||
|
||||
.search-form .el-col {
|
||||
padding: 0 0.625rem;
|
||||
box-sizing: border-box;
|
||||
min-width: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.search-form .el-form-item {
|
||||
margin-bottom: 1rem;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.search-form .el-form-item__label {
|
||||
font-weight: 500;
|
||||
color: #606266;
|
||||
white-space: nowrap;
|
||||
min-width: 5rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.search-form .el-form-item__content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.search-form .el-input,
|
||||
.search-form .el-select,
|
||||
.search-form .el-cascader {
|
||||
width: 100%;
|
||||
min-width: 8rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.search-form .el-input__inner,
|
||||
.search-form .el-select .el-input__inner {
|
||||
box-sizing: border-box;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* 搜索按钮样式 */
|
||||
.search-buttons {
|
||||
text-align: center;
|
||||
margin-top: 0.625rem;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.search-buttons .el-button {
|
||||
margin: 0 0.5rem;
|
||||
padding: 0.5rem 1.25rem;
|
||||
border-radius: 0.25rem;
|
||||
font-weight: 500;
|
||||
box-sizing: border-box;
|
||||
min-width: 4rem;
|
||||
}
|
||||
|
||||
.search-buttons .el-button--primary {
|
||||
background: linear-gradient(135deg, #409eff 0%, #1890ff 100%);
|
||||
border: none;
|
||||
box-shadow: 0 0.125rem 0.25rem rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
.search-buttons .el-button--primary:hover {
|
||||
background: linear-gradient(135deg, #66b1ff 0%, #40a9ff 100%);
|
||||
box-shadow: 0 0.25rem 0.5rem rgba(64, 158, 255, 0.4);
|
||||
}
|
||||
|
||||
/* 缩放适配 - 针对110%等非标准缩放 */
|
||||
@media screen and (min-resolution: 1.1dppx) and (max-resolution: 1.3dppx) {
|
||||
.search-form-container {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.search-form .el-form-item {
|
||||
margin-bottom: 0.875rem;
|
||||
}
|
||||
|
||||
.search-form .el-form-item__label {
|
||||
min-width: 4.5rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.search-form .el-input,
|
||||
.search-form .el-select,
|
||||
.search-form .el-cascader {
|
||||
min-width: 7rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 768px) {
|
||||
.search-form-container {
|
||||
padding: 0.9375rem;
|
||||
}
|
||||
|
||||
.search-form .el-form-item {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.search-form .el-form-item__label {
|
||||
min-width: auto;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.search-buttons {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.search-buttons .el-button {
|
||||
margin: 0.25rem 0.5rem 0.25rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 超小屏幕适配 */
|
||||
@media (max-width: 480px) {
|
||||
.search-form .el-col {
|
||||
flex: 0 0 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.search-form .el-form-item__label {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
/* 对话框样式 */
|
||||
.chat-dialog .el-dialog__body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
background: #f8f9fa;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.student-info {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.chat-content {
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.no-chat {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
.message-item {
|
||||
margin-bottom: 24px;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.message-header {
|
||||
background: #f5f7fa;
|
||||
padding: 12px 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.message-time {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.feedback-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.user-message,
|
||||
.ai-message {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.user-message:last-child,
|
||||
.ai-message:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.message-label {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.message-text {
|
||||
padding: 12px 16px;
|
||||
border-radius: 6px;
|
||||
line-height: 1.6;
|
||||
font-size: 14px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.user-text {
|
||||
background: #f0f9ff;
|
||||
border: 1px solid #e1f5fe;
|
||||
color: #0277bd;
|
||||
}
|
||||
|
||||
.ai-text {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.ai-text.feedback-like {
|
||||
background: #f0f9ff;
|
||||
border-color: #4caf50;
|
||||
box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.1);
|
||||
}
|
||||
|
||||
.ai-text.feedback-dislike {
|
||||
background: #fff5f5;
|
||||
border-color: #f56565;
|
||||
box-shadow: 0 0 0 2px rgba(245, 101, 101, 0.1);
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
.chat-content::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.chat-content::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.chat-content::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.chat-content::-webkit-scrollbar-thumb:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user