Skip to content

Commit

Permalink
feat: improve chat history ui
Browse files Browse the repository at this point in the history
  • Loading branch information
kirklin committed Apr 7, 2024
1 parent c46ee40 commit c672900
Show file tree
Hide file tree
Showing 7 changed files with 222 additions and 63 deletions.
16 changes: 12 additions & 4 deletions apps/admin/src/component/ActionIcon/src/ActionIcon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,30 @@
import ToolTipper from "./ToolTipper.vue";
withDefaults(
defineProps<{ tooltipText?: string;icon: string }>(),
defineProps<{
tooltipText?: string;
icon: string;
transparent?: boolean; // 是否设置背景透明
size?: number; // 按钮大小
}>(),
{
tooltipText: "icon Button",
tooltipText: undefined,
icon: "i-mdi-alert",
transparent: false, // 是否设置背景透明
size: 16, // 按钮大小
},
);
</script>

<template>
<ToolTipper :tooltip-text="tooltipText">
<NEl
tag="button" type="button" class="bg-[var(--action-color)] text-[var(--text-color-base)] w-8 h-8 text-base flex items-center justify-center rounded-lg p-2
tag="button" type="button" class="text-[var(--text-color-base)] w-8 h-8 text-base flex items-center justify-center rounded-lg p-2
hover:bg-[var(--hover-color)]"
:class="{ 'bg-[var(--action-color)]': !transparent, 'bg-transparent': transparent }"
>
<span class="sr-only">{{ tooltipText }}</span>
<CAIcon :icon="icon" :size="16" />
<CAIcon :icon="icon" :size="size" />
</NEl>
</ToolTipper>
</template>
Expand Down
5 changes: 3 additions & 2 deletions apps/admin/src/component/ActionIcon/src/ToolTipper.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts" setup>
import { computed } from "vue";
import type { PopoverPlacement } from "naive-ui";
import { isEmpty } from "@celeris/utils";
interface ToolTipperProps {
// Tooltip 显示的文本信息
Expand All @@ -17,12 +18,12 @@ interface ToolTipperProps {
}
const props = withDefaults(defineProps<ToolTipperProps>(), {
tooltipText: "",
tooltipText: undefined,
placement: "bottom",
contentClass: "",
});
const { tooltipText, placement, contentClass } = toRefs(props);
const shouldShowTooltip = computed(() => Boolean(tooltipText));
const shouldShowTooltip = computed(() => isEmpty(tooltipText));
</script>

<template>
Expand Down
106 changes: 106 additions & 0 deletions apps/admin/src/pages/chat/components/AssistantSidebar/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,119 @@
/**
* Interface representing a chat assistant.
* 表示一个聊天助手的接口。
*/
export interface Assistant {
/**
* Unique identifier for the assistant.
* 聊天助手的唯一标识符。
*/
id: string;

/**
* Name of the assistant.
* 聊天助手的名称。
*/
name: string;

/**
* URL of the assistant's avatar image.
* 聊天助手头像的 URL。
*/
avatar: string;

/**
* Prompt or introductory message for the assistant.
* 聊天助手的提示或介绍性消息。
*/
prompt: string;

/**
* Availability status of the assistant.
* 聊天助手的可用性状态。
*/
available: boolean;

/**
* Date when the assistant was last active.
* 聊天助手上次活动的日期。
*/
lastDate: Date;

/**
* Text representation of the last active date.
* 上次活动日期的文本表示。
*/
lastDateText: string;

/**
* Description or bio of the assistant.
* 聊天助手的描述或简介。
*/
description: string;

/**
* Number of likes received by the assistant.
* 聊天助手收到的点赞数。
*/
likes?: number;

/**
* Number of dislikes received by the assistant.
* 聊天助手收到的踩数。
*/
dislikes?: number;

/**
* Number of shares for the assistant.
* 聊天助手的分享数。
*/
shares?: number;
}

/**
* Interface representing summary information of a chat session.
* 表示聊天会话摘要信息的接口。
*/
export interface ChatSummary {
/**
* Unique identifier for the chat summary.
* 聊天摘要的唯一标识符。
*/
id: string;

/**
* ID of the associated chat assistant.
* 相关联的聊天助手的 ID。
*/
assistantId: string;

/**
* Title or summary of the chat session.
* 聊天会话的标题或摘要。
*/
title: string;

/**
* Indicates whether the chat session is archived.
* 表示聊天会话是否已归档。
*/
archived: boolean;
}

/**
* Interface representing the sidebar content for chat history.
* 表示聊天历史侧边栏内容的接口。
*/
export interface ChatHistorySidebar {
/**
* ID of the associated chat assistant.
* 相关联的聊天助手的 ID。
*/
assistantId: string;

/**
* List of chat summaries displayed in the sidebar.
* 显示在侧边栏中的聊天摘要列表。
*/
chatSummaries: ChatSummary[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<script setup lang="ts">
import { renderIcon } from "@celeris/components";
import type { RendererElement, RendererNode, VNode } from "vue";
import type { ChatSummary } from "~/pages/chat/components/AssistantSidebar/types";
import ActionIcon from "~/component/ActionIcon/src/ActionIcon.vue";
defineProps({
chatSummary: {
type: Object as () => ChatSummary,
required: true,
},
});
const showActions = ref(false);
const TrashIcon = "carbon:trash-can";
const MenuHorizontalIcon = "carbon:overflow-menu-horizontal";
const ExportIcon = "tabler:download";
const EditIcon = "tabler:edit";
const ArchiveIcon = "tabler:archive";
interface MenuItem {
label: string;
key: string;
icon?: () => VNode<RendererNode, RendererElement, { [key: string]: any }>;
}
const menuItems: MenuItem[] = [
{
label: "导出",
key: "Export",
icon: renderIcon(ExportIcon),
},
{
label: "重命名",
key: "Edit",
icon: renderIcon(EditIcon),
},
{
label: "删除",
key: "Delete",
icon: renderIcon(TrashIcon),
},
];
const menuOptions = ref<MenuItem[]>(menuItems);
</script>

<template>
<div class="w-full rounded-lg transition ease-out flex mx-2 px-2 py-1.5 hover:bg-[var(--hover-color)]" @mouseover="showActions = true" @mouseleave="showActions = false">
<div class="overflow-hidden flex flex-col grow gap-1 p-1.5">
<NText class="description text-ellipsis">
{{ chatSummary.title }}
</NText>
</div>
<!-- 显示操作组 -->
<div v-show="showActions" class="chat-history-item-actions-group flex gap-x-2">
<div class="menu-options flex justify-center opacity-80">
<NDropdown :options="menuOptions">
<NButton text>
<CAIcon :name="MenuHorizontalIcon" :size="20" />
</NButton>
</NDropdown>
</div>
<ActionIcon tooltip-text="归档" :icon="ArchiveIcon" :size="20" transparent />
</div>
</div>
</template>

<style scoped>
</style>
Original file line number Diff line number Diff line change
@@ -1,69 +1,47 @@
<script setup lang="ts">
interface Item {
uuid: string;
title: string;
isEdit: boolean;
}
import { onMounted, ref } from "vue";
import type { ChatHistorySidebar } from "~/pages/chat/components/AssistantSidebar/types";
import ChatHistoryItem from "~/pages/chat/components/ChatHistorySidebar/components/ChatHistoryItem.vue";
const chatHistory = ref<Item[]>([]);
const chatHistory = ref<ChatHistorySidebar>(); // 使用 ChatHistorySidebar 类型的 ref
// 模拟在组件挂载时获取或设置初始数据
onMounted(() => {
// Fetch or set initial dataSources
chatHistory.value = [
{ uuid: "1", title: "Item 1", isEdit: false },
{ uuid: "2", title: "Item 2", isEdit: false },
// Add more items as needed
];
// 这里可以替换为实际获取聊天历史摘要的逻辑
chatHistory.value = {
assistantId: "1", // 设置相关联的聊天助手 ID
chatSummaries: [
{ id: "1", assistantId: "1", title: "Chat with Customer Support", archived: false },
{ id: "2", assistantId: "1", title: "Product Inquiry", archived: false },
{ id: "3", assistantId: "1", title: "Technical Assistance", archived: false },
{ id: "4", assistantId: "1", title: "Sales Meeting", archived: false },
// 添加更多聊天摘要条目
],
};
});
function isActive(uuid: string): boolean {
return uuid === chatHistory.value[0]?.uuid;
}
function handleSelect(_item: Item): void {
// Handle selection of an item
// For example, update the selected item in your data store
}
function handleEnter(item: Item, isEdit: boolean, event: KeyboardEvent): void {
// Handle pressing Enter when editing an item
if (event.key === "Enter") {
item.isEdit = false; // Assuming you want to exit editing mode on Enter
// You may want to save changes or perform other actions here
}
}
</script>

<template>
<NCollapse :default-expanded-names="['history']" arrow-placement="left" class=" chat-history-list">
<NCollapse :default-expanded-names="['history']" arrow-placement="left" class="chat-history-list">
<NCollapseItem title="历史信息" name="history">
<template #header-extra>
<NBadge :value="chatHistory.length" :max="99" :offset="[0, 0]" color="transparent" />
<NBadge :value="chatHistory?.chatSummaries.length" :max="99" :offset="[0, 0]" color="transparent" />
</template>
<NScrollbar class="max-h-full p-2">
<div class="flex flex-col gap-2 text-sm overflow-hidden">
<template v-if="!chatHistory.length">
<NScrollbar class="max-h-full p-1">
<div class="flex flex-col text-sm overflow-hidden">
<template v-if="!chatHistory?.chatSummaries.length">
<!-- 如果没有聊天摘要,显示空状态 -->
<div class="flex flex-col items-center mt-4 text-center">
<NEmpty />
</div>
</template>
<template v-else>
<div v-for="(item, index) of chatHistory" :key="index">
<a
class="relative flex items-center gap-3 px-3 py-3 break-all border rounded-md cursor-pointer hover:bg-neutral-100 group dark:border-neutral-800 dark:hover:bg-[#24272e]"
:class="isActive(item.uuid) ? ['border-[#4b9e5f]', 'bg-neutral-100', 'text-[#4b9e5f]', 'dark:bg-[#24272e]', 'dark:border-[#4b9e5f]'] : ''"
@click="handleSelect(item)"
>
<div class="relative flex-1 overflow-hidden break-all text-ellipsis whitespace-nowrap">
<NInput
v-if="item.isEdit"
v-model:value="item.title"
size="tiny"
@keypress="handleEnter(item, false, $event)"
/>
<span v-else>{{ item.title }}</span>
</div>
</a>
<!-- 渲染聊天历史摘要列表 -->
<div
v-for="chatSummary in chatHistory?.chatSummaries" :key="chatSummary.id"
class="cursor-pointer flex items-center w-full overflow-hidden transition ease-out border-[var(--border-color)]"
>
<ChatHistoryItem :chat-summary="chatSummary" />
</div>
</template>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { RendererElement, RendererNode, VNode } from "vue";
import { renderIcon } from "@celeris/components";
import type { ChatContextInjectionKey } from "~/pages/chat/chatContext";
import { chatContextInjectionKey } from "~/pages/chat/chatContext";
import ActionIcon from "~/component/ActionIcon/src/ActionIcon.vue";
const { selectedAssistantRef } = inject<ChatContextInjectionKey>(chatContextInjectionKey)!;
const TrashIcon = "carbon:trash-can";
Expand Down Expand Up @@ -49,15 +50,11 @@ const menuOptions = ref<MenuItem[]>(menuItems);
</div>

<div class="actions-groups flex items-center gap-4 opacity-80 mr-4">
<NButton text>
<CAIcon :name="SHARE_ICON" :size="20" />
</NButton>
<ActionIcon tooltip-text="分享" :icon="SHARE_ICON" :size="20" transparent />
</div>
<div class="menu-options flex justify-center opacity-80">
<NDropdown :options="menuOptions">
<NButton text>
<CAIcon :name="MenuHorizontalIcon" :size="24" />
</NButton>
<ActionIcon :icon="MenuHorizontalIcon" :size="20" transparent />
</NDropdown>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import CardInnerIcon from "~/pages/dashboard/components/CardInnerIcon.vue";
<NGrid cols="2" item-responsive responsive="screen" :x-gap="16" :y-gap="16">
<NGridItem>
<!-- API余额监控:使用 "API Balance Monitoring" 来清晰地表达对API余额的追踪和监控。 -->
<DataCard title="API余额监控" centered currency="CNY" :val="642">
<DataCard title="API余额" centered currency="CNY" :val="64">
<template #icon>
<CardInnerIcon icon-name="material-symbols:account-balance-wallet-outline-rounded" container />
</template>
Expand Down

0 comments on commit c672900

Please sign in to comment.