fix(打印能力): 修复双端打印差异
build.yaml / build (push) Successful in 48s

This commit is contained in:
xiaoqidun committed 2026-10-24 21:19:49 +08:00
1 parent 0ae9ad6562
commit e73db10de1
2 files changed
+23 -12

No files matched your search

+1 -1
View File
@@ -684,7 +684,7 @@
<div class="form-row"><label for="printParity">奇偶筛选</label><div class="print-field"><select id="printParity"><option value="all">全部页面</option><option value="odd">奇数页面</option><option value="even">偶数页面</option></select><label class="print-check"><input id="printReverse" type="checkbox">逆序打印</label></div></div> <div class="form-row"><label for="printParity">奇偶筛选</label><div class="print-field"><select id="printParity"><option value="all">全部页面</option><option value="odd">奇数页面</option><option value="even">偶数页面</option></select><label class="print-check"><input id="printReverse" type="checkbox">逆序打印</label></div></div>
<div class="form-row"><label for="printMode">页面排版</label><select id="printMode"><option value="single">单页打印</option><option value="multiple">多页合印</option><option value="booklet">小册打印</option><option value="poster">海报拼接</option></select></div> <div class="form-row"><label for="printMode">页面排版</label><select id="printMode"><option value="single">单页打印</option><option value="multiple">多页合印</option><option value="booklet">小册打印</option><option value="poster">海报拼接</option></select></div>
<div class="form-row"><label for="printPaper">纸张大小</label><select id="printPaper"><option value="210,297">A4</option><option value="297,420">A3</option><option value="148,210">A5</option><option value="176,250">B5</option><option value="215.9,279.4">Letter</option><option value="215.9,355.6">Legal</option><option value="source">原页面尺寸</option><option value="custom">自定义</option></select></div> <div class="form-row"><label for="printPaper">纸张大小</label><select id="printPaper"><option value="210,297">A4</option><option value="297,420">A3</option><option value="148,210">A5</option><option value="176,250">B5</option><option value="215.9,279.4">Letter</option><option value="215.9,355.6">Legal</option><option value="source">原页面尺寸</option><option value="custom">自定义</option></select></div>
<div class="form-row" data-print-row="paper" hidden><label for="printWidth">纸张尺寸</label><div class="print-field"><input id="printWidth" type="number" value="210" min="1" max="5000" step="0.1" title="纸张宽度"><span>×</span><input id="printHeight" type="number" value="297" min="1" max="5000" step="0.1" title="纸张高度"><span>mm</span></div></div> <div class="form-row" data-print-row="paper" hidden><label for="printWidth">纸张尺寸</label><div class="print-field"><input id="printWidth" type="number" value="210" min="1" max="5000" step="any" title="纸张宽度"><span>×</span><input id="printHeight" type="number" value="297" min="1" max="5000" step="any" title="纸张高度"><span>mm</span></div></div>
<div class="form-row"><label for="printOrientation">纸张方向</label><select id="printOrientation"><option value="portrait">纵向</option><option value="landscape">横向</option></select></div> <div class="form-row"><label for="printOrientation">纸张方向</label><select id="printOrientation"><option value="portrait">纵向</option><option value="landscape">横向</option></select></div>
<div class="form-row"><label for="printScaling">页面缩放</label><select id="printScaling"><option value="shrink">缩小大页</option><option value="fit">适合纸张</option><option value="actual">实际大小</option><option value="custom">自定比例</option></select></div> <div class="form-row"><label for="printScaling">页面缩放</label><select id="printScaling"><option value="shrink">缩小大页</option><option value="fit">适合纸张</option><option value="actual">实际大小</option><option value="custom">自定比例</option></select></div>
<div class="form-row" data-print-row="scale" hidden><label for="printScale">缩放比例</label><div class="print-field"><input id="printScale" type="number" min="1" max="10000" value="100" step="1"><span>%</span></div></div> <div class="form-row" data-print-row="scale" hidden><label for="printScale">缩放比例</label><div class="print-field"><input id="printScale" type="number" min="1" max="10000" value="100" step="1"><span>%</span></div></div>
+22 -11
View File
@@ -37,7 +37,9 @@ export class PrintDialog {
this.side = 0; this.side = 0;
this.sequence++; this.sequence++;
this.el.Form.reset(); this.el.Form.reset();
this.mode = null; this.orientation = this.el.Orientation.value;
this.duplexBeforeBooklet = this.el.Duplex.value;
this.scalingBeforeBooklet = this.el.Scaling.value;
try { try {
const saved = JSON.parse(localStorage.getItem("ofdgo.print.v2") || "null"); const saved = JSON.parse(localStorage.getItem("ofdgo.print.v2") || "null");
for (const [name, value] of Object.entries(saved || {})) { for (const [name, value] of Object.entries(saved || {})) {
@@ -46,6 +48,7 @@ export class PrintDialog {
else if (node && typeof value === "string") node.value = value; else if (node && typeof value === "string") node.value = value;
} }
} catch {} } catch {}
this.mode = this.el.Mode.value;
if (this.el.BookletSheets.value === "0") this.el.BookletSheets.value = ""; if (this.el.BookletSheets.value === "0") this.el.BookletSheets.value = "";
this.el.Scope.value = context.selection.length ? "selected" : "all"; this.el.Scope.value = context.selection.length ? "selected" : "all";
this.el.Scope.querySelector('option[value="selected"]').disabled = !context.selection.length; this.el.Scope.querySelector('option[value="selected"]').disabled = !context.selection.length;
@@ -64,12 +67,16 @@ export class PrintDialog {
this.loading = true; this.loading = true;
this.controls(); this.controls();
this.el.Printer.innerHTML = this.printerOptions; this.el.Printer.innerHTML = this.printerOptions;
const browser = this.el.Printer.querySelector('option[value="browser"]');
browser.disabled = !!this.nativeHost || typeof window.print !== "function";
if (browser.disabled) this.el.Printer.value = "pdf";
try { try {
if (this.nativeHost) { if (this.nativeHost) {
this.el.Printer.querySelector('option[value="browser"]').textContent = "系统打印"; browser.textContent = "系统打印";
const capabilities = await this.nativeHost.call("print.capabilities", {}); const capabilities = await this.nativeHost.call("print.capabilities", {});
if (!this.current(sequence)) return; if (!this.current(sequence)) return;
this.capabilities = capabilities; this.capabilities = capabilities;
browser.disabled = !capabilities.systemDialog;
const printers = capabilities.direct ? await this.nativeHost.call("print.printers", {}) : []; const printers = capabilities.direct ? await this.nativeHost.call("print.printers", {}) : [];
if (!this.current(sequence)) return; if (!this.current(sequence)) return;
for (const printer of printers) this.el.Printer.add(new Option(printer.name, `device:${printer.name}`)); for (const printer of printers) this.el.Printer.add(new Option(printer.name, `device:${printer.name}`));
@@ -114,7 +121,7 @@ export class PrintDialog {
this.el.Orientation.value = profile.options.orientation; this.el.Orientation.value = profile.options.orientation;
this.el.Copies.max = Math.min(999, profile.maxCopies || 999); this.el.Copies.max = Math.min(999, profile.maxCopies || 999);
this.el.Copies.value = 1; this.el.Copies.value = 1;
this.el.Grayscale.checked = !profile.color; this.el.Grayscale.checked = profile.options.color === "grayscale";
this.el.Grayscale.disabled = !profile.color; this.el.Grayscale.disabled = !profile.color;
this.el.Properties.hidden = !profile.properties; this.el.Properties.hidden = !profile.properties;
} else this.el.Grayscale.disabled = false; } else this.el.Grayscale.disabled = false;
@@ -125,11 +132,12 @@ export class PrintDialog {
async properties() { async properties() {
if (!this.isDevice() || this.running || this.loading) return; if (!this.isDevice() || this.running || this.loading) return;
const sequence = this.sequence;
this.loading = true; this.loading = true;
this.controls(); this.controls();
try { await this.nativeHost.call("print.properties", {printer:this.el.Printer.value.slice(7)}); } try { await this.nativeHost.call("print.properties", {printer:this.el.Printer.value.slice(7)}); }
catch (error) { this.el.Status.textContent = error.message; } catch (error) { if (this.current(sequence)) this.el.Status.textContent = error.message; }
finally { this.loading = false; if (this.current()) await this.choosePrinter(); } finally { if (this.current(sequence)) { this.loading = false; await this.choosePrinter(); } }
} }
deviceOptions() { deviceOptions() {
@@ -150,7 +158,7 @@ export class PrintDialog {
return value; return value;
}; };
let [paperWidth, paperHeight] = el.Paper.value === "source" ? [0, 0] : el.Paper.value === "custom" ? [number("Width"), number("Height")] : el.Paper.value.split(",").map(Number); let [paperWidth, paperHeight] = el.Paper.value === "source" ? [0, 0] : el.Paper.value === "custom" ? [number("Width"), number("Height")] : el.Paper.value.split(",").map(Number);
if (el.Orientation.value === "landscape" && paperWidth < paperHeight) [paperWidth, paperHeight] = [paperHeight, paperWidth]; if (el.Orientation.value === "landscape") [paperWidth, paperHeight] = [paperHeight, paperWidth];
if (profile) [paperWidth, paperHeight] = [profile.width, profile.height]; if (profile) [paperWidth, paperHeight] = [profile.width, profile.height];
const scope = el.Scope.value; const scope = el.Scope.value;
const [rows, columns] = el.Grid.value.split(",").map(Number); const [rows, columns] = el.Grid.value.split(",").map(Number);
@@ -164,9 +172,9 @@ export class PrintDialog {
margins:{top:number("MarginTop"), right:number("MarginRight"), bottom:number("MarginBottom"), left:number("MarginLeft")}, margins:{top:number("MarginTop"), right:number("MarginRight"), bottom:number("MarginBottom"), left:number("MarginLeft")},
...(profile ? {printableArea:{x:profile.printableArea.x, y:profile.printableArea.y, w:profile.printableArea.width, h:profile.printableArea.height}} : {}), ...(profile ? {printableArea:{x:profile.printableArea.x, y:profile.printableArea.y, w:profile.printableArea.width, h:profile.printableArea.height}} : {}),
copies:this.isDevice() ? number("Copies") : 1, collate:el.Collation.checked, duplex, copies:this.isDevice() ? number("Copies") : 1, collate:el.Collation.checked, duplex,
scale:number("Scale") / 100, rotate:el.Rotate.checked, reverse:el.Reverse.checked !== (manual && el.Sides.value === "back" && el.BackReverse.checked), parity:el.Parity.value, sides:el.Sides.value, scale:el.Scale.disabled ? 1 : number("Scale") / 100, rotate:el.Rotate.checked, reverse:el.Reverse.checked !== (manual && el.Sides.value === "back" && el.BackReverse.checked), parity:el.Parity.value, sides:el.Sides.value,
backRotate:manual && el.BackRotate.checked, backRotate:manual && el.BackRotate.checked,
rows, columns, pageOrder:el.PageOrder.value, gap:number("Gap"), binding:el.Binding.value, bookletSheets:el.BookletSheets.value ? number("BookletSheets") : 0, overlap:number("Overlap"), rows, columns, pageOrder:el.PageOrder.value, gap:el.Gap.disabled ? 0 : number("Gap"), binding:el.Binding.value, bookletSheets:!el.BookletSheets.disabled && el.BookletSheets.value ? number("BookletSheets") : 0, overlap:el.Overlap.disabled ? 0 : number("Overlap"),
...(el.Mode.value === "booklet" ? {sheetFrom:el.SheetFrom.value ? number("SheetFrom") : 0, sheetTo:el.SheetTo.value ? number("SheetTo") : 0} : {}), ...(el.Mode.value === "booklet" ? {sheetFrom:el.SheetFrom.value ? number("SheetFrom") : 0, sheetTo:el.SheetTo.value ? number("SheetTo") : 0} : {}),
contents:el.Contents.value, grayscale:el.Grayscale.checked, borders:el.Borders.checked, cropMarks:el.CropMarks.checked, raster:el.Raster.checked, dpi:number("DPI"), tileOnlyLarge:el.TileOnlyLarge.checked, labels:el.Labels.checked contents:el.Contents.value, grayscale:el.Grayscale.checked, borders:el.Borders.checked, cropMarks:el.CropMarks.checked, raster:el.Raster.checked, dpi:number("DPI"), tileOnlyLarge:el.TileOnlyLarge.checked, labels:el.Labels.checked
}; };
@@ -201,6 +209,7 @@ export class PrintDialog {
const rows = {device:this.isDevice(), sides:el.Duplex.value !== "simplex", manual:isManual, range:el.Scope.value === "range", paper:el.Paper.value === "custom", scale:el.Scaling.value === "custom" || mode === "poster", multiple:mode === "multiple", gap:mode === "multiple" || mode === "booklet", booklet:mode === "booklet", poster:mode === "poster"}; const rows = {device:this.isDevice(), sides:el.Duplex.value !== "simplex", manual:isManual, range:el.Scope.value === "range", paper:el.Paper.value === "custom", scale:el.Scaling.value === "custom" || mode === "poster", multiple:mode === "multiple", gap:mode === "multiple" || mode === "booklet", booklet:mode === "booklet", poster:mode === "poster"};
for (const row of el.Panel.querySelectorAll("[data-print-row]")) { for (const row of el.Panel.querySelectorAll("[data-print-row]")) {
row.hidden = !rows[row.dataset.printRow]; row.hidden = !rows[row.dataset.printRow];
for (const input of row.querySelectorAll("input, select")) input.disabled = row.hidden;
} }
el.Scaling.disabled = mode === "poster" || mode === "booklet"; el.Scaling.disabled = mode === "poster" || mode === "booklet";
el.DPI.disabled = !el.Raster.checked && !el.Grayscale.checked; el.DPI.disabled = !el.Raster.checked && !el.Grayscale.checked;
@@ -246,7 +255,8 @@ export class PrintDialog {
current(sequence = this.sequence) { return this.el.Panel.open && sequence === this.sequence && this.context && !this.context.tab.closed; } current(sequence = this.sequence) { return this.el.Panel.open && sequence === this.sequence && this.context && !this.context.tab.closed; }
async preview(index) { async preview(index) {
if (!this.layout || this.running || index < 0 || index >= this.layout.sides.length) return; if (!this.layout || this.running) return;
if (!Number.isInteger(index) || index < 0 || index >= this.layout.sides.length) { this.controls(); return; }
const sequence = this.sequence, preview = this.previewSequence = (this.previewSequence || 0) + 1; const sequence = this.sequence, preview = this.previewSequence = (this.previewSequence || 0) + 1;
this.side = index; this.side = index;
this.controls(); this.controls();
@@ -266,7 +276,7 @@ export class PrintDialog {
this.previewURL = url; this.previewURL = url;
this.el.Preview.replaceChildren(img); this.el.Preview.replaceChildren(img);
this.resize(); this.resize();
} catch (error) { if (this.current(sequence)) { this.el.Status.textContent = error.message; this.layout = null; this.controls(); } } } catch (error) { if (this.current(sequence) && preview === this.previewSequence) { this.el.Status.textContent = error.message; this.layout = null; this.controls(); } }
} }
resize() { resize() {
@@ -336,7 +346,7 @@ export class PrintDialog {
if (!this.current(sequence)) throw new DOMException("打印已取消", "AbortError"); if (!this.current(sequence)) throw new DOMException("打印已取消", "AbortError");
this.previousTitle = document.title; this.previousTitle = document.title;
document.title = context.name; document.title = context.name;
this.waiting = job.deviceOptions ? "native" : "browser"; this.waiting = this.nativeHost ? "native" : "browser";
this.controls(); this.controls();
this.el.Status.textContent = job.deviceOptions ? "正在提交打印作业" : "正在打开打印窗口"; this.el.Status.textContent = job.deviceOptions ? "正在提交打印作业" : "正在打开打印窗口";
if (job.deviceOptions) { if (job.deviceOptions) {
@@ -353,6 +363,7 @@ export class PrintDialog {
const capabilities = await this.nativeHost.call("print.capabilities", {}); const capabilities = await this.nativeHost.call("print.capabilities", {});
if (!this.current(sequence)) throw new DOMException("打印已取消", "AbortError"); if (!this.current(sequence)) throw new DOMException("打印已取消", "AbortError");
if (!capabilities.systemDialog) throw new Error("当前运行时不支持系统打印"); if (!capabilities.systemDialog) throw new Error("当前运行时不支持系统打印");
if (!capabilities.dialogCompletion) this.waiting = "browser";
await this.nativeHost.call("print.dialog", {kind:"system"}); await this.nativeHost.call("print.dialog", {kind:"system"});
if (this.current(sequence) && capabilities.dialogCompletion) this.finishPrint(); if (this.current(sequence) && capabilities.dialogCompletion) this.finishPrint();
} else if (typeof window.print === "function") window.print(); } else if (typeof window.print === "function") window.print();