vue2写手写excel表格实现简单功能

图:

代码

<template>
  <div class="mnlbb">
    <!-- 操作按钮 -->
    <div class="btn-group">
      <div>
        <el-dropdown @command="handleInsertRowCommand">
          <el-button>插入行<i class="el-icon-arrow-down el-icon--right"></i></el-button>
          <el-dropdown-menu slot="dropdown">
            <el-dropdown-item command="before">在当前行前插入</el-dropdown-item>
            <el-dropdown-item command="after">在当前行后插入</el-dropdown-item>
            <el-dropdown-item command="end">在末尾添加</el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>

        <el-dropdown @command="handleInsertColumnCommand">
          <el-button style="margin: 0 10px">插入列<i class="el-icon-arrow-down el-icon--right"></i></el-button>
          <el-dropdown-menu slot="dropdown">
            <el-dropdown-item command="before">在当前列前插入</el-dropdown-item>
            <el-dropdown-item command="after">在当前列后插入</el-dropdown-item>
            <el-dropdown-item command="end">在末尾添加</el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>

        <el-button @click="deleteSelectedRows" :disabled="selectedRows.length === 0">删除选中行</el-button>
        <el-button @click="deleteSelectedColumns" :disabled="selectedColumns.length === 0">删除选中列</el-button>
        <el-button @click="mergeSelectedCells" :disabled="!canMerge">合并单元格</el-button>
        <el-button @click="unmergeCells" :disabled="!canUnmerge">取消合并</el-button>
      </div>
      <div>
        <el-button type="primary" @click="saveTable">保存</el-button>
      </div>
    </div>

    <!-- 表格 -->
    <div class="table-container" ref="tableContainer">
      <table border ref="table">
        <thead>
          <tr>
            <th width="40"></th>
            <th
              v-for="(header, index) in tableHeaders"
              :key="index"
              :width="header.width || currentColumnWidth"
              :height="currentRowHeight"
              :data-col="index"
              :style="{ minWidth: '50px' }"
            >
              <div class="header-cell" @dblclick="startEditingHeader(index)">
                <el-checkbox
                  :value="selectedColumns.includes(index)"
                  @change="() => toggleColumnSelection(index)"
                ></el-checkbox>
                <span style="margin-left: 5px">{{ header.name }}</span>
              </div>
              <div class="resize-handle" @mousedown="startResizeColumn($event, index)"></div>
            </th>
          </tr>
        </thead>
        <tbody>
          <tr
            v-for="(row, rowIndex) in tableData"
            :key="rowIndex"
            :height="row.height || currentRowHeight"
            :data-row="rowIndex"
          >
            <td width="30" style="text-align: center">
              <el-checkbox
                :value="selectedRows.includes(rowIndex)"
                @change="() => toggleRowSelection(rowIndex)"
              ></el-checkbox>
              <div class="resize-handle vertical" @mousedown="startResizeRow($event, rowIndex)"></div>
            </td>
            <td
              v-for="(cell, colIndex) in row"
              :key="colIndex"
              :width="tableHeaders[colIndex].width || currentColumnWidth"
              :rowspan="cell.merged && cell.isMaster ? cell.rowspan : 1"
              :colspan="cell.merged && cell.isMaster ? cell.colspan : 1"
              :class="{
                'merged-cell': cell.merged && !cell.isMaster,
                'selected-cell': isSelectedCell(rowIndex, colIndex),
                'selected-range': isInSelectedRange(rowIndex, colIndex),
                'merged-master': cell.merged && cell.isMaster,
              }"
              style="text-align: center"
              @dblclick="startEditingCell(rowIndex, colIndex)"
              @mousedown="startSelection(rowIndex, colIndex, $event)"
              @mouseover="extendSelection(rowIndex, colIndex, $event)"
            >
              <template v-if="!cell.merged || cell.isMaster">
                <span v-if="!isEditingCell(rowIndex, colIndex)">{{ cell.name }}</span>
                <div v-if="isEditingCell(rowIndex, colIndex)" style="display: flex; width: 100%">
                  <el-input
                    type="textarea"
                    autosize
                    :value="cell.name"
                    @input="updateCellValue(rowIndex, colIndex, $event)"
                    @blur="stopEditingCell(rowIndex, colIndex)"
                    @keyup.enter.native="stopEditingCell(rowIndex, colIndex)"
                    size="mini"
                    autofocus
                    style="flex: 1"
                    class="zdyCenter"
                  ></el-input>
                  <i
                    class="el-icon-more"
                    @click="handleVariableClick('tableData', rowIndex, colIndex)"
                    style="margin-left: 5px; line-height: 28px"
                  ></i>
                </div>
              </template>
              <template v-else>
                <!-- 显示合并单元格的内容 -->
                <span>{{ cell.name }}</span>
              </template>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
    <VariableSelectDialog :isShow.sync="isShowVarDialog" @confirm="handleVarConfirm" />
  </div>
</template>

<script>
import VariableSelectDialog from '../../CustomDialog/VariableSelectDialog.vue'
import { reportFormSave, reportFormList } from '@/api/menuLeft'
export default {
  components: {
    VariableSelectDialog,
  },
  data() {
    return {
      tableData: this.generateDefaultTableData(),
      tableHeaders: this.generateDefaultTableHeaders(),
      currentColumnWidth: 150,
      currentRowHeight: 40,
      editingHeaderIndex: null,
      editingCell: null,
      selectedRows: [],
      selectedColumns: [],
      allRowsSelected: false,
      allColumnsSelected: false,
      selectIndex1: 0,
      selectIndex2: 0,
      selectType: '',
      isShowVarDialog: false,
      selectionStart: null,
      selectionEnd: null,
      isResizing: false,
      resizingColumn: null,
      resizingRow: null,
      startX: 0,
      startY: 0,
      startWidth: 0,
      startHeight: 0,
      currentPage: {},
      details: {},
    }
  },
  computed: {
    canMerge() {
      if (!this.selectionStart || !this.selectionEnd) return false
      const [startRow, startCol] = this.selectionStart
      const [endRow, endCol] = this.selectionEnd
      return (
        (startRow !== endRow || startCol !== endCol)
        && !this.isInMergedArea(startRow, startCol)
        && !this.isInMergedArea(endRow, endCol)
      )
    },
    canUnmerge() {
      if (!this.selectionStart || !this.selectionEnd) return false
      const [row, col] = this.selectionStart
      return this.tableData[row] && this.tableData[row][col] && this.tableData[row][col].merged
    },
    selectedRange() {
      if (!this.selectionStart || !this.selectionEnd) return null
      const [startRow, startCol] = this.selectionStart
      const [endRow, endCol] = this.selectionEnd
      return {
        startRow: Math.min(startRow, endRow),
        endRow: Math.max(startRow, endRow),
        startCol: Math.min(startCol, endCol),
        endCol: Math.max(startCol, endCol),
      }
    },
  },
  created() {
    this.currentPage = JSON.parse(localStorage.getItem('pageData'))
    if (this.currentPage.report_type == 12) {
      this.getreportFormList()
    }
    console.log('自定义表格组件创建', this.currentPage)
  },
  methods: {
    getreportFormList() {
      reportFormList({ page_id: this.currentPage.id })
        .then((res) => {
          if (res.code === 0) {
            this.details = res.data.list.length > 0 ? res.data.list[0] : {}
            this.tableData = JSON.parse(this.details.tableData || this.generateDefaultTableData())
            this.tableHeaders = JSON.parse(this.details.tableHeaders || this.generateDefaultTableHeaders())
          }
        })
        .catch((error) => {
          console.error('获取报表列表失败:', error)
        })
    },
    updateCellValue(rowIndex, colIndex, value) {
      // 确保使用Vue.set来更新响应式数据
      this.$set(this.tableData[rowIndex][colIndex], 'name', value)

      // 如果是合并单元格的主单元格,更新合并区域的所有单元格
      if (this.tableData[rowIndex][colIndex].merged && this.tableData[rowIndex][colIndex].isMaster) {
        const cell = this.tableData[rowIndex][colIndex]
        for (let r = rowIndex; r < rowIndex + cell.rowspan; r++) {
          for (let c = colIndex; c < colIndex + cell.colspan; c++) {
            if (r === rowIndex && c === colIndex) continue
            this.$set(this.tableData[r][c], 'name', value)
          }
        }
      }

      // 强制更新视图
      this.$forceUpdate()
    },
    // 生成Excel风格的列名 (A, B, ..., Z, AA, AB, ...)
    generateColumnName(index) {
      let columnName = ''
      index++ // 从1开始而不是0

      while (index > 0) {
        const remainder = (index - 1) % 26
        columnName = String.fromCharCode(65 + remainder) + columnName
        index = Math.floor((index - 1) / 26)
      }

      return columnName
    },

    // 更新表头序号
    updateHeaderNames() {
      this.tableHeaders.forEach((header, index) => {
        header.name = this.generateColumnName(index)
      })
    },

    isSelectedCell(row, col) {
      return this.selectionStart && this.selectionStart[0] === row && this.selectionStart[1] === col
    },
    isInSelectedRange(row, col) {
      if (!this.selectedRange) return false
      const { startRow, endRow, startCol, endCol } = this.selectedRange
      return row >= startRow && row <= endRow && col >= startCol && col <= endCol
    },
    generateDefaultTableData() {
      const data = []
      for (let i = 0; i < 10; i++) {
        const row = []
        for (let j = 0; j < 8; j++) {
          row.push({
            name: '',
            merged: false,
            isMaster: false,
            rowspan: 1,
            colspan: 1,
          })
        }
        data.push(row)
      }
      return data
    },
    generateDefaultTableHeaders() {
      const headers = []
      for (let i = 0; i < 8; i++) {
        headers.push({
          name: this.generateColumnName(i),
          width: 150,
        })
      }
      return headers
    },
    isEditingCell(rowIndex, colIndex) {
      return this.editingCell && this.editingCell[0] === rowIndex && this.editingCell[1] === colIndex
    },
    startEditingHeader(index) {
      this.editingHeaderIndex = index
    },
    stopEditingHeader(index) {
      this.editingHeaderIndex = null
    },
    startEditingCell(rowIndex, colIndex) {
      if (this.tableData[rowIndex][colIndex].merged && !this.tableData[rowIndex][colIndex].isMaster) {
        return
      }
      this.editingCell = [rowIndex, colIndex]
    },
    stopEditingCell(rowIndex, colIndex) {
      this.editingCell = null

      // 确保数据同步
      this.$forceUpdate()
    },
    startSelection(rowIndex, colIndex, event) {
      if (this.tableData[rowIndex][colIndex].merged && !this.tableData[rowIndex][colIndex].isMaster) {
        return
      }
      if (event.shiftKey && this.selectionStart) {
        this.selectionEnd = [rowIndex, colIndex]
        return
      }
      this.selectionStart = [rowIndex, colIndex]
      this.selectionEnd = [rowIndex, colIndex]
    },
    extendSelection(rowIndex, colIndex, event) {
      if (!this.selectionStart || !event.buttons) return
      if (this.tableData[rowIndex][colIndex].merged && !this.tableData[rowIndex][colIndex].isMaster) {
        return
      }
      this.selectionEnd = [rowIndex, colIndex]
    },
    isInMergedArea(row, col) {
      for (let r = 0; r < this.tableData.length; r++) {
        for (let c = 0; c < this.tableData[r].length; c++) {
          const cell = this.tableData[r][c]
          if (cell.merged && cell.isMaster) {
            if (row >= r && row < r + cell.rowspan && col >= c && col < c + cell.colspan) {
              return true
            }
          }
        }
      }
      return false
    },
    mergeSelectedCells() {
      if (!this.selectedRange) return

      const { startRow, endRow, startCol, endCol } = this.selectedRange

      // 计算合并的行列数
      const rowspan = endRow - startRow + 1
      const colspan = endCol - startCol + 1

      // 设置主单元格
      this.tableData[startRow][startCol] = {
        ...this.tableData[startRow][startCol],
        merged: true,
        isMaster: true,
        rowspan,
        colspan,
      }

      // 标记其他单元格为合并状态
      for (let r = startRow; r <= endRow; r++) {
        for (let c = startCol; c <= endCol; c++) {
          if (r === startRow && c === startCol) continue

          this.tableData[r][c] = {
            ...this.tableData[r][c],
            merged: true,
            isMaster: false,
            rowspan: 1,
            colspan: 1,
          }
        }
      }

      this.clearSelection()
    },
    unmergeCells() {
      if (!this.selectedRange) return

      const [row, col] = this.selectionStart
      const cell = this.tableData[row][col]

      if (!cell.merged) return

      const rowspan = cell.rowspan
      const colspan = cell.colspan

      // 恢复所有单元格
      for (let r = row; r < row + rowspan; r++) {
        for (let c = col; c < col + colspan; c++) {
          this.tableData[r][c] = {
            ...this.tableData[r][c],
            name: r === row && c === col ? cell.name : '',
            merged: false,
            isMaster: false,
            rowspan: 1,
            colspan: 1,
          }
        }
      }

      this.clearSelection()
    },
    clearSelection() {
      this.selectionStart = null
      this.selectionEnd = null
    },
    startResizeColumn(e, colIndex) {
      e.preventDefault()
      e.stopPropagation()

      this.isResizing = true
      this.resizingColumn = colIndex
      this.startX = e.clientX
      this.startWidth = this.tableHeaders[colIndex].width || this.currentColumnWidth

      document.addEventListener('mousemove', this.handleColumnResize)
      document.addEventListener('mouseup', this.stopResize)
    },
    handleColumnResize(e) {
      if (!this.isResizing || this.resizingColumn === null) return

      const delta = e.clientX - this.startX
      const newWidth = Math.max(50, this.startWidth + delta)

      this.tableHeaders[this.resizingColumn].width = newWidth
    },
    startResizeRow(e, rowIndex) {
      e.preventDefault()
      e.stopPropagation()

      this.isResizing = true
      this.resizingRow = rowIndex
      this.startY = e.clientY
      this.startHeight = this.tableData[rowIndex].height || this.currentRowHeight

      document.addEventListener('mousemove', this.handleRowResize)
      document.addEventListener('mouseup', this.stopResize)
    },
    handleRowResize(e) {
      if (!this.isResizing || this.resizingRow === null) return

      const delta = e.clientY - this.startY
      const newHeight = Math.max(30, this.startHeight + delta)

      this.$set(this.tableData[this.resizingRow], 'height', newHeight)
    },
    stopResize() {
      this.isResizing = false
      this.resizingColumn = null
      this.resizingRow = null

      document.removeEventListener('mousemove', this.handleColumnResize)
      document.removeEventListener('mousemove', this.handleRowResize)
      document.removeEventListener('mouseup', this.stopResize)
    },
    handleInsertRowCommand(command) {
      if (command !== 'end' && this.selectedRows.length === 0) {
        this.$message.warning('请先选中要插入位置的行')
        return
      }
      const refRowIndex = command === 'end' ? null : this.selectedRows[0]
      this.insertRow(command, refRowIndex)
    },
    handleInsertColumnCommand(command) {
      if (command !== 'end' && this.selectedColumns.length === 0) {
        this.$message.warning('请先选中要插入位置的列')
        return
      }
      const refColIndex = command === 'end' ? null : this.selectedColumns[0]
      this.insertColumn(command, refColIndex)
    },
    toggleRowSelection(rowIndex) {
      const index = this.selectedRows.indexOf(rowIndex)
      if (index === -1) {
        this.selectedRows.push(rowIndex)
      } else {
        this.selectedRows.splice(index, 1)
      }
      this.updateAllRowsSelected()
    },
    toggleColumnSelection(colIndex) {
      const index = this.selectedColumns.indexOf(colIndex)
      if (index === -1) {
        this.selectedColumns.push(colIndex)
      } else {
        this.selectedColumns.splice(index, 1)
      }
      this.updateAllColumnsSelected()
    },
    saveTable() {
      console.log(this.tableData, this.tableHeaders, '保存数据====')
      const content = {
        page_id: this.currentPage.id,
        tableData: JSON.stringify(this.tableData),
        tableHeaders: JSON.stringify(this.tableHeaders),
        id: this.details.id || 0,
      }
      reportFormSave(content)
        .then((res) => {
          if (res.code === 0) {
            this.currentPage = JSON.parse(localStorage.getItem('pageData'))
            this.$message.success('保存成功')
            this.getreportFormList()
          }
        })
        .catch((error) => {
          console.error('保存失败:', error)
        })
    },
    handleVariableClick(type, index1, index2) {
      this.selectType = type
      this.selectIndex1 = index1
      this.selectIndex2 = index2
      this.isShowVarDialog = true
    },
    handleVarConfirm(addr, fieldName) {
      if (addr == '') {
        this.$message.warning('请选择变量')
        return
      }
      if (this.selectType == 'tableHeaders') {
        this.tableHeaders[this.selectIndex1].name = fieldName
        this.tableHeaders[this.selectIndex1].addr = addr
      } else if (this.selectType == 'tableData') {
        this.tableData[this.selectIndex1][this.selectIndex2].name = fieldName
        this.tableData[this.selectIndex1][this.selectIndex2].addr = addr
      } else {
        console.log('选择类型错误')
      }
    },
    insertRow(position = 'end', refRowIndex = null) {
      const newRow = []
      for (let i = 0; i < this.tableHeaders.length; i++) {
        newRow.push({
          name: '',
          merged: false,
          isMaster: false,
          rowspan: 1,
          colspan: 1,
        })
      }

      if (position === 'end' || refRowIndex === null) {
        this.tableData.push(newRow)
      } else if (position === 'before') {
        this.tableData.splice(refRowIndex, 0, newRow)
      } else if (position === 'after') {
        this.tableData.splice(refRowIndex + 1, 0, newRow)
      }
    },
    insertColumn(position = 'end', refColIndex = null) {
      const newHeader = {
        name: this.generateColumnName(this.tableHeaders.length),
        width: this.currentColumnWidth,
      }

      if (position === 'end' || refColIndex === null) {
        this.tableHeaders.push(newHeader)
        this.tableData.forEach((row) =>
          row.push({
            name: '',
            merged: false,
            isMaster: false,
            rowspan: 1,
            colspan: 1,
          }),
        )
      } else if (position === 'before') {
        this.tableHeaders.splice(refColIndex, 0, newHeader)
        this.tableData.forEach((row) =>
          row.splice(refColIndex, 0, {
            name: '',
            merged: false,
            isMaster: false,
            rowspan: 1,
            colspan: 1,
          }),
        )
      } else if (position === 'after') {
        this.tableHeaders.splice(refColIndex + 1, 0, newHeader)
        this.tableData.forEach((row) =>
          row.splice(refColIndex + 1, 0, {
            name: '',
            merged: false,
            isMaster: false,
            rowspan: 1,
            colspan: 1,
          }),
        )
      }

      // 更新所有表头名称
      this.updateHeaderNames()
    },
    unmergeCell(row, col) {
      const cell = this.tableData[row][col]
      const rowspan = cell.rowspan
      const colspan = cell.colspan

      for (let r = row; r < row + rowspan; r++) {
        for (let c = col; c < col + colspan; c++) {
          this.tableData[r][c] = {
            ...this.tableData[r][c],
            name: r === row && c === col ? cell.name : '',
            merged: false,
            isMaster: false,
            rowspan: 1,
            colspan: 1,
          }
        }
      }
    },
    deleteSelectedRows() {
      this.selectedRows
        .sort((a, b) => b - a)
        .forEach((rowIndex) => {
          // 检查要删除的行是否在某个合并区域内
          for (let r = 0; r < this.tableData.length; r++) {
            for (let c = 0; c < this.tableData[r].length; c++) {
              const cell = this.tableData[r][c]
              if (cell.merged && cell.isMaster) {
                // 如果删除的行在合并区域内
                if (rowIndex >= r && rowIndex < r + cell.rowspan) {
                  // 调整合并区域的行数
                  cell.rowspan -= 1

                  // 如果合并区域行数变为1,自动取消合并
                  if (cell.rowspan === 1) {
                    this.unmergeCell(r, c)
                  }
                }
                // 如果删除的行在合并区域上方,调整合并区域起始行
                else if (rowIndex < r) {
                  this.$set(this.tableData[r][c], 'rowspan', cell.rowspan)
                  this.$set(this.tableData[r - 1][c], 'rowspan', cell.rowspan)
                  this.tableData[r - 1][c] = { ...cell }
                  this.tableData[r][c] = {
                    name: '',
                    merged: false,
                    isMaster: false,
                    rowspan: 1,
                    colspan: 1,
                  }
                }
              }
            }
          }

          // 删除行
          this.tableData.splice(rowIndex, 1)
        })

      this.selectedRows = []
    },
    deleteSelectedColumns() {
      this.selectedColumns
        .sort((a, b) => b - a)
        .forEach((colIndex) => {
          // 检查要删除的列是否在某个合并区域内
          for (let r = 0; r < this.tableData.length; r++) {
            for (let c = 0; c < this.tableData[r].length; c++) {
              const cell = this.tableData[r][c]
              if (cell.merged && cell.isMaster) {
                // 如果删除的列在合并区域内
                if (colIndex >= c && colIndex < c + cell.colspan) {
                  // 如果删除的是主列,需要重新指定主单元格
                  if (colIndex === c) {
                    // 找到合并区域内第一个未删除的列作为新的主列
                    let newMasterCol = c + 1
                    while (newMasterCol < c + cell.colspan && this.selectedColumns.includes(newMasterCol)) {
                      newMasterCol++
                    }

                    if (newMasterCol < c + cell.colspan) {
                      // 将新列设为主单元格
                      this.tableData[r][newMasterCol] = {
                        ...this.tableData[r][newMasterCol],
                        merged: true,
                        isMaster: true,
                        rowspan: cell.rowspan,
                        colspan: cell.colspan - 1,
                      }

                      // 标记其他单元格为合并状态
                      for (let mr = r; mr < r + cell.rowspan; mr++) {
                        for (let mc = c; mc < c + cell.colspan; mc++) {
                          if (mc === newMasterCol) continue
                          this.tableData[mr][mc] = {
                            ...this.tableData[mr][mc],
                            merged: true,
                            isMaster: false,
                            rowspan: 1,
                            colspan: 1,
                          }
                        }
                      }
                    }
                  } else {
                    // 调整合并区域的列数
                    cell.colspan -= 1

                    // 如果合并区域列数变为1,自动取消合并
                    if (cell.colspan === 1) {
                      this.unmergeCell(r, c)
                    }
                  }
                }
                // 如果删除的列在合并区域左侧,调整合并区域起始列
                else if (colIndex < c) {
                  this.$set(this.tableData[r][c], 'colspan', cell.colspan)
                  this.$set(this.tableData[r][c - 1], 'colspan', cell.colspan)
                  this.tableData[r][c - 1] = { ...cell }
                  this.tableData[r][c] = {
                    name: '',
                    merged: false,
                    isMaster: false,
                    rowspan: 1,
                    colspan: 1,
                  }
                }
              }
            }
          }

          // 删除表头
          this.tableHeaders.splice(colIndex, 1)
          // 删除每行中对应的列数据
          this.tableData.forEach((row) => {
            row.splice(colIndex, 1)
          })

          // 更新表头名称
          this.updateHeaderNames()
        })

      this.selectedColumns = []
    },
    toggleAllColumns(checked) {
      this.selectedColumns = checked ? [...Array(this.tableHeaders.length).keys()] : []
    },
    updateAllColumnsSelected() {
      this.allColumnsSelected = this.selectedColumns.length === this.tableHeaders.length
    },
    toggleAllRows(checked) {
      this.selectedRows = checked ? [...Array(this.tableData.length).keys()] : []
    },
    updateAllRowsSelected() {
      this.allRowsSelected = this.selectedRows.length === this.tableData.length
    },
  },
}
</script>

<style lang="scss" scoped>
.mnlbb {
  margin: 10px;
  height: calc(100vh - 120px);
  overflow: auto;
}

.table-container {
  margin-top: 10px;
  height: calc(100% - 60px);
  overflow: auto;

  table {
    border-collapse: collapse;
    width: 100%;
    table-layout: fixed;

    th,
    td {
      padding: 5px;
      border: 1px solid #ddd;
      position: relative;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;

      &:hover {
        background-color: #f5f7fa;
        cursor: pointer;
      }
    }

    th {
      background-color: #f8f8f8;
      font-weight: bold;
      text-align: center;
    }

    .header-cell {
      display: flex;
      justify-content: center;

      .el-checkbox {
        margin-bottom: 5px;
      }

      .el-input {
        width: 100%;

        ::v-deep .el-input__inner {
          padding: 0 5px;
          height: 28px;
          line-height: 28px;
        }
      }
    }

    .merged-cell {
      display: none;
    }

    .merged-master {
      background-color: #f0f9eb;
      border: 2px solid #67c23a !important;
    }

    .selected-cell {
      background-color: #ecf5ff !important;
      border: 2px solid #409eff !important;
    }

    .selected-range {
      background-color: #d9ecff !important;
    }

    .resize-handle {
      position: absolute;
      right: 0;
      top: 0;
      bottom: 0;
      width: 5px;
      background: transparent;
      cursor: col-resize;

      &.vertical {
        right: auto;
        bottom: 0;
        left: 0;
        top: auto;
        width: 100%;
        height: 5px;
        cursor: row-resize;
      }

      &:hover {
        background-color: #409eff;
      }
    }
  }
}

.btn-group {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}

.el-checkbox {
  margin-right: 0;
}
</style>
<style>
.zdyCenter .el-textarea__inner {
  text-align: center !important;
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值