wps只读去掉工具栏
parent
e45c4d2404
commit
fcf1a193f2
|
|
@ -14,8 +14,8 @@ module.exports = {
|
|||
|
||||
// API_HOST: "http://192.168.0.134",
|
||||
// API_HOST: "http://192.168.0.150", //太浅
|
||||
// API_HOST: "https://gbs-gateway.qhdsafety.com",
|
||||
API_HOST: "http://192.168.0.103", //huwei
|
||||
API_HOST: "https://gbs-gateway.qhdsafety.com",
|
||||
// API_HOST: "http://192.168.0.103", //huwei
|
||||
},
|
||||
production: {
|
||||
// 应用后端分支名称,部署上线需要
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@
|
|||
"@rc-component/util": "^1.11.1",
|
||||
"antd": "^6.6.1",
|
||||
"dayjs": "^1.11.7",
|
||||
"docx-preview": "^0.4.0",
|
||||
"docxtemplater": "latest",
|
||||
"echarts": "^6.1.0",
|
||||
"history": "^4.10.1",
|
||||
|
|
|
|||
1992
pnpm-lock.yaml
1992
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
|
@ -1,193 +0,0 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import { Modal, Empty, Alert } from "antd";
|
||||
import { renderAsync } from "docx-preview";
|
||||
import "./index.less";
|
||||
|
||||
const FILE_TYPE_MAP = {
|
||||
docx: "word",
|
||||
pdf: "pdf",
|
||||
};
|
||||
|
||||
/** 根据 url/类型识别文件类型,仅支持 docx/pdf */
|
||||
function resolveFileType(url, type) {
|
||||
if (type && FILE_TYPE_MAP[type]) return type;
|
||||
const ext = (url || "").split("?")[0].split(".").pop()?.toLowerCase();
|
||||
return FILE_TYPE_MAP[ext] ? ext : "";
|
||||
}
|
||||
|
||||
/** 相对地址拼接文件服务前缀 */
|
||||
function resolveUrl(raw) {
|
||||
if (!raw) return "";
|
||||
const u = String(raw).trim();
|
||||
if (/^https?:\/\//i.test(u)) return u;
|
||||
const base = window.fileUrl || "";
|
||||
return base ? `${base}${u}` : u;
|
||||
}
|
||||
|
||||
/** 渲染 Word 文档到容器 */
|
||||
async function renderDocx(url, container) {
|
||||
if (!container) throw new Error("预览容器尚未就绪,请稍后重试");
|
||||
const res = await fetch(url, { credentials: "same-origin" });
|
||||
if (!res.ok) throw new Error(`文档加载失败:${res.status} ${res.statusText}`);
|
||||
const blob = await res.blob();
|
||||
await renderAsync(blob, container, container, {
|
||||
className: "office-preview-docx",
|
||||
inWrapper: true,
|
||||
ignoreWidth: false,
|
||||
ignoreHeight: false,
|
||||
ignoreFonts: false,
|
||||
breakPages: true,
|
||||
renderHeaders: true,
|
||||
renderFooters: true,
|
||||
renderFootnotes: true,
|
||||
renderEndnotes: true,
|
||||
});
|
||||
}
|
||||
|
||||
/** 文档预览组件:支持 Word(docx)、PDF,支持弹窗/页面两种模式 */
|
||||
export default function OfficePreview({
|
||||
url,
|
||||
fileType = "auto",
|
||||
mode = "modal",
|
||||
open = false,
|
||||
onCancel,
|
||||
title = "文档预览",
|
||||
width = 1000,
|
||||
className = "",
|
||||
style = {},
|
||||
emptyText = "暂无文档",
|
||||
}) {
|
||||
const containerRef = useRef(null);
|
||||
const aliveRef = useRef(true);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const [ready, setReady] = useState(false);
|
||||
const resolvedUrl = resolveUrl(url);
|
||||
const type = resolveFileType(resolvedUrl, fileType);
|
||||
|
||||
useEffect(() => {
|
||||
aliveRef.current = true;
|
||||
return () => {
|
||||
aliveRef.current = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (mode === "modal" && !open) {
|
||||
setReady(false);
|
||||
return;
|
||||
}
|
||||
if (!resolvedUrl || type !== "docx") return;
|
||||
|
||||
setReady(false);
|
||||
setError("");
|
||||
|
||||
let rafId = null;
|
||||
let cancelled = false;
|
||||
|
||||
const tryRender = () => {
|
||||
if (cancelled) return;
|
||||
const node = containerRef.current;
|
||||
if (!node) {
|
||||
rafId = requestAnimationFrame(tryRender);
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
renderDocx(resolvedUrl, node)
|
||||
.then(() => {
|
||||
if (aliveRef.current && !cancelled) {
|
||||
setReady(true);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error("OfficePreview 文档渲染失败", e);
|
||||
if (aliveRef.current && !cancelled) {
|
||||
setError(e?.message || "文档渲染失败,请稍后重试");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (aliveRef.current && !cancelled) {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
tryRender();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
if (rafId !== null) cancelAnimationFrame(rafId);
|
||||
const node = containerRef.current;
|
||||
if (node) node.innerHTML = "";
|
||||
};
|
||||
}, [resolvedUrl, type, open, mode]);
|
||||
|
||||
const renderContent = () => {
|
||||
if (!resolvedUrl) {
|
||||
return <Empty description={emptyText} image={Empty.PRESENTED_IMAGE_SIMPLE} />;
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
return (
|
||||
<Alert
|
||||
type="warning"
|
||||
showIcon
|
||||
message="不支持的文件类型"
|
||||
description="当前仅支持 Word(.docx) 和 PDF(.pdf) 格式预览。"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <Alert type="error" showIcon message={error} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`office-preview-body office-preview-${type}`} style={style}>
|
||||
{type === "docx" && (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="office-preview-docxContainer"
|
||||
style={{ display: ready ? "block" : "none" }}
|
||||
/>
|
||||
)}
|
||||
{type === "pdf" && (
|
||||
<iframe
|
||||
className="office-preview-pdfFrame"
|
||||
src={resolvedUrl}
|
||||
title={title}
|
||||
frameBorder="0"
|
||||
/>
|
||||
)}
|
||||
{loading && (
|
||||
<div className="office-preview-loading">
|
||||
<div className="office-preview-loading-text">文档加载中...</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const body = (
|
||||
<div className={`office-preview ${className}`}>{renderContent()}</div>
|
||||
);
|
||||
|
||||
if (mode === "page") {
|
||||
return body;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={title}
|
||||
open={open}
|
||||
onCancel={onCancel}
|
||||
width={width}
|
||||
footer={null}
|
||||
destroyOnHidden
|
||||
centered={false}
|
||||
className="office-preview-modal"
|
||||
>
|
||||
{body}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
.office-preview {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
|
||||
.office-preview-body {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.office-preview-docxContainer {
|
||||
/* 固定占弹窗 90% 视口高度,确保有足够可视区域同时保留滚动条 */
|
||||
height: 90vh;
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
background: #f5f5f5;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.office-preview-pdfFrame {
|
||||
height: 90vh;
|
||||
max-height: 100%;
|
||||
width: 100%;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 4px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.office-preview-loading {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.office-preview-loading-text {
|
||||
color: #1677ff;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.office-preview-modal {
|
||||
/* 弹窗顶部紧贴,让中间文档区域最大化 */
|
||||
.micro-temp-modal {
|
||||
top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.micro-temp-modal-content {
|
||||
/* 弹窗内容占满视口高度,由正文容器再以 90vh 显示 */
|
||||
height: 75vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.micro-temp-modal-header {
|
||||
flex-shrink: 0;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
.micro-temp-modal-body {
|
||||
flex: 1;
|
||||
/* 覆盖 src/pages/Container/index.less 中全局 .micro-temp-modal-body { max-height: 66vh } 的限制,
|
||||
让文档预览区域能占满更大可视高度 */
|
||||
max-height: none !important;
|
||||
overflow: hidden;
|
||||
padding: 4px 8px 8px 8px;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,2 @@
|
|||
export { default as OrgInfoGuard } from "./OrgInfoGuard";
|
||||
export { default as PreviewUrlButton } from "./PreviewUrlButton";
|
||||
export { default as OfficePreview } from "./OfficePreview";
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace";
|
|||
import { tools } from "@cqsjjb/jjb-common-lib";
|
||||
import { EVAL_TYPE_MAP, ROLE_DEFINITIONS_MAP } from "~/enumerate/constant";
|
||||
import { PROJECT_TYPE_MAP } from "~/enumerate/constant";
|
||||
import { OfficePreview } from "~/components";
|
||||
import WpsEditor from "~/pages/Container/WpsEditor";
|
||||
import "./index.less";
|
||||
|
||||
const { router } = tools;
|
||||
|
|
@ -369,8 +369,7 @@ const EvalProjectMonitorDetail = (props) => {
|
|||
const [surveyPersonnel, setSurveyPersonnel] = useState([]);
|
||||
const [surveyAttachments, setSurveyAttachments] = useState({});
|
||||
const [activeKey, setActiveKey] = useState("BUSINESS_CONTRACT");
|
||||
const [previewOpen, setPreviewOpen] = useState(false);
|
||||
const [previewUrl, setPreviewUrl] = useState("");
|
||||
const [wpsVisible, setWpsVisible] = useState(false);
|
||||
|
||||
const fetchContract = async () => {
|
||||
if (!projectId) return;
|
||||
|
|
@ -472,14 +471,11 @@ const EvalProjectMonitorDetail = (props) => {
|
|||
activeKey={activeKey}
|
||||
onChange={(key) => {
|
||||
if (key === "REPORT_PREPARATION") {
|
||||
// 2026-08-18 监管端报告改用本地 OfficePreview 预览,避免 WPS SDK 内部空指针报错
|
||||
const url = wpsDoc?.wpsUrl;
|
||||
if (!url) {
|
||||
if (!wpsDoc?.fileId) {
|
||||
message.warning("暂无报告文档,请先在机构端生成报告后再查看");
|
||||
return;
|
||||
}
|
||||
setPreviewUrl(url);
|
||||
setPreviewOpen(true);
|
||||
setWpsVisible(true);
|
||||
return;
|
||||
}
|
||||
setActiveKey(key);
|
||||
|
|
@ -516,13 +512,13 @@ const EvalProjectMonitorDetail = (props) => {
|
|||
},
|
||||
]}
|
||||
/>
|
||||
<OfficePreview
|
||||
mode="modal"
|
||||
open={previewOpen}
|
||||
onCancel={() => setPreviewOpen(false)}
|
||||
url={previewUrl}
|
||||
width={1100}
|
||||
{wpsVisible && (
|
||||
<WpsEditor
|
||||
onCancel={() => setWpsVisible(false)}
|
||||
fileId={wpsDoc?.fileId}
|
||||
readOnly
|
||||
/>
|
||||
)}
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -122,7 +122,15 @@ const App = (props) => {
|
|||
customArgs: {
|
||||
readOnly,
|
||||
},
|
||||
...(readOnly ? { commonOptions: { isBrowserViewMode: true } } : {}),
|
||||
...(readOnly
|
||||
? {
|
||||
commonOptions: {
|
||||
isBrowserViewMode: true,
|
||||
// 只读模式下隐藏 WPS 顶部区域(头部和工具栏),页面已有自己的标题栏
|
||||
isShowTopArea: false,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
await instance.ready();
|
||||
sdkRef.current = instance;
|
||||
|
|
|
|||
Loading…
Reference in New Issue