This commit is contained in:
zlwu committed 2026-08-28 17:38:37 +08:00
1 parent 3d98104455
commit 0d52442902
76 files changed
+21994 -12642

No files matched your search

+2 -1
View File
@@ -63,7 +63,8 @@ export const setWorktab = (to: RouteLocationNormalized): void => {
keepAlive: meta.keepAlive as boolean,
params,
query,
fixedTab: meta.fixedTab as boolean
fixedTab: meta.fixedTab as boolean,
worktabMulti: Boolean(meta.worktabMulti)
})
}
}
+201
View File
@@ -0,0 +1,201 @@
import ExcelJS from 'exceljs'
import FileSaver from 'file-saver'
export type OaHistoryExportColumn = {
key: string
label: string
kind?: 'business' | 'related'
subLabel?: string
}
export type OaHistoryExportFixedColumn = {
key: string
label: string
}
export type OaHistoryExportRow = {
index?: number
id?: string
business?: Record<string, string>
fixed?: Record<string, string>
}
export type OaHistoryExportPayload = {
appName?: string
businessColumns?: OaHistoryExportColumn[]
fixedColumns?: OaHistoryExportFixedColumn[]
rows?: OaHistoryExportRow[]
}
type SheetColumn = {
key: string
label: string
subLabel?: string
width: number
}
const HEADER_FILL: ExcelJS.Fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFF0FDF4' }
}
const HEADER_FONT: Partial<ExcelJS.Font> = {
bold: true,
color: { argb: 'FF065F46' },
size: 11
}
const BORDER: Partial<ExcelJS.Borders> = {
top: { style: 'thin', color: { argb: 'FFE2E8F0' } },
left: { style: 'thin', color: { argb: 'FFE2E8F0' } },
bottom: { style: 'thin', color: { argb: 'FFE2E8F0' } },
right: { style: 'thin', color: { argb: 'FFE2E8F0' } }
}
function pad(n: number) {
return String(n).padStart(2, '0')
}
function exportTimestamp() {
const d = new Date()
return (
`${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}` +
`${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`
)
}
function sanitizeFilename(name: string) {
return String(name || '历史记录')
.replace(/[\\/:*?"<>|]/g, '_')
.trim()
}
function styleHeaderCell(cell: ExcelJS.Cell) {
cell.fill = HEADER_FILL
cell.font = HEADER_FONT
cell.alignment = { vertical: 'middle', horizontal: 'center', wrapText: true }
cell.border = BORDER
}
function styleBodyCell(cell: ExcelJS.Cell) {
cell.alignment = { vertical: 'top', horizontal: 'left', wrapText: true }
cell.border = BORDER
}
function buildSheetColumns(payload: OaHistoryExportPayload): SheetColumn[] {
const cols: SheetColumn[] = [
{ key: '__index', label: '序号', width: 8 },
{ key: '__id', label: '数据id', width: 28 }
]
for (const col of payload.businessColumns || []) {
if (col.kind === 'related') {
cols.push({ key: col.key, label: col.label, width: 18 })
cols.push({
key: `${col.key}__titles`,
label: col.label,
subLabel: col.subLabel || '关联标题',
width: 24
})
} else {
cols.push({ key: col.key, label: col.label, width: 20 })
}
}
for (const col of payload.fixedColumns || []) {
cols.push({
key: col.key,
label: col.label,
width: col.key === 'approvalRecord' ? 48 : 18
})
}
return cols
}
function cellValue(row: OaHistoryExportRow, key: string) {
if (key === '__index') return row.index ?? ''
if (key === '__id') return row.id ?? ''
const fixed = row.fixed || {}
const business = row.business || {}
if (key in fixed) return fixed[key] ?? ''
return business[key] ?? ''
}
function writeHeaderRows(worksheet: ExcelJS.Worksheet, sheetColumns: SheetColumn[]) {
const headerRow1 = worksheet.getRow(1)
const headerRow2 = worksheet.getRow(2)
headerRow1.height = 24
headerRow2.height = 20
let colIdx = 1
let i = 0
while (i < sheetColumns.length) {
const col = sheetColumns[i]
const next = sheetColumns[i + 1]
const isRelatedPair = Boolean(next?.subLabel && next.label === col.label && !col.subLabel)
if (isRelatedPair) {
const c1 = headerRow1.getCell(colIdx)
c1.value = col.label
styleHeaderCell(c1)
worksheet.mergeCells(1, colIdx, 1, colIdx + 1)
const c2a = headerRow2.getCell(colIdx)
c2a.value = ''
styleHeaderCell(c2a)
const c2b = headerRow2.getCell(colIdx + 1)
c2b.value = next.subLabel || '关联标题'
styleHeaderCell(c2b)
colIdx += 2
i += 2
continue
}
const c1 = headerRow1.getCell(colIdx)
c1.value = col.label
styleHeaderCell(c1)
const c2 = headerRow2.getCell(colIdx)
c2.value = ''
styleHeaderCell(c2)
worksheet.mergeCells(1, colIdx, 2, colIdx)
colIdx += 1
i += 1
}
}
export async function downloadOaHistoryExcel(
payload: OaHistoryExportPayload,
options?: { filenamePrefix?: string }
) {
const rows = payload.rows || []
if (!rows.length) {
throw new Error('暂无可导出数据')
}
const sheetColumns = buildSheetColumns(payload)
const workbook = new ExcelJS.Workbook()
const sheetName = sanitizeFilename(payload.appName || '历史记录').slice(0, 31)
const worksheet = workbook.addWorksheet(sheetName)
worksheet.columns = sheetColumns.map((c) => ({ width: c.width }))
writeHeaderRows(worksheet, sheetColumns)
rows.forEach((row, rowIndex) => {
const excelRow = worksheet.getRow(rowIndex + 3)
sheetColumns.forEach((col, i) => {
const cell = excelRow.getCell(i + 1)
cell.value = cellValue(row, col.key)
styleBodyCell(cell)
})
})
worksheet.views = [{ state: 'frozen', ySplit: 2 }]
const buffer = await workbook.xlsx.writeBuffer()
const prefix = sanitizeFilename(options?.filenamePrefix || payload.appName || '历史记录')
const blob = new Blob([buffer], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
})
FileSaver.saveAs(blob, `${prefix}-${exportTimestamp()}.xlsx`)
}
@@ -74,7 +74,7 @@ export const PRODUCTION_WORK_ORDER_TABS: ProductionWorkOrderTabConfig[] = [
},
{
key: 'temporary',
label: '临时生产',
label: '扩增/传代生产',
group: 'temporary',
workOrderType: 'temporary',
listLayout: 'standalone'
+8
View File
@@ -119,6 +119,14 @@ const SOP_SHARED_CONTENT_STYLE = `
.sop-body-table tbody {
height: 100%;
}
.sop-header-table.sop-row-hidden,
.sop-body-table tr.sop-row-hidden {
display: none !important;
}
.sop-record.sop-a4:has(.sop-row-hidden) {
min-height: auto !important;
height: auto !important;
}
.sop-body-table tr.sop-fill-row {
height: 100%;
}