Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
何 广一
chatroom
Commits
29493cc4
Commit
29493cc4
authored
Aug 21, 2025
by
何 广一
Browse files
the real last time of frontend qwq
parent
353e4327
Changes
9
Show whitespace changes
Inline
Side-by-side
chat-room/src/app/chat/confirm-modal.tsx
0 → 100644
View file @
29493cc4
'
use client
'
import
{
useState
,
useEffect
}
from
'
react
'
import
*
as
Ty
from
'
./interface
'
import
{
useRoom
}
from
'
./room-context
'
export
default
function
ConfirmModalProvider
()
{
const
{
showCreateModal
,
setShowCreateModal
,
newRoomName
,
setNewRoomName
,
showErrorModal
,
setShowErrorModal
,
errorModalMsg
,
triggerCreateRoom
}
=
useRoom
();
return
(
<>
{
showCreateModal
&&
<
div
className
=
"fixed inset-0 bg-black/40 flex items-center justify-center z-50"
onClick
=
{
()
=>
setShowCreateModal
(
false
)
}
>
<
div
className
=
"bg-white p-8 rounded shadow-lg min-w-[300px]"
onClick
=
{
(
e
)
=>
e
.
stopPropagation
()
}
>
<
h2
className
=
"text-lg mb-2"
>
新建房间
</
h2
>
<
input
className
=
"border px-3 py-2 mb-3 w-full"
placeholder
=
"房间名称"
value
=
{
newRoomName
}
onChange
=
{
(
e
)
=>
setNewRoomName
(
e
.
target
.
value
)
}
/>
<
div
className
=
"flex justify-end space-x-2"
>
<
button
className
=
"bg-gray-300 px-4 py-2 rounded"
onClick
=
{
()
=>
setShowCreateModal
(
false
)
}
>
取消
</
button
>
<
button
className
=
"bg-blue-500 text-white px-4 py-2 rounded"
onClick
=
{
triggerCreateRoom
}
>
确定
</
button
>
</
div
>
</
div
>
</
div
>
}
{
showErrorModal
&&
<
div
className
=
"fixed inset-0 bg-black/40 flex items-center justify-center z-50 "
onClick
=
{
()
=>
setShowErrorModal
(
false
)
}
>
<
div
className
=
"bg-white p-8 rounded shadow-lg min-w-[300px]"
onClick
=
{
(
e
)
=>
e
.
stopPropagation
()
}
>
<
h2
className
=
"text-lg mb-2"
>
错误
</
h2
>
<
p
className
=
'p-1'
>
{
errorModalMsg
}
</
p
>
<
div
className
=
"flex justify-end space-x-2"
>
<
button
className
=
"bg-gray-300 px-4 py-2 mt-2 rounded hover:bg-gray-400 active:bg-gray-500"
onClick
=
{
()
=>
setShowErrorModal
(
false
)
}
>
关闭
</
button
>
</
div
>
</
div
>
</
div
>
}
</>
);
}
\ No newline at end of file
chat-room/src/app/chat/current-room.tsx
View file @
29493cc4
...
...
@@ -8,8 +8,10 @@ export default function RoomHeaderProvider() {
const
{
currentRoomInfo
}
=
useRoom
()
return
(
<
div
className
=
"bg-white p-4 mb-4"
>
<
h2
className
=
{
`text-lg font-bold
${
currentRoomInfo
?
''
:
'
text-gray-500
'
}
`
}
>
{
currentRoomInfo
?.
roomName
??
'
房间
'
}
</
h2
>
</
div
>
);
};
\ No newline at end of file
chat-room/src/app/chat/header.tsx
0 → 100644
View file @
29493cc4
'
use client
'
import
{
useRoom
}
from
'
./room-context
'
import
{
useRouter
}
from
'
next/navigation
'
;
export
default
function
PageHeaderProvider
()
{
const
{
username
}
=
useRoom
();
const
router
=
useRouter
();
const
logout
=
()
=>
{
/* 这段后面换成登录 */
router
.
push
(
'
/
'
);
};
return
(
<
header
className
=
"h-14 bg-white shadow flex items-center px-4 shrink-0"
>
<
h1
className
=
"text-lg font-bold text-blue-600"
>
XLAB聊天室
</
h1
>
<
div
className
=
"ml-auto flex items-center space-x-4"
>
<
span
className
=
"text-sm text-gray-600"
>
欢迎你,
{
username
}
</
span
>
<
button
onClick
=
{
logout
}
className
=
"text-sm text-gray-600 hover:underline"
>
退出
</
button
>
</
div
>
</
header
>
);
}
\ No newline at end of file
chat-room/src/app/chat/page.tsx
View file @
29493cc4
...
...
@@ -4,34 +4,30 @@ import RoomHeaderProvider from './current-room';
import
RoomMessageProvider
from
'
./room-message
'
;
import
SendMessageProvider
from
'
./send-message
'
;
import
UsernameCheck
from
'
./username-check
'
;
import
PageHeaderProvider
from
'
./header
'
import
ConfirmModalProvider
from
'
./confirm-modal
'
;
import
{
RoomProvider
}
from
'
./room-context
'
export
default
function
Home
()
{
return
(
<
RoomProvider
>
<
UsernameCheck
/>
<
div
className
=
"bg-gray-100"
>
<
div
className
=
"flex h-screen"
>
<
ConfirmModalProvider
/>
<
div
className
=
"bg-gray-100 flex-col flex h-screen"
>
<
PageHeaderProvider
/>
<
div
className
=
"flex flex-1 overflow-hidden"
>
<
div
className
=
"w-1/4 bg-white p-4 overflow-y-auto"
>
<
h2
className
=
"text-lg font-bold mb-4"
>
房间列表
</
h2
>
<
RoomListProvider
/>
</
div
>
<
div
className
=
"w-3/4 bg-gray-100 p-4"
>
<
div
className
=
"bg-white p-4 mb-4"
>
<
RoomHeaderProvider
/>
</
div
>
<
RoomMessageProvider
/>
<
div
className
=
"bg-white p-4"
>
<
SendMessageProvider
/>
</
div
>
</
div
>
</
div
>
</
div
>
</
RoomProvider
>
);
}
chat-room/src/app/chat/room-context.tsx
View file @
29493cc4
...
...
@@ -10,19 +10,41 @@ interface RoomCtxValue {
setUsername
:
(
u
:
string
)
=>
void
;
msgVersion
:
number
;
triggerRefresh
:
()
=>
void
;
showCreateModal
:
boolean
;
setShowCreateModal
:
(
show
:
boolean
)
=>
void
;
showErrorModal
:
boolean
;
setShowErrorModal
:
(
error
:
boolean
)
=>
void
;
errorModalMsg
:
string
;
triggerErrorModal
:
(
msg
:
string
)
=>
void
;
newRoomName
:
string
;
setNewRoomName
:
(
name
:
string
)
=>
void
;
createRoomVersion
:
number
;
triggerCreateRoom
:
()
=>
void
;
}
const
RoomContext
=
createContext
<
RoomCtxValue
>
({}
as
RoomCtxValue
)
export
function
RoomProvider
({
children
}:
{
children
:
ReactNode
})
{
/* 这段后面换成登录 */
const
searchParams
=
useSearchParams
();
const
userName
=
searchParams
.
get
(
'
username
'
)?.
trim
()
||
''
;
const
[
currentRoomInfo
,
setCurrentRoomInfo
]
=
useState
<
Ty
.
RoomPreviewInfo
|
null
>
(
null
)
const
[
username
,
setUsername
]
=
useState
<
string
>
(
userName
);
const
[
msgVersion
,
setMsgVersion
]
=
useState
(
0
);
const
[
showCreateModal
,
setShowCreateModal
]
=
useState
(
false
);
const
[
newRoomName
,
setNewRoomName
]
=
useState
(
''
);
const
[
createRoomVersion
,
setCreateRoomVersion
]
=
useState
(
0
);
const
[
showErrorModal
,
setShowErrorModal
]
=
useState
(
false
);
const
[
errorModalMsg
,
setErrorModalMsg
]
=
useState
(
''
);
const
triggerRefresh
=
useCallback
(()
=>
setMsgVersion
(
v
=>
v
+
1
),
[]);
const
triggerCreateRoom
=
useCallback
(()
=>
setCreateRoomVersion
(
v
=>
v
+
1
),
[]);
const
triggerErrorModal
=
useCallback
((
msg
:
string
)
=>
{
setErrorModalMsg
(
msg
);
setShowErrorModal
(
true
);
},
[]);
return
(
<
RoomContext
.
Provider
value
=
{
{
...
...
@@ -31,7 +53,17 @@ export function RoomProvider({ children }: { children: ReactNode }) {
username
,
setUsername
,
msgVersion
,
triggerRefresh
triggerRefresh
,
showCreateModal
,
setShowCreateModal
,
showErrorModal
,
setShowErrorModal
,
errorModalMsg
,
triggerErrorModal
,
newRoomName
,
setNewRoomName
,
createRoomVersion
,
triggerCreateRoom
}
}
>
{
children
}
</
RoomContext
.
Provider
>
...
...
chat-room/src/app/chat/room-list.tsx
View file @
29493cc4
...
...
@@ -8,8 +8,149 @@ export default function RoomListProvider() {
const
[
roomsLoading
,
setRoomsLoading
]
=
useState
<
boolean
>
(
false
)
const
[
roomsError
,
setRoomsError
]
=
useState
<
string
>
(
''
)
const
[
rooms
,
setRooms
]
=
useState
<
Ty
.
RoomPreviewInfo
[]
>
([])
const
{
setCurrentRoomInfo
,
msgVersion
}
=
useRoom
()
const
{
username
,
currentRoomInfo
,
setCurrentRoomInfo
,
msgVersion
,
triggerRefresh
,
setShowCreateModal
,
triggerErrorModal
,
createRoomVersion
,
newRoomName
,
setNewRoomName
}
=
useRoom
()
/*
- url: /api/room/add
- method: POST
- argument:
interface RoomAddArgs {
user: string;
roomName: string;
}
- response:
interface RoomAddResult {
roomId: number;
}
*/
const
createRoom
=
async
(
roomName
:
string
)
:
Promise
<
number
>
=>
{
const
args
:
Ty
.
RoomAddArgs
=
{
user
:
username
,
roomName
};
setRoomsError
(
''
);
try
{
const
res
=
await
fetch
(
Ty
.
urlPrefix
+
'
/api/room/add
'
,
{
method
:
'
POST
'
,
headers
:
{
'
Content-Type
'
:
'
application/json
'
},
body
:
JSON
.
stringify
(
args
)
});
if
(
!
res
.
ok
)
{
throw
new
Error
(
'
创建房间失败,错误码:
'
+
res
.
status
);
}
const
data
:
Ty
.
BackendResponse
<
Ty
.
RoomAddResult
>
=
await
res
.
json
();
if
(
data
.
code
!==
0
)
{
throw
new
Error
(
'
创建房间失败,错误信息:
'
+
data
.
message
);
}
return
data
.
data
.
roomId
;
}
catch
(
error
)
{
console
.
error
(
'
创建房间失败:
'
,
error
);
setRoomsError
(
error
.
message
);
}
return
-
1
;
}
const
handleCreate
=
async
()
=>
{
if
(
!
newRoomName
.
trim
())
return
;
const
roomId
=
await
createRoom
(
newRoomName
.
trim
());
if
(
roomId
===
-
1
)
return
;
setNewRoomName
(
''
);
setShowCreateModal
(
false
);
triggerRefresh
();
setCurrentRoomInfo
({
roomId
,
roomName
:
newRoomName
.
trim
(),
lastMessage
:
null
});
};
useEffect
(()
=>
{
handleCreate
();
},
[
createRoomVersion
]);
/*
- url: /api/room/delete
- method: POST
- argument:
interface RoomDeleteArgs {
user: string;
roomId: number;
}
- response: null (只要code为0即为成功)
*/
const
deleteRoom
=
async
(
roomId
:
number
)
=>
{
const
args
:
Ty
.
RoomDeleteArgs
=
{
user
:
username
,
roomId
};
setRoomsError
(
''
);
try
{
const
res
=
await
fetch
(
Ty
.
urlPrefix
+
'
/api/room/delete
'
,
{
method
:
'
POST
'
,
headers
:
{
'
Content-Type
'
:
'
application/json
'
},
body
:
JSON
.
stringify
(
args
)
});
if
(
!
res
.
ok
)
{
throw
new
Error
(
'
删除房间失败,错误码:
'
+
res
.
status
);
}
const
data
:
Ty
.
BackendResponse
<
null
>
=
await
res
.
json
();
/*
Error : You are not the creator. deletion failed.
{
"code": 2,
"message": "auth error: invalid identity",
"data": null
}
*/
if
(
data
.
code
===
2
)
{
triggerErrorModal
(
'
你不是房间创建者,无法删除房间。
'
);
return
false
;
}
else
if
(
data
.
code
!==
0
)
{
throw
new
Error
(
'
删除房间失败,错误信息:
'
+
data
.
message
);
}
return
true
;
}
catch
(
error
)
{
console
.
error
(
'
删除房间失败:
'
,
error
);
setRoomsError
(
error
.
message
);
}
return
false
;
}
const
handleDeleteRoom
=
async
(
roomId
:
number
)
=>
{
const
success
=
await
deleteRoom
(
roomId
);
if
(
success
)
{
if
(
currentRoomInfo
?.
roomId
===
roomId
)
{
setCurrentRoomInfo
(
null
);
}
triggerRefresh
();
}
}
/*
- url: /api/room/list
...
...
@@ -64,14 +205,37 @@ export default function RoomListProvider() {
}
},
[
msgVersion
]);
useEffect
(()
=>
{
if
(
roomsError
)
{
triggerErrorModal
(
roomsError
);
setRoomsError
(
''
);
}
},
[
roomsError
]);
return
(
<
div
className
=
"room-container"
>
{
roomsError
&&
<
div
className
=
"text-red-500"
>
{
roomsError
}
</
div
>
}
{
roomsLoading
&&
!
rooms
.
length
&&
<
div
className
=
"text-gray-500"
>
加载中...
</
div
>
}
<
ul
className
=
"mb-2"
>
{
rooms
&&
rooms
.
map
((
room
)
=>
(
<
li
key
=
{
room
.
roomId
}
>
<
li
key
=
{
room
.
roomId
}
className
=
"relative"
>
<
div
className
=
"bg-gray-200 p-4 mb-2 border-b border-gray-300 rounded w-full"
onClick
=
{
()
=>
handleRoomClick
(
room
)
}
>
<
button
className
=
"
absolute
top-1
right-1
w-6
h-6
bg-red-500
hover
:
bg-red-600
active
:
bg-red-700
text-white
text-sm
font-bold
rounded
flex
items-center
justify-center
z-10
"
onClick
=
{
(
e
)
=>
{
e
.
stopPropagation
();
// 阻止冒泡,避免触发整行点击
handleDeleteRoom
(
room
.
roomId
);
}
}
>
—
</
button
>
<
h3
>
{
room
.
roomName
}
(ID:
{
room
.
roomId
}
)
</
h3
>
{
room
.
lastMessage
?
(
<
p
>
{
room
.
lastMessage
.
sender
}
:
{
room
.
lastMessage
.
content
}
</
p
>
...
...
@@ -82,7 +246,16 @@ export default function RoomListProvider() {
</
li
>
))
}
<
li
>
<
button
className
=
"w-full bg-blue-500 text-white rounded p-4 text-2xl hover:bg-blue-600 active:bg-blue-700"
onClick
=
{
()
=>
setShowCreateModal
(
true
)
}
>
+
</
button
>
</
li
>
</
ul
>
</
div
>
);
}
\ No newline at end of file
chat-room/src/app/chat/room-message.tsx
View file @
29493cc4
...
...
@@ -5,7 +5,11 @@ import { useRoom } from './room-context'
export
default
function
RoomMessageProvider
()
{
const
{
currentRoomInfo
,
msgVersion
}
=
useRoom
()
const
{
currentRoomInfo
,
triggerErrorModal
,
msgVersion
}
=
useRoom
()
const
[
messagesLoading
,
setMessagesLoading
]
=
useState
<
boolean
>
(
false
)
const
[
messagesError
,
setMessagesError
]
=
useState
<
string
>
(
''
)
...
...
@@ -43,13 +47,13 @@ export default function RoomMessageProvider() {
});
if
(
!
res
.
ok
)
{
throw
new
Error
(
'
无法获取
房间
列表,错误码:
'
+
res
.
status
);
throw
new
Error
(
'
无法获取
消息
列表,错误码:
'
+
res
.
status
);
}
const
data
:
Ty
.
BackendResponse
<
Ty
.
RoomMessageListRes
>
=
await
res
.
json
();
if
(
data
.
code
!==
0
)
{
throw
new
Error
(
'
无法获取
房间
列表,错误信息:
'
+
data
.
message
);
throw
new
Error
(
'
无法获取
消息
列表,错误信息:
'
+
data
.
message
);
}
setMessages
(
data
.
data
.
messages
);
...
...
@@ -94,13 +98,13 @@ export default function RoomMessageProvider() {
});
if
(
!
res
.
ok
)
{
throw
new
Error
(
'
无法
获取房间
列表,错误码:
'
+
res
.
status
);
throw
new
Error
(
'
无法
更新消息
列表,错误码:
'
+
res
.
status
);
}
const
data
:
Ty
.
BackendResponse
<
Ty
.
RoomMessageGetUpdateRes
>
=
await
res
.
json
();
if
(
data
.
code
!==
0
)
{
throw
new
Error
(
'
无法
获取房间
列表,错误信息:
'
+
data
.
message
);
throw
new
Error
(
'
无法
更新消息
列表,错误信息:
'
+
data
.
message
);
}
setMessages
((
prevMessages
)
=>
[...
prevMessages
,
...
data
.
data
.
messages
]);
...
...
@@ -142,6 +146,13 @@ export default function RoomMessageProvider() {
}
},
[
messages
]);
useEffect
(()
=>
{
if
(
messagesError
)
{
triggerErrorModal
(
messagesError
);
setMessagesError
(
''
);
}
},
[
messagesError
]);
if
(
currentRoomInfo
)
{
return
(
...
...
@@ -150,7 +161,6 @@ export default function RoomMessageProvider() {
className
=
"bg-white p-4 overflow-y-auto h-3/5"
>
<
div
className
=
"bg-gray-100 min-h-full"
>
{
messagesError
&&
<
div
className
=
"text-red-500"
>
{
messagesError
}
</
div
>
}
<
ul
className
=
"mb-2"
>
{
messages
?.
map
((
msg
)
=>
(
<
li
key
=
{
msg
.
messageId
}
className
=
"mb-3"
>
...
...
chat-room/src/app/chat/send-message.tsx
View file @
29493cc4
'
use client
'
import
{
useRoom
}
from
'
./room-context
'
import
{
useState
}
from
'
react
'
import
{
useEffect
,
useState
}
from
'
react
'
import
*
as
Ty
from
'
./interface
'
export
default
function
SendMessageProvider
()
{
const
{
username
,
currentRoomInfo
,
triggerRefresh
}
=
useRoom
()
const
{
username
,
currentRoomInfo
,
triggerErrorModal
,
triggerRefresh
}
=
useRoom
()
const
[
text
,
setText
]
=
useState
(
''
);
const
[
sendError
,
setSendError
]
=
useState
<
string
|
null
>
(
null
);
...
...
@@ -54,9 +59,15 @@ export default function SendMessageProvider() {
}
}
useEffect
(()
=>
{
if
(
sendError
)
{
triggerErrorModal
(
sendError
);
setSendError
(
null
);
}
},
[
sendError
]);
return
(
<>
{
sendError
&&
<
div
className
=
"text-red-500"
>
{
sendError
}
</
div
>
}
<
div
className
=
"bg-white p-4"
>
<
textarea
className
=
"w-full resize-none"
rows
=
{
4
}
...
...
@@ -75,6 +86,6 @@ export default function SendMessageProvider() {
>
发送
</
button
>
</>
</
div
>
)
}
\ No newline at end of file
chat-room/src/app/page.tsx
View file @
29493cc4
...
...
@@ -8,6 +8,7 @@ export default function Home() {
const
handleSubmit
=
()
=>
{
if
(
!
username
.
trim
())
return
;
// 简单校验
/* 这段后面换成登录 */
router
.
push
(
`/chat?username=
${
encodeURIComponent
(
username
)}
`
);
};
...
...
@@ -19,6 +20,7 @@ export default function Home() {
placeholder
=
"用户名"
value
=
{
username
}
onChange
=
{
(
e
)
=>
setUsername
(
e
.
target
.
value
)
}
required
/>
<
button
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new 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