├── .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 |
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 |
18 |
19 |
20 |
21 |
22 | 
23 |
24 | 
25 |
26 |
27 | # 待完善架构图
28 |
29 |
30 |
31 | 
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
--------------------------------------------------------------------------------