改进 首页和输入框
This commit is contained in:
parent
97574df592
commit
c2a5d6340b
1
src/components.d.ts
vendored
1
src/components.d.ts
vendored
@ -14,6 +14,7 @@ declare module 'vue' {
|
||||
Chat: typeof import('./components/chat/Chat.vue')['default']
|
||||
ChatMenu: typeof import('./components/ChatMenu.vue')['default']
|
||||
ChatSettings: typeof import('./components/settings/ChatSettings.vue')['default']
|
||||
Layout: typeof import('./components/chat/Layout.vue')['default']
|
||||
LeftSettings: typeof import('./components/settings/LeftSettings.vue')['default']
|
||||
LibrarySettings: typeof import('./components/settings/LibrarySettings.vue')['default']
|
||||
Lottie: typeof import('./components/Lottie.vue')['default']
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="flex flex-col items-center justify-between">
|
||||
<div class="flex flex-col items-center justify-between overflow-hidden">
|
||||
<div class="min-w-full md:w-4/5">
|
||||
<n-scrollbar
|
||||
style="max-height: calc(100vh - (var(--header-height) * 3.5))"
|
||||
@ -11,9 +11,12 @@
|
||||
v-if="!chatMessages?.length"
|
||||
>
|
||||
<div class="text-5xl">
|
||||
<n-gradient-text type="success" class="pr-3 pb-2 pt-2 max-w-96 lg:max-w-full overflow-ellipsis overflow-hidden whitespace-nowrap">
|
||||
<n-gradient-text
|
||||
type="success"
|
||||
class="pr-3 pb-2 pt-2 max-w-96 lg:max-w-full overflow-ellipsis overflow-hidden whitespace-nowrap"
|
||||
>
|
||||
<!-- 你好,{{ userStore.user.name }} -->
|
||||
你好
|
||||
你好
|
||||
</n-gradient-text>
|
||||
<br />
|
||||
<div class="pr-3 mt-1 text-2xl">
|
||||
@ -57,8 +60,8 @@
|
||||
</n-scrollbar>
|
||||
</div>
|
||||
|
||||
<div class="w-full pt-2 relative">
|
||||
<div class="fixed bottom-0 left-0 right-0 pr-2 pl-2">
|
||||
<div class="min-w-full pt-2 relative " style="max-height: 120px">
|
||||
<div class="relative bottom-0 pr-2 pl-2 min-w-full" ref="textContainer">
|
||||
<div
|
||||
class="mx-auto w-2xl max-w-2xl text-center mb-3 animate__animated animate__pulse text-lg"
|
||||
v-if="isMobile && chatStore.toolName != ''"
|
||||
@ -87,6 +90,8 @@
|
||||
contenteditable="true"
|
||||
placeholder="请输入文本..."
|
||||
class="input-text max-w-full outline-none text-lg text-pretty pl-2 min-h-6"
|
||||
@change="updateInputPosition"
|
||||
@keyup="updateInputPosition"
|
||||
@keydown="onKeydown"
|
||||
@input="updateInputHeight"
|
||||
@compositionstart="handleCompositionStart"
|
||||
@ -204,7 +209,6 @@
|
||||
</n-modal>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from "../../stores/user";
|
||||
import { onMounted, onUnmounted, Ref, ref } from "vue";
|
||||
import {
|
||||
SendOutline,
|
||||
@ -260,6 +264,7 @@ const compositionStart = ref(false);
|
||||
const inputContainer: any = ref(null);
|
||||
const inputText: any = ref(null);
|
||||
const actionContainer: any = ref(null);
|
||||
const textContainer: any = ref(null);
|
||||
const isPlaceholderVisible = ref(true);
|
||||
const triggerTimes = ref(0);
|
||||
const showSendBtn = ref(false);
|
||||
@ -301,7 +306,16 @@ const updateInputContent = (content: string) => {
|
||||
updateInputHeight();
|
||||
};
|
||||
|
||||
const updateInputPosition = () => {
|
||||
const container = inputContainer.value;
|
||||
const text = textContainer.value;
|
||||
|
||||
// 设置 bottom
|
||||
text.style.bottom = `${container.offsetHeight - 80}px`;
|
||||
};
|
||||
|
||||
function onKeydown(e: KeyboardEvent) {
|
||||
updateInputPosition();
|
||||
// 如果是 Esc
|
||||
if (e.code === "Escape") {
|
||||
assistantStore.selectMenu = false;
|
||||
@ -336,6 +350,8 @@ function updateInputHeight() {
|
||||
return;
|
||||
}
|
||||
|
||||
updateInputPosition();
|
||||
|
||||
const input = inputText.value;
|
||||
const container = inputContainer.value;
|
||||
const action = actionContainer.value;
|
||||
@ -398,10 +414,14 @@ function updateInputHeight() {
|
||||
}
|
||||
|
||||
function handleCompositionStart() {
|
||||
updateInputPosition();
|
||||
|
||||
compositionStart.value = true;
|
||||
}
|
||||
|
||||
function handleCompositionEnd() {
|
||||
updateInputPosition();
|
||||
|
||||
compositionStart.value = false;
|
||||
}
|
||||
|
||||
|
144
src/components/chat/Layout.vue
Normal file
144
src/components/chat/Layout.vue
Normal file
@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<n-layout :has-sider="!isMobile">
|
||||
<n-layout-sider :native-scrollbar="false" v-if="!isMobile">
|
||||
<n-scrollbar :style="'max-height: ' + clientHeight + 'px'">
|
||||
<n-list hoverable clickable v-if="chatStore.chats?.length">
|
||||
<n-list-item
|
||||
v-for="c in chatStore.chats"
|
||||
:key="c.id"
|
||||
:class="
|
||||
c.id === chatStore.currentChat?.id
|
||||
? ' bg-gray-100 dark:bg-gray-700'
|
||||
: ''
|
||||
"
|
||||
@click="viewChat(c.id ?? 0)"
|
||||
>
|
||||
<n-thing>
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
{{ c.name }}
|
||||
</div>
|
||||
<div>
|
||||
<n-button
|
||||
quaternary
|
||||
circle
|
||||
type="warning"
|
||||
@click.stop="deleteChat(c.id ?? 0)"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon size="16" class="cursor-pointer">
|
||||
<TrashBinOutline />
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</n-thing>
|
||||
</n-list-item>
|
||||
</n-list>
|
||||
<div v-else>
|
||||
<n-result
|
||||
status="404"
|
||||
title="你还没有对话"
|
||||
description="不如现在就开始?"
|
||||
>
|
||||
</n-result>
|
||||
</div>
|
||||
</n-scrollbar>
|
||||
</n-layout-sider>
|
||||
<n-layout>
|
||||
<n-layout-content>
|
||||
<slot />
|
||||
</n-layout-content>
|
||||
</n-layout>
|
||||
</n-layout>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import { useDialog } from "naive-ui";
|
||||
import { TrashBinOutline } from "@vicons/ionicons5";
|
||||
import getApi from "../../plugins/api";
|
||||
import { useChatStore } from "../../stores/chat";
|
||||
import router from "@/router";
|
||||
import { ref } from "vue";
|
||||
import { EntityAssistant, EntityChat } from "@/api";
|
||||
import { useIsMobile } from "@/utils/composables";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
|
||||
const clientHeight = ref(100);
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const dialog = useDialog();
|
||||
const chatStore = useChatStore();
|
||||
const showSettingsDialog = ref(false);
|
||||
const currentChatId = ref();
|
||||
const currentChat: Ref<EntityChat> = ref({});
|
||||
const assistants: Ref<EntityAssistant[]> = ref([]);
|
||||
const assistantSelects = ref([
|
||||
{
|
||||
label: "不使用",
|
||||
value: null,
|
||||
},
|
||||
]);
|
||||
|
||||
async function getChats() {
|
||||
chatStore.chats = (await getApi().Chat.apiV1ChatsGet()).data.data;
|
||||
}
|
||||
|
||||
const viewChat = (chatId: number) => {
|
||||
router.push("/chat/" + chatId);
|
||||
};
|
||||
|
||||
const deleteChat = async (chatId: number) => {
|
||||
dialog.warning({
|
||||
title: "删除对话",
|
||||
content: "删除后,将不能恢复",
|
||||
positiveText: "确定",
|
||||
negativeText: "取消",
|
||||
onPositiveClick: async () => {
|
||||
await getApi().Chat.apiV1ChatsIdDelete(chatId);
|
||||
await getChats();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const getAssistants = async () => {
|
||||
assistants.value =
|
||||
(await getApi().Assistant.apiV1AssistantsGet()).data.data ?? [];
|
||||
|
||||
assistantSelects.value = [];
|
||||
|
||||
assistantSelects.value.push({
|
||||
label: "不使用",
|
||||
value: null,
|
||||
});
|
||||
assistants.value.forEach((a) => {
|
||||
if (a.name !== undefined && a.id !== undefined) {
|
||||
return assistantSelects.value.push({
|
||||
label: a.name,
|
||||
// @ts-ignore
|
||||
value: a.id,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const appStore = useAppStore();
|
||||
const updateClientHeight = () => {
|
||||
clientHeight.value = window.innerHeight - appStore.headerHeight;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
updateClientHeight();
|
||||
window.addEventListener("resize", updateClientHeight);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("resize", updateClientHeight);
|
||||
});
|
||||
|
||||
getChats();
|
||||
getAssistants();
|
||||
</script>
|
@ -19,6 +19,7 @@ if (process.env.NODE_ENV === "production") {
|
||||
|
||||
config.backend = "https://amber-api.leaflow.cn";
|
||||
|
||||
|
||||
// console.log("api endpoint: " + config.backend);
|
||||
|
||||
export default config;
|
||||
|
@ -1,15 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { NLayout } from "naive-ui";
|
||||
// import { useUserStore } from "../stores/user";
|
||||
import { useUserStore } from "../stores/user";
|
||||
// import Guest from "../pages/guest/index.vue";
|
||||
// import router from "../router";
|
||||
import Header from "./Header.vue";
|
||||
import element from "@/config/element";
|
||||
import { useIsMobile } from "@/utils/composables";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
import ChatLayout from "@/components/chat/Layout.vue";
|
||||
// const currentRoute = computed(() => router.currentRoute.value.name);
|
||||
|
||||
// const userStore = useUserStore();
|
||||
const userStore = useUserStore();
|
||||
const appStore = useAppStore();
|
||||
const route = useRoute();
|
||||
|
||||
@ -82,7 +83,14 @@ const onScroll = (e: Event) => {
|
||||
|
||||
<n-layout :native-scrollbar="isMobile">
|
||||
<div class="!pt-2">
|
||||
<router-view :key="route.path"> </router-view>
|
||||
<div v-if="userStore.logined">
|
||||
<ChatLayout>
|
||||
<router-view :key="route.path"> </router-view>
|
||||
</ChatLayout>
|
||||
</div>
|
||||
<div v-else>
|
||||
<router-view :key="route.path"> </router-view>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <Guest v-if="!userStore.logined && currentRoute != '/auth/login'" />
|
||||
|
70
src/pages/chat/Chat.vue
Normal file
70
src/pages/chat/Chat.vue
Normal file
@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<ChatComponent />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import ChatComponent from "@/components/chat/Chat.vue";
|
||||
import { useDialog } from "naive-ui";
|
||||
import getApi from "../../plugins/api";
|
||||
import { useChatStore } from "../../stores/chat";
|
||||
import router from "@/router";
|
||||
import { ref } from "vue";
|
||||
import { EntityAssistant, EntityChat } from "@/api";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
|
||||
const clientHeight = ref(100);
|
||||
|
||||
const dialog = useDialog();
|
||||
const chatStore = useChatStore();
|
||||
const showSettingsDialog = ref(false);
|
||||
const currentChatId = ref();
|
||||
const currentChat: Ref<EntityChat> = ref({});
|
||||
const assistants: Ref<EntityAssistant[]> = ref([]);
|
||||
const assistantSelects = ref([
|
||||
{
|
||||
label: "不使用",
|
||||
value: null,
|
||||
},
|
||||
]);
|
||||
|
||||
async function getChats() {
|
||||
chatStore.chats = (await getApi().Chat.apiV1ChatsGet()).data.data;
|
||||
}
|
||||
|
||||
const getAssistants = async () => {
|
||||
assistants.value =
|
||||
(await getApi().Assistant.apiV1AssistantsGet()).data.data ?? [];
|
||||
|
||||
assistantSelects.value = [];
|
||||
|
||||
assistantSelects.value.push({
|
||||
label: "不使用",
|
||||
value: null,
|
||||
});
|
||||
assistants.value.forEach((a) => {
|
||||
if (a.name !== undefined && a.id !== undefined) {
|
||||
return assistantSelects.value.push({
|
||||
label: a.name,
|
||||
// @ts-ignore
|
||||
value: a.id,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const appStore = useAppStore();
|
||||
const updateClientHeight = () => {
|
||||
clientHeight.value = window.innerHeight - appStore.headerHeight;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
updateClientHeight();
|
||||
window.addEventListener("resize", updateClientHeight);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("resize", updateClientHeight);
|
||||
});
|
||||
|
||||
getChats();
|
||||
getAssistants();
|
||||
</script>
|
@ -1,11 +1,12 @@
|
||||
<template>
|
||||
<Chat v-if="userStore.logined" />
|
||||
<Guest v-else />
|
||||
<Home v-else />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Guest from "@/pages/Guest.vue";
|
||||
import Home from "@/pages/home/index.vue";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import Chat from "@/pages/chat/Chat.vue";
|
||||
|
||||
const userStore = useUserStore();
|
||||
</script>
|
||||
|
@ -4,6 +4,7 @@ import { defineStore } from "pinia";
|
||||
export const useAppStore = defineStore("app", {
|
||||
persist: false,
|
||||
state: () => ({
|
||||
headerHeight: 64,
|
||||
updating: false,
|
||||
contentScrollHeight: 0,
|
||||
contentScrollPosition: 0,
|
||||
|
1
src/typed-router.d.ts
vendored
1
src/typed-router.d.ts
vendored
@ -24,6 +24,7 @@ declare module 'vue-router/auto-routes' {
|
||||
'/auth/login': RouteRecordInfo<'/auth/login', '/auth/login', Record<never, never>, Record<never, never>>,
|
||||
'/auth/logout': RouteRecordInfo<'/auth/logout', '/auth/logout', Record<never, never>, Record<never, never>>,
|
||||
'/chat/[id]/': RouteRecordInfo<'/chat/[id]/', '/chat/:id', { id: ParamValue<true> }, { id: ParamValue<false> }>,
|
||||
'/chat/Chat': RouteRecordInfo<'/chat/Chat', '/chat/Chat', Record<never, never>, Record<never, never>>,
|
||||
'/errors/400': RouteRecordInfo<'/errors/400', '/errors/400', Record<never, never>, Record<never, never>>,
|
||||
'/errors/401': RouteRecordInfo<'/errors/401', '/errors/401', Record<never, never>, Record<never, never>>,
|
||||
'/errors/404': RouteRecordInfo<'/errors/404', '/errors/404', Record<never, never>, Record<never, never>>,
|
||||
|
Loading…
Reference in New Issue
Block a user