App.vue 10.8 KB
Newer Older
璃白.'s avatar
璃白. 已提交
1
<template>
2 3 4 5
  <div
    :class="['md_container', { active: isFocus, fullScreen }]"
    :id="'md_' + id"
  >
璃白.'s avatar
璃白. 已提交
6
    <markdown-header
璃白.'s avatar
fix  
璃白. 已提交
7
      :id="textareaId"
璃白.'s avatar
fix  
璃白. 已提交
8
      :ref="'md_header' + id"
璃白.'s avatar
璃白. 已提交
9 10 11 12
      :text.sync="text"
      :selectionInfo.sync="selectionInfo"
      :showPreview.sync="showPreview"
      :isFocus.sync="isFocus"
璃白.'s avatar
璃白. 已提交
13 14
      :canPreview="canPreview"
      :toolsOptions="toolsOptions"
15
      :zIndex="zIndex"
璃白.'s avatar
fix  
璃白. 已提交
16
      :tabSize="tabSize"
璃白.'s avatar
璃白. 已提交
17
      :disabled="disabled"
璃白.'s avatar
璃白. 已提交
18
      :fullScreen.sync="fullScreen"
19
      :themeOptions="themeOptions"
璃白.'s avatar
璃白. 已提交
20 21 22
      @upload="handleUpload"
      @getFormatType="formatType = $event"
      @updateShowHelp="showHelp = $event"
璃白.'s avatar
璃白. 已提交
23
      @renderLinks="$emit('renderLinks', $event)"
24 25 26 27 28 29 30 31 32
    />
    <input
      ref="mdUploadFile"
      class="md_upload"
      type="file"
      hidden
      name="md-upload-file"
      id="md-upload-file"
      @change="upload"
璃白.'s avatar
璃白. 已提交
33
    />
璃白.'s avatar
璃白. 已提交
34 35 36 37
    <markdownPreview
      :id="textareaId"
      :fullScreen.sync="fullScreen"
      :text="text"
璃白.'s avatar
璃白. 已提交
38
      :height="textareaHeight"
璃白.'s avatar
璃白. 已提交
39
      :html.sync="html"
璃白.'s avatar
fix  
璃白. 已提交
40 41
      :htmlMinHeight="htmlMinHeight"
      v-if="showPreview"
璃白.'s avatar
璃白. 已提交
42
    />
璃白.'s avatar
璃白. 已提交
43

璃白.'s avatar
璃白. 已提交
44 45 46 47 48 49
    <markdown-editor
      :selectionInfo.sync="selectionInfo"
      :text.sync="text"
      :fileList.sync="fileList"
      :placeholder="placeholder"
      :isFocus.sync="isFocus"
50
      :throttleTime="throttle"
璃白.'s avatar
fix  
璃白. 已提交
51
      :htmlMinHeight.sync="htmlMinHeight"
璃白.'s avatar
璃白. 已提交
52
      :fullScreen.sync="fullScreen"
53 54 55
      :maxLength="maxLength"
      :textLength.sync="textLength"
      :rows="rows"
璃白.'s avatar
璃白. 已提交
56
      :height="textareaHeight"
璃白.'s avatar
fix  
璃白. 已提交
57
      :html.sync="html"
58
      :id="textareaId"
璃白.'s avatar
璃白. 已提交
59
      :disabled="disabled"
璃白.'s avatar
璃白. 已提交
60 61
      :show-help="showHelp"
      :formatType="formatType"
璃白.'s avatar
璃白. 已提交
62
      :userList="userList"
璃白.'s avatar
fix  
璃白. 已提交
63
      :ref="'md_textarea' + id"
璃白.'s avatar
fix  
璃白. 已提交
64
      @tab="$refs['md_header' + id].tab()"
璃白.'s avatar
璃白. 已提交
65
      @submit="submit"
璃白.'s avatar
fix  
璃白. 已提交
66
      @enter="handleEnter"
璃白.'s avatar
璃白. 已提交
67
      @getFilteredTags="filteredTags = $event"
璃白.'s avatar
璃白. 已提交
68
      @updateShowHelp="showHelp = $event"
璃白.'s avatar
璃白. 已提交
69
      @renderLinksHtml="renderLinksHtml"
璃白.'s avatar
璃白. 已提交
70
      @queryUserList="queryUserList"
璃白.'s avatar
fix  
璃白. 已提交
71
      v-else
璃白.'s avatar
璃白. 已提交
72
    />
璃白.'s avatar
璃白. 已提交
73
    <div v-if="maxLength && showWordLimit && !showPreview" class="word_limit">
74 75 76 77
      <span>{{ textLength }}</span
      >/<span>{{ maxLength }}</span>
    </div>
    <!-- <markdown-footer
璃白.'s avatar
璃白. 已提交
78 79 80
      :fileList.sync="fileList"
      :canAttachFile="canAttachFile"
      :isFocus.sync="isFocus"
璃白.'s avatar
璃白. 已提交
81 82
      :can-attach-file="canAttachFile"
      v-if="!showPreview && canAttachFile"
83
    /> -->
璃白.'s avatar
璃白. 已提交
84 85 86
  </div>
</template>
<script>
璃白.'s avatar
璃白. 已提交
87 88 89 90
import markdownHeader from "./components/header/md-header";
import markdownFooter from "./components/footer/md-footer";
import markdownEditor from "./components/content/md-textarea";
import markdownPreview from "./components/content/md-preview";
璃白.'s avatar
璃白. 已提交
91
import { formatText, checktUrl, getLinkTitle } from "@/assets/js/utils";
璃白.'s avatar
璃白. 已提交
92
export default {
璃白.'s avatar
璃白. 已提交
93 94 95 96
  components: {
    markdownHeader,
    markdownFooter,
    markdownEditor,
璃白.'s avatar
璃白. 已提交
97
    markdownPreview
璃白.'s avatar
璃白. 已提交
98
  },
璃白.'s avatar
璃白. 已提交
99 100 101 102 103
  props: {
    placeholder: {
      type: String,
      default: "请输入内容"
    },
104 105 106
    id: {
      type: String,
      default: ""
璃白.'s avatar
璃白. 已提交
107
    },
璃白.'s avatar
fix  
璃白. 已提交
108 109 110 111
    tabSize: {
      type: [String, Number],
      default: ""
    },
112 113 114 115 116
    // canAttachFile: {
    //   type: Boolean,
    //   default: true
    // },
    // 初始化时赋值
璃白.'s avatar
璃白. 已提交
117 118
    value: {
      type: [String, Number],
璃白.'s avatar
璃白. 已提交
119
      default: ""
璃白.'s avatar
璃白. 已提交
120
    },
121 122 123 124 125 126
    // 全屏时的z-index
    zIndex: {
      type: [String, Number],
      default: ""
    },
    // input时间节流
127 128
    throttle: {
      type: Number,
129
      default: 0
130
    },
璃白.'s avatar
fix  
璃白. 已提交
131 132 133 134
    setPreview: {
      type: Boolean,
      default: false
    },
璃白.'s avatar
璃白. 已提交
135 136 137 138
    disabled: {
      type: Boolean,
      default: false
    },
璃白.'s avatar
fix  
璃白. 已提交
139 140
    setFullScreen: {
      // type: Boolean,
璃白.'s avatar
fix  
璃白. 已提交
141 142
      default: ""
    },
143
    // 是否可以预览
璃白.'s avatar
璃白. 已提交
144 145 146 147
    canPreview: {
      type: Boolean,
      default: true
    },
148 149 150 151 152 153 154 155 156 157
    // 主题
    themeOptions: {
      type: Object,
      default: () => {}
    },
    focus: {
      type: Boolean,
      default: false
    },
    // 工具栏
璃白.'s avatar
璃白. 已提交
158 159 160
    toolsOptions: {
      type: Object,
      default: () => {}
161
    },
162
    // 行高度
163 164 165 166
    rows: {
      type: [Number, String],
      default: ""
    },
璃白.'s avatar
璃白. 已提交
167 168 169 170 171
    // 高度
    height: {
      type: Number,
      default: 0
    },
172
    // 最大长度
173 174 175 176
    maxLength: {
      type: [Number, String],
      default: ""
    },
177
    // 显示字数限制
178 179 180 181
    showWordLimit: {
      type: Boolean,
      default: false
    },
182 183
    // 图片路径规则
    filePathRule: {
184
      type: RegExp,
185 186 187 188
      default: () => {}
    }
  },
  computed: {
璃白.'s avatar
璃白. 已提交
189 190 191 192
    // isMobile() {
    //   const isMobile = checkBoswer();
    //   return isMobile;
    // },
193 194
    textareaId() {
      return "textarea_" + this.id;
璃白.'s avatar
璃白. 已提交
195
    },
璃白.'s avatar
fix  
璃白. 已提交
196 197 198
    headId() {
      return "md_header" + this.id;
    },
璃白.'s avatar
璃白. 已提交
199 200 201 202
    textareaHeight() {
      const height = this.height;
      if (!height) return 0;
      return height - 83;
璃白.'s avatar
璃白. 已提交
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
    },
    uploadImgCallBack() {
      const _this = this;
      return function({ url, file: { name } }) {
        const originalText = _this.text;
        const selectionInfo = _this.selectionInfo;
        const newText = formatText(
          originalText,
          selectionInfo,
          "\n![img](",
          `${url} '${name}')\n`
        );
        _this.text = newText;
        _this.$refs.mdUploadFile.value = "";
      };
    },
    uploadFileCallBack() {
      const _this = this;
      return function({ url, file: { name } }) {
        // url = "http://www.baidu.com";
        const originalText = _this.text;
        const selectionInfo = _this.selectionInfo;
        const newText = formatText(
          originalText,
          selectionInfo,
          "\n![file](",
          `${url} '${name}')\n`
        );
        _this.text = newText;
        _this.$refs.mdUploadFile.value = "";
      };
璃白.'s avatar
璃白. 已提交
234
    }
G
guoweijia 已提交
235
  },
璃白.'s avatar
璃白. 已提交
236 237 238 239 240
  data() {
    return {
      fullScreen: false,
      isFocus: false,
      showPreview: false,
璃白.'s avatar
璃白. 已提交
241
      uploadType: "img",
璃白.'s avatar
璃白. 已提交
242
      fileList: [],
璃白.'s avatar
璃白. 已提交
243
      filteredTags: [],
璃白.'s avatar
璃白. 已提交
244 245
      text: "",
      html: "",
璃白.'s avatar
fix  
璃白. 已提交
246
      ulNum: 1,
璃白.'s avatar
璃白. 已提交
247
      formatType: "",
璃白.'s avatar
fix  
璃白. 已提交
248
      htmlMinHeight: 150,
璃白.'s avatar
璃白. 已提交
249
      showHelp: false,
250
      textLength: "",
璃白.'s avatar
璃白. 已提交
251
      userList: [],
璃白.'s avatar
璃白. 已提交
252 253 254 255 256 257 258
      selectionInfo: {
        selectorId: "",
        selectionStart: "",
        selectionEnd: ""
      }
    };
  },
259 260 261 262 263 264 265 266
  created() {
    setTimeout(() => {
      this.$emit("load", {
        text: this.text,
        html: this.html
      });
    }, 0);
  },
G
guoweijia 已提交
267
  watch: {
268 269
    focus: {
      handler: function(val) {
璃白.'s avatar
fix  
璃白. 已提交
270 271
        setTimeout(() => {
          const textEl = document.getElementById(this.textareaId);
璃白.'s avatar
璃白. 已提交
272
          if (!textEl) return;
璃白.'s avatar
fix  
璃白. 已提交
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
          if (val) {
            textEl.focus();
          } else {
            textEl.blur();
          }
        });
      }
    },
    showPreview: {
      handler: function(val) {
        this.$emit("changeTab", val);
      }
    },
    setPreview: {
      handler: function(val) {
        this.showPreview = val;
289 290
      }
    },
璃白.'s avatar
fix  
璃白. 已提交
291
    setFullScreen: {
璃白.'s avatar
fix  
璃白. 已提交
292
      handler: function(val) {
璃白.'s avatar
fix  
璃白. 已提交
293 294 295 296 297 298
        this.fullScreen = val;
      }
    },
    fullScreen: {
      handler: function(val) {
        this.$emit("changeFullScreen", val);
璃白.'s avatar
fix  
璃白. 已提交
299 300
      }
    },
301 302 303 304 305 306
    text: {
      immediate: true,
      handler: function(val) {
        this.textLength = val.length;
      }
    },
G
guoweijia 已提交
307
    html: {
璃白.'s avatar
fix  
璃白. 已提交
308
      immediate: false,
G
guoweijia 已提交
309
      handler: function(val) {
310
        const emitContent = {
311 312
          text: this.text,
          html: this.html
313 314 315 316 317
        };
        if (this.filePathRule) {
          const checkResult = checktUrl(val, this.filePathRule);
          emitContent.invalidList = checkResult;
        }
璃白.'s avatar
璃白. 已提交
318
        emitContent.filteredTags = this.filteredTags;
319 320
        this.$emit("change", emitContent);
        this.$emit("input", emitContent);
G
guoweijia 已提交
321 322
      }
    },
璃白.'s avatar
璃白. 已提交
323 324 325 326 327 328 329
    isFocus: {
      handler: function(val) {
        const value = {
          text: this.text,
          html: this.html
        };
        if (val) {
璃白.'s avatar
璃白. 已提交
330
          this.showHelp = false;
璃白.'s avatar
璃白. 已提交
331 332 333 334 335 336
          this.$emit("focus", value);
        } else {
          this.$emit("blur", value);
        }
      }
    },
璃白.'s avatar
璃白. 已提交
337 338 339 340 341 342
    value: {
      immediate: true,
      handler: function(val) {
        this.text = val;
      }
    },
G
guoweijia 已提交
343 344 345 346
    fileList: {
      immediate: false,
      deep: true,
      handler: function(val) {
璃白.'s avatar
璃白. 已提交
347
        const uploadType = this.uploadType;
璃白.'s avatar
璃白. 已提交
348 349 350
        if (!val.length) return;
        this.$emit("upload", {
          val: val[0],
璃白.'s avatar
璃白. 已提交
351 352 353 354
          callback:
            uploadType === "img"
              ? this.uploadImgCallBack
              : this.uploadFileCallBack
璃白.'s avatar
璃白. 已提交
355 356
        });
        this.fileList = [];
G
guoweijia 已提交
357 358
      }
    }
璃白.'s avatar
璃白. 已提交
359 360
  },
  methods: {
璃白.'s avatar
璃白. 已提交
361 362
    handleUpload(type) {
      this.uploadType = type;
璃白.'s avatar
璃白. 已提交
363 364 365
      this.$refs.mdUploadFile.click();
      document.getElementById(this.textareaId).focus();
    },
璃白.'s avatar
fix  
璃白. 已提交
366 367 368
    handleEnter() {
      this.$refs[this.headId].resetUlNum();
    },
369 370 371
    upload(e) {
      this.fileList = Array.from(e.target.files);
    },
璃白.'s avatar
璃白. 已提交
372 373 374 375 376
    submit() {
      this.$emit("submit", {
        text: this.text,
        html: this.html
      });
璃白.'s avatar
璃白. 已提交
377
    },
璃白.'s avatar
璃白. 已提交
378 379 380 381 382 383 384 385 386
    queryUserList(keyWord) {
      const _this = this;
      this.$emit("queryUserList", {
        keyWord,
        callback: function(list) {
          _this.userList = list;
        }
      });
    },
璃白.'s avatar
璃白. 已提交
387 388 389 390 391 392 393 394 395 396 397 398
    renderLinksHtml({ vDom, links }) {
      new Promise((resolve, reject) => {
        this.$emit("renderLinks", {
          links,
          callback: function(list) {
            resolve(list);
          }
        });
      }).then(res => {
        console.log("返回的列表", res);
        res.forEach(item => {
          const linkEl = vDom.querySelector("#" + item.id);
璃白.'s avatar
璃白. 已提交
399 400 401
          const url = item.csdn
            ? "https://link.csdn.net/?target=" + item.url
            : item.url;
璃白.'s avatar
璃白. 已提交
402 403
          linkEl.className = "md_link_card";
          linkEl.setAttribute("target", "_blank");
璃白.'s avatar
璃白. 已提交
404
          linkEl.setAttribute("href", url);
璃白.'s avatar
璃白. 已提交
405
          const title = getLinkTitle(linkEl);
璃白.'s avatar
璃白. 已提交
406
          linkEl.innerHTML = `
璃白.'s avatar
璃白. 已提交
407
          <span class="md_link_title">${title || item.title}</span>
璃白.'s avatar
璃白. 已提交
408
              <span class="md_link_desc">${item.desc}</span>
璃白.'s avatar
璃白. 已提交
409 410 411 412 413 414
          <span class="md_flex_card">
            <img class="md_link_img" src="${item.img}" />
            <span class="flex-1">
              <span class="md_link_url">${item.url}</span>
            </span>
          </span>
璃白.'s avatar
璃白. 已提交
415 416 417 418 419
          `;
        });
        // return vDom.innerHTML;
        this.html = vDom.innerHTML;
      });
璃白.'s avatar
璃白. 已提交
420
    }
璃白.'s avatar
璃白. 已提交
421 422 423 424 425
  }
};
</script>
<style lang="less" scoped>
.md_container {
璃白.'s avatar
璃白. 已提交
426
  background: var(--md-editor-frame-bg-color);
璃白.'s avatar
璃白. 已提交
427
  border: 1px solid var(--md-editor-border-color);
璃白.'s avatar
璃白. 已提交
428 429 430
  border-radius: 4px;
  padding: 10px 16px;
  box-sizing: border-box;
璃白.'s avatar
璃白. 已提交
431
  transition: border 0.3s;
432
  position: relative;
璃白.'s avatar
璃白. 已提交
433
  // overflow: hidden; // 注释为了显示@用户的弹窗
434 435 436 437 438 439 440
  &.fullScreen {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    margin: 0;
璃白.'s avatar
璃白. 已提交
441
    border: 1px solid transparent !important;
442 443
    z-index: var(--md-editor-fullScrren-zIndex);
  }
璃白.'s avatar
璃白. 已提交
444
  &.active {
璃白.'s avatar
璃白. 已提交
445
    border: 1px solid var(--md-editor-border-color-active);
璃白.'s avatar
璃白. 已提交
446
  }
璃白.'s avatar
璃白. 已提交
447 448 449
  &.disabled {
    background: var(--md-editor-frame-bg-color-disabled);
  }
450 451 452 453 454 455 456
  .word_limit {
    position: absolute;
    right: 10px;
    bottom: 8px;
    font-size: 12px;
    color: var(--md-editor-text-color);
  }
璃白.'s avatar
璃白. 已提交
457 458
}
</style>