feat: 工作台对话列表添加删除功能
- 对话卡片 Hover 时显示删除按钮 - 点击删除按钮显示确认弹窗 - 删除按钮垂直居中对齐
This commit is contained in:
48
openspec/specs/conversation-delete/spec.md
Normal file
48
openspec/specs/conversation-delete/spec.md
Normal file
@@ -0,0 +1,48 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 对话列表项支持删除操作
|
||||
|
||||
对话列表中的每一项 SHALL 提供删除功能,允许用户移除不需要的对话记录。
|
||||
|
||||
#### Scenario: Hover 显示删除按钮
|
||||
|
||||
- **WHEN** 用户将鼠标悬停在对话卡片上
|
||||
- **THEN** 系统 SHALL 在卡片右侧显示删除图标按钮
|
||||
|
||||
#### Scenario: 默认隐藏删除按钮
|
||||
|
||||
- **WHEN** 用户未将鼠标悬停在对话卡片上
|
||||
- **THEN** 系统 SHALL 隐藏删除按钮,保持界面简洁
|
||||
|
||||
### Requirement: 删除操作需确认
|
||||
|
||||
系统 SHALL 在执行删除前显示确认弹窗,防止用户误操作。
|
||||
|
||||
#### Scenario: 点击删除按钮显示确认弹窗
|
||||
|
||||
- **WHEN** 用户点击删除按钮
|
||||
- **THEN** 系统 SHALL 显示确认弹窗,包含对话标题和确认/取消按钮
|
||||
|
||||
#### Scenario: 确认删除
|
||||
|
||||
- **WHEN** 用户在确认弹窗中点击"确定"按钮
|
||||
- **THEN** 系统 SHALL 关闭弹窗并模拟删除操作
|
||||
|
||||
#### Scenario: 取消删除
|
||||
|
||||
- **WHEN** 用户在确认弹窗中点击"取消"按钮或点击遮罩层
|
||||
- **THEN** 系统 SHALL 关闭弹窗,不执行删除操作
|
||||
|
||||
### Requirement: 删除按钮样式符合设计规范
|
||||
|
||||
删除按钮的样式 SHALL 符合项目现有的设计规范。
|
||||
|
||||
#### Scenario: 删除按钮默认样式
|
||||
|
||||
- **WHEN** 删除按钮显示时
|
||||
- **THEN** 按钮 SHALL 使用灰色图标,不干扰主要内容
|
||||
|
||||
#### Scenario: 删除按钮 Hover 样式
|
||||
|
||||
- **WHEN** 用户将鼠标悬停在删除按钮上
|
||||
- **THEN** 按钮 SHALL 变为红色,提示危险操作
|
||||
@@ -1,25 +1,40 @@
|
||||
import { useState } from 'react';
|
||||
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
|
||||
import { FiPlus, FiClock, FiList, FiUsers, FiBox } from 'react-icons/fi';
|
||||
import { FiTrash2 } from 'react-icons/fi';
|
||||
import { FaPuzzlePiece } from 'react-icons/fa';
|
||||
import Layout from '../Layout.jsx';
|
||||
import SidebarNavItem from './SidebarNavItem.jsx';
|
||||
import Modal from '../common/Modal.jsx';
|
||||
import api from '../../services/api.js';
|
||||
|
||||
function ConsoleLayout() {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const [deleteTarget, setDeleteTarget] = useState(null);
|
||||
|
||||
// 从 URL 提取当前 scene
|
||||
const sceneMatch = location.pathname.match(/\/console\/chat\/(.+)$/);
|
||||
const currentScene = sceneMatch ? sceneMatch[1] : null;
|
||||
|
||||
// 判断是否在 chat 页面(需要全宽布局)
|
||||
const isChatPage = location.pathname === '/console/chat' ||
|
||||
location.pathname.startsWith('/console/chat/');
|
||||
|
||||
// sidebar 高亮判断
|
||||
const isPathActive = (basePath) => location.pathname.startsWith(basePath);
|
||||
|
||||
const handleDeleteClick = (e, conv) => {
|
||||
e.stopPropagation();
|
||||
setDeleteTarget(conv);
|
||||
};
|
||||
|
||||
const handleDeleteConfirm = () => {
|
||||
console.log('删除对话:', deleteTarget?.id);
|
||||
setDeleteTarget(null);
|
||||
};
|
||||
|
||||
const handleDeleteCancel = () => {
|
||||
setDeleteTarget(null);
|
||||
};
|
||||
|
||||
const sidebar = (
|
||||
<>
|
||||
<div className="chat-sidebar-header">
|
||||
@@ -35,10 +50,21 @@ function ConsoleLayout() {
|
||||
<div
|
||||
key={conv.id}
|
||||
className={`conversation-item ${conv.scene === currentScene ? 'active' : ''}`}
|
||||
onClick={() => navigate(`/console/chat/${conv.scene}`)}
|
||||
>
|
||||
<div className="conversation-title">{conv.title}</div>
|
||||
<div className="conversation-time">{conv.time}</div>
|
||||
<div
|
||||
className="conversation-item__content"
|
||||
onClick={() => navigate(`/console/chat/${conv.scene}`)}
|
||||
>
|
||||
<div className="conversation-title">{conv.title}</div>
|
||||
<div className="conversation-time">{conv.time}</div>
|
||||
</div>
|
||||
<button
|
||||
className="conversation-item__delete"
|
||||
onClick={(e) => handleDeleteClick(e, conv)}
|
||||
title="删除对话"
|
||||
>
|
||||
<FiTrash2 size={14} />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -82,6 +108,14 @@ function ConsoleLayout() {
|
||||
onClick={() => navigate('/console/projects')}
|
||||
/>
|
||||
</div>
|
||||
<Modal
|
||||
visible={!!deleteTarget}
|
||||
title="确认删除"
|
||||
onConfirm={handleDeleteConfirm}
|
||||
onCancel={handleDeleteCancel}
|
||||
>
|
||||
确定要删除对话「{deleteTarget?.title}」吗?
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
@@ -143,6 +143,9 @@
|
||||
}
|
||||
|
||||
.conversation-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 14px;
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
@@ -153,6 +156,10 @@
|
||||
&:hover {
|
||||
background: var(--color-bg-1);
|
||||
border-color: var(--color-border-2);
|
||||
|
||||
.conversation-item__delete {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
@@ -162,6 +169,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
.conversation-item__content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.conversation-item__delete {
|
||||
flex-shrink: 0;
|
||||
padding: 4px;
|
||||
margin-left: 8px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--color-text-3);
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition: all var(--transition);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&:hover {
|
||||
color: var(--color-danger);
|
||||
}
|
||||
}
|
||||
|
||||
.conversation-title {
|
||||
font-size: 14px;
|
||||
margin-bottom: 4px;
|
||||
|
||||
Reference in New Issue
Block a user