Browse Source

Merge branch 'ETA_1.9.9'

hbchen 9 months ago
parent
commit
ba09f1f8c1

+ 16 - 0
src/lang/modules/ETATables/commonLang.js

@@ -52,6 +52,18 @@ export default {
       en: "Renewing",
       zh: "更新中...",
     },
+    merge_cell:{
+      en: "Merge Cells",
+      zh: "合并单元格",
+    },
+    unmerge_cell:{
+      en: "Unmerge Cells",
+      zh: "取消合并单元格",
+    },
+    unmerge:{
+      en: "Unmerge",
+      zh: "取消合并",
+    }
   },
   Msg: {
     is_del_table_msg: {
@@ -86,6 +98,10 @@ export default {
       en:'After deletion, this chart will no longer be referenced. Are you sure to delete it?',
       zh:'删除后该图表将不能再引用,确认删除吗?',
     },
+    merge_cell_fail_msg:{
+      en:'Select cells to merge, ensuring that at most one value or relationship exists within the selected cells.',
+      zh:'选择合并的单元格中最多存在一个值或关联关系',
+    },
   },
   Date: {
     monday: {

+ 70 - 32
src/views/datasheet_manage/common/customTable.js

@@ -70,32 +70,51 @@ export function splitString(str) {
 }
 
 //选中整行整列样式
-export function selectMoreCellStyle(e,type=null) {
+export function selectMoreCellStyle(e) {
   let table = document.querySelector('.table');
   resetStyle();
-
+  let tableRect = table.getBoundingClientRect()
   if($(e.target).hasClass('th-col')) { //点击列
-    // 指标列 日期列显示显示有所区分
-    const columnIndex = Array.from(e.target.parentNode.children).indexOf(e.target);
-    table.rows.forEach((cell,index)=> {
-
-      if(index !==0) {
-        if(type === 1) index === 2 ? $(cell.children[columnIndex-1]).addClass('td-col-select') : $(cell.children[columnIndex]).addClass('td-col-select');
+    let eRect = e.target.getBoundingClientRect()
+    // console.log(eRect,tableRect);
+    $(table.querySelector('#row-col-select')).css({
+      'top':`${eRect.height}px`,
+      'left':`${eRect.x - tableRect.x}px`,
+      'visibility':`visible`,
+      'height':`${tableRect.height-eRect.height-1}px`,
+      'width':`${eRect.width}px`,
+      'border-width':'0 1px 0 1px'})
+    // if(!type){
+    //   $(e.target).addClass('td-col-select-new');
+    // }
 
-        if(type === 2) index === 1 ? $(cell.children[columnIndex-1]).addClass('td-col-select') : $(cell.children[columnIndex]).addClass('td-col-select');
-
-        if(!type) $(cell.children[columnIndex]).addClass('td-col-select');
-      }
-    })
+    // 指标列 日期列显示显示有所区分
+    // const columnIndex = Array.from(e.target.parentNode.children).indexOf(e.target);
+    // table.rows.forEach((cell,index)=> {
+    //   if(index !==0) {
+    //     if(type === 1) index === 2 ? $(cell.children[columnIndex-1]).addClass('td-col-select') : $(cell.children[columnIndex]).addClass('td-col-select');
+
+    //     if(type === 2) index === 1 ? $(cell.children[columnIndex-1]).addClass('td-col-select') : $(cell.children[columnIndex]).addClass('td-col-select');
+        
+    //     if(!type) $(cell.children[columnIndex]).addClass('td-col-select');
+    //   }
+    // })
   }else if($(e.target).hasClass('th-row')) { //点击行
-    e.target.parentNode.children.forEach((cell,index)=> {
-      if(index !==0) {
-        index === 2 ? $(cell).addClass('td-row-select') : $(cell).addClass('td-row-select');
-      }
-    })
+    let eRect = e.target.getBoundingClientRect()
+    // e.target.parentNode.children.forEach((cell,index)=> {
+    //   console.log(cell,index,'index');
+    //   if(index !==0) {
+    //     index === 2 ? $(cell).addClass('td-row-select') : $(cell).addClass('td-row-select');
+    //   }
+    // })
+    $(table.querySelector('#row-col-select')).css({
+      'top':`${eRect.y - tableRect.y}px`,
+      'left':`${eRect.width}px`,
+      'visibility':`visible`,
+      'height':`${eRect.height}px`,
+      'width':`${tableRect.width-eRect.width-1}px`,
+      'border-width':'1px 0 1px 0'})
   }
-
-
 }
 
 // 选中格子样式
@@ -119,15 +138,21 @@ export function resetStyle() {
   table.querySelectorAll(".td-chose").forEach(el => {
     el.classList.remove('td-chose')
   })
-  table.querySelectorAll(".td-col-select").forEach(el => {
-    el.classList.remove('td-col-select')
-  })
-  table.querySelectorAll(".td-row-select").forEach(el => {
-    el.classList.remove('td-row-select')
-  })
+  // table.querySelectorAll(".td-col-select").forEach(el => {
+  //   el.classList.remove('td-col-select')
+  // })
+  // table.querySelectorAll(".td-row-select").forEach(el => {
+  //   el.classList.remove('td-row-select')
+  // })
   table.querySelectorAll(".td-relation").forEach(el => {
     el.classList.remove('td-relation')
   })
+  $(table.querySelector('#row-col-select')).css({
+    'top':0,
+    'left':0,
+    'visibility':`hidden`,
+    'height':0,
+    'width':0})
 }
 
 //单击聚焦
@@ -146,11 +171,19 @@ export function setCellBg(e) {
   e.target.parentNode.parentNode.nodeName==='TD' && $(e.target.parentNode.parentNode).addClass("insert")
 }
 
-//右键菜单
-export function getRightClickMenu(pos,canEdit=false,isStaticTable=false) {
-  // 如果是静态表则只有清空功能
+/**
+ * 右键菜单
+ * @param {*} pos 类型 cell-单元格 selection-选区 col-列头 row-行头
+ * @param {*} canEdit 单元格是否可编辑
+ * @param {*} isStaticTable 是否是静态表 -- 平衡表中
+ * @param {*} extraConfig 额外的配置 cellMerged-是否合并了单元格
+ * @returns 
+ */
+export function getRightClickMenu(pos,canEdit=false,isStaticTable=false,extraConfig={}) {
+  // 如果是静态表则有清空功能、清除单元格
   if(isStaticTable){
-    return [{ label: bus.$i18nt ? bus.$i18nt.t('SystemManage.ReportApprove.clear_btn'):"清空", key: "reset" }]
+    return [{ label: bus.$i18nt ? bus.$i18nt.t('ETable.Btn.unmerge_cell') : '取消合并单元格',key: 'cell-unmerge',hidden:!extraConfig.cellMerged },
+      { label: bus.$i18nt ? bus.$i18nt.t('SystemManage.ReportApprove.clear_btn'):"清空", key: "reset" }].filter(me => !me.hidden)
   }
 
   let cellMenu = [
@@ -178,6 +211,7 @@ export function getRightClickMenu(pos,canEdit=false,isStaticTable=false) {
         { label: bus.$i18nt ? bus.$i18nt.t('Edb.CalculatesAll.day_mean') : '日均值',source: 16,fromEdbKey:75 },
       ]
     },
+    { label: bus.$i18nt ? bus.$i18nt.t('ETable.Btn.unmerge_cell') : '取消合并单元格',key: 'cell-unmerge',hidden:!extraConfig.cellMerged },
     { label: bus.$i18nt ? bus.$i18nt.t('SystemManage.ReportApprove.clear_btn'):"清空", key: "reset" },
   ]
 
@@ -193,8 +227,12 @@ export function getRightClickMenu(pos,canEdit=false,isStaticTable=false) {
       { label: bus.$i18nt ? bus.$i18nt.t('ETable.Btn.delete_btn') : "删除", key: "del" },
     ],
     'cell': canEdit 
-      ? [{ label: bus.$i18nt ? bus.$i18nt.t('ETable.Btn.edit_btn') : '编辑',key: 'cell-edit' },...cellMenu]
-      : cellMenu
+      ? [{ label: bus.$i18nt ? bus.$i18nt.t('ETable.Btn.edit_btn') : '编辑',key: 'cell-edit' },...cellMenu.filter(me => !me.hidden)]
+      : cellMenu.filter(me => !me.hidden),
+    'selection':[
+      extraConfig.cellMerged ? { label: bus.$i18nt ? bus.$i18nt.t('ETable.Btn.unmerge_cell') : '取消合并单元格',key: 'cell-unmerge' }:
+      { label: bus.$i18nt ? bus.$i18nt.t('ETable.Btn.merge_cell') : '合并单元格',key: 'cell-merge' }
+    ]
   }
 
   return menuMap[pos];

File diff suppressed because it is too large
+ 742 - 85
src/views/datasheet_manage/components/BalanceTable.vue


+ 43 - 39
src/views/datasheet_manage/components/CustomTable.vue

@@ -12,7 +12,7 @@
 
       <!-- 日期行表格 -->
       <div class="table-wrapper" v-if="config.type === 1">
-        <table width="auto" border="0" class="table" :style="disabled ? 'width:100%':''">
+        <table width="auto" border="0" class="table" :style="disabled ? 'width:100%':''" style="position: relative;">
           <thead>
             <tr>
               <!-- 行头 -->
@@ -143,13 +143,16 @@
               </td>
             </tr>
           </tbody>
+          <!-- 行列选中样式 -->
+          <div ref="rowColSelectRef" id="row-col-select" 
+          style="position: absolute;border-color: #18ad18;border-style: solid;background-color: transparent;visibility: hidden;z-index: 1;pointer-events: none;"></div>
         </table>
       </div>
       
 
       <!-- 日期列表格 -->
       <div class="table-wrapper" v-else>
-        <table width="auto" border="0" class="table" :style="disabled ? 'width:100%':''">
+        <table width="auto" border="0" class="table" :style="disabled ? 'width:100%':''" style="position: relative;">
           <thead>
             <tr>
               <!-- 行头 -->
@@ -312,6 +315,9 @@
         
             </tr>
           </tbody>
+          <!-- 行列选中样式 -->
+          <div ref="rowColSelectRef" id="row-col-select" 
+          style="position: absolute;border-color: #18ad18;border-style: solid;background-color: transparent;visibility: hidden;z-index: 1;pointer-events: none;"></div>
         </table>
       </div>
 
@@ -429,15 +435,6 @@ export default {
         resetStyle();
       })
     },
-    // 'config.data':{
-    //   handler(value){
-    //     console.log(value,'valuevaluevalue');
-    //     if(!this.disabled && this.hasInit){
-    //       this.$emit("autoSave")
-    //     }
-    //   },
-    //   deep:true
-    // },
     'config.textRowData':{
       handler(value){
         if(!this.disabled && this.hasInit){
@@ -705,7 +702,7 @@ export default {
 			dom.style.left = e.clientX-3 + 'px';
 			dom.style.top = e.clientY-3 + 'px';
       
-      selectMoreCellStyle(e,this.config.type)
+      selectMoreCellStyle(e)
     },
 
     /*  */
@@ -732,6 +729,10 @@ export default {
           this.config.textRowData.forEach(row => {
             row.splice(index+1,1)
           })
+          // 删除的是最后一列
+          if(!(index < this.config.data[0].length)){
+            resetStyle()
+          }
       }else if(cindex === '-1') { //删除行
         console.log('删除行',rindex)
 
@@ -746,7 +747,10 @@ export default {
             _.Data.splice(index,1)
           })
         }
-
+        // 删除的是最后一行
+        if(!(index < this.config.data.length)){
+          resetStyle()
+        }
         
       }
       if(!this.disabled && this.hasInit){
@@ -926,32 +930,32 @@ export default {
       border: 2px solid #0033FF;
       box-shadow: 0 0 5px rgba(73, 177, 249, .5)
     }
-    &.td-col-select::after {
-      position: absolute;
-      top: 0;
-      left: 0;
-      right: 0;
-      bottom: 0;
-      content: "";
-      display: block;
-      outline: 0;
-      border: 1px solid rgb(24, 173, 24);
-      border-bottom: none;
-      border-top: none;
-    }
-    &.td-row-select::after {
-      position: absolute;
-      top: 0;
-      left: 0;
-      right: 0;
-      bottom: 0;
-      content: "";
-      display: block;
-      outline: 0;
-      border: 1px solid rgb(24, 173, 24);
-      border-left: none;
-      border-right: none;
-    }
+    // &.td-col-select::after {
+    //   position: absolute;
+    //   top: 0;
+    //   left: 0;
+    //   right: 0;
+    //   bottom: 0;
+    //   content: "";
+    //   display: block;
+    //   outline: 0;
+    //   border: 1px solid rgb(24, 173, 24);
+    //   border-bottom: none;
+    //   border-top: none;
+    // }
+    // &.td-row-select::after {
+    //   position: absolute;
+    //   top: 0;
+    //   left: 0;
+    //   right: 0;
+    //   bottom: 0;
+    //   content: "";
+    //   display: block;
+    //   outline: 0;
+    //   border: 1px solid rgb(24, 173, 24);
+    //   border-left: none;
+    //   border-right: none;
+    // }
     &.insert {
       background: #FFEFDD;
     }

+ 651 - 97
src/views/datasheet_manage/components/MixedTable.vue

@@ -3,7 +3,8 @@
     <template v-if="config.data.length">
 
       <!-- 工具栏 -->
-      <toolBarSection v-if="!disabled" :cell="selectCell" @updateCell="updateCellStyle"/>
+      <toolBarSection v-if="!disabled" :cell="selectCell?selectCell:selectedCells" @cellMerge="toolCellMergeFun"
+        :echoParameter="{hasMergedCell}"/>
       
       <!-- 公式显示区 -->
       <div class="formula-wrapper" v-if="!disabled">
@@ -20,6 +21,9 @@
         border="0"
         class="table"
         :style="disabled ? 'width:100%' : ''"
+        ref="tableRef"
+        style="position: relative;"
+        @mousedown="selectCellHandle"
       >
         <thead>
           <tr>
@@ -57,7 +61,12 @@
               :key="`${index}_${cell_index}`"
               :data-rindex="rowHeader[index]"
               :data-cindex="columnHeader[cell_index]"
+              :data-datarindex="index"
+              :data-datacindex="cell_index"
               :data-key="cell.Uid"
+              v-show="!cell.merData || cell.merData.type!=='merged'"
+              :colspan="(cell.merData && cell.merData.type=='merge' && cell.merData.mer)?cell.merData.mer.colspan || 1:1"
+              :rowspan="(cell.merData && cell.merData.type=='merge' && cell.merData.mer)?cell.merData.mer.rowspan || 1:1"
               @click="clickCell($event, cell)"
               @dblclick="dblClickCellHandle($event,cell)"
               @contextmenu.prevent="rightClickHandle($event,cell)"
@@ -99,6 +108,8 @@
                   slot="reference"
                   :data-rindex="rowHeader[index]"
                   :data-cindex="columnHeader[cell_index]"
+                  :data-datarindex="index"
+                  :data-datacindex="cell_index"
                   :data-key="cell.Uid"
                 >{{ cell.ShowStyle?cell.ShowFormatValue:cell.ShowValue }}</span>
               </el-popover>
@@ -108,6 +119,8 @@
                 v-else-if="cell.ShowStyle"
                 :data-rindex="rowHeader[index]"
                 :data-cindex="columnHeader[cell_index]"
+                :data-datarindex="index"
+                :data-datacindex="cell_index"
                 :data-key="cell.Uid"
               >
                 {{cell.ShowFormatValue}}
@@ -116,6 +129,8 @@
               <span
                 :data-rindex="rowHeader[index]"
                 :data-cindex="columnHeader[cell_index]"
+                :data-datarindex="index"
+                :data-datacindex="cell_index"
                 :data-key="cell.Uid"
                 v-else
               >{{ cell.ShowValue }}</span>
@@ -129,6 +144,8 @@
                 :data-key="cell.Uid"
                 :data-rindex="rowHeader[index]"
                 :data-cindex="columnHeader[cell_index]"
+                :data-datarindex="index"
+                :data-datacindex="cell_index"
                 :fetch-suggestions="searchTarget"
                 @change.native="changeVal($event, cell)"
                 @keydown.native="keyEnterHandle($event,cell)"
@@ -152,6 +169,12 @@
             </td>
           </tr>
         </tbody>
+        <!-- 选区 -->
+        <div ref="selectionRef" id="selection-id"
+        style="position: absolute;border: 2px solid #0052D9;background-color: rgba(0, 82, 217, 0.1);visibility: hidden;z-index: 1;pointer-events: none;"></div>
+        <!-- 行列选中样式 -->
+        <div ref="rowColSelectRef" id="row-col-select" 
+        style="position: absolute;border-color: #18ad18;border-style: solid;background-color: transparent;visibility: hidden;z-index: 1;pointer-events: none;"></div>
       </table>
 
       <!-- 右键菜单 -->
@@ -239,7 +262,8 @@ import {
   toUpperCase,
   findCellKeyByFactor,
   isNumberVal,
-  transDecimalPlace
+  transDecimalPlace,
+  resetStyle
 } from "../common/customTable";
 import * as sheetInterface from "@/api/modules/sheetApi.js";
 import { dataBaseInterface } from '@/api/api.js';
@@ -301,6 +325,16 @@ export default {
       },
       deep:true
     },
+    'endCell.row':{
+      handler(newVal){
+        this.setSelectionStyle()
+      }
+    },
+    'endCell.column':{
+      handler(newVal){
+        this.setSelectionStyle()
+      }
+    }
   },
   data() {
     return {
@@ -349,8 +383,40 @@ export default {
       isInsertCalculateDate: false,//日期计算弹窗
       insertCalculateDateInfo: {},//日期计算info
 
-      activeNames: []
-
+      activeNames: [],
+      // 合并单元格data
+      isSelectionStart:false, //选区开始
+      startCell:{// 选区时鼠标开始的单元格
+        row:null,
+        column:null
+      }, 
+      endCell:{// 选区时鼠标结束的单元格
+        row:null,
+        column:null
+      },
+      selectionStart:{// 选区范围的左上角单元格
+        row:null,
+        rowSpan:0,
+        column:null,
+        colSpan:0
+      },
+      selectionEnd:{// 选区范围的右下角角单元格
+        row:null,
+        rowSpan:0,
+        column:null,
+        colSpan:0
+      },
+      hasMergedCell:false,// 选区内是否有合并的单元格
+      selectedCells:[],
+      // 选区有'值'单元格 因为超一个单元格有值不让合并,所以记录一个就行
+      hasValueCellItem:{
+        cellNumber:0, //有几个
+        row:null,
+        column:null
+      },
+      // 做个缓存
+      cacheKey:'',
+      cacheCellDom:{}
     };
   },
   mounted() {
@@ -390,8 +456,9 @@ export default {
       if(this.disabled) return
 
       selectCellStyle(e);
+      this.clearSelection()
+      this.hasMergedCell = !!(cell && cell.merData)
       this.selectCell = cell;
-      
       setFocus(e);
 
       //是插值单元格时寻找关联依赖的单元格 设置选框
@@ -399,7 +466,6 @@ export default {
         const { key } = e.target.dataset;
         if(!this.insertRelationArr.find(_ => _.key===key)) return
         let { relation_date,relation_edb } = this.insertRelationArr.find(_ => _.key===key)
-
         relation_date.key && setRelationStyle(relation_date)
         relation_edb.key && setRelationStyle(relation_edb)
       }
@@ -519,26 +585,25 @@ export default {
     },
 
     /* 选择指标 单元格类型为2 已经是指标单元格了就重置单元格 否则就视为选择指标*/
-    selectTarget(e,cell) {
-      const { EdbName,EdbInfoId }  = e;
-
-      //如果已经是指标单元格了再次点击就清空
-      if(cell.DataType===2&&cell.EdbInfoId) {
-        this.clearCell()
-      }else {
-        cell.DataType = 2;
-        cell.DataTime = '';
-        cell.ShowValue = EdbName;
-        cell.Value = EdbName;
-        cell.EdbInfoId = EdbInfoId;
-      }
-
-      this.checkCellRelation(cell)
-    },
+    // selectTarget(e,cell) {
+    //   const { EdbName,EdbInfoId }  = e;
+
+    //   //如果已经是指标单元格了再次点击就清空
+    //   if(cell.DataType===2&&cell.EdbInfoId) {
+    //     this.clearCell()
+    //   }else {
+    //     cell.DataType = 2;
+    //     cell.DataTime = '';
+    //     cell.ShowValue = EdbName;
+    //     cell.Value = EdbName;
+    //     cell.EdbInfoId = EdbInfoId;
+    //   }
+
+    //   this.checkCellRelation(cell)
+    // },
 
     /* 输入框失焦 设置单元格类型 处理关联关系 */
     async changeVal(e, cell) {
-
       // 是日期格式 DataType为1
       // 自定义内容 DataType 3
       //有=号为输入公式 DataType 6
@@ -563,14 +628,13 @@ export default {
           cell.DataTime = dateFormat;
           cell.Value = dateFormat;
         }else if(value.startsWith('=')) { //公式单元格
+          cell.Value = value;
           cell.DataType = 6;
           let calculateVal = await this.getValueByFormula(value);
           if(!calculateVal) return
           cell.ShowValue = calculateVal;
           //处理公式关系
           this.$set(cell,'Extra',this.dealFormulaConstruction(value))
-          
-
         }else {//自定义值
           cell.DataType = 3;
           cell.ShowValue = value;
@@ -637,7 +701,11 @@ export default {
       let relationIndex = this.insertRelationArr.findIndex(_ => _.key===key)
       this.insertRelationArr.splice(relationIndex,1)
     },
-
+    // 替换关联关系
+    replaceCellRelation(originKey,replaceKey){
+      if(!this.insertRelationArr.length) return
+      this.insertRelationArr = JSON.parse(JSON.stringify(this.insertRelationArr).replaceAll(originKey,replaceKey))
+    },
     /* 单元格更新时去更新有依赖关系单元格的值 目前只更4 5 7*/
     updateRelationCell(relation,cell) {
       const cellTypeMap = {
@@ -770,7 +838,6 @@ export default {
 
       // 提取因数数组
       let factors = extractFactorsFromFormula(val)
-      console.log(factors)
       
       //根据因数找单元格
       let isAllCell = factors.some(_ => findCellByFactorMixed(this.config.data,_)===null||isNaN(findCellByFactorMixed(this.config.data,_)))
@@ -785,7 +852,7 @@ export default {
           TagMap[_] = Number(findCellByFactorMixed(this.config.data,_))
         }
       });
-
+      
       const res = await sheetInterface.calculateCustomCellData({
         CalculateFormula: val,
         TagMap
@@ -803,10 +870,12 @@ export default {
     rightClickHandle(e,cell) {
       if(this.disabled) return
 
-      const { rindex,cindex,key } = e.target.dataset;
+      const { rindex,cindex,key,datacindex,datarindex } = e.target.dataset;
       this.rightClickCell = {
         rindex,
         cindex,
+        datarindex,
+        datacindex,
         key
       }
 
@@ -817,12 +886,20 @@ export default {
         pos = 'col'
       }else if(cindex==='-1') { //行头
         pos = 'row'
+      }else if( datarindex>=this.selectionStart.row && datarindex<=this.selectionEnd.row &&
+      datacindex>=this.selectionStart.column && datacindex<=this.selectionEnd.column &&
+      (this.selectionEnd.row-this.selectionStart.row>0||this.selectionEnd.column-this.selectionStart.column>0)){
+        // 选区
+        pos = 'selection'
       }else {//单元格
         pos = 'cell'
       }
+
+      pos==='cell' && this.clickCell(e,cell);
+
       this.config.contextMenuOption = pos === 'cell' 
-        ? getRightClickMenu(pos,(cell.DataType===1&&[1,2].includes(cell.DataTimeType))||[5,7,8].includes(cell.DataType))
-        : getRightClickMenu(pos)
+        ? getRightClickMenu(pos,(cell.DataType===1&&[1,2].includes(cell.DataTimeType))||[5,7,8].includes(cell.DataType),false,{cellMerged:this.hasMergedCell})
+        : getRightClickMenu(pos,false,false,{cellMerged:this.hasMergedCell})
 
       this.$nextTick(() => {
         let dom = $('#contextMenu-wrapper')[0];
@@ -835,9 +912,10 @@ export default {
           dom.style.top = e.clientY-3 + 'px';
         }
 
-        ['col','row'].includes(pos) && selectMoreCellStyle(e);
-        pos==='cell' && this.clickCell(e,cell);
-
+        if(['col','row'].includes(pos)){
+          this.clearSelection()
+          selectMoreCellStyle(e);
+        }
       })
       
     },
@@ -872,7 +950,9 @@ export default {
         // 'insert-edb-date': this.insertDateOpen,//导入指标日期
         'insert-date-calculate': this.insertDateCalculateOpen,//日期计算弹窗
         'reset': this.clearCell, //清空
-        'cell-edit': this.selectCell ? editHandlesMap[this.selectCell.DataType] : null
+        'cell-edit': this.selectCell ? editHandlesMap[this.selectCell.DataType] : null,
+        'cell-merge':this.mergeCellFun,
+        'cell-unmerge':this.unmergeCellsFun
       }
       keyMap[key] && keyMap[key](key)
       
@@ -929,22 +1009,38 @@ export default {
 
     },
 
-    /* 清除单元格内容 格式 关联关系 */
-    clearCell() {
-      if([4,5].includes(this.selectCell.DataType)) resetRelationStyle();
+    /* 
+      清除单元格内容 格式 关联关系 
+      config:{
+        single:true 清除单个,不考虑合并单元格的联系
+      }
+    */
+    clearCell(c) {
+      const isCell = c && typeof(c)=='object' && c.Uid
+      const cell = isCell ? c: this.selectCell
+
+      if([4,5].includes(cell.DataType)) resetRelationStyle();
+
+      cell.DataType = 3;
+      cell.ShowValue = '';
+      cell.Value = '';
+      cell.DataTime = '';
+      cell.DataTimeType = 0;
+      cell.EdbInfoId = 0;
+      cell.ShowStyle = '';
+      cell.ShowFormatValue = '';
+      cell.Extra && (cell.Extra = '');
 
-      this.selectCell.DataType = 3;
-      this.selectCell.ShowValue = '';
-      this.selectCell.Value = '';
-      this.selectCell.DataTime = '';
-      this.selectCell.DataTimeType = 0;
-      this.selectCell.EdbInfoId = 0;
-      this.selectCell.ShowStyle = '';
-      this.selectCell.ShowFormatValue = '';
+      this.checkCellRelation(cell)
 
-      this.checkCellRelation(this.selectCell)
     },
-
+    findDataByStartKey(key){
+      if(!key) return {data:{},row:-1,col:-1}
+      let mergeCellDom = this.$refs.tableRef.querySelector(`[data-key="${key}"]`)
+      let row = +mergeCellDom.dataset.datarindex
+      let col = +mergeCellDom.dataset.datacindex
+      return {data:this.config.data[row][col],row,col}
+    },
     /* 删除行列 */
     delColOrRow() {
       let { rindex,cindex } = this.rightClickCell;
@@ -956,23 +1052,113 @@ export default {
 
         let index = this.columnHeader.findIndex(_ => _ === cindex);
 
+        // 处理删除列对合并单元格的影响
+        let startMainKey=''
+        this.config.data.map((row,rowInd) => {
+          let rowEle=row[index]
+          if(rowEle.merData){
+            let {data,row,col} = this.findDataByStartKey(rowEle.merData.mer.sKey)
+            let r = row
+            let c = col
+            let rs = data.merData.mer.rowspan
+            let cs = data.merData.mer.colspan
+            if(cs == 1) return //只有一列
+
+            if(rowEle.merData.type == 'merged'){
+              // 每个大单元格只处理一次
+              if(startMainKey != rowEle.merData.mer.sKey){
+                if(data.merData.mer.colspan==2 && data.merData.mer.rowspan==1){
+                  //删除这一列之后,只有一个单元格了
+                  data.merData=null
+                  return
+                }
+                data.merData.mer.colspan--
+                startMainKey = rowEle.merData.mer.sKey
+              }
+            }else{
+              // 右一列的单元格作为新的左上角单元格
+              const newStartCell = this.config.data[r][c+1]
+              if(rowEle.merData.mer.colspan==2 && rowEle.merData.mer.rowspan==1){
+                //删除这一列之后,只有一个单元格了
+                newStartCell.merData=null
+                return
+              }
+              newStartCell.merData=rowEle.merData
+              newStartCell.merData.mer.sKey = newStartCell.Uid
+              newStartCell.merData.mer.colspan--
+              for (let i = r; i < (r+rs); i++) {
+                for (let j = c+1; j < (c+cs); j++) {
+                  const element = this.config.data[i][j];
+                  if(element.merData.type == 'merged'){
+                    element.merData.mer.sKey = newStartCell.Uid
+                  }
+                }                  
+              }
+            }
+          }
+        })
         //删除时清除关系
         if(this.insertRelationArr.length) {
           let delCellIds = this.config.data.map(row => row[index].Uid);
-          
           this.clearRelationInsertCell(delCellIds);
         }
-
         this.config.data.forEach(row => {
           row.splice(index,1)
         })
+        // 删除的是最后一列
+        if(!(index < this.config.data[0].length)){
+          resetStyle()
+        }
       }else if(cindex === '-1') { //删除行
         console.log('删除行',rindex)
 
         if(this.rowHeader.length === 1) return this.$message.warning(this.$t('OnlineExcelPage.keep_one_row_msg') )
 
         let index = this.rowHeader.findIndex(_ => _ === rindex)
-        
+        // 处理删除行对合并单元格的影响
+        let startMainKey=''
+        this.config.data[index].map((rowEle,rowInd) => {
+          if(rowEle.merData){
+            let {data,row,col} = this.findDataByStartKey(rowEle.merData.mer.sKey)
+            let r = row
+            let c = col
+            let rs = data.merData.mer.rowspan
+            let cs = data.merData.mer.colspan
+            if(rs == 1) return //只有一行
+
+            if(rowEle.merData.type == 'merged'){
+              // 每个大单元格只处理一次
+              if(startMainKey != rowEle.merData.mer.sKey){
+                if(data.merData.mer.colspan==1 && data.merData.mer.rowspan==2){
+                  //删除这一行之后,只有一个单元格了
+                  data.merData=null
+                  return
+                }
+                data.merData.mer.rowspan--
+                startMainKey = rowEle.merData.mer.sKey
+              }
+            }else{
+              // 下一行的单元格作为新的左上角单元格
+              const newStartCell = this.config.data[r+1][c]
+              if(rowEle.merData.mer.colspan==1 && rowEle.merData.mer.rowspan==2){
+                //删除这一行之后,只有一个单元格了
+                newStartCell.merData=null
+                return
+              }
+              newStartCell.merData=rowEle.merData
+              newStartCell.merData.mer.sKey = newStartCell.Uid
+              newStartCell.merData.mer.rowspan--
+              for (let i = r+1; i < (r+rs); i++) {
+                for (let j = c; j < (c+cs); j++) {
+                  const element = this.config.data[i][j];
+                  if(element.merData.type == 'merged'){
+                    element.merData.mer.sKey = newStartCell.Uid
+                  }
+                }                  
+              }
+            }
+          }
+        })
         if(this.insertRelationArr.length) {
           //删除时清除关系
           let delCellIds = this.config.data[index].map(cell => cell.Uid);
@@ -981,7 +1167,10 @@ export default {
         }
 
         this.config.data.splice(index,1)
-
+        // 删除的是最后一行
+        if(!(index < this.config.data.length)){
+          resetStyle()
+        }
       }
     },
 
@@ -1003,18 +1192,30 @@ export default {
       let { cindex } = this.rightClickCell;
       
       let index = this.columnHeader.findIndex(_ => _ === cindex);
-
+      let checkIndex = key==='insert-col-left'?index-1:index+1
+      let startMainKey=''
       this.config.data.forEach((row,rindex) => {
+        let isEnlargeCell = !!(row[index].merData && row[checkIndex] && row[checkIndex].merData)
+        if(isEnlargeCell && startMainKey != row[index].merData.mer.sKey){
+          const data = row[index].merData.type == 'merge'?row[index]:this.findDataByStartKey(row[index].merData.mer.sKey).data
+          data.merData.mer.colspan++
+          startMainKey = row[index].merData.mer.sKey
+        }
         row.splice(key==='insert-col-left'?index:index+1,0,{
           ShowValue: "",
           Value: "",
           DataType: 3,
           DataTime: "",
           EdbInfoId: 0,
-          Uid: md5.hex_md5(`${new Date().getTime()}${rindex}`)
+          Uid: md5.hex_md5(`${new Date().getTime()}${rindex}`),
+          merData:isEnlargeCell?{
+            type:'merged',
+            mer:{
+              sKey:row[index].merData.mer.sKey,//左上角第一个单元格的Uid
+            }
+          }:null
         })
       })
-      
     },
 
     /* 插入行 */
@@ -1022,15 +1223,35 @@ export default {
       let { rindex } = this.rightClickCell;
       
       let index = this.rowHeader.findIndex(_ => _ === rindex)
+      let checkIndex = key==='insert-row-up'?index-1:index+1
+      let startMainKey=''
+
+      let row = new Array(this.columnHeader.length).fill("").map((_,cindex) => {
+
+        let isEnlargeCell = !!(this.config.data[index][cindex].merData && this.config.data[checkIndex][cindex] && this.config.data[checkIndex][cindex].merData)
+        if(isEnlargeCell && startMainKey != this.config.data[index][cindex].merData.mer.sKey){
+          const data = this.config.data[index][cindex].merData.type == 'merge'?
+                      this.config.data[index][cindex]:
+                      this.findDataByStartKey(this.config.data[index][cindex].merData.mer.sKey).data
+          data.merData.mer.rowspan++
+          startMainKey = this.config.data[index][cindex].merData.mer.sKey
+        }
 
-      let row = new Array(this.columnHeader.length).fill("").map((_,cindex) => ({
-        ShowValue: "",
-        Value: "",
-        DataType: 3,
-        DataTime: "",
-        EdbInfoId: 0,
-        Uid: md5.hex_md5(`${new Date().getTime()}${cindex}`)
-      }));
+        return {
+          ShowValue: "",
+          Value: "",
+          DataType: 3,
+          DataTime: "",
+          EdbInfoId: 0,
+          Uid: md5.hex_md5(`${new Date().getTime()}${cindex}`),
+          merData:isEnlargeCell?{
+            type:'merged',
+            mer:{
+              sKey:this.config.data[index][cindex].merData.mer.sKey,//左上角第一个单元格的Uid
+            }
+          }:null
+        }
+      });
 
       this.config.data.splice( key==='insert-row-up'?index:index+1,0,row)
 
@@ -1083,7 +1304,6 @@ export default {
 
     /* 插入系统/指标日期 */
     insertDatehandle({insertValue,dataTimeType,str,sourceName}) {
-
       this.updateSourceFrom(sourceName)
 
       this.insertTargetCell.DataType = 1;
@@ -1202,7 +1422,7 @@ export default {
             DataTimeType: 0,
             DataTime: "",
             EdbInfoId:0,
-            Uid: md5.hex_md5(`${new Date().getTime()}${_rindex}${_cindex}`)
+            Uid: md5.hex_md5(`${new Date().getTime()}${_rindex}${_cindex}`),
           }));
         });
       }
@@ -1241,6 +1461,7 @@ export default {
         cell.EdbInfoId = this.copyCellItem.EdbInfoId;
         cell.ShowStyle = this.copyCellItem.ShowStyle;
         cell.ShowFormatValue = this.copyCellItem.ShowFormatValue;
+        cell.Extra = this.copyCellItem.Extra;
       }else {
         cell.DataType = 3;
         cell.ShowValue = this.copyCellItem.ShowValue;
@@ -1298,12 +1519,11 @@ export default {
       }
     },
 
-    /* 改变单元格显示文本 */
-    updateCellStyle({ShowStyle,ShowFormatValue}) {
-      
-      this.$set(this.selectCell,'ShowStyle',ShowStyle)
-      this.$set(this.selectCell,'ShowFormatValue',ShowFormatValue)
-    },
+    // /* 改变单元格显示文本 */
+    // updateCellStyle({ShowStyle,ShowFormatValue}) {
+    //   this.$set(this.selectCell,'ShowStyle',ShowStyle)
+    //   this.$set(this.selectCell,'ShowFormatValue',ShowFormatValue)
+    // },
     // 更新数据来源
     updateSourceFrom(source){
       if(!source) return
@@ -1311,6 +1531,339 @@ export default {
       let sourceStr = Array.from(new Set(concatSourceArr)).join(',');
       this.sourceFrom.text=sourceStr
       this.$emit('updateSourceName')
+    },
+    // ==================================================合并单元格
+    // 选区开始
+    selectCellHandle(e) {
+      // 不是左键的mousedown事件
+      if(e.button!==0) return
+      if(this.disabled) return
+      this.isSelectionStart=true
+      
+      let startTd;
+      if(e.target.nodeName==='TD'){
+        startTd = e.target
+      }else if(e.target.parentNode.nodeName==='TD'){
+        startTd = e.target.parentNode
+      }else if(e.target.parentNode.parentNode.nodeName==='TD'){
+        startTd = e.target.parentNode.parentNode
+      }else if(e.target.parentNode.parentNode.parentNode.nodeName==='TD'){
+        startTd = e.target.parentNode.parentNode.parentNode
+      }
+
+      if(!startTd) return
+
+      this.startCell={
+        row:+startTd.dataset.datarindex,
+        column:+startTd.dataset.datacindex
+      }
+      document.addEventListener('mousemove',this.selectZoneHandle)
+      document.addEventListener('mouseup',this.selectCellEndHandle)
+    },
+    // 选取
+    selectZoneHandle(e) {
+      if(!this.isSelectionStart) return 
+      if(this.disabled) return
+      const selection = window.getSelection();
+      if (selection.rangeCount>0) {
+        // 清除选中的文本范围
+        selection.removeAllRanges();
+      }
+
+      let tableRect=this.$refs.tableRef.getBoundingClientRect()
+      const mouseX = e.pageX;
+      const mouseY = e.pageY;
+
+      let endTd;
+
+      if(mouseX >= tableRect.left && mouseX <= tableRect.right &&
+      mouseY >= tableRect.top && mouseY <= tableRect.bottom){
+        // 表格内
+        if(e.target.nodeName==='TD'){
+          endTd = e.target
+        }else if(e.target.parentNode.nodeName==='TD'){
+          endTd = e.target.parentNode
+        }else if(e.target.parentNode.parentNode.nodeName==='TD'){
+          endTd = e.target.parentNode.parentNode
+        }else if(e.target.parentNode.parentNode.parentNode.nodeName==='TD'){
+          endTd = e.target.parentNode.parentNode.parentNode
+        }
+        if(!endTd) return 
+
+        this.endCell={
+          row:+endTd.dataset.datarindex,
+          column:+endTd.dataset.datacindex
+        }
+      }
+    },
+    // 选区结束
+    selectCellEndHandle(){
+      this.isSelectionStart=false
+      // click事件走在mouseup事件前面,延迟下
+      setTimeout(()=>{
+        if((this.selectionStart.row || this.selectionStart.row==0) 
+      && (this.selectionEnd.row || this.selectionEnd.row==0)
+      && (this.selectionStart.column || this.selectionStart.column==0)
+      && (this.selectionEnd.column || this.selectionEnd.column==0)){
+          this.selectedCells=[]
+          resetStyle()
+          this.selectCell=null
+          // 选区
+          for (let i = this.selectionStart.row; i < this.selectionEnd.row+1; i++) {
+            for (let j = this.selectionStart.column; j < this.selectionEnd.column+1; j++) {
+              this.selectedCells.push(this.config.data[i][j])
+            }        
+          }
+        }else{
+          this.selectedCells=[]
+        }
+      },1)
+      document.removeEventListener('mousemove',this.selectZoneHandle)
+      document.removeEventListener('mouseup',this.selectCellEndHandle)
+    },
+    findCellDom(key){
+      if(!key) return null
+      if(key !== this.cacheKey){
+        // 重新找
+        this.cacheCellDom = this.$refs.tableRef.querySelector(`[data-key="${key}"]`)
+        this.cacheKey=key
+      }
+      return this.cacheCellDom
+    },
+    // 确定选区范围和设置选区样式
+    setSelectionStyle(){
+     if(!this.isSelectionStart) return
+      // 开始的单元格没有
+      if(!( (this.startCell.row || this.startCell.row==0) && 
+        (this.startCell.column || this.startCell.column==0))) return 
+      // 结束的单元格没有
+      if(!( (this.endCell.row || this.endCell.row==0) && 
+        (this.endCell.column || this.endCell.column==0))) return
+
+      // 递归确定选区范围
+      const findZone = ({sR,eR,sC,eC})=>{
+        this.hasValueCellItem.cellNumber=0
+        
+        for (let i = sR; i < eR+1; i++) {
+          for (let j = sC; j < eC+1; j++) {
+            const element = this.config.data[i][j]
+            if(element.Value){
+              this.hasValueCellItem.cellNumber++
+              this.hasValueCellItem.row=i
+              this.hasValueCellItem.column=j
+            }
+
+            if(element.merData && element.merData.type==='merge'){
+              if(i+element.merData.mer.rowspan-1 > eR){
+                // 该单元格的行有合并 重新规定选取范围
+                return findZone({sR,eR:i+element.merData.mer.rowspan-1,sC,eC})
+              }
+              if(j+element.merData.mer.colspan-1 > eC){
+                // 该单元格的列有合并 重新规定选取范围
+                return findZone({sR,eR,sC,eC:j+element.merData.mer.colspan-1})
+              }
+              this.hasMergedCell=true
+            }
+            
+            if(element.merData && element.merData.type==='merged' ){
+              let item = this.findCellDom(element.merData.mer.sKey)
+              let row = +item.dataset.datarindex
+              let col = +item.dataset.datacindex
+              if(row < sR){
+                // 该单元格的行有被合并 重新规定选取范围
+                return findZone({sR:row,eR,sC,eC})
+              }
+              if(col < sC){
+                // 该单元格的行有被合并 重新规定选取范围
+                return findZone({sR,eR,sC:col,eC})
+              }
+              this.hasMergedCell=true
+            }
+          }
+        }
+        // 防止选中的区域不是整个的单元格(合并后的),后面确定selectionRef 区域大小有问题
+        if(this.config.data[eR][eC].merData && this.config.data[eR][eC].merData.type==='merged'){
+          let item = this.findCellDom(this.config.data[eR][eC].merData.mer.sKey) 
+          let row = +item.dataset.datarindex
+          let col = +item.dataset.datacindex
+          eR=row
+          eC=col
+          this.hasMergedCell=true
+        }
+        return {startR:sR,
+                endR:eR,
+                startC:sC,
+                endC:eC}
+      }
+
+      // 看是否是从下往上、从右往左选的
+      let rowReverse = this.startCell.row > this.endCell.row
+      let colReverse = this.startCell.column > this.endCell.column
+      let postion={
+        sR:rowReverse?this.endCell.row:this.startCell.row,
+        eR:rowReverse?this.startCell.row:this.endCell.row,
+        sC:colReverse?this.endCell.column:this.startCell.column,
+        eC:colReverse?this.startCell.column:this.endCell.column
+      }
+
+      this.hasMergedCell=false
+      const zone = findZone(postion)
+
+      let start = this.config.data[zone.startR][zone.startC]
+      let end = this.config.data[zone.endR][zone.endC]
+
+      this.selectionStart.row = zone.startR
+      this.selectionStart.column = zone.startC
+      this.selectionStart.rowSpan = start.merData?start.merData.mer.rowspan:1
+      this.selectionStart.colSpan = start.merData?start.merData.mer.colspan:1
+
+      this.selectionEnd.row = zone.endR
+      this.selectionEnd.column = zone.endC
+      this.selectionEnd.rowSpan = end.merData?end.merData.mer.rowspan:1
+      this.selectionEnd.colSpan = end.merData?end.merData.mer.colspan:1
+
+      let tableRect = this.$refs.tableRef.getBoundingClientRect()
+      let startTd = this.$refs.tableRef.querySelector(`[data-key="${start.Uid}"]`)
+      let endTd = this.$refs.tableRef.querySelector(`[data-key="${end.Uid}"]`)
+      if(!(startTd && endTd)) return
+      let startRect = startTd.getBoundingClientRect()
+      let endRect = endTd.getBoundingClientRect()
+
+      this.$refs.selectionRef.style.left = startRect.left-tableRect.left+'px'
+      this.$refs.selectionRef.style.top = startRect.top-tableRect.top+'px'
+      let width = Math.abs(endRect.right - startRect.right) + startRect.width
+      let height = Math.abs(endRect.bottom - startRect.bottom) + startRect.height
+      this.$refs.selectionRef.style.width = width+'px'
+      this.$refs.selectionRef.style.height = height + 'px'
+      this.$refs.selectionRef.style.visibility='visible'
+    },
+    clearSelection(){
+      this.$refs.selectionRef.style.width = 0
+      this.$refs.selectionRef.style.height = 0
+      this.$refs.selectionRef.style.visibility='hidden'
+      this.selectionStart={
+        row:null,
+        rowSpan:0,
+        column:null,
+        colSpan:0
+      }
+      this.selectionEnd={
+        row:null,
+        rowSpan:0,
+        column:null,
+        colSpan:0,
+      }
+      this.selectedCells=[]
+    },
+    mergeCellFun(){
+      // 无选区
+      if(!((this.selectionStart.row || this.selectionStart.row==0) 
+      && (this.selectionEnd.row || this.selectionEnd.row==0)
+      && (this.selectionStart.column || this.selectionStart.column==0)
+      && (this.selectionEnd.column || this.selectionEnd.column==0))) return
+
+      if(this.hasValueCellItem.cellNumber>1 || this.hasValueCellItem.cellNumber<0){
+        return this.$message.warning(this.$t('ETable.Msg.merge_cell_fail_msg'))
+      }
+      const firstCell = this.config.data[this.selectionStart.row][this.selectionStart.column]
+      if(this.hasValueCellItem.cellNumber!=0 && (this.hasValueCellItem.row!=this.selectionStart.row || this.hasValueCellItem.column!=this.selectionStart.column)){
+
+        const reserveCell=this.config.data[this.hasValueCellItem.row][this.hasValueCellItem.column]
+        // 将原本有值的单元格 移给左上角第一个单元格
+        firstCell.ShowValue=reserveCell.ShowValue
+        firstCell.ShowStyle=reserveCell.ShowStyle
+        firstCell.ShowFormatValue=reserveCell.ShowFormatValue
+        firstCell.Value=reserveCell.Value
+        firstCell.DataType=reserveCell.DataType
+        firstCell.DataTimeType=reserveCell.DataTimeType
+        firstCell.DataTime=reserveCell.DataTime
+        firstCell.EdbInfoId=reserveCell.EdbInfoId
+        reserveCell.CanEdit && (firstCell.CanEdit=reserveCell.CanEdit)
+        reserveCell.Extra && (firstCell.Extra=reserveCell.Extra)
+        // 处理合并后的依赖关系 就是替换
+        this.replaceCellRelation(reserveCell.Uid,firstCell.Uid)
+        // 清空
+        this.clearCell(reserveCell)
+      }
+
+
+      this.$set(firstCell,'merData',{
+        type:'merge',
+        mer:{
+          sKey:firstCell.Uid,//保留的单元格的Uid
+          rowspan: this.selectionEnd.row - this.selectionStart.row+1,
+          colspan: this.selectionEnd.column - this.selectionStart.column+1,
+        }
+      })
+
+      for (let i = this.selectionStart.row; i < this.selectionEnd.row+1; i++) {
+        for (let j = this.selectionStart.column; j < this.selectionEnd.column+1; j++) {
+          const element = this.config.data[i][j]
+          if(i == this.selectionStart.row && j == this.selectionStart.column){
+            continue
+          }
+          element.merData={
+            type:'merged',
+            mer:{
+              sKey:firstCell.Uid,//左上角第一个单元格的Uid
+            }
+          }
+        }        
+      }
+
+      let startCell = this.config.data[this.selectionStart.row][this.selectionStart.column]
+      let target = this.$refs.tableRef.querySelector(`[data-key="${startCell && startCell.Uid}"]`)
+      // 触发单元格的点击事件
+      target.click()
+    },
+    unmergeCellsFun(){
+      if((this.selectionStart.row || this.selectionStart.row==0) 
+      && (this.selectionEnd.row || this.selectionEnd.row==0)
+      && (this.selectionStart.column || this.selectionStart.column==0)
+      && (this.selectionEnd.column || this.selectionEnd.column==0)){
+        // 选区
+        for (let i = this.selectionStart.row; i < this.selectionEnd.row+1; i++) {
+          for (let j = this.selectionStart.column; j < this.selectionEnd.column+1; j++) {
+            this.unmergeCellFun(this.config.data[i][j])
+          }        
+        }
+      }else{
+        // 单个单元格
+        this.selectCell && this.unmergeCellFun(this.selectCell)
+      }
+      this.hasMergedCell=false
+      // 取消合并后,调整选区(有的话)
+      if((this.selectionStart.row || this.selectionStart.row==0) 
+      && (this.selectionEnd.row || this.selectionEnd.row==0)
+      && (this.selectionStart.column || this.selectionStart.column==0)
+      && (this.selectionEnd.column || this.selectionEnd.column==0)){
+        this.selectionStart.rowSpan=1
+        this.selectionStart.colSpan=1
+
+        this.selectionEnd.row += (this.selectionEnd.rowSpan-1)
+        this.selectionEnd.column += (this.selectionEnd.colSpan-1)
+        this.selectionEnd.rowSpan=1
+        this.selectionEnd.colSpan=1
+      }
+
+    },
+    unmergeCellFun(cell){
+      if(!(cell.merData && cell.merData.type=='merge')) return
+      let {row,col} = this.findDataByStartKey(cell.merData.mer.sKey)
+      const sRow = row,
+          sCol = col,
+          eRow = row+cell.merData.mer.rowspan-1,
+          eCol = col+cell.merData.mer.colspan-1
+
+      for (let i = sRow; i < eRow+1; i++) {
+        for (let j = sCol; j < eCol+1; j++) {
+          this.config.data[i][j].merData=null
+        }
+      }
+    },
+    toolCellMergeFun(){
+      if(this.hasMergedCell) this.unmergeCellsFun()
+      else this.mergeCellFun()
     }
   },
 };
@@ -1325,6 +1878,7 @@ export default {
 .table-wrapper {
   width: 100%;
   overflow: auto;
+  * { box-sizing: border-box; }
 
   .formula-wrapper {
     height: 42px;
@@ -1375,32 +1929,32 @@ export default {
       border: 2px dashed #0033ff;
       box-shadow: 0 0 5px rgba(73, 177, 249, 0.5);
     }
-    &.td-col-select::after {
-      position: absolute;
-      top: 0;
-      left: 0;
-      right: 0;
-      bottom: 0;
-      content: "";
-      display: block;
-      outline: 0;
-      border: 1px solid rgb(24, 173, 24);
-      border-bottom: none;
-      border-top: none;
-    }
-    &.td-row-select::after {
-      position: absolute;
-      top: 0;
-      left: 0;
-      right: 0;
-      bottom: 0;
-      content: "";
-      display: block;
-      outline: 0;
-      border: 1px solid rgb(24, 173, 24);
-      border-left: none;
-      border-right: none;
-    }
+    // &.td-col-select::after {
+    //   position: absolute;
+    //   top: 0;
+    //   left: 0;
+    //   right: 0;
+    //   bottom: 0;
+    //   content: "";
+    //   display: block;
+    //   outline: 0;
+    //   border: 1px solid rgb(24, 173, 24);
+    //   border-bottom: none;
+    //   border-top: none;
+    // }
+    // &.td-row-select::after {
+    //   position: absolute;
+    //   top: 0;
+    //   left: 0;
+    //   right: 0;
+    //   bottom: 0;
+    //   content: "";
+    //   display: block;
+    //   outline: 0;
+    //   border: 1px solid rgb(24, 173, 24);
+    //   border-left: none;
+    //   border-right: none;
+    // }
     &.td-choose-insert-target::after {
       position: absolute;
       top: 0;

+ 116 - 40
src/views/datasheet_manage/components/toolBarSection.vue

@@ -2,7 +2,7 @@
   <!-- 混合表格顶部工具栏 -->
   <div class="toolbar-wrapper">
     <div 
-      class="tool-btn-item" v-for="tool in toolIcons" 
+      class="tool-btn-item" v-for="tool in toolIcons.filter(ic => tools.split(',').includes(ic.tool))" 
       :key="tool.key" 
       @click="dealToolHandles(tool)"
     >
@@ -11,6 +11,11 @@
         <template v-if="tool.type==='icon'">
           <span class="icon-wrap" v-html="tool.icon"></span>
         </template>
+        
+        <div class="icon-box" v-if="tool.type=='icon-text'">
+          <span class="icon-wrap" v-html="tool.icon"></span>
+          <span class="icon-txt">{{tool.title}}</span>
+        </div>
 
         <template v-if="tool.type==='select'">
           <el-select 
@@ -18,7 +23,7 @@
             style="width: 90px" 
             size="small" 
             v-if="tool.key==='cell-type-edit'"
-            @change="changeCellType"
+            @change="changeCellsType"
           >
             <el-option
               v-for="item in numberTypeOptions"
@@ -40,15 +45,35 @@ export default {
     cell: {
       type: Object,
     },
+    /**工具
+     * cell-point-type(数字/百分位显示切换) 
+     * cell-merge(合并单元格)
+    */
+    tools:{
+      type:'String',
+      default:'cell-point-type,cell-merge',
+    },
+    // 回显参数
+    echoParameter:{
+      type:Object,
+      default:()=>{
+        return {}
+      },
+    }
   },
   watch: {
     cell(nval) {
-      if(nval&&nval.ShowStyle) {
-        this.option = {
-          ...JSON.parse(nval.ShowStyle)
+      if(nval) {
+        this.cellArray = Array.isArray(nval)?nval:[nval]
+        this.option = this.cellArray[0].ShowStyle?{
+          ...JSON.parse(this.cellArray[0].ShowStyle)
+        }:{
+          nt: "",//numberType
+          pn: 0,//ponitNum
         }
-      }else {
-        this.option = {
+      }else{
+        this.cellArray=[]
+        this.option ={
           nt: "",//numberType
           pn: 0,//ponitNum
         }
@@ -61,8 +86,7 @@ export default {
         nt: "",//numberType  number percent
         pn: 0,//ponitNum
       },
-
-
+      cellArray:[],
     };
   },
   computed:{
@@ -83,7 +107,8 @@ export default {
           </svg>`,
           key: "add-point",
           type: 'icon',
-          title: this.$t('ETableChildren.number_increase_title') 
+          title: this.$t('ETableChildren.number_increase_title') ,
+          tool:'cell-point-type'
         },
         { 
           icon: `<svg viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
@@ -100,13 +125,25 @@ export default {
           </svg>`, 
           key: "del-point", 
           type: 'icon',
-          title: this.$t('ETableChildren.number_reduce_title') 
+          title: this.$t('ETableChildren.number_reduce_title'),
+          tool:'cell-point-type'
         },
         { 
           icon: '',
           key: 'cell-type-edit',
           type:'select',
-          title:'' 
+          title:'' ,
+          tool:'cell-point-type'
+        },
+        { 
+          icon: `<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+            <path d="M17.9 1.45H2.1C1.55 1.45 1.1 1.9 1.1 2.45V18.25C1.1 18.8 1.55 19.25 2.1 19.25H17.9C18.45 19.25 18.9 18.8 18.9 18.25V2.45C18.9 1.9 18.45 1.45 17.9 1.45ZM17.7 6.54H12.87V2.65H17.7V6.54ZM7.15 6.54V2.65H11.68V6.54H7.15ZM5.95 2.65V6.54H2.3V2.65H5.95ZM5.95 12.23H2.3V7.74H5.95V12.23ZM2.3 13.43H5.95V18.06H2.3V13.43ZM7.15 18.05V7.74H17.7V18.05H7.15Z" fill="#333333"/>
+            </svg>
+            `,
+          key: 'cell-merge',
+          type:'icon-text',
+          title:this.echoParameter.hasMergedCell?this.$t('ETable.Btn.unmerge'):this.$t('ETable.Btn.merge_cell'),
+          tool:'cell-merge'
         }
       ]
     },
@@ -119,54 +156,71 @@ export default {
   },
   methods: {
     dealToolHandles({key}) {
-      //单元格不是数字就不用转了
-      if(!isNumberVal(this.cell.ShowValue)) return
 
       const handlesMap = {
-        'add-point': this.changeCellPoint,
-        'del-point': this.changeCellPoint,
+        'add-point': this.changeCellsPoint,
+        'del-point': this.changeCellsPoint,
+        'cell-merge': this.cellMergeHandle,
       }
 
       handlesMap[key]&&handlesMap[key](key)
     },
-
+    changeCellsPoint(key){
+      this.cellArray.map(item =>{
+        if(!item.merData || item.merData.type=='merge'){
+          this.changeCellPoint(key,item)
+        }
+      })
+    },
     /* 处理小数点位数 */
-    changeCellPoint(key) {
-      let value = _.cloneDeep(this.cell.ShowValue)
-      key==='add-point' ? this.option.pn++ : this.option.pn--;
+    changeCellPoint(key,cell) {
+      //单元格不是数字就不用转了
+      if( !isNumberVal(cell.ShowValue)) return
+      let cellValueStyle = cell.ShowStyle?JSON.parse(cell.ShowStyle):{
+        nt: "",//numberType
+        pn: 0,//ponitNum
+      }
+      key==='add-point' ? cellValueStyle.pn++ : cellValueStyle.pn--;
       
       //判断小数点后尾数 整数最小pn为0 小数最小为负位数
-      if(this.option.pn <= parseInt(`-${getDecimalPlaces(value)}`)) {
-        this.option.pn = parseInt(`-${getDecimalPlaces(value)}`)
+      if(cellValueStyle.pn <= parseInt(`-${getDecimalPlaces(cell.ShowValue)}`)) {
+        cellValueStyle.pn = parseInt(`-${getDecimalPlaces(cell.ShowValue)}`)
       }
 
       //百分比格式的话 pn最小为位数-2
-      if(this.option.nt==='percent' && this.option.pn <= parseInt(`-${getDecimalPlaces(value)-2}`)) {
-        this.option.pn = parseInt(`-${getDecimalPlaces(value)-2}`)
+      if(cellValueStyle.nt==='percent' && cellValueStyle.pn <= parseInt(`-${getDecimalPlaces(cell.ShowValue)-2}`)) {
+        cellValueStyle.pn = parseInt(`-${getDecimalPlaces(cell.ShowValue)-2}`)
       }
 
-      let nval = transDecimalPlace(value,this.option)
+      cell.ShowFormatValue = transDecimalPlace(cell.ShowValue,{...cellValueStyle,pn:cellValueStyle.pn})
+      cell.ShowStyle = JSON.stringify({...cellValueStyle,pn:cellValueStyle.pn})
       // console.log(nval)
-
-      this.$emit('updateCell',{
-        ShowStyle: JSON.stringify(this.option),
-        ShowFormatValue: nval
+      // this.$emit('updateCell',{
+      //   ShowStyle: JSON.stringify({...this.option,cellValueStyle.pn}),
+      //   ShowFormatValue: nval
+      // })
+    },
+    changeCellsType(){
+      this.cellArray.map(item =>{
+        if(!item.merData || item.merData.type=='merge'){
+          this.changeCellType(item)
+        }
       })
     },
-
     /* 处理百分位或数字格式 */
-    changeCellType() {
-      if(!isNumberVal(this.cell.ShowValue)) return
+    changeCellType(item) {
+      if(!isNumberVal(item.ShowValue)) return
       this.option.pn = 0
 
-      let value = _.cloneDeep(this.cell.ShowValue)
-
-      let nval = transNumPercentType(value,this.option.nt)
-
-      this.$emit('updateCell',{
-        ShowStyle: JSON.stringify(this.option),
-        ShowFormatValue: nval
-      })
+      item.ShowFormatValue = transNumPercentType(item.ShowValue,this.option.nt)
+      item.ShowStyle = JSON.stringify(this.option)
+      // this.$emit('updateCell',{
+      //   ShowStyle: JSON.stringify(this.option),
+      //   ShowFormatValue: nval
+      // })
+    },
+    cellMergeHandle(){
+      this.$emit('cellMerge')
     }
   },
 };
@@ -197,6 +251,28 @@ export default {
         height: 20px;
         display: inline-block;
       }
+      .icon-box{
+        display: flex;
+        align-items: center;
+        padding: 6px;
+        border: 1px solid #C8CDD9;
+        border-radius: 4px;
+        .icon-wrap{
+          width: 18px;
+          height: 18px;
+          display: inline-block;
+        }
+        .icon-txt{
+          white-space: nowrap;
+          font-size: 14px;
+          color: #333333;
+          margin-left: 4px;
+        }
+        &:hover {
+          background: #F4F8FE;
+        }
+      }
+
     }
   }
 }

Some files were not shown because too many files changed in this diff