完成AI辅导员修改
This commit is contained in:
BIN
src/assets/ai/iconAI.png
Normal file
BIN
src/assets/ai/iconAI.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
50
src/components/AiIcon/index.vue
Normal file
50
src/components/AiIcon/index.vue
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<template>
|
||||||
|
<div class="ai-icon-container" :style="{ width: size + 'px', height: size + 'px' }">
|
||||||
|
<img
|
||||||
|
src="@/assets/ai/iconAI.png"
|
||||||
|
:width="size"
|
||||||
|
:height="size"
|
||||||
|
alt="AI助手"
|
||||||
|
class="ai-icon-image"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'AiIcon',
|
||||||
|
props: {
|
||||||
|
size: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 24
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: 'currentColor'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.ai-icon-container {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-icon-container:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-icon-image {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -295,6 +295,9 @@ export default {
|
|||||||
// 启动鼠标跟踪
|
// 启动鼠标跟踪
|
||||||
this.startMouseTracking()
|
this.startMouseTracking()
|
||||||
|
|
||||||
|
// 添加图片点击事件委托
|
||||||
|
this.setupImagePreviewEvents()
|
||||||
|
|
||||||
// 确保DOM完全渲染后再初始化聊天
|
// 确保DOM完全渲染后再初始化聊天
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
@@ -345,6 +348,12 @@ export default {
|
|||||||
// 停止鼠标跟踪
|
// 停止鼠标跟踪
|
||||||
this.stopMouseTracking()
|
this.stopMouseTracking()
|
||||||
|
|
||||||
|
// 移除图片点击事件监听器
|
||||||
|
const messageList = this.$refs.messageList
|
||||||
|
if (messageList && this.imageClickHandler) {
|
||||||
|
messageList.removeEventListener('click', this.imageClickHandler)
|
||||||
|
}
|
||||||
|
|
||||||
// 取消正在进行的请求
|
// 取消正在进行的请求
|
||||||
if (this.currentCancel) {
|
if (this.currentCancel) {
|
||||||
this.currentCancel()
|
this.currentCancel()
|
||||||
@@ -1112,7 +1121,10 @@ export default {
|
|||||||
|
|
||||||
// 渲染markdown
|
// 渲染markdown
|
||||||
const html = this.md.render(text)
|
const html = this.md.render(text)
|
||||||
const sanitizedHtml = DOMPurify.sanitize(html)
|
let sanitizedHtml = DOMPurify.sanitize(html)
|
||||||
|
|
||||||
|
// 处理图片:添加样式和点击预览功能
|
||||||
|
sanitizedHtml = this.processImages(sanitizedHtml)
|
||||||
|
|
||||||
// 缓存管理:如果缓存超过最大大小,删除最旧的条目
|
// 缓存管理:如果缓存超过最大大小,删除最旧的条目
|
||||||
if (this.markdownCache.size >= this.maxCacheSize) {
|
if (this.markdownCache.size >= this.maxCacheSize) {
|
||||||
@@ -1126,6 +1138,107 @@ export default {
|
|||||||
return sanitizedHtml
|
return sanitizedHtml
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理图片:添加样式和点击预览功能
|
||||||
|
*/
|
||||||
|
processImages(html) {
|
||||||
|
// 创建临时DOM元素来处理HTML
|
||||||
|
const tempDiv = document.createElement('div')
|
||||||
|
tempDiv.innerHTML = html
|
||||||
|
|
||||||
|
// 查找所有图片元素
|
||||||
|
const images = tempDiv.querySelectorAll('img')
|
||||||
|
|
||||||
|
images.forEach((img, index) => {
|
||||||
|
// 添加样式
|
||||||
|
img.style.maxWidth = '100%'
|
||||||
|
img.style.height = 'auto'
|
||||||
|
img.style.cursor = 'pointer'
|
||||||
|
img.style.borderRadius = '8px'
|
||||||
|
img.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.1)'
|
||||||
|
img.style.transition = 'all 0.3s ease'
|
||||||
|
|
||||||
|
// 添加hover效果的class
|
||||||
|
img.classList.add('markdown-image')
|
||||||
|
|
||||||
|
// 添加唯一标识符用于后续绑定事件
|
||||||
|
img.setAttribute('data-preview-src', img.src)
|
||||||
|
img.setAttribute('data-preview-image', 'true')
|
||||||
|
|
||||||
|
// 添加加载错误处理
|
||||||
|
img.setAttribute('onerror', 'this.style.display="none"')
|
||||||
|
})
|
||||||
|
|
||||||
|
return tempDiv.innerHTML
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片预览功能
|
||||||
|
*/
|
||||||
|
previewImage(src) {
|
||||||
|
// 创建预览遮罩层
|
||||||
|
const overlay = document.createElement('div')
|
||||||
|
overlay.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 9999;
|
||||||
|
cursor: pointer;
|
||||||
|
`
|
||||||
|
|
||||||
|
// 创建预览图片
|
||||||
|
const previewImg = document.createElement('img')
|
||||||
|
previewImg.src = src
|
||||||
|
previewImg.style.cssText = `
|
||||||
|
max-width: 90%;
|
||||||
|
max-height: 90%;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
`
|
||||||
|
|
||||||
|
// 点击遮罩层关闭预览
|
||||||
|
overlay.addEventListener('click', () => {
|
||||||
|
document.body.removeChild(overlay)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 阻止图片点击事件冒泡
|
||||||
|
previewImg.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
})
|
||||||
|
|
||||||
|
overlay.appendChild(previewImg)
|
||||||
|
document.body.appendChild(overlay)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置图片预览事件委托
|
||||||
|
*/
|
||||||
|
setupImagePreviewEvents() {
|
||||||
|
// 创建事件处理函数
|
||||||
|
this.imageClickHandler = (e) => {
|
||||||
|
// 检查点击的是否是标记为预览的图片
|
||||||
|
if (e.target.tagName === 'IMG' && e.target.getAttribute('data-preview-image') === 'true') {
|
||||||
|
const src = e.target.getAttribute('data-preview-src')
|
||||||
|
if (src) {
|
||||||
|
this.previewImage(src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用事件委托在消息列表容器上监听点击事件
|
||||||
|
const messageList = this.$refs.messageList
|
||||||
|
if (messageList) {
|
||||||
|
messageList.addEventListener('click', this.imageClickHandler)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 节流更新内容
|
* 节流更新内容
|
||||||
*/
|
*/
|
||||||
@@ -2633,6 +2746,23 @@ export default {
|
|||||||
color: #722ed1 !important;
|
color: #722ed1 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Markdown 图片样式 */
|
||||||
|
.content-text .markdown-image {
|
||||||
|
max-width: 100% !important;
|
||||||
|
height: auto !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important;
|
||||||
|
transition: all 0.3s ease !important;
|
||||||
|
display: block;
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-text .markdown-image:hover {
|
||||||
|
transform: scale(1.02);
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15) !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* 消息限制提示样式 */
|
/* 消息限制提示样式 */
|
||||||
.message-limit-notice {
|
.message-limit-notice {
|
||||||
background: #f6f8fa;
|
background: #f6f8fa;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<!-- 其他页面内容 -->
|
<!-- 其他页面内容 -->
|
||||||
<!-- 触发按钮,控制弹窗显示隐藏 -->
|
<!-- 触发按钮,控制弹窗显示隐藏 -->
|
||||||
<div class="ai-hover" @click="toggleAI">
|
<div class="ai-hover" @click="toggleAI">
|
||||||
<span v-if="!showAI" style="font-size: 30px; font-weight: bold;">AI</span>
|
<AiIcon v-if="!showAI" :size="48" color="white" />
|
||||||
<i v-else class="el-icon-close" style="font-size: 20px;"></i>
|
<i v-else class="el-icon-close" style="font-size: 20px;"></i>
|
||||||
</div>
|
</div>
|
||||||
<!-- 聊天弹窗,通过 v-if 控制显隐 -->
|
<!-- 聊天弹窗,通过 v-if 控制显隐 -->
|
||||||
@@ -40,6 +40,7 @@ import ResizeMixin from './mixin/ResizeHandler'
|
|||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import variables from '@/assets/styles/variables.scss'
|
import variables from '@/assets/styles/variables.scss'
|
||||||
import ChatPopup from '../layout/components/Aichat/ChatPopup.vue'
|
import ChatPopup from '../layout/components/Aichat/ChatPopup.vue'
|
||||||
|
import AiIcon from '@/components/AiIcon/index.vue'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
initCoze
|
initCoze
|
||||||
@@ -57,7 +58,8 @@ export default {
|
|||||||
Settings,
|
Settings,
|
||||||
Sidebar,
|
Sidebar,
|
||||||
TagsView,
|
TagsView,
|
||||||
ChatPopup // 注册ChatPopup组件
|
ChatPopup, // 注册ChatPopup组件
|
||||||
|
AiIcon // 注册AiIcon组件
|
||||||
},
|
},
|
||||||
mixins: [ResizeMixin],
|
mixins: [ResizeMixin],
|
||||||
data() {
|
data() {
|
||||||
|
|||||||
Reference in New Issue
Block a user