@innertia-solutions/innertia-nuxt 0.1.49 → 0.1.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/Files/Preview.vue +73 -12
- package/package.json +1 -1
|
@@ -60,10 +60,13 @@ const viewerType = computed(() => {
|
|
|
60
60
|
if (mime === 'text/markdown' || ['md', 'markdown'].includes(ext)) return 'markdown'
|
|
61
61
|
if (mime === 'text/csv' || ['csv', 'tsv'].includes(ext)) return 'csv'
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
// Office por EXTENSIÓN antes del check de text/*: un .xls/.xlsx/.doc/.docx
|
|
64
|
+
// suele llegar con mime mal detectado (p.ej. text/plain u octet-stream) y aun
|
|
65
|
+
// así debe usar el renderer rico (Excel con pestañas / docx), no el de texto.
|
|
66
|
+
if (['xlsx', 'xls'].includes(ext) || mime.includes('spreadsheet') || mime.includes('excel')) return 'xlsx'
|
|
67
|
+
if (['docx', 'doc'].includes(ext) || mime.includes('wordprocessing') || mime.includes('msword')) return 'docx'
|
|
64
68
|
|
|
65
|
-
if (
|
|
66
|
-
if (['docx','doc'].includes(ext) || mime.includes('wordprocessing') || mime.includes('msword')) return 'docx'
|
|
69
|
+
if (mime.startsWith('text/') || ['log','yml','yaml','env','sh','js','ts','tsx','jsx','css','html','xml','php','py','rb','go','rs','java','sql','toml','ini'].includes(ext)) return 'text'
|
|
67
70
|
|
|
68
71
|
return 'unknown'
|
|
69
72
|
})
|
|
@@ -271,30 +274,88 @@ function buildSheet(ws, workbook) {
|
|
|
271
274
|
return { name: ws.name, cols, rows, firstRow: 1, imageMap }
|
|
272
275
|
}
|
|
273
276
|
|
|
277
|
+
/** Detecta el delimitador más probable de un texto tabular (tab / ; / ,). */
|
|
278
|
+
function detectDelimiter(text) {
|
|
279
|
+
const line = text.split(/\r?\n/).find(l => l.trim()) ?? ''
|
|
280
|
+
const counts = { '\t': (line.match(/\t/g) || []).length, ';': (line.match(/;/g) || []).length, ',': (line.match(/,/g) || []).length }
|
|
281
|
+
const max = Math.max(counts['\t'], counts[';'], counts[','])
|
|
282
|
+
if (max === 0) return null
|
|
283
|
+
return counts['\t'] === max ? '\t' : (counts[';'] === max ? ';' : ',')
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Parser genérico de texto delimitado con soporte de comillas. */
|
|
287
|
+
function parseDelimited(text, delim) {
|
|
288
|
+
const rows = []
|
|
289
|
+
let row = [], cell = '', inQ = false
|
|
290
|
+
for (let i = 0; i < text.length; i++) {
|
|
291
|
+
const c = text[i]
|
|
292
|
+
if (inQ) {
|
|
293
|
+
if (c === '"') { if (text[i + 1] === '"') { cell += '"'; i++ } else inQ = false }
|
|
294
|
+
else cell += c
|
|
295
|
+
} else {
|
|
296
|
+
if (c === '"') inQ = true
|
|
297
|
+
else if (c === delim) { row.push(cell); cell = '' }
|
|
298
|
+
else if (c === '\n') { row.push(cell); rows.push(row); row = []; cell = '' }
|
|
299
|
+
else if (c === '\r') continue
|
|
300
|
+
else cell += c
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (cell.length || row.length) { row.push(cell); rows.push(row) }
|
|
304
|
+
return rows
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** Construye una sheet (misma forma que buildSheet) desde texto delimitado. */
|
|
308
|
+
function buildSheetFromDelimited(text, name = 'Hoja 1') {
|
|
309
|
+
const delim = detectDelimiter(text) ?? '\t'
|
|
310
|
+
const raw = parseDelimited(text, delim).filter(r => r.length && !(r.length === 1 && r[0] === ''))
|
|
311
|
+
const colCount = raw.reduce((m, r) => Math.max(m, r.length), 0)
|
|
312
|
+
const cols = []
|
|
313
|
+
for (let c = 0; c < colCount; c++) cols.push(colLetter(c))
|
|
314
|
+
const rows = raw.map((r, ri) => {
|
|
315
|
+
const rowData = []
|
|
316
|
+
for (let c = 0; c < colCount; c++) {
|
|
317
|
+
rowData.push({ v: r[c] ?? '', align: 'left', t: 'string', address: colLetter(c) + (ri + 1) })
|
|
318
|
+
}
|
|
319
|
+
return rowData
|
|
320
|
+
})
|
|
321
|
+
return { name, cols, rows, firstRow: 1, imageMap: {} }
|
|
322
|
+
}
|
|
323
|
+
|
|
274
324
|
const loadXlsx = async () => {
|
|
275
325
|
if (viewerType.value !== 'xlsx') return
|
|
276
326
|
isLoadingXlsx.value = true
|
|
277
327
|
xlsxError.value = null
|
|
278
328
|
xlsxActiveIdx.value = 0
|
|
329
|
+
let buf = null
|
|
279
330
|
try {
|
|
331
|
+
const res = await fetch(src.value)
|
|
332
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
|
333
|
+
buf = await res.arrayBuffer()
|
|
334
|
+
|
|
280
335
|
const mod = await import('exceljs').catch(() => null)
|
|
281
336
|
const ExcelJS = pickModule(mod, 'Workbook')
|
|
282
337
|
?? (mod?.default?.Workbook ? mod.default : null)
|
|
283
338
|
?? mod
|
|
284
|
-
if (!ExcelJS?.Workbook)
|
|
285
|
-
|
|
286
|
-
throw new Error('Para previsualizar .xlsx instalá la dep: pnpm add exceljs')
|
|
287
|
-
}
|
|
288
|
-
const res = await fetch(src.value)
|
|
289
|
-
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
|
290
|
-
const buf = await res.arrayBuffer()
|
|
339
|
+
if (!ExcelJS?.Workbook) throw new Error('exceljs no disponible')
|
|
340
|
+
|
|
291
341
|
const wb = new ExcelJS.Workbook()
|
|
292
342
|
await wb.xlsx.load(buf)
|
|
293
343
|
const sheets = wb.worksheets.map(ws => buildSheet(ws, wb))
|
|
294
344
|
xlsxWorkbook.value = { sheets }
|
|
295
345
|
} catch (e) {
|
|
296
|
-
|
|
297
|
-
|
|
346
|
+
// Fallback: el archivo no es un .xlsx OOXML válido (típico: un CSV/TSV exportado
|
|
347
|
+
// con extensión .xls, o mime mal detectado). Lo renderizamos como tabla delimitada.
|
|
348
|
+
try {
|
|
349
|
+
const text = new TextDecoder('utf-8').decode(buf ?? new ArrayBuffer(0))
|
|
350
|
+
if (text && text.trim()) {
|
|
351
|
+
xlsxWorkbook.value = { sheets: [buildSheetFromDelimited(text)] }
|
|
352
|
+
} else {
|
|
353
|
+
throw e
|
|
354
|
+
}
|
|
355
|
+
} catch (_) {
|
|
356
|
+
console.error('[FilesPreview] xlsx error', e)
|
|
357
|
+
xlsxError.value = 'No se pudo previsualizar la hoja de cálculo.'
|
|
358
|
+
}
|
|
298
359
|
} finally {
|
|
299
360
|
isLoadingXlsx.value = false
|
|
300
361
|
}
|