npm package: @gientech/modual
Description: Gientech 多模块集成包，提供聊天、数据库、文件预览等业务模块。
Author: hanfeng_Zhang
Latest version: 2.3.23

# @gientech/modual

Gientech 多模块集成包，提供聊天、数据库、文件预览等业务模块。

## 📦 安装

```bash
npm install @gientech/modual
```

**⚠️ 安装问题？** 如果使用 Yarn 安装时遇到 hoisting 错误，请查看 [安装故障排除指南](./INSTALL_TROUBLESHOOTING.md)。

## 🏗️ 打包逻辑

### 多入口打包架构

本包采用**多入口打包**策略，每个业务模块独立打包，支持按需引入：

```text
dist/
├── index.js              # 主入口（导出所有模块）
├── chat.js               # 聊天模块
├── database.js           # 数据库模块
├── databaseId.js         # 数据库ID模块
├── databaseTable.js      # 数据库表模块
├── modelManage.js        # 模型管理模块
├── sensitive.js          # 敏感词模块
├── streamFilesReader.js  # 流式文件读取器模块
├── assets/
│   └── style.css         # 统一样式文件
└── *.d.ts                # TypeScript 类型定义文件
```

### 打包配置说明

1. **入口文件**（`vite.config.ts`）：
   - `index`: `src/lib_enter.ts` - 主入口，导出所有模块
   - `chat`: `src/modules/chat/index.tsx` - 聊天模块
   - `database`: `src/modules/database/index.tsx` - 数据库模块
   - 其他模块类似...

2. **外部依赖**（不打包进 bundle）：
   - `react`, `react-dom`
   - `antd`, `axios`
   - `@mxmweb/*` 系列包

3. **样式处理**：
   - 所有 CSS/LESS 文件合并到 `dist/assets/style.css`
   - 支持通过 `@gientech/modual/style.css` 引入

4. **打包模式**：
   - `build`: 内联依赖模式（`VITE_INLINE_DEPS=true`）
   - `build:external`: 外部依赖模式（推荐用于 npm 发布）

## 🚀 快速开始

### 1. 注册 PDF Worker（必需）

**⚠️ 重要：必须在应用入口文件（main.tsx）中注册，不能在组件内部注册！**

```javascript
// main.tsx（应用入口文件）
import React from 'react';
import ReactDOM from 'react-dom/client';

// 方式一：从主入口引入（推荐）
import { registerPDFWorker } from '@gientech/modual';

// 方式二：从 chat 模块引入
// import { registerPDFWorker } from '@gientech/modual/chat';

// 方式三：直接从 @mxmweb/fviewer 引入
// import { registerPDFWorker } from '@mxmweb/fviewer';

// ⚠️ 必须在组件渲染之前注册 Worker！
registerPDFWorker('/worker/pdf.worker.min.js');

// 验证注册是否成功（可选）
import { isPDFWorkerRegistered } from '@mxmweb/fviewer';
if (isPDFWorkerRegistered()) {
  console.log('PDF Worker 注册成功');
} else {
  console.error('PDF Worker 注册失败，请检查路径是否正确');
}

import App from './App';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
```

**❌ 错误示例（不要在组件内部注册）：**

```javascript
// ❌ 错误：在组件内部注册
function App() {
  registerPDFWorker('/worker/pdf.worker.min.js'); // 错误！
  return <YourComponent />;
}
```

**✅ 正确示例（在入口文件注册）：**

```javascript
// ✅ 正确：在入口文件注册
// main.tsx
import { registerPDFWorker } from '@gientech/modual';
registerPDFWorker('/worker/pdf.worker.min.js'); // 在组件渲染前注册

import App from './App';
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
```

**Worker 文件部署**：

1. 从 `node_modules/pdfjs-dist/build/pdf.worker.min.js` 复制到 `public/worker/`
2. 或使用 CDN 提供 Worker 文件
3. 确保路径与注册时使用的路径一致

### 2. 引入样式

**⚠️ 重要：必须引入所有相关样式文件，否则组件样式会丢失！**

```javascript
// 在入口文件引入（推荐）
// 1. 引入 gientech 包样式
import '@gientech/modual/style.css';

// 2. 引入依赖包的样式（必需）
import '@mxmweb/rtext/style.css';
import '@mxmweb/aichat/style.css';
import '@mxmweb/zui/style.css';

// 3. 引入 Antd 样式（如果使用 Antd 组件）
import 'antd/dist/reset.css';
// 或者使用 Antd 5.x 的样式
// import 'antd/dist/reset.css';
```

**样式引入顺序**（建议按以下顺序）：

```javascript
// main.tsx 或 App.tsx
// 1. 先引入依赖包样式
import '@mxmweb/rtext/style.css';
import '@mxmweb/aichat/style.css';
import '@mxmweb/zui/style.css';
import 'antd/dist/reset.css';

// 2. 再引入 gientech 包样式
import '@gientech/modual/style.css';
```

#### 方式二：在 HTML 中引入

```html
<link rel="stylesheet" href="./node_modules/@mxmweb/rtext/dist/style.css">
<link rel="stylesheet" href="./node_modules/@mxmweb/aichat/dist/style.css">
<link rel="stylesheet" href="./node_modules/@mxmweb/zui/dist/style.css">
<link rel="stylesheet" href="./node_modules/antd/dist/reset.css">
<link rel="stylesheet" href="./node_modules/@gientech/modual/dist/assets/style.css">
```

### 3. 使用组件

#### 方式一：按需引入（推荐）

```javascript
// 只引入需要的模块，减少 bundle 大小
import withGientechChatAdopter from '@gientech/modual/chat';

// 引入样式（必需）
import '@mxmweb/rtext/style.css';
import '@mxmweb/aichat/style.css';
import '@mxmweb/zui/style.css';
import 'antd/dist/reset.css';
import '@gientech/modual/style.css';

// 使用高阶组件
const GientechChat = withGientechChatAdopter();

function App() {
  return (
    <GientechChat
      token="your-token"
      url="https://your-api.com"
      // 其他配置...
    />
  );
}
```

#### 方式二：从主入口引入

```javascript
// 引入所有模块（会增加 bundle 大小）
import { 
  withGientechChatAdopter,
  GientechStreamReader,
  registerPDFWorker 
} from '@gientech/modual';

// 引入样式（必需）
import '@mxmweb/rtext/style.css';
import '@mxmweb/aichat/style.css';
import '@mxmweb/zui/style.css';
import 'antd/dist/reset.css';
import '@gientech/modual/style.css';
```

## 📚 模块说明

### Chat 聊天模块

```javascript
import withGientechChatAdopter from '@gientech/modual/chat';

// 引入样式（必需）
import '@mxmweb/rtext/style.css';
import '@mxmweb/aichat/style.css';
import '@mxmweb/zui/style.css';
import 'antd/dist/reset.css';
import '@gientech/modual/style.css';

const GientechChat = withGientechChatAdopter();

function App() {
  return (
    <GientechChat
      token="your-token"
      url="https://your-api.com"
      styles={{
        theme: {
          colors: {
            primary: '#4E6EF2',
            // ... 其他主题配置
          }
        }
      }}
      eventsEmit={(eventName, data) => {
        console.log('事件:', eventName, data);
      }}
    />
  );
}
```

### StreamFilesReader 流式文件读取器

```javascript
import { GientechStreamReader } from '@gientech/modual/streamFilesReader';

function FileViewer() {
  return (
    <GientechStreamReader
      convertedFilePath="https://example.com/file.pdf"
      fileName="example.pdf"
      fileType="pdf"
      totalPages={10}
      authorization="your-token"
      streamApiUrl="/api/file/stream"
    />
  );
}
```

### Database 数据库模块

```javascript
import DatabaseViewAdopter from '@gientech/modual/database';

function DatabasePage() {
  return (
    <DatabaseViewAdopter
      token="your-token"
      url="https://your-api.com"
    />
  );
}
```

### 其他模块

- `@gientech/modual/databaseId` - 数据库ID模块
- `@gientech/modual/databaseTable` - 数据库表模块
- `@gientech/modual/modelManage` - 模型管理模块
- `@gientech/modual/sensitive` - 敏感词模块

## 📦 依赖要求

### Peer Dependencies（需要用户自行安装）

```json
{
  "react": ">=18 <20",
  "react-dom": ">=18 <20",
  "@mxmweb/fviewer": "^1",
  "@mxmweb/rtext": "^1.2.*",
  "@mxmweb/zui": "^1.3.x",
  "@mxmweb/aichat": "^1.8.0",
  "@mxmweb/xviewer": "^1.2.x",
  "antd": "^5.18.0",
  "axios": "^1.7.2",
  "pdfjs-dist": "2.16.105",
  "styled-components": "^6.1.19"
}
```

### 安装依赖

```bash
npm install react react-dom @mxmweb/fviewer @mxmweb/rtext @mxmweb/zui @mxmweb/aichat @mxmweb/xviewer antd axios pdfjs-dist@2.16.105 styled-components@^6.1.19
```

**⚠️ 重要**：`pdfjs-dist` 和 `styled-components` 是 `@mxmweb/fviewer` 的必需依赖，必须安装。

## 🔧 打包模式说明

### 内联依赖模式（`build`）

```bash
npm run build
# 或
cross-env VITE_INLINE_DEPS=true vite build --mode production
```

- 将部分依赖打包进 bundle
- 适合独立部署的场景
- Bundle 体积较大

### 外部依赖模式（`build:external`，推荐）

```bash
npm run build:external
# 或
vite build --mode production
```

- 依赖作为 peerDependencies
- 适合 npm 发布
- Bundle 体积较小
- 用户需要安装 peerDependencies

## 📝 完整使用示例

```javascript
// main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { registerPDFWorker } from '@gientech/modual';

// 1. 引入依赖包样式（必需，顺序很重要）
import '@mxmweb/rtext/style.css';
import '@mxmweb/aichat/style.css';
import '@mxmweb/zui/style.css';
import 'antd/dist/reset.css';

// 2. 引入 gientech 包样式
import '@gientech/modual/style.css';

import App from './App';

// 3. 注册 PDF Worker（必须在组件使用前）
registerPDFWorker('/worker/pdf.worker.min.js');

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// App.tsx
import React from 'react';
import withGientechChatAdopter from '@gientech/modual/chat';

const GientechChat = withGientechChatAdopter();

function App() {
  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <GientechChat
        token="your-token"
        url="https://your-api.com"
        styles={{
          theme: {
            colors: {
              primary: '#4E6EF2',
              background: '#ffffff',
              text: '#000000',
            }
          }
        }}
        eventsEmit={(eventName, data) => {
          console.log('事件:', eventName, data);
        }}
      />
    </div>
  );
}

export default App;
```

## ⚠️ 注意事项

1. **PDF Worker 注册**：
   - 必须在应用入口处注册
   - 只注册一次即可
   - Worker 文件路径需要根据实际部署情况调整

2. **样式引入**：
   - **必须引入所有相关样式文件**，否则组件样式会丢失
   - 依赖包样式（必需）：
     - `@mxmweb/rtext/style.css`
     - `@mxmweb/aichat/style.css`
     - `@mxmweb/zui/style.css`
     - `antd/dist/reset.css`（如果使用 Antd 组件）
   - gientech 包样式：`@gientech/modual/style.css`
   - **引入顺序**：先引入依赖包样式，再引入 gientech 包样式

3. **按需引入**：
   - 推荐使用按需引入，减少 bundle 大小
   - 例如：`import withGientechChatAdopter from '@gientech/modual/chat'`

4. **依赖安装**：
   - 确保所有 peerDependencies 已安装
   - 版本需要满足 package.json 中的要求

5. **TypeScript 支持**：
   - 包包含完整的 TypeScript 类型定义
   - 类型文件位于 `dist/*.d.ts`

## 🐛 常见问题

### Q: PDF 预览报错 "PDF Worker 未注册"

**A**: 需要**在应用入口文件（main.tsx）中**注册 Worker，不能在组件内部注册：

```javascript
// main.tsx（应用入口文件）
import { registerPDFWorker } from '@gientech/modual';

// ⚠️ 必须在组件渲染之前调用！
registerPDFWorker('/worker/pdf.worker.min.js');

import App from './App';
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
```

**常见错误**：

1. **在组件内部注册**（错误）：

   ```javascript
   function App() {
     registerPDFWorker('/worker/pdf.worker.min.js'); // ❌ 错误！
     return <YourComponent />;
   }
   ```

2. **在 useEffect 中注册**（错误）：

   ```javascript
   useEffect(() => {
     registerPDFWorker('/worker/pdf.worker.min.js'); // ❌ 错误！可能太晚了
   }, []);
   ```

3. **Worker 文件路径不正确**：
   - 确保 `pdf.worker.min.js` 文件存在于 `public/worker/` 目录
   - 确保路径与注册时使用的路径一致

**验证注册是否成功**：

```javascript
// 方式一：使用 @mxmweb/fviewer 的检查函数
import { isPDFWorkerRegistered } from '@mxmweb/fviewer';

if (isPDFWorkerRegistered()) {
  console.log('✅ PDF Worker 已注册');
} else {
  console.error('❌ PDF Worker 未注册');
}

// 方式二：直接检查 pdfjs-dist（推荐，更可靠）
import * as pdfjsLib from 'pdfjs-dist';

if (pdfjsLib.GlobalWorkerOptions.workerSrc) {
  console.log('✅ PDF Worker 已注册:', pdfjsLib.GlobalWorkerOptions.workerSrc);
} else {
  console.error('❌ PDF Worker 未注册');
}
```

**⚠️ 重要提示**：

如果 `registerPDFWorker` 显示成功但 `isPDFWorkerRegistered()` 返回 false，可能是因为：

1. **多个 pdfjs-dist 实例**：确保项目中只有一个 `pdfjs-dist` 实例

   ```bash
   # 检查是否有多个版本
   npm ls pdfjs-dist
   
   # 如果有多版本，统一版本
   npm install pdfjs-dist@2.16.105 --save
   ```

2. **Vite 配置问题**：确保 `pdfjs-dist` 被正确 externalize（见下方配置）

3. **模块解析问题**：在 `vite.config.ts` 中添加 `dedupe` 配置：

   ```typescript
   resolve: {
     dedupe: ['pdfjs-dist', 'styled-components'],
   }
   ```

### Q: 找不到 registerPDFWorker

**A**: `registerPDFWorker` 可以从以下位置引入：

```javascript
// 方式一：从主入口引入（推荐）
import { registerPDFWorker } from '@gientech/modual';

// 方式二：从 chat 模块引入
import { registerPDFWorker } from '@gientech/modual/chat';

// 方式三：直接从依赖包引入
import { registerPDFWorker } from '@mxmweb/fviewer';
```

**注意**：

- 如果使用 TypeScript，确保已安装 `@mxmweb/fviewer` 作为 peerDependency
- **必须安装 `pdfjs-dist@2.16.105`**，这是 `@mxmweb/fviewer` 的必需依赖
- 如果使用 Vite，可能需要在 `vite.config.ts` 中将 `pdfjs-dist` 标记为外部依赖（见下方配置示例）

### Q: 组件样式丢失或样式差距很大

**A**: 需要引入**所有相关样式文件**，包括依赖包的样式：

```javascript
// 1. 引入依赖包样式（必需）
import '@mxmweb/rtext/style.css';
import '@mxmweb/aichat/style.css';
import '@mxmweb/zui/style.css';
import 'antd/dist/reset.css';

// 2. 引入 gientech 包样式
import '@gientech/modual/style.css';
```

**注意**：

- 样式引入顺序很重要，建议先引入依赖包样式，再引入 gientech 包样式
- 如果只引入 `@gientech/modual/style.css`，依赖包的样式会丢失，导致组件样式不完整

### Q: 打包后 Worker 文件找不到

**A**: Worker 文件不应被打包，应作为静态资源部署：

- 将 `pdf.worker.min.js` 放在 `public/worker/` 目录
- 或使用 CDN 提供 Worker 文件

### Q: 报错 "Could not resolve pdfjs-dist"

**A**: 需要安装 `pdfjs-dist` 依赖：

```bash
npm install pdfjs-dist@2.16.105
```

如果使用 Vite，可能还需要在 `vite.config.ts` 中配置：

```typescript
// vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
  // ... 其他配置
  build: {
    rollupOptions: {
      external: ['pdfjs-dist'], // 将 pdfjs-dist 标记为外部依赖
    },
  },
  optimizeDeps: {
    include: ['pdfjs-dist'], // 预构建 pdfjs-dist
  },
});
```

**注意**：`pdfjs-dist` 是 `@mxmweb/fviewer` 的 peerDependency，必须安装。

### Q: 报错 "PDF Worker 自动注册失败" 或 "xe3 is not a function"

**A**: 这是 `@mxmweb/fviewer` 内部自动注册逻辑的问题，可以忽略。**请确保在应用入口处手动注册 Worker**：

```javascript
// main.tsx
import { registerPDFWorker } from '@gientech/modual';
registerPDFWorker('/worker/pdf.worker.min.js');
```

**原因**：`@mxmweb/fviewer` 内部有自动注册逻辑，但打包后可能因为函数引用问题导致失败。手动注册可以避免这个问题。

### Q: 警告 "several instances of 'styled-components'"

**A**: 这是因为 `styled-components` 被打包进了多个 bundle。解决方案：

1. **确保 `styled-components` 被正确 externalize**（在 `gientech` 包中已配置）

2. **在用户项目的 `vite.config.ts` 中配置**：

   ```typescript
   // vite.config.ts
   import { defineConfig } from 'vite';

   export default defineConfig({
     resolve: {
       dedupe: ['styled-components', 'pdfjs-dist'],
     },
     optimizeDeps: {
       include: ['styled-components'],
     },
   });
   ```

3. **检查是否有多个版本**：

```bash
npm ls styled-components
# 如果有多版本，统一版本
npm install styled-components@^6.1.19 --save
```

**注意**：这个警告通常不会影响功能，但会增加 bundle 大小。建议修复。

### Q: Yarn 安装时报错 "Invariant Violation: expected hoisted manifest"

**错误信息**：
```
error Invariant Violation: expected hoisted manifest for "@gientech/modual#@mxmweb/zui-components#@mxmweb/zui-elements"
```

**原因**：这是因为 `@mxmweb/zui-components` 和 `@mxmweb/zui-elements` 同时出现在 `dependencies` 和 `peerDependencies` 中，导致 Yarn 的 hoisting 算法无法正确解析依赖关系。

**解决方案**（用户端，无需修改包配置）：

#### 方案一：使用 `resolutions` 强制版本（推荐，适用于 Yarn）

在用户项目的 `package.json` 中添加 `resolutions` 字段：

```json
{
  "resolutions": {
    "@mxmweb/zui-components": "1.2.5",
    "@mxmweb/zui-elements": "1.1.5",
    "@mxmweb/zui-icons": "1.2.2",
    "@mxmweb/zui-layouts": "1.3.1",
    "@mxmweb/zui-theme": "2.1.5"
  }
}
```

然后重新安装：

```bash
yarn install
```

#### 方案二：使用 `.yarnrc.yml` 配置（适用于 Yarn 2+ / Berry）

在项目根目录创建或修改 `.yarnrc.yml` 文件：

```yaml
nodeLinker: node-modules

packageExtensions:
  "@gientech/modual@*":
    peerDependencies:
      "@mxmweb/zui-components": "*"
      "@mxmweb/zui-elements": "*"
```

然后重新安装：

```bash
yarn install
```

#### 方案三：使用 npm 或 pnpm（推荐）

如果使用 Yarn 遇到问题，可以切换到 npm 或 pnpm：

```bash
# 使用 npm
npm install @gientech/modual

# 或使用 pnpm
pnpm install @gientech/modual
```

#### 方案四：手动安装 peerDependencies

先安装 `@gientech/modual` 的 peerDependencies，然后再安装包本身：

```bash
# 先安装 peerDependencies
yarn add @mxmweb/zui-components@1.2.5 \
         @mxmweb/zui-elements@1.1.5 \
         @mxmweb/zui-icons@1.2.2 \
         @mxmweb/zui-layouts@1.3.1 \
         @mxmweb/zui-theme@2.1.5

# 再安装 @gientech/modual
yarn add @gientech/modual
```

#### 方案五：使用 `--ignore-scripts` 和手动安装

```bash
# 先清理
rm -rf node_modules yarn.lock

# 使用 --ignore-scripts 安装
yarn install --ignore-scripts

# 手动安装缺失的依赖
yarn add @mxmweb/zui-components@1.2.5 \
         @mxmweb/zui-elements@1.1.5 \
         @mxmweb/zui-icons@1.2.2 \
         @mxmweb/zui-layouts@1.3.1 \
         @mxmweb/zui-theme@2.1.5
```

**推荐方案**：

- **Yarn 1.x**：使用方案一（`resolutions`）
- **Yarn 2+ / Berry**：使用方案二（`.yarnrc.yml`）
- **新项目**：使用方案三（npm 或 pnpm）

**验证安装**：

安装完成后，验证依赖是否正确：

```bash
# 检查依赖树
yarn list --pattern "@mxmweb/zui-*"

# 或使用 npm
npm ls @mxmweb/zui-components @mxmweb/zui-elements
```

## 📄 许可证

MIT