Răsfoiți Sursa

其他消息展现

fancy 5 ani în urmă
părinte
comite
71078c3ba4

+ 8 - 0
o2ios/O2Platform.xcodeproj/project.pbxproj

@@ -130,6 +130,8 @@
 		B1489CAE2491FF13009EE9FD /* IMChatEmojiBarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1489CAD2491FF13009EE9FD /* IMChatEmojiBarView.xib */; };
 		B1489CB12492045D009EE9FD /* IMChatEmojiItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1489CAF2492045D009EE9FD /* IMChatEmojiItemCell.swift */; };
 		B1489CB22492045D009EE9FD /* IMChatEmojiItemCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1489CB02492045D009EE9FD /* IMChatEmojiItemCell.xib */; };
+		B1489CB624935104009EE9FD /* IMInstantMessageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1489CB424935104009EE9FD /* IMInstantMessageViewController.swift */; };
+		B1489CB724935104009EE9FD /* IMInstantMessageViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1489CB524935104009EE9FD /* IMInstantMessageViewController.xib */; };
 		B14B339F2356EB1500442968 /* CloudFileViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14B339E2356EB1500442968 /* CloudFileViewModel.swift */; };
 		B14E07532301137F00AE85A0 /* ContactPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14E07522301137F00AE85A0 /* ContactPickerViewController.swift */; };
 		B14E07862301418400AE85A0 /* ContactUnitPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14E07852301418400AE85A0 /* ContactUnitPickerViewController.swift */; };
@@ -1455,6 +1457,8 @@
 		B1489CAD2491FF13009EE9FD /* IMChatEmojiBarView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = IMChatEmojiBarView.xib; sourceTree = "<group>"; };
 		B1489CAF2492045D009EE9FD /* IMChatEmojiItemCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMChatEmojiItemCell.swift; sourceTree = "<group>"; };
 		B1489CB02492045D009EE9FD /* IMChatEmojiItemCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = IMChatEmojiItemCell.xib; sourceTree = "<group>"; };
+		B1489CB424935104009EE9FD /* IMInstantMessageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMInstantMessageViewController.swift; sourceTree = "<group>"; };
+		B1489CB524935104009EE9FD /* IMInstantMessageViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = IMInstantMessageViewController.xib; sourceTree = "<group>"; };
 		B14B339E2356EB1500442968 /* CloudFileViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudFileViewModel.swift; sourceTree = "<group>"; };
 		B14E07522301137F00AE85A0 /* ContactPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactPickerViewController.swift; sourceTree = "<group>"; };
 		B14E07852301418400AE85A0 /* ContactUnitPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactUnitPickerViewController.swift; sourceTree = "<group>"; };
@@ -2582,6 +2586,8 @@
 				B1173A692488D4AD005075F0 /* O2IM.swift */,
 				B1489B1A248E0F4D009EE9FD /* IMChatViewController.swift */,
 				B1489B1B248E0F4D009EE9FD /* IMChatViewController.xib */,
+				B1489CB424935104009EE9FD /* IMInstantMessageViewController.swift */,
+				B1489CB524935104009EE9FD /* IMInstantMessageViewController.xib */,
 			);
 			path = "IM-聊天";
 			sourceTree = "<group>";
@@ -5643,6 +5649,7 @@
 				E46E6C6F1DD41F5D00AB7561 /* ZSSclearstyle.png in Resources */,
 				E46E6CB11DD41F5D00AB7561 /* ZSSunorderedlist.png in Resources */,
 				E40E246D20B68AF6009F8BE7 /* OOAttandanceTotalHeaderView.xib in Resources */,
+				B1489CB724935104009EE9FD /* IMInstantMessageViewController.xib in Resources */,
 				E4C24C1720844F5200E426B0 /* yh_image_no_picked@3x.png in Resources */,
 				E46E6C7E1DD41F5D00AB7561 /* ZSSh5@2x.png in Resources */,
 				E46E6C871DD41F5D00AB7561 /* ZSSindent.png in Resources */,
@@ -5988,6 +5995,7 @@
 				E4CB27721E78D5B1004A7ACB /* UINavigationBar+Flat.swift in Sources */,
 				E45DA90C1DAF76C800E0735D /* SCommonViewController.swift in Sources */,
 				B108F401229E34D400778050 /* LBXScanWrapper.swift in Sources */,
+				B1489CB624935104009EE9FD /* IMInstantMessageViewController.swift in Sources */,
 				E40C41F21E83715C00568805 /* ZLUISearchBar.swift in Sources */,
 				E4B888791D9D48F1002E1A46 /* Person.swift in Sources */,
 				E4C24BA820844F3C00E426B0 /* JCChatViewController.swift in Sources */,

+ 22 - 0
o2ios/O2Platform/App/IM-聊天/IMChatViewController.swift

@@ -74,6 +74,27 @@ class IMChatViewController: UIViewController {
         }
         //获取聊天数据
         self.loadMsgList(page: page)
+        //阅读
+        self.viewModel.readConversation(conversationId: self.conversation?.id)
+    }
+    
+    override func viewWillAppear(_ animated: Bool) {
+         NotificationCenter.default.addObserver(self, selector: #selector(receiveMessageFromWs(notice:)), name: OONotification.websocket.notificationName, object: nil)
+    }
+    override func viewWillDisappear(_ animated: Bool) {
+        NotificationCenter.default.removeObserver(self)
+    }
+    
+    
+    @objc private func receiveMessageFromWs(notice: Notification) {
+        DDLogDebug("接收到websocket im 消息")
+        if let message = notice.object as? IMMessageInfo {
+            if message.conversationId == self.conversation?.id {
+                self.chatMessageList.append(message)
+                self.scrollMessageToBottom()
+                self.viewModel.readConversation(conversationId: self.conversation?.id)
+            }
+        }
     }
 
     //获取消息
@@ -131,6 +152,7 @@ class IMChatViewController: UIViewController {
         self.viewModel.sendMsg(msg: message)
             .then { (result)  in
                 DDLogDebug("发送消息成功 \(result)")
+                self.viewModel.readConversation(conversationId: self.conversation?.id)
         }.catch { (error) in
             DDLogError(error.localizedDescription)
             self.showError(title: "发送消息失败!")

+ 1 - 1
o2ios/O2Platform/App/IM-聊天/IMChatViewController.xib

@@ -24,7 +24,7 @@
             <subviews>
                 <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="ZIb-3p-hnq">
                     <rect key="frame" x="0.0" y="44" width="414" height="754"/>
-                    <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
+                    <color key="backgroundColor" red="0.95294117649999999" green="0.95294117649999999" blue="0.95294117649999999" alpha="1" colorSpace="calibratedRGB"/>
                     <color key="sectionIndexBackgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                 </tableView>
                 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sDg-us-Ed9">

+ 65 - 14
o2ios/O2Platform/App/IM-聊天/IMConversationListViewController.swift

@@ -39,24 +39,40 @@ class IMConversationListViewController: UIViewController {
     }()
 
     private var conversationList: [IMConversationInfo] = []
+    private var instantMsgList: [InstantMessage] = []
 
     override func viewDidLoad() {
         super.viewDidLoad()
         view.addSubview(tableview)
         view.addSubview(emptyView)
-        
+
         NotificationCenter.default.addObserver(self, selector: #selector(receiveMessageFromWs(notice:)), name: OONotification.websocket.notificationName, object: nil)
-        
-         
+
+
     }
-    
+
     override func viewWillAppear(_ animated: Bool) {
-        getConversationList()
+        getInstantMsgList()
+    }
+    
+    func getInstantMsgList() {
+        viewModel.getInstantMsgList().then { (list) in
+            self.instantMsgList = list
+            self.getConversationList()
+        }
     }
 
     func getConversationList() {
         viewModel.myConversationList().then { (list) in
             self.conversationList = list
+            var n = 0
+            if !self.conversationList.isEmpty {
+                for item in self.conversationList {
+                    if let number = item.unreadNumber {
+                        n += number
+                    }
+                }
+            }
             DispatchQueue.main.async {
                 if self.conversationList.count > 0 {
                     self.emptyView.isHidden = true
@@ -64,6 +80,7 @@ class IMConversationListViewController: UIViewController {
                     self.emptyView.isHidden = false
                 }
                 self.tableview.reloadData()
+                self.refreshRedPoint(number: n)
             }
 
         }.catch { (err) in
@@ -79,7 +96,7 @@ class IMConversationListViewController: UIViewController {
                 return info.id == message.conversationId
             }) {
                 DDLogDebug("有对应的会话 刷新列表")
-                var newList: [IMConversationInfo]  = []
+                var newList: [IMConversationInfo] = []
                 self.conversationList.forEach { (info) in
                     if message.conversationId != nil && info.id == message.conversationId {
                         info.lastMessage = message
@@ -91,22 +108,35 @@ class IMConversationListViewController: UIViewController {
                 DispatchQueue.main.async {
                     self.tableview.reloadData()
                 }
-            }else {
+            } else {
                 DDLogDebug("没有对应的会话 重新获取会话列表")
-                self.getConversationList()
+                self.getInstantMsgList()
             }
-        }else {
+        } else {
             DDLogError("不正确的消息类型。。。")
         }
     }
-    
-    
+
+
+    private func refreshRedPoint(number: Int) {
+        if number > 0 && number < 100 {
+            self.navigationController?.tabBarItem.badgeValue = "\(number)"
+        } else if number >= 100 {
+            self.navigationController?.tabBarItem.badgeValue = "99.."
+        }else {
+            self.navigationController?.tabBarItem.badgeValue = nil
+        }
+    }
+
 
 }
 
 // MARK: - tableview delegate
 extension IMConversationListViewController: UITableViewDelegate, UITableViewDataSource {
     func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        if self.instantMsgList.count > 0 {
+            return self.conversationList.count + 1
+        }
         return self.conversationList.count
     }
 
@@ -117,17 +147,38 @@ extension IMConversationListViewController: UITableViewDelegate, UITableViewData
         guard let c = cell as? IMConversationItemCell else {
             return
         }
-        c.bindConversation(conversation: self.conversationList[indexPath.row])
+        if self.instantMsgList.count > 0 {
+            if indexPath.row == 0 {
+                c.setInstantContent(item: self.instantMsgList.last!)
+            }else {
+                c.bindConversation(conversation: self.conversationList[indexPath.row - 1])
+            }
+        }else {
+            c.bindConversation(conversation: self.conversationList[indexPath.row])
+        }
     }
     func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
         return 64
     }
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
         DDLogDebug("点击了 row \(indexPath.row)")
+        if self.instantMsgList.count > 0 {
+            if indexPath.row == 0 {
+                let instantView = IMInstantMessageViewController()
+                instantView.instantMsgList = self.instantMsgList
+                self.navigationController?.pushViewController(instantView, animated: true)
+            }else {
+                gotoChatView(row: indexPath.row-1)
+            }
+        }else {
+            gotoChatView(row: indexPath.row)
+        }
+    }
+    
+    private func gotoChatView(row: Int) {
         let chatView = IMChatViewController()
-        chatView.conversation = self.conversationList[indexPath.row]
+        chatView.conversation = self.conversationList[row]
         self.navigationController?.pushViewController(chatView, animated: true)
     }
-    //todo can edit
 
 }

+ 64 - 0
o2ios/O2Platform/App/IM-聊天/IMInstantMessageViewController.swift

@@ -0,0 +1,64 @@
+//
+//  IMInstantMessageViewController.swift
+//  O2Platform
+//
+//  Created by FancyLou on 2020/6/12.
+//  Copyright © 2020 zoneland. All rights reserved.
+//
+
+import UIKit
+
+class IMInstantMessageViewController: UITableViewController {
+        
+    private lazy var viewModel: IMViewModel = {
+           return IMViewModel()
+       }()
+    
+    var instantMsgList: [InstantMessage] = []
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        self.title = "通知消息"
+        self.tableView.register(UINib(nibName: "IMChatMessageViewCell", bundle: nil), forCellReuseIdentifier: "IMChatMessageViewCell")
+        self.tableView.separatorStyle = .none
+        self.tableView.rowHeight = UITableView.automaticDimension
+        self.tableView.estimatedRowHeight = 144
+        self.tableView.backgroundColor = UIColor(hex: "#f3f3f3")
+       
+    }
+    
+    override func viewDidAppear(_ animated: Bool) {
+        self.scrollMessageToBottom()
+    }
+    
+    //刷新tableview 滚动到底部
+    private func scrollMessageToBottom() {
+        DispatchQueue.main.async {
+            if self.instantMsgList.count > 0 {
+                self.tableView.scrollToRow(at: IndexPath(row: self.instantMsgList.count-1, section: 0), at: .bottom, animated: true)
+            }
+        }
+    }
+
+    // MARK: - Table view data source
+
+    override func numberOfSections(in tableView: UITableView) -> Int {
+        return 1
+    }
+
+    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return self.instantMsgList.count
+    }
+
+    
+    
+    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        if let cell = tableView.dequeueReusableCell(withIdentifier: "IMChatMessageViewCell", for: indexPath) as? IMChatMessageViewCell {
+            cell.setInstantContent(item: self.instantMsgList[indexPath.row])
+            return cell
+        }
+
+        return UITableViewCell()
+    }
+    
+}

+ 30 - 0
o2ios/O2Platform/App/IM-聊天/IMInstantMessageViewController.xib

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_1" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="IMInstantMessageViewController" customModule="O2Platform" customModuleProvider="target">
+            <connections>
+                <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
+            </connections>
+        </placeholder>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableView opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" bouncesZoom="NO" style="plain" separatorStyle="none" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="i5M-Pr-FkT">
+            <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
+            <viewLayoutGuide key="safeArea" id="vLr-E1-eTs"/>
+            <color key="sectionIndexBackgroundColor" red="0.95294117649999999" green="0.95294117649999999" blue="0.95294117649999999" alpha="1" colorSpace="calibratedRGB"/>
+            <connections>
+                <outlet property="dataSource" destination="-1" id="Tng-2m-Rnh"/>
+                <outlet property="delegate" destination="-1" id="9aC-8N-iBw"/>
+            </connections>
+            <point key="canvasLocation" x="132" y="123"/>
+        </tableView>
+    </objects>
+</document>

+ 41 - 1
o2ios/O2Platform/App/IM-聊天/IMViewModel.swift

@@ -7,7 +7,7 @@
 //
 
 import Promises
-
+import CocoaLumberjack
 
 class IMViewModel: NSObject {
     override init() {
@@ -19,6 +19,22 @@ class IMViewModel: NSObject {
 }
 
 extension IMViewModel {
+    
+    //阅读会话
+    func readConversation(conversationId: String?) {
+        guard let id = conversationId else {
+            DDLogError("阅读会话失败, 传入id为空")
+            return
+        }
+        self.communicateAPI.request(.readConversation(id), completion: {result in
+            let response = OOResult<BaseModelClass<OOCommonIdModel>>(result)
+            if response.isResultSuccess() {
+                DDLogDebug("阅读当前会话成功!")
+            }else {
+                DDLogError("阅读会话失败!")
+            }
+        })
+    }
 
     //发送消息
     func sendMsg(msg: IMMessageInfo) -> Promise<Bool> {
@@ -79,4 +95,28 @@ extension IMViewModel {
                 })
         }
     }
+    
+    func getInstantMsgList() -> Promise<[InstantMessage]> {
+        return Promise { fulfill, reject in
+            self.communicateAPI.request(.instantMessageList(100), completion: { result in
+                    let response = OOResult<BaseModelClass<[InstantMessage]>>(result)
+                    if response.isResultSuccess() {
+                        if let list = response.model?.data {
+                            //列表翻转
+                            let rList = list.sorted { (f, s) -> Bool in
+                                if let ft = f.createTime, let st = s.createTime {
+                                    return ft.toDate(formatter: "yyyy-MM-dd HH:mm:ss") < st.toDate(formatter: "yyyy-MM-dd HH:mm:ss")
+                                }
+                                return true
+                            }
+                            fulfill(rList)
+                        } else {
+                            reject(OOAppError.apiEmptyResultError)
+                        }
+                    } else {
+                        reject(response.error!)
+                    }
+                })
+        }
+    }
 }

+ 20 - 0
o2ios/O2Platform/App/IM-聊天/Model/IMConversationInfo.swift

@@ -81,3 +81,23 @@ class WsMessage: NSObject, DataModel {
 
     }
 }
+
+//其他消息
+class InstantMessage: NSObject, DataModel {
+    @objc var id: String?
+    @objc var title: String?
+    @objc var type: String?
+    @objc var body: String?
+    @objc var consumerList: [String]?
+    @objc var person: String?
+    var consumed: Bool?
+    @objc var createTime: String?
+    @objc var updateTime: String?
+    
+    required override init() { }
+
+    func mapping(mapper: HelpingMapper) {
+
+    }
+
+}

+ 1 - 1
o2ios/O2Platform/App/IM-聊天/View/IMChatEmojiBarView.swift

@@ -47,7 +47,7 @@ extension IMChatEmojiBarView: UICollectionViewDelegate, UICollectionViewDataSour
     }
     
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
-        return CGSize(width:SCREEN_WIDTH / 8, height: 52)
+        return CGSize(width:SCREEN_WIDTH / 10, height: 42)
     }
     
     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

+ 0 - 4
o2ios/O2Platform/App/IM-聊天/View/IMChatMessageSendViewCell.swift

@@ -12,9 +12,7 @@ class IMChatMessageSendViewCell: UITableViewCell {
     @IBOutlet weak var timeLabel: UILabel!
     @IBOutlet weak var avatarImageView: UIImageView!
     @IBOutlet weak var nameLabel: UILabel!
-     
     @IBOutlet weak var messageBackgroundView: UIView!
-    
     @IBOutlet weak var messageBgWidth: NSLayoutConstraint!
     @IBOutlet weak var messageBgHeight: NSLayoutConstraint!
     
@@ -102,7 +100,6 @@ class IMChatMessageSendViewCell: UITableViewCell {
         label.translatesAutoresizingMaskIntoConstraints = false
         self.messageBackgroundView.addSubview(label)
         let top = NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: label.superview!, attribute: .top, multiplier: 1, constant: 10)
-//        let bottom = NSLayoutConstraint(item: label.superview! , attribute: .bottom, relatedBy: .equal, toItem: label, attribute: .bottom, multiplier: 1, constant: 10)
         let left = NSLayoutConstraint(item: label, attribute: .leading, relatedBy: .equal, toItem: label.superview!, attribute: .leading, multiplier: 1, constant: 10)
         let right = NSLayoutConstraint(item: label.superview!, attribute: .trailing, relatedBy: .equal, toItem: label, attribute: .trailing, multiplier: 1, constant: 10)
         NSLayoutConstraint.activate([top, left, right])
@@ -115,7 +112,6 @@ class IMChatMessageSendViewCell: UITableViewCell {
         label.numberOfLines = 0
         label.lineBreakMode = .byCharWrapping
         label.preferredMaxLayoutWidth = size.width
-        
         return label
     }
     

+ 51 - 2
o2ios/O2Platform/App/IM-聊天/View/IMChatMessageViewCell.swift

@@ -26,9 +26,59 @@ class IMChatMessageViewCell: UITableViewCell {
 
     override func setSelected(_ selected: Bool, animated: Bool) {
         super.setSelected(selected, animated: animated)
-        // Configure the view for the selected state
+    }
+   
+    //普通通知消息
+    func setInstantContent(item: InstantMessage) {
+        if let time = item.createTime {
+            let date = time.toDate(formatter: "yyyy-MM-dd HH:mm:ss")
+            self.timeLabel.text = date.friendlyTime()
+        }
+        if let msg = item.title {
+            textMsgRender(msg: msg)
+        }
+        if let type = item.type {
+            if type.starts(with: "task_") {
+                self.avatarImage.image = UIImage(named: "icon_daiban")
+                self.titleLabel.text = "待办消息"
+            }else if type.starts(with: "taskCompleted_") {
+                self.avatarImage.image = UIImage(named: "icon_taskcompleted")
+                self.titleLabel.text = "已办消息"
+            }else if type.starts(with: "read_") {
+                self.avatarImage.image = UIImage(named: "icon_read")
+                self.titleLabel.text = "待阅消息"
+            }else if type.starts(with: "readCompleted_") {
+                self.avatarImage.image = UIImage(named: "icon_readcompleted")
+                self.titleLabel.text = "已阅消息"
+            }else if type.starts(with: "review_") || type.starts(with: "work_") || type.starts(with: "process_") {
+                self.avatarImage.image = UIImage(named: "icon_daiban")
+                self.titleLabel.text = "工作消息"
+            }else if type.starts(with: "meeting_") {
+                self.avatarImage.image = UIImage(named: "icon_meeting")
+                self.titleLabel.text = "会议消息"
+            }else if type.starts(with: "attachment_") {
+                self.avatarImage.image = UIImage(named: "icon_yunpan")
+                self.titleLabel.text = "云盘消息"
+            }else if type.starts(with: "calendar_") {
+                self.avatarImage.image = UIImage(named: "icon_calendar")
+                self.titleLabel.text = "日历消息"
+            }else if type.starts(with: "cms_") {
+                self.avatarImage.image = UIImage(named: "icon_cms")
+                self.titleLabel.text = "信息中心消息"
+            }else if type.starts(with: "bbs_") {
+                self.avatarImage.image = UIImage(named: "icon_bbs")
+                self.titleLabel.text = "论坛消息"
+            }else if type.starts(with: "mind_") {
+                self.avatarImage.image = UIImage(named: "icon_mindMap")
+                self.titleLabel.text = "脑图消息"
+            }else {
+                self.avatarImage.image = UIImage(named: "icon_email")
+                self.titleLabel.text = "其他消息"
+            }
+        }
     }
     
+    //聊天消息
     func setContent(item: IMMessageInfo) {
         //time
         if let time = item.createTime {
@@ -102,7 +152,6 @@ class IMChatMessageViewCell: UITableViewCell {
         label.translatesAutoresizingMaskIntoConstraints = false
         self.messageBackgroundView.addSubview(label)
         let top = NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: label.superview!, attribute: .top, multiplier: 1, constant: 10)
-//        let bottom = NSLayoutConstraint(item: label.superview! , attribute: .bottom, relatedBy: .equal, toItem: label, attribute: .bottom, multiplier: 1, constant: 10)
         let left = NSLayoutConstraint(item: label, attribute: .leading, relatedBy: .equal, toItem: label.superview!, attribute: .leading, multiplier: 1, constant: 10)
         let right = NSLayoutConstraint(item: label.superview!, attribute: .trailing, relatedBy: .equal, toItem: label, attribute: .trailing, multiplier: 1, constant: 10)
         NSLayoutConstraint.activate([top, left, right])

+ 27 - 7
o2ios/O2Platform/App/IM-聊天/View/IMConversationItemCell.swift

@@ -16,21 +16,33 @@ class IMConversationItemCell: UITableViewCell {
     @IBOutlet weak var nameLabel: UILabel!
     @IBOutlet weak var timeLabel: UILabel!
     @IBOutlet weak var messageLabel: UILabel!
-    
+    @IBOutlet weak var unreadNumberLabel: UILabel!
     @IBOutlet weak var emojiImg: UIImageView!
     
     
     override func awakeFromNib() {
         super.awakeFromNib()
-        // Initialization code
     }
 
     override func setSelected(_ selected: Bool, animated: Bool) {
         super.setSelected(selected, animated: animated)
-
-        // Configure the view for the selected state
     }
     
+    
+    func setInstantContent(item: InstantMessage) {
+        self.avatarImg.image = UIImage(named: "icon_email")
+        self.nameLabel.text = "通知消息"
+        self.messageLabel.isHidden = false
+        self.messageLabel.text = item.title
+        if let time = item.createTime {
+            let date = time.toDate(formatter: "yyyy-MM-dd HH:mm:ss")
+            self.timeLabel.text = date.friendlyTime()
+        }
+        self.emojiImg.isHidden = true
+        self.unreadNumberLabel.isHidden = true
+    }
+
+    
     func bindConversation(conversation: IMConversationInfo) {
         //avatar name
         if conversation.type == o2_im_conversation_type_single {
@@ -75,17 +87,25 @@ class IMConversationItemCell: UITableViewCell {
             }else if body.type == o2_im_msg_type_emoji {
                 self.messageLabel.isHidden = true
                 self.emojiImg.isHidden = false
-//                self.emojiImg.image = UIImage(named: "setting_myCRM")
-                //todo emoji表情导入
                 let bundle = Bundle().o2EmojiBundle(anyClass: IMConversationItemCell.self)
                 let path = o2ImEmojiPath(emojiBody: body.body!)
-                DDLogDebug("path: \(path)")
                 self.emojiImg.image = UIImage(named: path, in: bundle, compatibleWith: nil)
             }else {
                 self.messageLabel.isHidden = true
                 self.emojiImg.isHidden = true
             }
         }
+        //unread number
+        let number = conversation.unreadNumber ?? 0
+        if number > 0 && number < 100 {
+            self.unreadNumberLabel.text = "\(number)"
+            self.unreadNumberLabel.isHidden = false
+        }else if number >= 100 {
+            self.unreadNumberLabel.text = "99.."
+            self.unreadNumberLabel.isHidden = false
+        }else {
+            self.unreadNumberLabel.isHidden = true
+        }
         
     }
     

+ 21 - 1
o2ios/O2Platform/App/IM-聊天/View/IMConversationItemCell.xib

@@ -42,7 +42,7 @@
                         <nil key="highlightedColor"/>
                     </label>
                     <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="这里是消息" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="I3j-lM-VtV" customClass="IMCon">
-                        <rect key="frame" x="70" y="37" width="71.5" height="17"/>
+                        <rect key="frame" x="70" y="37" width="245" height="17"/>
                         <fontDescription key="fontDescription" type="system" pointSize="14"/>
                         <color key="textColor" systemColor="systemGrayColor" red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                         <nil key="highlightedColor"/>
@@ -61,17 +61,36 @@
                             <constraint firstAttribute="height" constant="1" id="szG-5G-RRm"/>
                         </constraints>
                     </view>
+                    <label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="99.." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9Ff-Wo-dWM">
+                        <rect key="frame" x="34" y="10" width="20" height="20"/>
+                        <color key="backgroundColor" systemColor="systemRedColor" red="1" green="0.23137254900000001" blue="0.18823529410000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="20" id="XU3-CN-tXR"/>
+                            <constraint firstAttribute="height" constant="20" id="Yfh-b5-N7U"/>
+                        </constraints>
+                        <fontDescription key="fontDescription" type="system" weight="thin" pointSize="11"/>
+                        <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <nil key="highlightedColor"/>
+                        <userDefinedRuntimeAttributes>
+                            <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                <real key="value" value="10"/>
+                            </userDefinedRuntimeAttribute>
+                        </userDefinedRuntimeAttributes>
+                    </label>
                 </subviews>
                 <constraints>
                     <constraint firstAttribute="bottom" secondItem="I3j-lM-VtV" secondAttribute="bottom" constant="10" id="0mD-L5-TRo"/>
                     <constraint firstAttribute="trailing" secondItem="ckc-YE-j5q" secondAttribute="trailing" id="2LG-5s-Yui"/>
                     <constraint firstItem="pgV-nP-wwV" firstAttribute="leading" secondItem="i51-Sc-dds" secondAttribute="trailing" constant="16" id="4Ao-s1-kAM"/>
                     <constraint firstItem="I3j-lM-VtV" firstAttribute="leading" secondItem="i51-Sc-dds" secondAttribute="trailing" constant="16" id="BFD-a2-hUs"/>
+                    <constraint firstItem="9Ff-Wo-dWM" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="10" id="Frj-Mc-oGy"/>
                     <constraint firstItem="URQ-HD-MDc" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="10" id="IES-nF-jpE"/>
                     <constraint firstAttribute="trailing" secondItem="URQ-HD-MDc" secondAttribute="trailing" constant="5" id="Jeh-u4-Tnd"/>
+                    <constraint firstAttribute="trailing" secondItem="I3j-lM-VtV" secondAttribute="trailing" constant="5" id="PJD-Jd-y3N"/>
                     <constraint firstItem="bOp-mm-qCN" firstAttribute="leading" secondItem="i51-Sc-dds" secondAttribute="trailing" constant="12" id="RCn-dh-ggD"/>
                     <constraint firstItem="i51-Sc-dds" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="10" id="Z9Q-tH-mWi"/>
                     <constraint firstAttribute="bottom" secondItem="i51-Sc-dds" secondAttribute="bottom" constant="10" id="Zew-6c-hWd"/>
+                    <constraint firstItem="9Ff-Wo-dWM" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="34" id="cM0-LZ-1iA"/>
                     <constraint firstItem="bOp-mm-qCN" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="10" id="iUq-ik-5sX"/>
                     <constraint firstAttribute="bottom" secondItem="ckc-YE-j5q" secondAttribute="bottom" id="nX2-Nw-uiy"/>
                     <constraint firstItem="ckc-YE-j5q" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="66" id="t0M-PG-f03"/>
@@ -86,6 +105,7 @@
                 <outlet property="messageLabel" destination="I3j-lM-VtV" id="6z4-xX-xTv"/>
                 <outlet property="nameLabel" destination="bOp-mm-qCN" id="iqM-q2-qcC"/>
                 <outlet property="timeLabel" destination="URQ-HD-MDc" id="NbV-D2-ATA"/>
+                <outlet property="unreadNumberLabel" destination="9Ff-Wo-dWM" id="z3x-wN-Bfv"/>
             </connections>
             <point key="canvasLocation" x="-65.217391304347828" y="-31.473214285714285"/>
         </tableViewCell>

+ 81 - 34
o2ios/O2Platform/App/O2MainController.swift

@@ -30,6 +30,10 @@ class O2MainController: UITabBarController, UITabBarControllerDelegate {
     private let viewModel: OOLoginViewModel = {
         return OOLoginViewModel()
     }()
+    //获取消息数量
+    private lazy var imViewModel: IMViewModel = {
+        return IMViewModel()
+    }()
 
 
     override func viewDidLoad() {
@@ -42,13 +46,15 @@ class O2MainController: UITabBarController, UITabBarControllerDelegate {
         _initControllers()
         selectedIndex = 2
         currentIndex = 2
-        _loginIM()
+//        _loginIM()
         if O2IsConnect2Collect == false {
             //处理内部直连的时候推送的设备绑定
             O2JPushManager.shared.o2JPushBind()
         }
         //连接websocket
         self._startWebsocket()
+        //读取消息
+        self.getConversationList()
     }
 
     deinit {
@@ -71,19 +77,6 @@ class O2MainController: UITabBarController, UITabBarControllerDelegate {
 
     //MARK: -- delegate
     func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
-//        if currentIndex == 2 && tabBarController.selectedIndex == 2 {
-//            if tabBarController.selectedViewController is ZLNavigationController {
-//                (tabBarController.selectedViewController as! ZLNavigationController).viewControllers.forEach { (vc) in
-//                    if vc is MailViewController {
-//                        DDLogDebug("点击了首页 portal")
-//                        (vc as! MailViewController).loadDetailSubject()
-//                    }
-//                    if vc is MainTaskSecondViewController {
-//                        DDLogDebug("点击了首页index")
-//                    }
-//                }
-//            }
-//        }
         self.currentIndex = tabBarController.selectedIndex
     }
 
@@ -93,7 +86,6 @@ class O2MainController: UITabBarController, UITabBarControllerDelegate {
         let conversationVC = IMConversationListViewController()
         conversationVC.title = "消息"
         let messages = ZLNavigationController(rootViewController: conversationVC)
-
         messages.tabBarItem = UITabBarItem(title: "消息", image: UIImage(named: "icon_news_nor"), selectedImage: O2ThemeManager.image(for: "Icon.icon_news_pre"))
 
         //通讯录
@@ -148,28 +140,82 @@ class O2MainController: UITabBarController, UITabBarControllerDelegate {
         }
     }
 
-    private func _loginIM() {
-        viewModel.registerIM().then { (result) in
-            self.viewModel.loginIM().then({ (result) in
-                Log.debug(message: "IM登陆完成")
-            })
-        }.catch { (imError) in
-            let error = imError as! OOLoginError
-            switch error {
-            case .imRegisterFail(let myErr):
-                Log.debug(message: myErr.errorDescription!)
-                self.viewModel.loginIM().then({ (result) in
-                    Log.debug(message: "IM登陆完成")
-                }).catch({ (loginError) in
-                    Log.error(message: "im Login Error \(loginError)")
-                })
-                break
-            default:
-                break
+    
+    // MARK: - IM message
+    func getConversationList() {
+        imViewModel.myConversationList().then { (list) in
+            var n = 0
+            if !list.isEmpty {
+                for item in list {
+                    if let number = item.unreadNumber {
+                        n += number
+                    }
+                }
+            }
+            DispatchQueue.main.async {
+                DDLogDebug("消息数量: \(n)")
+                if n > 0 && n < 100 {
+                    self.showRedPoint(number: "\(n)")
+                } else if n >= 100 {
+                    self.showRedPoint(number: "99..")
+                }
             }
         }
     }
 
+    private func showRedPoint(number: String) {
+        self.viewControllers?.forEach({ (vc) in
+            if let zl = vc as? ZLNavigationController, zl.tabBarItem?.title == "消息" {
+                zl.tabBarItem.badgeValue = number
+            }
+        })
+    }
+    //消息模块未读消息数量加1
+    private func addUnreadNumber() {
+        self.viewControllers?.forEach({ (vc) in
+            if let zl = vc as? ZLNavigationController, zl.tabBarItem?.title == "消息" {
+                if let badge = zl.tabBarItem.badgeValue {
+                    if badge != "99.." {
+                        if let n = Int(string: badge) {
+                            let number = n + 1
+                            if number > 0 && number < 100 {
+                                self.showRedPoint(number: "\(number)")
+                            } else if number >= 100 {
+                                self.showRedPoint(number: "99..")
+                            }
+                        }
+                    }
+                }else {
+                    self.showRedPoint(number: "1")
+                }
+            }
+        })
+    }
+
+//    private func _loginIM() {
+//        viewModel.registerIM().then { (result) in
+//            self.viewModel.loginIM().then({ (result) in
+//                Log.debug(message: "IM登陆完成")
+//            })
+//        }.catch { (imError) in
+//            let error = imError as! OOLoginError
+//            switch error {
+//            case .imRegisterFail(let myErr):
+//                Log.debug(message: myErr.errorDescription!)
+//                self.viewModel.loginIM().then({ (result) in
+//                    Log.debug(message: "IM登陆完成")
+//                }).catch({ (loginError) in
+//                    Log.error(message: "im Login Error \(loginError)")
+//                })
+//                break
+//            default:
+//                break
+//            }
+//        }
+//    }
+
+    
+    // MARK: - app update 
     private func checkAppVersion() {
         O2VersionManager.shared.checkAppUpdate { (info, error) in
             if let iosInfo = info {
@@ -252,9 +298,10 @@ extension O2MainController: WebSocketDelegate {
                                 DDLogDebug("接收到im消息 发送通知。。")
                                 NotificationCenter.post(customeNotification: OONotification.websocket, object: messageInfo.body)
                             }
+                            self.addUnreadNumber()
                         }
                     }
-                } catch {  }
+                } catch { }
             }
             break
         case .connected(let headers):

+ 26 - 24
o2ios/O2Platform/Extension/Date+Extension.swift

@@ -276,34 +276,36 @@ extension Date {
     func friendlyTime() -> String {
         var returnTimeString = ""
         let now = Date()
-        if now.haveSameYearMonthDayAndHour(self) {
-            let gap = now.minute - self.minute
-            returnTimeString = "\(gap)分钟前"
-        }else if now.haveSameYearMonthAndDay(self) {
-            let gap = now.hour - self.hour
-            returnTimeString = "\(gap)小时前"
-        }else if now.haveSameYearAndMonth(self) {
-            let gap = now.day - self.day
-            if gap == 1 {
-                returnTimeString = "昨天"
-            }else if gap == 2 {
-                returnTimeString = "前天"
-            }else {
-                returnTimeString = "\(gap)天前"
-            }
-        }else if now.haveSameYear(self) {
-            let days = now.betweenDays(self)
-            if days > 30 {
-                let gap = now.month - self.month
-                if gap < 4 {
-                    returnTimeString = "\(gap)个月前"
+        let millisecond = CLongLong(round(self.timeIntervalSince1970*1000))
+        let nowMillisecond = CLongLong(round(now.timeIntervalSince1970*1000))
+        let lt = millisecond / 86400000
+        let ct = nowMillisecond / 86400000
+        let days = Int(ct - lt);
+        if (days == 0) {
+            let hour = Int((nowMillisecond - millisecond) / 3600000)
+            if (hour == 0) {
+                let minuts = Int((nowMillisecond - millisecond) / 60000)
+                if minuts > 1 {
+                    returnTimeString = "\(minuts)分钟前"
                 }else {
-                    returnTimeString = self.formatterDate(formatter: "yyyy-MM-dd")
+                    returnTimeString = "刚刚"
                 }
             }else {
-               returnTimeString = "\(days)天前"
+                returnTimeString = "\(hour)小时前"
             }
-        }else {
+        }else if (days == 1) {
+            returnTimeString = "昨天";
+        } else if (days == 2) {
+            returnTimeString = "前天 ";
+        } else if (days > 2 && days < 31) {
+            returnTimeString = "\(days)天前";
+        } else if (days >= 31 && days <= 2 * 31) {
+            returnTimeString = "一个月前";
+        } else if (days > 2 * 31 && days <= 3 * 31) {
+            returnTimeString = "2个月前";
+        } else if (days > 3 * 31 && days <= 4 * 31) {
+            returnTimeString = "3个月前";
+        } else {
             returnTimeString = self.formatterDate(formatter: "yyyy-MM-dd")
         }
         return returnTimeString

+ 10 - 2
o2ios/O2Platform/Framework/O2API/Communicate/CommunicateAPI.swift

@@ -15,6 +15,8 @@ enum CommunicateAPI {
     case myConversationList
     case msgListByPaging(Int, Int, String)
     case sendMsg(IMMessageInfo)
+    case readConversation(String)
+    case instantMessageList(Int)
     
     
 }
@@ -48,15 +50,21 @@ extension CommunicateAPI: TargetType {
             return "/jaxrs/im/msg/list/\(page)/size/\(size)"
         case .sendMsg(_):
             return "/jaxrs/im/msg"
+        case .readConversation(let conversationId):
+            return "/jaxrs/im/conversation/\(conversationId)/read"
+        case .instantMessageList(let count):
+            return "/jaxrs/instant/list/currentperson/noim/count/\(count)/desc"
         }
     }
     
     var method: Moya.Method {
         switch self {
-        case .myConversationList:
+        case .myConversationList, .instantMessageList(_):
             return .get
         case .msgListByPaging(_, _, _), .sendMsg(_):
             return .post
+        case .readConversation(_):
+            return .put
         }
     }
     
@@ -66,7 +74,7 @@ extension CommunicateAPI: TargetType {
     
     var task: Task {
         switch self {
-        case .myConversationList:
+        case .myConversationList, .instantMessageList(_), .readConversation(_):
             return .requestPlain
         case .msgListByPaging(_, _, let conversationId):
             let form = IMMessageRequestForm()

+ 2 - 2
o2ios/O2Platform/Info.plist

@@ -163,7 +163,7 @@
 		<key>centerContext</key>
 		<string>/x_program_center</string>
 		<key>centerHost</key>
-		<string>qywx.o2oa.net</string>
+		<string>dd.o2oa.net</string>
 		<key>centerPort</key>
 		<integer>20030</integer>
 		<key>httpProtocol</key>
@@ -171,7 +171,7 @@
 		<key>id</key>
 		<string>o2CenterServer</string>
 		<key>name</key>
-		<string>dev</string>
+		<string>dd</string>
 	</dict>
 </dict>
 </plist>