vue 3 + vxetable 单元格合并demo

<template>
  <div class="table">
    <sv-table :data="tableOptions.list" :height="800" :span-method="mergeRow">
      <template v-for="(item, index) in tableOptions.columns" :key="index">
        <sv-column
          :title="item.title"
          :field="item.field"
          :minWidth="item.minWidth || 120"
          :formatter="item.dict ? ['formatDict', item.dict] : null"
        >
          <template #default="{ row }">
            <template v-if="item.type === 'action'">
              <a-button>编辑</a-button>
            </template>
            <span v-else>
              {{ row[item.field] }}
            </span>
          </template>
        </sv-column>
      </template>
    </sv-table>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import dayjs from 'dayjs'
import { productList } from './mock'

const tableOptions = ref({})
const FIXED_COLUMNS = [
  { title: '操作', field: 'action', type: 'action' },
  { title: 'pid', field: 'pid' },
  { title: '供应商编码', field: 'vendorCode' },
  { title: '供应商名称', field: 'vendorName' },
  { title: '货品编码', field: 'goodsCode' },
  { title: '货品名称', field: 'goodsName' },
  { title: '货品规格', field: 'goodsModel' },
  { title: '生产订单完成进度', field: 'compRate', minWidth: 150 },
  { title: '排产订单总数', field: 'totalPlanQty' },
  { title: '实际生产订单数', field: 'totalActualQty' },
  { title: '项目名称', field: 'project', dict: [] }
]

const MERGE_GROUP_FIELDS = ['compRate', 'totalPlanQty', 'totalActualQty']
const MERGE_FIELDS = [
  'pid',
  'vendorCode',
  'vendorName',
  'goodsName',
  'goodsCode',
  'action'
]

function generateDateColumns(start, end) {
  const list = []
  let current = dayjs(start)
  while (!current.isAfter(end)) {
    list.push({
      title: current.format('YYYY-MM-DD'),
      field: current.format('YYYY-MM-DD')
    })
    current = current.add(1, 'day')
  }
  return list
}

function generateTableOptions(start, end) {
  const dateColumns = generateDateColumns(start, end)
  const columns = [...FIXED_COLUMNS, ...dateColumns]
  return {
    columns
  }
}

function setParentId(item, fields) {
  return fields.map((key) => item[key]).join('-')
}

function handleList(list) {
  const result = []
  for (const item of list) {
    const parentId = setParentId(item, ['vendorCode', 'goodsCode'])
    const { group } = item
    for (const groupItem of group) {
      const { projectList, totalActualQty, totalPlanQty, compRate } = groupItem
      for (const { dataList, projectCode } of projectList) {
        const newItem = {
          ...item,
          pid: parentId,
          groupId: `${parentId}-${group.indexOf(groupItem)}`,
          project: projectCode,
          totalActualQty,
          totalPlanQty,
          compRate
        }
        for (const key in dataList) {
          dataList.forEach((d) => {
            newItem[d.invDate] = d.qty
          })
        }
        result.push(newItem)
      }
    }
  }
  return result
}

function getMergeSpan(list, type) {
  const map = new Map()
  list.forEach((row) => {
    const val = row[type]
    if (!map.has(val)) {
      map.set(val, {
        span: 1
      })
    } else {
      map.get(val).span++
    }
  })
  return map
}

const mergeRow = ({ row, rowIndex, column, visibleData }) => {
  const columnFieldName = column.field
  const isFieldMergeable = MERGE_FIELDS.includes(columnFieldName)
  const isGroupMergeable = MERGE_GROUP_FIELDS.includes(columnFieldName)
  function handleMerge(groupKey) {
    const groupValue = row[groupKey]
    const groupSpan = getMergeSpan(tableOptions.value.list, groupKey)
    const index = tableOptions.value.list.findIndex(
      (r) => r[groupKey] === groupValue
    )
    if (rowIndex === index) {
      return {
        rowspan: groupSpan.get(groupValue).span,
        colspan: 1
      }
    } else {
      return {
        rowspan: 0,
        colspan: 0
      }
    }
  }
  if (isFieldMergeable) {
    return handleMerge('pid')
  }
  if (isGroupMergeable) {
    return handleMerge('groupId')
  }
  return {
    rowspan: 1,
    colspan: 1
  }
}

function getList() {
  return new Promise((resolve, reject) => {
    resolve(productList)
  })
}
async function init() {
  const start = dayjs('2024-04-10')
  const end = dayjs(start).add(3, 'day')
  const { columns } = generateTableOptions(start, end)
  const list = await getList()
  tableOptions.value = {
    columns,
    list: handleList(list) // 数据处理
  }
}

init()
</script>

<style scoped>
.table {
  padding: 10px;
}
</style>

const dataList = new Array(5).fill(0).map((_, index) => {
  return {
    dataList: [
      {
        invDate: '2024-04-10',
        qty: 10
      },
      {
        invDate: '2024-04-11',
        qty: 1
      },
      {
        invDate: '2024-04-12',
        qty: 1
      }
    ],
    projectCode: index
  }
})
const productList = new Array(2).fill(0).map((_, index) => {
  return {
    vendorCode: 'SM246' + index,
    vendorName: '义乌市钻杰饰品有限公司' + index,
    goodsCode: '711073' + index,
    goodsModel: '个',
    goodsName: '金色米饭碗碗身' + index,
    group: [
      {
        compRate: '10%',
        totalActualQty: 7 + index,
        totalPlanQty: 4 + index,
        projectList: dataList.slice(0, 2)
      },
      {
        compRate: '20%',
        totalActualQty: 7 + index,
        totalPlanQty: 4 + index,
        projectList: dataList.slice(2)
      }
    ]
  }
})
export { productList }

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Vue3中使用xlsx-style来冻结单元格,你可以按照以下步骤进操作: 1. 首先,安装xlsx-style和file-saver依赖: ```shell npm install xlsx-style file-saver ``` 2. 在Vue组件中引入xlsx-style和file-saver: ```javascript import XLSX from 'xlsx-style' import FileSaver from 'file-saver' ``` 3. 创建一个冻结单元格的函数,并在需要冻结的地方调用该函数: ```javascript export default { methods: { freezeCell() { // 创建一个工作簿 let wb = XLSX.utils.book_new() // 创建一个工作表 let ws = XLSX.utils.aoa_to_sheet([ ['Header 1', 'Header 2', 'Header 3'], ['Data 1', 'Data 2', 'Data 3'], ['Data 4', 'Data 5', 'Data 6'] ]) // 冻结第一和第一列 ws['!freeze'] = 'A2' // 将工作表添加到工作簿 XLSX.utils.book_append_sheet(wb, ws, 'Sheet1') // 将工作簿转换为二进制数据 let wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'binary' }) // 创建一个Blob对象并保存文件 FileSaver.saveAs(new Blob([this.s2ab(wbout)], { type: 'application/octet-stream' }), 'example.xlsx') }, s2ab(s) { // 将字符串转换为ArrayBuffer对象 let buf = new ArrayBuffer(s.length) let view = new Uint8Array(buf) for (let i = 0; i < s.length; i++) { view[i] = s.charCodeAt(i) & 0xFF } return buf } } } ``` 4. 在Vue模板中添加一个按钮,并调用冻结单元格的函数: ```html <template> <div> <button @click="freezeCell">冻结单元格</button> </div> </template> ``` 这样,当你点击"冻结单元格"按钮时,会生成一个带有冻结单元格的Excel文件,并自动下载到本地。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

web_Hsir

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值