| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- <template>
- <view class="textarea_wrap">
- <textarea
- class="custom_textarea"
- :value="inputValue"
- :placeholder="placeholder"
- :maxlength="-1"
- :auto-height="true"
- :show-confirm-bar="false"
- :adjust-position="false"
- :cursor-spacing="20"
- @input="handleInput"
- @focus="handleFocus"
- @blur="handleBlur"
- @confirm="handleConfirm"
- />
- </view>
- </template>
- <script>
- export default {
- props: {
- placeholder: {
- type: String,
- default: ''
- },
- value: {
- type: String,
- default: ''
- }
- },
- data() {
- return {
- inputValue: '',
- cursor: -1
- };
- },
- watch: {
- value: {
- immediate: true,
- handler(val) {
- this.inputValue = val;
- }
- }
- },
- methods: {
- handleInput(e) {
- const value = e.detail.value;
- this.inputValue = value;
- this.cursor = e.detail.cursor;
-
- // 检测 @ 输入
- const lastChar = value.slice(this.cursor - 1, this.cursor);
- if (lastChar === '@') {
- this.$emit('tryAt');
- }
-
- this.$emit('input', e);
- },
- handleFocus(e) {
- this.$emit('focus', e);
- },
- handleBlur(e) {
- this.$emit('blur', e);
- },
- handleConfirm(e) {
- this.$emit('confirm', e);
- },
- // 供外部调用的方法
- insertEmoji(emoji) {
- // 在光标处插入 emoji
- const value = this.inputValue;
- // 如果没有光标位置,默认追加到末尾
- const cursor = this.cursor >= 0 ? this.cursor : value.length;
-
- const newValue = value.slice(0, cursor) + emoji + value.slice(cursor);
- this.inputValue = newValue;
-
- // 更新光标位置
- this.cursor = cursor + emoji.length;
-
- // 触发 input 事件
- this.$emit('input', { detail: { value: newValue, cursor: this.cursor } });
- },
- insertAt(nickname) {
- // 在光标处插入 @Name
- // 简单处理:直接插入 @nickname + 空格
- // 如果需要处理用户已经输入了 @ 的情况,需要更复杂的判断(比如判断光标前一个字符是否为 @)
- const atText = `@${nickname} `;
- this.insertEmoji(atText);
- },
- createCanvasData(userID, nickname) {
- // 兼容 CustomEditor 的接口
- this.insertAt(nickname);
- },
- clear() {
- this.inputValue = '';
- this.cursor = -1;
- this.$emit('input', { detail: { value: '', cursor: -1 } });
- },
- backspace() {
- const value = this.inputValue || '';
- const cursor = this.cursor >= 0 ? this.cursor : value.length;
- if (cursor === 0) return;
-
- // 使用 Array.from 处理 surrogate pairs (emoji)
- const left = value.slice(0, cursor);
- const right = value.slice(cursor);
- const leftArr = Array.from(left);
- if (leftArr.length === 0) return;
-
- leftArr.pop();
- const newLeft = leftArr.join('');
- const newValue = newLeft + right;
-
- this.inputValue = newValue;
- // cursor 更新为新左侧字符串长度
- this.cursor = newLeft.length;
-
- this.$emit('input', { detail: { value: newValue, cursor: this.cursor } });
- },
- focus() {
- // textarea 无法通过 JS 强制聚焦,只能依赖用户操作或 auto-focus
- // 但我们可以尝试通过 v-if 刷新组件来触发 auto-focus,或者使用 uni.hideKeyboard() 后再 show
- }
- }
- };
- </script>
- <style lang="scss" scoped>
- .textarea_wrap {
- width: 100%;
- min-height: 30px;
- display: flex;
- align-items: center;
- background-color: #fff;
- border-radius: 4px;
- padding: 4px 8px;
- box-sizing: border-box; /* 确保 padding 不会撑大宽度 */
- }
- .custom_textarea {
- width: 100%;
- min-height: 20px; /* 调整最小高度,配合 padding */
- max-height: 100px; /* 限制最大高度 */
- line-height: 20px;
- font-size: 16px;
- padding: 0; /* padding 移到 wrap 上 */
- background-color: transparent; /* 背景透明,显示 wrap 的背景 */
- }
- </style>
|