From 582c29d50c0b355bd0635e0671d2595b3affad9b Mon Sep 17 00:00:00 2001 From: fuzhongyun <15339891972@163.com> Date: Fri, 21 Nov 2025 15:16:45 +0800 Subject: [PATCH] add --- vue3-frontend/package-lock.json | 13 ++ vue3-frontend/package.json | 1 + vue3-frontend/src/App.vue | 4 +- vue3-frontend/src/api/mock.ts | 10 +- vue3-frontend/src/api/server.ts | 6 +- vue3-frontend/src/components/ChatInput.vue | 27 ++-- vue3-frontend/src/components/MessageItem.vue | 33 +++- .../src/components/SimpleOrderCard.vue | 65 ++++++++ vue3-frontend/src/pages/ChatPage.vue | 68 ++------ vue3-frontend/src/router/index.ts | 21 +-- vue3-frontend/src/stores/chat.ts | 152 ++++++++++-------- vue3-frontend/src/types/index.ts | 13 +- ....timestamp-1763692841391-01acb3bfdaeb1.mjs | 38 +++++ ....timestamp-1763706544786-1b659ad8269ab.mjs | 38 +++++ 14 files changed, 317 insertions(+), 172 deletions(-) create mode 100644 vue3-frontend/src/components/SimpleOrderCard.vue create mode 100644 vue3-frontend/vite.config.ts.timestamp-1763692841391-01acb3bfdaeb1.mjs create mode 100644 vue3-frontend/vite.config.ts.timestamp-1763706544786-1b659ad8269ab.mjs diff --git a/vue3-frontend/package-lock.json b/vue3-frontend/package-lock.json index 56a826c..4deeb46 100644 --- a/vue3-frontend/package-lock.json +++ b/vue3-frontend/package-lock.json @@ -11,6 +11,7 @@ "ant-design-vue": "^4.0.8", "clsx": "^2.1.1", "lucide-vue-next": "^0.511.0", + "marked": "^17.0.1", "pinia": "^2.1.7", "tailwind-merge": "^3.3.0", "vue": "^3.4.15", @@ -3827,6 +3828,18 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/marked": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.1.tgz", + "integrity": "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", diff --git a/vue3-frontend/package.json b/vue3-frontend/package.json index ee20bab..3ca5af8 100644 --- a/vue3-frontend/package.json +++ b/vue3-frontend/package.json @@ -15,6 +15,7 @@ "ant-design-vue": "^4.0.8", "clsx": "^2.1.1", "lucide-vue-next": "^0.511.0", + "marked": "^17.0.1", "pinia": "^2.1.7", "tailwind-merge": "^3.3.0", "vue": "^3.4.15", diff --git a/vue3-frontend/src/App.vue b/vue3-frontend/src/App.vue index 2753989..e8b3b05 100644 --- a/vue3-frontend/src/App.vue +++ b/vue3-frontend/src/App.vue @@ -35,7 +35,7 @@ html, body { #app { height: 100vh; - overflow: hidden; + overflow: auto; } /* 字体大小类 */ @@ -162,4 +162,4 @@ html, body { font-size: 17px; } } - \ No newline at end of file + diff --git a/vue3-frontend/src/api/mock.ts b/vue3-frontend/src/api/mock.ts index 7715d3c..8936fd2 100644 --- a/vue3-frontend/src/api/mock.ts +++ b/vue3-frontend/src/api/mock.ts @@ -123,7 +123,7 @@ export class MockAPIService { } // 模拟聊天流式响应 - async *chatStream(message: string, conversationId?: string): AsyncGenerator { + async *chatStream(message: string, conversationId?: string, tag?: 'order' | 'product'): AsyncGenerator { const sessionId = generateSessionId(); try { @@ -136,7 +136,8 @@ export class MockAPIService { await new Promise(resolve => setTimeout(resolve, randomDelay(300, 800))); // 2. 判断是否需要显示订单信息 - const shouldShowOrder = message.includes('订单') || message.includes('查询') || message.includes('购买'); + const shouldShowOrder = tag === 'order' || message.includes('订单') || message.includes('查询') || message.includes('购买'); + const isProductQuery = tag === 'product' || message.includes('商品') || message.includes('价格') || message.includes('库存'); if (shouldShowOrder) { // 显示订单相关回复 @@ -158,6 +159,9 @@ export class MockAPIService { }, randomOrder ); + } else if (isProductQuery) { + const response = '我来为您查询商品信息与库存情况...'; + yield* this.generateStreamResponse(sessionId, response, 'general_chat_stream'); } else { // 普通回复 const response = mockResponses[Math.floor(Math.random() * mockResponses.length)]; @@ -219,4 +223,4 @@ export class MockAPIService { } // 导出单例实例 -export const mockAPI = MockAPIService.getInstance(); \ No newline at end of file +export const mockAPI = MockAPIService.getInstance(); diff --git a/vue3-frontend/src/api/server.ts b/vue3-frontend/src/api/server.ts index 90e00d9..54ce74f 100644 --- a/vue3-frontend/src/api/server.ts +++ b/vue3-frontend/src/api/server.ts @@ -14,14 +14,14 @@ export class DevServer { // 处理聊天请求 async handleChatRequest(request: ChatRequest): Promise { - const { message, conversation_id } = request; + const { message, conversation_id, tag } = request; // 创建可读流 const stream = new ReadableStream({ async start(controller) { try { // 使用 Mock API 生成流式响应 - for await (const response of mockAPI.chatStream(message, conversation_id)) { + for await (const response of mockAPI.chatStream(message, conversation_id, tag)) { // 格式化为 SSE 格式 const sseData = `data: ${JSON.stringify(response)}\n\n`; controller.enqueue(new TextEncoder().encode(sseData)); @@ -90,4 +90,4 @@ export const setupMockAPI = () => { export const restoreFetch = () => { // 这里可以实现恢复逻辑,但在开发环境中通常不需要 console.log('Mock API已禁用'); -}; \ No newline at end of file +}; diff --git a/vue3-frontend/src/components/ChatInput.vue b/vue3-frontend/src/components/ChatInput.vue index 9b9b57d..d39bfb3 100644 --- a/vue3-frontend/src/components/ChatInput.vue +++ b/vue3-frontend/src/components/ChatInput.vue @@ -43,7 +43,7 @@ v-for="action in quickActions" :key="action.text" class="quick-action-btn" - @click="selectQuickAction(action.text)" + @click="selectQuickAction(action)" > {{ action.text }} @@ -63,6 +63,7 @@ import { ref, computed, nextTick, onMounted } from 'vue'; interface QuickAction { text: string; value?: string; + tag?: 'order' | 'product'; } interface Props { @@ -75,7 +76,7 @@ interface Props { } interface Emits { - (e: 'send', message: string): void; + (e: 'send', message: string, options?: { tag?: 'order' | 'product' }): void; (e: 'input', value: string): void; } @@ -85,10 +86,8 @@ const props = withDefaults(defineProps(), { isLoading: false, showQuickActions: true, quickActions: () => [ - { text: '查询订单' }, - { text: '退换货' }, - { text: '物流信息' }, - { text: '联系人工客服' } + { text: '订单诊断', tag: 'order' }, + { text: '商品查询', tag: 'product' } ], maxLength: 1000 }); @@ -160,20 +159,18 @@ const handleSend = () => { const message = inputText.value.trim(); if (message) { - emit('send', message); + emit('send', message, currentTag.value ? { tag: currentTag.value } : undefined); inputText.value = ''; + currentTag.value = undefined; adjustHeight(); } }; // 选择快捷操作 -const selectQuickAction = (text: string) => { - inputText.value = text; - adjustHeight(); - // 自动发送快捷操作 - nextTick(() => { - handleSend(); - }); +const currentTag = ref<'order' | 'product' | undefined>(undefined); + +const selectQuickAction = (action: QuickAction) => { + currentTag.value = action.tag; }; // 聚焦输入框 @@ -314,4 +311,4 @@ onMounted(() => { .dark .input-wrapper:focus-within { @apply border-blue-400 ring-blue-400; } - \ No newline at end of file + diff --git a/vue3-frontend/src/components/MessageItem.vue b/vue3-frontend/src/components/MessageItem.vue index c997d98..cb8fd22 100644 --- a/vue3-frontend/src/components/MessageItem.vue +++ b/vue3-frontend/src/components/MessageItem.vue @@ -18,11 +18,11 @@
-
- {{ displayContent }} +
+
|
-
{{ message.content }}
+
{{ formatTime(message.timestamp) }} @@ -58,6 +58,10 @@ v-if="message.component?.type === 'order_card'" :order-data="message.component.data" /> +
{{ formatTime(message.timestamp) }} @@ -70,8 +74,10 @@ @@ -179,6 +188,16 @@ onMounted(() => { @apply bg-gray-100 text-gray-800 px-4 py-2 rounded-2xl rounded-bl-md max-w-xs md:max-w-md break-words; } +.markdown :is(h1,h2,h3,h4,h5,h6) { @apply font-semibold text-gray-900 } +.markdown p { @apply text-gray-800 leading-6 } +.markdown code { @apply bg-gray-200 rounded px-1 py-0.5 text-sm } +.markdown pre { @apply bg-gray-200 rounded p-3 overflow-auto text-sm } +.markdown ul { @apply list-disc pl-5 } +.markdown ol { @apply list-decimal pl-5 } +.dark .markdown :is(h1,h2,h3,h4,h5,h6) { @apply text-gray-100 } +.dark .markdown p { @apply text-gray-100 } +.dark .markdown code, .dark .markdown pre { @apply bg-gray-700 text-gray-100 } + /* 思考过程样式 */ .thinking-message { @apply flex items-start space-x-3 opacity-70; @@ -275,4 +294,4 @@ onMounted(() => { .dark .message-time { @apply text-gray-400; } - \ No newline at end of file + diff --git a/vue3-frontend/src/components/SimpleOrderCard.vue b/vue3-frontend/src/components/SimpleOrderCard.vue new file mode 100644 index 0000000..fb6a290 --- /dev/null +++ b/vue3-frontend/src/components/SimpleOrderCard.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/vue3-frontend/src/pages/ChatPage.vue b/vue3-frontend/src/pages/ChatPage.vue index 26a00b8..53fe542 100644 --- a/vue3-frontend/src/pages/ChatPage.vue +++ b/vue3-frontend/src/pages/ChatPage.vue @@ -9,37 +9,7 @@ {{ connectionStatusText }}
-
- - - -
+
@@ -89,16 +59,13 @@ import { ref, computed, onMounted, onUnmounted, watch } from 'vue'; import { useRouter } from 'vue-router'; import { useChatStore } from '@/stores/chat'; -import { useHistoryStore } from '@/stores/history'; import { useSettingsStore } from '@/stores/settings'; -import { setupMockAPI } from '@/api/server'; import MessageList from '@/components/MessageList.vue'; import ChatInput from '@/components/ChatInput.vue'; // 路由和状态管理 const router = useRouter(); const chatStore = useChatStore(); -const historyStore = useHistoryStore(); const settingsStore = useSettingsStore(); // 组件引用 @@ -128,7 +95,7 @@ const connectionStatusText = computed(() => { }); // 发送消息 -const handleSendMessage = async (message: string) => { +const handleSendMessage = async (message: string, options?: { tag?: 'order' | 'product' }) => { try { clearError(); @@ -138,12 +105,9 @@ const handleSendMessage = async (message: string) => { } // 发送消息 - await chatStore.sendMessage(message); + await chatStore.sendMessage(message, options); + - // 保存对话到历史记录 - if (chatStore.currentConversation) { - historyStore.saveConversation(chatStore.currentConversation); - } } catch (error) { console.error('发送消息失败:', error); @@ -162,14 +126,10 @@ const clearChat = () => { }; // 导航到历史页面 -const goToHistory = () => { - router.push('/history'); -}; +const goToHistory = () => {}; // 导航到设置页面 -const goToSettings = () => { - router.push('/settings'); -}; +const goToSettings = () => {}; // 显示错误信息 const showError = (message: string) => { @@ -224,21 +184,19 @@ const handleKeydown = (event: KeyboardEvent) => { }; onMounted(() => { - // 设置Mock API - setupMockAPI(); - + // 加载设置 settingsStore.loadSettings(); settingsStore.initializeTheme(); - // 加载历史对话 - historyStore.loadConversations(); - // 如果没有当前对话,创建新对话 if (!chatStore.currentConversation) { chatStore.createNewConversation(); } - + + // 页面初始化建立 WebSocket 连接 + chatStore.connectWS(); + // 聚焦输入框 chatInputRef.value?.focus(); @@ -304,7 +262,7 @@ onUnmounted(() => { /* 主体区域 */ .chat-main { - @apply flex-1 overflow-hidden; + @apply flex-1 overflow-y-auto; } /* 底部输入区域 */ @@ -406,4 +364,4 @@ onUnmounted(() => { @apply bottom-16 left-2 right-2; } } - \ No newline at end of file + diff --git a/vue3-frontend/src/router/index.ts b/vue3-frontend/src/router/index.ts index 58da34a..0d1f2a7 100644 --- a/vue3-frontend/src/router/index.ts +++ b/vue3-frontend/src/router/index.ts @@ -1,7 +1,5 @@ import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'; import ChatPage from '@/pages/ChatPage.vue'; -import HistoryPage from '@/pages/HistoryPage.vue'; -import SettingsPage from '@/pages/SettingsPage.vue'; const routes: RouteRecordRaw[] = [ { @@ -12,22 +10,7 @@ const routes: RouteRecordRaw[] = [ title: 'AI客服对话' } }, - { - path: '/history', - name: 'History', - component: HistoryPage, - meta: { - title: '历史对话' - } - }, - { - path: '/settings', - name: 'Settings', - component: SettingsPage, - meta: { - title: '设置' - } - }, + { path: '/:pathMatch(.*)*', redirect: '/' @@ -47,4 +30,4 @@ router.beforeEach((to, from, next) => { next(); }); -export default router; \ No newline at end of file +export default router; diff --git a/vue3-frontend/src/stores/chat.ts b/vue3-frontend/src/stores/chat.ts index 58dc981..3e313c9 100644 --- a/vue3-frontend/src/stores/chat.ts +++ b/vue3-frontend/src/stores/chat.ts @@ -9,6 +9,7 @@ export const useChatStore = defineStore('chat', () => { const isLoading = ref(false); const isConnected = ref(false); const currentSessionId = ref(''); + const wsRef = ref(null); // 计算属性 const hasMessages = computed(() => messages.value.length > 0); @@ -71,6 +72,17 @@ export const useChatStore = defineStore('chat', () => { } }; + const appendToLastMessage = (chunk: string, isComplete = false) => { + if (messages.value.length > 0) { + const lastMsg = messages.value[messages.value.length - 1]; + lastMsg.content = (lastMsg.content || '') + (chunk || ''); + if (currentConversation.value) { + currentConversation.value.messages = [...messages.value]; + currentConversation.value.updated_at = Date.now(); + } + } + }; + // 处理SSE响应 const handleSSEResponse = (response: SSEResponse) => { const { type, payload } = response; @@ -86,30 +98,29 @@ export const useChatStore = defineStore('chat', () => { // 根据数据类型处理不同的消息 switch (data_type) { case 'general_chat_stream': - if (content.chunk) { - // 如果是新的AI回复,创建新消息 + if (content.chunk || content.full_message) { if (messages.value.length === 0 || messages.value[messages.value.length - 1].type !== 'ai') { - addMessage({ - type: 'ai', - content: content.chunk - }); + addMessage({ type: 'ai', content: content.full_message || content.chunk }); } else { - // 更新现有消息 - updateLastMessage(content.full_message || content.chunk, content.is_final); + if (content.full_message) { + updateLastMessage(content.full_message, content.is_final); + } else if (content.chunk) { + appendToLastMessage(content.chunk, content.is_final); + } } } break; case 'thinking_process': - if (content.chunk) { - // 思考过程消息 + if (content.chunk || content.full_message) { if (messages.value.length === 0 || messages.value[messages.value.length - 1].type !== 'thinking') { - addMessage({ - type: 'thinking', - content: content.chunk - }); + addMessage({ type: 'thinking', content: content.full_message || content.chunk }); } else { - updateLastMessage(content.full_message || content.chunk, content.is_final); + if (content.full_message) { + updateLastMessage(content.full_message, content.is_final); + } else if (content.chunk) { + appendToLastMessage(content.chunk, content.is_final); + } } } break; @@ -131,7 +142,7 @@ export const useChatStore = defineStore('chat', () => { }; // 发送消息 - const sendMessage = async (content: string) => { + const sendMessage = async (content: string, options?: { tag?: 'order' | 'product' }) => { if (!content.trim()) return; // 如果没有当前对话,创建新对话 @@ -148,63 +159,71 @@ export const useChatStore = defineStore('chat', () => { isLoading.value = true; try { - // 调用Mock API(这里先用简单的模拟) - const response = await fetch('/api/chat/stream', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - message: content, - conversation_id: currentConversation.value?.id - }) - }); - - if (!response.ok) { - throw new Error('Network response was not ok'); - } - - // 处理SSE流 - const reader = response.body?.getReader(); - const decoder = new TextDecoder(); - - if (reader) { - isConnected.value = true; - - while (true) { - const { done, value } = await reader.read(); - if (done) break; - - const chunk = decoder.decode(value); - const lines = chunk.split('\n'); - - for (const line of lines) { - if (line.startsWith('data: ')) { - try { - const data = JSON.parse(line.slice(6)); - handleSSEResponse(data); - } catch (e) { - console.error('Failed to parse SSE data:', e); - } - } + if (wsRef.value && wsRef.value.readyState === WebSocket.OPEN) { + const payload: any = { message: content.trim(), session_id: 'default' }; + if (options?.tag) payload.tag = options.tag; + wsRef.value.send(JSON.stringify(payload)); + } else { + const WS_URL = 'ws://127.0.0.1:8302/api/chat/ws'; + const ws = new WebSocket(WS_URL); + wsRef.value = ws; + ws.onopen = () => { + isConnected.value = true; + const payload: any = { message: content.trim(), session_id: 'default' }; + if (options?.tag) payload.tag = options.tag; + ws.send(JSON.stringify(payload)); + }; + ws.onmessage = (event: MessageEvent) => { + try { + const raw = typeof event.data === 'string' ? event.data : ''; + const data: SSEResponse = JSON.parse(raw); + handleSSEResponse(data); + } catch (e) { + console.error('WS message parse error:', e); } - } - - isConnected.value = false; + }; + ws.onerror = () => { + isLoading.value = false; + isConnected.value = false; + addMessage({ type: 'ai', content: '抱歉,连接后端失败,请稍后重试。' }); + }; + ws.onclose = () => { + wsRef.value = null; + isConnected.value = false; + isLoading.value = false; + }; } } catch (error) { - console.error('Failed to send message:', error); isLoading.value = false; isConnected.value = false; - - // 添加错误消息 - addMessage({ - type: 'ai', - content: '抱歉,发送消息时出现错误,请稍后重试。' - }); + addMessage({ type: 'ai', content: '抱歉,建立连接失败,请稍后重试。' }); } }; + const connectWS = () => { + if (wsRef.value && wsRef.value.readyState === WebSocket.OPEN) return; + const WS_URL = 'ws://127.0.0.1:8302/api/chat/ws'; + const ws = new WebSocket(WS_URL); + wsRef.value = ws; + ws.onopen = () => { + isConnected.value = true; + }; + ws.onmessage = (event: MessageEvent) => { + try { + const raw = typeof event.data === 'string' ? event.data : ''; + const data: SSEResponse = JSON.parse(raw); + handleSSEResponse(data); + } catch {} + }; + ws.onerror = () => { + isConnected.value = false; + }; + ws.onclose = () => { + wsRef.value = null; + isConnected.value = false; + }; + }; + // 清空当前对话 const clearCurrentConversation = () => { messages.value = []; @@ -237,7 +256,8 @@ export const useChatStore = defineStore('chat', () => { updateLastMessage, handleSSEResponse, sendMessage, + connectWS, clearCurrentConversation, loadConversation }; -}); \ No newline at end of file +}); diff --git a/vue3-frontend/src/types/index.ts b/vue3-frontend/src/types/index.ts index 24f32ba..27a73e6 100644 --- a/vue3-frontend/src/types/index.ts +++ b/vue3-frontend/src/types/index.ts @@ -40,7 +40,7 @@ export interface SSEResponse { full_message: string; is_final: boolean; }; - component?: OrderCardData; + component?: OrderCardData | { order: WSOrder }; }; } @@ -59,6 +59,7 @@ export interface SSEErrorResponse { export interface ChatRequest { message: string; conversation_id?: string; + tag?: 'order' | 'product'; } // LocalStorage 键值结构 @@ -77,4 +78,12 @@ export interface Settings { theme: 'light' | 'dark' | 'auto'; fontSize: 'small' | 'medium' | 'large'; autoSave: boolean; -} \ No newline at end of file +} + +export interface WSOrder { + id: string; + product: string; + amount: number; + status: string; + create_time: string; +} diff --git a/vue3-frontend/vite.config.ts.timestamp-1763692841391-01acb3bfdaeb1.mjs b/vue3-frontend/vite.config.ts.timestamp-1763692841391-01acb3bfdaeb1.mjs new file mode 100644 index 0000000..260a365 --- /dev/null +++ b/vue3-frontend/vite.config.ts.timestamp-1763692841391-01acb3bfdaeb1.mjs @@ -0,0 +1,38 @@ +// vite.config.ts +import { defineConfig } from "file:///mnt/e/code/courseware/vue3-frontend/node_modules/vite/dist/node/index.js"; +import vue from "file:///mnt/e/code/courseware/vue3-frontend/node_modules/@vitejs/plugin-vue/dist/index.mjs"; +import { resolve } from "path"; +import Inspector from "file:///mnt/e/code/courseware/vue3-frontend/node_modules/unplugin-vue-dev-locator/dist/vite.mjs"; +import traeBadgePlugin from "file:///mnt/e/code/courseware/vue3-frontend/node_modules/vite-plugin-trae-solo-badge/dist/vite-plugin.esm.js"; +var __vite_injected_original_dirname = "/mnt/e/code/courseware/vue3-frontend"; +var vite_config_default = defineConfig({ + build: { + sourcemap: "hidden" + }, + plugins: [ + vue(), + Inspector(), + traeBadgePlugin({ + variant: "dark", + position: "bottom-right", + prodOnly: true, + clickable: true, + clickUrl: "https://www.trae.ai/solo?showJoin=1", + autoTheme: true, + autoThemeTarget: "#app" + }) + ], + resolve: { + alias: { + "@": resolve(__vite_injected_original_dirname, "src") + } + }, + server: { + port: 3e3, + open: true + } +}); +export { + vite_config_default as default +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvbW50L2UvY29kZS9jb3Vyc2V3YXJlL3Z1ZTMtZnJvbnRlbmRcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIi9tbnQvZS9jb2RlL2NvdXJzZXdhcmUvdnVlMy1mcm9udGVuZC92aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vbW50L2UvY29kZS9jb3Vyc2V3YXJlL3Z1ZTMtZnJvbnRlbmQvdml0ZS5jb25maWcudHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd2aXRlJ1xuaW1wb3J0IHZ1ZSBmcm9tICdAdml0ZWpzL3BsdWdpbi12dWUnXG5pbXBvcnQgeyByZXNvbHZlIH0gZnJvbSAncGF0aCdcbmltcG9ydCBJbnNwZWN0b3IgZnJvbSAndW5wbHVnaW4tdnVlLWRldi1sb2NhdG9yL3ZpdGUnXG5pbXBvcnQgdHJhZUJhZGdlUGx1Z2luIGZyb20gJ3ZpdGUtcGx1Z2luLXRyYWUtc29sby1iYWRnZSdcblxuLy8gaHR0cHM6Ly92aXRlLmRldi9jb25maWcvXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoe1xuICBidWlsZDoge1xuICAgIHNvdXJjZW1hcDogJ2hpZGRlbicsXG4gIH0sXG4gIHBsdWdpbnM6IFtcbiAgICB2dWUoKSxcbiAgICBJbnNwZWN0b3IoKSxcbiAgICB0cmFlQmFkZ2VQbHVnaW4oe1xuICAgICAgdmFyaWFudDogJ2RhcmsnLFxuICAgICAgcG9zaXRpb246ICdib3R0b20tcmlnaHQnLFxuICAgICAgcHJvZE9ubHk6IHRydWUsXG4gICAgICBjbGlja2FibGU6IHRydWUsXG4gICAgICBjbGlja1VybDogJ2h0dHBzOi8vd3d3LnRyYWUuYWkvc29sbz9zaG93Sm9pbj0xJyxcbiAgICAgIGF1dG9UaGVtZTogdHJ1ZSxcbiAgICAgIGF1dG9UaGVtZVRhcmdldDogJyNhcHAnLFxuICAgIH0pLFxuICBdLFxuICByZXNvbHZlOiB7XG4gICAgYWxpYXM6IHtcbiAgICAgICdAJzogcmVzb2x2ZShfX2Rpcm5hbWUsICdzcmMnKVxuICAgIH1cbiAgfSxcbiAgc2VydmVyOiB7XG4gICAgcG9ydDogMzAwMCxcbiAgICBvcGVuOiB0cnVlXG4gIH1cbn0pXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQThSLFNBQVMsb0JBQW9CO0FBQzNULE9BQU8sU0FBUztBQUNoQixTQUFTLGVBQWU7QUFDeEIsT0FBTyxlQUFlO0FBQ3RCLE9BQU8scUJBQXFCO0FBSjVCLElBQU0sbUNBQW1DO0FBT3pDLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzFCLE9BQU87QUFBQSxJQUNMLFdBQVc7QUFBQSxFQUNiO0FBQUEsRUFDQSxTQUFTO0FBQUEsSUFDUCxJQUFJO0FBQUEsSUFDSixVQUFVO0FBQUEsSUFDVixnQkFBZ0I7QUFBQSxNQUNkLFNBQVM7QUFBQSxNQUNULFVBQVU7QUFBQSxNQUNWLFVBQVU7QUFBQSxNQUNWLFdBQVc7QUFBQSxNQUNYLFVBQVU7QUFBQSxNQUNWLFdBQVc7QUFBQSxNQUNYLGlCQUFpQjtBQUFBLElBQ25CLENBQUM7QUFBQSxFQUNIO0FBQUEsRUFDQSxTQUFTO0FBQUEsSUFDUCxPQUFPO0FBQUEsTUFDTCxLQUFLLFFBQVEsa0NBQVcsS0FBSztBQUFBLElBQy9CO0FBQUEsRUFDRjtBQUFBLEVBQ0EsUUFBUTtBQUFBLElBQ04sTUFBTTtBQUFBLElBQ04sTUFBTTtBQUFBLEVBQ1I7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo= diff --git a/vue3-frontend/vite.config.ts.timestamp-1763706544786-1b659ad8269ab.mjs b/vue3-frontend/vite.config.ts.timestamp-1763706544786-1b659ad8269ab.mjs new file mode 100644 index 0000000..260a365 --- /dev/null +++ b/vue3-frontend/vite.config.ts.timestamp-1763706544786-1b659ad8269ab.mjs @@ -0,0 +1,38 @@ +// vite.config.ts +import { defineConfig } from "file:///mnt/e/code/courseware/vue3-frontend/node_modules/vite/dist/node/index.js"; +import vue from "file:///mnt/e/code/courseware/vue3-frontend/node_modules/@vitejs/plugin-vue/dist/index.mjs"; +import { resolve } from "path"; +import Inspector from "file:///mnt/e/code/courseware/vue3-frontend/node_modules/unplugin-vue-dev-locator/dist/vite.mjs"; +import traeBadgePlugin from "file:///mnt/e/code/courseware/vue3-frontend/node_modules/vite-plugin-trae-solo-badge/dist/vite-plugin.esm.js"; +var __vite_injected_original_dirname = "/mnt/e/code/courseware/vue3-frontend"; +var vite_config_default = defineConfig({ + build: { + sourcemap: "hidden" + }, + plugins: [ + vue(), + Inspector(), + traeBadgePlugin({ + variant: "dark", + position: "bottom-right", + prodOnly: true, + clickable: true, + clickUrl: "https://www.trae.ai/solo?showJoin=1", + autoTheme: true, + autoThemeTarget: "#app" + }) + ], + resolve: { + alias: { + "@": resolve(__vite_injected_original_dirname, "src") + } + }, + server: { + port: 3e3, + open: true + } +}); +export { + vite_config_default as default +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvbW50L2UvY29kZS9jb3Vyc2V3YXJlL3Z1ZTMtZnJvbnRlbmRcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIi9tbnQvZS9jb2RlL2NvdXJzZXdhcmUvdnVlMy1mcm9udGVuZC92aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vbW50L2UvY29kZS9jb3Vyc2V3YXJlL3Z1ZTMtZnJvbnRlbmQvdml0ZS5jb25maWcudHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd2aXRlJ1xuaW1wb3J0IHZ1ZSBmcm9tICdAdml0ZWpzL3BsdWdpbi12dWUnXG5pbXBvcnQgeyByZXNvbHZlIH0gZnJvbSAncGF0aCdcbmltcG9ydCBJbnNwZWN0b3IgZnJvbSAndW5wbHVnaW4tdnVlLWRldi1sb2NhdG9yL3ZpdGUnXG5pbXBvcnQgdHJhZUJhZGdlUGx1Z2luIGZyb20gJ3ZpdGUtcGx1Z2luLXRyYWUtc29sby1iYWRnZSdcblxuLy8gaHR0cHM6Ly92aXRlLmRldi9jb25maWcvXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoe1xuICBidWlsZDoge1xuICAgIHNvdXJjZW1hcDogJ2hpZGRlbicsXG4gIH0sXG4gIHBsdWdpbnM6IFtcbiAgICB2dWUoKSxcbiAgICBJbnNwZWN0b3IoKSxcbiAgICB0cmFlQmFkZ2VQbHVnaW4oe1xuICAgICAgdmFyaWFudDogJ2RhcmsnLFxuICAgICAgcG9zaXRpb246ICdib3R0b20tcmlnaHQnLFxuICAgICAgcHJvZE9ubHk6IHRydWUsXG4gICAgICBjbGlja2FibGU6IHRydWUsXG4gICAgICBjbGlja1VybDogJ2h0dHBzOi8vd3d3LnRyYWUuYWkvc29sbz9zaG93Sm9pbj0xJyxcbiAgICAgIGF1dG9UaGVtZTogdHJ1ZSxcbiAgICAgIGF1dG9UaGVtZVRhcmdldDogJyNhcHAnLFxuICAgIH0pLFxuICBdLFxuICByZXNvbHZlOiB7XG4gICAgYWxpYXM6IHtcbiAgICAgICdAJzogcmVzb2x2ZShfX2Rpcm5hbWUsICdzcmMnKVxuICAgIH1cbiAgfSxcbiAgc2VydmVyOiB7XG4gICAgcG9ydDogMzAwMCxcbiAgICBvcGVuOiB0cnVlXG4gIH1cbn0pXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQThSLFNBQVMsb0JBQW9CO0FBQzNULE9BQU8sU0FBUztBQUNoQixTQUFTLGVBQWU7QUFDeEIsT0FBTyxlQUFlO0FBQ3RCLE9BQU8scUJBQXFCO0FBSjVCLElBQU0sbUNBQW1DO0FBT3pDLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzFCLE9BQU87QUFBQSxJQUNMLFdBQVc7QUFBQSxFQUNiO0FBQUEsRUFDQSxTQUFTO0FBQUEsSUFDUCxJQUFJO0FBQUEsSUFDSixVQUFVO0FBQUEsSUFDVixnQkFBZ0I7QUFBQSxNQUNkLFNBQVM7QUFBQSxNQUNULFVBQVU7QUFBQSxNQUNWLFVBQVU7QUFBQSxNQUNWLFdBQVc7QUFBQSxNQUNYLFVBQVU7QUFBQSxNQUNWLFdBQVc7QUFBQSxNQUNYLGlCQUFpQjtBQUFBLElBQ25CLENBQUM7QUFBQSxFQUNIO0FBQUEsRUFDQSxTQUFTO0FBQUEsSUFDUCxPQUFPO0FBQUEsTUFDTCxLQUFLLFFBQVEsa0NBQVcsS0FBSztBQUFBLElBQy9CO0FBQUEsRUFDRjtBQUFBLEVBQ0EsUUFBUTtBQUFBLElBQ04sTUFBTTtBQUFBLElBQ04sTUFBTTtBQUFBLEVBQ1I7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo=