update
This commit is contained in:
+133
-55
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect, useRef } from 'react'
|
||||
import { Card, Button, Input, List, Badge, Modal, Form, Select, Tabs, Avatar, Empty, message, Popconfirm, Space, Tag, Tooltip } from 'antd'
|
||||
import { PlusOutlined, SendOutlined, TeamOutlined, UserOutlined, ArrowLeftOutlined, DeleteOutlined } from '@ant-design/icons'
|
||||
import { PlusOutlined, SendOutlined, TeamOutlined, UserOutlined, ArrowLeftOutlined, DeleteOutlined, SettingOutlined, UserDeleteOutlined } from '@ant-design/icons'
|
||||
import axios from 'axios'
|
||||
|
||||
const { TextArea } = Input
|
||||
@@ -25,6 +25,7 @@ function Messages({ user }) {
|
||||
const [inputValue, setInputValue] = useState('')
|
||||
const [sending, setSending] = useState(false)
|
||||
const [createVisible, setCreateVisible] = useState(false)
|
||||
const [membersVisible, setMembersVisible] = useState(false)
|
||||
const [createForm] = Form.useForm()
|
||||
const [creating, setCreating] = useState(false)
|
||||
const bottomRef = useRef(null)
|
||||
@@ -148,6 +149,31 @@ function Messages({ user }) {
|
||||
|
||||
const canDelete = (conv) => conv.type === 'direct' || conv.creator_id === user?.id
|
||||
|
||||
// 群主移除群成员
|
||||
const handleRemoveMember = async (memberId) => {
|
||||
try {
|
||||
const res = await axios.delete(`/api/chat/conversations/${activeConv.id}/members/${memberId}`)
|
||||
if (res.data.success) {
|
||||
message.success(res.data.message)
|
||||
setMembersVisible(false)
|
||||
// 刷新会话列表与当前会话
|
||||
const list = await axios.get('/api/chat/conversations')
|
||||
if (list.data.success) {
|
||||
setConversations(list.data.data)
|
||||
const conv = list.data.data.find(c => c.id === activeConv.id)
|
||||
if (conv) {
|
||||
setActiveConv(conv)
|
||||
activeConvRef.current = conv
|
||||
} else {
|
||||
closeConversation()
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
message.error(err.response?.data?.message || '操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
const otherOptions = allUsers.filter(u => u.id !== user?.id).map(u => ({
|
||||
value: u.id,
|
||||
label: (
|
||||
@@ -234,6 +260,11 @@ function Messages({ user }) {
|
||||
<Tag style={{ marginLeft: 8 }}>{activeConv.member_count}人</Tag>
|
||||
</Tooltip>
|
||||
)}
|
||||
{activeConv.type === 'group' && activeConv.creator_id === user?.id && (
|
||||
<Button size="small" type="text" icon={<SettingOutlined />} onClick={() => setMembersVisible(true)} style={{ marginLeft: 'auto' }}>
|
||||
成员管理
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="chat-messages">
|
||||
{messages.length === 0 && <Empty description="还没有消息,发送第一条吧" image={Empty.PRESENTED_IMAGE_SIMPLE} style={{ marginTop: 40 }} />}
|
||||
@@ -267,6 +298,103 @@ function Messages({ user }) {
|
||||
</div>
|
||||
)
|
||||
|
||||
// ============ 发起对话 / 建群弹窗 ============
|
||||
// 注意:这里必须用普通 JSX 变量而非内部函数组件,
|
||||
// 否则轮询重新渲染时组件类型变化会导致弹窗被卸载重建(闪退)
|
||||
const createModal = (
|
||||
<Modal
|
||||
title="发起对话"
|
||||
open={createVisible}
|
||||
onCancel={() => setCreateVisible(false)}
|
||||
footer={null}
|
||||
width={480}
|
||||
>
|
||||
<Form form={createForm} layout="vertical" onFinish={handleCreate} initialValues={{ tab: 'direct' }}>
|
||||
<Form.Item name="tab" noStyle>
|
||||
<Tabs
|
||||
centered
|
||||
items={[
|
||||
{
|
||||
key: 'direct',
|
||||
label: '单聊',
|
||||
children: (
|
||||
<Form.Item name="userId" label="选择对话对象" rules={[{ required: true, message: '请选择对话对象' }]}>
|
||||
<Select placeholder="选择用户(绿点为在线)" options={otherOptions} showSearch optionFilterProp="label" />
|
||||
</Form.Item>
|
||||
)
|
||||
},
|
||||
{
|
||||
key: 'group',
|
||||
label: '创建群聊',
|
||||
children: (
|
||||
<>
|
||||
<Form.Item name="name" label="群名称" rules={[{ required: true, message: '请输入群名称' }]}>
|
||||
<Input placeholder="例如:拍摄组工作群" />
|
||||
</Form.Item>
|
||||
<Form.Item name="memberIds" label="群成员(除自己外至少选1人)" rules={[{ required: true, message: '请选择群成员' }]}>
|
||||
<Select mode="multiple" placeholder="选择群成员" options={otherOptions} showSearch optionFilterProp="label" />
|
||||
</Form.Item>
|
||||
</>
|
||||
)
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
<Space>
|
||||
<Button onClick={() => setCreateVisible(false)}>取消</Button>
|
||||
<Button type="primary" htmlType="submit" loading={creating}>确定</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</Form>
|
||||
</Modal>
|
||||
)
|
||||
|
||||
// ============ 群成员管理弹窗(仅群主可见) ============
|
||||
const membersModal = (
|
||||
<Modal
|
||||
title={`群成员管理(${activeConv?.member_count || 0}人)`}
|
||||
open={membersVisible}
|
||||
onCancel={() => setMembersVisible(false)}
|
||||
footer={null}
|
||||
width={420}
|
||||
>
|
||||
<List
|
||||
dataSource={activeConv?.members || []}
|
||||
renderItem={(m) => (
|
||||
<List.Item
|
||||
actions={
|
||||
m.id !== activeConv?.creator_id
|
||||
? [
|
||||
<Popconfirm
|
||||
key="del"
|
||||
title={`确定将 ${m.display_name || m.username} 移出群聊吗?`}
|
||||
onConfirm={() => handleRemoveMember(m.id)}
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
>
|
||||
<Button type="text" size="small" danger icon={<UserDeleteOutlined />} />
|
||||
</Popconfirm>
|
||||
]
|
||||
: []
|
||||
}
|
||||
>
|
||||
<List.Item.Meta
|
||||
avatar={<Avatar icon={<UserOutlined />} />}
|
||||
title={
|
||||
<span>
|
||||
{m.display_name || m.username}
|
||||
{m.id === activeConv?.creator_id && <Tag color="gold" style={{ marginLeft: 8 }}>群主</Tag>}
|
||||
{m.id === user?.id && <Tag style={{ marginLeft: 8 }}>我</Tag>}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</Modal>
|
||||
)
|
||||
|
||||
// ============ 布局 ============
|
||||
if (isMobile) {
|
||||
return (
|
||||
@@ -275,7 +403,8 @@ function Messages({ user }) {
|
||||
{activeConv ? chatWindow : conversationList}
|
||||
</Card>
|
||||
|
||||
<CreateConvModal />
|
||||
{createModal}
|
||||
{membersModal}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -289,61 +418,10 @@ function Messages({ user }) {
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<CreateConvModal />
|
||||
{createModal}
|
||||
{membersModal}
|
||||
</div>
|
||||
)
|
||||
|
||||
// ============ 发起对话 / 建群弹窗 ============
|
||||
function CreateConvModal() {
|
||||
return (
|
||||
<Modal
|
||||
title="发起对话"
|
||||
open={createVisible}
|
||||
onCancel={() => setCreateVisible(false)}
|
||||
footer={null}
|
||||
width={480}
|
||||
>
|
||||
<Form form={createForm} layout="vertical" onFinish={handleCreate} initialValues={{ tab: 'direct' }}>
|
||||
<Form.Item name="tab" noStyle>
|
||||
<Tabs
|
||||
centered
|
||||
items={[
|
||||
{
|
||||
key: 'direct',
|
||||
label: '单聊',
|
||||
children: (
|
||||
<Form.Item name="userId" label="选择对话对象" rules={[{ required: true, message: '请选择对话对象' }]}>
|
||||
<Select placeholder="选择用户(绿点为在线)" options={otherOptions} showSearch optionFilterProp="label" />
|
||||
</Form.Item>
|
||||
)
|
||||
},
|
||||
{
|
||||
key: 'group',
|
||||
label: '创建群聊',
|
||||
children: (
|
||||
<>
|
||||
<Form.Item name="name" label="群名称" rules={[{ required: true, message: '请输入群名称' }]}>
|
||||
<Input placeholder="例如:拍摄组工作群" />
|
||||
</Form.Item>
|
||||
<Form.Item name="memberIds" label="群成员(除自己外至少选1人)" rules={[{ required: true, message: '请选择群成员' }]}>
|
||||
<Select mode="multiple" placeholder="选择群成员" options={otherOptions} showSearch optionFilterProp="label" />
|
||||
</Form.Item>
|
||||
</>
|
||||
)
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
<Space>
|
||||
<Button onClick={() => setCreateVisible(false)}>取消</Button>
|
||||
<Button type="primary" htmlType="submit" loading={creating}>确定</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</Form>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Messages
|
||||
|
||||
Reference in New Issue
Block a user