feat: 修改布局样式,引入lodash-es
This commit is contained in:
3
web/components.d.ts
vendored
3
web/components.d.ts
vendored
@@ -7,9 +7,12 @@ export {}
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
Avatar: typeof import('./src/components/avatar.vue')['default']
|
||||
Markdown: typeof import('./src/components/markdown.vue')['default']
|
||||
NButton: typeof import('naive-ui')['NButton']
|
||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||
NDivider: typeof import('naive-ui')['NDivider']
|
||||
NImage: typeof import('naive-ui')['NImage']
|
||||
NInput: typeof import('naive-ui')['NInput']
|
||||
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||
NPopconfirm: typeof import('naive-ui')['NPopconfirm']
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"@vueuse/core": "^13.4.0",
|
||||
"axios": "^1.10.0",
|
||||
"highlight.js": "^11.11.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"markdown-it": "^14.1.0",
|
||||
"pinia": "^3.0.3",
|
||||
"pinia-plugin-persistedstate": "^4.3.0",
|
||||
@@ -27,6 +28,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^4.16.1",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/markdown-it": "^14.1.2",
|
||||
"@types/node": "^24.0.4",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
|
||||
6
web/pnpm-lock.yaml
generated
6
web/pnpm-lock.yaml
generated
@@ -20,6 +20,9 @@ importers:
|
||||
highlight.js:
|
||||
specifier: ^11.11.1
|
||||
version: 11.11.1
|
||||
lodash-es:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
markdown-it:
|
||||
specifier: ^14.1.0
|
||||
version: 14.1.0
|
||||
@@ -51,6 +54,9 @@ importers:
|
||||
'@antfu/eslint-config':
|
||||
specifier: ^4.16.1
|
||||
version: 4.16.1(@vue/compiler-sfc@3.5.17)(eslint-plugin-format@1.0.1(eslint@9.29.0(jiti@2.4.2)))(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3)
|
||||
'@types/lodash-es':
|
||||
specifier: ^4.17.12
|
||||
version: 4.17.12
|
||||
'@types/markdown-it':
|
||||
specifier: ^14.1.2
|
||||
version: 14.1.2
|
||||
|
||||
BIN
web/src/assets/ai_avatar.png
Normal file
BIN
web/src/assets/ai_avatar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 MiB |
BIN
web/src/assets/user_avatar.jpg
Normal file
BIN
web/src/assets/user_avatar.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 228 KiB |
13
web/src/components/avatar.vue
Normal file
13
web/src/components/avatar.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
const { avatar } = defineProps<{
|
||||
avatar: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NImage
|
||||
:src="avatar" object-fit="cover" :preview-disabled="true" width="60" height="60" class="!block" :img-props="{
|
||||
class: 'rounded-lg !block',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
@@ -1,6 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectGroupOption, SelectOption } from "naive-ui";
|
||||
import { throttle } from "lodash-es";
|
||||
import AIAvatar from "@/assets/ai_avatar.png";
|
||||
import { ExclamationTriangleIcon, microphone, PaperAirplaneIcon, TrashIcon } from "@/assets/Icons";
|
||||
import UserAvatar from "@/assets/user_avatar.jpg";
|
||||
import markdown from "@/components/markdown.vue";
|
||||
import { useAsrStore, useChatStore } from "@/stores";
|
||||
|
||||
@@ -11,7 +14,7 @@ const { historyMessages, completing, modelList, modelInfo } = storeToRefs(chatSt
|
||||
const { isRecording } = storeToRefs(asrStore);
|
||||
|
||||
const inputData = ref("");
|
||||
|
||||
const scrollbarRef = ref<HTMLElement | null>(null);
|
||||
const options = ref<Array<SelectGroupOption | SelectOption>>([]);
|
||||
|
||||
// 处理选中模型的 ID
|
||||
@@ -59,14 +62,22 @@ const handleSendMessage = () => {
|
||||
};
|
||||
|
||||
// 开关语音输入
|
||||
const toggleRecording = () => {
|
||||
const toggleRecording = throttle(() => {
|
||||
if (isRecording.value) {
|
||||
asrStore.stopRecording();
|
||||
}
|
||||
else {
|
||||
asrStore.startRecording();
|
||||
}
|
||||
};
|
||||
}, 500);
|
||||
|
||||
watch(completing, (newVal) => {
|
||||
if (newVal) {
|
||||
nextTick(() => {
|
||||
scrollbarRef.value?.scrollTo({ top: 99999, behavior: "smooth" });
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
chatStore.getModelList();
|
||||
@@ -75,21 +86,41 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<div class="p-8 !pr-4 h-full w-full flex flex-col gap-4 border-l-[24px] border-l-[#FAFAFA] text-base">
|
||||
<NScrollbar class="flex-1 pr-4">
|
||||
<!-- 历史消息区 -->
|
||||
<NScrollbar ref="scrollbarRef" class="flex-1 pr-4 relative">
|
||||
<div class="flex items-start mb-4">
|
||||
<span class="text-base w-14 min-w-14">助手:</span>
|
||||
<NTag type="success" class="text-base max-w-full !h-auto">
|
||||
<span class="rounded-lg overflow-hidden">
|
||||
<avatar :avatar="AIAvatar" />
|
||||
</span>
|
||||
<div class="text-base w-full max-w-full ml-2 flex flex-col items-start">
|
||||
<span class="text-base font-bold">助手:</span>
|
||||
<span class="text-base">你好,我是你的智能助手,请问有什么可以帮助你的吗?</span>
|
||||
</NTag>
|
||||
<NDivider />
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(msg, idx) in historyMessages" :key="idx" class="flex items-start mb-4">
|
||||
<span v-if="msg.role === 'user'" class="text-base w-14 min-w-14">你:</span>
|
||||
<span v-else class="text-base w-14 min-w-14">助手:</span>
|
||||
<NTag :type="msg.role === 'user' ? 'info' : 'success'" class="max-w-full !h-auto">
|
||||
<markdown :content="msg.content || ''" />
|
||||
</NTag>
|
||||
<span v-if="msg.role === 'user'" class="rounded-lg overflow-hidden">
|
||||
<avatar :avatar="UserAvatar" />
|
||||
</span>
|
||||
<span v-else class="rounded-lg overflow-hidden">
|
||||
<avatar :avatar="AIAvatar" />
|
||||
</span>
|
||||
<div class="text-base w-full max-w-full ml-2 flex flex-col items-start">
|
||||
<span class="text-base font-bold">{{ msg.role === 'user' ? '你:' : '助手:' }}</span>
|
||||
<div class="w-full max-w-full">
|
||||
<markdown :content="msg.content || ''" />
|
||||
<NDivider />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="isRecording"
|
||||
class="absolute inset-0 pointer-events-none flex items-center justify-center text-[#7A7A7A] text-2xl bg-white/80"
|
||||
>
|
||||
正在语音输入...
|
||||
</div>
|
||||
</NScrollbar>
|
||||
<!-- 输入框 -->
|
||||
<NInput v-model:value="inputData" type="textarea" placeholder="在这里输入消息" @keyup.enter="handleSendMessage" />
|
||||
<!-- 操作区 -->
|
||||
<div class="flex justify-between items-center gap-2">
|
||||
|
||||
@@ -2,13 +2,22 @@
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
},
|
||||
"types": ["vite-svg-loader"]
|
||||
"types": [
|
||||
"vite-svg-loader",
|
||||
"lodash"
|
||||
]
|
||||
},
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
}
|
||||
],
|
||||
"files": []
|
||||
}
|
||||
|
||||
@@ -48,4 +48,7 @@ export default defineConfig({
|
||||
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||
},
|
||||
},
|
||||
esbuild: {
|
||||
treeShaking: true,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user