feat: chat

This commit is contained in:
wangdan-fit2cloud 2025-07-08 20:50:19 +08:00
parent 6866ff03b3
commit edca190a06
10 changed files with 447 additions and 468 deletions

View File

@ -1,23 +1,21 @@
import {Result} from '@/request/Result'
import {get, post, del, put, exportFile, exportExcel} from '@/request/index'
import {type Ref} from 'vue'
import type {pageRequest} from '@/api/type/common'
import type {knowledgeData} from '@/api/type/knowledge'
import { Result } from '@/request/Result'
import { get, post, del, put, exportFile, exportExcel } from '@/request/index'
import { type Ref } from 'vue'
import type { pageRequest } from '@/api/type/common'
import type { knowledgeData } from '@/api/type/knowledge'
import useStore from '@/stores'
const prefix = '/system/shared'
const prefix_workspace: any = {_value: 'workspace/'}
const prefix_workspace: any = { _value: 'workspace/' }
Object.defineProperty(prefix_workspace, 'value', {
get: function () {
const {user} = useStore()
const { user } = useStore()
return this._value + user.getWorkspaceId()
},
})
const getKnowledgeList: (loading?: Ref<boolean>) => Promise<Result<Array<any>>> = (
loading,
) => {
const getKnowledgeList: (loading?: Ref<boolean>) => Promise<Result<Array<any>>> = (loading) => {
return get(`${prefix}/${prefix_workspace.value}/knowledge`, {}, loading)
}
@ -75,9 +73,32 @@ const getDocumentDetail: (
document_id: string,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (knowledge_id, document_id, loading) => {
return get(`${prefix}/${prefix_workspace.value}/knowledge/${knowledge_id}/document/${document_id}`,
return get(
`${prefix}/${prefix_workspace.value}/knowledge/${knowledge_id}/document/${document_id}`,
{},
loading,)
loading,
)
}
/**
*
* @param knowledge_id,
* query {
"content": "string",
}
*/
const getProblemsPage: (
knowledge_id: string,
page: pageRequest,
param: any,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (knowledge_id, page, param, loading) => {
return get(
`${prefix}/${prefix_workspace.value}/knowledge/${knowledge_id}/problem/${page.current_page}/${page.page_size}`,
param,
loading,
)
}
/**
@ -102,17 +123,14 @@ const getParagraphPage: (
)
}
const getModelList: (
const getModelList: (param: any, loading?: Ref<boolean>) => Promise<Result<Array<any>>> = (
param: any,
loading?: Ref<boolean>,
) => Promise<Result<Array<any>>> = (param: any, loading) => {
loading,
) => {
return get(`${prefix}/${prefix_workspace.value}/model`, param, loading)
}
const getToolList: (loading?: Ref<boolean>) => Promise<Result<Array<any>>> = (
loading,
) => {
const getToolList: (loading?: Ref<boolean>) => Promise<Result<Array<any>>> = (loading) => {
return get(`${prefix}/${prefix_workspace.value}/tool`, {}, loading)
}
@ -121,17 +139,22 @@ const getToolListPage: (
param?: any,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (page, param, loading) => {
return get(`${prefix}/${prefix_workspace.value}/tool/${page.current_page}/${page.page_size}`, param, loading)
return get(
`${prefix}/${prefix_workspace.value}/tool/${page.current_page}/${page.page_size}`,
param,
loading,
)
}
export default {
getKnowledgeList,
getKnowledgeListPage,
getKnowledgeDetail,
getProblemsPage,
getDocumentPage,
getDocumentDetail,
getParagraphPage,
getModelList,
getToolList,
getToolListPage
getToolListPage,
}

View File

@ -4,9 +4,18 @@
<div class="layout-container__left_content">
<slot name="left"></slot>
</div>
<el-tooltip :content="isCollapse ? $t('common.expand') : $t('common.collapse')" placement="right">
<el-button v-if="props.showCollapse" class="collapse" size="small" circle @click="isCollapse = !isCollapse"
:icon="isCollapse ? 'ArrowRightBold' : 'ArrowLeftBold'" />
<el-tooltip
:content="isCollapse ? $t('common.expand') : $t('common.collapse')"
placement="right"
>
<el-button
v-if="props.showCollapse"
class="collapse"
size="small"
circle
@click="isCollapse = !isCollapse"
:icon="isCollapse ? 'ArrowRightBold' : 'ArrowLeftBold'"
/>
</el-tooltip>
</div>
<div class="layout-container__right">
@ -22,7 +31,7 @@ const slots = useSlots()
const props = defineProps({
header: String || null,
backTo: String,
showCollapse: Boolean
showCollapse: Boolean,
})
const isCollapse = ref(false)
@ -47,7 +56,7 @@ const showBack = computed(() => {
position: absolute;
top: 36px;
right: -15px;
box-shadow: 0px 5px 10px 0px #1f23291a;
box-shadow: 0px 5px 10px 0px rgba(31, 35, 41, 0.1);
z-index: 1;
}

View File

@ -20,7 +20,7 @@ export default {
downloadError: 'Download failed',
noModel: 'Model does not exist in Ollama',
},
model: {
modelType: {
allModel: 'All Models',
publicModel: 'Public Models',
privateModel: 'Private Models',

View File

@ -18,7 +18,7 @@ export default {
downloadError: '下載失敗',
noModel: '模型在Ollama不存在',
},
model: {
modelType: {
allModel: '全部模型',
publicModel: '公有模型',
privateModel: '私有模型',

View File

@ -0,0 +1,263 @@
<template>
<div class="history-component h-full">
<el-menu
:default-active="currentChatId"
:collapse="isPcCollapse"
collapse-transition
popper-class="chat-pc-popper"
class="h-full"
>
<div style="padding: 16px 18px 0 18px">
<div class="flex align-center mb-16">
<div class="flex mr-8">
<el-avatar
v-if="isAppIcon(applicationDetail?.icon)"
shape="square"
:size="32"
style="background: none"
>
<img :src="applicationDetail?.icon" alt="" />
</el-avatar>
<LogoIcon v-else height="32px" />
</div>
<h4 v-show="!isPcCollapse">{{ applicationDetail?.name }}</h4>
</div>
<el-button
type="primary"
plain
v-show="!isPcCollapse"
class="add-button primary medium w-full"
@click="newChat"
>
<AppIcon iconName="app-create-chat"></AppIcon>
<span class="ml-4">{{ $t('chat.createChat') }}</span>
</el-button>
<div v-show="!isPcCollapse" class="flex-between p-8 pb-0 color-secondary mt-8">
<span>{{ $t('chat.history') }}</span>
<el-button text>
<el-icon>
<Delete />
</el-icon>
</el-button>
</div>
</div>
<div v-show="!isPcCollapse" class="left-height">
<el-scrollbar>
<div class="p-16 pt-0">
<common-list
:data="chatLogData"
class="mt-8"
v-loading="leftLoading"
:defaultActive="currentChatId"
@click="handleClickList"
@mouseenter="mouseenter"
@mouseleave="mouseId = ''"
>
<template #default="{ row }">
<div class="flex-between">
<span :title="row.abstract">
{{ row.abstract }}
</span>
<div @click.stop v-show="mouseId === row.id && row.id !== 'new'">
<el-dropdown trigger="click" :teleported="false">
<el-button text>
<el-icon><MoreFilled /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click.stop="editLogTitle(row)">
<el-icon><EditPen /></el-icon>
{{ $t('common.edit') }}
</el-dropdown-item>
<el-dropdown-item @click.stop="deleteChatLog(row)">
<el-icon><Delete /></el-icon>
{{ $t('common.delete') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</template>
<template #empty>
<div class="text-center">
<el-text type="info">{{ $t('chat.noHistory') }}</el-text>
</div>
</template>
</common-list>
</div>
<div v-if="chatLogData?.length" class="text-center lighter color-secondary">
<span>{{ $t('chat.only20history') }}</span>
</div>
</el-scrollbar>
</div>
<el-menu-item index="1" v-show="isPcCollapse" @click="newChat">
<AppIcon iconName="app-create-chat"></AppIcon>
<template #title>{{ $t('chat.createChat') }}</template>
</el-menu-item>
<el-sub-menu v-show="isPcCollapse" index="2">
<template #title>
<AppIcon iconName="app-history-outlined" />
</template>
<el-menu-item-group v-loading="leftLoading">
<template #title
><span>{{ $t('chat.history') }}</span></template
>
<el-menu-item
v-for="row in chatLogData"
:index="row.id"
:key="row.id"
@click="handleClickList(row)"
>
<div class="flex-between w-full lighter">
<span :title="row.abstract">
{{ row.abstract }}
</span>
<div @click.stop class="flex" v-show="mouseId === row.id && row.id !== 'new'">
<el-dropdown trigger="click" :teleported="false">
<el-icon class="rotate-90 mt-4">
<MoreFilled />
</el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click.stop="editLogTitle(row)">
<el-icon>
<EditPen />
</el-icon>
{{ $t('common.edit') }}
</el-dropdown-item>
<el-dropdown-item @click.stop="deleteChatLog(row)">
<el-icon>
<Delete />
</el-icon>
{{ $t('common.delete') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</el-menu-item>
</el-menu-item-group>
<div v-if="!chatLogData?.length" class="text-center">
<el-text type="info">{{ $t('chat.noHistory') }}</el-text>
</div>
</el-sub-menu>
</el-menu>
<slot></slot>
<EditTitleDialog ref="EditTitleDialogRef" @refresh="refreshFieldTitle" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { isAppIcon } from '@/utils/common'
import EditTitleDialog from './EditTitleDialog.vue'
import useStore from '@/stores'
const { common, chatUser } = useStore()
const props = defineProps<{
applicationDetail: any
chatLogData: any[]
leftLoading: boolean
currentChatId: string
isPcCollapse: boolean
}>()
const emit = defineEmits(['newChat', 'clickLog', 'deleteLog', 'refreshFieldTitle'])
const EditTitleDialogRef = ref()
const mouseId = ref('')
function mouseenter(row: any) {
mouseId.value = row.id
}
const newChat = () => {
emit('newChat')
}
const handleClickList = (item: any) => {
emit('clickLog', item)
}
const deleteChatLog = (row: any) => {
emit('deleteLog', row)
}
function editLogTitle(row: any) {
EditTitleDialogRef.value.open(row, props.applicationDetail.id)
}
function refreshFieldTitle() {
emit('refreshFieldTitle')
}
</script>
<style lang="scss" scoped>
.history-component {
display: flex;
flex-direction: column;
border-right: 1px solid var(--el-menu-border-color);
background:
linear-gradient(187.61deg, rgba(235, 241, 255, 0.5) 39.6%, rgba(231, 249, 255, 0.5) 94.3%),
#eef1f4;
.el-menu {
background: none;
border: none;
&:not(.el-menu--collapse) {
width: 280px;
}
}
.left-height {
height: calc(100vh - 210px);
}
:deep(.common-list li.active) {
background-color: #ffffff;
font-weight: 500;
color: var(--el-text-color-primary);
&:hover {
background-color: #ffffff;
}
}
.add-button {
border: 1px solid var(--el-color-primary-light-6);
background-color: var(--el-color-primary-light-9);
color: var(--el-color-primary);
}
}
</style>
<style lang="scss">
.chat-pc-popper {
background:
linear-gradient(187.61deg, rgba(235, 241, 255, 0.5) 39.6%, rgba(231, 249, 255, 0.5) 94.3%),
#eef1f4 !important;
.el-menu {
background: transparent;
}
.el-menu-item-group__title {
padding-bottom: 16px;
font-weight: 500;
color: var(--app-text-color-secondary);
}
.el-menu-item {
border-radius: 6px;
height: 40px;
margin: 0 8px;
padding-left: 8px;
padding-right: 8px;
&:hover {
background-color: #1f23291a;
}
&.is-active {
background-color: #ffffff;
color: var(--el-text-color-primary);
& > div {
font-weight: 500;
}
}
}
}
</style>

View File

@ -4,7 +4,12 @@
<div>
<div class="flex align-center mb-16">
<div class="flex mr-8">
<el-avatar v-if="isAppIcon(applicationDetail?.icon)" shape="square" :size="32" style="background: none">
<el-avatar
v-if="isAppIcon(applicationDetail?.icon)"
shape="square"
:size="32"
style="background: none"
>
<img :src="applicationDetail?.icon" alt="" />
</el-avatar>
<LogoIcon v-else height="32px" />
@ -15,20 +20,36 @@
<AppIcon iconName="app-create-chat"></AppIcon>
<span class="ml-4">{{ $t('chat.createChat') }}</span>
</el-button>
<p class="mt-20 mb-8">{{ $t('chat.history') }}</p>
<p class="mt-20 mb-8 color-secondary">{{ $t('chat.history') }}</p>
</div>
<div class="left-height pt-0">
<el-scrollbar>
<div>
<common-list :style="{ '--el-color-primary': applicationDetail?.custom_theme?.theme_color }"
:data="chatLogData" v-loading="leftLoading" :defaultActive="currentChatId" @click="handleClickList"
@mouseenter="mouseenter" @mouseleave="mouseId = ''">
<common-list
:style="{ '--el-color-primary': applicationDetail?.custom_theme?.theme_color }"
:data="chatLogData"
v-loading="leftLoading"
:defaultActive="currentChatId"
@click="handleClickList"
@mouseenter="mouseenter"
@mouseleave="mouseId = ''"
>
<template #default="{ row }">
<div class="flex-between">
<ReadWrite @change="(val: string) => updateChatName(val, row)" :data="row.abstract" trigger="manual"
:write="row.writeStatus" @close="() => (row.writeStatus = false)" :maxlength="1024" />
<div @click.stop v-if="mouseId === row.id && row.id !== 'new' && !row.writeStatus" class="flex">
<ReadWrite
@change="(val: string) => updateChatName(val, row)"
:data="row.abstract"
trigger="manual"
:write="row.writeStatus"
@close="() => (row.writeStatus = false)"
:maxlength="1024"
/>
<div
@click.stop
v-if="mouseId === row.id && row.id !== 'new' && !row.writeStatus"
class="flex"
>
<el-button style="padding: 0" link @click.stop="() => (row.writeStatus = true)">
<el-icon>
<EditPen />
@ -55,7 +76,10 @@
</el-scrollbar>
</div>
<div class="flex align-center user-info" @click="toUserCenter">
<el-avatar :size="32" :class="`${!chatUser.chat_profile?.authentication || chatUser.chat_profile.authentication_type === 'password' ? 'cursor-default' : ''}`">
<el-avatar
:size="32"
:class="`${!chatUser.chat_profile?.authentication || chatUser.chat_profile.authentication_type === 'password' ? 'cursor-default' : ''}`"
>
<img src="@/assets/user-icon.svg" style="width: 54%" alt="" />
</el-avatar>
<span v-if="chatUser.chat_profile?.authentication" class="ml-8 color-text-primary">
@ -86,11 +110,7 @@ const props = defineProps<{
currentChatId: string
}>()
const emit = defineEmits([
'newChat',
'clickLog',
'deleteLog',
])
const emit = defineEmits(['newChat', 'clickLog', 'deleteLog'])
const { chatUser, chatLog } = useStore()
@ -118,7 +138,11 @@ const mouseenter = (row: any) => {
const userCenterDrawerShow = ref(false)
function toUserCenter() {
if (!chatUser.chat_profile?.authentication || chatUser.chat_profile.authentication_type === 'password') return
if (
!chatUser.chat_profile?.authentication ||
chatUser.chat_profile.authentication_type === 'password'
)
return
userCenterDrawerShow.value = true
}
</script>
@ -132,16 +156,11 @@ function toUserCenter() {
#eef1f4;
overflow: hidden;
.add-button {
border: 1px solid var(--el-color-primary);
background-color: #3370FF1A;
color: #3370FF;
font-weight: 500;
&:hover {
background-color: #3370FF33;
}
}
// .add-button {
// border: 1px solid var(--el-color-primary);
// color: var(--el-color-primary);
// font-weight: 500;
// }
.left-height {
height: calc(100vh - 212px);

View File

@ -6,209 +6,80 @@
:style="{
'--el-color-primary': applicationDetail?.custom_theme?.theme_color,
'--el-color-primary-light-9': hexToRgba(applicationDetail?.custom_theme?.theme_color, 0.1),
'--el-color-primary-light-6': hexToRgba(applicationDetail?.custom_theme?.theme_color, 0.4),
}"
>
<div class="flex h-full w-full">
<div class="chat-pc__left">
<el-menu
class="w-full h-full"
:default-active="currentChatId"
:collapse="isPcCollapse"
collapse-transition
popper-class="chat-pc-popper"
<history-component
:application-detail="applicationDetail"
:chat-log-data="chatLogData"
:left-loading="left_loading"
:currentChatId="currentChatId"
@new-chat="newChat"
@clickLog="clickListHandle"
@delete-log="deleteLog"
@refreshFieldTitle="refreshFieldTitle"
:isPcCollapse="isPcCollapse"
>
<div style="padding: 16px 18px 0 18px">
<div class="flex align-center mb-16">
<div class="flex mr-8">
<el-avatar
v-if="isAppIcon(applicationDetail?.icon)"
shape="square"
:size="32"
style="background: none"
>
<img :src="applicationDetail?.icon" alt="" />
</el-avatar>
<LogoIcon v-else height="32px" />
</div>
<h4 v-show="!isPcCollapse">{{ applicationDetail?.name }}</h4>
</div>
<el-button
size="large"
type="primary"
plain
v-show="!isPcCollapse"
class="add-button w-full primary"
@click="newChat"
<div class="user-info p-16 cursor">
<el-avatar
:size="32"
v-if="
!chatUser.chat_profile?.authentication ||
chatUser.chat_profile.authentication_type === 'password'
"
>
<AppIcon iconName="app-create-chat"></AppIcon>
<span class="ml-4">{{ $t('chat.createChat') }}</span>
</el-button>
<p v-show="!isPcCollapse" class="mt-20 mb-8">{{ $t('chat.history') }}</p>
</div>
<div v-show="!isPcCollapse" class="left-height pt-0">
<el-scrollbar>
<div class="p-8 pt-0">
<common-list
:style="{
'--el-color-primary': applicationDetail?.custom_theme?.theme_color,
'--el-color-primary-light-9': hexToRgba(
applicationDetail?.custom_theme?.theme_color,
0.1,
),
}"
:data="chatLogData"
class="mt-8"
v-loading="left_loading"
:defaultActive="currentChatId"
@click="clickListHandle"
@mouseenter="mouseenter"
@mouseleave="mouseId = ''"
>
<template #default="{ row }">
<div class="flex-between">
<span :title="row.abstract">
{{ row.abstract }}
</span>
<div @click.stop v-show="mouseId === row.id && row.id !== 'new'">
<el-dropdown trigger="click" :teleported="false">
<el-button text>
<el-icon><MoreFilled /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click.stop="editLogTitle(row)">
<el-icon><EditPen /></el-icon>
{{ $t('common.edit') }}
</el-dropdown-item>
<el-dropdown-item @click.stop="deleteLog(row)">
<el-icon><Delete /></el-icon>
{{ $t('common.delete') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</template>
<template #empty>
<div class="text-center">
<el-text type="info">{{ $t('chat.noHistory') }}</el-text>
</div>
</template>
</common-list>
</div>
<div v-if="chatLogData?.length" class="gradient-divider lighter mt-8">
<span>{{ $t('chat.only20history') }}</span>
</div>
</el-scrollbar>
</div>
<el-menu-item index="1" v-show="isPcCollapse" @click="newChat">
<AppIcon iconName="app-create-chat"></AppIcon>
<template #title>{{ $t('chat.createChat') }}</template>
</el-menu-item>
<el-sub-menu v-show="isPcCollapse" index="2">
<template #title>
<AppIcon iconName="app-history-outlined" />
</template>
<el-menu-item-group v-loading="left_loading">
<template #title
><span>{{ $t('chat.history') }}</span></template
>
<el-menu-item
v-for="row in chatLogData"
:index="row.id"
:key="row.id"
@click="clickListHandle(row)"
>
<div class="flex-between w-full lighter">
<span :title="row.abstract">
{{ row.abstract }}
</span>
<div @click.stop class="flex" v-show="mouseId === row.id && row.id !== 'new'">
<el-dropdown trigger="click" :teleported="false">
<el-icon class="rotate-90 mt-4">
<MoreFilled />
</el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click.stop="editLogTitle(row)">
<el-icon>
<EditPen />
</el-icon>
{{ $t('common.edit') }}
</el-dropdown-item>
<el-dropdown-item @click.stop="deleteLog(row)">
<el-icon>
<Delete />
</el-icon>
{{ $t('common.delete') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</el-menu-item>
</el-menu-item-group>
<div v-if="!chatLogData?.length" class="text-center">
<el-text type="info">{{ $t('chat.noHistory') }}</el-text>
</div>
</el-sub-menu>
<div v-if="!chatUser.chat_profile?.authentication || chatUser.chat_profile.authentication_type === 'password'" class="no-auth-avatar">
<el-avatar :size="32">
<img src="@/assets/user-icon.svg" style="width: 54%" alt="" />
</el-avatar>
</div>
<el-dropdown v-else trigger="click" type="primary" class="w-full">
<div class="flex align-center user-info">
<el-avatar :size="32">
<img src="@/assets/user-icon.svg" style="width: 54%" alt="" />
</el-avatar>
<span v-show="!isPcCollapse" class="ml-8 color-text-primary">{{
chatUser.chatUserProfile?.nick_name
}}</span>
</div>
<el-dropdown v-else trigger="click" type="primary" class="w-full">
<div class="flex align-center">
<el-avatar :size="32">
<img src="@/assets/user-icon.svg" style="width: 54%" alt="" />
</el-avatar>
<span v-show="!isPcCollapse" class="ml-8 color-text-primary">{{
chatUser.chatUserProfile?.nick_name
}}</span>
</div>
<template #dropdown>
<el-dropdown-menu class="avatar-dropdown">
<div class="flex align-center" style="padding: 8px 12px">
<div class="mr-8 flex align-center">
<el-avatar :size="40">
<img src="@/assets/user-icon.svg" style="width: 54%" alt="" />
</el-avatar>
</div>
<div>
<h4 class="medium mb-4">{{ chatUser.chatUserProfile?.nick_name }}</h4>
<div class="color-secondary">
{{ `${t('common.username')}: ${chatUser.chatUserProfile?.username}` }}
<template #dropdown>
<el-dropdown-menu class="avatar-dropdown">
<div class="flex align-center" style="padding: 8px 12px">
<div class="mr-8 flex align-center">
<el-avatar :size="40">
<img src="@/assets/user-icon.svg" style="width: 54%" alt="" />
</el-avatar>
</div>
<div>
<h4 class="medium mb-4">{{ chatUser.chatUserProfile?.nick_name }}</h4>
<div class="color-secondary">
{{ `${t('common.username')}: ${chatUser.chatUserProfile?.username}` }}
</div>
</div>
</div>
</div>
<el-dropdown-item
v-if="chatUser.chatUserProfile?.source === 'LOCAL'"
class="border-t"
style="padding-top: 8px; padding-bottom: 8px"
@click="openResetPassword"
>
<AppIcon iconName="app-export" />
{{ $t('views.login.resetPassword') }}
</el-dropdown-item>
<el-dropdown-item
v-if="chatUser.chatUserProfile?.source === 'LOCAL'"
class="border-t"
style="padding-top: 8px; padding-bottom: 8px"
@click="logout"
>
<AppIcon iconName="app-export" />
{{ $t('layout.logout') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-menu>
<el-dropdown-item
v-if="chatUser.chatUserProfile?.source === 'LOCAL'"
class="border-t"
style="padding-top: 8px; padding-bottom: 8px"
@click="openResetPassword"
>
<AppIcon iconName="app-export" />
{{ $t('views.login.resetPassword') }}
</el-dropdown-item>
<el-dropdown-item
v-if="chatUser.chatUserProfile?.source === 'LOCAL'"
class="border-t"
style="padding-top: 8px; padding-bottom: 8px"
@click="logout"
>
<AppIcon iconName="app-export" />
{{ $t('layout.logout') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</history-component>
<el-button
v-if="!common.isMobile()"
class="pc-collapse cursor"
@ -306,7 +177,6 @@
</div>
</div>
<EditTitleDialog ref="EditTitleDialogRef" @refresh="refreshFieldTitle" />
<ResetPassword
ref="resetPasswordRef"
emitConfirm
@ -320,11 +190,10 @@ import { ref, onMounted, nextTick, computed, watch } from 'vue'
import { marked } from 'marked'
import { saveAs } from 'file-saver'
import chatAPI from '@/api/chat/chat'
import { isAppIcon } from '@/utils/common'
import useStore from '@/stores'
import useResize from '@/layout/hooks/useResize'
import { hexToRgba } from '@/utils/theme'
import EditTitleDialog from './EditTitleDialog.vue'
import { useRouter } from 'vue-router'
import ResetPassword from '@/layout/layout-header/avatar/ResetPassword.vue'
import { t } from '@/locales'
@ -332,6 +201,7 @@ import type { ResetCurrentUserPasswordRequest } from '@/api/type/user'
import ExecutionDetailContent from '@/components/ai-chat/component/knowledge-source-component/ExecutionDetailContent.vue'
import ParagraphSourceContent from '@/components/ai-chat/component/knowledge-source-component/ParagraphSourceContent.vue'
import ParagraphDocumentContent from '@/components/ai-chat/component/knowledge-source-component/ParagraphDocumentContent.vue'
import HistoryComponent from '@/views/chat/history-component/index.vue'
import { cloneDeep } from 'lodash'
useResize()
@ -339,8 +209,6 @@ useResize()
const { common, chatUser } = useStore()
const router = useRouter()
const EditTitleDialogRef = ref()
const isCollapse = ref(false)
const isPcCollapse = ref(false)
watch(
@ -407,15 +275,7 @@ const paginationConfig = ref({
const currentRecordList = ref<any>([])
const currentChatId = ref('new') // Id 'new'
const currentChatName = ref(t('chat.createChat'))
const mouseId = ref('')
function mouseenter(row: any) {
mouseId.value = row.id
}
function editLogTitle(row: any) {
EditTitleDialogRef.value.open(row, applicationDetail.value.id)
}
function refreshFieldTitle(chatId: string, abstract: string) {
const find = chatLogData.value.find((item: any) => item.id == chatId)
if (find) {
@ -614,124 +474,21 @@ function closeExecutionDetail() {
rightPanelSize.value = 0
}
</script>
<style lang="scss">
<style lang="scss" scoped>
.chat-pc {
height: 100%;
overflow: hidden;
background: #eef1f4;
&__header {
background: var(--app-header-bg-color);
position: fixed;
width: 100%;
left: 0;
top: 0;
z-index: 100;
height: var(--app-header-height);
line-height: var(--app-header-height);
box-sizing: border-box;
border-bottom: 1px solid var(--el-border-color);
}
&__left {
position: relative;
z-index: 1;
.common-list li.active {
background-color: #ffffff;
font-weight: 500;
color: var(--el-text-color-primary);
&:hover {
background-color: #ffffff;
}
}
.el-menu {
display: flex;
flex-direction: column;
background:
linear-gradient(187.61deg, rgba(235, 241, 255, 0.5) 39.6%, rgba(231, 249, 255, 0.5) 94.3%),
#eef1f4;
&:not(.el-menu--collapse) {
width: 280px;
}
.el-menu-item:hover {
background: transparent;
}
.no-auth-avatar {
margin-top: auto;
padding: 16px;
.el-avatar {
cursor: default;
}
}
.el-dropdown {
margin-top: auto;
.user-info {
width: 100%;
cursor: pointer;
border-radius: 6px;
padding: 4px 8px;
margin: 16px;
box-sizing: border-box;
&:hover {
background-color: #1f23291a;
}
}
}
&.el-menu--collapse {
.el-menu-item,
.el-menu-tooltip__trigger,
.el-sub-menu__title {
padding: 0;
}
.el-menu-item .el-menu-tooltip__trigger,
.el-sub-menu__title {
position: static;
width: 40px;
height: 40px;
border-radius: 6px;
align-items: center;
justify-content: center;
margin: 0 auto;
}
.el-menu-item:hover .el-menu-tooltip__trigger,
.el-sub-menu__title:hover {
background-color: #1f23291a;
}
.user-info {
margin: 16px 8px;
}
}
}
.add-button {
border: 1px solid var(--el-color-primary);
background-color: #3370ff1a;
color: #3370ff;
font-weight: 500;
&:hover {
background-color: #3370ff33;
}
}
.left-height {
height: calc(100vh - 212px);
}
.pc-collapse {
position: absolute;
top: 20px;
right: -15px;
box-shadow: 0px 5px 10px 0px #1f23291a;
box-shadow: 0px 5px 10px 0px rgba(31, 35, 41, 0.1);
z-index: 1;
}
}
@ -766,108 +523,11 @@ function closeExecutionDetail() {
}
}
.gradient-divider {
position: relative;
text-align: center;
color: var(--el-color-info);
::before {
content: '';
width: 17%;
height: 1px;
background: linear-gradient(90deg, rgba(222, 224, 227, 0) 0%, #dee0e3 100%);
position: absolute;
left: 16px;
top: 50%;
}
::after {
content: '';
width: 17%;
height: 1px;
background: linear-gradient(90deg, #dee0e3 0%, rgba(222, 224, 227, 0) 100%);
position: absolute;
right: 16px;
top: 50%;
}
}
.collapse {
display: none;
}
}
.chat-pc-popper {
background:
linear-gradient(187.61deg, rgba(235, 241, 255, 0.5) 39.6%, rgba(231, 249, 255, 0.5) 94.3%),
#eef1f4 !important;
.el-menu {
background: transparent;
}
.el-menu-item-group__title {
padding-bottom: 16px;
font-weight: 500;
color: var(--app-text-color-secondary);
}
.el-menu-item {
border-radius: 6px;
height: 40px;
margin: 0 8px;
padding-left: 8px;
padding-right: 8px;
&:hover {
background-color: #1f23291a;
}
&.is-active {
background-color: #ffffff;
color: var(--el-text-color-primary);
& > div {
font-weight: 500;
}
}
}
}
//
.mobile {
.chat-pc {
&__right {
width: 100%;
}
&__left {
display: none;
width: 0;
}
}
.collapse {
display: block;
position: fixed;
bottom: 90px;
z-index: 99;
}
&.openLeft {
.chat-pc {
&__left {
display: block;
position: fixed;
width: 100%;
z-index: 99;
height: calc(100vh);
.el-menu {
width: 100%;
}
}
}
.collapse {
display: block;
position: absolute;
bottom: 90px;
right: 0;
z-index: 99;
}
}
}
.chat-width {
max-width: 80%;
margin: 0 auto;
@ -878,9 +538,6 @@ function closeExecutionDetail() {
margin: 0 auto;
}
}
</style>
<style lang="scss" scoped>
.avatar-dropdown {
min-width: 240px;
}

View File

@ -167,7 +167,7 @@
<el-button
@click="submit"
type="primary"
v-if=" !route.path.includes('share/') && permissionPrecise.setting(id)"
v-if="!route.path.includes('share/') && permissionPrecise.setting(id)"
>
{{ $t('common.save') }}</el-button
>
@ -191,7 +191,7 @@ import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
const route = useRoute()
const {
params: { id },
params: { id, folderId },
} = route as any
const apiType = computed(() => {
@ -208,6 +208,10 @@ const permissionPrecise = computed(() => {
return permissionMap['knowledge'][apiType.value]
})
const isShared = computed(() => {
return folderId === 'share'
})
const webFormRef = ref()
const BaseFormRef = ref()
const loading = ref(false)
@ -326,7 +330,7 @@ async function submit() {
}
function getDetail() {
loadSharedApi({ type: 'knowledge', systemType: apiType.value })
loadSharedApi({ type: 'knowledge', isShared: isShared.value, systemType: apiType.value })
.getKnowledgeDetail(id, loading)
.then((res: any) => {
detail.value = res.data

View File

@ -170,7 +170,7 @@ import permissionMap from '@/permission'
const route = useRoute()
const {
params: { id }, // id
params: { id, folderId }, // id
} = route as any
const apiType = computed(() => {
@ -188,6 +188,10 @@ const permissionPrecise = computed(() => {
return permissionMap['knowledge'][apiType.value]
})
const isShared = computed(() => {
return folderId === 'share'
})
const RelateProblemDialogRef = ref()
const DetailProblemRef = ref()
const CreateProblemDialogRef = ref()
@ -397,7 +401,7 @@ function handleSizeChange() {
}
function getList() {
return loadSharedApi({ type: 'problem', systemType: apiType.value })
return loadSharedApi({ type: 'problem', isShared: isShared.value, systemType: apiType.value })
.getProblemsPage(
id as string,
paginationConfig,