mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-14 03:30:19 +00:00
163 lines
4.0 KiB
Plaintext
163 lines
4.0 KiB
Plaintext
|
|
---
|
|||
|
|
description: Modal 命令式调用指南
|
|||
|
|
globs: "**/features/**/*.tsx"
|
|||
|
|
alwaysApply: false
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Modal 命令式调用指南
|
|||
|
|
|
|||
|
|
当需要创建可命令式调用的 Modal 组件时,使用 `@lobehub/ui` 提供的 `createModal` API。
|
|||
|
|
|
|||
|
|
## 核心理念
|
|||
|
|
|
|||
|
|
**命令式调用** vs **声明式调用**:
|
|||
|
|
|
|||
|
|
| 模式 | 特点 | 适用场景 |
|
|||
|
|
|------|------|----------|
|
|||
|
|
| 声明式 | 需要维护 `open` state,渲染 `<Modal />` 组件 | ❌ 不推荐 |
|
|||
|
|
| 命令式 | 直接调用函数打开,无需 state 管理 | ✅ 推荐 |
|
|||
|
|
|
|||
|
|
## 文件组织结构
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
features/
|
|||
|
|
└── MyFeatureModal/
|
|||
|
|
├── index.tsx # 导出 createXxxModal 函数
|
|||
|
|
├── MyFeatureContent.tsx # Modal 内容组件
|
|||
|
|
└── ...其他子组件
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## createModal 用法(推荐)
|
|||
|
|
|
|||
|
|
### 1. 定义 Content 组件 (`MyFeatureContent.tsx`)
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
'use client';
|
|||
|
|
|
|||
|
|
import { useModalContext } from '@lobehub/ui';
|
|||
|
|
import { useTranslation } from 'react-i18next';
|
|||
|
|
|
|||
|
|
export const MyFeatureContent = () => {
|
|||
|
|
const { t } = useTranslation('namespace');
|
|||
|
|
const { close } = useModalContext(); // 可选:获取关闭方法
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div>
|
|||
|
|
{/* Modal 内容 */}
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 导出 createModal 函数 (`index.tsx`)
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
'use client';
|
|||
|
|
|
|||
|
|
import { createModal } from '@lobehub/ui';
|
|||
|
|
import { t } from 'i18next'; // 注意:使用 i18next 而非 react-i18next
|
|||
|
|
|
|||
|
|
import { MyFeatureContent } from './MyFeatureContent';
|
|||
|
|
|
|||
|
|
export const createMyFeatureModal = () =>
|
|||
|
|
createModal({
|
|||
|
|
allowFullscreen: true,
|
|||
|
|
children: <MyFeatureContent />,
|
|||
|
|
destroyOnHidden: false,
|
|||
|
|
footer: null,
|
|||
|
|
styles: {
|
|||
|
|
body: { overflow: 'hidden', padding: 0 },
|
|||
|
|
},
|
|||
|
|
title: t('myFeature.title', { ns: 'setting' }),
|
|||
|
|
width: 'min(80%, 800px)',
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 调用方使用
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
import { useCallback } from 'react';
|
|||
|
|
import { createMyFeatureModal } from '@/features/MyFeatureModal';
|
|||
|
|
|
|||
|
|
const MyComponent = () => {
|
|||
|
|
const handleOpenModal = useCallback(() => {
|
|||
|
|
createMyFeatureModal();
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
return <Button onClick={handleOpenModal}>打开</Button>;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 关键要点
|
|||
|
|
|
|||
|
|
### i18n 处理
|
|||
|
|
|
|||
|
|
- **Content 组件内**:使用 `useTranslation` hook(React 上下文)
|
|||
|
|
- **createModal 参数中**:使用 `import { t } from 'i18next'`(非 hook,支持命令式调用)
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
// index.tsx - 命令式上下文
|
|||
|
|
import { t } from 'i18next';
|
|||
|
|
title: t('key', { ns: 'namespace' })
|
|||
|
|
|
|||
|
|
// Content.tsx - React 组件上下文
|
|||
|
|
import { useTranslation } from 'react-i18next';
|
|||
|
|
const { t } = useTranslation('namespace');
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### useModalContext Hook
|
|||
|
|
|
|||
|
|
在 Content 组件内可使用 `useModalContext` 获取 Modal 控制方法:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
const { close, setCanDismissByClickOutside } = useModalContext();
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### ModalHost
|
|||
|
|
|
|||
|
|
`createModal` 依赖全局 `<ModalHost />` 组件。项目中已在 `src/layout/GlobalProvider/index.tsx` 配置,无需额外添加。
|
|||
|
|
|
|||
|
|
## 常用配置项
|
|||
|
|
|
|||
|
|
| 属性 | 类型 | 说明 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `allowFullscreen` | `boolean` | 允许全屏模式 |
|
|||
|
|
| `destroyOnHidden` | `boolean` | 关闭时是否销毁内容(`destroyOnClose` 已废弃) |
|
|||
|
|
| `footer` | `ReactNode \| null` | 底部内容,`null` 表示无底部 |
|
|||
|
|
| `width` | `string \| number` | Modal 宽度 |
|
|||
|
|
| `styles.body` | `CSSProperties` | body 区域样式 |
|
|||
|
|
|
|||
|
|
## 迁移指南
|
|||
|
|
|
|||
|
|
### Before(声明式)
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
// 调用方需要维护 state
|
|||
|
|
const [open, setOpen] = useState(false);
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<>
|
|||
|
|
<Button onClick={() => setOpen(true)}>打开</Button>
|
|||
|
|
<MyModal open={open} setOpen={setOpen} />
|
|||
|
|
</>
|
|||
|
|
);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### After(命令式)
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
// 调用方无需 state,直接调用函数
|
|||
|
|
const handleOpen = useCallback(() => {
|
|||
|
|
createMyModal();
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
return <Button onClick={handleOpen}>打开</Button>;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 示例参考
|
|||
|
|
|
|||
|
|
- `src/features/SkillStore/index.tsx` - createModal 标准用法
|
|||
|
|
- `src/features/SkillStore/SkillStoreContent.tsx` - Content 组件示例
|
|||
|
|
- `src/features/LibraryModal/CreateNew/index.tsx` - 带回调的 createModal 用法
|
|||
|
|
- `src/features/Electron/updater/UpdateModal.tsx` - 复杂 Modal 控制示例
|