chat-input.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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. const {
  199. windowHeight,
  200. voiceObj,
  201. tabBarHeight,
  202. cancelLineYPosition
  203. } = this.data,
  204. y = windowHeight + tabBarHeight - e.touches[0].clientY;
  205. if (y > cancelLineYPosition) {
  206. if (!voiceObj.moveToCancel) {
  207. this.setData({
  208. 'voiceObj.moveToCancel': true
  209. });
  210. }
  211. } else {
  212. if (voiceObj.moveToCancel) { //如果移出了该区域
  213. this.setData({
  214. 'voiceObj.moveToCancel': false
  215. })
  216. }
  217. }
  218. }
  219. },
  220. _send$voice$move$end$event(e) {
  221. if ('send$voice$btn' === e.currentTarget.id) {
  222. const {
  223. singleVoiceTimeCount,
  224. minVoiceTime,
  225. timer
  226. } = this.data;
  227. if (singleVoiceTimeCount < minVoiceTime) { //语音时间太短
  228. this.setData({
  229. 'voiceObj.status': 'short'
  230. });
  231. this._delayDismissCancelView();
  232. } else { //语音时间正常
  233. this.setData({
  234. 'voiceObj.showCancelSendVoicePart': false,
  235. 'voiceObj.status': 'end'
  236. });
  237. }
  238. clearInterval(timer);
  239. this._endRecord();
  240. }
  241. },
  242. _initVoiceData() {
  243. const {
  244. windowWidth,
  245. windowHeight
  246. } = this.data, width = windowWidth / 2.6;
  247. this.setData({
  248. 'inputStatus': 'text',
  249. 'windowHeight': windowHeight,
  250. 'windowWidth': windowWidth,
  251. 'voiceObj.status': 'end',
  252. 'voiceObj.startStatus': 0,
  253. 'voiceObj.voicePartWidth': width,
  254. 'voiceObj.moveToCancel': false,
  255. 'voiceObj.voicePartPositionToBottom': (windowHeight - width / 2.4) / 2,
  256. 'voiceObj.voicePartPositionToLeft': (windowWidth - width) / 2
  257. });
  258. },
  259. _delayDismissCancelView() {
  260. setTimeout(() => {
  261. if (this.data.voiceObj.status !== 'start') {
  262. this.setData({
  263. 'voiceObj.showCancelSendVoicePart': false,
  264. 'voiceObj.status': 'end'
  265. });
  266. }
  267. }, 1000);
  268. },
  269. _endRecord() {
  270. this.setData({
  271. 'voiceObj.startStatus': 0
  272. }, () => {
  273. this.recorderManager.stop();
  274. });
  275. },
  276. _chatInput$bind$focus$event() {
  277. this.setData({
  278. 'inputType': 'text'
  279. })
  280. },
  281. _chatInput$send$text$message(e) {
  282. this.setData({
  283. textMessage: ''
  284. }, () => {
  285. this.triggerEvent(EVENT.SEND_MESSAGE, {
  286. value: e.detail.value
  287. });
  288. this.data.inputValueEventTemp = '';
  289. });
  290. },
  291. _chatInput$bind$blur$event() {
  292. setTimeout(() => {
  293. let obj = {};
  294. if (!this.data.inputValueEventTemp) {
  295. this.data.inputValueEventTemp = '';
  296. obj['inputType'] = 'none';
  297. }
  298. obj['extraObj.chatInputShowExtra'] = false;
  299. this.setData(obj);
  300. });
  301. },
  302. _chatInput$send$text$message02() {
  303. this.setData({
  304. textMessage: '',
  305. 'inputType': 'none'
  306. }, () => {
  307. if (!!this.data.inputValueEventTemp) {
  308. this.triggerEvent(EVENT.SEND_MESSAGE, {
  309. value: this.data.inputValueEventTemp
  310. });
  311. this.setData({
  312. inputValueEventTemp: '',
  313. textMessage: '',
  314. })
  315. // this.data.inputValueEventTemp = '';
  316. }
  317. });
  318. },
  319. _chatInput$getValue$event(e) {
  320. const {
  321. detail: {
  322. value: textMessage
  323. }
  324. } = e;
  325. this.data.inputValueEventTemp = textMessage;
  326. this.setData({
  327. textMessage
  328. })
  329. },
  330. _chatInput$extra$item$click$event(e) {
  331. const {
  332. currentTarget: {
  333. dataset
  334. }
  335. } = e;
  336. this.triggerEvent(EVENT.EXTRA_ITEM_CLICK, {
  337. ...dataset
  338. }, {});
  339. },
  340. _setVoiceListener() {
  341. this.recorderManager.onStop((res) => {
  342. console.log(res, this.data.voiceObj.status);
  343. if (this.data.voiceObj.status === 'short') { //录音时间太短或者移动到了取消录音区域, 则取消录音
  344. this._triggerVoiceRecordEvent({
  345. status: status.SHORT
  346. });
  347. return;
  348. } else if (this.data.voiceObj.moveToCancel) {
  349. this._triggerVoiceRecordEvent({
  350. status: status.CANCEL
  351. });
  352. return;
  353. }
  354. console.log('录音成功');
  355. this._triggerVoiceRecordEvent({
  356. status: status.SUCCESS,
  357. dataset: res
  358. });
  359. });
  360. this.recorderManager.onError((res) => {
  361. this._triggerVoiceRecordEvent({
  362. status: status.FAIL,
  363. dataset: res
  364. });
  365. });
  366. },
  367. _checkRecordAuth(cbOk, cbError) {
  368. wx.getSetting({
  369. success: (res) => {
  370. if (!res.authSetting['scope.record']) {
  371. wx.authorize({
  372. scope: 'scope.record',
  373. success: (res) => {
  374. // 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问
  375. console.log('同意', res);
  376. },
  377. fail: res => {
  378. console.log('拒绝', res);
  379. cbError && cbError();
  380. }
  381. })
  382. } else {
  383. cbOk && cbOk();
  384. }
  385. }
  386. })
  387. }
  388. },
  389. lifetimes: {
  390. created() {
  391. this.recorderManager = wx.getRecorderManager();
  392. const {
  393. windowHeight,
  394. windowWidth
  395. } = wx.getSystemInfoSync();
  396. if (!windowHeight || !windowWidth) {
  397. console.error('没有获取到手机的屏幕尺寸:windowWidth', windowWidth, 'windowHeight', windowHeight);
  398. return;
  399. }
  400. this.data.windowHeight = windowHeight;
  401. this.data.windowWidth = windowWidth;
  402. this.data.cancelLineYPosition = windowHeight * 0.12;
  403. this._dealVoiceLongClickEventWithHighVersion();
  404. this._setVoiceListener();
  405. },
  406. attached() {
  407. this._initVoiceData();
  408. },
  409. detached() {
  410. clearInterval(this.data.timer);
  411. }
  412. }
  413. });
  414. // module.exports = {
  415. // clickExtraListener: clickExtraItemListener,
  416. // closeExtraView: closeExtraView,
  417. // recordVoiceListener: sendVoiceListener,
  418. // setVoiceRecordStatusListener: setVoiceRecordStatusListener,
  419. // setTextMessageListener: setTextMessageListener,
  420. // setExtraButtonClickListener: setExtraButtonClickListener,
  421. // VRStatus: status
  422. // };