mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 10:02:46 +00:00
feat: chat log
This commit is contained in:
parent
fa818fc6e8
commit
e9527bdd2b
|
|
@ -3,7 +3,6 @@ import { get, post, postStream, del, put, request, download, exportFile } from '
|
|||
import type { pageRequest } from '@/api/type/common'
|
||||
import type { ApplicationFormType } from '@/api/type/application'
|
||||
import { type Ref } from 'vue'
|
||||
import type { FormField } from '@/components/dynamics-form/type'
|
||||
|
||||
const prefix = '/workspace/' + localStorage.getItem('workspace_id') + '/application'
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,200 @@
|
|||
import { Result } from '@/request/Result'
|
||||
import {
|
||||
get,
|
||||
post,
|
||||
exportExcelPost,
|
||||
postStream,
|
||||
del,
|
||||
put,
|
||||
request,
|
||||
download,
|
||||
exportFile,
|
||||
} from '@/request/index'
|
||||
import type { pageRequest } from '@/api/type/common'
|
||||
import type { ApplicationFormType } from '@/api/type/application'
|
||||
import { type Ref } from 'vue'
|
||||
|
||||
const prefix = '/workspace/' + localStorage.getItem('workspace_id') + '/application'
|
||||
/**
|
||||
* 对话记录提交至知识库
|
||||
* @param data
|
||||
* @param loading
|
||||
* @param application_id
|
||||
* @param knowledge_id
|
||||
*/
|
||||
|
||||
const postChatLogAddKnowledge: (
|
||||
application_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (application_id, data, loading) => {
|
||||
return post(`${prefix}/${application_id}/add_knowledge`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话日志
|
||||
* @param 参数
|
||||
* application_id
|
||||
* param {
|
||||
"start_time": "string",
|
||||
"end_time": "string",
|
||||
}
|
||||
*/
|
||||
const getChatLog: (
|
||||
application_id: String,
|
||||
page: pageRequest,
|
||||
param: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (application_id, page, param, loading) => {
|
||||
return get(
|
||||
`${prefix}/${application_id}/chat/${page.current_page}/${page.page_size}`,
|
||||
param,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得对话日志记录
|
||||
* @param 参数
|
||||
* application_id, chart_id,order_asc
|
||||
*/
|
||||
const getChatRecordLog: (
|
||||
application_id: String,
|
||||
chart_id: String,
|
||||
page: pageRequest,
|
||||
loading?: Ref<boolean>,
|
||||
order_asc?: boolean,
|
||||
) => Promise<Result<any>> = (application_id, chart_id, page, loading, order_asc) => {
|
||||
return get(
|
||||
`${prefix}/${application_id}/chat/${chart_id}/chat_record/${page.current_page}/${page.page_size}`,
|
||||
{ order_asc: order_asc !== undefined ? order_asc : true },
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标注段落列表信息
|
||||
* @param 参数
|
||||
* application_id, chart_id, chart_record_id, knowledge_id, document_id
|
||||
*/
|
||||
const getMarkChatRecord: (
|
||||
application_id: String,
|
||||
chart_id: String,
|
||||
chart_record_id: String,
|
||||
knowledge_id: String,
|
||||
document_id: String,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (
|
||||
application_id,
|
||||
chart_id,
|
||||
chart_record_id,
|
||||
knowledge_id,
|
||||
document_id,
|
||||
loading,
|
||||
) => {
|
||||
return get(
|
||||
`${prefix}/${application_id}/chat/${chart_id}/chat_record/${chart_record_id}/knowledge/${knowledge_id}/document/${document_id}/improve`,
|
||||
undefined,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改日志记录内容
|
||||
* @param 参数
|
||||
* application_id, chart_id, chart_record_id, knowledge_id, document_id
|
||||
* data {
|
||||
"title": "string",
|
||||
"content": "string",
|
||||
"problem_text": "string"
|
||||
}
|
||||
*/
|
||||
const putChatRecordLog: (
|
||||
application_id: String,
|
||||
chart_id: String,
|
||||
chart_record_id: String,
|
||||
knowledge_id: String,
|
||||
document_id: String,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (
|
||||
application_id,
|
||||
chart_id,
|
||||
chart_record_id,
|
||||
knowledge_id,
|
||||
document_id,
|
||||
data,
|
||||
loading,
|
||||
) => {
|
||||
return put(
|
||||
`${prefix}/${application_id}/chat/${chart_id}/chat_record/${chart_record_id}/dataset/${knowledge_id}/document/${document_id}/improve`,
|
||||
data,
|
||||
undefined,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除标注
|
||||
* @param 参数
|
||||
* application_id, chart_id, chart_record_id, dataset_id, document_id,paragraph_id
|
||||
*/
|
||||
const delMarkChatRecord: (
|
||||
application_id: String,
|
||||
chart_id: String,
|
||||
chart_record_id: String,
|
||||
knowledge_id: String,
|
||||
document_id: String,
|
||||
paragraph_id: String,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (
|
||||
application_id,
|
||||
chart_id,
|
||||
chart_record_id,
|
||||
knowledge_id,
|
||||
document_id,
|
||||
paragraph_id,
|
||||
loading,
|
||||
) => {
|
||||
return del(
|
||||
`${prefix}/${application_id}/chat/${chart_id}/chat_record/${chart_record_id}/knowledge/${knowledge_id}/document/${document_id}/paragraph/${paragraph_id}/improve`,
|
||||
undefined,
|
||||
{},
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出对话日志
|
||||
* @param 参数
|
||||
* application_id
|
||||
* param {
|
||||
"start_time": "string",
|
||||
"end_time": "string",
|
||||
}
|
||||
*/
|
||||
const postExportChatLog: (
|
||||
application_id: string,
|
||||
application_name: string,
|
||||
param: any,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => void = (application_id, application_name, param, data, loading) => {
|
||||
exportExcelPost(
|
||||
application_name + '.xlsx',
|
||||
`${prefix}/${application_id}/chat/export`,
|
||||
param,
|
||||
data,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
export default {
|
||||
postChatLogAddKnowledge,
|
||||
getChatLog,
|
||||
getChatRecordLog,
|
||||
getMarkChatRecord,
|
||||
putChatRecordLog,
|
||||
delMarkChatRecord,
|
||||
postExportChatLog,
|
||||
}
|
||||
|
|
@ -22,10 +22,10 @@ export default {
|
|||
mark: 'Marks',
|
||||
recenTimes: 'Last Chat Time'
|
||||
},
|
||||
addToDataset: 'Add to Knowledge',
|
||||
addToKnowledge: 'Add to Knowledge',
|
||||
daysText: 'Days ago',
|
||||
selectDataset: 'Select Knowledge',
|
||||
selectDatasetPlaceholder: 'Please select a knowledge',
|
||||
selectKnowledge: 'Select Knowledge',
|
||||
selectKnowledgePlaceholder: 'Please select a knowledge',
|
||||
saveToDocument: 'Save to Document',
|
||||
documentPlaceholder: 'Please select a document',
|
||||
editContent: 'Edit Content',
|
||||
|
|
@ -12,7 +12,7 @@ import model from './model'
|
|||
import document from './document'
|
||||
import paragraph from './paragraph'
|
||||
import problem from './problem'
|
||||
import log from './log'
|
||||
import chatLog from './chat-log'
|
||||
import applicationWorkflow from './application-workflow'
|
||||
import login from './login'
|
||||
import operateLog from './operate-log'
|
||||
|
|
@ -31,7 +31,7 @@ export default {
|
|||
document,
|
||||
paragraph,
|
||||
problem,
|
||||
log,
|
||||
chatLog,
|
||||
login,
|
||||
operateLog,
|
||||
role
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ export default {
|
|||
mark: '改进标注',
|
||||
recenTimes: '最近对话时间'
|
||||
},
|
||||
addToDataset: '添加至知识库',
|
||||
addToKnowledge: '添加至知识库',
|
||||
daysText: '天之前的对话记录',
|
||||
selectDataset: '选择知识库',
|
||||
selectDatasetPlaceholder: '请选择知识库',
|
||||
selectKnowledge: '选择知识库',
|
||||
selectKnowledgePlaceholder: '请选择知识库',
|
||||
saveToDocument: '保存至文档',
|
||||
documentPlaceholder: '请选择文档',
|
||||
editContent: '修改内容',
|
||||
|
|
@ -12,7 +12,7 @@ import problem from './problem'
|
|||
import applicationOverview from './application-overview'
|
||||
import applicationWorkflow from './application-workflow'
|
||||
import paragraph from './paragraph'
|
||||
import log from './log'
|
||||
import chatLog from './chat-log'
|
||||
// import notFound from './404'
|
||||
|
||||
// import operateLog from './operate-log'
|
||||
|
|
@ -31,7 +31,7 @@ export default {
|
|||
applicationOverview,
|
||||
applicationWorkflow,
|
||||
paragraph,
|
||||
log,
|
||||
chatLog,
|
||||
// notFound,
|
||||
|
||||
// operateLog
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ export default {
|
|||
mark: '改進標註',
|
||||
recenTimes: '最近對話時間'
|
||||
},
|
||||
addToDataset: '添加至知識庫',
|
||||
addToKnowledge: '添加至知識庫',
|
||||
daysText: '天之前的對話記錄',
|
||||
selectDataset: '選擇知識庫',
|
||||
selectDatasetPlaceholder: '請選擇知識庫',
|
||||
selectKnowledge: '選擇知識庫',
|
||||
selectKnowledgePlaceholder: '請選擇知識庫',
|
||||
saveToDocument: '保存至文件',
|
||||
documentPlaceholder: '請選擇文件',
|
||||
editContent: '修改內容',
|
||||
|
|
@ -12,7 +12,7 @@ import model from './model'
|
|||
import document from './document'
|
||||
import paragraph from './paragraph'
|
||||
import problem from './problem'
|
||||
import log from './log'
|
||||
import chatLog from './chat-log'
|
||||
import applicationWorkflow from './application-workflow'
|
||||
import login from './login'
|
||||
import operateLog from './operate-log'
|
||||
|
|
@ -31,7 +31,7 @@ export default {
|
|||
document,
|
||||
paragraph,
|
||||
problem,
|
||||
log,
|
||||
chatLog,
|
||||
login,
|
||||
operateLog,
|
||||
role
|
||||
|
|
|
|||
|
|
@ -57,19 +57,19 @@ const ApplicationDetailRouter = {
|
|||
},
|
||||
component: () => import('@/views/hit-test/index.vue'),
|
||||
},
|
||||
// {
|
||||
// path: 'log',
|
||||
// name: 'Log',
|
||||
// meta: {
|
||||
// icon: 'app-document',
|
||||
// iconActive: 'app-document-active',
|
||||
// title: 'views.log.title',
|
||||
// active: 'log',
|
||||
// parentPath: '/application/:id/:type',
|
||||
// parentName: 'ApplicationDetail'
|
||||
// },
|
||||
// component: () => import('@/views/log/index.vue')
|
||||
// }
|
||||
{
|
||||
path: 'chat-log',
|
||||
name: 'ChatLog',
|
||||
meta: {
|
||||
icon: 'app-document',
|
||||
iconActive: 'app-document-active',
|
||||
title: 'views.chatLog.title',
|
||||
active: 'log',
|
||||
parentPath: '/application/:id/:type',
|
||||
parentName: 'ApplicationDetail'
|
||||
},
|
||||
component: () => import('@/views/chat-log/index.vue')
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import useProblemStore from './modules/problem'
|
|||
import useParagraphStore from './modules/paragraph'
|
||||
import useDocumentStore from './modules/document'
|
||||
import useApplicationStore from './modules/application'
|
||||
|
||||
import useChatLogStore from './modules/chat-log'
|
||||
const useStore = () => ({
|
||||
common: useCommonStore(),
|
||||
login: useLoginStore(),
|
||||
|
|
@ -24,6 +24,7 @@ const useStore = () => ({
|
|||
paragraph: useParagraphStore(),
|
||||
document: useDocumentStore(),
|
||||
application: useApplicationStore(),
|
||||
chatLog: useChatLogStore(),
|
||||
})
|
||||
|
||||
export default useStore
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import chatLogApi from '@/api/application/chat-log'
|
||||
import { type Ref } from 'vue'
|
||||
import type { pageRequest } from '@/api/type/common'
|
||||
|
||||
const useChatLogStore = defineStore('chatLog',{
|
||||
state: () => ({}),
|
||||
actions: {
|
||||
async asyncGetChatLog(id: string, page: pageRequest, param: any, loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
chatLogApi
|
||||
.getChatLog(id, page, param, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
async asyncChatRecordLog(
|
||||
id: string,
|
||||
chatId: string,
|
||||
page: pageRequest,
|
||||
loading?: Ref<boolean>,
|
||||
order_asc?: boolean
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
chatLogApi
|
||||
.getChatRecordLog(id, chatId, page, loading, order_asc)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
async asyncGetChatLogClient(id: string, page: pageRequest, loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
chatLogApi
|
||||
.getChatLogClient(id, page, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
async asyncDelChatClientLog(id: string, chatId: string, loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
chatLogApi
|
||||
.delChatClientLog(id, chatId, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
async asyncPutChatClientLog(id: string, chatId: string, data: any, loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
chatLogApi
|
||||
.putChatClientLog(id, chatId, data, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default useChatLogStore
|
||||
|
|
@ -156,7 +156,7 @@
|
|||
<AppIcon iconName="app-operation" class="mr-4"></AppIcon>
|
||||
{{ $t('common.paramSetting') }}
|
||||
</el-button>
|
||||
<el-button type="primary" link @click="openDatasetDialog">
|
||||
<el-button type="primary" link @click="openKnowledgeDialog">
|
||||
<el-icon class="mr-4">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
|
|
@ -180,11 +180,11 @@
|
|||
v-for="(item, index) in applicationForm.knowledge_id_list"
|
||||
:key="index"
|
||||
>
|
||||
<el-card class="relate-dataset-card border-r-4" shadow="never">
|
||||
<el-card class="relate-knowledge-card border-r-4" shadow="never">
|
||||
<div class="flex-between">
|
||||
<div class="flex align-center" style="width: 80%">
|
||||
<el-avatar
|
||||
v-if="relatedObject(datasetList, item, 'id')?.type === '1'"
|
||||
v-if="relatedObject(knowledgeList, item, 'id')?.type === '1'"
|
||||
class="mr-8 avatar-purple"
|
||||
shape="square"
|
||||
:size="32"
|
||||
|
|
@ -196,7 +196,7 @@
|
|||
/>
|
||||
</el-avatar>
|
||||
<el-avatar
|
||||
v-else-if="relatedObject(datasetList, item, 'id')?.type === '2'"
|
||||
v-else-if="relatedObject(knowledgeList, item, 'id')?.type === '2'"
|
||||
class="mr-8 avatar-purple"
|
||||
shape="square"
|
||||
:size="32"
|
||||
|
|
@ -218,12 +218,12 @@
|
|||
|
||||
<span
|
||||
class="ellipsis cursor"
|
||||
:title="relatedObject(datasetList, item, 'id')?.name"
|
||||
:title="relatedObject(knowledgeList, item, 'id')?.name"
|
||||
>
|
||||
{{ relatedObject(datasetList, item, 'id')?.name }}</span
|
||||
{{ relatedObject(knowledgeList, item, 'id')?.name }}</span
|
||||
>
|
||||
</div>
|
||||
<el-button text @click="removeDataset(item)">
|
||||
<el-button text @click="removeKnowledge(item)">
|
||||
<el-icon>
|
||||
<Close />
|
||||
</el-icon>
|
||||
|
|
@ -470,12 +470,12 @@
|
|||
<AIModeParamSettingDialog ref="AIModeParamSettingDialogRef" @refresh="refreshForm" />
|
||||
<TTSModeParamSettingDialog ref="TTSModeParamSettingDialogRef" @refresh="refreshTTSForm" />
|
||||
<ParamSettingDialog ref="ParamSettingDialogRef" @refresh="refreshParam" />
|
||||
<AddDatasetDialog
|
||||
ref="AddDatasetDialogRef"
|
||||
@addData="addDataset"
|
||||
:data="datasetList"
|
||||
<AddKnowledgeDialog
|
||||
ref="AddKnowledgeDialogRef"
|
||||
@addData="addKnowledge"
|
||||
:data="knowledgeList"
|
||||
@refresh="refresh"
|
||||
:loading="datasetLoading"
|
||||
:loading="knowledgeLoading"
|
||||
/>
|
||||
|
||||
<EditAvatarDialog ref="EditAvatarDialogRef" @refresh="refreshIcon" />
|
||||
|
|
@ -491,7 +491,7 @@ import { useRoute } from 'vue-router'
|
|||
import { groupBy } from 'lodash'
|
||||
import AIModeParamSettingDialog from './component/AIModeParamSettingDialog.vue'
|
||||
import ParamSettingDialog from './component/ParamSettingDialog.vue'
|
||||
import AddDatasetDialog from './component/AddDatasetDialog.vue'
|
||||
import AddKnowledgeDialog from './component/AddKnowledgeDialog.vue'
|
||||
import EditAvatarDialog from '@/views/application-overview/component/EditAvatarDialog.vue'
|
||||
import applicationApi from '@/api/application/application'
|
||||
import { isAppIcon } from '@/utils/common'
|
||||
|
|
@ -529,11 +529,11 @@ const TTSModeParamSettingDialogRef = ref<InstanceType<typeof TTSModeParamSetting
|
|||
const ParamSettingDialogRef = ref<InstanceType<typeof ParamSettingDialog>>()
|
||||
|
||||
const applicationFormRef = ref<FormInstance>()
|
||||
const AddDatasetDialogRef = ref()
|
||||
const AddKnowledgeDialogRef = ref()
|
||||
const EditAvatarDialogRef = ref()
|
||||
|
||||
const loading = ref(false)
|
||||
const datasetLoading = ref(false)
|
||||
const knowledgeLoading = ref(false)
|
||||
const applicationForm = ref<ApplicationFormType>({
|
||||
name: '',
|
||||
desc: '',
|
||||
|
|
@ -578,7 +578,7 @@ const rules = reactive<FormRules<ApplicationFormType>>({
|
|||
],
|
||||
})
|
||||
const modelOptions = ref<any>(null)
|
||||
const datasetList = ref([])
|
||||
const knowledgeList = ref([])
|
||||
const sttModelOptions = ref<any>(null)
|
||||
const ttsModelOptions = ref<any>(null)
|
||||
const showEditIcon = ref(false)
|
||||
|
|
@ -660,7 +660,7 @@ function refreshTTSForm(data: any) {
|
|||
applicationForm.value.tts_model_params_setting = data
|
||||
}
|
||||
|
||||
function removeDataset(id: any) {
|
||||
function removeKnowledge(id: any) {
|
||||
if (applicationForm.value.knowledge_id_list) {
|
||||
applicationForm.value.knowledge_id_list.splice(
|
||||
applicationForm.value.knowledge_id_list.indexOf(id),
|
||||
|
|
@ -669,12 +669,12 @@ function removeDataset(id: any) {
|
|||
}
|
||||
}
|
||||
|
||||
function addDataset(val: Array<string>) {
|
||||
function addKnowledge(val: Array<string>) {
|
||||
applicationForm.value.knowledge_id_list = val
|
||||
}
|
||||
|
||||
function openDatasetDialog() {
|
||||
AddDatasetDialogRef.value.open(applicationForm.value.knowledge_id_list)
|
||||
function openKnowledgeDialog() {
|
||||
AddKnowledgeDialogRef.value.open(applicationForm.value.knowledge_id_list)
|
||||
}
|
||||
|
||||
function getDetail() {
|
||||
|
|
@ -692,9 +692,9 @@ function getDetail() {
|
|||
})
|
||||
}
|
||||
|
||||
function getDataset() {
|
||||
application.asyncGetApplicationDataset(id, datasetLoading).then((res: any) => {
|
||||
datasetList.value = res.data
|
||||
function getKnowledge() {
|
||||
application.asyncGetApplicationKnowledge(id, knowledgeLoading).then((res: any) => {
|
||||
knowledgeList.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -766,12 +766,12 @@ function refreshIcon() {
|
|||
}
|
||||
|
||||
function refresh() {
|
||||
getDataset()
|
||||
getKnowledge()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// getModel()
|
||||
// getDataset()
|
||||
// getKnowledge()
|
||||
// getDetail()
|
||||
// getSTTModel()
|
||||
// getTTSModel()
|
||||
|
|
@ -779,7 +779,7 @@ onMounted(() => {
|
|||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.application-setting {
|
||||
.relate-dataset-card {
|
||||
.relate-knowledge-card {
|
||||
color: var(--app-text-color);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,166 @@
|
|||
<template>
|
||||
<el-drawer v-model="visible" size="60%" @close="closeHandle" class="chat-record-drawer">
|
||||
<template #header>
|
||||
<h4 class="single-line">{{ currentAbstract }}</h4>
|
||||
</template>
|
||||
<div
|
||||
v-loading="paginationConfig.current_page === 1 && loading"
|
||||
class="h-full"
|
||||
style="padding: 24px 0"
|
||||
>
|
||||
<AiChat
|
||||
ref="AiChatRef"
|
||||
:application-details="application"
|
||||
type="log"
|
||||
:record="recordList"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
</AiChat>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div>
|
||||
<el-button @click="pre" :disabled="pre_disable || loading">{{
|
||||
$t('views.log.buttons.prev')
|
||||
}}</el-button>
|
||||
<el-button @click="next" :disabled="next_disable || loading">{{
|
||||
$t('views.log.buttons.next')
|
||||
}}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch, nextTick } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { type ApplicationFormType, type chatType } from '@/api/type/application'
|
||||
import useStore from '@/stores'
|
||||
const AiChatRef = ref()
|
||||
const { log } = useStore()
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
/**
|
||||
* 应用信息
|
||||
*/
|
||||
application?: ApplicationFormType
|
||||
/**
|
||||
* 对话 记录id
|
||||
*/
|
||||
chatId: string
|
||||
currentAbstract: string
|
||||
/**
|
||||
* 下一条
|
||||
*/
|
||||
next: () => void
|
||||
/**
|
||||
* 上一条
|
||||
*/
|
||||
pre: () => void
|
||||
|
||||
pre_disable: boolean
|
||||
|
||||
next_disable: boolean
|
||||
}>(),
|
||||
{}
|
||||
)
|
||||
|
||||
const emit = defineEmits(['update:chatId', 'update:currentAbstract', 'refresh'])
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id }
|
||||
} = route
|
||||
const loading = ref(false)
|
||||
const visible = ref(false)
|
||||
const recordList = ref<chatType[]>([])
|
||||
|
||||
const paginationConfig = reactive({
|
||||
current_page: 1,
|
||||
page_size: 20,
|
||||
total: 0
|
||||
})
|
||||
|
||||
function closeHandle() {
|
||||
recordList.value = []
|
||||
paginationConfig.total = 0
|
||||
paginationConfig.current_page = 1
|
||||
}
|
||||
|
||||
function getChatRecord() {
|
||||
return log
|
||||
.asyncChatRecordLog(id as string, props.chatId, paginationConfig, loading)
|
||||
.then((res: any) => {
|
||||
paginationConfig.total = res.data.total
|
||||
const list = res.data.records
|
||||
recordList.value = [...list, ...recordList.value].sort((a, b) =>
|
||||
a.create_time.localeCompare(b.create_time)
|
||||
)
|
||||
if (paginationConfig.current_page === 1) {
|
||||
nextTick(() => {
|
||||
// 将滚动条滚动到最下面
|
||||
AiChatRef.value.setScrollBottom()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.chatId,
|
||||
() => {
|
||||
recordList.value = []
|
||||
paginationConfig.total = 0
|
||||
paginationConfig.current_page = 1
|
||||
if (props.chatId) {
|
||||
getChatRecord()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
watch(visible, (bool) => {
|
||||
if (!bool) {
|
||||
emit('update:chatId', '')
|
||||
emit('update:currentAbstract', '')
|
||||
emit('refresh')
|
||||
}
|
||||
})
|
||||
|
||||
function handleScroll(event: any) {
|
||||
if (
|
||||
props.chatId !== 'new' &&
|
||||
event.scrollTop === 0 &&
|
||||
paginationConfig.total > recordList.value.length
|
||||
) {
|
||||
const history_height = event.dialogScrollbar.offsetHeight
|
||||
paginationConfig.current_page += 1
|
||||
getChatRecord().then(() => {
|
||||
event.scrollDiv.setScrollTop(event.dialogScrollbar.offsetHeight - history_height)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const open = () => {
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.single-line {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.chat-record-drawer {
|
||||
.el-drawer__body {
|
||||
background: var(--app-layout-bg-color);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:deep(.el-divider__text) {
|
||||
background: var(--app-layout-bg-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,307 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="$t('views.log.editContent')"
|
||||
v-model="dialogVisible"
|
||||
width="600"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
:rules="rules"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item :label="$t('views.paragraph.relatedProblem.title')">
|
||||
<el-input
|
||||
v-model="form.problem_text"
|
||||
:placeholder="$t('views.paragraph.relatedProblem.title')"
|
||||
maxlength="256"
|
||||
show-word-limit
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('common.content')" prop="content">
|
||||
<MdEditor
|
||||
v-model="form.content"
|
||||
:placeholder="$t('views.log.form.content.placeholder')"
|
||||
:maxLength="100000"
|
||||
:preview="false"
|
||||
:toolbars="toolbars"
|
||||
style="height: 300px"
|
||||
@onUploadImg="onUploadImg"
|
||||
:footers="footers"
|
||||
>
|
||||
<template #defFooters>
|
||||
<span style="margin-left: -6px">/ 100000</span>
|
||||
</template>
|
||||
</MdEditor>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('common.title')">
|
||||
<el-input
|
||||
show-word-limit
|
||||
v-model="form.title"
|
||||
:placeholder="$t('views.log.form.title.placeholder')"
|
||||
maxlength="256"
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('views.log.selectDataset')" prop="dataset_id">
|
||||
<el-select
|
||||
v-model="form.dataset_id"
|
||||
filterable
|
||||
:placeholder="$t('views.log.selectDatasetPlaceholder')"
|
||||
:loading="optionLoading"
|
||||
@change="changeDataset"
|
||||
>
|
||||
<el-option v-for="item in datasetList" :key="item.id" :label="item.name" :value="item.id">
|
||||
<span class="flex align-center">
|
||||
<AppAvatar
|
||||
v-if="!item.dataset_id && item.type === '1'"
|
||||
class="mr-12 avatar-purple"
|
||||
shape="square"
|
||||
:size="24"
|
||||
>
|
||||
<img src="@/assets/icon_web.svg" style="width: 58%" alt="" />
|
||||
</AppAvatar>
|
||||
<AppAvatar
|
||||
v-else-if="!item.dataset_id && item.type === '2'"
|
||||
class="mr-8 avatar-purple"
|
||||
shape="square"
|
||||
:size="24"
|
||||
style="background: none"
|
||||
>
|
||||
<img src="@/assets/logo_lark.svg" style="width: 100%" alt="" />
|
||||
</AppAvatar>
|
||||
<AppAvatar
|
||||
v-else-if="!item.dataset_id && item.type === '0'"
|
||||
class="mr-12 avatar-blue"
|
||||
shape="square"
|
||||
:size="24"
|
||||
>
|
||||
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
|
||||
</AppAvatar>
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('views.log.saveToDocument')" prop="document_id">
|
||||
<el-select
|
||||
v-model="form.document_id"
|
||||
filterable
|
||||
:placeholder="$t('views.log.documentPlaceholder')"
|
||||
:loading="optionLoading"
|
||||
@change="changeDocument"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in documentList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.name }}
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="submitForm(formRef)" :loading="loading">
|
||||
{{ $t('common.save') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, reactive } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import logApi from '@/api/log'
|
||||
import imageApi from '@/api/image'
|
||||
import useStore from '@/stores'
|
||||
import { t } from '@/locales'
|
||||
const { application, document, user } = useStore()
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id }
|
||||
} = route as any
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
const formRef = ref()
|
||||
|
||||
const toolbars = [
|
||||
'bold',
|
||||
'underline',
|
||||
'italic',
|
||||
'-',
|
||||
'title',
|
||||
'strikeThrough',
|
||||
'sub',
|
||||
'sup',
|
||||
'quote',
|
||||
'unorderedList',
|
||||
'orderedList',
|
||||
'task',
|
||||
'-',
|
||||
'codeRow',
|
||||
'code',
|
||||
'link',
|
||||
'image',
|
||||
'table',
|
||||
'mermaid',
|
||||
'katex',
|
||||
'-',
|
||||
'revoke',
|
||||
'next',
|
||||
'=',
|
||||
'pageFullscreen',
|
||||
'preview',
|
||||
'htmlPreview'
|
||||
] as any[]
|
||||
|
||||
const footers = ['markdownTotal', 0, '=', 1, 'scrollSwitch']
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const loading = ref(false)
|
||||
|
||||
const form = ref<any>({
|
||||
chat_id: '',
|
||||
record_id: '',
|
||||
problem_text: '',
|
||||
title: '',
|
||||
content: '',
|
||||
dataset_id: '',
|
||||
document_id: ''
|
||||
})
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
content: [{ required: true, message: t('views.log.form.content.placeholder'), trigger: 'blur' }],
|
||||
dataset_id: [
|
||||
{ required: true, message: t('views.log.selectDatasetPlaceholder'), trigger: 'change' }
|
||||
],
|
||||
document_id: [{ required: true, message: t('views.log.documentPlaceholder'), trigger: 'change' }]
|
||||
})
|
||||
|
||||
const datasetList = ref<any[]>([])
|
||||
const documentList = ref<any[]>([])
|
||||
const optionLoading = ref(false)
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
form.value = {
|
||||
chat_id: '',
|
||||
record_id: '',
|
||||
problem_text: '',
|
||||
title: '',
|
||||
content: '',
|
||||
dataset_id: '',
|
||||
document_id: ''
|
||||
}
|
||||
datasetList.value = []
|
||||
documentList.value = []
|
||||
formRef.value?.clearValidate()
|
||||
}
|
||||
})
|
||||
|
||||
const onUploadImg = async (files: any, callback: any) => {
|
||||
const res = await Promise.all(
|
||||
files.map((file: any) => {
|
||||
return new Promise((rev, rej) => {
|
||||
const fd = new FormData()
|
||||
fd.append('file', file)
|
||||
|
||||
imageApi
|
||||
.postImage(fd)
|
||||
.then((res: any) => {
|
||||
rev(res)
|
||||
})
|
||||
.catch((error) => rej(error))
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
callback(res.map((item) => item.data))
|
||||
}
|
||||
|
||||
function changeDataset(dataset_id: string) {
|
||||
localStorage.setItem(id + 'chat_dataset_id', dataset_id)
|
||||
form.value.document_id = ''
|
||||
getDocument(dataset_id)
|
||||
}
|
||||
|
||||
function changeDocument(document_id: string) {
|
||||
localStorage.setItem(id + 'chat_document_id', document_id)
|
||||
}
|
||||
|
||||
function getDocument(dataset_id: string) {
|
||||
document.asyncGetAllDocument(dataset_id, loading).then((res: any) => {
|
||||
documentList.value = res.data
|
||||
if (localStorage.getItem(id + 'chat_document_id')) {
|
||||
form.value.document_id = localStorage.getItem(id + 'chat_document_id') as string
|
||||
}
|
||||
if (!documentList.value.find((v) => v.id === form.value.document_id)) {
|
||||
form.value.document_id = ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getDataset() {
|
||||
application.asyncGetApplicationDataset(id, loading).then((res: any) => {
|
||||
datasetList.value = res.data
|
||||
if (localStorage.getItem(id + 'chat_dataset_id')) {
|
||||
form.value.dataset_id = localStorage.getItem(id + 'chat_dataset_id') as string
|
||||
if (!datasetList.value.find((v) => v.id === form.value.dataset_id)) {
|
||||
form.value.dataset_id = ''
|
||||
form.value.document_id = ''
|
||||
} else {
|
||||
getDocument(form.value.dataset_id)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const open = (data: any) => {
|
||||
getDataset()
|
||||
form.value.chat_id = data.chat_id
|
||||
form.value.record_id = data.id
|
||||
form.value.problem_text = data.problem_text ? data.problem_text.substring(0, 256) : ''
|
||||
form.value.content = data.answer_text
|
||||
formRef.value?.clearValidate()
|
||||
dialogVisible.value = true
|
||||
}
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
const obj = {
|
||||
title: form.value.title,
|
||||
content: form.value.content,
|
||||
problem_text: form.value.problem_text
|
||||
}
|
||||
logApi
|
||||
.putChatRecordLog(
|
||||
id,
|
||||
form.value.chat_id,
|
||||
form.value.record_id,
|
||||
form.value.dataset_id,
|
||||
form.value.document_id,
|
||||
obj,
|
||||
loading
|
||||
)
|
||||
.then((res: any) => {
|
||||
emit('refresh', res.data)
|
||||
dialogVisible.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="$t('views.log.editMark')"
|
||||
v-model="dialogVisible"
|
||||
width="600"
|
||||
class="edit-mark-dialog"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
<template #header="{ titleId, titleClass }">
|
||||
<div class="flex-between">
|
||||
<h4 :id="titleId" :class="titleClass">{{ $t('views.chatLog.editMark') }}</h4>
|
||||
<div class="text-right">
|
||||
<el-button text @click="isEdit = true" v-if="!isEdit">
|
||||
<el-icon><EditPen /></el-icon>
|
||||
</el-button>
|
||||
<el-button text style="margin-left: 4px" @click="deleteMark">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
<el-divider direction="vertical" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-scrollbar>
|
||||
<div style="min-height: 250px; max-height: 350px" v-loading="loading">
|
||||
<el-form
|
||||
v-if="isEdit"
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
:rules="rules"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item prop="content">
|
||||
<el-input
|
||||
v-model="form.content"
|
||||
:placeholder="$t('views.chatLog.form.content.placeholder')"
|
||||
:maxlength="100000"
|
||||
show-word-limit
|
||||
:rows="15"
|
||||
type="textarea"
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span v-else class="pre-wrap">{{ form?.content }}</span>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer" v-if="isEdit">
|
||||
<el-button @click.prevent="isEdit = false"> {{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="submit(formRef)" :loading="loading">
|
||||
{{ $t('common.save') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, reactive } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import chatLogApi from '@/api/application/chat-log'
|
||||
import useStore from '@/stores'
|
||||
import { t } from '@/locales'
|
||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id },
|
||||
} = route as any
|
||||
|
||||
const { paragraph } = useStore()
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const formRef = ref()
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const loading = ref(false)
|
||||
|
||||
const form = ref<any>({})
|
||||
const isEdit = ref(false)
|
||||
const detail = ref<any>({})
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
content: [
|
||||
{ required: true, message: t('views.chatLog.form.content.placeholder'), trigger: 'blur' },
|
||||
],
|
||||
})
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
form.value = {}
|
||||
isEdit.value = false
|
||||
}
|
||||
})
|
||||
|
||||
function deleteMark() {
|
||||
chatLogApi
|
||||
.delMarkChatRecord(
|
||||
id as string,
|
||||
detail.value.chat_id,
|
||||
detail.value.id,
|
||||
form.value.dataset,
|
||||
form.value.document,
|
||||
form.value.id,
|
||||
loading,
|
||||
)
|
||||
.then(() => {
|
||||
emit('refresh')
|
||||
MsgSuccess(t('common.deleteSuccess'))
|
||||
dialogVisible.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function getMark(data: any) {
|
||||
chatLogApi
|
||||
.getMarkChatRecord(
|
||||
id as string,
|
||||
data.chat_id,
|
||||
data.id,
|
||||
data.dataset,
|
||||
data.document,
|
||||
loading,
|
||||
)
|
||||
.then((res: any) => {
|
||||
if (res.data.length > 0) {
|
||||
form.value = res.data[0]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const open = (data: any) => {
|
||||
detail.value = data
|
||||
getMark(data)
|
||||
dialogVisible.value = true
|
||||
}
|
||||
const submit = async (formEl: FormInstance) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
paragraph
|
||||
.asyncPutParagraph(
|
||||
form.value.dataset,
|
||||
form.value.document,
|
||||
form.value.id,
|
||||
{
|
||||
content: form.value.content,
|
||||
},
|
||||
loading,
|
||||
)
|
||||
.then((res) => {
|
||||
dialogVisible.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.edit-mark-dialog {
|
||||
.el-dialog__header.show-close {
|
||||
padding-right: 15px;
|
||||
}
|
||||
.el-dialog__headerbtn {
|
||||
top: 13px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,662 @@
|
|||
<template>
|
||||
<LayoutContainer :header="$t('views.chatLog.title')">
|
||||
<div class="p-24">
|
||||
<div class="mb-16">
|
||||
<el-select
|
||||
v-model="history_day"
|
||||
class="mr-12"
|
||||
@change="changeDayHandle"
|
||||
style="width: 180px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in dayOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
v-if="history_day === 'other'"
|
||||
v-model="daterangeValue"
|
||||
type="daterange"
|
||||
:start-placeholder="$t('views.applicationOverview.monitor.startDatePlaceholder')"
|
||||
:end-placeholder="$t('views.applicationOverview.monitor.endDatePlaceholder')"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
@change="changeDayRangeHandle"
|
||||
/>
|
||||
<el-input
|
||||
v-model="search"
|
||||
@change="getList"
|
||||
:placeholder="$t('common.search')"
|
||||
prefix-icon="Search"
|
||||
class="w-240"
|
||||
clearable
|
||||
/>
|
||||
<div style="display: flex; align-items: center" class="float-right">
|
||||
<el-button @click="dialogVisible = true">{{
|
||||
$t('views.chatLog.buttons.clearStrategy')
|
||||
}}</el-button>
|
||||
<el-button @click="exportLog">{{ $t('common.export') }}</el-button>
|
||||
<el-button @click="openDocumentDialog" :disabled="multipleSelection.length === 0"
|
||||
>{{ $t('views.chatLog.addToKnowledge') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<app-table
|
||||
:data="tableData"
|
||||
:pagination-config="paginationConfig"
|
||||
@sizeChange="getList"
|
||||
@changePage="getList"
|
||||
@row-click="rowClickHandle"
|
||||
v-loading="loading"
|
||||
:row-class-name="setRowClass"
|
||||
@selection-change="handleSelectionChange"
|
||||
class="log-table"
|
||||
ref="multipleTableRef"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column
|
||||
prop="abstract"
|
||||
:label="$t('views.chatLog.table.abstract')"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="chat_record_count"
|
||||
:label="$t('views.chatLog.table.chat_record_count')"
|
||||
align="right"
|
||||
/>
|
||||
<el-table-column prop="star_num" align="right">
|
||||
<template #header>
|
||||
<div>
|
||||
<span>{{ $t('views.chatLog.table.feedback.label') }}</span>
|
||||
<el-popover :width="200" trigger="click" :visible="popoverVisible">
|
||||
<template #reference>
|
||||
<el-button
|
||||
style="margin-top: -2px"
|
||||
:type="filter.min_star || filter.min_trample ? 'primary' : ''"
|
||||
link
|
||||
@click="popoverVisible = !popoverVisible"
|
||||
>
|
||||
<el-icon>
|
||||
<Filter />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</template>
|
||||
<div class="filter">
|
||||
<div class="form-item mb-16">
|
||||
<div @click.stop>
|
||||
{{ $t('views.chatLog.table.feedback.star') }} >=
|
||||
<el-input-number
|
||||
v-model="filter.min_star"
|
||||
:min="0"
|
||||
:step="1"
|
||||
:value-on-clear="0"
|
||||
controls-position="right"
|
||||
style="width: 80px"
|
||||
size="small"
|
||||
step-strictly
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item mb-16">
|
||||
<div @click.stop>
|
||||
{{ $t('views.chatLog.table.feedback.trample') }} >=
|
||||
<el-input-number
|
||||
v-model="filter.min_trample"
|
||||
:min="0"
|
||||
:step="1"
|
||||
:value-on-clear="0"
|
||||
controls-position="right"
|
||||
style="width: 80px"
|
||||
size="small"
|
||||
step-strictly
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<el-button size="small" @click="filterChange('clear')">{{
|
||||
$t('common.clear')
|
||||
}}</el-button>
|
||||
<el-button type="primary" @click="filterChange" size="small">{{
|
||||
$t('common.confirm')
|
||||
}}</el-button>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="{ row }">
|
||||
<span class="mr-8" v-if="!row.trample_num && !row.star_num"> - </span>
|
||||
<span class="mr-8" v-else>
|
||||
<span v-if="row.star_num">
|
||||
<AppIcon iconName="app-like-color"></AppIcon>
|
||||
{{ row.star_num }}
|
||||
</span>
|
||||
<span v-if="row.trample_num" class="ml-8">
|
||||
<AppIcon iconName="app-oppose-color"></AppIcon>
|
||||
{{ row.trample_num }}
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="mark_sum" :label="$t('views.chatLog.table.mark')" align="right" />
|
||||
<el-table-column prop="asker" :label="$t('views.chatLog.table.user')">
|
||||
<template #default="{ row }">
|
||||
{{ row.asker?.user_name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('views.chatLog.table.recenTimes')" width="180">
|
||||
<template #default="{ row }">
|
||||
{{ datetimeFormat(row.update_time) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</app-table>
|
||||
</div>
|
||||
<ChatRecordDrawer
|
||||
:next="nextChatRecord"
|
||||
:pre="preChatRecord"
|
||||
ref="ChatRecordRef"
|
||||
v-model:chatId="currentChatId"
|
||||
v-model:currentAbstract="currentAbstract"
|
||||
:application="detail"
|
||||
:pre_disable="pre_disable"
|
||||
:next_disable="next_disable"
|
||||
@refresh="refresh"
|
||||
/>
|
||||
<el-dialog
|
||||
:title="$t('views.chatLog.buttons.clearStrategy')"
|
||||
v-model="dialogVisible"
|
||||
width="25%"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
<span>{{ $t('common.delete') }}</span>
|
||||
<el-input-number
|
||||
v-model="days"
|
||||
controls-position="right"
|
||||
:min="1"
|
||||
:max="100000"
|
||||
:value-on-clear="0"
|
||||
step-strictly
|
||||
style="width: 110px; margin-left: 8px; margin-right: 8px"
|
||||
></el-input-number>
|
||||
<span>{{ $t('views.chatLog.daysText') }}</span>
|
||||
<template #footer>
|
||||
<div class="dialog-footer" style="margin-top: 16px">
|
||||
<el-button @click="dialogVisible = false">{{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="saveCleanTime">
|
||||
{{ $t('common.save') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
:title="$t('views.chatLog.addToKnowledge')"
|
||||
v-model="documentDialogVisible"
|
||||
width="50%"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
:rules="rules"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item :label="$t('views.chatLog.selectKnowledge')" prop="knowledge_id">
|
||||
<el-select
|
||||
v-model="form.knowledge_id"
|
||||
filterable
|
||||
:placeholder="$t('views.chatLog.selectKnowledgePlaceholder')"
|
||||
:loading="optionLoading"
|
||||
@change="changeKnowledge"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in knowledgeList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
>
|
||||
<span class="flex align-center">
|
||||
<AppAvatar
|
||||
v-if="!item.knowledge_id && item.type === '1'"
|
||||
class="mr-12 avatar-purple"
|
||||
shape="square"
|
||||
:size="24"
|
||||
>
|
||||
<img src="@/assets/icon_web.svg" style="width: 58%" alt="" />
|
||||
</AppAvatar>
|
||||
<AppAvatar
|
||||
v-else-if="!item.knowledge_id && item.type === '2'"
|
||||
class="mr-12 avatar-purple"
|
||||
shape="square"
|
||||
:size="24"
|
||||
style="background: none"
|
||||
>
|
||||
<img src="@/assets/logo_lark.svg" style="width: 100%" alt="" />
|
||||
</AppAvatar>
|
||||
<AppAvatar
|
||||
v-else-if="!item.knowledge_id && item.type === '0'"
|
||||
class="mr-12 avatar-blue"
|
||||
shape="square"
|
||||
:size="24"
|
||||
>
|
||||
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
|
||||
</AppAvatar>
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('views.chatLog.saveToDocument')" prop="document_id">
|
||||
<el-select
|
||||
v-model="form.document_id"
|
||||
filterable
|
||||
:placeholder="$t('views.chatLog.documentPlaceholder')"
|
||||
:loading="optionLoading"
|
||||
@change="changeDocument"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in documentList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.name }}
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="documentDialogVisible = false">
|
||||
{{ $t('common.cancel') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="submitForm(formRef)" :loading="documentLoading">
|
||||
{{ $t('common.save') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</LayoutContainer>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, type Ref, onMounted, reactive, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import ChatRecordDrawer from './component/ChatRecordDrawer.vue'
|
||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||
import chatLogApi from '@/api/application/chat-log'
|
||||
import { beforeDay, datetimeFormat, nowDate } from '@/utils/time'
|
||||
import useStore from '@/stores'
|
||||
import type { Dict } from '@/api/type/common'
|
||||
import { t } from '@/locales'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { ElTable } from 'element-plus'
|
||||
|
||||
const { application, chatLog, document, user } = useStore()
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id },
|
||||
} = route as any
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
const formRef = ref()
|
||||
|
||||
const dayOptions = [
|
||||
{
|
||||
value: 7,
|
||||
// @ts-ignore
|
||||
label: t('views.applicationOverview.monitor.pastDayOptions.past7Days'), // 使用 t 方法来国际化显示文本
|
||||
},
|
||||
{
|
||||
value: 30,
|
||||
label: t('views.applicationOverview.monitor.pastDayOptions.past30Days'),
|
||||
},
|
||||
{
|
||||
value: 90,
|
||||
label: t('views.applicationOverview.monitor.pastDayOptions.past90Days'),
|
||||
},
|
||||
{
|
||||
value: 183,
|
||||
label: t('views.applicationOverview.monitor.pastDayOptions.past183Days'),
|
||||
},
|
||||
{
|
||||
value: 'other',
|
||||
label: t('views.applicationOverview.monitor.pastDayOptions.other'),
|
||||
},
|
||||
]
|
||||
const daterangeValue = ref('')
|
||||
// 提交日期时间
|
||||
const daterange = ref({
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
})
|
||||
|
||||
const multipleTableRef = ref<InstanceType<typeof ElTable>>()
|
||||
const multipleSelection = ref<any[]>([])
|
||||
|
||||
const ChatRecordRef = ref()
|
||||
const loading = ref(false)
|
||||
const documentLoading = ref(false)
|
||||
const paginationConfig = reactive({
|
||||
current_page: 1,
|
||||
page_size: 20,
|
||||
total: 0,
|
||||
})
|
||||
const dialogVisible = ref(false)
|
||||
const documentDialogVisible = ref(false)
|
||||
const days = ref<number>(180)
|
||||
const tableData = ref<any[]>([])
|
||||
const tableIndexMap = computed<Dict<number>>(() => {
|
||||
return tableData.value
|
||||
.map((row, index) => ({
|
||||
[row.id]: index,
|
||||
}))
|
||||
.reduce((pre, next) => ({ ...pre, ...next }), {})
|
||||
})
|
||||
const history_day = ref<number | string>(7)
|
||||
|
||||
const search = ref('')
|
||||
const detail = ref<any>(null)
|
||||
|
||||
const currentChatId = ref<string>('')
|
||||
const currentAbstract = ref<string>('')
|
||||
const popoverVisible = ref(false)
|
||||
const defaultFilter = {
|
||||
min_star: 0,
|
||||
min_trample: 0,
|
||||
comparer: 'and',
|
||||
}
|
||||
const filter = ref<any>({
|
||||
min_star: 0,
|
||||
min_trample: 0,
|
||||
comparer: 'and',
|
||||
})
|
||||
|
||||
const form = ref<any>({
|
||||
knowledge_id: '',
|
||||
document_id: '',
|
||||
})
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
knowledge_id: [
|
||||
{ required: true, message: t('views.chatLog.selectKnowledgePlaceholder'), trigger: 'change' },
|
||||
],
|
||||
document_id: [
|
||||
{
|
||||
required: true,
|
||||
message: t('views.chatLog.documentPlaceholder'),
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const optionLoading = ref(false)
|
||||
const documentList = ref<any[]>([])
|
||||
|
||||
function filterChange(val: string) {
|
||||
if (val === 'clear') {
|
||||
filter.value = cloneDeep(defaultFilter)
|
||||
}
|
||||
getList()
|
||||
popoverVisible.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 下一页
|
||||
*/
|
||||
const nextChatRecord = () => {
|
||||
let index = tableIndexMap.value[currentChatId.value] + 1
|
||||
if (index >= tableData.value.length) {
|
||||
if (
|
||||
index + (paginationConfig.current_page - 1) * paginationConfig.page_size >=
|
||||
paginationConfig.total - 1
|
||||
) {
|
||||
return
|
||||
}
|
||||
paginationConfig.current_page = paginationConfig.current_page + 1
|
||||
getList().then(() => {
|
||||
index = 0
|
||||
currentChatId.value = tableData.value[index].id
|
||||
currentAbstract.value = tableData.value[index].abstract
|
||||
})
|
||||
} else {
|
||||
currentChatId.value = tableData.value[index].id
|
||||
currentAbstract.value = tableData.value[index].abstract
|
||||
}
|
||||
}
|
||||
const pre_disable = computed(() => {
|
||||
let index = tableIndexMap.value[currentChatId.value] - 1
|
||||
return index < 0 && paginationConfig.current_page <= 1
|
||||
})
|
||||
|
||||
const next_disable = computed(() => {
|
||||
let index = tableIndexMap.value[currentChatId.value] + 1
|
||||
return (
|
||||
index >= tableData.value.length &&
|
||||
index + (paginationConfig.current_page - 1) * paginationConfig.page_size >=
|
||||
paginationConfig.total - 1
|
||||
)
|
||||
})
|
||||
/**
|
||||
* 上一页
|
||||
*/
|
||||
const preChatRecord = () => {
|
||||
let index = tableIndexMap.value[currentChatId.value] - 1
|
||||
|
||||
if (index < 0) {
|
||||
if (paginationConfig.current_page <= 1) {
|
||||
return
|
||||
}
|
||||
paginationConfig.current_page = paginationConfig.current_page - 1
|
||||
getList().then(() => {
|
||||
index = paginationConfig.page_size - 1
|
||||
currentChatId.value = tableData.value[index].id
|
||||
currentAbstract.value = tableData.value[index].abstract
|
||||
})
|
||||
} else {
|
||||
currentChatId.value = tableData.value[index].id
|
||||
currentAbstract.value = tableData.value[index].abstract
|
||||
}
|
||||
}
|
||||
|
||||
function rowClickHandle(row: any, column?: any) {
|
||||
if (column && column.type === 'selection') {
|
||||
return
|
||||
}
|
||||
currentChatId.value = row.id
|
||||
currentAbstract.value = row.abstract
|
||||
ChatRecordRef.value.open()
|
||||
}
|
||||
|
||||
const setRowClass = ({ row }: any) => {
|
||||
return currentChatId.value === row?.id ? 'highlight' : ''
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: any[]) => {
|
||||
multipleSelection.value = val
|
||||
}
|
||||
|
||||
// function deleteLog(row: any) {
|
||||
// MsgConfirm(`是否删除对话:${row.abstract} ?`, `删除后无法恢复,请谨慎操作。`, {
|
||||
// confirmButtonText: t('common.delete'),
|
||||
// confirmButtonClass: 'danger'
|
||||
// })
|
||||
// .then(() => {
|
||||
// loading.value = true
|
||||
// logApi.delChatLog(id as string, row.id, loading).then(() => {
|
||||
// MsgSuccess(t('common.deleteSuccess'))
|
||||
// getList()
|
||||
// })
|
||||
// })
|
||||
// .catch(() => {})
|
||||
// }
|
||||
|
||||
function getList() {
|
||||
let obj: any = {
|
||||
start_time: daterange.value.start_time,
|
||||
end_time: daterange.value.end_time,
|
||||
...filter.value,
|
||||
}
|
||||
if (search.value) {
|
||||
obj = { ...obj, abstract: search.value }
|
||||
}
|
||||
return chatLog.asyncGetChatLog(id as string, paginationConfig, obj, loading).then((res: any) => {
|
||||
tableData.value = res.data.records
|
||||
if (currentChatId.value) {
|
||||
currentChatId.value = tableData.value[0]?.id
|
||||
}
|
||||
paginationConfig.total = res.data.total
|
||||
})
|
||||
}
|
||||
|
||||
function getDetail(isLoading = false) {
|
||||
application
|
||||
.asyncGetApplicationDetail(id as string, isLoading ? loading : undefined)
|
||||
.then((res: any) => {
|
||||
detail.value = res.data
|
||||
days.value = res.data.clean_time
|
||||
})
|
||||
}
|
||||
|
||||
const exportLog = () => {
|
||||
const arr: string[] = []
|
||||
multipleSelection.value.map((v) => {
|
||||
if (v) {
|
||||
arr.push(v.id)
|
||||
}
|
||||
})
|
||||
if (detail.value) {
|
||||
let obj: any = {
|
||||
start_time: daterange.value.start_time,
|
||||
end_time: daterange.value.end_time,
|
||||
...filter.value,
|
||||
}
|
||||
if (search.value) {
|
||||
obj = { ...obj, abstract: search.value }
|
||||
}
|
||||
|
||||
chatLogApi.postExportChatLog(detail.value.id, detail.value.name, obj, { select_ids: arr }, loading)
|
||||
}
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
getList()
|
||||
}
|
||||
|
||||
function changeDayRangeHandle(val: string) {
|
||||
daterange.value.start_time = val[0]
|
||||
daterange.value.end_time = val[1]
|
||||
getList()
|
||||
}
|
||||
|
||||
function changeDayHandle(val: number | string) {
|
||||
if (val !== 'other') {
|
||||
daterange.value.start_time = beforeDay(val)
|
||||
daterange.value.end_time = nowDate
|
||||
getList()
|
||||
}
|
||||
}
|
||||
|
||||
function saveCleanTime() {
|
||||
const obj = {
|
||||
clean_time: days.value,
|
||||
}
|
||||
application
|
||||
.asyncPutApplication(id as string, obj, loading)
|
||||
.then(() => {
|
||||
MsgSuccess(t('common.saveSuccess'))
|
||||
dialogVisible.value = false
|
||||
getDetail(true)
|
||||
})
|
||||
.catch(() => {
|
||||
dialogVisible.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function changeKnowledge(knowledge_id: string) {
|
||||
localStorage.setItem(id + 'chat_knowledge_id', knowledge_id)
|
||||
form.value.document_id = ''
|
||||
getDocument(knowledge_id)
|
||||
}
|
||||
|
||||
function changeDocument(document_id: string) {
|
||||
localStorage.setItem(id + 'chat_document_id', document_id)
|
||||
}
|
||||
|
||||
const knowledgeList = ref<any[]>([])
|
||||
|
||||
function getKnowledge() {
|
||||
application.asyncGetApplicationKnowledge(id, documentLoading).then((res: any) => {
|
||||
knowledgeList.value = res.data
|
||||
if (localStorage.getItem(id + 'chat_knowledge_id')) {
|
||||
form.value.knowledge_id = localStorage.getItem(id + 'chat_knowledge_id') as string
|
||||
if (!knowledgeList.value.find((v) => v.id === form.value.knowledge_id)) {
|
||||
form.value.knowledge_id = ''
|
||||
form.value.document_id = ''
|
||||
} else {
|
||||
getDocument(form.value.knowledge_id)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
const arr: string[] = []
|
||||
multipleSelection.value.map((v) => {
|
||||
if (v) {
|
||||
arr.push(v.id)
|
||||
}
|
||||
})
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
const obj = {
|
||||
document_id: form.value.document_id,
|
||||
knowledge_id: form.value.knowledge_id,
|
||||
chat_ids: arr,
|
||||
}
|
||||
chatLogApi.postChatLogAddKnowledge(id, obj, documentLoading).then((res: any) => {
|
||||
multipleTableRef.value?.clearSelection()
|
||||
documentDialogVisible.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getDocument(knowledge_id: string) {
|
||||
document.asyncGetAllDocument(knowledge_id, documentLoading).then((res: any) => {
|
||||
documentList.value = res.data
|
||||
if (localStorage.getItem(id + 'chat_document_id')) {
|
||||
form.value.document_id = localStorage.getItem(id + 'chat_document_id') as string
|
||||
}
|
||||
if (!documentList.value.find((v) => v.id === form.value.document_id)) {
|
||||
form.value.document_id = ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function openDocumentDialog() {
|
||||
getKnowledge()
|
||||
formRef.value?.clearValidate()
|
||||
documentDialogVisible.value = true
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
changeDayHandle(history_day.value)
|
||||
getDetail()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.log-table {
|
||||
:deep(tr) {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -10,10 +10,10 @@
|
|||
label-width="auto"
|
||||
ref="DatasetNodeFormRef"
|
||||
>
|
||||
<el-form-item :label="$t('views.log.selectDataset')">
|
||||
<el-form-item :label="$t('views.chatLog.selectDataset')">
|
||||
<template #label>
|
||||
<div class="flex-between">
|
||||
<span>{{ $t('views.log.selectDataset') }}</span>
|
||||
<span>{{ $t('views.chatLog.selectDataset') }}</span>
|
||||
<el-button type="primary" link @click="openDatasetDialog">
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-button>
|
||||
|
|
|
|||
Loading…
Reference in New Issue