├── .DS_Store ├── .gitattributes ├── 01.项目源码 ├── .DS_Store ├── client │ ├── .gitignore │ ├── AddFriendWindow.qml │ ├── ChatListItem.qml │ ├── ChatScreen.qml │ ├── ContactScreen.qml │ ├── CreateGroup.qml │ ├── FileWidget.qml │ ├── GroupInf.qml │ ├── HistoryMessageListItem.qml │ ├── HistoryMessageScreen.qml │ ├── LoginWindow.qml │ ├── MainWindow.qml │ ├── README.md │ ├── RegisterWindow.qml │ ├── client.pro │ ├── components │ │ ├── AddFriendWidget.qml │ │ ├── AutoScrollTextEdit.qml │ │ ├── EmojiChoose.qml │ │ ├── FriendList.qml │ │ ├── GroupAddWidget.qml │ │ ├── InfListItem.qml │ │ ├── RoundImage.qml │ │ ├── SearchWidget.qml │ │ ├── SelectAvatar.qml │ │ └── VerticalTabWidget.qml │ ├── images │ │ ├── add.png │ │ ├── add1.png │ │ ├── addFriend.png │ │ ├── addFriend1.png │ │ ├── addGroup.png │ │ ├── emoji.png │ │ ├── emoji │ │ │ ├── Face_(0).png │ │ │ ├── Face_(1).png │ │ │ ├── Face_(10).png │ │ │ ├── Face_(11).png │ │ │ ├── Face_(12).png │ │ │ ├── Face_(13).png │ │ │ ├── Face_(14).png │ │ │ ├── Face_(15).png │ │ │ ├── Face_(16).png │ │ │ ├── Face_(17).png │ │ │ ├── Face_(18).png │ │ │ ├── Face_(19).png │ │ │ ├── Face_(2).png │ │ │ ├── Face_(20).png │ │ │ ├── Face_(21).png │ │ │ ├── Face_(22).png │ │ │ ├── Face_(23).png │ │ │ ├── Face_(24).png │ │ │ ├── Face_(25).png │ │ │ ├── Face_(26).png │ │ │ ├── Face_(27).png │ │ │ ├── Face_(28).png │ │ │ ├── Face_(29).png │ │ │ ├── Face_(3).png │ │ │ ├── Face_(30).png │ │ │ ├── Face_(31).png │ │ │ ├── Face_(32).png │ │ │ ├── Face_(33).png │ │ │ ├── Face_(34).png │ │ │ ├── Face_(35).png │ │ │ ├── Face_(36).png │ │ │ ├── Face_(37).png │ │ │ ├── Face_(38).png │ │ │ ├── Face_(39).png │ │ │ ├── Face_(4).png │ │ │ ├── Face_(40).png │ │ │ ├── Face_(41).png │ │ │ ├── Face_(42).png │ │ │ ├── Face_(43).png │ │ │ ├── Face_(44).png │ │ │ ├── Face_(45).png │ │ │ ├── Face_(46).png │ │ │ ├── Face_(47).png │ │ │ ├── Face_(48).png │ │ │ ├── Face_(49).png │ │ │ ├── Face_(5).png │ │ │ ├── Face_(50).png │ │ │ ├── Face_(51).png │ │ │ ├── Face_(52).png │ │ │ ├── Face_(53).png │ │ │ ├── Face_(54).png │ │ │ ├── Face_(55).png │ │ │ ├── Face_(56).png │ │ │ ├── Face_(6).png │ │ │ ├── Face_(7).png │ │ │ ├── Face_(8).png │ │ │ └── Face_(9).png │ │ ├── icon_add_contact.png │ │ ├── icon_chat.png │ │ ├── icon_contact.png │ │ ├── icon_file.png │ │ ├── icon_folder.png │ │ ├── message.png │ │ ├── message1.png │ │ ├── picture.png │ │ ├── rightArrow.png │ │ ├── rightArrow1.png │ │ └── search.png │ ├── js │ │ ├── config_utils.js │ │ ├── file_utils.js │ │ └── time_utils.js │ ├── main.cpp │ ├── personalInf.qml │ ├── qfileutils.h │ ├── qml.qrc │ ├── qqclient.cpp │ ├── qqclient.h │ ├── qsettingini.h │ ├── qtquickcontrols2.conf │ ├── tcpclient.cpp │ ├── tcpclient.h │ ├── tcpserver.cpp │ └── tcpserver.h └── server │ ├── .DS_Store │ ├── QQServer.pro │ ├── main.cpp │ ├── onlinelist.cpp │ ├── onlinelist.h │ ├── qqserver.cpp │ ├── qqserver.h │ ├── qqserver.ui │ ├── sqlaccountmodel.cpp │ ├── sqlaccountmodel.h │ ├── sqlfriendmodel.cpp │ ├── sqlfriendmodel.h │ ├── sqlgroupmodel.cpp │ ├── sqlgroupmodel.h │ ├── tcpclient.cpp │ ├── tcpclient.h │ ├── tcpserver.cpp │ └── tcpserver.h ├── 02.项目运行录屏 ├── .DS_Store └── 请到百度云下载.md ├── 03.项目文件 ├── .DS_Store ├── 个人日报(每人1份,每天持续更新) │ ├── .DS_Store │ ├── 个人日报 _刘恩泽.xlsx │ ├── 个人日报 _刘翎.xlsx │ ├── 个人日报 _沈宇航.xlsx │ ├── 个人日报 _罗家安.xlsx │ └── 个人日报_陈耀宇.xlsx ├── 人员分工表--WeTalk-1组.xls ├── 结合测试-WeTalk-1组.xlsx └── 需求跟踪矩阵-WeTalk-1组.xls ├── 04.实训总结报告-每人1份 ├── .DS_Store ├── 学生实训总结报告-刘恩泽.docx ├── 学生实训总结报告-刘翎.docx ├── 学生实训总结报告-沈宇航.docx ├── 学生实训总结报告-罗家安.pdf └── 学生实训总结报告-陈耀宇.docx ├── 05.环评报告-每人1份 ├── .DS_Store ├── 行业解决方案对环境和可持续发展影响分析报告-刘恩泽.docx ├── 行业解决方案对环境和可持续发展影响分析报告-刘翎.docx ├── 行业解决方案对环境和可持续发展影响分析报告-姓名.docx ├── 行业解决方案对环境和可持续发展影响分析报告-沈宇航.docx └── 行业解决方案对环境和可持续发展影响分析报告-罗家安.pdf ├── README.md ├── 团队贡献度.xlsx ├── 大众评审表-项目答辩.xlsx └── 答辩PPT-WeTalk聊天软件1组.pptx /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/.DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /01.项目源码/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/.DS_Store -------------------------------------------------------------------------------- /01.项目源码/client/.gitignore: -------------------------------------------------------------------------------- 1 | # This file is used to ignore files which are generated 2 | # ---------------------------------------------------------------------------- 3 | 4 | *~ 5 | *.autosave 6 | *.a 7 | *.core 8 | *.moc 9 | *.o 10 | *.obj 11 | *.orig 12 | *.rej 13 | *.so 14 | *.so.* 15 | *_pch.h.cpp 16 | *_resource.rc 17 | *.qm 18 | .#* 19 | *.*# 20 | core 21 | !core/ 22 | tags 23 | .DS_Store 24 | .directory 25 | *.debug 26 | Makefile* 27 | *.prl 28 | *.app 29 | moc_*.cpp 30 | ui_*.h 31 | qrc_*.cpp 32 | Thumbs.db 33 | *.res 34 | *.rc 35 | /.qmake.cache 36 | /.qmake.stash 37 | 38 | # qtcreator generated files 39 | *.pro.user* 40 | 41 | # xemacs temporary files 42 | *.flc 43 | 44 | # Vim temporary files 45 | .*.swp 46 | 47 | # Visual Studio generated files 48 | *.ib_pdb_index 49 | *.idb 50 | *.ilk 51 | *.pdb 52 | *.sln 53 | *.suo 54 | *.vcproj 55 | *vcproj.*.*.user 56 | *.ncb 57 | *.sdf 58 | *.opensdf 59 | *.vcxproj 60 | *vcxproj.* 61 | 62 | # MinGW generated files 63 | *.Debug 64 | *.Release 65 | 66 | # Python byte code 67 | *.pyc 68 | 69 | # Binaries 70 | # -------- 71 | *.dll 72 | *.exe 73 | 74 | -------------------------------------------------------------------------------- /01.项目源码/client/AddFriendWindow.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Window 2.2 3 | import QtQuick.Controls 2.5 4 | import QtQuick.Controls.Material 2.0 5 | import QtQuick.Layouts 1.3 6 | import QtQuick.Dialogs 1.3 7 | import QtQml 2.0 8 | import "./components" 9 | 10 | Window { 11 | id: addFriendWindow 12 | width: 300 13 | height: 120 14 | visible: false 15 | title: qsTr("加好友/群") 16 | objectName: "addFriendWindow" 17 | 18 | flags:(Qt.Window | Qt.CustomizeWindowHint) 19 | 20 | 21 | property string avatarImg: "https://c-ssl.dtstatic.com/uploads/blog/202203/25/20220325232426_17909.thumb.1000_0.jpeg" 22 | property string userName: "balabala" 23 | property int userid: 12345 24 | property bool isPerson: true //用户:true 群聊:false 25 | property bool finded: false // 是否查找到相应的用户/群聊 26 | 27 | //发送搜索内容(id) 28 | signal searchSignal(int idN) 29 | //发送添加信号 30 | signal addSignal(int idN) 31 | //已添加新好友(群聊)信号 32 | signal passSignal(int idN,string usrAvatar,string usrName,bool isPerson) 33 | 34 | //返回搜索结果 35 | function searchBack(data) 36 | { 37 | finded = data.finded 38 | if(false == finded) 39 | { 40 | findFalse.open() 41 | return; 42 | } 43 | userName = data.pName 44 | avatarImg = data.headImg 45 | isPerson = data.isPerson 46 | addFriendWindow.height = 180 47 | searchResult.visible = true 48 | } 49 | //返回添加成功信号 50 | function addBack(v) 51 | { 52 | if(0 == v) 53 | {//添加失败 54 | addFalse.open() 55 | } 56 | else 57 | { 58 | //这里应该把好友/群聊添加到通讯录 59 | passSignal(userid,avatarImg,userName,isPerson) 60 | addOk.open() 61 | } 62 | } 63 | 64 | MessageDialog 65 | { 66 | id:findFalse 67 | standardButtons: MessageDialog.Cancel 68 | text: "查无该用户/群聊" 69 | } 70 | MessageDialog 71 | { 72 | id:addFalse 73 | standardButtons: MessageDialog.Cancel 74 | text: "添加失败" 75 | } 76 | MessageDialog 77 | { 78 | id:addOk 79 | standardButtons: MessageDialog.Ok 80 | text: "添加成功" 81 | } 82 | 83 | Rectangle{ 84 | id:closeButton 85 | width:10 86 | height:10 87 | radius: closeButton.width/2 88 | anchors.top: parent.top 89 | anchors.right: parent.right 90 | 91 | Rectangle{ 92 | width:15 93 | height:15 94 | radius: 15/2 95 | anchors{ 96 | right:closeButton.right 97 | top:closeButton.top 98 | } 99 | Text { 100 | id: name 101 | anchors.centerIn: parent 102 | text: qsTr("X") 103 | color:"#FFFFFF" 104 | font.pixelSize: 16 105 | } 106 | color:"green" 107 | MouseArea { 108 | anchors.fill: parent 109 | onClicked: { 110 | addFriendWindow.visible = false 111 | } 112 | hoverEnabled: true 113 | onHoveredChanged: cursorShape = Qt.PointingHandCursor 114 | } 115 | } 116 | } 117 | 118 | // 搜索栏 119 | 120 | SearchWidget 121 | { 122 | id: searchWidget 123 | Layout.fillWidth: true 124 | Layout.alignment: Qt.AlignHCenter 125 | anchors.horizontalCenter: parent.horizontalCenter 126 | anchors.top: parent.top 127 | anchors.topMargin: 40 128 | 129 | } 130 | Rectangle 131 | { 132 | id:searchResult 133 | visible: false 134 | width: parent.width 135 | height: parent.height 136 | anchors.top: searchWidget.bottom 137 | anchors.topMargin: 20 138 | anchors.left: parent.left 139 | anchors.leftMargin: 18 140 | anchors.right: parent.right 141 | anchors.rightMargin: 8 142 | 143 | Text { 144 | id: pInf 145 | text: qsTr("联系人") 146 | visible: isPerson 147 | } 148 | 149 | Text { 150 | id: gInf 151 | anchors.top: pInfList.bottom 152 | anchors.topMargin: 0 153 | text: qsTr("群组") 154 | visible: !isPerson 155 | } 156 | Rectangle 157 | { 158 | id: pInfList 159 | anchors.top: pInf.bottom 160 | width: parent.width 161 | height: parent.height*0.4 162 | 163 | //好友搜索结果表 164 | InfListItem 165 | { 166 | id:resultList 167 | width: parent.width 168 | } 169 | } 170 | } 171 | 172 | } 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /01.项目源码/client/ChatListItem.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import "./components" 3 | 4 | Item { 5 | id: multiDelegate 6 | 7 | anchors.left: parent.left 8 | anchors.right: parent.right 9 | height: childrenRect.height 10 | readonly property bool sentByMe: uid == myUid 11 | objectName: "chatListItem" 12 | 13 | function bestDelegate(t) { 14 | if(t === 1) 15 | return fileDelegate; 16 | return txtDelegate; // t == "text" 17 | } 18 | 19 | 20 | 21 | //接收返回的个人信息信号 22 | function personalInfBack(data) 23 | { 24 | objectInf.personalSaying = data.personalSaying 25 | objectInf.sex_num = data.sex 26 | objectInf.birthday = data.birthday 27 | objectInf.areaFrom = data.areaFrom 28 | objectInf.edu = data.edu 29 | 30 | objectInf.visible = true 31 | } 32 | function openInfoWindow(data){ 33 | //获得用户信息 34 | getPInfSignal(uid,false) 35 | console.log("从聊天界面发出个人信息请求") 36 | objectInf.isMe = sentByMe 37 | console.log(sentByMe) 38 | objectInf.personalHead = avatar 39 | objectInf.personalName = name 40 | objectInf.personalID = uid 41 | 42 | // objectInf.visible = true // 与服务器通讯后注释 43 | } 44 | 45 | Component { 46 | id: fileDelegate 47 | Row { 48 | 49 | property var extraData: JSON.parse(message) 50 | 51 | layoutDirection: sentByMe ? Qt.RightToLeft : Qt.LeftToRight 52 | 53 | spacing: 6 54 | 55 | RoundImage { 56 | id: avatarImg 57 | width: height 58 | height: 24 59 | img_src: avatar 60 | MouseArea 61 | { 62 | anchors.fill: parent 63 | acceptedButtons: Qt.LeftButton 64 | onClicked: openInfoWindow() 65 | onHoveredChanged: cursorShape = Qt.PointingHandCursor 66 | } 67 | } 68 | 69 | FileWidget { 70 | id: fileWidget 71 | _fileName: extraData["fileName"] 72 | _fileSize: extraData["fileSize"] 73 | } 74 | } 75 | 76 | 77 | } 78 | 79 | Component { 80 | id: txtDelegate 81 | Row { 82 | // readonly property bool sentByMe: uid == myUid 83 | layoutDirection: sentByMe ? Qt.RightToLeft : Qt.LeftToRight 84 | 85 | spacing: 6 86 | 87 | RoundImage { 88 | id: avatarImg 89 | width: height 90 | height: 24 91 | img_src: avatar 92 | MouseArea 93 | { 94 | anchors.fill: parent 95 | acceptedButtons: Qt.LeftButton 96 | onClicked: openInfoWindow() 97 | onHoveredChanged: cursorShape = Qt.PointingHandCursor 98 | } 99 | } 100 | 101 | 102 | Rectangle { 103 | id: textContainer 104 | width: Math.min(messageText.implicitWidth + 16, 105 | chatListView.width * 0.6) 106 | height: messageText.implicitHeight + 12 107 | 108 | color: sentByMe ? "lightgrey" : "steelblue" 109 | radius: 4 110 | Text { 111 | id: messageText 112 | anchors.centerIn: parent 113 | anchors.fill: parent 114 | anchors.margins: 8 115 | text: message 116 | color: sentByMe ? "black" : "white" 117 | wrapMode: Text.WordWrap 118 | lineHeight: 1.5 119 | } 120 | } 121 | } 122 | 123 | } 124 | 125 | Loader { 126 | id: itemDisplay 127 | anchors.left: parent.left 128 | anchors.right: parent.right 129 | sourceComponent: bestDelegate(type) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /01.项目源码/client/ChatScreen.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.5 3 | import QtQuick.Controls.Material 2.3 4 | import QtQuick.Layouts 1.3 5 | import QtGraphicalEffects 1.0 6 | import Qt.labs.platform 1.0 as QtPlatform 7 | import "./components" 8 | 9 | ColumnLayout { 10 | 11 | spacing: 12 12 | anchors.margins: 24 13 | property int myUid: ClientInfo.id 14 | property string myName: ClientInfo.name 15 | property string myAvatar: ClientInfo.avatar 16 | property int targetId: 1 17 | 18 | property string targetName: "ll" 19 | property string targetAvatar: "https://tse2-mm.cn.bing.net/th/id/OIP-C.cS6phGwfJ3qgAtvSXv0rugAAAA?pid=ImgDet&rs=1" 20 | objectName: "chatScreen" //这句话用于C++抓取 21 | 22 | PersonalInf { 23 | id:personalInfPage 24 | } 25 | 26 | 27 | // 信号,当前端发送了一条信息时调用 28 | // targetId: 发给谁 29 | // message: 消息 30 | // time: 消息发送的 unix 时间戳(类似 1660893694) 31 | 32 | signal sendFileSignal(int targetId, string fileUrl, int time) 33 | signal sendMessageSignal(int targetId, string message, int time) 34 | 35 | // 拉取聊天记录(页面加载完后) 36 | signal requestMessageSignal(int sendId, int targetId) 37 | 38 | //从聊天记录获取个人信息 39 | signal getPInfSignal(int uid,bool isGroup) 40 | 41 | 42 | 43 | // 函数: 当c++层接收到新的消息时调用,往UI里添加一条消息(暂时不需要,或许直接appenddata就行) 44 | function sendChatMessageBack(sendId,content,time) 45 | { 46 | console.log(content) 47 | } 48 | 49 | // data 样例见下面的 chatListModel 50 | function appendData(data){ 51 | if(data["groupId"] >= 600000){ // 群发 52 | if(data["groupId"] === targetId) { 53 | chatListModel.append(data) 54 | } 55 | updateHistorySignal(targetId, data["message"], data["time"]); 56 | }else{ 57 | if(data["uid"] === targetId || data["uid"] === myUid) { 58 | chatListModel.append(data) 59 | } 60 | updateHistorySignal(targetId, data["message"], data["time"]); 61 | } 62 | } 63 | 64 | // 函数:设置消息列表。会替换当前所有内容为新内容 65 | function setMessages(messages){ 66 | chatListModel.clear() 67 | for(let each of messages){ 68 | chatListModel.append(each); 69 | } 70 | } 71 | 72 | // 下面的方法是qml用的 73 | signal updateHistorySignal(int targetId, string message, int time) 74 | 75 | // 设置聊天对象 76 | function setArg(uid){ 77 | console.log("targetId:" + uid); 78 | targetId = uid; 79 | } 80 | 81 | function appendInputText(text){ 82 | messageField.append(text) 83 | } 84 | 85 | function chatWithUid(uid){ 86 | for(i in chatListModel){ 87 | if(chatListModel.get(i).uid === uid){ 88 | chatListView.currentIndex = i 89 | } 90 | } 91 | } 92 | 93 | QtPlatform.FileDialog { 94 | id: openFileDialog 95 | title: "打开文件" 96 | nameFilters: ["All files(*.*)"] 97 | onAccepted: { 98 | try{ 99 | var url = openFileDialog.file.toString() 100 | var time = Date.parse(new Date()) / 1000 101 | var fileName = url.substring(url.lastIndexOf("/")) 102 | var data = { 103 | "uid": myUid, 104 | "name": myName, 105 | "time": time, 106 | 107 | "fileName": fileName, 108 | "fileSize": QFileUtils.getFileSize(url), 109 | "localPath": url, 110 | "message": "[文件]", 111 | 112 | "avatar": myAvatar, 113 | "type": 1 // 1代表文件 114 | } 115 | console.log(fileName + " " + data["fileSize"]) 116 | appendData(data) 117 | sendFileSignal(targetId, url, time); 118 | console.log("发送文件:" + url) 119 | }catch(e){ 120 | console.trace("打开文件出错:"+ e); 121 | }finally{ 122 | 123 | } 124 | } 125 | onRejected: console.log("取消打开文件") 126 | } 127 | 128 | ListModel { 129 | id: chatListModel 130 | } 131 | 132 | 133 | Rectangle { 134 | radius: 4 135 | Layout.fillWidth: true 136 | Layout.fillHeight: true 137 | 138 | ListView { 139 | id: chatListView 140 | objectName: "chatListView" 141 | anchors.fill: parent 142 | anchors.margins: 16 143 | clip: true 144 | spacing: 8 145 | model: chatListModel 146 | delegate: ChatListItem{} // getWidget(type) 147 | } 148 | } 149 | 150 | 151 | Rectangle { 152 | id: rect 153 | radius: 4 154 | Layout.fillWidth: true 155 | Layout.preferredHeight: childrenRect.height 156 | Layout.alignment: Qt.AlignBottom 157 | anchors.margins: 16 158 | 159 | Item { 160 | id: imgContainer 161 | width : 100 162 | height: 20 163 | anchors.left: parent.left 164 | anchors.top: parent.top 165 | anchors.leftMargin: 12 166 | anchors.topMargin: 8 167 | RowLayout 168 | { 169 | Rectangle 170 | { 171 | id: emojiImgArea 172 | width : 20 173 | height: 20 174 | Image { 175 | id: emojiImg 176 | source: "./images/emoji.png" 177 | anchors.fill: parent 178 | MouseArea { 179 | anchors.fill: emojiImg 180 | onClicked: { 181 | console.log("clickedEmoji") 182 | emojiChoose.show() 183 | } 184 | hoverEnabled: true 185 | onHoveredChanged: cursorShape = Qt.PointingHandCursor 186 | } 187 | } 188 | ColorOverlay { 189 | anchors.fill: emojiImg 190 | source: emojiImg 191 | color: Material.primary 192 | } 193 | } 194 | Rectangle 195 | { 196 | width : 20 197 | height: 20 198 | Layout.leftMargin: 5 199 | Image { 200 | id: folderImg 201 | source: "./images/icon_folder.png" 202 | anchors.fill: parent 203 | MouseArea { 204 | anchors.fill: parent 205 | onClicked: { 206 | openFileDialog.open(); 207 | } 208 | hoverEnabled: true 209 | onHoveredChanged: cursorShape = Qt.PointingHandCursor 210 | } 211 | } 212 | ColorOverlay { 213 | anchors.fill: folderImg 214 | source: folderImg 215 | color: Material.primary 216 | } 217 | } 218 | Rectangle 219 | { 220 | width : 22 221 | height: 22 222 | Layout.leftMargin: 5 223 | Image { 224 | id: pictureImg 225 | source: "./images/picture.png" 226 | anchors.fill: parent 227 | MouseArea { 228 | anchors.fill: parent 229 | onClicked: { 230 | openFileDialog.open() 231 | } 232 | hoverEnabled: true 233 | onHoveredChanged: cursorShape = Qt.PointingHandCursor 234 | } 235 | } 236 | ColorOverlay { 237 | anchors.fill: pictureImg 238 | source: pictureImg 239 | color: Material.primary 240 | } 241 | } 242 | 243 | } 244 | 245 | } 246 | 247 | RowLayout { 248 | 249 | width: parent.width 250 | height: 64 251 | anchors.top: imgContainer.bottom 252 | anchors.left: parent.left 253 | anchors.leftMargin: 12 254 | 255 | ScrollView { 256 | Layout.fillWidth: true 257 | TextArea { 258 | id: messageField 259 | width: maximumWidth 260 | placeholderText: qsTr("请输入……") 261 | wrapMode: TextArea.Wrap 262 | focus: true 263 | selectByMouse: true 264 | background: null 265 | 266 | Keys.onReturnPressed: { 267 | sendButton.onClicked() 268 | } 269 | } 270 | } 271 | 272 | 273 | 274 | Button { 275 | id: sendButton 276 | Layout.alignment: Qt.AlignRight 277 | Layout.rightMargin: 16 278 | text: `发送` 279 | enabled: messageField.length > 0 280 | Material.background: "#90CAF9" 281 | // Material.color: sendButton.enabled ? "white" : "lightgrey" 282 | flat: true 283 | onClicked: { 284 | var message = messageField.text 285 | if(message === "") return 286 | var time = Date.parse(new Date())/ 1000 287 | chatListModel.append({ 288 | "uid": myUid, 289 | "name": myName, 290 | "time": Date.parse(new Date())/ 1000, 291 | "message": message, 292 | "avatar": myAvatar 293 | }) 294 | messageField.clear() 295 | chatListView.currentIndex = chatListModel.count - 1; 296 | sendMessageSignal(targetId, message, time) 297 | updateHistorySignal(targetId, message, time) 298 | } 299 | } 300 | } 301 | } 302 | 303 | function isValidId(id){return 100000<=id&&id<=999999} 304 | 305 | //初始化测试 306 | Component.onCompleted: { 307 | console.log(`ClientInfo: id:${ClientInfo.id}`) 308 | chatListModel.append({ 309 | "uid": 100002, 310 | "name": "李四", 311 | "time": 1660893694, 312 | "message": `{"fileName": "来来来.txt","fileSize": 1389420,"localPath": "d:/test/来来来.txt"}`, 313 | "avatar": "https://ts1.cn.mm.bing.net/th/id/R-C.1eed2de61a172c6ca2d79fc5ea62eb01?rik=c7W7KrSN7xFOIg&riu=http%3a%2f%2fimg.crcz.com%2fallimg%2f202003%2f10%2f1583821081100057.jpg&ehk=q%2f9lt0hQhwZzKFdRKYyG2g4zxQKgTWKJ4gHeelom3Mo%3d&risl=&pid=ImgRaw&r=0&sres=1&sresct=1", 314 | "type": 1 // 1代表文件 315 | }) 316 | chatListModel.append({ 317 | "uid": 100002, 318 | "name": "李四", 319 | "time": 1660893694, 320 | 321 | "message": "这是一条很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长的消息", 322 | 323 | "avatar": "https://ts1.cn.mm.bing.net/th/id/R-C.1eed2de61a172c6ca2d79fc5ea62eb01?rik=c7W7KrSN7xFOIg&riu=http%3a%2f%2fimg.crcz.com%2fallimg%2f202003%2f10%2f1583821081100057.jpg&ehk=q%2f9lt0hQhwZzKFdRKYyG2g4zxQKgTWKJ4gHeelom3Mo%3d&risl=&pid=ImgRaw&r=0&sres=1&sresct=1", 324 | "type": 0 325 | }) 326 | chatListModel.append({ 327 | "uid": 100001, 328 | "name": "cyy", 329 | "time": 1660893694, 330 | 331 | "message": "你好,我是丁真", 332 | "avatar": "https://ts1.cn.mm.bing.net/th/id/R-C.1eed2de61a172c6ca2d79fc5ea62eb01?rik=c7W7KrSN7xFOIg&riu=http%3a%2f%2fimg.crcz.com%2fallimg%2f202003%2f10%2f1583821081100057.jpg&ehk=q%2f9lt0hQhwZzKFdRKYyG2g4zxQKgTWKJ4gHeelom3Mo%3d&risl=&pid=ImgRaw&r=0&sres=1&sresct=1", 333 | "type": 0 334 | }) 335 | 336 | // 组件加载完后自动拉消息 337 | if(isValidId(myUid) && isValidId(targetId)){ 338 | requestMessageSignal(myUid, targetId); 339 | console.log(`开始拉取 myUid(${myUid}) 和 targetId(${targetId}) 间的聊天记录……`); 340 | }else{ 341 | console.log(`myUid(${myUid})或者targetId(${targetId})不正确,不拉消息`); 342 | } 343 | } 344 | 345 | 346 | 347 | } 348 | -------------------------------------------------------------------------------- /01.项目源码/client/ContactScreen.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.5 3 | import QtQuick.Controls.Material 2.3 4 | import QtQuick.Layouts 1.3 5 | import QtQuick.Dialogs 1.3 6 | 7 | import "./components" 8 | 9 | Rectangle { 10 | id: rectangle 11 | radius: 4 12 | objectName: "contactScreen" 13 | 14 | // 信号,qml用,点击和某人/群聊天触发,参数为对应项的data 15 | signal messageWithConact(int id, string name, string avatar) 16 | signal messageWithGroup(int id, string name, string avatar) 17 | 18 | // 信号,C++用,打开通讯录页面时获取联系人列表和群组列表 19 | signal requestContactSignal() 20 | signal requestGroupSignal() 21 | 22 | //创建群聊信号 23 | signal createGroupSignal(var mId) 24 | 25 | // 获取到联系人后调这个函数,messages为QJsonArray。每一项的数据data参考如下 26 | /** 27 | userId: 1 28 | userName: "张三" 29 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 30 | */ 31 | function setContacts(contacts){ 32 | contactListModel.clear() 33 | for(let each of contacts){ 34 | contactListModel.append(each) 35 | } 36 | } 37 | 38 | // 添加一条好友信息 39 | function appendContact(data){ 40 | contactListModel.append(data) 41 | } 42 | 43 | // 设置群列表 44 | function setGroups(groups){ 45 | groupListModel.clear() 46 | for(let each of groups){ 47 | groupListModel.append(each) 48 | } 49 | } 50 | 51 | // 添加一条群组信息 52 | function appendGroup(data){ 53 | groupListModel.append(data) 54 | } 55 | 56 | 57 | // 下面是qml用的 58 | function sendRequestDataSignal(){ 59 | console.log("获取通讯录信息……") 60 | requestContactSignal() 61 | requestGroupSignal() 62 | } 63 | 64 | AddFriendWindow{ 65 | id : addFriendWindow; visible: false; 66 | onPassSignal: (uid, avatar, name, isPerson) => { 67 | if(isPerson) appendContact({"userId":uid,"userName":name,"avatar":avatar}) 68 | else appendGroup({"userId":uid,"userName":name,"avatar":avatar}) 69 | } 70 | } 71 | CreateGroup{ 72 | id:createGroup; 73 | visible: false; 74 | objectName: "createGroup" 75 | } 76 | 77 | /* 78 | 获取好友/群聊信息=>信息界面 79 | */ 80 | 81 | PersonalInf{id: personalInf; visible: false} 82 | GroupInf{id:groupInf; visible:false} 83 | 84 | //向服务器发送获取个人或群聊信息的信号 85 | signal infSignal(int uid, bool isGroup) 86 | 87 | //接收返回的个人信息信号 88 | function personalInfBack(data) 89 | { 90 | 91 | personalInf.personalSaying = data.signature 92 | personalInf.sex_num = Number(data.gender) 93 | personalInf.birthday = data.birth 94 | personalInf.areaFrom = data.area 95 | personalInf.edu = data.education 96 | personalInf.personalHead = data.icon 97 | personalInf.personalName = data.name 98 | personalInf.personalID = data.id 99 | 100 | personalInf.visible = true 101 | } 102 | function openPInfoWindow(data,){ 103 | //获得用户信息 104 | console.log("emit") 105 | infSignal(data["userId"], false) 106 | 107 | personalInf.isMe = false 108 | personalInf.personalHead = data["avatar"] 109 | personalInf.personalName = data["userName"] 110 | personalInf.personalID = data["userId"] 111 | 112 | // personalInf.visible = true // 与服务器通讯后注释 113 | } 114 | 115 | //接收返回的群组信息信号 116 | function groupInfBack(data) 117 | { 118 | groupInf.groupNotic = data.groupNotice //群公告 119 | groupInf.groupSummary = data.groupSummary //群简介 120 | groupInf.isOwner = data.isOwner 121 | 122 | //groupInf.visible = true 123 | 124 | 125 | } 126 | 127 | 128 | function openGInfoWindow(data){ 129 | //获得群组信息 130 | infSignal(data["userId"],true) 131 | 132 | groupInf.groupHead = data["avatar"] 133 | groupInf.groupName = data["userName"] 134 | groupInf.groupID = data["userId"] 135 | 136 | groupInf.visible = true //与服务器通讯后注释 137 | } 138 | 139 | function createGroupIsSuccess(v) 140 | { 141 | (0 != v)? (createGroupSuccess.open()):(createGroupError.open()) 142 | } 143 | 144 | MessageDialog 145 | {//创建群聊成功提示 146 | id: createGroupSuccess 147 | title: "提示" 148 | icon: StandardIcon.Warning 149 | text: "创建群聊成功" 150 | standardButtons: StandardButton.Cancel 151 | } 152 | MessageDialog 153 | {//创建群聊失败提示 154 | id: createGroupError 155 | title: "提示" 156 | icon: StandardIcon.Warning 157 | text: "创建群聊失败" 158 | standardButtons: StandardButton.Cancel 159 | } 160 | 161 | /* 162 | ui界面 163 | */ 164 | ListModel { 165 | id: contactListModel 166 | ListElement { 167 | userId: 100001 168 | userName: "张三" 169 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 170 | isGroup: false 171 | } 172 | ListElement { 173 | userId: 1 174 | userName: "张三" 175 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 176 | isGroup: false 177 | } 178 | ListElement { 179 | userId: 1 180 | userName: "张三" 181 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 182 | isGroup: false 183 | } 184 | ListElement { 185 | userId: 100001 186 | userName: "张三" 187 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 188 | isGroup: false 189 | } 190 | ListElement { 191 | userId: 1 192 | userName: "张三" 193 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 194 | isGroup: false 195 | } 196 | ListElement { 197 | userId: 1 198 | userName: "张三" 199 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 200 | isGroup: false 201 | } 202 | } 203 | 204 | ListModel { 205 | id: groupListModel 206 | ListElement { 207 | userId: 1 208 | userName: "张三" 209 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 210 | isGroup: true 211 | } 212 | ListElement { 213 | userId: 1 214 | userName: "张三" 215 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 216 | isGroup: true 217 | } 218 | ListElement { 219 | userId: 1 220 | userName: "张三" 221 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 222 | isGroup: true 223 | } 224 | } 225 | 226 | Component { 227 | id: contactListItem 228 | RowLayout 229 | { 230 | 231 | width: 240 232 | height: 64 233 | RoundImage 234 | { 235 | img_src: avatar 236 | width: 40 237 | height: width 238 | color: "black" 239 | Layout.alignment: Qt.AlignLeft 240 | 241 | MouseArea 242 | { 243 | anchors.fill: parent 244 | hoverEnabled: true 245 | onHoveredChanged: 246 | { 247 | cursorShape = Qt.PointingHandCursor 248 | } 249 | acceptedButtons: Qt.LeftButton 250 | onClicked: 251 | { 252 | if(!isGroup&&Number(userId)<600000) 253 | { 254 | openPInfoWindow(contactListModel.get(index)) 255 | } 256 | else 257 | { 258 | openGInfoWindow(groupListModel.get(index)) 259 | } 260 | } 261 | 262 | } 263 | } 264 | Text 265 | { 266 | Layout.leftMargin: 5 267 | text: userName + qsTr("(") + userId + qsTr(")") 268 | elide: Text.ElideRight 269 | Layout.alignment: Qt.AlignLeft 270 | } 271 | Button 272 | { 273 | id: contactButton 274 | width: 20 275 | height: 20 276 | background: Item{ 277 | opacity:1 278 | } 279 | onClicked: { 280 | 281 | if(100000 <= userId && userId <= 599999){ 282 | messageWithConact(userId, userName, avatar); 283 | }else messageWithGroup(userId, userName, avatar); 284 | } 285 | 286 | Layout.alignment: Qt.AlignRight 287 | icon.source: "./images/icon_chat.png" 288 | icon.color: contactButton.hovered ? Material.accent : "transparent" 289 | } 290 | } 291 | 292 | } 293 | 294 | 295 | // 好友群聊显示界面 296 | ColumnLayout { 297 | anchors.margins: 12 298 | anchors.top: parent.top 299 | anchors.bottom: parent.bottom 300 | width: 400 301 | spacing: 0 302 | anchors.left: parent.left 303 | anchors.leftMargin: 12 304 | 305 | Label { 306 | text: "好友" 307 | Layout.alignment: Qt.AlignLeft | Qt.AlignTop 308 | Layout.leftMargin: 4 309 | Layout.bottomMargin: 4 310 | } 311 | 312 | ListView { 313 | id: contactListView 314 | model: contactListModel 315 | spacing: 8 316 | focus: true 317 | Layout.fillWidth: true 318 | height: Math.min(contactListView.contentHeight, 280) 319 | // Layout.fillHeight: true 320 | Layout.alignment: Qt.AlignLeft | Qt.AlignTop 321 | clip: true 322 | delegate: contactListItem 323 | } 324 | 325 | Label { 326 | text: "群聊" 327 | Layout.alignment: Qt.AlignLeft | Qt.AlignTop 328 | Layout.leftMargin: 4 329 | Layout.bottomMargin: 4 330 | } 331 | 332 | ListView { 333 | id: groupListView 334 | model: groupListModel 335 | spacing: 8 336 | focus: true 337 | Layout.fillWidth: true 338 | Layout.fillHeight: true 339 | Layout.alignment: Qt.AlignLeft | Qt.AlignTop 340 | clip: true 341 | delegate: contactListItem 342 | 343 | } 344 | RowLayout 345 | { 346 | Button {// 添加好友 347 | height: 56; width: 56 348 | background: Rectangle { 349 | color: "transparent" 350 | } 351 | icon.source: "../images/addFriend.png" 352 | icon.width: 32 353 | icon.height: 32 354 | 355 | Layout.alignment: Qt.AlignLeft 356 | onClicked: { 357 | addFriendWindow.show() 358 | } 359 | } 360 | Button {// 创建群聊 361 | height: 56; width: 56 362 | background: Rectangle { 363 | color: "transparent" 364 | } 365 | icon.source: "../images/addGroup.png" 366 | icon.width: 25 367 | icon.height: 25 368 | 369 | Layout.alignment: Qt.AlignLeft 370 | onClicked: { 371 | // createGroup.show() 372 | createGroupSignal(1) 373 | } 374 | } 375 | } 376 | 377 | 378 | 379 | } 380 | 381 | } 382 | 383 | /*##^## 384 | Designer { 385 | D{i:0;autoSize:true;height:480;width:640} 386 | } 387 | ##^##*/ 388 | -------------------------------------------------------------------------------- /01.项目源码/client/CreateGroup.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Window 2.2 3 | import QtQuick.Controls 2.5 4 | import QtQuick.Controls.Material 2.0 5 | import QtQuick.Layouts 1.3 6 | import QtQuick.Dialogs 1.3 7 | import QtQml 2.0 8 | 9 | import "./components" 10 | 11 | Window { 12 | id: createGroupWindow 13 | width: 300 14 | height: 200 15 | visible: false 16 | 17 | title: qsTr("创建群聊") 18 | 19 | flags:(Qt.Window | Qt.CustomizeWindowHint) 20 | 21 | 22 | property string avatarImg: "https://c-ssl.dtstatic.com/uploads/blog/202203/25/20220325232426_17909.thumb.1000_0.jpeg" 23 | property string userName: "XXX" 24 | property int userid: 12345 25 | property var memberId: [] 26 | property string printStr : "" 27 | 28 | //创建群聊信号 29 | signal createGroupSignal(var mId) 30 | 31 | 32 | //返回生成的群聊号和拉群成功指令 33 | function createGroupBack(groupID) 34 | { 35 | if(0 == groupID) 36 | { 37 | createGroupError.open() 38 | } 39 | else 40 | { 41 | createGroupSuccess.open() 42 | } 43 | } 44 | 45 | 46 | 47 | function printMember() 48 | { 49 | printStr = "确认拉取成员创建群聊?\n" 50 | return printStr 51 | } 52 | 53 | MessageDialog 54 | {//拉群人数少于3 55 | id: lessThanThree 56 | title: "提示" 57 | icon: StandardIcon.Warning 58 | text: "3人以下无法建群" 59 | standardButtons: StandardButton.Cancel 60 | } 61 | MessageDialog 62 | {//创建群聊失败提示 63 | id: createGroupError 64 | title: "提示" 65 | icon: StandardIcon.Warning 66 | text: "创建群聊失败" 67 | standardButtons: StandardButton.Cancel 68 | } 69 | MessageDialog 70 | {//创建群聊成功提示 71 | id: createGroupSuccess 72 | title: "提示" 73 | icon: StandardIcon.Warning 74 | text: "创建群聊成功" 75 | standardButtons: StandardButton.Cancel 76 | } 77 | 78 | Dialog 79 | {//添加该成员提示 80 | id: idDialog 81 | width: 200; 82 | height: 100 83 | title: qsTr("添加成员确认") 84 | standardButtons: StandardButton.Ok|StandardButton.Cancel 85 | 86 | Text { 87 | id: text 88 | font.pixelSize: 15 89 | text: printMember() 90 | anchors.centerIn: parent 91 | } 92 | 93 | onAccepted: 94 | { 95 | console.log("create") 96 | createGroupSignal(memberId) 97 | } 98 | } 99 | 100 | 101 | Rectangle{ 102 | id:closeButton 103 | width:10 104 | height:10 105 | radius: closeButton.width/2 106 | anchors.top: parent.top 107 | anchors.right: parent.right 108 | 109 | Rectangle{ 110 | width:15 111 | height:15 112 | radius: 15/2 113 | anchors{ 114 | right:closeButton.right 115 | top:closeButton.top 116 | } 117 | Text { 118 | id: name 119 | anchors.centerIn: parent 120 | text: qsTr("X") 121 | color:"#FFFFFF" 122 | font.pixelSize: 16 123 | } 124 | color:"green" 125 | MouseArea { 126 | anchors.fill: parent 127 | onClicked: { 128 | createGroupWindow.visible = false 129 | } 130 | hoverEnabled: true 131 | onHoveredChanged: cursorShape = Qt.PointingHandCursor 132 | } 133 | } 134 | } 135 | 136 | 137 | // 搜索栏 138 | 139 | GroupAddWidget 140 | { 141 | id: searchWidget 142 | Layout.fillWidth: true 143 | Layout.alignment: Qt.AlignHCenter 144 | anchors.horizontalCenter: parent.horizontalCenter 145 | anchors.top: parent.top 146 | anchors.topMargin: 20 147 | onIdpp: 148 | { 149 | userid = idN 150 | searchResult.visible = true 151 | } 152 | 153 | } 154 | Rectangle 155 | { 156 | z:1 157 | id:searchResult 158 | width: parent.width 159 | height: parent.height 160 | anchors.top: searchWidget.bottom 161 | anchors.topMargin: 20 162 | anchors.left: parent.left 163 | anchors.leftMargin: 18 164 | anchors.right: parent.right 165 | anchors.rightMargin: 8 166 | 167 | visible: false 168 | 169 | Text { 170 | id: pInf 171 | text: qsTr("联系人") 172 | } 173 | Rectangle 174 | { 175 | z:1 176 | id: pInfList 177 | anchors.top: pInf.bottom 178 | anchors.topMargin: 10 179 | width: parent.width 180 | height: parent.height*0.4 181 | 182 | //好友搜索结果表 183 | Rectangle 184 | { 185 | width:parent.width 186 | RowLayout 187 | { 188 | Layout.fillWidth: true 189 | RoundImage 190 | { 191 | id: avatarImgP 192 | img_src: avatarImg 193 | 194 | width: 40 195 | height: avatarImgP.width 196 | color: "black" 197 | } 198 | Text 199 | { 200 | id: searchName 201 | Layout.leftMargin: 20 202 | text: userName + qsTr("(") + userid + qsTr(")") 203 | } 204 | Rectangle 205 | { 206 | z:100 207 | width: 20 208 | height: 20 209 | Layout.leftMargin: 80 210 | Image 211 | { 212 | id:addMember 213 | anchors.fill: parent 214 | source: "qrc:/images/addGroup.png" 215 | MouseArea 216 | { 217 | 218 | anchors.fill: parent 219 | hoverEnabled: true 220 | onHoveredChanged: cursorShape = Qt.PointingHandCursor 221 | acceptedButtons: Qt.LeftButton 222 | onClicked: 223 | { 224 | if(userid != memberId[memberId.length-1]) 225 | { 226 | //发出创建群聊添加成员的信号 227 | memberId.push(userid) 228 | console.log("addFriend") 229 | console.log(memberId) 230 | 231 | } 232 | } 233 | } 234 | } 235 | } 236 | } 237 | } 238 | } 239 | } 240 | Rectangle 241 | { 242 | z:100 243 | width: 100 244 | height: 30 245 | anchors.bottom: parent.bottom 246 | anchors.bottomMargin: 5 247 | anchors.horizontalCenter: parent.horizontalCenter 248 | Button 249 | { 250 | anchors.fill: parent 251 | text: "拉取群聊" 252 | 253 | onClicked: 254 | { 255 | if(memberId.length < 2) 256 | { 257 | lessThanThree.open() 258 | } 259 | else 260 | { 261 | idDialog.open() 262 | console.log("拉群成功") 263 | createGroup.close() 264 | } 265 | } 266 | } 267 | } 268 | 269 | 270 | } 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | -------------------------------------------------------------------------------- /01.项目源码/client/FileWidget.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.5 3 | import QtQuick.Controls.Material 2.3 4 | import QtQuick.Layouts 1.3 5 | 6 | 7 | Rectangle { 8 | property string _fileName: "张三.txt" 9 | property int _fileSize: 10000 10 | property double _progress: 0.5 11 | 12 | id: fileContainer 13 | width: 180 14 | height: 80 15 | radius: 4 16 | color: "#edf5f9" 17 | z: 0 18 | 19 | MouseArea { 20 | 21 | anchors.fill: parent 22 | onClicked: { 23 | 24 | } 25 | } 26 | 27 | function bytesToSize(bytes) { 28 | if (bytes === 0) return '0 B'; 29 | var k = 1000, // or 1024 30 | sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 31 | i = Math.floor(Math.log(bytes) / Math.log(k)); 32 | 33 | return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i]; 34 | } 35 | 36 | 37 | RowLayout { 38 | anchors.centerIn: parent 39 | spacing: 8 40 | ColumnLayout { 41 | Label { 42 | color: "#dd000000" 43 | text: _fileName 44 | font.pointSize: 12 45 | font.bold: true 46 | styleColor: "#ffffff" 47 | } 48 | 49 | Label { 50 | color: "#ddab9d9d" 51 | text: bytesToSize(_fileSize) 52 | Layout.topMargin: 2 53 | Layout.margins: 0 54 | } 55 | } 56 | 57 | Image { 58 | Layout.leftMargin: 8 59 | source: "./images/icon_file.png" 60 | sourceSize.width : 36 61 | sourceSize.height: 36 62 | fillMode: Image.PreserveAspectFit 63 | 64 | } 65 | } 66 | 67 | // Button { 68 | // text: "打开" 69 | // anchors.rightMargin: 20 70 | // flat: true 71 | // background: undefined 72 | 73 | // anchors.right: parent.right 74 | // anchors.bottom: parent.bottom 75 | // } 76 | 77 | ProgressBar { 78 | id: fileUploadProgressBar 79 | anchors.bottom: parent.bottom 80 | anchors.left: parent.left 81 | anchors.right: parent.right 82 | visible: 0 < _progress && _progress < 1 83 | value: _progress 84 | clip: true 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /01.项目源码/client/GroupInf.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Window 2.12 3 | import QtQuick.Layouts 1.3 4 | import QtQuick.Controls.Material 2.0 5 | import QtGraphicalEffects 1.0 6 | import QtQuick.Controls 2.5 7 | 8 | /* 9 | 10 | */ 11 | 12 | Window { 13 | id: groupPage 14 | width: 338 15 | height: 506 16 | visible: false 17 | 18 | property string groupHead: "https://c-ssl.dtstatic.com/uploads/item/202007/23/20200723100019_v4nJm.thumb.1000_0.jpeg" 19 | property string pBackground: "https://c-ssl.dtstatic.com/uploads/item/202004/25/20200425235603_ybgrj.thumb.1000_0.jpg" 20 | property string groupName: "东拼西凑组" 21 | property int groupID: 12345 22 | property string groupNotic: "见证一款聊天软件的诞生w" 23 | property string groupSummary: "这是一个组建于2022-08-14的群组\n欢迎新成员加入lol" 24 | property bool setFlag: true // true:显示模式 false:设置模式 25 | 26 | //接收发来信号判断是否是群主,若是群主拥有设置权限 27 | property bool isOwner: true 28 | 29 | 30 | 31 | flags:(Qt.Window | Qt.CustomizeWindowHint) 32 | 33 | 34 | Rectangle{ 35 | z:100 36 | id:closeButton 37 | width:15 38 | height:15 39 | radius: closeButton.width/2 40 | anchors.top: parent.top 41 | anchors.left: parent.left 42 | 43 | Rectangle{ 44 | width:15 45 | height:15 46 | radius: 15/2 47 | anchors.fill: parent 48 | Text { 49 | id: closeName 50 | anchors.centerIn: parent 51 | text: qsTr("X") 52 | color:"#FFFFFF" 53 | font.pixelSize: 16 54 | } 55 | color:"green" 56 | MouseArea { 57 | anchors.fill: parent 58 | hoverEnabled: true 59 | onHoveredChanged: cursorShape = Qt.PointingHandCursor 60 | onClicked: { 61 | groupPage.visible = false 62 | } 63 | } 64 | } 65 | } 66 | 67 | 68 | 69 | /* 70 | 接收服务端发来的群聊信息,并进行显示 71 | */ 72 | ColumnLayout 73 | { 74 | z:1 75 | anchors.fill: parent 76 | 77 | //设置按钮:设置成stackView多界面切换 78 | ToolButton 79 | { 80 | z:100 81 | id: reset 82 | width: 15 83 | height: 15 84 | Layout.alignment: Qt.AlignRight 85 | 86 | visible: isOwner 87 | 88 | font.pixelSize :15 89 | 90 | text: setFlag?(reset.pressed? qsTr("设置") : 91 | reset.hovered? qsTr("设置") : 92 | ("设置")): 93 | (reset.pressed? qsTr("完成") : 94 | reset.hovered? qsTr("完成") : 95 | ("完成")) 96 | 97 | onClicked: 98 | { 99 | if(1 == setFlag) 100 | {//readonly -> write 101 | name.visible = false 102 | nameC.visible = true 103 | sendMsg.visible = false 104 | summaryDisplay.visible = false 105 | summaryC.visible = true 106 | noticeContext.visible = false 107 | noticeC.visible = true 108 | } 109 | else 110 | {//write -> readonly 111 | name.visible = true 112 | nameC.visible = false 113 | groupName = nameC.text 114 | 115 | sendMsg.visible = true 116 | 117 | summaryDisplay.visible = true 118 | summaryC.visible = false 119 | groupSummary = summaryC.text 120 | 121 | noticeContext.visible = true 122 | noticeC.visible = false 123 | groupNotic = noticeC.text 124 | 125 | //avatar = groupHead 126 | 127 | QQClient.changeGInfoRequest(groupName,groupNotic,groupSummary,groupID) 128 | console.log("群组信息修改已完成") 129 | } 130 | 131 | setFlag = !setFlag 132 | } 133 | } 134 | 135 | // 头像 136 | Rectangle 137 | { 138 | id: img 139 | Layout.alignment: Qt.AlignHCenter 140 | Layout.topMargin: -175 141 | width: 338 142 | height: 325 143 | radius: width/2 144 | Image 145 | { 146 | //背景图 147 | anchors.fill: img 148 | source: pBackground 149 | } 150 | Rectangle 151 | { 152 | id: imgHead 153 | width: 100 154 | height: 100 155 | radius: imgHead.width/2 156 | 157 | anchors.centerIn: img 158 | anchors.verticalCenterOffset: 20 159 | 160 | Image { 161 | id: _image 162 | smooth: true 163 | anchors.fill: parent 164 | visible: false 165 | source: groupHead 166 | antialiasing: true 167 | } 168 | Rectangle { 169 | id: _mask 170 | anchors.fill: parent 171 | radius: imgHead.width/2 172 | visible: false 173 | antialiasing: true 174 | smooth: true 175 | } 176 | OpacityMask { 177 | id:mask_image 178 | anchors.fill: _image 179 | source: _image 180 | maskSource: _mask 181 | visible: true 182 | antialiasing: true 183 | } 184 | } 185 | } 186 | //头像下部分信息:群名称+群号 187 | ColumnLayout 188 | { 189 | id: belowInf 190 | Layout.alignment: Qt.AlignHCenter 191 | Layout.topMargin: -120 192 | 193 | Text 194 | { 195 | id: name 196 | Layout.alignment: Qt.AlignHCenter 197 | horizontalAlignment: Text.AlignHCenter 198 | font.pixelSize: 20 199 | font.family: "SimHei" 200 | font.bold: true 201 | text: groupName 202 | } 203 | TextInput 204 | { 205 | id: nameC 206 | visible: false 207 | Layout.alignment: Qt.AlignHCenter 208 | horizontalAlignment: Text.AlignHCenter 209 | font.pixelSize: 20 210 | font.family: "SimHei" 211 | font.bold: true 212 | text: groupName 213 | } 214 | 215 | Text 216 | { 217 | id: groupNum 218 | Layout.topMargin: 0 219 | Layout.alignment: Qt.AlignHCenter 220 | horizontalAlignment: Text.AlignHCenter 221 | font.pixelSize: 12 222 | color: "#767777" 223 | text: qsTr("群号:") + groupID 224 | } 225 | Button 226 | { 227 | //跳转页面到群聊天界面 228 | id: sendMsg 229 | Layout.alignment: Qt.AlignHCenter 230 | Layout.topMargin: -9 231 | background: 232 | { 233 | opacity: 1 234 | } 235 | icon.source: sendMsg.pressed? "qrc:/images/message1.png": 236 | sendMsg.hovered? "qrc:/images/message1.png" : 237 | ("qrc:/images/message.png") 238 | icon.color:"transparent" 239 | } 240 | } 241 | 242 | 243 | //群公告 244 | ColumnLayout 245 | { 246 | id:notice 247 | Layout.topMargin: 0 248 | Layout.alignment: Qt.AlignHCenter 249 | 250 | Text { 251 | text: qsTr("群公告") 252 | } 253 | Rectangle 254 | { 255 | width: 280 256 | Text 257 | { 258 | id: noticeContext 259 | text: groupNotic 260 | font.pixelSize: 13 261 | } 262 | TextArea 263 | { 264 | id: noticeC 265 | visible: false 266 | width : parent.width 267 | 268 | text: groupNotic 269 | font: font.pixelSize = 13 270 | background: Rectangle 271 | { 272 | border.color : "white" 273 | } 274 | } 275 | } 276 | } 277 | //分割线 278 | Canvas 279 | { 280 | id: canvas1 281 | width: parent.width 282 | height: parent.height 283 | onPaint: { 284 | var ctx = getContext("2d"); 285 | draw(ctx); 286 | } 287 | function draw(ctx ) { 288 | // 画之前清空画布 289 | ctx.clearRect(0, 0, parent.width, parent.height); 290 | 291 | ctx.fillStyle ="#f2f2f2"; // 设置画笔属性 292 | ctx.strokeStyle = "#f2f2f2"; 293 | ctx.lineWidth = 2 294 | 295 | ctx.beginPath(); // 开始一条路径 296 | ctx.moveTo(40,340); // 移动到指定位置 297 | ctx.lineTo(298,340); 298 | 299 | ctx.stroke(); 300 | } 301 | } 302 | 303 | //群简介 304 | ColumnLayout 305 | { 306 | id:summary 307 | Layout.alignment: Qt.AlignHCenter 308 | Layout.topMargin: 50 309 | 310 | Text { 311 | text: qsTr("群介绍") 312 | } 313 | Rectangle 314 | { 315 | width: 280 316 | height: 100 317 | Text 318 | { 319 | id: summaryDisplay 320 | text: groupSummary 321 | } 322 | TextArea 323 | { 324 | id:summaryC 325 | visible: false 326 | width: parent.width 327 | text: groupSummary 328 | font: font.pixelSize = 13 329 | background: Rectangle 330 | { 331 | border.color : "white" 332 | } 333 | } 334 | } 335 | } 336 | 337 | } 338 | 339 | 340 | 341 | 342 | 343 | } 344 | -------------------------------------------------------------------------------- /01.项目源码/client/HistoryMessageListItem.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.5 3 | import QtQuick.Controls.Material 2.3 4 | import QtQuick.Layouts 1.3 5 | 6 | import "./js/time_utils.js" as TimeUtils 7 | import "./components" 8 | 9 | Rectangle { 10 | id: itemContainer 11 | anchors.left: parent.left 12 | anchors.right: parent.right 13 | height: 72 14 | clip: true 15 | color: ListView.isCurrentItem ? "#f2f2f2" : "white" 16 | radius: ListView.isCurrentItem ? 12 : 8 17 | 18 | /**参数说明: 19 | * 根据长度截取先使用字符串,超长部分追加… 20 | * str 对象字符串 21 | * len 目标字节长度 22 | * 返回值: 处理结果字符串 23 | */ 24 | function cutString(str, len) { 25 | //length属性读出来的汉字长度为1 26 | if(str.length*2 <= len) { 27 | return str; 28 | } 29 | var strlen = 0; 30 | var s = ""; 31 | for(var i = 0;i < str.length; i++) { 32 | s = s + str.charAt(i); 33 | if (str.charCodeAt(i) > 128) { 34 | strlen = strlen + 2; 35 | if(strlen >= len){ 36 | return s.substring(0,s.length-1) + "..."; 37 | } 38 | } else { 39 | strlen = strlen + 1; 40 | if(strlen >= len){ 41 | return s.substring(0,s.length-2) + "..."; 42 | } 43 | } 44 | } 45 | return s; 46 | } 47 | 48 | 49 | Rectangle { 50 | id: rectangle 51 | height: parent.height 52 | opacity: 1 53 | width: parent.width 54 | color: "transparent" 55 | 56 | RoundImage { 57 | id: avatarImg 58 | img_src: avatar 59 | width: 48 60 | height: avatarImg.width 61 | anchors.leftMargin: 12 62 | color: "blue" 63 | anchors.top: parent.top 64 | anchors.topMargin: 12 65 | anchors.left: parent.left 66 | } 67 | 68 | RowLayout { 69 | anchors.top: parent.top 70 | anchors.topMargin: 14 71 | anchors.left: avatarImg.right 72 | anchors.right: parent.right 73 | 74 | ColumnLayout { 75 | id: columnLayout 76 | Layout.topMargin: 0 77 | Layout.leftMargin: 12 78 | Layout.alignment: Qt.AlignLeft 79 | Label{ 80 | id: userNameLabel 81 | text: userName 82 | font.bold: true 83 | elide: "ElideRight" 84 | } 85 | Label { 86 | color: "#ee000000" 87 | text: `${cutString(latestMessage, 20)}` 88 | elide: Text.ElideRight 89 | font.pointSize: 9 90 | clip: true 91 | width: 200 92 | } 93 | 94 | } 95 | Label { 96 | text: TimeUtils.getTimeString(new Date(latestTime*1000)); 97 | Layout.alignment: Qt.AlignRight | Qt.AlignTop 98 | Layout.rightMargin: 8 99 | font.pointSize: 8 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /01.项目源码/client/HistoryMessageScreen.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.5 3 | import QtQuick.Controls.Material 2.3 4 | import QtQuick.Layouts 1.3 5 | 6 | Rectangle { 7 | 8 | radius: 4 9 | 10 | // 信号,点击item触发,参数为对应项的data 11 | // data 的数据参考同下 12 | 13 | signal clickHistoryMessageItem(var data) 14 | signal refreshChatListSignal(int targetId) 15 | 16 | // 获取到聊天记录后调这个函数,messages为json数组。每一项的数据data参考如下 17 | /** 18 | userId: 1 19 | userName: "张三" 20 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 21 | latestTime: 1661053691 22 | latestMessage: "你说咱们要不就继续加油吧" 23 | */ 24 | 25 | function setMessages(messages){ 26 | historyMessageListModel.clear() 27 | for(each of messages){ 28 | historyMessageListModel.append(each) 29 | } 30 | } 31 | 32 | function setMessageWithUid(uid, message, time){ 33 | for(let i =0; i < historyMessageListModel.count; i++){ 34 | let data = historyMessageListModel.get(i); 35 | if(data.userId === uid){ 36 | data["latestTime"] = time 37 | data["latestMessage"] = message 38 | historyMessageListModel.set(i, data); 39 | return 40 | } 41 | } 42 | } 43 | 44 | function startChatWith(uid, name, avatar){ 45 | for(let i =0; i < historyMessageListModel.count; i++){ 46 | if(historyMessageListModel.get(i).userId === uid){ 47 | historyMessageListView.currentIndex = i 48 | clickHistoryMessageItem(historyMessageListModel.get(i)) 49 | refreshChatListSignal(historyMessageListModel.get(i).userId) 50 | console.log(`找到了和${uid}的历史聊天记录,位于${i}`) 51 | return 52 | } 53 | } 54 | console.log(`没找到和${uid}的历史聊天记录,新建一条`); 55 | historyMessageListModel.insert(0, { 56 | "userId": uid, 57 | "userName": name, 58 | "avatar": avatar, 59 | "latestTime": Date.parse(new Date())/ 1000, 60 | "latestMessage": "" 61 | }) 62 | historyMessageListView.currentIndex = 0 63 | clickHistoryMessageItem(historyMessageListModel.get(0)) 64 | refreshChatListSignal(historyMessageListModel.get(0).userId) 65 | } 66 | 67 | ListModel { 68 | id: historyMessageListModel 69 | } 70 | 71 | ListView { 72 | id: historyMessageListView 73 | model: historyMessageListModel 74 | anchors.fill: parent 75 | spacing: 8 76 | focus: true 77 | anchors.margins: 8 78 | 79 | delegate: HistoryMessageListItem{ 80 | MouseArea { 81 | anchors.fill: parent 82 | onClicked: { 83 | historyMessageListView.currentIndex = index 84 | clickHistoryMessageItem(historyMessageListModel.get(index)) 85 | refreshChatListSignal(historyMessageListModel.get(index).userId) 86 | } 87 | } 88 | } 89 | } 90 | } 91 | 92 | 93 | -------------------------------------------------------------------------------- /01.项目源码/client/LoginWindow.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Window 2.12 3 | import QtQuick.Controls 2.5 4 | import QtQuick.Controls.Material 2.0 5 | import QtGraphicalEffects 1.0 6 | import QtQuick.Dialogs 1.3 7 | import "./js/config_utils.js" as ConfigUtils 8 | 9 | Window { 10 | property string usrID: Config.read("myID","") //置空失败 11 | property string usrPSW: "" 12 | property string usrHead: Config.read("myHead", "https://c-ssl.duitang.com/uploads/blog/201408/15/20140815095903_ttcnF.jpeg") //登陆时若本地就有头像自动获取;否则使用自带默认头像(类qq的企鹅) 13 | property string usrName: "" 14 | property string usrPassword: "" 15 | 16 | id: loginWindows 17 | width: 248 18 | height: 400 19 | visible: true //窗口是否可见 20 | title: qsTr("登陆") //窗口标题 21 | flags: Qt.Window 22 | 23 | RegisterWindow{id: registerWindows; visible: false;} 24 | MainWindow{id: mainWindows; visible: false;} 25 | 26 | // 发送登陆信号,在数据库中验证 27 | signal loginSignal(int usrID,string usrPSW) 28 | 29 | 30 | MessageDialog 31 | { 32 | id: wrongLogin 33 | title: "提示" 34 | icon: StandardIcon.Warning 35 | text: "账号或密码错误" 36 | standardButtons: StandardButton.Cancel 37 | } 38 | MessageDialog 39 | { 40 | id: notNumInputText 41 | title: "提示" 42 | icon: StandardIcon.Warning 43 | text: "请输入数字" 44 | standardButtons: StandardButton.Cancel 45 | } 46 | 47 | //接收服务器返回 登陆成功 48 | function loginBack(v) 49 | { 50 | if(1 == v) 51 | { 52 | Config.write("myID", usrID) 53 | ClientInfo.id = Number(usrID) 54 | ClientInfo.avatar = usrHead 55 | mainWindows.show() 56 | // loginWindows.hide() 57 | loginWindows.visible = false 58 | } 59 | else 60 | { 61 | wrongLogin.open() 62 | } 63 | } 64 | 65 | 66 | // 头像 67 | Rectangle { 68 | id: img 69 | anchors.centerIn: parent 70 | anchors.verticalCenterOffset: -125 71 | width: 100 72 | height: 100 73 | radius: width/2 74 | color: "black" 75 | 76 | Image { 77 | id: _image 78 | smooth: true 79 | visible: false 80 | anchors.fill: parent 81 | source: usrHead 82 | antialiasing: true 83 | } 84 | Rectangle { 85 | id: _mask 86 | anchors.fill: parent 87 | radius: width/2 88 | visible: false 89 | antialiasing: true 90 | smooth: true 91 | } 92 | OpacityMask { 93 | id:mask_image 94 | anchors.fill: _image 95 | source: _image 96 | maskSource: _mask 97 | visible: true 98 | antialiasing: true 99 | } 100 | } 101 | 102 | // 账号 密码 103 | TextField 104 | { 105 | id: inputID 106 | width:200 107 | height: 50 108 | y:150 109 | x:20 110 | leftPadding: 4 111 | placeholderText: qsTr("
账号
") 112 | text: usrID 113 | } 114 | TextField 115 | { 116 | id: inputPSW 117 | width:200 118 | height: 50 119 | y:200 120 | x:20 121 | leftPadding: 4 122 | echoMode: TextInput.Password 123 | placeholderText: qsTr("
密码
") 124 | text: usrPassword 125 | } 126 | 127 | // 确认 128 | Button 129 | { 130 | id: sendMsg 131 | objectName: "loginButton" 132 | anchors.centerIn: parent 133 | anchors.verticalCenterOffset: 90 134 | width: 40 135 | height: 40 136 | icon.source: sendMsg.pressed? "qrc:/images/rightArrow1.png": 137 | ("qrc:/images/rightArrow.png") 138 | onClicked: 139 | { 140 | if(isNaN(inputID.text)) 141 | { 142 | notNumInputText.open() 143 | inputID.text = "" 144 | } 145 | else 146 | { 147 | usrID = Number(inputID.text) 148 | usrPSW = inputPSW.text 149 | //发送消息 150 | console.log(usrPSW) 151 | console.log("登录") 152 | loginSignal(usrID,usrPSW) 153 | } 154 | } 155 | } 156 | 157 | 158 | ToolButton 159 | { 160 | width:50 161 | height: 30 162 | objectName: "registerButton" 163 | anchors.centerIn: parent 164 | anchors.verticalCenterOffset: 180 165 | highlighted: true 166 | text: qsTr("注册") 167 | onClicked: 168 | { 169 | registerWindows.show() 170 | loginWindows.hide() 171 | } 172 | } 173 | 174 | 175 | } 176 | -------------------------------------------------------------------------------- /01.项目源码/client/MainWindow.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Window 2.12 3 | import QtQuick.Controls 2.5 4 | import QtQuick.Controls.Material 2.3 5 | import QtQuick.Layouts 1.3 6 | import "./components/" 7 | 8 | Window { 9 | id:mainWindowP 10 | visible: false 11 | width: 1000 12 | height: 600 13 | title: qsTr("WeTalk") 14 | color: "#edf5f9" 15 | objectName: "mainWindow" 16 | 17 | // 信号:开始请求历史消息,参数为我的UID 18 | signal requestHistoryMessage(int myUid) 19 | 20 | // 设置历史聊天记录, 21 | // 参数:json数组,每项格式如下 22 | /** 23 | userId: 1 24 | userName: "张三" 25 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 26 | latestTime: 1661053691 // 最后一条消息时间 27 | latestMessage: "你说咱们要不就继续加油吧" // 最后一条消息内容 28 | */ 29 | 30 | function setHistoryMessage(messages){ 31 | historyMessageScreen.setMessages(messages); 32 | } 33 | 34 | function startChatTo(uid, name, avatar){ 35 | console.log(`开始和${name}聊天……`) 36 | historyMessageScreen.startChatWith(uid, name, avatar); 37 | tabWidget.current = 0 38 | } 39 | 40 | Component.onCompleted: { 41 | ClientInfo.id = "100001" 42 | ClientInfo.name = "Shen" 43 | ClientInfo.avatar = "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 44 | requestHistoryMessage(ClientInfo.id); 45 | } 46 | 47 | EmojiChoose{ 48 | id: emojiChoose; 49 | onClickEmoji: (text)=>chatScreen.appendInputText(text) 50 | } 51 | 52 | VerticalTabWidget { 53 | id: tabWidget 54 | anchors.rightMargin: 12 55 | anchors.leftMargin: 12 56 | anchors.bottomMargin: 12 57 | anchors.topMargin: 12 58 | anchors.fill: parent 59 | objectName: "chatWindow1" 60 | onChangeToContact: contactScreen.sendRequestDataSignal() 61 | 62 | // 聊天页面 63 | RowLayout { 64 | id:rowLayout 65 | spacing: 12 66 | // anchors.fill: parent 67 | objectName: "chatWindow2" 68 | 69 | HistoryMessageScreen { 70 | id: historyMessageScreen 71 | Layout.fillHeight: true 72 | width: 270 73 | objectName: "historyMessageScreen" 74 | Component.onCompleted: { 75 | clickHistoryMessageItem.connect((data)=>{ 76 | // 历史消息列表与聊天窗口的同步 77 | chatScreen.setArg(data["userId"]) 78 | }) 79 | } 80 | } 81 | 82 | ChatScreen { 83 | id: chatScreen 84 | Layout.fillHeight: true 85 | onUpdateHistorySignal: (id, message, time)=>historyMessageScreen.setMessageWithUid(id, message, time) 86 | } 87 | } 88 | 89 | // 联系人页面 90 | ContactScreen { 91 | // anchors.fill: parent 92 | id: contactScreen 93 | visible: false 94 | radius: 4 95 | onMessageWithConact: (uid, name, avatar)=>startChatTo(uid, name, avatar) 96 | onMessageWithGroup: (uid, name, avatar)=>startChatTo(uid, name, avatar) 97 | } 98 | 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /01.项目源码/client/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /01.项目源码/client/RegisterWindow.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Window 2.2 3 | import QtQuick.Controls 2.5 4 | import QtQuick.Controls.Material 2.0 5 | import QtQuick.Layouts 1.3 6 | import QtQuick.Dialogs 1.3 7 | import "./js/config_utils.js" as ConfigUtils 8 | import "./components" 9 | 10 | Window { 11 | id: registerWindows 12 | width: 248 13 | height: 400 14 | visible: false //窗口是否可见 15 | title: qsTr("注册") //窗口标题 16 | objectName: "registerWindow" 17 | 18 | //发送信号 19 | signal registerSignal(string usrName,string usrPassword) 20 | 21 | property var sendArg 22 | 23 | 24 | MessageDialog 25 | {//注册失败提示 26 | id: wrongRegister 27 | title: "提示" 28 | icon: StandardIcon.Warning 29 | text: "注册失败" 30 | standardButtons: StandardButton.Cancel 31 | } 32 | 33 | MessageDialog 34 | {//验证失败提示 35 | id: wrongCheck 36 | title: "提示" 37 | icon: StandardIcon.Warning 38 | text: "验证失败" 39 | standardButtons: StandardButton.Cancel 40 | } 41 | MessageDialog 42 | {//邮箱输入错误提示 43 | id: wrongEmail 44 | title: "提示" 45 | icon: StandardIcon.Warning 46 | text: "邮箱输入错误" 47 | standardButtons: StandardButton.Cancel 48 | } 49 | 50 | Dialog 51 | {//返回注册结果 52 | id: idDialog 53 | width: 300; 54 | height: 300 55 | title: qsTr("注册信息确认") 56 | standardButtons: StandardButton.Ok 57 | 58 | Text { 59 | id: text 60 | font.pixelSize: 15 61 | text: qsTr("您的账号:") + usrID + qsTr("\n您的用户名:") + usrName 62 | anchors.centerIn: parent 63 | } 64 | 65 | 66 | onAccepted: 67 | { 68 | console.log("Having checked the new usr inf") 69 | mainWindow.show() 70 | registerWindows.hide() 71 | } 72 | } 73 | 74 | 75 | 76 | //接收服务器返回的账号,打印后进入主界面 77 | function registerBack(pID) 78 | { 79 | console.log(1) 80 | if (0 == pID) 81 | { 82 | wrongRegister.open() 83 | } 84 | else 85 | { 86 | idDialog.open() 87 | usrID = pID.toString() 88 | inputID.text = usrID 89 | inputPSW.text = usrPassword 90 | Config.write("myID",usrID) 91 | 92 | } 93 | } 94 | 95 | 96 | // POST 97 | function post(url, arg,flag) 98 | { 99 | 100 | var xhr = new XMLHttpRequest; 101 | xhr.open("POST", url); 102 | xhr.setRequestHeader("Content-Length", arg.length); 103 | xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;"); //post必备 104 | xhr.onreadystatechange = function() { 105 | handleResponse(xhr,flag); 106 | } 107 | xhr.send(arg); 108 | } 109 | 110 | property int check : 0 111 | 112 | // 处理返回值 113 | function handleResponse(xhr,flag){ 114 | if (xhr.readyState == XMLHttpRequest.DONE) 115 | { 116 | print("DONE") 117 | const response = JSON.parse(xhr.responseText.toString()) 118 | console.log(response.code) 119 | 120 | if(50 != response.code) 121 | { 122 | console.log(response.error_msg) 123 | } 124 | else 125 | { 126 | console.log(response.message) 127 | } 128 | 129 | if(response.code == -1) 130 | { 131 | if(flag == 1) 132 | { 133 | wrongEmail.open() 134 | } 135 | else if(flag == 0) 136 | { 137 | wrongCheck.open() 138 | check = 1 139 | } 140 | } 141 | else 142 | { 143 | if(flag == 0) 144 | { 145 | console.log("inf config ok") 146 | registerSignal(usrName,usrPassword) 147 | 148 | loginWindows.show() 149 | close() 150 | } 151 | } 152 | 153 | 154 | 155 | } 156 | else if(xhr.readyState == XMLHttpRequest.HEADERS_RECEIVED) 157 | { 158 | print('HEADERS_RECEIVED') 159 | } 160 | } 161 | 162 | 163 | 164 | 165 | MessageDialog 166 | { 167 | id:wrongPSW 168 | title: "提示" 169 | icon: StandardIcon.Warning 170 | text: "输入密码不匹配" 171 | standardButtons: StandardButton.Cancel 172 | } 173 | MessageDialog 174 | { 175 | id: noUsrName 176 | title: "提示" 177 | icon: StandardIcon.Warning 178 | text: "请输入用户名" 179 | standardButtons: StandardButton.Cancel 180 | } 181 | MessageDialog 182 | { 183 | id: noPassword 184 | title: "提示" 185 | icon: StandardIcon.Warning 186 | text: "请输入密码" 187 | standardButtons: StandardButton.Cancel 188 | } 189 | 190 | 191 | ColumnLayout 192 | { 193 | Layout.alignment: Qt.AlignHCenter 194 | anchors.centerIn: parent 195 | anchors.verticalCenterOffset: -20 196 | 197 | 198 | TextField 199 | { 200 | Layout.alignment: Qt.AlignHCenter 201 | id: usr 202 | width:200 203 | height: 50 204 | leftPadding: 4 205 | placeholderText: qsTr("
用户名
") 206 | } 207 | TextField 208 | { 209 | Layout.alignment: Qt.AlignHCenter 210 | id: password 211 | Layout.topMargin: 15 212 | width:200 213 | height: 50 214 | leftPadding: 4 215 | echoMode: TextInput.Password 216 | placeholderText: qsTr("
密码
") 217 | } 218 | TextField 219 | { 220 | Layout.alignment: Qt.AlignHCenter 221 | id: passwordAgain 222 | Layout.topMargin: 15 223 | width:200 224 | height: 50 225 | leftPadding: 4 226 | echoMode: TextInput.Password 227 | placeholderText: qsTr("
确认密码
") 228 | } 229 | TextField 230 | { 231 | Layout.alignment: Qt.AlignHCenter 232 | id: emailAddress 233 | Layout.topMargin: 15 234 | width:200 235 | height: 50 236 | leftPadding: 4 237 | placeholderText: qsTr("
邮件地址
") 238 | } 239 | Rectangle 240 | { 241 | Layout.alignment: Qt.AlignHCenter 242 | width: 200 243 | Layout.topMargin: 15 244 | TextField 245 | { 246 | id: captcha 247 | width:100 248 | height: 50 249 | leftPadding: 4 250 | placeholderText: qsTr("
验证码
") 251 | } 252 | Button 253 | { 254 | anchors.left: captcha.right 255 | anchors.leftMargin: 10 256 | id:sendEmail 257 | text: "发送验证码" 258 | onClicked: 259 | { 260 | if("" == usr.text) 261 | { 262 | noUsrName.open() 263 | } 264 | else 265 | { 266 | //"email=xxx.@xxxx.com&username=xxx" 267 | 268 | sendArg = qsTr("email=") + emailAddress.text + qsTr("&username=") +usr.text 269 | //发送验证码 270 | post("http://wetalk.funnysaltyfish.fun/send_verify_email",sendArg, 1 ) 271 | } 272 | } 273 | } 274 | } 275 | 276 | 277 | 278 | RoundButton 279 | { 280 | //注册,自动分配账号,并弹出message写出对应的账号和用户名,跳过登陆步骤进入主界面 281 | id: register 282 | Layout.topMargin: 50 283 | Layout.alignment: Qt.AlignHCenter 284 | text: qsTr("注册") 285 | flat: true 286 | 287 | onClicked: 288 | { 289 | usrName = usr.text 290 | usrPassword = password.text 291 | sendArg = qsTr("email=") + emailAddress.text + qsTr("&verify_code=") +captcha.text 292 | 293 | post("http://wetalk.funnysaltyfish.fun/verify_email",sendArg,0) 294 | 295 | 296 | 297 | if(passwordAgain.text != password.text) 298 | { 299 | console.log("psw not match.") 300 | wrongPSW.open() 301 | 302 | } 303 | else if(usrName == "") 304 | { 305 | console.log("no usr name") 306 | noUsrName.open() 307 | } 308 | else if(password.text == "") 309 | { 310 | console.log("no password") 311 | noPassword.open() 312 | } 313 | else 314 | { 315 | post("http://wetalk.funnysaltyfish.fun/verify_email",sendArg,0) 316 | 317 | } 318 | 319 | } 320 | 321 | anchors.verticalCenterOffset: 125 322 | } 323 | 324 | } 325 | Button 326 | { 327 | id: backToLogin 328 | anchors.bottom: parent.bottom 329 | anchors.horizontalCenter: parent.horizontalCenter 330 | text: "返回" 331 | onClicked: 332 | { 333 | loginWindows.show() 334 | close() 335 | } 336 | 337 | } 338 | 339 | 340 | } 341 | -------------------------------------------------------------------------------- /01.项目源码/client/client.pro: -------------------------------------------------------------------------------- 1 | QT += quick network 2 | 3 | CONFIG += c++11 4 | 5 | # The following define makes your compiler emit warnings if you use 6 | # any Qt feature that has been marked deprecated (the exact warnings 7 | # depend on your compiler). Refer to the documentation for the 8 | # deprecated API to know how to port your code away from it. 9 | DEFINES += QT_DEPRECATED_WARNINGS 10 | 11 | # You can also make your code fail to compile if it uses deprecated APIs. 12 | # In order to do so, uncomment the following line. 13 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 14 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 15 | 16 | SOURCES += \ 17 | main.cpp \ 18 | qqclient.cpp \ 19 | tcpclient.cpp \ 20 | tcpserver.cpp 21 | 22 | RESOURCES += qml.qrc 23 | 24 | # Additional import path used to resolve QML modules in Qt Creator's code model 25 | QML_IMPORT_PATH = 26 | 27 | # Additional import path used to resolve QML modules just for Qt Quick Designer 28 | QML_DESIGNER_IMPORT_PATH = 29 | 30 | # Default rules for deployment. 31 | qnx: target.path = /tmp/$${TARGET}/bin 32 | else: unix:!android: target.path = /opt/$${TARGET}/bin 33 | !isEmpty(target.path): INSTALLS += target 34 | 35 | DISTFILES += 36 | 37 | HEADERS += \ 38 | qfileutils.h \ 39 | qsettingini.h \ 40 | qqclient.h \ 41 | qsettingini.h \ 42 | tcpclient.h \ 43 | tcpserver.h 44 | -------------------------------------------------------------------------------- /01.项目源码/client/components/AddFriendWidget.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtGraphicalEffects 1.0 3 | import QtQuick.Controls.Material 2.0 4 | import "../" 5 | 6 | Rectangle 7 | { 8 | width: 40 9 | height: 40 10 | radius: width/2 11 | 12 | AddFriendWindow{id:addFriendWindow; visible: false} 13 | 14 | Image { 15 | id: addImg 16 | source: "../images/add.png" 17 | anchors.fill: parent 18 | MouseArea { 19 | anchors.fill: parent 20 | onClicked: { 21 | console.log("添加好友"); 22 | addFriendWindow.show() 23 | } 24 | hoverEnabled: true 25 | onHoveredChanged: 26 | { 27 | cursorShape = Qt.PointingHandCursor 28 | } 29 | } 30 | } 31 | ColorOverlay { 32 | anchors.fill: addImg 33 | source: addImg 34 | color: Material.primary 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /01.项目源码/client/components/AutoScrollTextEdit.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | Flickable { 4 | id: flick 5 | property int mWidth: 100 6 | property int mHeight: 50 7 | width: mWidth 8 | height: mHeight 9 | contentWidth: edit.paintedWidth 10 | contentHeight: edit.paintedHeight 11 | clip: true 12 | 13 | function ensureVisible(r) 14 | { 15 | if (contentX >= r.x) 16 | contentX = r.x; 17 | else if (contentX+width <= r.x+r.width) 18 | contentX = r.x+r.width-width; 19 | if (contentY >= r.y) 20 | contentY = r.y; 21 | else if (contentY+height <= r.y+r.height) 22 | contentY = r.y+r.height-height; 23 | } 24 | 25 | TextEdit { 26 | id: edit 27 | width: flick.width 28 | height: flick.height 29 | focus: true 30 | wrapMode: TextEdit.Wrap 31 | onCursorRectangleChanged: flick.ensureVisible(cursorRectangle) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /01.项目源码/client/components/EmojiChoose.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Window 2.2 3 | import QtQuick.Controls 2.0 4 | import QtQuick.Layouts 1.0 5 | 6 | 7 | Window { 8 | id:emojiChoose 9 | visible: false 10 | height: gridView.cellHeight*5+4*2 11 | width: gridView.cellWidth*12+4*2+5 12 | 13 | flags:(Qt.Window | Qt.CustomizeWindowHint) 14 | 15 | signal clickEmoji(string emojiText) 16 | 17 | 18 | Rectangle{ 19 | z:100 20 | id:closeButton 21 | width:10 22 | height:10 23 | radius: closeButton.width/2 24 | anchors.top: parent.top 25 | anchors.right: parent.right 26 | 27 | Rectangle{ 28 | width:15 29 | height:15 30 | radius: 15/2 31 | anchors{ 32 | right:closeButton.right 33 | top:closeButton.top 34 | } 35 | Text { 36 | id: name 37 | anchors.centerIn: parent 38 | text: qsTr("X") 39 | color:"#FFFFFF" 40 | font.pixelSize: 16 41 | } 42 | color:"green" 43 | MouseArea { 44 | anchors.fill: parent 45 | onClicked: { 46 | emojiChoose.visible = false 47 | } 48 | hoverEnabled: true 49 | onHoveredChanged: cursorShape = Qt.PointingHandCursor 50 | } 51 | } 52 | } 53 | 54 | ListModel { 55 | id: listModel 56 | } 57 | 58 | Component{ 59 | id: baseListDelegate 60 | 61 | Item { 62 | z:1 63 | id: delegateItem 64 | height: gridView.cellHeight 65 | width: gridView.cellWidth 66 | 67 | Rectangle{ 68 | id: delegateItemBack 69 | anchors.topMargin: 20 70 | anchors.fill: parent 71 | color : "#AAAAAA" 72 | 73 | MouseArea { 74 | anchors.fill: parent 75 | hoverEnabled: true 76 | onEntered: { 77 | delegateItem.GridView.view.currentIndex = model.index; 78 | } 79 | } 80 | 81 | Image { 82 | id: image 83 | anchors.centerIn: parent 84 | source: face 85 | width: 32 86 | height: 32 87 | MouseArea{ 88 | anchors.fill: parent 89 | onClicked: { 90 | console.debug(qsTr("选择的名称: ") + face) 91 | var addEmoji = ""; 92 | switch (face) 93 | { 94 | case ("qrc:/images/emoji/Face_(0).png"): 95 | addEmoji = ":)" 96 | break; 97 | case ("qrc:/images/emoji/Face_(1).png"): 98 | addEmoji = ":D" 99 | break; 100 | case ("qrc:/images/emoji/Face_(2).png"): 101 | addEmoji = "^_^" 102 | break; 103 | default: 104 | addEmoji = ":)" 105 | break; 106 | } 107 | //文本框中添加选择的表情内容 108 | clickEmoji(addEmoji); 109 | 110 | emojiChoose.visible = false 111 | } 112 | } 113 | } 114 | } 115 | 116 | states: [ 117 | State { 118 | name: "isCurrentItem" 119 | when: delegateItem.GridView.isCurrentItem 120 | PropertyChanges { 121 | target: delegateItemBack; color : "#AAAAAA"; 122 | } 123 | }, 124 | State { 125 | name: "isNotCurrentItem" 126 | when: !delegateItem.GridView.isCurrentItem 127 | PropertyChanges { 128 | target: delegateItemBack; color : "transparent"; 129 | } 130 | } 131 | ] 132 | } 133 | } 134 | 135 | GridView { 136 | id: gridView 137 | anchors.margins: 4 138 | anchors.fill: parent 139 | clip: true 140 | cellWidth: 40 141 | cellHeight: 40 142 | model: listModel 143 | delegate: baseListDelegate 144 | boundsBehavior: Flickable.StopAtBounds 145 | 146 | Component.onCompleted: { 147 | listModel.append({"face" : "qrc:/images/emoji/Face_(0).png"}) 148 | listModel.append({"face" : "qrc:/images/emoji/Face_(1).png"}) 149 | listModel.append({"face" : "qrc:/images/emoji/Face_(2).png"}) 150 | listModel.append({"face" : "qrc:/images/emoji/Face_(3).png"}) 151 | listModel.append({"face" : "qrc:/images/emoji/Face_(4).png"}) 152 | listModel.append({"face" : "qrc:/images/emoji/Face_(5).png"}) 153 | listModel.append({"face" : "qrc:/images/emoji/Face_(6).png"}) 154 | listModel.append({"face" : "qrc:/images/emoji/Face_(7).png"}) 155 | listModel.append({"face" : "qrc:/images/emoji/Face_(8).png"}) 156 | listModel.append({"face" : "qrc:/images/emoji/Face_(9).png"}) 157 | listModel.append({"face" : "qrc:/images/emoji/Face_(10).png"}) 158 | listModel.append({"face" : "qrc:/images/emoji/Face_(11).png"}) 159 | listModel.append({"face" : "qrc:/images/emoji/Face_(12).png"}) 160 | listModel.append({"face" : "qrc:/images/emoji/Face_(13).png"}) 161 | listModel.append({"face" : "qrc:/images/emoji/Face_(14).png"}) 162 | listModel.append({"face" : "qrc:/images/emoji/Face_(15).png"}) 163 | listModel.append({"face" : "qrc:/images/emoji/Face_(16).png"}) 164 | listModel.append({"face" : "qrc:/images/emoji/Face_(17).png"}) 165 | listModel.append({"face" : "qrc:/images/emoji/Face_(18).png"}) 166 | listModel.append({"face" : "qrc:/images/emoji/Face_(19).png"}) 167 | listModel.append({"face" : "qrc:/images/emoji/Face_(20).png"}) 168 | listModel.append({"face" : "qrc:/images/emoji/Face_(21).png"}) 169 | listModel.append({"face" : "qrc:/images/emoji/Face_(22).png"}) 170 | listModel.append({"face" : "qrc:/images/emoji/Face_(23).png"}) 171 | listModel.append({"face" : "qrc:/images/emoji/Face_(24).png"}) 172 | listModel.append({"face" : "qrc:/images/emoji/Face_(25).png"}) 173 | listModel.append({"face" : "qrc:/images/emoji/Face_(26).png"}) 174 | listModel.append({"face" : "qrc:/images/emoji/Face_(27).png"}) 175 | listModel.append({"face" : "qrc:/images/emoji/Face_(28).png"}) 176 | listModel.append({"face" : "qrc:/images/emoji/Face_(29).png"}) 177 | listModel.append({"face" : "qrc:/images/emoji/Face_(30).png"}) 178 | listModel.append({"face" : "qrc:/images/emoji/Face_(31).png"}) 179 | listModel.append({"face" : "qrc:/images/emoji/Face_(32).png"}) 180 | listModel.append({"face" : "qrc:/images/emoji/Face_(33).png"}) 181 | listModel.append({"face" : "qrc:/images/emoji/Face_(34).png"}) 182 | listModel.append({"face" : "qrc:/images/emoji/Face_(35).png"}) 183 | listModel.append({"face" : "qrc:/images/emoji/Face_(36).png"}) 184 | listModel.append({"face" : "qrc:/images/emoji/Face_(37).png"}) 185 | listModel.append({"face" : "qrc:/images/emoji/Face_(38).png"}) 186 | listModel.append({"face" : "qrc:/images/emoji/Face_(39).png"}) 187 | listModel.append({"face" : "qrc:/images/emoji/Face_(40).png"}) 188 | listModel.append({"face" : "qrc:/images/emoji/Face_(41).png"}) 189 | listModel.append({"face" : "qrc:/images/emoji/Face_(42).png"}) 190 | listModel.append({"face" : "qrc:/images/emoji/Face_(43).png"}) 191 | listModel.append({"face" : "qrc:/images/emoji/Face_(44).png"}) 192 | listModel.append({"face" : "qrc:/images/emoji/Face_(45).png"}) 193 | listModel.append({"face" : "qrc:/images/emoji/Face_(46).png"}) 194 | listModel.append({"face" : "qrc:/images/emoji/Face_(47).png"}) 195 | listModel.append({"face" : "qrc:/images/emoji/Face_(48).png"}) 196 | listModel.append({"face" : "qrc:/images/emoji/Face_(49).png"}) 197 | listModel.append({"face" : "qrc:/images/emoji/Face_(50).png"}) 198 | listModel.append({"face" : "qrc:/images/emoji/Face_(51).png"}) 199 | listModel.append({"face" : "qrc:/images/emoji/Face_(52).png"}) 200 | listModel.append({"face" : "qrc:/images/emoji/Face_(53).png"}) 201 | listModel.append({"face" : "qrc:/images/emoji/Face_(54).png"}) 202 | listModel.append({"face" : "qrc:/images/emoji/Face_(55).png"}) 203 | listModel.append({"face" : "qrc:/images/emoji/Face_(56).png"}) 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /01.项目源码/client/components/FriendList.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Window 2.2 3 | import QtQuick.Controls 2.5 4 | import QtQuick.Controls.Material 2.0 5 | import QtQuick.Layouts 1.3 6 | import QtQuick.Dialogs 1.3 7 | import QtQml 2.0 8 | 9 | Window { 10 | //与AddFriendWindow公用InfListItem和SearchWidget (不知道全局变量相同名字是否会影响赋值 11 | id: friendList 12 | width: 300 13 | height: 500 14 | visible: true 15 | title: qsTr("加好友/群") 16 | 17 | 18 | property string headImg: "https://c-ssl.dtstatic.com/uploads/blog/202203/25/20220325232426_17909.thumb.1000_0.jpeg" 19 | 20 | // 发送搜索信号 21 | signal sendSearch(string num) 22 | 23 | //接收搜索结果 json数组 24 | function receiveSearchResult(data) 25 | { 26 | var pData = data.pData 27 | var gData = data.gData 28 | 29 | } 30 | 31 | 32 | // 搜索栏 33 | 34 | SearchWidget 35 | { 36 | id: searchWidget 37 | Layout.fillWidth: true 38 | Layout.alignment: Qt.AlignHCenter 39 | anchors.horizontalCenter: parent.horizontalCenter 40 | anchors.top: parent.top 41 | anchors.topMargin: 20 42 | 43 | } 44 | Rectangle 45 | { 46 | id:searchResult 47 | width: parent.width 48 | height: parent.height 49 | anchors.top: searchWidget.bottom 50 | anchors.topMargin: 10 51 | anchors.left: parent.left 52 | anchors.leftMargin: 18 53 | anchors.right: parent.right 54 | anchors.rightMargin: 8 55 | 56 | Text { 57 | id: pInf 58 | text: qsTr("联系人") 59 | } 60 | Rectangle 61 | { 62 | id: pInfList 63 | anchors.top: pInf.bottom 64 | width: parent.width 65 | 66 | //无法计算listView当前含有内容=>对应应该展示的高度 67 | height: parent.height*0.4 68 | 69 | //好友搜索结果表 70 | ListModel { 71 | id: pInfModel 72 | ListElement { 73 | userName: "张三" 74 | userid: 123 75 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 76 | } 77 | ListElement { 78 | userName: "张三" 79 | userid: 123 80 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 81 | } 82 | ListElement { 83 | userName: "张三" 84 | userid: 123 85 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 86 | } 87 | ListElement { 88 | userName: "张三" 89 | userid: 123 90 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 91 | } 92 | ListElement { 93 | userName: "张三" 94 | userid: 123 95 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 96 | } 97 | } 98 | ListView { 99 | id: pInfListView 100 | anchors.fill: parent 101 | spacing: 60 102 | focus: true 103 | anchors.margins: 8 104 | 105 | 106 | model: pInfModel 107 | delegate:InfListItem 108 | { 109 | width: parent.width 110 | } 111 | 112 | //listView 内部滚动条设置 113 | orientation: ListView.Vertical//垂直列表 114 | clip: true 115 | ScrollBar.vertical: ScrollBar { 116 | id: scrollBar 117 | // policy: ScrollBar.AlwaysOn 118 | } 119 | flickableDirection: Flickable.VerticalFlick 120 | boundsBehavior: Flickable.StopAtBounds 121 | 122 | } 123 | 124 | 125 | 126 | 127 | Text { 128 | id: gInf 129 | 130 | anchors.top: pInfList.bottom 131 | anchors.topMargin: 0 132 | text: qsTr("群组") 133 | } 134 | 135 | Rectangle 136 | { 137 | id: gInfList 138 | anchors.top: gInf.bottom 139 | anchors.topMargin: 5 140 | width: parent.width 141 | 142 | //无法计算listView当前含有内容=>对应应该展示的高度 143 | height: parent.height*0.9 144 | // height: parent.height/3 145 | 146 | //好友搜索结果表 147 | ListModel { 148 | id: gInfModel 149 | ListElement { 150 | userName: "张三" 151 | userid: 123 152 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 153 | } 154 | ListElement { 155 | userName: "张三" 156 | userid: 123 157 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 158 | } 159 | ListElement { 160 | userName: "张三" 161 | userid: 123 162 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 163 | } 164 | ListElement { 165 | userName: "张三" 166 | userid: 123 167 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 168 | } 169 | ListElement { 170 | userName: "张三" 171 | userid: 123 172 | avatar: "https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg" 173 | } 174 | } 175 | ListView { 176 | id: gInfListView 177 | anchors.fill: parent 178 | spacing: 60 179 | 180 | 181 | focus: true 182 | anchors.margins: 8 183 | 184 | model: gInfModel 185 | delegate:InfListItem 186 | { 187 | width: parent.width 188 | } 189 | //listView 内部滚动条设置 190 | orientation: ListView.Vertical//垂直列表 191 | clip: true 192 | ScrollBar.vertical: ScrollBar { 193 | id: scrollBar2 194 | // policy: ScrollBar.AlwaysOn 195 | } 196 | flickableDirection: Flickable.VerticalFlick 197 | boundsBehavior: Flickable.StopAtBounds 198 | } 199 | } 200 | 201 | } 202 | } 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | } 226 | -------------------------------------------------------------------------------- /01.项目源码/client/components/GroupAddWidget.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Layouts 1.3 3 | import QtQuick.Controls 2.5 4 | import QtQuick.Dialogs 1.3 5 | 6 | 7 | // 搜索栏 8 | RowLayout 9 | { 10 | signal idpp(int idN) 11 | 12 | MessageDialog 13 | { 14 | id: noInputText 15 | title: "提示" 16 | icon: StandardIcon.Warning 17 | text: "请输入搜索内容" 18 | standardButtons: StandardButton.Cancel 19 | } 20 | MessageDialog 21 | { 22 | id: notNumInputText 23 | title: "提示" 24 | icon: StandardIcon.Warning 25 | text: "请输入数字" 26 | standardButtons: StandardButton.Cancel 27 | } 28 | 29 | 30 | Rectangle 31 | { 32 | id: imgSearch 33 | width: 16 34 | height: 16 35 | Image { 36 | Layout.preferredHeight: 16 37 | anchors.fill:imgSearch 38 | source: "qrc:/images/search.png" 39 | fillMode:Image.PreserveAspectFit 40 | } 41 | } 42 | 43 | 44 | TextField 45 | { 46 | id: inputID 47 | Layout.leftMargin: 12 48 | 49 | 50 | padding: 5 51 | placeholderText: qsTr("请输入好友账号") 52 | 53 | 54 | background: Rectangle 55 | { 56 | implicitWidth: 200 57 | implicitHeight: 10 58 | radius: implicitHeight/2 59 | border.color: "grey" 60 | 61 | } 62 | } 63 | 64 | 65 | RoundButton 66 | { 67 | // 搜索好友/群 68 | Layout.leftMargin: 5 69 | id: search 70 | text: qsTr("搜索") 71 | flat: true 72 | 73 | onClicked: 74 | { 75 | if("" == inputID.text) 76 | { 77 | noInputText.open() 78 | } 79 | else if(isNaN(inputID.text)) 80 | { 81 | console.log("输入非数字") 82 | notNumInputText.open() 83 | inputID.text = "" 84 | } 85 | else 86 | { 87 | //将该id压入数组内 88 | idpp(Number(inputID.text)) 89 | } 90 | } 91 | 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /01.项目源码/client/components/InfListItem.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Layouts 1.3 3 | import QtQuick.Controls 2.5 4 | import QtQuick.Dialogs 1.3 5 | 6 | Rectangle 7 | { 8 | 9 | width:parent.width 10 | 11 | MessageDialog 12 | {//不可搜索自身提示 13 | id: cantSearchMyself 14 | title: "提示" 15 | icon: StandardIcon.Warning 16 | text: "不可搜索自身" 17 | standardButtons: StandardButton.Cancel 18 | } 19 | 20 | RowLayout 21 | { 22 | Layout.fillWidth: true 23 | 24 | RoundImage 25 | { 26 | id: avatarImgP 27 | img_src: avatarImg 28 | 29 | width: 40 30 | height: avatarImgP.width 31 | color: "black" 32 | } 33 | Text 34 | { 35 | id: searchName 36 | Layout.leftMargin: 20 37 | text: userName + qsTr("(") + userid + qsTr(")") 38 | } 39 | Button 40 | { 41 | id: addFriendButton 42 | width: 20 43 | height: 20 44 | background: Item{ 45 | opacity:1 46 | } 47 | Layout.leftMargin: 70 48 | icon.source: addFriendButton.pressed? "qrc:/images/addFriend1.png": 49 | addFriendButton.hovered? "qrc:/images/addFriend1.png" : 50 | ("qrc:/images/addFriend.png") 51 | icon.color: "transparent" 52 | onClicked: 53 | { 54 | // 不可搜索自身id 55 | if(ClientInfo.id == userid) 56 | { 57 | cantSearchMyself.open() 58 | return 59 | } 60 | 61 | //发出好友(群聊)添加信号 62 | addSignal(userid); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /01.项目源码/client/components/RoundImage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtGraphicalEffects 1.0 3 | 4 | Rectangle { 5 | property string img_src 6 | radius: width / 2 7 | 8 | Image { 9 | id: _image 10 | smooth: true 11 | visible: false 12 | anchors.fill: parent 13 | source: img_src 14 | sourceSize: Qt.size(parent.size, parent.size) 15 | antialiasing: true 16 | } 17 | Rectangle { 18 | id: _mask 19 | color: "black" 20 | anchors.fill: parent 21 | radius: width / 2 22 | visible: false 23 | antialiasing: true 24 | smooth: true 25 | } 26 | OpacityMask { 27 | id: mask_image 28 | anchors.fill: _image 29 | source: _image 30 | maskSource: _mask 31 | visible: true 32 | antialiasing: true 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /01.项目源码/client/components/SearchWidget.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Layouts 1.3 3 | import QtQuick.Controls 2.5 4 | import QtQuick.Dialogs 1.3 5 | 6 | // 搜索栏 7 | RowLayout 8 | { 9 | // anchors.horizontalCenter: parent.horizontalCenter 10 | // anchors.top: parent.top 11 | // anchors.topMargin: 10 12 | 13 | 14 | MessageDialog 15 | { 16 | id: noInputText 17 | title: "提示" 18 | icon: StandardIcon.Warning 19 | text: "请输入搜索内容" 20 | standardButtons: StandardButton.Cancel 21 | } 22 | MessageDialog 23 | { 24 | id: notNumInputText 25 | title: "提示" 26 | icon: StandardIcon.Warning 27 | text: "请输入数字" 28 | standardButtons: StandardButton.Cancel 29 | } 30 | 31 | MessageDialog 32 | { 33 | id: cantAddMyself 34 | title: "提示" 35 | icon: StandardIcon.Warning 36 | text: "不能添加自己为好友" 37 | standardButtons: StandardButton.Cancel 38 | } 39 | 40 | Rectangle 41 | { 42 | id: imgSearch 43 | width: 16 44 | height: 16 45 | Image { 46 | Layout.preferredHeight: 16 47 | anchors.fill:imgSearch 48 | source: "qrc:/images/search.png" 49 | fillMode:Image.PreserveAspectFit 50 | } 51 | } 52 | 53 | 54 | TextField 55 | { 56 | id: inputID 57 | Layout.leftMargin: 12 58 | 59 | 60 | padding: 5 61 | placeholderText: qsTr("好友账号/群号/群名") 62 | 63 | 64 | background: Rectangle 65 | { 66 | implicitWidth: 200 67 | implicitHeight: 10 68 | radius: implicitHeight/2 69 | border.color: "grey" 70 | 71 | } 72 | } 73 | 74 | 75 | RoundButton 76 | { 77 | // 搜索好友/群 78 | Layout.leftMargin: 5 79 | id: search 80 | text: qsTr("搜索") 81 | flat: true 82 | 83 | onClicked: 84 | { 85 | if("" == inputID.text) 86 | { 87 | noInputText.open() 88 | } 89 | else if(isNaN(inputID.text)) 90 | { 91 | notNumInputText.open() 92 | inputID.text = "" 93 | } 94 | else if(ClientInfo.id == Number(inputID.text)) 95 | { 96 | cantAddMyself.open() 97 | inputID.text = "" 98 | } 99 | 100 | else 101 | { 102 | //将搜索内容发送 103 | userid = Number(inputID.text) 104 | searchSignal(inputID.text) 105 | console.log("search") 106 | console.log(inputID.text) 107 | 108 | } 109 | 110 | 111 | } 112 | 113 | } 114 | } 115 | 116 | -------------------------------------------------------------------------------- /01.项目源码/client/components/SelectAvatar.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Window 2.2 3 | import QtQuick.Controls 2.0 4 | import QtQuick.Layouts 1.0 5 | 6 | Window { 7 | id:selectAvatar 8 | width: 350 9 | height:260 10 | visible: false 11 | 12 | 13 | ColumnLayout 14 | { 15 | anchors.fill: parent 16 | Layout.margins: 15 17 | Layout.alignment: Qt.AlignCenter 18 | 19 | RowLayout 20 | { 21 | Layout.fillWidth: true 22 | spacing: 20 23 | Rectangle 24 | { 25 | width: 100 26 | height: 100 27 | Image { 28 | id: img1 29 | source: "https://c-ssl.dtstatic.com/uploads/item/201403/08/20140308212425_n8JsB.thumb.1000_0.jpeg" 30 | anchors.fill: parent 31 | MouseArea 32 | { 33 | anchors.fill: parent 34 | acceptedButtons: Qt.LeftButton 35 | onClicked: 36 | { 37 | personalHead = img1.source 38 | selectAvatar.visible = false 39 | } 40 | } 41 | } 42 | } 43 | Rectangle 44 | { 45 | width: 100 46 | height: 100 47 | Image { 48 | id: img2 49 | source: "https://c-ssl.duitang.com/uploads/blog/201408/15/20140815095903_ttcnF.jpeg" 50 | anchors.fill: parent 51 | MouseArea 52 | { 53 | anchors.fill: parent 54 | acceptedButtons: Qt.LeftButton 55 | onClicked: 56 | { 57 | personalHead = img2.source 58 | selectAvatar.visible = false 59 | } 60 | } 61 | } 62 | } 63 | Rectangle 64 | { 65 | width: 100 66 | height: 100 67 | Image { 68 | id: img3 69 | source: "https://c-ssl.dtstatic.com/uploads/item/201403/08/20140308212431_eFs8Y.thumb.1000_0.jpeg" 70 | anchors.fill: parent 71 | MouseArea 72 | { 73 | anchors.fill: parent 74 | acceptedButtons: Qt.LeftButton 75 | onClicked: 76 | { 77 | personalHead = img3.source 78 | selectAvatar.visible = false 79 | } 80 | } 81 | } 82 | } 83 | 84 | 85 | } 86 | 87 | RowLayout 88 | { 89 | Layout.fillWidth: true 90 | spacing: 20 91 | Rectangle 92 | { 93 | width: 100 94 | height: 100 95 | Image { 96 | id: img4 97 | source: "https://c-ssl.dtstatic.com/uploads/item/201403/08/20140308212528_ymkrF.thumb.1000_0.jpeg" 98 | anchors.fill: parent 99 | MouseArea 100 | { 101 | anchors.fill: parent 102 | acceptedButtons: Qt.LeftButton 103 | onClicked: 104 | { 105 | personalHead = img4.source 106 | selectAvatar.visible = false 107 | } 108 | } 109 | } 110 | } 111 | Rectangle 112 | { 113 | width: 100 114 | height: 100 115 | Image { 116 | id: img5 117 | source: "https://c-ssl.dtstatic.com/uploads/blog/201408/14/20140814181135_wNBzN.thumb.1000_0.jpeg" 118 | anchors.fill: parent 119 | MouseArea 120 | { 121 | anchors.fill: parent 122 | acceptedButtons: Qt.LeftButton 123 | onClicked: 124 | { 125 | personalHead = img5.source 126 | selectAvatar.visible = false 127 | } 128 | } 129 | } 130 | } 131 | Rectangle 132 | { 133 | width: 100 134 | height: 100 135 | Image { 136 | id: img6 137 | source: "https://c-ssl.dtstatic.com/uploads/item/201403/08/20140308212511_LYudw.thumb.1000_0.jpeg" 138 | anchors.fill: parent 139 | MouseArea 140 | { 141 | anchors.fill: parent 142 | acceptedButtons: Qt.LeftButton 143 | onClicked: 144 | { 145 | personalHead = img6.source 146 | selectAvatar.visible = false 147 | } 148 | } 149 | } 150 | } 151 | 152 | 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /01.项目源码/client/components/VerticalTabWidget.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.5 3 | import QtGraphicalEffects 1.0 4 | import QtQuick.Controls.Material 2.3 5 | import QtQuick.Layouts 1.3 6 | Item { 7 | id: tabWidget 8 | 9 | // Setting the default property to stack.children means any child items 10 | // of the TabWidget are actually added to the 'stack' item's children. 11 | // See the "Property Binding" 12 | // documentation for details on default properties. 13 | default property alias content: stack.children 14 | //property list content 15 | 16 | property int current: 0 17 | 18 | property var icons: ["../images/icon_chat.png", "../images/icon_contact.png"] 19 | 20 | signal changeToContact() 21 | 22 | onCurrentChanged: stack.replace(null, content[current]) 23 | 24 | 25 | Column { 26 | id: side 27 | anchors.top: parent.top 28 | anchors.bottom: parent.bottom 29 | 30 | Repeater { 31 | model: icons.length 32 | delegate: Rectangle { 33 | height: 56; width: 56 34 | color: tabWidget.current == index ? "white" : "transparent" 35 | radius: 8 36 | 37 | Image { 38 | id: img 39 | source: icons[index] 40 | width: 32 41 | height: 32 42 | anchors.centerIn: parent 43 | } 44 | 45 | ColorOverlay { 46 | anchors.fill: img 47 | source: img 48 | color: tabWidget.current == index ? Material.primary : "black" 49 | } 50 | 51 | MouseArea { 52 | anchors.fill: parent 53 | onClicked: { 54 | tabWidget.current = index 55 | if (1 == index)//联系人界面 56 | { 57 | changeToContact() 58 | } 59 | } 60 | } 61 | } 62 | } 63 | } 64 | 65 | StackView { 66 | id: stack 67 | height: tabWidget.height 68 | anchors.left: side.right; anchors.right: tabWidget.right 69 | // anchors.fill: parent 70 | anchors.leftMargin: 12 71 | initialItem: content[0] 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /01.项目源码/client/images/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/add.png -------------------------------------------------------------------------------- /01.项目源码/client/images/add1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/add1.png -------------------------------------------------------------------------------- /01.项目源码/client/images/addFriend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/addFriend.png -------------------------------------------------------------------------------- /01.项目源码/client/images/addFriend1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/addFriend1.png -------------------------------------------------------------------------------- /01.项目源码/client/images/addGroup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/addGroup.png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji.png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(0).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(0).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(1).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(10).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(10).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(11).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(11).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(12).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(12).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(13).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(13).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(14).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(14).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(15).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(15).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(16).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(16).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(17).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(17).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(18).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(18).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(19).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(19).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(2).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(2).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(20).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(20).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(21).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(21).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(22).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(22).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(23).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(23).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(24).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(24).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(25).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(25).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(26).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(26).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(27).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(27).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(28).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(28).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(29).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(29).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(3).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(3).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(30).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(30).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(31).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(31).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(32).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(32).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(33).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(33).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(34).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(34).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(35).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(35).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(36).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(36).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(37).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(37).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(38).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(38).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(39).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(39).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(4).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(4).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(40).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(40).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(41).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(41).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(42).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(42).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(43).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(43).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(44).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(44).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(45).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(45).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(46).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(46).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(47).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(47).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(48).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(48).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(49).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(49).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(5).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(5).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(50).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(50).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(51).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(51).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(52).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(52).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(53).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(53).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(54).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(54).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(55).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(55).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(56).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(56).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(6).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(6).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(7).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(7).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(8).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(8).png -------------------------------------------------------------------------------- /01.项目源码/client/images/emoji/Face_(9).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/emoji/Face_(9).png -------------------------------------------------------------------------------- /01.项目源码/client/images/icon_add_contact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/icon_add_contact.png -------------------------------------------------------------------------------- /01.项目源码/client/images/icon_chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/icon_chat.png -------------------------------------------------------------------------------- /01.项目源码/client/images/icon_contact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/icon_contact.png -------------------------------------------------------------------------------- /01.项目源码/client/images/icon_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/icon_file.png -------------------------------------------------------------------------------- /01.项目源码/client/images/icon_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/icon_folder.png -------------------------------------------------------------------------------- /01.项目源码/client/images/message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/message.png -------------------------------------------------------------------------------- /01.项目源码/client/images/message1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/message1.png -------------------------------------------------------------------------------- /01.项目源码/client/images/picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/picture.png -------------------------------------------------------------------------------- /01.项目源码/client/images/rightArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/rightArrow.png -------------------------------------------------------------------------------- /01.项目源码/client/images/rightArrow1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/rightArrow1.png -------------------------------------------------------------------------------- /01.项目源码/client/images/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/client/images/search.png -------------------------------------------------------------------------------- /01.项目源码/client/js/config_utils.js: -------------------------------------------------------------------------------- 1 | .pragma library 2 | Qt.include("file_utils.js") 3 | 4 | const BASE_FILE_URL = "file://./config/" 5 | let CONFIG_USER_INFO = {} 6 | 7 | function test(){ 8 | saveFile(BASE_FILE_URL + "test.txt", "测试一下"); 9 | readFile(BASE_FILE_URL + "test.txt") 10 | }; 11 | 12 | 13 | -------------------------------------------------------------------------------- /01.项目源码/client/js/file_utils.js: -------------------------------------------------------------------------------- 1 | .pragma library 2 | 3 | /** 4 | * this function is copied from planets demo of qt version of threejs 5 | * I modified some of it, now it works fine for me 6 | **/ 7 | function readFile(url, onLoad, onProgress, onError) { 8 | var request = new XMLHttpRequest(); 9 | 10 | request.onreadystatechange = function() { 11 | if (request.readyState === XMLHttpRequest.DONE) { 12 | // TODO: Re-visit https://bugreports.qt.io/browse/QTBUG-45581 is solved in Qt 13 | if (request.status === 200 || request.status === 0) { 14 | console.time('Process file: "' + url + '"'); 15 | onLoad( request.responseText ); 16 | console.log("responseText: " + request.responseText); 17 | console.timeEnd('Process file: "' + url + '"'); 18 | } 19 | else if ( onError !== undefined ) { 20 | onError(); 21 | } 22 | } 23 | else if (request.readyState === XMLHttpRequest.HEADERS_RECEIVED) { 24 | if ( onProgress !== undefined ) { 25 | onProgress(); 26 | } 27 | } 28 | }; 29 | 30 | request.open( 'GET', url, true ); 31 | request.send( null ); 32 | } 33 | 34 | function saveFile(url, text, onLoad, onProgress, onError) { 35 | var request = new XMLHttpRequest(); 36 | 37 | request.onreadystatechange = function() { 38 | if (request.readyState === XMLHttpRequest.DONE) { 39 | // TODO: Re-visit https://bugreports.qt.io/browse/QTBUG-45581 is solved in Qt 40 | if (request.status === 200 || request.status === 0) { 41 | console.time('Process file: "' + url + '"'); 42 | onLoad( request.responseText ); 43 | console.log("responseText: " + request.responseText); 44 | console.timeEnd('Process file: "' + url + '"'); 45 | } 46 | else if ( onError !== undefined ) { 47 | onError(); 48 | } 49 | } 50 | else if (request.readyState === XMLHttpRequest.HEADERS_RECEIVED) { 51 | if ( onProgress !== undefined ) { 52 | onProgress(); 53 | } 54 | } 55 | }; 56 | 57 | request.open( 'PUT', url, true ); 58 | request.send( text ); 59 | } 60 | -------------------------------------------------------------------------------- /01.项目源码/client/js/time_utils.js: -------------------------------------------------------------------------------- 1 | .pragma library 2 | 3 | const NOW_TIME = new Date(); 4 | 5 | Date.prototype.format = function(fmt) { 6 | var o = { 7 | "M+" : this.getMonth()+1, //月份 8 | "d+" : this.getDate(), //日 9 | "h+" : this.getHours(), //小时 10 | "m+" : this.getMinutes(), //分 11 | "s+" : this.getSeconds(), //秒 12 | "q+" : Math.floor((this.getMonth()+3)/3), //季度 13 | "S" : this.getMilliseconds() //毫秒 14 | }; 15 | if(/(y+)/.test(fmt)) { 16 | fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length)); 17 | } 18 | for(var k in o) { 19 | if(new RegExp("("+ k +")").test(fmt)){ 20 | fmt = fmt.replace(RegExp.$1, (RegExp.$1.length===1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length))); 21 | } 22 | } 23 | return fmt; 24 | } 25 | 26 | 27 | 28 | function getTimeString(time){ 29 | let now_day = NOW_TIME.getDate(); 30 | let this_day = time.getDate(); 31 | if(this_day===now_day){ 32 | return time.format("hh:mm"); 33 | }else if(this_day===now_day-1){ 34 | return "昨天"; 35 | }else{ 36 | return time.format("MM-dd"); 37 | // return this.time.getFullYear()+"/"+this.time.getMonth()+"/"+this_day; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /01.项目源码/client/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "qsettingini.h" 6 | #include "qfileutils.h" 7 | #include "qqclient.h" 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 12 | 13 | QGuiApplication app(argc, argv); 14 | 15 | QSettingIni qSettingIni("config.ini"); 16 | QFileUtils qFileUtils; 17 | ClientInfo clientInfo; 18 | 19 | QQmlApplicationEngine engine; 20 | 21 | QQmlContext* root = engine.rootContext(); 22 | //engine.rootContext()->setContextProperty("QQClient",); 23 | 24 | 25 | root->setContextProperty("Config", &qSettingIni); 26 | root->setContextProperty("QFileUtils", &qFileUtils); 27 | root->setContextProperty("ClientInfo", &clientInfo); 28 | 29 | const QUrl url(QStringLiteral("qrc:/LoginWindow.qml")); 30 | QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, 31 | &app, [url](QObject *obj, const QUrl &objUrl) { 32 | if (!obj && url == objUrl) 33 | QCoreApplication::exit(-1); 34 | }, Qt::QueuedConnection); 35 | engine.load(url); 36 | //C++后台通讯 37 | QQClient* qqClient=new QQClient(&engine,&app); 38 | engine.rootContext()->setContextProperty("QQClient",qqClient); 39 | 40 | return app.exec(); 41 | } 42 | -------------------------------------------------------------------------------- /01.项目源码/client/qfileutils.h: -------------------------------------------------------------------------------- 1 | #ifndef QFILEUTILS_H 2 | #define QFILEUTILS_H 3 | 4 | #include "QObject" 5 | #include "QFile" 6 | 7 | class QFileUtils: public QObject { 8 | Q_OBJECT 9 | public: 10 | QFileUtils(){} 11 | Q_INVOKABLE qint64 getFileSize(QString filePath){ 12 | QFile* file = new QFile(filePath); 13 | return file->size(); 14 | } 15 | }; 16 | 17 | #endif // QFILEUTILS_H 18 | -------------------------------------------------------------------------------- /01.项目源码/client/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow.qml 4 | ChatScreen.qml 5 | qtquickcontrols2.conf 6 | FileWidget.qml 7 | ChatListItem.qml 8 | images/icon_file.png 9 | HistoryMessageScreen.qml 10 | HistoryMessageListItem.qml 11 | images/icon_chat.png 12 | images/icon_contact.png 13 | images/icon_folder.png 14 | components/VerticalTabWidget.qml 15 | components/AutoScrollTextEdit.qml 16 | components/RoundImage.qml 17 | js/time_utils.js 18 | LoginWindow.qml 19 | RegisterWindow.qml 20 | images/rightArrow.png 21 | images/rightArrow1.png 22 | images/message.png 23 | images/message1.png 24 | GroupInf.qml 25 | PersonalInf.qml 26 | js/file_utils.js 27 | js/config_utils.js 28 | images/addFriend.png 29 | images/addFriend1.png 30 | images/search.png 31 | components/InfListItem.qml 32 | components/SearchWidget.qml 33 | components/FriendList.qml 34 | AddFriendWindow.qml 35 | ContactScreen.qml 36 | images/add.png 37 | images/emoji.png 38 | images/picture.png 39 | components/EmojiChoose.qml 40 | images/emoji/Face_(0).png 41 | images/emoji/Face_(1).png 42 | images/emoji/Face_(10).png 43 | images/emoji/Face_(11).png 44 | images/emoji/Face_(12).png 45 | images/emoji/Face_(13).png 46 | images/emoji/Face_(14).png 47 | images/emoji/Face_(15).png 48 | images/emoji/Face_(16).png 49 | images/emoji/Face_(17).png 50 | images/emoji/Face_(18).png 51 | images/emoji/Face_(19).png 52 | images/emoji/Face_(2).png 53 | images/emoji/Face_(20).png 54 | images/emoji/Face_(21).png 55 | images/emoji/Face_(22).png 56 | images/emoji/Face_(23).png 57 | images/emoji/Face_(24).png 58 | images/emoji/Face_(25).png 59 | images/emoji/Face_(26).png 60 | images/emoji/Face_(27).png 61 | images/emoji/Face_(28).png 62 | images/emoji/Face_(29).png 63 | images/emoji/Face_(3).png 64 | images/emoji/Face_(30).png 65 | images/emoji/Face_(31).png 66 | images/emoji/Face_(32).png 67 | images/emoji/Face_(33).png 68 | images/emoji/Face_(34).png 69 | images/emoji/Face_(35).png 70 | images/emoji/Face_(36).png 71 | images/emoji/Face_(37).png 72 | images/emoji/Face_(38).png 73 | images/emoji/Face_(39).png 74 | images/emoji/Face_(4).png 75 | images/emoji/Face_(40).png 76 | images/emoji/Face_(41).png 77 | images/emoji/Face_(42).png 78 | images/emoji/Face_(43).png 79 | images/emoji/Face_(44).png 80 | images/emoji/Face_(45).png 81 | images/emoji/Face_(46).png 82 | images/emoji/Face_(47).png 83 | images/emoji/Face_(48).png 84 | images/emoji/Face_(49).png 85 | images/emoji/Face_(5).png 86 | images/emoji/Face_(50).png 87 | images/emoji/Face_(51).png 88 | images/emoji/Face_(52).png 89 | images/emoji/Face_(53).png 90 | images/emoji/Face_(54).png 91 | images/emoji/Face_(55).png 92 | images/emoji/Face_(56).png 93 | images/emoji/Face_(6).png 94 | images/emoji/Face_(7).png 95 | images/emoji/Face_(8).png 96 | images/emoji/Face_(9).png 97 | images/add1.png 98 | components/AddFriendWidget.qml 99 | images/icon_add_contact.png 100 | components/SelectAvatar.qml 101 | images/addGroup.png 102 | CreateGroup.qml 103 | components/GroupAddWidget.qml 104 | 105 | 106 | -------------------------------------------------------------------------------- /01.项目源码/client/qqclient.h: -------------------------------------------------------------------------------- 1 | #ifndef QQCLIENT_H 2 | #define QQCLIENT_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class ClientInfo: public QObject//储存当前客户端用户 15 | { 16 | Q_OBJECT 17 | Q_PROPERTY(int id READ id WRITE setId NOTIFY idChanged) 18 | Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) 19 | Q_PROPERTY(QString avatar READ avatar WRITE setAvatar NOTIFY avatarChanged) 20 | 21 | public: 22 | ClientInfo(){} 23 | ClientInfo(QJsonObject infoObj) 24 | { 25 | parse(infoObj); 26 | } 27 | 28 | int mId=0; 29 | QString mName="默认昵称"; 30 | QString mAvatar="https://www.com8.cn/wp-content/uploads/2020/08/20200823052248-5f41fd28d49e4.jpg"; 31 | 32 | int id(){return mId;}; 33 | void setId(int id){if(id!=mId){ 34 | mId = id; 35 | emit idChanged(); 36 | }} 37 | 38 | QString name(){return mName;} 39 | void setName(QString name){if(name != mName){ 40 | mName = name; 41 | emit nameChanged(); 42 | } 43 | } 44 | 45 | QString avatar(){return mAvatar;} 46 | void setAvatar(QString avatar){if(avatar != mAvatar){ 47 | mAvatar = avatar; 48 | emit avatarChanged(); 49 | } 50 | } 51 | 52 | void parse(QJsonObject infoObj) 53 | { 54 | mId=infoObj["id"].toInt(); 55 | mName=infoObj["name"].toString(); 56 | mAvatar=infoObj["icon"].toString(); 57 | qDebug()<<"ClientInfo"<id=id; 72 | this->name=name; 73 | this->icon=icon; 74 | qDebug()<<"构建User:"<id=id; 88 | this->name=name; 89 | this->icon=icon; 90 | this->intro=intro; 91 | this->notice=notice; 92 | qDebug()<<"构建Group:"< friendList;//储存好友列表 145 | QList groupList;//储存群组列表 146 | //前端通信engine 147 | QQmlApplicationEngine* engine=NULL; 148 | QObject* root=NULL; 149 | //基本通信方法 150 | void on_udpSocket_readyRead(); 151 | void sendMessage(QString content,QString ip,QString port);//不同参数类型的的发送信息 152 | void sendMessage(QString content,QHostAddress ip,quint16 port); 153 | void sendMessage(QByteArray content,QHostAddress ip,quint16 port); 154 | void sendMessage(QByteArray content,QString ip,QString port); 155 | //数据包通信解析 156 | void parseCommand(QString jsonStr,QHostAddress ip,quint16 port); 157 | //响应函数:C++到QML的函数 158 | void registerBack(QJsonObject obj); 159 | void loginBack(QJsonObject obj); 160 | void sendChatMessageBack(QJsonObject obj); 161 | void searchBack(QJsonObject obj); 162 | void addBack(QJsonObject obj); 163 | void createGroupBack(QJsonObject obj); 164 | void deleteBack(QJsonObject obj); 165 | void friendBack(QJsonObject obj);//请求好友列表(从服务端拉取数据的系列函数) 166 | void messageBack(QJsonObject obj);//请求历史聊天记录 167 | void groupBack(QJsonObject obj);//请求群列表 168 | void personInfoBack(QJsonObject obj);//某人信息 169 | void groupInfoBack(QJsonObject obj);//某群信息 170 | //其他函数 171 | 172 | }; 173 | 174 | 175 | 176 | #endif // QQCLIENT_H 177 | -------------------------------------------------------------------------------- /01.项目源码/client/qsettingini.h: -------------------------------------------------------------------------------- 1 | #ifndef QREADINI_H 2 | #define QREADINI_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class QSettingIni: public QObject 11 | { 12 | Q_OBJECT 13 | public: 14 | QSettingIni(QString fileName){ 15 | qSettings = new QSettings(fileName, QSettings::IniFormat); 16 | } 17 | 18 | Q_INVOKABLE void write(QString key, QVariant value){ 19 | qSettings->setValue(key, value); 20 | }; 21 | 22 | Q_INVOKABLE QVariant read(QString key, QVariant defaultValue){ 23 | return qSettings->value(key, defaultValue); 24 | }; 25 | 26 | private: 27 | QSettings* qSettings; 28 | }; 29 | 30 | 31 | #endif // QREADINI_H 32 | -------------------------------------------------------------------------------- /01.项目源码/client/qtquickcontrols2.conf: -------------------------------------------------------------------------------- 1 | ; This file can be edited to change the style of the application 2 | ; Read "Qt Quick Controls 2 Configuration File" for details: 3 | ; http://doc.qt.io/qt-5/qtquickcontrols2-configuration.html 4 | 5 | [Controls] 6 | Style=Material 7 | 8 | [Material] 9 | Theme=Light 10 | Accent=Teal 11 | Primary=Blue 12 | -------------------------------------------------------------------------------- /01.项目源码/client/tcpclient.cpp: -------------------------------------------------------------------------------- 1 | #include "tcpclient.h" 2 | 3 | TcpClient::TcpClient(QObject *parent) :QThread(parent) 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /01.项目源码/client/tcpclient.h: -------------------------------------------------------------------------------- 1 | #ifndef TCPCLIENT_H 2 | #define TCPCLIENT_H 3 | 4 | #include 5 | #include 6 | 7 | class TcpClient : public QThread 8 | { 9 | Q_OBJECT 10 | public: 11 | explicit TcpClient(QObject *parent = nullptr); 12 | 13 | signals: 14 | 15 | }; 16 | 17 | #endif // TCPCLIENT_H 18 | -------------------------------------------------------------------------------- /01.项目源码/client/tcpserver.cpp: -------------------------------------------------------------------------------- 1 | #include "tcpserver.h" 2 | 3 | TcpServer::TcpServer(QString filePath,int targetId,QString time,QObject *parent) : QThread(parent) 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /01.项目源码/client/tcpserver.h: -------------------------------------------------------------------------------- 1 | #ifndef TCPSERVER_H 2 | #define TCPSERVER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class TcpServer : public QThread 10 | { 11 | Q_OBJECT 12 | public: 13 | explicit TcpServer(QString filePath,int targetId,QString time,QObject *parent = nullptr); 14 | 15 | signals: 16 | 17 | private: 18 | //套接字 19 | QTcpServer *tcpServer; 20 | QTcpSocket *tcpSocket; 21 | //文件相关 22 | 23 | 24 | }; 25 | 26 | #endif // TCPSERVER_H 27 | -------------------------------------------------------------------------------- /01.项目源码/server/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/01.项目源码/server/.DS_Store -------------------------------------------------------------------------------- /01.项目源码/server/QQServer.pro: -------------------------------------------------------------------------------- 1 | QT += core gui network sql 2 | 3 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 4 | 5 | CONFIG += c++11 6 | 7 | # The following define makes your compiler emit warnings if you use 8 | # any Qt feature that has been marked deprecated (the exact warnings 9 | # depend on your compiler). Please consult the documentation of the 10 | # deprecated API in order to know how to port your code away from it. 11 | DEFINES += QT_DEPRECATED_WARNINGS 12 | 13 | # You can also make your code fail to compile if it uses deprecated APIs. 14 | # In order to do so, uncomment the following line. 15 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 16 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 17 | 18 | SOURCES += \ 19 | main.cpp \ 20 | qqserver.cpp \ 21 | sqlaccountmodel.cpp \ 22 | sqlfriendmodel.cpp \ 23 | sqlgroupmodel.cpp \ 24 | tcpclient.cpp \ 25 | tcpserver.cpp 26 | 27 | HEADERS += \ 28 | ../../Tcp-Version/QQServer/onlinelist.h \ 29 | qqserver.h \ 30 | sqlaccountmodel.h \ 31 | sqlfriendmodel.h \ 32 | sqlgroupmodel.h \ 33 | tcpclient.h \ 34 | tcpserver.h 35 | 36 | FORMS += \ 37 | qqserver.ui 38 | 39 | # Default rules for deployment. 40 | qnx: target.path = /tmp/$${TARGET}/bin 41 | else: unix:!android: target.path = /opt/$${TARGET}/bin 42 | !isEmpty(target.path): INSTALLS += target 43 | -------------------------------------------------------------------------------- /01.项目源码/server/main.cpp: -------------------------------------------------------------------------------- 1 | #include "qqserver.h" 2 | 3 | #include 4 | 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | //正经代码 9 | QApplication a(argc, argv); 10 | QQServer w; 11 | w.show(); 12 | return a.exec(); 13 | } 14 | -------------------------------------------------------------------------------- /01.项目源码/server/onlinelist.cpp: -------------------------------------------------------------------------------- 1 | #include "onlinelist.h" 2 | 3 | -------------------------------------------------------------------------------- /01.项目源码/server/onlinelist.h: -------------------------------------------------------------------------------- 1 | #ifndef ONLINELIST_H 2 | #define ONLINELIST_H 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | 9 | 10 | 11 | 12 | #endif // ONLINELIST_H 13 | -------------------------------------------------------------------------------- /01.项目源码/server/qqserver.h: -------------------------------------------------------------------------------- 1 | #ifndef QQSERVER_H 2 | #define QQSERVER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include //默认toJson是utf-8 9 | #include 10 | #include 11 | #include "sqlaccountmodel.h" 12 | #include "sqlfriendmodel.h" 13 | #include "sqlgroupmodel.h" 14 | #include "onlinelist.h" 15 | #include 16 | 17 | QT_BEGIN_NAMESPACE 18 | namespace Ui { class QQServer; } 19 | QT_END_NAMESPACE 20 | 21 | class User //在线成员的节点 22 | { 23 | public: 24 | User(QHostAddress ip,quint16 port,int id) //把名字和IP PORT绑定。 25 | { 26 | this->id=id; 27 | this->ip=ip; 28 | this->port=port; 29 | } 30 | ~User(); 31 | 32 | int id; 33 | QHostAddress ip; 34 | quint16 port; 35 | }; 36 | 37 | class Group //一个群数据 38 | { 39 | public: 40 | Group(int gId,SqlGroupModel* gpModel) 41 | { 42 | QJsonObject obj=QJsonDocument::fromJson(gpModel->memberList(gId)).object(); 43 | QJsonArray list=obj["list"].toArray(); 44 | for(int i=0;i memberList; 60 | }; 61 | 62 | class QQServer : public QMainWindow 63 | { 64 | Q_OBJECT 65 | 66 | public: 67 | QQServer(QWidget *parent = nullptr); 68 | ~QQServer(); 69 | 70 | 71 | private slots: 72 | //通用收信息函数 73 | void onUdpSocketReadyRead(); 74 | 75 | private: 76 | Ui::QQServer *ui; 77 | //Socket 78 | QUdpSocket* udpSocket=NULL; 79 | //在线用户和群链表 80 | QList onlineUser; 81 | QList groupList; 82 | QJsonObject getTargetIpPort(int targetId); 83 | //数据库操作 84 | SqlAccountModel *atModel; 85 | SqlFriendModel *fdModel; 86 | SqlGroupModel * gpModel; 87 | //数据包通信解析 88 | void parseCommand(QString jsonStr,QHostAddress ip,quint16 port); 89 | //不同参数类型的的发送信息 90 | void sendMessage(QString content,QString ip,QString port); 91 | void sendMessage(QString content,QHostAddress ip,quint16 port); 92 | void sendMessage(QByteArray content,QHostAddress ip,quint16 port); 93 | void sendMessage(QByteArray content,QString ip,QString port); 94 | //对客户端请求响应 95 | void registerRespond(QJsonObject obj,QHostAddress ip,quint16 port);//注册 96 | void loginRespond(QJsonObject obj,QHostAddress ip,quint16 port);//登录 97 | void sendChatMessageRespond(QJsonObject obj,QHostAddress ip,quint16 port);//发消息(单发+群发) 98 | void searchRespond(QJsonObject obj,QHostAddress ip,quint16 port);//查找(好友或群) 99 | void addRespond(QJsonObject obj,QHostAddress ip,quint16 port);//添加(好友或群) 100 | void createGroupRespond(QJsonObject obj,QHostAddress ip,quint16 port);//创建群聊 101 | void deleteRespond(QJsonObject obj,QHostAddress ip,quint16 port);//删除(好友或群) 102 | void friendRespond(QJsonObject obj,QHostAddress ip,quint16 port);//好友列表 103 | void groupRespond(QJsonObject obj,QHostAddress ip,quint16 port);//群列表 104 | void messageRespond(QJsonObject obj,QHostAddress ip,quint16 port);//历史消息 105 | void getPersonalInfoRespond(QJsonObject obj, QHostAddress ip, quint16 port);//获取个人信息 106 | void getGroupInfoRespond(QJsonObject obj, QHostAddress ip, quint16 port);//获取群信息 107 | void changePInfo(QJsonObject obj, QHostAddress ip, quint16 port);//更改用户信息 108 | void changeGInfo(QJsonObject obj, QHostAddress ip, quint16 port);//更改群聊信息 109 | //测试代码 110 | void test(SqlAccountModel* atModel,SqlFriendModel* fdModel,SqlGroupModel* gpModel); 111 | 112 | }; 113 | #endif // QQSERVER_H 114 | -------------------------------------------------------------------------------- /01.项目源码/server/qqserver.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | QQServer 4 | 5 | 6 | 7 | 0 8 | 0 9 | 709 10 | 560 11 | 12 | 13 | 14 | QQServer 15 | 16 | 17 | 18 | 19 | 20 | 5 21 | 1 22 | 691 23 | 501 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 0 32 | 0 33 | 709 34 | 21 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /01.项目源码/server/sqlaccountmodel.cpp: -------------------------------------------------------------------------------- 1 | #include "sqlaccountmodel.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static const char *accountTableName = "USERINFO"; 9 | static const char* friendMessageTableName = "FRIENDMESSAGEINFO"; 10 | 11 | static void createTable(QSqlDatabase db) 12 | { 13 | if(db.tables().contains(accountTableName)) 14 | { 15 | return; 16 | } 17 | if(db.open()) 18 | { 19 | QSqlQuery query(db); 20 | if(!query.exec("CREATE TABLE IF NOT EXISTS USERINFO (" 21 | "ID INTEGER PRIMARY KEY AUTOINCREMENT, " 22 | "NAME CHAR(15) NOT NULL, " 23 | "PASSWORD CHAR(15) NOT NULL," 24 | "ICON TEXT DEFAULT 'https://c-ssl.duitang.com/uploads/blog/201408/15/20140815095903_ttcnF.jpeg'," 25 | "GENDER BOOLEAN," 26 | "BIRTH TEXT," 27 | "AREA CHAR(20)," 28 | "EDUCATION CHAR(20)," 29 | "SIGNATURE TEXT DEFAULT '这个人太懒了,没有个性签名')")) 30 | { 31 | qDebug() << "表创建发生错误"; 32 | qDebug() << query.lastError(); 33 | } 34 | else 35 | { 36 | //根账号 37 | if(!query.exec("INSERT INTO USERINFO(ID, NAME, PASSWORD) VALUES" 38 | "(100000, 'ADMIN', 'ADMIN')")) 39 | { 40 | qDebug() << "插入根账号错误"; 41 | qDebug() << query.lastError(); 42 | } 43 | } 44 | } 45 | } 46 | 47 | SqlAccountModel::SqlAccountModel(QObject *parent, QSqlDatabase db): 48 | QSqlTableModel(parent, db) 49 | { 50 | createTable(this->database()); 51 | setEditStrategy(QSqlTableModel::OnFieldChange); 52 | } 53 | 54 | SqlAccountModel::~SqlAccountModel() 55 | { 56 | database().close(); 57 | } 58 | 59 | QByteArray SqlAccountModel::addUserAccount(const QString& userName, const QString& userPassword) 60 | { 61 | setTable(accountTableName); 62 | QJsonObject obj; 63 | QJsonDocument doc; 64 | QByteArray bArry; 65 | QSqlQuery query; 66 | int id=0; 67 | if(!query.exec(QString("INSERT INTO USERINFO(NAME, PASSWORD) VALUES(" 68 | "'%1', '%2')").arg(userName, userPassword))) 69 | { 70 | qDebug() << "插入账号信息发生错误"; 71 | qDebug() << query.lastError(); 72 | } 73 | else 74 | { 75 | qDebug() << "插入账号信息成功"; 76 | select(); 77 | QSqlRecord record = QSqlTableModel::record(rowCount()-1); 78 | id = record.value(0).toInt(); 79 | if(!query.exec(QString("INSERT INTO FRIENDINFO(USERID, FRIENDID) VALUES(" 80 | "%1, %2)").arg(QString::number(id), QString::number(id)))) 81 | { 82 | qDebug() << "插入好友信息发生错误"; 83 | qDebug() << query.lastError(); 84 | } 85 | } 86 | obj.insert("command","registerBack"); 87 | obj.insert("id", QJsonValue(id)); 88 | doc = QJsonDocument(obj); 89 | bArry = doc.toJson(); 90 | return bArry; 91 | } 92 | 93 | QByteArray SqlAccountModel::checkAccount(const int& userID, const QString &userPassword) 94 | { 95 | bool rel; 96 | QString password; 97 | int id; 98 | QJsonObject obj; 99 | QJsonDocument doc; 100 | QByteArray bArry; 101 | 102 | setTable(accountTableName); 103 | setFilter(QString("ID = %1").arg(userID)); 104 | select(); 105 | 106 | if(rowCount()==0) 107 | { 108 | qDebug() << "该用户不存在"; 109 | rel = false; 110 | } 111 | else 112 | { 113 | QSqlRecord record = QSqlTableModel::record(0); 114 | id = record.value("ID").toInt(); 115 | password = record.value("PASSWORD").toString(); 116 | if(id==userID && password==userPassword) 117 | { 118 | rel = true; 119 | } 120 | else rel = false; 121 | } 122 | obj.insert("command","loginBack"); 123 | obj.insert("result", QJsonValue(rel)); 124 | doc = QJsonDocument(obj); 125 | bArry = doc.toJson(); 126 | return bArry; 127 | } 128 | 129 | QByteArray SqlAccountModel::userInfo(const int &userID) 130 | { 131 | QSqlQuery query; 132 | QByteArray bAry; 133 | QJsonObject finalObj; 134 | if(!query.exec(QString("SELECT * FROM USERINFO WHERE " 135 | "ID=%1 ").arg(QString::number(userID)))) 136 | { 137 | qDebug() << "选择个人信息发生错误"; 138 | qDebug() << query.lastError(); 139 | } 140 | else 141 | { 142 | qDebug() << "选择个人信息成功"; 143 | while(query.next()) 144 | { 145 | QJsonObject obj; 146 | obj.insert("id", QJsonValue(query.value("ID").toInt())); 147 | obj.insert("name", QJsonValue(query.value("NAME").toString())); 148 | obj.insert("icon", QJsonValue(query.value("ICON").toString())); 149 | obj.insert("gender", QJsonValue(query.value("GENDER").toBool())); 150 | obj.insert("birth", QJsonValue(query.value("BIRTH").toString())); 151 | obj.insert("area", QJsonValue(query.value("AREA").toString())); 152 | obj.insert("education", QJsonValue(query.value("EDUCATION").toString())); 153 | obj.insert("signature", QJsonValue(query.value("SIGNATURE").toString())); 154 | finalObj.insert("result", QJsonValue(obj)); 155 | } 156 | qDebug() << finalObj; 157 | } 158 | finalObj.insert("command", QJsonValue("personInfoBack")); 159 | bAry = QJsonDocument(finalObj).toJson(); 160 | return bAry; 161 | } 162 | 163 | void SqlAccountModel::updateIcon(const int& userID, const QString &iconURL) 164 | { 165 | setTable(accountTableName); 166 | setFilter(QString("ID = %1").arg(userID)); 167 | if(!select()) 168 | { 169 | qDebug() << lastError(); 170 | } 171 | else 172 | { 173 | QSqlRecord record = QSqlTableModel::record(0); 174 | record.setValue("ICON", iconURL); 175 | setRecord(0, record); 176 | if(!submitAll()) 177 | { 178 | qDebug() << lastError(); 179 | } 180 | } 181 | } 182 | 183 | void SqlAccountModel::updateUserInfo(const int &userID, const QString &name, const int &gender, const QString &birth, const QString &area, const QString &education, const QString &signature) 184 | { 185 | setTable(accountTableName); 186 | setFilter(QString("ID = %1").arg(userID)); 187 | if(!select()) 188 | { 189 | qDebug() << lastError(); 190 | } 191 | else 192 | { 193 | QSqlRecord record = QSqlTableModel::record(0); 194 | record.setValue("NAME", name); 195 | record.setValue("GENDER", gender); 196 | record.setValue("BIRTH", birth); 197 | record.setValue("AREA", area); 198 | record.setValue("EDUCATION", education); 199 | record.setValue("SIGNATURE", signature); 200 | setRecord(0, record); 201 | if(!submitAll()) 202 | { 203 | qDebug() << lastError(); 204 | } 205 | } 206 | } 207 | 208 | QByteArray SqlAccountModel::messageList(const int &ID) 209 | { 210 | setTable(friendMessageTableName); 211 | QSqlQuery query; 212 | QSqlQuery query1; 213 | QJsonArray jsonItem; 214 | QJsonObject jsonTotalF; 215 | QJsonObject jsonTotalG; 216 | QByteArray bAry; 217 | QSet set; 218 | QJsonObject finalObj; 219 | int lastID=0; 220 | if(!query.exec(QString("SELECT * FROM FRIENDMESSAGEINFO " 221 | "WHERE SENDID=%1 OR RECEIVEID=%2 " 222 | "ORDER BY ID ASC, DATETIME ASC").arg(QString::number(ID), QString::number(ID)))) 223 | { 224 | qDebug() << "选择好友聊天信息错误"; 225 | qDebug() << query.lastError(); 226 | } 227 | else 228 | { 229 | qDebug() << "选择好友聊天信息成功"; 230 | while(query.next()) 231 | { 232 | QJsonObject obj; 233 | if(lastID==0 || lastID==query.value("ID").toInt()) 234 | { 235 | 236 | lastID = query.value("ID").toInt(); 237 | set.insert(query.value("SENDID").toInt()); 238 | set.insert(query.value("RECEIVEID").toInt()); 239 | obj.insert("sid", QJsonValue(query.value("SENDID").toInt())); 240 | obj.insert("rid", QJsonValue(query.value("RECEIVEID").toInt())); 241 | obj.insert("datetime", QJsonValue(query.value("DATETIME").toInt())); 242 | obj.insert("message", QJsonValue(query.value("MESSAGE").toString())); 243 | jsonItem.append(QJsonValue(obj)); 244 | } 245 | else 246 | { 247 | set.remove(ID); 248 | jsonTotalF.insert(QString::number(*(set.begin())), QJsonValue(jsonItem)); 249 | lastID = query.value("ID").toInt(); 250 | set.clear(); 251 | set.insert(query.value("SENDID").toInt()); 252 | set.insert(query.value("RECEIVEID").toInt()); 253 | while(!jsonItem.empty()) jsonItem.removeLast(); 254 | obj.insert("sid", QJsonValue(query.value("SENDID").toInt())); 255 | obj.insert("rid", QJsonValue(query.value("RECEIVEID").toInt())); 256 | obj.insert("datetime", QJsonValue(query.value("DATETIME").toInt())); 257 | obj.insert("message", QJsonValue(query.value("MESSAGE").toString())); 258 | jsonItem.append(QJsonValue(obj)); 259 | } 260 | } 261 | set.remove(ID); 262 | jsonTotalF.insert(QString::number(*(set.begin())), QJsonValue(jsonItem)); 263 | finalObj.insert("friendlist", QJsonValue(jsonTotalF)); 264 | } 265 | lastID = 0; 266 | while(!jsonItem.empty()) jsonItem.removeLast(); 267 | if(!query.exec(QString("SELECT GROUPMESSAGEINFO.GID AS GID, GROUPMESSAGEINFO.MID AS MID, GROUPMESSAGEINFO.DATETIME AS DATETIME, GROUPMESSAGEINFO.MESSAGE AS MESSAGE " 268 | "FROM MEMBERINFO join GROUPMESSAGEINFO ON MEMBERINFO.GID=GROUPMESSAGEINFO.GID " 269 | "WHERE MEMBERINFO.MID=%1 ORDER BY GID ASC, DATETIME ASC").arg(QString::number(ID)))) 270 | { 271 | qDebug() << "选择群聊天信息错误"; 272 | qDebug() << query.lastError(); 273 | } 274 | else 275 | { 276 | qDebug() << "选择群聊天信息成功"; 277 | while(query.next()) 278 | { 279 | QJsonObject obj; 280 | if(lastID==0 || lastID==query.value("GID")) 281 | { 282 | lastID = query.value("GID").toInt(); 283 | obj.insert("mid", QJsonValue(query.value("MID").toInt())); 284 | obj.insert("datetime", QJsonValue(query.value("DATETIME").toInt())); 285 | obj.insert("message", QJsonValue(query.value("MESSAGE").toString())); 286 | jsonItem.append(QJsonValue(obj)); 287 | } 288 | else 289 | { 290 | jsonTotalG.insert(QString::number(lastID), QJsonValue(jsonItem)); 291 | lastID = query.value("GID").toInt(); 292 | while(!jsonItem.empty()) jsonItem.removeLast(); 293 | obj.insert("mid", QJsonValue(query.value("MID").toInt())); 294 | obj.insert("datetime", QJsonValue(query.value("DATETIME").toInt())); 295 | obj.insert("message", QJsonValue(query.value("MESSAGE").toString())); 296 | jsonItem.append(QJsonValue(obj)); 297 | } 298 | } 299 | jsonTotalG.insert(QString::number(lastID), QJsonValue(jsonItem)); 300 | finalObj.insert("grouplist", QJsonValue(jsonTotalG)); 301 | } 302 | finalObj.insert("command", QJsonValue("messageBack")); 303 | qDebug() << finalObj; 304 | bAry = QJsonDocument(finalObj).toJson(); 305 | return bAry; 306 | } 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | -------------------------------------------------------------------------------- /01.项目源码/server/sqlaccountmodel.h: -------------------------------------------------------------------------------- 1 | #ifndef SQLACCOUNTMODEL_H 2 | #define SQLACCOUNTMODEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | class SqlAccountModel : public QSqlTableModel 13 | { 14 | Q_OBJECT 15 | public: 16 | SqlAccountModel(QObject *parent, QSqlDatabase db); 17 | ~SqlAccountModel(); 18 | 19 | //添加用户账号信息,如果添加成功返回id, 添加失败返回0, 包装在json下的id字段 20 | QByteArray addUserAccount(const QString& userName, const QString& userPassword); 21 | //检查用户账号是否正确,如果正确返回true, 错误返回false, 包装在json下的result字段 22 | QByteArray checkAccount(const int& userID, const QString& userPassword); 23 | //获取用户个人信息 24 | QByteArray userInfo(const int& userID); 25 | //添加或更新用户头像的url 26 | void updateIcon(const int& userID, const QString& iconURL); 27 | //更新用户信息 28 | void updateUserInfo(const int& userID, const QString& name, const int& gender, const QString& birth, const QString& area, const QString& education, const QString& signature); 29 | //获取用户所有聊天信息 30 | QByteArray messageList(const int& ID); 31 | }; 32 | #endif // SQLACCOUNTMODEL_H 33 | -------------------------------------------------------------------------------- /01.项目源码/server/sqlfriendmodel.cpp: -------------------------------------------------------------------------------- 1 | #include "sqlfriendmodel.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static const char* friendTableName = "FRIENDINFO"; 10 | static const char* friendMessageTableName = "FRIENDMESSAGEINFO"; 11 | static void createTable(QSqlDatabase db) 12 | { 13 | if(!db.tables().contains(friendTableName)) 14 | { 15 | if(db.open()) 16 | { 17 | QSqlQuery query(db); 18 | if(!query.exec(QString("CREATE TABLE IF NOT EXISTS %1(" 19 | "ID INTEGER PRIMARY KEY AUTOINCREMENT," 20 | "USERID INTEGER NOT NULL, " 21 | "FRIENDID INTEGER NOT NULL CHECK (USERID<=FRIENDID)," 22 | "UNIQUE (USERID, FRIENDID)," 23 | "FOREIGN KEY (USERID) REFERENCES USERINFO(ID) ON DELETE CASCADE," 24 | "FOREIGN KEY (FRIENDID) REFERENCES USERINFO(ID) ON DELETE CASCADE)" 25 | ).arg(friendTableName))) 26 | { 27 | qDebug() << "表创建发生错误"; 28 | qDebug() << query.lastError(); 29 | } 30 | } 31 | } 32 | if(!db.tables().contains(friendMessageTableName)) 33 | { 34 | if(db.open()) 35 | { 36 | QSqlQuery query(db); 37 | if(!query.exec(QString("CREATE TABLE IF NOT EXISTS %1(" 38 | "ID INTEGER REFERENCES FRIENDINFO(ID)," 39 | "SENDID INTEGER NOT NULL, " 40 | "RECEIVEID INTEGER NOT NULL," 41 | "DATETIME INT NOT NULL," 42 | "MESSAGE TEXT NOT NULL," 43 | "TYPE INTEGER NOT NULL," 44 | "PRIMARY KEY (SENDID, RECEIVEID, DATETIME)," 45 | "FOREIGN KEY (SENDID) REFERENCES USERINFO(ID)," 46 | "FOREIGN KEY (RECEIVEID) REFERENCES USERINFO(ID))" 47 | ).arg(friendMessageTableName))) 48 | { 49 | qDebug() << "好友聊天表创建发生错误"; 50 | qDebug() << query.lastError(); 51 | } 52 | } 53 | } 54 | } 55 | 56 | SqlFriendModel::SqlFriendModel(QObject *parent, QSqlDatabase db): 57 | QSqlTableModel(parent, db) 58 | { 59 | createTable(this->database()); 60 | } 61 | 62 | SqlFriendModel::~SqlFriendModel() 63 | { 64 | database().close(); 65 | } 66 | 67 | bool SqlFriendModel::addFriend(const int &aID, const int &bID) 68 | { 69 | setTable(friendTableName); 70 | QSqlQuery query; 71 | int id1, id2; 72 | id1 = qMin(aID, bID); 73 | id2 = qMax(aID, bID); 74 | if(!query.exec(QString("INSERT INTO FRIENDINFO(USERID, FRIENDID) VALUES(" 75 | "%1, %2)").arg(QString::number(id1), QString::number(id2)))) 76 | { 77 | qDebug() << "添加好友发生错误"; 78 | qDebug() << query.lastError(); 79 | return false; 80 | } 81 | else 82 | { 83 | qDebug() << "添加好友成功"; 84 | return true; 85 | } 86 | } 87 | 88 | void SqlFriendModel::delFriend(const int &aID, const int &bID) 89 | { 90 | setTable(friendTableName); 91 | QSqlQuery query; 92 | int id1, id2; 93 | id1 = qMin(aID, bID); 94 | id2 = qMax(aID, bID); 95 | if(!query.exec(QString("DELETE FROM FRIENDINFO WHERE " 96 | "USERID=%1 AND FRIENDID=%2").arg(QString::number(id1), QString::number(id2)))) 97 | { 98 | qDebug() << "删除好友发生错误"; 99 | qDebug() << query.lastError(); 100 | } 101 | else 102 | { 103 | qDebug() << "删除好友成功"; 104 | } 105 | } 106 | 107 | QByteArray SqlFriendModel::friendList(const int &ID) 108 | { 109 | setTable(friendTableName); 110 | QSqlQuery query; 111 | QSqlQuery query1; 112 | QJsonArray jsonAry; 113 | QByteArray bAry; 114 | QJsonObject finalObj; 115 | if(!query1.exec(QString("SELECT * FROM USERINFO WHERE " 116 | "ID=%1").arg(QString::number(ID)))) 117 | { 118 | qDebug() << "选择好友发生错误"; 119 | qDebug() << query.lastError(); 120 | } 121 | else 122 | { 123 | query1.next(); 124 | QJsonObject obj; 125 | obj.insert("id", QJsonValue(query1.value("ID").toInt())); 126 | obj.insert("name", QJsonValue(query1.value("NAME").toString())); 127 | obj.insert("icon", QJsonValue(query1.value("ICON").toString())); 128 | obj.insert("gender", QJsonValue(query1.value("GENDER").toBool())); 129 | obj.insert("birth", QJsonValue(query1.value("BIRTH").toString())); 130 | obj.insert("area", QJsonValue(query1.value("AREA").toString())); 131 | obj.insert("education", QJsonValue(query1.value("EDUCATION").toString())); 132 | obj.insert("signature", QJsonValue(query1.value("SIGNATURE").toString())); 133 | jsonAry.append(QJsonValue(obj)); 134 | } 135 | if(!query.exec(QString("SELECT * FROM (SELECT USERID AS ID FROM FRIENDINFO WHERE " 136 | "FRIENDID=%1 AND USERID !=%1 UNION " 137 | "SELECT FRIENDID AS ID FROM FRIENDINFO WHERE " 138 | "USERID=%1 AND FRIENDID!=%1) AS A JOIN USERINFO AS B " 139 | "ON A.ID=B.ID").arg(QString::number(ID)))) 140 | { 141 | qDebug() << "选择好友发生错误"; 142 | qDebug() << query.lastError(); 143 | } 144 | else 145 | { 146 | qDebug() << "选择好友成功"; 147 | while(query.next()) 148 | { 149 | QJsonObject obj; 150 | obj.insert("id", QJsonValue(query.value("ID").toInt())); 151 | obj.insert("name", QJsonValue(query.value("NAME").toString())); 152 | obj.insert("icon", QJsonValue(query.value("ICON").toString())); 153 | obj.insert("gender", QJsonValue(query.value("GENDER").toBool())); 154 | obj.insert("birth", QJsonValue(query.value("BIRTH").toString())); 155 | obj.insert("area", QJsonValue(query.value("AREA").toString())); 156 | obj.insert("education", QJsonValue(query.value("EDUCATION").toString())); 157 | obj.insert("signature", QJsonValue(query.value("SIGNATURE").toString())); 158 | jsonAry.append(QJsonValue(obj)); 159 | } 160 | qDebug() << jsonAry; 161 | finalObj.insert("list", QJsonValue(jsonAry)); 162 | } 163 | finalObj.insert("command", QJsonValue("friendBack")); 164 | bAry = QJsonDocument(finalObj).toJson(); 165 | return bAry; 166 | } 167 | 168 | void SqlFriendModel::sendMessage(const int &sendID, const int &receiveID, const int& type, const int &datetime, const QString &message) 169 | { 170 | QSqlQuery query; 171 | int id; 172 | if(!query.exec(QString("SELECT ID FROM FRIENDINFO WHERE " 173 | "(USERID=%1 AND FRIENDID=%2) OR (USERID=%2 AND FRIENDID=%1)").arg(QString::number(sendID), QString::number(receiveID)))) 174 | { 175 | qDebug() << "选择好友对编号发生错误"; 176 | qDebug() << query.lastError(); 177 | } 178 | query.next(); 179 | id = query.value("ID").toInt(); 180 | if(!query.exec(QString("INSERT INTO FRIENDMESSAGEINFO VALUES(" 181 | "%1, %2, %3, %4, '%5', %6)").arg(QString::number(id),QString::number(sendID), QString::number(receiveID), QString::number(datetime), message,QString::number(type)))) 182 | { 183 | qDebug() << "添加好友聊天信息发生错误"; 184 | qDebug() << query.lastError(); 185 | } 186 | else 187 | { 188 | qDebug() << "添加好友聊天信息成功"; 189 | } 190 | } 191 | 192 | QByteArray SqlFriendModel::messageList(const int &aID, const int &bID) 193 | { 194 | QSqlQuery query; 195 | QJsonArray jsonItem; 196 | QByteArray bAry; 197 | QJsonObject finalObj; 198 | if(!query.exec(QString("SELECT * FROM FRIENDMESSAGEINFO " 199 | "WHERE (SENDID=%1 AND RECEIVEID=%2) " 200 | "OR (SENDID=%2 AND RECEIVEID=%1) " 201 | "ORDER BY ID ASC, DATETIME ASC").arg(QString::number(aID), QString::number(bID)))) 202 | { 203 | qDebug() << "选择好友聊天信息错误"; 204 | qDebug() << query.lastError(); 205 | } 206 | else 207 | { 208 | qDebug() << "选择好友聊天信息成功"; 209 | while(query.next()) 210 | { 211 | QJsonObject obj; 212 | obj.insert("sid", QJsonValue(query.value("SENDID").toInt())); 213 | obj.insert("rid", QJsonValue(query.value("RECEIVEID").toInt())); 214 | obj.insert("datetime", QJsonValue(query.value("DATETIME").toInt())); 215 | obj.insert("message", QJsonValue(query.value("MESSAGE").toString())); 216 | jsonItem.append(QJsonValue(obj)); 217 | } 218 | finalObj.insert("command","messageBack"); 219 | finalObj.insert("targetId",bID); 220 | finalObj.insert("messagelist", QJsonValue(jsonItem)); 221 | } 222 | bAry = QJsonDocument(finalObj).toJson(); 223 | return bAry; 224 | } 225 | -------------------------------------------------------------------------------- /01.项目源码/server/sqlfriendmodel.h: -------------------------------------------------------------------------------- 1 | #ifndef SQLFRIENDMODEL_H 2 | #define SQLFRIENDMODEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | class SqlFriendModel : public QSqlTableModel 13 | { 14 | Q_OBJECT 15 | public: 16 | SqlFriendModel(QObject *parent, QSqlDatabase db); 17 | ~SqlFriendModel(); 18 | //添加好友 19 | bool addFriend(const int& aID, const int& bID); 20 | //删除好友 21 | void delFriend(const int& aID, const int& bID); 22 | //获取好友列表及好友个人信息 23 | QByteArray friendList(const int& ID); 24 | //发送聊天信息 25 | void sendMessage(const int& sendID, const int& receiveID, const int& type,const int& datetime, const QString& message); 26 | //两人的历史聊天 27 | QByteArray messageList(const int& aID, const int& bID); 28 | }; 29 | 30 | #endif // SQLFRIENDSMODEL_H 31 | -------------------------------------------------------------------------------- /01.项目源码/server/sqlgroupmodel.cpp: -------------------------------------------------------------------------------- 1 | #include "sqlgroupmodel.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static const char* groupTableName = "GROUPINFO"; 10 | static const char* memberTableName = "MEMBERINFO"; 11 | static const char* groupMessageTableName = "GROUPMESSAGEINFO"; 12 | static void createTable(QSqlDatabase db) 13 | { 14 | if(!db.tables().contains(groupTableName)) 15 | { 16 | if(db.open()) 17 | { 18 | QSqlQuery query(db); 19 | if(!query.exec(QString("CREATE TABLE IF NOT EXISTS %1(" 20 | "ID INTEGER PRIMARY KEY AUTOINCREMENT," 21 | "NAME CHAR(15) NOT NULL," 22 | "ICON TEXT DEFAULT 'https://c-ssl.duitang.com/uploads/blog/201408/15/20140815095903_ttcnF.jpeg'," 23 | "INTRO TEXT DEFAULT '这个群太新了,还没有简介。'," 24 | "NOTICE TEXT)" 25 | ).arg(groupTableName))) 26 | { 27 | qDebug() << "群总表创建发生错误"; 28 | qDebug() << query.lastError(); 29 | } 30 | else 31 | { 32 | //根账号 33 | if(!query.exec("INSERT INTO GROUPINFO(ID, NAME) VALUES" 34 | "(600000, 'ADMIN')")) 35 | { 36 | qDebug() << "插入根账号错误"; 37 | qDebug() << query.lastError(); 38 | } 39 | } 40 | } 41 | } 42 | if(!db.tables().contains(memberTableName)) 43 | { 44 | if(db.isOpen()) 45 | { 46 | QSqlQuery query(db); 47 | if(!query.exec(QString("CREATE TABLE IF NOT EXISTS %1(" 48 | "GID INTEGER REFERENCES GROUPINFO(ID) ON DELETE CASCADE," 49 | "MID INTEGER NOT NULL REFERENCES USERINFO(ID) ON DELETE CASCADE," 50 | "RANK INTEGER NOT NULL," 51 | "PRIMARY KEY (GID, MID))" 52 | ).arg(memberTableName))) 53 | { 54 | qDebug() << "成员表创建发生错误"; 55 | qDebug() << query.lastError(); 56 | } 57 | } 58 | } 59 | if(!db.tables().contains(groupMessageTableName)) 60 | { 61 | if(db.isOpen()) 62 | { 63 | QSqlQuery query(db); 64 | if(!query.exec(QString("CREATE TABLE IF NOT EXISTS %1(" 65 | "GID INTEGER REFERENCES GROUPINFO(ID) ON DELETE CASCADE," 66 | "MID INTEGER NOT NULL REFERENCES USERINFO(ID) ON DELETE CASCADE," 67 | "DATETIME INT NOT NULL," 68 | "MESSAGE TEXT NOT NULL," 69 | "TYPE INTEGER NOT NULL," 70 | "PRIMARY KEY (GID, MID, DATETIME))" 71 | ).arg(groupMessageTableName))) 72 | { 73 | qDebug() << "群聊消息表创建发生错误"; 74 | qDebug() << query.lastError(); 75 | } 76 | } 77 | } 78 | } 79 | SqlGroupModel::SqlGroupModel(QObject *parent, QSqlDatabase db): 80 | QSqlTableModel(parent, db) 81 | { 82 | createTable(this->database()); 83 | } 84 | 85 | SqlGroupModel::~SqlGroupModel() 86 | { 87 | database().close(); 88 | } 89 | 90 | int SqlGroupModel::createGroup(const int& masterID) 91 | { 92 | setTable(groupTableName); 93 | QSqlQuery query; 94 | QJsonObject obj; 95 | QJsonDocument doc; 96 | QByteArray bArry; 97 | int id = 0; 98 | if(!query.exec(QString("INSERT INTO GROUPINFO(NAME) VALUES(" 99 | "'%1')").arg(QString::number(masterID)+"s Group"))) 100 | { 101 | qDebug() << "创建群聊发生错误"; 102 | qDebug() << query.lastError(); 103 | } 104 | else 105 | { 106 | qDebug() << "创建群聊成功"; 107 | select(); 108 | QSqlRecord record = QSqlTableModel::record(rowCount()-1); 109 | id = record.value(0).toInt(); 110 | for(int i=0;i<100000;i++);//循环阻塞 111 | this->joinGroup(id, masterID, 1); 112 | } 113 | 114 | return id; 115 | } 116 | 117 | QByteArray SqlGroupModel::memberList(const int & gID) 118 | { 119 | setTable(memberTableName); 120 | QSqlQuery query; 121 | QJsonArray jsonAry; 122 | QByteArray bAry; 123 | QJsonObject finalObj; 124 | if(!query.exec(QString("SELECT MID, RANK FROM MEMBERINFO " 125 | "WHERE GID=%1").arg(QString::number(gID)))) 126 | { 127 | qDebug() << "选择群成员错误"; 128 | qDebug() << query.lastError(); 129 | } 130 | else 131 | { 132 | qDebug() << "选择群成员成功"; 133 | while(query.next()) 134 | { 135 | QJsonObject obj; 136 | obj.insert("id", QJsonValue(query.value("MID").toInt())); 137 | obj.insert("rank", QJsonValue(query.value("RANK").toInt())); 138 | jsonAry.append(QJsonValue(obj)); 139 | } 140 | qDebug() << jsonAry; 141 | finalObj.insert("list", QJsonValue(jsonAry)); 142 | } 143 | finalObj.insert("command", QJsonValue("memberinfo")); 144 | bAry = QJsonDocument(finalObj).toJson(); 145 | return bAry; 146 | } 147 | 148 | void SqlGroupModel::updateGroupInfo(const int& id, const QString & name, const QString & intro, const QString & notice) 149 | { 150 | setTable(groupTableName); 151 | setFilter(QString("ID = %1").arg(id)); 152 | if(!select()) 153 | { 154 | qDebug() << lastError(); 155 | } 156 | else 157 | { 158 | QSqlRecord record = QSqlTableModel::record(0); 159 | record.setValue("ID", id); 160 | record.setValue("NAME", name); 161 | record.setValue("INTRO", intro); 162 | record.setValue("NOTICE", notice); 163 | setRecord(0, record); 164 | if(!submitAll()) 165 | { 166 | qDebug() << lastError(); 167 | } 168 | } 169 | } 170 | 171 | void SqlGroupModel::delGroup(const int & gID) 172 | { 173 | setTable(groupTableName); 174 | QSqlQuery query; 175 | if(!query.exec(QString("DELETE FROM GROUPINFO WHERE " 176 | "GID=%1").arg(QString::number(gID)))) 177 | { 178 | qDebug() << "删除群聊发生错误"; 179 | qDebug() << query.lastError(); 180 | } 181 | else 182 | { 183 | qDebug() << "删除群聊成功"; 184 | } 185 | } 186 | 187 | bool SqlGroupModel::joinGroup(const int & gID, const int & mID, const int & rank) 188 | { 189 | setTable(memberTableName); 190 | QSqlQuery query; 191 | if(!query.exec(QString("INSERT INTO MEMBERINFO(GID, MID, RANK) VALUES(" 192 | "%1, %2, %3)").arg(QString::number(gID), QString::number(mID), QString::number(rank)))) 193 | { 194 | qDebug() << "加入群聊发生错误"; 195 | qDebug() << query.lastError(); 196 | return false; 197 | } 198 | else 199 | { 200 | qDebug() << "加入群聊成功"; 201 | return true; 202 | } 203 | } 204 | 205 | void SqlGroupModel::quitGroup(const int & gID, const int & mID) 206 | { 207 | setTable(memberTableName); 208 | QSqlQuery query; 209 | if(!query.exec(QString("DELETE FROM MEMBERINFO WHERE " 210 | "GID=%1 AND MID=%2").arg(QString::number(gID), QString::number(mID)))) 211 | { 212 | qDebug() << "退出群聊发生错误"; 213 | qDebug() << query.lastError(); 214 | } 215 | else 216 | { 217 | qDebug() << "退出群聊成功"; 218 | } 219 | } 220 | 221 | void SqlGroupModel::sendMessage(const int &gID, const int &mID, const int& type, const int &datetime, const QString &message) 222 | { 223 | setTable(groupMessageTableName); 224 | QSqlQuery query; 225 | if(!query.exec(QString("INSERT INTO GROUPMESSAGEINFO VALUES(" 226 | "%1, %2, %3, '%4', %5)").arg(QString::number(gID), QString::number(mID), QString::number(datetime), message, QString::number(type)))) 227 | { 228 | qDebug() << "添加群聊天信息发生错误"; 229 | qDebug() << query.lastError(); 230 | } 231 | else 232 | { 233 | qDebug() << "添加群聊天信息成功"; 234 | } 235 | } 236 | 237 | QByteArray SqlGroupModel::groupList(const int &mID) 238 | { 239 | QSqlQuery query; 240 | QByteArray bAry; 241 | QJsonObject finalObj; 242 | QJsonArray jsonAry; 243 | if(!query.exec(QString("SELECT ID, NAME, ICON, INTRO, NOTICE FROM MEMBERINFO JOIN GROUPINFO " 244 | "ON MEMBERINFO.GID=GROUPINFO.ID " 245 | "WHERE MID=%1").arg(QString::number(mID)))) 246 | { 247 | qDebug() << "获取群列表发生错误"; 248 | qDebug() << query.lastError(); 249 | } 250 | else 251 | { 252 | qDebug() << "获取群列表成功"; 253 | while(query.next()) 254 | { 255 | QJsonObject obj; 256 | obj.insert("id", QJsonValue(query.value("ID").toInt())); 257 | obj.insert("name", QJsonValue(query.value("NAME").toString())); 258 | obj.insert("icon", QJsonValue(query.value("ICON").toString())); 259 | obj.insert("intro", QJsonValue(query.value("INTRO").toString())); 260 | obj.insert("notice", QJsonValue(query.value("NOTICE").toString())); 261 | jsonAry.append(QJsonValue(obj)); 262 | } 263 | } 264 | finalObj.insert("groupList", QJsonValue(jsonAry)); 265 | finalObj.insert("command", QJsonValue("groupBack")); 266 | bAry = QJsonDocument(finalObj).toJson(); 267 | return bAry; 268 | } 269 | 270 | QByteArray SqlGroupModel::messageList(const int &gID) 271 | { 272 | QSqlQuery query; 273 | QByteArray bAry; 274 | QJsonObject finalObj; 275 | QJsonArray jsonItem; 276 | if(!query.exec(QString("SELECT * FROM GROUPMESSAGEINFO " 277 | "WHERE GID=%1 ORDER BY GID ASC, DATETIME ASC").arg(QString::number(gID)))) 278 | { 279 | qDebug() << "选择群聊天信息错误"; 280 | qDebug() << query.lastError(); 281 | } 282 | else 283 | { 284 | qDebug() << "选择群聊天信息成功"; 285 | while(query.next()) 286 | { 287 | QJsonObject obj; 288 | obj.insert("mid", QJsonValue(query.value("MID").toInt())); 289 | obj.insert("datetime", QJsonValue(query.value("DATETIME").toInt())); 290 | obj.insert("message", QJsonValue(query.value("MESSAGE").toString())); 291 | jsonItem.append(QJsonValue(obj)); 292 | } 293 | 294 | finalObj.insert("grouplist", QJsonValue(jsonItem)); 295 | finalObj.insert("targetId",gID); 296 | finalObj.insert("command", QJsonValue("messageBack")); 297 | } 298 | bAry = QJsonDocument(finalObj).toJson(); 299 | return bAry; 300 | } 301 | 302 | 303 | QByteArray SqlGroupModel::groupInfo(const int &gID, const int& mID) 304 | { 305 | QSqlQuery query; 306 | QSqlQuery query1; 307 | QByteArray bAry; 308 | QJsonObject finalObj; 309 | int rank; 310 | if(!query1.exec(QString("SELECT RANK FROM MEMBERINFO WHERE " 311 | "GID=%1 AND MID=%2 ").arg(QString::number(gID),QString::number(mID)))) 312 | { 313 | qDebug() << "确认群主发生错误"; 314 | qDebug() << query1.lastError(); 315 | } 316 | else 317 | { 318 | qDebug() << "确认群主成功"; 319 | query1.next(); 320 | rank = query1.value("RANK").toInt(); 321 | } 322 | if(!query.exec(QString("SELECT * FROM GROUPINFO WHERE " 323 | "ID=%1 ").arg(QString::number(gID)))) 324 | { 325 | qDebug() << "选择群信息发生错误"; 326 | qDebug() << query.lastError(); 327 | } 328 | else 329 | { 330 | qDebug() << "选择群信息成功"; 331 | while(query.next()) 332 | { 333 | QJsonObject obj; 334 | obj.insert("groupId", QJsonValue(query.value("ID").toInt())); 335 | obj.insert("groupName", QJsonValue(query.value("NAME").toString())); 336 | obj.insert("icon", QJsonValue(query.value("ICON").toString())); 337 | obj.insert("groupSummary", QJsonValue(query.value("INTRO").toString())); 338 | obj.insert("groupNotice", QJsonValue(query.value("NOTICE").toString())); 339 | obj.insert("isOwner", QJsonValue(rank==1)); 340 | finalObj.insert("result", QJsonValue(obj)); 341 | } 342 | qDebug() << finalObj; 343 | } 344 | finalObj.insert("command", QJsonValue("groupInfoBack")); 345 | bAry = QJsonDocument(finalObj).toJson(); 346 | return bAry; 347 | } 348 | 349 | -------------------------------------------------------------------------------- /01.项目源码/server/sqlgroupmodel.h: -------------------------------------------------------------------------------- 1 | #ifndef SQLGROUPMODEL_H 2 | #define SQLGROUPMODEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | class SqlGroupModel : public QSqlTableModel 13 | { 14 | Q_OBJECT 15 | public: 16 | SqlGroupModel(QObject *parent, QSqlDatabase db); 17 | ~SqlGroupModel(); 18 | //创建群聊 19 | int createGroup(const int& masterID); 20 | //获取群成员信息(id, 级别) 21 | QByteArray memberList(const int& gID); 22 | //修改群信息 23 | void updateGroupInfo(const int& id, const QString& name, const QString& intro, const QString& notice); 24 | //删除群聊 25 | void delGroup(const int& gID); 26 | //加入群聊 27 | bool joinGroup(const int& gID, const int& mID, const int& rank); 28 | //退出群聊 29 | void quitGroup(const int& gID, const int& mID); 30 | //添加群消息 31 | void sendMessage(const int& gID, const int& mID, const int& type, const int& datetime, const QString& message); 32 | //获取用户加入的群信息 33 | QByteArray groupList(const int& mID); 34 | //获取特定群的聊天记录 35 | QByteArray messageList(const int& gID); 36 | //获取特定群信息 37 | QByteArray groupInfo(const int& gID,const int& mID); 38 | }; 39 | 40 | #endif // SQLGROUPMODEL_H 41 | -------------------------------------------------------------------------------- /01.项目源码/server/tcpclient.cpp: -------------------------------------------------------------------------------- 1 | #include "tcpclient.h" 2 | 3 | TcpClient::TcpClient(QObject *parent) :QThread(parent) 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /01.项目源码/server/tcpclient.h: -------------------------------------------------------------------------------- 1 | #ifndef TCPCLIENT_H 2 | #define TCPCLIENT_H 3 | 4 | #include 5 | #include 6 | 7 | class TcpClient : public QThread 8 | { 9 | Q_OBJECT 10 | public: 11 | explicit TcpClient(QObject *parent = nullptr); 12 | 13 | signals: 14 | 15 | }; 16 | 17 | #endif // TCPCLIENT_H 18 | -------------------------------------------------------------------------------- /01.项目源码/server/tcpserver.cpp: -------------------------------------------------------------------------------- 1 | #include "tcpserver.h" 2 | 3 | TcpServer::TcpServer(QString filePath,int targetId,QString time,QObject *parent) : QThread(parent) 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /01.项目源码/server/tcpserver.h: -------------------------------------------------------------------------------- 1 | #ifndef TCPSERVER_H 2 | #define TCPSERVER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class TcpServer : public QThread 10 | { 11 | Q_OBJECT 12 | public: 13 | explicit TcpServer(QString filePath,int targetId,QString time,QObject *parent = nullptr); 14 | 15 | signals: 16 | 17 | private: 18 | //套接字 19 | QTcpServer *tcpServer; 20 | QTcpSocket *tcpSocket; 21 | //文件相关 22 | 23 | 24 | }; 25 | 26 | #endif // TCPSERVER_H 27 | -------------------------------------------------------------------------------- /02.项目运行录屏/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/02.项目运行录屏/.DS_Store -------------------------------------------------------------------------------- /02.项目运行录屏/请到百度云下载.md: -------------------------------------------------------------------------------- 1 | 链接:https://pan.baidu.com/s/1AQf4nviCl7cgeWXzP3zvyw?pwd=cyyy 2 | 提取码:cyyy 3 | -------------------------------------------------------------------------------- /03.项目文件/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/03.项目文件/.DS_Store -------------------------------------------------------------------------------- /03.项目文件/个人日报(每人1份,每天持续更新)/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/03.项目文件/个人日报(每人1份,每天持续更新)/.DS_Store -------------------------------------------------------------------------------- /03.项目文件/个人日报(每人1份,每天持续更新)/个人日报 _刘恩泽.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/03.项目文件/个人日报(每人1份,每天持续更新)/个人日报 _刘恩泽.xlsx -------------------------------------------------------------------------------- /03.项目文件/个人日报(每人1份,每天持续更新)/个人日报 _刘翎.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/03.项目文件/个人日报(每人1份,每天持续更新)/个人日报 _刘翎.xlsx -------------------------------------------------------------------------------- /03.项目文件/个人日报(每人1份,每天持续更新)/个人日报 _沈宇航.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/03.项目文件/个人日报(每人1份,每天持续更新)/个人日报 _沈宇航.xlsx -------------------------------------------------------------------------------- /03.项目文件/个人日报(每人1份,每天持续更新)/个人日报 _罗家安.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/03.项目文件/个人日报(每人1份,每天持续更新)/个人日报 _罗家安.xlsx -------------------------------------------------------------------------------- /03.项目文件/个人日报(每人1份,每天持续更新)/个人日报_陈耀宇.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/03.项目文件/个人日报(每人1份,每天持续更新)/个人日报_陈耀宇.xlsx -------------------------------------------------------------------------------- /03.项目文件/人员分工表--WeTalk-1组.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/03.项目文件/人员分工表--WeTalk-1组.xls -------------------------------------------------------------------------------- /03.项目文件/结合测试-WeTalk-1组.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/03.项目文件/结合测试-WeTalk-1组.xlsx -------------------------------------------------------------------------------- /03.项目文件/需求跟踪矩阵-WeTalk-1组.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/03.项目文件/需求跟踪矩阵-WeTalk-1组.xls -------------------------------------------------------------------------------- /04.实训总结报告-每人1份/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/04.实训总结报告-每人1份/.DS_Store -------------------------------------------------------------------------------- /04.实训总结报告-每人1份/学生实训总结报告-刘恩泽.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/04.实训总结报告-每人1份/学生实训总结报告-刘恩泽.docx -------------------------------------------------------------------------------- /04.实训总结报告-每人1份/学生实训总结报告-刘翎.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/04.实训总结报告-每人1份/学生实训总结报告-刘翎.docx -------------------------------------------------------------------------------- /04.实训总结报告-每人1份/学生实训总结报告-沈宇航.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/04.实训总结报告-每人1份/学生实训总结报告-沈宇航.docx -------------------------------------------------------------------------------- /04.实训总结报告-每人1份/学生实训总结报告-罗家安.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/04.实训总结报告-每人1份/学生实训总结报告-罗家安.pdf -------------------------------------------------------------------------------- /04.实训总结报告-每人1份/学生实训总结报告-陈耀宇.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/04.实训总结报告-每人1份/学生实训总结报告-陈耀宇.docx -------------------------------------------------------------------------------- /05.环评报告-每人1份/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/05.环评报告-每人1份/.DS_Store -------------------------------------------------------------------------------- /05.环评报告-每人1份/行业解决方案对环境和可持续发展影响分析报告-刘恩泽.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/05.环评报告-每人1份/行业解决方案对环境和可持续发展影响分析报告-刘恩泽.docx -------------------------------------------------------------------------------- /05.环评报告-每人1份/行业解决方案对环境和可持续发展影响分析报告-刘翎.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/05.环评报告-每人1份/行业解决方案对环境和可持续发展影响分析报告-刘翎.docx -------------------------------------------------------------------------------- /05.环评报告-每人1份/行业解决方案对环境和可持续发展影响分析报告-姓名.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/05.环评报告-每人1份/行业解决方案对环境和可持续发展影响分析报告-姓名.docx -------------------------------------------------------------------------------- /05.环评报告-每人1份/行业解决方案对环境和可持续发展影响分析报告-沈宇航.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/05.环评报告-每人1份/行业解决方案对环境和可持续发展影响分析报告-沈宇航.docx -------------------------------------------------------------------------------- /05.环评报告-每人1份/行业解决方案对环境和可持续发展影响分析报告-罗家安.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/05.环评报告-每人1份/行业解决方案对环境和可持续发展影响分析报告-罗家安.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 详细代码解释以及报告详见源代码注释+实训报告文件夹 2 | 3 | # WeTalk 4 | 北理工计科小学期:Qt聊天室。本项目采用云服务器架构+QML前端,实现多人在线即时聊天。 5 | 6 | 我们这个项目在同一届中算做的很好的了,之所以没有做到最好,是因为有两个技术点没有实现: 7 | 8 | 1. 服务器多线程并发 9 | 2. 文件传输 10 | 11 | 实在是没时间了,如果有后来者可以读懂我们这个项目,并且完善,相信可以拿到最好的成绩,没有之一。 12 | 13 | 另一个备份的链接:https://github.com/llada60/2022_Chatroom_qt 14 | 15 | # 效果展示 16 | 17 | 9fbdfed36872cb93bb2a6d3fa0126e7 18 | 19 | d9c7c924541f761d40227f55ce19197 20 | 21 | 22 | ![26f8d8356a64bbacb5117c2e16adf28](https://user-images.githubusercontent.com/94102418/186854111-9a0de8da-f072-4bee-a4a7-49023ea05324.jpg) 23 | 24 | ![88f0fb21be6ff8491a5d866927191d9](https://user-images.githubusercontent.com/94102418/186854399-c57f386d-fcab-4c2c-a6bd-77d22da6023c.jpg) 25 | 26 | 27 | # 待完善架构图 28 | 29 | image 30 | 31 | ![image](https://user-images.githubusercontent.com/94102418/186810557-059342d8-2393-491d-80a3-3d331aadb49e.png) 32 | 33 | -------------------------------------------------------------------------------- /团队贡献度.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/团队贡献度.xlsx -------------------------------------------------------------------------------- /大众评审表-项目答辩.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/大众评审表-项目答辩.xlsx -------------------------------------------------------------------------------- /答辩PPT-WeTalk聊天软件1组.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiaoyao/WeTalk/f3b1d7849ead86bcbd65cdcad8545d9cb235f23e/答辩PPT-WeTalk聊天软件1组.pptx --------------------------------------------------------------------------------