TheCurrentSession.vue 45 KB

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