auto-gen-crud 0.1.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.
Files changed (62) hide show
  1. package/API.md +130 -0
  2. package/README.md +234 -0
  3. package/dist/adapters/ant-design-vue.d.ts +8 -0
  4. package/dist/adapters/element-plus.d.ts +8 -0
  5. package/dist/adapters/index.d.ts +4 -0
  6. package/dist/adapters/index.js +7 -0
  7. package/dist/adapters/index.js.map +1 -0
  8. package/dist/adapters/naive.d.ts +8 -0
  9. package/dist/adapters/types.d.ts +162 -0
  10. package/dist/components/AutoCrud.vue.d.ts +398 -0
  11. package/dist/components/CopyText.vue.d.ts +6 -0
  12. package/dist/components/CrudModal.vue.d.ts +47 -0
  13. package/dist/components/DecodeText.vue.d.ts +8 -0
  14. package/dist/components/DynamicForm.vue.d.ts +63 -0
  15. package/dist/components/DynamicFormItem.vue.d.ts +20 -0
  16. package/dist/components/DynamicTable.vue.d.ts +30 -0
  17. package/dist/context.d.ts +36 -0
  18. package/dist/core/date-format-utils.d.ts +8 -0
  19. package/dist/core/form-runtime-cache.d.ts +27 -0
  20. package/dist/core/form-utils.d.ts +51 -0
  21. package/dist/core/object-utils.d.ts +43 -0
  22. package/dist/core/table-utils.d.ts +19 -0
  23. package/dist/core/types.d.ts +443 -0
  24. package/dist/generator/fields.d.ts +27 -0
  25. package/dist/generator/index.d.ts +14 -0
  26. package/dist/generator/index.js +740 -0
  27. package/dist/generator/index.js.map +1 -0
  28. package/dist/generator/openapi.d.ts +10 -0
  29. package/dist/generator/operations.d.ts +8 -0
  30. package/dist/generator/schema.d.ts +32 -0
  31. package/dist/generator/string-utils.d.ts +11 -0
  32. package/dist/generator/templates.d.ts +6 -0
  33. package/dist/generator/types.d.ts +185 -0
  34. package/dist/generator/writer.d.ts +8 -0
  35. package/dist/hooks/index.d.ts +3 -0
  36. package/dist/hooks/index.js +7 -0
  37. package/dist/hooks/index.js.map +1 -0
  38. package/dist/hooks/useModalForm.d.ts +11 -0
  39. package/dist/hooks/useSearchForm.d.ts +10 -0
  40. package/dist/hooks/useTable.d.ts +11 -0
  41. package/dist/index-Dc-solHn.js +299 -0
  42. package/dist/index-Dc-solHn.js.map +1 -0
  43. package/dist/index.d.ts +12 -0
  44. package/dist/index.js +985 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/plugin.d.ts +17 -0
  47. package/dist/style.css +1 -0
  48. package/dist/useTable-BwDSqD1q.js +214 -0
  49. package/dist/useTable-BwDSqD1q.js.map +1 -0
  50. package/dist/utils/clipboard.d.ts +16 -0
  51. package/dist/utils/index.d.ts +4 -0
  52. package/dist/utils/index.js +532 -0
  53. package/dist/utils/index.js.map +1 -0
  54. package/dist/utils/message.d.ts +9 -0
  55. package/dist/utils/request.d.ts +8 -0
  56. package/dist/utils/sm4-core.d.ts +12 -0
  57. package/dist/utils/sm4.d.ts +29 -0
  58. package/docs/AI_AGENT_GUIDE.md +210 -0
  59. package/mcp/auto-crud-mcp.mjs +502 -0
  60. package/mcp/auto-gen-crud-mcp.mjs +586 -0
  61. package/package.json +93 -0
  62. package/skills/auto-gen-crud-agent/SKILL.md +114 -0
@@ -0,0 +1,740 @@
1
+ import { resolve as P, join as C, dirname as W, isAbsolute as Y } from "node:path";
2
+ import { readFile as U, mkdir as Q, writeFile as X, access as ee } from "node:fs/promises";
3
+ function c(e, t) {
4
+ if (t) {
5
+ if (t.$ref) {
6
+ const r = t.$ref.split("/").pop();
7
+ return r ? c(e, e.components?.schemas?.[r]) : t;
8
+ }
9
+ return t.allOf?.length ? re(e, t.allOf) : t;
10
+ }
11
+ }
12
+ function y(e) {
13
+ return e.parameters.filter((t) => t.in === "query");
14
+ }
15
+ function A(e, t) {
16
+ if (!t) return;
17
+ const r = t.operation.requestBody?.content || {}, n = r["application/json"]?.schema || Object.values(r)[0]?.schema;
18
+ return c(e, n);
19
+ }
20
+ function N(e, t) {
21
+ const r = t.operation.responses || {}, o = (r[200] || r[201] || r.default || Object.values(r)[0])?.content || {}, a = o["application/json"]?.schema || Object.values(o)[0]?.schema;
22
+ return c(e, a);
23
+ }
24
+ function te(e, t) {
25
+ const r = N(e, t), n = v(e, r);
26
+ return c(e, n) || { type: "object", properties: {} };
27
+ }
28
+ function ne(e, t) {
29
+ const r = N(e, t), n = O(e, r);
30
+ return n ? {
31
+ listPath: n.path,
32
+ totalPath: oe(r, n.parentPath) || "total"
33
+ } : { listPath: "result.records", totalPath: "result.total" };
34
+ }
35
+ function F(e, t) {
36
+ return c(e, t)?.properties || {};
37
+ }
38
+ function x(e) {
39
+ return Array.isArray(e?.required) ? e.required : [];
40
+ }
41
+ function re(e, t) {
42
+ return t.reduce(
43
+ (r, n) => {
44
+ const o = c(e, n);
45
+ return {
46
+ ...r,
47
+ ...o,
48
+ properties: {
49
+ ...r.properties || {},
50
+ ...o?.properties || {}
51
+ },
52
+ required: [.../* @__PURE__ */ new Set([...r.required || [], ...o?.required || []])]
53
+ };
54
+ },
55
+ { type: "object", properties: {} }
56
+ );
57
+ }
58
+ function v(e, t) {
59
+ const r = c(e, t);
60
+ if (!r) return;
61
+ if (E(r)) return c(e, r.items);
62
+ const n = r.properties || {};
63
+ for (const o of R(r, n)) {
64
+ const a = c(e, n[o]), i = v(e, a);
65
+ if (i) return i;
66
+ }
67
+ }
68
+ function O(e, t, r = "") {
69
+ const n = c(e, t);
70
+ if (!n) return;
71
+ if (E(n))
72
+ return { path: r, parentPath: r.split(".").slice(0, -1).join(".") };
73
+ const o = n.properties || {};
74
+ for (const a of R(n, o)) {
75
+ const i = r ? `${r}.${a}` : a, s = c(e, o[a]), p = O(e, s, i);
76
+ if (p) return p;
77
+ }
78
+ }
79
+ function oe(e, t) {
80
+ if (!e) return;
81
+ const n = ae(e, t)?.properties || {}, o = ["total", "totalCount", "count"].find((s) => s in n);
82
+ if (o) return t ? `${t}.${o}` : o;
83
+ const a = e.properties || {};
84
+ return ["total", "totalCount", "count"].find((s) => s in a);
85
+ }
86
+ function ae(e, t) {
87
+ return t ? t.split(".").reduce((r, n) => {
88
+ if (r)
89
+ return r.properties?.[n];
90
+ }, e) : e;
91
+ }
92
+ function E(e) {
93
+ return e.type === "array" || Array.isArray(e.type) && e.type.includes("array");
94
+ }
95
+ function R(e, t) {
96
+ const r = e["x-apifox-orders"] || [];
97
+ return [...r.filter((n) => n in t), ...Object.keys(t).filter((n) => !r.includes(n))];
98
+ }
99
+ const ie = /* @__PURE__ */ new Set([
100
+ "web",
101
+ "api",
102
+ "admin",
103
+ "manage",
104
+ "manager",
105
+ "query",
106
+ "list",
107
+ "page",
108
+ "search",
109
+ "add",
110
+ "create",
111
+ "save",
112
+ "edit",
113
+ "update",
114
+ "modify",
115
+ "delete",
116
+ "remove"
117
+ ]);
118
+ function se(e) {
119
+ return e && `${e[0].toUpperCase()}${e.slice(1)}`;
120
+ }
121
+ function pe(e) {
122
+ return e && `${e[0].toLowerCase()}${e.slice(1)}`;
123
+ }
124
+ function S(e) {
125
+ const r = e.replace(/\.json$/i, "").replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^a-zA-Z0-9\u4e00-\u9fa5]+/).filter(Boolean).filter((n) => /^[a-zA-Z0-9]+$/.test(n));
126
+ return r.length === 0 ? "Generated" : r.map((n) => se(n.toLowerCase())).join("");
127
+ }
128
+ function M(e) {
129
+ return pe(S(e));
130
+ }
131
+ function ce(e) {
132
+ return e.split(/[\\\/]/).filter(Boolean).pop() || e;
133
+ }
134
+ function j(e) {
135
+ let t = e, r = "";
136
+ for (; t && t !== r; )
137
+ r = t, t = t.replace(/^(query|get|list|page|search|create|add|save|edit|update|modify|delete|remove)(?=[A-Z_/\-.]|$)/i, "").replace(/(list|page|query|search|create|add|save|edit|update|modify|delete|remove)$/i, "");
138
+ return t;
139
+ }
140
+ function ue(e) {
141
+ const t = e.split("/").map((n) => n.replace(/\.json$/i, "")).flatMap((n) => n.split(/[^a-zA-Z0-9]+/)).filter(Boolean).filter((n) => !ie.has(n.toLowerCase())), r = t.length > 0 ? t[t.length - 1] : "";
142
+ return S(j(r) || r || "Generated");
143
+ }
144
+ function q(e, t) {
145
+ return (e || t).trim().replace(/[((].*?[))]/g, "").trim() || t;
146
+ }
147
+ function l(e) {
148
+ return JSON.stringify(e);
149
+ }
150
+ function m(e, t = 2) {
151
+ const r = " ".repeat(t);
152
+ return e.split(`
153
+ `).map((n) => n && `${r}${n}`).join(`
154
+ `);
155
+ }
156
+ const I = /* @__PURE__ */ new Set(["currentPage", "page", "pageNum", "pageNo"]), L = /* @__PURE__ */ new Set(["pageSize", "size", "limit"]), D = /* @__PURE__ */ new Set(["traceLogId", "operator", "createdBy"]), k = /* @__PURE__ */ new Set(["id", "ids"]);
157
+ function le(e, t, r) {
158
+ return y(t).filter((n) => !B(n.name)).filter((n) => !D.has(n.name)).map((n) => J(e, n, "search", r));
159
+ }
160
+ function fe(e, t, r) {
161
+ const n = /* @__PURE__ */ new Map();
162
+ return t.filter(Boolean).forEach((o) => {
163
+ const a = o;
164
+ y(a).filter((s) => !B(s.name)).filter((s) => !D.has(s.name)).filter((s) => !k.has(s.name)).forEach((s) => {
165
+ const p = J(e, s, "modal", r), u = n.get(p.name);
166
+ n.set(p.name, u ? { ...u, required: u.required || p.required } : p);
167
+ });
168
+ const i = A(e, a);
169
+ G(e, i, x(i), "modal", r, n, !0);
170
+ }), [...n.values()];
171
+ }
172
+ function de(e, t, r) {
173
+ const n = te(e, t), o = /* @__PURE__ */ new Map();
174
+ return G(e, n, x(n), "table", r, o, !1), [...o.values()];
175
+ }
176
+ function me(e, t) {
177
+ const r = /* @__PURE__ */ new Map();
178
+ return t.forEach((n) => {
179
+ y(n).forEach((a) => {
180
+ const i = c(e, a.schema);
181
+ w(a.name, i, r);
182
+ });
183
+ const o = A(e, n);
184
+ Object.entries(F(e, o)).forEach(([a, i]) => w(a, i, r));
185
+ }), r;
186
+ }
187
+ function B(e) {
188
+ return I.has(e) || L.has(e);
189
+ }
190
+ function z(e) {
191
+ return y(e).find((t) => I.has(t.name))?.name || "currentPage";
192
+ }
193
+ function _(e) {
194
+ return y(e).find((t) => L.has(t.name))?.name || "pageSize";
195
+ }
196
+ function G(e, t, r, n, o, a, i) {
197
+ Object.entries(F(e, t)).forEach(([s, p]) => {
198
+ if (i && k.has(s)) return;
199
+ const u = c(e, p);
200
+ w(s, u, o), a.set(s, ye(s, u, r.includes(s), n, o));
201
+ });
202
+ }
203
+ function J(e, t, r, n) {
204
+ const o = c(e, t.schema);
205
+ return w(t.name, o, n), {
206
+ name: t.name,
207
+ label: q(t.description || o?.title || o?.description, t.name),
208
+ tsType: Z(o),
209
+ required: !!t.required,
210
+ // 同名字段可能只有新增接口声明了枚举,列表接口没声明;只要统一枚举表里有,就按 select 生成。
211
+ component: V(t.name, o, t.description, n.has(t.name)),
212
+ description: t.description || o?.description,
213
+ enumName: n.get(t.name)?.textMapName,
214
+ source: r
215
+ };
216
+ }
217
+ function ye(e, t, r, n, o) {
218
+ return {
219
+ name: e,
220
+ label: q(t?.title || t?.description, e),
221
+ tsType: Z(t),
222
+ required: r,
223
+ component: V(e, t, t?.description || t?.title, o.has(e)),
224
+ description: t?.description,
225
+ enumName: o.get(e)?.textMapName,
226
+ source: n
227
+ };
228
+ }
229
+ function w(e, t, r) {
230
+ if (!t || r.has(e)) return;
231
+ const n = t["x-apifox-enum"], o = n?.length ? n.map((i) => ({ label: i.name || String(i.value), value: i.value })) : t.enum?.map((i) => ({ label: String(i), value: i }));
232
+ if (!o || o.length === 0) return;
233
+ const a = M(e);
234
+ r.set(e, {
235
+ optionsName: `${a}Options`,
236
+ textMapName: `${a}TextMap`,
237
+ sourceField: e,
238
+ options: o
239
+ });
240
+ }
241
+ function V(e, t, r, n = !1) {
242
+ if (n || t?.enum?.length || t?.["x-apifox-enum"]?.length) return "select";
243
+ const o = e.toLowerCase(), a = `${e} ${r || ""}`;
244
+ return /remark|desc|description|备注|描述|说明/.test(a) ? "textarea" : /time|date|createdAt|updatedAt|开始时间|结束时间|日期/.test(a) ? "datePicker" : ge(t) ? "inputNumber" : he(t) ? "switch" : o.includes("status") || o.includes("type") ? "select" : "input";
245
+ }
246
+ function Z(e) {
247
+ if (!e) return "unknown";
248
+ const t = Array.isArray(e.type) ? e.type.filter((r) => r !== "null")[0] : e.type;
249
+ return t === "integer" || t === "number" ? "number" : t === "boolean" ? "boolean" : t === "array" ? "unknown[]" : t === "object" ? "Record<string, unknown>" : "string";
250
+ }
251
+ function ge(e) {
252
+ const t = Array.isArray(e?.type) ? e?.type : [e?.type];
253
+ return t.includes("number") || t.includes("integer");
254
+ }
255
+ function he(e) {
256
+ return (Array.isArray(e?.type) ? e?.type : [e?.type]).includes("boolean");
257
+ }
258
+ const $e = 5;
259
+ async function we(e) {
260
+ if (typeof e.openapi != "string") return e.openapi;
261
+ const t = Te(e.openapi) ? await be(e.openapi, e.maxRetries) : await U(P(e.cwd || ".", e.openapi), "utf8");
262
+ return Se(t);
263
+ }
264
+ function Se(e) {
265
+ const t = e.trim();
266
+ if (t.startsWith("{"))
267
+ return JSON.parse(t);
268
+ throw new Error("当前生成器首版只解析 JSON OpenAPI 文档,请先在接口平台导出 JSON。");
269
+ }
270
+ function Te(e) {
271
+ return /^https?:\/\//i.test(e);
272
+ }
273
+ async function be(e, t = 2) {
274
+ const r = Math.min(Math.max(t, 0), $e);
275
+ let n;
276
+ for (let a = 0; a <= r; a += 1)
277
+ try {
278
+ const i = await fetch(e);
279
+ if (!i.ok)
280
+ throw new Error(`下载 OpenAPI 文档失败:${i.status} ${i.statusText}`);
281
+ return i.text();
282
+ } catch (i) {
283
+ if (n = i, a >= r) break;
284
+ }
285
+ const o = n instanceof Error ? n.message : String(n);
286
+ throw new Error(`读取 OpenAPI 文档失败,已按限制停止重试:${o}`);
287
+ }
288
+ const Pe = ["get", "post", "put", "patch", "delete"];
289
+ function Ce(e, t) {
290
+ const r = Ae(e);
291
+ if (r.length === 0)
292
+ throw new Error("OpenAPI 文档中没有可用于生成 CRUD 页面的接口。");
293
+ const n = t.tag || Ne(r), o = r.filter((d) => d.tags.includes(n)), a = o.length > 0 ? o : r, i = t.pageTitle || Fe(n, e), s = t.operationMap || {}, p = g("list", a, s) || a[0];
294
+ if (!p)
295
+ throw new Error("无法识别列表接口,请通过 operationMap.list 显式指定。");
296
+ const u = S(
297
+ t.entityName || ue(p.path) || j(p.operationId) || i || "Generated"
298
+ );
299
+ return {
300
+ tag: n,
301
+ pageTitle: i,
302
+ entityName: u,
303
+ list: p,
304
+ create: g("create", a, s),
305
+ update: g("update", a, s),
306
+ delete: g("delete", a, s)
307
+ };
308
+ }
309
+ function Ae(e) {
310
+ const t = [];
311
+ return Object.entries(e.paths || {}).forEach(([r, n]) => {
312
+ Pe.forEach((o) => {
313
+ const a = n?.[o];
314
+ if (!a) return;
315
+ const i = [...n.parameters || [], ...a.parameters || []], s = a.operationId || `${o}_${r.replace(/[^a-zA-Z0-9]+/g, "_")}`;
316
+ t.push({
317
+ method: o,
318
+ path: r,
319
+ operation: a,
320
+ operationId: s,
321
+ summary: a.summary || a.description || s,
322
+ tags: a.tags || [],
323
+ parameters: i
324
+ });
325
+ });
326
+ }), t;
327
+ }
328
+ function Ne(e) {
329
+ const t = /* @__PURE__ */ new Map();
330
+ e.forEach((n) => {
331
+ n.tags.forEach((o) => {
332
+ t.set(o, (t.get(o) || 0) + 1);
333
+ });
334
+ });
335
+ const [r] = [...t.entries()].sort((n, o) => o[1] - n[1])[0] || [];
336
+ return r || "默认模块";
337
+ }
338
+ function Fe(e, t) {
339
+ return e !== "默认模块" ? ce(e) : t.info?.title || "生成页面";
340
+ }
341
+ function g(e, t, r) {
342
+ const n = r[e];
343
+ return n ? t.find((o) => xe(o, n)) : t.map((o) => ({ operation: o, score: ve(e, o) })).filter((o) => o.score > 0).sort((o, a) => a.score - o.score)[0]?.operation;
344
+ }
345
+ function xe(e, t) {
346
+ const r = t.trim().toLowerCase();
347
+ return e.operationId.toLowerCase() === r || e.path.toLowerCase() === r || `${e.method} ${e.path}`.toLowerCase() === r;
348
+ }
349
+ function ve(e, t) {
350
+ const r = `${t.operationId} ${t.path} ${t.summary}`.toLowerCase(), n = `${t.summary} ${t.path}`;
351
+ return e === "list" ? h(r, n, ["query", "list", "page", "search"], ["查询", "列表", "分页"]) : e === "create" ? h(r, n, ["add", "create", "save"], ["新增", "添加", "创建"]) : e === "update" ? h(r, n, ["edit", "update", "modify"], ["编辑", "修改", "更新"]) : h(r, n, ["delete", "remove", "del"], ["删除", "移除"]);
352
+ }
353
+ function h(e, t, r, n) {
354
+ let o = 0;
355
+ return r.forEach((a) => {
356
+ e.includes(a) && (o += 10);
357
+ }), n.forEach((a) => {
358
+ t.includes(a) && (o += 10);
359
+ }), o;
360
+ }
361
+ function Oe(e) {
362
+ return [
363
+ { path: "index.vue", content: Ee(e) },
364
+ { path: "api/index.ts", content: Re(e) },
365
+ { path: "api/types.ts", content: je(e) },
366
+ { path: "config/enums.ts", content: qe(e) },
367
+ { path: "config/searchForm.ts", content: Ie(e) },
368
+ { path: "config/table.ts", content: Le(e) },
369
+ { path: "config/modalForm.ts", content: De(e) }
370
+ ];
371
+ }
372
+ function Ee(e) {
373
+ const t = e.operations.create ? `await create${e.entityName}(data);` : 'console.warn("当前 OpenAPI 文档未提供新增接口,请在这里补充新增逻辑。", data);', r = e.operations.update ? `await update${e.entityName}(data);` : 'console.warn("当前 OpenAPI 文档未提供编辑接口,请在这里补充编辑逻辑。", data);', n = [
374
+ `query${e.entityName}List`,
375
+ e.operations.create ? `create${e.entityName}` : "",
376
+ e.operations.update ? `update${e.entityName}` : ""
377
+ ].filter(Boolean), o = z(e.operations.list), a = _(e.operations.list);
378
+ return `<template>
379
+ <section class="demo-route-panel">
380
+ <AutoCrud
381
+ title=${l(e.pageTitle)}
382
+ :search-form-config="searchForm"
383
+ :table-config="table"
384
+ :modal-form-config="modalForm"
385
+ />
386
+ </section>
387
+ </template>
388
+
389
+ <script setup lang="ts">
390
+ import { reactive } from "vue";
391
+ import { AutoCrud } from "auto-gen-crud";
392
+ import { ${n.join(", ")} } from "./api";
393
+ import { getModalConfig } from "./config/modalForm";
394
+ import { getSearchConfig } from "./config/searchForm";
395
+ import { getTableConfig } from "./config/table";
396
+ import type { ActionsType, PageContextType, RowType } from "./api/types";
397
+
398
+ // 页面行为统一放在 actions 中,配置文件只消费动作,不反向引用页面变量。
399
+ const actions: ActionsType = {
400
+ onSubmit,
401
+ onEdit: row => {
402
+ modalForm.openEdit(row);
403
+ },
404
+ onView: row => {
405
+ modalForm.openView(row);
406
+ },
407
+ getDataList,
408
+ };
409
+
410
+ // 页面上下文默认保持为空,业务二开时再按权限、联动状态或页面缓存逐步补充。
411
+ const pageContext = reactive<PageContextType>({});
412
+
413
+ const searchForm = getSearchConfig();
414
+ const modalForm = getModalConfig(pageContext, actions);
415
+ const table = getTableConfig(pageContext, actions);
416
+
417
+ async function onSubmit(data: RowType) {
418
+ // 新增和编辑共用弹窗表单,通过 mode 区分最终调用哪个接口。
419
+ if (modalForm.mode === "edit") {
420
+ ${r}
421
+ } else {
422
+ ${t}
423
+ }
424
+
425
+ modalForm.close();
426
+ await table.load();
427
+ }
428
+
429
+ async function getDataList() {
430
+ const { page, pageSize } = table.pagination;
431
+
432
+ // 搜索条件整体透传,避免生成器为每个查询字段重复展开,业务二开时也更容易增删字段。
433
+ const result = await query${e.entityName}List({
434
+ ${o}: page,
435
+ ${a}: pageSize,
436
+ ...searchForm.data,
437
+ });
438
+
439
+ table.data = result.${e.responsePath.listPath};
440
+ table.pagination.total = result.${e.responsePath.totalPath};
441
+ }
442
+ <\/script>
443
+ `;
444
+ }
445
+ function Re(e) {
446
+ const t = [
447
+ "ListResponse",
448
+ "SearchParamsType",
449
+ e.operations.create || e.operations.update ? "SaveParams" : "",
450
+ e.operations.delete ? "DeleteParams" : ""
451
+ ].filter(Boolean), r = [
452
+ $(e, "list", e.operations.list),
453
+ e.operations.create ? $(e, "create", e.operations.create) : "",
454
+ e.operations.update ? $(e, "update", e.operations.update) : "",
455
+ e.operations.delete ? $(e, "delete", e.operations.delete) : ""
456
+ ].filter(Boolean);
457
+ return `${e.requestAdapter.importCode}
458
+ import type { ${t.join(", ")} } from "./types";
459
+
460
+ ${r.join(`
461
+
462
+ `)}
463
+ `;
464
+ }
465
+ function $(e, t, r) {
466
+ const n = Ve(e.entityName, t), o = t === "list" || t === "delete" ? "params" : "data", a = t === "list" ? "SearchParamsType" : t === "delete" ? "DeleteParams" : "SaveParams", i = t === "list" ? "ListResponse" : "unknown", s = {
467
+ functionName: n,
468
+ method: r.method,
469
+ path: r.path,
470
+ payloadName: o,
471
+ responseType: i
472
+ };
473
+ return `export function ${n}(${o}: ${a}) {
474
+ ${Me(e, s)}
475
+ }`;
476
+ }
477
+ function Me(e, t) {
478
+ return e.requestAdapter.renderCall(t).trim();
479
+ }
480
+ function je(e) {
481
+ return `${f(`${e.pageTitle}表格与表单共用数据结构。字段保持可选,方便新增、编辑表单复用同一份页面数据。`)}
482
+ export interface RowType {
483
+ ${m(Be(e.tableFields), 2)}
484
+ [key: string]: unknown;
485
+ }
486
+
487
+ ${f("新增、修改接口提交体。生成器默认复用 RowType,业务可按真实接口继续收窄字段。")}
488
+ export interface SaveParams extends RowType {}
489
+
490
+ ${f("删除接口参数。默认保留 id,若后端批量删除可在这里扩展 ids。")}
491
+ export interface DeleteParams {
492
+ id: number | string;
493
+ [key: string]: unknown;
494
+ }
495
+
496
+ ${f("分页接口原始响应结构,字段路径按当前 OpenAPI 响应自动生成。")}
497
+ export interface ListResponse {
498
+ ${m(_e(e), 2)}
499
+ [key: string]: unknown;
500
+ }
501
+
502
+ ${f("搜索表单数据,字段名与查询接口保持一致。")}
503
+ export type SearchParamsType = Record<string, unknown> & {
504
+ ${m(ze(e), 2)}
505
+ };
506
+
507
+ ${f("页面操作函数集合。由页面统一组装后传给表格和弹窗配置,避免配置文件之间互相引用。")}
508
+ export interface ActionsType {
509
+ onSubmit: (data: RowType) => void | Promise<void>;
510
+ onEdit: (row: RowType) => void;
511
+ onView: (row: RowType) => void;
512
+ getDataList: () => void | Promise<void>;
513
+ }
514
+
515
+ ${f("页面级上下文默认不预设字段,业务二开时再补充权限、枚举、联动状态或页面缓存。")}
516
+ export type PageContextType = Record<string, unknown>;
517
+ `;
518
+ }
519
+ function qe(e) {
520
+ const { enums: t } = e;
521
+ return t.length === 0 ? `// 当前 OpenAPI 文档没有枚举字段,预留空模块便于后续业务补充下拉选项。
522
+ export {};
523
+ ` : `${e.generateTypeOptions ? `import type { CrudOption } from "auto-gen-crud";
524
+
525
+ ` : ""}${t.map((n) => Je(n, e.generateTypeOptions)).join(`
526
+
527
+ `)}
528
+ `;
529
+ }
530
+ function Ie(e) {
531
+ return `import { useSearchForm } from "auto-gen-crud";
532
+ ${K(e.searchFields)}import type { SearchParamsType } from "../api/types";
533
+
534
+ // 搜索表单配置只描述字段,不直接关心列表接口和表格状态。
535
+ export function getSearchConfig() {
536
+ return useSearchForm<SearchParamsType>({
537
+ columns: [
538
+ ${m(e.searchFields.map(H).join(`,
539
+ `), 6)}
540
+ ],
541
+ });
542
+ }
543
+ `;
544
+ }
545
+ function Le(e) {
546
+ return `import { useTable } from "auto-gen-crud";
547
+ import type { ActionsType, PageContextType, RowType } from "../api/types";
548
+
549
+ // 表格配置只负责列展示和操作入口,真实请求由页面层 actions.getDataList 处理。
550
+ export function getTableConfig(pageContext: PageContextType, actions: ActionsType) {
551
+ return useTable<RowType>({
552
+ columns: [
553
+ ${m(e.tableFields.map(Ge).join(`,
554
+ `), 6)},
555
+ {
556
+ title: "操作",
557
+ width: "200px",
558
+ fixed: "right",
559
+ actions: [
560
+ {
561
+ key: "edit",
562
+ label: "编辑",
563
+ type: "primary",
564
+ show: () => Boolean(pageContext.canEdit${e.permissionSuffix} ?? true),
565
+ onClick: actions.onEdit,
566
+ },
567
+ {
568
+ key: "view",
569
+ label: "查看",
570
+ type: "link",
571
+ show: () => Boolean(pageContext.canView${e.permissionSuffix} ?? true),
572
+ onClick: actions.onView,
573
+ },
574
+ ],
575
+ },
576
+ ],
577
+ data: [],
578
+ getDataList: actions.getDataList,
579
+ });
580
+ }
581
+ `;
582
+ }
583
+ function De(e) {
584
+ return `import { useModalForm } from "auto-gen-crud";
585
+ ${K(e.modalFields)}import type { ActionsType, PageContextType, RowType } from "../api/types";
586
+
587
+ // 弹窗表单配置只描述新增、编辑、查看共用字段,提交动作交给页面层处理。
588
+ export function getModalConfig(pageContext: PageContextType, actions: ActionsType) {
589
+ // 保留 pageContext 参数,便于后续按权限、状态或枚举动态控制字段。
590
+ void pageContext;
591
+
592
+ return useModalForm<RowType>({
593
+ columns: [
594
+ ${m(e.modalFields.map(H).join(`,
595
+ `), 6)}
596
+ ],
597
+ onSubmit: actions.onSubmit,
598
+ });
599
+ }
600
+ `;
601
+ }
602
+ function ke(e) {
603
+ return `{ ${e.join(", ")} }`;
604
+ }
605
+ function Be(e) {
606
+ return e.length === 0 ? `id?: number | string;
607
+ ` : e.map((t) => `/** ${t.label} */
608
+ ${t.name}?: ${t.tsType};`).join(`
609
+ `);
610
+ }
611
+ function ze(e) {
612
+ const t = z(e.operations.list), r = _(e.operations.list);
613
+ return [
614
+ `${t}?: number;`,
615
+ `${r}?: number;`,
616
+ ...e.searchFields.map((o) => `${o.name}?: ${o.tsType};`)
617
+ ].join(`
618
+ `);
619
+ }
620
+ function _e(e) {
621
+ return e.responsePath.listPath === "result.records" && e.responsePath.totalPath === "result.total" ? `result: {
622
+ records: RowType[];
623
+ total: number;
624
+ [key: string]: unknown;
625
+ };` : `/** 列表字段路径:${e.responsePath.listPath},总数字段路径:${e.responsePath.totalPath}。 */
626
+ result: Record<string, unknown>;
627
+ data?: Record<string, unknown>;`;
628
+ }
629
+ function H(e) {
630
+ const t = [
631
+ `label: ${l(e.label)}`,
632
+ `prop: ${l(e.name)}`,
633
+ `component: ${l(e.component)}`
634
+ ];
635
+ return e.required && t.push("required: true"), e.enumName && t.push(`options: ${e.enumName}`), `{ ${t.join(", ")} }`;
636
+ }
637
+ function Ge(e) {
638
+ const t = [
639
+ `title: ${l(e.label)}`,
640
+ `field: ${l(e.name)}`
641
+ ];
642
+ return /time|date|createdAt|updatedAt/i.test(e.name) && t.push('dateTime: "YYYY-MM-DD HH:mm"'), `{ ${t.join(", ")} }`;
643
+ }
644
+ function Je(e, t) {
645
+ const r = e.options.map((a) => ` { label: ${l(a.label)}, value: ${JSON.stringify(a.value)} },`).join(`
646
+ `), n = e.options.map((a) => ` ${JSON.stringify(a.value)}: ${l(a.label)},`).join(`
647
+ `);
648
+ return `${t ? `export const ${e.optionsName}: CrudOption[] = [
649
+ ${r}
650
+ ];
651
+
652
+ ` : ""}export const ${e.textMapName}: Record<string, string> = {
653
+ ${n}
654
+ };`;
655
+ }
656
+ function K(e) {
657
+ const t = [...new Set(e.map((r) => r.enumName).filter(Boolean))];
658
+ return t.length === 0 ? "" : `import ${ke(t)} from "./enums";
659
+ `;
660
+ }
661
+ function Ve(e, t) {
662
+ return t === "list" ? `query${e}List` : t === "create" ? `create${e}` : t === "update" ? `update${e}` : `delete${e}`;
663
+ }
664
+ function f(e) {
665
+ return `/** ${e} */`;
666
+ }
667
+ async function Ze(e, t, r = !1) {
668
+ for (const n of t)
669
+ await He(C(e, n.path), n.content, r);
670
+ }
671
+ async function He(e, t, r) {
672
+ if (!r && await Ke(e))
673
+ throw new Error(`生成文件已存在:${e}。如需覆盖请传入 overwrite: true。`);
674
+ await Q(W(e), { recursive: !0 }), await X(e, We(t), "utf8");
675
+ }
676
+ async function Ke(e) {
677
+ try {
678
+ return await ee(e), !0;
679
+ } catch {
680
+ return !1;
681
+ }
682
+ }
683
+ function We(e) {
684
+ return e.endsWith(`
685
+ `) ? e : `${e}
686
+ `;
687
+ }
688
+ async function rt(e) {
689
+ const t = Ye(e), r = await we(t), n = Ce(r, t), o = Qe(t.cwd || ".", t.outDir, n.entityName), a = [n.list, n.create, n.update, n.delete].filter(Ue), i = me(r, a), s = le(r, n.list, i), p = fe(r, [n.create, n.update], i), u = de(r, n.list, i), d = {
690
+ tag: n.tag,
691
+ pageTitle: n.pageTitle,
692
+ entityName: n.entityName,
693
+ entityVariable: M(n.entityName),
694
+ permissionSuffix: n.entityName,
695
+ operations: n,
696
+ searchFields: s,
697
+ // 如果文档只有查询接口,也生成一个能跑起来的弹窗表单,业务后续再按真实新增/编辑接口调整。
698
+ modalFields: p.length > 0 ? p : s,
699
+ tableFields: u,
700
+ enums: [...i.values()],
701
+ generateTypeOptions: !!t.generateTypeOptions,
702
+ responsePath: ne(r, n.list),
703
+ requestAdapter: Xe(t.requestAdapter)
704
+ }, T = Oe(d).map((b) => ({
705
+ path: b.path,
706
+ content: b.content
707
+ }));
708
+ return t.dryRun || await Ze(o, T, t.overwrite), {
709
+ tag: d.tag,
710
+ pageTitle: d.pageTitle,
711
+ entityName: d.entityName,
712
+ outDir: o,
713
+ files: T,
714
+ dryRun: !!t.dryRun
715
+ };
716
+ }
717
+ function Ye(e) {
718
+ return typeof e == "string" ? { openapi: e } : e;
719
+ }
720
+ function Ue(e) {
721
+ return e !== void 0;
722
+ }
723
+ function Qe(e, t, r) {
724
+ const n = t || C("src", "views", `${r}Page`);
725
+ return Y(n) ? n : P(e, n);
726
+ }
727
+ function Xe(e) {
728
+ return {
729
+ importCode: e?.importCode || 'import { http } from "@/utils/http";',
730
+ renderCall: e?.renderCall || et
731
+ };
732
+ }
733
+ function et(e) {
734
+ const t = e.method.toLowerCase();
735
+ return t === "get" || t === "delete" ? `return http.${t}<${e.responseType}>(${JSON.stringify(e.path)}, { query: ${e.payloadName} });` : `return http.${t}<${e.responseType}, typeof ${e.payloadName}>(${JSON.stringify(e.path)}, ${e.payloadName});`;
736
+ }
737
+ export {
738
+ rt as generateCrudPage
739
+ };
740
+ //# sourceMappingURL=index.js.map