zhao-chat.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. <template>
  2. <view class="chat-bottom">
  3. <view :style="{height: height}"></view>
  4. <view class="safe-area-inset-bottom"></view>
  5. <view class="chat-down-box u-flex-col u-col-center">
  6. <view class="chat-down-btn u-flex u-row-between" v-if="isjianpan">
  7. <image @click="openchangyong" v-if="showtype == 2" class="img2" src="@/static/images/jianpan.png"
  8. mode=""></image>
  9. <text @click="openchangyong" v-else class="changyong">常</text>
  10. <input class="input" :cursor-spacing="15" v-model="text" type="text" confirm-type="send" @confirm="send"
  11. placeholder="请输入消息内容…">
  12. <image @click="openemoji" class="img1" src="@/static/images/biaoqing.png" mode=""></image>
  13. <text @click="send" v-if="text" class="send">发送</text>
  14. <image @click="openmore" v-else-if="showtype == 1" class="img2" src="@/static/images/jianpan.png"
  15. mode="">
  16. </image>
  17. <image @click="openmore" v-else-if="showtype != 1" class="img2" src="@/static/images/wenjian.png"
  18. mode=""></image>
  19. </view>
  20. <view class="chat-down-btn luyin u-flex u-row-between" v-else>
  21. <image @click="isjianpan = true" class="img2" src="@/static/images/jianpan.png" mode=""></image>
  22. <button class="record-btn" :class="longPress == '1' ? 'record-btn-1' : 'record-btn-2'"
  23. @longpress="longpressBtn()" @touchend="touchendBtn()">
  24. <!-- <image src="@/static/images/luyin.png" mode="" style="changyong" /> -->
  25. <text>{{longPress == '1' ? '按住说话' : '说话中...'}}</text>
  26. </button>
  27. </view>
  28. <view class="prompt-layer prompt-layer-1" v-if="longPress == '2'">
  29. <view class="prompt-loader">
  30. <view class="em" v-for="(item,index) in 15" :key="index"></view>
  31. </view>
  32. <text class="span">松手结束录音</text>
  33. </view>
  34. <view v-if="showtype == 1" class="more-box u-flex acenter">
  35. <!-- <view class="more-item u-flex-col u-col-center">
  36. <view class="item-img u-flex u-row-center">
  37. <image src="../static/images/more1.png" mode=""></image>
  38. </view>
  39. <view class="item-text">
  40. 发简历
  41. </view>
  42. </view> -->
  43. <view class="more-item u-flex-col u-col-center" @click="toalbum">
  44. <view class="item-img u-flex u-row-center">
  45. <image src="/static/images/more2.png" mode=""></image>
  46. </view>
  47. <view class="item-text">
  48. 相册
  49. </view>
  50. </view>
  51. <view class="more-item u-flex-col u-col-center" @click="tocapture">
  52. <view class="item-img u-flex u-row-center">
  53. <image src="/static/images/more3.png" mode=""></image>
  54. </view>
  55. <view class="item-text">
  56. 拍摄
  57. </view>
  58. </view>
  59. <view class="more-item u-flex-col u-col-center" @click="isjianpan = false;showtype = 0">
  60. <view class="item-img u-flex u-row-center">
  61. <image src="/static/images/more4.png" mode=""></image>
  62. </view>
  63. <view class="item-text">
  64. 语音
  65. </view>
  66. </view>
  67. </view>
  68. <view v-if="showtype == 2" class="changyong-box u-flex-col">
  69. <scroll-view scroll-y="true" class="changyong-scroll u-flex-1">
  70. <view class="changyong-text" v-for="(a,b) in changlist" :key="b" @click="select(a)">{{a.content}}
  71. </view>
  72. </scroll-view>
  73. <view class="changyong-btn u-flex u-row-between">
  74. <text class="text1" @click="toadd">新增</text>
  75. <text class="text2"></text>
  76. <text class="text1" @click="tolist">管理</text>
  77. </view>
  78. </view>
  79. <scroll-view v-if="showtype == 3" scroll-y="true" class="emoji-scroll">
  80. <view class="empji-box u-flex u-flex-wrap">
  81. <text v-for="(a,b) in emoji" :key="b" @click="changeemoji(a)">{{a}}</text>
  82. </view>
  83. </scroll-view>
  84. <view class="safe-area-inset-bottom"></view>
  85. </view>
  86. </view>
  87. </template>
  88. <script>
  89. import {
  90. phraseList
  91. } from '@/units/inquire.js'
  92. import {
  93. conn,
  94. } from '@/utils/WebIM';
  95. import WebIM from '@/newSDK/Easemob-chat.js'
  96. // import emoji from "@/common/emoji.js"
  97. const recorderManager = uni.getRecorderManager()
  98. var init // 录制时长计时器
  99. export default {
  100. props: {
  101. to: {
  102. typeof: String,
  103. default: ''
  104. },
  105. list: {
  106. typeof: Array,
  107. default: []
  108. }
  109. },
  110. data() {
  111. return {
  112. height: '100rpx',
  113. showtype: 0, //默认0,1显示更多,2显示常用,3显示表情
  114. // emoji: emoji,
  115. text: '',
  116. changlist: [],
  117. isjianpan: true,
  118. longPress: '1',
  119. time: 0, //录音时长
  120. duration: 60000, //录音最大值ms 60000/1分钟
  121. tempFilePath: '', //音频路径
  122. playStatus: 0, //录音播放状态 0:未播放 1:正在播放
  123. }
  124. },
  125. created() {
  126. this.phraseList()
  127. },
  128. methods: {
  129. phraseList() {
  130. phraseList().then(res => {
  131. console.log('changlist', res);
  132. this.changlist = res.data
  133. })
  134. },
  135. longpressBtn() {
  136. this.longPress = '2';
  137. // this.countdown(60); // 倒计时
  138. // clearInterval(init) // 清除定时器
  139. recorderManager.onStop((res) => {
  140. console.log('onstop', res);
  141. this.tempFilePath = res.tempFilePath;
  142. this.recordingTimer(this.time);
  143. })
  144. const options = {
  145. duration: this.duration, // 指定录音的时长,单位 ms
  146. sampleRate: 16000, // 采样率
  147. numberOfChannels: 1, // 录音通道数
  148. encodeBitRate: 96000, // 编码码率
  149. format: 'mp3', // 音频格式,有效值 aac/mp3
  150. frameSize: 10, // 指定帧大小,单位 KB
  151. }
  152. this.recordingTimer();
  153. recorderManager.start(options);
  154. // 监听音频开始事件
  155. recorderManager.onStart((res) => {})
  156. recorderManager.onError((res) => {})
  157. },
  158. // 长按松开录音事件
  159. touchendBtn() {
  160. let _this = this
  161. this.longPress = '1';
  162. recorderManager.onStop((res) => {
  163. this.tempFilePath = res.tempFilePath
  164. uni.uploadFile({
  165. url: 'https://hire.hdlkeji.com' + '/api/common/upload',
  166. filePath: _this.tempFilePath,
  167. name: 'file',
  168. success: (res) => {
  169. uni.hideLoading()
  170. if (JSON.parse(res.data).code == 1) {
  171. _this.sendluyin(JSON.parse(res.data).data.fullurl);
  172. } else {
  173. _this.$u.toast(JSON.parse(res.data).msg)
  174. }
  175. },
  176. fail: (e) => {
  177. console.log(e);
  178. }
  179. })
  180. })
  181. this.recordingTimer(this.time)
  182. recorderManager.stop()
  183. },
  184. recordingTimer(time) {
  185. var that = this;
  186. if (time == undefined) {
  187. // 将计时器赋值给init
  188. init = setInterval(function() {
  189. that.time++
  190. }, 1000);
  191. } else {
  192. clearInterval(init)
  193. }
  194. },
  195. sendluyin(url) {
  196. var option = {
  197. type: "audio",
  198. chatType: "singleChat",
  199. filename: url,
  200. // 消息接收方:单聊为对端用户 ID,群聊和聊天室分别为群组 ID 和聊天室 ID。
  201. to: this.to,
  202. body: {
  203. //文件 URL。
  204. url: url,
  205. //文件类型。
  206. type: "audio",
  207. //文件名。
  208. filename: url,
  209. // 音频文件时长,单位为秒。
  210. length: this.time,
  211. },
  212. };
  213. let msg = new WebIM.message.create(option);
  214. // 调用 `send` 方法发送该语音消息。
  215. conn.send(msg).then((res) => {
  216. // 语音消息成功发送。
  217. console.log("Success");
  218. setTimeout(() => {
  219. this.$emit('success', true)
  220. }, 800)
  221. })
  222. .catch((e) => {
  223. // 语音消息发送失败。
  224. console.log("Fail", e);
  225. });
  226. },
  227. send() {
  228. let _this = this
  229. if (this.text == '' || this.text == null) {
  230. this.$u.toast('发送消息不能为空')
  231. return
  232. }
  233. let id = conn.getUniqueId(); // 生成本地消息id
  234. let msg = new WebIM.message('txt', id); // 创建文本消息
  235. msg.set({
  236. msg: this.text, // 消息内容
  237. to: this.to, // 接收消息对象(用户id)
  238. chatType: 'singleChat', // 设置为单聊
  239. success: function(id, serverMsgId) {
  240. console.log('成功发送消息');
  241. setTimeout(() => {
  242. _this.$emit('success', true)
  243. }, 800);
  244. },
  245. fail: function(e) {
  246. console.log("发送消息失败");
  247. }
  248. });
  249. conn.send(msg.body);
  250. this.text = ''
  251. },
  252. toalbum() {
  253. let _this = this
  254. uni.chooseImage({
  255. count: 1,
  256. sizeType: ["original", "compressed"],
  257. sourceType: ["album"],
  258. success(img) {
  259. uni.showLoading({
  260. mask: true,
  261. title: "请稍后"
  262. })
  263. uni.uploadFile({
  264. url: 'https://hire.hdlkeji.com' + '/api/common/upload',
  265. filePath: img.tempFilePaths[0],
  266. name: 'file',
  267. success: (res) => {
  268. uni.hideLoading()
  269. if (JSON.parse(res.data).code == 1) {
  270. _this.sendPrivateImg(JSON.parse(res.data).data.fullurl);
  271. } else {
  272. _this.$u.toast(JSON.parse(res.data).msg)
  273. }
  274. },
  275. fail: (e) => {
  276. console.log(e);
  277. }
  278. })
  279. },
  280. });
  281. },
  282. tocapture() {
  283. let _this = this
  284. uni.chooseImage({
  285. count: 1,
  286. sizeType: ["original", "compressed"],
  287. sourceType: ["camera"],
  288. success(img) {
  289. uni.showLoading({
  290. mask: true,
  291. title: "请稍后"
  292. })
  293. uni.uploadFile({
  294. url: 'https://hire.hdlkeji.com' + '/api/common/upload',
  295. filePath: img.tempFilePaths[0],
  296. name: 'file',
  297. success: (res) => {
  298. uni.hideLoading()
  299. if (JSON.parse(res.data).code == 1) {
  300. _this.sendPrivateImg(JSON.parse(res.data).data.fullurl);
  301. } else {
  302. _this.$u.toast(JSON.parse(res.data).msg)
  303. }
  304. },
  305. fail: (e) => {
  306. console.log(e);
  307. }
  308. })
  309. },
  310. });
  311. },
  312. sendPrivateImg(res) {
  313. let option = {
  314. chatType: "singleChat",
  315. // 消息类型。
  316. type: "img",
  317. // 图片文件的 URL 地址。
  318. url: res,
  319. // 消息接收方:单聊为对方用户 ID,群聊和聊天室分别为群组 ID 和聊天室 ID。
  320. to: this.to,
  321. };
  322. // 创建一条图片消息。
  323. let msg = new WebIM.message.create(option);
  324. // 调用 `send` 方法发送该图片消息。
  325. conn.send(msg).then((res) => {
  326. console.log('发送图片成功');
  327. setTimeout(() => {
  328. this.$emit('success', true)
  329. }, 800)
  330. });
  331. },
  332. select(item) {
  333. this.text = item.content
  334. },
  335. tolist() {
  336. uni.navigateTo({
  337. url: "/pagesC/changyong-list"
  338. })
  339. },
  340. toadd() {
  341. uni.navigateTo({
  342. url: "/pagesC/changyong-add"
  343. })
  344. },
  345. changeemoji(item) {
  346. this.text = this.text + item
  347. },
  348. openemoji() {
  349. this.showtype = this.showtype == 3 ? 0 : 3
  350. this.height = this.showtype == 3 ? '400rpx' : '100rpx'
  351. },
  352. openmore() {
  353. this.showtype = this.showtype == 1 ? 0 : 1
  354. this.height = this.showtype == 1 ? '300rpx' : '100rpx'
  355. },
  356. openchangyong() {
  357. this.showtype = this.showtype == 2 ? 0 : 2
  358. this.height = this.showtype == 2 ? '600rpx' : '100rpx'
  359. this.isjianpan = true
  360. }
  361. }
  362. }
  363. </script>
  364. <style lang="scss">
  365. .send {
  366. font-size: 26rpx;
  367. color: #fff;
  368. padding: 16rpx 20rpx;
  369. background: #0C66C2;
  370. border-radius: 16rpx;
  371. }
  372. .chat-bottom {
  373. .chat-down-box {
  374. position: fixed;
  375. bottom: 0;
  376. left: 0;
  377. z-index: 99;
  378. width: 750rpx;
  379. background-color: #fff;
  380. .emoji-scroll {
  381. height: 300rpx;
  382. .empji-box {
  383. width: 750rpx;
  384. text {
  385. padding: 10rpx 0;
  386. text-align: center;
  387. width: 10%;
  388. font-size: 40rpx;
  389. }
  390. }
  391. }
  392. .changyong-box {
  393. height: 500rpx;
  394. width: 100%;
  395. .changyong-scroll {
  396. min-height: 1rpx;
  397. padding: 0 32rpx;
  398. width: 100%;
  399. box-sizing: border-box;
  400. border-top: 2rpx solid #F0F0F0;
  401. .changyong-text {
  402. border-bottom: 2rpx solid #F0F0F0;
  403. padding: 32rpx 0;
  404. font-size: 30rpx;
  405. font-family: PingFangSC-Regular, PingFang SC;
  406. font-weight: 400;
  407. color: #222222;
  408. }
  409. .changyong-text:last-child {
  410. border: none;
  411. }
  412. }
  413. .changyong-btn {
  414. height: 90rpx;
  415. width: 100%;
  416. box-shadow: 0rpx -1rpx 0rpx 0rpx rgba(0, 0, 0, 0.2);
  417. .text2 {
  418. height: 44rpx;
  419. border: 2rpx solid #E5E5E5;
  420. }
  421. .text1 {
  422. line-height: 90rpx;
  423. width: 45%;
  424. text-align: center;
  425. font-size: 32rpx;
  426. font-family: PingFangSC-Regular, PingFang SC;
  427. font-weight: 400;
  428. color: #0C66C2;
  429. }
  430. }
  431. }
  432. .more-box {
  433. height: 200rpx;
  434. padding: 0 60rpx;
  435. width: 100%;
  436. .more-item {
  437. margin: 0 58rpx 0 0;
  438. .item-text {
  439. font-size: 22rpx;
  440. font-family: PingFangSC-Regular, PingFang SC;
  441. font-weight: 400;
  442. color: #444444;
  443. }
  444. .item-img {
  445. width: 114rpx;
  446. height: 114rpx;
  447. background: #F3F3F3;
  448. border-radius: 20rpx;
  449. margin-bottom: 24rpx;
  450. .hide {
  451. position: absolute;
  452. top: 0;
  453. right: 0;
  454. min-width: 100%;
  455. min-height: 100%;
  456. filter: alpha(opacity=0);
  457. opacity: 0;
  458. cursor: inherit;
  459. display: none;
  460. }
  461. image {
  462. width: 52rpx;
  463. height: 52rpx;
  464. }
  465. }
  466. }
  467. }
  468. .chat-down-btn {
  469. margin: 12rpx auto;
  470. width: 686rpx;
  471. height: 84rpx;
  472. background: #F3F3F3;
  473. border-radius: 42rpx;
  474. padding: 0 28rpx;
  475. .input {
  476. flex: 1;
  477. margin-left: 28rpx;
  478. font-size: 28rpx;
  479. }
  480. .img1 {
  481. width: 44rpx;
  482. height: 44rpx;
  483. margin: 0 28rpx;
  484. }
  485. .img2 {
  486. width: 44rpx;
  487. height: 44rpx;
  488. }
  489. .changyong {
  490. width: 44rpx;
  491. height: 44rpx;
  492. line-height: 44rpx;
  493. border: 3rpx solid #131415;
  494. text-align: center;
  495. border-radius: 100rpx;
  496. font-size: 26rpx;
  497. font-family: PingFangSC-Medium, PingFang SC;
  498. font-weight: 500;
  499. color: #131415;
  500. }
  501. }
  502. }
  503. .record-box {
  504. width: 100%;
  505. position: relative;
  506. }
  507. .luyin button::after {
  508. border: none;
  509. }
  510. .luyin button {
  511. font-size: 14px;
  512. line-height: 38px;
  513. width: 100%;
  514. height: 38px;
  515. border-radius: 8px;
  516. text-align: center;
  517. background: #f3f3f3;
  518. }
  519. .luyin button image {
  520. width: 16px;
  521. height: 16px;
  522. margin-right: 4px;
  523. vertical-align: middle;
  524. }
  525. .record-btn-2 {
  526. background: rgba(255, 211, 0, 0.2);
  527. }
  528. /* 提示小弹窗 */
  529. .prompt-layer {
  530. border-radius: 8px;
  531. background: #fff;
  532. padding: 8px 16px;
  533. box-sizing: border-box;
  534. position: absolute;
  535. left: 50%;
  536. transform: translateX(-50%);
  537. }
  538. .prompt-layer::after {
  539. content: '';
  540. display: block;
  541. border: 6px solid rgba(0, 0, 0, 0);
  542. border-top-color: rgba(255, 211, 0, 1);
  543. position: absolute;
  544. bottom: -10px;
  545. left: 50%;
  546. transform: translateX(-50%);
  547. }
  548. .prompt-layer-1 {
  549. font-size: 12px;
  550. width: 128px;
  551. text-align: center;
  552. display: flex;
  553. flex-direction: column;
  554. align-items: center;
  555. justify-content: center;
  556. top: -80px;
  557. }
  558. .prompt-layer-1 .p {
  559. color: #000000;
  560. }
  561. .prompt-layer-1 .span {
  562. color: rgba(0, 0, 0, .6);
  563. }
  564. .prompt-loader .em {}
  565. /* 语音音阶------------- */
  566. .prompt-loader {
  567. width: 96px;
  568. height: 20px;
  569. display: flex;
  570. align-items: center;
  571. justify-content: space-between;
  572. margin-bottom: 6px;
  573. }
  574. .prompt-loader .em {
  575. display: block;
  576. background: #333333;
  577. width: 1px;
  578. height: 10%;
  579. margin-right: 2.5px;
  580. float: left;
  581. }
  582. .prompt-loader .em:last-child {
  583. margin-right: 0px;
  584. }
  585. .prompt-loader .em:nth-child(1) {
  586. animation: load 2.5s 1.4s infinite linear;
  587. }
  588. .prompt-loader .em:nth-child(2) {
  589. animation: load 2.5s 1.2s infinite linear;
  590. }
  591. .prompt-loader .em:nth-child(3) {
  592. animation: load 2.5s 1s infinite linear;
  593. }
  594. .prompt-loader .em:nth-child(4) {
  595. animation: load 2.5s 0.8s infinite linear;
  596. }
  597. .prompt-loader .em:nth-child(5) {
  598. animation: load 2.5s 0.6s infinite linear;
  599. }
  600. .prompt-loader .em:nth-child(6) {
  601. animation: load 2.5s 0.4s infinite linear;
  602. }
  603. .prompt-loader .em:nth-child(7) {
  604. animation: load 2.5s 0.2s infinite linear;
  605. }
  606. .prompt-loader .em:nth-child(8) {
  607. animation: load 2.5s 0s infinite linear;
  608. }
  609. .prompt-loader .em:nth-child(9) {
  610. animation: load 2.5s 0.2s infinite linear;
  611. }
  612. .prompt-loader .em:nth-child(10) {
  613. animation: load 2.5s 0.4s infinite linear;
  614. }
  615. .prompt-loader .em:nth-child(11) {
  616. animation: load 2.5s 0.6s infinite linear;
  617. }
  618. .prompt-loader .em:nth-child(12) {
  619. animation: load 2.5s 0.8s infinite linear;
  620. }
  621. .prompt-loader .em:nth-child(13) {
  622. animation: load 2.5s 1s infinite linear;
  623. }
  624. .prompt-loader .em:nth-child(14) {
  625. animation: load 2.5s 1.2s infinite linear;
  626. }
  627. .prompt-loader .em:nth-child(15) {
  628. animation: load 2.5s 1.4s infinite linear;
  629. }
  630. @keyframes load {
  631. 0% {
  632. height: 10%;
  633. }
  634. 50% {
  635. height: 100%;
  636. }
  637. 100% {
  638. height: 10%;
  639. }
  640. }
  641. /* 语音音阶-------------------- */
  642. .prompt-layer-2 {
  643. top: -40px;
  644. }
  645. .prompt-layer-2 .text {
  646. color: rgba(0, 0, 0, 1);
  647. font-size: 12px;
  648. }
  649. }
  650. </style>