Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
S
sdream-ai-fe
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
侯明涛
sdream-ai-fe
Commits
94991ba6
Commit
94991ba6
authored
Aug 08, 2024
by
HoMeTown
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 历史记录列表
parent
d1d231b0
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
211 additions
and
40 deletions
+211
-40
package.json
+1
-0
src/layouts/HistoryBar/HistoryBar.tsx
+3
-36
src/layouts/HistoryBar/components/HistoryBarList/index.tsx
+46
-0
src/layouts/HistoryBar/motionAnimate.ts
+1
-1
src/layouts/MainLayout/MainLayout.tsx
+8
-1
src/store/conversationSlice.helper.ts
+64
-0
src/store/conversationSlice.ts
+69
-0
src/store/index.ts
+3
-1
src/types/conversation.ts
+15
-0
src/utils/request.ts
+1
-1
No files found.
package.json
View file @
94991ba6
...
...
@@ -45,6 +45,7 @@
"
ahooks
"
:
"^3.8.0"
,
"
axios
"
:
"^1.7.3"
,
"
clsx
"
:
"^2.1.1"
,
"
date-fns
"
:
"^3.6.0"
,
"
react
"
:
"^18.3.1"
,
"
react-dom
"
:
"^18.3.1"
,
"
react-hot-toast
"
:
"^2.4.1"
,
...
...
src/layouts/HistoryBar/HistoryBar.tsx
View file @
94991ba6
import
{
AnimatePresence
,
motion
}
from
'framer-motion'
import
{
Button
,
Input
}
from
'@nextui-org/react'
import
{
containerVariants
,
itemVariants
,
variants
}
from
'./motionAnimate'
import
{
variants
}
from
'./motionAnimate'
import
{
HistoryBarList
}
from
'./components/HistoryBarList'
import
SearchIcon
from
'@/assets/svg/search.svg?react'
import
HistoryMenuIcon
from
'@/assets/svg/historyMenu.svg?react'
...
...
@@ -24,41 +25,7 @@ export const HistoryBar: React.FC<HistoryBarProps> = ({ isVisible }) => {
<
Input
classNames=
{
{
inputWrapper
:
[
'bg-white'
,
'data-[hover=true]:bg-[#fff]'
,
'group-data-[focus=true]:bg-white'
,
'rounded-[24px]'
]
}
}
placeholder=
"搜索历史记录"
startContent=
{
<
SearchIcon
/>
}
/>
</
div
>
<
div
className=
"px-[32px] flex-1 overflow-y-auto"
>
<
motion
.
ul
variants=
{
containerVariants
}
initial=
"hidden"
animate=
"visible"
className=
"w-full flex flex-col gap-[6px]"
>
<
motion
.
li
key=
"1"
custom=
"1"
variants=
{
itemVariants
}
initial=
"hidden"
animate=
"visible"
exit=
"exit"
layout
className=
"mt-[32px] text-[13px] text-[#B1C6D2]"
>
今日
</
motion
.
li
>
<
motion
.
li
key=
"2"
custom=
"2"
variants=
{
itemVariants
}
initial=
"hidden"
animate=
"visible"
exit=
"exit"
layout
className=
"w-full"
>
<
Button
color=
"primary"
variant=
"light"
className=
"w-full text-[#333] rounded-[23px] data-[hover=true]:bg-[#E5F6FF] data-[hover=true]:text-primary"
>
<
div
className=
"w-full text-nowrap text-ellipsis overflow-hidden"
>
<
span
>
推荐几款60周岁还能投的医疗保推荐几款60周岁还能投的医疗保
</
span
>
</
div
>
</
Button
>
</
motion
.
li
>
</
motion
.
ul
>
<
HistoryBarList
/>
</
div
>
<
div
className=
"text-[12px] border-t-solid border-t-[1px] border-t-[#82969C12] w-full h-[48px] flex items-center justify-center"
>
<
Button
className=
"w-full"
color=
"primary"
variant=
"light"
startContent=
{
<
HistoryMenuIcon
/>
}
>
...
...
src/layouts/HistoryBar/components/HistoryBarList/index.tsx
0 → 100644
View file @
94991ba6
import
{
motion
}
from
'framer-motion'
import
{
Button
}
from
'@nextui-org/react'
import
{
containerVariants
,
itemVariants
}
from
'../../motionAnimate'
import
{
useAppSelector
}
from
'@/store/hook'
export
const
HistoryBarList
:
React
.
FC
=
()
=>
{
const
{
conversations
}
=
useAppSelector
(
state
=>
state
.
conversation
)
return
(
<
motion
.
ul
variants=
{
containerVariants
}
initial=
"hidden"
animate=
"visible"
className=
"w-full flex flex-col gap-[6px]"
>
{
conversations
.
map
((
item
,
index
)
=>
(
<
motion
.
li
key=
{
`${item.conversationId}-${index}`
}
custom=
{
index
}
variants=
{
itemVariants
}
initial=
"hidden"
animate=
"visible"
exit=
"exit"
layout
>
{
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"
>
<
div
className=
"w-full text-nowrap text-ellipsis overflow-hidden"
>
<
span
>
{
item
.
conversationTitle
}
</
span
>
</
div
>
</
Button
>
)
:
(
<
div
className=
"mt-[32px] text-[13px] text-[#B1C6D2]"
>
{
item
.
conversationTitle
}
</
div
>
)
}
</
motion
.
li
>
))
}
</
motion
.
ul
>
)
}
src/layouts/HistoryBar/motionAnimate.ts
View file @
94991ba6
...
...
@@ -54,7 +54,7 @@ export const itemVariants = {
y
:
0
,
scale
:
1
,
transition
:
{
delay
:
i
*
0.0
8
,
delay
:
i
*
0.0
5
,
type
:
'spring'
,
stiffness
:
200
,
damping
:
20
,
...
...
src/layouts/MainLayout/MainLayout.tsx
View file @
94991ba6
import
type
React
from
'react'
import
{
motion
}
from
'framer-motion'
import
{
useState
}
from
'react'
import
{
use
Effect
,
use
State
}
from
'react'
import
{
Navbar
}
from
'../Navbar'
import
{
HistoryBar
}
from
'../HistoryBar/HistoryBar'
import
styles
from
'./MainLayout.module.less'
import
{
useAuth
}
from
'@/auth/AuthContext'
import
{
LoginModal
}
from
'@/components/LoginModal'
import
{
useAppDispatch
}
from
'@/store/hook'
import
{
fetchConversations
}
from
'@/store/conversationSlice'
interface
MainLayoutProps
{
children
:
React
.
ReactNode
...
...
@@ -25,6 +27,11 @@ const contentVariants = {
export
const
MainLayout
:
React
.
FC
<
MainLayoutProps
>
=
({
children
})
=>
{
const
{
showLoginModal
,
toggleLoginModal
}
=
useAuth
()
const
[
isHistoryVisible
,
setHistoryVisible
]
=
useState
(
false
)
const
dispatch
=
useAppDispatch
()
useEffect
(()
=>
{
dispatch
(
fetchConversations
())
},
[
dispatch
])
return
(
<
motion
.
main
className=
{
styles
.
layoutMain
}
>
...
...
src/store/conversationSlice.helper.ts
0 → 100644
View file @
94991ba6
import
{
isThisWeek
,
isToday
,
isWithinInterval
,
subDays
}
from
'date-fns'
import
type
{
Conversation
}
from
'@/types/conversation'
export
function
processConversationData
(
records
:
Conversation
[]):
Conversation
[]
{
const
today
=
new
Date
()
const
thirtyDaysAgo
=
subDays
(
today
,
30
)
const
processedData
:
Conversation
[]
=
[]
let
hasToday
=
false
let
hasThisWeek
=
false
let
hasLast30Days
=
false
records
.
forEach
((
item
)
=>
{
const
endDate
=
new
Date
(
item
.
endTime
)
let
timeLabel
=
''
if
(
isToday
(
endDate
))
{
if
(
!
hasToday
)
{
processedData
.
push
({
conversationId
:
''
,
conversationTitle
:
'今天'
,
currentConversationFlag
:
false
,
endTime
:
''
,
}
as
any
)
hasToday
=
true
}
timeLabel
=
'今天'
}
else
if
(
isThisWeek
(
endDate
,
{
weekStartsOn
:
1
}))
{
if
(
!
hasThisWeek
)
{
processedData
.
push
({
conversationId
:
''
,
conversationTitle
:
'最近一周'
,
currentConversationFlag
:
false
,
endTime
:
''
,
}
as
Conversation
)
hasThisWeek
=
true
}
timeLabel
=
'最近一周'
}
else
if
(
isWithinInterval
(
endDate
,
{
start
:
thirtyDaysAgo
,
end
:
today
}))
{
if
(
!
hasLast30Days
)
{
processedData
.
push
({
conversationId
:
''
,
conversationTitle
:
'最近30天'
,
currentConversationFlag
:
false
,
endTime
:
''
,
}
as
Conversation
)
hasLast30Days
=
true
}
// eslint-disable-next-line unused-imports/no-unused-vars
timeLabel
=
'最近30天'
}
else
{
return
}
processedData
.
push
({
...
item
,
})
})
return
processedData
}
src/store/conversationSlice.ts
0 → 100644
View file @
94991ba6
import
type
{
PayloadAction
}
from
'@reduxjs/toolkit'
import
{
createAsyncThunk
,
createSlice
}
from
'@reduxjs/toolkit'
import
{
processConversationData
}
from
'./conversationSlice.helper'
import
type
{
Conversation
,
ConversationState
}
from
'@/types/conversation'
import
{
queryUserConversationPage
}
from
'@/api/conversation'
const
initialState
:
ConversationState
=
{
conversations
:
[],
currentConversationId
:
null
,
isLoading
:
false
,
error
:
null
,
}
export
const
fetchConversations
=
createAsyncThunk
(
'conversation/fetchConversations'
,
async
(
_
,
{
rejectWithValue
})
=>
{
try
{
const
response
=
await
queryUserConversationPage
({
keyword
:
''
,
pageNum
:
0
,
pageSize
:
100
,
})
const
processedData
=
processConversationData
(
response
.
data
.
records
)
return
processedData
}
// eslint-disable-next-line unused-imports/no-unused-vars
catch
(
error
)
{
return
rejectWithValue
(
'Failed to fetch conversations'
)
}
},
)
const
conversationSlice
=
createSlice
({
name
:
'conversation'
,
initialState
,
reducers
:
{
setCurrentConversation
:
(
state
,
action
:
PayloadAction
<
string
>
)
=>
{
state
.
currentConversationId
=
action
.
payload
},
addConversation
:
(
state
,
action
:
PayloadAction
<
Conversation
>
)
=>
{
state
.
conversations
.
unshift
(
action
.
payload
)
},
removeConversation
:
(
state
,
action
:
PayloadAction
<
string
>
)
=>
{
state
.
conversations
=
state
.
conversations
.
filter
(
conv
=>
conv
.
conversationId
!==
action
.
payload
)
if
(
state
.
currentConversationId
===
action
.
payload
)
{
state
.
currentConversationId
=
state
.
conversations
[
0
]?.
conversationId
||
null
}
},
},
extraReducers
:
(
builder
)
=>
{
builder
.
addCase
(
fetchConversations
.
pending
,
(
state
)
=>
{
state
.
isLoading
=
true
state
.
error
=
null
})
.
addCase
(
fetchConversations
.
fulfilled
,
(
state
,
action
)
=>
{
state
.
isLoading
=
false
state
.
conversations
=
action
.
payload
})
.
addCase
(
fetchConversations
.
rejected
,
(
state
,
action
)
=>
{
state
.
isLoading
=
false
state
.
error
=
action
.
payload
as
string
})
},
})
export
const
{
setCurrentConversation
}
=
conversationSlice
.
actions
export
default
conversationSlice
.
reducer
src/store/index.ts
View file @
94991ba6
import
{
configureStore
}
from
'@reduxjs/toolkit'
import
conversationReducer
from
'./conversationSlice'
export
const
store
=
configureStore
({
reducer
:
{
// 我们暂时还没有任何reducer,所以这里是空的
conversation
:
conversationReducer
,
},
})
// 为了在TypeScript中使用,我们导出这些类型
...
...
src/types/conversation.ts
0 → 100644
View file @
94991ba6
export
interface
Conversation
{
conversationId
:
string
conversationTitle
:
string
currentConversationFlag
:
boolean
endTime
:
string
startTime
:
string
qaNum
:
number
}
export
interface
ConversationState
{
conversations
:
Conversation
[]
currentConversationId
:
string
|
null
isLoading
:
boolean
error
:
string
|
null
}
src/utils/request.ts
View file @
94991ba6
...
...
@@ -29,7 +29,7 @@ const service = axios.create({
service
.
interceptors
.
request
.
use
(
(
config
:
any
)
=>
{
config
.
headers
=
{
'X-Token'
:
window
.
localStorage
.
getItem
(
'__TOKEN__'
)
||
''
,
'X-Token'
:
JSON
.
parse
(
window
.
localStorage
.
getItem
(
'__TOKEN__'
)
||
''
)
,
'X-Request-Id'
:
`
${
Date
.
now
()}${
Math
.
random
().
toString
(
36
).
substring
(
2
)}
`
,
'X-Request-By'
:
config
.
url
,
// 'X-App-Type': getAppType() || '',
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment