【Quasar】【自定义组件】-重写Table、分页,并将内容固定于页面

使用:

    <cqsb-table
      :loading="loading"
      :data="vesselSchedulesDatas"
      :columns="vesselSchedulesColumns"
    />

组件:

<script>
import CqsbPagination from 'components/CqsbPagination.vue'
import { dom } from 'quasar'
import {decryptData} from "boot/encryAndDecrypt";

const {/* ready,*/ width, css } = dom

function headerFixed (el, bind, vnode ) {
  if (!vnode.context.fixedHeight) return
  vnode.context.$nextTick(() => {
    const style = {
      position: 'sticky',
      top: '0',
      'z-index': '1'
    }
    const topArea = el.querySelector('.q-table__top')
    const tHeader = el.querySelector('.q-table__middle thead')
    if (topArea) {
      css(topArea, style)
    }
    if (tHeader) {
      css(tHeader, style)
    }
    /**
     * 表格的最大高度 = 视口高度 - 页头的高度 - 表格的上下填充
     * 使其达到最佳视觉: 中间表格最高占满剩余高度
     * */
    const header = document.querySelector('header.q-header')
    // const publicAreas = document.querySelector('div.__public__areas')
    const pagination = document.querySelector('div.mark_pagination')
    if (header) {
      // console.log(header.getBoundingClientRect())
      // console.log(getComputedStyle(el.parentNode))
      const { paddingBottom, paddingTop } = getComputedStyle(el.parentNode)
      const py = parseInt(paddingTop) + parseInt(paddingBottom)
      el.style.maxHeight = `calc(100vh - ${header.offsetHeight + (pagination?.offsetHeight || 0) + py + 16}px)`
    }
  })
}

function columnFixed (el, bind, vnode) {

  vnode.context.$nextTick(() => {
    const { left, right } = bind.value
    const table = el.querySelector('.cqsb_table')
    table.style.position = 'relative'

    const thead = el.querySelector('thead tr')
    const tbody = el.querySelectorAll('tbody tr')
    const style = {
      position: 'sticky'
    }

    const shadow = document.createElement('div')
    const shadowStyle = {
      width: '1px',
      height: '100%',
      'z-index': '1',
      position: 'sticky'
    }


    if (left) {
      let leftOffset = 0
      for (let i = 0; i < left; i++) {
        const w = width(thead.children[i])
        style.left = `${leftOffset}px`
        css(thead.children[i], style)
        tbody.forEach(tr => css(tr.children[i], style))
        leftOffset += w
      }
    }
    if (right) {
      let rightOffset = 0
      for (let i = 0; i < right; i++) {
        const len = thead.children.length - 1 - i
        const w = width(thead.children[i])
        style.right = `${rightOffset}px`
        css(thead.children[len], style)
        tbody.forEach(tr => css(tr.children[len], style))
        rightOffset += w
      }
      if (!table.querySelector('div[style*="right"]')) {
        shadowStyle.right = rightOffset + 'px'
        shadowStyle.top = '0px'
        css(shadow, shadowStyle)
        table.appendChild(shadow)
      }
    }
  })
}

export default {
  name: 'CqsbTable',
  components: { CqsbPagination },
  props: {
    colFixed: {
      type: Object,
      default: () => ({ right: 1 })
    },
    data: {
      type: Array,
      default: () => []
    },
    columns: {
      type: Array,
      default: () => []
    },
    hidePage: {
      type: Boolean,
      default: false
    },
    fixedHeight: {
      type: Boolean,
      default: true
    }
  },
  data () {
    return {
      page: {
        pageNum: 1,
        pageSize: 20
      },
      selectedIndex: null, // 用于追踪选中的行索引
    }
  },
  computed: {
    tableData () {
      const { pageNum, pageSize } = this.page

      let start = (pageNum - 1) * pageSize
      if (start >= this.data.length) start = 0

      let end = pageNum * pageSize
      if (end >= this.data.length) end = this.data.length

      return this.data.slice(start, end)
    },
    pages () {
      return Math.ceil(this.data.length / this.page.pageSize)
    },
    total () {
      return this.data.length
    }
  },
  methods: {
    pageChange ({ pageNum, pageSize }) {
      this.page.pageNum = pageNum
      this.page.pageSize = pageSize
    },
    handleRowClick(evt, row, index){
      // 如果之前的选中索引不为null,且对应的元素存在,则移除其背景色
      if (this.selectedIndex !== null && this.$el.querySelector(`.cqsb_table tbody tr:nth-child(${this.selectedIndex + 1})`)) {
        const previousRow = this.$el.querySelector(`.cqsb_table tbody tr:nth-child(${this.selectedIndex + 1})`);
        previousRow.classList.remove('bg-amber-2'); // 假设'bg-amber-2'是应用于整个行的背景色类
      }
      // 更新选中索引并给新的行添加背景色
      this.selectedIndex = index;
      const newRow = this.$el.querySelector(`.cqsb_table tbody tr:nth-child(${index + 1})`);
      if (newRow) {
        newRow.classList.add('bg-amber-2'); // 直接给整个<tr>添加背景色类
      }
    }
  },
  directives: {
/*    rowSelected (el, _, vnode) {
      vnode.context.$nextTick(() => {
/!*        const tbody = el.querySelector('.cqsb_table tbody')
        let rowIndex = -1
        const rowBgClass = 'bg-amber-2'
        for (let i = 0; i < tbody.children.length; i++) {
          updateLastCellStyles()

          function updateLastCellStyles() {
            for (let i = 0; i < tbody.children.length; i++) {
              const tds = tbody.children[i].children
              const lastTd = tds[tds.length-1]

              if (i === rowIndex) {
                lastTd.classList.remove('bg-white')
                lastTd.classList.add(rowBgClass)
              } else {
                lastTd.classList.remove(rowBgClass)
                lastTd.classList.add('bg-white')
              }
            }
          }
          tbody.children[i].addEventListener('click', () => {
            if (rowIndex !== -1) {
              tbody.children[rowIndex].classList.remove(rowBgClass)
            }
            tbody.children[i].classList.add(rowBgClass)
            rowIndex = i
            updateLastCellStyles()
          })
        }*!/
        const tbody = el.querySelector('.cqsb_table tbody')
        const rowBgClass = 'bg-amber-2'
        let selectedRow = null

        function updateRowStyles(row, isSelected) {
          row.classList.toggle(rowBgClass, isSelected)
          const lastCell = row.lastElementChild
          // lastCell.classList.toggle('bg-white', !isSelected)
          lastCell.classList.toggle(rowBgClass, isSelected)
        }

        function handleRowClick(event) {
          const clickedRow = event.currentTarget
          if (selectedRow) {
            updateRowStyles(selectedRow, false)
          }
          updateRowStyles(clickedRow, true)
          selectedRow = clickedRow
        }

        Array.from(tbody.children).forEach(row => {
          updateRowStyles(row, false)
          row.addEventListener('click', handleRowClick)
        })
      })
    },*/
    columnFixed,
    headerFixed
  },
  created() {
    const encryPageSize =  localStorage.getItem('pageSize')
    if(encryPageSize !== null){
      this.page.pageSize = decryptData(encryPageSize)
    }
  },
}
</script>

<template>
<div>
  <q-table
    flat square
    v-header-fixed
    v-bind="$attrs"
    hide-pagination
    v-on="$listeners"
    :data="tableData"
    :columns="columns"
    v-column-fixed="colFixed"
    :pagination="{ rowsPerPage: 0 }"
    table-class="cqsb_table"
    @row-click="handleRowClick"
    table-header-class="bg-grey-2"
  >
    <template
      v-for="(_, slotName) in $scopedSlots"
      #[slotName]="scope"
    >
      <slot :name="slotName" v-bind="scope"></slot>
    </template>
  </q-table>
  <cqsb-pagination
    v-if="!hidePage"
    :max="pages"
    :options="page"
    :total="total"
    @change="pageChange"
    class="justify-end q-py-sm"
  />
</div>
</template>

<style scoped lang="scss">

</style>
在Vue 3中,Quasar框架的`q-table`组件用于展示数据表,如果要在每一行的最后添加一个删除按钮,你可以这样做: 首先,确保已安装Quasar CLI引入了`q-table`组件。然后,在你的组件模板部分(template),可以使用v-for指令遍历数据数组,在每个条目的末尾插入删除按钮的HTML元素: ```html <template> <q-table :data="items" bordered striped dense header-row show-bottom-space @cell-click="handleCellClick" > <!-- 添加定义 --> <q-tr key="tableRow" v-for="(item, index) in items"> <q-td v-for="(columnData, colIndex) in columnDefs" :key="colIndex"> {{ item[columnData.field] }} <!-- 行末删除按钮 --> <q-btn color="danger" tiny flat icon-only @click="$emit('deleteRow', index)" style="margin-left: auto; display: flex; align-items: center;" > <q-icon name="close-circle-o" /> </q-btn> </q-td> </q-tr> </q-table> </template> <script setup> import { ref } from 'vue'; import { useQuasar } from 'quasar/app'; const items = ref([]); // 数据源 const deleteRow = (index) => { // 根据业务逻辑处理删除操作 items.value.splice(index, 1); }; // 假设columnDefs是一个对象数组,存储你的定义 const columnDefs = [ // ... your column definitions ]; // 如果需要监听单元格点击事件 const handleCellClick = (event, cell) => { if (cell.columnDef.field === 'delete') { deleteRow(cell.row); } }; </script> ``` 这里的关键点包括: - `@cell-click`事件监听单元格点击,判断是否是删除按钮所在,如果是则触发删除操作。 - 使用`v-for`遍历数据和定义。 - 删除按钮的`@click`事件绑定到删除方法,传递当前行索引。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值