|
@@ -1,15 +1,24 @@
|
|
<template>
|
|
<template>
|
|
- <Editor
|
|
|
|
- v-bind="$attrs"
|
|
|
|
- :id="id"
|
|
|
|
- ref="richText"
|
|
|
|
- model-events="change keyup undo redo setContent"
|
|
|
|
- :value="value"
|
|
|
|
- :class="['rich-text', isBorder ? 'is-border' : '']"
|
|
|
|
- :init="init"
|
|
|
|
- v-on="$listeners"
|
|
|
|
- @onBlur="handleRichTextBlur"
|
|
|
|
- />
|
|
|
|
|
|
+ <div class="rich-wrapper">
|
|
|
|
+ <Editor
|
|
|
|
+ v-bind="$attrs"
|
|
|
|
+ :id="id"
|
|
|
|
+ ref="richText"
|
|
|
|
+ model-events="change keyup undo redo setContent"
|
|
|
|
+ :value="value"
|
|
|
|
+ :class="['rich-text', isBorder ? 'is-border' : '']"
|
|
|
|
+ :init="init"
|
|
|
|
+ v-on="$listeners"
|
|
|
|
+ @onBlur="handleRichTextBlur"
|
|
|
|
+ />
|
|
|
|
+ <div v-show="isShow" :style="contentmenu" class="contentmenu">
|
|
|
|
+ <SvgIcon icon-class="slice" size="16" @click="setFill" />
|
|
|
|
+ <span class="button" @click="setFill">设为填空</span>
|
|
|
|
+ <span class="line"></span>
|
|
|
|
+ <SvgIcon icon-class="close-circle" size="16" @click="deleteFill" />
|
|
|
|
+ <span class="button" @click="deleteFill">删除填空</span>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
</template>
|
|
</template>
|
|
|
|
|
|
<script>
|
|
<script>
|
|
@@ -89,6 +98,11 @@ export default {
|
|
},
|
|
},
|
|
data() {
|
|
data() {
|
|
return {
|
|
return {
|
|
|
|
+ isShow: false,
|
|
|
|
+ contentmenu: {
|
|
|
|
+ top: 0,
|
|
|
|
+ left: 0,
|
|
|
|
+ },
|
|
id: getRandomNumber(),
|
|
id: getRandomNumber(),
|
|
init: {
|
|
init: {
|
|
inline: this.inline,
|
|
inline: this.inline,
|
|
@@ -134,6 +148,16 @@ export default {
|
|
},
|
|
},
|
|
};
|
|
};
|
|
},
|
|
},
|
|
|
|
+ created() {
|
|
|
|
+ if (this.isFill) {
|
|
|
|
+ window.addEventListener('click', this.hideContentmenu);
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ beforeDestroy() {
|
|
|
|
+ if (this.isFill) {
|
|
|
|
+ window.removeEventListener('click', this.hideContentmenu);
|
|
|
|
+ }
|
|
|
|
+ },
|
|
methods: {
|
|
methods: {
|
|
/**
|
|
/**
|
|
* 图片上传自定义逻辑函数
|
|
* 图片上传自定义逻辑函数
|
|
@@ -194,14 +218,14 @@ export default {
|
|
*/
|
|
*/
|
|
initInstanceCallback(editor) {
|
|
initInstanceCallback(editor) {
|
|
editor.on('SetContent Undo Redo ViewUpdate keyup mousedown', () => {
|
|
editor.on('SetContent Undo Redo ViewUpdate keyup mousedown', () => {
|
|
- this.$emit('hideContentmenu');
|
|
|
|
|
|
+ this.hideContentmenu();
|
|
});
|
|
});
|
|
|
|
|
|
editor.on('click', (e) => {
|
|
editor.on('click', (e) => {
|
|
if (e.target.classList.contains('rich-fill')) {
|
|
if (e.target.classList.contains('rich-fill')) {
|
|
editor.selection.select(e.target); // 选中填空
|
|
editor.selection.select(e.target); // 选中填空
|
|
let { offsetLeft, offsetTop } = e.target;
|
|
let { offsetLeft, offsetTop } = e.target;
|
|
- this.$emit('showContentmenu', {
|
|
|
|
|
|
+ this.showContentmenu({
|
|
pixelsFromLeft: offsetLeft - 14,
|
|
pixelsFromLeft: offsetLeft - 14,
|
|
pixelsFromTop: offsetTop,
|
|
pixelsFromTop: offsetTop,
|
|
});
|
|
});
|
|
@@ -218,7 +242,7 @@ export default {
|
|
let end = editor.selection.getEnd();
|
|
let end = editor.selection.getEnd();
|
|
let rng = editor.selection.getRng();
|
|
let rng = editor.selection.getRng();
|
|
if (start !== end || rng.collapsed) {
|
|
if (start !== end || rng.collapsed) {
|
|
- this.$emit('hideContentmenu');
|
|
|
|
|
|
+ this.hideContentmenu();
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
if (e.offsetX < mouseX) {
|
|
if (e.offsetX < mouseX) {
|
|
@@ -252,7 +276,7 @@ export default {
|
|
const fontSize = parseFloat(computedStyle.fontSize);
|
|
const fontSize = parseFloat(computedStyle.fontSize);
|
|
const canvas = document.createElement('canvas');
|
|
const canvas = document.createElement('canvas');
|
|
const context = canvas.getContext('2d');
|
|
const context = canvas.getContext('2d');
|
|
- context.font = `${fontSize}px ${computedStyle.fontFamily}`;
|
|
|
|
|
|
+ context.font = `${fontSize}pt ${computedStyle.fontFamily}`;
|
|
// 计算文字距离左侧的像素位置
|
|
// 计算文字距离左侧的像素位置
|
|
const width = context.measureText(textBeforeOffset).width;
|
|
const width = context.measureText(textBeforeOffset).width;
|
|
const lineHeight = context.measureText('M').fontBoundingBoxAscent + 3.8; // 获取行高
|
|
const lineHeight = context.measureText('M').fontBoundingBoxAscent + 3.8; // 获取行高
|
|
@@ -263,7 +287,7 @@ export default {
|
|
row = row % 1 > 0.8 ? Math.ceil(row) : Math.floor(row);
|
|
row = row % 1 > 0.8 ? Math.ceil(row) : Math.floor(row);
|
|
const offsetTop = start.offsetTop; // 获取选中文本距离顶部的像素位置
|
|
const offsetTop = start.offsetTop; // 获取选中文本距离顶部的像素位置
|
|
let pixelsFromTop = offsetTop + lineHeight * row; // 计算选中文本距离顶部的像素位置
|
|
let pixelsFromTop = offsetTop + lineHeight * row; // 计算选中文本距离顶部的像素位置
|
|
- this.$emit('showContentmenu', {
|
|
|
|
|
|
+ this.showContentmenu({
|
|
pixelsFromLeft: mouseX,
|
|
pixelsFromLeft: mouseX,
|
|
pixelsFromTop,
|
|
pixelsFromTop,
|
|
});
|
|
});
|
|
@@ -303,7 +327,7 @@ export default {
|
|
let editor = tinymce.get(this.id);
|
|
let editor = tinymce.get(this.id);
|
|
let rng = editor.selection.getRng();
|
|
let rng = editor.selection.getRng();
|
|
if (!rng.collapsed) {
|
|
if (!rng.collapsed) {
|
|
- this.$emit('hideContentmenu');
|
|
|
|
|
|
+ this.hideContentmenu();
|
|
editor.selection.collapse();
|
|
editor.selection.collapse();
|
|
}
|
|
}
|
|
},
|
|
},
|
|
@@ -318,6 +342,26 @@ export default {
|
|
handleRichTextBlur() {
|
|
handleRichTextBlur() {
|
|
this.$emit('handleRichTextBlur');
|
|
this.$emit('handleRichTextBlur');
|
|
},
|
|
},
|
|
|
|
+ // 设置填空
|
|
|
|
+ setFill() {
|
|
|
|
+ this.setContent();
|
|
|
|
+ this.hideContentmenu();
|
|
|
|
+ },
|
|
|
|
+ // 删除填空
|
|
|
|
+ deleteFill() {
|
|
|
|
+ this.deleteContent();
|
|
|
|
+ this.hideContentmenu();
|
|
|
|
+ },
|
|
|
|
+ hideContentmenu() {
|
|
|
|
+ this.isShow = false;
|
|
|
|
+ },
|
|
|
|
+ showContentmenu({ pixelsFromLeft, pixelsFromTop }) {
|
|
|
|
+ this.isShow = true;
|
|
|
|
+ this.contentmenu = {
|
|
|
|
+ left: `${pixelsFromLeft + 14}px`,
|
|
|
|
+ top: `${pixelsFromTop - 18}px`,
|
|
|
|
+ };
|
|
|
|
+ },
|
|
},
|
|
},
|
|
};
|
|
};
|
|
</script>
|
|
</script>
|
|
@@ -355,4 +399,27 @@ export default {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+.contentmenu {
|
|
|
|
+ position: absolute;
|
|
|
|
+ z-index: 999;
|
|
|
|
+ display: flex;
|
|
|
|
+ column-gap: 4px;
|
|
|
|
+ align-items: center;
|
|
|
|
+ padding: 4px 8px;
|
|
|
|
+ font-size: 14px;
|
|
|
|
+ background-color: #fff;
|
|
|
|
+ border-radius: 2px;
|
|
|
|
+ box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 10%);
|
|
|
|
+
|
|
|
|
+ .svg-icon,
|
|
|
|
+ .button {
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .line {
|
|
|
|
+ min-height: 16px;
|
|
|
|
+ margin: 0 4px;
|
|
|
|
+ }
|
|
|
|
+}
|
|
</style>
|
|
</style>
|