TheCurrentSession.vue 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576
  1. <template>
  2. <div class="row allAlignment">
  3. <leftNav/>
  4. <div>
  5. <hader/>
  6. <messageCenter></messageCenter>
  7. <div class="row">
  8. <!-- 聊天列表模块 -->
  9. <div class="sessionList">
  10. <el-collapse>
  11. <el-collapse-item>
  12. <template slot="title">
  13. <span>会话中 <span>({{sessionList.length }})</span></span>
  14. <!-- <span class="sessionList_span">排队人数(10)</span> -->
  15. </template>
  16. <div :class="{session_choose:dataIndex ==index && sessionType ==1}"
  17. class="user session_style" @click="chooseDialogue(1,index)"
  18. v-for="(item,index) in sessionList">
  19. <div class="row allAlignment item-center">
  20. <span style="font-weight:bold;color:#666;font-size:14px;">{{item.name}}</span>
  21. <span style="color:#999;font-size:.12px;">{{item.intime}}</span>
  22. </div>
  23. <div class="row allAlignment item-center">
  24. <div style="width:90%;font-size:.12px;" class="ellipsis">
  25. <span v-html="item.text"></span>
  26. </div>
  27. <span v-if="item.num > 0" class="markNumber">{{item.num}}</span>
  28. </div>
  29. </div>
  30. </el-collapse-item>
  31. <el-collapse-item>
  32. <template slot="title">
  33. <span>已离线<span>({{offlineList.length }})</span></span>
  34. </template>
  35. <div :class="{session_choose:dataIndex ==index && sessionType ==2}"
  36. class="user session_style" @click="chooseDialogue(2,index)"
  37. v-for="(item,index) in offlineList">
  38. <div class="row allAlignment item-center">
  39. <span style="font-weight:bold;color:#999;font-size:14px;">{{item.name}}</span>
  40. <span style="color:#999;font-size:12px;">{{item.intime}}</span>
  41. </div>
  42. <div class="row allAlignment item-center">
  43. <div style="width:90%;color:#999;font-size:12px;" class="ellipsis">
  44. <span v-html="item.text"></span>
  45. </div>
  46. </div>
  47. </div>
  48. </el-collapse-item>
  49. <el-collapse-item>
  50. <template slot="title">
  51. <span>排队中<span>({{lineUp.length}})</span></span>
  52. </template>
  53. <div class="user" v-for="item in lineUp">
  54. <div class="row allAlignment item-center">
  55. <span style="font-weight:bold;color:#666;font-size:14px;">{{item}}</span>
  56. <span style="color:#999;font-size:12px;">{{item}}</span>
  57. </div>
  58. </div>
  59. </el-collapse-item>
  60. </el-collapse>
  61. </div>
  62. <!-- 聊天模块 -->
  63. <div class="chat">
  64. <div class="chatTop row item-center allAlignment" style="padding:0 10px;">
  65. <p class="ellipsis"
  66. style="width:40%;font-size:14px;font-weight:bold;color:#666;"
  67. >{{sessionName}}</p>
  68. <div class="row item-center rightAlignment"
  69. style="width:50%;height:100%;color:#999;font-size:12px;">
  70. <p class="row item-center" @click="getServiceList()">
  71. <!-- 转接 -->
  72. <el-popover placement="right" width="400" trigger="click">
  73. <el-table :data="transferList">
  74. <el-table-column width="150" property="groupname" label="组名"></el-table-column>
  75. <el-table-column width="100" property="kfname" label="姓名"></el-table-column>
  76. <el-table-column label="操作">
  77. <template slot-scope="scope">
  78. <el-button size="mini" @click="transfer(scope.$index, scope.row)">转入
  79. </el-button>
  80. </template>
  81. </el-table-column>
  82. </el-table>
  83. <el-button slot="reference"><i style="font-size:14px" class="el-icon-refresh"></i>
  84. 转接
  85. </el-button>
  86. </el-popover>
  87. </p>
  88. <p class="row item-center" style="margin-left:10px;">
  89. <el-button size="mini" type="text" @click="open">
  90. <i style="font-size:15px" class="el-icon-close"></i>结束服务
  91. </el-button>
  92. </p>
  93. </div>
  94. </div>
  95. <div class="chat-box scroll" id='chat_box'>
  96. <div v-for="(item,index) in data" :key="index">
  97. <!-- 系统转接 -->
  98. <!-- <div class="row center" v-if="item.type == 'system'">
  99. <p style="font-size:12px;color:#bbb;">09.30.45 由问答机器人转接</p>
  100. </div> -->
  101. <!-- 用户消息 -->
  102. <div style="padding:0 10px;margin-top:20px;" v-if="item.type == 'user'">
  103. <div style="height:37px;font-size:.12px;color:#bbb;"
  104. class="row item-center"
  105. >{{item.time}}
  106. </div>
  107. <div class="row">
  108. <div v-if="item.content.img != ''">
  109. <!-- :fit="contain " style="width: 100%; height: 100px"-->
  110. <el-image style="width: 200px; height: 100px"
  111. @click="handlePictureCardPreview(img_http+item.content.img)"
  112. :src="img_http + item.content.img" :fit="fit"></el-image>
  113. </div>
  114. <p v-else class="chatMsg user_box" v-html="item.content.text"></p>
  115. </div>
  116. </div>
  117. <!-- 客服消息 -->
  118. <div style="padding:0 10px;margin-top:20px;" class="rightAlignment"
  119. v-if="item.type == 'service'">
  120. <div style="height:37px;font-size:12px;color:#bbb;"
  121. class="row item-center rightAlignment">{{item.time}}
  122. </div>
  123. <div class="row item-center rightAlignment">
  124. <div v-if="item.content.img !=''">
  125. <!-- :fit="contain" style="background:#F5F5F5; border-radius: 10px 0 10px 10px;"-->
  126. <el-image style="width: 200px; height: 100px"
  127. @click="handlePictureCardPreview(img_http+item.content.img)"
  128. :src="img_http + item.content.img" :fit="fit"></el-image>
  129. </div>
  130. <p v-else class="chatMsg message_box" v-html="item.content.text"></p>
  131. </div>
  132. </div>
  133. <!-- 点击图片放大 -->
  134. <el-dialog :visible.sync="dialogVisible">
  135. <img width="100%" :src="dialogImageUrl" alt="">
  136. </el-dialog>
  137. </div>
  138. </div>
  139. <div class="key column allAlignment">
  140. <div class="frce row item-center allAlignment">
  141. <div class="row ">
  142. <img class="hover"
  143. :src="isFrce?require('@/assets/img/frcea.png'):require('@/assets/img/frce.png')"
  144. @click="frceClick" style="margin-right:16px;"/>
  145. <input type="file" id="file" style="display:none;" @change="uploadIMG"/>
  146. <label for="file" class="row item-center" style="height:100%">
  147. <img class="hover" src="@/assets/img/img.png"/>
  148. </label>
  149. </div>
  150. <div>
  151. <i :class="{disable : is_eva_btn }" @click="evaluation()"
  152. class="el-icon-postcard hover evaluation"></i>
  153. </div>
  154. </div>
  155. <textarea draggable="false" class="input scroll" id="input" v-model="inputValue"
  156. @keyup.enter="listenEnter($event)" @keyup.ctrl.enter="lineFeed()"></textarea>
  157. <div class="row rightAlignment" style="width: 100%;">
  158. <div @click="sendMessage()" class="msgInputBtn row center">发送</div>
  159. </div>
  160. <div class="frceBox scroll" v-if="isFrce">
  161. <div class="row wrap average">
  162. <p class="row center" v-for="(item,index) in frceArr" :key="item.id">
  163. <img class="hover" @click="frceCenterClick(item)"
  164. :src="require(`@/assets/img/${index}.gif`)"/>
  165. </p>
  166. </div>
  167. </div>
  168. </div>
  169. <!-- 发送消息音频提示 -->
  170. <!-- <audio id="send" src="@/assets/audio/send.wav"></audio> -->
  171. </div>
  172. <!-- 快捷回复模块 -->
  173. <div class="FastReply">
  174. <div class="FastReplyHader row item-center">快捷回复</div>
  175. <div class="FastReplySwitch row item-center average">
  176. <p :class="FastReplySwitch=='one'?'active':''" class="hover"
  177. @click="FastReplySwitchClick('one')">公有库</p>
  178. <span style="width:1px;height:18px;background:#ccc;display: block;"></span>
  179. <p :class="FastReplySwitch=='tow'?'active':''"
  180. class="hover"
  181. @click="FastReplySwitchClick('tow')"
  182. >私有库</p>
  183. </div>
  184. <!-- <div class="serch row center">
  185. <div class="serchBox">
  186. <input placeholder="搜索快捷回复" v-model="serch" type="text" />
  187. <img src="@/assets/img/serch.png" />
  188. </div>
  189. </div> -->
  190. <!-- <div> -->
  191. <!-- <el-collapse>
  192. <el-collapse-item>
  193. <template slot="title" name="el-icon-caret-bottom">
  194. <span>常用语</span>
  195. </template> -->
  196. <div class="FastReplyBox">
  197. <!-- <ul class="FastReplyList"> -->
  198. <!-- <li @click="getLanguage(item.content)" class="row cursor_text" v-for="item in FastReply.sysWords" :key="item.id"> -->
  199. <div @click="getLanguage(item.content)" class="row cursor_text item-center"
  200. v-for="item in FastReply.sysWords" :key="item.id" v-if="FastReplySwitch == 'one'">
  201. <span class="title_span" style=" width: 100%">#{{item.title}}</span>
  202. <!-- <div class="ellipsis" style=" width: 70%">
  203. <span class="content_span">{{item.content}}</span>
  204. </div> -->
  205. </div>
  206. <!-- </li> -->
  207. <div @click="getLanguage(item.content)" class="row cursor_text item-center"
  208. v-for="item in FastReply.userWords" :key="item.id" v-if="FastReplySwitch == 'tow'">
  209. <span class="title_span" style=" width: 100%">#{{item.title}}</span>
  210. <!-- <div class="ellipsis" style=" width: 70%">
  211. <span class="content_span">{{item.content}}</span>
  212. </div> -->
  213. </div>
  214. <!-- <li class="row cursor_text" v-for="item in FastReply.userWords" :key="item.id">
  215. <div v-if="FastReplySwitch == 'tow'" class="row item-center">
  216. <p style="margin-right:10px;color:#666;font-size:14px;font-weight: bold;"
  217. >#{{item.title}}</p>
  218. <p class="ellipsis" style="width: 60%; color:#999;font-size:14px;">{{item.content}}</p>
  219. </div>
  220. </li> -->
  221. <!-- </ul> -->
  222. </div>
  223. <!-- </el-collapse-item>
  224. </el-collapse> -->
  225. <!-- </div> -->
  226. </div>
  227. <!-- 用户信息模块 -->
  228. <div class="userinfo">
  229. <div style="color:#666;font-weight:bold;font-size:14px;">访问信息</div>
  230. <div style="margin-top:10px;color:#999;font-size:14px;" class="userData wrap">
  231. <!-- <p>来源:{{session_user_info.website}}</p> -->
  232. <p @dblclick="get_ip_Info" class="get_ip" :data-clipboard-text="session_user_info.ip" >IP地址:{{session_user_info.ip}}</p>
  233. <p>来源终端:{{session_user_info.system}}-{{session_user_info.browse}}</p>
  234. </div>
  235. <div style="color:#666;font-weight:bold;font-size:14px; margin-top:30px;">用户信息</div>
  236. <div style="margin-top:10px;color:#999;font-size:14px;" class="userData wrap">
  237. <div class="user_info_box">
  238. <span>账号:</span>
  239. <input class="user_text" style="background: #ECF4FF;"
  240. v-model="session_user_info.account_name" readonly type="text">
  241. </div>
  242. <div class="user_info_box">
  243. <span>标签:</span>
  244. <el-select v-model="value" @focus='focusFun' @change='labelChange' placeholder="请选择">
  245. <el-option
  246. v-for="item in options"
  247. :key="item.id"
  248. :label="item.name"
  249. :value="item.id">
  250. </el-option>
  251. </el-select>
  252. </div>
  253. <div class="user_info_box">
  254. <span>昵称:</span>
  255. <el-input style="width:70%" v-model="session_user_info.nick_name"
  256. placeholder="请输入内容"></el-input>
  257. </div>
  258. <div class="user_info_box">
  259. <span>手机:</span>
  260. <el-input style="width:70%" v-model="session_user_info.account_phone"
  261. placeholder="请输入内容"></el-input>
  262. </div>
  263. <div class="user_info_box">
  264. <span>邮箱:</span>
  265. <el-input style="width:70%" v-model="session_user_info.account_email"
  266. placeholder="请输入内容"></el-input>
  267. </div>
  268. <div class="user_info_box">
  269. <span>地址:</span>
  270. <el-input style="width:70%" v-model="session_user_info.address"
  271. placeholder="请输入内容"></el-input>
  272. </div>
  273. <p>访客备注:</p>
  274. </div>
  275. <textarea draggable="false" class="userDataInput" v-model="session_user_info.remark"
  276. style="margin-top:10px;color:#999;"></textarea>
  277. <div class=" row rightAlignment">
  278. <div class="userBtn row center" @click="bt_update()">保存</div>
  279. </div>
  280. </div>
  281. </div>
  282. <el-dialog
  283. :close-on-click-modal="true"
  284. :close-on-press-escape ="true"
  285. title="是否发送图片"
  286. :visible.sync="dialogPaste"
  287. width="35%">
  288. <!-- :before-close="handleClose" -->
  289. <!-- <span>这是一段信息</span> -->
  290. <el-image
  291. :src="pasteUrl" :fit="fit"></el-image>
  292. <span slot="footer" class="dialog-footer">
  293. <el-button @click="dialogPaste = false">取 消</el-button>
  294. <el-button type="primary" @click="handleClose()">确 定</el-button>
  295. </span>
  296. </el-dialog>
  297. </div>
  298. </div>
  299. </template>
  300. <script>
  301. import "@/css/index.css";
  302. import Clipboard from 'clipboard';
  303. import {mapState, mapGetters} from 'vuex'; //先要引入
  304. import leftNav from "@/components/leftNav";
  305. import hader from "@/components/hader";
  306. import messageCenter from "@/components/messageCenter";
  307. export default {
  308. name: "TheCurrentSession",
  309. data() {
  310. return {
  311. /***********************************/
  312. inputValue: "", //输入框内容
  313. isFrce: false, //表情包开关
  314. frceArr: [], //表情包数组
  315. // data: [], //当前对话数据
  316. // dataIndex:0,//定位当前会话,默认是0
  317. FastReply: "", //快捷回复
  318. FastReplySwitch: "one", //快捷回复开关
  319. user_info: '',//用户信息
  320. token: "",
  321. time: "",
  322. sessionList: [],//会话列表
  323. offlineList: [],//离线列表
  324. transferList: [],//转接列表
  325. // sessionType:1,//选择列表状态默认未1(1是会话中,2是离线)
  326. lineUp: [],//排队列表
  327. conversationId: '',//会话工单
  328. // sessionName:'',//当前会话用户名字
  329. session_user_info: {},//当前会话用户信息
  330. img_http:'http://kfadmin.bocai186.com',//图片路径域
  331. // img_http: 'http://192.168.2.187:8090',//图片路径域
  332. fit: 'scale-down',//图片渲染样式
  333. is_eva_btn: true,//
  334. trigger_condition: 0,//评价触发条件
  335. webTime: '',//客服会话时间
  336. session_marked: '',//客服当前会话标记
  337. sensitive: [],//客服敏感词数据
  338. userSensitiveWords: [],//用户敏感词
  339. sensitiveNumber: 0,//关键词次数
  340. isTrue: true,//编辑用户信息按钮开关
  341. options: [],
  342. label_id: '',
  343. value: '请选择',
  344. dialogPaste: false,
  345. dialogVisible: false, //图片放大
  346. dialogImageUrl: '', // 放大的图片
  347. dialogUrl: false,
  348. pasteUrl:'',//截屏图片
  349. // fit:'contain',//
  350. }
  351. },
  352. /**
  353. *
  354. */
  355. methods: {
  356. /***************图片放大*************** */
  357. handlePictureCardPreview(url) {
  358. this.dialogImageUrl = url;
  359. this.dialogVisible = true;
  360. },
  361. /************回车提交************/
  362. listenEnter(event) {
  363. if (event.keyCode === 13) {
  364. this.inputValue = this.inputValue.replace(/\s/g, '')
  365. // this.inputValue = this.inputValue.replace(/\n /g,'')
  366. this.sendMessage(); // 发送文本
  367. }
  368. },
  369. /************ctrl+center 换行***********/
  370. lineFeed() {
  371. this.inputValue = this.inputValue + '\n'
  372. },
  373. /********************发送获取标签数据指令*******************/
  374. focusFun() {
  375. // console.log('haha');
  376. this.$websocket.send(JSON.stringify({type: 'userlabeall'}));
  377. },
  378. /**************************获取选择标签ID****************/
  379. labelChange(e) {
  380. this.label_id = e;
  381. },
  382. /******************消息数据接收********************/
  383. chatMessage(redata) {
  384. // 用户接入数据
  385. if (redata.message_type == "connect") {
  386. let _this = this;
  387. this.webTime = redata.data.user_info.intime;
  388. let arr = [
  389. redata.data,
  390. this.offlineList,
  391. this.sessionList,
  392. this.sessionType,
  393. this.dataIndex,
  394. this.data,
  395. this.sessionName
  396. ]
  397. _this.$public.visitorsConnect(arr, function (data) {
  398. //离线匹配列表访客链接回调
  399. _this.$store.dispatch("SET_OFFLINE", data);
  400. }, function (data, list, offline, type, num, dataList, name) {
  401. // console.log(name)
  402. //新访客链接回调
  403. _this.sessionType = type;
  404. _this.dataIndex = num;
  405. //将用户添加到会话列表中
  406. _this.sessionList.push(data.user_info);
  407. //获取接入的用户信息
  408. _this.get_user_info(list);
  409. //获取接入的用户信息写入vuex
  410. _this.$store.dispatch("SET_SESSION", list);
  411. _this.$store.dispatch("SET_OFFLINE", offline);
  412. _this.$store.dispatch("SET_TYPE", type);//选择类型(会话/离线)
  413. _this.$store.dispatch("SET_NUM", num);//列表下标
  414. _this.$store.dispatch("SET_CURRENT", dataList);//当前会话数据
  415. if (name) {
  416. _this.$store.dispatch("SET_SESSION_NAME", name);//当前会话对象名
  417. }
  418. })
  419. }
  420. //用户离线后会话窗口切换
  421. if (redata.message_type == "userClose") {
  422. let _this = this;
  423. let arr = [
  424. redata.data,
  425. this.sessionList,
  426. this.offlineList,
  427. this.sessionType,
  428. this.dataIndex,
  429. this.sessionName,
  430. this.data
  431. ]
  432. _this.$public.userOffline(arr, function (session, offline, type, index, name, list, userInfo) {
  433. _this.sessionList = [];
  434. _this.sessionList = session;
  435. _this.offlineList = [];
  436. _this.offlineList = offline;
  437. _this.sessionType = type;
  438. _this.dataIndex = index;
  439. this.$store.dispatch("SET_CURRENT", list);//当前会话数据
  440. _this.$store.dispatch("SET_NUM", index);
  441. _this.$store.dispatch("SET_TYPE", type);
  442. _this.$store.dispatch("SET_SESSION", session);
  443. _this.$store.dispatch("SET_OFFLINE", offline);
  444. _this.get_user_info(userInfo);
  445. })
  446. }
  447. //用户会话结束窗口切换
  448. if (redata.message_type == "delUser") {
  449. let _this = this;
  450. let arr = [
  451. redata.data,
  452. this.sessionList,
  453. this.offlineList,
  454. this.sessionType,
  455. this.dataIndex,
  456. this.sessionName,
  457. this.data
  458. ]
  459. _this.$public.sessionEnd(arr, function (session, offline, type, index, name, list, userInfo) {
  460. _this.sessionList = [];
  461. _this.sessionList = session;
  462. _this.offlineList = [];
  463. _this.offlineList = offline;
  464. _this.sessionType = type;
  465. _this.dataIndex = index;
  466. _this.$store.dispatch("SET_CURRENT", list);//当前会话数据
  467. _this.$store.dispatch("SET_NUM", index);
  468. _this.$store.dispatch("SET_TYPE", type);
  469. _this.$store.dispatch("SET_SESSION", session);
  470. _this.$store.dispatch("SET_OFFLINE", offline);
  471. _this.get_user_info(userInfo);
  472. })
  473. }
  474. //获取标签数据信息
  475. if (redata.message_type == "userlabeall") {
  476. if (Object.values(redata.data).length > 0) {
  477. this.options = [];
  478. this.options = Object.values(redata.data);
  479. this.value = this.options[0].name;
  480. }
  481. }
  482. //接收系统操作信息状态在线客服列表
  483. if (redata.message_type == "onlinekfs") {
  484. this.transferList = [];
  485. this.transferList = this.transferList.concat(redata.data);
  486. }
  487. //接收会话时间
  488. if (redata.message_type == "webTime") {
  489. // console.log(this.data);
  490. this.$set(this.data[this.session_marked], 'time', redata.data.webTime);
  491. }
  492. //客服手动转接用户成功
  493. if (redata.message_type == "trunconnect") {
  494. this.sessionList.splice(this.dataIndex, 1);
  495. this.$message({
  496. message: '转接用户成功...',
  497. type: 'success'
  498. });
  499. if (this.sessionList.length > 1) {
  500. this.dataIndex = 0;
  501. this.$store.dispatch("SET_NUM", 0);
  502. // this.data = []
  503. } else {
  504. // this.data = [];
  505. this.$store.dispatch("SET_CURRENT", []);//当前会话数据
  506. this.$store.dispatch("SET_SESSION_NAME", '');
  507. }
  508. }
  509. this.automaticRolling();
  510. },
  511. /*********************图片发送*********************/
  512. uploadIMG(e) {
  513. //console.log(e);
  514. let self = this;
  515. let files = e.target.files || e.dataTransfer.files;
  516. if (!files.length) return;
  517. const isJPG = files.type == 'image/jpeg' || 'image/jpg' || 'image/png' || 'image/svg';
  518. const isLt2M = file.size / 1024 / 1024 < 2;
  519. if (!isJPG) {
  520. this.$message.error('上传只能是图片格式!');
  521. return false;
  522. }
  523. if (!isLt2M) {
  524. this.$message.error('上传图片大小不能超过 2MB!');
  525. return false;
  526. }
  527. let picavalue = files[0];
  528. //console.log(picavalue);
  529. if (picavalue.size / 1000 > 20000) {
  530. this.$message({
  531. message: "图片过大不支持上传",
  532. type: "warning"
  533. });
  534. } else {
  535. this.$public.imgPreview(picavalue, function (imgSrc, formData) {
  536. self.upImg(formData)
  537. });
  538. }
  539. },
  540. /****/
  541. upImg(formData){
  542. let self = this;
  543. // 数据结构请求
  544. self.post('api/' + self.$ports.uploadImg, formData).then(res => {
  545. //console.log(res.data.data.src)
  546. if (res.data.code == 1) {
  547. let isFirst = true;
  548. //判断客服是否发送的第一句话
  549. if (self.data.length > 0) {
  550. for (let i = 0; i < self.data.length; i++) {
  551. if (self.data[i].type == 'service') {
  552. isFirst = false;
  553. break;
  554. }
  555. }
  556. }
  557. let obj = {
  558. text: '',
  559. img: res.data.data.src,
  560. type: false,
  561. };
  562. let chatList = self.sessionList[self.dataIndex];
  563. chatList.data.push({type: 'service', content: obj, time:self.webTime});
  564. // self.data = [];
  565. // self.data = chatList.data;
  566. // self.$store.dispatch("SET_CURRENT", []);
  567. self.$store.dispatch("SET_CURRENT", chatList.data);
  568. let type = 'chatMessage';
  569. let data = {
  570. from_name: self.user_info.user_name,//发送者
  571. from_avatar: self.user_info.avatar,//发送者头像
  572. from_id: "KF" + self.user_info.id,//发送者id
  573. content: JSON.stringify(obj),
  574. to_id: self.sessionList[self.dataIndex].id,
  575. to_name: self.sessionList[self.dataIndex].name,
  576. conversationId: self.sessionList[self.dataIndex].conversationId,
  577. isFirst: isFirst,//是否是第一句话
  578. sensitiveNumber: 0,//敏感词次数
  579. }
  580. self.websocketsend(JSON.stringify({type, data}))
  581. }
  582. })
  583. },
  584. /*****************转接会话用户确认******************/
  585. transfer(e) {
  586. if (this.sessionList.length > 0) {
  587. this.$confirm('此操作将当前用户转出, 是否继续?', '温馨提示!', {
  588. confirmButtonText: '确定',
  589. cancelButtonText: '取消',
  590. type: 'warning'
  591. }).then(() => {
  592. let type = "changeOtherhKeFu";
  593. let data = {
  594. conversationId: this.sessionType == 1 ? this.sessionList[this.dataIndex].conversationId : this.offlineList[this.dataIndex].conversationId,
  595. togroup: this.transferList[e].groupid,
  596. fromgroup: this.user_info.group_id,
  597. toukfuid: this.transferList[e].kfuid,
  598. fromkfuid: "KF" + this.user_info.id,
  599. uid: this.sessionType == 1 ? this.sessionList[this.dataIndex].id : this.offlineList[this.dataIndex].id,
  600. word: '',
  601. }
  602. //console.log({type,data});
  603. this.websocketsend(JSON.stringify({type, data}))
  604. }).catch(() => {
  605. })
  606. } else {
  607. this.$message({
  608. message: '亲!您没有和用户会话怎么能转接呢。',
  609. type: 'warning'
  610. });
  611. }
  612. },
  613. /******************获取客服人员列表*****************/
  614. getServiceList() {
  615. //console.log(12313);
  616. this.websocketsend(JSON.stringify({"type": "getkfonlines"}))
  617. },
  618. /*****************切换用户会话对象******************/
  619. chooseDialogue(type, index) {
  620. let data = [];
  621. this.sessionType = type;
  622. this.$store.dispatch("SET_TYPE", type);
  623. this.dataIndex = index;
  624. this.$store.dispatch("SET_NUM", index);
  625. if (type == 1) {
  626. data = this.sessionList[index].data;
  627. this.session_user_info = this.sessionList[index];
  628. this.$set(this.sessionList[index], 'num', 0);
  629. this.$store.dispatch("SET_SESSION_NAME", this.sessionList[index].name);
  630. } else if (type == 2) {
  631. data = this.offlineList[index].data;
  632. this.$store.dispatch("SET_SESSION_NAME", this.offlineList[index].name);
  633. this.session_user_info = this.offlineList[index];
  634. }
  635. //this.data = data;
  636. this.$store.dispatch("SET_CURRENT", data);//当前会话数据
  637. },
  638. /***************关闭当前和用户聊天对话***************/
  639. open() {
  640. let _this = this;
  641. if (this.sessionType == 1) {
  642. if (this.sessionList.length < 1) return
  643. } else {
  644. if (this.offlineList.length < 1) return
  645. }
  646. this.$confirm('此操作将关闭和当前用户通话, 是否继续?', '温馨提示!', {
  647. confirmButtonText: '确定',
  648. cancelButtonText: '取消',
  649. type: 'warning'
  650. }).then(() => {
  651. let to_id = this.sessionType == 1 ? this.sessionList[this.dataIndex].id : this.offlineList[this.dataIndex].id;
  652. let conversationId = this.sessionType == 1 ? this.sessionList[this.dataIndex].conversationId : this.offlineList[this.dataIndex].conversationId;
  653. let data = {
  654. "type": "kfCloseUser",
  655. data: {
  656. to_id,
  657. kf_id: "KF" + _this.user_info.id,
  658. group_id: _this.user_info.group_id,
  659. conversationId: conversationId,
  660. type:3,
  661. }
  662. }
  663. if (this.sessionType == 1) {
  664. this.sessionList.splice(this.dataIndex, 1);
  665. if (this.sessionList.length > 0) {
  666. this.$store.dispatch("SET_CURRENT", this.sessionList[0]);//当前会话数据
  667. this.$store.dispatch("SET_SESSION_NAME", this.sessionList[0].name);
  668. } else {
  669. this.$store.dispatch("SET_CURRENT", []);//当前会话数据
  670. this.$store.dispatch("SET_SESSION_NAME", '');
  671. }
  672. } else {
  673. this.offlineList.splice(this.dataIndex, 1);
  674. this.$store.dispatch("SET_CURRENT", []);//当前会话数据
  675. this.$store.dispatch("SET_SESSION_NAME", '');
  676. }
  677. this.websocketsend(JSON.stringify(data))
  678. }).catch(() => {
  679. // this.$message({
  680. // type: 'info',
  681. // message: '已取消删除'
  682. // });
  683. });
  684. },
  685. /*****************数据发送方法调用******************/
  686. websocketsend(Data) {
  687. this.$websocket.send(Data);
  688. this.automaticRolling();
  689. },
  690. // 消息发送声音提示
  691. sendAudio() {
  692. let send = new Audio()
  693. send.src = "../../static/audio/send.wav"
  694. send.play()
  695. },
  696. /*********************发送消息*********************/
  697. sendMessage() {
  698. if (!this.inputValue) return;
  699. if (this.sessionType == 2) {
  700. this.$message({
  701. message: '只能给在线用户发送消息哦...',
  702. type: 'warning'
  703. });
  704. return false;
  705. }
  706. // if(this.offlineList.length <= 0 ) return
  707. let isFirst = true, sensitiveNumber = 0;
  708. this.isFrce = false;
  709. //判断客服是否发送的第一句话
  710. if (this.data.length > 0) {
  711. for (let i = 0; i < this.data.length; i++) {
  712. if (this.data[i].type == 'service') {
  713. isFirst = false;
  714. break;
  715. }
  716. }
  717. }
  718. //检测发送信息是否含有敏感词
  719. let sensitive_data = this.$public.shieldingKeyword(this.$public.turnFace(this.inputValue, this.$frce), this.sensitive);
  720. //组合发送数据
  721. let data = {
  722. from_name: this.user_info.user_name,//发送者
  723. from_avatar: this.user_info.avatar,//发送者头像
  724. from_id: "KF" + this.user_info.id,//发送者id
  725. content: JSON.stringify({
  726. text: this.inputValue,
  727. img: '',
  728. }),
  729. to_id: this.sessionList[this.dataIndex].id,
  730. to_name: this.sessionList[this.dataIndex].name,
  731. conversationId: this.sessionList[this.dataIndex].conversationId,
  732. isFirst: isFirst,//是否是第一句话
  733. sensitiveNumber: this.sensitiveNumber,//敏感词次数
  734. }
  735. let chatList = this.sessionList[this.dataIndex];
  736. //有敏感词提示并且禁止发送
  737. if (sensitive_data.num == 0) {
  738. // 本地储存
  739. chatList.data.push({
  740. type: 'service', content: {
  741. text: this.$public.turnFace(this.inputValue, this.$frce),
  742. img: '',
  743. }, time: this.time[1]
  744. });
  745. //this.data = [];
  746. //标记当前会话位置
  747. this.session_marked = chatList.data.length - 1;
  748. //this.data = chatList.data;
  749. this.$store.dispatch("SET_CURRENT", chatList.data);//当前会话数据
  750. this.websocketsend(JSON.stringify({type: 'chatMessage', data}))
  751. this.sendAudio();
  752. this.inputValue = '';
  753. this.sensitiveNumber = 0;
  754. } else {
  755. this.$message({
  756. message: '敏感词' + sensitive_data.text,
  757. type: 'warning'
  758. });
  759. //计算关键词次数
  760. this.sensitiveNumber = this.sensitiveNumber + sensitive_data.num;
  761. }
  762. },
  763. /******************开启或关闭表情包******************/
  764. frceClick() {
  765. this.isFrce = !this.isFrce;
  766. },
  767. /*******************发送评价指令*******************/
  768. evaluation() {
  769. if (!this.is_eva_btn && this.sessionType == 1) {
  770. this.$message({
  771. message: '评价指令已发送',
  772. type: 'success'
  773. });
  774. let type = 'getEvaluate';
  775. let data = {
  776. conversationId: this.sessionList[this.dataIndex].conversationId,
  777. to_id: this.sessionList[this.dataIndex].id,
  778. }
  779. this.websocketsend(JSON.stringify({type, data}));
  780. this.is_eva_btn = true;
  781. } else if (this.sessionType == 2) {
  782. this.$message.error('用户已离线指令无法发送');
  783. } else {
  784. this.$message.error('评价指令无法发送');
  785. }
  786. },
  787. /******************查询快捷数据信息*****************/
  788. quickReplyInfo() {
  789. let obj = {
  790. headers: {
  791. apiToken: this.$md5("userwords" + "customer-service" + "index" + this.time[0] + "service"),
  792. userToken: this.token
  793. }
  794. };
  795. this.get("api" + this.$ports.FastReply.userWords, obj).then(res => {
  796. if (res.data.code == 1) {
  797. this.FastReply = res.data.data;
  798. }
  799. });
  800. },
  801. /******************获取快捷语文字******************/
  802. getLanguage(e) {
  803. // console.log(e, "公有库");
  804. this.inputValue = this.inputValue + e;
  805. },
  806. /*****************表情包输入框赋值******************/
  807. frceCenterClick(text) {
  808. this.inputValue = this.inputValue + "#" + text + "/";
  809. //console.log(this.inputValue);
  810. },
  811. /******************* 快捷语库开关 ******************/
  812. FastReplySwitchClick(num) {
  813. this.FastReplySwitch = num;
  814. },
  815. /*****************获取当前聊天用户信息****************/
  816. get_user_info(type) {
  817. if (type.length > 0) {
  818. this.session_user_info = {};
  819. if (this.dataIndex <= type.length) {
  820. let obj = {
  821. headers: {
  822. "apiToken": this.$md5('accountInfo' + "customer-service" + 'service' + this.time[0] + 'service'),
  823. 'userToken': this.token
  824. },
  825. account_id: type[this.dataIndex].id
  826. };
  827. this.post('api' + this.$ports.userInfo.accountInfo, obj).then(res => {
  828. if (res.data.code == 1) {
  829. // console.log(res.data.data);
  830. this.session_user_info = res.data.data;
  831. console.log(this.options);
  832. for (let i = 0; this.options.length > i; i++) {
  833. if (this.options[i].id == res.data.data.label_id) {
  834. this.value = this.options[i].name;
  835. }
  836. break;
  837. }
  838. //this.value = res.data.data.label_id
  839. }
  840. })
  841. // this.session_user_info = type[this.dataIndex].id;
  842. // console.log(type[this.dataIndex].id);
  843. // this.value = this.session_user_info.label;
  844. }
  845. } else {
  846. this.session_user_info = {};
  847. }
  848. },
  849. /********************获取配置信息********************/
  850. get_config_info() {
  851. let obj = {
  852. headers: {
  853. apiToken: this.$md5("minround" + "customer-service" + "evaluate" + this.time[0] + "index"),
  854. userToken: this.token
  855. }
  856. };
  857. this.get("api/" + this.$ports.minRound, obj).then(res => {
  858. if (res.data.code == 1) {
  859. this.trigger_condition = res.data.data.systemconfig_data;
  860. }
  861. });
  862. },
  863. /***********发送消息或接收消息后自动滚动高度*************/
  864. automaticRolling() {
  865. this.$nextTick(() => {
  866. let msg = document.getElementById('chat_box') // 获取对象
  867. msg.scrollTop = msg.scrollHeight // 滚动高度
  868. })
  869. },
  870. /**************获取敏感词*****************/
  871. getSensitive() {
  872. let obj = {
  873. headers: {
  874. "apiToken": this.$md5('sensitivewords' + "customer-service" + 'index' + this.time[0] + 'index'),
  875. 'userToken': this.token
  876. }
  877. };
  878. this.post('api' + this.$ports.sensitiveWords, '', obj).then(res => {
  879. if (res.data.code == 1) {
  880. res.data.data.serverSensitive.forEach(res => {
  881. this.sensitive.push(res.sensitivewords_word)
  882. })
  883. res.data.data.userSensitive.forEach(res => {
  884. this.userSensitiveWords.push(res.sensitivewords_word)
  885. })
  886. }
  887. })
  888. },
  889. /********************获取vuex数据***********************/
  890. get_vuex_info() {
  891. let getters = this.$store.getters
  892. this.token = getters.get_user_info.token;//token
  893. this.user_info = getters.get_user_info;//用户消息
  894. console.log(this.user_info,'用户信息');
  895. this.sessionList = getters.get_session;//会话
  896. this.offlineList = getters.get_offline;//离线
  897. this.dataIndex = getters.get_num; //getters.get_num;列表下标
  898. this.sessionType = getters.get_type;//选择类型
  899. // this.data = getters.get_current;//当前会话数据
  900. // this.sessionName = getters.get_session_name;//会话对象名字
  901. this.time = JSON.parse(sessionStorage.getItem("time"));
  902. },
  903. /**********************编辑用户信息**********************/
  904. bt_update() {
  905. if (!this.isTrue) return false;
  906. this.isTrue = false;
  907. let obj = {
  908. headers: {
  909. "apiToken": this.$md5('update' + "customer-service" + 'service' + this.time[0] + 'service'),
  910. 'userToken': this.token
  911. },
  912. account_id: this.session_user_info.id,
  913. nick_name: this.session_user_info.nick_name,
  914. account_email: this.session_user_info.account_email,
  915. phone: this.session_user_info.account_phone,
  916. address: this.session_user_info.address,
  917. remark: this.session_user_info.remark,
  918. label_id: this.label_id
  919. };
  920. this.post('api' + this.$ports.userInfo.update, obj).then(res => {
  921. if (res.data.code == 1) {
  922. this.$message({
  923. showClose: true,
  924. message: `恭喜你,${res.data.msg}`,
  925. type: 'success'
  926. });
  927. }
  928. this.isTrue = true;
  929. })
  930. },
  931. /******************qq截图粘贴---获取剪切板数据*****************/
  932. getClipboardData( e ){
  933. // 添加到事件对象中的访问系统剪贴板的接口
  934. let clipboardData = e.clipboardData,
  935. i = 0,
  936. items, item, types;
  937. if( clipboardData ){
  938. items = clipboardData.items;
  939. if( !items ){
  940. return;
  941. }
  942. item = items[0];
  943. // 保存在剪贴板中的数据类型
  944. types = clipboardData.types || [];
  945. for( ; i < types.length; i++ ){
  946. if( types[i] === 'Files' ){
  947. item = items[i];
  948. break;
  949. }
  950. }
  951. // 判断是否为图片数据
  952. if( item && item.kind === 'file' && item.type.match(/^image\//i) ){
  953. this.imgReader( item );
  954. }
  955. }
  956. },
  957. /**********qq截图粘贴---获取剪切板图片信息进行转换blob***********/
  958. imgReader( item ){
  959. let _this = this;
  960. var blob = item.getAsFile(),reader = new FileReader();
  961. // 读取文件后将其显示在网页中
  962. reader.onload = function( e ){
  963. var img = new Image();
  964. img.src = e.target.result;
  965. _this.dialogPaste =true;
  966. _this.pasteUrl = e.target.result;
  967. // console.log(img.src)
  968. //document.body.appendChild( img );
  969. };
  970. // 读取文件
  971. reader.readAsDataURL( blob );
  972. },
  973. /*******************qq截图粘贴---监听时间捆绑*****************/
  974. paste(){
  975. document.getElementById( 'input' ).addEventListener( 'paste',this.getClipboardData);
  976. },
  977. /**********************qq截图粘贴---发送截图*****************/
  978. handleClose(done) {
  979. this.dialogPaste = false;
  980. let blob = this.$public.dataURItoBlob(this.pasteUrl)
  981. let self = this;
  982. var formData = new FormData();
  983. formData.append("file", blob);
  984. self.upImg(formData);
  985. },
  986. /****************双击获取ip*******************/
  987. get_ip_Info(){
  988. var clipboard = new Clipboard(".get_ip")
  989. clipboard.on('success', e => {
  990. // console.log('复制成功')
  991. this.$message({
  992. message: '复制成功',
  993. type: 'success'
  994. });
  995. // 释放内存
  996. clipboard.destroy()
  997. })
  998. clipboard.on('error', e => {
  999. // 不支持复制
  1000. this.$message({
  1001. message: '该浏览器不支持自动复制',
  1002. type: 'warning'
  1003. });
  1004. // 释放内存
  1005. clipboard.destroy()
  1006. })
  1007. }
  1008. },
  1009. /**
  1010. * 挂载前执行
  1011. */
  1012. mounted() {
  1013. //获取vuex里面数据
  1014. this.get_vuex_info();
  1015. let _this = this;
  1016. this.get_config_info();
  1017. //客服人员登陆会话
  1018. if (this.$store.getters.get_is_init) {
  1019. // this.$websocket.onmessage = this.;
  1020. _this.websocketsend(JSON.stringify({
  1021. type: 'init',
  1022. data: {
  1023. uid: 'KF' + _this.user_info.id,
  1024. group: _this.user_info.group_id,
  1025. token: this.token,
  1026. name: _this.user_info.user_name,
  1027. avatar: _this.user_info.user_avatar,
  1028. }
  1029. }));
  1030. this.$store.dispatch("SET_IS_INIT", false);
  1031. }
  1032. //获取客服快捷语
  1033. this.quickReplyInfo();
  1034. //获取敏感词
  1035. this.getSensitive();
  1036. //获取
  1037. this.frceArr = this.$frce;
  1038. //调用截图粘贴捆绑方法
  1039. this.paste();
  1040. },
  1041. beforeDestroy(){
  1042. document.getElementById( 'input' ).removeEventListener('paste',this.getClipboardData)
  1043. },
  1044. /**
  1045. * 事件监听
  1046. */
  1047. watch: {
  1048. get_session_message(e) {
  1049. this.chatMessage(e)
  1050. },
  1051. /*****************评价按钮显示隐藏处理*****************/
  1052. get_is_eva_btn(data) {
  1053. if (this.sessionType != 2) {
  1054. if (this.data.length > this.trigger_condition) {
  1055. if (!this.data[this.dataIndex].isEva) {
  1056. // this.is_eva_btn = true;
  1057. // }else{
  1058. this.data[this.dataIndex].isEva = true;
  1059. this.is_eva_btn = false;
  1060. }
  1061. } else {
  1062. this.is_eva_btn = true;
  1063. }
  1064. } else {
  1065. this.is_eva_btn = true;
  1066. }
  1067. }
  1068. },
  1069. /**
  1070. * 计算属性
  1071. */
  1072. computed: {
  1073. ...mapGetters({
  1074. data: 'get_current',
  1075. sessionName: 'get_session_name',
  1076. }),
  1077. get_session_message() {
  1078. return this.$store.getters.get_session_message;
  1079. },
  1080. /****************判断是否可以点击评价*****************/
  1081. get_is_eva_btn() {
  1082. return this.data;
  1083. }
  1084. },
  1085. components: {
  1086. leftNav,
  1087. hader,
  1088. messageCenter
  1089. }
  1090. };
  1091. </script>
  1092. <style>
  1093. .el-icon-arrow-right:before {
  1094. content: "\E791";
  1095. color: #ccc;
  1096. }
  1097. .session_choose {
  1098. background: #F6F8FF;
  1099. }
  1100. .session_style:hover {
  1101. /* background:#F6F8FF; */
  1102. background: #ECF4FF;
  1103. cursor: pointer;
  1104. }
  1105. .sessionList {
  1106. width: 21vw;
  1107. background: #fff;
  1108. }
  1109. .markNumber {
  1110. display: inline-block;
  1111. width: 20px;
  1112. height: 20px;
  1113. border-radius: 50%;
  1114. background: #f60;
  1115. font-size: 12px;
  1116. color: #fff;
  1117. text-align: center;
  1118. line-height: 20px;
  1119. }
  1120. .el-collapse-item__header {
  1121. position: relative;
  1122. font-size: 14px;
  1123. font-weight: bold;
  1124. color: #666;
  1125. font-family: PingFang SC;
  1126. padding-left: 32px;
  1127. height: 49px;
  1128. justify-content: space-between;
  1129. padding-right: 10px;
  1130. }
  1131. .el-collapse-item__header.is-active {
  1132. border-bottom: 1px solid #d5e5ff;
  1133. }
  1134. .el-collapse-item__header {
  1135. border-bottom: 1px solid #d5e5ff;
  1136. }
  1137. .el-icon-arrow-right {
  1138. position: absolute;
  1139. left: 10px;
  1140. top: auto;
  1141. }
  1142. .sessionList_span {
  1143. color: #999;
  1144. }
  1145. .user {
  1146. padding: 10px;
  1147. }
  1148. .evaluation {
  1149. font-size: 24px;
  1150. color: #969696;
  1151. }
  1152. .el-collapse-item__content {
  1153. padding-bottom: 0;
  1154. }
  1155. .chat {
  1156. width: 29vw;
  1157. height: 93vh;
  1158. border: 1px solid #d5e5ff;
  1159. border-top: none;
  1160. background: #fff;
  1161. }
  1162. .user_text {
  1163. border: 1px solid #ECF4FF;
  1164. color: #999;
  1165. line-height: 30px;
  1166. width: 80%;
  1167. }
  1168. .user_text:focus {
  1169. outline: none;
  1170. }
  1171. .chatTop {
  1172. height: 50px;
  1173. border-bottom: 1px solid #d5e5ff;
  1174. }
  1175. .chat-box {
  1176. height: 62vh;
  1177. /* background: red; */
  1178. /* padding: 40px 0; */
  1179. border-bottom: 1px solid #d5e5ff;
  1180. overflow-x: hidden;
  1181. overflow-y: auto;
  1182. }
  1183. .chatMsg {
  1184. display: inline-block;
  1185. padding: 10px;
  1186. font-size: 14px;
  1187. max-width: 85%;
  1188. word-wrap: break-word;
  1189. }
  1190. p {
  1191. margin-block-start: 0em;
  1192. margin-block-end: 0em;
  1193. margin-inline-start: 0px;
  1194. margin-inline-end: 0px;
  1195. }
  1196. .frce {
  1197. height: 0.38rem;
  1198. width: 100%;
  1199. padding: 16px 10px;
  1200. }
  1201. .input {
  1202. height: 100px;
  1203. background: #fff;
  1204. resize: none;
  1205. font-size: 14px;
  1206. margin: 0 10px;
  1207. border: none;
  1208. /* border: 1px solid #d5e5ff; */
  1209. }
  1210. .input:focus {
  1211. outline: none;
  1212. border: none;
  1213. }
  1214. .user_info_box {
  1215. margin: 10px 0;
  1216. }
  1217. .title_span {
  1218. color: #666;
  1219. font-size: 14px;
  1220. font-weight: bold;
  1221. }
  1222. .content_span {
  1223. color: #999;
  1224. font-size: 14px;
  1225. }
  1226. .key {
  1227. /* width: 27.8vw; */
  1228. /* position: absolute; */
  1229. bottom: 22px;
  1230. height: 23vh;
  1231. /* background: #409EFF; */
  1232. }
  1233. .msgInputBtn {
  1234. right: 10px;
  1235. bottom: 10px;
  1236. width: 60px;
  1237. height: 30px;
  1238. /* background:#ECF4FF; */
  1239. background: #5399f5;
  1240. margin-right: 10px;
  1241. border-radius: 5px;
  1242. font-size: 14px;
  1243. color: #fff;
  1244. cursor: pointer;
  1245. }
  1246. /* .msgInputBtn:hover{
  1247. background:#5399f5;
  1248. } */
  1249. .frceBox {
  1250. position: absolute;
  1251. /* top: 25vw; */
  1252. bottom: 25vh;
  1253. left: 31.5vw;
  1254. width: 28vw;
  1255. height: 25vh;
  1256. background: #fff;
  1257. border: 1px solid #d5e5ff;
  1258. overflow-x: hidden;
  1259. overflow-y: auto;
  1260. z-index: 999999;
  1261. }
  1262. .frceBox p {
  1263. width: 50px;
  1264. height: 50px;
  1265. }
  1266. .FastReply {
  1267. width: 19vw;
  1268. height: 92vh;
  1269. border-right: 1px solid #d5e5ff;
  1270. background: #fff;
  1271. }
  1272. .FastReplyBox {
  1273. padding: 0.4rem 0;
  1274. height: 80vh;
  1275. overflow-x: hidden;
  1276. overflow-y: auto;
  1277. }
  1278. .FastReplyHader {
  1279. height: 50px;
  1280. border-bottom: 1px solid #d5e5ff;
  1281. padding: 0 10px;
  1282. font-size: 14px;
  1283. color: #666;
  1284. font-weight: bold;
  1285. }
  1286. .FastReplySwitch {
  1287. height: 50px;
  1288. border-bottom: 1px solid #d5e5ff;
  1289. color: #666;
  1290. font-size: 14px;
  1291. padding: 0 40px;
  1292. }
  1293. .FastReplySwitch p.active {
  1294. position: relative;
  1295. color: #5399f5;
  1296. }
  1297. .FastReplySwitch p.active::after {
  1298. content: "";
  1299. display: inline-block;
  1300. position: absolute;
  1301. width: 42px;
  1302. height: 2px;
  1303. left: 1px;
  1304. bottom: -15px;
  1305. background: #5399f5;
  1306. }
  1307. ul {
  1308. margin-block-start: 0.4em;
  1309. margin-block-end: 0.4em;
  1310. }
  1311. .cursor_text {
  1312. cursor: pointer;
  1313. margin: 10px 20px;
  1314. }
  1315. .cursor_text:hover {
  1316. color: #d5e5ff;
  1317. background: #f6f8ff;
  1318. }
  1319. .serch {
  1320. padding: 20px 10px;
  1321. }
  1322. .disable {
  1323. color: #f0f0f0 !important;
  1324. }
  1325. .serchBox {
  1326. width: 200px;
  1327. height: 40px;
  1328. border: 1px solid #eee;
  1329. position: relative;
  1330. }
  1331. .serchBox img {
  1332. position: absolute;
  1333. right: 10px;
  1334. top: 12px;
  1335. }
  1336. .serchBox input {
  1337. width: 100%;
  1338. height: 100%;
  1339. background: #f5f5f5;
  1340. border: none;
  1341. color: #999;
  1342. padding: 0 10px;
  1343. }
  1344. .serchBox input:focus {
  1345. outline: none;
  1346. color: #666;
  1347. }
  1348. .FastReplyList {
  1349. width: 100%;
  1350. }
  1351. .userinfo {
  1352. width: 21vw;
  1353. padding: 20px;
  1354. background: #ECF4FF;
  1355. }
  1356. .userData p {
  1357. line-height: 30px;
  1358. display: block;
  1359. width: 100%;
  1360. }
  1361. .message_box {
  1362. background: #F5F5F5;
  1363. border-radius: 10px 0 10px 10px;
  1364. }
  1365. .user_box {
  1366. background: #DFF0FF;
  1367. border-radius: 0 10px 10px 10px;
  1368. }
  1369. .userDataInput {
  1370. width: 80%;
  1371. padding: 10px;
  1372. height: 80px;
  1373. resize: none;
  1374. border: 1px solid #d5e5ff;
  1375. background: #fff;
  1376. }
  1377. .userDataInput:focus {
  1378. outline: none;
  1379. }
  1380. .userBtn {
  1381. width: 60px;
  1382. height: 34px;
  1383. border: 1px solid rgba(221, 221, 221, 1);
  1384. background: linear-gradient(
  1385. 180deg,
  1386. rgba(245, 245, 245, 1) 0%,
  1387. rgba(238, 238, 238, 1) 100%
  1388. );
  1389. border-radius: 5px;
  1390. color: #888;
  1391. font-size: 14px;
  1392. margin-top: 10px;
  1393. }
  1394. .userBtn:hover {
  1395. background: #5399f5;
  1396. color: #f0f0f0;
  1397. cursor: pointer;
  1398. }
  1399. .chatmin {
  1400. height: 46.5vh;
  1401. }
  1402. .el-button--primary {
  1403. background: #409EFF !important;
  1404. /* color:#fff !important; */
  1405. border: 1px solid #DCDFE6;
  1406. line-height: 1;
  1407. padding: 4px 8px;
  1408. }
  1409. .cell button {
  1410. line-height: 1;
  1411. -webkit-transition: .1s;
  1412. border-radius: 4px;
  1413. border: 1px solid #DCDFE6;
  1414. padding: 4px 8px;
  1415. }
  1416. .el-button {
  1417. line-height: 0;
  1418. /* width: 0; */
  1419. border: none;
  1420. padding: 0;
  1421. color: #999;
  1422. font-size: 12px;
  1423. font-weight: 400;
  1424. }
  1425. </style>