状态管理
UI 状态由 Pinia 管理,拆分为两个 store。本指南描述 store 架构、各 store 持有的关键状态,以及 apis/ 层如何与之交互。
Store 设置
packages/webview/src/stores/index.js 创建单个 Pinia 实例并导出 setupStore(app) 安装。store 遵循严格的双 store 分离(记录于 stores/AGENTS.md):
- Viewer store —— "如何看待文档"(UI/视图状态)。
- Document store —— "文档里有什么 & 如何在其上绘制"(数据 + 工具样式)。
两个 store 被 components/ 和 apis/ 中约 102 个文件引用。
Viewer store — modules/viewer.js(useViewerStore,约 1770 行)
持有 UI/视图状态。要点:
| 类别 | 字段 |
|---|---|
| 视图/导航 | currentPage、scale、oldScale、newScale、zoomLevel(预设数组)、scrollMode、pageMode、pageLabels。 |
| 主题/显示 | theme('light'/'dark')、fullScreen、language、languages[]。 |
| 工具模式 | toolMode(默认 'view')、activeHand、activeStickNote、activeActiveMeasure。 |
| 元素开关 | activeElements{}(按 DataElements.* 键的布尔映射)、activeElementsTab{}、panelSpace{left,right}、openedPanelsNum{left,right}。 |
| 禁用元素 | disabledElements{} —— 每元素 { disabled, style }。 |
| 工具栏定义 | headers{}(按分组)、rightHeaders[]、toolItems{}、documentEditorHeaders、toolbarGroup、activeHeaderGroup。 |
| 签名配置 | signatureDialogTabs、signatureDialogProperties{}、enableSignatureTemplates、digitalSignature{}、selectedSignatureField、disableSignatureTool、electronicSignaturesList[]、digitalSignaturesList[]。 |
| 印章 | customStamps[]、stampPanelTabs[]、importStamps[]。 |
| 对比 | compareStatus、compareMode、showCompareTip。 |
| 弹出菜单 | textPopup[]、cropPagePopup[]、contentEditorPopup[]、annotationPopup[]、showAnnotationPopup、popoverChanged。 |
| 表单面板 | formBoardFold、formBoardPosition{}、enableFillForm、skipNoRequired、consecutiveType。 |
| 杂项标志 | license、verified、readOnly、userAdmin、isDisabledHeader、downloading、upload、uploadLoading、customLoading、documentEditorSaveStatus、documentEditorSaveAsStatus、webviewerMode。 |
| 字体 | customFonts[]、fontSizes[]。 |
| 面板配置 | genericPanels[]、customModals[]。 |
| 批注标志 | annotation{isAnnotationExport, isAnnotationImport}。 |
关键 action
openElement(dataElement, capability?)—— 设置activeElements[de]=true;关闭同侧兄弟面板;在LEFT_PANEL时切换core.toggleSidebar();在SEARCH_PANEL时还打开LEFT_PANEL并切换其标签。closeElement/openElements/closeElements/toggleElement/resetPanels—— 变体。关闭LEFT_PANEL还会调用core.webViewerPageMode('none')并关闭SEARCH_PANEL。setActiveElementTab(dataElement, tab)—— 将ThumbnailsButton映射为core.webViewerPageMode('thumbs'),OutlinesButton为'outline'。setActiveToolMode(mode)—— 守卫readOnly和移动端(阻止separation/measurement);关闭上下文面板;调用core.setToolMode(mode)。setToolbarGroup(value)—— 移动端对分色/测量的守卫。disableElement/enableElement/disableElements/enableElements(de, key?)—— 切换禁用标志。- 印章/签名列表修改器持久化到
localStorage。 dispatch(action)/getState(key)—— 明确的 API 访问通道;向apis/暴露 store action/state。
Document store — modules/document.js(useDocumentStore,约 600 行)
持有文档数据 + 工具配置。要点:
| 类别 | 字段 |
|---|---|
| 文档元数据 | totalPages、loadingProgress、annotationsCount、info、currentPdfData{}、fileHasPwd、pwd。 |
| 批注 | annotations(shallowReactive)、selected{}、selectedAnnotation、outlines[]、activeOutlineId、searchResults[]、activeSearchResult、layers[]、selectedLayer。 |
| 当前工具 | activeTool、markupTool(默认 HIGHLIGHT)、shapeTool(默认 SQUARE)。 |
| 工具样式预设 | 按工具的 { color, opacity?, stroke? }(高亮、下划线、波浪线、删除线、矩形、弧、多边形、折线、圆、箭头、线、墨迹、自由文本、文本、表单字段、密文、移除)。 |
| 属性面板 | propertyPanel{} —— 表单字段/编辑器属性模型。 |
| 对比 | compareFiles{}、compareSettings{}、compareResult。 |
| 签名 | signatures[]、selectedSignature、trustedCertificateLists[]、pkcs12、author、annotator、highlightLink、highlightForm、autoJumpNextSign、disableDigitalSignature、disableElectronicSignature。 |
| 测量 | measure{scale, precision}。 |
| 文档编辑 | documentEditor{operationList[], pageList[], selectedPageList[]}。 |
| 撤销/重做 | canUndo{content, annotation}、canRedo{content, annotation}。 |
关键 action
setActiveToolColor(color)—— 写入this[activeTool].color并调用core.setTool(activeTool)。setToolState(tool)—— 相同则切换关闭;否则查找map[tool],调用core.setTool(mapTool.tool, mapTool.toolName),设置activeTool,表单工具还会调用core.setInitProperty()。setActiveToolColor/setPropertyPanel/setCurrentUser/setHighlightLink/setHighlightForm/setAutoJumpNextSign—— 存储的同时推送到core。initAnnotations/setAnnotations(annotation, type)—— 按页索引管理批注数组。dispatch(action)/getState(key)—— API 访问。
apis/ 如何与 store 交互
每个 apis/*.js 模块是工厂 (stores...) => (...args) => result。两种访问模式:
- 通过 getter 读取:
getActiveTool.js→store.getActiveTool。 - 通过
dispatch调用 action:closeElement.js→store.dispatch('closeElement')(dataElement)。
部分 API(openElement、closeElements、enableElements、disableElements、addPanel、getPanels、isElementOpen、getPassword)使用 dispatch/getState;其他直接调用 getter。api_helpers/createEncapsulatedStore.js 构建封装的 viewerStore / documentStore 包装器(仅暴露 dispatch/getState),供弹出菜单 API、getPassword 和 getPanels 使用。
持久化
localStorage['language']—— 所选语言。localStorage['customStamps']、['electronicSignatures']、['digitalSignatures']—— 印章/签名列表。