|
|
@@ -222,23 +222,23 @@ export default {
|
|
|
// 导入文件
|
|
|
async importFile(file) {
|
|
|
try {
|
|
|
- // 显示加载状态
|
|
|
const loading = this.$loading({
|
|
|
lock: true,
|
|
|
text: '正在导入Excel文件...',
|
|
|
spinner: 'el-icon-loading',
|
|
|
background: 'rgba(0, 0, 0, 0.7)'
|
|
|
})
|
|
|
-
|
|
|
+
|
|
|
try {
|
|
|
- // 使用导入模块处理文件
|
|
|
const sheetData = await importExcelFile(file)
|
|
|
-
|
|
|
- if (sheetData && sheetData.length > 0) {
|
|
|
- // 更新本地数据并发送到iframe显示
|
|
|
- this.sheetData = sheetData
|
|
|
+
|
|
|
+ // 裁剪每个 sheet 的空行空列
|
|
|
+ const trimmedSheetData = this.trimSheetData(sheetData)
|
|
|
+
|
|
|
+ if (trimmedSheetData && trimmedSheetData.length > 0) {
|
|
|
+ this.sheetData = trimmedSheetData
|
|
|
this.sendToIframe('setData', this.sheetData)
|
|
|
- this.$message.success(`导入成功!共 ${sheetData.length} 个 Sheet`)
|
|
|
+ this.$message.success(`导入成功!共 ${trimmedSheetData.length} 个 Sheet`)
|
|
|
// 导入后不自动保存,等待用户点击保存按钮
|
|
|
} else {
|
|
|
this.$message.error('导入失败:Excel 文件为空或格式错误!')
|
|
|
@@ -247,7 +247,6 @@ export default {
|
|
|
this.$message.error(`导入失败:${error.message || '文件解析错误,请检查文件格式是否正确'}`)
|
|
|
} finally {
|
|
|
loading.close()
|
|
|
- // 清空文件选择器
|
|
|
if (this.$refs.fileInput) {
|
|
|
this.$refs.fileInput.value = ''
|
|
|
}
|
|
|
@@ -269,11 +268,14 @@ export default {
|
|
|
|
|
|
// 标记为内部更新,避免循环
|
|
|
this.isUpdatingFromParent = true
|
|
|
+
|
|
|
+ // 保存前先裁剪一次,去掉空行空列
|
|
|
+ const trimmed = this.trimSheetData(this.sheetData)
|
|
|
|
|
|
// 将表格数据以 JSON 字符串格式传递给父组件,key 使用 'biaoGeNeiRong'
|
|
|
this.$emit('change-data', 'biaoGeNeiRong', JSON.stringify({
|
|
|
sheetId: this.localSheetId,
|
|
|
- sheets: this.sheetData
|
|
|
+ sheets: trimmed
|
|
|
}))
|
|
|
|
|
|
// 重置标记
|
|
|
@@ -348,8 +350,10 @@ export default {
|
|
|
await new Promise(resolve => setTimeout(resolve, 500))
|
|
|
|
|
|
try {
|
|
|
+ //导出前先裁剪一次
|
|
|
+ const trimmed = this.trimSheetData(this.sheetData)
|
|
|
// 传 fileName 给 exportToExcel
|
|
|
- const result = await exportToExcel(this.sheetData, fileName)
|
|
|
+ const result = await exportToExcel(trimmed, fileName)
|
|
|
|
|
|
if (result.success) {
|
|
|
this.$message.success(`导出成功!文件: ${result.fileName}`)
|
|
|
@@ -378,6 +382,124 @@ export default {
|
|
|
|
|
|
// 兜底
|
|
|
return `在线表格导出_${ym}`
|
|
|
+ },
|
|
|
+ // 裁剪 sheet 数据中的空行空列
|
|
|
+ trimSheetData(sheets) {
|
|
|
+ if (!sheets || !Array.isArray(sheets)) return sheets
|
|
|
+
|
|
|
+ return sheets.map(sheet => {
|
|
|
+ if (!sheet) return sheet
|
|
|
+
|
|
|
+ // 判断单元格是否真的有值(值 / 显示值 / 公式 / 富文本)
|
|
|
+ const hasRealValue = (cell) => {
|
|
|
+ if (!cell || typeof cell !== 'object') return false
|
|
|
+ const v = cell.v
|
|
|
+ if (v && typeof v === 'object' && v !== null) {
|
|
|
+ // celldata 格式
|
|
|
+ if (v.v !== undefined && v.v !== null && v.v !== '') return true
|
|
|
+ if (v.m !== undefined && v.m !== null && v.m !== '') return true
|
|
|
+ if (v.f !== undefined && v.f !== null && v.f !== '') return true
|
|
|
+ if (v.ct && Array.isArray(v.ct.s) && v.ct.s.length > 0) {
|
|
|
+ const txt = v.ct.s
|
|
|
+ .map(x => (x && x.v !== undefined && x.v !== null) ? x.v : '')
|
|
|
+ .join('')
|
|
|
+ if (txt !== '') return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ // data 数组格式
|
|
|
+ if (v !== undefined && v !== null && v !== '') return true
|
|
|
+ if (cell.m !== undefined && cell.m !== null && cell.m !== '') return true
|
|
|
+ if (cell.f !== undefined && cell.f !== null && cell.f !== '') return true
|
|
|
+ if (cell.ct && Array.isArray(cell.ct.s) && cell.ct.s.length > 0) {
|
|
|
+ const txt = cell.ct.s
|
|
|
+ .map(x => (x && x.v !== undefined && x.v !== null) ? x.v : '')
|
|
|
+ .join('')
|
|
|
+ if (txt !== '') return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ // 判断是否有"有意义的样式"(背景、字体、字号、边框、非默认格式等)
|
|
|
+ const hasMeaningfulStyle = (cell) => {
|
|
|
+ if (!cell || typeof cell !== 'object') return false
|
|
|
+ const v = (cell.v && typeof cell.v === 'object') ? cell.v : cell
|
|
|
+ if (v.bg) return true // 背景色
|
|
|
+ if (v.bd) return true // 边框
|
|
|
+ if (v.fc && v.fc !== '#000000') return true // 字体色非黑
|
|
|
+ if (v.ff && v.ff !== '宋体') return true // 非默认字体
|
|
|
+ if (v.fs && v.fs !== 11) return true // 非默认字号
|
|
|
+ if (v.bl === 1 || v.it === 1 || v.un === 1 || v.cl === 1) return true
|
|
|
+ if (v.ct && v.ct.fa && v.ct.fa !== 'General') return true // 非默认数字格式
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ // ========== 处理 celldata ==========
|
|
|
+ if (Array.isArray(sheet.celldata)) {
|
|
|
+ // 先过滤掉"既没值也没意义样式"的垃圾项(如 {tb:1})
|
|
|
+ sheet.celldata = sheet.celldata.filter(item => {
|
|
|
+ if (!item || typeof item.r !== 'number' || typeof item.c !== 'number') return false
|
|
|
+ return hasRealValue(item) || hasMeaningfulStyle(item)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // ========== 重新计算真实行列边界 ==========
|
|
|
+ let maxRow = -1
|
|
|
+ let maxCol = -1
|
|
|
+
|
|
|
+ if (Array.isArray(sheet.celldata)) {
|
|
|
+ sheet.celldata.forEach(item => {
|
|
|
+ if (item.r > maxRow) maxRow = item.r
|
|
|
+ if (item.c > maxCol) maxCol = item.c
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Array.isArray(sheet.data)) {
|
|
|
+ for (let r = 0; r < sheet.data.length; r++) {
|
|
|
+ const row = sheet.data[r]
|
|
|
+ if (!Array.isArray(row)) continue
|
|
|
+ for (let c = 0; c < row.length; c++) {
|
|
|
+ const cell = row[c]
|
|
|
+ if (cell && (hasRealValue(cell) || hasMeaningfulStyle(cell))) {
|
|
|
+ if (r > maxRow) maxRow = r
|
|
|
+ if (c > maxCol) maxCol = c
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 强制重设 row / column(不再有"比大小"的旧逻辑)
|
|
|
+ if (maxRow >= 0 && maxCol >= 0) {
|
|
|
+ sheet.row = maxRow + 1
|
|
|
+ sheet.column = maxCol + 1
|
|
|
+
|
|
|
+ // 同步裁 data 数组
|
|
|
+ if (Array.isArray(sheet.data) && sheet.data.length > sheet.row) {
|
|
|
+ sheet.data = sheet.data.slice(0, sheet.row)
|
|
|
+ }
|
|
|
+ if (Array.isArray(sheet.data)) {
|
|
|
+ sheet.data = sheet.data.map(row =>
|
|
|
+ Array.isArray(row) ? row.slice(0, sheet.column) : row
|
|
|
+ )
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 完全没内容
|
|
|
+ sheet.row = 1
|
|
|
+ sheet.column = 1
|
|
|
+ sheet.celldata = []
|
|
|
+ sheet.data = [[{ v: '' }]]
|
|
|
+ }
|
|
|
+
|
|
|
+ // 裁 rowlen / columnlen
|
|
|
+ if (Array.isArray(sheet.rowlen)) {
|
|
|
+ sheet.rowlen = sheet.rowlen.slice(0, sheet.row)
|
|
|
+ }
|
|
|
+ if (Array.isArray(sheet.columnlen)) {
|
|
|
+ sheet.columnlen = sheet.columnlen.slice(0, sheet.column)
|
|
|
+ }
|
|
|
+
|
|
|
+ return sheet
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
}
|