瀏覽代碼

src/state/terminalStore.ts下 sendMessage() 加 if (this.sending) return 防重入
src/chat/ChatInput.vue submit() 加 if (store.sending) return 防双击
src/cards/ErrorCard.vue "重试"和"重新开始"拆分为两个独立事件 (@retry / @restart)
src/layouts/ContextPanel.vue 新增 handleRetry() 方法,重新提交当前卡片动作而非清空状态

ligao 1 月之前
父節點
當前提交
e3a51a116e

+ 2 - 0
.env.development

@@ -0,0 +1,2 @@
+VITE_API_BASE_URL=http://localhost:6040/api/v1
+VITE_DEMO_MODE=false

+ 10 - 0
.idea/.gitignore

@@ -0,0 +1,10 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# 依赖于环境的 Maven 主目录路径
+/mavenHomeManager.xml
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml

+ 9 - 0
.idea/emoon-portal.iml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 6 - 0
.idea/misc.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_22" default="true" project-jdk-name="graalvm-ce-22" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/out" />
+  </component>
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/emoon-portal.iml" filepath="$PROJECT_DIR$/.idea/emoon-portal.iml" />
+    </modules>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>

+ 1 - 1
src/api/types.ts

@@ -15,7 +15,7 @@ export interface ChatMessage {
 
 export interface CardAction {
   actionName: string;
-  label: string;
+  label?: string;
   requiredConfirm?: boolean;
 }
 

+ 11 - 3
src/cards/ErrorCard.vue

@@ -11,15 +11,23 @@
     <div class="error-desc">{{ message }}</div>
     <div v-if="traceId" class="error-trace">traceId: {{ traceId }}</div>
     <div class="error-btns">
-      <button class="btn-accent" @click="$emit('restart')">重试</button>
-      <button class="btn-outline" @click="$emit('restart')">重新开始</button>
+      <button class="btn-accent" @click="onRetry">重试</button>
+      <button class="btn-outline" @click="onRestart">重新开始</button>
     </div>
   </section>
 </template>
 
 <script setup lang="ts">
 defineProps<{ message: string; traceId?: string }>();
-defineEmits<{ restart: [] }>();
+const emit = defineEmits<{ retry: []; restart: [] }>();
+
+function onRetry() {
+  emit('retry');
+}
+
+function onRestart() {
+  emit('restart');
+}
 </script>
 
 <style scoped>

+ 1 - 0
src/chat/ChatInput.vue

@@ -37,6 +37,7 @@ const emit = defineEmits<{ submit: [text: string] }>();
 const store = useTerminalStore();
 
 function submit() {
+  if (store.sending) return;
   const text = store.inputText.trim();
   if (!text) return;
   store.inputText = '';

+ 9 - 0
src/env.d.ts

@@ -1,5 +1,14 @@
 /// <reference types="vite/client" />
 
+interface ImportMetaEnv {
+  readonly VITE_API_BASE_URL: string;
+  readonly VITE_DEMO_MODE: string;
+}
+
+interface ImportMeta {
+  readonly env: ImportMetaEnv;
+}
+
 declare module '*.vue' {
   import type { DefineComponent } from 'vue';
   const component: DefineComponent<object, object, unknown>;

+ 12 - 0
src/layouts/ContextPanel.vue

@@ -42,6 +42,7 @@
       <ErrorCard
         :message="store.errorMessage"
         :trace-id="store.currentTraceId"
+        @retry="handleRetry"
         @restart="store.resetToIdle"
       />
     </template>
@@ -65,6 +66,17 @@ async function handleAction(data: { actionName: string; payload: Record<string,
     // error already set in store
   }
 }
+
+async function handleRetry() {
+  store.errorMessage = '';
+  if (store.activeCard && store.selection) {
+    try {
+      await store.submitCard(store.selection);
+    } catch {
+      // error already set in store
+    }
+  }
+}
 </script>
 
 <style scoped>

+ 1 - 0
src/state/terminalStore.ts

@@ -186,6 +186,7 @@ export const useTerminalStore = defineStore('terminal', {
     },
 
     async sendMessage(text: string): Promise<void> {
+      if (this.sending) return; // 防重入:正在发送中不允许重复调用
       this.addMessage({ id: crypto.randomUUID(), role: 'user', content: text });
       this.errorMessage = '';
       this.sending = true;