md-textarea.vue 10.5 KB
Newer Older
璃白.'s avatar
璃白. 已提交
1
<template>
璃白.'s avatar
璃白. 已提交
2
  <div :class="['md_textarea', { fullScreen, isFocus }]">
璃白.'s avatar
璃白. 已提交
3
    <textarea
璃白.'s avatar
璃白. 已提交
4
      spellcheck="false"
璃白.'s avatar
璃白. 已提交
5
      :id="id"
璃白.'s avatar
璃白. 已提交
6
      @change="$emit('update:text', textContent)"
7
      @input="input"
璃白.'s avatar
璃白. 已提交
8 9
      @focus="setFocus(true)"
      @blur="setFocus(false)"
G
guoweijia 已提交
10
      @paste="pasteFile"
璃白.'s avatar
璃白. 已提交
11
      @keydown.stop.50="handleCallUser"
璃白.'s avatar
璃白. 已提交
12 13
      @keydown.stop.up="changeActiveUserIndex($event, 'up')"
      @keydown.stop.down="changeActiveUserIndex($event, 'down')"
璃白.'s avatar
璃白. 已提交
14
      @keydown.enter="handleEnter"
璃白.'s avatar
璃白. 已提交
15 16
      @keydown.meta.enter.exact="submit"
      @keydown.ctrl.enter.exact="submit"
璃白.'s avatar
fix  
璃白. 已提交
17
      @keydown.tab.prevent="$emit('tab')"
璃白.'s avatar
璃白. 已提交
18
      v-model="textContent"
璃白.'s avatar
璃白. 已提交
19
      :placeholder="placeholder"
20 21
      :maxlength="maxLength"
      :rows="rows"
璃白.'s avatar
璃白. 已提交
22
      :disabled="disabled"
璃白.'s avatar
璃白. 已提交
23
      :class="{ disabled }"
璃白.'s avatar
璃白. 已提交
24 25 26
      :style="{
        height: editorHeight,
        overflow: editorOverFlow,
璃白.'s avatar
璃白. 已提交
27 28 29
        cursor: disabled
          ? 'not-allowed'
          : formatType
璃白.'s avatar
璃白. 已提交
30 31 32
          ? `url(https://codechina.csdn.net/codechina/operation-work/uploads/a1b7c2a995b2320dca911e2f2ecb9b88/format.png),text`
          : 'text'
      }"
璃白.'s avatar
璃白. 已提交
33 34
    >
    </textarea>
璃白.'s avatar
璃白. 已提交
35 36 37 38 39 40 41
    <transition name="slide-fade">
      <helpDoc
        v-if="showHelp"
        @updateShowHelp="$emit('updateShowHelp', $event)"
        :showHelp.sync="showHelp"
      />
    </transition>
璃白.'s avatar
璃白. 已提交
42
    <transition name="slideup-fade">
璃白.'s avatar
璃白. 已提交
43 44
      <selectUser
        :userList="userList"
璃白.'s avatar
璃白. 已提交
45
        :activeUserIndex.sync="activeUserIndex"
璃白.'s avatar
璃白. 已提交
46 47
        v-show="showSelectUser"
        :position="selectUserPosition"
璃白.'s avatar
璃白. 已提交
48
        @selectUser="handleSelectUser"
璃白.'s avatar
璃白. 已提交
49
      />
璃白.'s avatar
璃白. 已提交
50
    </transition>
璃白.'s avatar
璃白. 已提交
51 52 53
  </div>
</template>
<script>
54 55 56
import {
  getSelectionInfo,
  getPosition,
璃白.'s avatar
璃白. 已提交
57
  preventDefault,
58 59
  throttle as throttleFn
} from "@/assets/js/utils";
璃白.'s avatar
璃白. 已提交
60
import selectUser from "./components/user-select";
璃白.'s avatar
璃白. 已提交
61
import helpDoc from "./components/help-doc";
璃白.'s avatar
璃白. 已提交
62 63
import renderMix from "./mixins/render-mixins";
import selectUserMix from "./mixins/select-user-mixins";
璃白.'s avatar
璃白. 已提交
64
export default {
璃白.'s avatar
璃白. 已提交
65
  components: { helpDoc, selectUser },
璃白.'s avatar
璃白. 已提交
66
  mixins: [renderMix, selectUserMix],
璃白.'s avatar
璃白. 已提交
67
  props: {
68 69 70 71
    id: {
      type: String,
      default: ""
    },
璃白.'s avatar
fix  
璃白. 已提交
72 73 74 75 76 77 78
    html: {
      type: String,
      default: ""
    },
    htmlMinHeight: {
      default: ""
    },
璃白.'s avatar
璃白. 已提交
79 80 81 82
    fullScreen: {
      type: Boolean,
      default: false
    },
83 84 85 86
    throttleTime: {
      type: Number,
      default: 1000
    },
璃白.'s avatar
璃白. 已提交
87 88 89 90
    isFocus: {
      type: Boolean,
      default: false
    },
璃白.'s avatar
璃白. 已提交
91 92 93 94
    disabled: {
      type: Boolean,
      default: false
    },
璃白.'s avatar
璃白. 已提交
95 96 97 98 99 100
    placeholder: {
      type: String,
      default: false
    },
    fileList: {
      type: Array,
璃白.'s avatar
璃白. 已提交
101
      default: () => []
璃白.'s avatar
璃白. 已提交
102 103
    },
    text: {
璃白.'s avatar
璃白. 已提交
104 105
      type: [String, Number],
      default: ""
璃白.'s avatar
璃白. 已提交
106
    },
107 108 109 110 111 112 113 114
    maxLength: {
      type: [String, Number],
      default: ""
    },
    rows: {
      type: [String, Number],
      default: ""
    },
璃白.'s avatar
璃白. 已提交
115 116 117 118
    height: {
      type: Number,
      default: 0
    },
璃白.'s avatar
璃白. 已提交
119 120
    selectionInfo: {
      type: Object,
璃白.'s avatar
璃白. 已提交
121
      default: () => {}
璃白.'s avatar
璃白. 已提交
122 123 124 125 126 127 128
    },
    formatType: {
      default: ""
    },
    showHelp: {
      type: Boolean,
      default: false
璃白.'s avatar
璃白. 已提交
129 130 131 132
    },
    userList: {
      type: Array,
      default: () => []
璃白.'s avatar
璃白. 已提交
133 134
    }
  },
135

璃白.'s avatar
璃白. 已提交
136 137
  data() {
    return {
138
      textContent: "",
139
      editorHeight: "auto",
璃白.'s avatar
璃白. 已提交
140 141
      editorOverFlow: "auto",
      showSelectUser: false,
璃白.'s avatar
璃白. 已提交
142 143 144 145 146
      queryInfo: {
        startPosition: "",
        endPosition: "",
        keyWord: ""
      },
璃白.'s avatar
璃白. 已提交
147
      activeUserIndex: 0,
璃白.'s avatar
璃白. 已提交
148
      selectUserPosition: { left: 0, top: 0 }
璃白.'s avatar
璃白. 已提交
149 150 151 152 153
    };
  },
  created() {
    document.addEventListener("mouseup", this.checkSelection);
  },
璃白.'s avatar
fix  
璃白. 已提交
154
  mounted() {
璃白.'s avatar
fix  
璃白. 已提交
155
    this.resetPreviewMinHeight();
璃白.'s avatar
fix  
璃白. 已提交
156
  },
璃白.'s avatar
璃白. 已提交
157
  watch: {
璃白.'s avatar
fix  
璃白. 已提交
158 159 160 161
    isFocus: {
      handler: function(val) {
        if (val) {
          this.resetPreviewMinHeight();
璃白.'s avatar
璃白. 已提交
162
        } else {
璃白.'s avatar
璃白. 已提交
163
          setTimeout(() => {
璃白.'s avatar
璃白. 已提交
164
            this.showSelectUser = false;
璃白.'s avatar
璃白. 已提交
165
          }, 200);
璃白.'s avatar
fix  
璃白. 已提交
166 167 168
        }
      }
    },
璃白.'s avatar
璃白. 已提交
169 170 171 172
    text: {
      immediate: true,
      handler: function(val) {
        this.textContent = val;
璃白.'s avatar
fix  
璃白. 已提交
173
        this.transferMarkdown(val);
璃白.'s avatar
璃白. 已提交
174
      }
175
    },
176 177
    fullScreen: {
      immediate: true,
璃白.'s avatar
璃白. 已提交
178 179 180 181 182 183
      handler: function(val) {
        if (val) {
          document.body.style.overflow = "hidden";
        } else {
          document.body.style.overflow = "auto";
        }
184
        setTimeout(() => {
璃白.'s avatar
fix  
璃白. 已提交
185
          this.reSizeTextareaHeight();
186 187 188
        }, 0);
      }
    },
189 190 191 192 193
    textContent: {
      immediate: true,
      handler: function() {
        setTimeout(() => {
          if (!this.autoSize) return;
璃白.'s avatar
fix  
璃白. 已提交
194
          this.reSizeTextareaHeight();
195 196
        }, 0);
      }
璃白.'s avatar
璃白. 已提交
197 198 199 200 201 202 203
    },
    showSelectUser: {
      handler: function(val) {
        if (!val) {
          this.resetQueryInfo();
        }
      }
璃白.'s avatar
璃白. 已提交
204 205 206 207 208
    }
  },
  beforeDestroy() {
    document.removeEventListener("mouseup", this.checkSelection);
  },
209 210
  computed: {
    emitText() {
璃白.'s avatar
fix  
璃白. 已提交
211 212
      // return throttleFn(() => {}, this.throttleTime);
      return () => {
213
        this.$emit("update:text", this.textContent);
璃白.'s avatar
fix  
璃白. 已提交
214
      };
215 216 217 218 219
    },
    autoSize() {
      return this.rows === "auto";
    }
  },
璃白.'s avatar
璃白. 已提交
220
  methods: {
璃白.'s avatar
璃白. 已提交
221
    changeActiveUserIndex(event, type) {
璃白.'s avatar
璃白. 已提交
222
      if (this.showSelectUser) {
璃白.'s avatar
璃白. 已提交
223
        event.preventDefault();
璃白.'s avatar
璃白. 已提交
224 225 226 227 228 229
        const max = this.userList.length;
        if (type === "down") {
          this.activeUserIndex++;
          if (this.activeUserIndex >= max) {
            this.activeUserIndex = 0;
          }
璃白.'s avatar
璃白. 已提交
230 231 232 233 234 235 236
          const index = this.activeUserIndex;
          if (index % 3 === 0) {
            document
              .getElementById("md_user_id_" + this.activeUserIndex)
              .parentNode.scrollTo(0, 56 * index);
            // .scrollIntoView({ behavior: "smooth" });
          }
璃白.'s avatar
璃白. 已提交
237 238 239 240 241
        } else {
          this.activeUserIndex--;
          if (this.activeUserIndex < 0) {
            this.activeUserIndex = max - 1;
          }
璃白.'s avatar
璃白. 已提交
242 243 244 245 246
          const index = this.activeUserIndex;
          document
            .getElementById("md_user_id_" + this.activeUserIndex)
            .parentNode.scrollTo(0, 56 * index);
          // .scrollIntoView({ behavior: "smooth" });
璃白.'s avatar
璃白. 已提交
247 248 249
        }
      }
    },
璃白.'s avatar
fix  
璃白. 已提交
250 251 252 253 254 255 256
    resetPreviewMinHeight() {
      setTimeout(() => {
        const textEl = document.getElementById(this.id);
        if (!textEl) return;
        const height = textEl.offsetHeight;
        this.$emit("update:htmlMinHeight", height);
      }, 0);
璃白.'s avatar
fix  
璃白. 已提交
257
    },
璃白.'s avatar
璃白. 已提交
258 259 260 261 262 263 264
    resetQueryInfo() {
      this.queryInfo = {
        startPosition: "",
        endPosition: "",
        keyWord: ""
      };
    },
璃白.'s avatar
璃白. 已提交
265 266
    input(e) {
      if (this.showSelectUser) this.handleQueryUser(e);
267 268 269
      this.$emit("update:textLength", this.textContent.length);
      this.emitText();
    },
璃白.'s avatar
璃白. 已提交
270
    createHideEl(type) {
271 272 273 274 275 276 277 278 279
      const textEl = document.getElementById(this.id);
      if (!textEl) return;
      const fontSize = getComputedStyle(textEl).getPropertyValue("font-size");
      const lineHeight = getComputedStyle(textEl).getPropertyValue(
        "line-height"
      );
      const fontFamily = getComputedStyle(textEl).getPropertyValue(
        "font-family"
      );
璃白.'s avatar
璃白. 已提交
280
      const hideElId = type + this.id;
281 282 283 284 285 286 287 288 289
      let hideEl = document.getElementById(hideElId);
      if (!hideEl) {
        hideEl = document.createElement("div");
        textEl.parentNode.appendChild(hideEl);
      }
      hideEl.id = hideElId;
      hideEl.style.fontSize = fontSize;
      hideEl.style.lineHeight = lineHeight;
      hideEl.style.fontFamily = fontFamily;
璃白.'s avatar
璃白. 已提交
290 291 292 293 294 295 296 297
      return hideEl;
    },
    reSizeTextareaHeight() {
      const textEl = document.getElementById(this.id);
      if (!textEl) return;
      const fontSize = getComputedStyle(textEl).getPropertyValue("font-size");

      const hideEl = this.createHideEl("clac_height_El_");
298 299
      hideEl.innerText = this.textContent;
      const contentHeight = hideEl.offsetHeight;
300 301
      this.editorHeight = this.fullScreen
        ? "calc(100% - 42px)"
璃白.'s avatar
璃白. 已提交
302 303
        : this.height
        ? this.height + "px"
304 305 306 307
        : this.autoSize
        ? `${contentHeight + parseFloat(fontSize) * 1.2}px`
        : "auto";
      this.editorOverFlow =
璃白.'s avatar
fix  
璃白. 已提交
308
        this.autoSize && !this.fullScreen && !this.height ? "hidden" : "auto";
309 310
      textEl.parentNode.removeChild(hideEl);
    },
璃白.'s avatar
璃白. 已提交
311
    handleEnter(e) {
璃白.'s avatar
璃白. 已提交
312
      if (this.showSelectUser) {
璃白.'s avatar
璃白. 已提交
313 314 315
        const activeUser = this.userList[this.activeUserIndex];
        this.handleSelectUser(activeUser);
        e.preventDefault();
璃白.'s avatar
璃白. 已提交
316 317 318 319
        return;
      }
      this.$emit("enter");
    },
璃白.'s avatar
璃白. 已提交
320 321 322
    submit() {
      this.$emit("submit");
    },
璃白.'s avatar
璃白. 已提交
323
    setFocus(val) {
璃白.'s avatar
璃白. 已提交
324
      this.$emit("update:isFocus", val);
璃白.'s avatar
璃白. 已提交
325
    },
璃白.'s avatar
璃白. 已提交
326 327
    checkSelection() {
      const info = getSelectionInfo(this.id);
璃白.'s avatar
璃白. 已提交
328 329
      if (!info) {
        const cursorPoint = getPosition(this.id);
璃白.'s avatar
璃白. 已提交
330
        this.$emit("update:selectionInfo", {
璃白.'s avatar
璃白. 已提交
331 332 333
          selectorId: this.id,
          selectionStart: cursorPoint,
          selectionEnd: cursorPoint
璃白.'s avatar
璃白. 已提交
334
        });
璃白.'s avatar
璃白. 已提交
335 336
        return;
      }
璃白.'s avatar
璃白. 已提交
337
      this.$emit("update:selectionInfo", info);
G
guoweijia 已提交
338 339 340 341 342 343 344 345 346 347 348
    },
    pasteFile(event) {
      let fileList = [];
      const items = (event.clipboardData || window.clipboardData).items;
      for (let i = 0; i < items.length; i++) {
        if (items[i].type.indexOf("image") !== -1) {
          fileList.push(items[i].getAsFile());
          break;
        }
      }
      if (!fileList.length) return;
璃白.'s avatar
璃白. 已提交
349
      this.checkSelection();
璃白.'s avatar
璃白. 已提交
350
      this.$emit("update:fileList", fileList);
璃白.'s avatar
璃白. 已提交
351 352 353 354 355 356 357
    }
  }
};
</script>
<style lang="less" scoped>
.md_textarea {
  position: relative;
358
  padding: 14px 0;
璃白.'s avatar
璃白. 已提交
359
  background: var(--md-editor-content-bg-color);
360 361
  // border-left: 1px solid var(--md-editor-border-color);
  // border-right: 1px solid var(--md-editor-border-color);
璃白.'s avatar
璃白. 已提交
362
  transition: border 0.3s;
363
  // padding: 14px;
璃白.'s avatar
璃白. 已提交
364
  box-sizing: border-box;
365 366 367 368
  // &.isFocus {
  //   border-left: 1px solid var(--md-editor-border-color-active);
  //   border-right: 1px solid var(--md-editor-border-color-active);
  // }
369

璃白.'s avatar
璃白. 已提交
370
  &.fullScreen {
371
    height: 100%;
璃白.'s avatar
璃白. 已提交
372 373
    textarea {
      font-size: 20px;
374 375
      max-height: 100%;
      overflow-y: auto;
璃白.'s avatar
璃白. 已提交
376 377
    }
  }
璃白.'s avatar
璃白. 已提交
378 379 380
  &.disabled {
    background: var(--md-editor-content-bg-color-disabled);
  }
381

璃白.'s avatar
璃白. 已提交
382 383 384 385 386
  textarea {
    display: block;
    width: 100%;
    height: 100%;
    box-sizing: border-box;
璃白.'s avatar
璃白. 已提交
387
    background: var(--md-editor-content-bg-color);
388 389
    color: var(--md-editor-text-color-active);
    height: var(--md-editor-height);
璃白.'s avatar
璃白. 已提交
390
    resize: none;
璃白.'s avatar
璃白. 已提交
391
    font-size: 14px;
璃白.'s avatar
璃白. 已提交
392
    line-height: 1.625;
璃白.'s avatar
璃白. 已提交
393
    word-break: break-all;
璃白.'s avatar
璃白. 已提交
394 395 396
    font-family: "Menlo", -apple-system, SF UI Text, Arial, PingFang SC,
      Hiragino Sans GB, Microsoft YaHei, WenQuanYi Micro Hei, sans-serif, SimHei,
      SimSun;
397 398 399
    &::placeholder {
      color: var(--md-editor-text-color);
    }
璃白.'s avatar
璃白. 已提交
400 401 402
    &.disabled {
      opacity: var(--md-editor-disabled-opacity);
    }
璃白.'s avatar
璃白. 已提交
403 404 405
    // &:disabled {
    //   background: var(--md-editor-content-bg-color-disabled);
    // }
璃白.'s avatar
璃白. 已提交
406 407 408 409 410 411 412 413 414 415
  }
  .icon {
    position: absolute;
    top: 20px;
    right: 20px;
    font-size: 32px;
    cursor: pointer;
  }
}
</style>