crud-page-react 0.0.5 → 0.0.7

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.js CHANGED
@@ -812,12 +812,40 @@ function DynamicForm({ schema, mode, visible, initialValues, onSubmit, onCancel,
812
812
  }
813
813
 
814
814
  const { Title } = antd.Typography;
815
- /** 动态替换 URL 模板中的占位符 */
816
- function buildUrl(template, record) {
817
- return template.replace(/:(\w+)/g, (match, fieldName) => {
818
- const value = record[fieldName];
819
- return value !== undefined ? String(value) : match;
820
- });
815
+ /** 处理模板数据,支持 {{fieldName}} 格式的变量替换 */
816
+ function processTemplateData(data, record) {
817
+ const result = {};
818
+ for (const [key, value] of Object.entries(data)) {
819
+ if (typeof value === 'string' && value.includes('{{') && value.includes('}}')) {
820
+ // 替换模板变量 {{fieldName}}
821
+ result[key] = value.replace(/\{\{(\w+)\}\}/g, (match, fieldName) => {
822
+ const fieldValue = record[fieldName];
823
+ return fieldValue !== undefined ? String(fieldValue) : match;
824
+ });
825
+ }
826
+ else {
827
+ result[key] = value;
828
+ }
829
+ }
830
+ return result;
831
+ }
832
+ /** 解析 API 配置,支持字符串和对象两种格式 */
833
+ function parseApiConfig(apiDef) {
834
+ if (!apiDef)
835
+ return undefined;
836
+ if (typeof apiDef === 'string') {
837
+ // 简单字符串 URL 配置
838
+ return {
839
+ url: apiDef,
840
+ method: 'GET',
841
+ responseType: 'json'
842
+ };
843
+ }
844
+ else if (typeof apiDef === 'object') {
845
+ // 完整的 API 配置对象
846
+ return apiDef;
847
+ }
848
+ return undefined;
821
849
  }
822
850
  /** 通用请求封装 */
823
851
  async function apiRequest(url, options) {
@@ -867,19 +895,39 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest }) =>
867
895
  }, []);
868
896
  // ---------- 获取列表 ----------
869
897
  const fetchList = react.useCallback(async (params = filterParams, p = page, ps = pageSize) => {
870
- if (!schema.api.list) {
898
+ const listApiConfig = parseApiConfig(schema.api.list);
899
+ if (!listApiConfig) {
871
900
  // 没有配置 API,使用初始数据
872
901
  initializeData();
873
902
  return;
874
903
  }
875
904
  setLoading(true);
876
905
  try {
906
+ // 构建查询参数
877
907
  const query = new URLSearchParams({ page: String(p), pageSize: String(ps) });
878
908
  Object.entries(params).forEach(([k, v]) => {
879
909
  if (v !== undefined && v !== null && v !== '')
880
910
  query.set(k, String(v));
881
911
  });
882
- const json = await request(`${schema.api.list}?${query}`);
912
+ // 构建请求选项
913
+ const options = {
914
+ method: listApiConfig.method || 'GET',
915
+ headers: Object.assign({ 'Content-Type': 'application/json' }, listApiConfig.headers)
916
+ };
917
+ // 如果是 POST 请求,将查询参数放到请求体中
918
+ let url = listApiConfig.url;
919
+ if (listApiConfig.method === 'POST') {
920
+ const queryParams = {};
921
+ query.forEach((value, key) => {
922
+ queryParams[key] = value;
923
+ });
924
+ const requestData = Object.assign(Object.assign({}, queryParams), listApiConfig.data);
925
+ options.body = JSON.stringify(requestData);
926
+ }
927
+ else {
928
+ url = `${url}?${query}`;
929
+ }
930
+ const json = await request(url, options);
883
931
  const { list, total: tot } = extractListResponse(json);
884
932
  setData(list);
885
933
  setTotal(tot);
@@ -917,12 +965,29 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest }) =>
917
965
  }, []);
918
966
  // ---------- 删除 ----------
919
967
  const handleDelete = react.useCallback(async (record) => {
920
- if (!schema.api.delete) {
968
+ const deleteApiConfig = parseApiConfig(schema.api.delete);
969
+ if (!deleteApiConfig) {
921
970
  messageApi.error('删除功能未配置');
922
971
  return;
923
972
  }
924
973
  try {
925
- await request(buildUrl(schema.api.delete, record), { method: 'DELETE' });
974
+ // 构建 URL,动态替换占位符
975
+ let url = deleteApiConfig.url;
976
+ url = url.replace(/:(\w+)/g, (match, fieldName) => {
977
+ const value = record[fieldName];
978
+ return value !== undefined ? String(value) : match;
979
+ });
980
+ // 构建请求选项
981
+ const options = {
982
+ method: deleteApiConfig.method || 'DELETE',
983
+ headers: Object.assign({ 'Content-Type': 'application/json' }, deleteApiConfig.headers)
984
+ };
985
+ // 处理请求体数据
986
+ if (deleteApiConfig.data && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(deleteApiConfig.method || 'DELETE')) {
987
+ const processedData = processTemplateData(deleteApiConfig.data, record);
988
+ options.body = JSON.stringify(Object.assign(Object.assign({}, processedData), { recordId: record[rowKey], timestamp: new Date().toISOString() }));
989
+ }
990
+ await request(url, options);
926
991
  messageApi.success('删除成功');
927
992
  fetchList();
928
993
  }
@@ -930,7 +995,7 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest }) =>
930
995
  console.error('Delete failed:', error);
931
996
  messageApi.error('删除失败,请稍后重试');
932
997
  }
933
- }, [request, schema.api.delete, fetchList, messageApi]);
998
+ }, [request, schema.api.delete, fetchList, messageApi, rowKey]);
934
999
  // ---------- 操作列点击 ----------
935
1000
  const handleAction = react.useCallback(async (action, record) => {
936
1001
  if (action.type === 'view') {
@@ -942,31 +1007,55 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest }) =>
942
1007
  else if (action.type === 'delete') {
943
1008
  handleDelete(record);
944
1009
  }
945
- else if (action.type === 'custom' && action.api) {
1010
+ else if (action.type === 'custom') {
946
1011
  // 处理自定义 action 的 API 调用
1012
+ let apiConfig;
1013
+ // 优先使用 apiKey 引用统一配置,向后兼容 api 直接配置
1014
+ if (action.apiKey) {
1015
+ const apiDef = schema.api[action.apiKey];
1016
+ if (typeof apiDef === 'string') {
1017
+ // 简单字符串 URL 配置
1018
+ apiConfig = {
1019
+ url: apiDef,
1020
+ method: 'GET',
1021
+ responseType: 'json'
1022
+ };
1023
+ }
1024
+ else if (apiDef && typeof apiDef === 'object') {
1025
+ // 完整的 API 配置对象
1026
+ apiConfig = apiDef;
1027
+ }
1028
+ }
1029
+ else if (action.api) {
1030
+ // 向后兼容:使用 action.api 配置
1031
+ apiConfig = action.api;
1032
+ }
1033
+ if (!apiConfig) {
1034
+ messageApi.error(`${action.label}未配置 API`);
1035
+ return;
1036
+ }
947
1037
  try {
948
1038
  // 构建 URL,动态替换占位符
949
- let url = action.api.url;
950
- // 替换所有 :fieldName 格式的占位符
1039
+ let url = apiConfig.url;
951
1040
  url = url.replace(/:(\w+)/g, (match, fieldName) => {
952
1041
  const value = record[fieldName];
953
1042
  return value !== undefined ? String(value) : match;
954
1043
  });
955
1044
  // 构建请求选项
956
1045
  const options = {
957
- method: action.api.method,
958
- headers: Object.assign({ 'Content-Type': 'application/json' }, action.api.headers)
1046
+ method: apiConfig.method || 'GET',
1047
+ headers: Object.assign({ 'Content-Type': 'application/json' }, apiConfig.headers)
959
1048
  };
960
- // 添加请求体数据
961
- if (action.api.data && ['POST', 'PUT', 'PATCH'].includes(action.api.method)) {
962
- options.body = JSON.stringify(Object.assign(Object.assign({}, action.api.data), {
963
- // 可以添加动态数据
964
- recordId: record[rowKey], timestamp: new Date().toISOString() }));
1049
+ // 处理请求体数据
1050
+ if (apiConfig.data && ['POST', 'PUT', 'PATCH'].includes(apiConfig.method || 'GET')) {
1051
+ // 支持模板变量替换
1052
+ const processedData = processTemplateData(apiConfig.data, record);
1053
+ options.body = JSON.stringify(Object.assign(Object.assign({}, processedData), { recordId: record[rowKey], timestamp: new Date().toISOString() }));
965
1054
  }
966
1055
  // 调用 API
967
1056
  const response = await request(url, options);
968
1057
  // 处理特殊响应类型
969
- if (action.api.responseType === 'blob') {
1058
+ if (apiConfig.responseType === 'blob') {
970
1059
  // 处理文件下载
971
1060
  const blob = new Blob([response]);
972
1061
  const downloadUrl = URL.createObjectURL(blob);
@@ -1000,15 +1089,25 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest }) =>
1000
1089
  const handleFormOk = react.useCallback(async (values) => {
1001
1090
  const isCreate = modalState.mode === 'create';
1002
1091
  if (isCreate) {
1003
- if (!schema.api.create) {
1092
+ const createApiConfig = parseApiConfig(schema.api.create);
1093
+ if (!createApiConfig) {
1004
1094
  messageApi.error('新增功能未配置');
1005
1095
  return;
1006
1096
  }
1007
1097
  try {
1008
- await request(schema.api.create, {
1009
- method: 'POST',
1010
- body: JSON.stringify(values),
1011
- });
1098
+ // 构建请求选项
1099
+ const options = {
1100
+ method: createApiConfig.method || 'POST',
1101
+ headers: Object.assign({ 'Content-Type': 'application/json' }, createApiConfig.headers)
1102
+ };
1103
+ // 处理请求体数据
1104
+ let requestData = Object.assign({}, values);
1105
+ if (createApiConfig.data) {
1106
+ const processedData = processTemplateData(createApiConfig.data, values);
1107
+ requestData = Object.assign(Object.assign(Object.assign({}, requestData), processedData), { timestamp: new Date().toISOString() });
1108
+ }
1109
+ options.body = JSON.stringify(requestData);
1110
+ await request(createApiConfig.url, options);
1012
1111
  messageApi.success('新增成功');
1013
1112
  setModalState({ open: false, mode: 'create' });
1014
1113
  fetchList();
@@ -1019,15 +1118,31 @@ const CrudPage = ({ schema, initialData = [], apiRequest: customApiRequest }) =>
1019
1118
  }
1020
1119
  }
1021
1120
  else {
1022
- if (!schema.api.update) {
1121
+ const updateApiConfig = parseApiConfig(schema.api.update);
1122
+ if (!updateApiConfig) {
1023
1123
  messageApi.error('编辑功能未配置');
1024
1124
  return;
1025
1125
  }
1026
1126
  try {
1027
- await request(buildUrl(schema.api.update, values), {
1028
- method: 'PUT',
1029
- body: JSON.stringify(values),
1127
+ // 构建 URL,动态替换占位符
1128
+ let url = updateApiConfig.url;
1129
+ url = url.replace(/:(\w+)/g, (match, fieldName) => {
1130
+ const value = values[fieldName];
1131
+ return value !== undefined ? String(value) : match;
1030
1132
  });
1133
+ // 构建请求选项
1134
+ const options = {
1135
+ method: updateApiConfig.method || 'PUT',
1136
+ headers: Object.assign({ 'Content-Type': 'application/json' }, updateApiConfig.headers)
1137
+ };
1138
+ // 处理请求体数据
1139
+ let requestData = Object.assign({}, values);
1140
+ if (updateApiConfig.data) {
1141
+ const processedData = processTemplateData(updateApiConfig.data, values);
1142
+ requestData = Object.assign(Object.assign(Object.assign({}, requestData), processedData), { timestamp: new Date().toISOString() });
1143
+ }
1144
+ options.body = JSON.stringify(requestData);
1145
+ await request(url, options);
1031
1146
  messageApi.success('编辑成功');
1032
1147
  setModalState({ open: false, mode: 'create' });
1033
1148
  fetchList();