refactor(web-antd): 修正 Tinyflow 组件中的导入路径

- 将 './ui/typing' 修改为 './ui/typeing'(可能是原代码中的拼写错误)
- 将 './ui/typing' 修改为 './ui/index',以符合常见的模块入口文件结构
This commit is contained in:
gjd
2025-06-16 13:16:07 +08:00
parent 0f701dd49b
commit 31a98ba9f8
27 changed files with 700 additions and 1592 deletions

View File

@@ -303,23 +303,26 @@ onMounted(async () => {
</script>
<template>
<Layout.Sider width="260px" class="conversation-container h-full">
<Layout.Sider
width="260px"
class="conversation-container relative flex h-full flex-col justify-between overflow-hidden bg-[hsl(var(--primary-foreground))!important] p-[10px_10px_0]"
>
<Drawer />
<!-- 左顶部对话 -->
<div class="flex h-full" style="flex-direction: column">
<div class="flex h-full flex-col">
<Button
class="w-1/1 btn-new-conversation"
class="btn-new-conversation h-[38px] w-full"
type="primary"
@click="createConversation"
>
<IconifyIcon icon="ep:plus" class="mr-[5px]" />
新建对话
</Button>
<!-- 左顶部搜索对话 -->
<Input
v-model:value="searchName"
size="large"
class="search-input mt-[10px]"
class="search-input mt-[20px]"
placeholder="搜索历史记录"
@keyup="searchConversation"
>
@@ -329,50 +332,57 @@ onMounted(async () => {
</Input>
<!-- 左中间对话列表 -->
<div class="conversation-list">
<div class="conversation-list mt-[10px] flex-1 overflow-auto">
<!-- 情况一加载中 -->
<Empty v-if="loading" description="." v-loading="loading" />
<!-- 情况二按照 group 分组展示聊天会话 list 列表 -->
<!-- 情况二按照 group 分组 -->
<div
v-for="conversationKey in Object.keys(conversationMap)"
:key="conversationKey"
class=""
>
<div
class="conversation-item classify-title"
v-if="conversationMap[conversationKey].length > 0"
class="conversation-item classify-title pt-[10px]"
>
<b class="mx-1">
<b class="mx-[4px]">
{{ conversationKey }}
</b>
</div>
<div
class="conversation-item"
v-for="conversation in conversationMap[conversationKey]"
:key="conversation.id"
@click="handleConversationClick(conversation.id)"
@mouseover="hoverConversationId = conversation.id"
@mouseout="hoverConversationId = null"
class="conversation-item mt-[5px]"
>
<div
:class="
conversation.id === activeConversationId
? 'conversation active'
: 'conversation'
"
class="conversation flex cursor-pointer flex-row items-center justify-between rounded-[5px] px-[5px] leading-[30px]"
:class="[
conversation.id === activeConversationId ? 'bg-[#e6e6e6]' : '',
]"
>
<div class="title-wrapper">
<div class="title-wrapper flex items-center">
<img
class="avatar"
class="avatar h-[25px] w-[25px] rounded-[5px]"
:src="conversation.roleAvatar ?? '/static/gpt.svg'"
/>
<span class="title">{{ conversation.title }}</span>
<span
class="title text-black/77 max-w-[150px] overflow-hidden text-ellipsis whitespace-nowrap px-[10px] py-[2px] text-[14px] font-normal"
>
{{ conversation.title }}
</span>
</div>
<div
class="button-wrapper"
v-show="hoverConversationId === conversation.id"
class="button-wrapper relative right-[2px] flex items-center text-[#606266]"
>
<Button
class="btn"
class="btn mr-0 px-[5px]"
type="link"
@click.stop="handleTop(conversation)"
>
@@ -386,14 +396,14 @@ onMounted(async () => {
></span>
</Button>
<Button
class="btn"
class="btn mr-0 px-[5px]"
type="link"
@click.stop="updateConversationTitle(conversation)"
>
<IconifyIcon icon="ep:edit" />
</Button>
<Button
class="btn"
class="btn mr-0 px-[5px]"
type="link"
@click.stop="deleteChatConversation(conversation)"
>
@@ -404,143 +414,29 @@ onMounted(async () => {
</div>
</div>
</div>
<!-- 底部占位 -->
<div class="w-100% h-[50px]"></div>
<!-- 底部占位 -->
<div class="h-[50px] w-full"></div>
</div>
<!-- 左底部工具栏 -->
<div class="tool-box">
<div @click="handleRoleRepository">
<div
class="tool-box absolute bottom-0 left-0 right-0 flex items-center justify-between bg-[#f4f4f4] px-[20px] leading-[35px] text-[var(--el-text-color)] shadow-[0_0_1px_1px_rgba(228,228,228,0.8)]"
>
<div
class="flex cursor-pointer items-center text-[#606266]"
@click="handleRoleRepository"
>
<IconifyIcon icon="ep:user" />
<span>角色仓库</span>
<span class="ml-[5px]">角色仓库</span>
</div>
<div @click="handleClearConversation">
<div
class="flex cursor-pointer items-center text-[#606266]"
@click="handleClearConversation"
>
<IconifyIcon icon="ep:delete" />
<span>清空未置顶对话</span>
<span class="ml-[5px]">清空未置顶对话</span>
</div>
</div>
</Layout.Sider>
</template>
<style scoped lang="scss">
.conversation-container {
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 10px 10px 0;
overflow: hidden;
background-color: hsl(var(--primary-foreground));
.btn-new-conversation {
width: 100%;
height: 38px;
}
.search-input {
margin-top: 20px;
}
.conversation-list {
height: 100%;
overflow: auto;
.classify-title {
padding-top: 10px;
}
.conversation-item {
margin-top: 5px;
}
.conversation {
display: flex;
flex: 1;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 0 5px;
line-height: 30px;
cursor: pointer;
border-radius: 5px;
&.active {
background-color: #e6e6e6;
.button {
display: inline-block;
}
}
.title-wrapper {
display: flex;
flex-direction: row;
align-items: center;
}
.title {
max-width: 150px;
padding: 2px 10px;
overflow: hidden;
text-overflow: ellipsis;
font-size: 14px;
font-weight: 400;
color: rgb(0 0 0 / 77%);
white-space: nowrap;
}
.avatar {
display: flex;
flex-direction: row;
justify-items: center;
width: 25px;
height: 25px;
border-radius: 5px;
}
// 对话编辑、删除
.button-wrapper {
right: 2px;
display: flex;
flex-direction: row;
place-items: center center;
color: #606266;
.btn {
padding: 0 5px 0 0;
margin: 0;
}
}
}
}
// 角色仓库、清空未设置对话
.tool-box {
position: absolute;
right: 0;
bottom: 0;
left: 0;
display: flex;
align-items: center;
justify-content: space-between;
//width: 100%;
padding: 0 20px;
line-height: 35px;
color: var(--el-text-color);
background-color: #f4f4f4;
box-shadow: 0 0 1px 1px rgb(228 228 228 / 80%);
> div {
display: flex;
align-items: center;
padding: 0;
margin: 0;
color: #606266;
cursor: pointer;
> span {
margin-left: 5px;
}
}
}
}
</style>

View File

@@ -113,66 +113,92 @@ onMounted(async () => {
</script>
<template>
<div ref="messageContainer" class="relative h-full overflow-y-auto">
<div class="chat-list" v-for="(item, index) in list" :key="index">
<!-- 靠左 messagesystemassistant 类型 -->
<div class="left-message message-item" v-if="item.type !== 'user'">
<div
v-for="(item, index) in list"
:key="index"
class="mt-[50px] flex flex-col overflow-y-hidden px-[20px]"
>
<!-- 左侧消息systemassistant -->
<div v-if="item.type !== 'user'" class="flex flex-row">
<div class="avatar">
<Avatar :src="roleAvatar" />
</div>
<div class="message">
<div>
<div class="time">{{ formatDate(item.createTime) }}</div>
<div class="mx-[15px] flex flex-col text-left">
<div class="text-left leading-[30px]">
{{ formatDate(item.createTime) }}
</div>
<div class="left-text-container">
<MarkdownView class="left-text" :content="item.content" />
<div
class="relative flex flex-col break-words rounded-[10px] bg-[#e4e4e4cc] p-[10px] pb-[5px] pt-[10px] shadow-[0_0_0_1px_rgba(228,228,228,0.8)]"
>
<MarkdownView
class="text-[0.95rem] text-[#393939]"
:content="item.content"
/>
<MessageKnowledge v-if="item.segments" :segments="item.segments" />
</div>
<div class="left-btns">
<div class="mt-[8px] flex flex-row">
<Button
class="btn-cus"
class="flex items-center bg-transparent px-[5px] hover:bg-[#f6f6f6]"
type="text"
@click="copyContent(item.content)"
>
<img class="btn-image" src="/static/copy.svg" />
<img class="h-[20px]" src="/static/copy.svg" />
</Button>
<Button
v-if="item.id > 0"
class="btn-cus"
class="flex items-center bg-transparent px-[5px] hover:bg-[#f6f6f6]"
type="text"
@click="onDelete(item.id)"
>
<img class="btn-image h-[17px]" src="/static/delete.svg" />
<img class="h-[17px]" src="/static/delete.svg" />
</Button>
</div>
</div>
</div>
<!-- 靠右 messageuser 类型 -->
<div class="right-message message-item" v-if="item.type === 'user'">
<!-- 右侧消息user -->
<div v-else class="flex flex-row-reverse justify-start">
<div class="avatar">
<Avatar :src="userAvatar" />
</div>
<div class="message">
<div>
<div class="time">{{ formatDate(item.createTime) }}</div>
<div class="mx-[15px] flex flex-col text-left">
<div class="text-left leading-[30px]">
{{ formatDate(item.createTime) }}
</div>
<div class="right-text-container">
<div class="right-text">{{ item.content }}</div>
<div class="flex flex-row-reverse">
<div
class="inline w-auto whitespace-pre-wrap break-words rounded-[10px] bg-[#267fff] p-[10px] text-[0.95rem] text-white shadow-[0_0_0_1px_#267fff]"
>
{{ item.content }}
</div>
</div>
<div class="right-btns">
<div class="mt-[8px] flex flex-row-reverse">
<Button
class="btn-cus"
class="flex items-center bg-transparent px-[5px] hover:bg-[#f6f6f6]"
type="text"
@click="copyContent(item.content)"
>
<img class="btn-image" src="/static/copy.svg" />
<img class="h-[20px]" src="/static/copy.svg" />
</Button>
<Button class="btn-cus" type="text" @click="onDelete(item.id)">
<img class="btn-image h-[17px]" src="/static/delete.svg" />
<Button
class="flex items-center bg-transparent px-[5px] hover:bg-[#f6f6f6]"
type="text"
@click="onDelete(item.id)"
>
<img class="h-[17px]" src="/static/delete.svg" />
</Button>
<Button class="btn-cus" type="text" @click="onRefresh(item)">
<Button
class="flex items-center bg-transparent px-[5px] hover:bg-[#f6f6f6]"
type="text"
@click="onRefresh(item)"
>
<span class="icon-[ant-design--redo-outlined]"></span>
</Button>
<Button class="btn-cus" type="text" @click="onEdit(item)">
<Button
class="flex items-center bg-transparent px-[5px] hover:bg-[#f6f6f6]"
type="text"
@click="onEdit(item)"
>
<span class="icon-[ant-design--form-outlined]"></span>
</Button>
</div>
@@ -180,117 +206,15 @@ onMounted(async () => {
</div>
</div>
</div>
<!-- 回到底部 -->
<div v-if="isScrolling" class="to-bottom" @click="handleGoBottom">
<!-- 回到底部按钮 -->
<div
v-if="isScrolling"
class="absolute bottom-0 right-1/2 z-[1000]"
@click="handleGoBottom"
>
<Button shape="circle">
<span class="icon-[ant-design--down-outlined]"></span>
</Button>
</div>
</template>
<style scoped lang="scss">
// 中间
.chat-list {
display: flex;
flex-direction: column;
padding: 0 20px;
overflow-y: hidden;
.message-item {
margin-top: 50px;
}
.left-message {
display: flex;
flex-direction: row;
}
.right-message {
display: flex;
flex-direction: row-reverse;
justify-content: flex-start;
}
.message {
display: flex;
flex-direction: column;
margin: 0 15px;
text-align: left;
.time {
line-height: 30px;
text-align: left;
}
.left-text-container {
position: relative;
display: flex;
flex-direction: column;
padding: 10px 10px 5px;
overflow-wrap: break-word;
background-color: rgb(228 228 228 / 80%);
border-radius: 10px;
box-shadow: 0 0 0 1px rgb(228 228 228 / 80%);
.left-text {
font-size: 0.95rem;
color: #393939;
}
}
.right-text-container {
display: flex;
flex-direction: row-reverse;
.right-text {
display: inline;
width: auto;
padding: 10px;
font-size: 0.95rem;
color: #fff;
overflow-wrap: break-word;
white-space: pre-wrap;
background-color: #267fff;
border-radius: 10px;
box-shadow: 0 0 0 1px #267fff;
}
}
.left-btns {
display: flex;
flex-direction: row;
margin-top: 8px;
}
.right-btns {
display: flex;
flex-direction: row-reverse;
margin-top: 8px;
}
}
// 复制、删除按钮
.btn-cus {
display: flex;
align-items: center;
padding: 0 5px;
background-color: transparent;
.btn-image {
height: 20px;
}
}
.btn-cus:hover {
cursor: pointer;
background-color: #f6f6f6;
}
}
// 回到底部
.to-bottom {
position: absolute;
right: 50%;
bottom: 0;
z-index: 1000;
}
</style>

View File

@@ -16,16 +16,21 @@ const handlerPromptClick = async (prompt: any) => {
};
</script>
<template>
<div class="chat-empty">
<!-- title -->
<div class="center-container">
<div class="title">芋道 AI</div>
<div class="role-list">
<div class="relative flex h-full w-full flex-row justify-center">
<!-- center-container -->
<div class="flex flex-col justify-center">
<!-- title -->
<div class="text-center text-[28px] font-bold">芋道 AI</div>
<!-- role-list -->
<div
class="mt-[20px] flex w-[460px] flex-wrap items-center justify-center"
>
<div
class="role-item"
v-for="prompt in promptList"
:key="prompt.prompt"
@click="handlerPromptClick(prompt)"
class="m-[10px] flex w-[180px] cursor-pointer justify-center rounded-[10px] border border-[#e4e4e4] leading-[50px] hover:bg-[rgba(243,243,243,0.73)]"
>
{{ prompt.prompt }}
</div>
@@ -33,49 +38,3 @@ const handlerPromptClick = async (prompt: any) => {
</div>
</div>
</template>
<style scoped lang="scss">
.chat-empty {
position: relative;
display: flex;
flex-direction: row;
justify-content: center;
width: 100%;
height: 100%;
.center-container {
display: flex;
flex-direction: column;
justify-content: center;
.title {
font-size: 28px;
font-weight: bold;
text-align: center;
}
.role-list {
display: flex;
flex-flow: row wrap;
align-items: center;
justify-content: center;
width: 460px;
margin-top: 20px;
.role-item {
display: flex;
justify-content: center;
width: 180px;
margin: 10px;
line-height: 50px;
cursor: pointer;
border: 1px solid #e4e4e4;
border-radius: 10px;
}
.role-item:hover {
background-color: rgb(243 243 243 / 73%);
}
}
}
}
</style>

View File

@@ -3,13 +3,7 @@ import { Skeleton } from 'ant-design-vue';
</script>
<template>
<div class="message-loading">
<div class="p-[30px]">
<Skeleton active />
</div>
</template>
<style scoped lang="scss">
.message-loading {
padding: 30px;
}
</style>

View File

@@ -10,39 +10,14 @@ const handlerNewChat = () => {
</script>
<template>
<div class="new-chat">
<div class="box-center">
<div class="tip">点击下方按钮开始你的对话吧</div>
<div class="btns">
<div class="flex h-full w-full flex-row justify-center">
<div class="flex flex-col justify-center">
<div class="text-center text-[14px] text-[#858585]">
点击下方按钮开始你的对话吧
</div>
<div class="mt-[20px] flex flex-row justify-center">
<Button type="primary" round @click="handlerNewChat">新建对话</Button>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.new-chat {
display: flex;
flex-direction: row;
justify-content: center;
width: 100%;
height: 100%;
.box-center {
display: flex;
flex-direction: column;
justify-content: center;
.tip {
font-size: 14px;
color: #858585;
}
.btns {
display: flex;
flex-direction: row;
justify-content: center;
margin-top: 20px;
}
}
}
</style>

View File

@@ -25,8 +25,12 @@ const handleCategoryClick = async (category: string) => {
</script>
<template>
<div class="category-list">
<div class="category" v-for="category in categoryList" :key="category">
<div class="flex flex-wrap items-center">
<div
class="mr-[10px] flex flex-row"
v-for="category in categoryList"
:key="category"
>
<Button
size="small"
shape="round"
@@ -38,17 +42,3 @@ const handleCategoryClick = async (category: string) => {
</div>
</div>
</template>
<style scoped lang="scss">
.category-list {
display: flex;
flex-flow: row wrap;
align-items: center;
.category {
display: flex;
flex-direction: row;
margin-right: 10px;
}
}
</style>

View File

@@ -60,10 +60,18 @@ const handleTabsScroll = async () => {
</script>
<template>
<div class="card-list" ref="tabsRef" @scroll="handleTabsScroll">
<div class="card-item" v-for="role in roleList" :key="role.id">
<div
class="relative flex h-full flex-wrap content-start items-start overflow-auto px-[25px] pb-[140px]"
ref="tabsRef"
@scroll="handleTabsScroll"
>
<div
class="mb-[20px] mr-[20px] inline-block"
v-for="role in roleList"
:key="role.id"
>
<Card
class="card"
class="relative rounded-[10px]"
:body-style="{
position: 'relative',
display: 'flex',
@@ -75,7 +83,7 @@ const handleTabsScroll = async () => {
}"
>
<!-- 更多操作 -->
<div class="more-container" v-if="showMore">
<div v-if="showMore" class="absolute right-[12px] top-0">
<Dropdown>
<Button type="text">
<span class="icon-[ant-design--more-outlined] text-2xl"></span>
@@ -91,23 +99,32 @@ const handleTabsScroll = async () => {
<Menu.Item @click="handleMoreClick(['delete', role])">
<div class="flex items-center">
<IconifyIcon icon="ep:delete" color="red" />
<span style="color: red">编辑</span>
<span class="text-red-500">编辑</span>
</div>
</Menu.Item>
</Menu>
</template>
</Dropdown>
</div>
<!-- 角色信息 -->
<div>
<img class="avatar" :src="role.avatar" />
<img
:src="role.avatar"
class="h-[40px] w-[40px] overflow-hidden rounded-[10px]"
/>
</div>
<div class="right-container">
<div class="content-container">
<div class="title">{{ role.name }}</div>
<div class="description">{{ role.description }}</div>
<div class="ml-[10px] w-full">
<div class="h-[85px]">
<div class="max-w-[140px] text-[18px] font-bold text-[#3e3e3e]">
{{ role.name }}
</div>
<div class="mt-[10px] text-[14px] text-[#6a6a6a]">
{{ role.description }}
</div>
</div>
<div class="btn-container">
<div class="mt-[2px] flex flex-row-reverse">
<Button type="primary" size="small" @click="handleUseClick(role)">
使用
</Button>
@@ -117,67 +134,3 @@ const handleTabsScroll = async () => {
</div>
</div>
</template>
<style scoped lang="scss">
// 卡片列表
.card-list {
position: relative;
display: flex;
flex-flow: row wrap;
place-content: flex-start start;
align-items: start;
height: 100%;
padding: 0 25px;
padding-bottom: 140px;
overflow: auto;
.card {
position: relative;
display: inline-block;
margin-right: 20px;
margin-bottom: 20px;
border-radius: 10px;
.more-container {
position: absolute;
top: 0;
right: 12px;
}
.avatar {
width: 40px;
height: 40px;
overflow: hidden;
border-radius: 10px;
}
.right-container {
width: 100%;
margin-left: 10px;
//height: 100px;
.content-container {
height: 85px;
.title {
max-width: 140px;
font-size: 18px;
font-weight: bold;
color: #3e3e3e;
}
.description {
margin-top: 10px;
font-size: 14px;
color: #6a6a6a;
}
}
.btn-container {
display: flex;
flex-direction: row-reverse;
margin-top: 2px;
}
}
}
}
</style>

View File

@@ -171,15 +171,18 @@ onMounted(async () => {
<template>
<Drawer>
<Layout class="role-container">
<Layout
class="absolute inset-0 flex h-full w-full flex-col overflow-hidden bg-white"
>
<FormModal @success="handlerAddRoleSuccess" />
<Layout.Content class="role-main">
<div class="search-container">
<!-- 搜索按钮 -->
<Layout.Content class="relative m-0 flex-1 overflow-hidden p-0">
<div class="absolute right-0 top-[-5px] z-[100] mr-[20px] mt-[20px]">
<!-- 搜索输入框 -->
<Input.Search
:loading="loading"
v-model:value="search"
class="search-input"
class="w-[240px]"
placeholder="请输入搜索的内容"
@search="getActiveTabsRole"
/>
@@ -193,12 +196,18 @@ onMounted(async () => {
添加角色
</Button>
</div>
<!-- 标签页内容 -->
<Tabs
v-model:value="activeTab"
class="tabs p-4"
class="relative h-full p-4"
@tab-click="handleTabsClick"
>
<TabPane key="my-role" class="role-pane" tab="我的角色">
<TabPane
key="my-role"
class="flex h-full flex-col overflow-y-auto"
tab="我的角色"
>
<RoleList
:loading="loading"
:role-list="myRoleList"
@@ -210,12 +219,17 @@ onMounted(async () => {
class="mt-[20px]"
/>
</TabPane>
<TabPane key="public-role" class="role-pane" tab="公共角色">
<TabPane
key="public-role"
class="flex h-full flex-col overflow-y-auto"
tab="公共角色"
>
<RoleCategoryList
class="role-category-list"
:category-list="categoryList"
:active="activeCategory"
@on-category-click="handlerCategoryClick"
class="mx-[27px]"
/>
<RoleList
:role-list="publicRoleList"
@@ -232,56 +246,3 @@ onMounted(async () => {
</Layout>
</Drawer>
</template>
<style scoped lang="scss">
// 跟容器
.role-container {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
overflow: hidden;
background-color: #fff;
.role-main {
position: relative;
flex: 1;
padding: 0;
margin: 0;
overflow: hidden;
.search-container {
position: absolute;
top: -5px;
right: 0;
z-index: 100;
margin: 20px 20px 0;
}
.search-input {
width: 240px;
}
.tabs {
position: relative;
height: 100%;
.role-category-list {
margin: 0 27px;
}
}
.role-pane {
position: relative;
display: flex;
flex-direction: column;
height: 100%;
overflow-y: auto;
}
}
}
</style>

View File

@@ -494,7 +494,7 @@ onMounted(async () => {
<template>
<Page auto-content-height>
<Layout class="ai-layout">
<Layout class="absolute left-0 top-0 h-full w-full flex-1">
<!-- 左侧对话列表 -->
<ConversationList
:active-id="activeConversationId"
@@ -504,19 +504,24 @@ onMounted(async () => {
@on-conversation-clear="handleConversationClear"
@on-conversation-delete="handlerConversationDelete"
/>
<Layout class="detail-container">
<Layout.Header class="header">
<div class="title">
<!-- 右侧详情部分 -->
<Layout class="bg-white">
<Layout.Header
class="flex items-center justify-between bg-[#fbfbfb!important] shadow-none"
>
<div class="text-[18px] font-bold">
{{ activeConversation?.title ? activeConversation?.title : '对话' }}
<span v-if="activeMessageList.length > 0">
({{ activeMessageList.length }})
</span>
</div>
<div class="btns" v-if="activeConversation">
<div class="flex w-[300px] justify-end" v-if="activeConversation">
<Button
type="primary"
ghost
class="mr-[10px]"
class="mr-[10px] px-[10px]"
size="small"
@click="openChatConversationUpdateForm"
>
@@ -525,7 +530,7 @@ onMounted(async () => {
</Button>
<Button
size="small"
class="btn mr-[10px]"
class="mr-[10px] px-[10px]"
@click="handlerMessageClear"
>
<IconifyIcon
@@ -533,54 +538,52 @@ onMounted(async () => {
color="#787878"
/>
</Button>
<Button size="small" class="btn mr-[10px]">
<Button size="small" class="mr-[10px] px-[10px]">
<IconifyIcon icon="ep:download" color="#787878" />
</Button>
<Button
size="small"
class="btn mr-[10px]"
class="mr-[10px] px-[10px]"
@click="handleGoTopMessage"
>
<IconifyIcon icon="ep:top" color="#787878" />
</Button>
</div>
</Layout.Header>
<Layout.Content class="main-container">
<div>
<div class="message-container">
<!-- 情况一消息加载中 -->
<MessageLoading v-if="activeMessageListLoading" />
<!-- 情况二无聊天对话时 -->
<MessageNewConversation
v-if="!activeConversation"
@on-new-conversation="handleConversationCreate"
/>
<!-- 情况三消息列表为空 -->
<MessageListEmpty
v-if="
!activeMessageListLoading &&
messageList.length === 0 &&
activeConversation
"
@on-prompt="doSendMessage"
/>
<!-- 情况四:消息列表不为空 -->
<MessageList
v-if="!activeMessageListLoading && messageList.length > 0"
ref="messageRef"
:conversation="activeConversation"
:list="messageList"
@on-delete-success="handleMessageDelete"
@on-edit="handleMessageEdit"
@on-refresh="handleMessageRefresh"
/>
</div>
<Layout.Content class="relative m-0 h-full w-full p-0">
<div class="absolute inset-0 m-0 overflow-y-hidden p-0">
<MessageLoading v-if="activeMessageListLoading" />
<MessageNewConversation
v-if="!activeConversation"
@on-new-conversation="handleConversationCreate"
/>
<MessageListEmpty
v-if="
!activeMessageListLoading &&
messageList.length === 0 &&
activeConversation
"
@on-prompt="doSendMessage"
/>
<MessageList
v-if="!activeMessageListLoading && messageList.length > 0"
ref="messageRef"
:conversation="activeConversation"
:list="messageList"
@on-delete-success="handleMessageDelete"
@on-edit="handleMessageEdit"
@on-refresh="handleMessageRefresh"
/>
</div>
</Layout.Content>
<Layout.Footer class="footer-container">
<form class="prompt-from">
<Layout.Footer class="m-0 flex flex-col bg-[white!important] p-0">
<form
class="m-[10px_20px_20px] flex flex-col rounded-[10px] border border-[#e3e3e3] p-[9px_10px]"
>
<textarea
class="prompt-input"
class="box-border h-[80px] resize-none overflow-auto border-none p-[0_2px] focus:outline-none"
v-model="prompt"
@keydown="handleSendByKeydown"
@input="handlePromptInput"
@@ -588,10 +591,10 @@ onMounted(async () => {
@compositionend="onCompositionend"
placeholder="问我任何问题...Shift+Enter 换行,按下 Enter 发送)"
></textarea>
<div class="prompt-btns">
<div>
<div class="flex justify-between pb-0 pt-[5px]">
<div class="flex items-center">
<Switch v-model:checked="enableContext" />
<span class="ml-5px text-14px text-#8f8f8f">上下文</span>
<span class="ml-[5px] text-[14px] text-[#8f8f8f]">上下文</span>
</div>
<Button
type="primary"
@@ -616,202 +619,3 @@ onMounted(async () => {
<FormModal @success="handleConversationUpdateSuccess" />
</Page>
</template>
<style lang="scss" scoped>
.ai-layout {
position: absolute;
top: 0;
left: 0;
flex: 1;
width: 100%;
height: 100%;
}
.conversation-container {
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 10px 10px 0;
.btn-new-conversation {
padding: 18px 0;
}
.search-input {
margin-top: 20px;
}
.conversation-list {
margin-top: 20px;
.conversation {
display: flex;
flex: 1;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 0 5px;
margin-top: 10px;
line-height: 30px;
cursor: pointer;
border-radius: 5px;
&.active {
background-color: #e6e6e6;
.button {
display: inline-block;
}
}
.title-wrapper {
display: flex;
flex-direction: row;
align-items: center;
}
.title {
max-width: 220px;
padding: 5px 10px;
overflow: hidden;
text-overflow: ellipsis;
font-size: 14px;
white-space: nowrap;
}
.avatar {
display: flex;
flex-direction: row;
justify-items: center;
width: 28px;
height: 28px;
}
// 对话编辑、删除
.button-wrapper {
right: 2px;
display: flex;
flex-direction: row;
justify-items: center;
color: #606266;
.el-icon {
margin-right: 5px;
}
}
}
}
// 角色仓库、清空未设置对话
.tool-box {
display: flex;
align-items: center;
justify-content: space-between;
line-height: 35px;
color: var(--el-text-color);
> div {
display: flex;
align-items: center;
padding: 0;
margin: 0;
color: #606266;
cursor: pointer;
> span {
margin-left: 5px;
}
}
}
}
// 头部
.detail-container {
background: #fff;
.header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
background: #fbfbfb;
box-shadow: 0 0 0 0 #dcdfe6;
.title {
font-size: 18px;
font-weight: bold;
}
.btns {
display: flex;
flex-direction: row;
justify-content: flex-end;
width: 300px;
//justify-content: space-between;
.btn {
padding: 0 10px;
}
}
}
}
// main 容器
.main-container {
position: relative;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
.message-container {
position: absolute;
inset: 0;
padding: 0;
margin: 0;
overflow-y: hidden;
}
}
// 底部
.footer-container {
display: flex;
flex-direction: column;
height: auto;
padding: 0;
margin: 0;
background-color: white;
.prompt-from {
display: flex;
flex-direction: column;
height: auto;
padding: 9px 10px;
margin: 10px 20px 20px;
border: 1px solid #e3e3e3;
border-radius: 10px;
}
.prompt-input {
box-sizing: border-box;
height: 80px;
padding: 0 2px;
overflow: auto;
resize: none;
//box-shadow: none;
border: none;
}
.prompt-input:focus {
outline: none;
}
.prompt-btns {
display: flex;
justify-content: space-between;
padding-top: 5px;
padding-bottom: 0;
}
}
</style>