feat: chat log

This commit is contained in:
wangdan-fit2cloud 2025-06-11 14:45:12 +08:00
parent e9527bdd2b
commit 238821e96b
9 changed files with 237 additions and 144 deletions

View File

@ -0,0 +1,95 @@
<template>
<el-card
shadow="hover"
class="card-checkbox cursor"
:class="modelValue.includes(toModelValue) ? 'active' : ''"
@click="checked"
>
<div class="flex-between">
<div class="flex align-center">
<slot name="icon">
<KnowledgeIcon :type="data.type" />
</slot>
<slot></slot>
</div>
<el-checkbox v-bind:modelValue="modelValue.includes(toModelValue)" @change="checkboxChange">
</el-checkbox>
</div>
</el-card>
</template>
<script setup lang="ts">
import KnowledgeIcon from '@/views/knowledge/component/KnowledgeIcon.vue'
import { computed } from 'vue'
defineOptions({ name: 'CardCheckbox' })
const props = defineProps<{
data: any
modelValue: Array<any>
valueField?: string
}>()
const toModelValue = computed(() => (props.valueField ? props.data[props.valueField] : props.data))
// const isChecked = computed({
// get: () => props.modelValue.includes(toModelValue.value)),
// set: (val) => val
// })
const emit = defineEmits(['update:modelValue', 'change'])
const checked = () => {
const value = props.modelValue ? props.modelValue : []
if (props.modelValue.includes(toModelValue.value)) {
emit(
'update:modelValue',
value.filter((item) => item !== toModelValue.value),
)
} else {
emit('update:modelValue', [...value, toModelValue.value])
}
checkboxChange()
}
function checkboxChange() {
emit('change')
}
</script>
<style lang="scss">
.card-checkbox {
&.active {
border: 1px solid var(--el-color-primary);
}
input.checkbox[type='checkbox'] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
height: 14px;
width: 14px;
position: relative;
&::after {
position: absolute;
top: 0;
background-color: white;
color: #000;
height: 13px;
width: 13px;
visibility: visible;
text-align: center;
box-sizing: border-box;
border: var(--el-border);
border-radius: var(--el-border-radius-small);
box-sizing: content-box;
content: '';
}
&:checked::after {
content: '✓';
color: #ffffff;
border-color: var(--el-color-primary);
background: var(--el-color-primary);
}
}
}
</style>

View File

@ -20,6 +20,7 @@ import MdEditor from './markdown/MdEditor.vue'
import MdPreview from './markdown/MdPreview.vue'
import MdEditorMagnify from './markdown/MdEditorMagnify.vue'
import TagEllipsis from './tag-ellipsis/index.vue'
import CardCheckbox from './card-checkbox/index.vue'
export default {
install(app: App) {
app.component('LogoFull', LogoFull)
@ -43,5 +44,6 @@ export default {
app.component('MdEditor', MdEditor)
app.component('MdEditorMagnify', MdEditorMagnify)
app.component('TagEllipsis', TagEllipsis)
app.component('CardCheckbox', CardCheckbox)
},
}

View File

@ -64,7 +64,7 @@ const ApplicationDetailRouter = {
icon: 'app-document',
iconActive: 'app-document-active',
title: 'views.chatLog.title',
active: 'log',
active: 'chat-log',
parentPath: '/application/:id/:type',
parentName: 'ApplicationDetail'
},

View File

@ -35,7 +35,7 @@ const useApplicationStore = defineStore('application', {
})
},
async asyncGetApplicationDataset(id: string, loading?: Ref<boolean>) {
async asyncGetApplicationKnowledge(id: string, loading?: Ref<boolean>) {
return new Promise((resolve, reject) => {
applicationApi
.getApplicationDataset(id, loading)

View File

@ -424,8 +424,8 @@ function mapToUrlParams(map: any[]) {
}
onMounted(() => {
getDetail()
getAccessToken()
// getDetail()
// getAccessToken()
// changeDayHandle(history_day.value)
})
</script>

View File

@ -89,118 +89,127 @@
</el-dropdown>
</div>
</template>
<div>
<el-row v-if="applicationList.length > 0" :gutter="15">
<template v-for="(item, index) in applicationList" :key="index">
<el-col
v-if="item.resource_type === 'folder'"
:xs="24"
:sm="12"
:md="12"
:lg="8"
:xl="6"
class="mb-16"
>
<CardBox
:title="item.name"
:description="item.desc || $t('common.noData')"
class="cursor"
<div v-loading.fullscreen.lock="paginationConfig.current_page === 1 && loading">
<InfiniteScroll
:size="applicationList.length"
:total="paginationConfig.total"
:page_size="paginationConfig.page_size"
v-model:current_page="paginationConfig.current_page"
@load="getList"
:loading="loading"
>
<el-row v-if="applicationList.length > 0" :gutter="15">
<template v-for="(item, index) in applicationList" :key="index">
<el-col
v-if="item.resource_type === 'folder'"
:xs="24"
:sm="12"
:md="12"
:lg="8"
:xl="6"
class="mb-16"
>
<template #icon>
<el-avatar shape="square" :size="32" style="background: none">
<AppIcon iconName="app-folder" style="font-size: 32px"></AppIcon>
</el-avatar>
</template>
<template #subTitle>
<el-text class="color-secondary lighter" size="small">
{{ $t('common.creator') }}: {{ item.username }}
</el-text>
</template>
</CardBox>
</el-col>
<el-col v-else :xs="24" :sm="12" :md="12" :lg="8" :xl="6" class="mb-16">
<CardBox
:title="item.name"
:description="item.desc"
class="cursor"
@click="router.push({ path: `/application/${item.id}/${item.type}/overview` })"
>
<template #icon>
<LogoIcon height="28px" style="width: 28px; height: 28px; display: block" />
</template>
<template #subTitle>
<el-text class="color-secondary" size="small">
<auto-tooltip :content="item.username">
<CardBox
:title="item.name"
:description="item.desc || $t('common.noData')"
class="cursor"
>
<template #icon>
<el-avatar shape="square" :size="32" style="background: none">
<AppIcon iconName="app-folder" style="font-size: 32px"></AppIcon>
</el-avatar>
</template>
<template #subTitle>
<el-text class="color-secondary lighter" size="small">
{{ $t('common.creator') }}: {{ item.username }}
</auto-tooltip>
</el-text>
</template>
<template #tag>
<el-tag type="warning" v-if="isWorkFlow(item.type)" style="height: 22px">
{{ $t('views.application.workflow') }}
</el-tag>
<el-tag class="blue-tag" v-else style="height: 22px">
{{ $t('views.application.simple') }}
</el-tag>
</template>
</el-text>
</template>
</CardBox>
</el-col>
<el-col v-else :xs="24" :sm="12" :md="12" :lg="8" :xl="6" class="mb-16">
<CardBox
:title="item.name"
:description="item.desc"
class="cursor"
@click="router.push({ path: `/application/${item.id}/${item.type}/overview` })"
>
<template #icon>
<LogoIcon height="28px" style="width: 28px; height: 28px; display: block" />
</template>
<template #subTitle>
<el-text class="color-secondary" size="small">
<auto-tooltip :content="item.username">
{{ $t('common.creator') }}: {{ item.username }}
</auto-tooltip>
</el-text>
</template>
<template #tag>
<el-tag type="warning" v-if="isWorkFlow(item.type)" style="height: 22px">
{{ $t('views.application.workflow') }}
</el-tag>
<el-tag class="blue-tag" v-else style="height: 22px">
{{ $t('views.application.simple') }}
</el-tag>
</template>
<template #footer>
<div v-if="item.is_publish" class="flex align-center">
<el-icon class="color-success mr-8" style="font-size: 16px">
<SuccessFilled />
</el-icon>
<span class="color-secondary">
{{ $t('views.application.status.published') }}
</span>
<el-divider direction="vertical" />
<el-icon class="mr-8"><Clock /></el-icon>
<span class="color-secondary">{{ dateFormat(item.update_time) }}</span>
</div>
<div v-else class="flex align-center">
<AppIcon iconName="app-disabled" class="color-secondary mr-8"></AppIcon>
<span class="color-secondary">
{{ $t('views.application.status.unpublished') }}
</span>
</div>
</template>
<template #mouseEnter>
<div @click.stop>
<el-dropdown trigger="click">
<el-button text @click.stop>
<el-icon>
<MoreFilled />
</el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click.stop="getAccessToken(item.id)">
<AppIcon iconName="app-create-chat"></AppIcon>
{{ $t('views.application.operation.toChat') }}
</el-dropdown-item>
<el-dropdown-item @click.stop="settingApplication(item)">
<el-icon><Setting /></el-icon>
{{ $t('common.setting') }}
</el-dropdown-item>
<el-dropdown-item divided @click.stop="exportApplication(item)">
<AppIcon iconName="app-export"></AppIcon>
{{ $t('common.export') }}
</el-dropdown-item>
<el-dropdown-item
divided
icon="Delete"
@click.stop="deleteApplication(item)"
>{{ $t('common.delete') }}</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
</CardBox>
</el-col>
</template>
</el-row>
<el-empty :description="$t('common.noData')" v-else />
<template #footer>
<div v-if="item.is_publish" class="flex align-center">
<el-icon class="color-success mr-8" style="font-size: 16px">
<SuccessFilled />
</el-icon>
<span class="color-secondary">
{{ $t('views.application.status.published') }}
</span>
<el-divider direction="vertical" />
<el-icon class="mr-8"><Clock /></el-icon>
<span class="color-secondary">{{ dateFormat(item.update_time) }}</span>
</div>
<div v-else class="flex align-center">
<AppIcon iconName="app-disabled" class="color-secondary mr-8"></AppIcon>
<span class="color-secondary">
{{ $t('views.application.status.unpublished') }}
</span>
</div>
</template>
<template #mouseEnter>
<div @click.stop>
<el-dropdown trigger="click">
<el-button text @click.stop>
<el-icon>
<MoreFilled />
</el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click.stop="getAccessToken(item.id)">
<AppIcon iconName="app-create-chat"></AppIcon>
{{ $t('views.application.operation.toChat') }}
</el-dropdown-item>
<el-dropdown-item @click.stop="settingApplication(item)">
<el-icon><Setting /></el-icon>
{{ $t('common.setting') }}
</el-dropdown-item>
<el-dropdown-item divided @click.stop="exportApplication(item)">
<AppIcon iconName="app-export"></AppIcon>
{{ $t('common.export') }}
</el-dropdown-item>
<el-dropdown-item
divided
icon="Delete"
@click.stop="deleteApplication(item)"
>{{ $t('common.delete') }}</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
</CardBox>
</el-col>
</template>
</el-row>
<el-empty :description="$t('common.noData')" v-else />
</InfiniteScroll>
</div>
</ContentContainer>
<CreateApplicationDialog ref="CreateApplicationDialogRef" />

View File

@ -1,6 +1,8 @@
<template>
<LayoutContainer :header="$t('views.chatLog.title')">
<div class="p-24">
<div class="p-16-24">
<h2 class="mb-16">{{ $t('views.chatLog.title') }}</h2>
<el-card style="--el-card-padding: 24px">
<div class="mb-16">
<el-select
v-model="history_day"
@ -24,6 +26,8 @@
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
@change="changeDayRangeHandle"
style="width: 240px;"
class="mr-12"
/>
<el-input
v-model="search"
@ -153,7 +157,7 @@
</template>
</el-table-column>
</app-table>
</div>
</el-card>
<ChatRecordDrawer
:next="nextChatRecord"
:pre="preChatRecord"
@ -223,31 +227,7 @@
: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>
<KnowledgeIcon :type="item.type" v-if="!item.knowledge_id" />
{{ item.name }}
</span>
</el-option>
@ -283,12 +263,13 @@
</span>
</template>
</el-dialog>
</LayoutContainer>
</div>
</template>
<script setup lang="ts">
import { ref, type Ref, onMounted, reactive, computed } from 'vue'
import { useRoute } from 'vue-router'
import { cloneDeep } from 'lodash'
import KnowledgeIcon from '@/views/knowledge/component/KnowledgeIcon.vue'
import ChatRecordDrawer from './component/ChatRecordDrawer.vue'
import { MsgSuccess, MsgConfirm } from '@/utils/message'
import chatLogApi from '@/api/application/chat-log'
@ -542,7 +523,13 @@ const exportLog = () => {
obj = { ...obj, abstract: search.value }
}
chatLogApi.postExportChatLog(detail.value.id, detail.value.name, obj, { select_ids: arr }, loading)
chatLogApi.postExportChatLog(
detail.value.id,
detail.value.name,
obj,
{ select_ids: arr },
loading,
)
}
}

View File

@ -134,7 +134,7 @@ import { set } from 'lodash'
import { app } from '@/main'
import NodeContainer from '@/workflow/common/NodeContainer.vue'
import NodeCascader from '@/workflow/common/NodeCascader.vue'
import AddDatasetDialog from '@/views/application/component/AddDatasetDialog.vue'
import AddDatasetDialog from '@/views/application/component/AddKnowledgeDialog.vue'
import ParamSettingDialog from '@/views/application/component/ParamSettingDialog.vue'
import type { FormInstance } from 'element-plus'
import { ref, computed, onMounted } from 'vue'