@wecode-team/we0-cms 1.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +450 -0
- package/dist/components/componets/message.d.ts +42 -0
- package/dist/components/filters/DynamicFilters.d.ts +7 -0
- package/dist/components/json/JsonField.d.ts +11 -0
- package/dist/components/json/json-utils.d.ts +19 -0
- package/dist/components/page-shadcn.d.ts +16 -0
- package/dist/components/pages/admin.d.ts +2 -0
- package/dist/components/pages/data-list.d.ts +2 -0
- package/dist/components/pages/data-manage.d.ts +3 -0
- package/dist/components/pages/login.d.ts +6 -0
- package/dist/components/pages/model-create.d.ts +2 -0
- package/dist/components/pages/test-json.d.ts +2 -0
- package/dist/components/prase.d.ts +5 -0
- package/dist/components/routes.d.ts +12 -0
- package/dist/components/types.d.ts +69 -0
- package/dist/components/ui/badge.d.ts +9 -0
- package/dist/components/ui/button.d.ts +11 -0
- package/dist/components/ui/card.d.ts +8 -0
- package/dist/components/ui/dialog.d.ts +19 -0
- package/dist/components/ui/dropdown-menu.d.ts +27 -0
- package/dist/components/ui/input.d.ts +3 -0
- package/dist/components/ui/label.d.ts +5 -0
- package/dist/components/ui/select.d.ts +13 -0
- package/dist/components/ui/separator.d.ts +4 -0
- package/dist/components/ui/sheet.d.ts +25 -0
- package/dist/components/ui/sidebar.d.ts +50 -0
- package/dist/components/ui/skeleton.d.ts +3 -0
- package/dist/components/ui/switch.d.ts +4 -0
- package/dist/components/ui/table.d.ts +10 -0
- package/dist/components/ui/textarea.d.ts +3 -0
- package/dist/components/ui/tooltip.d.ts +7 -0
- package/dist/hooks/use-mobile.d.ts +1 -0
- package/dist/index.css +3 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.esm.js +11 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/request.d.ts +14 -0
- package/dist/services/index.d.ts +121 -0
- package/package.json +89 -0
package/README.md
ADDED
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
# @wecode-team/we0-cms
|
|
2
|
+
|
|
3
|
+
一个基于 React 的动态 CMS 前端组件库,支持 shadcn/ui 风格的现代化 UI,与 `@wecode-team/cms-supabase-api` 配合使用实现完整的内容管理系统。
|
|
4
|
+
|
|
5
|
+
## 📖 设计理念
|
|
6
|
+
|
|
7
|
+
### 核心思想
|
|
8
|
+
|
|
9
|
+
本包的核心理念是 **"配置驱动的 CMS UI"**:
|
|
10
|
+
|
|
11
|
+
1. **模型驱动 UI**:根据 JSON Schema 模型配置自动生成表单和表格
|
|
12
|
+
2. **零代码管理**:无需编写代码,通过配置即可实现数据的增删改查
|
|
13
|
+
3. **关系可视化**:支持关联字段的下拉选择和数据展示
|
|
14
|
+
4. **多租户支持**:通过 Session ID 实现数据隔离
|
|
15
|
+
|
|
16
|
+
### 架构设计
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
20
|
+
│ CmsLayoutShadcn │
|
|
21
|
+
│ (主布局组件) │
|
|
22
|
+
├─────────────────────────────────────────────────────────────┤
|
|
23
|
+
│ UI Components (shadcn/ui) │
|
|
24
|
+
│ ├── Sidebar 侧边栏导航 │
|
|
25
|
+
│ ├── DataTable 数据表格 │
|
|
26
|
+
│ ├── Dialog 弹窗表单 │
|
|
27
|
+
│ └── Form Controls 表单控件 │
|
|
28
|
+
├─────────────────────────────────────────────────────────────┤
|
|
29
|
+
│ Pages (页面组件) │
|
|
30
|
+
│ ├── LoginPage 登录页面 │
|
|
31
|
+
│ ├── DataListPage 数据列表页 │
|
|
32
|
+
│ └── DataManagePage 数据管理页(按模型) │
|
|
33
|
+
├─────────────────────────────────────────────────────────────┤
|
|
34
|
+
│ Services (服务层) │
|
|
35
|
+
│ ├── modelApi 模型管理 API │
|
|
36
|
+
│ ├── dataApi 数据操作 API │
|
|
37
|
+
│ └── authApi 认证 API │
|
|
38
|
+
├─────────────────────────────────────────────────────────────┤
|
|
39
|
+
│ Request Layer (请求层) │
|
|
40
|
+
│ └── request.ts Axios 封装,自动添加 Token │
|
|
41
|
+
└─────────────────────────────────────────────────────────────┘
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 数据流
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
用户操作 → UI 组件 → Services API → HTTP 请求 → 后端 API → Supabase
|
|
48
|
+
↓
|
|
49
|
+
用户界面 ← UI 更新 ← State 更新 ← API 响应 ←
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 🚀 安装
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm install @wecode-team/we0-cms
|
|
56
|
+
# 或
|
|
57
|
+
pnpm add @wecode-team/we0-cms
|
|
58
|
+
# 或
|
|
59
|
+
yarn add @wecode-team/we0-cms
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 📋 依赖
|
|
63
|
+
|
|
64
|
+
### Peer Dependencies(必须安装)
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"react": "^18.2.0",
|
|
69
|
+
"react-dom": "^18.2.0"
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 内置依赖
|
|
74
|
+
|
|
75
|
+
- `@radix-ui/*` - 无障碍 UI 原语
|
|
76
|
+
- `lucide-react` - 图标库
|
|
77
|
+
- `tailwind-merge` - Tailwind 类名合并
|
|
78
|
+
- `class-variance-authority` - 变体样式管理
|
|
79
|
+
- `dayjs` - 日期处理
|
|
80
|
+
|
|
81
|
+
## 🔧 快速开始
|
|
82
|
+
|
|
83
|
+
### 第一步:配置 Tailwind CSS
|
|
84
|
+
|
|
85
|
+
确保你的项目已配置 Tailwind CSS:
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
// tailwind.config.js
|
|
89
|
+
module.exports = {
|
|
90
|
+
content: [
|
|
91
|
+
'./src/**/*.{js,jsx,ts,tsx}',
|
|
92
|
+
'./node_modules/@wecode-team/we0-cms/dist/**/*.{js,jsx}'
|
|
93
|
+
],
|
|
94
|
+
theme: {
|
|
95
|
+
extend: {}
|
|
96
|
+
},
|
|
97
|
+
plugins: []
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 第二步:引入样式
|
|
102
|
+
|
|
103
|
+
```jsx
|
|
104
|
+
import '@wecode-team/we0-cms/dist/index.css'
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 第三步:使用组件
|
|
108
|
+
|
|
109
|
+
```jsx
|
|
110
|
+
import React from 'react'
|
|
111
|
+
import { CmsLayoutShadcn, setSessionId } from '@wecode-team/we0-cms'
|
|
112
|
+
import '@wecode-team/we0-cms/dist/index.css'
|
|
113
|
+
|
|
114
|
+
// 设置 Session ID(用于多租户隔离)
|
|
115
|
+
setSessionId('your-session-id')
|
|
116
|
+
|
|
117
|
+
// 定义模型配置
|
|
118
|
+
const modelData = {
|
|
119
|
+
models: [
|
|
120
|
+
{
|
|
121
|
+
id: 1,
|
|
122
|
+
name: '用户模型',
|
|
123
|
+
table_name: 'users',
|
|
124
|
+
json_schema: {
|
|
125
|
+
fields: [
|
|
126
|
+
{
|
|
127
|
+
name: 'name',
|
|
128
|
+
type: 'string',
|
|
129
|
+
comment: '用户姓名',
|
|
130
|
+
required: true,
|
|
131
|
+
maxLength: 100
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: 'email',
|
|
135
|
+
type: 'email',
|
|
136
|
+
unique: true,
|
|
137
|
+
comment: '用户邮箱',
|
|
138
|
+
required: true
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'age',
|
|
142
|
+
type: 'integer',
|
|
143
|
+
comment: '年龄',
|
|
144
|
+
required: false
|
|
145
|
+
}
|
|
146
|
+
]
|
|
147
|
+
},
|
|
148
|
+
created_at: '2023-07-18T19:00:28.098Z',
|
|
149
|
+
updated_at: '2023-07-18T19:00:28.099Z'
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function App() {
|
|
155
|
+
return (
|
|
156
|
+
<div className="min-h-screen">
|
|
157
|
+
<CmsLayoutShadcn inputModels={modelData} />
|
|
158
|
+
</div>
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export default App
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## 📚 组件 API
|
|
166
|
+
|
|
167
|
+
### CmsLayoutShadcn
|
|
168
|
+
|
|
169
|
+
主布局组件,包含侧边栏、导航和内容区域。
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
interface CmsLayoutProps {
|
|
173
|
+
inputModels: {
|
|
174
|
+
models: CmsModel[]
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* 是否跳过登录验证(免登录模式)
|
|
178
|
+
* 当设置为 true 时,组件将跳过所有登录检查,直接进入后台配置页面
|
|
179
|
+
* 适用于由使用方统一处理鉴权的场景
|
|
180
|
+
* @default false
|
|
181
|
+
*/
|
|
182
|
+
skipAuth?: boolean
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### Props
|
|
187
|
+
|
|
188
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
189
|
+
|------|------|--------|------|
|
|
190
|
+
| `inputModels` | `{ models: CmsModel[] }` | - | 模型配置对象 |
|
|
191
|
+
| `skipAuth` | `boolean` | `false` | 是否跳过登录验证 |
|
|
192
|
+
|
|
193
|
+
#### 使用示例
|
|
194
|
+
|
|
195
|
+
```jsx
|
|
196
|
+
// 标准模式(需要登录)
|
|
197
|
+
<CmsLayoutShadcn inputModels={modelData} />
|
|
198
|
+
|
|
199
|
+
// 免登录模式(适用于已有鉴权系统)
|
|
200
|
+
<CmsLayoutShadcn inputModels={modelData} skipAuth={true} />
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### setSessionId
|
|
204
|
+
|
|
205
|
+
设置会话 ID,用于多租户数据隔离。
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
import { setSessionId } from '@wecode-team/we0-cms'
|
|
209
|
+
|
|
210
|
+
// 设置 Session ID
|
|
211
|
+
setSessionId('tenant-123')
|
|
212
|
+
|
|
213
|
+
// 后续 API 调用会自动添加前缀
|
|
214
|
+
// 例如:GET /data/users → GET /data/tenant-123_users
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## 🏗️ 模型配置
|
|
218
|
+
|
|
219
|
+
### CmsModel 结构
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
interface CmsModel {
|
|
223
|
+
id: number
|
|
224
|
+
name: string // 模型显示名称
|
|
225
|
+
table_name: string // 数据表名
|
|
226
|
+
json_schema: {
|
|
227
|
+
fields: SchemaField[]
|
|
228
|
+
}
|
|
229
|
+
created_at: string
|
|
230
|
+
updated_at: string
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### SchemaField 结构
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
interface SchemaField {
|
|
238
|
+
name: string // 字段名
|
|
239
|
+
type: string // 字段类型
|
|
240
|
+
comment?: string // 字段显示名称
|
|
241
|
+
required?: boolean // 是否必填
|
|
242
|
+
unique?: boolean // 是否唯一
|
|
243
|
+
maxLength?: number // 最大长度
|
|
244
|
+
defaultValue?: any // 默认值
|
|
245
|
+
relation?: RelationConfig // 关联配置
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### 字段类型
|
|
250
|
+
|
|
251
|
+
| 类型 | 渲染组件 | 说明 |
|
|
252
|
+
|------|----------|------|
|
|
253
|
+
| `string` | `<Input />` | 单行文本输入 |
|
|
254
|
+
| `text` | `<Textarea />` | 多行文本输入 |
|
|
255
|
+
| `integer` | `<Input type="number" />` | 整数输入 |
|
|
256
|
+
| `float` | `<Input type="number" />` | 浮点数输入 |
|
|
257
|
+
| `boolean` | `<Switch />` | 开关切换 |
|
|
258
|
+
| `date` | `<Input type="date" />` | 日期选择 |
|
|
259
|
+
| `datetime` | `<Input type="datetime-local" />` | 日期时间选择 |
|
|
260
|
+
| `email` | `<Input type="email" />` | 邮箱输入 |
|
|
261
|
+
| `relation` | `<Select />` | 关联下拉选择 |
|
|
262
|
+
|
|
263
|
+
### 关联字段配置
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
interface RelationConfig {
|
|
267
|
+
type: 'belongsTo' | 'hasMany' | 'belongsToMany'
|
|
268
|
+
target: string // 目标表名
|
|
269
|
+
foreignKey?: string // 外键字段名
|
|
270
|
+
displayField?: string // 下拉显示的字段
|
|
271
|
+
showInList?: boolean // 是否在列表中显示
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
#### 示例:文章关联作者
|
|
276
|
+
|
|
277
|
+
```javascript
|
|
278
|
+
{
|
|
279
|
+
name: 'author',
|
|
280
|
+
type: 'relation',
|
|
281
|
+
comment: '作者',
|
|
282
|
+
required: true,
|
|
283
|
+
relation: {
|
|
284
|
+
type: 'belongsTo',
|
|
285
|
+
target: 'users', // 关联 users 表
|
|
286
|
+
foreignKey: 'author_id', // 外键字段
|
|
287
|
+
displayField: 'name' // 下拉显示用户姓名
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## 📡 API 配置
|
|
293
|
+
|
|
294
|
+
### 配置 API 基础 URL
|
|
295
|
+
|
|
296
|
+
在 `request.ts` 中配置:
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
// 创建自定义 request 实例
|
|
300
|
+
import axios from 'axios'
|
|
301
|
+
|
|
302
|
+
const request = axios.create({
|
|
303
|
+
baseURL: 'http://your-api-url/api/cms',
|
|
304
|
+
timeout: 10000
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
// 请求拦截器 - 自动添加 Token
|
|
308
|
+
request.interceptors.request.use((config) => {
|
|
309
|
+
const token = localStorage.getItem('cms_token')
|
|
310
|
+
if (token) {
|
|
311
|
+
config.headers.Authorization = `Bearer ${token}`
|
|
312
|
+
}
|
|
313
|
+
return config
|
|
314
|
+
})
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### API 服务
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
import { modelApi, dataApi, authApi } from '@wecode-team/we0-cms'
|
|
321
|
+
|
|
322
|
+
// 模型 API
|
|
323
|
+
await modelApi.getModels()
|
|
324
|
+
await modelApi.createModel(data)
|
|
325
|
+
await modelApi.updateModel(data)
|
|
326
|
+
await modelApi.deleteModel(id)
|
|
327
|
+
|
|
328
|
+
// 数据 API
|
|
329
|
+
await dataApi.getTableData('users', { page: 1, limit: 10 })
|
|
330
|
+
await dataApi.createData('users', { name: 'John' })
|
|
331
|
+
await dataApi.updateData('users', { id: '1', name: 'Jane' })
|
|
332
|
+
await dataApi.deleteData('users', '1')
|
|
333
|
+
|
|
334
|
+
// 关联选项 API
|
|
335
|
+
await dataApi.getRelationOptions('users', { displayField: 'name' })
|
|
336
|
+
|
|
337
|
+
// 认证 API
|
|
338
|
+
await authApi.login({ username: 'admin', password: '123456' })
|
|
339
|
+
await authApi.verifyAuth()
|
|
340
|
+
await authApi.getCurrentUser()
|
|
341
|
+
await authApi.logout()
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## 🎨 自定义样式
|
|
345
|
+
|
|
346
|
+
### 覆盖 CSS 变量
|
|
347
|
+
|
|
348
|
+
```css
|
|
349
|
+
:root {
|
|
350
|
+
--background: 0 0% 100%;
|
|
351
|
+
--foreground: 222.2 84% 4.9%;
|
|
352
|
+
--primary: 222.2 47.4% 11.2%;
|
|
353
|
+
--primary-foreground: 210 40% 98%;
|
|
354
|
+
/* ... 更多变量 */
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.dark {
|
|
358
|
+
--background: 222.2 84% 4.9%;
|
|
359
|
+
--foreground: 210 40% 98%;
|
|
360
|
+
/* ... 暗色模式变量 */
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### 扩展组件
|
|
365
|
+
|
|
366
|
+
```jsx
|
|
367
|
+
import { CmsLayoutShadcn } from '@wecode-team/we0-cms'
|
|
368
|
+
|
|
369
|
+
function CustomCms() {
|
|
370
|
+
return (
|
|
371
|
+
<div className="custom-wrapper">
|
|
372
|
+
<header className="custom-header">
|
|
373
|
+
<h1>我的 CMS</h1>
|
|
374
|
+
</header>
|
|
375
|
+
<CmsLayoutShadcn inputModels={modelData} skipAuth={true} />
|
|
376
|
+
</div>
|
|
377
|
+
)
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## 🔐 认证流程
|
|
382
|
+
|
|
383
|
+
### 1. 登录流程
|
|
384
|
+
|
|
385
|
+
```
|
|
386
|
+
用户输入账号密码 → 调用 authApi.login() → 保存 Token 到 localStorage
|
|
387
|
+
↓
|
|
388
|
+
跳转到数据管理页
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### 2. Token 验证
|
|
392
|
+
|
|
393
|
+
```
|
|
394
|
+
页面加载 → 检查 localStorage 中的 Token → 调用 authApi.verifyAuth()
|
|
395
|
+
↓
|
|
396
|
+
验证成功:显示内容
|
|
397
|
+
验证失败:跳转登录页
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### 3. 免登录模式
|
|
401
|
+
|
|
402
|
+
```jsx
|
|
403
|
+
// 适用于已有鉴权系统的场景
|
|
404
|
+
<CmsLayoutShadcn inputModels={modelData} skipAuth={true} />
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## 📱 响应式设计
|
|
408
|
+
|
|
409
|
+
组件内置响应式支持:
|
|
410
|
+
|
|
411
|
+
- **桌面端**:完整侧边栏 + 内容区域
|
|
412
|
+
- **平板端**:可折叠侧边栏
|
|
413
|
+
- **移动端**:底部抽屉式导航
|
|
414
|
+
|
|
415
|
+
## 🐛 常见问题
|
|
416
|
+
|
|
417
|
+
### 1. 样式不生效
|
|
418
|
+
|
|
419
|
+
确保引入了 CSS 文件:
|
|
420
|
+
```jsx
|
|
421
|
+
import '@wecode-team/we0-cms/dist/index.css'
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### 2. Tailwind 类名冲突
|
|
425
|
+
|
|
426
|
+
确保 Tailwind 配置包含了组件库的路径:
|
|
427
|
+
```javascript
|
|
428
|
+
content: [
|
|
429
|
+
'./node_modules/@wecode-team/we0-cms/dist/**/*.{js,jsx}'
|
|
430
|
+
]
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### 3. 关联字段不显示数据
|
|
434
|
+
|
|
435
|
+
检查模型配置中的 `relation.displayField` 是否正确设置为目标表中存在的字段。
|
|
436
|
+
|
|
437
|
+
### 4. API 请求失败
|
|
438
|
+
|
|
439
|
+
检查:
|
|
440
|
+
1. Session ID 是否正确设置
|
|
441
|
+
2. API 基础 URL 是否正确
|
|
442
|
+
3. CORS 是否配置正确
|
|
443
|
+
|
|
444
|
+
## 📄 许可证
|
|
445
|
+
|
|
446
|
+
MIT
|
|
447
|
+
|
|
448
|
+
## 🔗 相关包
|
|
449
|
+
|
|
450
|
+
- [@wecode-team/cms-supabase-api](../cms-supabase-api) - 后端 API 包
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type MessageType = "info" | "success" | "error" | "warning" | "loading";
|
|
3
|
+
export interface MessageArgs {
|
|
4
|
+
key?: React.Key;
|
|
5
|
+
content: React.ReactNode;
|
|
6
|
+
duration?: number;
|
|
7
|
+
type?: MessageType;
|
|
8
|
+
onClose?: () => void;
|
|
9
|
+
icon?: React.ReactNode;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
interface GlobalConfig {
|
|
13
|
+
top: number;
|
|
14
|
+
duration: number;
|
|
15
|
+
maxCount: number;
|
|
16
|
+
}
|
|
17
|
+
type OpenArg = MessageArgs | React.ReactNode;
|
|
18
|
+
export interface MessageAPI {
|
|
19
|
+
open: (args: MessageArgs) => {
|
|
20
|
+
close: () => void;
|
|
21
|
+
};
|
|
22
|
+
info: (arg1: OpenArg, duration?: number, onClose?: () => void) => {
|
|
23
|
+
close: () => void;
|
|
24
|
+
};
|
|
25
|
+
success: (arg1: OpenArg, duration?: number, onClose?: () => void) => {
|
|
26
|
+
close: () => void;
|
|
27
|
+
};
|
|
28
|
+
error: (arg1: OpenArg, duration?: number, onClose?: () => void) => {
|
|
29
|
+
close: () => void;
|
|
30
|
+
};
|
|
31
|
+
warning: (arg1: OpenArg, duration?: number, onClose?: () => void) => {
|
|
32
|
+
close: () => void;
|
|
33
|
+
};
|
|
34
|
+
loading: (arg1: OpenArg, duration?: number, onClose?: () => void) => {
|
|
35
|
+
close: () => void;
|
|
36
|
+
};
|
|
37
|
+
destroy: (key?: React.Key) => void;
|
|
38
|
+
config: (cfg: Partial<GlobalConfig>) => void;
|
|
39
|
+
useMessage: () => [MessageAPI, React.ReactElement];
|
|
40
|
+
}
|
|
41
|
+
export declare const message: MessageAPI;
|
|
42
|
+
export default message;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export declare function JsonViewerCell({ value, title }: {
|
|
3
|
+
value: any;
|
|
4
|
+
title?: string;
|
|
5
|
+
}): React.JSX.Element;
|
|
6
|
+
export declare function JsonEditorField(props: {
|
|
7
|
+
id: string;
|
|
8
|
+
value: string;
|
|
9
|
+
required?: boolean;
|
|
10
|
+
onChange: (next: string) => void;
|
|
11
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type ParsedJsonResult = {
|
|
2
|
+
ok: true;
|
|
3
|
+
value: any;
|
|
4
|
+
pretty: string;
|
|
5
|
+
compact: string;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
message: string;
|
|
9
|
+
pretty: string;
|
|
10
|
+
compact: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function parseJsonLike(textOrValue: any): ParsedJsonResult;
|
|
13
|
+
export declare function formatJsonText(text: string, indentSize?: number): {
|
|
14
|
+
ok: true;
|
|
15
|
+
formatted: string;
|
|
16
|
+
} | {
|
|
17
|
+
ok: false;
|
|
18
|
+
message: string;
|
|
19
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { CmsModel } from "./types";
|
|
3
|
+
interface CmsLayoutProps {
|
|
4
|
+
inputModels: {
|
|
5
|
+
models: CmsModel[];
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* 是否跳过登录验证(免登录模式)
|
|
9
|
+
* 当设置为 true 时,组件将跳过所有登录检查,直接进入后台配置页面
|
|
10
|
+
* 适用于由使用方统一处理鉴权的场景
|
|
11
|
+
* @default false
|
|
12
|
+
*/
|
|
13
|
+
skipAuth?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export default function CmsLayoutShadcn({ inputModels, skipAuth }: CmsLayoutProps): React.JSX.Element;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { CmsModel } from "./types";
|
|
3
|
+
export interface RouteConfig {
|
|
4
|
+
path: string;
|
|
5
|
+
name: string;
|
|
6
|
+
icon: React.ReactNode;
|
|
7
|
+
component: React.ComponentType;
|
|
8
|
+
routes?: RouteConfig[];
|
|
9
|
+
}
|
|
10
|
+
export declare const cmsRoutes: (inputModels: {
|
|
11
|
+
models: CmsModel[];
|
|
12
|
+
}) => RouteConfig[];
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export type RelationType = 'belongsTo' | 'hasMany' | 'belongsToMany';
|
|
2
|
+
export interface RelationConfig {
|
|
3
|
+
/**
|
|
4
|
+
* 关系类型
|
|
5
|
+
* - belongsTo: 多对一,当前表存储外键 (如:文章表的 author_id 关联作者表)
|
|
6
|
+
* - hasMany: 一对多,目标表存储外键 (如:作者表关联多篇文章)
|
|
7
|
+
* - belongsToMany: 多对多,需要中间表 (如:文章和标签)
|
|
8
|
+
*/
|
|
9
|
+
type: RelationType;
|
|
10
|
+
/**
|
|
11
|
+
* 目标表名
|
|
12
|
+
*/
|
|
13
|
+
target: string;
|
|
14
|
+
/**
|
|
15
|
+
* 外键字段名
|
|
16
|
+
* - belongsTo: 当前表中的外键字段,默认为 ${target}_id
|
|
17
|
+
* - hasMany: 目标表中的外键字段,默认为 ${currentTable}_id
|
|
18
|
+
*/
|
|
19
|
+
foreignKey?: string;
|
|
20
|
+
/**
|
|
21
|
+
* 目标表的主键字段,默认为 'id'
|
|
22
|
+
*/
|
|
23
|
+
targetKey?: string;
|
|
24
|
+
/**
|
|
25
|
+
* 多对多关系的中间表名 (仅 belongsToMany 需要)
|
|
26
|
+
*/
|
|
27
|
+
through?: string;
|
|
28
|
+
/**
|
|
29
|
+
* 用于下拉选项显示的字段名,默认为 'name' 或 'title'
|
|
30
|
+
*/
|
|
31
|
+
displayField?: string;
|
|
32
|
+
/**
|
|
33
|
+
* 是否在列表中显示关联数据
|
|
34
|
+
*/
|
|
35
|
+
showInList?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface RelationOption {
|
|
38
|
+
id: number | string;
|
|
39
|
+
label: string;
|
|
40
|
+
[key: string]: any;
|
|
41
|
+
}
|
|
42
|
+
export interface SchemaField {
|
|
43
|
+
name: string;
|
|
44
|
+
/**
|
|
45
|
+
* 字段类型
|
|
46
|
+
* - 基础类型: string, text, integer, float, boolean, date, datetime, json, email
|
|
47
|
+
* - 关系类型: relation (需要配置 relation 属性)
|
|
48
|
+
*/
|
|
49
|
+
type: string;
|
|
50
|
+
required?: boolean;
|
|
51
|
+
unique?: boolean;
|
|
52
|
+
maxLength?: number;
|
|
53
|
+
defaultValue?: any;
|
|
54
|
+
comment?: string;
|
|
55
|
+
/**
|
|
56
|
+
* 关系配置 (当 type 为 'relation' 时必填)
|
|
57
|
+
*/
|
|
58
|
+
relation?: RelationConfig;
|
|
59
|
+
}
|
|
60
|
+
export interface CmsModel {
|
|
61
|
+
id: number;
|
|
62
|
+
name: string;
|
|
63
|
+
table_name: string;
|
|
64
|
+
json_schema: {
|
|
65
|
+
fields: SchemaField[];
|
|
66
|
+
};
|
|
67
|
+
created_at: string;
|
|
68
|
+
updated_at: string;
|
|
69
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type VariantProps } from "class-variance-authority";
|
|
3
|
+
declare const badgeVariants: (props?: ({
|
|
4
|
+
variant?: "default" | "destructive" | "outline" | "secondary" | null | undefined;
|
|
5
|
+
} & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
|
|
6
|
+
export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
|
|
7
|
+
}
|
|
8
|
+
declare function Badge({ className, variant, ...props }: BadgeProps): React.JSX.Element;
|
|
9
|
+
export { Badge, badgeVariants };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type VariantProps } from "class-variance-authority";
|
|
3
|
+
declare const buttonVariants: (props?: ({
|
|
4
|
+
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | null | undefined;
|
|
5
|
+
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
6
|
+
} & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
|
|
7
|
+
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
8
|
+
asChild?: boolean;
|
|
9
|
+
}
|
|
10
|
+
declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
11
|
+
export { Button, buttonVariants };
|