|
@@ -127,7 +127,7 @@ function ImageLightbox({ src, onClose }: { src: string; onClose: () => void }) {
|
|
|
role="dialog"
|
|
role="dialog"
|
|
|
aria-label="图片浏览"
|
|
aria-label="图片浏览"
|
|
|
className="fixed inset-0 z-[100] flex items-center justify-center overflow-hidden select-none"
|
|
className="fixed inset-0 z-[100] flex items-center justify-center overflow-hidden select-none"
|
|
|
- style={{ background: "rgba(26,82,153,.88)" }}
|
|
|
|
|
|
|
+ style={{ background: "rgba(var(--dms-deep-rgb),.88)" }}
|
|
|
>
|
|
>
|
|
|
<img
|
|
<img
|
|
|
src={src}
|
|
src={src}
|
|
@@ -614,7 +614,7 @@ export function FilePreviewModal({ item, onClose, mode = "side", asideRef, style
|
|
|
|
|
|
|
|
const previewChildren = (
|
|
const previewChildren = (
|
|
|
<>
|
|
<>
|
|
|
- {loading && <div className="absolute inset-0 z-10 flex items-center justify-center text-base font-black" style={{ color: "#1A5299", background: "rgba(235,244,253,.9)" }}><RefreshCw size={15} className="animate-spin mr-2" />正在加载预览…</div>}
|
|
|
|
|
|
|
+ {loading && <div className="absolute inset-0 z-10 flex items-center justify-center text-base font-black" style={{ color: "var(--dms-deep)", background: "rgba(235,244,253,.9)" }}><RefreshCw size={15} className="animate-spin mr-2" />正在加载预览…</div>}
|
|
|
{pdfBlob && (
|
|
{pdfBlob && (
|
|
|
<PdfViewer
|
|
<PdfViewer
|
|
|
blob={pdfBlob}
|
|
blob={pdfBlob}
|
|
@@ -666,20 +666,20 @@ export function FilePreviewModal({ item, onClose, mode = "side", asideRef, style
|
|
|
</div>
|
|
</div>
|
|
|
{/* 错误提示必须绝对定位铺满:上方 docx 滚动容器带 h-full,即使为空也占满
|
|
{/* 错误提示必须绝对定位铺满:上方 docx 滚动容器带 h-full,即使为空也占满
|
|
|
内容区,流式提示会被 overflow-hidden 裁掉而不可见(403 无权/415 不支持等同理) */}
|
|
内容区,流式提示会被 overflow-hidden 裁掉而不可见(403 无权/415 不支持等同理) */}
|
|
|
- {error && <div className="absolute inset-0 z-10 flex flex-col items-center justify-center p-8 text-center" style={{ color: "#1A5299" }}>
|
|
|
|
|
|
|
+ {error && <div className="absolute inset-0 z-10 flex flex-col items-center justify-center p-8 text-center" style={{ color: "var(--dms-deep)" }}>
|
|
|
<FileWarning size={38} className="mx-auto mb-3 opacity-60" />
|
|
<FileWarning size={38} className="mx-auto mb-3 opacity-60" />
|
|
|
<div className="text-base font-black leading-6">{errorText(error, current.fileExtension)}</div>
|
|
<div className="text-base font-black leading-6">{errorText(error, current.fileExtension)}</div>
|
|
|
{error.requestId && <div className="text-sm font-bold opacity-60 mt-2">错误编号:{error.requestId}</div>}
|
|
{error.requestId && <div className="text-sm font-bold opacity-60 mt-2">错误编号:{error.requestId}</div>}
|
|
|
- {error.httpStatus !== 401 && <button onClick={() => setRetryVersion((value) => value + 1)} className="mt-4 px-3 py-1.5 text-base font-black border" style={{ borderColor: "#3191eb" }}>重试预览</button>}
|
|
|
|
|
|
|
+ {error.httpStatus !== 401 && <button onClick={() => setRetryVersion((value) => value + 1)} className="mt-4 px-3 py-1.5 text-base font-black border" style={{ borderColor: "var(--dms-bright)" }}>重试预览</button>}
|
|
|
</div>}
|
|
</div>}
|
|
|
</>
|
|
</>
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
const textPanel = (
|
|
const textPanel = (
|
|
|
- <aside ref={textPanelRef} className="w-[360px] flex-shrink-0 flex flex-col border-l bg-white min-h-0" style={{ borderColor: "rgba(40,118,193,.2)" }}>
|
|
|
|
|
|
|
+ <aside ref={textPanelRef} className="w-[360px] flex-shrink-0 flex flex-col border-l bg-white min-h-0" style={{ borderColor: "rgba(var(--dms-line-rgb),.2)" }}>
|
|
|
{/* 补充说明:具备编辑权限的可编辑保存,其他用户只读展示 */}
|
|
{/* 补充说明:具备编辑权限的可编辑保存,其他用户只读展示 */}
|
|
|
- <div className="px-4 py-3 border-b flex-shrink-0" style={{ borderColor: "rgba(40,118,193,.15)" }}>
|
|
|
|
|
- <div className="text-sm font-black mb-2" style={{ color: "#1A5299" }}>补充说明</div>
|
|
|
|
|
|
|
+ <div className="px-4 py-3 border-b flex-shrink-0" style={{ borderColor: "rgba(var(--dms-line-rgb),.15)" }}>
|
|
|
|
|
+ <div className="text-sm font-black mb-2" style={{ color: "var(--dms-deep)" }}>补充说明</div>
|
|
|
{canEditDescription ? (
|
|
{canEditDescription ? (
|
|
|
<>
|
|
<>
|
|
|
<textarea
|
|
<textarea
|
|
@@ -687,7 +687,7 @@ export function FilePreviewModal({ item, onClose, mode = "side", asideRef, style
|
|
|
onChange={(event) => setDescriptionDraft(event.target.value)}
|
|
onChange={(event) => setDescriptionDraft(event.target.value)}
|
|
|
placeholder="输入文档补充说明…"
|
|
placeholder="输入文档补充说明…"
|
|
|
className="w-full h-28 px-2.5 py-2 text-base font-bold leading-6 rounded-sm outline-none resize-none border"
|
|
className="w-full h-28 px-2.5 py-2 text-base font-bold leading-6 rounded-sm outline-none resize-none border"
|
|
|
- style={{ color: "#1A5299", borderColor: "rgba(40,118,193,.3)", background: "rgba(40,118,193,.03)" }}
|
|
|
|
|
|
|
+ style={{ color: "var(--dms-deep)", borderColor: "rgba(var(--dms-line-rgb),.3)", background: "rgba(var(--dms-line-rgb),.03)" }}
|
|
|
/>
|
|
/>
|
|
|
<div className="flex items-center gap-2 mt-2">
|
|
<div className="flex items-center gap-2 mt-2">
|
|
|
<button
|
|
<button
|
|
@@ -695,7 +695,7 @@ export function FilePreviewModal({ item, onClose, mode = "side", asideRef, style
|
|
|
onClick={() => void saveDescription()}
|
|
onClick={() => void saveDescription()}
|
|
|
disabled={savingDescription}
|
|
disabled={savingDescription}
|
|
|
className="px-3 py-1 text-base font-black text-white rounded-sm disabled:opacity-60"
|
|
className="px-3 py-1 text-base font-black text-white rounded-sm disabled:opacity-60"
|
|
|
- style={{ background: "#3191eb" }}
|
|
|
|
|
|
|
+ style={{ background: "var(--dms-bright)" }}
|
|
|
>
|
|
>
|
|
|
{savingDescription ? "保存中…" : "保存说明"}
|
|
{savingDescription ? "保存中…" : "保存说明"}
|
|
|
</button>
|
|
</button>
|
|
@@ -704,26 +704,26 @@ export function FilePreviewModal({ item, onClose, mode = "side", asideRef, style
|
|
|
onClick={() => { setDescriptionDraft(description ?? ""); setDescriptionMessage(""); }}
|
|
onClick={() => { setDescriptionDraft(description ?? ""); setDescriptionMessage(""); }}
|
|
|
disabled={savingDescription}
|
|
disabled={savingDescription}
|
|
|
className="px-3 py-1 text-base font-black border rounded-sm disabled:opacity-60"
|
|
className="px-3 py-1 text-base font-black border rounded-sm disabled:opacity-60"
|
|
|
- style={{ color: "#1A5299", borderColor: "rgba(40,118,193,.35)" }}
|
|
|
|
|
|
|
+ style={{ color: "var(--dms-deep)", borderColor: "rgba(var(--dms-line-rgb),.35)" }}
|
|
|
>
|
|
>
|
|
|
重置
|
|
重置
|
|
|
</button>
|
|
</button>
|
|
|
{descriptionMessage && (
|
|
{descriptionMessage && (
|
|
|
- <span className="text-base font-bold opacity-70 break-all" style={{ color: "#1A5299" }}>{descriptionMessage}</span>
|
|
|
|
|
|
|
+ <span className="text-base font-bold opacity-70 break-all" style={{ color: "var(--dms-deep)" }}>{descriptionMessage}</span>
|
|
|
)}
|
|
)}
|
|
|
</div>
|
|
</div>
|
|
|
</>
|
|
</>
|
|
|
) : (
|
|
) : (
|
|
|
- <div className="max-h-56 overflow-y-auto text-sm leading-6 whitespace-pre-wrap break-words" style={{ color: "#1A5299" }}>
|
|
|
|
|
|
|
+ <div className="max-h-56 overflow-y-auto text-sm leading-6 whitespace-pre-wrap break-words" style={{ color: "var(--dms-deep)" }}>
|
|
|
{description?.trim() ? description : "暂无说明"}
|
|
{description?.trim() ? description : "暂无说明"}
|
|
|
</div>
|
|
</div>
|
|
|
)}
|
|
)}
|
|
|
</div>
|
|
</div>
|
|
|
{/* 关联方案:modal 模式展示,点击后在本弹窗内跳转查看目标方案 */}
|
|
{/* 关联方案:modal 模式展示,点击后在本弹窗内跳转查看目标方案 */}
|
|
|
{(relations.length > 0 || relationsError) && (
|
|
{(relations.length > 0 || relationsError) && (
|
|
|
- <div className="px-4 py-3 border-b flex-shrink-0" style={{ borderColor: "rgba(40,118,193,.15)" }} data-testid="preview-relations">
|
|
|
|
|
- <div className="text-base font-black mb-2 flex items-center gap-1" style={{ color: "#1A5299" }}><Link2 size={13} />关联方案</div>
|
|
|
|
|
- {relationsError && <div className="text-base font-bold opacity-70 mb-1" style={{ color: "#1A5299" }}>{relationsError}</div>}
|
|
|
|
|
|
|
+ <div className="px-4 py-3 border-b flex-shrink-0" style={{ borderColor: "rgba(var(--dms-line-rgb),.15)" }} data-testid="preview-relations">
|
|
|
|
|
+ <div className="text-base font-black mb-2 flex items-center gap-1" style={{ color: "var(--dms-deep)" }}><Link2 size={13} />关联方案</div>
|
|
|
|
|
+ {relationsError && <div className="text-base font-bold opacity-70 mb-1" style={{ color: "var(--dms-deep)" }}>{relationsError}</div>}
|
|
|
<div className="space-y-1">
|
|
<div className="space-y-1">
|
|
|
{relations.map((relation) => (
|
|
{relations.map((relation) => (
|
|
|
<button
|
|
<button
|
|
@@ -731,8 +731,8 @@ export function FilePreviewModal({ item, onClose, mode = "side", asideRef, style
|
|
|
type="button"
|
|
type="button"
|
|
|
disabled={relationJumping}
|
|
disabled={relationJumping}
|
|
|
onClick={() => void jumpToRelation(relation)}
|
|
onClick={() => void jumpToRelation(relation)}
|
|
|
- className="block w-full text-left px-2.5 py-1.5 text-base font-bold rounded-sm transition-colors hover:bg-[rgba(49,145,235,0.18)] disabled:opacity-60"
|
|
|
|
|
- style={{ color: "#1A5299" }}
|
|
|
|
|
|
|
+ className="block w-full text-left px-2.5 py-1.5 text-base font-bold rounded-sm transition-colors hover:bg-[rgba(var(--dms-bright-rgb),0.18)] disabled:opacity-60"
|
|
|
|
|
+ style={{ color: "var(--dms-deep)" }}
|
|
|
{...tipProps("点击查看该方案")}
|
|
{...tipProps("点击查看该方案")}
|
|
|
>
|
|
>
|
|
|
{relation.relationType} · {relation.relatedDocumentName}
|
|
{relation.relationType} · {relation.relatedDocumentName}
|
|
@@ -741,7 +741,7 @@ export function FilePreviewModal({ item, onClose, mode = "side", asideRef, style
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
)}
|
|
)}
|
|
|
- <div className="px-4 py-3 text-base font-black flex-shrink-0" style={{ color: "#1A5299", borderBottom: "1px solid rgba(40,118,193,.15)" }}>
|
|
|
|
|
|
|
+ <div className="px-4 py-3 text-base font-black flex-shrink-0" style={{ color: "var(--dms-deep)", borderBottom: "1px solid rgba(var(--dms-line-rgb),.15)" }}>
|
|
|
提取文本
|
|
提取文本
|
|
|
</div>
|
|
</div>
|
|
|
<div className="flex-1 min-h-0 overflow-y-auto px-4 py-3 space-y-2.5">
|
|
<div className="flex-1 min-h-0 overflow-y-auto px-4 py-3 space-y-2.5">
|
|
@@ -755,12 +755,12 @@ export function FilePreviewModal({ item, onClose, mode = "side", asideRef, style
|
|
|
<div
|
|
<div
|
|
|
key={index}
|
|
key={index}
|
|
|
className={`rounded-sm px-2.5 py-2 ${hasCurrent ? "ring-1 ring-orange-400" : ""}`}
|
|
className={`rounded-sm px-2.5 py-2 ${hasCurrent ? "ring-1 ring-orange-400" : ""}`}
|
|
|
- style={{ background: "rgba(40,118,193,.04)" }}
|
|
|
|
|
|
|
+ style={{ background: "rgba(var(--dms-line-rgb),.04)" }}
|
|
|
>
|
|
>
|
|
|
- <div className="text-sm leading-6 whitespace-pre-wrap break-words" style={{ color: "#1A5299" }}>
|
|
|
|
|
|
|
+ <div className="text-sm leading-6 whitespace-pre-wrap break-words" style={{ color: "var(--dms-deep)" }}>
|
|
|
{renderParagraphWithHighlights(info, search.currentIndex)}
|
|
{renderParagraphWithHighlights(info, search.currentIndex)}
|
|
|
</div>
|
|
</div>
|
|
|
- <div className="mt-1.5 text-base font-black opacity-50" style={{ color: "#1A5299" }}>
|
|
|
|
|
|
|
+ <div className="mt-1.5 text-base font-black opacity-50" style={{ color: "var(--dms-deep)" }}>
|
|
|
第 {index + 1} 段 · 共 {info.text.length} 字{info.ranges.length > 0 && ` · 命中 ${info.ranges.length} 处`}
|
|
第 {index + 1} 段 · 共 {info.text.length} 字{info.ranges.length > 0 && ` · 命中 ${info.ranges.length} 处`}
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -781,7 +781,7 @@ export function FilePreviewModal({ item, onClose, mode = "side", asideRef, style
|
|
|
: sideWidth == null
|
|
: sideWidth == null
|
|
|
? "relative flex-1 min-w-[360px] flex flex-col border-l bg-white min-h-0"
|
|
? "relative flex-1 min-w-[360px] flex flex-col border-l bg-white min-h-0"
|
|
|
: "relative flex-shrink-0 flex flex-col border-l bg-white min-h-0"}
|
|
: "relative flex-shrink-0 flex flex-col border-l bg-white min-h-0"}
|
|
|
- style={{ borderColor: "rgba(40,118,193,.2)", ...(isModal ? null : { ...asideRestStyle, ...(sideWidth == null ? null : { width: sideWidth }) }) }}
|
|
|
|
|
|
|
+ style={{ borderColor: "rgba(var(--dms-line-rgb),.2)", ...(isModal ? null : { ...asideRestStyle, ...(sideWidth == null ? null : { width: sideWidth }) }) }}
|
|
|
>
|
|
>
|
|
|
{!isModal && (
|
|
{!isModal && (
|
|
|
<div
|
|
<div
|
|
@@ -789,7 +789,7 @@ export function FilePreviewModal({ item, onClose, mode = "side", asideRef, style
|
|
|
aria-orientation="vertical"
|
|
aria-orientation="vertical"
|
|
|
aria-label="拖动调整预览区宽度"
|
|
aria-label="拖动调整预览区宽度"
|
|
|
{...tipProps("拖动调整预览区宽度")}
|
|
{...tipProps("拖动调整预览区宽度")}
|
|
|
- className="absolute left-0 top-0 bottom-0 z-10 w-2.5 -ml-[5px] cursor-col-resize transition-colors hover:bg-[rgba(49,145,235,0.35)]"
|
|
|
|
|
|
|
+ className="absolute left-0 top-0 bottom-0 z-10 w-2.5 -ml-[5px] cursor-col-resize transition-colors hover:bg-[rgba(var(--dms-bright-rgb),0.35)]"
|
|
|
onPointerDown={(event) => {
|
|
onPointerDown={(event) => {
|
|
|
if (event.button !== 0) return;
|
|
if (event.button !== 0) return;
|
|
|
event.preventDefault();
|
|
event.preventDefault();
|
|
@@ -831,7 +831,7 @@ export function FilePreviewModal({ item, onClose, mode = "side", asideRef, style
|
|
|
{!(isModal && windowState === "fullscreen") && (
|
|
{!(isModal && windowState === "fullscreen") && (
|
|
|
<div
|
|
<div
|
|
|
className={`px-4 py-3 flex gap-3 text-white ${isModal ? "items-center" : "items-start"}`}
|
|
className={`px-4 py-3 flex gap-3 text-white ${isModal ? "items-center" : "items-start"}`}
|
|
|
- style={{ background: "#1A5299" }}
|
|
|
|
|
|
|
+ style={{ background: "var(--dms-deep)" }}
|
|
|
onDoubleClick={isModal ? (event) => {
|
|
onDoubleClick={isModal ? (event) => {
|
|
|
if ((event.target as HTMLElement).closest("input, button")) return;
|
|
if ((event.target as HTMLElement).closest("input, button")) return;
|
|
|
setWindowState((current) => (current === "maximized" ? "normal" : "maximized"));
|
|
setWindowState((current) => (current === "maximized" ? "normal" : "maximized"));
|
|
@@ -855,7 +855,7 @@ export function FilePreviewModal({ item, onClose, mode = "side", asideRef, style
|
|
|
{isModal && (
|
|
{isModal && (
|
|
|
<div className="flex items-center gap-1.5 flex-shrink-0">
|
|
<div className="flex items-center gap-1.5 flex-shrink-0">
|
|
|
<div className="relative">
|
|
<div className="relative">
|
|
|
- <Search size={12} className="absolute left-1.5 top-1/2 -translate-y-1/2 opacity-60" style={{ color: "#1A5299" }} />
|
|
|
|
|
|
|
+ <Search size={12} className="absolute left-1.5 top-1/2 -translate-y-1/2 opacity-60" style={{ color: "var(--dms-deep)" }} />
|
|
|
<input
|
|
<input
|
|
|
value={search.keyword}
|
|
value={search.keyword}
|
|
|
onChange={(e) => search.setKeyword(e.target.value)}
|
|
onChange={(e) => search.setKeyword(e.target.value)}
|
|
@@ -903,12 +903,12 @@ export function FilePreviewModal({ item, onClose, mode = "side", asideRef, style
|
|
|
type="button"
|
|
type="button"
|
|
|
onClick={() => setWindowState("normal")}
|
|
onClick={() => setWindowState("normal")}
|
|
|
className="absolute top-3 right-4 z-30 flex items-center gap-1.5 px-3 py-1.5 text-base font-black text-white rounded-sm shadow-lg"
|
|
className="absolute top-3 right-4 z-30 flex items-center gap-1.5 px-3 py-1.5 text-base font-black text-white rounded-sm shadow-lg"
|
|
|
- style={{ background: "rgba(26,82,153,.85)" }}
|
|
|
|
|
|
|
+ style={{ background: "rgba(var(--dms-deep-rgb),.85)" }}
|
|
|
>
|
|
>
|
|
|
<Shrink size={14} />退出全屏
|
|
<Shrink size={14} />退出全屏
|
|
|
</button>
|
|
</button>
|
|
|
)}
|
|
)}
|
|
|
- <div className={`flex-1 min-h-0 overflow-hidden relative ${isModal ? "flex flex-row" : ""}`} style={{ background: "#EBF4FD" }}>
|
|
|
|
|
|
|
+ <div className={`flex-1 min-h-0 overflow-hidden relative ${isModal ? "flex flex-row" : ""}`} style={{ background: "var(--dms-canvas)" }}>
|
|
|
{isModal ? (
|
|
{isModal ? (
|
|
|
/* 外层 flex 子项容器把内容区限制在文本面板左侧(textPanel 360px),
|
|
/* 外层 flex 子项容器把内容区限制在文本面板左侧(textPanel 360px),
|
|
|
absolute inset-0 相对本层定位 */
|
|
absolute inset-0 相对本层定位 */
|
|
@@ -941,7 +941,7 @@ export function FilePreviewModal({ item, onClose, mode = "side", asideRef, style
|
|
|
// modalZoom.ref 挂遮罩根:弹窗全域(标题栏/提取文本面板/遮罩)Ctrl+滚轮统一缩放
|
|
// modalZoom.ref 挂遮罩根:弹窗全域(标题栏/提取文本面板/遮罩)Ctrl+滚轮统一缩放
|
|
|
// 文档并 preventDefault,与 side 模式(监听挂 aside 根)语义一致。
|
|
// 文档并 preventDefault,与 side 模式(监听挂 aside 根)语义一致。
|
|
|
return createPortal(
|
|
return createPortal(
|
|
|
- <div ref={modalZoom.ref} className="fixed inset-0 z-[80] flex items-center justify-center" style={{ background: "rgba(26,82,153,.58)" }}><div>{panel}</div>{lightboxSrc && <ImageLightbox key={lightboxSrc} src={lightboxSrc} onClose={() => setLightboxSrc(null)} />}</div>,
|
|
|
|
|
|
|
+ <div ref={modalZoom.ref} className="fixed inset-0 z-[80] flex items-center justify-center" style={{ background: "rgba(var(--dms-deep-rgb),.58)" }}><div>{panel}</div>{lightboxSrc && <ImageLightbox key={lightboxSrc} src={lightboxSrc} onClose={() => setLightboxSrc(null)} />}</div>,
|
|
|
document.body,
|
|
document.body,
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
@@ -952,7 +952,7 @@ export function FilePreviewModal({ item, onClose, mode = "side", asideRef, style
|
|
|
ref={guideEl}
|
|
ref={guideEl}
|
|
|
aria-hidden
|
|
aria-hidden
|
|
|
className="fixed left-0 top-0 bottom-0 z-[90] w-0.5 pointer-events-none"
|
|
className="fixed left-0 top-0 bottom-0 z-[90] w-0.5 pointer-events-none"
|
|
|
- style={{ display: "none", background: "#3191eb", willChange: "transform" }}
|
|
|
|
|
|
|
+ style={{ display: "none", background: "var(--dms-bright)", willChange: "transform" }}
|
|
|
/>
|
|
/>
|
|
|
{panel}
|
|
{panel}
|
|
|
{/* 图片浏览遮罩在内容区之外渲染:docx 缩放后内层带 transform,
|
|
{/* 图片浏览遮罩在内容区之外渲染:docx 缩放后内层带 transform,
|