├── README.md ├── customeSessionWidget.js ├── customeSessionWidget.json ├── customeSessionWidget.wxml ├── customeSessionWidget.wxss └── picture ├── 1.png ├── 2.png ├── 3.png ├── 4.png └── GIF.gif /README.md: -------------------------------------------------------------------------------- 1 | # InputWidget 2 | 一个适用于微信小程序的输入组件, 可以发送文字,语音, 图片 3 | 4 | # 效果 5 | 6 | 7 | ### 切换到文字 8 | 9 | 10 | ### 切换到语音 11 | 12 | 13 | ### 录音 14 | 15 | 16 | ### 取消录音 17 | 18 | 19 | 20 | 21 | 22 | # 使用 23 | 文件置于components/customeSessionWidget文件夹下面 24 | 在所需要页面json文件引用改自定义组件, 路径根据具体文件结构决定 25 | ``` 26 | "usingComponents": { 27 | "customeSessionWidget": "../../components/customeSessionWidget/customeSessionWidget", 28 | } 29 | ``` 30 | 31 | 在页面的wxml中使用该自定义组件 32 | ``` 33 | 34 | ``` 35 | 36 | 需要绑定send事件, 该事件会在确认发送文字,确认发送图片, 以及确认发送语音时触发 37 | 38 | 根据发送的不同内容, 事件对象分别如下:(通过e.detail获取) 39 | ``` 40 | { 41 | content: 'xxxxx', 42 | type: 'text' 43 | } 44 | { 45 | tempFilePaths: [path1, path2], 46 | type: 'picture' 47 | } 48 | { 49 | tempFilePath: path, 50 | type: 'voice' 51 | } 52 | ``` 53 | -------------------------------------------------------------------------------- /customeSessionWidget.js: -------------------------------------------------------------------------------- 1 | // component/customeSessionWidget.js 2 | Component({ 3 | /** 4 | * 组件的属性列表 5 | */ 6 | properties: { 7 | 8 | }, 9 | 10 | /** 11 | * 组件的初始数据 12 | */ 13 | data: { 14 | // 输入组件的图标 15 | voiceIcon: "http://120.78.124.36/wxxcx/C_PLP/shengyin.png", 16 | keyboardIcon: "http://120.78.124.36/wxxcx/C_PLP/keyboard_icon.png", 17 | pictureIcon: "http://120.78.124.36/wxxcx/C_PLP/tupian.png", 18 | 19 | //录音中 取消发送的图标 20 | statusIcon: "", 21 | voicingIcon: "http://120.78.124.36/wxxcx/C_PLP/luyinzhong.png", 22 | cancelSendIcon: "http://120.78.124.36/wxxcx/C_PLP/quxiaofasong.png", 23 | 24 | tip: "", 25 | tip1: "手指上滑,取消发送", 26 | tip2: "松开手指, 取消发送", 27 | 28 | keyboardTip: "", 29 | keyboardTip1: "按住 说话", 30 | keyboardTip2: "松开 结束", 31 | 32 | selectedInputWay: 0, //选择的输入方式 只能是0和1 0:键盘 1:语音 33 | startY: 0, //按住说话 初始触摸位置 34 | spaceDistance: -100, //出现 取消发送的 Y轴距离间隔 35 | showRecordStatusView: false, //显示 录音状态的 view 36 | cancelSendStatus: false, //是否为 取消发送 状态 37 | 38 | hasRecordSetting: false, //是否拥有录音权限 39 | 40 | inputContent: "", //输入框值 41 | }, 42 | 43 | /** 44 | * 组件的方法列表 45 | */ 46 | methods: { 47 | checkSetting: function () { 48 | //如果 当前输入方式是文字输入,点击icon时(也就是准备切换语音输入), 提前查询权限 49 | var that = this; 50 | return new Promise((resolve, reject) => { 51 | if (that.data.selectedInputWay == 0) { 52 | wx.getSetting({ 53 | success(res) { 54 | if (!res.authSetting['scope.record']) { 55 | wx.authorize({ 56 | scope: 'scope.record', 57 | success() { 58 | // 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问 59 | console.log("获取录音权限成功"); 60 | that.data.hasRecordSetting = true; 61 | resolve(); 62 | }, 63 | fail: (err) => { 64 | console.log(err); 65 | reject(); 66 | } 67 | }) 68 | } else { 69 | that.data.hasRecordSetting = true; 70 | resolve(); 71 | } 72 | }, 73 | fail: (err) => { 74 | console.log(err); 75 | reject(); 76 | } 77 | }); 78 | } 79 | }); 80 | }, 81 | //输入框值改变事件 82 | changeInputContent: function (e) { 83 | this.setData({ 84 | inputContent: e.detail.value 85 | }); 86 | }, 87 | changeInputWay: function () { 88 | var that = this; 89 | if (!this.data.hasRecordSetting) { 90 | this.checkSetting().then(() => { 91 | that.setData({ 92 | selectedInputWay: this.data.selectedInputWay == 0 ? 1 : 0, 93 | keyboardTip: this.data.keyboardTip1 94 | }); 95 | }).catch(() => { 96 | wx.showModal({ 97 | title: '提示', 98 | content: '未授权录音功能', 99 | showCancel:false 100 | }) 101 | }); 102 | } else { 103 | that.setData({ 104 | selectedInputWay: this.data.selectedInputWay == 0 ? 1 : 0, 105 | keyboardTip: this.data.keyboardTip1 106 | }); 107 | } 108 | }, 109 | touchStart: function (e) { 110 | console.log(e) 111 | var startY = e.touches[0].clientY; //初始Y坐标 112 | this.setData({ 113 | showRecordStatusView: true, 114 | startY: startY, 115 | keyboardTip: this.data.keyboardTip2, 116 | tip: this.data.tip1, 117 | statusIcon: this.data.voicingIcon 118 | }); 119 | this.record(); 120 | }, 121 | touchMove: function (e) { 122 | var moveY = e.touches[0].clientY; //移动的Y坐标 123 | if (moveY - this.data.startY <= this.data.spaceDistance) { 124 | this.setData({ 125 | cancelSendStatus: true, 126 | tip: this.data.tip2, //显示提示语改变 127 | statusIcon: this.data.cancelSendIcon 128 | }); 129 | } else { 130 | this.setData({ 131 | cancelSendStatus: false, 132 | tip: this.data.tip1, //显示提示语改变 133 | statusIcon: this.data.voicingIcon 134 | }); 135 | } 136 | }, 137 | touchEnd: function (e) { 138 | wx.stopRecord(); 139 | this.setData({ 140 | keyboardTip: this.data.keyboardTip1, 141 | showRecordStatusView: false 142 | }); 143 | if (!this.data.cancelSendStatus) { 144 | 145 | } 146 | }, 147 | //录音 148 | record: function () { 149 | var that = this; 150 | wx.startRecord({ 151 | success(res) { 152 | console.log(res); 153 | if (!that.data.cancelSendStatus) { 154 | that.triggerEvent('send', { tempFilePath: res.tempFilePath, type: 'voice'}); 155 | } 156 | }, 157 | fail: (err) =>{ 158 | console.log(err); 159 | wx.showToast({ 160 | title: '录音错误', 161 | duration: 500 162 | }); 163 | } 164 | }) 165 | }, 166 | //发送文本 167 | sendText: function () { 168 | if (!this.data.inputContent) { 169 | return; 170 | } 171 | this.triggerEvent('send', { content: this.data.inputContent, type: 'text' }); 172 | this.setData({ 173 | inputContent: '' 174 | }); 175 | }, 176 | //发送图片 177 | sendMessage: function () { 178 | var that = this; 179 | wx.chooseImage({ 180 | count: 9, // 默认9 181 | sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有 182 | sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有 183 | success: res => { 184 | // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片 185 | that.triggerEvent('send', { tempFilePaths: res.tempFilePaths, type: 'picture' }); 186 | }, 187 | fail: (err) => { 188 | console.log(err); 189 | } 190 | }); 191 | } 192 | 193 | } 194 | }) 195 | -------------------------------------------------------------------------------- /customeSessionWidget.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /customeSessionWidget.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 9 | {{keyboardTip}} 11 | 12 | 13 | 14 | 18 | -------------------------------------------------------------------------------- /customeSessionWidget.wxss: -------------------------------------------------------------------------------- 1 | /* component/customeSessionWidget.wxss */ 2 | .session-view { 3 | display: flex; 4 | flex-direction: rows; 5 | justify-content: flex-start; 6 | background-color: #eee; 7 | padding: 20rpx 20rpx 60rpx 20rpx; 8 | align-items: center; /*项目在行中居中对齐, 不然会出现图片和中间view对不齐情况*/ 9 | position: fixed; 10 | bottom: 0; 11 | left: 0; 12 | right: 0; 13 | border-top: 1px solid rgb(158, 158, 158); 14 | } 15 | 16 | .middle-view{ 17 | width: 100%; 18 | margin: 0 20rpx; 19 | border: 1px solid rgb(158, 158, 158); 20 | padding: 15rpx; 21 | border-radius: 10rpx; 22 | text-align: center; 23 | background-color: #fff; 24 | } 25 | 26 | .iconImage { 27 | width: 60rpx; 28 | height: 50rpx; 29 | } 30 | 31 | .voice-tip { 32 | position: fixed; 33 | width: 300rpx; 34 | height: 250rpx; 35 | left: 50%; 36 | margin-left: -150rpx; 37 | top: 40%; 38 | margin-top: -150rpx; 39 | background-color: #555151; 40 | color: #fff; 41 | text-align: center; 42 | padding: 20rpx; 43 | border-radius: 20rpx; 44 | font-size: 30rpx; 45 | } 46 | 47 | .voice-tip image{ 48 | width: 150rpx; 49 | height: 170rpx; 50 | } 51 | -------------------------------------------------------------------------------- /picture/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SJcz/InputWidget/dda62476203a77a04ed3cc878182334703a07bc4/picture/1.png -------------------------------------------------------------------------------- /picture/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SJcz/InputWidget/dda62476203a77a04ed3cc878182334703a07bc4/picture/2.png -------------------------------------------------------------------------------- /picture/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SJcz/InputWidget/dda62476203a77a04ed3cc878182334703a07bc4/picture/3.png -------------------------------------------------------------------------------- /picture/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SJcz/InputWidget/dda62476203a77a04ed3cc878182334703a07bc4/picture/4.png -------------------------------------------------------------------------------- /picture/GIF.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SJcz/InputWidget/dda62476203a77a04ed3cc878182334703a07bc4/picture/GIF.gif --------------------------------------------------------------------------------