mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-31 10:12:51 +00:00
feat: Add a back to bottom button to the chat page(#2957)
This commit is contained in:
parent
1fb280aa3d
commit
0c53e31b2a
|
|
@ -3,8 +3,10 @@
|
|||
<div class="text-center mb-8" v-if="loading">
|
||||
<el-button class="border-primary video-stop-button" @click="stopChat">
|
||||
<app-icon iconName="app-video-stop" class="mr-8"></app-icon>
|
||||
{{ $t('chat.operation.stopChat') }}</el-button>
|
||||
{{ $t('chat.operation.stopChat') }}</el-button
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="operate-textarea">
|
||||
<el-scrollbar max-height="136">
|
||||
<div
|
||||
|
|
@ -332,7 +334,7 @@ const props = withDefaults(
|
|||
available: true,
|
||||
},
|
||||
)
|
||||
const emit = defineEmits(['update:chatId', 'update:loading', 'update:showUserInput'])
|
||||
const emit = defineEmits(['update:chatId', 'update:loading', 'update:showUserInput', 'backBottom'])
|
||||
const chartOpenId = ref<string>()
|
||||
const chatId_context = computed({
|
||||
get: () => {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,12 @@
|
|||
position: relative;
|
||||
color: var(--app-text-color);
|
||||
box-sizing: border-box;
|
||||
|
||||
.back-bottom-button {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
top: -30px;
|
||||
}
|
||||
&__content {
|
||||
padding-top: 0;
|
||||
box-sizing: border-box;
|
||||
|
|
@ -16,7 +22,6 @@
|
|||
}
|
||||
|
||||
.content {
|
||||
|
||||
:deep(ol) {
|
||||
margin-left: 16px !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,35 +66,39 @@
|
|||
</TransitionContent>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
|
||||
<ChatInputOperate
|
||||
:app-id="appId"
|
||||
:application-details="applicationDetails"
|
||||
:is-mobile="isMobile"
|
||||
:type="type"
|
||||
:send-message="sendMessage"
|
||||
:open-chat-id="openChatId"
|
||||
:validate="validate"
|
||||
:chat-management="ChatManagement"
|
||||
v-model:chat-id="chartOpenId"
|
||||
v-model:loading="loading"
|
||||
v-model:show-user-input="showUserInput"
|
||||
v-if="type !== 'log'"
|
||||
>
|
||||
<template #userInput>
|
||||
<el-button
|
||||
v-if="isUserInput || isAPIInput"
|
||||
class="user-input-button mb-8"
|
||||
@click="toggleUserInput"
|
||||
>
|
||||
<AppIcon iconName="app-edit" :size="16" class="mr-4"></AppIcon>
|
||||
<span class="ellipsis">
|
||||
{{ userInputTitle || $t('chat.userInput') }}
|
||||
</span>
|
||||
</el-button>
|
||||
</template>
|
||||
</ChatInputOperate>
|
||||
|
||||
<div style="position: relative">
|
||||
<!-- 置底按钮 -->
|
||||
<el-button v-if="isBottom" circle class="back-bottom-button" @click="setScrollBottom">
|
||||
<el-icon><ArrowDownBold /></el-icon>
|
||||
</el-button>
|
||||
<ChatInputOperate
|
||||
:app-id="appId"
|
||||
:application-details="applicationDetails"
|
||||
:is-mobile="isMobile"
|
||||
:type="type"
|
||||
:send-message="sendMessage"
|
||||
:open-chat-id="openChatId"
|
||||
:validate="validate"
|
||||
:chat-management="ChatManagement"
|
||||
v-model:chat-id="chartOpenId"
|
||||
v-model:loading="loading"
|
||||
v-model:show-user-input="showUserInput"
|
||||
v-if="type !== 'log'"
|
||||
>
|
||||
<template #userInput>
|
||||
<el-button
|
||||
v-if="isUserInput || isAPIInput"
|
||||
class="user-input-button mb-8"
|
||||
@click="toggleUserInput"
|
||||
>
|
||||
<AppIcon iconName="app-edit" :size="16" class="mr-4"></AppIcon>
|
||||
<span class="ellipsis">
|
||||
{{ userInputTitle || $t('chat.userInput') }}
|
||||
</span>
|
||||
</el-button>
|
||||
</template>
|
||||
</ChatInputOperate>
|
||||
</div>
|
||||
<Control></Control>
|
||||
</template>
|
||||
</div>
|
||||
|
|
@ -603,10 +607,12 @@ function chatMessage(chat?: any, problem?: string, re_chat?: boolean, other_para
|
|||
const scrollTop = ref(0)
|
||||
|
||||
const scorll = ref(true)
|
||||
const isBottom = ref(false)
|
||||
|
||||
const getMaxHeight = () => {
|
||||
return dialogScrollbar.value!.scrollHeight
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动滚动条到最上面
|
||||
* @param $event
|
||||
|
|
@ -621,6 +627,8 @@ const handleScrollTop = ($event: any) => {
|
|||
} else {
|
||||
scorll.value = false
|
||||
}
|
||||
isBottom.value =
|
||||
scrollTop.value + scrollDiv.value.wrapRef.offsetHeight < dialogScrollbar.value!.scrollHeight
|
||||
emit('scroll', { ...$event, dialogScrollbar: dialogScrollbar.value, scrollDiv: scrollDiv.value })
|
||||
}
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
<el-pagination
|
||||
v-model:current-page="paginationConfig.current_page"
|
||||
v-model:page-size="paginationConfig.page_size"
|
||||
:page-sizes="pageSizes"
|
||||
:page-sizes="paginationConfig.page_sizes|| pageSizes"
|
||||
:total="paginationConfig.total"
|
||||
layout="total, prev, pager, next, sizes"
|
||||
@size-change="handleSizeChange"
|
||||
|
|
|
|||
|
|
@ -290,6 +290,11 @@
|
|||
height: 20px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.el-tag--plain.el-tag--info {
|
||||
color: var(--app-text-color-secondary);
|
||||
font-weight: 500;
|
||||
border-color: var(--app-text-color-light-1);
|
||||
}
|
||||
|
||||
// el-select
|
||||
.el-select__selected-item {
|
||||
|
|
|
|||
|
|
@ -207,6 +207,7 @@ const paginationConfig = reactive({
|
|||
current_page: 1,
|
||||
page_size: 10,
|
||||
total: 0,
|
||||
page_sizes: [10, 20, 50, 100, 1000],
|
||||
})
|
||||
|
||||
const filterText = ref('')
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@
|
|||
<template #title>
|
||||
<div>
|
||||
{{ item.name }}
|
||||
<el-tag v-if="item.version" class="ml-4">
|
||||
<el-tag v-if="item.version" class="ml-4" type="info" effect="plain">
|
||||
{{ item.version }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,29 @@
|
|||
<template>
|
||||
<CardBox :title="props.tool.name" :description="props.tool.desc" class="cursor">
|
||||
<template #icon>
|
||||
<el-avatar v-if="isAppIcon(props.tool?.icon)" shape="square" :size="32" style="background: none">
|
||||
<el-avatar
|
||||
v-if="isAppIcon(props.tool?.icon)"
|
||||
shape="square"
|
||||
:size="32"
|
||||
style="background: none"
|
||||
>
|
||||
<img :src="resetUrl(props.tool?.icon)" alt="" />
|
||||
</el-avatar>
|
||||
<el-avatar v-else-if="props.tool?.name" :name="props.tool?.name" pinyinColor shape="square" :size="32" />
|
||||
<el-avatar
|
||||
v-else-if="props.tool?.name"
|
||||
:name="props.tool?.name"
|
||||
pinyinColor
|
||||
shape="square"
|
||||
:size="32"
|
||||
/>
|
||||
</template>
|
||||
<template #title>
|
||||
<div>
|
||||
{{ props.tool?.name }}
|
||||
<el-tag v-if="props.tool?.version" class="ml-4" type="info" effect="plain">
|
||||
{{ props.tool?.version }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
<template #subTitle>
|
||||
<el-text class="color-secondary" size="small">
|
||||
|
|
@ -29,24 +48,24 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import {isAppIcon, resetUrl} from '@/utils/common'
|
||||
import { isAppIcon, resetUrl } from '@/utils/common'
|
||||
|
||||
const props = defineProps<{
|
||||
tool: any,
|
||||
tool: any
|
||||
getSubTitle: (v: any) => string
|
||||
addLoading: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'handleAdd'): void;
|
||||
(e: 'handleDetail'): void;
|
||||
}>();
|
||||
(e: 'handleAdd'): void
|
||||
(e: 'handleDetail'): void
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-card {
|
||||
:deep(.card-footer) {
|
||||
&>div:first-of-type {
|
||||
& > div:first-of-type {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,34 @@
|
|||
<template>
|
||||
<el-dialog v-model="dialogVisible" width="1000" append-to-body class="tool-store-dialog" align-center
|
||||
:close-on-click-modal="false" :close-on-press-escape="false">
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
width="1000"
|
||||
append-to-body
|
||||
class="tool-store-dialog"
|
||||
align-center
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
<template #header="{ titleId }">
|
||||
<div class="dialog-header flex-between mb-8">
|
||||
<h4 :id="titleId" class="medium">
|
||||
{{ $t('views.tool.toolStore.title') }}
|
||||
</h4>
|
||||
<el-radio-group v-model="toolType" @change="radioChange" class="app-radio-button-group">
|
||||
<el-radio-button value="INTERNAL">{{ $t('views.tool.toolStore.internal') }}</el-radio-button>
|
||||
<el-radio-button value="INTERNAL">{{
|
||||
$t('views.tool.toolStore.internal')
|
||||
}}</el-radio-button>
|
||||
<el-radio-button value="APPSTORE">{{ $t('views.tool.toolStore.title') }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
|
||||
<div class="flex align-center" style="margin-right: 28px;">
|
||||
<el-input v-model="searchValue" :placeholder="$t('common.search')" prefix-icon="Search" class="w-240 mr-8"
|
||||
clearable @change="getList" />
|
||||
<div class="flex align-center" style="margin-right: 28px">
|
||||
<el-input
|
||||
v-model="searchValue"
|
||||
:placeholder="$t('common.search')"
|
||||
prefix-icon="Search"
|
||||
class="w-240 mr-8"
|
||||
clearable
|
||||
@change="getList"
|
||||
/>
|
||||
<el-divider direction="vertical" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -21,23 +36,40 @@
|
|||
|
||||
<LayoutContainer v-loading="loading">
|
||||
<template #left>
|
||||
<el-anchor direction="vertical" :offset="130" type="default" container=".category-scrollbar"
|
||||
@click="handleClick">
|
||||
<el-anchor-link v-for="category in categories" :key="category.id" :href="`#category-${category.id}`"
|
||||
:title="category.title" />
|
||||
<el-anchor
|
||||
direction="vertical"
|
||||
:offset="130"
|
||||
type="default"
|
||||
container=".category-scrollbar"
|
||||
@click="handleClick"
|
||||
>
|
||||
<el-anchor-link
|
||||
v-for="category in categories"
|
||||
:key="category.id"
|
||||
:href="`#category-${category.id}`"
|
||||
:title="category.title"
|
||||
/>
|
||||
</el-anchor>
|
||||
</template>
|
||||
|
||||
<el-scrollbar class="layout-bg" wrap-class="p-16-24 category-scrollbar">
|
||||
<template v-if="filterList === null">
|
||||
<div v-for="category in categories" :key="category.id">
|
||||
<h4 class="title-decoration-1 mb-16 mt-8 color-text-primary" :id="`category-${category.id}`">
|
||||
<h4
|
||||
class="title-decoration-1 mb-16 mt-8 color-text-primary"
|
||||
:id="`category-${category.id}`"
|
||||
>
|
||||
{{ category.title }}
|
||||
</h4>
|
||||
<el-row :gutter="16">
|
||||
<el-col v-for="tool in category.tools" :key="tool.id" :span="12" class="mb-16">
|
||||
<ToolCard :tool="tool" :addLoading="addLoading" :get-sub-title="getSubTitle"
|
||||
@handleAdd="handleOpenAdd(tool)" @handleDetail="handleDetail(tool)" />
|
||||
<ToolCard
|
||||
:tool="tool"
|
||||
:addLoading="addLoading"
|
||||
:get-sub-title="getSubTitle"
|
||||
@handleAdd="handleOpenAdd(tool)"
|
||||
@handleDetail="handleDetail(tool)"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
|
@ -49,8 +81,13 @@
|
|||
</h4>
|
||||
<el-row :gutter="16" v-if="filterList.length">
|
||||
<el-col v-for="tool in filterList" :key="tool.id" :span="12" class="mb-16">
|
||||
<ToolCard :tool="tool" :addLoading="addLoading" :get-sub-title="getSubTitle"
|
||||
@handleAdd="handleOpenAdd(tool)" @handleDetail="handleDetail(tool)" />
|
||||
<ToolCard
|
||||
:tool="tool"
|
||||
:addLoading="addLoading"
|
||||
:get-sub-title="getSubTitle"
|
||||
@handleAdd="handleOpenAdd(tool)"
|
||||
@handleDetail="handleDetail(tool)"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-empty v-else :description="$t('common.noData')" />
|
||||
|
|
@ -70,7 +107,7 @@ import ToolCard from './ToolCard.vue'
|
|||
import { MsgSuccess } from '@/utils/message'
|
||||
import InternalDescDrawer from './InternalDescDrawer.vue'
|
||||
import AddInternalToolDialog from './AddInternalToolDialog.vue'
|
||||
import { loadSharedApi } from "@/utils/dynamics-api/shared-api.ts";
|
||||
import { loadSharedApi } from '@/utils/dynamics-api/shared-api.ts'
|
||||
import useStore from '@/stores'
|
||||
const { user } = useStore()
|
||||
interface ToolCategory {
|
||||
|
|
@ -81,7 +118,7 @@ interface ToolCategory {
|
|||
const props = defineProps({
|
||||
apiType: {
|
||||
type: String as () => 'workspace' | 'systemShare' | 'systemManage',
|
||||
default: 'workspace'
|
||||
default: 'workspace',
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
|
@ -102,12 +139,12 @@ const categories = ref<ToolCategory[]>([
|
|||
{
|
||||
id: 'web_search',
|
||||
title: t('views.tool.toolStore.webSearch'),
|
||||
tools: []
|
||||
tools: [],
|
||||
},
|
||||
{
|
||||
id: 'database_search',
|
||||
title: t('views.tool.toolStore.databaseQuery'),
|
||||
tools: []
|
||||
tools: [],
|
||||
},
|
||||
// {
|
||||
// id: 'image',
|
||||
|
|
@ -128,7 +165,7 @@ const categories = ref<ToolCategory[]>([
|
|||
const filterList = ref<any>(null)
|
||||
|
||||
function getSubTitle(tool: any) {
|
||||
return categories.value.find(i => i.id === tool.label)?.title ?? ''
|
||||
return categories.value.find((i) => i.id === tool.label)?.title ?? ''
|
||||
}
|
||||
|
||||
function open(id: string) {
|
||||
|
|
@ -156,7 +193,7 @@ async function getInternalToolList() {
|
|||
filterList.value = res.data
|
||||
} else {
|
||||
filterList.value = null
|
||||
categories.value.forEach(category => {
|
||||
categories.value.forEach((category) => {
|
||||
// if (category.id === 'recommend') {
|
||||
// category.tools = res.data
|
||||
// } else {
|
||||
|
|
@ -183,7 +220,7 @@ async function getStoreToolList() {
|
|||
categories.value = tags.map((tag: any) => ({
|
||||
id: tag.key,
|
||||
title: tag.name, // 国际化
|
||||
tools: storeTools.filter((tool: any) => tool.label === tag.key)
|
||||
tools: storeTools.filter((tool: any) => tool.label === tag.key),
|
||||
}))
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
|
@ -244,7 +281,7 @@ async function handleStoreAdd(tool: any) {
|
|||
download_callback_url: tool.downloadCallbackUrl,
|
||||
icon: tool.icon,
|
||||
versions: tool.versions,
|
||||
label: tool.label
|
||||
label: tool.label,
|
||||
}
|
||||
await loadSharedApi({ type: 'tool', systemType: props.apiType })
|
||||
.addStoreTool(tool.id, obj, addLoading)
|
||||
|
|
@ -268,6 +305,9 @@ defineExpose({ open })
|
|||
<style lang="scss">
|
||||
.tool-store-dialog {
|
||||
padding: 0;
|
||||
.el-dialog__headerbtn {
|
||||
top: 7px;
|
||||
}
|
||||
|
||||
.el-dialog__header {
|
||||
padding: 12px 20px 4px 24px;
|
||||
|
|
@ -314,7 +354,6 @@ defineExpose({ open })
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.category-scrollbar {
|
||||
|
|
|
|||
Loading…
Reference in New Issue