crud-page-react 0.0.4 → 0.0.5

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
@@ -9,7 +9,9 @@
9
9
  - 🔍 **智能筛选** - 自动生成筛选器,支持范围查询
10
10
  - 📊 **灵活表格** - 可配置列宽、排序、固定列等
11
11
  - 🎨 **Raw JSON 模式** - 支持原始 JSON 编辑和查看
12
- - 🔌 **自定义 API** - 支持自定义 API 请求函数
12
+ - 🔌 **自定义 API** - 支持自定义 API 请求函数和动态 URL 参数
13
+ - 🌐 **动态 URL 参数** - 支持任意字段作为 URL 参数 (`:id`, `:orderNo`, `:customerName` 等)
14
+ - ⚡ **自定义操作** - 支持配置自定义按钮操作和 API 调用
13
15
  - 📱 **响应式设计** - 适配移动端和桌面端
14
16
  - 🎯 **TypeScript 支持** - 完整的类型定义
15
17
 
@@ -103,6 +105,53 @@ function App() {
103
105
  export default App;
104
106
  ```
105
107
 
108
+ ## 动态 URL 参数
109
+
110
+ 支持在 API 配置中使用任意字段作为 URL 参数:
111
+
112
+ ```tsx
113
+ const schema = {
114
+ api: {
115
+ list: '/api/orders',
116
+ create: '/api/orders',
117
+ update: '/api/orders/:id', // 使用 id 字段
118
+ delete: '/api/orders/:orderNo', // 使用 orderNo 字段
119
+ detail: '/api/orders/:id',
120
+ },
121
+ actions: [
122
+ {
123
+ key: 'track',
124
+ label: '物流跟踪',
125
+ type: 'custom',
126
+ api: {
127
+ url: '/api/tracking/:orderNo', // 使用 orderNo 字段
128
+ method: 'GET',
129
+ responseType: 'json'
130
+ }
131
+ },
132
+ {
133
+ key: 'notify',
134
+ label: '通知客户',
135
+ type: 'custom',
136
+ api: {
137
+ url: '/api/notify/:customerName', // 使用 customerName 字段
138
+ method: 'POST',
139
+ data: {
140
+ message: '您的订单状态已更新'
141
+ }
142
+ }
143
+ }
144
+ ],
145
+ // ... 其他配置
146
+ };
147
+ ```
148
+
149
+ ### 支持的占位符格式
150
+ - `:id` → 记录中的 `id` 字段值
151
+ - `:orderNo` → 记录中的 `orderNo` 字段值
152
+ - `:customerName` → 记录中的 `customerName` 字段值
153
+ - 任意 `:fieldName` → 记录中的 `fieldName` 字段值
154
+
106
155
  ## 自定义 API 请求
107
156
 
108
157
  ```tsx
package/dist/index.esm.js CHANGED
@@ -846,8 +846,8 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest }) =>
846
846
  const rowKey = schema.rowKey || 'id';
847
847
  // 使用传入的apiRequest或默认的
848
848
  const request = customApiRequest || apiRequest;
849
- // 本地兜底数据(API 失败时使用)
850
- const localDataRef = useRef(initialData);
849
+ // 初始数据引用
850
+ const initialDataRef = useRef(initialData);
851
851
  const [data, setData] = useState([]);
852
852
  const [loading, setLoading] = useState(false);
853
853
  const [total, setTotal] = useState(0);
@@ -856,33 +856,20 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest }) =>
856
856
  const [pageSize, setPageSize] = useState(((_a = schema.pagination) === null || _a === void 0 ? void 0 : _a.pageSize) || 10);
857
857
  const [modalState, setModalState] = useState({ open: false, mode: 'create' });
858
858
  const [messageApi, contextHolder] = message.useMessage();
859
- // ---------- 本地过滤(API 失败时兜底) ----------
860
- const localFilter = useCallback((params, p, ps) => {
861
- const filtered = localDataRef.current.filter(row => {
862
- return Object.entries(params).every(([key, val]) => {
863
- if (val === undefined || val === null || val === '')
864
- return true;
865
- if (key.endsWith('_min'))
866
- return Number(row[key.slice(0, -4)]) >= Number(val);
867
- if (key.endsWith('_max'))
868
- return Number(row[key.slice(0, -4)]) <= Number(val);
869
- if (key.endsWith('_start') || key.endsWith('_end'))
870
- return true;
871
- const rowVal = row[key];
872
- if (Array.isArray(rowVal)) {
873
- return Array.isArray(val) ? val.some(v => rowVal.includes(v)) : rowVal.includes(val);
874
- }
875
- if (typeof val === 'string')
876
- return String(rowVal).toLowerCase().includes(val.toLowerCase());
877
- return rowVal === val;
878
- });
879
- });
880
- const start = (p - 1) * ps;
881
- setData(filtered.slice(start, start + ps));
882
- setTotal(filtered.length);
859
+ // ---------- 初始化数据 ----------
860
+ const initializeData = useCallback(() => {
861
+ if (initialDataRef.current.length > 0) {
862
+ setData(initialDataRef.current);
863
+ setTotal(initialDataRef.current.length);
864
+ }
883
865
  }, []);
884
866
  // ---------- 获取列表 ----------
885
867
  const fetchList = useCallback(async (params = filterParams, p = page, ps = pageSize) => {
868
+ if (!schema.api.list) {
869
+ // 没有配置 API,使用初始数据
870
+ initializeData();
871
+ return;
872
+ }
886
873
  setLoading(true);
887
874
  try {
888
875
  const query = new URLSearchParams({ page: String(p), pageSize: String(ps) });
@@ -895,14 +882,22 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest }) =>
895
882
  setData(list);
896
883
  setTotal(tot);
897
884
  }
898
- catch (_a) {
899
- // API 不可用 本地演示模式
900
- localFilter(params, p, ps);
885
+ catch (error) {
886
+ console.error('Failed to fetch list:', error);
887
+ messageApi.error('获取数据失败,请检查网络连接或联系管理员');
888
+ // 如果有初始数据,显示初始数据
889
+ if (initialDataRef.current.length > 0) {
890
+ initializeData();
891
+ }
892
+ else {
893
+ setData([]);
894
+ setTotal(0);
895
+ }
901
896
  }
902
897
  finally {
903
898
  setLoading(false);
904
899
  }
905
- }, [request, schema.api.list, filterParams, page, pageSize, localFilter]);
900
+ }, [request, schema.api.list, filterParams, page, pageSize, initializeData, messageApi]);
906
901
  // 初始加载 & 参数变化时重新请求
907
902
  useEffect(() => {
908
903
  fetchList(filterParams, page, pageSize);
@@ -920,22 +915,20 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest }) =>
920
915
  }, []);
921
916
  // ---------- 删除 ----------
922
917
  const handleDelete = useCallback(async (record) => {
923
- const id = record[rowKey];
924
- if (schema.api.delete) {
925
- try {
926
- await request(buildUrl(schema.api.delete, record), { method: 'DELETE' });
927
- messageApi.success('删除成功');
928
- fetchList();
929
- return;
930
- }
931
- catch (_a) {
932
- // 降级本地
933
- }
918
+ if (!schema.api.delete) {
919
+ messageApi.error('删除功能未配置');
920
+ return;
921
+ }
922
+ try {
923
+ await request(buildUrl(schema.api.delete, record), { method: 'DELETE' });
924
+ messageApi.success('删除成功');
925
+ fetchList();
926
+ }
927
+ catch (error) {
928
+ console.error('Delete failed:', error);
929
+ messageApi.error('删除失败,请稍后重试');
934
930
  }
935
- localDataRef.current = localDataRef.current.filter(r => r[rowKey] !== id);
936
- localFilter(filterParams, page, pageSize);
937
- messageApi.success('删除成功(演示模式)');
938
- }, [request, schema.api.delete, rowKey, fetchList, localFilter, filterParams, page, pageSize, messageApi]);
931
+ }, [request, schema.api.delete, fetchList, messageApi]);
939
932
  // ---------- 操作列点击 ----------
940
933
  const handleAction = useCallback(async (action, record) => {
941
934
  if (action.type === 'view') {
@@ -1005,52 +998,45 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest }) =>
1005
998
  const handleFormOk = useCallback(async (values) => {
1006
999
  const isCreate = modalState.mode === 'create';
1007
1000
  if (isCreate) {
1008
- if (schema.api.create) {
1009
- try {
1010
- await request(schema.api.create, {
1011
- method: 'POST',
1012
- body: JSON.stringify(values),
1013
- });
1014
- messageApi.success('新增成功');
1015
- setModalState({ open: false, mode: 'create' });
1016
- fetchList();
1017
- return;
1018
- }
1019
- catch (_a) {
1020
- // 降级本地
1021
- }
1001
+ if (!schema.api.create) {
1002
+ messageApi.error('新增功能未配置');
1003
+ return;
1004
+ }
1005
+ try {
1006
+ await request(schema.api.create, {
1007
+ method: 'POST',
1008
+ body: JSON.stringify(values),
1009
+ });
1010
+ messageApi.success('新增成功');
1011
+ setModalState({ open: false, mode: 'create' });
1012
+ fetchList();
1013
+ }
1014
+ catch (error) {
1015
+ console.error('Create failed:', error);
1016
+ messageApi.error('新增失败,请稍后重试');
1022
1017
  }
1023
- const newRecord = Object.assign({ [rowKey]: `local-${Date.now()}` }, values);
1024
- localDataRef.current = [newRecord, ...localDataRef.current];
1025
- localFilter(filterParams, 1, pageSize);
1026
- setPage(1);
1027
- messageApi.success('新增成功(演示模式)');
1028
1018
  }
1029
1019
  else {
1030
- const id = values[rowKey];
1031
- if (schema.api.update) {
1032
- try {
1033
- await request(buildUrl(schema.api.update, values), {
1034
- method: 'PUT',
1035
- body: JSON.stringify(values),
1036
- });
1037
- messageApi.success('编辑成功');
1038
- setModalState({ open: false, mode: 'create' });
1039
- fetchList();
1040
- return;
1041
- }
1042
- catch (_b) {
1043
- // 降级本地
1044
- }
1020
+ if (!schema.api.update) {
1021
+ messageApi.error('编辑功能未配置');
1022
+ return;
1023
+ }
1024
+ try {
1025
+ await request(buildUrl(schema.api.update, values), {
1026
+ method: 'PUT',
1027
+ body: JSON.stringify(values),
1028
+ });
1029
+ messageApi.success('编辑成功');
1030
+ setModalState({ open: false, mode: 'create' });
1031
+ fetchList();
1032
+ }
1033
+ catch (error) {
1034
+ console.error('Update failed:', error);
1035
+ messageApi.error('编辑失败,请稍后重试');
1045
1036
  }
1046
- localDataRef.current = localDataRef.current.map(r => r[rowKey] === id ? Object.assign(Object.assign({}, r), values) : r);
1047
- localFilter(filterParams, page, pageSize);
1048
- messageApi.success('编辑成功(演示模式)');
1049
1037
  }
1050
- setModalState({ open: false, mode: 'create' });
1051
1038
  }, [
1052
- request, modalState.mode, schema.api, rowKey,
1053
- fetchList, localFilter, filterParams, page, pageSize, messageApi,
1039
+ request, modalState.mode, schema.api, fetchList, messageApi,
1054
1040
  ]);
1055
1041
  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: {
1056
1042
  current: page,