From 5e143d9755c291a6874579785a359f5037eadbff Mon Sep 17 00:00:00 2001 From: awen Date: Thu, 17 Apr 2025 16:34:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=8F=AF=E7=9C=8B=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/root.tsx | 3 +- app/routes/rules.new1.tsx | 2093 +++++++++++-------------------------- app/tmp/debug-docx.js | 57 + app/tmp/test-docx.html | 221 ++++ app/types.d.ts | 44 + package-lock.json | 868 ++++++++++++++- package.json | 3 + vite.config.ts | 2 +- 8 files changed, 1733 insertions(+), 1558 deletions(-) create mode 100644 app/tmp/debug-docx.js create mode 100644 app/tmp/test-docx.html create mode 100644 app/types.d.ts diff --git a/app/root.tsx b/app/root.tsx index ea3e333..3b9de98 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -1,7 +1,7 @@ // import React from 'react'; import { Links, - LiveReload, + // LiveReload, // 不再需要,使用Vite时会与内置HMR冲突 Meta, Outlet, Scripts, @@ -77,7 +77,6 @@ export default function App() { - ); diff --git a/app/routes/rules.new1.tsx b/app/routes/rules.new1.tsx index d4d56df..fe3ed81 100644 --- a/app/routes/rules.new1.tsx +++ b/app/routes/rules.new1.tsx @@ -1,1539 +1,622 @@ -/** - * 评查点管理页面 - 创建或编辑评查点规则 - * - * 功能概述: - * - 支持创建新的评查点规则或编辑现有规则 - * - 评查点包含基本信息、抽取设置和评查设置三大部分 - * - 支持使用三种抽取方式: 大模型(LLM)抽取、多模态(VLM)抽取和正则表达式抽取 - * - 支持配置各种类型的评查规则,如存在性检查、内容一致性检查等 - * - 支持保存评查规则和保存草稿功能 - * - * 组件结构: - * - PageHeader: 页面标题和保存按钮 - * - BasicInfo: 评查点的基本信息设置,如名称、编码、风险级别等 - * - ExtractionSettings: 抽取设置,配置从文档中抽取哪些字段及抽取方式 - * - ReviewSettings: 评查设置,基于抽取字段配置评查规则、评查结果消息等 - * - ActionButtons: 页面底部的操作按钮,包括保存、保存草稿和返回 - * - * 数据流转: - * 1. 页面加载时检查URL参数,确定是新建还是编辑模式 - * 2. 编辑模式下从API获取评查点数据并填充表单 - * 3. ExtractionSettings通过RuleContext将抽取的字段传递给ReviewSettings - * 4. 各组件的onChange回调收集表单变更并更新formData状态 - * 5. 保存时将formData转换为API需要的格式并提交 - * - * @author 中国烟草AI合同及卷宗审核系统开发团队 - */ +import { useState, useEffect, useRef } from "react"; +import { useLoaderData } from "@remix-run/react"; +import { Document, Page, pdfjs } from "react-pdf"; +import type { LoaderFunctionArgs } from "@remix-run/node"; +import mammoth from "mammoth"; -import { type MetaFunction } from "@remix-run/node"; -import { useState, useEffect } from "react"; -import { BasicInfo } from "~/components/rules/new/BasicInfo"; -import { ExtractionSettings } from "~/components/rules/new/ExtractionSettings"; -import { ReviewSettings } from "~/components/rules/new/ReviewSettings"; -import { RuleContext } from "~/contexts/RuleContext"; -import { ActionButtons } from "~/components/rules/new/ActionButtons"; -import { PageHeader } from "~/components/rules/new/PageHeader"; -import rulesStyles from "~/styles/rules.css?url"; -import { useNavigate, useLocation } from "@remix-run/react"; +// 设置 pdfjs 工作线程 +pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`; -export const meta: MetaFunction = () => { - return [ - { title: "评查点管理 - 中国烟草AI合同及卷宗审核系统" }, - { - name: "description", - content: "创建或修改评查点,设置规则参数" - } - ]; -}; +// 模拟后端返回的抽取内容数据 +const mockExtractedContent = [ + { id: 1, text: "合同条款", page: 2, position: { start: 50, end: 60 } }, + { id: 2, text: "签署日期", page: 5, position: { start: 120, end: 130 } }, + { id: 3, text: "责任划分", page: 3, position: { start: 80, end: 90 } }, +]; -export function links() { - return [{ rel: "stylesheet", href: rulesStyles }]; -} - -export const handle = { - breadcrumb: "评查点管理" -}; - -// 定义类型 -/** - * 正则表达式字段定义 - * @property field - 字段名称 - * @property pattern - 正则表达式模式 - */ -interface RegexField { - field: string; - pattern: string; -} - -/** - * 多模态(VLM)字段定义,包含字段名和类型 - * @property name - 字段名称 - * @property type - 字段类型,如"default"、"table"等 - */ -interface ApiVlmField { - name: string; - type: string; -} - -/** - * 提示词设置 - * @property type - 提示词类型,如"system"、"user"等 - * @property template - 提示词模板内容 - */ -interface PromptSetting { - type: string; - template: string; -} - -/** - * 抽取配置,包含三种抽取方式 - * @property llm - 大模型抽取配置 - * @property vlm - 多模态抽取配置 - * @property regex - 正则表达式抽取配置 - */ -interface ExtactionConfigType { - llm: { - fields: string[]; - prompt_setting: PromptSetting; - }; - vlm: { - fields: (ApiVlmField | string)[]; - prompt_setting: PromptSetting; - }; - regex: { - fields: RegexField[]; - }; -} - -/** - * API请求使用的抽取配置,字段可选 - */ -interface ApiExtactionConfigType { - llm?: { - fields?: string[]; - prompt_setting?: { - type?: string; - template?: string; - } - }; - vlm?: { - fields?: ApiVlmField[]; - prompt_setting?: { - type?: string; - template?: string; - } - }; - regex?: { - fields?: RegexField[]; - }; -} - -/** - * 规则配置类型,使用索引签名支持不同类型规则的不同配置项 - */ -interface RuleConfigType { - [key: string]: unknown; -} - -/** - * 评查规则定义 - * @property id - 规则唯一标识 - * @property type - 规则类型,如"exists"、"consistency"等 - * @property config - 规则配置,根据规则类型不同而不同 - */ -interface Rule { - id: string; - type: string; - config: RuleConfigType; -} - -/** - * 评查配置 - * @property logicType - 规则组合逻辑,如"and"、"or"、"custom" - * @property customLogic - 自定义逻辑表达式 - * @property rules - 规则列表 - */ -interface EvaluationConfigType { - logicType: string; - customLogic: string; - rules: Rule[]; -} - -/** - * 表单数据类型,包含评查点的所有配置项 - */ -interface FormDataType { - id?: number; - name: string; // 评查点名称 - code: string; // 评查点编码 - risk: string; // 风险级别 - is_enabled: boolean; // 是否启用 - description: string; // 描述 - references_laws: { // 法律法规引用 - name: string; // 法规名称 - articles: string[]; // 条款列表 - content: string; // 法规内容 - }; - evaluation_point_groups_id: number | null; // 所属评查组ID - extraction_config: ExtactionConfigType; // 抽取配置 - evaluation_config: EvaluationConfigType; // 评查配置 - pass_message: string; // 通过消息 - fail_message: string; // 失败消息 - suggestion_message: string; // 建议消息 - suggestion_message_type: string; // 建议消息类型 - post_action: string; // 评查后动作 - action_config: string; // 动作配置 - type: string; // 评查点类型 - evaluation_point_groups_pid: number | null; // 评查点类型组ID - score: number; // 分数 - scoreDisplay?: string; // 分数显示值 -} - -/** - * API返回的评查点数据 - */ -interface ApiPointData { +interface ExtractedContent { id: number; - name: string; - code: string; - risk: string; - is_enabled: boolean; - description: string; - references_laws: { - name: string; - articles: string[]; - content: string; - }; - evaluation_point_groups_id: number | null; - evaluation_point_groups_pid?: number | null; - extraction_config: ApiExtactionConfigType; - evaluation_config: { - logicType?: string; - customLogic?: string; - rules?: Array<{ - id: string; - type: string; - config: Record; - }>; - }; - pass_message: string; - fail_message: string; - suggestion_message: string; - suggestion_message_type: string; - post_action: string; - action_config: string; - type?: string; - meta?: { - type: string; - pid?: number; - [key: string]: unknown; - }; - score?: number; - scoreDisplay?: string; + text: string; + page: number; + position: { start: number; end: number }; } -/** - * API响应数据类型 - */ -interface ApiResponse { - code?: number; - msg?: string; - data?: Array> | Record; - [key: string]: unknown; +interface LoaderData { + fileUrl: string; + initialPage: number; + extractedContent: ExtractedContent[]; + fileType: "pdf" | "docx"; + urls: Record; } -/** - * API提交数据格式 - */ -interface ApiRuleData { - id?: number; - name: string; - version?: string; - description: string; - doc_type?: string[]; - extraction_config: { - llm: { - fields: string[]; - prompt_setting: PromptSetting; - }; - vlm: { - fields: ApiVlmField[]; - prompt_setting: PromptSetting; - }; - regex: { - fields: RegexField[]; - }; - }; - evaluation_config: { - logic?: string; - auto_check?: boolean; - output_items?: string[]; - points?: Array>; - [key: string]: unknown; - }; +// 定义文档加载成功回调类型 +interface DocumentLoadSuccess { + numPages: number; } -/** - * 深拷贝工具函数 - * 用于创建对象的深拷贝,避免引用类型导致的意外修改 - * @param obj 要复制的对象 - * @returns 深拷贝后的对象 - */ -function deepClone(obj: T): T { - if (obj === null || obj === undefined || typeof obj !== 'object') { - return obj; +// 根据URL判断文件类型 +function getFileTypeFromUrl(url: string): "pdf" | "docx" { + const lowerCaseUrl = url.toLowerCase(); + if (lowerCaseUrl.endsWith(".pdf")) { + return "pdf"; + } else if (lowerCaseUrl.endsWith(".docx") || lowerCaseUrl.endsWith(".doc")) { + return "docx"; } + // 默认当作PDF处理 + return "pdf"; +} + +// Remix Loader 函数 +export const loader = async ({ request }: LoaderFunctionArgs) => { + const url = new URL(request.url); + const page = url.searchParams.get("page") || 1; - try { - return JSON.parse(JSON.stringify(obj)); - } catch (err) { - console.error('深拷贝对象失败:', err); - return obj; - } -} + // 实际文档 URL (PDF示例) + // const fileUrl = "http://172.18.0.100:9000/docauditai/documents/%E5%90%88%E5%90%8C%E6%96%87%E6%A1%A3/2025/04%E6%9C%8816%E6%97%A5/%E7%AC%AC16%E5%8F%B7--%E9%94%80%E5%94%AE%E6%97%A0%E6%A0%87%E5%BF%97%E5%A4%96%E5%9B%BD%E5%8D%B7%E7%83%9F_10%E6%97%B626%E5%88%8632%E7%A7%92/%E7%AC%AC16%E5%8F%B7--%E9%94%80%E5%94%AE%E6%97%A0%E6%A0%87%E5%BF%97%E5%A4%96%E5%9B%BD%E5%8D%B7%E7%83%9F.pdf"; + + // 示例文档URLs + const urls = { + // 1. 原始文档URL - 可能有CORS限制 + original: "https://dev-xc-enroll.oss-cn-guangzhou.aliyuncs.com/uploads/7840-230620112939.docx", + // 2. 公开示例文档 - 仍可能有CORS限制 + public: "https://dev-xc-enroll.oss-cn-guangzhou.aliyuncs.com/uploads/7840-230620112939.docx", + // 3. 通过CORS代理 (示例) + proxy: "https://dev-xc-enroll.oss-cn-guangzhou.aliyuncs.com/uploads/7840-230620112939.docx", + // 4. 本地服务器上的文档 (假设已经部署) + local: "/uploads/sample.docx", + // 5. PDF示例 (如果Word文档问题无法解决) + pdf: "http://172.18.0.100:9000/docauditai/documents/%E5%90%88%E5%90%8C%E6%96%87%E6%A1%A3/2025/04%E6%9C%8816%E6%97%A5/%E7%AC%AC16%E5%8F%B7--%E9%94%80%E5%94%AE%E6%97%A0%E6%A0%87%E5%BF%97%E5%A4%96%E5%9B%BD%E5%8D%B7%E7%83%9F_10%E6%97%B626%E5%88%8632%E7%A7%92/%E7%AC%AC16%E5%8F%B7--%E9%94%80%E5%94%AE%E6%97%A0%E6%A0%87%E5%BF%97%E5%A4%96%E5%9B%BD%E5%8D%B7%E7%83%9F.pdf" + }; + + // 使用本地文档或通过CORS代理的URL + const fileUrl = urls.public; // 可以切换到其他URL进行测试 + + // 判断文件类型 + const fileType = getFileTypeFromUrl(fileUrl); + + return { + fileUrl, + initialPage: Number(page), + extractedContent: mockExtractedContent, + fileType, + urls // 传递所有URL供前端选择 + }; +}; -export default function RuleNew() { - const navigate = useNavigate(); - const location = useLocation(); - // 保存从抽取设置中获取的字段列表,用于评查规则选择 - const [extractionFields, setExtractionFields] = useState([]); - // 标记是否为编辑模式(通过URL参数判断) - const [isEditMode, setIsEditMode] = useState(false); - // 标记数据加载状态 - const [isLoading, setIsLoading] = useState(false); - // 保存评查点组数据,用于基本信息表单中选择 - const [evaluationPointGroups, setEvaluationPointGroups] = useState>([]); - // 表单数据,包含评查点的所有信息 - const [formData, setFormData] = useState({ - // 基本信息字段 - name: '', - code: '', - risk: 'medium', - is_enabled: true, - description: '', - references_laws: { - name: '', - articles: [], - content: '' - }, - evaluation_point_groups_id: null, - type: '', - evaluation_point_groups_pid: null, - - // 抽取设置 - extraction_config: { - llm: { - fields: [], - prompt_setting: { - type: 'system', - template: '' - } - }, - vlm: { - fields: [], - prompt_setting: { - type: 'system', - template: '' - } - }, - regex: { - fields: [{ field: '', pattern: '' }] +export default function Documents() { + const { fileUrl, extractedContent, fileType, urls } = useLoaderData(); + const [numPages, setNumPages] = useState(null); + const [scrollToPage, setScrollToPage] = useState(null); + const [docxLoading, setDocxLoading] = useState(false); // 设置为false以避免加载指示器 + const [loadError, setLoadError] = useState(null); + const [debugInfo, setDebugInfo] = useState([]); + const docxContainerRef = useRef(null); + const [docxContentPositions, setDocxContentPositions] = useState<{[id: number]: number}>({}); + const [currentUrl, setCurrentUrl] = useState(fileUrl); + // 默认使用iframe模式 + const [showIframe, setShowIframe] = useState(true); + const [docxHtml, setDocxHtml] = useState(""); + + // 处理抽取内容点击 + const handleContentClick = (item: ExtractedContent) => { + setScrollToPage(item.page); + if (fileType === "pdf") { + // 使用ID滚动到指定页面 + const pageElement = document.getElementById(`page-${item.page}`); + if (pageElement) { + pageElement.scrollIntoView({ behavior: 'smooth' }); } - }, - - // 评查设置 - evaluation_config: { - logicType: 'and', - customLogic: '', - rules: [] - }, - - // 评查结果消息 - pass_message: '文档检查通过,符合规范要求。', - fail_message: '文档存在以下问题,请修改后重新提交。', - suggestion_message: '', - suggestion_message_type: 'warning', - - // 评查后动作 - post_action: 'none', - action_config: '', - - // 分数 - score: 0 - }); + } else if (fileType === "docx" && !showIframe) { + // 对于Word文档,滚动到提取内容位置 (仅本地渲染模式) + const position = docxContentPositions[item.id]; + if (position !== undefined && docxContainerRef.current) { + // 找到Word内容容器内的位置并滚动 + docxContainerRef.current.scrollTop = position; + + // 高亮显示这个区域(模拟) + highlightDocxContent(item); + } + } else if (fileType === "docx" && showIframe) { + // 对于iframe中的Word文档,我们只能切换到特定iframe页面 + // 这里我们无法控制iframe内部的滚动,只能提示用户 + addDebugInfo(`在iframe中无法直接定位到"${item.text}",请在文档中手动查找`); + } + }; - /** - * 页面加载时初始化处理 - * 1. 检查URL参数,判断是新建还是编辑模式 - * 2. 编辑模式下获取评查点数据 - * 3. 获取评查点组数据(用于表单选择项) - */ + // 模拟在Word文档中高亮内容 + const highlightDocxContent = (item: ExtractedContent) => { + // 移除之前的高亮 + const previousHighlights = document.querySelectorAll('.docx-highlight'); + previousHighlights.forEach(el => el.classList.remove('docx-highlight')); + + // 由于我们没有确切的位置信息,这里使用一个模拟的方法 + // 实际项目中,您需要一个更精确的方法来找到文本位置 + if (docxContainerRef.current) { + const textNodes = Array.from(docxContainerRef.current.querySelectorAll('p, span, div')) + .filter(node => node.textContent?.includes(item.text)); + + textNodes.forEach(node => { + node.classList.add('docx-highlight'); + }); + } + }; + + // PDF文档加载成功回调 + function onDocumentLoadSuccess({ numPages }: DocumentLoadSuccess) { + setNumPages(numPages); + console.log("PDF加载成功,页数:", numPages); + } + + // 简化的调试日志 + const addDebugInfo = (info: string) => { + console.log(info); + setDebugInfo(prev => [...prev, `${new Date().toISOString().split('T')[1].split('.')[0]}: ${info}`]); + }; + + // 切换到不同的文档URL + const switchDocumentUrl = (urlKey: keyof typeof urls) => { + setCurrentUrl(urls[urlKey]); + setDebugInfo([]); + setLoadError(null); + setDocxLoading(false); + setShowIframe(true); + addDebugInfo(`切换到新的文档URL: ${urls[urlKey]}`); + }; + + // 切换到iframe模式 (当直接加载文档有CORS问题时) + const switchToIframeMode = () => { + setShowIframe(true); + setDocxLoading(false); + addDebugInfo("切换到iframe嵌入模式"); + }; + + // 使用mammoth处理Word文档 useEffect(() => { - const searchParams = new URLSearchParams(location.search); - const id = searchParams.get('id'); - - if (id) { - setIsEditMode(true); - fetchEvaluationPoint(parseInt(id)); - } - - // 获取评查点组数据 - fetchEvaluationPointGroups(); - }, [location.search]); - - /** - * 获取评查点组数据 - * 从API获取所有评查点组,用于基本信息表单中选择 - */ - const fetchEvaluationPointGroups = async () => { - try { - console.log("获取评查点组数据"); - const response = await fetch("http://127.0.0.1:9000/admin/evaluation_point_groups", { - method: 'GET', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - } - }); - - if (!response.ok) { - const errorText = await response.text(); - console.error(`API响应错误: ${response.status}`, errorText); - throw new Error(`获取评查点组数据失败: ${response.status} - ${errorText}`); - } - - const responseData = await response.json(); - console.log("评查点组数据原始响应:", responseData); + if (fileType === "docx" && docxContainerRef.current && !showIframe) { + setDocxLoading(true); + setDebugInfo([]); // 清空之前的调试信息 + addDebugInfo(`准备加载Word文档: ${currentUrl}`); - // 适配API响应格式 - let groupsData: Array<{id: number, pid: number, code: string, name: string, is_enabled: boolean}> = []; - if (responseData && typeof responseData === 'object') { - if (responseData.code === 0 && responseData.data) { - // 新API格式 - groupsData = Array.isArray(responseData.data) ? responseData.data : [responseData.data]; - } else if (Array.isArray(responseData)) { - // 旧API格式 - groupsData = responseData; - } else if (responseData.data && Array.isArray(responseData.data)) { - // 旧API格式 - groupsData = responseData.data; - } - } - - // 确保所有项都有必需的字段 - groupsData = groupsData.filter(item => - item && typeof item === 'object' && - 'id' in item && - 'pid' in item && - 'code' in item && - 'name' in item - ); - - console.log("处理后的评查点组数据:", groupsData); - console.log("根级评查点类型:", groupsData.filter(group => group.pid === 0)); - setEvaluationPointGroups(groupsData); - - // 如果表单数据已加载但类型未设置,尝试根据evaluation_point_groups_pid设置类型 - if (formData.id && !formData.type && formData.evaluation_point_groups_pid) { - console.log("评查点组数据加载后更新类型,当前pid:", formData.evaluation_point_groups_pid); - - const typeGroup = groupsData.find(group => group.id === formData.evaluation_point_groups_pid); - if (typeGroup) { - console.log("找到对应的类型组:", typeGroup); - setFormData(prevData => ({ - ...prevData, - type: typeGroup.code - })); - console.log("根据评查点类型ID更新类型为:", typeGroup.code); - } else if (formData.evaluation_point_groups_id) { - // 通过规则组查找类型 - const selectedGroup = groupsData.find(group => group.id === formData.evaluation_point_groups_id); - if (selectedGroup && selectedGroup.pid !== 0) { - const parentTypeGroup = groupsData.find(group => group.id === selectedGroup.pid); - if (parentTypeGroup) { - console.log("通过规则组找到类型组:", parentTypeGroup); - setFormData(prevData => ({ - ...prevData, - type: parentTypeGroup.code, - evaluation_point_groups_pid: parentTypeGroup.id - })); - console.log("根据规则组更新类型为:", parentTypeGroup.code); - } - } - } - } - } catch (error) { - console.error('获取评查点组数据失败:', error); - // 显示错误提示但不影响应用继续使用 - alert(`获取评查点组数据失败: ${error instanceof Error ? error.message : '未知错误'}\n将使用默认数据`); - } - }; - - /** - * 获取评查点数据 - * 编辑模式下从API获取指定ID的评查点数据 - * @param id 评查点ID - */ - const fetchEvaluationPoint = async (id: number) => { - setIsLoading(true); - try { - console.log(`获取评查点数据,ID: ${id}`); - const response = await fetch(`http://127.0.0.1:9000/admin/evaluation_points?id=eq.${id}`, { - method: 'GET', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - } - }); - - if (!response.ok) { - const errorText = await response.text(); - console.error(`API响应错误: ${response.status}`, errorText); - throw new Error(`获取评查点数据失败: ${response.status} - ${errorText}`); - } - - const responseData = await response.json(); - console.log("API响应数据:", responseData); - - // 新的API响应格式适配 - if (responseData && typeof responseData === 'object') { - if (responseData.code === 0 && responseData.data) { - // 符合新的API响应格式 - if (Array.isArray(responseData.data)) { - if (responseData.data.length > 0) { - console.log("处理数组数据的第一项"); - processPointData(responseData.data[0]); - } else { - console.error("数据数组为空"); - throw new Error(`未找到ID为${id}的评查点数据`); - } - } else if (typeof responseData.data === 'object') { - console.log("处理单个对象数据"); - processPointData(responseData.data); - } - } else if (responseData.code !== 0) { - // API返回错误 - throw new Error(responseData.msg || `获取数据失败,错误码: ${responseData.code}`); - } else if (Array.isArray(responseData)) { - // 处理旧API格式:数组形式 - if (responseData.length > 0) { - console.log("数组格式响应,使用第一项"); - processPointData(responseData[0]); - } else { - console.error("数组为空,未找到数据"); - throw new Error(`未找到ID为${id}的评查点数据`); - } - } else if (responseData.data) { - // 处理旧API格式:包含data字段的对象 - console.log("包装对象格式响应,使用data字段"); - if (Array.isArray(responseData.data)) { - if (responseData.data.length > 0) { - processPointData(responseData.data[0]); - } else { - console.error("data数组为空"); - throw new Error(`未找到ID为${id}的评查点数据`); - } - } else if (typeof responseData.data === 'object') { - processPointData(responseData.data); - } else { - console.error("data字段格式不正确"); - throw new Error('数据格式不正确: data字段不是预期的格式'); - } - } else if (responseData.id) { - // 处理旧API格式:直接返回的对象 - console.log("单对象格式响应"); - processPointData(responseData); - } else { - console.error("无法识别的对象格式", responseData); - throw new Error('未找到评查点数据或数据格式不正确'); - } - } else { - console.error("响应格式无法识别", responseData); - throw new Error('未找到评查点数据或数据格式不正确'); - } - } catch (error) { - console.error('获取评查点数据失败:', error); - alert(`获取评查点数据失败: ${error instanceof Error ? error.message : '未知错误'}`); - // 获取数据失败时返回上一页 - navigate(-1); - } finally { - setIsLoading(false); - } - }; - - /** - * 处理获取到的评查点数据 - * 将API返回的数据格式转换为表单数据格式 - * @param pointData API返回的评查点数据 - */ - const processPointData = (pointData: ApiPointData) => { - console.log("处理评查点数据:", pointData); - - if (!pointData) { - console.error("评查点数据为空"); - throw new Error("评查点数据为空"); - } - - try { - // 转换API数据为表单数据格式 - const extractionConfig = pointData.extraction_config || {}; - const evaluationConfig = pointData.evaluation_config || {}; - - console.log("提取配置:", extractionConfig); - console.log("评查配置:", evaluationConfig); - console.log("评查规则详细信息:", JSON.stringify(evaluationConfig.rules || [], null, 2)); - - // 提取字段列表,用于规则设置 - const extractedFields: string[] = []; - - // 处理LLM字段 - if (extractionConfig.llm && extractionConfig.llm.fields) { - extractedFields.push(...extractionConfig.llm.fields); - } - - // 处理VLM字段 - if (extractionConfig.vlm && extractionConfig.vlm.fields) { - extractedFields.push(...(extractionConfig.vlm.fields || []).map((field) => { - if (typeof field === 'object' && field.name) { - return field.type && field.type !== 'default' ? `${field.name}_${field.type}` : field.name; - } - return ''; - }).filter(Boolean)); - } - - // 处理正则字段 - if (extractionConfig.regex && extractionConfig.regex.fields) { - extractedFields.push(...(extractionConfig.regex.fields || []).map((field) => field.field || '').filter(Boolean)); - } - - console.log("提取的字段:", extractedFields); - setExtractionFields(extractedFields); - - // 提取评查点类型ID - let pointGroupPid: number | null = null; - if (pointData.evaluation_point_groups_pid !== undefined && pointData.evaluation_point_groups_pid !== null) { - // 直接使用字段值 - pointGroupPid = typeof pointData.evaluation_point_groups_pid === 'number' - ? pointData.evaluation_point_groups_pid - : null; - console.log("从evaluation_point_groups_pid获取类型ID:", pointGroupPid); - } else if (pointData.meta && typeof pointData.meta === 'object' && pointData.meta.pid !== undefined) { - // 从meta中提取 - pointGroupPid = typeof pointData.meta.pid === 'number' - ? pointData.meta.pid - : null; - console.log("从meta.pid获取类型ID:", pointGroupPid); - } - - console.log("最终确定的类型ID:", pointGroupPid); - - // 处理分数 - let pointScore = 0; - if (pointData.score !== undefined && pointData.score !== null) { - pointScore = typeof pointData.score === 'number' ? pointData.score : 0; - console.log("从score字段获取分数:", pointScore); - } else if (pointData.meta && typeof pointData.meta === 'object' && pointData.meta.score !== undefined) { - pointScore = typeof pointData.meta.score === 'number' ? pointData.meta.score : 0; - console.log("从meta.score获取分数:", pointScore); - } - - // 将API数据格式转换为内部使用的格式 - setFormData({ - id: pointData.id, - name: pointData.name || '', - code: pointData.code || '', - risk: pointData.risk || 'medium', - is_enabled: pointData.is_enabled !== undefined ? pointData.is_enabled : true, - description: pointData.description || '', - references_laws: pointData.references_laws || { - name: '', - articles: [], - content: '' - }, - evaluation_point_groups_id: pointData.evaluation_point_groups_id || null, - evaluation_point_groups_pid: pointGroupPid, - type: pointData.type || (pointData.meta && pointData.meta.type ? pointData.meta.type : ''), - - // 将API数据格式转换为内部使用的格式 - extraction_config: { - llm: { - fields: extractionConfig.llm?.fields || [], - prompt_setting: { - type: extractionConfig.llm?.prompt_setting?.type || 'system', - template: extractionConfig.llm?.prompt_setting?.template || '' - } - }, - vlm: { - fields: extractionConfig.vlm?.fields?.map(field => { - if (typeof field === 'string') { - return { name: field, type: 'default' }; + const loadDocx = async () => { + try { + // 获取文件 + addDebugInfo(`开始获取文件...`); + let response; + try { + response = await fetch(currentUrl, { + // 添加CORS相关选项 + mode: 'cors', + credentials: 'omit', + headers: { + 'Access-Control-Allow-Origin': '*' } - return field; - }) || [], - prompt_setting: { - type: extractionConfig.vlm?.prompt_setting?.type || 'system', - template: extractionConfig.vlm?.prompt_setting?.template || '' - } - }, - regex: { - fields: (extractionConfig.regex?.fields || []).map((field) => ({ - field: field.field || '', - pattern: field.pattern || '' - })) + }); + addDebugInfo(`fetch请求状态: ${response.status} ${response.statusText}`); + } catch (fetchError) { + addDebugInfo(`fetch请求失败: ${fetchError instanceof Error ? fetchError.message : String(fetchError)}`); + throw new Error(`网络请求失败: ${fetchError instanceof Error ? fetchError.message : String(fetchError)}`); } - }, - - // 将API评查配置转换为内部使用的格式 - evaluation_config: { - logicType: evaluationConfig.logicType || 'and', - customLogic: evaluationConfig.customLogic || '', - rules: (evaluationConfig.rules || []).map(rule => { - // 规则ID处理 - if (!rule.id) { - rule.id = `rule_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + + if (!response.ok) { + throw new Error(`文档无法访问,状态码: ${response.status}`); + } + addDebugInfo(`文档下载成功,状态码: ${response.status}`); + + // 转换为ArrayBuffer + addDebugInfo(`开始读取响应内容为ArrayBuffer...`); + let buffer; + try { + buffer = await response.arrayBuffer(); + addDebugInfo(`获取到文档数据,大小: ${buffer.byteLength} 字节`); + } catch (bufferError) { + addDebugInfo(`读取为ArrayBuffer失败: ${bufferError instanceof Error ? bufferError.message : String(bufferError)}`); + throw new Error(`转换文档内容失败: ${bufferError instanceof Error ? bufferError.message : String(bufferError)}`); + } + + // 使用mammoth.js将Word转换为HTML,添加自定义选项 + addDebugInfo("使用mammoth开始转换文档为HTML..."); + try { + // 添加自定义样式映射 + const styleMap = ` + p[style-name='Heading 1'] => h1:fresh + p[style-name='Heading 2'] => h2:fresh + p[style-name='Title'] => h1.title:fresh + p[style-name='Subtitle'] => h2.subtitle:fresh + table => table.docx-table + `; + + // 创建简化版的转换选项 + const options = { + arrayBuffer: buffer, + styleMap: styleMap, + includeDefaultStyleMap: true + }; + + const result = await mammoth.convertToHtml(options); + + // 检查转换警告 + if (result.messages.length > 0) { + result.messages.forEach(message => { + addDebugInfo(`转换警告: [${message.type}] ${message.message}`); + }); } - // 根据规则类型调整config字段 - if (rule.config) { - switch (rule.type) { - case 'exists': - if (rule.config.fields) { - // 确保config中有fields字段 - console.log(`[调试] API数据转UI - exists规则 ${rule.id} 字段: ${JSON.stringify(rule.config.fields)}`); - } else if (rule.config.selectedFields) { - // 兼容旧字段名 - rule.config.fields = rule.config.selectedFields; - delete rule.config.selectedFields; - console.log(`[调试] API数据转UI - exists规则 ${rule.id} 从selectedFields映射到fields`); - } - - // 确保config中有logic字段 - if (rule.config.existsLogic && !rule.config.logic) { - rule.config.logic = rule.config.existsLogic; - delete rule.config.existsLogic; - console.log(`[调试] API数据转UI - exists规则 ${rule.id} 从existsLogic映射到logic`); - } - break; - - case 'consistency': - case 'logic': - // 将logicRelation转换为标准的logic - if (rule.config.logicRelation && !rule.config.logic) { - rule.config.logic = rule.config.logicRelation; - delete rule.config.logicRelation; - console.log(`[调试] API数据转UI - ${rule.type}规则 ${rule.id} 从logicRelation映射到logic`); - } - break; - } - } + addDebugInfo("文档转换成功,获取到HTML内容"); - return rule; - }) - }, - - pass_message: pointData.pass_message || '文档检查通过,符合规范要求。', - fail_message: pointData.fail_message || '文档存在以下问题,请修改后重新提交。', - suggestion_message: pointData.suggestion_message || '', - suggestion_message_type: pointData.suggestion_message_type || 'warning', - post_action: pointData.post_action || 'none', - action_config: pointData.action_config || '', - score: typeof pointData.score === 'number' ? pointData.score : 0 - }); - - console.log("已成功解析并加载评查点数据"); - } catch (error) { - console.error("处理评查点数据时出错:", error); - throw error; - } - }; - - /** - * 更新抽取字段列表 - * 在抽取设置和评查设置之间共享字段列表信息 - * @param fields 更新后的字段列表 - */ - const updateExtractionFields = (fields: string[]) => { - setExtractionFields(fields); - }; - - /** - * 处理BasicInfo组件数据变更 - * 更新基本信息相关的表单数据 - * @param data 基本信息组件传回的数据 - */ - const handleBasicInfoChange = (data: Record) => { - setFormData(prevData => ({ - ...prevData, - ...data - })); - }; - - /** - * 处理ExtractionSettings组件数据变更 - * 更新抽取设置相关的表单数据 - * @param data 抽取设置组件传回的数据 - */ - const handleExtractionSettingsChange = (data: Record) => { - console.log("抽取设置更新:", data); - - setFormData(prevData => { - // 深拷贝以避免不可预期的状态更新 - const updatedExtractionConfig = { ...prevData.extraction_config }; - - // 根据不同类型的数据进行处理 - // 处理regexFields数据 - 保留正则表达式配置 - if (data.regexFields) { - const regexFields = data.regexFields as Array<{ field: string; pattern: string }>; - - // 检查是否有 activeFieldId 标记,表示正在编辑中 - const activeFieldId = data.activeFieldId as string; - if (activeFieldId) { - console.log("检测到正在编辑的正则字段,保留所有字段"); - updatedExtractionConfig.regex.fields = regexFields; - } else { - // 正常更新模式,过滤掉空字段名的项 - updatedExtractionConfig.regex.fields = regexFields - .filter(field => field.field && field.field.trim() !== '') - .map(field => ({ - field: field.field, - pattern: field.pattern || '' - })); - } - } - - // 处理字段数据 - 保留提取的字段名称 - if (data.fields) { - const extractionFields = data.fields as { llm?: string[], vlm?: string[] }; - const activeFieldId = data.activeFieldId as string; - - if (activeFieldId) { - // 检测到正在编辑的大模型或多模态字段,保留所有字段 - console.log("检测到正在编辑的大模型或多模态字段,保留所有字段"); - if (extractionFields.llm) { - updatedExtractionConfig.llm.fields = extractionFields.llm; - } - if (extractionFields.vlm) { - // 保存字符串形式的字段,在提交API时再进行处理 - updatedExtractionConfig.vlm.fields = extractionFields.vlm; - } - } else { - // 正常更新模式,过滤掉空字段 - if (extractionFields.llm) { - updatedExtractionConfig.llm.fields = extractionFields.llm.filter(field => field && field.trim() !== ''); - } - if (extractionFields.vlm) { - // 保存字符串形式的字段,在提交API时再进行处理 - updatedExtractionConfig.vlm.fields = extractionFields.vlm.filter(field => field && field.trim() !== ''); - } - } - } - - // 处理提示词设置 - if (data.promptSettings) { - const promptSettings = data.promptSettings as Record; - - // 更新大模型抽取提示词设置 - if (promptSettings.llm) { - updatedExtractionConfig.llm.prompt_setting = { - type: promptSettings.llm.type as string || 'system', - template: promptSettings.llm.content as string || '' - }; - } - - // 更新多模态抽取提示词设置 - if (promptSettings.vlm) { - updatedExtractionConfig.vlm.prompt_setting = { - type: promptSettings.vlm.type as string || 'system', - template: promptSettings.vlm.content as string || '' - }; - } - } - - // 单独处理promptType,确保提示词设置类型正确 - if (data.promptType) { - const promptType = data.promptType as Record; - if (promptType.llm) { - updatedExtractionConfig.llm.prompt_setting.type = promptType.llm; - } - if (promptType.vlm) { - updatedExtractionConfig.vlm.prompt_setting.type = promptType.vlm; - } - } - - // 单独处理promptContent,确保提示词内容保存正确 - if (data.promptContent) { - const promptContent = data.promptContent as Record; - if (promptContent.llm) { - updatedExtractionConfig.llm.prompt_setting.template = promptContent.llm; - } - if (promptContent.vlm) { - updatedExtractionConfig.vlm.prompt_setting.template = promptContent.vlm; - } - } - - // 更新或调试其他字段数据 - console.log("更新抽取配置:", { - pendingUpdate: data.pendingUpdate, - fieldsCount: extractionFields ? { - llm: extractionFields.llm?.length || 0, - vlm: extractionFields.vlm?.length || 0 - } : 'unchanged', - regexFieldsCount: data.regexFields ? (data.regexFields as any[]).length : 0, - activeFieldId: data.activeFieldId, - promptTypeChanged: !!data.promptType, - promptContentChanged: !!data.promptContent, - promptSettingsChanged: !!data.promptSettings - }); - - // 生成所有字段列表,保存到状态中供评查设置使用 - if (data.allFields) { - console.log("更新字段列表:", data.allFields); - // 将新字段列表保存到状态中 - updateExtractionFields(data.allFields as string[]); - } - - // 返回更新后的表单数据 - return { - ...prevData, - extraction_config: updatedExtractionConfig - }; - }); - }; - - /** - * 处理ReviewSettings组件数据变更 - * 更新评查设置相关的表单数据 - * @param data 评查设置组件传回的数据 - */ - const handleReviewSettingsChange = (data: Record) => { - setFormData(prevData => { - const updatedData = { ...prevData }; - - // 记录所有收到的数据 - console.log("评查设置更新数据:", data); - - // 更新规则 - 只有当明确提供了rules数据时才更新 - if (data.rules) { - // 验证并修复规则数据 - const validatedRules = validateAndFixRules(data.rules as Rule[]); - console.log("规则数据验证后:", validatedRules); - updatedData.evaluation_config.rules = validatedRules; - } - - // 更新组合逻辑 - if (data.combinationLogic !== undefined) { - updatedData.evaluation_config.logicType = data.combinationLogic as string; - } - - // 更新自定义逻辑 - if (data.customLogic !== undefined) { - updatedData.evaluation_config.customLogic = data.customLogic as string; - } - - // 更新通过/不通过/建议消息 - if (data.pass_message !== undefined) { - updatedData.pass_message = data.pass_message as string; - } - - if (data.fail_message !== undefined) { - updatedData.fail_message = data.fail_message as string; - } - - if (data.suggestion_message !== undefined) { - updatedData.suggestion_message = data.suggestion_message as string; - } - - if (data.suggestion_message_type !== undefined) { - updatedData.suggestion_message_type = data.suggestion_message_type as string; - } - - // 更新评查后动作 - if (data.post_action !== undefined) { - updatedData.post_action = data.post_action as string; - } - - if (data.action_config !== undefined) { - updatedData.action_config = data.action_config as string; - } - - // 更新分数 - if (data.score !== undefined) { - const scoreValue = parseFloat(data.score as string); - updatedData.score = isNaN(scoreValue) ? 0 : scoreValue; - } - - // 更新分数显示值 - if (data.scoreDisplay !== undefined) { - updatedData.scoreDisplay = data.scoreDisplay as string; - } - - return updatedData; - }); - }; - - /** - * 检查并修复评查规则数据 - * 确保数据的完整性和有效性 - * @param rules 原始规则数据 - * @returns 验证和修复后的规则数据 - */ - const validateAndFixRules = (rules: Rule[] | undefined): Rule[] => { - if (!rules || !Array.isArray(rules)) { - console.log("规则数据无效或为空,返回空数组"); - return []; - } - - // 过滤无效规则,确保每个规则都有type字段 - const validRules = rules.filter(rule => { - if (!rule || typeof rule !== 'object') { - console.log("发现无效规则对象:", rule); - return false; - } - - if (!rule.type || typeof rule.type !== 'string' || rule.type.trim() === '') { - console.log("发现无效规则类型:", rule); - return false; - } - - if (!rule.id || typeof rule.id !== 'string') { - console.log("发现缺少ID的规则:", rule); - // 为缺少ID的规则自动生成ID - rule.id = `rule_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; - } - - // 确保config存在 - if (!rule.config || typeof rule.config !== 'object') { - console.log("规则缺少配置对象,自动创建:", rule); - rule.config = {}; - } - - return true; - }); - - console.log(`规则验证结果: 输入${rules.length}条,有效${validRules.length}条`); - return validRules; - }; - - /** - * 格式化数据,准备提交到接口 - * 将内部表单数据转换为API期望的格式 - * @param data 表单数据 - * @returns 格式化后的API数据 - */ - const formatDataForApi = (data: FormDataType): ApiRuleData => { - console.log('格式化数据用于API提交:', data); - - // 基本信息处理 - const formattedData: ApiRuleData = { - name: data.name || '', - version: '', // 从FormDataType中获取或使用默认值 - description: data.description || '', - doc_type: [], // 从FormDataType中获取或使用默认值 - extraction_config: { - llm: { - fields: data.extraction_config.llm.fields || [], - prompt_setting: data.extraction_config.llm.prompt_setting || { - type: 'system', - template: '' - } - }, - vlm: { - fields: Array.isArray(data.extraction_config.vlm.fields) - ? data.extraction_config.vlm.fields.map((field: string | ApiVlmField) => { - // 如果是字符串,处理为对象 - if (typeof field === 'string') { - if (field.includes('_')) { - const [name, type] = field.split('_'); - return { name, type: type || 'default' }; - } - return { name: field, type: 'default' }; + // 为生成的HTML文档添加包装容器和样式 + const enhancedHtml = ` +
+ ${result.value} +
+

注意:本地转换使用了简化版格式,一些高级格式(如页眉页脚、复杂表格格式)可能无法完全显示。

+

如需查看完整格式,请使用"嵌入模式"或下载文档。

+
+
+ `; + + // 存储HTML内容 + setDocxHtml(enhancedHtml); + + // 查找匹配的内容并创建位置映射 + setTimeout(() => { + try { + if (docxContainerRef.current) { + const positionsMap: {[id: number]: number} = {}; + + extractedContent.forEach((item) => { + // 在HTML内容中查找文本 + // 使用更安全的查询方式 + if (docxContainerRef.current) { + // 获取所有可能包含文本的元素 + const elements = docxContainerRef.current.querySelectorAll('p, h1, h2, h3, h4, h5, h6, li, td, th, span'); + + // 转为数组并过滤包含目标文本的元素 + const textElements = Array.from(elements).filter(element => + element.textContent?.includes(item.text) + ); + + if (textElements.length > 0) { + // 使用找到的第一个元素的位置 + const element = textElements[0]; + const rect = element.getBoundingClientRect(); + const containerRect = docxContainerRef.current.getBoundingClientRect(); + // 计算相对于容器的位置 + positionsMap[item.id] = rect.top - containerRect.top + docxContainerRef.current.scrollTop; + + // 标记找到的元素 + element.classList.add('docx-content-found'); + } + } + }); + + setDocxContentPositions(positionsMap); + addDebugInfo(`已创建 ${Object.keys(positionsMap).length} 个内容位置映射`); } - // 如果已经是对象,直接返回 - return field; - }) - : [], - prompt_setting: data.extraction_config.vlm.prompt_setting || { - type: 'system', - template: '' + } catch (positionError) { + addDebugInfo(`创建位置映射时出错: ${positionError instanceof Error ? positionError.message : String(positionError)}`); + } + }, 500); + + setDocxLoading(false); + } catch (mammothError) { + addDebugInfo(`Mammoth转换失败: ${mammothError instanceof Error ? mammothError.message : String(mammothError)}`); + throw new Error(`Word转HTML失败: ${mammothError instanceof Error ? mammothError.message : String(mammothError)}`); } - }, - regex: { - fields: data.extraction_config.regex.fields.filter(f => f.field && f.field.trim() !== '').map(f => ({ - field: f.field, - pattern: f.pattern || '' - })) + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : String(error); + addDebugInfo(`文档处理错误: ${errorMessage}`); + setLoadError(`加载Word文档失败: ${errorMessage}`); + setDocxLoading(false); } - }, - evaluation_config: { - // 默认值或从当前数据中转换 - logic: 'and', // 默认值 - auto_check: true, // 默认值 - output_items: [], // 默认值 - points: [] // 默认值 - } - }; - - console.log('API数据格式化完成:', formattedData); - return formattedData; - }; - - /** - * 确定使用的HTTP方法和URL - * 根据是否为编辑模式返回不同的HTTP方法和端点 - * @param id 评查点ID,编辑模式下存在 - * @returns HTTP方法和端点 - */ - const getEndpointAndMethod = (id?: number) => { - let method = 'POST'; - let endpoint = 'http://127.0.0.1:9000/admin/evaluation_points'; - - // 如果是编辑模式,使用PATCH更新现有记录 - if (id) { - method = 'PATCH'; - endpoint = `http://127.0.0.1:9000/admin/evaluation_points?id=eq.${id}`; + }; + + loadDocx(); } + }, [currentUrl, fileType, extractedContent, showIframe]); - return { method, endpoint }; - }; - - /** - * 保存评查点 - * 验证数据并提交到API - */ - const handleSave = async () => { - try { - setIsLoading(true); - - // 记录当前评查规则状态 - console.log("保存前评查规则状态:", formData.evaluation_config.rules); - - // 基本验证 - if (!formData.name || !formData.code) { - alert("请填写评查点名称和编码,这些是必填项"); - setIsLoading(false); - return; - } - - // 检查评查点类型 - if (!formData.type) { - alert("请选择评查点类型"); - setIsLoading(false); - return; - } - - // 检查所属规则组 - if (!formData.evaluation_point_groups_id) { - alert("请选择所属规则组"); - setIsLoading(false); - return; - } - - // 检查评查规则 - 只在新增模式下检查规则是否为空 - if (!isEditMode && - (!formData.evaluation_config.rules || - formData.evaluation_config.rules.length === 0 || - !formData.evaluation_config.rules.some(rule => rule.type && rule.type.trim() !== ''))) { - console.log("规则验证失败,当前规则:", formData.evaluation_config.rules); - console.log("规则数量:", formData.evaluation_config.rules?.length || 0); - console.log("规则有效性:", formData.evaluation_config.rules?.some(rule => rule.type && rule.type.trim() !== '')); - alert("请至少添加一条有效的评查规则"); - setIsLoading(false); - return; - } - - // 编辑模式下,只有当规则数组不为空时才验证规则有效性 - if (isEditMode && - formData.evaluation_config.rules && - formData.evaluation_config.rules.length > 0 && - !formData.evaluation_config.rules.some(rule => rule.type && rule.type.trim() !== '')) { - console.log("编辑模式下规则无效,当前规则:", formData.evaluation_config.rules); - console.log("编辑模式-规则数量:", formData.evaluation_config.rules.length); - console.log("编辑模式-规则详情:", JSON.stringify(formData.evaluation_config.rules, null, 2)); - console.log("编辑模式-规则有效性:", formData.evaluation_config.rules.some(rule => rule.type && rule.type.trim() !== '')); - alert("请至少添加一条有效的评查规则或清空规则列表"); - setIsLoading(false); - return; - } - - const evaluationPointData = formatDataForApi(formData); - console.log("保存数据:", evaluationPointData); - - const { method, endpoint } = getEndpointAndMethod(formData.id); - console.log(`发送${method}请求到`, endpoint); - - // 发送数据到API - const response = await fetch(endpoint, { - method: method, - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json' - }, - body: JSON.stringify(evaluationPointData) - }); - - // 输出完整响应信息 - console.log("响应状态:", response.status, response.statusText); - - if (!response.ok) { - const errorText = await response.text(); - console.error(`API响应错误: ${response.status}`, errorText); - try { - // 尝试解析错误响应为JSON - const errorJson = JSON.parse(errorText); - console.error("解析的错误JSON:", errorJson); - throw new Error(`保存失败: ${errorJson.msg || response.statusText}`); - } catch (parseError) { - // 如果无法解析为JSON,使用原始文本 - throw new Error(`保存失败: ${response.status} - ${errorText}`); - } - } - - const contentType = response.headers.get('content-type'); - console.log("响应Content-Type:", contentType); - - // 检查是否是JSON响应 - if (contentType && contentType.includes('application/json')) { - const responseData = await response.json(); - console.log("API响应数据:", responseData); - - // 处理响应 - await handleApiResponse(responseData, isEditMode); - } else { - // 非JSON响应 - const text = await response.text(); - console.log("非JSON响应:", text); - alert("操作已完成!"); - navigate('/rules'); - } - } catch (error) { - console.error('保存失败:', error); - alert(`保存失败: ${error instanceof Error ? error.message : '未知错误'}`); - } finally { - setIsLoading(false); - } - }; - - /** - * 保存为草稿 - * 验证基本数据并提交到API,与保存正式版相比校验更宽松 - */ - const handleSaveDraft = async () => { - try { - setIsLoading(true); - - // 基本验证 - if (!formData.name || !formData.code) { - alert("请填写评查点名称和编码,这些是必填项"); - setIsLoading(false); - return; - } - - // 检查评查点类型 - if (!formData.type) { - alert("请选择评查点类型"); - setIsLoading(false); - return; - } - - // 检查所属规则组 - if (!formData.evaluation_point_groups_id) { - alert("请选择所属规则组"); - setIsLoading(false); - return; - } - - // 编辑模式下不检查评查规则是否为空 - // 草稿模式下规则可以为空 - - const draftData = formatDataForApi(formData); - console.log("保存草稿数据:", draftData); - - const { method, endpoint } = getEndpointAndMethod(formData.id); - console.log(`发送${method}请求到`, endpoint); - - // 发送数据到API - const response = await fetch(endpoint, { - method: method, - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json' - }, - body: JSON.stringify(draftData) - }); - - // 输出完整响应信息 - console.log("响应状态:", response.status, response.statusText); - - if (!response.ok) { - const errorText = await response.text(); - console.error(`API响应错误: ${response.status}`, errorText); - try { - // 尝试解析错误响应为JSON - const errorJson = JSON.parse(errorText); - console.error("解析的错误JSON:", errorJson); - throw new Error(`保存失败: ${errorJson.msg || response.statusText}`); - } catch (parseError) { - // 如果无法解析为JSON,使用原始文本 - throw new Error(`保存失败: ${response.status} - ${errorText}`); - } - } - - const contentType = response.headers.get('content-type'); - console.log("响应Content-Type:", contentType); - - // 检查是否是JSON响应 - if (contentType && contentType.includes('application/json')) { - const responseData = await response.json(); - console.log("API响应数据:", responseData); - - // 处理响应 - await handleApiResponse(responseData, isEditMode, true); - } else { - // 非JSON响应 - const text = await response.text(); - console.log("非JSON响应:", text); - alert("草稿已保存!"); - navigate('/rules'); - } - } catch (error) { - console.error('保存草稿失败:', error); - alert(`保存草稿失败: ${error instanceof Error ? error.message : '未知错误'}`); - } finally { - setIsLoading(false); - } - }; - - /** - * 处理API响应 - * 根据API响应结果进行页面跳转等后续操作 - * @param responseData API响应数据 - * @param isEditMode 是否为编辑模式 - * @param isDraft 是否为草稿模式 - */ - const handleApiResponse = async ( - responseData: - | ApiResponse - | Array<{ id: number; [key: string]: unknown }>, - isEditMode: boolean, - isDraft: boolean = false - ) => { - // 适配新的API响应格式 - if (responseData && typeof responseData === 'object') { - // 符合新的API规范 - if ('code' in responseData && responseData.code === 0) { - console.log(`${isDraft ? '草稿' : ''}保存成功 (新API格式):`, responseData.data); - alert(`${isDraft ? '草稿' : ''}保存成功!`); - - // 根据操作类型重定向 - if (isEditMode) { - // 编辑模式,保留在当前编辑页面 - navigate(`/rules/new?id=${formData.id}`); - } else { - // 创建模式,跳转到新创建数据的编辑页面 - let newId: number | undefined; - - if (responseData.data && Array.isArray(responseData.data) && responseData.data.length > 0) { - newId = responseData.data[0].id as number; - } else if (responseData.data && typeof responseData.data === 'object' && 'id' in responseData.data) { - newId = responseData.data.id as number; - } - - if (newId) { - navigate(`/rules/new?id=${newId}`); - } else { - // 无法获取ID,返回列表页 - navigate('/rules'); - } - } - return; - } else if ('code' in responseData && responseData.code !== 0) { - // API返回错误 - console.warn("API返回错误:", responseData.msg); - throw new Error(responseData.msg as string || "操作失败"); - } - } - - // 兼容处理旧的响应格式 - if (Array.isArray(responseData)) { - if (responseData.length > 0) { - console.log(`${isDraft ? '草稿' : ''}保存成功 (数组响应):`, responseData[0]); - alert(`${isDraft ? '草稿' : ''}保存成功!`); - - // 如果是创建,跳转到编辑页面 - if (!isEditMode && responseData[0].id) { - navigate(`/rules/new?id=${responseData[0].id}`); - } else { - navigate('/rules'); - } - } else { - console.warn("响应数组为空"); - alert("操作已完成,但服务器未返回数据"); - navigate('/rules'); - } - } else if (responseData && typeof responseData === 'object') { - if ('data' in responseData && responseData.data) { - console.log(`${isDraft ? '草稿' : ''}保存成功 (带data字段):`, responseData.data); - alert(`${isDraft ? '草稿' : ''}保存成功!`); - - let newId: number | undefined; - if (Array.isArray(responseData.data) && responseData.data.length > 0 && 'id' in responseData.data[0]) { - newId = responseData.data[0].id as number; - } else if (typeof responseData.data === 'object' && 'id' in responseData.data) { - newId = responseData.data.id as number; - } - - if (!isEditMode && newId) { - navigate(`/rules/new?id=${newId}`); - } else { - navigate('/rules'); - } - } else if ('id' in responseData && responseData.id) { - console.log(`${isDraft ? '草稿' : ''}保存成功 (直接对象):`, responseData); - alert(`${isDraft ? '草稿' : ''}保存成功!`); - - if (!isEditMode) { - navigate(`/rules/new?id=${responseData.id as number}`); - } else { - navigate('/rules'); - } - } else { - console.warn("响应对象格式不符合预期", responseData); - alert("操作已完成,但返回的数据格式不符合预期"); - navigate('/rules'); - } - } else { - console.warn("响应不是数组或对象", responseData); - alert("操作已完成,但返回的数据格式不符合预期"); - navigate('/rules'); - } - }; - - /** - * 当评查点组数据和表单数据都加载完成后,确保类型信息被正确设置 - */ + // 页面渲染完成后检查是否需要滚动 useEffect(() => { - // 仅在编辑模式下,且表单数据已加载,评查点组数据也已加载的情况下执行 - if ( - isEditMode && - formData.id && - evaluationPointGroups.length > 0 && - (!formData.type || formData.type === '') - ) { - console.log("检测到编辑模式下类型未设置,尝试自动设置类型"); - - // 首先尝试通过evaluation_point_groups_pid设置类型 - if (formData.evaluation_point_groups_pid) { - const typeGroup = evaluationPointGroups.find( - group => group.id === formData.evaluation_point_groups_pid - ); - - if (typeGroup) { - console.log("通过评查点类型ID找到类型组:", typeGroup); - setFormData(prevData => ({ - ...prevData, - type: typeGroup.code - })); - console.log("自动设置类型为:", typeGroup.code); - return; - } - } - - // 如果无法通过evaluation_point_groups_pid设置,尝试通过evaluation_point_groups_id设置 - if (formData.evaluation_point_groups_id) { - const ruleGroup = evaluationPointGroups.find( - group => group.id === formData.evaluation_point_groups_id - ); - - if (ruleGroup && ruleGroup.pid && ruleGroup.pid !== 0) { - const parentGroup = evaluationPointGroups.find( - group => group.id === ruleGroup.pid - ); - - if (parentGroup) { - console.log("通过规则组找到父级类型组:", parentGroup); - setFormData(prevData => ({ - ...prevData, - type: parentGroup.code, - evaluation_point_groups_pid: parentGroup.id - })); - console.log("自动设置类型为:", parentGroup.code); - } - } + if (scrollToPage && fileType === "pdf") { + const pageElement = document.getElementById(`page-${scrollToPage}`); + if (pageElement) { + pageElement.scrollIntoView({ behavior: 'smooth' }); } + setScrollToPage(null); } - }, [isEditMode, formData.id, formData.type, formData.evaluation_point_groups_id, formData.evaluation_point_groups_pid, evaluationPointGroups]); + }, [scrollToPage, fileType]); - // 渲染页面内容 - return ( -
- {/* 页面标题和右上角保存按钮 */} - - - {/* 加载状态显示 */} - {isLoading ? ( -
-
- 加载中... -
- ) : ( - <> - {/* 评查点基本信息设置 */} -
- -
- - {/* 抽取设置 - 配置从文档中提取的字段 */} -
- - - -
- - {/* 评查设置 - 配置评查规则、消息等 */} -
- - - -
- - {/* 底部操作按钮区域 */} - { + if (!numPages) return null; + + const pages = []; + for (let i = 1; i <= numPages; i++) { + pages.push( +
+
第 {i} 页
+ - - )} +
+ ); + } + return pages; + }; + + return ( +
+ {/* 文档展示区域 */} +
+
+

文档预览 ({fileType.toUpperCase()})

+ + {fileType === "docx" && ( +
+
+

Word文档预览模式

+
+ + +
+
+ {!showIframe && ( +
+

本地渲染说明:

+
    +
  • 本地渲染使用mammoth.js库将Word文档转换为HTML
  • +
  • 部分复杂格式(页眉页脚、复杂表格样式、特殊字体等)可能无法完全还原
  • +
  • 嵌入模式使用Google Docs提供原生渲染,格式更完整但加载较慢
  • +
+
+ )} +
+ )} + +
+ {loadError ? ( +
+

加载错误:

+

{loadError}

+
+

调试信息:

+ {debugInfo.map((info, index) => ( +
{info}
+ ))} +
+
+

尝试其他方式:

+
+ + + + + + 下载文档 + +
+
+
+ ) : fileType === "pdf" ? ( + { + console.error("PDF加载错误:", error); + setLoadError("PDF文档加载失败:" + (error.message || "未知错误")); + }} + className="flex flex-col items-center" + error={
PDF文档加载失败,请检查链接或网络连接。
} + noData={
无数据
} + loading={
PDF加载中...
} + > + {renderAllPages()} +
+ ) : ( + <> + {docxLoading ? ( +
+
+
+
+

Word文档加载中...

+ {debugInfo.length > 0 && ( +
+

加载过程:

+ {debugInfo.map((info, index) => ( +
{info}
+ ))} +
+ )} +
+ ) : showIframe ? ( + // 嵌入模式显示Word文档 +
+ +
+ +
+ +
+ +
+
+

在线预览无法工作?

+

您可以下载文档在本地打开

+ + 下载文档 + +
+
+
+ +
+
测试页面已加载,正在尝试不同的文档预览方案...
+
+
+ + + + \ No newline at end of file diff --git a/app/types.d.ts b/app/types.d.ts new file mode 100644 index 0000000..e1786b8 --- /dev/null +++ b/app/types.d.ts @@ -0,0 +1,44 @@ +declare module 'react-pdf' { + import * as React from 'react'; + + interface TextItem { + str: string; + transform: number[]; + width: number; + height: number; + fontName: string; + } + + export interface DocumentProps { + file: string | Uint8Array | ArrayBuffer; + onLoadSuccess?: ({ numPages }: { numPages: number }) => void; + onLoadError?: (error: Error) => void; + className?: string; + error?: React.ReactNode; + noData?: React.ReactNode; + loading?: React.ReactNode; + children?: React.ReactNode; + } + + export interface PageProps { + pageNumber: number; + renderTextLayer?: boolean; + renderAnnotationLayer?: boolean; + className?: string; + customTextRenderer?: (textItem: TextItem) => string; + } + + export const Document: React.FC; + export const Page: React.FC; + export const pdfjs: { + GlobalWorkerOptions: { + workerSrc: string; + }; + version: string; + }; +} + +declare module '*.css' { + const content: Record; + export default content; +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 030ba7c..b4abac6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@uiw/react-codemirror": "^4.23.10", "dayjs": "^1.11.13", "diff": "^7.0.0", + "docx-preview": "^0.3.5", "html-docx-js": "^0.3.1", "isbot": "^4.1.0", "mammoth": "^1.9.0", @@ -26,12 +27,14 @@ "prismjs": "^1.30.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-pdf": "^5.7.2", "remixicon": "^4.6.0" }, "devDependencies": { "@remix-run/dev": "^2.16.2", "@types/react": "^18.2.20", "@types/react-dom": "^18.2.7", + "@types/react-pdf": "^7.0.0", "@typescript-eslint/eslint-plugin": "^6.7.4", "@typescript-eslint/parser": "^6.7.4", "autoprefixer": "^10.4.21", @@ -1262,7 +1265,6 @@ "version": "0.3.8", "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -1276,7 +1278,6 @@ "version": "3.1.2", "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -1285,22 +1286,29 @@ "version": "1.2.1", "resolved": "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, "engines": { "node": ">=6.0.0" } }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -2547,11 +2555,30 @@ "@types/ms": "*" } }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmmirror.com/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "peer": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmmirror.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "peer": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" }, "node_modules/@types/estree-jsx": { "version": "1.0.5", @@ -2574,8 +2601,7 @@ "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "node_modules/@types/json5": { "version": "0.0.29", @@ -2608,7 +2634,6 @@ "version": "22.13.11", "resolved": "https://registry.npmmirror.com/@types/node/-/node-22.13.11.tgz", "integrity": "sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g==", - "dev": true, "dependencies": { "undici-types": "~6.20.0" } @@ -2617,13 +2642,13 @@ "version": "15.7.14", "resolved": "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.14.tgz", "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "dev": true + "devOptional": true }, "node_modules/@types/react": { "version": "18.3.19", "resolved": "https://registry.npmmirror.com/@types/react/-/react-18.3.19.tgz", "integrity": "sha512-fcdJqaHOMDbiAwJnXv6XCzX0jDW77yI3tJqYh1Byn8EL5/S628WRx9b/y3DnNe55zTukUQKrfYxiZls2dHcUMw==", - "dev": true, + "devOptional": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -2638,6 +2663,16 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/react-pdf": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/@types/react-pdf/-/react-pdf-7.0.0.tgz", + "integrity": "sha512-G0a+5UiKk3AvEauBP/Js7r9kGZNW3iBbS6kXkH0foGSaKWR6K3ElTe7Y4tlolc2VKbM9udmMxpkbxh/dtR2wXA==", + "deprecated": "This is a stub types definition. react-pdf provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "react-pdf": "*" + } + }, "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmmirror.com/@types/semver/-/semver-7.5.8.tgz", @@ -4018,6 +4053,152 @@ "resolved": "https://registry.npmmirror.com/@web3-storage/multipart-parser/-/multipart-parser-1.0.0.tgz", "integrity": "sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==" }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "peer": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "peer": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "peer": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "peer": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "peer": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, "node_modules/@xmldom/xmldom": { "version": "0.8.10", "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz", @@ -4026,6 +4207,18 @@ "node": ">=10.0.0" } }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "peer": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmmirror.com/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "peer": true + }, "node_modules/@zxing/text-encoding": { "version": "0.9.0", "resolved": "https://registry.npmmirror.com/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", @@ -4073,7 +4266,6 @@ "version": "8.14.1", "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.14.1.tgz", "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -4119,7 +4311,6 @@ "version": "6.12.6", "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4131,6 +4322,53 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "peer": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "peer": true + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -4522,6 +4760,14 @@ "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmmirror.com/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -4618,7 +4864,6 @@ "version": "4.24.4", "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.24.4.tgz", "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -4790,7 +5035,6 @@ "version": "1.0.30001707", "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz", "integrity": "sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==", - "dev": true, "funding": [ { "type": "opencollective", @@ -4919,6 +5163,15 @@ "node": ">=10" } }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "peer": true, + "engines": { + "node": ">=6.0" + } + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmmirror.com/clean-stack/-/clean-stack-2.2.0.tgz", @@ -5188,7 +5441,7 @@ "version": "3.1.3", "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "devOptional": true }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -5472,6 +5725,14 @@ "node": ">=6.0.0" } }, + "node_modules/docx-preview": { + "version": "0.3.5", + "resolved": "https://registry.npmmirror.com/docx-preview/-/docx-preview-0.3.5.tgz", + "integrity": "sha512-nod1jG5PkvzDIiZAcgAY4gSFQzgmAAChcuZH4Hj9dj7oCzscY3Hn8NfbUv7X7Jk4xL1lfKO113JLDhWKOt6fYw==", + "dependencies": { + "jszip": ">=3.0.0" + } + }, "node_modules/dotenv": { "version": "16.4.7", "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.4.7.tgz", @@ -5567,8 +5828,7 @@ "node_modules/electron-to-chromium": { "version": "1.5.123", "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.123.tgz", - "integrity": "sha512-refir3NlutEZqlKaBLK0tzlVLe5P2wDKS7UQt/3SpibizgsRAPOsqQC3ffw1nlv3ze5gjRQZYHoPymgVZkplFA==", - "dev": true + "integrity": "sha512-refir3NlutEZqlKaBLK0tzlVLe5P2wDKS7UQt/3SpibizgsRAPOsqQC3ffw1nlv3ze5gjRQZYHoPymgVZkplFA==" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -5576,6 +5836,14 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "engines": { + "node": ">= 4" + } + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz", @@ -5593,6 +5861,19 @@ "once": "^1.4.0" } }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/err-code": { "version": "2.0.3", "resolved": "https://registry.npmmirror.com/err-code/-/err-code-2.0.3.tgz", @@ -5710,8 +5991,7 @@ "node_modules/es-module-lexer": { "version": "1.6.0", "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", - "dev": true + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==" }, "node_modules/es-object-atoms": { "version": "1.1.1", @@ -5829,7 +6109,6 @@ "version": "3.2.0", "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "engines": { "node": ">=6" } @@ -6361,7 +6640,6 @@ "version": "4.3.0", "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -6373,7 +6651,6 @@ "version": "5.3.0", "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "engines": { "node": ">=4.0" } @@ -6504,6 +6781,15 @@ "node": ">=6" } }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "peer": true, + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz", @@ -6625,8 +6911,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { "version": "3.3.3", @@ -6647,8 +6932,7 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -6656,6 +6940,22 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "peer": true + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.19.1.tgz", @@ -6690,6 +6990,38 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", @@ -7112,6 +7444,12 @@ "node": ">= 6" } }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "peer": true + }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz", @@ -7177,8 +7515,7 @@ "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/graphemer": { "version": "1.4.0", @@ -7219,7 +7556,6 @@ "version": "4.0.0", "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -8157,6 +8493,35 @@ "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==", "dev": true }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/jiti": { "version": "1.21.7", "resolved": "https://registry.npmmirror.com/jiti/-/jiti-1.21.7.tgz", @@ -8213,8 +8578,7 @@ "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -8226,7 +8590,6 @@ "version": "2.2.3", "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "bin": { "json5": "lib/cli.js" }, @@ -8384,6 +8747,15 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "peer": true, + "engines": { + "node": ">=6.11.5" + } + }, "node_modules/loader-utils": { "version": "3.3.1", "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-3.3.1.tgz", @@ -8621,6 +8993,14 @@ "yallist": "^3.0.2" } }, + "node_modules/make-cancellable-promise": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/make-cancellable-promise/-/make-cancellable-promise-1.3.2.tgz", + "integrity": "sha512-GCXh3bq/WuMbS+Ky4JBPW1hYTOU+znU+Q5m9Pu+pI8EoUqIHk9+tviOKC6/qhHh8C4/As3tzJ69IF32kdz85ww==", + "funding": { + "url": "https://github.com/wojtekmaj/make-cancellable-promise?sponsor=1" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-3.1.0.tgz", @@ -8645,6 +9025,14 @@ "semver": "bin/semver.js" } }, + "node_modules/make-event-props": { + "version": "1.6.2", + "resolved": "https://registry.npmmirror.com/make-event-props/-/make-event-props-1.6.2.tgz", + "integrity": "sha512-iDwf7mA03WPiR8QxvcVHmVWEPfMY1RZXerDVNCRYW7dUr2ppH3J58Rwb39/WG39yTZdRSxr3x+2v22tvI0VEvA==", + "funding": { + "url": "https://github.com/wojtekmaj/make-event-props?sponsor=1" + } + }, "node_modules/mammoth": { "version": "1.9.0", "resolved": "https://registry.npmmirror.com/mammoth/-/mammoth-1.9.0.tgz", @@ -8906,6 +9294,14 @@ "node": ">= 0.6" } }, + "node_modules/merge-class-names": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/merge-class-names/-/merge-class-names-1.4.2.tgz", + "integrity": "sha512-bOl98VzwCGi25Gcn3xKxnR5p/WrhWFQB59MS/aGENcmUc6iSm96yrFDF0XSNurX9qN4LbJm0R9kfvsQ17i8zCw==", + "funding": { + "url": "https://github.com/wojtekmaj/merge-class-names?sponsor=1" + } + }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -8914,11 +9310,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge-refs": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/merge-refs/-/merge-refs-1.3.0.tgz", + "integrity": "sha512-nqXPXbso+1dcKDpPCXvwZyJILz+vSLqGGOnDrYHQYE+B8n9JTCekVLC65AfCpR4ggVyA/45Y0iR9LDyS2iI+zA==", + "funding": { + "url": "https://github.com/wojtekmaj/merge-refs?sponsor=1" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" }, "node_modules/merge2": { "version": "1.4.1", @@ -9944,6 +10355,12 @@ "node": ">= 0.6" } }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "peer": true + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz", @@ -9967,8 +10384,7 @@ "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" }, "node_modules/nopt": { "version": "5.0.0", @@ -10097,7 +10513,6 @@ "version": "4.1.1", "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -10654,8 +11069,7 @@ "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -11088,7 +11502,6 @@ "version": "15.8.1", "resolved": "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -11142,7 +11555,6 @@ "version": "2.3.1", "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "engines": { "node": ">=6" } @@ -11197,6 +11609,15 @@ } ] }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "peer": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz", @@ -11245,8 +11666,44 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-pdf": { + "version": "5.7.2", + "resolved": "https://registry.npmmirror.com/react-pdf/-/react-pdf-5.7.2.tgz", + "integrity": "sha512-hdDwvf007V0i2rPCqQVS1fa70CXut17SN3laJYlRHzuqcu8sLLjEoeXihty6c0Ev5g1mw31b8OT8EwRw1s8C4g==", + "dependencies": { + "@babel/runtime": "^7.0.0", + "file-loader": "^6.0.0", + "make-cancellable-promise": "^1.0.0", + "make-event-props": "^1.1.0", + "merge-class-names": "^1.1.1", + "merge-refs": "^1.0.0", + "pdfjs-dist": "2.12.313", + "prop-types": "^15.6.2", + "tiny-invariant": "^1.0.0", + "tiny-warning": "^1.0.0" + }, + "funding": { + "url": "https://github.com/wojtekmaj/react-pdf?sponsor=1" + }, + "peerDependencies": { + "react": "^16.3.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.3.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-pdf/node_modules/pdfjs-dist": { + "version": "2.12.313", + "resolved": "https://registry.npmmirror.com/pdfjs-dist/-/pdfjs-dist-2.12.313.tgz", + "integrity": "sha512-1x6iXO4Qnv6Eb+YFdN5JdUzt4pAkxSp3aLAYPX93eQCyg/m7QFzXVWJHJVtoW48CI8HCXju4dSkhQZwoheL5mA==", + "peerDependencies": { + "worker-loader": "^3.0.8" + }, + "peerDependenciesMeta": { + "worker-loader": { + "optional": true + } + } }, "node_modules/react-refresh": { "version": "0.14.2", @@ -11459,6 +11916,15 @@ "resolved": "https://registry.npmmirror.com/remixicon/-/remixicon-4.6.0.tgz", "integrity": "sha512-bKM5odjqE1yzVxEZGJE7F79WHhNrJFIKHXR+GG+P1IWXn8AnJZhl8SbIRDJsNAvIqx4VPkNwjuHfc42tutMDpQ==" }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-like": { "version": "0.1.2", "resolved": "https://registry.npmmirror.com/require-like/-/require-like-0.1.2.tgz", @@ -11785,6 +12251,23 @@ "loose-envify": "^1.1.0" } }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/semver": { "version": "7.7.1", "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.1.tgz", @@ -11841,6 +12324,15 @@ "node": ">= 0.8" } }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "peer": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/serve-static": { "version": "1.16.2", "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.16.2.tgz", @@ -12560,6 +13052,15 @@ "node": ">=4" } }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmmirror.com/tar/-/tar-6.2.1.tgz", @@ -12660,6 +13161,117 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "devOptional": true }, + "node_modules/terser": { + "version": "5.39.0", + "resolved": "https://registry.npmmirror.com/terser/-/terser-5.39.0.tgz", + "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", + "peer": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.14", + "resolved": "https://registry.npmmirror.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "peer": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "peer": true + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz", @@ -12733,6 +13345,16 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, "node_modules/tinyglobby": { "version": "0.2.12", "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.12.tgz", @@ -13053,8 +13675,7 @@ "node_modules/undici-types": { "version": "6.20.0", "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" }, "node_modules/unified": { "version": "10.1.2", @@ -13237,7 +13858,6 @@ "version": "1.1.3", "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, "funding": [ { "type": "opencollective", @@ -13267,7 +13887,6 @@ "version": "4.4.1", "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -13512,6 +14131,19 @@ "resolved": "https://registry.npmmirror.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz", "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" }, + "node_modules/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "peer": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz", @@ -13546,6 +14178,142 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "optional": true }, + "node_modules/webpack": { + "version": "5.99.5", + "resolved": "https://registry.npmmirror.com/webpack/-/webpack-5.99.5.tgz", + "integrity": "sha512-q+vHBa6H9qwBLUlHL4Y7L0L1/LlyBKZtS9FHNCQmtayxjI5RKC9yD8gpvLeqGv5lCQp1Re04yi0MF40pf30Pvg==", + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "peer": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/webpack/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "peer": true + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "peer": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz", diff --git a/package.json b/package.json index 9753dc3..9e7d336 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@uiw/react-codemirror": "^4.23.10", "dayjs": "^1.11.13", "diff": "^7.0.0", + "docx-preview": "^0.3.5", "html-docx-js": "^0.3.1", "isbot": "^4.1.0", "mammoth": "^1.9.0", @@ -31,12 +32,14 @@ "prismjs": "^1.30.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-pdf": "^5.7.2", "remixicon": "^4.6.0" }, "devDependencies": { "@remix-run/dev": "^2.16.2", "@types/react": "^18.2.20", "@types/react-dom": "^18.2.7", + "@types/react-pdf": "^7.0.0", "@typescript-eslint/eslint-plugin": "^6.7.4", "@typescript-eslint/parser": "^6.7.4", "autoprefixer": "^10.4.21", diff --git a/vite.config.ts b/vite.config.ts index b1697ac..2ad6037 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -23,7 +23,7 @@ export default defineConfig({ ], server: { host: '0.0.0.0', - port: 5173, + port: 5178, open: true, allowedHosts: ['nas.7bm.co', 'localhost', '127.0.0.1'], // 允许的主机名列表 },