Selaa lähdekoodia

fix: 调研表格调整

ldong 6 päivää sitten
vanhempi
commit
9823072ea8
3 muutettua tiedostoa jossa 460 lisäystä ja 6 poistoa
  1. 458 4
      src/components/sheet.vue
  2. 1 1
      src/reset.css
  3. 1 1
      src/views/sheetShow/index.vue

+ 458 - 4
src/components/sheet.vue

@@ -1,7 +1,9 @@
 <script setup lang='ts'>
-import { PropType,computed,ref } from 'vue';
+import { PropType,computed,ref,nextTick } from 'vue';
 import { isMobile } from '@/utils/utils'
- 
+import { useRoute } from 'vue-router';
+const route = useRoute();
+
 const props = defineProps({
   data: {
     type: Array as PropType<any[]>,
@@ -12,6 +14,7 @@ const props = defineProps({
   }
 })
 
+
 //手机端pc端又要不同样式
 const dynamicSty = computed(()=>{
   return isMobile() ? 'mobile-sty' : 'pc-sty';
@@ -23,11 +26,393 @@ const HtObj=ref({
   2:'right'
 })
 
+// 默认取消鼠标右键操作
+const preventRight=()=>{
+  document.addEventListener('contextmenu', function(event) {
+    event.preventDefault();
+  });
+}
+preventRight()
+
+const moveVal=ref(6)
+
+/* 右键 */
+const activeNames=ref('')
+const sizeOptions=ref([
+    { label:'5',val:'5' },
+    { label:'6',val:'6' },
+    { label:'7',val:'7' },
+    { label:'8',val:'8' },
+    { label:'9',val:'9' },
+    { label:'10',val:'10' },
+    { label:'11',val:'11' },
+    { label:'12',val:'12' },
+    { label:'13',val:'13' },
+    { label:'14',val:'14' },
+    { label:'15',val:'15' },
+    { label:'16',val:'16' },
+    { label:'17',val:'17' },
+    { label:'18',val:'18' },
+    { label:'20',val:'20' },
+    { label:'24',val:'24' },
+    { label:'28',val:'28' },
+    { label:'32',val:'32' },
+    { label:'36',val:'36' },
+    { label:'40',val:'40' },
+])
+const contextMenuRef=ref(null)
+
+function rightClickHandle(e,cell,rowIndex,colIndex) {
+  if(route.query.type==2){
+    return
+  }
+  if(clickStatus.value || !selectionStart.value || !selectionEnd.value) return
+  const start = {
+    row: Math.min(selectionStart.value.row, selectionEnd.value.row),
+    col: Math.min(selectionStart.value.col, selectionEnd.value.col)
+  };
+  const end = {
+    row: Math.max(selectionStart.value.row, selectionEnd.value.row),
+    col: Math.max(selectionStart.value.col, selectionEnd.value.col)
+  };
+  if(rowIndex >= start.row && rowIndex <= end.row && colIndex >= start.col && colIndex <= end.col){
+    nextTick(() => {
+      let dom = contextMenuRef.value;
+      console.log(dom)
+      console.log(e.clientX,e)
+      if(e.clientY > window.innerHeight/2) {
+        dom.style.left = e.clientX-3 + 'px';
+        dom.style.top = e.clientY-dom.offsetHeight-3 + 'px';
+      }else {
+        dom.style.left = e.clientX-3 + 'px';
+        dom.style.top = e.clientY-3 + 'px';
+      }
+      if(e.clientX > window.innerWidth/2){
+        dom.style.left = e.clientX-dom.offsetWidth-3 + 'px';
+      }
+    })
+  }
+}
+
+function hideContextMenu() {
+  const dom = contextMenuRef.value;
+  dom.style.left = '-9999px';
+  dom.style.top = '-9999px';
+}
+
+function chooseCellSize(val){
+  let indexArr = getCellIndices(selectionStart.value.row,selectionEnd.value.row,selectionStart.value.col,selectionEnd.value.col);
+  console.log(indexArr)
+  indexArr.forEach(el=>{
+    props.data[el.row][el.column].fs=val
+  })
+  resetSelect()
+  hideContextMenu()
+}
+
+// 获取区间所有坐标
+function getCellIndices(startRow, endRow, startCol, endCol) {
+    const indices = [];
+    for (let i = startRow; i <= endRow; i++) {
+        for (let j = startCol; j <= endCol; j++) {
+            indices.push({ row: i, column: j });
+        }
+    }
+    return indices;
+}
+
+// 选区选择
+const selectionStart=ref(null)
+const selectionEnd=ref(null)
+const clickStatus=ref(false)
+
+function isCellSelected(row, col) {
+  if (!selectionStart.value || !selectionEnd.value) {
+    return false;
+  }
+  const start = {
+    row: Math.min(selectionStart.value.row, selectionEnd.value.row),
+    col: Math.min(selectionStart.value.col, selectionEnd.value.col)
+  };
+  const end = {
+    row: Math.max(selectionStart.value.row, selectionEnd.value.row),
+    col: Math.max(selectionStart.value.col, selectionEnd.value.col)
+  };
+  return (
+    row >= start.row && row <= end.row && col >= start.col && col <= end.col
+  );
+}
+
+function updateSelection(event,endRow, endCol) {
+  if(event.button!=0){
+    return
+  }
+  if (selectionStart.value&&clickStatus.value) {
+    selectionEnd.value = { row: endRow, col: endCol };
+  }
+}
+
+function endSelection(event,endRow, endCol) {
+  if(event.button!=0){
+    return
+  }
+  if (selectionStart.value&&clickStatus.value) {
+    selectionEnd.value = { row: endRow, col: endCol };
+    clickStatus.value=false
+  }
+}
+
+//列宽行高
+const getSize=(cell,index,key)=>{
+  if(key=='width'){
+    return columnWidths.value[index]?columnWidths.value[index]+'px':''
+  }else{
+    return rowHeights.value[index]?rowHeights.value[index]+'px':'20px'
+  }
+}
+
+const tableRef=ref(null)
+const dragging=ref(false)
+const dragState=ref({})
+const columnWidths=ref([])
+
+const draggingHeight=ref(false)
+const dragStateHeight=ref({})
+const rowHeights=ref([])
+
+measureColumnWidths();
+
+// 初始化获取高度
+function measureColumnWidths(type='init') {
+  nextTick(()=>{
+    const table = tableRef.value;
+    if (table) {
+      const cells = table.rows[0].cells;
+      let widthArr= Array.from(cells).map(cell => cell.offsetWidth);
+      console.log(widthArr)
+      const rows=table.rows
+      let heightArr=Array.from(rows).map(row => row.offsetHeight);
+      console.log(heightArr)
+      if(type=='init'){
+        columnWidths.value=widthArr
+        rowHeights.value=heightArr
+        // console.log(columnWidths.value)
+        // console.log(rowHeights.value)
+      }
+    }
+  })
+}
+
+// 鼠标拖动列宽
+function handleMouseMove(event) {
+  if(route.query.type==2){
+    return
+  }
+  let target = event.target
+  if (!dragging.value) {
+      let rect = target.getBoundingClientRect()
+      const bodyStyle = event.target.style
+      if (rect.width > 12 && rect.right - event.pageX < moveVal.value) {
+          // 拖拽的鼠标样式
+          bodyStyle.cursor = 'col-resize'
+      }else if (rect.height > 12 && rect.bottom - event.pageY < moveVal.value) {
+          // 拖拽的鼠标样式
+          bodyStyle.cursor = 'row-resize'
+      } else if (!dragging.value) {
+          bodyStyle.cursor = ''
+      }
+  }
+}
+function handleMouseOut(event) {
+  event.target.style.cursor = ''
+}
+
+const startResize = (event,index,rowIndex) => {
+  document.onselectstart = function() { return false; };//解决拖动会选中文字的问题
+  if(route.query.type==2){
+    return
+  }
+  if(event.button!=0){
+    return
+  }
+  let target = event.target
+  // let cellWidth=0
+  // let startX, startWidth;
+  let rect = target.getBoundingClientRect()
+  if (rect.width > 12 && rect.right - event.pageX < moveVal.value) {
+    // 开始拖拽
+    // 当前拖拽的列所在的表格
+    let tableEl = event.target
+    // 当前所在列(单元格)
+    let thEL = event.target
+    dragging.value = true
+     // 获取列宽拖拽的显示线(拖拽线)
+    let resizeProxy = tableRef.value.querySelector(
+        'tbody'
+    )
+    // console.log(resizeProxy)
+    const columnRect = thEL.getBoundingClientRect()
+    thEL.classList.add('noclick')
+    dragState.value = {
+        startMouseLeft: event.clientX, // 鼠标开始的地方
+        columnWidth: columnRect.width, // th开始拖拽的宽度
+    }
+    resizeProxy.classList.remove('dn')
+    document.onselectstart = function () {
+        return false
+    }
+    document.ondragstart = function () {
+        return false
+    }
+    // startX = event.clientX;
+    // startWidth = event.target.clientWidth;
+    const doResize = (event) => {
+       // 拖拽中,拖拽线与鼠标的位置同步
+        resizeProxy.style.left = event.clientX + 'px'
+    };
+
+    const endResize = (event) => {
+        if(dragging.value){
+        // 拖拽完毕
+          const { startMouseLeft, columnWidth } = dragState.value
+          const finalLeft = parseInt(resizeProxy.style.left, 10)
+          const columnWidthDiff = finalLeft - startMouseLeft
+          const finalColumnWidth = columnWidthDiff + columnWidth
+          // console.log(finalColumnWidth)
+          columnWidths.value[index]=finalColumnWidth>=30?finalColumnWidth:30
+          console.log(columnWidths.value,111)
+          let widthTotal=0
+          columnWidths.value.forEach((item,colIndex)=>{
+            widthTotal+=item
+          })
+          // console.log(widthTotal)
+          //多出来的宽度
+          let otherWidth=widthTotal-tableRef.value.offsetWidth
+          // console.log(otherWidth)
+          //其余每个减去的宽度
+          let oneWidth=parseFloat((+(parseFloat(otherWidth).toFixed(2)))/(columnWidths.value.length-1)).toFixed(2)
+          // console.log(oneWidth)
+          columnWidths.value.forEach((item,colIndex)=>{
+            if(colIndex!=index){
+              columnWidths.value[colIndex]=item-(+oneWidth)
+            }
+          })
+          // console.log(columnWidths.value,222)
+          setTimeout(()=>{
+            measureColumnWidths('get')
+          },500)
+          resizeProxy.style.left = '0px'
+          event.target.style.cursor = ''
+          dragging.value = false
+          dragState.value = {}
+          resizeProxy.classList.add('dn')
+        }
+        document.removeEventListener('mousemove', doResize);
+        document.removeEventListener('mouseup', endResize);
+        document.onselectstart = null
+        document.ondragstart = null
+        setTimeout(function () {
+            thEL.classList.remove('noclick')
+        }, 0)
+      }
+
+      document.addEventListener('mousemove',doResize);
+      document.addEventListener('mouseup', endResize);
+    }
+    //else if (rect.height > 12 && rect.bottom - event.pageY < moveVal.value){
+    //}else{
+      //hideContextMenu()
+      //clickStatus.value=true
+      //selectionStart.value = null;
+      //selectionEnd.value = null;
+      //document.onselectstart = function() { return false; };//解决拖动会选中文字的问题
+      //selectionStart.value = { row: rowIndex, col: index };
+    //}
+};
+
+function resetSelect(){
+  clickStatus.value=false
+  selectionStart.value = null;
+  selectionEnd.value = null;
+}
+
+const startResizeHeight= (event,index) => {
+  if(route.query.type==2){
+    return
+  }
+  if(event.button!=0){
+    return
+  }
+  let target = event.target
+  let rect = target.getBoundingClientRect()
+  if (rect.height > 12 && rect.bottom - event.pageY < moveVal.value) {
+    // 开始拖拽
+    // 当前拖拽的列所在的表格
+    let tableEl = event.target
+    // 当前所在列(单元格)
+    let thEL = event.target
+    draggingHeight.value = true
+     // 获取列宽拖拽的显示线(拖拽线)
+    let resizeProxy = tableRef.value.querySelector(
+        'tbody'
+    )
+    // console.log(resizeProxy)
+    const columnRect = thEL.getBoundingClientRect()
+    thEL.classList.add('noclick')
+    dragStateHeight.value = {
+        startMouseTop: event.clientY, // 鼠标开始的地方
+        columnHeight: columnRect.height, // th开始拖拽的宽度
+    }
+    resizeProxy.classList.remove('dn')
+    document.onselectstart = function () {
+        return false
+    }
+    document.ondragstart = function () {
+        return false
+    }
+    const doResize = (event) => {
+       // 拖拽中,拖拽线与鼠标的位置同步
+        resizeProxy.style.top = event.clientY + 'px'
+    };
+
+    const endResize = (event) => {
+        if(draggingHeight.value){
+          // 拖拽完毕
+          const { startMouseTop, columnHeight } = dragStateHeight.value
+          const finalTop = parseInt(resizeProxy.style.top, 10)
+          const columnHeightDiff = finalTop - startMouseTop
+          const finalColumnHeight = columnHeightDiff + columnHeight
+          console.log(finalColumnHeight,index)
+          rowHeights.value[index]=finalColumnHeight>=20?finalColumnHeight:20
+          setTimeout(()=>{
+            measureColumnWidths('get')
+          },500)
+          resizeProxy.style.top = '0px'
+          event.target.style.cursor = ''
+          draggingHeight.value = false
+          dragStateHeight.value = {}
+          resizeProxy.classList.add('dn')
+        }
+        document.removeEventListener('mousemove', doResize);
+        document.removeEventListener('mouseup', endResize);
+        document.onselectstart = null
+        document.ondragstart = null
+        setTimeout(function () {
+            thEL.classList.remove('noclick')
+        }, 0)
+      }
+
+      document.addEventListener('mousemove',doResize);
+      document.addEventListener('mouseup', endResize);
+    }
+};
+
 </script>
 
 <template>
 <div :class="['table-wrapper',dynamicSty ]">
   <table
+    ref="tableRef"
     cellpadding="0" 
     cellspacing="0" 
     :class="config.Watermark?'background-watermark':'no-water'"
@@ -35,15 +420,24 @@ const HtObj=ref({
   >
     <tbody>
       <tr 
+        :style="`height:${getSize(item,index,'height')}`"
+        @mousedown="event=>startResizeHeight(event,index)"
+        @mouseout="handleMouseOut"
         v-for="(item,index) in props.data"
         :key="index"
       >
-        <td 
+      <!-- @contextmenu.prevent="event=>rightClickHandle(event,cell,index,cell_index)" -->
+        <td
+          @mouseout="handleMouseOut"
+          @mousemove="handleMouseMove" 
+          @mousedown="event=>startResize(event,cell_index,index)"
+          @mouseover="event=>updateSelection(event,index, cell_index)"
+          @mouseup="event=>endSelection(event,index, cell_index)"
           :class="['data-cell',{
             'one-bg':(index+1)%2&&index>0&&!config.Watermark,
             'tow-bg': (index+1)%2!==0&&index>0,
             'head-column': index === 0
-          }]"
+          },{ 'selected': isCellSelected(index, cell_index) }]"
           v-for="(cell,cell_index) in item"
           :key="cell_index"
           :colspan="cell.mc.cs||1"
@@ -52,8 +446,10 @@ const HtObj=ref({
             color: ${cell.fc};
             font-weight: ${cell.bl ? 'bold' : 'normal'};
             font-style: ${cell.it ? 'italic' : 'normal'};
+            font-size: ${cell.fs ? cell.fs: config.FontSize?config.FontSize:12}px;
             background: ${cell.bg};
             text-align: ${HtObj[cell.HorizontalType]};
+            width:${index==0?getSize(cell,cell_index,'width'):'auto'}
           `"
         >
           <!-- 单元格拆分 -->
@@ -75,10 +471,26 @@ const HtObj=ref({
       </tr>
     </tbody>
   </table>
+   <!-- 右键菜单 -->
+   <div class="contextMenu-wrapper" ref="contextMenuRef" @mouseleave="()=>{activeNames=[];hideContextMenu()}">
+      <el-collapse v-model="activeNames" accordion>
+        <el-collapse-item name="1">
+          <template #title>
+            <div class="title-size">字号</div>
+          </template>
+          <div @click="chooseCellSize(item.val)" class="size-text" v-for="(item,index) in sizeOptions" :key="index">
+            {{ item.label }}px
+          </div>
+        </el-collapse-item>
+      </el-collapse>
+    </div>
 </div>
 </template>
 
 <style lang='less' scoped>
+.selected {
+  background-color: rgba(0, 82, 217, 0.1);
+}
 ::-webkit-scrollbar {
   width: 6px;
   height: 6px;
@@ -125,6 +537,12 @@ table {
 
   .data-cell{
     color: #333;
+    min-width: 30px;
+    min-height: 20px;
+    padding: 0 !important;
+    &>div{
+      padding: 0.4em !important;
+    }
     &.one-bg {
       background-color: #EFEEF1;
     }
@@ -179,4 +597,40 @@ table {
   background-position: center center;
   background-size: 100%;
 }
+
+.contextMenu-wrapper {
+    position: fixed;
+    z-index: 99;
+    top: -9999px;
+    left: -9999px;
+    background: #fff;
+    min-width: 100px;
+    max-height: 100px;
+    overflow-y: auto;
+    /* border: 1px solid #999; */
+    box-shadow: 0 1px 4px #999;
+    .item {
+      padding: 10px 25px;
+      cursor: pointer;
+      &:hover {
+        background-color: #f5f7fa;
+      }
+      &:hover .subMenu-wrapper {
+        display: block;
+      }
+    }
+    .title-size{
+      text-align: center;
+      width: 100%;
+      position: absolute;
+    }
+    .size-text{
+      text-align: center;
+      padding: 5px 0;
+      cursor: pointer;
+      &:hover{
+        background: #f5f7fa;
+      }
+    }
+  }
 </style>

+ 1 - 1
src/reset.css

@@ -14,7 +14,7 @@ body {
 	font-family: 'PingFangSC-Regular','微软雅黑','宋体'!important;
 	color: #333;
 	font-size: 14px;
-	background-color: #fff;
+	/* background-color: #fff; */
 	-webkit-overflow-scrolling: touch;
 	
 }

+ 1 - 1
src/views/sheetShow/index.vue

@@ -104,7 +104,7 @@ const refreshSheet = async()=>{
   overflow: hidden;
   position: relative;
   margin: 0 auto;
-  background: #fff;
+  // background: #fff;
   .title {
     font-size: 17px;
     font-weight: normal;