Compare commits
2 Commits
1c34579ebf
...
3c451db37c
Author | SHA1 | Date | |
---|---|---|---|
3c451db37c | |||
163190abe8 |
@@ -1,85 +1,10 @@
|
|||||||
import request from '@/utils/request';
|
import request from '@/utils/request'
|
||||||
|
|
||||||
/**
|
// 获取学生AI对话消息列表(管理员查看)
|
||||||
* 获取会话列表
|
export function getMessagesToAdmin(params) {
|
||||||
* @param {Object} params - 请求参数
|
|
||||||
* @param {number} params.user - 用户ID
|
|
||||||
* @returns {Promise}
|
|
||||||
*/
|
|
||||||
export function getConversationList(params) {
|
|
||||||
return request({
|
return request({
|
||||||
url: '/aitutor/aichat/conversations',
|
url: '/aitutor/aichat/getMessagesToAdmin',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params
|
params: 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
|
|
||||||
});
|
|
||||||
}
|
}
|
@@ -1,273 +1,585 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<div class="search-form">
|
<div class="search-form-container">
|
||||||
<el-form :inline="true" :model="queryParams" ref="queryForm" label-width="68px">
|
<el-form :model="queryParams" ref="queryForm" size="small" v-show="showSearch" label-width="80px" class="search-form">
|
||||||
<el-form-item label="用户ID" prop="user">
|
<el-row :gutter="20">
|
||||||
<el-input v-model="queryParams.user" placeholder="请输入用户ID" clearable size="small" />
|
<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-form-item>
|
||||||
<el-form-item label="会话ID" prop="conversation_id">
|
</el-col>
|
||||||
<el-input v-model="queryParams.conversation_id" placeholder="请输入会话ID" clearable size="small" />
|
<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-form-item>
|
||||||
<el-form-item label="消息内容" prop="content">
|
</el-col>
|
||||||
<el-input v-model="queryParams.content" placeholder="请输入消息内容" clearable size="small" />
|
<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-form-item>
|
||||||
<el-form-item label="时间段">
|
</el-col>
|
||||||
<el-date-picker
|
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6">
|
||||||
v-model="dateRange"
|
<el-form-item label="学院" prop="deptId">
|
||||||
type="daterange"
|
<el-select v-model="queryParams.deptId" filterable clearable placeholder="请选择学院" style="width: 100%">
|
||||||
range-separator="至"
|
<el-option v-for="item in dept_list" :key="item.value" :label="item.label" :value="item.value">
|
||||||
start-placeholder="开始日期"
|
</el-option>
|
||||||
end-placeholder="结束日期"
|
</el-select>
|
||||||
size="small"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
</el-col>
|
||||||
<el-button type="primary" icon="el-icon-search" size="small" @click="handleQuery">搜索</el-button>
|
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6">
|
||||||
<el-button type="success" icon="el-icon-menu" size="small" @click="getConversationsByUser">查询会话</el-button>
|
<el-form-item label="辅导员" prop="cphName">
|
||||||
<el-button icon="el-icon-refresh" size="small" @click="resetQuery">重置</el-button>
|
<el-input v-model="queryParams.cphName" placeholder="请输入辅导员" clearable @keyup.enter.native="handleQuery" />
|
||||||
</el-form-item>
|
</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>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="historyList" border fit highlight-current-row style="width: 100%">
|
<el-row :gutter="10" class="mb8">
|
||||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
<el-table-column prop="user" label="用户ID" width="100" align="center" />
|
</el-row>
|
||||||
<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 v-loading="loading" :data="studentList" style="width: 100%">
|
||||||
<el-table-column prop="type" label="消息类型" width="80" align="center">
|
<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">
|
<template slot-scope="scope">
|
||||||
<el-tag :type="scope.row.type === 'user' ? 'primary' : 'success'" size="small">
|
<span>{{ scope.row.dept.deptName }}</span>
|
||||||
{{ scope.row.type === 'user' ? '用户' : 'AI' }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="content" label="消息内容" min-width="300">
|
<el-table-column label="专业名称" align="center" prop="majorId" min-width="200">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<div v-html="scope.row.content"></div>
|
<span>{{ scope.row.srsMajors.majorName }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="feedback" label="反馈状态" width="100" align="center">
|
<el-table-column label="班级名称" align="center" prop="classId" min-width="200">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<div v-if="scope.row.feedback === 'like'"><i class="el-icon-thumb text-primary"></i> 点赞</div>
|
<span>{{ scope.row.srsClass.className }}</span>
|
||||||
<div v-else-if="scope.row.feedback === 'dislike'"><i class="el-icon-minus text-danger"></i> 点踩</div>
|
|
||||||
<div v-else>无反馈</div>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="created_at" label="创建时间" width="180" align="center" />
|
<el-table-column label="辅导员" align="center" prop="cphName" min-width="100" />
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<el-table-column label="学生状态" align="center" prop="status" min-width="100">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button type="primary" icon="el-icon-view" size="mini" @click="viewMessage(scope.row)">查看详情</el-button>
|
<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>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<div class="pagination-container">
|
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum"
|
||||||
<el-pagination
|
:limit.sync="queryParams.pageSize" @pagination="getList" />
|
||||||
v-show="total > 0"
|
|
||||||
:current-page="queryParams.pageNum"
|
<!-- AI对话记录弹出对话框 -->
|
||||||
:page-size="queryParams.pageSize"
|
<el-dialog
|
||||||
:total="total"
|
title="AI对话记录"
|
||||||
:page-sizes="[10, 20, 50, 100]"
|
:visible.sync="dialogVisible"
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
width="80%"
|
||||||
@size-change="handleSizeChange"
|
:before-close="closeChatHistory"
|
||||||
@current-change="handleCurrentChange"
|
class="chat-dialog"
|
||||||
/>
|
>
|
||||||
|
<div class="dialog-header" v-if="currentStudent">
|
||||||
|
<span class="student-info">学生:{{ currentStudent.name }}({{ currentStudent.stuNo }})</span>
|
||||||
</div>
|
</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>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getHistoryMessages, getFeedbacks, getConversationList } from "@/api/aitutor/chat";
|
import { listStudent, getClassName } from "@/api/stuCQS/basedata/student";
|
||||||
import { getTokenKeySessionStorage } from "@/utils/auth";
|
import { getMessagesToAdmin } from "@/api/aitutor/chat";
|
||||||
|
import { listGrade } from "@/api/stuCQS/basedata/grade";
|
||||||
|
import { getDeptName } from "@/api/system/dept";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ChatHistory",
|
name: "ChatHistory",
|
||||||
|
dicts: ['srs_stu_status'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
// 遮罩层
|
||||||
historyList: [],
|
loading: true,
|
||||||
conversationList: [], // 存储会话列表
|
// 显示搜索条件
|
||||||
|
showSearch: true,
|
||||||
|
// 总条数
|
||||||
total: 0,
|
total: 0,
|
||||||
|
// 学生信息表格数据
|
||||||
|
studentList: [],
|
||||||
|
// 查询参数
|
||||||
queryParams: {
|
queryParams: {
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
user: undefined,
|
name: null,
|
||||||
conversation_id: undefined,
|
stuNo: null,
|
||||||
content: undefined
|
deptId: null,
|
||||||
|
classId: null,
|
||||||
|
cphName: null,
|
||||||
|
gradeId: null
|
||||||
},
|
},
|
||||||
dateRange: [],
|
// 班级名称列表
|
||||||
userToken: getTokenKeySessionStorage()
|
ClassNameList: [],
|
||||||
|
// 班级搜索选择
|
||||||
|
classVlue1: [],
|
||||||
|
// 学院列表
|
||||||
|
dept_list: [],
|
||||||
|
// 年级列表
|
||||||
|
grade_list: [],
|
||||||
|
// AI对话记录相关
|
||||||
|
dialogVisible: false,
|
||||||
|
chatMessages: [],
|
||||||
|
chatLoading: false,
|
||||||
|
currentStudent: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.getList();
|
this.getList();
|
||||||
|
this.getClassNameList();
|
||||||
|
this.listDept();
|
||||||
|
this.listGrade();
|
||||||
},
|
},
|
||||||
methods: {
|
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') {
|
|
||||||
try {
|
|
||||||
conversationData = JSON.parse(conversationData);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('解析会话列表数据失败:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 格式化数据
|
|
||||||
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() {
|
getList() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
// 处理日期范围
|
listStudent(this.queryParams).then(response => {
|
||||||
if (this.dateRange && this.dateRange.length > 0) {
|
console.log('学生列表API响应:', response);
|
||||||
this.queryParams.start_time = this.dateRange[0];
|
// 根据实际API响应结构调整数据获取方式
|
||||||
this.queryParams.end_time = this.dateRange[1];
|
if (response.data) {
|
||||||
|
this.studentList = response.data.rows || response.data || [];
|
||||||
|
this.total = response.data.total || response.total || 0;
|
||||||
} else {
|
} else {
|
||||||
this.queryParams.start_time = undefined;
|
this.studentList = response.rows || [];
|
||||||
this.queryParams.end_time = undefined;
|
this.total = response.total || 0;
|
||||||
}
|
|
||||||
|
|
||||||
// 检查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') {
|
|
||||||
try {
|
|
||||||
historyData = JSON.parse(historyData);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('解析历史消息数据失败:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 格式化数据
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error('获取历史消息失败:', error);
|
console.error('获取学生列表失败:', error);
|
||||||
this.$message.error("网络错误:" + error.message);
|
this.$message.error('获取学生列表失败');
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
/** 获取班级名称列表 */
|
||||||
|
getClassNameList() {
|
||||||
|
getClassName().then(res => {
|
||||||
|
this.ClassNameList = res.data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 获取学院列表 */
|
||||||
|
async listDept() {
|
||||||
|
try {
|
||||||
|
let res = await getDeptName();
|
||||||
|
this.dept_list = [...res.data];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取学院列表失败:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 获取年级列表 */
|
||||||
|
async listGrade() {
|
||||||
|
try {
|
||||||
|
let res = await listGrade();
|
||||||
|
if (res.code == 200) {
|
||||||
|
this.grade_list = [...res.rows];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取年级列表失败:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 搜索班级选择 */
|
||||||
|
handleChange1(value) {
|
||||||
|
this.queryParams.classId = value[2];
|
||||||
|
},
|
||||||
/** 搜索按钮操作 */
|
/** 搜索按钮操作 */
|
||||||
handleQuery() {
|
handleQuery() {
|
||||||
this.queryParams.pageNum = 1;
|
this.queryParams.pageNum = 1;
|
||||||
this.getList();
|
this.getList();
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 重置按钮操作 */
|
/** 重置按钮操作 */
|
||||||
resetQuery() {
|
resetQuery() {
|
||||||
this.dateRange = [];
|
this.resetForm("queryForm");
|
||||||
this.$refs.queryForm.resetFields();
|
this.classVlue1 = [];
|
||||||
this.handleQuery();
|
this.handleQuery();
|
||||||
},
|
},
|
||||||
|
/** 查看AI对话记录 */
|
||||||
|
viewConversations(row) {
|
||||||
|
this.currentStudent = row;
|
||||||
|
this.dialogVisible = true;
|
||||||
|
this.chatLoading = true;
|
||||||
|
this.chatMessages = [];
|
||||||
|
|
||||||
/** 查看消息详情 */
|
getMessagesToAdmin({
|
||||||
viewMessage(row) {
|
user: row.stuNo
|
||||||
// 这里可以实现查看消息详情的逻辑
|
}).then(response => {
|
||||||
this.$modal.msgDetail('消息详情', {
|
console.log('对话记录API响应:', response);
|
||||||
user: row.user,
|
|
||||||
conversation_id: row.conversation_id,
|
if (response.code === 200 && response.data && response.data.data) {
|
||||||
message_id: row.message_id,
|
this.chatMessages = response.data.data;
|
||||||
type: row.type,
|
} else {
|
||||||
content: row.content,
|
this.$modal.msgWarning(response.msg || '该学生暂无对话记录');
|
||||||
feedback: row.feedback,
|
}
|
||||||
created_at: row.created_at
|
|
||||||
|
this.chatLoading = false;
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('获取对话记录失败:', error);
|
||||||
|
this.$modal.msgError('获取对话记录失败');
|
||||||
|
this.chatLoading = false;
|
||||||
});
|
});
|
||||||
// 如果需要更复杂的详情展示,可以打开一个新的弹窗组件
|
|
||||||
// this.$refs.detailDialog.open(row);
|
|
||||||
},
|
},
|
||||||
|
/** 关闭对话记录 */
|
||||||
/** 分页大小改变 */
|
closeChatHistory() {
|
||||||
handleSizeChange(val) {
|
this.dialogVisible = false;
|
||||||
this.queryParams.pageSize = val;
|
this.currentStudent = null;
|
||||||
this.getList();
|
this.chatMessages = [];
|
||||||
},
|
|
||||||
|
|
||||||
/** 当前页码改变 */
|
|
||||||
handleCurrentChange(val) {
|
|
||||||
this.queryParams.pageNum = val;
|
|
||||||
this.getList();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.search-form {
|
.app-container {
|
||||||
margin-bottom: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-container {
|
.mb8 {
|
||||||
margin-top: 20px;
|
margin-bottom: 8px;
|
||||||
text-align: right;
|
}
|
||||||
|
|
||||||
|
.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>
|
</style>
|
Reference in New Issue
Block a user