| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758 |
- <template>
- <div class="dialogue-panel">
- <div class="dp_messageList" ref="messageListRef">
- <div class="dp_ml_item" v-for="item in messageList" :key="item.id">
- <div class="dp_ml_i_message right" v-if="item.userContent"><!--v-if="item.userContent"-->
- <div class="dp_ml_i_m_msgArea">
- <div class="dp_ml_i_m_ma_name" v-if="item.userName" v-text="item.userName"></div>
- <div class="dp_ml_i_m_ma_textBlok">
- <span v-html="item.userContent"></span>
- </div>
- </div>
- <div class="dp_ml_i_m_avatarArea">
- <div>
- <img src="../../../assets/img/avatar.png" alt="">
- </div>
- </div>
- </div>
- <div class="dp_ml_i_message left" v-if="item.aiContent || item.loading"><!--v-if="item.aiContent || item.loading"-->
- <div class="dp_ml_i_m_avatarArea">
- <div>
- <img src="../../../assets/img/ai_agent_header2.png" alt="">
- </div>
- </div>
- <div class="dp_ml_i_m_msgArea">
- <div class="dp_ml_i_m_ma_name" v-if="item.aiName" v-text="item.aiName"></div>
- <div class="dp_ml_i_m_ma_textBlok">
- <span v-if="item.aiContent" v-html="htmlContent(item.aiContent)"></span>
- <svg v-else xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from SVG Spinners by Utkarsh Verma - https://github.com/n3r4zzurr0/svg-spinners/blob/main/LICENSE --><circle cx="4" cy="12" r="3" fill="currentColor"><animate id="svgSpinners3DotsBounce0" attributeName="cy" begin="0;svgSpinners3DotsBounce1.end+0.25s" calcMode="spline" dur="0.6s" keySplines=".33,.66,.66,1;.33,0,.66,.33" values="12;6;12"/></circle><circle cx="12" cy="12" r="3" fill="currentColor"><animate attributeName="cy" begin="svgSpinners3DotsBounce0.begin+0.1s" calcMode="spline" dur="0.6s" keySplines=".33,.66,.66,1;.33,0,.66,.33" values="12;6;12"/></circle><circle cx="20" cy="12" r="3" fill="currentColor"><animate id="svgSpinners3DotsBounce1" attributeName="cy" begin="svgSpinners3DotsBounce0.begin+0.2s" calcMode="spline" dur="0.6s" keySplines=".33,.66,.66,1;.33,0,.66,.33" values="12;6;12"/></circle></svg>
- </div>
- </div>
- </div>
- </div>
- </div>
- <!-- 清空聊天记录按钮区域 -->
- <div class="dp_clearArea">
- <div class="dp_clear_btn"
- v-if="!sendMessageLoading && !dialogueLoading"
- @click.stop="openClearConfirm"
- title="清空聊天记录">
- <svg
- width="20"
- height="20"
- viewBox="0 0 20 20"
- fill="none"
- xmlns="http://www.w3.org/2000/svg"
- >
- <path
- fill-rule="evenodd"
- clip-rule="evenodd"
- d="M2.5 3.125C2.5 2.77982 2.77982 2.5 3.125 2.5H16.875C17.2202 2.5 17.5 2.77982 17.5 3.125V8.02715C17.5 8.37233 17.2202 8.65215 16.875 8.65215C16.5298 8.65215 16.25 8.37233 16.25 8.02715V3.75H3.75V16.25H8.125C8.47018 16.25 8.75 16.5298 8.75 16.875C8.75 17.2202 8.47018 17.5 8.125 17.5H3.125C2.77982 17.5 2.5 17.2202 2.5 16.875V3.125Z"
- fill="black"
- fill-opacity="0.6"
- />
- <path
- fill-rule="evenodd"
- clip-rule="evenodd"
- d="M5.625 6.1521C5.625 5.80692 5.90482 5.5271 6.25 5.5271H13.125C13.4702 5.5271 13.75 5.80692 13.75 6.1521C13.75 6.49728 13.4702 6.7771 13.125 6.7771H6.25C5.90482 6.7771 5.625 6.49728 5.625 6.1521Z"
- fill="black"
- fill-opacity="0.6"
- />
- <path
- fill-rule="evenodd"
- clip-rule="evenodd"
- d="M5.625 9.2771C5.625 8.93192 5.90482 8.6521 6.25 8.6521H9.37496C9.72014 8.6521 9.99996 8.93192 9.99996 9.2771C9.99996 9.62228 9.72014 9.9021 9.37496 9.9021H6.25C5.90482 9.9021 5.625 9.62228 5.625 9.2771Z"
- fill="black"
- fill-opacity="0.6"
- />
- <path
- fill-rule="evenodd"
- clip-rule="evenodd"
- d="M12.465 11.507L15.9141 14.9048C16.1279 14.5365 16.25 14.1088 16.25 13.6521C16.25 12.2714 15.1307 11.1521 13.75 11.1521C13.2799 11.1521 12.8406 11.2815 12.465 11.507ZM15.0374 15.7957L11.5873 12.397C11.3726 12.7659 11.25 13.1944 11.25 13.6521C11.25 15.0328 12.3693 16.1521 13.75 16.1521C14.2211 16.1521 14.6613 16.0222 15.0374 15.7957ZM11.0797 11.0192C11.759 10.3303 12.7051 9.9021 13.75 9.9021C15.8211 9.9021 17.5 11.581 17.5 13.6521C17.5 14.6767 17.0882 15.6064 16.4226 16.2827C15.7431 16.9729 14.7961 17.4021 13.75 17.4021C11.6789 17.4021 10 15.7232 10 13.6521C10 12.6263 10.4127 11.6957 11.0797 11.0192Z"
- fill="black"
- fill-opacity="0.6"
- />
- </svg>
- </div>
- </div>
- <div class="dp_inputArea">
- <div class="dp_ia_bottom">
- <div class="dp_ia_b_left">
- <div>
- <textarea
- v-model="inputText"
- type="text"
- placeholder="请输入"
- @keydown.enter.exact.prevent="sendMessage"
- ></textarea>
- </div>
- </div>
- <div class="dp_ia_b_right">
- <!-- 清空聊天记录按钮 -->
- <div
- class="dp_ia_b_r_btn"
- v-if="!sendMessageLoading"
- @click.stop="sendMessage"
- >
- <svg
- t="1756432184712"
- class="icon"
- viewBox="0 0 1024 1024"
- version="1.1"
- xmlns="http://www.w3.org/2000/svg"
- p-id="7156"
- width="200"
- height="200"
- >
- <path
- d="M211.649242 813.217191a425.984 425.984 0 1 0 602.421836-602.442865 425.984 425.984 0 1 0-602.421836 602.442865Z"
- fill="#00A0E9"
- p-id="7157"
- ></path>
- <path
- d="M266.3936 427.7248l422.5024-103.5776c20.4288-5.0176 37.5296 15.9744 28.4672 34.9696l-188.2112 395.1616c-9.728 20.3776-39.3728 18.432-46.2848-3.072l-48.0256-149.4016a25.06752 25.06752 0 0 1 5.2224-24.3712L522.1888 486.4c5.0176-5.5808-1.6896-13.9264-8.192-10.0864l-108.9024 63.5392a24.9856 24.9856 0 0 1-24.6272 0.3072L260.2496 473.8048c-19.8656-10.9568-15.9232-40.6528 6.144-46.08z"
- fill="#FFFFFF"
- p-id="7158"
- ></path>
- </svg>
- </div>
- <div
- class="dp_ia_b_r_btn"
- v-if="sendMessageLoading"
- @click.stop="stopSendMessage"
- >
- <svg
- t="1756432604950"
- class="icon"
- viewBox="0 0 1024 1024"
- version="1.1"
- xmlns="http://www.w3.org/2000/svg"
- p-id="11335"
- width="200"
- height="200"
- >
- <path
- d="M512 960C264.57 960 64 759.43 64 512S264.57 64 512 64s448 200.57 448 448-200.57 448-448 448z m0.43-83.66c201.01 0 364.01-163.01 364.01-364.01s-163-364.02-364.01-364.02-364.01 163.01-364.01 364.01 162.9 364.02 364.01 364.02zM371.99 343.92h280.03c15.44 0 27.96 12.52 27.96 27.96v280.13c0 15.44-12.52 27.96-27.96 27.96H371.99c-15.44 0-27.96-12.52-27.96-27.96V371.99c0-15.55 12.52-28.07 27.96-28.07z"
- p-id="11336"
- fill="#707070"
- ></path>
- </svg>
- </div>
- </div>
- </div>
- </div>
- <div class="dialogueLoading" v-if="dialogueLoading">
- <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z" opacity=".25"/><path fill="currentColor" d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"><animateTransform attributeName="transform" dur="0.75s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/></path></svg>
- </div>
- <!-- 清空聊天记录确认弹窗 -->
- <Modal
- :visible="clearConfirmVisible"
- :width="420"
- :closeButton="true"
- :closeOnClickMask="false"
- :closeOnEsc="false"
- @update:visible="val => clearConfirmVisible = val"
- >
- <div class="clear-confirm">
- <div class="clear-confirm__title">清空聊天记录</div>
- <div class="clear-confirm__content">
- 确定要清空所有聊天记录吗?此操作不可恢复。
- </div>
- <div class="clear-confirm__footer">
- <Button type="default" @click="clearConfirmVisible = false">取消</Button>
- <Button type="primary" @click="handleConfirmClear">确认清空</Button>
- </div>
- </div>
- </Modal>
- </div>
- </template>
- <script lang="ts" setup>
- import { ref, reactive, onMounted, computed } from 'vue'
- import { fetchEventSource } from '@microsoft/fetch-event-source'
- import MarkdownIt from 'markdown-it'
- import api from '../../../services/course'
- import message from '@/utils/message'
- import Modal from '@/components/Modal.vue'
- import Button from '@/components/Button.vue'
- interface Props {
- courseid?: string | null
- userid?: string | null
- }
- const props = withDefaults(defineProps<Props>(), {
- courseid: null,
- userid: null,
- })
- interface Message {
- id: string;
- userContent: string;
- aiContent: string;
- createTime: string;
- userName: string;
- aiName: string;
- loading: boolean;
- }
- // 对话消息列表
- const messageList = reactive<Message[]>([
- {
- id: '1',
- userContent: '',
- aiContent: '你好!我是你的学习助手,有什么可以帮助你的吗?', // 你好!我是你的学习助手,有什么可以帮助你的吗?
- createTime: new Date().toLocaleString().replace(/\//g, '-'),
- userName: '老师',
- aiName: 'AI助手',
- loading: false,
- },
- ])
- // 发送消息loading
- const sendMessageLoading = ref<boolean>(false)
- // 整个对话加载loading
- const dialogueLoading = ref<boolean>(false)
- // 输入框文本
- const inputText = ref('')
- // 消息区域Ref
- const messageListRef = ref<HTMLDivElement>()
- // 发送消息请求实例
- const curRequestController = ref<AbortController | null>(null)
- // 用户名称
- const userName = ref<string>('')
- // 代理配置 / 会话信息
- const agentData = ref<any>({})
- // 清空聊天记录二次确认弹窗
- const clearConfirmVisible = ref(false)
- const htmlContent = computed(() => {
- const md = new MarkdownIt()
- return (_md:string) => {
- return md.render(_md)
- }
- })
- // 发送消息
- const sendMessage = () => {
- if (sendMessageLoading.value) return
- if (!inputText.value.trim()) return
- const userInput = inputText.value.trim()
- inputText.value = ''
- sendMessageLoading.value = true
- const _uid = new Date().getTime().toString()
- const newMessage = {
- id: _uid,
- userContent: userInput,
- aiContent: '',
- createTime: new Date().toLocaleString().replace(/\//g, '-'),
- userName: userName.value || '老师',
- aiName: 'AI助手',
- loading: true,
- }
- messageList.push(newMessage)
- messageListScrollBottom()
- // const params = {
- // model: 'gpt-4o-2024-11-20',
- // temperature: 0,
- // max_tokens: 4096,
- // top_p: 1,
- // frequency_penalty: 0,
- // presence_penalty: 0,
- // messages: [{ role: 'user', content: userInput}],
- // uid: new Date().getTime().toString(),
- // mind_map_question: '',
- // stream: true,
- // }
- const params = {
- id: '978252d7-a26e-4e70-8d75-3336497abac1',
- message: userInput,
- userId: props.userid,
- // model: node.properties.item.modelType,
- model: 'open-gpt-4.1',
- file_ids: [],
- sound_url: '',
- temperature: 0.2,
- top_p: 1,
- max_completion_tokens: 4096,
- stream: true,
- uid: _uid,
- session_name: `${props.userid}_${props.courseid}_pptCourse`,
- }
- let _addText = ''
- curRequestController.value = new AbortController()
- fetchEventSource(
- 'https://appapi.cocorobo.cn/api/agentchats/ai_agent_chat',
- {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(params),
- signal: curRequestController.value.signal,
- onmessage(ev) {
- const _data = JSON.parse(ev.data).content
- if (_data === '[DONE]') {
- insertChat(_uid)
- return (sendMessageLoading.value = false)
- }
- _addText += _data
- const msgItem = messageList.find((item) => item.id === _uid)
-
- if (msgItem) {
- msgItem.aiContent = _addText
- msgItem.loading = false
- }
- messageListScrollBottom()
- },
- onclose() {
- sendMessageLoading.value = false
- curRequestController.value = null
- console.log('连接关闭')
- },
- onerror(err) {
- console.log(err)
- sendMessageLoading.value = false
- const errorMsgItem = messageList.find((item) => item.id === _uid)
- if (errorMsgItem) {
- errorMsgItem.aiContent = '网络错误'
- }
- },
- }
- )
- // 发送消息
- }
- const stopSendMessage = () => {
- sendMessageLoading.value = false
- curRequestController.value?.abort()
- if (messageList.length > 0) {
- insertChat(messageList[messageList.length - 1].id)
- }
- console.log('主动关闭连接')
- }
- // 消息区域触底
- const messageListScrollBottom = () => {
- if (messageListRef.value) messageListRef.value.scrollTop = messageListRef.value.scrollHeight
- }
- // 保存消息
- const insertChat = async (uid: string) => {
- const _msg = messageList.find(i => i.id === uid)
- if ( props.userid && props.courseid && _msg) {
- const params = {
- userId: props.userid,
- userName: userName.value
- ? userName.value
- : await getUserName(props.userid),
- groupId: `${props.courseid?.slice(0, 31)}${props.userid?.slice(0, 5)}`,
- answer: _msg.aiContent,
- problem: _msg.userContent,
- file_id: '',
- alltext: _msg.aiContent,
- type: 'chat',
- filename: '',
- session_name: `${props.userid}_${props.courseid}_pptCourse`,
- }
- api.insertChat(params)
- }
- }
- // 获取用户名称
- const getUserName = (uid:string | null) => {
- if (!uid) return '-'
- return new Promise(resolve => {
- return api.getUser(uid).then((res: any) => {
- const data = res[0][0]
- userName.value = data.username
- resolve(data.username)
- })
- })
- }
- // 获取对话内容
- const getMessageList = () => {
- if (props.courseid && props.userid) {
- dialogueLoading.value = true
- const params = {
- userid: props.userid,
- groupid: `${props.courseid?.slice(0, 31)}${props.userid?.slice(0, 5)}`,
- session_name: `${props.userid}_${props.courseid}_pptCourse`,
- }
- api.getChatList(params).then((res: any) => {
- const data = JSON.parse(res.FunctionResponse).response
- if (data && data.length > 0) {
- data.forEach((item:any) => {
- const oldMessage = {
- id: item.id,
- userContent: item.problem,
- aiContent: item.answer,
- createTime: item.createtime,
- userName: userName.value || '老师',
- aiName: 'AI助手',
- loading: false,
- }
- messageList.push(oldMessage)
- })
- }
- dialogueLoading.value = false
- }).catch(() => {
- dialogueLoading.value = false
- })
- }
- }
- // 打开清空聊天记录确认弹窗
- const openClearConfirm = () => {
- if (dialogueLoading.value) return
- clearConfirmVisible.value = true
- }
- // 实际执行清空聊天记录
- const clearChatHistory = async () => {
- if (dialogueLoading.value) return
- console.log(agentData.value)
- dialogueLoading.value = true
- try {
- const params = {
- model: 'open-gpt-4.1',
- thread_id: agentData.value.thread_id,
- groupid: `${props.courseid?.slice(0, 31)}${props.userid?.slice(0, 5)}`,
- userid: props.userid,
- }
- const response = await api.clearDialogue(params)
- if (response) {
- // 清空本地消息列表,只保留初始欢迎消息
- messageList.splice(1) // 保留第一个欢迎消息
- // 重置消息列表为初始状态
- messageList[0] = {
- id: '1',
- userContent: '',
- aiContent: '你好!我是你的学习助手,有什么可以帮助你的吗?',
- createTime: new Date().toLocaleString().replace(/\//g, '-'),
- userName: '老师',
- aiName: 'AI助手',
- loading: false,
- }
- console.log('聊天记录清空成功')
- message.success('聊天记录已清空')
- }
- else {
- throw new Error(`HTTP error! status: ${response.status}`)
- }
- }
- catch (error) {
- console.error('清空聊天记录失败:', error)
- message.error('清空聊天记录失败,请重试')
- }
- finally {
- dialogueLoading.value = false
- }
- }
- // 确认弹窗中点击“确认清空”
- const handleConfirmClear = async () => {
- clearConfirmVisible.value = false
- await clearChatHistory()
- }
- const getAgentData = () => {
- dialogueLoading.value = true
- api.getAgentData({
- id: '978252d7-a26e-4e70-8d75-3336497abac1',
- }).then((res: any) => {
- console.log(res)
- agentData.value = {source: 'agent', ...res}
- getMessageList()
- })
- }
- onMounted(() => {
- getUserName(props.userid)
-
- getAgentData()
- })
- </script>
- <style lang="scss" scoped>
- .dialogue-panel {
- width: 100%;
- height: 100%;
- display: flex;
- flex-direction: column;
- position: relative;
- padding: 0 8px;
- box-sizing: border-box;
- .dp_messageList {
- width: 100%;
- height: calc(100% - 60px - 40px - 10px); // 减去输入框和清空按钮的高度
- overflow: auto;
- .dp_ml_item{
- width: 100%;
- height: auto;
- .dp_ml_i_message{
- width: 100%;
- height: auto;
- display: flex;
- margin-bottom: 20px;
- .dp_ml_i_m_avatarArea{
- width: 50px;
- height: auto;
- display: flex;
- justify-content: center;
- div{
- width: 40px;
- height: 40px;
- margin-top: 10px;
- border-radius: 50%;
- overflow: hidden;
- img{
- width: 100%;
- height: 100%;
- object-fit: cover;
- }
- }
- }
- .dp_ml_i_m_msgArea{
- width: calc(100% - 50px - 10px);
- height: auto;
- margin: 0 5px;
- margin-top: 5px;
- display: flex;
- flex-direction: column;
- .dp_ml_i_m_ma_name{
- max-width: 100%;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- font-size: 14px;
- color: #333;
- font-weight: 500;
- margin-bottom: 5px;
- }
- .dp_ml_i_m_ma_textBlok{
- width: fit-content;
- display: block;
- background-color: #F8F9FA;
- padding: 10px;
- border: solid 1px #E9ECEF;
- border-radius: 4px;
- &>svg{
- width: 17px;
- height: 17px;
- }
- }
- }
- }
- }
- .right{
- justify-content: flex-end;
- .dp_ml_i_m_msgArea{
- align-items: flex-end;
- .dp_ml_i_m_ma_name{
- text-align: right;
- }
- .dp_ml_i_m_ma_textBlok{
- background-color: #3681FC !important;
- color: #fff;
- border-color: #69A1FD !important;
- }
- }
-
- }
- }
- .dp_inputArea {
- width: 100%;
- height: 60px;
- .dp_ia_bottom {
- width: 100%;
- height: 100%;
- display: flex;
- align-items: center;
- .dp_ia_b_left {
- width: calc(100% - 50px);
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- & > div {
- width: 100%;
- height: auto;
- display: flex;
- align-items: flex-end;
- border: solid 1px #ececec;
- border-radius: 6px;
- box-sizing: border-box;
- box-shadow: 0 0 10px #ececec;
- & > textarea {
- border: none;
- outline: none;
- width: 100%;
- height: 40px;
- background: none;
- text-indent: 0.5em;
- font-size: 14px;
- resize: none;
- box-sizing: border-box;
- padding: 10px 0;
- &::placeholder {
- color: #949494;
- }
- }
- }
- }
- .dp_ia_b_right {
- width: 45px;
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- margin-left: 5px;
- .dp_ia_b_r_btn {
- width: 40px;
- height: 40px;
- border-radius: 50%;
- cursor: pointer;
- & > svg {
- width: 40px;
- height: 40px;
- }
- }
- }
- }
- }
- .dialogueLoading {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background-color: rgba(255, 255, 255, 0.5);
- display: flex;
- align-items: center;
- justify-content: center;
- z-index: 1000;
- }
- }
- // 清空按钮区域样式
- .dp_clearArea {
- width: 100%;
- height: 40px;
- display: flex;
- align-items: center;
- justify-content: flex-start;
- padding: 0;
- border-bottom: 1px solid #f0f0f0;
-
- .dp_clear_btn {
- display: flex;
- align-items: center;
- gap: 8px;
- padding: 5px;
- border: .5px solid #e0eafb;
- border-radius: 6px;
- background-color: #fff;
- color: #fff;
- cursor: pointer;
- transition: all 0.3s ease;
- font-size: 14px;
-
- &:hover {
- transform: translateY(-1px);
- }
-
- .icon {
- width: 20px;
- height: 20px;
- flex-shrink: 0;
- }
-
- .loading-icon {
- animation: spin 1s linear infinite;
- }
-
- .clear-text {
- font-weight: 500;
- white-space: nowrap;
- }
- }
- }
- // 清空按钮样式
- .clear-btn {
- margin-right: 8px;
- background-color: #fff;
- border: 1px solid #ff4d4f;
- transition: all 0.3s ease;
-
- &:hover {
- background-color: #ff4d4f;
- transform: scale(1.05);
-
- .icon {
- fill: #fff;
- }
- }
-
- .loading-icon {
- animation: spin 1s linear infinite;
- }
- }
- // 清空聊天记录确认弹窗样式
- .clear-confirm {
- padding-top: 4px;
- &__title {
- font-size: 16px;
- font-weight: 600;
- margin-bottom: 12px;
- color: #333;
- }
- &__content {
- font-size: 14px;
- color: #666;
- line-height: 1.6;
- margin-bottom: 20px;
- }
- &__footer {
- display: flex;
- justify-content: flex-end;
- gap: 8px;
- }
- }
- @keyframes spin {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
- }
- </style>
|