mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-04 05:10:19 +08:00
Add chat box
This commit is contained in:
@ -37,8 +37,17 @@ func (c *ApiController) GetMessages() {
|
|||||||
value := c.Input().Get("value")
|
value := c.Input().Get("value")
|
||||||
sortField := c.Input().Get("sortField")
|
sortField := c.Input().Get("sortField")
|
||||||
sortOrder := c.Input().Get("sortOrder")
|
sortOrder := c.Input().Get("sortOrder")
|
||||||
|
chat := c.Input().Get("chat")
|
||||||
|
|
||||||
if limit == "" || page == "" {
|
if limit == "" || page == "" {
|
||||||
c.Data["json"] = object.GetMaskedMessages(object.GetMessages(owner))
|
var messages []*object.Message
|
||||||
|
if chat == "" {
|
||||||
|
messages = object.GetMessages(owner)
|
||||||
|
} else {
|
||||||
|
messages = object.GetChatMessages(chat)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = object.GetMaskedMessages(messages)
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
} else {
|
} else {
|
||||||
limit := util.ParseInt(limit)
|
limit := util.ParseInt(limit)
|
||||||
|
@ -27,7 +27,7 @@ type Message struct {
|
|||||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||||
|
|
||||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||||
Chat string `xorm:"varchar(100)" json:"chat"`
|
Chat string `xorm:"varchar(100) index" json:"chat"`
|
||||||
Author string `xorm:"varchar(100)" json:"author"`
|
Author string `xorm:"varchar(100)" json:"author"`
|
||||||
Text string `xorm:"mediumtext" json:"text"`
|
Text string `xorm:"mediumtext" json:"text"`
|
||||||
}
|
}
|
||||||
@ -67,6 +67,16 @@ func GetMessages(owner string) []*Message {
|
|||||||
return messages
|
return messages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetChatMessages(chat string) []*Message {
|
||||||
|
messages := []*Message{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Find(&messages, &Message{Chat: chat})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages
|
||||||
|
}
|
||||||
|
|
||||||
func GetPaginationMessages(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Message {
|
func GetPaginationMessages(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Message {
|
||||||
messages := []*Message{}
|
messages := []*Message{}
|
||||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||||
|
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 React from "react";
|
||||||
import {Menu} from "antd";
|
import {Menu} from "antd";
|
||||||
import {MailOutlined} from "@ant-design/icons";
|
import {LayoutOutlined} from "@ant-design/icons";
|
||||||
|
|
||||||
class ChatMenu extends React.Component {
|
class ChatMenu extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
openKeys: ["0"],
|
openKeys: ["0"],
|
||||||
|
selectedKeys: ["0-0"],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,19 +37,37 @@ class ChatMenu extends React.Component {
|
|||||||
return Object.keys(categories).map((category, index) => {
|
return Object.keys(categories).map((category, index) => {
|
||||||
return {
|
return {
|
||||||
key: `${index}`,
|
key: `${index}`,
|
||||||
icon: <MailOutlined />,
|
icon: <LayoutOutlined />,
|
||||||
label: category,
|
label: category,
|
||||||
children: categories[category].map((chat) => ({
|
children: categories[category].map((chat, chatIndex) => {
|
||||||
key: chat.id,
|
const globalChatIndex = chats.indexOf(chat);
|
||||||
|
return {
|
||||||
|
key: `${index}-${chatIndex}`,
|
||||||
|
dataIndex: globalChatIndex,
|
||||||
label: chat.displayName,
|
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) => {
|
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);
|
const latestOpenKey = keys.find((key) => this.state.openKeys.indexOf(key) === -1);
|
||||||
|
|
||||||
if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
|
if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
|
||||||
@ -65,10 +84,9 @@ class ChatMenu extends React.Component {
|
|||||||
<Menu
|
<Menu
|
||||||
mode="inline"
|
mode="inline"
|
||||||
openKeys={this.state.openKeys}
|
openKeys={this.state.openKeys}
|
||||||
|
selectedKeys={this.state.selectedKeys}
|
||||||
onOpenChange={this.onOpenChange}
|
onOpenChange={this.onOpenChange}
|
||||||
style={{
|
onSelect={this.onSelect}
|
||||||
width: 256,
|
|
||||||
}}
|
|
||||||
items={items}
|
items={items}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -12,13 +12,13 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import {Col, Row} from "antd";
|
|
||||||
// import React from "react";
|
|
||||||
import moment from "moment";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import moment from "moment";
|
||||||
import ChatMenu from "./ChatMenu";
|
import ChatMenu from "./ChatMenu";
|
||||||
|
import ChatBox from "./ChatBox";
|
||||||
import * as Setting from "./Setting";
|
import * as Setting from "./Setting";
|
||||||
import * as ChatBackend from "./backend/ChatBackend";
|
import * as ChatBackend from "./backend/ChatBackend";
|
||||||
|
import * as MessageBackend from "./backend/MessageBackend";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import BaseListPage from "./BaseListPage";
|
import BaseListPage from "./BaseListPage";
|
||||||
|
|
||||||
@ -77,14 +77,17 @@ class ChatListPage extends BaseListPage {
|
|||||||
|
|
||||||
renderTable(chats) {
|
renderTable(chats) {
|
||||||
return (
|
return (
|
||||||
<Row style={{marginTop: "10px"}} >
|
<div style={{display: "flex", height: "calc(100vh - 197px)"}}>
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<div style={{width: "250px", height: "100%", backgroundColor: "lightblue"}}>
|
||||||
<ChatMenu chats={this.state.data} />
|
<ChatMenu chats={this.state.data} onSelect={(i) => {
|
||||||
</Col>
|
const chat = chats[i];
|
||||||
<Col span={22} >
|
this.getMessages(chat.name);
|
||||||
222
|
}} />
|
||||||
</Col>
|
</div>
|
||||||
</Row>
|
<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;
|
export default ChatListPage;
|
||||||
|
@ -24,6 +24,16 @@ export function getMessages(owner, page = "", pageSize = "", field = "", value =
|
|||||||
}).then(res => res.json());
|
}).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) {
|
export function getMessage(owner, name) {
|
||||||
return fetch(`${Setting.ServerUrl}/api/get-message?id=${owner}/${encodeURIComponent(name)}`, {
|
return fetch(`${Setting.ServerUrl}/api/get-message?id=${owner}/${encodeURIComponent(name)}`, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
Reference in New Issue
Block a user