Chatbot 聊天记录管理(2)-- 删除聊天

从UI来讲,删掉一个还是很简单的,无非是打开选择框,点击删除,在弹出框中选择确定。
以ChatGPT为例,总体流程为:

  1. 点击删除框
    在这里插入图片描述

  2. 出现一个删除弹窗
    在这里插入图片描述

  3. 点击删除删除成功(ChatGPT没有删除成功的提示,我们可以视情况添加)

在上一篇我们提到过,每一个聊天记录都是通过id这个凭证来进行绑定的,对于删除,我们也同样需要这个id来删除对应的聊天记录。
从组件分析,一步一步实现,我们这里主要使用两个和

在具体实践中,可能会有几个问题:

  1. 我们不是在Dropdown这个框里点击就删除的,而是在AlertDialog里面点击Delete删除的,点击Dropdown里的删除是记住我们要删除的id,以便我们可能的取消删除, UI的更新
  2. 由于是在AlertDialog里面点击Delete删除的,我们需要在父组件里的创建AlertDialog这个弹出框,实现handleDelete,避免子组件多次渲染,同时简化逻辑。
  3. 在handleDelete这里面呢,要注意,当删除成功后,需要将前端的数据一同删除。同时当我们删除后,我们可能自动需要关闭这个弹窗,这时我们就还需要一个状态来管理AlertDialog弹窗的开关。
  4. 当我们进行到这里的时候,有一点需要考虑,就是如果当我们所在的位置就是删除的那个聊天记录的页面,我们需要跳转到新建页面,即根路由。所以我们要获取一下当前的路由id。
  5. 到这,我们的删除逻辑基本结束了,不过在删除这个函数里有一个优化点,就是我们不希望需要等到删除成功后再切换,而是不管是否删除成功,当我们点击delete时就直接关闭弹窗,可以进行别的操作了,这里处理方法很多。整体代码架构

从整体的构成来讲,我们需要从父组件来实现:

  1. 获取当前的路由id:id = useParams();
  2. 存储选中删除的的聊天id:const [deleteId, setDeleteId] = useState<string | null>(null);
  3. 删除的函数:handleDelete
  const handleDelete = async () => {
    // 请求的代码,由于是假数据,直接采用下面的更新
    // fetch(`/api/chat/${deleteId}`, {
    //   method: 'DELETE',
    // })
    // .then((response) => {
    //   if (!response.ok) {
    //     throw new Error('删除失败');
    //   }
    //   setChatHistory(chatHistory.filter((chat) => chat.id !== deleteId));
    //   toast.success('删除成功');
    // })
    // .catch(() => {
    //   toast.error('删除失败');
    // })
    
    setChatHistory(chatHistory.filter((chat) => chat.id !== deleteId));

    setIsDeleteDialogOpen(false)

    if (id === deleteId) {
      router.push('/');
    }
  }
  1. 管理弹窗的状态:const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
  2. 上一篇里我们知道,我们通过如下代码渲染每一个记录条:
            {history.map((chat) => 
              <ChatItem 
                key={chat.id} 
                chat={chat}
              />)}

那么当我们点击某一条的时候,我们可以通过ChatItem得到点击那一条的id,并将它记录下来。所以在这里可以添加一下

{history.map((chat) => {
<ChatItem 
  key={chat.id}
  chat={chat}
  onDelete={(chatId) => {
    setDeleteId(chatId);
    setIsDialogOpen(true);
  }}
/>
})}

完整代码

const ChatItem = ({
  chat,
  isActive,
  onDelete,
}: {
  chat: any,
  isActive: boolean,
  onDelete: (chatId: string) => void,
}) => {
  return (
    <SidebarMenuItem>
      <SidebarMenuButton asChild isActive={isActive}>
        {/* 提供id,方便跳转到对应的路由,使用id获取数据 */}
        <Link href={`/chat/${chat.id}`}>
          <span>{chat.title}</span>
        </Link>
      </SidebarMenuButton>

      <DropdownMenu>
        <DropdownMenuTrigger asChild>
          <SidebarMenuAction>
            <MoreHorizontalIcon />
          </SidebarMenuAction>
        </DropdownMenuTrigger>

        <DropdownMenuContent side='bottom' align='end'>
          <DropdownMenuItem
            className='cursor-pointer text-destructive focus:bg-destructive/10 focus:text-destructive'
            onClick={() => onDelete(chat.id)}
          >
            <Trash2Icon />
            <span>删除</span>
          </DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
    </SidebarMenuItem>
  )
}

// 从后端获取记录,这里使用一些假记录
const history = [
  {
    createdAt: "2025-03-14T12:39:15.441Z",
    id: "1",
    title: "用户问候",
    userId: "110",
  },
  {
    createdAt: "2025-03-14T12:39:15.441Z",
    id: "2",
    title: "介绍自己",
    userId: "110",
  },
  {
    createdAt: "2025-03-14T12:39:15.441Z",
    id: "3",
    title: "OpenAI倒闭",
    userId: "110",
  },
]

function History() {
  // 获取id
  const { id } = useParams();
  const router = useRouter();
  // 使用假数据设置一个初始的聊天记录
  const [chatHistory, setChatHistory] = useState(history);

  const [deleteId, setDeleteId] = useState<string | null>(null)
  const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState<boolean>(false)

  const handleDelete = async () => {
    // 请求的代码,由于是假数据,直接采用下面的更新
    // fetch(`/api/chat/${deleteId}`, {
    //   method: 'DELETE',
    // })
    // .then((response) => {
    //   if (!response.ok) {
    //     throw new Error('删除失败');
    //   }
    //   setChatHistory(chatHistory.filter((chat) => chat.id !== deleteId));
    //   toast.success('删除成功');
    // })
    // .catch(() => {
    //   toast.error('删除失败');
    // })
    
    setChatHistory(chatHistory.filter((chat) => chat.id !== deleteId));

    setIsDeleteDialogOpen(false)

    if (id === deleteId) {
      router.push('/');
    }
  }

  return (
    <>
      <SidebarGroup>
        <SidebarGroupContent>
          <SidebarMenu>
            {/* 将其渲染 */}
            {chatHistory.map((chat) => 
              <ChatItem 
                key={chat.id} 
                chat={chat}
                isActive={id === chat.id}
                onDelete={(chatId) => {
                  setDeleteId(chatId)
                  setIsDeleteDialogOpen(true)
                }}
              />)} 
          </SidebarMenu>
        </SidebarGroupContent>
      </SidebarGroup>
      <AlertDialog open={isDeleteDialogOpen} onOpenChange={setIsDeleteDialogOpen}>
        <AlertDialogContent>
          <AlertDialogHeader>删除记录</AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>取消</AlertDialogCancel>
            <AlertDialogAction
              onClick={handleDelete}
            >确定</AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </>
  )
}

export default SidebarHistory

最终效果:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值