feat: 完善视频任务与 KOC 资源库
This commit is contained in:
@@ -13,6 +13,9 @@ export type RecoveryWorkbookRow = {
|
||||
images: Array<{
|
||||
column: number;
|
||||
image: RecoveryWorkbookImage;
|
||||
offsetX?: number;
|
||||
maxWidth?: number;
|
||||
maxHeight?: number;
|
||||
}>;
|
||||
hyperlinks?: Array<{
|
||||
column: number;
|
||||
@@ -25,6 +28,7 @@ type WorkbookOptions = {
|
||||
headers: string[];
|
||||
columnWidths: number[];
|
||||
rows: RecoveryWorkbookRow[];
|
||||
hiddenColumns?: number[];
|
||||
};
|
||||
|
||||
const XML_HEADER = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
|
||||
@@ -130,9 +134,17 @@ function imageDimensions(image: RecoveryWorkbookImage) {
|
||||
return { width: 4, height: 3 };
|
||||
}
|
||||
|
||||
function imageDisplaySize(image: RecoveryWorkbookImage) {
|
||||
function imageDisplaySize(
|
||||
image: RecoveryWorkbookImage,
|
||||
maxWidth = 160,
|
||||
maxHeight = 150,
|
||||
) {
|
||||
const dimensions = imageDimensions(image);
|
||||
const scale = Math.min(160 / dimensions.width, 150 / dimensions.height, 1);
|
||||
const scale = Math.min(
|
||||
maxWidth / dimensions.width,
|
||||
maxHeight / dimensions.height,
|
||||
1,
|
||||
);
|
||||
return {
|
||||
width: Math.max(28, Math.round(dimensions.width * scale)),
|
||||
height: Math.max(28, Math.round(dimensions.height * scale)),
|
||||
@@ -152,6 +164,14 @@ export function buildRecoveryWorkbook(options: WorkbookOptions) {
|
||||
const imageEntries = options.rows.flatMap((row, rowIndex) =>
|
||||
row.images.map((item) => ({ ...item, row: rowIndex + 1 })),
|
||||
);
|
||||
const imageCells = new Set<string>();
|
||||
imageEntries.forEach((entry) => {
|
||||
const key = `${entry.row}:${entry.column}`;
|
||||
if (imageCells.has(key)) {
|
||||
throw new Error("Excel 单元格内只能嵌入一张图片,请为每张图片分配独立列");
|
||||
}
|
||||
imageCells.add(key);
|
||||
});
|
||||
const hyperlinkEntries = options.rows.flatMap((row, rowIndex) =>
|
||||
(row.hyperlinks ?? [])
|
||||
.map((item) => ({
|
||||
@@ -178,7 +198,7 @@ export function buildRecoveryWorkbook(options: WorkbookOptions) {
|
||||
const reference = `${columnName(columnIndex)}${number}`;
|
||||
const value = row.cells[columnIndex] ?? "";
|
||||
if (imageColumns.has(columnIndex)) {
|
||||
return inlineCell(reference, value || "见图", 4);
|
||||
return inlineCell(reference, value, 4);
|
||||
}
|
||||
return typeof value === "number"
|
||||
? numberCell(reference, value, 3)
|
||||
@@ -196,17 +216,22 @@ export function buildRecoveryWorkbook(options: WorkbookOptions) {
|
||||
const columns = options.headers
|
||||
.map((_, index) => {
|
||||
const width = Math.min(70, Math.max(8, options.columnWidths[index] ?? 14));
|
||||
return `<col min="${index + 1}" max="${index + 1}" width="${width}" customWidth="1"/>`;
|
||||
const hidden = options.hiddenColumns?.includes(index) ? ' hidden="1"' : "";
|
||||
return `<col min="${index + 1}" max="${index + 1}" width="${width}" customWidth="1"${hidden}/>`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
const drawingXml = imageEntries.length
|
||||
? `${XML_HEADER}<xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">${imageEntries
|
||||
.map((entry, index) => {
|
||||
const size = imageDisplaySize(entry.image);
|
||||
const width = size.width * 9525;
|
||||
const height = size.height * 9525;
|
||||
return `<xdr:oneCellAnchor><xdr:from><xdr:col>${entry.column}</xdr:col><xdr:colOff>57150</xdr:colOff><xdr:row>${entry.row}</xdr:row><xdr:rowOff>57150</xdr:rowOff></xdr:from><xdr:ext cx="${width}" cy="${height}"/><xdr:pic><xdr:nvPicPr><xdr:cNvPr id="${index + 1}" name="${cleanXmlText(entry.image.description || `图片${index + 1}`)}"/><xdr:cNvPicPr><a:picLocks noChangeAspect="1"/></xdr:cNvPicPr></xdr:nvPicPr><xdr:blipFill><a:blip r:embed="rId${index + 1}"/><a:stretch><a:fillRect/></a:stretch></xdr:blipFill><xdr:spPr><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></xdr:spPr></xdr:pic><xdr:clientData/></xdr:oneCellAnchor>`;
|
||||
const size = imageDisplaySize(
|
||||
entry.image,
|
||||
entry.maxWidth ?? 160,
|
||||
entry.maxHeight ?? 150,
|
||||
);
|
||||
const offsetX = entry.offsetX ?? 6;
|
||||
const offsetY = 6;
|
||||
return `<xdr:twoCellAnchor editAs="twoCell"><xdr:from><xdr:col>${entry.column}</xdr:col><xdr:colOff>${offsetX * 9525}</xdr:colOff><xdr:row>${entry.row}</xdr:row><xdr:rowOff>${offsetY * 9525}</xdr:rowOff></xdr:from><xdr:to><xdr:col>${entry.column}</xdr:col><xdr:colOff>${(offsetX + size.width) * 9525}</xdr:colOff><xdr:row>${entry.row}</xdr:row><xdr:rowOff>${(offsetY + size.height) * 9525}</xdr:rowOff></xdr:to><xdr:pic><xdr:nvPicPr><xdr:cNvPr id="${index + 1}" name="${cleanXmlText(entry.image.description || `图片${index + 1}`)}"/><xdr:cNvPicPr><a:picLocks noChangeAspect="1"/></xdr:cNvPicPr></xdr:nvPicPr><xdr:blipFill><a:blip r:embed="rId${index + 1}"/><a:stretch><a:fillRect/></a:stretch></xdr:blipFill><xdr:spPr><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></xdr:spPr></xdr:pic><xdr:clientData/></xdr:twoCellAnchor>`;
|
||||
})
|
||||
.join("")}</xdr:wsDr>`
|
||||
: "";
|
||||
@@ -227,7 +252,10 @@ export function buildRecoveryWorkbook(options: WorkbookOptions) {
|
||||
const imageContentTypes = [...imageFormats.entries()]
|
||||
.map(([extension, contentType]) => `<Default Extension="${extension}" ContentType="${contentType}"/>`)
|
||||
.join("");
|
||||
const contentTypes = `${XML_HEADER}<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/>${imageContentTypes}<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>${imageEntries.length ? '<Override PartName="/xl/drawings/drawing1.xml" ContentType="application/vnd.openxmlformats-officedocument.drawing+xml"/>' : ""}<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/></Types>`;
|
||||
const drawingContentType = imageEntries.length
|
||||
? '<Override PartName="/xl/drawings/drawing1.xml" ContentType="application/vnd.openxmlformats-officedocument.drawing+xml"/>'
|
||||
: "";
|
||||
const contentTypes = `${XML_HEADER}<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/>${imageContentTypes}<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>${drawingContentType}<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/></Types>`;
|
||||
const hyperlinkRelationshipOffset = imageEntries.length ? 2 : 1;
|
||||
const hyperlinksXml = hyperlinkEntries.length
|
||||
? `<hyperlinks>${hyperlinkEntries
|
||||
@@ -266,7 +294,9 @@ export function buildRecoveryWorkbook(options: WorkbookOptions) {
|
||||
}
|
||||
if (imageEntries.length) {
|
||||
files["xl/drawings/drawing1.xml"] = strToU8(drawingXml);
|
||||
files["xl/drawings/_rels/drawing1.xml.rels"] = strToU8(drawingRelationships);
|
||||
files["xl/drawings/_rels/drawing1.xml.rels"] = strToU8(
|
||||
drawingRelationships,
|
||||
);
|
||||
imageEntries.forEach((entry, index) => {
|
||||
const format = imageFormat(entry.image.contentType, entry.image.bytes);
|
||||
files[`xl/media/image${index + 1}.${format.extension}`] = entry.image.bytes;
|
||||
|
||||
Reference in New Issue
Block a user