Browse Source

将"AI问答"改为“AI助手”,添加图标×

zkn 2 tuần trước cách đây
mục cha
commit
7855695625

+ 4 - 3
shudao-vue-frontend/src/components/Sidebar.test.js

@@ -26,7 +26,8 @@ describe('Sidebar', () => {
   it('renders the desktop feature navigation buttons', () => {
     const wrapper = mount(Sidebar)
 
-    expect(wrapper.text()).toContain('AI问答')
+    expect(wrapper.text()).toContain('AI助手')
+    expect(wrapper.text()).not.toContain('AI问答')
     expect(wrapper.text()).toContain('隐患提示')
     expect(wrapper.text()).toContain('AI写作')
     expect(wrapper.text()).toContain('安全培训')
@@ -34,7 +35,7 @@ describe('Sidebar', () => {
   })
 
   it.each([
-    ['AI问答', '/chat', '/chat'],
+    ['AI助手', '/chat', '/chat'],
     ['隐患提示', '/hazard-detection', '/hazard-detection']
   ])('routes %s to %s', async (label, path, expectedTarget) => {
     routeState.name = 'AIWriting'
@@ -63,7 +64,7 @@ describe('Sidebar', () => {
   })
 
   it.each([
-    ['AI问答', '/chat', 'ai-qa'],
+    ['AI助手', '/chat', 'ai-qa'],
     ['AI写作', '/ai-writing', 'ai-writing'],
     ['安全培训', '/safety-hazard', 'safety-training'],
     ['考试工坊', '/exam-workshop', 'exam-workshop']

+ 1 - 1
shudao-vue-frontend/src/components/Sidebar.vue

@@ -53,7 +53,7 @@ const props = defineProps({
 const emit = defineEmits(['select-mode'])
 
 const navItems = [
-  { routeName: 'Chat', path: '/chat', label: 'AI问答', icon: chatIcon, testId: 'sidebar-nav-chat', mode: 'ai-qa', target: '/chat' },
+  { routeName: 'Chat', path: '/chat', label: 'AI助手', icon: chatIcon, testId: 'sidebar-nav-chat', mode: 'ai-qa', target: '/chat' },
   { routeName: 'HazardDetection', path: '/hazard-detection', label: '隐患提示', icon: hazardIcon, testId: 'sidebar-nav-hazard-detection', target: '/hazard-detection' },
   { routeName: 'AIWriting', path: '/ai-writing', label: 'AI写作', icon: aiWritingIcon, testId: 'sidebar-nav-ai-writing', mode: 'ai-writing', target: { path: '/chat', query: { mode: 'ai-writing' } } },
   { routeName: 'SafetyHazard', path: '/safety-hazard', label: '安全培训', icon: safetyIcon, testId: 'sidebar-nav-safety-hazard', mode: 'safety-training', target: { path: '/chat', query: { mode: 'safety-training' } } },

+ 31 - 0
shudao-vue-frontend/src/views/Chat.modeChips.test.js

@@ -0,0 +1,31 @@
+import { readFileSync } from 'node:fs'
+import { dirname, resolve } from 'node:path'
+import { fileURLToPath } from 'node:url'
+import { describe, expect, it } from 'vitest'
+
+const __filename = fileURLToPath(import.meta.url)
+const __dirname = dirname(__filename)
+const chatSource = readFileSync(resolve(__dirname, 'Chat.vue'), 'utf8')
+
+describe('Chat mode chips', () => {
+  it.each([
+    ['AI写作', 'ai-writing', 'mode-chip-close-ai-writing'],
+    ['安全培训', 'safety-training', 'mode-chip-close-safety-training']
+  ])('shows a close control beside the active %s chip', (_label, mode, testId) => {
+    expect(chatSource).toContain(`v-if="currentMode === '${mode}'"`)
+    expect(chatSource).toContain(`data-testid="${testId}"`)
+  })
+
+  it('returns to the default assistant mode when a close control is clicked', () => {
+    const closeButtonCount = chatSource.match(/class="mode-chip-close"/g)?.length || 0
+
+    expect(closeButtonCount).toBe(2)
+    expect(chatSource).toContain('@click.stop="setMode(\'ai-qa\')"')
+  })
+
+  it('hides the input area after switching to exam workshop', () => {
+    expect(chatSource).toContain('v-show="currentMode !== \'exam-workshop\'"')
+    expect(chatSource).not.toContain('data-testid="mode-chip-close-exam-workshop"')
+    expect(chatSource).not.toContain("'exam-workshop-mode': currentMode === 'exam-workshop'")
+  })
+})

+ 46 - 2
shudao-vue-frontend/src/views/Chat.vue

@@ -533,6 +533,16 @@
                   >
                     <img src="../assets/Chat/13.png" alt="AI写作" class="chip-icon">
                     <span>AI写作</span>
+                    <button
+                      v-if="currentMode === 'ai-writing'"
+                      type="button"
+                      class="mode-chip-close"
+                      data-testid="mode-chip-close-ai-writing"
+                      aria-label="关闭AI写作"
+                      @click.stop="setMode('ai-qa')"
+                    >
+                      ×
+                    </button>
                   </div>
                   
                   <div 
@@ -543,6 +553,16 @@
                   >
                     <img src="../assets/Chat/14.png" alt="安全培训" class="chip-icon">
                     <span>安全培训</span>
+                    <button
+                      v-if="currentMode === 'safety-training'"
+                      type="button"
+                      class="mode-chip-close"
+                      data-testid="mode-chip-close-safety-training"
+                      aria-label="关闭安全培训"
+                      @click.stop="setMode('ai-qa')"
+                    >
+                      ×
+                    </button>
                   </div>
 
                   <div 
@@ -564,7 +584,7 @@
                   :class="{ 'active': isOnlineModel }"
                   @click="toggleModelType" 
                   :disabled="isSending || hasTypingMessage" 
-                  :title="isOnlineModel ? '当前:在线大模型 (qwen3-max)' : '当前:本地模型'"
+                  :title="isOnlineModel ? '当前:在线大模型 (qwen3.6-plus)' : '当前:本地模型'"
                 >
                   <div class="icon-container">
                     <img :src="isOnlineModel ? networkSearchIconOn : networkSearchIconOff" alt="在线大模型" class="action-icon network-search-icon" :class="{ 'active-icon': isOnlineModel }">
@@ -844,7 +864,7 @@ const isOnlineModel = ref(true)  // 是否使用在线大模型
 const toggleModelType = () => {
   isOnlineModel.value = !isOnlineModel.value
   showToast(
-    isOnlineModel.value ? '启动在线大模型 (qwen3-max)' : '已禁用在线大模型'
+    isOnlineModel.value ? '启动在线大模型 (qwen3.6-plus)' : '已禁用在线大模型'
   )
 }
 
@@ -7723,6 +7743,30 @@ onActivated(async () => {
             filter: invert(41%) sepia(91%) saturate(3574%) hue-rotate(213deg) brightness(101%) contrast(96%);
           }
         }
+
+        .mode-chip-close {
+          width: 16px;
+          height: 16px;
+          display: inline-flex;
+          align-items: center;
+          justify-content: center;
+          flex-shrink: 0;
+          margin-left: 2px;
+          padding: 0;
+          border: none;
+          border-radius: 50%;
+          background: rgba(62, 123, 250, 0.12);
+          color: #3E7BFA;
+          font-size: 13px;
+          line-height: 1;
+          cursor: pointer;
+          transition: all 0.2s ease;
+
+          &:hover {
+            background: rgba(62, 123, 250, 0.2);
+            color: #2b65e2;
+          }
+        }
       }
 
       .expand-btn {