wansfa 1 rok pred
rodič
commit
1f11353fd2

+ 3 - 0
package.json

@@ -49,6 +49,7 @@
     "js-beautify": "1.10.2",
     "js-cookie": "2.2.0",
     "jsencrypt": "3.0.0-rc.1",
+    "lemon-imui": "^1.7.7",
     "lodash.clonedeep": "^4.5.0",
     "lodash.merge": "^4.6.2",
     "moment": "^2.29.4",
@@ -60,6 +61,8 @@
     "quill": "1.3.7",
     "screenfull": "4.2.0",
     "sortablejs": "1.8.4",
+    "stylus": "^0.54.7",
+    "stylus-loader": "^3.0.2",
     "v-clipboard": "^2.2.3",
     "vue": "2.6.10",
     "vue-clipboard2": "^0.3.1",

+ 11 - 10
src/main.js

@@ -1,9 +1,6 @@
 import Vue from 'vue'
-
 import Cookies from 'js-cookie'
-
 import 'normalize.css/normalize.css' // a modern alternative to CSS resets
-
 import Element from 'element-ui'
 import './assets/styles/element-variables.scss'
 
@@ -23,7 +20,7 @@ import './permission' // permission control
 import { getDicts } from "@/api/system/dict/data";
 import { getConfigKey } from "@/api/company/companyConfig";
 import { callMobile } from "@/api/company/companyVoiceApi"
-import { getAge,formatDate,parsePost,parseArr,formatMoney,parseTime,dateFormat, resetForm, addDateRange, selectDictLabel, selectDictLabels, download, handleTree,formatTime } from "@/utils/common";
+import { getAge,formatDate,parsePost,parseArr,formatMoney, resetForm, addDateRange, selectDictLabel, selectDictLabels, download, handleTree,parseTime,dateFormat,friendlyDate,formatTime } from "@/utils/common";
 import { callNumber,callOff } from "@/utils/call";
 
 
@@ -41,20 +38,20 @@ Vue.component('ImageUpload',ImageUpload)
 import audio from 'vue-mobile-audio'
 Vue.use(audio)
 
+import LemonIMUI from 'lemon-imui';
+Vue.use(LemonIMUI);
+
 Vue.prototype.callNumber = callNumber
 Vue.prototype.callOff = callOff
-
 Vue.prototype.callMobile = callMobile
+
 // Vue.prototype.callOffMobile = callOffMobile
 // Vue.prototype.getSipAccount = getSipAccount
 
 
-
 Vue.prototype.getDicts = getDicts
 Vue.prototype.getConfigKey = getConfigKey
-
-Vue.prototype.parseTime = parseTime
-Vue.prototype.transformDateFormat = dateFormat
+Vue.prototype.getAge=getAge
 Vue.prototype.resetForm = resetForm
 Vue.prototype.addDateRange = addDateRange
 Vue.prototype.selectDictLabel = selectDictLabel
@@ -64,9 +61,13 @@ Vue.prototype.handleTree = handleTree
 Vue.prototype.formatMoney = formatMoney
 Vue.prototype.parseArr = parseArr
 Vue.prototype.parsePost = parsePost
+
+Vue.prototype.parseTime = parseTime
+Vue.prototype.transformDateFormat = dateFormat
 Vue.prototype.formatDate = formatDate
-Vue.prototype.getAge=getAge
 Vue.prototype.formatTime=formatTime
+Vue.prototype.friendlyDate=friendlyDate
+
 
 
 import moment from "moment"

+ 115 - 71
src/utils/common.js

@@ -46,47 +46,6 @@ export function getAge(strBirthday) {
 }
 
 
-// 日期格式化
-export function parseTime(time, pattern) {
-	if (arguments.length === 0 || !time) {
-		return null
-	}
-	const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
-	let date
-	if (typeof time === 'object') {
-		date = time
-	} else {
-		if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
-			time = parseInt(time)
-		} else if (typeof time === 'string') {
-			time = time.replace(new RegExp(/-/gm), '/');
-		}
-		if ((typeof time === 'number') && (time.toString().length === 10)) {
-			time = time * 1000
-		}
-		date = new Date(time)
-	}
-	const formatObj = {
-		y: date.getFullYear(),
-		m: date.getMonth() + 1,
-		d: date.getDate(),
-		h: date.getHours(),
-		i: date.getMinutes(),
-		s: date.getSeconds(),
-		a: date.getDay()
-	}
-	const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
-		let value = formatObj[key]
-		// Note: getDay() returns 0 on Sunday
-		if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
-		if (result.length > 0 && value < 10) {
-			value = '0' + value
-		}
-		return value || 0
-	})
-	return time_str
-}
-
 // 表单重置
 export function resetForm(refName) {
 	if (this.$refs[refName]) {
@@ -188,29 +147,6 @@ export function handleTree(data, id, parentId, children, rootId) {
 	return treeData != '' ? treeData : data;
 }
 
-export function dateFormat(fmt, date) {
-	let ret="";
-	date=new Date(date);
-	const opt = {
-	  'Y+': date.getFullYear().toString(), // 年
-	  'm+': (date.getMonth() + 1).toString(), // 月
-	  'd+': date.getDate().toString(), // 日
-	  'H+': date.getHours().toString(), // 时
-	  'M+': date.getMinutes().toString(), // 分
-	  'S+': date.getSeconds().toString() // 秒
-	  // 有其他格式化字符需求可以继续添加,必须转化成字符串
-	}
-	for (let k in opt) {
-	  ret = new RegExp('(' + k + ')').exec(fmt)
-	  if (ret) {
-		fmt = fmt.replace(
-		  ret[1],
-		  ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, '0')
-		)
-	  }
-	}
-	return fmt
-  }
 
 export function formatMoney(s,dot) {     
 	s = parseFloat((s + "").replace(/[^\d\.-]/g, "")).toFixed(2) + "";   
@@ -221,8 +157,9 @@ export function formatMoney(s,dot) {
 	   t += l[i] + ((i + 1) % 3 == 0 && (i + 1) != l.length ? dot : "");   
 	}   
 	return t.split("").reverse().join("") + "." + r;   
- } 
- export function parseArr(str) {
+} 
+
+export function parseArr(str) {
 	if(str==null||str==undefined||str==""){
 		return []
 	}
@@ -249,6 +186,74 @@ export function parsePost(posts) {
 	return postNames;
 }
 
+// 日期格式化
+export function parseTime(time, pattern) {
+	if (arguments.length === 0 || !time) {
+		return null
+	}
+	const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
+	let date
+	if (typeof time === 'object') {
+		date = time
+	} 
+	else {
+		if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
+			time = parseInt(time)
+		} else if (typeof time === 'string') {
+			time = time.replace(new RegExp(/-/gm), '/');
+		}
+		if ((typeof time === 'number') && (time.toString().length === 10)) {
+			time = time * 1000
+		}
+		date = new Date(time)
+	}
+	const formatObj = {
+		y: date.getFullYear(),
+		m: date.getMonth() + 1,
+		d: date.getDate(),
+		h: date.getHours(),
+		i: date.getMinutes(),
+		s: date.getSeconds(),
+		a: date.getDay()
+	}
+	const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
+		let value = formatObj[key]
+		// Note: getDay() returns 0 on Sunday
+		if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
+		if (result.length > 0 && value < 10) {
+			value = '0' + value
+		}
+		return value || 0
+	})
+	return time_str
+}
+
+
+export function dateFormat(fmt, date) {
+	let ret="";
+	date=new Date(date);
+	const opt = {
+	  'Y+': date.getFullYear().toString(), // 年
+	  'm+': (date.getMonth() + 1).toString(), // 月
+	  'd+': date.getDate().toString(), // 日
+	  'H+': date.getHours().toString(), // 时
+	  'M+': date.getMinutes().toString(), // 分
+	  'S+': date.getSeconds().toString() // 秒
+	  // 有其他格式化字符需求可以继续添加,必须转化成字符串
+	}
+	for (let k in opt) {
+	  ret = new RegExp('(' + k + ')').exec(fmt)
+	  if (ret) {
+		fmt = fmt.replace(
+		  ret[1],
+		  ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, '0')
+		)
+	  }
+	}
+	return fmt
+}
+
+
 export function formatDate(datetime) {
 	var date = new Date(datetime);//时间戳为10位需*1000,时间戳为13位的话不需乘1000
 	var year = date.getFullYear(),
@@ -264,17 +269,13 @@ export function formatDate(datetime) {
 }
 
 
-
 export function formatTime(timer) {
 	// let timeStarts = startTime.getTime(); //开始时间,转换成时间戳
 	// let timeEnds = endTime.getTime(); //结束时间,转换成时间戳
-	
-	//let timer = endTime - startTime  //将时间戳进行相减
-
+		//let timer = endTime - startTime  //将时间戳进行相减
 	let hour = parseInt((timer  / 60 / 60 % 24)); 
 	let minute = parseInt((timer  / 60 % 60));  
 	let second = parseInt((timer  % 60)); 
-
 	if(hour>0){
 		return (hour>10?hour:'0'+hour) + ':' + (minute>10?minute:'0'+minute) + ':' + (second>10?second:'0'+second);
 	}else if(minute>0){
@@ -285,6 +286,49 @@ export function formatTime(timer) {
 }
 
 
+/**
+ * @param {number} time
+ * @param {string} option
+ * @returns {string}
+ */
+ export function friendlyDate(time, option) {
+	if (('' + time).length === 10) {
+	  time = parseInt(time) * 1000
+	} else {
+	  time = +time
+	}
+	const d = new Date(time)
+	const now = Date.now()
+	const diff = (now - d) / 1000
+	if (diff < 30) {
+	  return '刚刚'
+	} else if (diff < 3600) {
+	  // less 1 hour
+	  return Math.ceil(diff / 60) + '分钟前'
+	} else if (diff < 3600 * 24) {
+	  return Math.ceil(diff / 3600) + '小时前'
+	} else if (diff < 3600 * 24 * 2) {
+	  return '1天前'
+	}
+
+	if (option) {
+	  return parseTime(time, option)
+	} else {
+	  return (
+		d.getMonth() +
+		1 +
+		'-' +
+		d.getDate() +
+		' ' +
+		d.getHours() +
+		':' +
+		d.getMinutes() +
+		''
+	  )
+	}
+  }
+
+
 // export function callNumber(mobile){
 // 	var that=this;
 //     this.callMobile(mobile).then(response => {

+ 0 - 2
src/utils/index.js

@@ -28,9 +28,7 @@ export function formatTime(time, option) {
   }
   const d = new Date(time)
   const now = Date.now()
-
   const diff = (now - d) / 1000
-
   if (diff < 30) {
     return '刚刚'
   } else if (diff < 3600) {

+ 1 - 1
src/views/company/components/cardSelect.vue

@@ -1,5 +1,5 @@
 <template>
-  <div  >
+  <div>
     <el-row>
       <el-col>
         <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">

+ 0 - 0
src/views/qw/components


+ 148 - 0
src/views/qw/database/contacts.js

@@ -0,0 +1,148 @@
+export default [
+  {
+    id: 1,
+    displayName: "像梦一样自由",
+    avatar: "https://p.qqan.com/up/2020-2/2020022821001845128.jpg",
+    index: "X",
+    unread: 0,
+    lastSendTime: 1566047865417,
+    lastContent: "你开心吗",
+  },
+  {
+    id: 2,
+    displayName: "梦醒时分、",
+    avatar: "https://p.qqan.com/up/2021-1/20211301122243621.jpg",
+    index: "M",
+    unread: 0,
+    lastSendTime: 1566047865417,
+    lastContent: "",
+  },
+  {
+    id: 3,
+    displayName: "凌云",
+    avatar: "https://p.qqan.com/up/2021-1/2021129102387841.jpg",
+    index: "L",
+    unread: 0,
+    lastSendTime: 1566047865417,
+    lastContent: "",
+  },
+  {
+    id: 4,
+    displayName: "小郭",
+    avatar: "https://p.qqan.com/up/2021-1/2021122135507881.jpg",
+    index: "X",
+    unread: 0,
+    lastSendTime: 1566047865417,
+    lastContent: "",
+  },
+  {
+    id: 5,
+    displayName: "杨玉泉",
+    avatar: "https://p.qqan.com/up/2021-1/20211211131598147.jpg",
+    index: "Y",
+    unread: 0,
+    lastSendTime: 1566047865417,
+    lastContent: "",
+  },
+  {
+    id: 6,
+    displayName: "森系Style",
+    avatar: "https://p.qqan.com/up/2021-1/2021113104111220.jpg",
+    index: "S",
+    unread: 0,
+    lastSendTime: 1566047865417,
+    lastContent: "",
+  },
+  {
+    id: 7,
+    displayName: "霸王花",
+    avatar: "https://p.qqan.com/up/2021-1/20211411391666.jpg",
+    index: "B",
+    unread: 0,
+    lastSendTime: 1566047865417,
+    lastContent: "你怎么还不睡呀?",
+  },
+  {
+    id: 8,
+    displayName: "曾平",
+    avatar: "https://p.qqan.com/up/2020-12/202012291044425822.jpg",
+    index: "Z",
+    unread: 0,
+    lastSendTime: 1566047865417,
+    lastContent: "",
+  },
+  {
+    id: 9,
+    displayName: "淡然",
+    avatar: "https://p.qqan.com/up/2020-12/202012141813343503.jpg",
+    index: "D",
+    unread: 0,
+    lastSendTime: 1566047865417,
+    lastContent: "",
+  },
+  {
+    id: 10,
+    displayName: "叶子。",
+    avatar: "https://p.qqan.com/up/2021-1/20211301122243621.jpg",
+    index: "Y",
+    unread: 0,
+    lastSendTime: 1566047865417,
+    lastContent: "",
+  },
+  {
+    id: 11,
+    displayName: "土豆",
+    avatar: "https://p.qqan.com/up/2020-12/202012111157268739.jpg",
+    index: "T",
+    unread: 0,
+    lastSendTime: 1566047865417,
+    lastContent: "",
+  },
+  {
+    id: 12,
+    displayName: "清沫",
+    avatar: "https://p.qqan.com/up/2020-12/202012415467996.jpg",
+    index: "Q",
+    unread: 0,
+    lastSendTime: 1566047865417,
+    lastContent: "",
+  },
+  {
+    id: 13,
+    displayName: "Lemon-imui交流群",
+    avatar: "https://p.qqan.com/up/2020-11/20201127157109035.jpg",
+    index: "L",
+    isGroup: true,
+    unread: 0,
+    lastSendTime: 1566047865417,
+    lastContent: "这个咋处理啊?",
+  },
+  {
+    id: 14,
+    displayName: "系统通知",
+    avatar: "https://p.qqan.com/up/2020-6/2020061117234279854.jpg",
+    index: "[1]系統通知",
+    unread: 0,
+    lastSendTime: 1566047865417,
+    lastContent: "宁静致远通过了你的好友请求",
+    renderContainer() {
+      return (
+        ""
+        // <div style="padding:15px;">
+        //   <div>宁静致远通过了你的好友请求</div>
+        //   <div>宁静致远通过了你的好友请求</div>
+        //   <div>宁静致远通过了你的好友请求</div>
+        // </div>
+      );
+    },
+  },
+  {
+    id: 15,
+    displayName: "宁静致远。",
+    avatar: "https://p.qqan.com/up/2020-6/2020060308522797777.jpg",
+    index: "N",
+    unread: 0,
+    lastSendTime: 1566047865417,
+    lastContent: "",
+  },
+];

+ 292 - 0
src/views/qw/database/emoji.js

@@ -0,0 +1,292 @@
+export default [
+  {
+    label: "表情",
+    children: [
+      {
+        name: "1f600",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f600.png",
+      },
+      {
+        name: "1f62c",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f62c.png",
+      },
+      {
+        name: "1f601",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f601.png",
+      },
+      {
+        name: "1f602",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f602.png",
+      },
+      {
+        name: "1f923",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f923.png",
+      },
+      {
+        name: "1f973",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f973.png",
+      },
+      {
+        name: "1f603",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f603.png",
+      },
+      {
+        name: "1f604",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f604.png",
+      },
+      {
+        name: "1f605",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f605.png",
+      },
+      {
+        name: "1f606",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f606.png",
+      },
+      {
+        name: "1f607",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f607.png",
+      },
+      {
+        name: "1f609",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f609.png",
+      },
+      {
+        name: "1f60a",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f60a.png",
+      },
+      {
+        name: "1f642",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f642.png",
+      },
+      {
+        name: "1f643",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f643.png",
+      },
+      {
+        name: "1263a",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/263a.png",
+      },
+      {
+        name: "1f60b",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f60b.png",
+      },
+      {
+        name: "1f60c",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f60c.png",
+      },
+      {
+        name: "1f60d",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f60d.png",
+      },
+      {
+        name: "1f970",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f970.png",
+      },
+      {
+        name: "1f618",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f618.png",
+      },
+      {
+        name: "1f617",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f617.png",
+      },
+      {
+        name: "1f619",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f619.png",
+      },
+      {
+        name: "1f61a",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f61a.png",
+      },
+      {
+        name: "1f61c",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f61c.png",
+      },
+      {
+        name: "1f92a",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f92a.png",
+      },
+      {
+        name: "1f928",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f928.png",
+      },
+      {
+        name: "1f9d0",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f9d0.png",
+      },
+      {
+        name: "1f61d",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f61d.png",
+      },
+      {
+        name: "1f61b",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f61b.png",
+      },
+      {
+        name: "1f911",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f911.png",
+      },
+      {
+        name: "1f913",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f913.png",
+      },
+      {
+        name: "1f60e",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f60e.png",
+      },
+      {
+        name: "1f929",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f929.png",
+      },
+      {
+        name: "1f921",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f921.png",
+      },
+      {
+        name: "1f920",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f920.png",
+      },
+      {
+        name: "1f917",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f917.png",
+      },
+      {
+        name: "1f60f",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f60f.png",
+      },
+      {
+        name: "1f636",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f636.png",
+      },
+      {
+        name: "1f610",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f610.png",
+      },
+      {
+        name: "1f611",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f611.png",
+      },
+      {
+        name: "1f612",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f612.png",
+      },
+      {
+        name: "1f644",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f644.png",
+      },
+      {
+        name: "1f914",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f914.png",
+      },
+      {
+        name: "1f925",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f925.png",
+      },
+      {
+        name: "1f92d",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f92d.png",
+      },
+      {
+        name: "1f92b",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f92b.png",
+      },
+      {
+        name: "1f92c",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f92c.png",
+      },
+      {
+        name: "1f92f",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f92f.png",
+      },
+      {
+        name: "1f633",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f633.png",
+      },
+      {
+        name: "1f61e",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f61e.png",
+      },
+      {
+        name: "1f61f",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f61f.png",
+      },
+      {
+        name: "1f620",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f620.png",
+      },
+      {
+        name: "1f621",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f621.png",
+      },
+    ],
+  },
+  {
+    label: "收藏",
+    children: [
+      {
+        name: "1f62c",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f62c.png",
+      },
+      {
+        name: "1f621",
+        title: "微笑",
+        src: "https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f621.png",
+      },
+    ],
+  },
+];

+ 219 - 0
src/views/qw/database/messages.js

@@ -0,0 +1,219 @@
+import ContactsData from "lemon-imui/examples/database/contacts";
+import UserData from "lemon-imui/examples/database/user";
+const generateRandId = () => {
+  return Math.random()
+    .toString(36)
+    .substr(-8);
+};
+const getContact = id => {
+  const data = ContactsData.find(contact => contact.id == id);
+  return { id: data.id, avatar: data.avatar, displayName: data.displayName };
+};
+export default {
+  1: [
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1566047865417,
+      content: "问你件事",
+      toContactId: 1,
+      fromUser: UserData,
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1566047865417,
+      content: "啥子。",
+      toContactId: 1,
+      fromUser: getContact(1),
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1566047865417,
+      content: "为什么",
+      toContactId: 1,
+      fromUser: UserData,
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1566047865417,
+      content: "你穿了高跟鞋还这么矮",
+      toContactId: 1,
+      fromUser: UserData,
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1566047865417,
+      content: "因为我矮啊。[!1f600][!1f600][!1f600]",
+      toContactId: 1,
+      fromUser: getContact(1),
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1566047865417,
+      content: "你开心吗",
+      toContactId: 1,
+      fromUser: getContact(1),
+    },
+  ],
+  2: [],
+  3: [],
+  4: [],
+  5: [],
+  6: [],
+  7: [],
+  8: [],
+  9: [],
+  10: [],
+  11: [],
+  12: [],
+  13: [
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1670481209000,
+      content: "我是测试时候看到的",
+      toContactId: 1,
+      fromUser: getContact(4),
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1670541395000,
+      content: "上新版本了,玩玩",
+      toContactId: 1,
+      fromUser: getContact(4),
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1670544995000,
+      content: "项目内没有搞这个",
+      toContactId: 1,
+      fromUser: getContact(4),
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1670545175000,
+      content: "@awesome 最新的,不然哪有这功能",
+      toContactId: 1,
+      fromUser: getContact(5),
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1670631575000,
+      content: "其实是跟你的遮罩层有冲突",
+      toContactId: 1,
+      fromUser: getContact(4),
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1670804375000,
+      content: "自己修改index哈",
+      toContactId: 1,
+      fromUser: UserData,
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1670977175000,
+      content: "你们升级到最近版了吗?",
+      toContactId: 1,
+      fromUser: getContact(6),
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1670980775000,
+      content: "wo 现在用的142",
+      toContactId: 1,
+      fromUser: getContact(7),
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1671247175000,
+      content: "问你件事",
+      toContactId: 1,
+      fromUser: UserData,
+    },
+    {
+      id: generateRandId(),
+      status: "failed",
+      type: "text",
+      sendTime: 1671340182000,
+      content: "啥子。",
+      toContactId: 1,
+      fromUser: getContact(1),
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1696907382000,
+      content: "为什么",
+      toContactId: 1,
+      fromUser: UserData,
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1696920042000,
+      content: "你穿了高跟鞋还这么矮[!1f62c][!1f601][!1f602]",
+      toContactId: 1,
+      fromUser: UserData,
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1696995642000,
+      content: "因为我矮啊。[!1f600][!1f600][!1f600]",
+      toContactId: 1,
+      fromUser: getContact(1),
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1696995642000,
+      content: "你有意见吗。[!1f643][!1f643][!1f643]",
+      toContactId: 1,
+      fromUser: getContact(1),
+    },
+    {
+      id: generateRandId(),
+      status: "succeed",
+      type: "text",
+      sendTime: 1696995942000,
+      content: "你开心吗",
+      toContactId: 1,
+      fromUser: getContact(1),
+    },
+  ],
+  14: [],
+  15: [],
+};

+ 5 - 0
src/views/qw/database/user.js

@@ -0,0 +1,5 @@
+export default {
+  id: 1000,
+  avatar: "https://p.qqan.com/up/2018-4/15244505348390471.jpg",
+  displayName: "野火。",
+};

+ 681 - 0
src/views/qw/qwChat/index.vue

@@ -0,0 +1,681 @@
+<template>
+  <div>
+      <div class="imui-center qq-lemon-imui">
+        <lemon-imui
+          :width="windowWidth"
+          :height="windowHeight"
+          :user="user"
+          ref="IMUI"
+          :contextmenu="contextmenu"
+          :contact-contextmenu="contactContextmenu"
+          :theme="theme"
+          :hide-menu="hideMenu"
+          :hide-menu-avatar="hideMenuAvatar"
+          :hide-message-name="hideMessageName"
+          :hide-message-time="hideMessageTime"
+          @change-menu="handleChangeMenu"
+          @change-contact="handleChangeContact"
+          @pull-messages="handlePullMessages"
+          @message-click="handleMessageClick"
+          @menu-avatar-click="handleMenuAvatarClick"
+          @send="handleSend">
+          <template #cover>
+            <div class="cover">
+              <i class="lemon-icon-message"></i>
+              <p><b>自定义封面 Lemon</b> IMUI</p>
+            </div>
+          </template>
+          <template #message-title="contact">
+            <span>{{ contact.displayName }}</span>
+            <small class="more" @click="changeDrawer(contact, $refs.IMUI)">{{
+                ($refs.IMUI ? $refs.IMUI.drawerVisible : false) ? "关闭" : "打开"
+              }}抽屉</small>
+            <br />
+          </template>
+        </lemon-imui>
+    </div>
+  </div>
+</template>
+
+<script>
+import { getQrCode } from '@/api/qw/login';
+import { getDeviceId} from '@/api/qw/account';
+
+import LemonMessageVoice from "lemon-imui/examples/lemon-message-voice";
+import QQIMUI from "lemon-imui/examples/qq";
+import ContactsData from "../database/contacts";
+import MessagesData from "../database/messages";
+import EmojiData from "../database/emoji";
+import 'lemon-imui/dist/index.css';
+
+const tip = `export default {
+          //组件的name必须以lemonMessage开头,后面跟上 Message.type
+          name: "lemonMessageVoice",
+          inheritAttrs: false,
+          //如果需要使用父组件的方法,可以使用注入。
+          inject: ["IMUI"],
+          render() {
+            //lemon-message-basic 组件对气泡框、头像、事件等信息进行了公共的处理。
+            return (
+              <lemon-message-basic
+                    class="lemon-message-voice"
+                    props={{ ...this.$attrs }}
+                    scopedSlots={{
+                      content: props => {
+                        //返回HTML结构
+                        return <span>{props.content}&nbsp;🔈</span>
+                      }
+                    }}
+                />
+            );
+          }
+  };
+  <style lang="stylus">
+  .lemon-message.lemon-message-voice
+    user-select none
+    .lemon-message__content
+      border 2px solid #000
+      font-size 12px
+      cursor pointer
+      &::before
+        display none
+  </style>`;
+
+const getTime = () => {
+  return new Date().getTime();
+};
+const generateRandId = () => {
+  return Math.random()
+    .toString(36)
+    .substr(-8);
+};
+const generateRandWord = () => {
+  return Math.random()
+    .toString(36)
+    .substr(2);
+};
+const generateMessage = (toContactId = "", fromUser) => {
+  if (!fromUser) {
+    fromUser = {
+      id: "system",
+      displayName: "系统测试",
+      avatar: "http://upload.qqbodys.com/allimg/1710/1035512943-0.jpg",
+    };
+  }
+  return {
+    id: generateRandId(),
+    status: "succeed",
+    type: "text",
+    sendTime: getTime(),
+    content: generateRandWord(),
+    //fileSize: 1231,
+    //fileName: "asdasd.doc",
+    toContactId,
+    fromUser,
+  };
+};
+
+export default {
+  name: "qwChat",
+  components: { LemonMessageVoice,QQIMUI },
+  data() {
+    return {
+      theme: "default",
+      contactContextmenu: [
+        {
+          text: "删除该聊天",
+          click: (e, instance, hide) => {
+            const { IMUI, contact } = instance;
+            IMUI.updateContact({
+              id: contact.id,
+              lastContent: null,
+            });
+            if (IMUI.currentContactId == contact.id) IMUI.changeContact(null);
+            hide();
+          },
+        },
+        {
+          text: "设置备注和标签",
+        },
+        {
+          text: "投诉",
+        },
+        {
+          icon: "lemon-icon-message",
+          text: "加入黑名单",
+          render: (h, instance, hide) => {
+            return (
+              <div style="display:flex;justify-content:space-between;align-items:center;width:130px">
+                <span>加入黑名单</span>
+                <span>
+                  <input type="checkbox" id="switch" />
+                  <label id="switch-label" for="switch">
+                    Toggle
+                  </label>
+                </span>
+              </div>
+            );
+          },
+        },
+        {
+          click(e, instance, hide) {
+            const { IMUI, contact } = instance;
+            IMUI.removeContact(contact.id);
+            if (IMUI.currentContactId == contact.id) IMUI.changeContact(null);
+            hide();
+          },
+          color: "red",
+          text: "删除好友",
+        },
+      ],
+      contextmenu: [
+        {
+          click: (e, instance, hide) => {
+            const { IMUI, message } = instance;
+            const data = {
+              id: generateRandId(),
+              type: "event",
+              //使用 jsx 时 click必须使用箭头函数(使上下文停留在vue内)
+              content: (
+                <div>
+                  <span>
+                    你撤回了一条消息{" "}
+                    <span
+                      v-show={message.type == "text"}
+                      style="color:#333;cursor:pointer"
+                      content={message.content}
+                      on-click={e => {
+                        IMUI.setEditorValue(e.target.getAttribute("content"));
+                      }}
+                    >
+                      重新编辑
+                    </span>
+                  </span>
+                </div>
+              ),
+              toContactId: message.toContactId,
+              sendTime: getTime(),
+            };
+            IMUI.removeMessage(message.id);
+            IMUI.appendMessage(data, true);
+            hide();
+          },
+          visible: instance => {
+            return instance.message.fromUser.id == this.user.id;
+          },
+          text: "撤回消息",
+        },
+        {
+          visible: instance => {
+            return instance.message.fromUser.id != this.user.id;
+          },
+          text: "举报",
+        },
+        {
+          text: "转发",
+        },
+        {
+          visible: instance => {
+            return instance.message.type == "text";
+          },
+          text: "复制文字",
+        },
+        {
+          visible: instance => {
+            return instance.message.type == "image";
+          },
+          text: "下载图片",
+        },
+        {
+          visible: instance => {
+            return instance.message.type == "file";
+          },
+          text: "下载文件",
+        },
+        {
+          click: (e, instance, hide) => {
+            const { IMUI, message } = instance;
+            IMUI.removeMessage(message.id);
+            hide();
+          },
+          icon: "lemon-icon-folder",
+          color: "red",
+          text: "删除",
+        },
+      ],
+      tip: tip,
+      hideMenuAvatar: false,
+      hideMenu: false,
+      hideMessageName: false,
+      hideMessageTime: true,
+      user: {
+        id: "1000",
+        displayName: "June",
+        avatar: "https://p.qqan.com/up/2018-4/15244505348390471.jpg",
+      },
+      windowWidth: document.documentElement.clientWidth*0.65,  //实时屏幕宽度
+      windowHeight: document.documentElement.clientHeight*0.7,   //实时屏幕高度
+    };
+  },
+  mounted() {
+    const contactData1 = {
+      id: "contact-1",
+      displayName: "工作协作群",
+      avatar: "http://upload.qqbodys.com/img/weixin/20170804/ji5qxg1am5ztm.jpg",
+      index: "[1]群组",
+      unread: 0,
+      lastSendTime: 1566047865417,
+      lastContent: "2",
+    };
+    const contactData2 = {
+      id: "contact-2",
+      displayName: "自定义内容",
+      avatar: "http://upload.qqbodys.com/img/weixin/20170807/jibfvfd00npin.jpg",
+      //index: "Z",
+      click(next) {
+        next();
+      },
+      renderContainer: () => {
+        return <h1 style="text-indent:20px">自定义页面</h1>;
+      },
+      lastSendTime: 1345209465000,
+      lastContent: "12312",
+      unread: 2,
+    };
+    const contactData3 = {
+      id: "contact-3",
+      displayName: "铁牛",
+      avatar: "http://upload.qqbodys.com/img/weixin/20170803/jiq4nzrkrnd0e.jpg",
+      index: "T",
+      unread: 32,
+      lastSendTime: 3,
+      lastContent: "你好123",
+    };
+    const contactData4 = {
+      id: "contact-4",
+      displayName: "如花",
+      avatar:
+        "https://dss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4275424924,2201401076&fm=111&gp=0.jpg",
+      index: "",
+      unread: 1,
+      lastSendTime: 3,
+      lastContent: "吃饭了嘛",
+    };
+
+    const { IMUI } = this.$refs;
+   
+    // setTimeout(() => {
+    //   var info = JSON.parse(
+    //     '{"type":0,"fromId":"666666666","toId":"8888888","fromName":"AAAA","toName":"BBBB","message":"您与客户[AAAA]建立连接","isError":false,"time":"2021-05-13T16:04:18.7158482+08:00","id":"666666666","avatar":"","displayName":"AAAA","unread":1,"lastSendTime":1620893058612,"lastContent":"您与客户[AAAA]建立连接"}',
+    //   );
+    //   console.log(info);
+    //   IMUI.appendContact(info);
+
+    //   setTimeout(() => {
+    //     var message = JSON.parse(
+    //       '{"type":"text","fromId":"666666666","toId":"8888888","fromName":"AAAA","toName":"BBBB","message":"我是消息123","isError":false,"time":"2021-05-13T16:04:19.0679223+08:00","id":"1426f5d4-0616-4d4b-93ac-499873f1b7ff","status":"succeed","sendTime":1620893057833,"content":"111","toContactId":"666666666","fromUser":{"type":0,"fromId":"666666666","toId":"8888888","fromName":"AAAA","toName":"BBBB","message":"您与客户[AAAA]建立连接","isError":false,"time":"2021-05-13T16:04:18.7158482+08:00","id":"666666666","avatar":"","displayName":"AAAA","unread":0,"lastSendTime":1620893058614,"lastContent":"[通知]","pageNum":0}}',
+    //     );
+    //     console.log("message", message);
+    //     IMUI.appendMessage(message);
+    //   }, 2000);
+    // }, 1000);
+
+    IMUI.setLastContentRender("event", message => {
+      return `[自定义通知内容]`;
+    });
+
+    let contactList = [
+      { ...contactData1 },
+      { ...contactData2 },
+      { ...contactData3 },
+    ];
+    //IMUI.initContacts(contactList);
+    IMUI.initContacts(ContactsData);
+    IMUI.initMenus([
+      {
+        name: "messages",
+      },
+      {
+        name: "contacts",
+      },
+      {
+        name: "custom1",
+        title: "自定义按钮1",
+        unread: 0,
+        render: menu => {
+          return <i class="lemon-icon-attah" />;
+        },
+        renderContainer: () => {
+          return (
+            <div class="article">
+              <ul>
+                <li class="article-item">
+                  <h2>人民日报谈网红带货:产品真的值得买吗?</h2>
+                </li>
+                <li class="article-item">
+                  甘肃夏河县发生5.7级地震 暂未接到人员伤亡报告
+                </li>
+                <li class="article-item">
+                  北方多地风力仍强沙尘相伴,东北内蒙古等地迎雨雪
+                </li>
+                <li class="article-item">
+                  英货车案:越南警方采集疑死者家属DNA作比对
+                </li>
+                <li class="article-item">
+                  知名连锁咖啡店的蛋糕吃出活虫 曝光内幕太震惊
+                </li>
+              </ul>
+              <lemon-contact  props={{ contact: contactData1 }} style="margin:20px"/>
+              <lemon-contact props={{ contact: contactData3 }} style="margin:20px" />
+            </div>
+          );
+        },
+        isBottom: true,
+      },
+      {
+        name: "custom2",
+        title: "自定义按钮2",
+        unread: 0,
+        click: () => {
+          alert("拦截导航点击事件");
+        },
+        render: menu => {
+          return <i class="lemon-icon-group" />;
+        },
+        isBottom: true,
+      },
+    ]);
+
+    IMUI.initEditorTools([
+      {
+        name: "emoji",
+      },
+      {
+        name: "uploadFile",
+      },
+      {
+        name: "uploadImage",
+      },
+      {
+        name: "test1",
+        click: () => {
+          IMUI.$refs.editor.selectFile("application/vnd.ms-excel");
+        },
+        render: () => {
+          return <span>Excel</span>;
+        },
+      },
+      {
+        name: "test1",
+        click: () => {
+          IMUI.initEditorTools([{ name: "uploadFile" }, { name: "emoji" }]);
+        },
+        render: () => {
+          return <span>重制工具栏</span>;
+        },
+      },
+      {
+        name: "test2",
+        isRight: true,
+        title: "上传 Excel",
+        click: () => {
+          alert("点击了 ··· ");
+        },
+        render: () => {
+          return <b>···</b>;
+        },
+      },
+    ]);
+    IMUI.initEmoji(EmojiData);
+
+    IMUI.setLastContentRender("voice", message => {
+      return <span>[语音]</span>;
+    });
+    setTimeout(() => {
+      IMUI.changeContact("13");
+    }, 500);
+  },
+  
+  methods: {
+    changeTheme() {
+       this.theme = this.theme == "default" ? "blue" : "default";
+    },
+    scrollToTop() {
+      document.body.scrollIntoView();
+    },
+    handleMenuAvatarClick() {
+      console.log("Event:menu-avatar-click");
+    },
+    handleMessageClick(e, key, message, instance) {
+      console.log("点击了消息", e, key, message);
+      if (key == "status") {
+        instance.updateMessage({
+          id: message.id,
+          status: "going",
+          content: "正在重新发送消息...",
+        });
+        setTimeout(() => {
+          instance.updateMessage({
+            id: message.id,
+            status: "succeed",
+            content: "发送成功",
+          });
+        }, 2000);
+      }
+    },
+    changeMenuAvatarVisible() {
+      this.hideMenuAvatar = !this.hideMenuAvatar;
+    },
+    changeMenuVisible() {
+      this.hideMenu = !this.hideMenu;
+    },
+    changeMessageNameVisible() {
+      this.hideMessageName = !this.hideMessageName;
+    },
+    changeMessageTimeVisible() {
+      this.hideMessageTime = !this.hideMessageTime;
+    },
+    removeMessage() {
+      const { IMUI } = this.$refs;
+      const messages = IMUI.getCurrentMessages();
+      const id = messages[messages.length - 1].id;
+      if (messages.length > 0) {
+        IMUI.removeMessage(id);
+      }
+    },
+    updateMessage() {
+      const { IMUI } = this.$refs;
+      const messages = IMUI.getCurrentMessages();
+      const message = messages[messages.length - 1];
+      if (messages.length > 0) {
+        const update = {
+          id: message.id,
+          status: "succeed",
+          type: "file",
+          fileName: "被修改成文件了.txt",
+          fileSize: "4200000",
+        };
+        if (message.type == "event") {
+          update.fromUser = this.user;
+        }
+        IMUI.updateMessage(update);
+        IMUI.messageViewToBottom();
+      }
+    },
+    appendCustomMessage() {
+      const { IMUI } = this.$refs;
+      const message = {
+        id: generateRandId(),
+        status: "succeed",
+        type: "voice",
+        sendTime: getTime(),
+        content: "语音消息",
+        params1: "1",
+        params2: "2",
+        toContactId: "contact-1",
+        fromUser: this.user,
+      };
+      IMUI.appendMessage(message, true);
+    },
+    appendMessage() {
+      const { IMUI } = this.$refs;
+      const contact = IMUI.currentContact;
+      const message = generateMessage("contact-3");
+      message.fromUser = {
+        ...message.fromUser,
+        ...this.user,
+      };
+      IMUI.appendMessage(message, true);
+      console.log("🚀 ~ file: App.vue ~ line 1508 ~ appendMessage ~ message", message)
+    },
+    appendEventMessage() {
+      const { IMUI } = this.$refs;
+      const message = {
+        id: generateRandId(),
+        type: "event",
+        content: (
+          <span>
+            邀请你加入群聊{" "}
+            <span
+              style="color:#333;cursor:pointer"
+              on-click={() => alert("OK")}
+            >
+              接受
+            </span>
+          </span>
+        ),
+        toContactId: "contact-3",
+        sendTime: getTime(),
+      };
+      IMUI.appendMessage(message, true);
+    },
+    updateContact() {
+      this.$refs.IMUI.updateContact({
+        id: "contact-3",
+        unread: 10,
+        displayName: generateRandWord(),
+        lastSendTime: getTime(),
+        lastContent: "修改昵称为随机字母",
+      });
+    },
+
+    changeDrawer(contact, instance) {
+      instance.changeDrawer({
+        //width: 240,
+        //height: "90%",
+        //offsetX:0 ,
+        //offsetY: ,
+        //position: "center",
+        // inside: true,
+        // offsetX: -280,
+        // offsetY: -100,
+        render: () => {
+          return (
+            <div class="drawer-content">
+              <p>
+                <b>自定义抽屉</b>
+              </p>
+              <p>{contact.displayName}</p>
+            </div>
+          );
+        },
+      });
+    },
+    handleChangeContact(contact, instance) {
+      console.log("Event:change-contact");
+      instance.updateContact({
+        id: contact.id,
+        unread: 0,
+      });
+      instance.closeDrawer();
+    },
+    handleSend(message, next, file) {
+      console.log(message, next, file);
+      setTimeout(() => {
+        next();
+      }, 1000);
+    },
+    handlePullMessages(contact, next, instance) {
+            const { IMUI } = this.$refs;
+            setTimeout(() => {
+                    next(MessagesData[contact.id], true);
+             }, 3000);
+            return;
+            const otheruser = {
+                id: contact.id,
+                displayName: contact.displayName,
+                avatar: contact.avatar,
+            };
+            setTimeout(() => {
+                const messages = [
+                    generateMessage(instance.currentContactId, this.user),
+                    generateMessage(instance.currentContactId, otheruser),
+                    generateMessage(instance.currentContactId, this.user),
+                    generateMessage(instance.currentContactId, otheruser),
+                    generateMessage(instance.currentContactId, this.user),
+                    generateMessage(instance.currentContactId, this.user),
+                    generateMessage(instance.currentContactId, otheruser),
+                    {
+                        ...generateMessage(instance.currentContactId, this.user),
+                        ...{ status: "failed" },
+                    },
+                    ];
+                    let isEnd = false;
+                    if (instance.getMessages(instance.currentContactId).length+messages.length >11)
+                    isEnd = true;
+                    next(messages, isEnd);
+            }, 500);      
+    },
+    handleChangeMenu() {
+      console.log("Event:change-menu");
+    },
+    openCustomContainer() {
+
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+   
+   .imui-center{
+      margin-bottom:"60px"; 
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      height: 80vh;
+   }
+
+ .lemon-wrapper{
+          border:"1px solid #ddd";
+          height: "60%" !important;
+     }
+     .lemon-drawer{
+          border:"1px solid #ddd";
+          border-left:"0";
+     }
+
+
+ .more {
+    font-size: 12px;
+    line-height: 24px;
+    height: 24px;
+    position: absolute;
+    top: 14px;
+    right: 14px;
+    cursor: pointer;
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+    color: #f1f1f1;
+    display: inline-block;
+    border-radius: 4px;
+    background: #111;
+    padding: 0 8px;
+}
+
+
+</style>
+
+

+ 616 - 0
src/views/qw/qwChat/qq.vue

@@ -0,0 +1,616 @@
+<template>
+  <div class="app-container">
+      <div class="imui-center qq-lemon-imui">
+        <lemon-imui  class="lemon-slot"
+          :width="windowWidth"
+          :height="windowHeight"
+          :user="UserData"
+           ref="IMUI"  
+          :contextmenu="contextmenu"
+          :contact-contextmenu="contactContextmenu"
+          :theme="theme"
+          :hide-menu="hideMenu"
+          :hide-menu-avatar="hideMenuAvatar"
+          :hide-message-name="hideMessageName"
+          :hide-message-time="hideMessageTime"
+          :messageTimeFormat="messageTimeFormat"
+          @change-menu="handleChangeMenu"
+          @pull-messages="handlePullMessages"
+          @change-contact="handleChangeContact"
+          @message-click="handleMessageClick"
+          @menu-avatar-click="handleMenuAvatarClick"
+          @send="handleSend">
+          <template #cover>
+                <div class="cover">
+                <i class="lemon-icon-message"></i>
+                <p><b>自定义封面 Lemon</b>IMUI</p>
+                </div>
+          </template>
+          <template #message-title="contact">
+            <div>
+                <div style="display:flex;justify-content:space-between">
+                    <span>{{contact.displayName}}</span>
+                    <span style="font-size:12px;">
+                      <span>打开抽屉:</span>
+                      <span style="cursor:pointer;"  @click="openDrawer('right')">
+                        右侧  | 
+                      </span>
+                      <span style="cursor:pointer;" @click="openDrawer('rightInside')">
+                        右侧内部  | 
+                      </span>
+                      <span style="cursor:pointer;" @click="openDrawer('center')">
+                        居中
+                      </span>
+                    </span>
+                 </div>
+                  <div v-if="contact.isGroup" class="slot-group-menu">
+                      <span>聊天</span>
+                      <span>公告</span>
+                      <span>相册</span>
+                      <span>文件</span>
+                      <span>活动</span>
+                      <span>设置(左键弹出菜单)</span> 
+                </div>
+            </div>
+          </template>
+
+          <template #sidebar-contact-fixedtop="contact">
+                <div class="slot-contact-fixedtop">
+                  <input class="slot-search" placeholder="搜索通讯录" />
+                </div>
+          </template>
+
+         <template #message-side="contact">
+              <div v-if="contact.isGroup" class="slot-group">
+                    <div class="slot-group-title">群通知</div>
+                    <div class="slot-group-notice">
+                      进群请改备注,格式,工作地点-姓名,请大家配合谢谢
+                    </div>
+                    <div class="slot-group-title">群成员</div>
+                    <div class="slot-group-panel">
+                      <input class="slot-search" placeholder="搜索群成员" />
+                      <div class="slot-group-member">
+                        <div class="avatar"><img src="https://p.qqan.com/up/2020-2/2020022821001845128.jpg"></div>
+                        <div class="name">像梦一样自由</div> 
+                      </div>
+                      <div class="slot-group-member">
+                        <div class="avatar"><img src="https://p.qqan.com/up/2018-4/15244505348390471.jpg"></div>
+                        <div class="name">野火</div> 
+                      </div>
+                      <div class="slot-group-member">
+                        <div class="avatar"><img src="https://p.qqan.com/up/2021-1/20211411391666.jpg"></div>
+                        <div class="name">霸王花</div> 
+                      </div>
+                       <div class="slot-group-member">
+                        <div class="avatar"><img src="https://p.qqan.com/up/2021-1/2021113104111220.jpg"></div>
+                        <div class="name">森系Style</div> 
+                      </div>
+                      
+                    </div>
+               </div>
+          </template>
+        
+        </lemon-imui>
+      </div>
+   
+  </div>
+</template>
+
+<script>
+
+import { getQrCode } from '@/api/qw/login';
+import { getDeviceId} from '@/api/qw/account';
+
+import LemonMessageVoice from "lemon-imui/examples/lemon-message-voice";
+
+import ContactsData from "../database/contacts";
+import MessagesData from "../database/messages";
+import EmojiData from "../database/emoji";
+import 'lemon-imui/dist/index.css';
+
+export default {
+  name: "qqChat",
+  components: {  },
+  data() {
+    return {
+      theme: "default",
+      contactContextmenu: [
+        {
+          text: "删除该聊天",
+          click: (e, instance, hide) => {
+            const { IMUI, contact } = instance;
+            IMUI.updateContact({
+              id: contact.id,
+              lastContent: null,
+            });
+            if (IMUI.currentContactId == contact.id) IMUI.changeContact(null);
+            hide();
+          },
+        },
+        {
+          text: "设置备注和标签",
+        },
+        {
+          text: "投诉",
+        },
+        {
+          icon: "lemon-icon-message",
+          text: "加入黑名单",
+          render: (h, instance, hide) => {
+            return (
+              <div style="display:flex;justify-content:space-between;align-items:center;width:130px">
+                <span>加入黑名单</span>
+                <span>
+                  <input type="checkbox" id="switch" />
+                  <label id="switch-label" for="switch">
+                    Toggle
+                  </label>
+                </span>
+              </div>
+            );
+          },
+        },
+        {
+          click(e, instance, hide) {
+            const { IMUI, contact } = instance;
+            IMUI.removeContact(contact.id);
+            if (IMUI.currentContactId == contact.id) IMUI.changeContact(null);
+            hide();
+          },
+          color: "red",
+          text: "删除好友",
+        },
+      ],
+      contextmenu: [
+        {
+          click: (e, instance, hide) => {
+            const { IMUI, message } = instance;
+            const data = {
+              id: generateRandId(),
+              type: "event",
+              //使用 jsx 时 click必须使用箭头函数(使上下文停留在vue内)
+              content: (
+                <div>
+                  <span>
+                    你撤回了一条消息{" "}
+                    <span
+                      v-show={message.type == "text"}
+                      style="color:#333;cursor:pointer"
+                      content={message.content}
+                      on-click={e => {
+                        IMUI.setEditorValue(e.target.getAttribute("content"));
+                      }}
+                    >
+                      重新编辑
+                    </span>
+                  </span>
+                </div>
+              ),
+              toContactId: message.toContactId,
+              sendTime: getTime(),
+            };
+            IMUI.removeMessage(message.id);
+            IMUI.appendMessage(data, true);
+            hide();
+          },
+          visible: instance => {
+            return instance.message.fromUser.id == this.user.id;
+          },
+          text: "撤回消息",
+        },
+        {
+          visible: instance => {
+            return instance.message.fromUser.id != this.user.id;
+          },
+          text: "举报",
+        },
+        {
+          text: "转发",
+        },
+        {
+          visible: instance => {
+            return instance.message.type == "text";
+          },
+          text: "复制文字",
+        },
+        {
+          visible: instance => {
+            return instance.message.type == "image";
+          },
+          text: "下载图片",
+        },
+        {
+          visible: instance => {
+            return instance.message.type == "file";
+          },
+          text: "下载文件",
+        },
+        {
+          click: (e, instance, hide) => {
+            const { IMUI, message } = instance;
+            IMUI.removeMessage(message.id);
+            hide();
+          },
+          icon: "lemon-icon-folder",
+          color: "red",
+          text: "删除",
+        },
+      ],
+      hideMenuAvatar: false,
+      hideMenu: false,
+      hideMessageName: false,
+      hideMessageTime: true,
+      UserData: {
+        id: "1000",
+        displayName: "June",
+        avatar: "https://p.qqan.com/up/2018-4/15244505348390471.jpg",
+      },
+      windowWidth: document.documentElement.clientWidth*0.65,  //实时屏幕宽度
+      windowHeight: document.documentElement.clientHeight*0.7,   //实时屏幕高度
+    };
+  },
+  mounted() {
+    const contactData1 = {
+            id: "contact-1",
+            displayName: "工作协作群",
+            avatar: "http://upload.qqbodys.com/img/weixin/20170804/ji5qxg1am5ztm.jpg",
+            index: "[1]群组",
+            unread: 0,
+            lastSendTime: 1566047865417,
+            lastContent: "2",
+    };
+    const contactData2 = {
+            id: "contact-2",
+            displayName: "自定义内容",
+            avatar: "http://upload.qqbodys.com/img/weixin/20170807/jibfvfd00npin.jpg",
+            //index: "Z",
+            click(next) {
+                next();
+            },
+            renderContainer: () => {
+                return <h1 style="text-indent:20px">自定义页面</h1>;
+            },
+            lastSendTime: 1345209465000,
+            lastContent: "12312",
+            unread: 2,
+    };
+    const contactData3 = {
+            id: "contact-3",
+            displayName: "铁牛",
+            avatar: "http://upload.qqbodys.com/img/weixin/20170803/jiq4nzrkrnd0e.jpg",
+            index: "T",
+            unread: 32,
+            lastSendTime: 3,
+            lastContent: "你好123",
+    };
+    const IMUI = this.$refs.IMUI;
+    IMUI.initContacts(ContactsData);
+    IMUI.initMenus([
+      {
+        name: "messages",
+      },
+      {
+        name: "contacts",
+      },
+      {
+        name: "custom1",
+        title: "自定义按钮1",
+        unread: 0,
+        render: menu => {
+          return <i class="lemon-icon-attah" />;
+        },
+        renderContainer: () => {
+          return (
+            <div class="article">
+              <ul>
+                <li class="article-item">
+                  <h2>人民日报谈网红带货:产品真的值得买吗?</h2>
+                </li>
+                <li class="article-item">
+                  甘肃夏河县发生5.7级地震 暂未接到人员伤亡报告
+                </li>
+                <li class="article-item">
+                  北方多地风力仍强沙尘相伴,东北内蒙古等地迎雨雪
+                </li>
+                <li class="article-item">
+                  英货车案:越南警方采集疑死者家属DNA作比对
+                </li>
+                <li class="article-item">
+                  知名连锁咖啡店的蛋糕吃出活虫 曝光内幕太震惊
+                </li>
+              </ul>
+              <lemon-contact  props={{ contact: contactData1 }} style="margin:20px"/>
+              <lemon-contact props={{ contact: contactData3 }} style="margin:20px" />
+            </div>
+          );
+        },
+        isBottom: true,
+      },
+      {
+        name: "custom2",
+        title: "自定义按钮2",
+        unread: 0,
+        click: () => {
+          alert("拦截导航点击事件");
+        },
+        render: menu => {
+          return <i class="lemon-icon-group" />;
+        },
+        isBottom: true,
+      },
+    ]);
+
+    IMUI.initEmoji(EmojiData);  
+    IMUI.changeContact(13);
+  },
+  methods: {
+
+    messageTimeFormat(time) {
+      console.log("qxj messageTimeFormat:"+time);
+      return this.friendlyDate(time);
+    },
+    changeTheme() {
+       this.theme = this.theme == "default" ? "blue" : "default";
+    },
+    scrollToTop() {
+      document.body.scrollIntoView();
+    },
+    openDrawer(position) {
+            const IMUI = this.$refs.IMUI;
+            const params = {
+                position,
+                render: contact => {
+                return (
+                    <div style="padding:15px">
+                    <h5>{contact.displayName}</h5>
+                    <span style="cursor:pointer;" on-click={IMUI.closeDrawer}>关闭抽屉</span>
+                    </div>
+                );
+                },
+      };
+      if (position == "center") {
+        params.width = "50%";
+        params.height = "50%";
+      } else if (position == "rightInside") {
+        params.height = "90%";
+        params.offsetY = "10%";
+      }
+      IMUI.openDrawer(params);
+    },
+    handlePullMessages(contact, next,instance) {
+      const { IMUI } = this.$refs;
+      console.log("qxj instance:"+instance.getMessages(instance.currentContactId));
+      setTimeout(() => {
+            var tmessages=MessagesData[contact.id];
+            let messages= tmessages.sort((c1, c2) => c1.sendTime - c2.sendTime);
+            var currentMsgs=instance.getMessages(instance.currentContactId);
+            var sorMsgs=[];
+            var num=0
+            for (let index = currentMsgs.length; index < messages.length; index++) {
+                const item = messages[index];
+                if(num>=5){
+                    break;
+                }
+                sorMsgs.push(item);
+                num++;
+            }
+            let isEnd = false;
+            if (currentMsgs.length+sorMsgs.length>=messages.length){
+                isEnd = true;
+            }
+           next(MessagesData[contact.id], isEnd);
+      }, 3000);
+    },
+    handleChangeContact(contact, instance) {
+        console.log("qxj contact:"+JSON.stringify(contact));
+        //instance.updateContact(contact);
+        instance.closeDrawer();
+    },
+    handleSend(message, next, file) {
+      console.log(message, next, file);
+      setTimeout(() => {
+        next();
+      }, 1000);
+    },
+    handleMenuAvatarClick() {
+      console.log("Event:menu-avatar-click");
+    },
+
+    handleMessageClick(e, key, message, instance) {
+      console.log("点击了消息", e, key, message);
+      if (key == "status") {
+        instance.updateMessage({
+          id: message.id,
+          status: "going",
+          content: "正在重新发送消息...",
+        });
+        setTimeout(() => {
+          instance.updateMessage({
+            id: message.id,
+            status: "succeed",
+            content: "发送成功",
+          });
+        }, 2000);
+      }
+    },
+
+    changeMenuAvatarVisible() {
+      this.hideMenuAvatar = !this.hideMenuAvatar;
+    },
+    changeMenuVisible() {
+      this.hideMenu = !this.hideMenu;
+    },
+    changeMessageNameVisible() {
+      this.hideMessageName = !this.hideMessageName;
+    },
+    changeMessageTimeVisible() {
+      this.hideMessageTime = !this.hideMessageTime;
+    },
+    removeMessage() {
+        const { IMUI } = this.$refs;
+        const messages = IMUI.getCurrentMessages();
+        const id = messages[messages.length - 1].id;
+        if (messages.length > 0) {
+            IMUI.removeMessage(id);
+        }
+    },
+    updateMessage() {
+        const { IMUI } = this.$refs;
+        const messages = IMUI.getCurrentMessages();
+        const message = messages[messages.length - 1];
+        if (messages.length > 0) {
+            const update = {
+            id: message.id,
+            status: "succeed",
+            type: "file",
+            fileName: "被修改成文件了.txt",
+            fileSize: "4200000",
+            };
+            if (message.type == "event") {
+            update.fromUser = this.user;
+            }
+            IMUI.updateMessage(update);
+            IMUI.messageViewToBottom();
+      }
+    },
+    appendCustomMessage() {
+        const { IMUI } = this.$refs;
+        const message = {
+            id: generateRandId(),
+            status: "succeed",
+            type: "voice",
+            sendTime: getTime(),
+            content: "语音消息",
+            params1: "1",
+            params2: "2",
+            toContactId: "contact-1",
+            fromUser: this.user,
+        };
+        IMUI.appendMessage(message, true);
+    },
+    appendMessage() {
+        const { IMUI } = this.$refs;
+        const contact = IMUI.currentContact;
+        const message = generateMessage("contact-3");
+        message.fromUser = {
+            ...message.fromUser,
+            ...this.user,
+        };
+        IMUI.appendMessage(message, true);
+        console.log("🚀 ~ file: App.vue ~ line 1508 ~ appendMessage ~ message", message)
+    },
+    appendEventMessage() {
+      const { IMUI } = this.$refs;
+      const message = {
+        id: generateRandId(),
+        type: "event",
+        content: (
+          <span>
+            邀请你加入群聊{" "}
+            <span
+              style="color:#333;cursor:pointer"
+              on-click={() => alert("OK")}
+            >
+              接受
+            </span>
+          </span>
+        ),
+        toContactId: "contact-3",
+        sendTime: getTime(),
+      };
+      IMUI.appendMessage(message, true);
+    },
+    updateContact() {
+      this.$refs.IMUI.updateContact({
+        id: "contact-3",
+        unread: 10,
+        displayName: generateRandWord(),
+        lastSendTime: getTime(),
+        lastContent: "修改昵称为随机字母",
+      });
+    },
+    handleChangeMenu() {
+      console.log("Event:change-menu");
+    },
+    openCustomContainer() {
+
+    }
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+    .imui-center{
+        margin-bottom:"60px"; 
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        height: 80vh;
+    }
+    .lemon-wrapper{
+        border:"1px solid #ddd";
+        height: "60%" !important;
+    }
+    .lemon-drawer{
+        border:"1px solid #ddd";
+        border-left:"0";
+    }
+    .more {
+        font-size: 12px;
+        line-height: 24px;
+        height: 24px;
+        position: absolute;
+        top: 14px;
+        right: 14px;
+        cursor: pointer;
+        -webkit-user-select: none;
+        -moz-user-select: none;
+        -ms-user-select: none;
+        user-select: none;
+        color: #f1f1f1;
+        display: inline-block;
+        border-radius: 4px;
+        background: #111;
+        padding: 0 8px;
+    }
+    
+</style>
+
+
+<style lang="stylus">
+   .lemon-container__title
+     padding-bottom:10px;
+   .slot-group
+      width:200px;
+      .slot-group-title
+        padding:0 0 10px 0;
+        .slot-group-notice
+           padding: 10px 10px;
+     
+      .slot-search
+           width:calc(100% - 20px) ;
+           margin:5px 10px;
+           padding:3px 0px;
+
+      .slot-group-member
+          display: flex;
+          padding: 5px 0;
+          flex:1;
+          font-size: 14px;
+          align-items: center;
+          .avatar
+              width: 30px;
+              height: 30px;
+              line-height: 30px;
+              margin-right:5px;
+              img 
+                vertical-align: middle;
+                border-style: none;
+                width: 100%;
+                height: 100%;
+                line-height: 30px;
+                border-radius: 50%;
+
+      
+
+</style>

+ 2 - 8
src/views/qw/qwLogin/index.vue

@@ -6,10 +6,9 @@
           <el-card class="scan-card" shadow="hover">
             <div class="scan-card-content">
               <h1 class="title">扫码登录</h1>
-               <el-form-item label="手机号" prop="account" style="width:100%">
+               <el-form-item label="号" prop="account" style="width:100%">
                   <el-input  v-model="qwForm.account" placeholder="请输入账号" style="margin-bottom: 20px"></el-input>
                </el-form-item>
-              
               <el-button type="primary" @click.native.prevent="handleLogin">登录</el-button>
               <div class="qrcode-container" v-show="showQRCode">
                 <div>
@@ -53,7 +52,6 @@ export default {
   },
   methods: {
     handleLogin() {
-     
       this.$refs.qwForm.validate(valid => {
           if (valid) {
                   const account = this.qwForm.account;
@@ -104,7 +102,7 @@ export default {
 };
 </script>
 
-<style scoped>
+<style lang="scss" scoped>
 .scan-login {
   height: 80vh;
   display: flex;
@@ -112,24 +110,20 @@ export default {
   align-items: center;
   justify-content: center;
 }
-
 .scan-card {
   width: 400px;
 }
-
 .scan-card-content {
   display: flex;
   flex-direction: column;
   align-items: center;
   padding: 20px;
 }
-
 .title {
   font-size: 24px;
   margin-bottom: 30px;
   margin-top: 0px;
 }
-
 .qrcode-container {
   position: relative;
   text-align: center;

+ 10 - 0
webpack.config.js

@@ -0,0 +1,10 @@
+module.exports = {
+    module: {
+      rules: [
+        {
+            test: /\.css$/,
+            use: ['style-loader', 'css-loader']
+        },
+      ],
+    },
+ };