liveCommentInput.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. <template>
  2. <view
  3. v-if="visible || focused"
  4. class="live-comment-input"
  5. :class="[
  6. 'live-comment-input--' + variant,
  7. 'live-comment-input--' + theme,
  8. { 'live-comment-input--focused': focused }
  9. ]">
  10. <!-- 未聚焦:点击唤起键盘 -->
  11. <view v-if="!focused" class="live-comment-input__bar">
  12. <view class="live-comment-input__trigger" @click="openInput">
  13. <text class="live-comment-input__trigger-text">{{ displayText }}</text>
  14. </view>
  15. <view v-if="showActions" class="live-comment-input__actions">
  16. <slot name="actions" />
  17. </view>
  18. </view>
  19. <!-- 聚焦:键盘上方输入框 + 发送 -->
  20. <view
  21. v-if="focused"
  22. class="live-comment-input__keyboard-panel"
  23. :style="keyboardPanelStyle">
  24. <view class="live-comment-input__editor">
  25. <input
  26. ref="commentInput"
  27. class="live-comment-input__field"
  28. type="text"
  29. :value="value"
  30. :placeholder="placeholder"
  31. :placeholder-style="placeholderStyle"
  32. placeholder-class="live-comment-input__placeholder-class"
  33. :focus="focused"
  34. :disabled="disabled"
  35. :adjust-position="false"
  36. cursor-spacing="100"
  37. :confirm-type="value ? 'send' : 'done'"
  38. @input="onInput"
  39. @focus="onFocus"
  40. @blur="onBlur"
  41. @confirm="handleSend" />
  42. <view
  43. class="live-comment-input__send"
  44. :class="{ 'live-comment-input__send--horizontal': showType === 1 }"
  45. @touchstart.stop.prevent="handleSend"
  46. @tap.stop="handleSend">
  47. 发送
  48. </view>
  49. </view>
  50. </view>
  51. </view>
  52. </template>
  53. <script>
  54. export default {
  55. name: 'LiveCommentInput',
  56. props: {
  57. value: {
  58. type: String,
  59. default: ''
  60. },
  61. placeholder: {
  62. type: String,
  63. default: '说点什么...'
  64. },
  65. showType: {
  66. type: Number,
  67. default: 2
  68. },
  69. variant: {
  70. type: String,
  71. default: 'inline'
  72. },
  73. disabled: {
  74. type: Boolean,
  75. default: false
  76. },
  77. focused: {
  78. type: Boolean,
  79. default: false
  80. },
  81. keyboardHeight: {
  82. type: Number,
  83. default: 0
  84. },
  85. visible: {
  86. type: Boolean,
  87. default: true
  88. },
  89. showActions: {
  90. type: Boolean,
  91. default: true
  92. }
  93. },
  94. data() {
  95. return {
  96. ignoreBlur: false
  97. };
  98. },
  99. computed: {
  100. theme() {
  101. return this.showType === 1 ? 'light' : 'dark';
  102. },
  103. displayText() {
  104. return this.value || this.placeholder;
  105. },
  106. placeholderStyle() {
  107. return this.theme === 'light' ? 'color:#999999;' : 'color:#e7e7e7;';
  108. },
  109. keyboardPanelStyle() {
  110. const offset = this.keyboardHeight > 0 ? this.keyboardHeight : 0;
  111. return {
  112. transform: offset ? `translateY(-${offset}rpx)` : 'none'
  113. };
  114. }
  115. },
  116. watch: {
  117. focused(val) {
  118. if (val) {
  119. this.$nextTick(() => this.focusInput());
  120. }
  121. }
  122. },
  123. methods: {
  124. openInput() {
  125. if (this.disabled) return;
  126. this.$emit('focus');
  127. this.$nextTick(() => this.focusInput());
  128. },
  129. focusInput() {
  130. const input = this.$refs.commentInput;
  131. if (input && typeof input.focus === 'function') {
  132. input.focus();
  133. }
  134. },
  135. onInput(e) {
  136. this.$emit('input', e.detail.value);
  137. },
  138. onFocus() {
  139. this.$emit('focus');
  140. },
  141. onBlur() {
  142. if (this.ignoreBlur) return;
  143. this.$emit('blur');
  144. },
  145. handleSend() {
  146. if (this.disabled) return;
  147. const text = (this.value || '').trim();
  148. if (!text) {
  149. uni.showToast({
  150. title: '不能发送空消息',
  151. icon: 'none'
  152. });
  153. return;
  154. }
  155. this.ignoreBlur = true;
  156. this.$emit('send');
  157. this.$nextTick(() => {
  158. uni.hideKeyboard();
  159. setTimeout(() => {
  160. this.ignoreBlur = false;
  161. }, 200);
  162. });
  163. },
  164. clearInput() {
  165. this.$emit('input', '');
  166. }
  167. }
  168. };
  169. </script>
  170. <style scoped lang="scss">
  171. .live-comment-input {
  172. width: 100%;
  173. box-sizing: border-box;
  174. }
  175. .live-comment-input--overlay {
  176. position: absolute;
  177. left: 0;
  178. right: 0;
  179. bottom: 0;
  180. padding: 24rpx;
  181. z-index: 99999;
  182. }
  183. .live-comment-input__bar {
  184. display: flex;
  185. flex-direction: row;
  186. align-items: center;
  187. justify-content: space-between;
  188. width: 100%;
  189. min-width: 0;
  190. }
  191. .live-comment-input__trigger {
  192. flex: 1;
  193. min-width: 0;
  194. border-radius: 36rpx;
  195. padding: 18rpx 20rpx;
  196. box-sizing: border-box;
  197. }
  198. .live-comment-input--dark .live-comment-input__trigger {
  199. background-color: rgba(0, 0, 0, 0.3);
  200. }
  201. .live-comment-input--light .live-comment-input__trigger {
  202. background: var(--input-bg, #f5f7fa);
  203. }
  204. .live-comment-input__trigger-text {
  205. font-size: 28rpx;
  206. line-height: 1.4;
  207. overflow: hidden;
  208. white-space: nowrap;
  209. text-overflow: ellipsis;
  210. }
  211. .live-comment-input--dark .live-comment-input__trigger-text {
  212. color: #e7e7e7;
  213. }
  214. .live-comment-input--light .live-comment-input__trigger-text {
  215. color: #999999;
  216. }
  217. .live-comment-input__actions {
  218. flex-shrink: 0;
  219. margin-left: 16rpx;
  220. display: flex;
  221. align-items: center;
  222. }
  223. .live-comment-input__keyboard-panel {
  224. position: fixed;
  225. left: 0;
  226. right: 0;
  227. bottom: 0;
  228. z-index: 10001;
  229. padding: 24rpx;
  230. box-sizing: border-box;
  231. background-color: var(--bottom-color, #ffffff);
  232. transform: translateZ(0);
  233. }
  234. .live-comment-input--dark .live-comment-input__keyboard-panel {
  235. background-color: transparent;
  236. }
  237. .live-comment-input__editor {
  238. display: flex;
  239. flex-direction: row;
  240. align-items: center;
  241. border-radius: 36rpx;
  242. padding: 10rpx 10rpx 10rpx 20rpx;
  243. box-sizing: border-box;
  244. width: 100%;
  245. }
  246. .live-comment-input--dark .live-comment-input__editor {
  247. background-color: rgba(0, 0, 0, 0.3);
  248. }
  249. .live-comment-input--light .live-comment-input__editor {
  250. background: var(--input-bg, #f5f7fa);
  251. }
  252. .live-comment-input__field {
  253. flex: 1;
  254. min-width: 0;
  255. border: none;
  256. background: transparent;
  257. font-size: 32rpx;
  258. color: var(--text-color, #333333);
  259. }
  260. .live-comment-input--dark .live-comment-input__field {
  261. color: #ffffff;
  262. }
  263. .live-comment-input__send {
  264. flex-shrink: 0;
  265. background-color: #ffffff;
  266. border-radius: 28rpx;
  267. padding: 14rpx 16rpx;
  268. color: #181818;
  269. font-weight: 500;
  270. font-size: 28rpx;
  271. min-width: 80rpx;
  272. text-align: center;
  273. margin-left: 16rpx;
  274. }
  275. .live-comment-input__send--horizontal {
  276. background-color: #FF233C;
  277. color: #ffffff;
  278. }
  279. .live-comment-input--dark .live-comment-input__send {
  280. background-color: #FF233C;
  281. color: #ffffff;
  282. padding: 14rpx 8rpx;
  283. }
  284. </style>