crud-page-react 0.1.5 → 0.2.1
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 +33 -0
- package/dist/components/CrudPage.d.ts +3 -0
- package/dist/index.d.ts +23 -1
- package/dist/index.esm.js +62 -16
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +64 -15
- package/dist/index.js.map +1 -1
- package/dist/types/schema.d.ts +31 -1
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -66,6 +66,39 @@ function App() {
|
|
|
66
66
|
}
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
+
## 国际化支持
|
|
70
|
+
|
|
71
|
+
组件库支持 Ant Design 的国际化配置,默认使用中文。
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
import React from 'react';
|
|
75
|
+
import { CrudPage, enUS, zhCN } from 'crud-page-react';
|
|
76
|
+
|
|
77
|
+
// 使用英文
|
|
78
|
+
function EnglishApp() {
|
|
79
|
+
return <CrudPage schema={schema} locale={enUS} />;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 使用中文(默认)
|
|
83
|
+
function ChineseApp() {
|
|
84
|
+
return <CrudPage schema={schema} locale={zhCN} />;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 不指定 locale 时默认使用中文
|
|
88
|
+
function DefaultApp() {
|
|
89
|
+
return <CrudPage schema={schema} />;
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 支持的语言
|
|
94
|
+
|
|
95
|
+
- `zhCN` - 简体中文(默认)
|
|
96
|
+
- `enUS` - 英文
|
|
97
|
+
- `jaJP` - 日文
|
|
98
|
+
- `koKR` - 韩文
|
|
99
|
+
|
|
100
|
+
更多语言包请参考 [Ant Design 国际化文档](https://ant.design/docs/react/i18n-cn)。
|
|
101
|
+
|
|
69
102
|
## 新特性:操作按钮分组 (v0.1.3)
|
|
70
103
|
|
|
71
104
|
自定义操作现在会自动折叠到"其它操作"下拉菜单中,保持界面整洁:
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import type { Locale } from 'antd/es/locale';
|
|
2
3
|
import type { CrudPageSchema, ApiRequest } from '../types/schema';
|
|
3
4
|
interface CrudPageProps {
|
|
4
5
|
schema: CrudPageSchema;
|
|
5
6
|
initialData?: Record<string, unknown>[];
|
|
6
7
|
apiRequest?: ApiRequest;
|
|
8
|
+
/** Ant Design 语言配置,默认为中文 */
|
|
9
|
+
locale?: Locale;
|
|
7
10
|
}
|
|
8
11
|
declare const CrudPage: React.FC<CrudPageProps>;
|
|
9
12
|
export default CrudPage;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { Locale } from 'antd/es/locale';
|
|
3
|
+
export { Locale } from 'antd/es/locale';
|
|
2
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
5
|
import { Rule } from 'antd/es/form';
|
|
6
|
+
export { default as zhCN } from 'antd/locale/zh_CN';
|
|
7
|
+
export { default as enUS } from 'antd/locale/en_US';
|
|
8
|
+
export { default as jaJP } from 'antd/locale/ja_JP';
|
|
9
|
+
export { default as koKR } from 'antd/locale/ko_KR';
|
|
4
10
|
|
|
5
11
|
/** API 请求函数类型 */
|
|
6
12
|
interface ApiRequest {
|
|
@@ -144,7 +150,12 @@ interface ActionSchema {
|
|
|
144
150
|
danger?: boolean;
|
|
145
151
|
color?: string;
|
|
146
152
|
permission?: ActionPermission;
|
|
147
|
-
|
|
153
|
+
/**
|
|
154
|
+
* 操作按钮显示条件
|
|
155
|
+
* 支持字段值匹配:{ status: ['active', 'pending'] }
|
|
156
|
+
* 支持嵌套字段:{ 'user.role': ['admin'] }
|
|
157
|
+
*/
|
|
158
|
+
condition?: ActionCondition;
|
|
148
159
|
confirm?: {
|
|
149
160
|
title: string;
|
|
150
161
|
content?: string;
|
|
@@ -203,11 +214,22 @@ declare function getNestedValue(obj: Record<string, unknown>, path: string): unk
|
|
|
203
214
|
* 'name' → null
|
|
204
215
|
*/
|
|
205
216
|
declare function getFieldGroupPrefix(key: string): string | null;
|
|
217
|
+
/**
|
|
218
|
+
* 操作按钮显示条件配置
|
|
219
|
+
* 支持两种格式:
|
|
220
|
+
* 1. 字段值匹配:{ fieldName: [value1, value2] } - 当字段值在数组中时显示
|
|
221
|
+
* 2. 自定义表达式:{ expression: "record.status === 'active'" } - 使用表达式判断
|
|
222
|
+
*/
|
|
223
|
+
interface ActionCondition {
|
|
224
|
+
[fieldName: string]: (string | number | boolean)[];
|
|
225
|
+
}
|
|
206
226
|
|
|
207
227
|
interface CrudPageProps {
|
|
208
228
|
schema: CrudPageSchema;
|
|
209
229
|
initialData?: Record<string, unknown>[];
|
|
210
230
|
apiRequest?: ApiRequest;
|
|
231
|
+
/** Ant Design 语言配置,默认为中文 */
|
|
232
|
+
locale?: Locale;
|
|
211
233
|
}
|
|
212
234
|
declare const CrudPage: React.FC<CrudPageProps>;
|
|
213
235
|
|
package/dist/index.esm.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
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, Dropdown, Table, message, Tag, Modal, Divider, Card, Checkbox, Typography } from 'antd';
|
|
3
|
+
import { DatePicker, Form, Row, Col, Space, Button, Input, Switch, Radio, Select, InputNumber, Tooltip, Popconfirm, Dropdown, Table, message, Tag, Modal, Divider, Card, Checkbox, Typography, ConfigProvider } from 'antd';
|
|
4
4
|
import { SearchOutlined, ReloadOutlined, EyeOutlined, EditOutlined, DeleteOutlined, CopyOutlined, MoreOutlined, FormOutlined, CodeOutlined, PlusOutlined } from '@ant-design/icons';
|
|
5
|
+
import zhCN from 'antd/locale/zh_CN';
|
|
6
|
+
export { default as zhCN } from 'antd/locale/zh_CN';
|
|
5
7
|
import dayjs from 'dayjs';
|
|
6
8
|
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
|
9
|
+
export { default as enUS } from 'antd/locale/en_US';
|
|
10
|
+
export { default as jaJP } from 'antd/locale/ja_JP';
|
|
11
|
+
export { default as koKR } from 'antd/locale/ko_KR';
|
|
7
12
|
|
|
8
13
|
/* ─── 工具函数 ──────────────────────────────────────── */
|
|
9
14
|
/**
|
|
@@ -61,6 +66,35 @@ function getFieldGroupPrefix(key) {
|
|
|
61
66
|
const idx = key.indexOf('.');
|
|
62
67
|
return idx >= 0 ? key.slice(0, idx) : null;
|
|
63
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* 检查操作按钮是否应该显示
|
|
71
|
+
*
|
|
72
|
+
* @param condition - 条件配置
|
|
73
|
+
* @param record - 当前行数据
|
|
74
|
+
* @returns 是否应该显示该操作
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* // 字段值匹配
|
|
78
|
+
* shouldShowAction({ status: ['active', 'pending'] }, { status: 'active' }) → true
|
|
79
|
+
* shouldShowAction({ status: ['active', 'pending'] }, { status: 'deleted' }) → false
|
|
80
|
+
*
|
|
81
|
+
* // 支持嵌套字段(点分路径)
|
|
82
|
+
* shouldShowAction({ 'user.role': ['admin'] }, { user: { role: 'admin' } }) → true
|
|
83
|
+
*/
|
|
84
|
+
function shouldShowAction(condition, record) {
|
|
85
|
+
if (!condition || Object.keys(condition).length === 0) {
|
|
86
|
+
return true; // 没有条件时默认显示
|
|
87
|
+
}
|
|
88
|
+
for (const [fieldPath, allowedValues] of Object.entries(condition)) {
|
|
89
|
+
// 支持点分路径获取嵌套值
|
|
90
|
+
const actualValue = getNestedValue(record, fieldPath);
|
|
91
|
+
// 检查值是否在允许的值列表中
|
|
92
|
+
if (!allowedValues.includes(actualValue)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
64
98
|
|
|
65
99
|
const { RangePicker } = DatePicker;
|
|
66
100
|
/** 解析 FilterConfig */
|
|
@@ -248,9 +282,21 @@ function DynamicTable({ schema, data, loading, pagination, onView, onEdit, onDel
|
|
|
248
282
|
render: (_, record) => {
|
|
249
283
|
var _a;
|
|
250
284
|
const actions = (_a = schema.actions) !== null && _a !== void 0 ? _a : [];
|
|
251
|
-
//
|
|
252
|
-
const basicActions = actions.filter(action =>
|
|
253
|
-
|
|
285
|
+
// 分离基础操作和自定义操作,并根据 condition 过滤
|
|
286
|
+
const basicActions = actions.filter(action => {
|
|
287
|
+
// 检查是否是基础操作类型
|
|
288
|
+
const isBasicAction = action.type === 'view' || action.type === 'edit' || action.type === 'delete';
|
|
289
|
+
if (!isBasicAction)
|
|
290
|
+
return false;
|
|
291
|
+
// 检查 condition 条件
|
|
292
|
+
return shouldShowAction(action.condition, record);
|
|
293
|
+
});
|
|
294
|
+
const customActions = actions.filter(action => {
|
|
295
|
+
if (action.type !== 'custom')
|
|
296
|
+
return false;
|
|
297
|
+
// 检查 condition 条件
|
|
298
|
+
return shouldShowAction(action.condition, record);
|
|
299
|
+
});
|
|
254
300
|
// 构建下拉菜单项(仅包含自定义操作)
|
|
255
301
|
const dropdownItems = customActions.map(action => ({
|
|
256
302
|
key: action.key,
|
|
@@ -862,7 +908,7 @@ function extractListResponse(json) {
|
|
|
862
908
|
}
|
|
863
909
|
return { list: [], total: 0 };
|
|
864
910
|
}
|
|
865
|
-
const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest }) => {
|
|
911
|
+
const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest, locale = zhCN }) => {
|
|
866
912
|
var _a;
|
|
867
913
|
const rowKey = schema.rowKey || 'id';
|
|
868
914
|
// 使用传入的apiRequest或默认的
|
|
@@ -1146,17 +1192,17 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest }) =>
|
|
|
1146
1192
|
}, [
|
|
1147
1193
|
request, modalState.mode, schema.api, fetchList, messageApi,
|
|
1148
1194
|
]);
|
|
1149
|
-
return (jsxs("div", { style: { padding: 24, background: '#f5f6fa', minHeight: '100vh' }, children: [contextHolder, jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }, children: [jsx(Title, { level: 4, style: { margin: 0 }, children: schema.title }), schema.api.create && (jsx(Button, { type: "primary", icon: jsx(PlusOutlined, {}), onClick: () => setModalState({ open: true, mode: 'create', record: undefined }), children: schema.createButtonLabel || '新增' }))] }), jsx(DynamicFilter, { schema: schema, onSearch: handleSearch, onReset: () => handleSearch({}) }), jsx(DynamicTable, { schema: schema, data: data, loading: loading, pagination: {
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1195
|
+
return (jsx(ConfigProvider, { locale: locale, children: jsxs("div", { style: { padding: 24, background: '#f5f6fa', minHeight: '100vh' }, children: [contextHolder, jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }, children: [jsx(Title, { level: 4, style: { margin: 0 }, children: schema.title }), schema.api.create && (jsx(Button, { type: "primary", icon: jsx(PlusOutlined, {}), onClick: () => setModalState({ open: true, mode: 'create', record: undefined }), children: schema.createButtonLabel || '新增' }))] }), jsx(DynamicFilter, { schema: schema, onSearch: handleSearch, onReset: () => handleSearch({}) }), jsx(DynamicTable, { schema: schema, data: data, loading: loading, pagination: {
|
|
1196
|
+
current: page,
|
|
1197
|
+
pageSize,
|
|
1198
|
+
total,
|
|
1199
|
+
onChange: handlePageChange,
|
|
1200
|
+
}, onView: (record) => handleAction({ key: 'view', label: '查看', type: 'view' }, record), onEdit: (record) => handleAction({ key: 'edit', label: '编辑', type: 'edit' }, record), onDelete: (record) => handleDelete(record), onCustomAction: (actionKey, record) => {
|
|
1201
|
+
var _a;
|
|
1202
|
+
const action = (_a = schema.actions) === null || _a === void 0 ? void 0 : _a.find(a => a.key === actionKey);
|
|
1203
|
+
if (action)
|
|
1204
|
+
handleAction(action, record);
|
|
1205
|
+
} }), jsx(DynamicForm, { schema: schema, visible: modalState.open, mode: modalState.mode, initialValues: modalState.record, onSubmit: handleFormOk, onCancel: () => setModalState({ open: false, mode: 'create' }) })] }) }));
|
|
1160
1206
|
};
|
|
1161
1207
|
|
|
1162
1208
|
export { BUILTIN_RULES, CrudPage, DynamicFilter, DynamicForm, DynamicTable, defaultWidgetForType, getAllRules, getEffectiveWidget, getFieldGroupPrefix, getNestedValue, getRuleByKey, keysToAntdRules, loadCustomRules, ruleConfigToAntdRule, saveCustomRules };
|