|
@@ -101,6 +101,7 @@
|
|
|
@keyup="updateToolbar"
|
|
@keyup="updateToolbar"
|
|
|
@keydown="handleKeydown"
|
|
@keydown="handleKeydown"
|
|
|
@click="handleEditorClick"
|
|
@click="handleEditorClick"
|
|
|
|
|
+ @paste="handlePaste"
|
|
|
></div>
|
|
></div>
|
|
|
|
|
|
|
|
<!-- @mention 下拉菜单 -->
|
|
<!-- @mention 下拉菜单 -->
|
|
@@ -1029,6 +1030,161 @@ function handleKeydown(e) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function handlePaste(e) {
|
|
|
|
|
+ e.preventDefault();
|
|
|
|
|
+
|
|
|
|
|
+ const clipboardData = e.clipboardData || window.clipboardData;
|
|
|
|
|
+ if (!clipboardData) return;
|
|
|
|
|
+
|
|
|
|
|
+ const html = clipboardData.getData('text/html');
|
|
|
|
|
+ const text = clipboardData.getData('text/plain');
|
|
|
|
|
+
|
|
|
|
|
+ if (html) {
|
|
|
|
|
+ handlePasteHtml(html);
|
|
|
|
|
+ } else if (text) {
|
|
|
|
|
+ document.execCommand('insertText', false, text);
|
|
|
|
|
+ immediateSave();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function handlePasteHtml(html) {
|
|
|
|
|
+ const tempDiv = document.createElement('div');
|
|
|
|
|
+ tempDiv.innerHTML = html;
|
|
|
|
|
+
|
|
|
|
|
+ const mentions = tempDiv.querySelectorAll('[data-mention][data-uid]');
|
|
|
|
|
+
|
|
|
|
|
+ if (mentions.length === 0) {
|
|
|
|
|
+ // 没有指标,直接粘贴
|
|
|
|
|
+ document.execCommand('insertHTML', false, html);
|
|
|
|
|
+ immediateSave();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 有指标,需要处理
|
|
|
|
|
+ const selection = window.getSelection();
|
|
|
|
|
+ if (!selection.rangeCount) return;
|
|
|
|
|
+
|
|
|
|
|
+ const range = selection.getRangeAt(0);
|
|
|
|
|
+ const fragment = document.createDocumentFragment();
|
|
|
|
|
+
|
|
|
|
|
+ // 存储需要复制的配置信息
|
|
|
|
|
+ const configToCopy = [];
|
|
|
|
|
+
|
|
|
|
|
+ // 处理tempDiv中的所有节点
|
|
|
|
|
+ processNodes(tempDiv.childNodes, fragment, configToCopy);
|
|
|
|
|
+
|
|
|
|
|
+ // 插入处理后的内容
|
|
|
|
|
+ range.deleteContents();
|
|
|
|
|
+ range.insertNode(fragment);
|
|
|
|
|
+
|
|
|
|
|
+ // 移动光标到插入内容的末尾
|
|
|
|
|
+ const newRange = document.createRange();
|
|
|
|
|
+ if (fragment.lastChild) {
|
|
|
|
|
+ newRange.setStartAfter(fragment.lastChild);
|
|
|
|
|
+ } else if (fragment.firstChild) {
|
|
|
|
|
+ newRange.setStart(fragment.firstChild, 0);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 如果fragment为空,将光标设置到编辑器末尾
|
|
|
|
|
+ const editor = editorRef.value;
|
|
|
|
|
+ if (editor.lastChild) {
|
|
|
|
|
+ newRange.setStartAfter(editor.lastChild);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ newRange.setStart(editor, 0);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ newRange.collapse(true);
|
|
|
|
|
+ selection.removeAllRanges();
|
|
|
|
|
+ selection.addRange(newRange);
|
|
|
|
|
+
|
|
|
|
|
+ // 延迟复制配置信息,确保DOM已经更新
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ configToCopy.forEach(({ oldUid, newUid }) => {
|
|
|
|
|
+ reportEditor.copyMetricConfigByUid(oldUid, newUid);
|
|
|
|
|
+ });
|
|
|
|
|
+ immediateSave();
|
|
|
|
|
+ syncMentionsData();
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function processNodes(nodes, fragment, configToCopy = []) {
|
|
|
|
|
+ for (let i = 0; i < nodes.length; i++) {
|
|
|
|
|
+ const node = nodes[i];
|
|
|
|
|
+ if (!node) continue;
|
|
|
|
|
+
|
|
|
|
|
+ if (node.nodeType === Node.ELEMENT_NODE) {
|
|
|
|
|
+ if (node.dataset.mention === 'true') {
|
|
|
|
|
+ // 处理指标
|
|
|
|
|
+ const oldUid = node.getAttribute('data-uid');
|
|
|
|
|
+ if (oldUid) {
|
|
|
|
|
+ const oldMentionData = mentionsData.value.find((it) => it && it.uid === oldUid);
|
|
|
|
|
+ if (oldMentionData?.option) {
|
|
|
|
|
+ // 复制指标
|
|
|
|
|
+ const clonedOpt = JSON.parse(JSON.stringify(oldMentionData.option));
|
|
|
|
|
+ const newUid = generateUID();
|
|
|
|
|
+
|
|
|
|
|
+ // 创建新的指标元素
|
|
|
|
|
+ const newMention = document.createElement('span');
|
|
|
|
|
+ newMention.setAttribute('contenteditable', 'false');
|
|
|
|
|
+ newMention.setAttribute('data-mention', 'true');
|
|
|
|
|
+ newMention.setAttribute('data-uid', newUid);
|
|
|
|
|
+ newMention.style.cssText = `
|
|
|
|
|
+ display: inline-flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ background-color: var(--question_bg);
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ padding: 0 4px;
|
|
|
|
|
+ margin: 0 2px;
|
|
|
|
|
+ user-select: none;
|
|
|
|
|
+ -webkit-user-select: none;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ color: var(--primary-default);
|
|
|
|
|
+ `;
|
|
|
|
|
+ const svg_icon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
|
|
|
+ fill="none" version="1.1" width="16" height="16" viewBox="0 0 16 16"><defs><clipPath id="master_svg0_2_674"><rect x="0" y="0"
|
|
|
|
|
+ width="16" height="26" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_2_674)"><g>
|
|
|
|
|
+ <path d="M12.30009745625,5.94999981C12.15009785625,5.799999714,11.95009705625,5.75,11.75009725625,5.75L4.25009751625
|
|
|
|
|
+ ,5.800000191C4.05009770625,5.800000191,3.85009741625,5.85000038,3.70009755625,6C3.40009760825,6.30000019,3.40009760825,6.75,3.70009755625,
|
|
|
|
|
+ 7.0500001999999995L7.45009735625,10.8000002C7.50009725625,10.850000399999999,7.60009765625,10.9000006,7.65009685625,10.9499998C7.65009685625,
|
|
|
|
|
+ 10.9499998,7.70009705625,11,7.70009705625,11C7.95009705625,11.100000399999999,8.30009745625,11.0500002,8.500097256250001,10.850000399999999L12.25009725625,
|
|
|
|
|
+ 7.0500001999999995C12.60009765625,6.69999981,12.55009745625,6.25,12.30009745625,5.94999981Z" fill="var(--primary-default)" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>`;
|
|
|
|
|
+ newMention.innerHTML = `{{${clonedOpt.field_name}}} ${svg_icon}`;
|
|
|
|
|
+
|
|
|
|
|
+ // 添加到fragment
|
|
|
|
|
+ fragment.appendChild(newMention);
|
|
|
|
|
+ fragment.appendChild(document.createTextNode(' '));
|
|
|
|
|
+
|
|
|
|
|
+ // 添加到mentionsData
|
|
|
|
|
+ mentionsData.value.push({
|
|
|
|
|
+ uid: newUid,
|
|
|
|
|
+ option: clonedOpt
|
|
|
|
|
+ });
|
|
|
|
|
+ console.log('复制新元素');
|
|
|
|
|
+ // 存储需要复制的配置信息
|
|
|
|
|
+ configToCopy.push({ oldUid, newUid });
|
|
|
|
|
+ console.log('配置信息完成');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 处理普通元素
|
|
|
|
|
+ const newElement = document.createElement(node.tagName);
|
|
|
|
|
+ // 复制属性
|
|
|
|
|
+ for (let j = 0; j < node.attributes.length; j++) {
|
|
|
|
|
+ const attr = node.attributes[j];
|
|
|
|
|
+ newElement.setAttribute(attr.name, attr.value);
|
|
|
|
|
+ }
|
|
|
|
|
+ // 递归处理子节点
|
|
|
|
|
+ for (let j = 0; j < node.childNodes.length; j++) {
|
|
|
|
|
+ processNodes([node.childNodes[j]], newElement, configToCopy);
|
|
|
|
|
+ }
|
|
|
|
|
+ fragment.appendChild(newElement);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (node.nodeType === Node.TEXT_NODE) {
|
|
|
|
|
+ // 处理文本节点
|
|
|
|
|
+ fragment.appendChild(document.createTextNode(node.textContent));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function hideDropdown() {
|
|
function hideDropdown() {
|
|
|
showDropdown.value = false;
|
|
showDropdown.value = false;
|
|
|
}
|
|
}
|