mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-31 16:39:26 +08:00
feat(界面设计): 统一材质与弹窗交互
This commit is contained in:
1 parent
4278ed7845
commit
f0047a0eb9
3 files changed
+115
-58
No files matched your search
@@ -203,6 +203,13 @@
|
||||
<div class="about-row"><span>项目仓库</span><a href="https://github.com/xiaoqidun/ofdgo" target="_blank" rel="noreferrer">https://github.com/xiaoqidun/ofdgo</a></div>
|
||||
</form>
|
||||
</dialog>
|
||||
<dialog id="confirmPanel" class="form-dialog confirm-dialog">
|
||||
<form method="dialog">
|
||||
<h2 id="confirmTitle" class="form-title">操作确认</h2>
|
||||
<p class="confirm-message"></p>
|
||||
<div class="form-actions"><button id="confirmCancel" class="button" type="button">取消</button><button class="button" type="submit" value="confirm" autofocus>确定</button></div>
|
||||
</form>
|
||||
</dialog>
|
||||
<dialog id="closePanel" class="form-dialog confirm-dialog">
|
||||
<form method="dialog">
|
||||
<h2 id="closeTitle" class="form-title">关闭文档</h2>
|
||||
|
||||
+54
-23
@@ -13,6 +13,7 @@
|
||||
--accent-soft: rgba(15, 123, 104, 0.12);
|
||||
--selected: #dfefeb;
|
||||
--accent-strong: #095c4e;
|
||||
--accent-hover: #095c4e;
|
||||
--focus: #087f6d;
|
||||
--sidebar-width: clamp(220px, 20vw, 292px);
|
||||
--bar-bg: #fafbfc;
|
||||
@@ -30,7 +31,9 @@
|
||||
--radius-dialog: 8px;
|
||||
--page-space: clamp(14px, 2.5vw, 28px);
|
||||
--shadow-flyout: 0 4px 12px rgba(31, 36, 40, 0.12);
|
||||
--shadow-soft: 0 10px 28px rgba(31, 36, 40, 0.12);
|
||||
--shadow-soft: 0 12px 32px rgba(31, 36, 40, 0.18);
|
||||
--shadow-choice: 0 1px 4px rgb(31 36 40 / 10%);
|
||||
--popup-line: color-mix(in srgb, var(--text) 18%, transparent);
|
||||
--shadow-page: 0 14px 30px rgba(31, 36, 40, 0.16);
|
||||
--base: #eef1f3;
|
||||
--text: #263238;
|
||||
@@ -48,7 +51,7 @@
|
||||
--warning-soft: #fbefd3;
|
||||
--warning-line: #ded0a6;
|
||||
--success-line: #bdd8cc;
|
||||
--backdrop: rgb(31 36 40 / 18%);
|
||||
--backdrop: rgb(0 0 0 / 18%);
|
||||
--control-fill: var(--input);
|
||||
--panel-fill: var(--panel);
|
||||
--well-fill: var(--base);
|
||||
@@ -77,6 +80,7 @@
|
||||
--accent-soft: rgba(101, 203, 182, 0.16);
|
||||
--selected: #24463e;
|
||||
--accent-strong: #65cbb6;
|
||||
--accent-hover: #80d6c4;
|
||||
--focus: #65cbb6;
|
||||
--hover: #343e45;
|
||||
--pressed-overlay: rgb(255 255 255 / 8%);
|
||||
@@ -100,6 +104,7 @@
|
||||
--backdrop: rgb(0 0 0 / 35%);
|
||||
--shadow-flyout: 0 4px 12px #0005;
|
||||
--shadow-soft: 0 12px 36px #0006, 0 2px 6px #0003;
|
||||
--shadow-choice: 0 1px 4px rgb(0 0 0 / 24%);
|
||||
--shadow-page: 0 14px 30px #0005;
|
||||
--dropdown-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23e0e7eb' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 9 7 7 7-7'/%3E%3C/svg%3E");
|
||||
}
|
||||
@@ -697,7 +702,7 @@ a:focus-visible {
|
||||
position: fixed;
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
border: 1px solid var(--line);
|
||||
border: 1px solid var(--popup-line);
|
||||
background: var(--popup-fill);
|
||||
color: var(--text);
|
||||
box-shadow: var(--shadow-flyout);
|
||||
@@ -830,11 +835,11 @@ a:focus-visible {
|
||||
}
|
||||
|
||||
.mixed-color::-webkit-color-swatch {
|
||||
background: repeating-linear-gradient(135deg, #fff 0 4px, #aeb7bc 4px 8px);
|
||||
background: repeating-linear-gradient(135deg, var(--input) 0 4px, var(--line-strong) 4px 8px);
|
||||
}
|
||||
|
||||
.mixed-color::-moz-color-swatch {
|
||||
background: repeating-linear-gradient(135deg, #fff 0 4px, #aeb7bc 4px 8px);
|
||||
background: repeating-linear-gradient(135deg, var(--input) 0 4px, var(--line-strong) 4px 8px);
|
||||
}
|
||||
|
||||
.app-bar {
|
||||
@@ -1161,7 +1166,7 @@ button:not(:disabled):hover {
|
||||
overflow: auto;
|
||||
margin: auto;
|
||||
padding: 0;
|
||||
border: 1px solid var(--line);
|
||||
border: 1px solid var(--popup-line);
|
||||
border-radius: var(--radius-dialog);
|
||||
background: var(--popup-fill);
|
||||
color: var(--text);
|
||||
@@ -1240,6 +1245,8 @@ button:not(:disabled):hover {
|
||||
.confirm-message {
|
||||
margin: 0 0 16px;
|
||||
line-height: 1.5;
|
||||
white-space: pre-line;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.confirm-dialog .form-actions {
|
||||
@@ -1247,6 +1254,10 @@ button:not(:disabled):hover {
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
#confirmPanel .form-actions {
|
||||
grid-template-columns: repeat(2, max-content);
|
||||
}
|
||||
|
||||
.confirm-dialog .form-actions button[hidden] {
|
||||
display: none;
|
||||
}
|
||||
@@ -1878,12 +1889,17 @@ button:not(:disabled):hover {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#createImageOrder input:not(:disabled):not(:checked) + span:hover {
|
||||
background: var(--hover-fill);
|
||||
.form-options input:not(:disabled):not(:checked) + span:hover {
|
||||
background-color: var(--hover-fill);
|
||||
}
|
||||
|
||||
#createImageOrder input:disabled + span {
|
||||
.form-options input:disabled + span {
|
||||
opacity: 0.48;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.form-options input:not(:disabled) + span:active {
|
||||
background-image: linear-gradient(var(--pressed-overlay), var(--pressed-overlay));
|
||||
}
|
||||
|
||||
.info-rows textarea,
|
||||
@@ -2154,7 +2170,7 @@ button:not(:disabled):hover {
|
||||
}
|
||||
|
||||
.form-actions button[type="submit"]:not(:disabled):hover {
|
||||
background: var(--accent-strong);
|
||||
background: var(--accent-hover);
|
||||
}
|
||||
|
||||
.zoom-label {
|
||||
@@ -2187,6 +2203,10 @@ body[data-hide-meta] .meta-panel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.form-dialog,
|
||||
.form-dialog *,
|
||||
.font-listbox,
|
||||
.dropdown-list,
|
||||
.navigation-content,
|
||||
.viewer-panel,
|
||||
.search-results,
|
||||
@@ -2197,6 +2217,10 @@ body[data-hide-meta] .meta-panel {
|
||||
scrollbar-color: var(--line-strong) transparent;
|
||||
}
|
||||
|
||||
:is(.form-dialog, .form-dialog *, .font-listbox, .dropdown-list, .navigation-content, .viewer-panel, .search-results, .editor-toolbar, .document-list, .meta-panel):hover {
|
||||
scrollbar-color: var(--text-muted) transparent;
|
||||
}
|
||||
|
||||
.page-list-panel,
|
||||
.meta-panel {
|
||||
min-height: 0;
|
||||
@@ -2453,7 +2477,7 @@ body[data-hide-meta] .meta-panel {
|
||||
height: 180px;
|
||||
margin-top: 12px;
|
||||
overflow: auto;
|
||||
background: white;
|
||||
background: var(--well-fill);
|
||||
}
|
||||
|
||||
.resource-viewer[hidden] {
|
||||
@@ -2467,6 +2491,10 @@ body[data-hide-meta] .meta-panel {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.resource-viewer img {
|
||||
background: var(--page);
|
||||
}
|
||||
|
||||
.resource-viewer audio {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -3350,13 +3378,13 @@ button.outline-link:hover {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.thumb-paper.pending,
|
||||
.thumb-paper.error {
|
||||
.thumb-paper.pending {
|
||||
background:
|
||||
linear-gradient(90deg, rgba(31, 36, 40, 0.04) 1px, transparent 1px),
|
||||
linear-gradient(0deg, rgba(31, 36, 40, 0.04) 1px, transparent 1px),
|
||||
#ffffff;
|
||||
linear-gradient(90deg, var(--canvas-grid) 1px, transparent 1px),
|
||||
linear-gradient(0deg, var(--canvas-grid) 1px, transparent 1px),
|
||||
var(--panel);
|
||||
background-size: 12px 12px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.thumb-svg {
|
||||
@@ -3472,7 +3500,7 @@ button.outline-link:hover {
|
||||
gap: 8px 10px;
|
||||
width: min(420px, calc(100vw - 32px));
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--line);
|
||||
border: 1px solid var(--popup-line);
|
||||
border-radius: var(--radius-dialog);
|
||||
background: var(--popup-fill);
|
||||
box-shadow: var(--shadow-flyout);
|
||||
@@ -3584,8 +3612,11 @@ button.outline-link:hover {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.page-shell.error {
|
||||
background: #fff8f7;
|
||||
.page-shell.error,
|
||||
.page-shell.error .page-placeholder,
|
||||
.thumb-paper.error {
|
||||
background: var(--danger-soft);
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.page-surface {
|
||||
@@ -4141,9 +4172,9 @@ audio.page-media {
|
||||
color: var(--text-muted);
|
||||
font-size: 13px;
|
||||
background:
|
||||
linear-gradient(90deg, rgba(31, 36, 40, 0.04) 1px, transparent 1px),
|
||||
linear-gradient(0deg, rgba(31, 36, 40, 0.04) 1px, transparent 1px),
|
||||
#ffffff;
|
||||
linear-gradient(90deg, var(--canvas-grid) 1px, transparent 1px),
|
||||
linear-gradient(0deg, var(--canvas-grid) 1px, transparent 1px),
|
||||
var(--panel);
|
||||
background-size: 18px 18px;
|
||||
}
|
||||
|
||||
@@ -5219,7 +5250,7 @@ audio.page-media {
|
||||
background: var(--control-fill);
|
||||
color: var(--accent);
|
||||
border-color: var(--line);
|
||||
box-shadow: 0 1px 4px #0001;
|
||||
box-shadow: var(--shadow-choice);
|
||||
}
|
||||
|
||||
.appearance-choices [data-pressed="true"]::after {
|
||||
|
||||
+54
-35
@@ -40,6 +40,7 @@ let wasmRequestID = 0;
|
||||
let wasmRecoveryTimer = 0;
|
||||
let credentialRequest = null;
|
||||
let documentDialogs = null;
|
||||
let confirmationTask = Promise.resolve();
|
||||
let cancelPageTouch = null;
|
||||
|
||||
const workspace = { tabs: [], active: null, sequence: 0, nextID: 0, switching: false, closing: false, objectClipboard: null, textClipboard: null };
|
||||
@@ -285,6 +286,8 @@ const el = {
|
||||
ofdInput: document.querySelector("#ofdInput"),
|
||||
ofdButton: document.querySelector("#ofdButton"),
|
||||
closeButton: document.querySelector("#closeButton"),
|
||||
confirmPanel: document.querySelector("#confirmPanel"),
|
||||
confirmMessage: document.querySelector("#confirmPanel .confirm-message"),
|
||||
closePanel: document.querySelector("#closePanel"),
|
||||
closeTitle: document.querySelector("#closePanel .form-title"),
|
||||
closeMessage: document.querySelector("#closePanel .confirm-message"),
|
||||
@@ -725,7 +728,7 @@ el.editorTools.addEventListener("scroll", () => fontPicker.position());
|
||||
|
||||
const canvasEditor = new CanvasEditor(el.viewerPanel, {
|
||||
textControls: [el.appearanceButton, el.appearancePanel, el.textAlign, el.textFont, el.textFontToggle, el.textFontAdd, el.textFontList, el.textSize, el.textColor, el.textLineHeight, el.textSpacing, el.textWrap, el.paragraphButton, el.paragraphPanel, textBackground, ...textFormatButtons, el.undoButton, el.redoButton, el.saveButton, el.saveAsButton, el.fontInput, document.querySelector("#textReflow")],
|
||||
keepTextFocus: () => el.appearancePanel.open || fontPicker.open || el.closePanel.open || el.paragraphPanel.open || canvasEditor.input?.fontDialog,
|
||||
keepTextFocus: () => el.appearancePanel.open || fontPicker.open || el.confirmPanel.open || el.closePanel.open || el.paragraphPanel.open || canvasEditor.input?.fontDialog,
|
||||
prepareTextCommit: flushEditorInputs,
|
||||
onTextFormat: changeRangeFormat,
|
||||
onRichInput: ensureRichInput,
|
||||
@@ -984,7 +987,7 @@ resourceElements.Delete.addEventListener("click", () => resourceTask(async () =>
|
||||
const item = resourceManager.selected;
|
||||
if (!item) return;
|
||||
const message = item.kind === "Attachment" ? `删除附件“${item.name}”?相关附件链接将一并移除。` : `删除资源“${item.name}”?`;
|
||||
if (window.confirm(message)) await changeManagedResource("delete", item);
|
||||
if (await requestConfirmation(message)) await changeManagedResource("delete", item);
|
||||
}));
|
||||
for (const [button, action] of [[resourceElements.Bind, "bind"], [resourceElements.Unbind, "unbind"]]) {
|
||||
button.addEventListener("click", () => resourceTask(() => changeManagedResource(action, resourceManager.selected)));
|
||||
@@ -1024,6 +1027,7 @@ el.versionList.addEventListener("keydown", event => {
|
||||
event.preventDefault();
|
||||
selectManagedVersion(manager.entries[Math.max(0, Math.min(index, manager.entries.length - 1))].id, true);
|
||||
});
|
||||
document.querySelector("#confirmCancel").addEventListener("click", () => el.confirmPanel.close("cancel"));
|
||||
el.documentClose.addEventListener("click", () => el.documentPanel.close());
|
||||
el.documentPanel.addEventListener("cancel", event => { if (el.documentForm.inert) event.preventDefault(); });
|
||||
el.documentPanel.addEventListener("close", () => {
|
||||
@@ -1040,9 +1044,9 @@ el.documentList.addEventListener("keydown", event => {
|
||||
selectManagedDocument(event.key === "Home" ? 0 : event.key === "End" ? count - 1
|
||||
: Math.max(0, Math.min(count - 1, index + (event.key === "ArrowUp" ? -1 : 1))), true);
|
||||
});
|
||||
editorClick(el.documentDeleteButton, () => {
|
||||
editorClick(el.documentDeleteButton, async () => {
|
||||
const index = managedDocumentIndex(), entry = state.doc.documents[index];
|
||||
if (window.confirm(`删除文档“${entry.title || "未命名"}”?`)) return changeDocument("ofdgoChangeDocument", null, "delete", index);
|
||||
if (await requestConfirmation(`删除文档“${entry.title || "未命名"}”?`)) return changeDocument("ofdgoChangeDocument", null, "delete", index);
|
||||
});
|
||||
editorClick(el.documentUpButton, () => {
|
||||
const index = managedDocumentIndex();
|
||||
@@ -1366,7 +1370,7 @@ el.importForm.addEventListener("submit", async event => {
|
||||
const position = state.importDocumentMode ? managedDocumentIndex() + 1 : { before:state.pageIndex, after:state.pageIndex+1, first:0, last:state.doc.pageCount }[el.importPosition.value];
|
||||
const options = [];
|
||||
if (state.importEncrypted) {
|
||||
if (!window.confirm(state.doc.encryption?.encrypted ? "按目标文档的加密策略导入,是否继续?" : "目标未加密,导入内容将按明文保存,是否继续?")) return;
|
||||
if (!await requestConfirmation(state.doc.encryption?.encrypted ? "按目标文档的加密策略导入,是否继续?" : "目标未加密,导入内容将按明文保存,是否继续?")) return;
|
||||
options.push(true);
|
||||
}
|
||||
const range = el.importRange.value === "custom" ? state.importDocumentMode ? selectedImportDocuments() : el.importPages.value : "";
|
||||
@@ -1844,7 +1848,7 @@ document.querySelector("#objectActionsButton").addEventListener("click", async (
|
||||
if (!item || item.scoped || item.items) return;
|
||||
const original = state.objectLinkOriginal;
|
||||
if ((el.objectLinkKind.value !== original.kind || el.objectLinkAddress.value !== original.address || el.objectLinkPage.value !== original.page)
|
||||
&& !window.confirm("链接尚未应用,放弃链接更改?")) return;
|
||||
&& !await requestConfirmation("链接尚未应用,放弃链接更改?")) return;
|
||||
syncObjectLink(item);
|
||||
settings.scope = "object";
|
||||
settings.kind = "actions";
|
||||
@@ -2174,7 +2178,7 @@ function handleKeyDown(event) {
|
||||
|
||||
function formDialogOpen() {
|
||||
if (documentTab().print?.context) return true;
|
||||
return el.appearancePanel.open || Boolean(sealPreview || resourceManager.pick) || el.aboutPanel.open || el.closePanel.open || el.attachmentDialog.open || settingsElements.Panel.open || fontBindingElements.Panel.open || el.signPanel.open || el.verifyPanel.open || el.credentialsPanel.open || el.encryptionPanel.open || batchElements.Panel.open || el.exportPanel.open || el.createPanel.open || el.insertPanel.open || el.pagePanel.open || el.paragraphPanel.open || el.importPanel.open || el.infoPanel.open || el.documentPanel.open
|
||||
return el.confirmPanel.open || el.appearancePanel.open || Boolean(sealPreview || resourceManager.pick) || el.aboutPanel.open || el.closePanel.open || el.attachmentDialog.open || settingsElements.Panel.open || fontBindingElements.Panel.open || el.signPanel.open || el.verifyPanel.open || el.credentialsPanel.open || el.encryptionPanel.open || batchElements.Panel.open || el.exportPanel.open || el.createPanel.open || el.insertPanel.open || el.pagePanel.open || el.paragraphPanel.open || el.importPanel.open || el.infoPanel.open || el.documentPanel.open
|
||||
|| resourceElements.Panel.open || el.structureDialog.open || el.versionPanel.open || el.batchPagesPanel.open || el.objectStylePanel.open || el.outlinePanel.open || el.objectBoundsPanel.open || el.annotationNote.open || el.annotationCreate.open || el.objectPicker.open;
|
||||
}
|
||||
|
||||
@@ -2780,8 +2784,8 @@ async function closeWorkspace() {
|
||||
for (const tab of workspace.tabs) cancelDocumentPreparation(tab);
|
||||
await Promise.all(workspace.tabs.filter(tab => tab.state.saving && tab.saveCompletion).map(tab => tab.saveCompletion));
|
||||
if (workspace.tabs.some(tab => tab.state.saving)) { setStatus("文档正在保存,请稍后关闭"); return; }
|
||||
if (batch.running && !window.confirm("批量转换尚未完成,是否退出?")) return;
|
||||
if (workspace.tabs.some(tab => tab.opening || tab.state.exporting) && !window.confirm("文档正在处理,是否取消并退出?")) return;
|
||||
if (batch.running && !await requestConfirmation("批量转换尚未完成,是否退出?", null)) return;
|
||||
if (workspace.tabs.some(tab => tab.opening || tab.state.exporting) && !await requestConfirmation("文档正在处理,是否取消并退出?", null)) return;
|
||||
const activeDirty = state.dirty || canvasEditor.textChanged() || canvasEditor.cropChanged() || canvasEditor.nudgeChanged();
|
||||
const dirty = activeDirty || workspace.tabs.some(tab => tab.state.dirty);
|
||||
if (dirty && (workspace.switching || workspace.tabs.some(tab => tab.closing) || formDialogOpen() || (activeDirty && document.body.hasAttribute("data-busy")))) {
|
||||
@@ -3203,6 +3207,21 @@ function setDirty(dirty) {
|
||||
updateWorkspaceDirty();
|
||||
}
|
||||
|
||||
function requestConfirmation(message, tab = documentTab()) {
|
||||
const sequence = tab?.state.openSeq, scope = tab && documentPanelScope(tab.state);
|
||||
const current = () => !tab || isForegroundDocument(tab) && sequence === tab.state.openSeq && scope === documentPanelScope(tab.state);
|
||||
const task = confirmationTask.then(async () => {
|
||||
if (!current()) return false;
|
||||
el.confirmMessage.textContent = message;
|
||||
el.confirmPanel.returnValue = "cancel";
|
||||
const choice = new Promise(resolve => el.confirmPanel.addEventListener("close", () => resolve(el.confirmPanel.returnValue), { once: true }));
|
||||
documentDialogs.showApplication(el.confirmPanel);
|
||||
return await choice === "confirm" && current();
|
||||
});
|
||||
confirmationTask = task.then(() => {}, () => {});
|
||||
return task;
|
||||
}
|
||||
|
||||
async function requestCloseAction(title, message, save, tab = documentTab()) {
|
||||
const setup = () => {
|
||||
el.closeTitle.textContent = title;
|
||||
@@ -3412,7 +3431,7 @@ async function changeVersion(action) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (action === "delete" && (!entry.id || !window.confirm(`删除版本“${entry.name || entry.index}”?`))) return;
|
||||
if (action === "delete" && (!entry.id || !await requestConfirmation(`删除版本“${entry.name || entry.index}”?`))) return;
|
||||
el.versionStatus.textContent = "正在处理版本";
|
||||
if (await changeDocument("ofdgoChangeVersion", null, action, entry.id, el.versionName.value.trim())) {
|
||||
if (tab.closed || state.versionManagement !== manager) return;
|
||||
@@ -3508,7 +3527,7 @@ async function exportManagedVersion() {
|
||||
const manager = state.versionManagement;
|
||||
const entry = manager?.entries?.find(item => item.id === manager.id);
|
||||
if (!entry?.id || !state.ready || state.exporting || manager.loading || manager.failed || document.body.hasAttribute("data-busy")) return;
|
||||
if (state.doc.signatureCount && !window.confirm("版本副本可能使原签名失效,是否继续?")) return;
|
||||
if (state.doc.signatureCount && !await requestConfirmation("版本副本可能使原签名失效,是否继续?")) return;
|
||||
const originalDocument = state.doc, wasmSeq = state.wasmSeq;
|
||||
const name = `${baseFileName()}_${entry.name || `版本${entry.index}`}`.replace(/[\\/:*?"<>|\u0000-\u001f]/g, "_") + ".ofd";
|
||||
state.exporting = true;
|
||||
@@ -3811,9 +3830,9 @@ async function toggleEditor() {
|
||||
}
|
||||
}
|
||||
|
||||
function confirmTextReflow(item) {
|
||||
async function confirmTextReflow(item) {
|
||||
return canEditObject(item, "reflow") && (canEditObject(item, "layoutKnown")
|
||||
|| window.confirm("将重新排版原文,是否继续?"));
|
||||
|| await requestConfirmation("将重新排版原文,是否继续?"));
|
||||
}
|
||||
|
||||
function openCreatePanel(insert = false, packageDocument = false) {
|
||||
@@ -4596,7 +4615,7 @@ async function ensureRichInput(reflow = false) {
|
||||
let rich = await loadRichText(editing.item, editing.face, tab, editing.fontResource, reflow);
|
||||
if (canvasEditor.input !== editing || !isForegroundDocument(tab)) return false;
|
||||
if (!rich && !reflow) {
|
||||
if (!confirmTextReflow(editing.item)) return false;
|
||||
if (!await confirmTextReflow(editing.item)) return false;
|
||||
rich = await loadRichText(editing.item, editing.face, tab, editing.fontResource, true);
|
||||
}
|
||||
if (canvasEditor.input !== editing || !isForegroundDocument(tab)) return false;
|
||||
@@ -4607,7 +4626,7 @@ async function ensureRichInput(reflow = false) {
|
||||
async function reflowCanvasText() {
|
||||
const editing = canvasEditor.input;
|
||||
if (document.body.hasAttribute("data-busy") || !editing?.source || editing.composing || editing.saving || !canEditObject(editing.item, "reflow")) return;
|
||||
if (!window.confirm("将重新排版原文,是否继续?")) return;
|
||||
if (!await requestConfirmation("将重新排版原文,是否继续?")) return;
|
||||
editing.reflow = true;
|
||||
canvasEditor.recordSourceText();
|
||||
await canvasEditor.previewSourceText();
|
||||
@@ -4668,7 +4687,7 @@ async function changeRangeFormat(key, value) {
|
||||
const item = canvasEditor.selected;
|
||||
if (!canvasEditor.input && item) {
|
||||
const reflow = !canEditObject(item, "layoutKnown");
|
||||
if (reflow && !confirmTextReflow(item)) return;
|
||||
if (reflow && !await confirmTextReflow(item)) return;
|
||||
const current = currentTextStyle()[key];
|
||||
const patch = { [key]: value ?? (key === "background" ? current ? "" : textBackground.value : current !== true) };
|
||||
await changeDocument("ofdgoFormatText", { ...item, id: (item.items || [item]).map(member => member.id) }, JSON.stringify(patch), reflow);
|
||||
@@ -5054,7 +5073,7 @@ async function changeTextFace(font, key, value) {
|
||||
if (!editing && item?.type === "TextObject") {
|
||||
if (!key && font?.embedded && textFontRuns(item).every(run => run.format.font === font.id.slice(9))) return;
|
||||
const reflow = Boolean(key && !canEditObject(item, "layoutKnown"));
|
||||
if (!canEditObject(item, "replaceFont") || reflow && !confirmTextReflow(item)) return;
|
||||
if (!canEditObject(item, "replaceFont") || reflow && !await confirmTextReflow(item)) return;
|
||||
await changeDocument("ofdgoStyleTextFonts", { ...item, id: (item.items || [item]).map(member => member.id) }, async () => {
|
||||
const result = await prepareTextFaces(tab, textFontRuns(item), font, key, value);
|
||||
return { changes: JSON.stringify(result.changes), fonts: result.fonts, reflow };
|
||||
@@ -5184,7 +5203,7 @@ async function changeTextAlignment() {
|
||||
const sequence = editing ? editing.alignSequence = (editing.alignSequence || 0) + 1 : 0;
|
||||
try {
|
||||
if (editing) {
|
||||
if (!editing.rich && !editing.item.draft && !confirmTextReflow(editing.item)) return;
|
||||
if (!editing.rich && !editing.item.draft && !await confirmTextReflow(editing.item)) return;
|
||||
if (!await ensureRichInput(!canEditObject(editing.item, "layoutKnown")) || !isForegroundDocument(tab) || canvasEditor.input !== editing || editing.alignSequence !== sequence) return;
|
||||
editing.rich.align(align);
|
||||
} else await saveTextParagraphs(currentTextStyle(), { align });
|
||||
@@ -5201,7 +5220,7 @@ async function saveTextParagraphs(item, patch) {
|
||||
Object.assign(item, patch);
|
||||
return true;
|
||||
}
|
||||
if (!confirmTextReflow(item)) return false;
|
||||
if (!await confirmTextReflow(item)) return false;
|
||||
return changeDocument("ofdgoLayoutTexts", item, JSON.stringify(patch));
|
||||
}
|
||||
|
||||
@@ -5296,7 +5315,7 @@ async function changeTextStyle(color) {
|
||||
return;
|
||||
}
|
||||
if (size !== item.size || fill !== null) {
|
||||
if (color ? !canEditObject(item, "paint") : !confirmTextReflow(item)) {
|
||||
if (color ? !canEditObject(item, "paint") : !await confirmTextReflow(item)) {
|
||||
updateObjectControls(item, true);
|
||||
return;
|
||||
}
|
||||
@@ -6118,7 +6137,7 @@ async function refreshApplication() {
|
||||
const temporaryFonts = fontManager.userFonts.some((font) => font.source === "upload");
|
||||
const dirty = state.dirty || workspace.tabs.some(tab => tab.state.dirty);
|
||||
const message = [dirty ? "文档尚未保存,刷新将丢失更改" : "刷新后需重新打开文件", temporaryFonts ? "未保存字体需重新添加" : ""].filter(Boolean).join("\n");
|
||||
if ((workspace.tabs.some(tab => tab.state.doc || tab.state.ofdBytes) || temporaryFonts) && !window.confirm(message)) {
|
||||
if ((workspace.tabs.some(tab => tab.state.doc || tab.state.ofdBytes) || temporaryFonts) && !await requestConfirmation(message, null)) {
|
||||
return;
|
||||
}
|
||||
setBusy(true, "正在更新应用", null, "正在更新应用");
|
||||
@@ -7531,10 +7550,10 @@ function restoreSettingsDraft() {
|
||||
if (body) body.scrollTop = draft.scroll;
|
||||
}
|
||||
|
||||
function closeDocumentSettings() {
|
||||
async function closeDocumentSettings() {
|
||||
if (settings.busy) return;
|
||||
rememberSettingsDraft();
|
||||
if ((settings.drafts.size || [...invoicePanel.drafts.values()].some(draft => draft.dirty.size)) && !window.confirm(settings.invoice ? "发票尚未应用,放弃更改?" : "配置尚未应用,放弃更改?")) return;
|
||||
if ((settings.drafts.size || [...invoicePanel.drafts.values()].some(draft => draft.dirty.size)) && !await requestConfirmation(settings.invoice ? "发票尚未应用,放弃更改?" : "配置尚未应用,放弃更改?")) return;
|
||||
settingsElements.Panel.close();
|
||||
}
|
||||
|
||||
@@ -7902,7 +7921,7 @@ function renderInvoiceSettings() {
|
||||
settingsButton("还原", () => resetInvoiceDraft(draft), !draft.dirty.size, "InvoiceReset");
|
||||
if (draft.entry) settingsButton("删除", async () => {
|
||||
if (draft.dirty.size) throw new Error("请先应用或还原草稿");
|
||||
if (window.confirm("删除此来源中的发票数据?页面将保留" + invoiceOtherDraftWarning(draft))) await commitInvoice(draft, { operation: "delete" });
|
||||
if (await requestConfirmation("删除此来源中的发票数据?页面将保留" + invoiceOtherDraftWarning(draft))) await commitInvoice(draft, { operation: "delete" });
|
||||
}, !draft.detail.capabilities?.delete, "InvoiceDelete");
|
||||
}
|
||||
settingsElements.Status.textContent = draft.status || invoiceIssues(draft.detail.issues) || "";
|
||||
@@ -7912,7 +7931,7 @@ function renderInvoiceSettings() {
|
||||
async function newInvoiceDraft() {
|
||||
const tab = documentTab(), state = tab.state, settings = tab.settings, invoicePanel = tab.invoice;
|
||||
if (!state.editing) return;
|
||||
if (invoicePanel.drafts.get("new")?.dirty.size && !window.confirm("放弃尚未应用的新建发票?")) return;
|
||||
if (invoicePanel.drafts.get("new")?.dirty.size && !await requestConfirmation("放弃尚未应用的新建发票?")) return;
|
||||
const format = invoicePanel.formats.find(value => value.create && value.standard !== invoiceReferenceStandard) || invoicePanel.formats.find(value => value.create);
|
||||
if (!format) throw new Error("暂无可新建票种");
|
||||
const invoice = { kind: format.kind, standard: format.standard, version: format.version, buyer: {}, seller: {}, items: [], facts: [] };
|
||||
@@ -8365,9 +8384,9 @@ function invoiceOtherDraftWarning(draft) {
|
||||
return [...invoicePanel.drafts.values()].some(value => value !== draft && value.entry && value.dirty.size && invoiceSourceKey(value.entry.source) === invoiceSourceKey(draft.entry.source)) ? "\n该来源其他发票的未应用修改也将放弃" : "";
|
||||
}
|
||||
|
||||
function resetInvoiceDraft(draft) {
|
||||
async function resetInvoiceDraft(draft) {
|
||||
const section = { xml: "xml", fields: draft.entry ? "fields" : "model", appearance: "bindings" }[invoicePanel.tab] || "model";
|
||||
if (!draft.dirty.has(section) || !window.confirm("放弃当前编辑项的未应用修改?")) return;
|
||||
if (!draft.dirty.has(section) || !await requestConfirmation("放弃当前编辑项的未应用修改?")) return;
|
||||
if (section === "xml") draft.xml = draft.detail.xml || "";
|
||||
else if (section === "fields") draft.changes.clear();
|
||||
else if (section === "bindings") draft.bindings = structuredClone(draft.detail.bindings || []);
|
||||
@@ -8450,7 +8469,7 @@ async function applyInvoiceDraft(draft, synchronize = draft.synchronize) {
|
||||
request.synchronize = draft.invoice.standard === invoiceReferenceStandard || synchronize;
|
||||
if (!await prepareInvoiceRequest(draft, request)) return;
|
||||
if (!isForegroundDocument(tab)) return;
|
||||
if (request.operation === "replace" && !window.confirm("替换此来源中的全部发票原文?" + invoiceOtherDraftWarning(draft))) return;
|
||||
if (request.operation === "replace" && !await requestConfirmation("替换此来源中的全部发票原文?" + invoiceOtherDraftWarning(draft))) return;
|
||||
await commitInvoice(draft, request);
|
||||
}
|
||||
|
||||
@@ -8992,7 +9011,7 @@ function renderTemplateSettings() {
|
||||
settingsPages(source, "SourcePage", settings.page);
|
||||
document.querySelector("#settingsSourcePage").closest("label").firstElementChild.textContent = "来源";
|
||||
const write = async (operation, order = 0) => {
|
||||
if (operation === "replace" && !window.confirm("替换模板内容?当前文档中引用此模板的页面将同步更新。")) return;
|
||||
if (operation === "replace" && !await requestConfirmation("替换模板内容?当前文档中引用此模板的页面将同步更新。")) return;
|
||||
const selected = settings.selected;
|
||||
if (operation === "add") settings.selected = entries.length;
|
||||
if (!await commitSettings("ofdgoChangeTemplate", operation, ["add", "replace"].includes(operation) ? Number(settingsValue("SourcePage")) : settings.page, entry?.ID || "", settingsValue("Name"), ["bind", "order"].includes(operation) ? settingsValue("BindingOrder") : settingsValue("TemplateOrder"), order)) settings.selected = selected;
|
||||
@@ -9605,7 +9624,7 @@ async function previewManagedResource() {
|
||||
}
|
||||
const item = resourceManager.selected;
|
||||
if (!item?.file) return;
|
||||
if (state.doc.encryption?.encrypted && !window.confirm("资源将按明文读取,是否继续?")) return;
|
||||
if (state.doc.encryption?.encrypted && !await requestConfirmation("资源将按明文读取,是否继续?")) return;
|
||||
releaseResourcePreview();
|
||||
const sequence = resourceManager.sequence, openSeq = state.openSeq;
|
||||
let result;
|
||||
@@ -9701,7 +9720,7 @@ async function downloadResource(attachment, api, label) {
|
||||
if (!state.doc || document.body.hasAttribute("data-busy")) {
|
||||
return;
|
||||
}
|
||||
if (state.doc.encryption?.encrypted && !window.confirm(`${label}将按明文下载,是否继续?`)) return;
|
||||
if (state.doc.encryption?.encrypted && !await requestConfirmation(`${label}将按明文下载,是否继续?`)) return;
|
||||
const openSeq = state.openSeq;
|
||||
const originalDocument = state.doc;
|
||||
state.exporting = true;
|
||||
@@ -10059,7 +10078,7 @@ async function signDocument(event) {
|
||||
el.signStatus.textContent = "请选择证书、私钥和信任根";
|
||||
return;
|
||||
}
|
||||
if (el.signMode.value === "replace" && !window.confirm("替换现有签名,是否继续?")) return;
|
||||
if (el.signMode.value === "replace" && !await requestConfirmation("替换现有签名,是否继续?")) return;
|
||||
let key;
|
||||
const keyPassword = new TextEncoder().encode(el.signKeyPassword.value);
|
||||
const files = Object.fromEntries(["Key", "Certificate", "Roots", "Intermediates", "Seal"].map(name => [name, Array.from(el[`sign${name}`].files || [])]));
|
||||
@@ -10561,7 +10580,7 @@ async function runBatch() {
|
||||
options.credentials = await unlock;
|
||||
if (!options.credentials) throw new DOMException("已取消解锁", "AbortError");
|
||||
const preservesEncryption = err.code === "pdfPassword" && format.value === "pdf" && compression.mode !== 0 && !options.pages;
|
||||
if (!preservesEncryption && !window.confirm("转换结果将不保留原加密,是否继续?")) throw new DOMException("已取消转换", "AbortError");
|
||||
if (!preservesEncryption && (!await requestConfirmation("转换结果将不保留原加密,是否继续?", null) || batch.canceled)) throw new DOMException("已取消转换", "AbortError");
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -10643,7 +10662,7 @@ async function exportFile(whole, indices = null, value = el.exportFormat.value,
|
||||
if (saving && !state.editorInfo && !state.ofdBytes) {
|
||||
return;
|
||||
}
|
||||
if (!saving && state.doc.encryption?.encrypted && !window.confirm("此格式将输出明文,是否继续?")) return;
|
||||
if (!saving && state.doc.encryption?.encrypted && !await requestConfirmation("此格式将输出明文,是否继续?")) return;
|
||||
const format = saving ? { value: "ofd", label: "OFD", extension: "ofd", mime: "application/ofd" } : exportFormatInfo(value);
|
||||
if (!format) {
|
||||
return;
|
||||
@@ -11817,7 +11836,7 @@ async function activateOpenActions() {
|
||||
const surface = pageView.pageShell(state.pageIndex)?.querySelector(".page-surface");
|
||||
const target = pageLinkTarget(link, info, surface, task);
|
||||
if (!target) continue;
|
||||
if ((target.external || link.attachment) && !window.confirm(link.attachment
|
||||
if ((target.external || link.attachment) && !await requestConfirmation(link.attachment
|
||||
? `文档请求下载附件,是否继续?\n${link.fileName || ""}` : `文档请求打开链接,是否继续?\n${target.href}`)) return;
|
||||
task.navigating = !!link.dest;
|
||||
if (await target.activate() === false) return;
|
||||
|
||||
Reference in new issue
Block a user