Selaa lähdekoodia

feat:新增富文本中段落复制同时复制内部指标,修改数据源配置中表格宽度,修改富文本中指标配置下拉菜单之间的间距

韩洋 4 kuukautta sitten
vanhempi
sitoutus
c127c23d00

BIN
src.zip


+ 2 - 1
src/base/store/use-report-editor.js

@@ -201,6 +201,7 @@ export const useReportEditor = defineStore('report-editor', {
     changeMetricConfig(config) {
       this.metrics_list.forEach(item => {
         if (item.index === this.target_metric_id) {
+          console.log('item.metric_config', item.metric_config)
           item.metric_config[config.name] = config.value
           // 当聚合方式发生变化时,同时更新对应的 aggs_config 中的 aggs_type
           if (config.name === 'aggregation_type' && item.aggs_config) {
@@ -241,7 +242,7 @@ export const useReportEditor = defineStore('report-editor', {
             item.aggs_config.group_by_field_key.push(dimension_item.field_key)
           } else {
             item.aggs_config = {
-              aggs_type: 'sum',
+              aggs_type: item.metric_config.aggregation_type || '',
               aggs_field_key: item.option.field_key,
               group_by_field_key: [dimension_item.field_key],
             }

+ 2 - 1
src/modules/data-source-manage/add-source/components/ApiFieldConfig.vue

@@ -240,7 +240,7 @@ const deleteField = (index) => {
 .field-table {
   width: 100%;
   border-collapse: collapse;
-  min-width: 1200px;
+  min-width: 1800px;
 }
 
 .field-table thead th {
@@ -251,6 +251,7 @@ const deleteField = (index) => {
 
 .field-table th,
 .field-table td {
+  min-width: 180px;
   padding: 8px;
   border: 1px solid #e8e8e8;
   text-align: left;

+ 1 - 0
src/modules/data-source-manage/add-source/components/DbFieldConfig.vue

@@ -228,6 +228,7 @@ const deleteField = (index) => {
 
 .field-table th,
 .field-table td {
+  min-width: 180px;
   padding: 8px;
   border: 1px solid #e8e8e8;
   text-align: left;

+ 156 - 0
src/modules/report-template/editor-model/EditorDetail.vue

@@ -101,6 +101,7 @@
       @keyup="updateToolbar"
       @keydown="handleKeydown"
       @click="handleEditorClick"
+      @paste="handlePaste"
     ></div>
 
     <!-- @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}}}&nbsp;&nbsp;${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() {
   showDropdown.value = false;
 }

+ 8 - 5
src/modules/report-template/editor-select/MentionActionMenu.vue

@@ -533,10 +533,7 @@ watch(
       const children = actionMenuItems.value.yoy_or_mom_config.children;
       const hasNone = children.some((it) => it && it.value === '');
       if (!hasNone) {
-        actionMenuItems.value.yoy_or_mom_config.children = [
-          { name: '无', value: '', is_show: 1 },
-          ...children
-        ];
+        actionMenuItems.value.yoy_or_mom_config.children = [{ name: '无', value: '', is_show: 1 }, ...children];
       }
     }
 
@@ -663,7 +660,7 @@ watch(
 .submenu {
   position: absolute;
   top: 0;
-  left: calc(100% + 6px);
+  left: calc(100% + 3px);
   margin-left: -1px;
   background: white;
   border: 1px solid #ccc;
@@ -673,12 +670,18 @@ watch(
   padding: 8px 2px;
   z-index: 1002;
   box-sizing: border-box;
+  /* 增加菜单之间的重叠区域,避免划到空隙时菜单关闭 */
+  margin-right: -5px;
+  padding-right: 7px;
 }
 
 /* 向上显示的二级子菜单 */
 .submenu.submenu-up {
   top: auto;
   bottom: 0;
+  /* 增加菜单之间的重叠区域,避免划到空隙时菜单关闭 */
+  margin-right: -5px;
+  padding-right: 7px;
 }
 
 .submenu-item {