chat-input.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. const MIN_VOICE_TIME = 1,
  2. MAX_VOICE_TIME = 60,
  3. START_TIME_DOWN = 54,
  4. status = {
  5. START: 1,
  6. SUCCESS: 2,
  7. CANCEL: 3,
  8. SHORT: 4,
  9. FAIL: 5,
  10. UNAUTH: 6
  11. },
  12. EVENT = {
  13. EXTRA_CLICK: 'extraClickEvent',
  14. EXTRA_ITEM_CLICK: 'extraItemClickEvent',
  15. VOICE_RECORD: 'voiceRecordEvent',
  16. SEND_MESSAGE: 'sendMessageEvent'
  17. };
  18. Component({
  19. properties: {
  20. minVoiceTime: {
  21. type: Number,
  22. value: MIN_VOICE_TIME
  23. },
  24. maxVoiceTime: {
  25. type: Number,
  26. value: MAX_VOICE_TIME
  27. },
  28. startTimeDown: {
  29. type: Number,
  30. value: START_TIME_DOWN,
  31. },
  32. tabBarHeight: {
  33. type: Number,
  34. value: 0
  35. },
  36. format: {
  37. type: String,
  38. value: 'mp3'
  39. },
  40. extraArray: {
  41. type: Array,
  42. value: []
  43. }
  44. },
  45. data: {
  46. windowHeight: 0,
  47. windowWidth: 0,
  48. cancelLineYPosition: 0,
  49. _startTimeDown: START_TIME_DOWN,
  50. timer: -1,
  51. singleVoiceTimeCount: 0,
  52. textMessage: '',
  53. voiceObj: {
  54. moveToCancel: false
  55. },
  56. extraObj: {
  57. chatInputShowExtra: false,
  58. chatInputExtraArr: []
  59. },
  60. inputStatus: 'text',
  61. inputValueEventTemp: ''
  62. },
  63. observers: {
  64. 'extraArray'(value) {
  65. this.setData({
  66. 'extraObj.chatInputExtraArr': value || []
  67. })
  68. },
  69. 'startTimeDown'(startTimeDown) {
  70. const data = this.data;
  71. data._startTimeDown = startTimeDown && startTimeDown < data.maxVoiceTime && startTimeDown > 0 ? startTimeDown : START_TIME_DOWN;
  72. }
  73. },
  74. methods: {
  75. getRecordStatus() {
  76. return {
  77. ...status
  78. };
  79. },
  80. closeExtraView() {
  81. this.setData({
  82. 'extraObj.chatInputShowExtra': false
  83. });
  84. },
  85. _chatInput$extra$click$event() {
  86. const isShow = !this.data.extraObj.chatInputShowExtra;
  87. this.setData({
  88. 'extraObj.chatInputShowExtra': isShow
  89. }, () => {
  90. this.triggerEvent(EVENT.EXTRA_CLICK, {
  91. isShow
  92. }, {});
  93. });
  94. },
  95. _change$input$way$event() {
  96. this.setData({
  97. 'inputStatus': this.data.inputStatus === 'text' ? 'voice' : 'text',
  98. 'extraObj.chatInputShowExtra': false
  99. });
  100. },
  101. _triggerVoiceRecordEvent({
  102. status,
  103. dataset
  104. }) {
  105. this.triggerEvent(EVENT.VOICE_RECORD, {
  106. recordStatus: status,
  107. ...dataset
  108. }, {});
  109. },
  110. _long$click$voice$btn(e) {
  111. if ('send$voice$btn' === e.currentTarget.id) { //长按时需要打开录音功能,开始录音
  112. this._checkRecordAuth(() => {
  113. const {
  114. maxVoiceTime,
  115. singleVoiceTimeCount
  116. } = this.data;
  117. this.setData({ //调出取消弹窗
  118. 'voiceObj.showCancelSendVoicePart': true,
  119. 'voiceObj.timeDownNum': maxVoiceTime - singleVoiceTimeCount,
  120. 'voiceObj.status': 'start',
  121. 'voiceObj.startStatus': 1,
  122. 'voiceObj.moveToCancel': false
  123. }, () => {
  124. this._triggerVoiceRecordEvent({
  125. status: status.START
  126. });
  127. });
  128. this.recorderManager.start({
  129. duration: 60000,
  130. format: this.data.format
  131. });
  132. }, (res) => {
  133. //录音失败
  134. console.error('录音拒绝授权');
  135. clearInterval(timer);
  136. this._endRecord();
  137. this.setData({
  138. 'voiceObj.status': 'end',
  139. 'voiceObj.showCancelSendVoicePart': false
  140. });
  141. this._triggerVoiceRecordEvent({
  142. status: status.UNAUTH
  143. });
  144. wx.showModal({
  145. title: '您未授权语音功能',
  146. content: '暂时不能使用语音',
  147. confirmText: '去设置',
  148. success: res => {
  149. if (res.confirm) {
  150. wx.openSetting({
  151. success: res => {
  152. if (res.authSetting['scope.record']) {
  153. this.setData({
  154. 'extraObj.chatInputShowExtra': false
  155. });
  156. }
  157. }
  158. });
  159. } else {
  160. this.setData({
  161. 'inputStatus': 'text',
  162. 'extraObj.chatInputShowExtra': false
  163. });
  164. }
  165. }
  166. });
  167. });
  168. }
  169. },
  170. _dealVoiceLongClickEventWithHighVersion() {
  171. this.recorderManager.onStart(() => {
  172. this.data.singleVoiceTimeCount = 0;
  173. const {
  174. _startTimeDown,
  175. maxVoiceTime
  176. } = this.data;
  177. //设置定时器计时60秒
  178. this.data.timer = setInterval(() => {
  179. const voiceTimeCount = ++this.data.singleVoiceTimeCount;
  180. if (voiceTimeCount >= _startTimeDown && voiceTimeCount < maxVoiceTime) {
  181. this.setData({
  182. 'voiceObj.timeDownNum': maxVoiceTime - voiceTimeCount,
  183. 'voiceObj.status': 'timeDown'
  184. })
  185. } else if (voiceTimeCount >= maxVoiceTime) {
  186. this.setData({
  187. 'voiceObj.status': 'timeout'
  188. });
  189. this._delayDismissCancelView();
  190. clearInterval(this.data.timer);
  191. this._endRecord();
  192. }
  193. }, 1000);
  194. })
  195. },
  196. _send$voice$move$event(e) {
  197. if ('send$voice$btn' === e.currentTarget.id) {
  198. console.log(this.data)
  199. const {
  200. windowHeight,
  201. voiceObj,
  202. tabBarHeight,
  203. cancelLineYPosition
  204. } = this.data,
  205. y = windowHeight + tabBarHeight - e.touches[0].clientY;
  206. if (y > cancelLineYPosition) {
  207. if (!voiceObj.moveToCancel) {
  208. this.setData({
  209. 'voiceObj.moveToCancel': true
  210. });
  211. }
  212. } else {
  213. if (voiceObj.moveToCancel) { //如果移出了该区域
  214. this.setData({
  215. 'voiceObj.moveToCancel': false
  216. })
  217. }
  218. }
  219. }
  220. },
  221. _send$voice$move$end$event(e) {
  222. if ('send$voice$btn' === e.currentTarget.id) {
  223. const {
  224. singleVoiceTimeCount,
  225. minVoiceTime,
  226. timer
  227. } = this.data;
  228. if (singleVoiceTimeCount < minVoiceTime) { //语音时间太短
  229. this.setData({
  230. 'voiceObj.status': 'short'
  231. });
  232. this._delayDismissCancelView();
  233. } else { //语音时间正常
  234. this.setData({
  235. 'voiceObj.showCancelSendVoicePart': false,
  236. 'voiceObj.status': 'end'
  237. });
  238. }
  239. clearInterval(timer);
  240. this._endRecord();
  241. }
  242. },
  243. _initVoiceData() {
  244. const {
  245. windowWidth,
  246. windowHeight
  247. } = this.data, width = windowWidth / 2.6;
  248. this.setData({
  249. 'inputStatus': 'text',
  250. 'windowHeight': windowHeight,
  251. 'windowWidth': windowWidth,
  252. 'voiceObj.status': 'end',
  253. 'voiceObj.startStatus': 0,
  254. 'voiceObj.voicePartWidth': width,
  255. 'voiceObj.moveToCancel': false,
  256. 'voiceObj.voicePartPositionToBottom': (windowHeight - width / 2.4) / 2,
  257. 'voiceObj.voicePartPositionToLeft': (windowWidth - width) / 2
  258. });
  259. },
  260. _delayDismissCancelView() {
  261. setTimeout(() => {
  262. if (this.data.voiceObj.status !== 'start') {
  263. this.setData({
  264. 'voiceObj.showCancelSendVoicePart': false,
  265. 'voiceObj.status': 'end'
  266. });
  267. }
  268. }, 1000);
  269. },
  270. _endRecord() {
  271. this.setData({
  272. 'voiceObj.startStatus': 0
  273. }, () => {
  274. this.recorderManager.stop();
  275. });
  276. },
  277. _chatInput$bind$focus$event() {
  278. this.setData({
  279. 'inputType': 'text'
  280. })
  281. },
  282. _chatInput$send$text$message(e) {
  283. this.setData({
  284. textMessage: ''
  285. }, () => {
  286. this.triggerEvent(EVENT.SEND_MESSAGE, {
  287. value: e.detail.value
  288. });
  289. this.data.inputValueEventTemp = '';
  290. });
  291. },
  292. _chatInput$bind$blur$event() {
  293. setTimeout(() => {
  294. let obj = {};
  295. if (!this.data.inputValueEventTemp) {
  296. this.data.inputValueEventTemp = '';
  297. obj['inputType'] = 'none';
  298. }
  299. obj['extraObj.chatInputShowExtra'] = false;
  300. this.setData(obj);
  301. });
  302. },
  303. _chatInput$send$text$message02() {
  304. this.setData({
  305. textMessage: '',
  306. 'inputType': 'none'
  307. }, () => {
  308. if (!!this.data.inputValueEventTemp) {
  309. this.triggerEvent(EVENT.SEND_MESSAGE, {
  310. value: this.data.inputValueEventTemp
  311. });
  312. this.setData({
  313. inputValueEventTemp: '',
  314. textMessage: '',
  315. })
  316. // this.data.inputValueEventTemp = '';
  317. }
  318. });
  319. },
  320. _chatInput$getValue$event(e) {
  321. const {
  322. detail: {
  323. value: textMessage
  324. }
  325. } = e;
  326. this.data.inputValueEventTemp = textMessage;
  327. this.setData({
  328. textMessage
  329. })
  330. },
  331. _chatInput$extra$item$click$event(e) {
  332. const {
  333. currentTarget: {
  334. dataset
  335. }
  336. } = e;
  337. this.triggerEvent(EVENT.EXTRA_ITEM_CLICK, {
  338. ...dataset
  339. }, {});
  340. },
  341. _setVoiceListener() {
  342. this.recorderManager.onStop((res) => {
  343. console.log(res, this.data.voiceObj.status);
  344. if (this.data.voiceObj.status === 'short') { //录音时间太短或者移动到了取消录音区域, 则取消录音
  345. this._triggerVoiceRecordEvent({
  346. status: status.SHORT
  347. });
  348. return;
  349. } else if (this.data.voiceObj.moveToCancel) {
  350. this._triggerVoiceRecordEvent({
  351. status: status.CANCEL
  352. });
  353. return;
  354. }
  355. console.log('录音成功');
  356. this._triggerVoiceRecordEvent({
  357. status: status.SUCCESS,
  358. dataset: res
  359. });
  360. });
  361. this.recorderManager.onError((res) => {
  362. this._triggerVoiceRecordEvent({
  363. status: status.FAIL,
  364. dataset: res
  365. });
  366. });
  367. },
  368. _checkRecordAuth(cbOk, cbError) {
  369. wx.getSetting({
  370. success: (res) => {
  371. if (!res.authSetting['scope.record']) {
  372. wx.authorize({
  373. scope: 'scope.record',
  374. success: (res) => {
  375. // 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问
  376. console.log('同意', res);
  377. },
  378. fail: res => {
  379. console.log('拒绝', res);
  380. cbError && cbError();
  381. }
  382. })
  383. } else {
  384. cbOk && cbOk();
  385. }
  386. }
  387. })
  388. }
  389. },
  390. lifetimes: {
  391. created() {
  392. this.recorderManager = wx.getRecorderManager();
  393. const {
  394. windowHeight,
  395. windowWidth
  396. } = wx.getSystemInfoSync();
  397. if (!windowHeight || !windowWidth) {
  398. console.error('没有获取到手机的屏幕尺寸:windowWidth', windowWidth, 'windowHeight', windowHeight);
  399. return;
  400. }
  401. this.data.windowHeight = windowHeight;
  402. this.data.windowWidth = windowWidth;
  403. this.data.cancelLineYPosition = windowHeight * 0.12;
  404. this._dealVoiceLongClickEventWithHighVersion();
  405. this._setVoiceListener();
  406. },
  407. attached() {
  408. this._initVoiceData();
  409. },
  410. detached() {
  411. clearInterval(this.data.timer);
  412. }
  413. }
  414. });
  415. // module.exports = {
  416. // clickExtraListener: clickExtraItemListener,
  417. // closeExtraView: closeExtraView,
  418. // recordVoiceListener: sendVoiceListener,
  419. // setVoiceRecordStatusListener: setVoiceRecordStatusListener,
  420. // setTextMessageListener: setTextMessageListener,
  421. // setExtraButtonClickListener: setExtraButtonClickListener,
  422. // VRStatus: status
  423. // };