123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636 |
- <script setup lang='ts'>
- 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[]>,
- required: true
- },
- config: {
- type: Object
- }
- })
- const dynamicSty = computed(()=>{
- return isMobile() ? 'mobile-sty' : 'pc-sty';
- })
- const HtObj=ref({
- 0:"center",
- 1:'left',
- 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
-
-
- }
- }
- })
- }
- 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 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'
- )
-
- const columnRect = thEL.getBoundingClientRect()
- thEL.classList.add('noclick')
- dragState.value = {
- startMouseLeft: event.clientX,
- columnWidth: columnRect.width,
- }
- resizeProxy.classList.remove('dn')
- document.onselectstart = function () {
- return false
- }
- document.ondragstart = function () {
- return false
- }
-
-
- 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
-
- columnWidths.value[index]=finalColumnWidth>=30?finalColumnWidth:30
- console.log(columnWidths.value,111)
- let widthTotal=0
- columnWidths.value.forEach((item,colIndex)=>{
- widthTotal+=item
- })
-
-
- let otherWidth=widthTotal-tableRef.value.offsetWidth
-
-
- let oneWidth=parseFloat((+(parseFloat(otherWidth).toFixed(2)))/(columnWidths.value.length-1)).toFixed(2)
-
- columnWidths.value.forEach((item,colIndex)=>{
- if(colIndex!=index){
- columnWidths.value[colIndex]=item-(+oneWidth)
- }
- })
-
- 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);
- }
-
-
-
-
-
-
-
-
-
- };
- 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'
- )
-
- const columnRect = thEL.getBoundingClientRect()
- thEL.classList.add('noclick')
- dragStateHeight.value = {
- startMouseTop: event.clientY,
- columnHeight: columnRect.height,
- }
- 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'"
- :style="{'font-size':(config.FontSize||12)+'px',backgroundImage: 'url('+config.Watermark+')'}"
- >
- <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
- @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"
- :rowspan="cell.mc.rs||1"
- :style="`
- 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'}
- `"
- >
-
- <div class="split-word" v-if="cell.ct.s">
- <span
- v-for="(word,word_index) in cell.ct.s"
- :key="`${index}_${cell_index}_${word_index}`"
- :style="`
- color: ${word.fc};
- font-weight: ${word.bl ? 'bold' : 'normal'};
- font-style: ${word.it ? 'italic' : 'normal'};
- text-decoration:${word.un?'underline':word.cl?'line-through':'normal'};
- `"
- >{{word.v}}</span>
- </div>
- <div v-else
- :style="`text-decoration:${cell.un?'underline':cell.cl?'line-through':'normal'};`">{{cell.m}}</div>
- </td>
- </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;
- }
- ::-webkit-scrollbar-track {
- background: rgb(239, 239, 239);
- border-radius: 2px;
- }
- ::-webkit-scrollbar-thumb {
- background: #ccc;
- border-radius: 10px;
- }
- ::-webkit-scrollbar-thumb:hover {
- background: #888;
- }
- ::-webkit-scrollbar-corner {
- background: #666;
- }
- .table-wrapper {
- max-width: calc(100vw - 20px);
- margin: 0 auto;
- // margin-right: -5px;
- overflow: auto;
- }
- table {
- width: 100%;
- font-size: 14px;
- color: #333;
- td,
- th {
- // min-width: 120px;
- word-break: break-all;
- word-wrap: break-word;
- line-height: 1.2em;
- border: 1px solid #dcdfe6;
- // height: 40px;
- text-align: center;
- border-left: none;
- border-top: none;
- &:first-child {
- border-left: 1px solid #dcdfe6;
- }
- }
- .data-cell{
- color: #333;
- min-width: 30px;
- min-height: 20px;
- padding: 0 !important;
- &>div{
- padding: 0.4em !important;
- }
- &.one-bg {
- background-color: #EFEEF1;
- }
- &.two-bg {
- background-color: #fff;
- }
- }
- .thead-sticky {
- position: sticky;
- top: 0;
- }
- .head-column {
- background-color: #505B78;
- color: #fff;
- }
- .split-word {
- span { display: inline; }
- }
- }
- .no-water{
- td,
- th {
- background-color: #fff;
- }
- .head-column {
- background-color: #505B78;
- color: #fff;
- }
- }
- .pc-sty table {
- table-layout: auto;
- td,th {
- width: auto;
- height: auto;
- padding: 0.4em 0;
- }
- }
- .mobile-sty table {
- table-layout: auto;
- td,th {
- min-width: 120px;
- height: 40px;
- }
- }
- .background-watermark{
- background-repeat: no-repeat;
- 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>
|