refactor(web-antd): 修正 Tinyflow 组件中的导入路径
- 将 './ui/typing' 修改为 './ui/typeing'(可能是原代码中的拼写错误) - 将 './ui/typing' 修改为 './ui/index',以符合常见的模块入口文件结构
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
<!-- 靠左 message:system、assistant 类型 -->
|
||||
<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]"
|
||||
>
|
||||
<!-- 左侧消息:system、assistant -->
|
||||
<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>
|
||||
<!-- 靠右 message:user 类型 -->
|
||||
<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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user