Commit 30e06255 by HoMeTown

feat: 新建会话

parent 7a829213
...@@ -4,6 +4,14 @@ import http from '@/utils/request' ...@@ -4,6 +4,14 @@ import http from '@/utils/request'
* 查询用户历史会话列表 * 查询用户历史会话列表
* @params * @params
*/ */
export function queryUserConversationPage<T>(data: T) { export function fetchQueryUserConversationPage<T>(data: T) {
return http.post('/conversation/api/conversation/mobile/v1/query_user_conversation_page', data) return http.post('/conversation/api/conversation/mobile/v1/query_user_conversation_page', data)
} }
/**
* 创建会话
* @params { * }
*/
export function fetchCreateConversation<T>(data: T) {
return http.post('/conversation/api/conversation/mobile/v1/create_conversation', data)
}
...@@ -33,7 +33,7 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => { ...@@ -33,7 +33,7 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
const [showLoginTip, setShowLoginTip] = useState(!token) const [showLoginTip, setShowLoginTip] = useState(!token)
const login = async () => { const login = async () => {
const res = await fetchLoginByUid('123') const res = await fetchLoginByUid('123123')
if (res.data) { if (res.data) {
setToken(res.data.token) setToken(res.data.token)
setIsLoggedIn(true) setIsLoggedIn(true)
......
...@@ -5,7 +5,7 @@ import EmptyIcon from '@/assets/svg/empty.svg?react' ...@@ -5,7 +5,7 @@ import EmptyIcon from '@/assets/svg/empty.svg?react'
import { useAppSelector } from '@/store/hook' import { useAppSelector } from '@/store/hook'
export const HistoryBarList: React.FC = () => { export const HistoryBarList: React.FC = () => {
const { conversations } = useAppSelector(state => state.conversation) const { currentConversationId, conversations } = useAppSelector(state => state.conversation)
return ( return (
conversations.length !== 0 conversations.length !== 0
...@@ -30,7 +30,11 @@ export const HistoryBarList: React.FC = () => { ...@@ -30,7 +30,11 @@ export const HistoryBarList: React.FC = () => {
{ {
item.conversationId item.conversationId
? ( ? (
<Button color="primary" variant="light" className="text-left w-full text-[#333] rounded-[23px] data-[hover=true]:bg-[#E5F6FF] data-[hover=true]:text-primary"> <Button
color="primary"
variant="light"
className={`text-left w-full text-[#333] rounded-[23px] data-[hover=true]:bg-[#E5F6FF] data-[hover=true]:text-primary ${currentConversationId === item.conversationId ? 'bg-[#E5F6FF] text-primary' : ''}`}
>
<div className="w-full text-nowrap text-ellipsis overflow-hidden"> <div className="w-full text-nowrap text-ellipsis overflow-hidden">
<span>{item.conversationTitle}</span> <span>{item.conversationTitle}</span>
</div> </div>
......
import type React from 'react' import type React from 'react'
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
import styles from './Navbar.module.less' import styles from './Navbar.module.less'
import { NavBarItem } from './components/NavBarItem' import { NavBarItem } from './components/NavBarItem'
import { User } from './components/User' import { User } from './components/User'
import { clearNavigationFlag, createConversation } from '@/store/conversationSlice'
import type { WithAuthProps } from '@/auth/withAuth' import type { WithAuthProps } from '@/auth/withAuth'
import { withAuth } from '@/auth/withAuth' import { withAuth } from '@/auth/withAuth'
import { NAV_BAR_ITEMS } from '@/config/nav' import { NAV_BAR_ITEMS } from '@/config/nav'
import { useAppDispatch, useAppSelector } from '@/store/hook'
interface NavbarProps { interface NavbarProps {
isHistoryVisible: boolean isHistoryVisible: boolean
...@@ -13,10 +17,23 @@ interface NavbarProps { ...@@ -13,10 +17,23 @@ interface NavbarProps {
} }
const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, checkAuth, onSetHistoryVisible }) => { const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, checkAuth, onSetHistoryVisible }) => {
const dispatch = useAppDispatch()
const navigate = useNavigate()
const { currentConversationId, shouldNavigateToNewConversation } = useAppSelector(state => state.conversation)
const handleCreateConversation = () => {
dispatch(createConversation({}))
}
const handleClick = (type: string | undefined) => { const handleClick = (type: string | undefined) => {
if (!checkAuth()) if (!checkAuth())
return return
if (type === 'add') {
handleCreateConversation()
}
if (type === 'history') { if (type === 'history') {
onSetHistoryVisible(!isHistoryVisible) onSetHistoryVisible(!isHistoryVisible)
} }
...@@ -25,6 +42,13 @@ const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, c ...@@ -25,6 +42,13 @@ const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, c
onSetHistoryVisible(false) onSetHistoryVisible(false)
} }
useEffect(() => {
if (shouldNavigateToNewConversation && currentConversationId) {
navigate(`/chat/${currentConversationId}`)
dispatch(clearNavigationFlag())
}
}, [shouldNavigateToNewConversation, currentConversationId, navigate, dispatch])
return ( return (
<motion.nav className="h-full flex-shrink-0 flex flex-col items-center justify-center"> <motion.nav className="h-full flex-shrink-0 flex flex-col items-center justify-center">
<motion.div className={`${styles.layoutNavBarAgent} sm:flex hidden w-[64px] bg-white gap-[24px]`}> <motion.div className={`${styles.layoutNavBarAgent} sm:flex hidden w-[64px] bg-white gap-[24px]`}>
......
import React from 'react' import React from 'react'
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import { Button, Tooltip } from '@nextui-org/react' import { Button, Tooltip } from '@nextui-org/react'
import { useNavigate } from 'react-router-dom'
import { NavBarDivider } from '../NavBarDivider' import { NavBarDivider } from '../NavBarDivider'
import styles from './NavBarItem.module.less' import styles from './NavBarItem.module.less'
import Logo from '@/assets/svg/logo.svg?react' import Logo from '@/assets/svg/logo.svg?react'
...@@ -12,12 +13,22 @@ interface NavBarItemProps { ...@@ -12,12 +13,22 @@ interface NavBarItemProps {
type: string type: string
} }
export const NavBarItem: React.FC<NavBarItemProps> = ({ onClick, icon, label, type }) => { export const NavBarItem: React.FC<NavBarItemProps> = ({ onClick, icon, label, type }) => {
const navigate = useNavigate()
const handleClickLogo = () => {
navigate('/')
}
if (label === '' && icon === '') { if (label === '' && icon === '') {
return <NavBarDivider /> return <NavBarDivider />
} }
if (label === '' && icon !== '') { if (label === '' && icon !== '') {
return ( return (
<motion.div className="nav-logo"> <motion.div
whileHover={{ scale: 1.05 }}
transition={{ type: 'spring', stiffness: 400, damping: 10 }}
className="nav-logo cursor-pointer"
onClick={handleClickLogo}
>
<Logo /> <Logo />
</motion.div> </motion.div>
) )
......
import React from 'react' import React, { useEffect } from 'react'
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import { useAppDispatch, useAppSelector } from '@/store/hook'
import { setCurrentConversation } from '@/store/conversationSlice'
export const Chat: React.FC = () => { export const Chat: React.FC = () => {
const { chatId } = useParams<{ chatId: string }>() const { id } = useParams<{ id: string }>()
const dispatch = useAppDispatch()
const { currentConversationId } = useAppSelector(state => state.conversation)
useEffect(() => {
if (id && id !== currentConversationId) {
dispatch(setCurrentConversation(id))
// dispatch(fetchConversationDetails(id))
}
}, [id, currentConversationId, dispatch])
return ( return (
<h1> <h1>
聊天页面 - Chat ID: 聊天页面 - Chat ID:
{chatId} {id}
</h1> </h1>
) )
} }
...@@ -7,7 +7,7 @@ export const AppRoutes: React.FC = () => { ...@@ -7,7 +7,7 @@ export const AppRoutes: React.FC = () => {
return ( return (
<Routes> <Routes>
<Route path="/" element={<Home />} /> <Route path="/" element={<Home />} />
<Route path="/chat/:chatId" element={<Chat />} /> <Route path="/chat/:id" element={<Chat />} />
</Routes> </Routes>
) )
} }
...@@ -2,20 +2,21 @@ import type { PayloadAction } from '@reduxjs/toolkit' ...@@ -2,20 +2,21 @@ import type { PayloadAction } from '@reduxjs/toolkit'
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { processConversationData } from './conversationSlice.helper' import { processConversationData } from './conversationSlice.helper'
import type { Conversation, ConversationState } from '@/types/conversation' import type { Conversation, ConversationState } from '@/types/conversation'
import { queryUserConversationPage } from '@/api/conversation' import { fetchCreateConversation, fetchQueryUserConversationPage } from '@/api/conversation'
const initialState: ConversationState = { const initialState: ConversationState = {
conversations: [], conversations: [],
currentConversationId: null, currentConversationId: null,
isLoading: false, isLoading: false,
error: null, error: null,
shouldNavigateToNewConversation: false,
} }
export const fetchConversations = createAsyncThunk( export const fetchConversations = createAsyncThunk(
'conversation/fetchConversations', 'conversation/fetchConversations',
async (_, { rejectWithValue }) => { async (_, { rejectWithValue }) => {
try { try {
const response = await queryUserConversationPage({ const response = await fetchQueryUserConversationPage({
keyword: '', keyword: '',
pageNum: 0, pageNum: 0,
pageSize: 100, pageSize: 100,
...@@ -30,6 +31,22 @@ export const fetchConversations = createAsyncThunk( ...@@ -30,6 +31,22 @@ export const fetchConversations = createAsyncThunk(
}, },
) )
export const createConversation = createAsyncThunk<
{ conversation: Conversation, shouldNavigate: boolean },
Partial<Conversation>,
{ state: { conversation: ConversationState } }
>(
'conversation/createConversation',
async (conversationData, { dispatch }) => {
const response = await fetchCreateConversation(conversationData)
const newConversation = response.data
dispatch(fetchConversations())
return { conversation: newConversation, shouldNavigate: true }
},
)
const conversationSlice = createSlice({ const conversationSlice = createSlice({
name: 'conversation', name: 'conversation',
initialState, initialState,
...@@ -40,6 +57,9 @@ const conversationSlice = createSlice({ ...@@ -40,6 +57,9 @@ const conversationSlice = createSlice({
addConversation: (state, action: PayloadAction<Conversation>) => { addConversation: (state, action: PayloadAction<Conversation>) => {
state.conversations.unshift(action.payload) state.conversations.unshift(action.payload)
}, },
clearNavigationFlag: (state) => {
state.shouldNavigateToNewConversation = false
},
removeConversation: (state, action: PayloadAction<string>) => { removeConversation: (state, action: PayloadAction<string>) => {
state.conversations = state.conversations.filter(conv => conv.conversationId !== action.payload) state.conversations = state.conversations.filter(conv => conv.conversationId !== action.payload)
if (state.currentConversationId === action.payload) { if (state.currentConversationId === action.payload) {
...@@ -61,9 +81,22 @@ const conversationSlice = createSlice({ ...@@ -61,9 +81,22 @@ const conversationSlice = createSlice({
state.isLoading = false state.isLoading = false
state.error = action.payload as string state.error = action.payload as string
}) })
.addCase(createConversation.pending, (state) => {
state.isLoading = true
state.error = null
})
.addCase(createConversation.fulfilled, (state, action) => {
state.isLoading = false
state.currentConversationId = action.payload.conversation.conversationId
state.shouldNavigateToNewConversation = action.payload.shouldNavigate
})
.addCase(createConversation.rejected, (state, action) => {
state.isLoading = false
state.error = action.error.message || 'Failed to create conversation'
})
}, },
}) })
export const { setCurrentConversation } = conversationSlice.actions export const { setCurrentConversation, clearNavigationFlag } = conversationSlice.actions
export default conversationSlice.reducer export default conversationSlice.reducer
...@@ -11,5 +11,6 @@ export interface ConversationState { ...@@ -11,5 +11,6 @@ export interface ConversationState {
conversations: Conversation[] conversations: Conversation[]
currentConversationId: string | null currentConversationId: string | null
isLoading: boolean isLoading: boolean
shouldNavigateToNewConversation: boolean
error: string | null error: string | null
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment