preface
Some time ago, MUI was used to make an APP, and the customer put forward ideas on whether to add a chat function to ensure the communication of all participating roles in the work order system.
OK, arrange!
Chat layout analysis
We need to implement a chat window, and we need to give priority to the composition of its layout.
Referring to QQ or wechat chat groups, we can see that they have the following common points:
- 1. The group chat window needs an information display window.
- 2. Messages sent by yourself are displayed on the right, and chat messages sent by others are displayed on the left.
- 3. There needs to be an input box to trigger the soft keyboard, and a send button.
- 4. Everyone has different information and needs to use token for judgment.
In addition, voice chat and animated expressions are not considered at present. Let's first realize a simple chat window and information display.
Page layout
In the interface template of MUI, there is an IM chat HTML chat page layout case, with reference to its functions, get rid of cumbersome other function options.
Message presentation template
In the case, there is a template class. When the message is sent by itself, the information will be displayed and displayed on the right side of the screen, otherwise it will be displayed on the left side of the screen.
<script id='msg-template' type="text/template"> <!--Traversal message array object--> <% for(var i in record){ var item=record[i]; %> <!-- Get the attribute parameter information in the object, judge the generator of the current message, and save other data information at the same time --> <div class="msg-item <%= (item.sender=='self'?' msg-item-self':'') %>" msg-type='<%=(item.type)%>' msg-content='<%=(item.content)%>'> <!--Message time display--> <% if(item.time){ %> <div class="" style="text-align: center;"> <span style="color: #C7C7CC;font-size: 12px;" id="chatTime"> <%= item.time %> </span> </div> <% } %> <!--Chat content Avatar--> <% if(item.sender=='self' ) { %> <i class="msg-user"> <span style="display: block;font-size: 10px;overflow:hidden;line-height: 30px;" > <%= item.username %> </span> </i> <% } else { %> <!--<img class="msg-user-img" src="../images/logo.png" alt="" />--> <i class="msg-user-img"> <span style="display: block;font-size: 10px;overflow:hidden;line-height: 30px;"> <%= item.username %> </span> </i> <% } %> <!--Chat content--> <div class="msg-content"> <div class="msg-content-inner"> <% if(item.type=='text' ) { %> <%=( item.content|| ' ') %> <% } %> </div> <!--div Chat content border layout--> <div class="msg-content-arrow"></div> </div> <!--There is a space between each chat message--> <div class="mui-item-clear"></div> </div> <% } %> </script>
Message display and message input box, send button layout
<!--Intermediate content display--> <div class="mui-content"> <div id='msg-list'> </div> </div> <!--bottom--> <footer> <!--picture--> <div class="footer-left"> <!--Remove the photo function and keep the layout--> </div> <!--Input box--> <div class="footer-center"> <textarea id='msg-text' type="text" class='input-text'></textarea> </div> <!--Icon click event is label label--> <label for="" class="footer-right"> <i id='msg-type' class="mui-icon mui-icon-paperplane"></i> </label> </footer>
Complete page layout and logical analysis
The ui intercepted above shows the layout considerations, and then shows the complete layout and other js operation logic.
<!doctype html> <html> <head> <meta charset="UTF-8"> <title></title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <link href="../css/mui.min.css" rel="stylesheet" /> <style type="text/css"> html, body { height: 100%; margin: 0px; padding: 0px; overflow: hidden; -webkit-touch-callout: none; -webkit-user-select: none; } footer { position: fixed; width: 100%; height: 50px; min-height: 50px; border-top: solid 1px #bbb; left: 0px; bottom: 0px; overflow: hidden; padding: 0px 50px; background-color: #fafafa; } .footer-left { position: absolute; width: 50px; height: 50px; left: 0px; bottom: 0px; text-align: center; vertical-align: middle; line-height: 100%; padding: 12px 4px; } .footer-right { position: absolute; width: 50px; height: 50px; right: 0px; bottom: 0px; text-align: center; vertical-align: middle; line-height: 100%; padding: 12px 5px; display: inline-block; } .footer-center { height: 100%; padding: 5px 0px; } .footer-center [class*=input] { width: 100%; height: 100%; border-radius: 5px; } .footer-center .input-text { background: #fff; border: solid 1px #ddd; padding: 10px !important; font-size: 16px !important; line-height: 18px !important; font-family: verdana !important; overflow: hidden; } .footer-center .input-sound { background-color: #eee; } .mui-content { height: 100%; padding: 44px 0px 50px 0px; /*When there are many messages, add a scroll bar*/ overflow: auto; background-color: #eaeaea; } #msg-list { height: 100%; overflow: auto; -webkit-overflow-scrolling: touch; } .msg-item { padding: 8px; clear: both; } .msg-item .mui-item-clear { clear: both; } /*.msg-item .msg-user { width: 38px; height: 38px; border: solid 1px #d3d3d3; display: inline-block; background: #fff; border-radius: 3px; vertical-align: top; text-align: center;*/ /*Messages not sent by yourself float to the left*/ /*float: left; padding: 3px; color: #ddd; }*/ .msg-item .msg-user { width: 40px; height: 40px; border: solid 1px #d3d3d3; display: inline-block; background: #fff; border-radius: 4px; vertical-align: top; text-align: center; /*Messages not sent by yourself float to the left*/ float: left; padding: 3px; background-color: #ddd; } .msg-item .msg-user-img { width: 40px; height: 40px; border: solid 1px #d3d3d3; display: inline-block; border-radius: 4px; vertical-align: top; text-align: center; float: left; padding: 3px; background-color: #ddd; } .msg-item .msg-content { display: inline-block; border-radius: 5px; border: solid 1px #d3d3d3; background-color: #FFFFFF; color: #333; padding: 8px; vertical-align: top; font-size: 15px; position: relative; margin: 0px 8px; max-width: 75%; min-width: 35px; float: left; } .msg-item .msg-content .msg-content-inner { overflow-x: hidden; } .msg-item .msg-content .msg-content-arrow { position: absolute; border: solid 1px #d3d3d3; border-right: none; border-top: none; background-color: #FFFFFF; width: 10px; height: 10px; left: -5px; top: 12px; -webkit-transform: rotateZ(45deg); transform: rotateZ(45deg); } /*Self sending caution, and finally float to the right*/ .msg-item-self .msg-user, .msg-item-self .msg-content { float: right; } .msg-item-self .msg-content .msg-content-arrow { left: auto; right: -5px; -webkit-transform: rotateZ(225deg); transform: rotateZ(225deg); } .msg-item-self .msg-content, .msg-item-self .msg-content .msg-content-arrow { background-color: #4CD964; color: #fff; border-color: #2AC845; } footer .mui-icon { color: #000; } footer .mui-icon:active { color: #007AFF !important; } /*The pseudo class before indicates that the content content is placed before the class content*/ footer .mui-icon-paperplane:before { content: "send out"; } footer .mui-icon-paperplane { /*-webkit-transform: rotateZ(45deg); transform: rotateZ(45deg);*/ font-size: 16px; word-break: keep-all; line-height: 100%; padding-top: 6px; color: rgba(0, 135, 250, 1); } /*voice*/ #msg-sound { -webkit-user-select: none !important; user-select: none !important; } @-webkit-keyframes spin { 0% { -webkit-transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); } } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } #h { background: #fff; border: solid 1px #ddd; padding: 10px !important; font-size: 16px !important; font-family: verdana !important; line-height: 18px !important; overflow: visible; position: absolute; left: -1000px; right: 0px; word-break: break-all; word-wrap: break-word; } .cancel { background-color: darkred; } </style> <header class="mui-bar mui-bar-nav"> <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a> <!--Name information of each role in the chat group(Super long use"..."replace)--> <h1 class="mui-title" id="headerText"></h1> </header> </head> <body contextmenu="return false;"> <pre id='h'></pre> <script id='msg-template' type="text/template"> <% for(var i in record){ var item=record[i]; %> <div class="msg-item <%= (item.sender=='self'?' msg-item-self':'') %>" msg-type='<%=(item.type)%>' msg-content='<%=(item.content)%>'> <!--Message time display--> <% if(item.time){ %> <div class="" style="text-align: center;"> <span style="color: #C7C7CC;font-size: 12px;" id="chatTime"> <%= item.time %> </span> </div> <% } %> <!--Chat content Avatar--> <% if(item.sender=='self' ) { %> <i class="msg-user"> <span style="display: block;font-size: 10px;overflow:hidden;line-height: 30px;" > <%= item.username %> </span> </i> <% } else { %> <!--<img class="msg-user-img" src="../images/logo.png" alt="" />--> <i class="msg-user-img"> <span style="display: block;font-size: 10px;overflow:hidden;line-height: 30px;"> <%= item.username %> </span> </i> <% } %> <!--Chat content--> <div class="msg-content"> <div class="msg-content-inner"> <% if(item.type=='text' ) { %> <%=( item.content|| ' ') %> <% } %> </div> <!--div Chat content border layout--> <div class="msg-content-arrow"></div> </div> <!--There is a space between each chat message--> <div class="mui-item-clear"></div> </div> <% } %> </script> <!--Intermediate content display--> <div class="mui-content"> <div id='msg-list'> </div> </div> <!--bottom--> <footer> <!--picture--> <div class="footer-left"> <!--Remove the photo function and keep the layout--> </div> <!--Input box--> <div class="footer-center"> <textarea id='msg-text' type="text" class='input-text'></textarea> </div> <!--Icon click event is label label--> <label for="" class="footer-right"> <i id='msg-type' class="mui-icon mui-icon-paperplane"></i> </label> </footer> <script src="../js/apps.js" type="text/javascript" charset="utf-8"></script> <script src="../js/moment.min.js" type="text/javascript" charset="utf-8"></script> <script src="../js/mui.min.js"></script> <script src="../js/arttmpl.js"></script> <script type="text/javascript" charset="utf-8"> (function($, doc) { $.init({ //Gesture event gestureConfig: { tap: true, //The default is true doubletap: true, //The default is false longtap: true, //The default is false swipe: true, //The default is true drag: true, //The default is true hold: true, //The default value is false. Do not listen release: true //The default value is false. Do not listen } }); //js/arttmpl.js must import the JS file template.config('escape', false); $.plusReady(function() { var self = plus.webview.currentWebview(); var workId = self.workId; var workName = self.workName; //The title section of the chat box displays the task name if(typeof(workName) == "undefined") { mui.alert("Failed to get the task name. Please re-enter this page", 'reminder','determine',function(){},'div'); return; } if(workName.length > 6) { workName = workName.substring(0, 5) + "..."; console.log("Modify the task name after splicing:" + workName); } document.getElementById("headerText").innerText = workName; //Define a global array, and store the id in the array every time a message is received or sent successfully (avoid repeatedly requesting the server to obtain data information) var chatMsgIdArray = new Array(); // Input box soft keyboard style plus.webview.currentWebview().setStyle({ softinputMode: "adjustResize" }); //The server requests message recording first getChatMsg(workId, ""); //Scheduled refresh (the current maximum dialog id value is passed in each time) // In principle, it is better to use websocket technology here setInterval(function() { getChatMsg(workId, ""); }, 3500); //Display keyboard // var showKeyboard = function() { // var Context = plus.android.importClass("android.content.Context"); // var InputMethodManager = plus.android.importClass("android.view.inputmethod.InputMethodManager"); // var main = plus.android.runtimeMainActivity(); // var imm = main.getSystemService(Context.INPUT_METHOD_SERVICE); // imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED); // //var view = ((ViewGroup)main.findViewById(android.R.id.content)).getChildAt(0); // imm.showSoftInput(main.getWindow().getDecorView(), InputMethodManager.SHOW_IMPLICIT); // //alert("ll"); // }; //Record information (message object body, including message sender and sent content, message style, etc. at present, only text is considered) var record = [ //Display format /*{ sender: 'zs', type: 'text', content: 'Hi,I'm MUI housekeeper! " }*/ ]; // It is a bit similar to object-oriented. Here, each node object is obtained, saved and encapsulated var ui = { body: doc.querySelector('body'), footer: doc.querySelector('footer'), footerRight: doc.querySelector('.footer-right'), btnMsgType: doc.querySelector('#msg-type'), boxMsgText: doc.querySelector('#msg-text'), areaMsgList: doc.querySelector('#msg-list'), h: doc.querySelector('#h'), content: doc.querySelector('.mui-content') }; // Set segmentation for each message ui.h.style.width = ui.boxMsgText.offsetWidth + 'px'; //alert(ui.boxMsgText.offsetWidth );--261 var footerPadding = ui.footer.offsetHeight - ui.boxMsgText.offsetHeight; //Display the message content function and recalculate the scrollTop property value of the message display area var bindMsgList = function() { //First call template processing (avoid garbled code) to display the information in areaMsgList //MSG template the id of the script that traverses the loop ui.areaMsgList.innerHTML = template('msg-template', { "record": record }); //The data should be displayed to the latest message after each transmission ui.areaMsgList.scrollTop = ui.areaMsgList.scrollHeight + ui.areaMsgList.offsetHeight; }; // Call the function to display the content bindMsgList(); //Monitoring when the interface window changes window.addEventListener('resize', function() { ui.areaMsgList.scrollTop = ui.areaMsgList.scrollHeight + ui.areaMsgList.offsetHeight; }, false); //Send message function var send = function(msg) { // The message object array holds the new message information record.push(msg); // Send the message and recalculate the scrollTop property bindMsgList(); //Server communication (multi hot group chat, messages need to be delivered to the server) toRobot(msg.content); }; //The server requests to send a chat message var toRobot = function(info) { //Send conversation console.log("Send conversation content:" + info); mui.ajax(config.host1 + "/work/workdialog/chat", { data: { workid: workId, content: info }, dataType: "json", type: "post", timeout: 10000, headers: { 'Content-Type': 'application/json', 'authorization': localStorage.getItem('accessToken') }, success: function(res) { console.log("Send successfully, return:" + JSON.stringify(res)); // The retransmission mechanism should also be considered here }, error: function(xhr, type, errorThrown) { console.log(type); if(type == "abort") { mui.toast("Please check your network!"); } else if(type == "timeout") { mui.toast("Message sending timeout!"); } } }) }; // Message input box received focus function msgTextFocus() { ui.boxMsgText.focus(); // Delay double reception to ensure that the input box will get the focus setTimeout(function() { ui.boxMsgText.focus(); }, 100); } //Solve the problem that the keyboard is closed due to long pressing the "send" button; ui.footerRight.addEventListener('touchstart', function(event) { if(ui.btnMsgType.classList.contains('mui-icon-paperplane')) { msgTextFocus(); event.preventDefault(); } }); //Solve the problem that the keyboard is closed due to long pressing the "send" button; ui.footerRight.addEventListener('touchmove', function(event) { if(ui.btnMsgType.classList.contains('mui-icon-paperplane')) { msgTextFocus(); event.preventDefault(); } }); //Release gesture listening event after long pressing send button ui.footerRight.addEventListener('release', function(event) { if(ui.btnMsgType.classList.contains('mui-icon-paperplane')) { //showKeyboard(); ui.boxMsgText.focus(); setTimeout(function() { ui.boxMsgText.focus(); }, 150); //event.detail.gesture.preventDefault(); // Get the login account user name information in the current cache var userName = localStorage.getItem('userName'); if(userName.length >= 2){ userName = userName.substring(0,2)+"..."; } //The input of this machine will be displayed on the interface first send({ sender: 'self', type: 'text', //g global matching i case sensitive m multiline matching content: ui.boxMsgText.value.replace(new RegExp('\n', 'gm'), '<br/>'), username: userName, time: moment().format('HH:mm:ss') }); //Clear the data in the input box ui.boxMsgText.value = ''; $.trigger(ui.boxMsgText, 'input', null); } }, false); //Monitor the event information when the content in the input box changes ui.boxMsgText.addEventListener('input', function(event) { //Original code //ui.btnMsgType.classList[ui.boxMsgText.value == '' ? 'remove' : 'add']('mui-icon-paperplane'); //Change remove to add, and "pseudo class" will be displayed regardless of whether there is a value in the input tag -- that is, the word "send" will be displayed ui.btnMsgType.classList['add']('mui-icon-paperplane'); ui.btnMsgType.setAttribute("for", ui.boxMsgText.value == '' ? '' : 'msg-text'); ui.h.innerText = ui.boxMsgText.value.replace(new RegExp('\n', 'gm'), '\n-') || '-'; ui.footer.style.height = (ui.h.offsetHeight + footerPadding) + 'px'; console.log("Important parameters offsetHeight: " + ui.h.offsetHeight); console.log("Important parameters:" + footerPadding); ui.content.style.paddingBottom = ui.footer.style.height; }); var focus = false; ui.boxMsgText.addEventListener('tap', function(event) { ui.boxMsgText.focus(); setTimeout(function() { ui.boxMsgText.focus(); }, 0); focus = true; setTimeout(function() { focus = false; }, 1000); event.detail.gesture.preventDefault(); }, false); //Click the message list to close the keyboard ui.areaMsgList.addEventListener('click', function(event) { if(!focus) { // After sending, the message input box loses focus ui.boxMsgText.blur(); } }) /** * Request server chat message record * @param {Object} taskIDs Task id * @param {Object} dialogIDs Last maximum conversation ID (it needs to be stored in the local database and read here every time) */ function getChatMsg(taskIDs, dialogIDs) { mui.ajax(config.host1 + "/work/workdialog/chat", { data: { workid: taskIDs }, dataType: "json", type: "get", timeout: 10000, headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'authorization': localStorage.getItem('accessToken') }, success: function(res) { console.log(JSON.stringify(res)); if(res.code != 200) { //Sending failed (subsequent processing) return; } // Parsing data information var datas = res.data; // Traversal data information for (var i = 0; i < datas.length; i++) { var obj = datas[i]; // Get message id information var msgId = obj.id; // Determine whether the message already exists in the array if(idWhetherInArray(msgId)){ return; } // If it does not exist in the array, the id information is saved to the array chatMsgIdArray.push(msgId); // Get sender name var user_name = obj.user_name?obj.user_name:"Not yet"; if(user_name.length >= 2){ user_name = user_name.substring(0,2)+"..."; } var content = obj.content; var create_time = obj.create_time; var nowDate = moment().format('YYYY-MM-DD'); var serviceDate = moment.unix(create_time).utcOffset(8).format('YYYY-MM-DD'); //Display the time today and the specific date and time in the future var times; if(nowDate == serviceDate) { times = moment.unix(create_time).utcOffset(8).format('HH:mm:ss'); } else { times = moment.unix(create_time).utcOffset(8).format('YYYY-MM-DD HH:mm:ss'); } // Obtain the distinguishing identifier to judge whether the message is sent by yourself or by others var is_mine = obj.is_mine; if(1 == is_mine){ // Self sent messages record.push({ sender: 'self', type: 'text', content: content, username: user_name, time: times }); }else{ // It is not sent by yourself and is displayed on the left side of the window record.push({ sender: 'zs', type: 'text', content: content, username: user_name, time: times }); } //Without this, it will not be displayed (put it in the judgment -- to avoid that each successful callback will make the layout recalculate, so that the historical message cannot be viewed upward) bindMsgList(); } }, error: function(xhr, type, errorThrown) { } }) } //Determine whether the id in the ajax callback has the same value in the array function idWhetherInArray(id) { var isExist = false; for(var i = 0; i < chatMsgIdArray.length; i++) { if(chatMsgIdArray[i] == id) { isExist = true; break; } } return isExist; } }); }(mui, document)); </script> </body> </html>
matters needing attention
You must ensure that arttmpl JS file.
Get detailed Library
Right click the hbuiller editor to create an APP application and select the MUI style template.