123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- <script>
- import { toEmojiName } from "utils";
- const exec = (val, command = "insertHTML") => {
- document.execCommand(command, false, val);
- };
- const selection = window.getSelection();
- let lastSelectionRange;
- let emojiData = [];
- export default {
- name: "LemonEditor",
- components: {},
- props: {},
- data() {
- return {
- submitDisabled: true,
- accept: ""
- };
- },
- created() {},
- mounted() {
- //this.$refs.fileInput.addEventListener("change", this._handleChangeFile);
- },
- computed: {},
- watch: {},
- render() {
- //<a-popover trigger="click" overlay-class-name="lemon-editor__emoji">
- return (
- <div class="lemon-editor">
- <input
- style="display:none"
- type="file"
- multiple="multiple"
- ref="fileInput"
- accept={this.accept}
- onChange={this._handleChangeFile}
- />
- <div class="lemon-editor__tool">
- {emojiData.length > 0 && (
- <lemon-popover class="lemon-editor__emoji">
- <template slot="content">{this._renderEmojiTabs()}</template>
- <div class="lemon-editor__tool-item">
- <i class="lemon-icon-emoji" />
- </div>
- </lemon-popover>
- )}
- <div
- class="lemon-editor__tool-item"
- on-click={() => this._handleSelectFile("*")}
- >
- <i class="lemon-icon-folder" />
- </div>
- <div
- class="lemon-editor__tool-item"
- on-click={() => this._handleSelectFile("image/*")}
- >
- <i class="lemon-icon-image" />
- </div>
- </div>
- <div class="lemon-editor__inner">
- <div
- class="lemon-editor__input"
- ref="textarea"
- contenteditable="true"
- on-keyup={this._handleKeyup}
- on-keydown={this._handleKeydown}
- on-paste={this._handlePaste}
- on-click={this._handleClick}
- on-input={this._handleInput}
- spellcheck="false"
- />
- </div>
- <div class="lemon-editor__footer">
- <div class="lemon-editor__tip">使用 ctrl + enter 快捷发送消息</div>
- <div class="lemon-editor__submit">
- <lemon-button
- disabled={this.submitDisabled}
- on-click={this._handleSend}
- >
- 发 送
- </lemon-button>
- </div>
- </div>
- </div>
- );
- },
- methods: {
- _saveLastRange() {
- lastSelectionRange = selection.getRangeAt(0);
- },
- _focusLastRange() {
- this.$refs.textarea.focus();
- if (lastSelectionRange) {
- selection.removeAllRanges();
- selection.addRange(lastSelectionRange);
- }
- },
- _handleClick() {
- this._saveLastRange();
- },
- _handleInput() {
- this._checkSubmitDisabled();
- },
- _renderEmojiTabs() {
- const renderImageGrid = items => {
- return items.map(item => (
- <img
- src={item.src}
- title={item.title}
- class="lemon-editor__emoji-item"
- on-click={() => this._handleSelectEmoji(item)}
- />
- ));
- };
- if (emojiData[0].label) {
- const nodes = emojiData.map((item, index) => {
- return (
- <div slot="tab-pane" index={index} tab={item.label}>
- {renderImageGrid(item.children)}
- </div>
- );
- });
- return <lemon-tabs style="width: 412px">{nodes}</lemon-tabs>;
- } else {
- return (
- <div class="lemon-tabs-content" style="width:406px">
- {renderImageGrid(emojiData)}
- </div>
- );
- }
- },
- _handleSelectEmoji(item) {
- this._focusLastRange();
- exec(`<img emoji-name="${item.name}" src="${item.src}"></img>`);
- this._saveLastRange();
- },
- async _handleSelectFile(accept) {
- this.accept = accept;
- await this.$nextTick();
- this.$refs.fileInput.click();
- },
- _handlePaste(e) {
- e.preventDefault();
- const { clipboardData } = e;
- const text = clipboardData.getData("text");
- exec(text, "insertText");
- // Array.from(clipboardData.items).forEach(item => {
- // console.log(item.type);
- // });
- //e.target.innerText = text;
- },
- _handleKeyup(e) {
- this._saveLastRange();
- //this._checkSubmitDisabled();
- },
- _handleKeydown(e) {
- const { keyCode } = e;
- if (keyCode == 13) {
- // e.preventDefault();
- // document.execCommand("defaultParagraphSeparator", false, false);
- // exec("<br>");
- }
- },
- getFormatValue() {
- return toEmojiName(
- this.$refs.textarea.innerHTML
- .replace(/<br>|<\/br>/, "")
- .replace(/<div>|<p>/g, "\r\n")
- .replace(/<\/div>|<\/p>/g, "")
- );
- },
- _checkSubmitDisabled() {
- this.submitDisabled = !this.$refs.textarea.innerHTML.trim();
- },
- _handleSend(e) {
- const text = this.getFormatValue();
- this.$emit("send", text);
- this.clear();
- this._checkSubmitDisabled();
- },
- _handleChangeFile(e) {
- const { fileInput } = this.$refs;
- Array.from(fileInput.files).forEach(file => {
- this.$emit("upload", file);
- });
- fileInput.value = "";
- },
- clear() {
- this.$refs.textarea.innerHTML = "";
- },
- initEmoji(data) {
- emojiData = data;
- this.$forceUpdate();
- }
- }
- };
- </script>
- <style lang="stylus">
- @import '~styles/utils/index'
- gap = 10px;
- +b(lemon-editor)
- height 200px
- flex-column()
- +e(tool)
- display flex
- height 40px
- align-items center
- padding-left 5px
- +e(tool-item)
- cursor pointer
- padding 4px gap
- height 28px
- color #999
- transition all ease .3s
- [class^='lemon-icon-']
- line-height 26px
- font-size 22px
- &:hover
- color #333
- +e(inner)
- flex 1
- overflow-x hidden
- overflow-y auto
- scrollbar-light()
- +e(input)
- height 100%
- box-sizing border-box
- border none
- outline none
- padding 0 gap
- scrollbar-light()
- p,div
- margin 0
- img
- height 20px
- padding 0 2px
- pointer-events none
- vertical-align middle
- +e(footer)
- display flex
- height 52px
- justify-content flex-end
- padding 0 gap
- align-items center
- +e(tip)
- margin-right 10px
- font-size 12px
- color #999
- user-select none
- +e(emoji)
- user-select none
- .lemon-popover
- background #f6f6f6
- .lemon-popover__content
- padding 0
- .lemon-popover__arrow
- background #f6f6f6
- .lemon-tabs-content
- box-sizing border-box
- padding 8px
- height 200px
- overflow-x hidden
- overflow-y auto
- scrollbar-light()
- margin-bottom 8px
- +e(emoji-item)
- cursor pointer
- width 22px
- padding 4px
- border-radius 4px
- &:hover
- background #e9e9e9
- </style>
|