Parcourir la source

fix(preview): open office docs in new tab and enable xlsx sheet navigation

root il y a 1 mois
Parent
commit
6a29beb621
3 fichiers modifiés avec 114 ajouts et 9 suppressions
  1. 55 4
      filebrowser-proxy/fb-oo-hook.js
  2. 6 0
      nginx/default.conf
  3. 53 5
      site/static/oo-preview.html

+ 55 - 4
filebrowser-proxy/fb-oo-hook.js

@@ -1,6 +1,8 @@
 (function(){
   var OFFICE_EXT = ["docx","pptx","ppt","xlsx","xls"];
   var PREVIEW_BASE = "http://" + location.hostname + ":8080";
+  var LAST_OPEN = { path: "", ts: 0 };
+  var OPEN_GUARD_MS = 1200;
 
   function startsWithFiles(p){ return (p || "").toLowerCase().indexOf("/files/") === 0; }
 
@@ -42,15 +44,63 @@
     var ext = (rel.split(".").pop() || "").toLowerCase();
     var rawUrl = PREVIEW_BASE + buildRawPath(rel);
     var nonce = Date.now().toString() + "-" + Math.random().toString(16).slice(2, 8);
-    return PREVIEW_BASE + "/oo-preview.html?url=" + encodeURIComponent(rawUrl) + "&type=" + encodeURIComponent(ext) + "&title=" + encodeURIComponent(rel) + "&_fbv=" + encodeURIComponent(nonce);
+    var mode = (ext === "xlsx" || ext === "xls") ? "edit" : "view";
+    return PREVIEW_BASE + "/oo-preview.html?url=" + encodeURIComponent(rawUrl) + "&type=" + encodeURIComponent(ext) + "&mode=" + encodeURIComponent(mode) + "&title=" + encodeURIComponent(rel) + "&_fbv=" + encodeURIComponent(nonce);
+  }
+
+  function parentDirPath(pathname){
+    var p = pathname || "";
+    if (!startsWithFiles(p)) return "/files/";
+    var idx = p.lastIndexOf("/");
+    if (idx <= 6) return "/files/";
+    return p.slice(0, idx + 1);
+  }
+
+  function openPreviewInNewTab(pathname){
+    var p = pathname || "";
+    var u = buildPreviewUrlFromPath(p);
+    if (!u) return false;
+    var now = Date.now();
+    if (LAST_OPEN.path === p && (now - LAST_OPEN.ts) < OPEN_GUARD_MS) return true;
+    LAST_OPEN.path = p;
+    LAST_OPEN.ts = now;
+
+    var w = window.open(u, "_blank", "noopener");
+    if (!w) {
+      // Popup blocked fallback
+      location.assign(u);
+    }
+    return true;
+  }
+
+  function interceptFileClick(){
+    document.addEventListener("click", function(e){
+      var a = e.target && e.target.closest && e.target.closest("a[href]");
+      if (!a) return;
+      var href = a.getAttribute("href") || "";
+      if (!href) return;
+      var pathname = "";
+      try {
+        pathname = new URL(href, location.origin).pathname || "";
+      } catch (err) {
+        pathname = href;
+      }
+      if (!startsWithFiles(pathname)) return;
+      if (!buildPreviewUrlFromPath(pathname)) return;
+      e.preventDefault();
+      e.stopPropagation();
+      if (e.stopImmediatePropagation) e.stopImmediatePropagation();
+      openPreviewInNewTab(pathname);
+    }, true);
   }
 
   function maybeJump(){
     var p = location.pathname || "";
-    var u = buildPreviewUrlFromPath(p);
-    if (!u) return;
+    if (!buildPreviewUrlFromPath(p)) return;
     if (location.pathname === "/oo-preview.html") return;
-    location.replace(u);
+    if (openPreviewInNewTab(p)) {
+      location.replace(parentDirPath(p));
+    }
   }
 
   var pushed = history.pushState;
@@ -68,5 +118,6 @@
   window.addEventListener("popstate", function(){ setTimeout(maybeJump, 0); });
   window.addEventListener("hashchange", function(){ setTimeout(maybeJump, 0); });
 
+  interceptFileClick();
   setTimeout(maybeJump, 0);
 })();

+ 6 - 0
nginx/default.conf

@@ -54,6 +54,12 @@ server {
         add_header Cache-Control "no-store";
     }
 
+    location = /onlyoffice-callback {
+        default_type application/json;
+        add_header Cache-Control "no-store" always;
+        return 200 '{"error":0}';
+    }
+
     location ^~ /onlyoffice/ {
         proxy_pass http://onlyoffice:80/;
         proxy_http_version 1.1;

+ 53 - 5
site/static/oo-preview.html

@@ -24,6 +24,11 @@
     const rawUrl = p.get("url") || "";
     const title = p.get("title") || (rawUrl ? rawUrl.split("/").pop() : "untitled.docx");
     const fileType = (p.get("type") || title.split(".").pop() || "docx").toLowerCase();
+    const fileDocType = docType(fileType);
+    const isCellDoc = fileDocType === "cell";
+    const requestedMode = (p.get("mode") || "").toLowerCase();
+    const forceReadOnly = (p.get("readonly") || "").toLowerCase() === "1";
+    const editorMode = requestedMode || (isCellDoc ? "edit" : "view");
 
     function docType(ext){
       if(["xlsx","xls","csv","ods"].includes(ext)) return "cell";
@@ -126,9 +131,49 @@
     }
 
     function render(){
+      const permissions = isCellDoc
+        ? {
+            edit: !forceReadOnly,
+            review: false,
+            comment: false,
+            chat: false,
+            copy: true,
+            download: true,
+            print: true
+          }
+        : {
+            edit: false,
+            review: false,
+            comment: false,
+            copy: true,
+            download: true,
+            print: true
+          };
+
+      const customization = {
+        autosave: false,
+        forcesave: false,
+        compactHeader: false,
+        toolbarNoTabs: false,
+        statusBar: true
+      };
+      if (isCellDoc) {
+        customization.showVerticalScroll = true;
+        customization.showHorizontalScroll = true;
+        customization.toolbar = true;
+        customization.leftMenu = true;
+        customization.rightMenu = false;
+        customization.layout = {
+          toolbar: true,
+          leftMenu: true,
+          rightMenu: false,
+          statusBar: true
+        };
+      }
+
       try {
         new DocsAPI.DocEditor("editor", {
-          documentType: docType(fileType),
+          documentType: fileDocType,
           type: "desktop",
           width: "100%",
           height: "100%",
@@ -136,16 +181,19 @@
             title: title,
             url: fileUrlWithNonce,
             fileType: fileType,
-            key: docKey
+            key: docKey,
+            permissions: permissions
           },
           editorConfig: {
-            mode: "view",
+            mode: editorMode,
             lang: "zh-CN",
-            customization: { autosave: false, forcesave: false }
+            callbackUrl: location.origin + "/onlyoffice-callback",
+            user: { id: "preview-user", name: "preview-user" },
+            customization: customization
           },
           events: {
             onAppReady: function(){
-              hintEl.innerHTML = `预览中: ${title} | <a href='${fileUrl}' target='_blank'>原文件</a> | <span class='ok'>编辑器已启动</span>`;
+              hintEl.innerHTML = `预览中: ${title} | <a href='${fileUrl}' target='_blank'>原文件</a> | <span class='ok'>编辑器已启动(模式: ${editorMode})</span>`;
             },
             onDocumentReady: function(){
               hintEl.innerHTML = `预览中: ${title} | <a href='${fileUrl}' target='_blank'>原文件</a> | <span class='ok'>文档已加载</span>`;