.idea/inspectionProfiles/Project_Default.xml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/api/session.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/assets/images/u64.png | 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/assets/images/u69.png | 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/assets/images/u74.png | 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/assets/images/u767.png | 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/router/routes/modules/session.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/router/routes/modules/sessionRecords.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/session/sessionManager/components/addSession.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/session/sessionManager/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/sessionRecords/sessionRecordsManager/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
.idea/inspectionProfiles/Project_Default.xml
File was deleted src/api/session.ts
New file @@ -0,0 +1,29 @@ import axios from 'axios'; export interface ISessionListResult { code:number; msg:string; data:any; } // 会话列表 export function sessionListApi() { return axios.get<ISessionListResult>('/api/conversation/list?modeltype=localragflow'); } // 删除会话 export function deleteSessionApi(conversation_ids:string[]) { return axios.post<ISessionListResult>('/api/conversation/del?modeltype=localragflow',{conversation_ids}); } // 新增会话 export function addSessionApi(params:any) { return axios.get<ISessionListResult>('/api/getConId/kdwithai?platform=localragflow',{params}); } // 聊天 export function chatApi(data:{conversation_id:string,messages:string}) { return axios.post<ISessionListResult>('/api/tech/cloudminds/query?modeltype=localragflow',data); } // 获取会话详情 export function getSessionDetailsApi(conversation_id:string) { return axios.get<ISessionListResult>('/api/conversation/get?modeltype=localragflow',{params:{conversation_id}}); } src/assets/images/u64.png
src/assets/images/u69.png
src/assets/images/u74.png
src/assets/images/u767.png
src/router/routes/modules/session.ts
New file @@ -0,0 +1,28 @@ import { DEFAULT_LAYOUT } from '../base'; import { AppRouteRecordRaw } from '../types'; const session: AppRouteRecordRaw = { path: '/session', name: 'session', component: DEFAULT_LAYOUT, meta: { locale: 'menu.session', requiresAuth: true, icon: 'icon-voice', order: 3, hideInMenu:false }, children:[ { path:'sessionManager', name:"sessionManager", meta:{ requiresAuth: true, hideInMenu:true, roles: ['*'], activeMenu:'session' }, component:()=>import('@/views/session/sessionManager/index.vue'), } ] }; export default session; src/router/routes/modules/sessionRecords.ts
New file @@ -0,0 +1,28 @@ import { DEFAULT_LAYOUT } from '../base'; import { AppRouteRecordRaw } from '../types'; const sessionRecords: AppRouteRecordRaw = { path: '/sessionRecords', name: 'sessionRecords', component: DEFAULT_LAYOUT, meta: { locale: 'menu.sessionRecords', requiresAuth: true, icon: 'icon-list', order: 3, hideInMenu:false }, children:[ { path:'sessionRecordsManager', name:"sessionRecordsManager", meta:{ requiresAuth: true, hideInMenu:true, roles: ['*'], activeMenu:'sessionRecords' }, component:()=>import('@/views/sessionRecords/sessionRecordsManager/index.vue'), } ] }; export default sessionRecords; src/views/session/sessionManager/components/addSession.vue
New file @@ -0,0 +1,40 @@ <script setup lang="ts"> import { defineProps ,ref,defineEmits} from 'vue'; import { Message } from '@arco-design/web-vue'; import { addSessionApi }from '@/api/session'; const props=defineProps({ modalObj:Object }); const conversation_name=ref(''); const emit = defineEmits(['addSession']); const handleOk=async ()=>{ if(conversation_name.value){ const {code}=await addSessionApi({conversation_name:conversation_name.value}); if(code===200){ Message.success('添加成功'); emit('addSession') } }else{ Message.warning('会话名称不能为空'); } } </script> <template> <div> <a-modal v-model:visible="modalObj.add" @ok="handleOk" @cancel="modalObj.add=false"> <template #title> 新增会话 </template> <a-form> <a-form-item label="会话名称:"> <a-input placeholder="请输入会话名称" v-model="conversation_name" style="width: 80%"></a-input> </a-form-item> </a-form> </a-modal> </div> </template> <style scoped lang="scss"> </style> src/views/session/sessionManager/index.vue
New file @@ -0,0 +1,350 @@ <script setup lang="ts"> import { IconMoreVertical,IconQuestionCircleFill,IconPoweroff,IconCommon ,IconSend} from '@arco-design/web-vue/es/icon'; import img1 from '@/assets/images/u64.png' import img2 from '@/assets/images/u69.png' import img3 from '@/assets/images/u74.png'; import AddSession from './components/addSession.vue' import { ref, onMounted, computed, reactive, nextTick } from 'vue'; import { useUserStore,useAppStore } from '@/store'; import { sessionListApi, deleteSessionApi,getSessionDetailsApi,chatApi }from '@/api/session'; import { Message } from '@arco-design/web-vue'; const userStore = useUserStore(); const appStore = useAppStore(); const theme = computed(() => { return appStore.theme; }); const sessionList=ref([]);//会话列表 const sessionDetailList=ref([]);//根据会话id出来的会话详情 const activeSessionId=ref(''); const inputMsg=ref(''); const scrollbar = ref(null); const currIndex = ref(0) const displayedText = ref('');// 正在显示的文字 let timer: number|null = null; const streamStr=ref(''); const modalObj=reactive({ add:false }); //查询会话列表 const querySessionList = async () => { const { code, data } =await sessionListApi(); if (code === 200) { sessionList.value = data; if(Array.isArray(data)&&data.length>0){ activeSessionId.value=data[0].id; const res= await getSessionDetailsApi(data[0].id); if(res.code===200){ sessionDetailList.value=res.data.message; refreshScroll(); } } }else{ Message.warning('查询失败'); } }; //根据会话id删除会话 const deleteSession=async (session)=>{ const {code}=await deleteSessionApi([session.id]); if(code===200){ Message.success('删除成功'); querySessionList(); } }; // eslint-disable-next-line prettier/prettier // 新增会话之后刷新会话列表 const addSession=()=>{ querySessionList(); }; // 初始化数据 const initData =()=>{ querySessionList(); }; // 获取登录信息 const userName=computed(()=>{ return userStore.name; }); const avatar = computed(() => { return userStore.avatar; }); const refreshScroll=()=>{ nextTick(()=>{ const container = document.getElementById('home'); scrollbar.value.scrollTop(container.scrollHeight); }); }; // 根据会话id 查询会话详情 const querySessionDetail=async (session)=>{ activeSessionId.value=session.id; const {code,data}= await getSessionDetailsApi(session.id); if(code===200){ sessionDetailList.value=data.message; refreshScroll();//刷新滚动条位置 } }; const sendMessage= async ()=>{ if(inputMsg.value){ const {code,data} =await chatApi({conversation_id:activeSessionId.value,messages:inputMsg.value}); const res= await getSessionDetailsApi(activeSessionId.value); if(res.code===200){ sessionDetailList.value=res.data.message.map((item,index)=>{ if(index===res.data.message.length-1){ item.role='last'; streamStr.value=item.content; startDisplayStr(); } return item; }); refreshScroll(); } inputMsg.value=''; }else{ Message.warning('消息不能为空'); } }; onMounted(()=>{ initData(); }); //文字一个一个输出 const startDisplayStr = () => { if (timer) { clearTimeout(timer!); } const res = streamStr.value; // 将数组中的字符串拼接起来 if (currIndex.value < res.length) { displayedText.value += res[currIndex.value]; currIndex.value++; setTimeout(startDisplayStr, 100); } else { clearTimeout(timer!); timer = null } } </script> <template> <div class="container"> <AddSession :modalObj="modalObj" @addSession="addSession"></AddSession> <a-row :gutter="[0,0]"> <a-col :span="5"> <a-card class="left"> <template #cover> <a-button type="outline" class="card-btn" @click="modalObj.add=true"> +新建会话 </a-button> </template> <a-scrollbar class="left-list" style="height: 60vh;overflow-y: auto;"> <div class="item" :class="{isLeftActive:activeSessionId===session.id}" v-for="session in sessionList" :key="session.id" @click="querySessionDetail(session)"> <div class="item-left"> <IconQuestionCircleFill/> {{session.name}} </div> <div class="item-right"> <a-popover position="bottom"> <icon-more-vertical></icon-more-vertical> <template #content> <!--<div> <a-button type="text" size="mini">编辑</a-button> </div>--> <div> <a-button type="text" size="mini" @click="deleteSession(session)">删除</a-button> </div> </template> </a-popover> </div> </div> </a-scrollbar> <div class="left-bottom"> <div class="item"><IconCommon/> <span>向朋友推荐</span></div> <div class="item"><IconCommon/> <span>最新消息</span></div> <div class="item"><IconCommon/> <span>问题反馈</span></div> <div class="item"><IconCommon/> <span>常见问题</span></div> <div class="item"><IconPoweroff/> <span>退出</span></div> </div> </a-card> </a-col> <a-col :span="19"> <a-card class="right"> <div v-if="sessionDetailList.length===0"> <div class="right-title">ChatAI</div> <a-row justify="center" class="right-middle"> <a-col :span="5" class="item"> <p><a-image :src="img1" width="50px"></a-image></p> <p>示例</p> </a-col> <a-col :span="5" class="item"> <p><a-image :src="img2" width="50px"></a-image></p> <p>核心功能</p> </a-col> <a-col :span="5" class="item"> <p><a-image :src="img3" width="50px"></a-image></p> <p>局限性</p> </a-col> </a-row> <a-row justify="center" class="right-middle-list"> <a-col :span="6" class="item">“请用简单的术语解释量子计算”</a-col> <a-col :span="6" class="item">可以对历史对话进行记忆</a-col> <a-col :span="6" class="item">可能会出现错误的内容</a-col> </a-row> <a-row justify="center" class="right-middle-list"> <a-col :span="6" class="item">“提供一些庆祝10岁孩子生日的创意?”</a-col> <a-col :span="6" class="item">允许用户对答案进行修正</a-col> <a-col :span="6" class="item">可能会产生有害或虚假的内容</a-col> </a-row> <a-row justify="center" class="right-middle-list"> <a-col :span="6" class="item">“如何在Javascript中发出http请求?”</a-col> <a-col :span="6" class="item">可以接受或拒绝不恰当的训练</a-col> <a-col :span="6" class="item">对2023年以后得世界和事件了解有限</a-col> </a-row> </div> <a-scrollbar ref="scrollbar" id="home" v-else class="chat-list" style="width:90%;overflow:auto;height: 70vh;margin: 0px auto"> <div class="chat-item" v-for="sessionDetail in sessionDetailList"> <a-comment v-if="sessionDetail.role==='user'" avatar="https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/3ee5f13fb09879ecb5185e440cef6eb9.png~tplv-uwbnlip3yd-webp.webp" > <template #content> <div :class="{light:theme==='light'}">{{sessionDetail.content}}</div> </template> </a-comment> <a-comment v-else-if="sessionDetail.role==='assistant'" avatar="https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/9eeb1800d9b78349b24682c3518ac4a3.png~tplv-uwbnlip3yd-webp.webp" > <template #content> <a-card class="chat-item-answer" style="background-color: rgba(63, 64, 79, 1);"> <div :class="{light:theme==='light'}">{{sessionDetail.content}}</div> </a-card> </template> </a-comment> <a-comment v-else-if="sessionDetail.role==='last'" avatar="https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/9eeb1800d9b78349b24682c3518ac4a3.png~tplv-uwbnlip3yd-webp.webp" > <template #content> <a-card class="chat-item-answer" style="background-color: rgba(63, 64, 79, 1);"> <div :class="{light:theme==='light'}"> {{displayedText}}</div> </a-card> </template> </a-comment> </div> </a-scrollbar> <div class="bottom"> <div class="input"> <a-input v-model="inputMsg"> <template #suffix> <icon-send style="cursor: pointer" @click="sendMessage"/> </template> </a-input></div> <div class="text">内容由AI生成,仅供参考,请遵守《用户协议》、《个人信息保护规则》,基于ChatGPT大模型,版本:V3.3.0</div> </div> </a-card> </a-col> </a-row> </div> </template> <style scoped lang="scss"> .isLeftActive{ background-color:rgba(52, 53, 66, 1) ; } .light{ color: white !important; } .container{ background-color: #1d2129; .left,.right{ color: white; height: calc(100vh - 60px); border: 0px; } .left{ position: relative; background-color: rgba(30, 33, 34, 1); .card-btn { width: 90%; margin: 15px auto; border: 1px solid white; color: white; } .left-list{ .item{ display: flex; justify-content: space-between; padding-left: 30px; color: white; cursor: pointer; line-height: 40px; .item-right{ margin-right: 10px; } &:hover{ background-color:rgba(52, 53, 66, 1) ; } } } .left-bottom{ position: absolute; bottom: 30px; left:40px; .item{ text-align: left; color: white; font-size: 12px; line-height: 30px; } } } .right{ position: relative; background-color: rgba(52, 53, 66, 1); .right-title{ font-size: 30px; color: white; text-align: center; margin-top: 100px; } .right-middle{ margin-top: 40px; text-align: center; color: white; } .right-middle-list{ color: white; .item{ padding-top:15px; text-align: center; height: 50px; background-color: rgba(63, 64, 79, 1); margin:8px 20px; border-radius: 10px; } } .bottom{ width: 100%; position: absolute; bottom: 40px; left:20%; .input{ width: 60%; } .text{ font-size: 12px; color: lightgrey; line-height: 40px; } } .chat-list{ width: 90%; margin: 0px auto; .chat-item{ margin-top: 20px; .chat-item-answer{ color: white; } } } } } </style> src/views/sessionRecords/sessionRecordsManager/index.vue
New file @@ -0,0 +1,312 @@ <script setup lang="ts"> import { IconSearch,IconTiktokColor ,IconSend,IconClose} from '@arco-design/web-vue/es/icon'; import { useAppStore} from '@/store'; import {computed,ref,onMounted,reactive} from 'vue'; import {sessionListApi}from '@/api/session'; import { Message } from '@arco-design/web-vue'; import moment from 'moment'; import AddSession from '@/views/session/sessionManager/components/addSession.vue'; const sessionList=ref([]);//会话列表 const modalObj=reactive({add:false}); //查询会话列表 const querySessionList=async ()=>{ const {code,data} =await sessionListApi(); if(code===200){ sessionList.value=data; }else{ Message.warning('查询失败'); } }; //新增会话之后刷新会话列表 const addSession=()=>{ querySessionList(); }; onMounted(()=>{ querySessionList(); }); const appStore = useAppStore(); const theme = computed(() => { return appStore.theme; }); </script> <template> <div class="container"> <AddSession :modalObj="modalObj" @addSession="addSession"></AddSession> <a-card class="top-title">AI会话记录</a-card> <a-row :gutter="[5,5]" style="margin-top: 3px"> <a-col :span="4"> <a-card style="height: 60px"> <template #cover> <div style="display: flex;justify-content: space-between"> <a-button type="primary" shape="round" class="card-btn-1" @click="modalObj.add=true"> +新建会话 </a-button> <a-button type="default" shape="circle" class="card-btn-2"> <icon-search /> </a-button> </div> </template> </a-card> <a-card class="left"> <a-scrollbar class="left-list" style="height: calc(100vh - 160px);overflow-y: auto;overflow-x: hidden;"> <div class="item" v-for="session in sessionList"> <div class="text" :class="{light:theme==='dark'}">{{session.name}}</div> <div class="time">{{moment(new Date(session.create_time)).format('YYYY-MM-DD HH:mm:ss')}}</div> </div> </a-scrollbar> </a-card> </a-col> <a-col :span="15"> <a-card class="center"> <div class="center-title">智能问答</div> <div class="center-content"> 我可以理解和学习人类的语言,具备多轮对话的能力,现在和我开始交流吧~ </div> <div class="center-question"> <div class="center-question-left">试一试这样问我</div> <div class="center-question-right"> <a-button type="primary">换一换</a-button> </div> </div> <a-row justify="space-around" class="center-list"> <a-col :span="7" class="item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor>抖音带货脚本 </div> <div class="item-content" :class="{dark:theme==='dark'}"> 编写引人注目且具有说服力的、适用于产品的... </div> </a-col> <a-col :span="7" class="item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor>抖音带货脚本 </div> <div class="item-content" :class="{dark:theme==='dark'}"> 编写引人注目且具有说服力的、适用于产品的... </div> </a-col> <a-col :span="7" class="item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor>抖音带货脚本 </div> <div class="item-content" :class="{dark:theme==='dark'}"> 编写引人注目且具有说服力的、适用于产品的... </div> </a-col> <a-col :span="7" class="item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor>抖音带货脚本 </div> <div class="item-content" :class="{dark:theme==='dark'}"> 编写引人注目且具有说服力的、适用于产品的... </div> </a-col> <a-col :span="7" class="item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor>抖音带货脚本 </div> <div class="item-content" :class="{dark:theme==='dark'}"> 编写引人注目且具有说服力的、适用于产品的... </div> </a-col> <a-col :span="7" class="item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor>抖音带货脚本 </div> <div class="item-content" :class="{dark:theme==='dark'}"> 编写引人注目且具有说服力的、适用于产品的... </div> </a-col> </a-row> <div class="center-bottom"> <a-textarea style="height: 180px" placeholder="输入您想了解的内容,Shift+Enter换行" :max-length="500" allow-clear show-word-limit> </a-textarea> </div> </a-card> </a-col> <a-col :span="5"> <a-card class="right"> <div class="right-top"> <div class="right-title">数智库</div> <div class="right-btn"> <a-button type="outline" shape="circle" style="border: none;"> <icon-search /> </a-button> <a-button type="outline" shape="circle" style="border: none;margin-left: -10px"> <icon-close/> </a-button> </div> </div> <div class="right-tag"> <a-space> <a-button type="primary" shape="round" size="mini" class="btn">全部</a-button> <a-button type="outline" shape="round" size="mini" class="btn">文档创作</a-button> <a-button type="outline" shape="round" size="mini" class="btn">知识学习</a-button> </a-space> <a-space style="margin-top:10px "> <a-button type="outline" shape="round" size="mini" class="btn">效率提升</a-button> </a-space> </div> <div class="right-list"> <div class="right-item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor>抖音带货脚本 </div> <div class="item-content" :class="{dark:theme==='dark'}"> 编写引人注目且具有说服力的、适用于产品的... </div> </div> <div class="right-item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor>抖音带货脚本 </div> <div class="item-content" :class="{dark:theme==='dark'}"> 编写引人注目且具有说服力的、适用于产品的... </div> </div> <div class="right-item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor>抖音带货脚本 </div> <div class="item-content" :class="{dark:theme==='dark'}"> 编写引人注目且具有说服力的、适用于产品的... </div> </div> </div> </a-card> </a-col> </a-row> </div> </template> <style scoped lang="scss"> .light{ color: white !important; } .dark{ color: gray !important; } .container{ .top-title{ line-height:60px; font-size: 25px; font-family: 黑体; } .center,.right{ box-sizing: border-box; height: calc(100vh - 100px); } .left{ /* height: calc(100vh - 160px); overflow-y: auto; overflow-x: hidden;*/ border: 0px; .left-list{ .item{ cursor: pointer; .text,.time{ line-height: 30px; } .text{ color: black; } .time{ color: gray; font-size: 12px; } } } } .card-btn-1 { margin: 10px; width: 75%; } .card-btn-2 { margin:10px 10px; } .center{ position: relative; .center-title{ line-height:60px; font-size: 25px; font-family: 黑体; color: deepskyblue; } .center-content{ font-size: 14px; color: gray; } .center-question{ margin-top: 20px; display: flex; justify-content: space-between; .center-question-left{ margin-top: 5px; margin-left: 20px; } .center-question-right{ margin-right: 20px; } } .center-list{ margin-top: 10px; .item{ border-radius: 10px; margin-top: 10px; padding: 10px; height: 120px; background-color: lightcyan; .item-title{ text-align: center; line-height:40px; font-size: 20px; font-family: 黑体; color: black; } } } .center-bottom{ position: absolute; width: 90%; bottom: 70px; left:5%; } } .right{ .right-top{ display: flex; justify-content: space-between; .right-title{ font-size: 25px; font-family: 黑体; color: black; } .right-btn{ position: relative; left:20px; top:0px; } } .right-tag{ margin-top: 20px; } .right-list{ .right-item{ border-radius: 10px; margin-top: 10px; padding: 10px; height: 120px; background-color: lightcyan; .item-title{ text-align: center; line-height:40px; font-size: 20px; font-family: 黑体; color: black; } } } } } </style>