chat-input.js 13 KB

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