mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-04 21:30:24 +08:00
Add chat box
This commit is contained in:
44
web/src/ChatBox.js
Normal file
44
web/src/ChatBox.js
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import {Avatar, List} from "antd";
|
||||
import {CopyOutlined, DislikeOutlined, LikeOutlined} from "@ant-design/icons";
|
||||
|
||||
class ChatBox extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<List
|
||||
itemLayout="horizontal"
|
||||
dataSource={this.props.messages}
|
||||
renderItem={(item, index) => (
|
||||
<List.Item style={{backgroundColor: index % 2 === 0 ? "white" : "rgb(247,247,248)", borderBottom: "1px solid rgb(229, 229, 229)", position: "relative"}}>
|
||||
<List.Item.Meta
|
||||
avatar={<Avatar style={{width: "30px", height: "30px", borderRadius: "3px"}} src={item.author === `${this.props.account.owner}/${this.props.account.name}` ? this.props.account.avatar : "https://cdn.casbin.com/casdoor/resource/built-in/admin/gpt.png"} />}
|
||||
title={<div style={{fontSize: "16px", fontWeight: "normal", lineHeight: "24px", marginTop: "-15px", marginLeft: "5px", marginRight: "70px"}}>{item.text}</div>}
|
||||
/>
|
||||
<div style={{position: "absolute", top: "10px", right: "10px"}}
|
||||
>
|
||||
<CopyOutlined style={{color: "rgb(172,172,190)", margin: "5px"}} />
|
||||
<LikeOutlined style={{color: "rgb(172,172,190)", margin: "5px"}} />
|
||||
<DislikeOutlined style={{color: "rgb(172,172,190)", margin: "5px"}} />
|
||||
</div>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ChatBox;
|
@ -14,13 +14,14 @@
|
||||
|
||||
import React from "react";
|
||||
import {Menu} from "antd";
|
||||
import {MailOutlined} from "@ant-design/icons";
|
||||
import {LayoutOutlined} from "@ant-design/icons";
|
||||
|
||||
class ChatMenu extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
openKeys: ["0"],
|
||||
selectedKeys: ["0-0"],
|
||||
};
|
||||
}
|
||||
|
||||
@ -36,19 +37,37 @@ class ChatMenu extends React.Component {
|
||||
return Object.keys(categories).map((category, index) => {
|
||||
return {
|
||||
key: `${index}`,
|
||||
icon: <MailOutlined />,
|
||||
icon: <LayoutOutlined />,
|
||||
label: category,
|
||||
children: categories[category].map((chat) => ({
|
||||
key: chat.id,
|
||||
label: chat.displayName,
|
||||
})),
|
||||
children: categories[category].map((chat, chatIndex) => {
|
||||
const globalChatIndex = chats.indexOf(chat);
|
||||
return {
|
||||
key: `${index}-${chatIndex}`,
|
||||
dataIndex: globalChatIndex,
|
||||
label: chat.displayName,
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// 处理菜单展开事件
|
||||
onSelect = (info) => {
|
||||
const [categoryIndex, chatIndex] = info.selectedKeys[0].split("-").map(Number);
|
||||
const selectedItem = this.chatsToItems(this.props.chats)[categoryIndex].children[chatIndex];
|
||||
this.setState({selectedKeys: [`${categoryIndex}-${chatIndex}`]});
|
||||
|
||||
if (this.props.onSelect) {
|
||||
this.props.onSelect(selectedItem.dataIndex);
|
||||
}
|
||||
};
|
||||
|
||||
getRootSubmenuKeys(items) {
|
||||
return items.map((item, index) => `${index}`);
|
||||
}
|
||||
|
||||
onOpenChange = (keys) => {
|
||||
const rootSubmenuKeys = this.props.chats.map((_, index) => `${index}`);
|
||||
const items = this.chatsToItems(this.props.chats);
|
||||
const rootSubmenuKeys = this.getRootSubmenuKeys(items);
|
||||
const latestOpenKey = keys.find((key) => this.state.openKeys.indexOf(key) === -1);
|
||||
|
||||
if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
|
||||
@ -65,10 +84,9 @@ class ChatMenu extends React.Component {
|
||||
<Menu
|
||||
mode="inline"
|
||||
openKeys={this.state.openKeys}
|
||||
selectedKeys={this.state.selectedKeys}
|
||||
onOpenChange={this.onOpenChange}
|
||||
style={{
|
||||
width: 256,
|
||||
}}
|
||||
onSelect={this.onSelect}
|
||||
items={items}
|
||||
/>
|
||||
);
|
||||
|
@ -12,13 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import {Col, Row} from "antd";
|
||||
// import React from "react";
|
||||
import moment from "moment";
|
||||
import React from "react";
|
||||
import moment from "moment";
|
||||
import ChatMenu from "./ChatMenu";
|
||||
import ChatBox from "./ChatBox";
|
||||
import * as Setting from "./Setting";
|
||||
import * as ChatBackend from "./backend/ChatBackend";
|
||||
import * as MessageBackend from "./backend/MessageBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
|
||||
@ -77,14 +77,17 @@ class ChatListPage extends BaseListPage {
|
||||
|
||||
renderTable(chats) {
|
||||
return (
|
||||
<Row style={{marginTop: "10px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<ChatMenu chats={this.state.data} />
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
222
|
||||
</Col>
|
||||
</Row>
|
||||
<div style={{display: "flex", height: "calc(100vh - 197px)"}}>
|
||||
<div style={{width: "250px", height: "100%", backgroundColor: "lightblue"}}>
|
||||
<ChatMenu chats={this.state.data} onSelect={(i) => {
|
||||
const chat = chats[i];
|
||||
this.getMessages(chat.name);
|
||||
}} />
|
||||
</div>
|
||||
<div style={{flex: 1, height: "100%", backgroundColor: "lightgreen"}}>
|
||||
<ChatBox messages={this.state.messages} account={this.props.account} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -122,6 +125,15 @@ class ChatListPage extends BaseListPage {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
getMessages(chatName) {
|
||||
MessageBackend.getChatMessages(chatName)
|
||||
.then((messages) => {
|
||||
this.setState({
|
||||
messages: messages,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default ChatListPage;
|
||||
|
@ -24,6 +24,16 @@ export function getMessages(owner, page = "", pageSize = "", field = "", value =
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getChatMessages(chat) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-messages?chat=${chat}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getMessage(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-message?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "GET",
|
||||
|
Reference in New Issue
Block a user