| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- <template>
- <view class="editor_wrap">
- <textarea
- class="editor_component"
- id="editor2"
- ref="editor2"
- :value="textValue"
- :placeholder="placeholder"
- :maxlength="-1"
- :adjust-position="false"
- :show-confirm-bar="false"
- :auto-height="true"
- :disable-default-padding="true"
- @input="onInput"
- @focus="onFocus"
- @blur="onBlur"
- @confirm="onConfirm"
- />
- </view>
- </template>
- <script>
- export default {
- props: {
- placeholder: {
- type: String,
- default: ''
- }
- },
- data() {
- return {
- textValue: '',
- cursorPos: -1,
- };
- },
- mounted() {
- this.$nextTick(() => {
- const mockCtx = {
- clear: () => {
- this.textValue = '';
- this.$emit('input', { detail: { html: '' } });
- },
- undo: () => {
- // nvue textarea 暂不支持撤销
- }
- };
- this.$emit('ready', { context: mockCtx });
- });
- },
- methods: {
- /**
- * 输入事件处理
- */
- onInput(e) {
- this.textValue = e.detail.value;
- this.cursorPos = e.detail.cursor;
-
- // 保持与 editor 相同的事件格式
- this.$emit('input', { detail: { html: this.textValue } });
- // 检测 @ 输入
- if (e.detail.cursor > 0) {
- const lastChar = this.textValue.slice(e.detail.cursor - 1, e.detail.cursor);
- if (lastChar === '@') {
- this.$emit('tryAt');
- }
- }
- },
- /**
- * 聚焦事件
- */
- onFocus(e) {
- this.$emit('focus');
- },
- /**
- * 失焦事件
- */
- onBlur(e) {
- this.cursorPos = e.detail.cursor;
- this.$emit('blur');
- },
-
- /**
- * 确认事件(如点击键盘完成)
- */
- onConfirm(e) {
- // 如果需要处理回车发送,可以在这里添加逻辑
- },
- /**
- * 插入表情(文本形式)
- */
- insertEmoji(emoji) {
- if (this.cursorPos < 0) {
- this.textValue += emoji;
- } else {
- const left = this.textValue.substring(0, this.cursorPos);
- const right = this.textValue.substring(this.cursorPos);
- this.textValue = left + emoji + right;
- this.cursorPos += emoji.length;
- }
- this.$emit('input', { detail: { html: this.textValue } });
- },
- /**
- * 插入 @ 人员(文本形式,替代原本的 canvas 图片方案)
- */
- createCanvasData(userID, nickname) {
- const atText = `@${nickname} `;
- if (this.cursorPos < 0) {
- this.textValue += atText;
- } else {
- // 如果光标前已经是 @,则替换它?
- // 简单起见,直接插入
- const left = this.textValue.substring(0, this.cursorPos);
- const right = this.textValue.substring(this.cursorPos);
- this.textValue = left + atText + right;
- this.cursorPos += atText.length;
- }
- this.$emit('input', { detail: { html: this.textValue } });
- },
-
- /**
- * 聚焦方法
- */
- focus() {
- // nvue textarea 通常无法通过方法聚焦,依赖属性 focus
- // 如果需要,可以在 data 中添加 focus 属性绑定到 textarea
- }
- }
- };
- </script>
- <style lang="scss" scoped>
- .editor_wrap {
- position: relative;
- flex: 1;
- flex-direction: column;
- }
- #editor2 {
- flex: 1;
- width: 100%;
- background-color: #fff;
- min-height: 72rpx;
- max-height: 240rpx;
- padding-left: 10rpx;
- padding-right: 10rpx;
- padding-top: 20rpx;
- padding-bottom: 20rpx;
- font-size: 32rpx;
- color: #333;
- /* 垂直居中可以通过 line-height 或者 flex 布局辅助,但在 nvue textarea 中 padding 是最直接的 */
- }
- </style>
|