crud-page-react 0.1.1 → 0.1.3

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 CHANGED
@@ -4,40 +4,21 @@
4
4
 
5
5
  ## 特性
6
6
 
7
- - 🚀 **零代码配置** - 通过 JSON Schema 配置即可生成完整 CRUD 界面
8
- - 📝 **丰富的表单组件** - 支持 15+ 种表单控件类型
9
- - 🔍 **智能筛选** - 自动生成筛选器,支持范围查询
10
- - 📊 **灵活表格** - 可配置列宽、排序、固定列等
11
- - 🎨 **Raw JSON 模式** - 支持原始 JSON 编辑和查看
12
- - 🔌 **自定义 API** - 支持自定义 API 请求函数和动态 URL 参数
13
- - 🌐 **动态 URL 参数** - 支持任意字段作为 URL 参数 (`:id`, `:orderNo`, `:customerName` 等)
14
- - ⚡ **自定义操作** - 支持配置自定义按钮操作和 API 调用
15
- - 📱 **响应式设计** - 适配移动端和桌面端
16
- - 🎯 **TypeScript 支持** - 完整的类型定义
7
+ - 🚀 **零代码配置** - 通过 JSON Schema 快速生成 CRUD 界面
8
+ - 🎨 **美观易用** - 基于 Ant Design 设计语言
9
+ - 🔧 **高度可定制** - 支持自定义 API、字段配置、操作按钮
10
+ - 📱 **响应式设计** - 自适应各种屏幕尺寸
11
+ - 🔒 **类型安全** - 完整的 TypeScript 类型定义
12
+ - 🎯 **操作分组** - 自定义操作自动折叠到下拉菜单
17
13
 
18
14
  ## 安装
19
15
 
20
16
  ```bash
21
17
  npm install crud-page-react
22
- # 或
23
- yarn add crud-page-react
24
- ```
25
-
26
- ### 依赖要求
27
-
28
- ```json
29
- {
30
- "react": ">=16.8.0",
31
- "react-dom": ">=16.8.0",
32
- "antd": ">=5.0.0",
33
- "dayjs": ">=1.11.0"
34
- }
35
18
  ```
36
19
 
37
20
  ## 快速开始
38
21
 
39
- ### 基础用法
40
-
41
22
  ```tsx
42
23
  import React from 'react';
43
24
  import { CrudPage } from 'crud-page-react';
@@ -47,22 +28,10 @@ const schema: CrudPageSchema = {
47
28
  id: 'users',
48
29
  title: '用户管理',
49
30
  api: {
50
- list: {
51
- url: '/api/users',
52
- method: 'GET'
53
- },
54
- create: {
55
- url: '/api/users',
56
- method: 'POST'
57
- },
58
- update: {
59
- url: '/api/users/:id',
60
- method: 'PUT'
61
- },
62
- delete: {
63
- url: '/api/users/:id',
64
- method: 'DELETE'
65
- },
31
+ list: { url: '/api/users', method: 'GET' },
32
+ create: { url: '/api/users', method: 'POST' },
33
+ update: { url: '/api/users/:id', method: 'PUT' },
34
+ delete: { url: '/api/users/:id', method: 'DELETE' },
66
35
  },
67
36
  fields: [
68
37
  {
@@ -87,260 +56,50 @@ const schema: CrudPageSchema = {
87
56
  filter: true,
88
57
  table: true,
89
58
  form: { required: true },
90
- rules: ['email'],
91
- },
92
- {
93
- key: 'status',
94
- label: '状态',
95
- type: 'string',
96
- widget: 'select',
97
- config: {
98
- options: [
99
- { label: '启用', value: 'active' },
100
- { label: '禁用', value: 'inactive' },
101
- ],
102
- },
103
- filter: true,
104
- table: true,
105
- form: true,
106
59
  },
107
60
  ],
108
61
  rowKey: 'id',
109
62
  };
110
63
 
111
64
  function App() {
112
- return (
113
- <div>
114
- <CrudPage schema={schema} />
115
- </div>
116
- );
65
+ return <CrudPage schema={schema} />;
117
66
  }
118
-
119
- export default App;
120
67
  ```
121
68
 
122
- ### 扩展 API 配置
69
+ ## 新特性:操作按钮分组 (v0.1.3)
123
70
 
124
- v0.1.0+ 版本要求使用完整的 API 配置对象,支持自定义 HTTP 方法、请求头、请求体数据和模板变量:
71
+ 自定义操作现在会自动折叠到"其它操作"下拉菜单中,保持界面整洁:
125
72
 
126
73
  ```tsx
127
- const advancedSchema: CrudPageSchema = {
128
- id: 'orders',
129
- title: '订单管理',
130
- api: {
131
- // 基础配置
132
- list: {
133
- url: '/api/orders',
134
- method: 'GET'
135
- },
136
-
137
- // 扩展配置
138
- create: {
139
- url: '/api/orders',
140
- method: 'POST',
141
- headers: {
142
- 'X-Request-Source': 'admin-panel'
143
- },
144
- data: {
145
- source: 'web',
146
- createdBy: '{{currentUser}}',
147
- timestamp: '{{timestamp}}'
148
- }
149
- },
150
-
151
- update: {
152
- url: '/api/orders/:id',
153
- method: 'PUT',
154
- data: {
155
- updatedBy: '{{currentUser}}',
156
- updateTime: '{{timestamp}}'
157
- }
158
- },
159
-
160
- delete: {
161
- url: '/api/orders/:orderNo',
162
- method: 'DELETE',
163
- data: {
164
- deletedBy: '{{currentUser}}',
165
- deleteReason: '{{deleteReason}}',
166
- timestamp: '{{timestamp}}'
167
- }
168
- }
169
- },
74
+ const schema: CrudPageSchema = {
170
75
  // ... 其他配置
171
- };
172
- ```
173
-
174
- ### 支持的配置选项
175
-
176
- - **HTTP 方法**:GET、POST、PUT、PATCH、DELETE
177
- - **自定义请求头**:添加认证、来源标识等
178
- - **请求体数据**:固定参数和动态参数
179
- - **模板变量**:`{{fieldName}}` 格式的动态值替换
180
- - **URL 占位符**:`:fieldName` 格式的动态 URL 构建
181
-
182
- ### 💥 Breaking Changes (v0.1.0)
183
-
184
- v0.1.0 版本移除了简单字符串配置支持,所有 API 配置必须使用对象格式:
185
-
186
- ```tsx
187
- // ❌ 不再支持 (v0.0.x)
188
- api: {
189
- list: '/api/users',
190
- create: '/api/users'
191
- }
192
-
193
- // ✅ 必须使用 (v0.1.0+)
194
- api: {
195
- list: {
196
- url: '/api/users',
197
- method: 'GET'
198
- },
199
- create: {
200
- url: '/api/users',
201
- method: 'POST'
202
- }
203
- }
204
- ```
205
-
206
- ## 动态 URL 参数
207
-
208
- 支持在 API 配置中使用任意字段作为 URL 参数:
209
-
210
- ```tsx
211
- const schema = {
212
- api: {
213
- list: '/api/orders',
214
- create: '/api/orders',
215
- update: '/api/orders/:id', // 使用 id 字段
216
- delete: '/api/orders/:orderNo', // 使用 orderNo 字段
217
- detail: '/api/orders/:id',
218
- },
219
76
  actions: [
220
- {
221
- key: 'track',
222
- label: '物流跟踪',
223
- type: 'custom',
224
- api: {
225
- url: '/api/tracking/:orderNo', // 使用 orderNo 字段
226
- method: 'GET',
227
- responseType: 'json'
228
- }
229
- },
230
- {
231
- key: 'notify',
232
- label: '通知客户',
233
- type: 'custom',
234
- api: {
235
- url: '/api/notify/:customerName', // 使用 customerName 字段
236
- method: 'POST',
237
- data: {
238
- message: '您的订单状态已更新'
239
- }
240
- }
241
- }
77
+ // 基础操作 - 显示为独立按钮
78
+ { key: 'view', label: '查看', type: 'view' },
79
+ { key: 'edit', label: '编辑', type: 'edit' },
80
+ { key: 'delete', label: '删除', type: 'delete' },
81
+
82
+ // 自定义操作 - 自动折叠到下拉菜单
83
+ { key: 'approve', label: '审批', type: 'custom', apiKey: 'approve' },
84
+ { key: 'reject', label: '拒绝', type: 'custom', apiKey: 'reject', danger: true },
85
+ { key: 'export', label: '导出', type: 'custom', apiKey: 'export' },
242
86
  ],
243
- // ... 其他配置
244
87
  };
245
88
  ```
246
89
 
247
- ### 支持的占位符格式
248
- - `:id` → 记录中的 `id` 字段值
249
- - `:orderNo` → 记录中的 `orderNo` 字段值
250
- - `:customerName` → 记录中的 `customerName` 字段值
251
- - 任意 `:fieldName` → 记录中的 `fieldName` 字段值
90
+ ### 操作类型
252
91
 
253
- ## 自定义 API 请求
92
+ - **基础操作** (`view`, `edit`, `delete`) - 显示为独立的图标按钮
93
+ - **自定义操作** (`custom`) - 折叠到"其它操作"下拉菜单中
94
+ - **复制功能** - 始终在下拉菜单中提供"复制 JSON"功能
254
95
 
255
- ```tsx
256
- import { CrudPage, ApiRequest } from 'crud-page-react';
96
+ ## 文档
257
97
 
258
- // 自定义 API 请求函数
259
- const customApiRequest: ApiRequest = async (url, options) => {
260
- const token = localStorage.getItem('token');
261
- const response = await fetch(url, {
262
- headers: {
263
- 'Content-Type': 'application/json',
264
- 'Authorization': token ? `Bearer ${token}` : '',
265
- ...options?.headers,
266
- },
267
- ...options,
268
- });
269
-
270
- if (!response.ok) {
271
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
272
- }
273
-
274
- return response.json();
275
- };
276
-
277
- // 使用自定义 API 请求
278
- <CrudPage
279
- schema={schema}
280
- apiRequest={customApiRequest}
281
- />
282
- ```
98
+ 更多详细文档和示例,请查看 [example.md](./example.md)
283
99
 
284
- ## API 文档
100
+ ## 更新日志
285
101
 
286
- ### CrudPageProps
287
-
288
- | 属性 | 类型 | 必填 | 默认值 | 说明 |
289
- |------|------|------|--------|------|
290
- | schema | CrudPageSchema | ✓ | - | 页面配置 schema |
291
- | initialData | Record<string, unknown>[] | ✗ | [] | 初始数据,API 失败时作为降级数据 |
292
- | apiRequest | ApiRequest | ✗ | 内置 fetch | 自定义 API 请求函数 |
293
-
294
- ### ApiRequest
295
-
296
- ```typescript
297
- interface ApiRequest {
298
- <T = unknown>(url: string, options?: RequestInit): Promise<T>;
299
- }
300
- ```
301
-
302
- ### CrudPageSchema
303
-
304
- 完整的 schema 配置请参考类型定义文件。
305
-
306
- ## 字段类型支持
307
-
308
- - `string` - 字符串
309
- - `number` - 数字
310
- - `boolean` - 布尔值
311
- - `date` - 日期
312
- - `datetime` - 日期时间
313
- - `array` - 数组
314
- - `objectArray` - 对象数组
315
-
316
- ## 组件类型支持
317
-
318
- - `input` - 输入框
319
- - `textarea` - 文本域
320
- - `inputNumber` - 数字输入
321
- - `select` - 下拉选择
322
- - `multiselect` - 多选下拉
323
- - `radio` - 单选按钮
324
- - `checkbox` - 复选框
325
- - `switch` - 开关
326
- - `datePicker` - 日期选择
327
- - `rangePicker` - 日期范围
328
- - `timePicker` - 时间选择
329
- - `editableTable` - 可编辑表格
330
- - `jsonInput` - JSON 编辑器
331
-
332
- ## 开发
333
-
334
- ```bash
335
- # 安装依赖
336
- npm install
337
-
338
- # 开发模式
339
- npm run dev
340
-
341
- # 构建
342
- npm run build
343
- ```
102
+ 查看 [CHANGELOG.md](./CHANGELOG.md) 了解版本更新内容。
344
103
 
345
104
  ## 许可证
346
105
 
package/dist/index.esm.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { useState, useEffect, useRef, useCallback } from 'react';
3
- import { DatePicker, Form, Row, Col, Space, Button, Input, Switch, Radio, Select, InputNumber, Tooltip, Popconfirm, Table, message, Tag, Modal, Divider, Card, Checkbox, Typography } from 'antd';
4
- import { SearchOutlined, ReloadOutlined, EyeOutlined, EditOutlined, DeleteOutlined, CopyOutlined, FormOutlined, CodeOutlined, PlusOutlined } from '@ant-design/icons';
3
+ import { DatePicker, Form, Row, Col, Space, Button, Input, Switch, Radio, Select, InputNumber, Tooltip, Popconfirm, Dropdown, Table, message, Tag, Modal, Divider, Card, Checkbox, Typography } from 'antd';
4
+ import { SearchOutlined, ReloadOutlined, CopyOutlined, EyeOutlined, EditOutlined, DeleteOutlined, MoreOutlined, FormOutlined, CodeOutlined, PlusOutlined } from '@ant-design/icons';
5
5
  import dayjs from 'dayjs';
6
6
  import customParseFormat from 'dayjs/plugin/customParseFormat';
7
7
 
@@ -213,7 +213,7 @@ function renderCell(field, value) {
213
213
  return jsx("span", { children: String(value) });
214
214
  }
215
215
  function DynamicTable({ schema, data, loading, pagination, onView, onEdit, onDelete, onCustomAction, }) {
216
- var _a, _b, _c, _d, _e, _f;
216
+ var _a, _b, _c, _d;
217
217
  const tableFields = schema.fields.filter((f) => f.table !== false && f.table !== undefined);
218
218
  const columns = tableFields.map((field) => {
219
219
  const cfg = getTableConfig(field);
@@ -244,10 +244,32 @@ function DynamicTable({ schema, data, loading, pagination, onView, onEdit, onDel
244
244
  title: '操作',
245
245
  key: '__actions',
246
246
  fixed: 'right',
247
- width: ((_b = (_a = schema.actions) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) * 60 + 60,
247
+ width: 200, // 固定宽度,因为现在有下拉菜单
248
248
  render: (_, record) => {
249
249
  var _a;
250
- return (jsxs(Space, { size: 4, children: [((_a = schema.actions) !== null && _a !== void 0 ? _a : []).map((action) => {
250
+ const actions = (_a = schema.actions) !== null && _a !== void 0 ? _a : [];
251
+ // 分离基础操作和自定义操作
252
+ const basicActions = actions.filter(action => action.type === 'view' || action.type === 'edit' || action.type === 'delete');
253
+ const customActions = actions.filter(action => action.type === 'custom');
254
+ // 构建下拉菜单项
255
+ const dropdownItems = [
256
+ ...customActions.map(action => ({
257
+ key: action.key,
258
+ label: action.label,
259
+ onClick: () => onCustomAction === null || onCustomAction === void 0 ? void 0 : onCustomAction(action.key, record),
260
+ danger: action.danger,
261
+ })),
262
+ ...(customActions.length > 0 ? [{
263
+ type: 'divider',
264
+ }] : []),
265
+ {
266
+ key: 'copy-json',
267
+ label: '复制 JSON',
268
+ icon: jsx(CopyOutlined, {}),
269
+ onClick: () => copyJson(record),
270
+ }
271
+ ];
272
+ return (jsxs(Space, { size: 4, children: [basicActions.map((action) => {
251
273
  var _a, _b, _c;
252
274
  if (action.type === 'view') {
253
275
  return (jsx(Tooltip, { title: action.label, children: jsx(Button, { type: "link", size: "small", icon: jsx(EyeOutlined, {}), onClick: () => onView(record) }) }, action.key));
@@ -258,20 +280,20 @@ function DynamicTable({ schema, data, loading, pagination, onView, onEdit, onDel
258
280
  if (action.type === 'delete') {
259
281
  return (jsx(Popconfirm, { title: (_b = (_a = action.confirm) === null || _a === void 0 ? void 0 : _a.title) !== null && _b !== void 0 ? _b : '确定删除?', description: (_c = action.confirm) === null || _c === void 0 ? void 0 : _c.content, onConfirm: () => onDelete(record), okText: "\u786E\u5B9A", cancelText: "\u53D6\u6D88", okButtonProps: { danger: true }, children: jsx(Tooltip, { title: action.label, children: jsx(Button, { type: "link", size: "small", danger: true, icon: jsx(DeleteOutlined, {}) }) }) }, action.key));
260
282
  }
261
- return (jsx(Tooltip, { title: action.label, children: jsx(Button, { type: "link", size: "small", onClick: () => onCustomAction === null || onCustomAction === void 0 ? void 0 : onCustomAction(action.key, record), children: action.label }) }, action.key));
262
- }), jsx(Tooltip, { title: "\u590D\u5236 JSON", children: jsx(Button, { type: "link", size: "small", icon: jsx(CopyOutlined, {}), onClick: () => copyJson(record) }) })] }));
283
+ return null;
284
+ }), jsx(Dropdown, { menu: { items: dropdownItems }, trigger: ['click'], placement: "bottomRight", children: jsx(Tooltip, { title: "\u5176\u5B83\u64CD\u4F5C", children: jsx(Button, { type: "link", size: "small", icon: jsx(MoreOutlined, {}) }) }) })] }));
263
285
  },
264
286
  });
265
- return (jsx(Table, { rowKey: (_c = schema.rowKey) !== null && _c !== void 0 ? _c : 'id', columns: columns, dataSource: data, loading: loading, scroll: { x: 'max-content' }, pagination: {
287
+ return (jsx(Table, { rowKey: (_a = schema.rowKey) !== null && _a !== void 0 ? _a : 'id', columns: columns, dataSource: data, loading: loading, scroll: { x: 'max-content' }, pagination: {
266
288
  current: pagination.current,
267
289
  pageSize: pagination.pageSize,
268
290
  total: pagination.total,
269
291
  showSizeChanger: true,
270
- showTotal: ((_d = schema.pagination) === null || _d === void 0 ? void 0 : _d.showTotal)
292
+ showTotal: ((_b = schema.pagination) === null || _b === void 0 ? void 0 : _b.showTotal)
271
293
  ? (total) => `共 ${total} 条`
272
294
  : undefined,
273
295
  onChange: pagination.onChange,
274
- pageSizeOptions: (_f = (_e = schema.pagination) === null || _e === void 0 ? void 0 : _e.pageSizeOptions) !== null && _f !== void 0 ? _f : [10, 20, 50],
296
+ pageSizeOptions: (_d = (_c = schema.pagination) === null || _c === void 0 ? void 0 : _c.pageSizeOptions) !== null && _d !== void 0 ? _d : [10, 20, 50],
275
297
  } }));
276
298
  }
277
299