config/vite.config.dev.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/api/agentSession.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/api/authority.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/locale/en-US.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/locale/zh-CN.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/router/routes/modules/authority.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/authority/role/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/authority/role/locale/en-US.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/authority/role/locale/zh-CN.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/dmx/IntelligentAgent/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/sessionManager/components/addSession.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/sessionManager/components/agentSession.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/sessionManager/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/sessionManager/index1.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
config/vite.config.dev.ts
@@ -14,8 +14,8 @@ proxy: { '/base': { // target: 'http://aiotlink.com:8189', target: 'http://192.168.20.116:8089', // target: 'http://192.168.20.158:8089', //target: 'http://192.168.20.116:8089', target: 'http://192.168.20.158:8089', changeOrigin: true, ws: true, // rewrite: (path) => path.replace(new RegExp(`^/basic-api`), ''), @@ -23,17 +23,17 @@ // secure: false }, '/api': { target: 'http://192.168.20.116:8089', // target: 'http://192.168.20.158:8089', // target: 'http://aiotlink.com:8189', //target: 'http://192.168.20.116:8089', target: 'http://192.168.20.158:8089', changeOrigin: true, ws: true, // rewrite: (path) => path.replace(new RegExp(`^/basic-api`), ''), }, '/api/v1': { target: 'http://192.168.20.116:8089', // target: 'http://192.168.20.158:8089', // target: 'http://aiotlink.com:8189', //target: 'http://192.168.20.116:8089', target: 'http://192.168.20.158:8089', changeOrigin: true, ws: true, // rewrite: (path) => path.replace(new RegExp(`^/basic-api`), ''), src/api/agentSession.ts
New file @@ -0,0 +1,61 @@ import axios from 'axios'; export interface ISessionListResult { code: number; msg: string; data: any; } // 会话列表 // export function sessionListApi(dialog_id) { // return axios.get<ISessionListResult>( // "/api/conversation/list?modeltype=localragflow&dialogid=" + dialog_id // ); // } // 删除会话 // 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 getDialogListApi() { // return axios.get<ISessionListResult>('/api/dialog/list'); // } // 聊天 export function chatAgentApi(data) { return axios.post( '/api/v1/canvas/completion', data ); } // 获取会话详情 export function getAgentSessionDetailsApi(id: string) { return axios.get( '/api/v1/canvas/get/' + id, {} ); } // 设置 export function agentSetApi(data) { return axios.post( '/api/v1/canvas/set', data ); } // 重置 export function agentResetApi(data) { return axios.post( '/api/v1/canvas/reset', data ); } src/api/authority.ts
@@ -63,6 +63,44 @@ total: number; } export interface Role { roleId: string; createTime: string; updateTime: string; roleName: string; remark: string; iconCls: string; roleSort: string; status: string; roleKey: string; dataScope: string; dept: any; resources: any; } export function RoleList(params: Pagination) { return axios.get<Result<Role[]>>('/base/system/role/list', { params }); } export function RoleDelete(id) { return axios.delete('/base/system/role/' + id); } export function RoleEdit(role) { return axios.put('/base/system/role', { role }); } export function RoleAdd(role) { return axios.post('/base/system/role', { role }); } export function Rolestatus(roleId, status) { return axios.put('/base/system/role/changeStatus', { roleId: roleId, status: status, }); } export function UserList(params: Pagination) { return axios.get<Result<User[]>>('/base/system/user/list', { params }); } src/locale/en-US.ts
@@ -6,6 +6,7 @@ import localeUser from '@/views/authority/users/locale/en-US'; import localeOrganization from '@/views/authority/organization/locale/en-US'; import localeResource from '@/views/authority/resource/locale/en-US'; import localeRole from '@/views/authority/role/locale/en-US'; import localeMonitor from '@/views/dashboard/monitor/locale/en-US'; @@ -59,6 +60,7 @@ ...localeUser, ...localeOrganization, ...localeResource, ...localeRole, ...localeMonitor, ...localeSearchTable, src/locale/zh-CN.ts
@@ -6,6 +6,7 @@ import localeUser from '@/views/authority/users/locale/zh-CN'; import localeOrganization from '@/views/authority/organization/locale/zh-CN'; import localeResource from '@/views/authority/resource/locale/zh-CN'; import localeRole from '@/views/authority/role/locale/zh-CN'; import localeMonitor from '@/views/dashboard/monitor/locale/zh-CN'; @@ -64,6 +65,7 @@ ...localeUser, ...localeOrganization, ...localeResource, ...localeRole, ...localeMonitor, ...localeSearchTable, src/router/routes/modules/authority.ts
@@ -42,6 +42,16 @@ roles: ['*'], }, }, { path: 'role', name: 'role', component: () => import('@/views/authority/role/index.vue'), meta: { locale: 'menu.role.title', requiresAuth: true, roles: ['*'], }, }, ], }; src/views/authority/role/index.vue
New file @@ -0,0 +1,809 @@ <template> <div class="container"> <authheader :items="menuTips"></authheader> <a-card ref="account" class="general-card"> <div class="table-page-search-wrapper"> <div class="search-wrapper"> <div> <a-input v-model="formModel.name" :style="{ width: '320px' }" :placeholder="$t('请输入')" /> </div> <div> <a-button type="primary" @click="search" style="margin-right: 20px; margin-left: 10px" > <template #icon> <icon-search /> </template> {{ $t('searchTable.form.search') }} </a-button> <a-button @click="reset"> <template #icon> <icon-refresh /> </template> {{ $t('searchTable.form.reset') }} </a-button> </div> </div> <div class="search-wrapper"> <div> <a-space> <a-button type="primary" :align="'right'" @click="operation(0)" >+ 新建角色</a-button > </a-space> </div> <div class="wrapper-icon"> <a-tooltip :content="$t('searchTable.actions.refresh')"> <div class="action-icon" @click="search"> <icon-refresh size="18" /> </div> </a-tooltip> <a-dropdown @select="handleSelectDensity"> <a-tooltip :content="$t('searchTable.actions.density')"> <div class="action-icon"> <icon-line-height size="18" /> </div> </a-tooltip> <template #content> <a-doption v-for="item in densityList" :key="item.value" :value="item.value" :class="{ active: item.value === size }" > <span>{{ item.name }}</span> </a-doption> </template> </a-dropdown> </div> </div> </div> <a-table row-key="id" :loading="loading" :pagination="pagination" :columns="columns" :data="renderData" :bordered="false" :size="size" @page-change="onPageChange" > <template #index="{ rowIndex }"> {{ rowIndex + 1 + (pagination.current - 1) * pagination.pageSize }} </template> <template #dept="{ record }">{{ record.dept ? record.dept.deptName : '' }}</template> <template #status="{ record }"> <a-switch checked-value="1" unchecked-value="0" @change="statusChange(record.status, record)" v-model="record.status" /> </template> <template #operations="{ record }"> <a-space> <a-button type="outline" @click="operation(2, record)" >编辑</a-button > <a-popconfirm content="Are you sure you want to delete?" type="success" @ok="operation(3, record)" > <a-button type="outline" status="danger">删除</a-button> </a-popconfirm> <a-button type="dashed" status="warning" @click="operation(4, record)" >权限配置</a-button > <a-button status="success" @click="operation(5, record)" >部门配置</a-button > </a-space> </template> </a-table> </a-card> <a-modal v-model:visible="visible" :title="save" @cancel="handleCancel(1)" @ok="editHandleOk" > <a-form :model="editform"> <a-form-item field="roleName" label="角色名"> <a-input v-model="editform.roleName" /> </a-form-item> <a-form-item field="roleKey" label="权限字符串"> <a-input v-model="editform.roleKey" /> </a-form-item> <a-form-item field="remark" label="备注"> <a-input v-model="editform.remark" /> </a-form-item> </a-form> </a-modal> <a-modal width="50%" v-model:visible="deptvisible" title="部门配置" @cancel="handleCancel(2)" @ok="editDeptHandleOk" > <div :style="{ display: 'flex' }"> <a-card :style="{ 'width': '460px', 'height': '500px', 'overflow-y': 'auto' }" title="机构" hoverable > <a-tree class="tree-demo" v-model:checked-keys="checkedKeys" v-model:expanded-keys="expandKdys" :checkable="true" :data="treeData" :show-line="showLine" @check="onCheck" :fieldNames="{ key: 'deptId', title: 'deptName', children: 'children', }" :check-strictly="checkStrictly" > </a-tree> </a-card> <a-card class="card-demo" title="用户所属机构" hoverable> <a-space wrap> <a-tag v-for="(tag, index) of checkStrictly" :key="tag.deptId" @close="handleRemove(tag)" > {{ tag.deptName }} </a-tag> </a-space> </a-card> </div> </a-modal> <a-modal width="30%" v-model:visible="resourcevisible" title="权限配置" @cancel="handleCancel(3)" @ok="editResourceHandleOk" > <div :style="{ 'display': 'flex', 'flex-direction': 'column' }"> <a-tabs :style="{ 'width': '100%', 'height': '500px', 'overflow-y': 'auto' }" > <a-tab-pane key="1"> <template #title> <icon-calendar /> 菜单 </template> <a-tree class="tree-demo" v-model:checked-keys="checkedKeysMenu" v-model:expanded-keys="expandKdysMenu" :checkable="true" :data="treeDataMenu" :show-line="showLineMenu" @check="onCheckMenu" :fieldNames="{ key: 'menuId', title: 'menuName', children: 'children', }" :check-strictly="checkStrictlyMenu" > </a-tree> </a-tab-pane> <a-tab-pane key="2"> <template #title> <icon-clock-circle /> 知识库 </template> <a-space direction="vertical" size="large"> <a-checkbox-group v-model="checkedKeysKnowledge" direction="vertical" @change="onCheckKnowledge" > <a-checkbox v-for="(knowledg, index) of knowledgeList" :value="knowledg.id" :lable="knowledg.name" @change="onCheckKnowledge" > {{ knowledg.name }} </a-checkbox> </a-checkbox-group> </a-space> </a-tab-pane> <a-tab-pane key="3"> <template #title> <icon-user /> 智能体 </template> <a-checkbox-group v-model="checkedKeysDialog" direction="vertical" @change="onCheckDialog" > <a-checkbox v-for="(dialog, index) of DialogsList" :value="dialog.id" :lable="dialog.name" @change="onCheckDialog" > {{ dialog.name }} </a-checkbox> </a-checkbox-group> </a-tab-pane> </a-tabs> <a-card :style="{ 'width': '100%', 'height': '200px', 'overflow-y': 'auto', 'margin': '1px', }" class="card-demo" title="用户所有权限" hoverable > <a-space wrap> 菜单功能: <a-tag v-for="(tag, index) of checkStrictlyMenu" :key="tag.menuId" @close="handleMenuRemove(tag)" > {{ tag.menuName }} </a-tag> </a-space> <a-divider /> <a-space wrap> 知识库: <a-tag v-for="(tag, index) of checkStrictlyKnowledge" :key="tag.knowledgeId" @close="handleKnowledgeRemove(tag)" > {{ tag.knowledgeName }} </a-tag> </a-space> <a-divider /> <a-space wrap> 智能体: <a-tag v-for="(tag, index) of checkStrictlyDialog" :key="tag.dialogId" @close="handleDialogRemove(tag)" > {{ tag.dialogName }} </a-tag> </a-space> </a-card> </div> </a-modal> </div> </template> <script lang="ts" setup> import { computed, reactive, ref } from 'vue'; import { useI18n } from 'vue-i18n'; import useLoading from '@/hooks/loading'; import { Pagination } from '@/types/global'; import type { TableColumnData } from '@arco-design/web-vue/es/table/interface'; import { DialogList, KnowledgeList, OrganizationList, ResourceList, Role, RoleAdd, RoleDelete, RoleEdit, RoleList, Rolestatus, User, UserAdd, UserChangePwd, UserDelete, UserEdit, UserList, Userstatus } from "@/api/authority"; import { Modal } from '@arco-design/web-vue'; import Authheader from '@/views/authority/components/authheader.vue'; let treeData = ref([]); let checkedKeys = ref([]); let expandKdys = ref([]); let checkStrictly = ref([]); let treeDataMenu = ref([]); let checkedKeysMenu = ref([]); let expandKdysMenu = ref([]); let checkStrictlyMenu = ref([]); let checkedKeysKnowledge = ref([]); let checkStrictlyKnowledge = ref([]); let checkedKeysDialog = ref([]); let checkStrictlyDialog = ref([]); let knowledgeList = ref([]); let DialogsList = ref([]); let menuTips = ref(['权限管理', '角色']); type SizeProps = 'mini' | 'small' | 'medium' | 'large'; const account = ref(null); const generateFormModel = () => { return { name: '', }; }; let showLine = ref(true); const { loading, setLoading } = useLoading(true); const { t } = useI18n(); let save = ref('新增'); let renderData = ref<Role[]>([]); let formModel = ref(generateFormModel()); let editform = ref<Role>({ createTime: "", dataScope: "", iconCls: "", remark: "", roleId: "", roleKey: "", roleName: "", roleSort: "0", status: "", updateTime: "" }); let size = ref<SizeProps>('medium'); let visible = ref(false); let deptvisible = ref(false); let resourcevisible = ref(false); let selectRole = ref({}); const onCheck = (newCheckedKeys, event) => { let o = { deptId: event.node.deptId, deptName: event.node.deptName }; if (event.checked) { checkStrictly.value.push(o); } else { checkStrictly.value.forEach((val, idx, array) => { // val: 当前值 if (val.deptId == event.node.deptId) { checkStrictly.value.splice(idx, 1); return true; } }); } }; const onCheckMenu = (newCheckedKeys, event) => { let o = { menuId: event.node.menuId, menuName: event.node.menuName }; if (event.checked) { checkStrictlyMenu.value.push(o); } else { checkStrictlyMenu.value.forEach((val, idx, array) => { // val: 当前值 if (val.menuId == event.node.menuId) { checkStrictlyMenu.value.splice(idx, 1); return true; } }); } }; const onCheckKnowledge = (newCheckedKeys, event) => { let o = { knowledgeId: event.target.value, knowledgeName: event.target.labels[0].innerText, }; if (event.target.checked) { checkStrictlyKnowledge.value.push(o); } else { checkStrictlyKnowledge.value.forEach((val, idx, array) => { // val: 当前值 if (val.knowledgeId == event.target.value) { checkStrictlyKnowledge.value.splice(idx, 1); return true; } }); } }; const onCheckDialog = (newCheckedKeys, event) => { let o = { dialogId: event.target.value, dialogName: event.target.labels[0].innerText, }; if (event.target.checked) { checkStrictlyDialog.value.push(o); } else { checkStrictlyDialog.value.forEach((val, idx, array) => { // val: 当前值 if (val.dialogId == event.target.value) { checkStrictlyDialog.value.splice(idx, 1); return true; } }); } }; const handleRemove = (key) => { checkStrictly.value = checkStrictly.value.filter((tag) => tag !== key); }; const handleMenuRemove = (key) => { checkStrictlyMenu.value = checkStrictlyMenu.value.filter( (tag) => tag !== key ); }; const handleKnowledgeRemove = (key) => { checkStrictlyKnowledge.value = checkStrictlyKnowledge.value.filter( (tag) => tag !== key ); }; const handleDialogRemove = (key) => { checkStrictlyDialog.value = checkStrictlyDialog.value.filter( (tag) => tag !== key ); }; const basePagination: Pagination = { current: 1, pageSize: 15, }; const pagination = reactive({ ...basePagination, }); const densityList = computed(() => [ { name: t('searchTable.size.mini'), value: 'mini', }, { name: t('searchTable.size.small'), value: 'small', }, { name: t('searchTable.size.medium'), value: 'medium', }, { name: t('searchTable.size.large'), value: 'large', }, ]); const columns = computed<TableColumnData[]>(() => [ { title: t('序号'), dataIndex: 'index', slotName: 'index', }, { title: t('角色名'), dataIndex: 'roleName', }, { title: t('创建时间'), dataIndex: 'createTime', }, { title: t('权限字符串'), dataIndex: 'roleKey', slotName: 'roleKey', }, { title: t('状态'), dataIndex: 'status', slotName: 'status', }, { title: t('searchTable.columns.operations'), dataIndex: 'operations', slotName: 'operations', }, ]); const statusChange = async (value, record) => { await Rolestatus(record.roleId, value).then((res) => {}); }; const handleCancel = (type) => { if (type == 1) { visible.value = false; } if (type == 2) { deptvisible.value = false; } if (type == 2) { resourcevisible.value = false; } }; const editDeptHandleOk = async () => { let depts: Array = [], role: Role = { roleId: selectRole.value.roleId }; checkStrictly.value.forEach((val) => { depts.push(val.deptId); }); role.dept = depts; await RoleEdit(role).then((res) => { fetchData(); }); }; const editResourceHandleOk = async () => { let resources: Array = [], dialogs: Array = [], Knowledges: Array = [], role: Role = { roleId: selectRole.value.roleId }; checkStrictlyMenu.value.forEach((val) => { resources.push(val.menuId); }); role.resources = resources; checkStrictlyKnowledge.value.forEach((val) => { Knowledges.push(val.knowledgeId); }); role.knowledges = Knowledges; checkStrictlyDialog.value.forEach((val) => { dialogs.push(val.dialogId); }); role.dialogs = dialogs; await RoleEdit(role).then((res) => { fetchData(); }); }; const editHandleOk = async () => { if (editform.value.roleId.length > 0) { await RoleEdit({ ...editform.value, } as unknown as Role).then((res) => { fetchData(); }); } else { await RoleAdd({ ...editform.value, } as unknown as Role).then((res) => { fetchData(); }); } }; const operation = async (t, record) => { if (t == 0) { save.value = '新增'; visible.value = true; editform.value.roleName = ""; editform.value.roleKey = ""; editform.value.remark = ""; } //编辑 if (t == 2) { visible.value = true; save.value = '编辑'; editform.value.roleId=record.roleId editform.value.roleName = record.roleName; editform.value.roleKey = record.roleKey; editform.value.remark = record.remark; } //删除 if (t == 3) { await RoleDelete(record.roleId).then((res) => { if (res.code == 200) { fetchData(); } }); } //权限 if (t == 4) { resourcevisible.value = true; checkedKeysMenu.value = []; expandKdysMenu.value = []; checkStrictlyMenu.value = []; checkStrictlyKnowledge.value = []; checkedKeysKnowledge.value = []; checkStrictlyDialog.value = []; checkedKeysDialog.value = []; selectRole.value = record; if (record.resources) { record.resources.forEach((val) => { checkStrictlyMenu.value.push({ menuId: val.menuId, menuName: val.menuName, }); checkedKeysMenu.value.push(val.menuId); expandKdysMenu.value.push(val.menuId); }); } if (record.knowledges) { record.knowledges.forEach((val) => { checkStrictlyKnowledge.value.push({ knowledgeId: val.id, knowledgeName: val.name, }); checkedKeysKnowledge.value.push(val.id); }); } if (record.dialogs) { record.dialogs.forEach((val) => { checkStrictlyDialog.value.push({ dialogId: val.id, dialogName: val.name, }); checkedKeysDialog.value.push(val.id); }); } } //机构 if (t == 5) { deptvisible.value = true; checkedKeys.value = []; expandKdys.value = []; checkStrictly.value = []; selectRole.value = record; expandKdys.value.push('0'); if (record.dept) { record.dept.forEach((val) => { checkStrictly.value.push({ deptId: val.deptId, deptName: val.deptName, }); checkedKeys.value.push(val.deptId); expandKdys.value.push(val.deptId); }); } } }; const fetchData = async ( params: Pagination = { current: 1, pageSize: 20 } ) => { setLoading(true); try { await RoleList(params).then((res) => { renderData.value = res.rows; console.log(renderData); pagination.current = params.current; pagination.total = res.total; }); } catch (err) { // you can report use errorHandler or other } finally { setLoading(false); } }; const search = () => { fetchData({ ...basePagination, ...formModel.value, } as unknown as Pagination); }; const onPageChange = (current: number) => { fetchData({ ...basePagination, current }); }; const OrganizationData = async (key) => { await OrganizationList(key).then((res) => { treeData.value = [...res.rows]; }); }; const MenuData = async (key) => { await ResourceList(key).then((res) => { treeDataMenu.value = [...res.rows]; }); }; KnowledgeList().then((res) => { knowledgeList.value = res.rows; }); DialogList().then((res) => { DialogsList.value = res.rows; }); fetchData(); OrganizationData(''); MenuData(); const reset = () => { formModel.value = generateFormModel(); }; const handleSelectDensity = ( val: string | number | Record<string, any> | undefined, e: Event ) => { size.value = val as SizeProps; }; </script> <style scoped lang="less"> .card-demo { width: 460px; margin-left: 24px; transition-property: all; } .card-demo:hover { transform: translateY(-4px); } .table-page-search-wrapper { padding-top: 10px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; padding-bottom: 10px; border-bottom: 1px solid #e8e8e8; .search-wrapper { display: flex; .wrapper-icon { display: flex; align-items: center; margin-left: 40px; // margin-right: 40px; } } } </style> <style lang="less"> .ant-table-wrapper { .ant-table-tbody { tr { td { .ant-table-row-cell-break-word { .ant-table-column-sorter { display: none; } } } } } .search-wrapper { display: flex; .wrapper-icon { display: flex; align-items: center; margin-left: 40px; // margin-right: 40px; } } } </style> <style lang="less"> .ant-table-wrapper { .ant-table-tbody { tr { td { .ant-table-row-cell-break-word { .ant-table-column-sorter { display: none; } } } } } } </style> <style lang="less" scoped> .table-page-search-wrapper { .ant-form-inline { :deep(.ant-form-item) { display: flex; } } } </style> src/views/authority/role/locale/en-US.ts
New file @@ -0,0 +1,4 @@ export default { "menu.role.title": "Role", }; // export default { "menu.user.title": "Account" }; src/views/authority/role/locale/zh-CN.ts
New file @@ -0,0 +1,3 @@ export default { 'menu.role.title': '角色', }; src/views/dmx/IntelligentAgent/index.vue
@@ -132,7 +132,7 @@ > <icon-calendar-clock /> <span style="font-size: 12px;margin-left: 10px"> {{ parseTime(item.create_date) }} {{ moment(item.create_date).format('YYYY-MM-DD HH:mm:ss') }} </span> </div> <!-- <div--> @@ -215,6 +215,7 @@ import AgentConfig from '@/views/dmx/IntelligentAgent/components/agentConfig.vue'; import logo from '../../../assets/images/model.png'; import { documentHeight, parseTime } from "@/utils"; import moment from "moment"; // console.log(documentHeight,'高度'); let count = 5; const activeKey = ref(1); src/views/sessionManager/components/addSession.vue
@@ -85,7 +85,7 @@ > <template #title> 新增会话 </template> <a-form ref="formRef" :model="conversation" :rules="rules"> <a-form-item label="助手关联:" field="dialog_id" @submit="handleSubmit"> <a-form-item label="助手关联:" field="dialog_id" > <a-select style="width: 80%" v-model="conversation.dialog_id" src/views/sessionManager/components/agentSession.vue
New file @@ -0,0 +1,504 @@ <template> <!-- 内容--> <a-scrollbar ref="scrollbar" id="home" class="chat-list" style=" width: 90%; overflow: auto; height: calc(100vh - 380px); margin: 0px auto 20px; " > <div class="chat-item" v-for="sessionDetail in sessionDetailList"> <a-comment v-if="sessionDetail.role === 'user'"> <template #avatar> <img class="icon-user-jpg" src="../../../assets/images/icon-user.jpg" alt="本地图片" /> </template> <template #content> <div :class="{ chartUserText: theme === 'light' }" >{{ sessionDetail.content }} </div> </template> </a-comment> <a-comment v-else-if="sessionDetail.role === 'assistant'"> <template #avatar> <img class="icon-user-jpg" src="../../../assets/images/icon-chart.png" alt="本地图片" /> </template> <template #content> <a-card :class="{ chatItemAnswer: theme === 'light' }"> <div :class="{ light: theme === 'light' }" >{{ sessionDetail.content }} </div> </a-card> </template> </a-comment> <a-comment v-else-if="sessionDetail.role === 'last'"> <template #avatar> <img class="icon-user-jpg" src="../../../assets/images/icon-chart.png" alt="本地图片" /> </template> <template #content> <a-textarea readonly auto-size v-model="displayedText" :class="{ chatItemAnswer: theme === 'light' }" > </a-textarea> </template> </a-comment> </div> <!-- <div class="chartStart" v-if="isStart" @click="startChat" >停止生成</div > <div class="chartStart v-else" @click="stopChat">重新生成</div> --> </a-scrollbar> <div class="chat_bottom"> <div class="center-bottom"> <a-textarea v-model="inputMsg" @keydown.shift.enter="handleShiftEnter" @keydown.enter="sendMessage" placeholder="输入您想了解的内容,Shift+Enter换行,Enter发送" allow-clear show-word-limit :disabled="chatDis" :class="{ textItemAnswer: theme === 'dark' }" :auto-size="{ minRows: 12, maxRows: 5, }" /> <div class="btn-send"> <!-- <icon-send size="32" /> --> <a-button :disabled="chatDis" @click="sentClick" type="primary" style="border-radius: 24px" :loading="loading" size="large" >发送</a-button > </div> </div> </div> </template> <script setup lang="ts"> import { defineProps, ref, watch, defineEmits, onMounted, reactive, computed, nextTick } from "vue"; import { Message } from '@arco-design/web-vue'; import { useAppStore } from "@/store"; import { getAuthorization } from "@/utils/auth"; import { EventSourceParserStream } from 'eventsource-parser/stream'; import { agentResetApi, agentSetApi, getAgentSessionDetailsApi } from "@/api/agentSession"; const props = defineProps({ modalObj: Object, }); // const emit = defineEmits(['addSession']); const sessionDetailList = ref([]); //根据会话id出来的会话详情 const sessionList = ref([]); //会话列表 const modalObj = reactive({ }); const dialogId = ref(''); const chatDis = ref(false); const loading = ref(false); const currIndex = ref(0); const displayedText = ref(''); // 正在显示的文字 let timer: number | null = null; const streamStr = ref(''); const inputMsg = ref(''); const activeSessionId = ref(''); const fieldNames = { value: 'id', label: 'name' }; const agentObj = reactive({}); const agentList = ref([]); const selectValue = ref(''); const sectionList = ref({}); const appStore = useAppStore(); const theme = computed(() => { return appStore.theme; }); // 初始化页面 const initPage = async () => { agentSet(); agentReset(); agentCompletion(); queryAgentSessionDetail(agentObj.id); }; // 调用set方法 const agentSet = async () => { const res = await agentSetApi({ id: agentObj.id, title: agentObj.title, dsl: agentObj.dsl, }); if (res.code === 200) { // Message.success('修改成功'); } } // 调用reset方法 const agentReset = async () => { const res = await agentResetApi({ id: agentObj.id, }); if (res.code === 200) { // Message.success('修改成功'); } } // 调用completion方法 const agentCompletion = async () => { const response = await fetch( '/api/v1/canvas/completion', { method: 'POST', headers: { 'Authorization': getAuthorization(), 'Content-Type': 'application/json', }, body: JSON.stringify({ id: agentObj.id }), } ); const reader = response?.body ?.pipeThrough(new TextDecoderStream()) .pipeThrough(new EventSourceParserStream()) .getReader(); currIndex.value = 0; while (true) { const x = await reader?.read(); if (x) { const { done, value } = x; console.log(x, 999); try { const val = JSON.parse(value?.data || ''); const d = val?.data; if (typeof d !== 'boolean') { console.info('data:', d); streamStr.value = d.content; startDisplayStr(); } } catch (e) { console.warn(e); } if (done) { console.info('done'); displayedText.value = ''; queryAgentSessionDetail(agentObj.id); break; } } } chatDis.value = false; loading.value = false; inputMsg.value = ''; } // 调用get方法 const queryAgentSessionDetail = async (id) => { const { code, data } = await getAgentSessionDetailsApi(id); if (code == 0) { console.log(data,'会话详情'); sessionDetailList.value = data.dsl.messages; // refreshScroll(); //刷新滚动条位置 } }; const sentClick = () => { sendMessage('click'); }; const sendMessage = async (event) => { if (event.keyCode == 13 || event === 'click') { if (!event.shiftKey) { //只有enter没有shift,或进行你的其他逻辑 if (event !== 'click') { event.preventDefault(); // 阻止默认行为,即不换行 } chatDis.value = true; loading.value = true; if (!activeSessionId.value) { Message.warning('请选择会话'); chatDis.value = false; loading.value = false; return; } // if (displayedText.value) { // querySessionList(); // } if (inputMsg.value) { sessionDetailList.value.push({ content: inputMsg.value, role: 'user', }); sessionDetailList.value.push({ role: 'last' }); refreshScroll(); const response = await fetch( '/api/v1/canvas/completion', { method: 'POST', headers: { 'Authorization': getAuthorization(), 'Content-Type': 'application/json', }, body: JSON.stringify({ conversation_id: activeSessionId.value, messages: inputMsg.value, }), } ); const reader = response?.body ?.pipeThrough(new TextDecoderStream()) .pipeThrough(new EventSourceParserStream()) .getReader(); currIndex.value = 0; while (true) { const x = await reader?.read(); if (x) { const { done, value } = x; console.log(x, 999); try { const val = JSON.parse(value?.data || ''); const d = val?.data; if (typeof d !== 'boolean') { console.info('data:', d); streamStr.value = d.content; startDisplayStr(); } } catch (e) { console.warn(e); } if (done) { console.info('done'); displayedText.value = ''; queryAgentSessionDetail(agentObj.id); break; } } } chatDis.value = false; loading.value = false; inputMsg.value = ''; } else { Message.warning('消息不能为空'); chatDis.value = false; loading.value = false; } } } }; const handleShiftEnter = (event) => { event.preventDefault(); inputMsg.value += '\n'; }; //文字动态输出 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); refreshScroll(); } else { clearTimeout(timer!); timer = null; } }; const scrollbar = ref(null); const refreshScroll = () => { nextTick(() => { const container = document.getElementById('home'); scrollbar.value.scrollTop(container.scrollHeight); }); }; onMounted(() => { }); watch( () => props.modalObj, (newVal, oldVal) => { console.log(newVal,'监听变化'); Object.assign(agentObj, newVal); //调用agent初始化方法 if(JSON.stringify(newVal) != '{}'){ initPage(); } },{ immediate: true, deep: true } ); </script> <style scoped lang="scss"> .dark { color: gray !important; } .container { .chatItemAnswer { box-sizing: border-box; background: #f1f1f1; border-radius: 14px; .light { box-sizing: border-box; background: #f1f1f1; border-radius: 14px; } } .textItemAnswer { background-color: #373739; } .center { box-sizing: border-box; height: calc(100vh - 200px); 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; min-height: 120px; background-color: #e9f3ff; .item-content { color: #666; } .item-title { text-align: center; line-height: 40px; font-size: 20px; font-family: 黑体; color: #333; } } } .chartStart { color: #4955f5; cursor: pointer; font-family: PingFangSC-Medium; font-size: 12px; font-weight: 500; } .chat_bottom { display: flex; align-items: center; .center-bottom { // position: absolute; // width: 90%; // bottom: 20px; // left: 5%; background: #fff; border: 1px solid #00000014; border-radius: 24px; display: flex; flex: 1 1; flex-direction: column; overflow: hidden; position: relative; // padding-top:10px; :deep(.arco-textarea-wrapper) { border-radius: 24px; } .btn-send { position: absolute !important; right: 10px; bottom: 10px; z-index: 10; } :deep(.arco-btn-size-large) { height: 28px; width: 50px; } } :deep(.arco-textarea-wrapper) { padding-top: 5px; } } } .chat-item { padding: 10px 0; .chartUserText { font-weight: 600; font-size: 14px; color: #333; margin-top: 4px; } .icon-user-jpg { border: 1px solid #d9d9d9; } } } </style> src/views/sessionManager/index.vue
@@ -63,7 +63,8 @@ </a-scrollbar> </a-card> </a-col> <a-col :span="18"> <!-- 智能体会话--> <a-col :span="18" v-show="agentType == '1'"> <a-card class="center"> <!-- <div v-if="sessionDetailList.length === 0" @@ -249,6 +250,15 @@ </div> </a-card> </a-col> <a-col :span="18" v-show="agentType == '2'"> <a-card class="center"> <agentSession :modalObj="agentObj"></agentSession> </a-card> </a-col> <!-- <a-col :span="5"> <a-card class="right"> <div class="right-top"> @@ -325,6 +335,7 @@ import { EventSourceParserStream } from 'eventsource-parser/stream'; import moment from 'moment'; import AddSession from '@/views/sessionManager/components/addSession.vue'; import agentSession from '@/views/sessionManager/components/agentSession.vue'; import { chatApi, getDialogListApi, @@ -332,6 +343,8 @@ sessionListApi, } from '@/api/session'; import { getAuthorization } from '@/utils/auth'; import { queryCanvasList } from "@/api/Agent"; import { getAgentSessionDetailsApi } from "@/api/agentSession"; const sessionDetailList = ref([]); //根据会话id出来的会话详情 const sessionList = ref([]); //会话列表 @@ -339,6 +352,7 @@ const dialogId = ref(''); const chatDis = ref(false); const loading = ref(false); const agentType = ref('1'); const currIndex = ref(0); const displayedText = ref(''); // 正在显示的文字 @@ -348,6 +362,9 @@ const activeSessionId = ref(''); const fieldNames = { value: 'id', label: 'name' }; const dialogs = ref([]); const dialogObj = reactive({}); const agentObj = reactive({}); const agentList = ref([]); const selectValue = ref(''); const sectionList = ref({}); const DialogList = async () => { @@ -355,18 +372,71 @@ if (code === 200) { if (data) { selectValue.value = data[0].id; dialogs.value = data; querySessionList(); dialogs.value = data.map((item) => { return { ...item, type: 1,//智能体 } }); console.log(data, 'dialogs'); queryCanvas(); // querySessionList(); } } }; const queryCanvas = async (params = {}) => { try { const { data } = await queryCanvasList(params); console.log(data, 'agent'); agentList.value = data.map((item) => { return { ...item, name: item.title, type: 2,//agent } }); // 合并数组 dialogs.value = dialogs.value.concat(agentList.value); // 判断当前是智能体或agent // console.log(val, 'val'); if(dialogs.value.length>0){ dialogChange(dialogs.value[0].id); } } catch (err) { // you can report use errorHandler or other } finally { } }; const handleShiftEnter = (event) => { event.preventDefault(); inputMsg.value += '\n'; }; const dialogChange = (val) => { // 判断当前是智能体或agent // console.log(val, 'val'); dialogId.value = val; dialogs.value.forEach((item) => { if (item.id === val) { Object.assign(dialogObj, item) } }) console.log(dialogObj.type, 'dialogObj'); if (dialogObj.type == 1) { agentType.value = 1; querySessionList(); } else { agentType.value = 2; queryAgentSessionList() } // querySessionList(); }; const sentClick = () => { sendMessage('click'); @@ -463,6 +533,14 @@ refreshScroll(); //刷新滚动条位置 } }; const queryAgentSessionDetail = async (id) => { const { code, data } = await getAgentSessionDetailsApi(id); if (code == 0) { Object.assign(agentObj, data) // sessionDetailList.value = data.message; // refreshScroll(); //刷新滚动条位置 } }; const scrollbar = ref(null); const refreshScroll = () => { nextTick(() => { @@ -481,6 +559,20 @@ Message.warning('查询失败'); } }; // 查询会话列表 const queryAgentSessionList = async () => { // const { code, data } = await sessionListApi(selectValue.value); // if (code === 200) { // // } else { // Message.warning('查询失败'); // } queryAgentSessionDetail(selectValue.value); }; //新增会话之后刷新会话列表 const addSession = () => { querySessionList(); src/views/sessionManager/index1.vue
New file @@ -0,0 +1,844 @@ <template> <div class="container"> <AddSession :modalObj="modalObj" @addSession="addSession" :dialogId="dialogId" ></AddSession> <a-card class="top-title">AI会话记录</a-card> <a-row :gutter="[5, 5]" style="margin-top: 3px"> <a-col :span="6"> <a-card class="left-select"> <a-select v-model="selectValue" :options="dialogs" :field-names="fieldNames" @change="dialogChange" > </a-select> </a-card> <a-card style="height: 50px"> <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="text" 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 - 350px); overflow-y: auto; overflow-x: hidden; " > <div class="item left-list-item" v-for="session in sessionList" @click="querySessionDetail(session)" :class="{ isLeftActive: activeSessionId === session.id }" > <div class="text" :class="{ time: 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="18" v-show="agentType == '1'"> <a-card class="center"> <!-- <div v-if="sessionDetailList.length === 0" style=" width: 90%; overflow: auto; height: 65vh; margin: 0px auto 20px; " > <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> --> <a-scrollbar ref="scrollbar" id="home" class="chat-list" style=" width: 90%; overflow: auto; height: calc(100vh - 380px); margin: 0px auto 20px; " > <div class="chat-item" v-for="sessionDetail in sessionDetailList"> <a-comment v-if="sessionDetail.role === 'user'"> <template #avatar> <img class="icon-user-jpg" src="../../assets/images/icon-user.jpg" alt="本地图片" /> </template> <template #content> <div :class="{ chartUserText: theme === 'light' }" >{{ sessionDetail.content }} </div> </template> </a-comment> <a-comment v-else-if="sessionDetail.role === 'assistant'"> <template #avatar> <img class="icon-user-jpg" src="../../assets/images/icon-chart.png" alt="本地图片" /> </template> <template #content> <a-card :class="{ chatItemAnswer: theme === 'light' }"> <div :class="{ light: theme === 'light' }" >{{ sessionDetail.content }} </div> </a-card> </template> </a-comment> <a-comment v-else-if="sessionDetail.role === 'last'"> <template #avatar> <img class="icon-user-jpg" src="../../assets/images/icon-chart.png" alt="本地图片" /> </template> <template #content> <a-textarea readonly auto-size v-model="displayedText" :class="{ chatItemAnswer: theme === 'light' }" > </a-textarea> </template> </a-comment> </div> <!-- <div class="chartStart" v-if="isStart" @click="startChat" >停止生成</div > <div class="chartStart v-else" @click="stopChat">重新生成</div> --> </a-scrollbar> <div class="chat_bottom"> <div class="center-bottom"> <!-- <a-textarea v-model="inputMsg" @keydown.shift.enter="sendMessage" style="height: 180px" placeholder="输入您想了解的内容,Shift+Enter发送" :max-length="500" allow-clear show-word-limit > </a-textarea> --> <a-textarea v-model="inputMsg" @keydown.shift.enter="handleShiftEnter" @keydown.enter="sendMessage" placeholder="输入您想了解的内容,Shift+Enter换行,Enter发送" allow-clear show-word-limit :disabled="chatDis" :class="{ textItemAnswer: theme === 'dark' }" :auto-size="{ minRows: 12, maxRows: 5, }" /> <div class="btn-send"> <!-- <icon-send size="32" /> --> <a-button :disabled="chatDis" @click="sentClick" type="primary" style="border-radius: 24px" :loading="loading" size="large" >发送</a-button > </div> </div> </div> </a-card> </a-col> <a-col :span="18" v-show="agentType == '2'"> <a-card class="center"> <agentSession ></agentSession> </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-button type="primary" size="mini" class="btn">全部 </a-button> <a-button type="outline" size="mini" class="btn" >文档创作 </a-button> <a-button type="outline" size="mini" class="btn" >知识学习 </a-button> <a-button type="outline" size="mini" class="btn" >效率提升 </a-button> </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> <script setup lang="ts"> import { IconClose, IconSearch, IconTiktokColor, } from '@arco-design/web-vue/es/icon'; import { useAppStore, useUserStore } from '@/store'; import { computed, nextTick, onMounted, watch, reactive, ref } from 'vue'; import { Message } from '@arco-design/web-vue'; import { EventSourceParserStream } from 'eventsource-parser/stream'; import moment from 'moment'; import AddSession from '@/views/sessionManager/components/addSession.vue'; import agentSession from '@/views/sessionManager/components/agentSession.vue'; import { chatApi, getDialogListApi, getSessionDetailsApi, sessionListApi, } from '@/api/session'; import { getAuthorization } from '@/utils/auth'; import { queryCanvasList } from "@/api/Agent"; import { getAgentSessionDetailsApi } from "@/api/agentSession"; const sessionDetailList = ref([]); //根据会话id出来的会话详情 const sessionList = ref([]); //会话列表 const modalObj = reactive({ add: false }); const dialogId = ref(''); const chatDis = ref(false); const loading = ref(false); const agentType = ref('1'); const currIndex = ref(0); const displayedText = ref(''); // 正在显示的文字 let timer: number | null = null; const streamStr = ref(''); const inputMsg = ref(''); const activeSessionId = ref(''); const fieldNames = { value: 'id', label: 'name' }; const dialogs = ref([]); const dialogObj = reactive({}); const agentList = ref([]); const selectValue = ref(''); const sectionList = ref({}); const DialogList = async () => { const { code, data } = await getDialogListApi(); if (code === 200) { if (data) { selectValue.value = data[0].id; dialogs.value = data.map((item) => { return { ...item, type: 1,//智能体 } }); console.log(data, 'dialogs'); queryCanvas(); // querySessionList(); } } }; const queryCanvas = async (params = {}) => { try { const { data } = await queryCanvasList(params); console.log(data, 'agent'); agentList.value = data.map((item) => { return { ...item, name: item.title, type: 2,//agent } }); // 合并数组 dialogs.value = dialogs.value.concat(agentList.value); // 判断当前是智能体或agent // console.log(val, 'val'); if(dialogs.value.length>0){ dialogChange(dialogs.value[0].id); } } catch (err) { // you can report use errorHandler or other } finally { } }; const handleShiftEnter = (event) => { event.preventDefault(); inputMsg.value += '\n'; }; const dialogChange = (val) => { // 判断当前是智能体或agent // console.log(val, 'val'); dialogId.value = val; dialogs.value.forEach((item) => { if (item.id === val) { Object.assign(dialogObj, item) } }) console.log(dialogObj.type, 'dialogObj'); if (dialogObj.type == 1) { agentType.value = 1; querySessionList(); } else { agentType.value = 2; queryAgentSessionList() } // querySessionList(); }; const sentClick = () => { sendMessage('click'); }; const sendMessage = async (event) => { if (event.keyCode == 13 || event === 'click') { if (!event.shiftKey) { //只有enter没有shift,或进行你的其他逻辑 if (event !== 'click') { event.preventDefault(); // 阻止默认行为,即不换行 } chatDis.value = true; loading.value = true; if (!activeSessionId.value) { Message.warning('请选择会话'); chatDis.value = false; loading.value = false; return; } // if (displayedText.value) { // querySessionList(); // } if (inputMsg.value) { sessionDetailList.value.push({ content: inputMsg.value, role: 'user', }); sessionDetailList.value.push({ role: 'last' }); refreshScroll(); const response = await fetch( '/api/tech/cloudminds/query?modeltype=localragflow', { method: 'POST', headers: { 'Authorization': getAuthorization(), 'Content-Type': 'application/json', }, body: JSON.stringify({ conversation_id: activeSessionId.value, messages: inputMsg.value, }), } ); const reader = response?.body ?.pipeThrough(new TextDecoderStream()) .pipeThrough(new EventSourceParserStream()) .getReader(); currIndex.value = 0; while (true) { const x = await reader?.read(); if (x) { const { done, value } = x; console.log(x, 999); try { const val = JSON.parse(value?.data || ''); const d = val?.data; if (typeof d !== 'boolean') { console.info('data:', d); streamStr.value = d.content; startDisplayStr(); } } catch (e) { console.warn(e); } if (done) { console.info('done'); displayedText.value = ''; querySessionDetail(sectionList.value); break; } } } chatDis.value = false; loading.value = false; inputMsg.value = ''; } else { Message.warning('消息不能为空'); chatDis.value = false; loading.value = false; } } } }; const querySessionDetail = async (session) => { sectionList.value = session; activeSessionId.value = session.id; const { code, data } = await getSessionDetailsApi(session.id); if (code === 200) { sessionDetailList.value = data.message; refreshScroll(); //刷新滚动条位置 } }; const queryAgentSessionDetail = async (id) => { const { code, data } = await getAgentSessionDetailsApi(id); if (code === 200) { // sessionDetailList.value = data.message; // refreshScroll(); //刷新滚动条位置 } }; const scrollbar = ref(null); const refreshScroll = () => { nextTick(() => { const container = document.getElementById('home'); scrollbar.value.scrollTop(container.scrollHeight); }); }; // 查询会话列表 const querySessionList = async () => { const { code, data } = await sessionListApi(selectValue.value); if (code === 200) { sessionList.value = data; activeSessionId.value = data[0].id; //默认选择第一个 querySessionDetail(data[0]); } else { Message.warning('查询失败'); } }; // 查询会话列表 const queryAgentSessionList = async () => { // const { code, data } = await sessionListApi(selectValue.value); // if (code === 200) { // // } else { // Message.warning('查询失败'); // } queryAgentSessionDetail(selectValue.value); }; //新增会话之后刷新会话列表 const addSession = () => { querySessionList(); }; onMounted(() => { DialogList(); }); const appStore = useAppStore(); const theme = computed(() => { return appStore.theme; }); //文字动态输出 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); refreshScroll(); } else { clearTimeout(timer!); timer = null; } }; </script> <style scoped lang="scss"> .isLeftActive { background-color: #ededf5; } .left-list-item { margin-bottom: 2px; } .left-list-item:hover { background-color: #ededf5; } .dark { color: gray !important; } .container { .top-title { line-height: 32px; font-size: 16px; color: #333; } .left-select { :deep(.arco-select-view-single) { border-radius: 5px; } } .center, .right { box-sizing: border-box; height: calc(100vh - 200px); } .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; padding-left: 10px; } .time { color: gray; font-size: 12px; padding-left: 10px; } } } } .card-btn-1 { margin: 8px 15px; width: 100%; border-radius: 5px; } .card-btn-2 { margin: 10px 10px; } .chatItemAnswer { box-sizing: border-box; background: #f1f1f1; border-radius: 14px; .light { box-sizing: border-box; background: #f1f1f1; border-radius: 14px; } } .textItemAnswer { background-color: #373739; } .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; min-height: 120px; background-color: #e9f3ff; .item-content { color: #666; } .item-title { text-align: center; line-height: 40px; font-size: 20px; font-family: 黑体; color: #333; } } } .chartStart { color: #4955f5; cursor: pointer; font-family: PingFangSC-Medium; font-size: 12px; font-weight: 500; } .chat_bottom { display: flex; align-items: center; .center-bottom { // position: absolute; // width: 90%; // bottom: 20px; // left: 5%; background: #fff; border: 1px solid #00000014; border-radius: 24px; display: flex; flex: 1 1; flex-direction: column; overflow: hidden; position: relative; // padding-top:10px; :deep(.arco-textarea-wrapper) { border-radius: 24px; } .btn-send { position: absolute !important; right: 10px; bottom: 10px; z-index: 10; } :deep(.arco-btn-size-large) { height: 28px; width: 50px; } } :deep(.arco-textarea-wrapper) { padding-top: 5px; } } } .chat-item { padding: 10px 0; .chartUserText { font-weight: 600; font-size: 14px; color: #333; margin-top: 4px; } .icon-user-jpg { border: 1px solid #d9d9d9; } } .right { .right-top { display: flex; justify-content: space-between; .right-title { font-size: 25px; color: black; } .right-btn { position: relative; left: 20px; top: 0px; } } .right-tag { margin-top: 20px; display: flex; justify-content: space-between; flex-wrap: wrap; :deep(.arco-btn-primary) { margin-bottom: 10px !important; &:hover { background-color: #e9f3ff; color: rgb(22, 93, 255); } } } .right-list { .right-item { border-radius: 10px; margin-top: 10px; padding: 10px; min-height: 120px; background-color: #e9f3ff; .item-title { text-align: center; line-height: 40px; font-size: 20px; font-family: 黑体; color: #333; } } } } } </style>