改进
This commit is contained in:
parent
a0fa982cfd
commit
20574c967e
BIN
src/assets/images/leaflow.png
Normal file
BIN
src/assets/images/leaflow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 136 KiB |
3
src/components.d.ts
vendored
3
src/components.d.ts
vendored
@ -11,10 +11,9 @@ declare module 'vue' {
|
|||||||
Chat: typeof import('./components/chat/chat.vue')['default']
|
Chat: typeof import('./components/chat/chat.vue')['default']
|
||||||
ChatMenu: typeof import('./components/ChatMenu.vue')['default']
|
ChatMenu: typeof import('./components/ChatMenu.vue')['default']
|
||||||
Container: typeof import('./components/Container.vue')['default']
|
Container: typeof import('./components/Container.vue')['default']
|
||||||
copy: typeof import('./components/AssistantMenu.vue')['default']
|
|
||||||
LeftSetting: typeof import('./components/LeftSetting.vue')['default']
|
|
||||||
LeftSettings: typeof import('./components/LeftSettings.vue')['default']
|
LeftSettings: typeof import('./components/LeftSettings.vue')['default']
|
||||||
Menu: typeof import('./components/Menu.vue')['default']
|
Menu: typeof import('./components/Menu.vue')['default']
|
||||||
|
MessageList: typeof import('./components/chat/MessageList.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
UserMenu: typeof import('./components/UserMenu.vue')['default']
|
UserMenu: typeof import('./components/UserMenu.vue')['default']
|
||||||
|
@ -15,6 +15,7 @@ const userStore = useUserStore();
|
|||||||
></Header>
|
></Header>
|
||||||
|
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
|
<n-scrollbar >
|
||||||
<router-view v-slot="{ Component }" :key="route.path">
|
<router-view v-slot="{ Component }" :key="route.path">
|
||||||
<transition mode="out-in" name="fade">
|
<transition mode="out-in" name="fade">
|
||||||
<div>
|
<div>
|
||||||
@ -22,6 +23,8 @@ const userStore = useUserStore();
|
|||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</router-view>
|
</router-view>
|
||||||
|
</n-scrollbar>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <router-view></router-view> -->
|
<!-- <router-view></router-view> -->
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-tabs type="segment" animated>
|
<n-tabs type="segment" animated class="select-none">
|
||||||
<n-tab-pane name="chap1" tab="对话">
|
<n-tab-pane name="chap1" tab="对话">
|
||||||
<n-list hoverable clickable>
|
<n-list hoverable clickable>
|
||||||
<n-list-item
|
<n-list-item
|
||||||
v-for="c in chatStore.chats"
|
v-for="c in chatStore.chats"
|
||||||
:key="c.id"
|
:key="c.id"
|
||||||
:class="c.id == chatStore.currentChatId ? 'text-primary' : ''"
|
:class="
|
||||||
|
c.id === chatStore.currentChatId ? ' bg-gray-100' : 'text-red-50'
|
||||||
|
"
|
||||||
@click="viewChat(c.id ?? 0)"
|
@click="viewChat(c.id ?? 0)"
|
||||||
>
|
>
|
||||||
<n-thing> {{ c.name }} </n-thing>
|
<n-thing> {{ c.name }} </n-thing>
|
||||||
|
60
src/components/chat/MessageList.vue
Normal file
60
src/components/chat/MessageList.vue
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<template>
|
||||||
|
<!-- flex 布局 -->
|
||||||
|
<n-flex> </n-flex>
|
||||||
|
|
||||||
|
<n-divider> <small>这是聊天的开头</small> </n-divider>
|
||||||
|
|
||||||
|
<div v-for="(message, index) in chat_messages" :key="index">
|
||||||
|
<div v-if="message.role === 'user'">
|
||||||
|
<!-- 用户消息 -->
|
||||||
|
<n-flex justify="end">
|
||||||
|
<div class="align-middle">
|
||||||
|
<v-md-preview :text="message.content" height="500px"></v-md-preview>
|
||||||
|
</div>
|
||||||
|
<n-avatar round size="large" :src="userStore.user.avatar" />
|
||||||
|
</n-flex>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="message.role === 'assistant'">
|
||||||
|
<!-- 助理消息 -->
|
||||||
|
<n-flex justify="start">
|
||||||
|
<n-avatar round size="large" :src="leaflowPng" />
|
||||||
|
|
||||||
|
<div class="align-middle">
|
||||||
|
<v-md-preview :text="message.content" height="500px"></v-md-preview>
|
||||||
|
</div>
|
||||||
|
</n-flex>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 这里要加上输入框占的部分,但是那个占了很多,所以设置大一点 -->
|
||||||
|
<div style="margin-bottom: 10rem;"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Ref } from "vue";
|
||||||
|
import { EntityChatMessage } from "@/api";
|
||||||
|
import { useUserStore } from "@/stores/user";
|
||||||
|
import VMdPreview from "@kangc/v-md-editor/lib/preview";
|
||||||
|
import "@kangc/v-md-editor/lib/style/preview.css";
|
||||||
|
import githubTheme from "@kangc/v-md-editor/lib/theme/github.js";
|
||||||
|
import "@kangc/v-md-editor/lib/theme/style/github.css";
|
||||||
|
import leaflowPng from "@/assets/images/leaflow.png";
|
||||||
|
|
||||||
|
// highlightjs
|
||||||
|
import hljs from "highlight.js";
|
||||||
|
|
||||||
|
VMdPreview.use(githubTheme, {
|
||||||
|
Hljs: hljs,
|
||||||
|
});
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
chat_messages: {
|
||||||
|
required: true,
|
||||||
|
type: Array as () => EntityChatMessage[],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const chat_messages = toRef(props, "chat_messages") as Ref<EntityChatMessage[]>;
|
||||||
|
</script>
|
@ -1,6 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative flex flex-col h-[calc(100vh-var(--header-height)*1.9)]">
|
<div
|
||||||
<div class="flex-grow mt-3 mb-1 text-5xl">
|
class="relative flex flex-col h-[calc(100vh-var(--header-height)*1.9)] lg:items-center"
|
||||||
|
>
|
||||||
|
<div class="w-4/5">
|
||||||
|
<div>
|
||||||
|
<div class="flex-grow mt-3 mb-1 text-5xl" v-if="!chatMessages?.length">
|
||||||
<n-gradient-text type="info" class="pr-3 pb-2 pt-2">
|
<n-gradient-text type="info" class="pr-3 pb-2 pt-2">
|
||||||
你好,{{ userStore.user.name }}
|
你好,{{ userStore.user.name }}
|
||||||
</n-gradient-text>
|
</n-gradient-text>
|
||||||
@ -10,12 +14,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="absolute bottom-0 left-0 right-0 pb-5">
|
<div v-else>
|
||||||
|
<MessageList :chat_messages="chatMessages" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fixed bottom-0 left-0 right-0 pb-10">
|
||||||
<div
|
<div
|
||||||
ref="inputContainer"
|
ref="inputContainer"
|
||||||
class="mx-auto w-2xl max-w-2xl outline-none input-color input-bg rounded-full flex pl-5 pr-5 bg-white shadow-lg items-center p-4 pb-4 transition-all"
|
class="mx-auto w-2xl max-w-2xl outline-none input-color input-bg rounded-full flex pl-5 pr-5 bg-white shadow-lg items-center p-4 pb-4 transition-all"
|
||||||
@focusin="onFocused"
|
@keyup.enter="sendText"
|
||||||
@focusout="onBlurred"
|
|
||||||
>
|
>
|
||||||
<div class="overflow-x-hidden h-full w-full flex items-center">
|
<div class="overflow-x-hidden h-full w-full flex items-center">
|
||||||
<n-scrollbar class="max-h-96">
|
<n-scrollbar class="max-h-96">
|
||||||
@ -24,7 +32,7 @@
|
|||||||
:class="{ 'has-placeholder': isPlaceholderVisible }"
|
:class="{ 'has-placeholder': isPlaceholderVisible }"
|
||||||
contenteditable="true"
|
contenteditable="true"
|
||||||
placeholder="请输入文本..."
|
placeholder="请输入文本..."
|
||||||
class="input-text max-w-full outline-none text-lg text-pretty pl-2"
|
class="input-text max-w-full outline-none text-lg text-pretty pl-2 min-h-6"
|
||||||
@input="updateInputHeight"
|
@input="updateInputHeight"
|
||||||
></div>
|
></div>
|
||||||
</n-scrollbar>
|
</n-scrollbar>
|
||||||
@ -52,29 +60,50 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useMessage } from "naive-ui";
|
import { useMessage } from "naive-ui";
|
||||||
import { useUserStore } from "../../stores/user";
|
import { useUserStore } from "../../stores/user";
|
||||||
import { InputHTMLAttributes, onMounted, ref } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import {
|
import {
|
||||||
SendOutline,
|
SendOutline,
|
||||||
MicOutline,
|
MicOutline,
|
||||||
DocumentAttachOutline,
|
DocumentAttachOutline,
|
||||||
} from "@vicons/ionicons5";
|
} from "@vicons/ionicons5";
|
||||||
|
import { EntityChatMessage } from "@/api";
|
||||||
|
import getApi from "@/plugins/api";
|
||||||
|
|
||||||
|
// 获取组件传入的 chatId
|
||||||
|
const chatId: Ref<string | number | undefined | null> = ref(null);
|
||||||
|
|
||||||
|
// const props = defineProps<{
|
||||||
|
// chatId: string | number | undefined | null;
|
||||||
|
// }>();
|
||||||
|
|
||||||
|
// 定义 prop, chatId 是可选的
|
||||||
|
const props = defineProps({
|
||||||
|
chatId: {
|
||||||
|
type: [String, Number],
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
chatId.value = props.chatId;
|
||||||
|
});
|
||||||
|
|
||||||
const message = useMessage();
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
const compositionStart = ref(false);
|
||||||
const inputMessage = ref("");
|
|
||||||
const inputContainer: any = ref(null);
|
const inputContainer: any = ref(null);
|
||||||
const inputText: InputHTMLAttributes | null = ref(null);
|
const inputText: any = ref(null);
|
||||||
const actionContainer: any = ref(null);
|
const actionContainer: any = ref(null);
|
||||||
const isPlaceholderVisible = ref(true);
|
const isPlaceholderVisible = ref(true);
|
||||||
const triggerTimes = ref(0);
|
const triggerTimes = ref(0);
|
||||||
const showSendBtn = ref(false);
|
const showSendBtn = ref(false);
|
||||||
const content = ref("");
|
const content = ref("");
|
||||||
|
const chatMessages: Ref<EntityChatMessage[] | undefined> = ref([]);
|
||||||
|
|
||||||
function updateInputHeight() {
|
function updateInputHeight() {
|
||||||
if (!inputText?.value || !inputContainer.value || !actionContainer.value) {
|
if (!inputText?.value || !inputContainer.value || !actionContainer.value) {
|
||||||
@ -137,6 +166,45 @@ function updateInputHeight() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleCompositionStart() {
|
||||||
|
compositionStart.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCompositionEnd() {
|
||||||
|
compositionStart.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendText() {
|
||||||
|
if (!inputText?.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const input = inputText.value;
|
||||||
|
const textContent = input.innerText.trim();
|
||||||
|
|
||||||
|
if (textContent === "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送文本到服务器
|
||||||
|
sendMessage(textContent);
|
||||||
|
|
||||||
|
// 清空输入框
|
||||||
|
input.innerText = "";
|
||||||
|
|
||||||
|
updateInputHeight();
|
||||||
|
|
||||||
|
chatMessages.value?.push({
|
||||||
|
content: textContent,
|
||||||
|
role: "user",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendMessage(text: string) {
|
||||||
|
console.log("发送文本:", text);
|
||||||
|
// 实际发送文本到服务器的逻辑
|
||||||
|
}
|
||||||
|
|
||||||
function onFocused() {
|
function onFocused() {
|
||||||
if (!inputContainer.value) {
|
if (!inputContainer.value) {
|
||||||
return;
|
return;
|
||||||
@ -157,8 +225,20 @@ function onBlurred() {
|
|||||||
container.classList.add("max-w-2xl");
|
container.classList.add("max-w-2xl");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getChatMessages() {
|
||||||
|
// 获取当前聊天记录
|
||||||
|
if (chatId.value) {
|
||||||
|
const cm = await getApi().ChatMessage.apiV1ChatsIdMessagesGet(
|
||||||
|
Number(chatId.value)
|
||||||
|
);
|
||||||
|
|
||||||
|
chatMessages.value = cm.data.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
updateInputHeight();
|
updateInputHeight();
|
||||||
|
getChatMessages();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -16,8 +16,6 @@ if (process.env.NODE_ENV === "production") {
|
|||||||
config.oauth_client_id = "16";
|
config.oauth_client_id = "16";
|
||||||
}
|
}
|
||||||
|
|
||||||
config.backend = "https://amber-api.leaflow.cn";
|
|
||||||
|
|
||||||
// config.backend = "https://amber-api.leaflow.cn";
|
// config.backend = "https://amber-api.leaflow.cn";
|
||||||
|
|
||||||
// console.log("api endpoint: " + config.backend);
|
// console.log("api endpoint: " + config.backend);
|
||||||
|
@ -6,6 +6,7 @@ import Menu from "../components/Menu.vue";
|
|||||||
import { useUserStore } from "../stores/user";
|
import { useUserStore } from "../stores/user";
|
||||||
import Guest from "../pages/guest/index.vue";
|
import Guest from "../pages/guest/index.vue";
|
||||||
import router from "../router";
|
import router from "../router";
|
||||||
|
import Header from "./Header.vue";
|
||||||
const currentRoute = computed(() => router.currentRoute.value.name);
|
const currentRoute = computed(() => router.currentRoute.value.name);
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
@ -19,7 +20,7 @@ const menuCollapsed = ref({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-layout position="absolute" :has-sider="true">
|
<n-layout position="absolute" :has-sider="false">
|
||||||
<!-- <n-layout-sider
|
<!-- <n-layout-sider
|
||||||
v-if="userStore.logined && !isMobile"
|
v-if="userStore.logined && !isMobile"
|
||||||
:collapsed-width="0"
|
:collapsed-width="0"
|
||||||
@ -35,7 +36,7 @@ const menuCollapsed = ref({
|
|||||||
>
|
>
|
||||||
<Menu v-show="!isMobile"></Menu>
|
<Menu v-show="!isMobile"></Menu>
|
||||||
</n-layout-sider> -->
|
</n-layout-sider> -->
|
||||||
<n-layout-content>
|
<n-layout-content :native-scrollbar="false">
|
||||||
<!-- <Guest v-if="!userStore.logined && currentRoute != '/auth/login'" />
|
<!-- <Guest v-if="!userStore.logined && currentRoute != '/auth/login'" />
|
||||||
<Container v-else /> -->
|
<Container v-else /> -->
|
||||||
<Guest v-if="!userStore.logined && !currentRoute?.startsWith('/auth')" />
|
<Guest v-if="!userStore.logined && !currentRoute?.startsWith('/auth')" />
|
||||||
|
@ -24,14 +24,15 @@
|
|||||||
:default-width="width"
|
:default-width="width"
|
||||||
placement="left"
|
placement="left"
|
||||||
>
|
>
|
||||||
<n-drawer-content>
|
<n-drawer-content title="有什么我可以帮您的吗" closable>
|
||||||
<LeftSettings></LeftSettings>
|
<LeftSettings></LeftSettings>
|
||||||
</n-drawer-content>
|
</n-drawer-content>
|
||||||
</n-drawer>
|
</n-drawer>
|
||||||
<n-icon
|
<n-icon
|
||||||
size="20"
|
size="24"
|
||||||
style="margin-left: 12px"
|
style="margin-left: 12px"
|
||||||
@click="showDrawer = true"
|
@click="showDrawer = true"
|
||||||
|
class="cursor-pointer"
|
||||||
>
|
>
|
||||||
<menu-outline />
|
<menu-outline />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
@ -42,6 +43,10 @@
|
|||||||
|
|
||||||
<n-grid-item class="flex items-center justify-end mr-1.5">
|
<n-grid-item class="flex items-center justify-end mr-1.5">
|
||||||
<!-- 右侧 -->
|
<!-- 右侧 -->
|
||||||
|
<!-- 新对话 -->
|
||||||
|
<n-icon class="text-2xl mr-4 cursor-pointer" @click="backToHome">
|
||||||
|
<AddOutline />
|
||||||
|
</n-icon>
|
||||||
<!-- 助理选择 -->
|
<!-- 助理选择 -->
|
||||||
<n-popover
|
<n-popover
|
||||||
:placement="userPlacement"
|
:placement="userPlacement"
|
||||||
@ -50,7 +55,7 @@
|
|||||||
style="padding: 0"
|
style="padding: 0"
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<n-icon class="text-2xl mr-4">
|
<n-icon class="text-2xl mr-4 cursor-pointer">
|
||||||
<PersonOutline />
|
<PersonOutline />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
</template>
|
</template>
|
||||||
@ -85,7 +90,10 @@ import AssistantMenu from "../components/AssistantMenu.vue";
|
|||||||
import { useAppStore } from "../stores/app";
|
import { useAppStore } from "../stores/app";
|
||||||
import { useUserStore } from "../stores/user";
|
import { useUserStore } from "../stores/user";
|
||||||
import { useIsMobile, useIsTablet } from "../utils/composables";
|
import { useIsMobile, useIsTablet } from "../utils/composables";
|
||||||
import { MenuOutline, PersonOutline } from "@vicons/ionicons5";
|
import { MenuOutline, PersonOutline, AddOutline } from "@vicons/ionicons5";
|
||||||
|
import router from "@/router";
|
||||||
|
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
const isTablet = useIsTablet();
|
const isTablet = useIsTablet();
|
||||||
@ -99,11 +107,17 @@ if (isMobile.value) {
|
|||||||
width.value = window.innerWidth - 100;
|
width.value = window.innerWidth - 100;
|
||||||
} else {
|
} else {
|
||||||
// 获取当前屏幕宽度 40% 的宽度
|
// 获取当前屏幕宽度 40% 的宽度
|
||||||
width.value = window.innerWidth * 0.4
|
width.value = window.innerWidth * 0.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
const userPlacement = ref("bottom");
|
const userPlacement = ref("bottom");
|
||||||
if (isMobile.value) {
|
if (isMobile.value) {
|
||||||
userPlacement.value = "bottom";
|
userPlacement.value = "bottom";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const backToHome = () => {
|
||||||
|
router.push("/");
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
console.log("load")
|
|
||||||
const meta = document.createElement("meta");
|
const meta = document.createElement("meta");
|
||||||
meta.name = "naive-ui-style";
|
meta.name = "naive-ui-style";
|
||||||
document.head.appendChild(meta);
|
document.head.appendChild(meta);
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>聊天记录 {{ chatId }}</div>
|
<div>
|
||||||
|
<Chat :chat-id="chatId" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { useChatStore } from "@/stores/chat";
|
||||||
|
|
||||||
|
const chatStore = useChatStore();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const chatId = useRoute().params.id as number;
|
const chatId = useRoute().params.id as number;
|
||||||
// chatStore.currentChatId = chatId;
|
chatStore.currentChatId = chatId;
|
||||||
</script>
|
</script>
|
||||||
|
@ -5,34 +5,6 @@ import chat from "../components/chat/chat.vue";
|
|||||||
const message = useMessage();
|
const message = useMessage();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
const options = [
|
|
||||||
{
|
|
||||||
label: "滨海湾金沙,新加坡",
|
|
||||||
key: "marina bay sands",
|
|
||||||
disabled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "布朗酒店,伦敦",
|
|
||||||
key: "brown's hotel, london",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "亚特兰蒂斯巴哈马,拿骚",
|
|
||||||
key: "atlantis nahamas, nassau",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "比佛利山庄酒店,洛杉矶",
|
|
||||||
key: "the beverly hills hotel, los angeles",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
function handleSelect(key: string | number) {
|
|
||||||
message.info(String(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
function logout() {
|
|
||||||
userStore.logout();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
Loading…
Reference in New Issue
Block a user