kuaifan 5 years atrás
parent
commit
44a27b44a1

+ 1 - 22
app/Http/Controllers/Api/ChatController.php

@@ -49,33 +49,12 @@ class ChatController extends Controller
         }
         foreach ($lists AS $key => $item) {
             $lists[$key] = array_merge($item, Users::username2basic($item['user1'] == $user['username'] ? $item['user2'] : $item['user1']));
+            $lists[$key]['unread'] = $item['user1'] == $user['username'] ? $item['unread1'] : $item['unread2'];
             $lists[$key]['lastdate'] = $item['lastdate'] ?: $item['indate'];
         }
         return Base::retSuccess('success', $lists);
     }
 
-
-    /**
-     * 添加/创建对话
-     *
-     * @apiParam {String} username             用户名
-     */
-    public function dialog__add()
-    {
-        $user = Users::authE();
-        if (Base::isError($user)) {
-            return $user;
-        } else {
-            $user = $user['data'];
-        }
-        //
-        $target = Users::username2basic(trim(Request::input('username')));
-        if (empty($target)) {
-            return Base::retError('用户不存在');
-        }
-        return Chat::openDialog($user['username'], $target['username']);
-    }
-
     /**
      * 消息列表
      *

+ 11 - 3
app/Module/Chat.php

@@ -19,6 +19,9 @@ class Chat
      */
     public static function openDialog($username, $receive)
     {
+        if (!$username || !$receive) {
+            return Base::retError('参数错误!');
+        }
         $cacheKey = $username . "@" . $receive;
         $result = Cache::remember($cacheKey, now()->addMinutes(10), function() use ($receive, $username) {
             $row = Base::DBC2A(DB::table('chat_dialog')->where([
@@ -26,6 +29,7 @@ class Chat
                 'user2' => $receive,
             ])->first());
             if ($row) {
+                $row['recField'] = 2;   //接受信息用户的字段位置
                 return Base::retSuccess('already1', $row);
             }
             $row = Base::DBC2A(DB::table('chat_dialog')->where([
@@ -33,6 +37,7 @@ class Chat
                 'user2' => $username,
             ])->first());
             if ($row) {
+                $row['recField'] = 1;
                 return Base::retSuccess('already2', $row);
             }
             //
@@ -46,6 +51,7 @@ class Chat
                 'user2' => $receive,
             ])->first());
             if ($row) {
+                $row['recField'] = 2;
                 return Base::retSuccess('success', $row);
             }
             //
@@ -98,10 +104,12 @@ class Chat
                 break;
         }
         if ($lastText) {
-            DB::table('chat_dialog')->where('id', $dialog['id'])->update([
-                'lasttext' => $lastText,
-                'lastdate' => $indate,
+            $upArray = Base::DBUP([
+                ($dialog['recField'] == 1 ? 'unread1' : 'unread2') => 1,
             ]);
+            $upArray['lasttext'] = $lastText;
+            $upArray['lastdate'] = $indate;
+            DB::table('chat_dialog')->where('id', $dialog['id'])->update($upArray);
         }
         $inArray['id'] = DB::table('chat_msg')->insertGetId($inArray);
         $inArray['message'] = $message;

+ 38 - 1
app/Services/WebSocketService.php

@@ -103,6 +103,40 @@ class WebSocketService implements WebSocketHandlerInterface
             'message' => '',
         ];
         switch ($data['type']) {
+            /**
+             * 未读消息总数
+             */
+            case 'unread':
+                $from = self::fd2name($frame->fd);
+                if ($from) {
+                    $num = intval(DB::table('chat_dialog')->where('user1', $from)->sum('unread1'));
+                    $num+= intval(DB::table('chat_dialog')->where('user2', $from)->sum('unread2'));
+                    $feedback['message'] = $num;
+                } else {
+                    $feedback['message'] = 0;
+                }
+                break;
+
+            /**
+             * 已读消息
+             */
+            case 'read':
+                $to = self::name2fd($data['target']);
+                if ($to) {
+                    $dialog = Chat::openDialog(self::fd2name($frame->fd), $data['target']);
+                    if (!Base::isError($dialog)) {
+                        $dialog = $dialog['data'];
+                        $upArray = [
+                            ($dialog['recField'] == 1 ? 'unread2' : 'unread1') => 0,
+                        ];
+                        DB::table('chat_dialog')->where('id', $dialog['id'])->update($upArray);
+                    }
+                }
+                break;
+
+            /**
+             * 发给用户
+             */
             case 'user':
                 $to = self::name2fd($data['target']);
                 if ($to) {
@@ -125,7 +159,10 @@ class WebSocketService implements WebSocketHandlerInterface
                 }
                 break;
 
-            case 'all':
+            /**
+             * 发给整个团队
+             */
+            case 'team':
                 foreach (self::getUsersAll() as $user) {
                     $data['target'] = $user['username'];
                     $server->push($user['wsid'], Base::array2json($data));

+ 18 - 4
resources/assets/js/main/components/WHeader.vue

@@ -27,6 +27,7 @@
                 </Dropdown>
                 <div class="right-info" @click="chatDrawerShow=true">
                     <Icon class="right-mticon" type="md-notifications" size="24"/>
+                    <em v-if="chatUnreadTotal > 0" class="right-info-num">{{chatUnreadTotal}}</em>
                 </div>
                 <Dropdown class="right-info" trigger="click" @on-click="setLanguage" transfer>
                     <div>
@@ -105,10 +106,8 @@
             </Tabs>
         </WDrawer>
         <WDrawer v-model="chatDrawerShow" :closable="false" maxWidth="1080">
-            <chat-index></chat-index>
-            <div class="w-header-chat-close" @click="chatDrawerShow=false">
-                <Icon type="ios-close" />
-            </div>
+            <chat-index v-model="chatUnreadTotal" :openWindow="chatDrawerShow" @on-open-notice="chatDrawerShow=true"></chat-index>
+            <div class="w-header-chat-close" @click="chatDrawerShow=false"><Icon type="ios-close" /></div>
         </WDrawer>
     </div>
 </template>
@@ -200,6 +199,20 @@
                         vertical-align: top;
                         margin-top: 8px;
                     }
+                    .right-info-num {
+                        position: absolute;
+                        top: 2px;
+                        right: -20%;
+                        height: auto;
+                        line-height: normal;
+                        color: #ffffff;
+                        background-color: #ff0000;
+                        text-align: center;
+                        border-radius: 10px;
+                        padding: 1px 5px;
+                        font-size: 12px;
+                        transform: scale(0.9);
+                    }
                 }
             }
         }
@@ -268,6 +281,7 @@
                 },
 
                 chatDrawerShow: false,
+                chatUnreadTotal: 0,
             }
         },
         created() {

+ 170 - 27
resources/assets/js/main/components/chat/Index.vue

@@ -6,8 +6,13 @@
             <li class="self">
                 <img :src="userInfo.userimg">
             </li>
-            <li :class="{active:chatTap=='dialog'}" @click="chatTap='dialog'"><Icon type="md-text" /></li>
-            <li :class="{active:chatTap=='team'}" @click="chatTap='team'"><Icon type="md-person" /></li>
+            <li :class="{active:chatTap=='dialog'}" @click="chatTap='dialog'">
+                <Icon type="md-text" />
+                <em v-if="unreadTotal > 0" class="chat-num">{{unreadTotal}}</em>
+            </li>
+            <li :class="{active:chatTap=='team'}" @click="chatTap='team'">
+                <Icon type="md-person" />
+            </li>
         </ul>
 
         <!--对话列表-->
@@ -27,6 +32,7 @@
                     </div>
                     <div class="user-msg-text">{{dialog.lasttext}}</div>
                 </div>
+                <em v-if="dialog.unread > 0" class="user-msg-num">{{dialog.unread}}</em>
             </li>
             <li v-if="dialogLists.length == 0" class="chat-none">{{dialogNoDataText}}</li>
         </ul>
@@ -65,7 +71,7 @@
                 </div>
                 <div class="manage-lists-message-new" v-if="messageNew > 0" @click="messageBottomGo(true)">有{{messageNew}}条新消息</div>
             </ScrollerY>
-            <div class="manage-send">
+            <div class="manage-send" @click="clickDialog(dialogTarget.username)">
                 <textarea ref="textarea" class="manage-input" v-model="messageText" placeholder="请输入要发送的消息" @keydown="messageSend($event)"></textarea>
             </div>
             <div class="manage-quick">
@@ -95,6 +101,30 @@
     </div>
 </template>
 
+<style lang="scss">
+    .chat-notice-box {
+        display: flex;
+        align-items: flex-start;
+        .chat-notice-userimg {
+            width: 42px;
+            height: 42px;
+            border-radius: 4px;
+        }
+        .ivu-notice-with-desc {
+            flex: 1;
+            padding: 0 12px;
+        }
+        .ivu-notice-desc {
+            word-break:break-all;
+            line-height: 1.3;
+            text-overflow: ellipsis;
+            display: -webkit-box;
+            -webkit-line-clamp: 2;
+            overflow:hidden;
+            -webkit-box-orient: vertical;
+        }
+    }
+</style>
 <style lang="scss" scoped>
     .chat-index {
         display: flex;
@@ -121,6 +151,7 @@
             height: 100%;
             padding-top: 20px;
             li {
+                position: relative;
                 padding: 12px 0;
                 text-align: center;
                 font-size: 28px;
@@ -138,6 +169,20 @@
                     color: #ffffff;
                     background-color: rgba(255, 255, 255, 0.06);
                 }
+                .chat-num {
+                    position: absolute;
+                    top: 50%;
+                    left: 50%;
+                    height: auto;
+                    line-height: normal;
+                    color: #ffffff;
+                    background-color: #ff0000;
+                    text-align: center;
+                    border-radius: 10px;
+                    padding: 1px 5px;
+                    font-size: 12px;
+                    transform: scale(0.9) translate(5px, -20px);
+                }
             }
         }
         .chat-user {
@@ -211,6 +256,21 @@
                         text-overflow: ellipsis;
                     }
                 }
+                .user-msg-num {
+                    position: absolute;
+                    top: 6px;
+                    left: 44px;
+                    height: auto;
+                    line-height: normal;
+                    color: #ffffff;
+                    background-color: #ff0000;
+                    text-align: center;
+                    border-radius: 10px;
+                    padding: 1px 5px;
+                    font-size: 12px;
+                    transform: scale(0.9);
+                    border: 1px solid #ffffff;
+                }
             }
         }
         .chat-team {
@@ -454,6 +514,15 @@
     export default {
         name: 'ChatIndex',
         components: {ImgUpload, ChatMessage, EmojiPicker, ScrollerY, DrawerTabsContainer},
+        props: {
+            value: {
+                default: 0
+            },
+            openWindow: {
+                type: Boolean,
+                default: false
+            },
+        },
         data () {
             return {
                 loadIng: 0,
@@ -476,6 +545,8 @@
                 messageLists: [],
                 messageNoDataText: '',
                 messageEmojiSearch: '',
+
+                unreadTotal: 0,
             }
         },
 
@@ -486,9 +557,27 @@
         },
 
         mounted() {
-            this.userInfo = $A.getUserInfo((res) => {
-                this.userInfo = res;
+            let resCall = () => {
+                if ($A.getToken() === false) {
+                    return;
+                }
+                $A.WS.sendTo('unread', (res) => {
+                    if (res.status === 1) {
+                        this.unreadTotal = $A.runNum(res.message);
+                    } else {
+                        this.unreadTotal = 0;
+                    }
+                });
+                this.getDialogLists();
+                this.messageBottomAuto();
+            };
+            this.userInfo = $A.getUserInfo((res, isLogin) => {
+                if (this.userInfo.id != res.id) {
+                    this.userInfo = res;
+                    resCall();
+                }
             }, false);
+            resCall();
             //
             $A.WS.setOnMsgListener("chat/index", (msgDetail) => {
                 if (msgDetail.sender == $A.getUserName()) {
@@ -511,20 +600,43 @@
                         lasttext = '[未知类型]';
                         break;
                 }
-                this.openDialog({
+                this.addDialog({
                     username: data.username,
                     userimg: data.userimg,
-                }, {
                     lasttext: lasttext,
                     lastdate: data.indate
-                });
+                }, msgDetail.sender != this.dialogTarget.username || !this.openWindow);
                 if (msgDetail.sender == this.dialogTarget.username) {
                     this.addMessageData(data, true);
                 }
-            })
-            //
-            this.getDialogLists();
-            this.messageBottomAuto();
+                if (!this.openWindow) {
+                    this.$Notice.close('chat-notice');
+                    this.$Notice.open({
+                        name: 'chat-notice',
+                        duration: 0,
+                        render: h => {
+                            return h('div', {
+                                class: 'chat-notice-box',
+                                on: {
+                                    click: () => {
+                                        this.$Notice.close('chat-notice');
+                                        this.$emit("on-open-notice", data.username);
+                                        this.clickDialog(data.username);
+                                    }
+                                }
+                            }, [
+                                h('img', { class: 'chat-notice-userimg', attrs: { src: data.userimg } }),
+                                h('div', { class: 'ivu-notice-with-desc' }, [
+                                    h('div', { class: 'ivu-notice-title' }, [
+                                        h('UserView', { props: { username: data.username } })
+                                    ]),
+                                    h('div', { class: 'ivu-notice-desc' }, lasttext)
+                                ])
+                            ])
+                        }
+                    });
+                }
+            });
         },
 
         watch: {
@@ -539,6 +651,15 @@
                     });
                 }
             },
+
+            unreadTotal(val) {
+                if (val < 0) {
+                    this.unreadTotal = 0;
+                    return;
+                }
+                this.$emit('input', val);
+            },
+
             dialogTarget: {
                 handler: function () {
                     this.getDialogMessage();
@@ -667,18 +788,38 @@
                 });
             },
 
-            openDialog(user, lastMsg) {
-                if (typeof lastMsg === "object") {
-                    user = Object.assign(user, lastMsg);
-                    this.dialogLists = this.dialogLists.filter((item) => {return item.username != user.username});
+            addDialog(data, plusUnread = false) {
+                if (!data.username) {
+                    return;
                 }
-                let lists = this.dialogLists.filter((item) => {return item.username == user.username});
-                if (lists.length === 0) {
-                    this.dialogLists.unshift(user);
+                let lists = this.dialogLists.filter((item) => {return item.username == data.username});
+                if (lists.length > 0) {
+                    data.unread = $A.runNum(lists[0].unread);
+                    this.dialogLists = this.dialogLists.filter((item) => {return item.username != data.username});
+                } else {
+                    data.unread = 0;
                 }
-                if (typeof lastMsg !== "object") {
-                    this.chatTap = 'dialog';
-                    this.dialogTarget = user;
+                if (plusUnread) {
+                    data.unread += 1;
+                    this.unreadTotal += 1;
+                }
+                this.dialogLists.unshift(data);
+            },
+
+            openDialog(user) {
+                this.chatTap = 'dialog';
+                this.dialogTarget = user;
+                if (typeof user.unread === "number" && user.unread > 0) {
+                    this.unreadTotal -= user.unread;
+                    this.$set(user, 'unread', 0);
+                    $A.WS.sendTo('read', user.username);
+                }
+            },
+
+            clickDialog(username) {
+                let lists = this.dialogLists.filter((item) => {return item.username == username});
+                if (lists.length > 0) {
+                    this.openDialog(lists[0]);
                 }
             },
 
@@ -703,7 +844,7 @@
                         }
                         this.messageBottomAuto();
                     }
-                }, 1200);
+                }, 1000);
             },
 
             messageBottomGo(animation = false) {
@@ -735,10 +876,11 @@
                             this.$set(data, res.status === 1 ? 'id' : 'error', res.message)
                         });
                         //
-                        this.openDialog(this.dialogTarget, {
+                        this.addDialog(Object.assign(this.dialogTarget, {
                             lasttext: '[图片]',
                             lastdate: data.indate
-                        });
+                        }));
+                        this.openDialog(this.dialogTarget);
                         this.addMessageData(data, true);
                     }
                 }
@@ -791,10 +933,11 @@
                         this.$set(data, res.status === 1 ? 'id' : 'error', res.message)
                     });
                     //
-                    this.openDialog(this.dialogTarget, {
+                    this.addDialog(Object.assign(this.dialogTarget, {
                         lasttext: text,
                         lastdate: data.indate
-                    });
+                    }));
+                    this.openDialog(this.dialogTarget);
                     this.addMessageData(data, true);
                 }
                 this.$nextTick(() => {

+ 29 - 11
resources/assets/js/main/main.js

@@ -270,7 +270,7 @@ import '../../sass/main.scss';
                 }
             }
             if (sendToWS === true) {
-                $A.WS.sendTo('all', null, {
+                $A.WS.sendTo('team', null, {
                     type: "task",
                     act: act,
                     taskDetail: taskDetail
@@ -313,6 +313,10 @@ import '../../sass/main.scss';
                     return;
                 }
 
+                if ($A.getToken() === false) {
+                    return;
+                }
+
                 if (this.__instance !== null && force !== true) {
                     return;
                 }
@@ -389,28 +393,42 @@ import '../../sass/main.scss';
 
             /**
              * 发送消息
-             * @param type      会话类型:user:指定target、all:所有会员
-             * @param target    接收方的标识,type=all时此项无
+             * @param type      会话类型:user:指定target、team:团队会员
+             * @param target    接收方的标识,仅type=user时有
              * @param content   发送内容
              * @param callback  发送回调
-             * @param isAgain
+             * @param againNum
              */
-            sendTo(type, target, content, callback, isAgain = false) {
+            sendTo(type, target, content, callback, againNum = 0) {
+                if (typeof target === "function") {
+                    content = target;
+                    target = null;
+                }
+                if (typeof content === "function") {
+                    callback = content;
+                    content = null;
+                }
+                //
                 if (this.__instance === null) {
-                    console.log("[WS] 服务未连接");
-                    if (isAgain === true) {
+                    console.log("[WS] 服务未连接: " + againNum);
+                    if (againNum >= 12) {
                         typeof callback === "function" && callback({status: 0, message: '服务未连接'});
-                        return;
+                    } else {
+                        setTimeout(() => {
+                            this.sendTo(type, target, content, callback, $A.runNum(againNum) + 1)
+                        }, 500);
+                        if (againNum === 0) {
+                            this.connection();
+                        }
                     }
-                    this.connection();
-                    setTimeout(() => { this.sendTo(type, target, content, callback, true); }, 6000);
+                    return;
                 }
                 if (this.__connected === false) {
                     console.log("[WS] 未连接成功");
                     typeof callback === "function" && callback({status: 0, message: '未连接成功'});
                     return;
                 }
-                if (['user', 'all'].indexOf(type) === -1) {
+                if (['unread', 'read', 'user', 'team'].indexOf(type) === -1) {
                     console.log("[WS] 错误的消息类型-" + type);
                     typeof callback === "function" && callback({status: 0, message: '错误的消息类型-' + type});
                     return;