docs(plan): frontend 顶部重排 + 搜索高亮 + 分组默认展开
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,542 @@
|
|||||||
|
# Frontend 顶部重排 + 搜索高亮 + 分组默认展开 Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** 把 ChatHeader 顶部右侧改为"清空 icon + 模型下拉 + 文件面板折叠 icon";标题前加 agentName 首字圆形 icon;文件面板默认展开;侧栏搜索框聚焦高亮;分组默认全部展开。
|
||||||
|
|
||||||
|
**Architecture:** 三个文件局部改动,不新增组件。ChatHeader 改为 props-driven(`modelOptions`、`modelValue`、`filePanelVisible`),通过 `update:modelValue` 与 `toggle-file-panel` 两个事件与父组件通讯。ConversationSidebar 调整 `syncExpandedWithCurrent` 默认行为并补充搜索框 `:focus` 样式。index.vue 默认 `filePanelVisible = true`、新增 `handleModelSelectChange` / `handleToggleFilePanel`、删除 `handleTabChange` 与 `setActiveTab` 调用。LlmModelModal 文件保留不引用。
|
||||||
|
|
||||||
|
**Tech Stack:** Vue 3.5 + Composition API + Element Plus 2.13(`el-select` / `el-option` / `Fold` / `Expand` / `Delete` 图标)+ Sass(sass-embedded 1.97)。无前端测试框架,沿用手动 dev 验证(Task 5)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
| 文件 | 操作 | 职责 |
|
||||||
|
|---|---|---|
|
||||||
|
| `src/views/frontend/components/ChatHeader.vue` | 改 | 模板重排(avatar / clear icon / model select / sidebar fold icon);脚本替换(LlmModelModal 移除、新增 modelOptions 等 props、单一 update:modelValue 通道);样式新增(.agent-avatar / .icon-btn / .model-select) |
|
||||||
|
| `src/views/frontend/components/ConversationSidebar.vue` | 改 | `.sidebar-search` 追加 `:focus` 样式;`syncExpandedWithCurrent` 改为全部展开 |
|
||||||
|
| `src/views/frontend/index.vue` | 改 | `filePanelVisible` 默认 `true`;删除 `handleTabChange`;`handleFilePanelClose` 中移除 `setActiveTab` 调用;新增 `modelSelectOptions` computed、`handleModelSelectChange`、`handleToggleFilePanel`;模板 ChatHeader props 与事件调整 |
|
||||||
|
| `src/views/frontend/components/LlmModelModal.vue` | 保留 | 文件保留,未来如需"更多模型"入口再启用 |
|
||||||
|
|
||||||
|
无新增文件;不新增组件抽象。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 1: ChatHeader —— props 与事件契约改造
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `src/views/frontend/components/ChatHeader.vue:33-67`(`<script setup>` 段)
|
||||||
|
|
||||||
|
- [ ] **Step 1: 替换 icon 导入**
|
||||||
|
|
||||||
|
将:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { Cpu } from '@element-plus/icons-vue'
|
||||||
|
import LlmModelModal from './LlmModelModal.vue'
|
||||||
|
```
|
||||||
|
|
||||||
|
改为:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { Delete, Fold, Expand } from '@element-plus/icons-vue'
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 删除 LlmModelModal 引用与 tab 状态**
|
||||||
|
|
||||||
|
删除以下声明(出现在 `import` 之后、`defineProps` 之前):
|
||||||
|
|
||||||
|
```js
|
||||||
|
const activeTab = ref('agent')
|
||||||
|
const showModelModal = ref(false)
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 扩展 props**
|
||||||
|
|
||||||
|
将:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const props = defineProps({
|
||||||
|
agentName: {
|
||||||
|
type: String,
|
||||||
|
default: '请选择会话'
|
||||||
|
},
|
||||||
|
currentModel: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
改为:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const props = defineProps({
|
||||||
|
agentName: {
|
||||||
|
type: String,
|
||||||
|
default: '请选择会话'
|
||||||
|
},
|
||||||
|
currentModel: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
modelOptions: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
filePanelVisible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 替换 emits**
|
||||||
|
|
||||||
|
将:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const emit = defineEmits(['tab-change', 'model-change', 'clear'])
|
||||||
|
```
|
||||||
|
|
||||||
|
改为:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const emit = defineEmits(['clear', 'update:modelValue', 'toggle-file-panel'])
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 5: 删除 tab/modal 相关函数、删除 expose,新增 `agentNameInitial` / `handleSelectChange` / `handleToggleClick`**
|
||||||
|
|
||||||
|
将整个 `<script setup>` 中除 `defineProps` / `defineEmits` / icon 导入之外的所有函数(含 `handleTabClick` / `handleModelSelect` / `defineExpose` 块)替换为:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const agentNameInitial = computed(() => {
|
||||||
|
const name = (props.agentName || '').trim()
|
||||||
|
if (!name) return '?'
|
||||||
|
// 中文取首字,英文取首字母
|
||||||
|
return [...name][0]
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleSelectChange(value) {
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleToggleClick() {
|
||||||
|
emit('toggle-file-panel')
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
并在 `<script setup>` 顶部补充 `import { computed } from 'vue'`(如果尚未导入)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 2: ChatHeader —— 模板替换
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `src/views/frontend/components/ChatHeader.vue:0-31`(`<template>` 段)
|
||||||
|
|
||||||
|
- [ ] **Step 1: 整段替换 `<template>` 内容**
|
||||||
|
|
||||||
|
将 `<template>` 块从:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div class="chat-header">
|
||||||
|
<div class="header-left">
|
||||||
|
<span class="conversation-name">{{ agentName }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="header-right">
|
||||||
|
<!-- 清空会话 -->
|
||||||
|
<el-button @click="emit('clear')">
|
||||||
|
清空会话
|
||||||
|
</el-button>
|
||||||
|
<!-- 模型选择 -->
|
||||||
|
<el-button @click="showModelModal = true">
|
||||||
|
{{ currentModel?.name || currentModel?.modelName || '选择模型' }}
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
:type="activeTab === 'agent' ? 'primary' : 'default'"
|
||||||
|
@click="handleTabClick('agent')"
|
||||||
|
>
|
||||||
|
智能体
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
:type="activeTab === 'file' ? 'primary' : 'default'"
|
||||||
|
@click="handleTabClick('file')"
|
||||||
|
>
|
||||||
|
文件
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 模型选择弹窗 -->
|
||||||
|
<LlmModelModal v-model="showModelModal" @select="handleModelSelect" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
替换为:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div class="chat-header">
|
||||||
|
<div class="header-left">
|
||||||
|
<div class="agent-avatar">{{ agentNameInitial }}</div>
|
||||||
|
<span class="conversation-name">{{ agentName }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="header-right">
|
||||||
|
<el-tooltip content="清空会话" placement="bottom" :show-after="300">
|
||||||
|
<el-button class="icon-btn" link @click="emit('clear')">
|
||||||
|
<el-icon><Delete /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
|
||||||
|
<el-select
|
||||||
|
class="model-select"
|
||||||
|
:model-value="modelValue"
|
||||||
|
@update:model-value="handleSelectChange"
|
||||||
|
placeholder="选择模型"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="opt in modelOptions"
|
||||||
|
:key="opt.value"
|
||||||
|
:label="opt.label"
|
||||||
|
:value="opt.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
|
||||||
|
<el-tooltip
|
||||||
|
:content="filePanelVisible ? '收起文件面板' : '展开文件面板'"
|
||||||
|
placement="bottom"
|
||||||
|
:show-after="300"
|
||||||
|
>
|
||||||
|
<el-button class="icon-btn" link @click="handleToggleClick">
|
||||||
|
<el-icon>
|
||||||
|
<component :is="filePanelVisible ? Fold : Expand" />
|
||||||
|
</el-icon>
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 3: ChatHeader —— 样式新增
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `src/views/frontend/components/ChatHeader.vue`(`<style scoped lang="scss">` 段)
|
||||||
|
|
||||||
|
- [ ] **Step 1: 重写 `<style scoped lang="scss">` 内容**
|
||||||
|
|
||||||
|
将现有 `<style scoped lang="scss">` 整段替换为:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
.chat-header {
|
||||||
|
padding: 11px;
|
||||||
|
border-bottom: 1px solid var(--fe-border);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--fe-bg-elevated);
|
||||||
|
color: var(--fe-text-primary);
|
||||||
|
|
||||||
|
.header-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
max-width: 480px;
|
||||||
|
|
||||||
|
.agent-avatar {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--fe-accent);
|
||||||
|
color: var(--fe-text-inverse);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-right {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.icon-btn {
|
||||||
|
padding: 4px 8px;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--fe-text-secondary);
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: var(--fe-radius-md);
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--fe-bg-hover);
|
||||||
|
color: var(--fe-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-select {
|
||||||
|
width: 180px;
|
||||||
|
:deep(.el-select__wrapper) {
|
||||||
|
background: var(--fe-bg-elevated);
|
||||||
|
box-shadow: 0 0 0 1px var(--fe-border) inset;
|
||||||
|
border-radius: var(--fe-radius-md);
|
||||||
|
}
|
||||||
|
:deep(.el-select__placeholder) { color: var(--fe-text-muted); }
|
||||||
|
:deep(.el-select__selected-item) { color: var(--fe-text-primary); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 4: ConversationSidebar —— 搜索框聚焦高亮
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `src/views/frontend/components/ConversationSidebar.vue`(`<style scoped lang="scss">` 中 `.sidebar-search` 块)
|
||||||
|
|
||||||
|
- [ ] **Step 1: 在 `.sidebar-search` 内追加 `:focus` 规则**
|
||||||
|
|
||||||
|
找到:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
.sidebar-search {
|
||||||
|
padding: 8px 12px 0;
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
background: var(--fe-bg-elevated);
|
||||||
|
box-shadow: 0 0 0 1px var(--fe-border) inset;
|
||||||
|
border-radius: var(--fe-radius-md);
|
||||||
|
}
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
color: var(--fe-text-primary);
|
||||||
|
&::placeholder { color: var(--fe-text-muted); }
|
||||||
|
}
|
||||||
|
:deep(.el-input__prefix .el-icon) { color: var(--fe-text-muted); }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
替换为:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
.sidebar-search {
|
||||||
|
padding: 8px 12px 0;
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
background: var(--fe-bg-elevated);
|
||||||
|
box-shadow: 0 0 0 1px var(--fe-border) inset;
|
||||||
|
border-radius: var(--fe-radius-md);
|
||||||
|
transition: box-shadow 0.15s, background 0.15s;
|
||||||
|
}
|
||||||
|
:deep(.el-input__wrapper.is-focus) {
|
||||||
|
box-shadow: 0 0 0 1px var(--fe-accent) inset;
|
||||||
|
background: var(--fe-bg-elevated);
|
||||||
|
}
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
color: var(--fe-text-primary);
|
||||||
|
&::placeholder { color: var(--fe-text-muted); }
|
||||||
|
}
|
||||||
|
:deep(.el-input__wrapper.is-focus .el-input__inner::placeholder) {
|
||||||
|
color: var(--fe-text-secondary);
|
||||||
|
}
|
||||||
|
:deep(.el-input__prefix .el-icon) { color: var(--fe-text-muted); }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 5: ConversationSidebar —— 分组默认全部展开
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `src/views/frontend/components/ConversationSidebar.vue`(`<script setup>` 中 `syncExpandedWithCurrent`)
|
||||||
|
|
||||||
|
- [ ] **Step 1: 重写 `syncExpandedWithCurrent` 函数体**
|
||||||
|
|
||||||
|
将:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function syncExpandedWithCurrent() {
|
||||||
|
if (currentSession.value) {
|
||||||
|
expandedGroups.value = new Set([groupOf(currentSession.value)])
|
||||||
|
} else {
|
||||||
|
expandedGroups.value = new Set(['today'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
替换为:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function syncExpandedWithCurrent() {
|
||||||
|
// 用户要求默认全部展开;用户可点击分组标题手动收/展
|
||||||
|
expandedGroups.value = new Set(['today', 'yesterday', 'earlier'])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 6: index.vue —— 文件面板默认展开 + 新增模型切换与折叠处理
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `src/views/frontend/index.vue`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 文件面板默认 `true`**
|
||||||
|
|
||||||
|
将 `const filePanelVisible = ref(false);` 改为 `const filePanelVisible = ref(true);`。
|
||||||
|
|
||||||
|
- [ ] **Step 2: 新增 `modelSelectOptions` computed 与 `handleModelSelectChange`**
|
||||||
|
|
||||||
|
在 `currentModel = ref(null)` 附近、`initModels` 之前,插入:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const modelSelectOptions = computed(() => modelList.value.map(m => ({
|
||||||
|
label: m.name || m.modelName || '',
|
||||||
|
value: m.modelName || m.name || ''
|
||||||
|
})))
|
||||||
|
|
||||||
|
function handleModelSelectChange(value) {
|
||||||
|
const found = modelList.value.find(m => (m.modelName || m.name) === value)
|
||||||
|
if (found) {
|
||||||
|
currentModel.value = found
|
||||||
|
ElMessage.success(`已选择模型: ${found.name || found.modelName}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 新增 `handleToggleFilePanel`**
|
||||||
|
|
||||||
|
在 `handleModelChange` 之后插入:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function handleToggleFilePanel() {
|
||||||
|
filePanelVisible.value = !filePanelVisible.value
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 删除 `handleTabChange` 函数体**
|
||||||
|
|
||||||
|
将 `handleTabChange` 整个函数(含之前的 `function handleTabChange(tab) { ... }`)连同其上方空行一起删除。
|
||||||
|
|
||||||
|
- [ ] **Step 5: 删除 `handleFilePanelClose` 中的 `setActiveTab` 调用**
|
||||||
|
|
||||||
|
将:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function handleFilePanelClose() {
|
||||||
|
filePanelVisible.value = false;
|
||||||
|
previewVisible.value = false;
|
||||||
|
chatHeaderRef.value?.setActiveTab('agent');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
改为:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function handleFilePanelClose() {
|
||||||
|
filePanelVisible.value = false;
|
||||||
|
previewVisible.value = false;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 6: 替换模板上的 `<ChatHeader>` 标签**
|
||||||
|
|
||||||
|
将:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<ChatHeader
|
||||||
|
ref="chatHeaderRef"
|
||||||
|
:agent-name="currentConversation?.agentName || '请选择会话'"
|
||||||
|
:current-model="currentModel"
|
||||||
|
@tab-change="handleTabChange"
|
||||||
|
@model-change="handleModelChange"
|
||||||
|
@clear="handleClear"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
替换为:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<ChatHeader
|
||||||
|
ref="chatHeaderRef"
|
||||||
|
:agent-name="currentConversation?.agentName || '请选择会话'"
|
||||||
|
:current-model="currentModel"
|
||||||
|
:model-options="modelSelectOptions"
|
||||||
|
:model-value="currentModel?.modelName || currentModel?.name || ''"
|
||||||
|
:file-panel-visible="filePanelVisible"
|
||||||
|
@update:model-value="handleModelSelectChange"
|
||||||
|
@toggle-file-panel="handleToggleFilePanel"
|
||||||
|
@clear="handleClear"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 7: 端到端冒烟清单
|
||||||
|
|
||||||
|
**Files:** 无新增;仅执行 dev 自检。
|
||||||
|
|
||||||
|
- [ ] **Step 1: ChatHeader 视觉**
|
||||||
|
|
||||||
|
启动 `npm run dev`,进入 `/frontend`,目测:
|
||||||
|
- 左侧 `agent-avatar` 圆形 icon(accent 背景、白字、显示 agentName 首字),后跟 agentName;
|
||||||
|
- 右侧依次:垃圾桶 icon(清空)、模型 el-select 下拉、文件面板折叠 icon(默认 Fold,因为 filePanelVisible 默认 true);
|
||||||
|
- 不再有 "智能体" / "文件" 两个 tab 按钮;不再有 `LlmModelModal` 弹窗入口。
|
||||||
|
|
||||||
|
- [ ] **Step 2: 文件面板默认展开 + icon 切换**
|
||||||
|
|
||||||
|
- 页面加载后文件面板可见;
|
||||||
|
- 点击 ChatHeader 右侧 Fold icon → 文件面板收起,icon 变为 Expand;
|
||||||
|
- 再点 Expand icon → 文件面板展开,icon 变回 Fold。
|
||||||
|
|
||||||
|
- [ ] **Step 3: 模型下拉**
|
||||||
|
|
||||||
|
- el-select 显示当前模型名称;
|
||||||
|
- 切换下拉 → 立即生效(chatWindow 后续发送消息会带上新模型);
|
||||||
|
- `ElMessage` 提示"已选择模型: …"。
|
||||||
|
|
||||||
|
- [ ] **Step 4: 清空 icon**
|
||||||
|
|
||||||
|
- 点击垃圾桶 icon → WebSocket 发送 `clear` 类型消息,聊天区消息清空;
|
||||||
|
- 控制台无 error。
|
||||||
|
|
||||||
|
- [ ] **Step 5: 搜索框聚焦高亮**
|
||||||
|
|
||||||
|
- 点击侧栏搜索框 → 输入框边框变为 accent 色;
|
||||||
|
- placeholder 颜色由 muted 变 secondary;
|
||||||
|
- 失焦恢复。
|
||||||
|
|
||||||
|
- [ ] **Step 6: 分组默认全部展开**
|
||||||
|
|
||||||
|
- 页面加载后 今天 / 昨天 / 更早 三段同时展开;
|
||||||
|
- 切换会话后仍全部展开(不回到"仅当前分组");
|
||||||
|
- 点击任一分组标题可手动折叠;切换会话或刷新会重置为全部展开。
|
||||||
|
|
||||||
|
- [ ] **Step 7: 不做 git 推送**
|
||||||
|
|
||||||
|
本次仅修改本地文件,不运行 `git push` / `git commit`(用户自行决定是否提交)。
|
||||||
Reference in New Issue
Block a user