crud-page-react 0.2.1 → 0.3.0

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/dist/index.esm.js CHANGED
@@ -88,8 +88,10 @@ function shouldShowAction(condition, record) {
88
88
  for (const [fieldPath, allowedValues] of Object.entries(condition)) {
89
89
  // 支持点分路径获取嵌套值
90
90
  const actualValue = getNestedValue(record, fieldPath);
91
+ // 确保 allowedValues 是数组
92
+ const valuesArray = Array.isArray(allowedValues) ? allowedValues : [allowedValues];
91
93
  // 检查值是否在允许的值列表中
92
- if (!allowedValues.includes(actualValue)) {
94
+ if (!valuesArray.includes(actualValue)) {
93
95
  return false;
94
96
  }
95
97
  }
@@ -292,7 +294,9 @@ function DynamicTable({ schema, data, loading, pagination, onView, onEdit, onDel
292
294
  return shouldShowAction(action.condition, record);
293
295
  });
294
296
  const customActions = actions.filter(action => {
295
- if (action.type !== 'custom')
297
+ // 检查是否是自定义操作类型(非基础操作)
298
+ const isBasicAction = action.type === 'view' || action.type === 'edit' || action.type === 'delete';
299
+ if (isBasicAction)
296
300
  return false;
297
301
  // 检查 condition 条件
298
302
  return shouldShowAction(action.condition, record);
@@ -867,7 +871,22 @@ function DynamicForm({ schema, mode, visible, initialValues, onSubmit, onCancel,
867
871
  }
868
872
 
869
873
  const { Title } = Typography;
870
- /** 处理模板数据,支持 {{fieldName}} 格式的变量替换 */
874
+ /** 动态替换 URL 模板中的占位符,支持 :fieldName 和 {{fieldName}} 两种格式 */
875
+ function buildUrl(template, record) {
876
+ let result = template;
877
+ // 替换 :fieldName 格式
878
+ result = result.replace(/:(\w+)/g, (match, fieldName) => {
879
+ const value = record[fieldName];
880
+ return value !== undefined ? String(value) : match;
881
+ });
882
+ // 替换 {{fieldName}} 格式
883
+ result = result.replace(/\{\{(\w+)\}\}/g, (match, fieldName) => {
884
+ const value = record[fieldName];
885
+ return value !== undefined ? String(value) : match;
886
+ });
887
+ return result;
888
+ }
889
+ /** 处理模板数据,支持 {{fieldName}} 格式的变量替换,支持嵌套对象和数组 */
871
890
  function processTemplateData(data, record) {
872
891
  const result = {};
873
892
  for (const [key, value] of Object.entries(data)) {
@@ -878,6 +897,27 @@ function processTemplateData(data, record) {
878
897
  return fieldValue !== undefined ? String(fieldValue) : match;
879
898
  });
880
899
  }
900
+ else if (typeof value === 'object' && value !== null) {
901
+ if (Array.isArray(value)) {
902
+ // 处理数组
903
+ result[key] = value.map(item => {
904
+ if (typeof item === 'object' && item !== null) {
905
+ return processTemplateData(item, record);
906
+ }
907
+ else if (typeof item === 'string' && item.includes('{{') && item.includes('}}')) {
908
+ return item.replace(/\{\{(\w+)\}\}/g, (match, fieldName) => {
909
+ const fieldValue = record[fieldName];
910
+ return fieldValue !== undefined ? String(fieldValue) : match;
911
+ });
912
+ }
913
+ return item;
914
+ });
915
+ }
916
+ else {
917
+ // 处理嵌套对象
918
+ result[key] = processTemplateData(value, record);
919
+ }
920
+ }
881
921
  else {
882
922
  result[key] = value;
883
923
  }
@@ -951,17 +991,32 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest, loca
951
991
  method: listApiConfig.method || 'GET',
952
992
  headers: Object.assign({ 'Content-Type': 'application/json' }, listApiConfig.headers)
953
993
  };
954
- // 如果是 POST 请求,将查询参数放到请求体中
955
- let url = listApiConfig.url;
994
+ // 构建 URL,支持动态占位符替换
995
+ let url = buildUrl(listApiConfig.url, params);
956
996
  if (listApiConfig.method === 'POST') {
957
997
  const queryParams = {};
958
998
  query.forEach((value, key) => {
959
999
  queryParams[key] = value;
960
1000
  });
961
- const requestData = Object.assign(Object.assign({}, queryParams), listApiConfig.data);
1001
+ // 处理模板数据(如果有的话)
1002
+ let processedApiData = {};
1003
+ if (listApiConfig.data) {
1004
+ // 对于list API,通常没有特定的record,使用查询参数作为上下文
1005
+ processedApiData = processTemplateData(listApiConfig.data, queryParams);
1006
+ }
1007
+ const requestData = Object.assign(Object.assign({}, queryParams), processedApiData);
962
1008
  options.body = JSON.stringify(requestData);
963
1009
  }
964
1010
  else {
1011
+ // 对于GET请求,如果有data配置,将其作为查询参数处理
1012
+ if (listApiConfig.data) {
1013
+ const processedApiData = processTemplateData(listApiConfig.data, params);
1014
+ Object.entries(processedApiData).forEach(([key, value]) => {
1015
+ if (value !== undefined && value !== null && value !== '') {
1016
+ query.set(key, String(value));
1017
+ }
1018
+ });
1019
+ }
965
1020
  url = `${url}?${query}`;
966
1021
  }
967
1022
  const json = await request(url, options);
@@ -1009,11 +1064,7 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest, loca
1009
1064
  const deleteApiConfig = schema.api.delete;
1010
1065
  try {
1011
1066
  // 构建 URL,动态替换占位符
1012
- let url = deleteApiConfig.url;
1013
- url = url.replace(/:(\w+)/g, (match, fieldName) => {
1014
- const value = record[fieldName];
1015
- return value !== undefined ? String(value) : match;
1016
- });
1067
+ let url = buildUrl(deleteApiConfig.url, record);
1017
1068
  // 构建请求选项
1018
1069
  const options = {
1019
1070
  method: deleteApiConfig.method || 'DELETE',
@@ -1035,11 +1086,56 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest, loca
1035
1086
  }, [request, schema.api.delete, fetchList, messageApi, rowKey]);
1036
1087
  // ---------- 操作列点击 ----------
1037
1088
  const handleAction = useCallback(async (action, record) => {
1038
- if (action.type === 'view') {
1039
- setModalState({ open: true, mode: 'view', record });
1040
- }
1041
- else if (action.type === 'edit') {
1042
- setModalState({ open: true, mode: 'edit', record });
1089
+ var _a, _b;
1090
+ if (action.type === 'view' || action.type === 'edit') {
1091
+ // 如果配置了detail API,先调用获取详细数据
1092
+ if (schema.api.detail) {
1093
+ try {
1094
+ const detailApiConfig = schema.api.detail;
1095
+ // 构建 URL,动态替换占位符
1096
+ let url = buildUrl(detailApiConfig.url, record);
1097
+ // 构建请求选项
1098
+ const options = {
1099
+ method: detailApiConfig.method || 'GET',
1100
+ headers: Object.assign({ 'Content-Type': 'application/json' }, detailApiConfig.headers)
1101
+ };
1102
+ // 处理请求体数据(对于GET请求,通常不需要body,但有些API可能需要)
1103
+ if (detailApiConfig.data && ['POST', 'PUT', 'PATCH'].includes(detailApiConfig.method || 'GET')) {
1104
+ const processedData = processTemplateData(detailApiConfig.data, record);
1105
+ options.body = JSON.stringify(processedData);
1106
+ }
1107
+ else if (detailApiConfig.method === 'GET' && detailApiConfig.data) {
1108
+ // 对于GET请求,将data作为查询参数
1109
+ const processedData = processTemplateData(detailApiConfig.data, record);
1110
+ const query = new URLSearchParams();
1111
+ Object.entries(processedData).forEach(([key, value]) => {
1112
+ if (value !== undefined && value !== null) {
1113
+ query.set(key, String(value));
1114
+ }
1115
+ });
1116
+ url = `${url}?${query}`;
1117
+ }
1118
+ const response = await request(url, options);
1119
+ // 提取详细数据
1120
+ let detailData = record; // 默认使用列表数据
1121
+ if (response && typeof response === 'object') {
1122
+ const responseObj = response;
1123
+ // 尝试从不同的响应结构中提取数据
1124
+ detailData = ((_b = (_a = responseObj.data) !== null && _a !== void 0 ? _a : responseObj.result) !== null && _b !== void 0 ? _b : responseObj);
1125
+ }
1126
+ setModalState({ open: true, mode: action.type, record: detailData });
1127
+ }
1128
+ catch (error) {
1129
+ console.error('Failed to fetch detail:', error);
1130
+ messageApi.error('获取详细数据失败,使用列表数据');
1131
+ // 如果获取详细数据失败,仍然使用列表数据
1132
+ setModalState({ open: true, mode: action.type, record });
1133
+ }
1134
+ }
1135
+ else {
1136
+ // 没有配置detail API,直接使用列表数据
1137
+ setModalState({ open: true, mode: action.type, record });
1138
+ }
1043
1139
  }
1044
1140
  else if (action.type === 'delete') {
1045
1141
  handleDelete(record);
@@ -1073,11 +1169,7 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest, loca
1073
1169
  }
1074
1170
  try {
1075
1171
  // 构建 URL,动态替换占位符
1076
- let url = apiConfig.url;
1077
- url = url.replace(/:(\w+)/g, (match, fieldName) => {
1078
- const value = record[fieldName];
1079
- return value !== undefined ? String(value) : match;
1080
- });
1172
+ let url = buildUrl(apiConfig.url, record);
1081
1173
  // 构建请求选项
1082
1174
  const options = {
1083
1175
  method: apiConfig.method || 'GET',
@@ -1144,7 +1236,9 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest, loca
1144
1236
  requestData = Object.assign(Object.assign(Object.assign({}, requestData), processedData), { timestamp: new Date().toISOString() });
1145
1237
  }
1146
1238
  options.body = JSON.stringify(requestData);
1147
- await request(createApiConfig.url, options);
1239
+ // 构建 URL,支持动态占位符替换
1240
+ const url = buildUrl(createApiConfig.url, values);
1241
+ await request(url, options);
1148
1242
  messageApi.success('新增成功');
1149
1243
  setModalState({ open: false, mode: 'create' });
1150
1244
  fetchList();
@@ -1162,11 +1256,7 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest, loca
1162
1256
  const updateApiConfig = schema.api.update;
1163
1257
  try {
1164
1258
  // 构建 URL,动态替换占位符
1165
- let url = updateApiConfig.url;
1166
- url = url.replace(/:(\w+)/g, (match, fieldName) => {
1167
- const value = values[fieldName];
1168
- return value !== undefined ? String(value) : match;
1169
- });
1259
+ let url = buildUrl(updateApiConfig.url, values);
1170
1260
  // 构建请求选项
1171
1261
  const options = {
1172
1262
  method: updateApiConfig.method || 'PUT',