m8-mcp-server 1.0.4 → 1.0.6

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 (67) hide show
  1. package/README.md +184 -14
  2. package/dist/cli.d.ts +1 -1
  3. package/dist/cli.js +1 -1
  4. package/dist/docs/apis.d.ts +1 -1
  5. package/dist/docs/apis.js +1 -1
  6. package/dist/docs/components.d.ts +1 -1
  7. package/dist/docs/components.js +1 -1
  8. package/dist/docs/index.d.ts +1 -1
  9. package/dist/docs/index.js +1 -1
  10. package/dist/docs/loader.d.ts +1 -1
  11. package/dist/docs/loader.js +1 -1
  12. package/dist/docs/search.d.ts +1 -1
  13. package/dist/docs/search.js +1 -1
  14. package/dist/docs/standards.d.ts +1 -1
  15. package/dist/docs/standards.js +1 -1
  16. package/dist/docs/utils.d.ts +1 -1
  17. package/dist/docs/utils.js +1 -1
  18. package/dist/generator/header.d.ts +1 -1
  19. package/dist/generator/header.js +1 -1
  20. package/dist/generator/index.d.ts +1 -1
  21. package/dist/generator/index.js +1 -1
  22. package/dist/generator/vue-template.d.ts +22 -1
  23. package/dist/generator/vue-template.d.ts.map +1 -1
  24. package/dist/generator/vue-template.js +498 -34
  25. package/dist/generator/vue-template.js.map +1 -1
  26. package/dist/index.d.ts +1 -1
  27. package/dist/index.js +1 -1
  28. package/dist/recommend/index.d.ts +1 -1
  29. package/dist/recommend/index.js +1 -1
  30. package/dist/tools/generate-code.d.ts +1 -1
  31. package/dist/tools/generate-code.js +1 -1
  32. package/dist/tools/get-api-info.d.ts +1 -1
  33. package/dist/tools/get-api-info.js +1 -1
  34. package/dist/tools/get-coding-standard.d.ts +1 -1
  35. package/dist/tools/get-coding-standard.js +1 -1
  36. package/dist/tools/get-component-info.d.ts +1 -1
  37. package/dist/tools/get-component-info.js +1 -1
  38. package/dist/tools/get-util-info.d.ts +1 -1
  39. package/dist/tools/get-util-info.js +1 -1
  40. package/dist/tools/index.d.ts +1 -1
  41. package/dist/tools/index.js +1 -1
  42. package/dist/tools/recommend-solution.d.ts +1 -1
  43. package/dist/tools/recommend-solution.js +1 -1
  44. package/dist/tools/search-docs.d.ts +1 -1
  45. package/dist/tools/search-docs.js +1 -1
  46. package/dist/types/index.d.ts +1 -1
  47. package/dist/types/index.js +1 -1
  48. package/package.json +4 -4
  49. package/resources/cases/.gitkeep +0 -0
  50. package/resources/cases/form-submit-vue2.json +0 -15
  51. package/resources/cases/index.json +0 -8
  52. package/resources/cases/list-detail-vue2.json +0 -27
  53. package/resources/components/.gitkeep +0 -0
  54. package/resources/components/button.json +0 -74
  55. package/resources/components/cell.json +0 -69
  56. package/resources/components/field.json +0 -88
  57. package/resources/components/index.json +0 -72
  58. package/resources/standards/.gitkeep +0 -0
  59. package/resources/standards/css.json +0 -108
  60. package/resources/standards/javascript.json +0 -129
  61. package/resources/standards/project-structure.json +0 -101
  62. package/resources/standards/vue.json +0 -122
  63. package/resources/utils/.gitkeep +0 -0
  64. package/resources/utils/ajax.json +0 -76
  65. package/resources/utils/common.json +0 -129
  66. package/resources/utils/index.json +0 -7
  67. package/resources/utils/string.json +0 -112
@@ -1,32 +1,67 @@
1
1
  /**
2
2
  * Vue 模板生成器
3
- * @作者 M8 Team
3
+ * @作者 li peng
4
4
  * @创建时间 2024-12-29
5
5
  * @描述 生成 Vue2 和 Vue3 组件模板,严格遵循 M8 规范
6
6
  */
7
7
  import { generateVueFileHeader, generateFileHeader } from './header.js';
8
+ import { readdirSync, existsSync } from 'fs';
9
+ import { join, dirname } from 'path';
10
+ import { fileURLToPath } from 'url';
11
+ // ESM 兼容的 __dirname
12
+ const __vueTemplateFilename = fileURLToPath(import.meta.url);
13
+ const __vueTemplateDir = dirname(__vueTemplateFilename);
14
+ /**
15
+ * 从 resources/components 目录动态加载可用组件列表
16
+ */
17
+ function loadValidComponents() {
18
+ const componentsDir = join(__vueTemplateDir, '../resources/components');
19
+ const components = new Set();
20
+ // 默认组件列表(作为备用)
21
+ const fallbackComponents = [
22
+ 'em-actionsheet', 'em-amap', 'em-button', 'em-cell', 'em-checkbox',
23
+ 'em-circle', 'em-datepicker', 'em-field', 'em-form', 'em-header',
24
+ 'em-icon', 'em-loading', 'em-noticebar', 'em-numberkeyboard',
25
+ 'em-pagination', 'em-panel', 'em-passwordinput', 'em-picker',
26
+ 'em-popup', 'em-progress', 'em-radio', 'em-rate', 'em-search',
27
+ 'em-slider', 'em-stepper', 'em-swipecell', 'em-switch',
28
+ 'em-switchcell', 'em-tag', 'em-treeselect', 'em-uploader',
29
+ 'em-verifycode', 'em-minirefresh', 'em-layout', 'em-image',
30
+ 'em-toast', 'em-calendar', 'em-area', 'em-tab', 'em-dialog',
31
+ 'em-dropdownmenu', 'em-notify', 'em-overlay', 'em-collapse',
32
+ 'em-grid', 'em-countdown', 'em-divider', 'em-empty',
33
+ 'em-imagepreview', 'em-lazyload', 'em-skeleton', 'em-steps',
34
+ 'em-sticky', 'em-indexbar', 'em-sidebar', 'em-tabbar', 'em-badge',
35
+ 'em-popover', 'em-cascader', 'em-selectperson', 'em-swipe',
36
+ 'em-easycalendar', 'em-qrcode', 'em-imagescale', 'em-dragsort',
37
+ 'em-chart', 'em-rtc', 'em-table'
38
+ ];
39
+ try {
40
+ if (existsSync(componentsDir)) {
41
+ const files = readdirSync(componentsDir).filter(f => f.endsWith('.json') && f !== 'index.json');
42
+ for (const file of files) {
43
+ // 从文件名提取组件 ID,如 button.json -> em-button
44
+ const id = file.replace('.json', '');
45
+ components.add(`em-${id}`);
46
+ }
47
+ }
48
+ }
49
+ catch {
50
+ // 静默处理错误
51
+ }
52
+ // 如果动态加载失败,使用备用列表
53
+ if (components.size === 0) {
54
+ for (const comp of fallbackComponents) {
55
+ components.add(comp);
56
+ }
57
+ }
58
+ return components;
59
+ }
8
60
  /**
9
61
  * 可用的 M8 UI 组件列表
10
- * 只有这些组件才是有效的,其他组件名称会被过滤
62
+ * 从 resources/components 目录动态加载,确保与组件文档同步
11
63
  */
12
- const VALID_COMPONENTS = new Set([
13
- 'em-actionsheet', 'em-amap', 'em-button', 'em-cell', 'em-checkbox',
14
- 'em-circle', 'em-datepicker', 'em-field', 'em-form', 'em-header',
15
- 'em-icon', 'em-loading', 'em-noticebar', 'em-numberkeyboard',
16
- 'em-pagination', 'em-panel', 'em-passwordinput', 'em-picker',
17
- 'em-popup', 'em-progress', 'em-radio', 'em-rate', 'em-search',
18
- 'em-slider', 'em-stepper', 'em-swipecell', 'em-switch',
19
- 'em-switchcell', 'em-tag', 'em-treeselect', 'em-uploader',
20
- 'em-verifycode', 'em-minirefresh', 'em-layout', 'em-image',
21
- 'em-toast', 'em-calendar', 'em-area', 'em-tab', 'em-dialog',
22
- 'em-dropdownmenu', 'em-notify', 'em-overlay', 'em-collapse',
23
- 'em-grid', 'em-countdown', 'em-divider', 'em-empty',
24
- 'em-imagepreview', 'em-lazyload', 'em-skeleton', 'em-steps',
25
- 'em-sticky', 'em-indexbar', 'em-sidebar', 'em-tabbar', 'em-badge',
26
- 'em-popover', 'em-cascader', 'em-selectperson', 'em-swipe',
27
- 'em-easycalendar', 'em-qrcode', 'em-imagescale', 'em-dragsort',
28
- 'em-chart', 'em-rtc', 'em-table'
29
- ]);
64
+ const VALID_COMPONENTS = loadValidComponents();
30
65
  /**
31
66
  * 组件名称映射:常见错误名称 -> 正确名称
32
67
  * 确保使用者输入的无效组件名称能被正确映射到 M8 组件库中的有效组件
@@ -148,6 +183,91 @@ export function filterValidComponents(components) {
148
183
  }
149
184
  return { valid, warnings };
150
185
  }
186
+ /**
187
+ * 从需求描述中提取表单字段信息
188
+ * @param requirement 需求描述
189
+ * @returns 表单字段列表
190
+ */
191
+ export function extractFormFields(requirement) {
192
+ const fields = [];
193
+ const req = requirement.toLowerCase();
194
+ // 手机号相关
195
+ if (req.includes('手机号') || req.includes('手机') || req.includes('mobile')) {
196
+ fields.push({
197
+ name: '手机号',
198
+ propertyName: 'mobile',
199
+ type: 'tel',
200
+ validation: 'mobile',
201
+ validationMethod: 'Util.string.isMobile'
202
+ });
203
+ }
204
+ // 身份证相关
205
+ if (req.includes('身份证') || req.includes('idcard') || req.includes('证件号')) {
206
+ fields.push({
207
+ name: '身份证号',
208
+ propertyName: 'idCard',
209
+ type: 'text',
210
+ validation: 'idcard',
211
+ validationMethod: 'Util.string.isIdCard'
212
+ });
213
+ }
214
+ // 邮箱相关
215
+ if (req.includes('邮箱') || req.includes('email')) {
216
+ fields.push({
217
+ name: '邮箱',
218
+ propertyName: 'email',
219
+ type: 'text',
220
+ validation: 'email',
221
+ validationMethod: 'Util.string.isEmail'
222
+ });
223
+ }
224
+ // 电话相关(固定电话)
225
+ if (req.includes('固定电话') || req.includes('固话') || req.includes('座机')) {
226
+ fields.push({
227
+ name: '固定电话',
228
+ propertyName: 'tel',
229
+ type: 'tel',
230
+ validation: 'tel',
231
+ validationMethod: 'Util.string.isTel'
232
+ });
233
+ }
234
+ // 姓名相关
235
+ if (req.includes('姓名') || req.includes('名字')) {
236
+ fields.push({
237
+ name: '姓名',
238
+ propertyName: 'name',
239
+ type: 'text',
240
+ validation: 'required'
241
+ });
242
+ }
243
+ // 用户名相关
244
+ if (req.includes('用户名') || req.includes('username')) {
245
+ fields.push({
246
+ name: '用户名',
247
+ propertyName: 'username',
248
+ type: 'text',
249
+ validation: 'required'
250
+ });
251
+ }
252
+ // 密码相关
253
+ if (req.includes('密码') || req.includes('password')) {
254
+ fields.push({
255
+ name: '密码',
256
+ propertyName: 'password',
257
+ type: 'password',
258
+ validation: 'required'
259
+ });
260
+ }
261
+ // 附件/文件上传相关
262
+ if (req.includes('附件') || req.includes('上传') || req.includes('文件上传') || req.includes('upload')) {
263
+ fields.push({
264
+ name: '附件',
265
+ propertyName: 'attachments',
266
+ type: 'uploader'
267
+ });
268
+ }
269
+ return fields;
270
+ }
151
271
  /**
152
272
  * 根据需求智能推荐组件
153
273
  * @param requirement 需求描述
@@ -156,11 +276,12 @@ export function filterValidComponents(components) {
156
276
  export function suggestComponents(requirement) {
157
277
  const components = [];
158
278
  const req = requirement.toLowerCase();
279
+ const fields = extractFormFields(requirement);
159
280
  // 登录/表单相关
160
281
  if (req.includes('登录') || req.includes('login')) {
161
282
  components.push('em-field', 'em-button');
162
283
  }
163
- if (req.includes('表单') || req.includes('form')) {
284
+ if (req.includes('表单') || req.includes('form') || req.includes('提交')) {
164
285
  components.push('em-form', 'em-field', 'em-button');
165
286
  }
166
287
  if (req.includes('输入') || req.includes('input')) {
@@ -184,10 +305,23 @@ export function suggestComponents(requirement) {
184
305
  if (req.includes('搜索') || req.includes('search')) {
185
306
  components.push('em-search');
186
307
  }
187
- // 上传相关
188
- if (req.includes('上传') || req.includes('upload') || req.includes('图片')) {
308
+ // 上传相关 - 包含"附件"关键词
309
+ if (req.includes('上传') || req.includes('upload') || req.includes('附件') || req.includes('文件')) {
189
310
  components.push('em-uploader');
190
311
  }
312
+ // 根据提取的字段自动添加组件
313
+ for (const field of fields) {
314
+ if (field.type === 'uploader') {
315
+ components.push('em-uploader');
316
+ }
317
+ else {
318
+ components.push('em-field');
319
+ }
320
+ }
321
+ // 如果有表单字段,自动添加表单和按钮
322
+ if (fields.length > 0) {
323
+ components.push('em-form', 'em-button');
324
+ }
191
325
  // 去重
192
326
  return [...new Set(components)];
193
327
  }
@@ -211,10 +345,19 @@ export function generateVue2Template(options) {
211
345
  const finalComponents = validComponents.length > 0
212
346
  ? validComponents
213
347
  : suggestComponents(description);
348
+ // 提取表单字段信息
349
+ const formFields = extractFormFields(description);
214
350
  // 生成模板内容
215
351
  const templateContent = generateTemplateContent(finalComponents, name, description);
216
352
  // 生成 CSS 类名 (使用下划线风格,符合 M8 规范)
217
353
  const cssClassName = name.replace(/-/g, '_');
354
+ // 生成动态 formData 初始化
355
+ const formDataInit = generateFormDataInit(formFields);
356
+ // 生成校验方法
357
+ const validateMethod = generateValidateMethod(formFields);
358
+ // 检查是否需要 uploader 处理
359
+ const hasUploader = finalComponents.includes('em-uploader') || formFields.some(f => f.type === 'uploader');
360
+ const uploaderMethod = hasUploader ? generateUploaderMethod() : '';
218
361
  const template = `${header}
219
362
  <template>
220
363
  <div class="${cssClassName}">
@@ -231,7 +374,7 @@ export default {
231
374
  data() {
232
375
  return {
233
376
  // 表单数据
234
- formData: {},
377
+ formData: ${formDataInit},
235
378
  // 加载状态
236
379
  loading: false
237
380
  };
@@ -279,11 +422,16 @@ export default {
279
422
  ejs.ui.closeWaiting();
280
423
  }
281
424
  },
282
-
425
+ ${validateMethod}
283
426
  /**
284
427
  * 提交表单
285
428
  */
286
429
  async handleSubmit() {
430
+ // 表单校验
431
+ if (!this.validateForm()) {
432
+ return;
433
+ }
434
+
287
435
  try {
288
436
  ejs.ui.showWaiting({ message: '提交中...' });
289
437
 
@@ -307,7 +455,7 @@ export default {
307
455
  } finally {
308
456
  ejs.ui.closeWaiting();
309
457
  }
310
- }
458
+ }${uploaderMethod}
311
459
  }
312
460
  };
313
461
  </script>
@@ -318,6 +466,123 @@ export default {
318
466
  `;
319
467
  return template;
320
468
  }
469
+ /**
470
+ * 生成 formData 初始化代码
471
+ * @param fields 表单字段列表
472
+ * @returns formData 初始化对象字符串
473
+ */
474
+ function generateFormDataInit(fields) {
475
+ if (fields.length === 0) {
476
+ return '{}';
477
+ }
478
+ const props = [];
479
+ for (const field of fields) {
480
+ if (field.type === 'uploader') {
481
+ props.push(`${field.propertyName}: []`);
482
+ }
483
+ else {
484
+ props.push(`${field.propertyName}: ''`);
485
+ }
486
+ }
487
+ return `{\n ${props.join(',\n ')}\n }`;
488
+ }
489
+ /**
490
+ * 生成表单校验方法
491
+ * @param fields 表单字段列表
492
+ * @returns 校验方法代码
493
+ */
494
+ function generateValidateMethod(fields) {
495
+ if (fields.length === 0) {
496
+ return `
497
+ /**
498
+ * 表单校验
499
+ */
500
+ validateForm() {
501
+ return true;
502
+ },
503
+ `;
504
+ }
505
+ const validations = [];
506
+ for (const field of fields) {
507
+ if (field.validationMethod) {
508
+ // 使用 Util.string 校验方法
509
+ validations.push(` // 校验${field.name}
510
+ if (!this.formData.${field.propertyName}) {
511
+ ejs.ui.toast({ message: '请输入${field.name}' });
512
+ return false;
513
+ }
514
+ if (!${field.validationMethod}(this.formData.${field.propertyName})) {
515
+ ejs.ui.toast({ message: '${field.name}格式不正确' });
516
+ return false;
517
+ }`);
518
+ }
519
+ else if (field.validation === 'required' && field.type !== 'uploader') {
520
+ // 仅必填校验
521
+ validations.push(` // 校验${field.name}
522
+ if (!this.formData.${field.propertyName}) {
523
+ ejs.ui.toast({ message: '请输入${field.name}' });
524
+ return false;
525
+ }`);
526
+ }
527
+ }
528
+ return `
529
+ /**
530
+ * 表单校验
531
+ * 使用 Util.string 提供的校验方法,符合 M8 规范
532
+ */
533
+ validateForm() {
534
+ ${validations.join('\n\n')}
535
+
536
+ return true;
537
+ },
538
+ `;
539
+ }
540
+ /**
541
+ * 生成 uploader 处理方法
542
+ * @returns uploader 相关方法代码
543
+ */
544
+ function generateUploaderMethod() {
545
+ return `,
546
+
547
+ /**
548
+ * 文件上传后的回调
549
+ * @param file 上传的文件信息
550
+ * @param detail 额外信息
551
+ */
552
+ afterRead(file, detail) {
553
+ // 设置上传中状态
554
+ file.status = 'uploading';
555
+ file.message = '上传中...';
556
+
557
+ // TODO: 调用上传接口
558
+ // 示例:使用 Util.ajax 上传文件
559
+ // Util.ajax({
560
+ // url: Config.serverUrl + '/rest/api/upload',
561
+ // type: 'POST',
562
+ // data: {
563
+ // file: file.file
564
+ // }
565
+ // }).then(res => {
566
+ // if (res.status?.code === 1) {
567
+ // file.status = 'done';
568
+ // file.url = res.data.url;
569
+ // } else {
570
+ // file.status = 'failed';
571
+ // file.message = '上传失败';
572
+ // }
573
+ // }).catch(() => {
574
+ // file.status = 'failed';
575
+ // file.message = '上传失败';
576
+ // });
577
+
578
+ // 模拟上传成功
579
+ setTimeout(() => {
580
+ file.status = 'done';
581
+ file.message = '';
582
+ console.log('文件上传完成:', file);
583
+ }, 1000);
584
+ }`;
585
+ }
321
586
  /**
322
587
  * 生成 Vue3 组件模板 (Composition API with script setup)
323
588
  * 严格遵循 M8 规范:样式外部引入、使用 ejs.ui API
@@ -337,10 +602,19 @@ export function generateVue3Template(options) {
337
602
  const finalComponents = validComponents.length > 0
338
603
  ? validComponents
339
604
  : suggestComponents(description);
605
+ // 提取表单字段信息
606
+ const formFields = extractFormFields(description);
340
607
  // 生成模板内容
341
608
  const templateContent = generateTemplateContent(finalComponents, name, description);
342
609
  // 生成 CSS 类名
343
610
  const cssClassName = name.replace(/-/g, '_');
611
+ // 生成动态 formData 初始化
612
+ const formDataInitVue3 = generateFormDataInitVue3(formFields);
613
+ // 生成校验方法
614
+ const validateMethodVue3 = generateValidateMethodVue3(formFields);
615
+ // 检查是否需要 uploader 处理
616
+ const hasUploader = finalComponents.includes('em-uploader') || formFields.some(f => f.type === 'uploader');
617
+ const uploaderMethodVue3 = hasUploader ? generateUploaderMethodVue3() : '';
344
618
  const template = `${header}
345
619
  <template>
346
620
  <div class="${cssClassName}">
@@ -356,7 +630,7 @@ ${templateContent}
356
630
  import { ref, reactive, onMounted } from 'vue';
357
631
 
358
632
  // ==================== 响应式数据 ====================
359
- const formData = reactive({});
633
+ const formData = reactive(${formDataInitVue3});
360
634
  const loading = ref(false);
361
635
 
362
636
  // ==================== 生命周期 ====================
@@ -403,11 +677,16 @@ const loadData = async () => {
403
677
  ejs.ui.closeWaiting();
404
678
  }
405
679
  };
406
-
680
+ ${validateMethodVue3}
407
681
  /**
408
682
  * 提交表单
409
683
  */
410
684
  const handleSubmit = async () => {
685
+ // 表单校验
686
+ if (!validateForm()) {
687
+ return;
688
+ }
689
+
411
690
  try {
412
691
  ejs.ui.showWaiting({ message: '提交中...' });
413
692
 
@@ -432,6 +711,7 @@ const handleSubmit = async () => {
432
711
  ejs.ui.closeWaiting();
433
712
  }
434
713
  };
714
+ ${uploaderMethodVue3}
435
715
  </script>
436
716
 
437
717
  <style lang="scss" scoped>
@@ -440,6 +720,122 @@ const handleSubmit = async () => {
440
720
  `;
441
721
  return template;
442
722
  }
723
+ /**
724
+ * 生成 Vue3 formData 初始化代码
725
+ * @param fields 表单字段列表
726
+ * @returns formData 初始化对象字符串
727
+ */
728
+ function generateFormDataInitVue3(fields) {
729
+ if (fields.length === 0) {
730
+ return '{}';
731
+ }
732
+ const props = [];
733
+ for (const field of fields) {
734
+ if (field.type === 'uploader') {
735
+ props.push(`${field.propertyName}: []`);
736
+ }
737
+ else {
738
+ props.push(`${field.propertyName}: ''`);
739
+ }
740
+ }
741
+ return `{\n ${props.join(',\n ')}\n}`;
742
+ }
743
+ /**
744
+ * 生成 Vue3 表单校验方法
745
+ * @param fields 表单字段列表
746
+ * @returns 校验方法代码
747
+ */
748
+ function generateValidateMethodVue3(fields) {
749
+ if (fields.length === 0) {
750
+ return `
751
+ /**
752
+ * 表单校验
753
+ */
754
+ const validateForm = () => {
755
+ return true;
756
+ };
757
+ `;
758
+ }
759
+ const validations = [];
760
+ for (const field of fields) {
761
+ if (field.validationMethod) {
762
+ // 使用 Util.string 校验方法
763
+ validations.push(` // 校验${field.name}
764
+ if (!formData.${field.propertyName}) {
765
+ ejs.ui.toast({ message: '请输入${field.name}' });
766
+ return false;
767
+ }
768
+ if (!${field.validationMethod}(formData.${field.propertyName})) {
769
+ ejs.ui.toast({ message: '${field.name}格式不正确' });
770
+ return false;
771
+ }`);
772
+ }
773
+ else if (field.validation === 'required' && field.type !== 'uploader') {
774
+ // 仅必填校验
775
+ validations.push(` // 校验${field.name}
776
+ if (!formData.${field.propertyName}) {
777
+ ejs.ui.toast({ message: '请输入${field.name}' });
778
+ return false;
779
+ }`);
780
+ }
781
+ }
782
+ return `
783
+ /**
784
+ * 表单校验
785
+ * 使用 Util.string 提供的校验方法,符合 M8 规范
786
+ */
787
+ const validateForm = () => {
788
+ ${validations.join('\n\n')}
789
+
790
+ return true;
791
+ };
792
+ `;
793
+ }
794
+ /**
795
+ * 生成 Vue3 uploader 处理方法
796
+ * @returns uploader 相关方法代码
797
+ */
798
+ function generateUploaderMethodVue3() {
799
+ return `
800
+ /**
801
+ * 文件上传后的回调
802
+ * @param file 上传的文件信息
803
+ * @param detail 额外信息
804
+ */
805
+ const afterRead = (file, detail) => {
806
+ // 设置上传中状态
807
+ file.status = 'uploading';
808
+ file.message = '上传中...';
809
+
810
+ // TODO: 调用上传接口
811
+ // 示例:使用 Util.ajax 上传文件
812
+ // Util.ajax({
813
+ // url: Config.serverUrl + '/rest/api/upload',
814
+ // type: 'POST',
815
+ // data: {
816
+ // file: file.file
817
+ // }
818
+ // }).then(res => {
819
+ // if (res.status?.code === 1) {
820
+ // file.status = 'done';
821
+ // file.url = res.data.url;
822
+ // } else {
823
+ // file.status = 'failed';
824
+ // file.message = '上传失败';
825
+ // }
826
+ // }).catch(() => {
827
+ // file.status = 'failed';
828
+ // file.message = '上传失败';
829
+ // });
830
+
831
+ // 模拟上传成功
832
+ setTimeout(() => {
833
+ file.status = 'done';
834
+ file.message = '';
835
+ console.log('文件上传完成:', file);
836
+ }, 1000);
837
+ };`;
838
+ }
443
839
  /**
444
840
  * 根据组件列表生成模板内容
445
841
  * @param components 组件列表
@@ -451,12 +847,17 @@ function generateTemplateContent(components, name, description) {
451
847
  const lines = [];
452
848
  const cssClassName = name.replace(/-/g, '_');
453
849
  const req = description.toLowerCase();
850
+ // 提取表单字段信息
851
+ const formFields = extractFormFields(description);
454
852
  // 判断页面类型
455
- const isLoginPage = req.includes('登录') || req.includes('login');
456
- const isFormPage = req.includes('表单') || req.includes('form') || isLoginPage;
853
+ // 注意:如果有除用户名/密码之外的明确字段,应该使用动态表单模板
854
+ const hasCustomFields = formFields.some(f => !['username', 'password'].includes(f.propertyName));
855
+ const isLoginPage = (req.includes('登录') || req.includes('login')) && !hasCustomFields;
856
+ const isFormPage = req.includes('表单') || req.includes('form') || req.includes('提交') || formFields.length > 0;
457
857
  const isListPage = req.includes('列表') || req.includes('list');
458
- if (isLoginPage) {
459
- // 登录页面模板
858
+ const hasUploader = components.includes('em-uploader') || formFields.some(f => f.type === 'uploader');
859
+ if (isLoginPage && !hasCustomFields) {
860
+ // 纯登录页面模板(只有用户名和密码)
460
861
  lines.push(` <!-- 登录表单 -->`);
461
862
  lines.push(` <div class="${cssClassName}__form">`);
462
863
  lines.push(` <em-field`);
@@ -464,6 +865,7 @@ function generateTemplateContent(components, name, description) {
464
865
  lines.push(` label="用户名"`);
465
866
  lines.push(` placeholder="请输入用户名"`);
466
867
  lines.push(` clearable`);
868
+ lines.push(` required`);
467
869
  lines.push(` />`);
468
870
  lines.push(` <em-field`);
469
871
  lines.push(` v-model="formData.password"`);
@@ -471,14 +873,59 @@ function generateTemplateContent(components, name, description) {
471
873
  lines.push(` label="密码"`);
472
874
  lines.push(` placeholder="请输入密码"`);
473
875
  lines.push(` clearable`);
876
+ lines.push(` required`);
474
877
  lines.push(` />`);
475
878
  lines.push(` <div class="${cssClassName}__actions">`);
476
879
  lines.push(` <em-button type="primary" block @click="handleSubmit">登录</em-button>`);
477
880
  lines.push(` </div>`);
478
881
  lines.push(` </div>`);
479
882
  }
883
+ else if (isFormPage && formFields.length > 0) {
884
+ // 有明确字段的表单页面模板
885
+ lines.push(` <!-- 表单内容 -->`);
886
+ lines.push(` <em-form ref="formRef">`);
887
+ // 根据提取的字段生成表单项
888
+ for (const field of formFields) {
889
+ if (field.type === 'uploader') {
890
+ // 文件上传组件
891
+ lines.push(` <!-- ${field.name}上传 -->`);
892
+ lines.push(` <div class="${cssClassName}__uploader">`);
893
+ lines.push(` <div class="${cssClassName}__uploader-label">${field.name}</div>`);
894
+ lines.push(` <em-uploader`);
895
+ lines.push(` v-model="formData.${field.propertyName}"`);
896
+ lines.push(` :after-read="afterRead"`);
897
+ lines.push(` :max-count="9"`);
898
+ lines.push(` multiple`);
899
+ lines.push(` />`);
900
+ lines.push(` </div>`);
901
+ }
902
+ else {
903
+ // 普通输入字段
904
+ lines.push(` <em-field`);
905
+ lines.push(` v-model="formData.${field.propertyName}"`);
906
+ if (field.type === 'tel') {
907
+ lines.push(` type="tel"`);
908
+ }
909
+ else if (field.type === 'password') {
910
+ lines.push(` type="password"`);
911
+ }
912
+ lines.push(` label="${field.name}"`);
913
+ lines.push(` placeholder="请输入${field.name}"`);
914
+ lines.push(` clearable`);
915
+ if (field.validation === 'required' || field.validationMethod) {
916
+ lines.push(` required`);
917
+ }
918
+ lines.push(` />`);
919
+ }
920
+ }
921
+ lines.push(` <div class="${cssClassName}__actions">`);
922
+ const buttonText = req.includes('登录') ? '登录' : '提交';
923
+ lines.push(` <em-button type="primary" block @click="handleSubmit">${buttonText}</em-button>`);
924
+ lines.push(` </div>`);
925
+ lines.push(` </em-form>`);
926
+ }
480
927
  else if (isFormPage) {
481
- // 通用表单页面模板
928
+ // 通用表单页面模板(没有明确字段)
482
929
  lines.push(` <!-- 表单内容 -->`);
483
930
  lines.push(` <em-form ref="formRef">`);
484
931
  for (const comp of components) {
@@ -490,6 +937,18 @@ function generateTemplateContent(components, name, description) {
490
937
  lines.push(` required`);
491
938
  lines.push(` />`);
492
939
  }
940
+ else if (comp === 'em-uploader') {
941
+ lines.push(` <!-- 附件上传 -->`);
942
+ lines.push(` <div class="${cssClassName}__uploader">`);
943
+ lines.push(` <div class="${cssClassName}__uploader-label">附件</div>`);
944
+ lines.push(` <em-uploader`);
945
+ lines.push(` v-model="formData.attachments"`);
946
+ lines.push(` :after-read="afterRead"`);
947
+ lines.push(` :max-count="9"`);
948
+ lines.push(` multiple`);
949
+ lines.push(` />`);
950
+ lines.push(` </div>`);
951
+ }
493
952
  }
494
953
  lines.push(` <div class="${cssClassName}__actions">`);
495
954
  lines.push(` <em-button type="primary" block @click="handleSubmit">提交</em-button>`);
@@ -518,7 +977,12 @@ function generateTemplateContent(components, name, description) {
518
977
  lines.push(` <div class="${cssClassName}__content">`);
519
978
  for (const comp of components) {
520
979
  const compTag = comp.startsWith('em-') ? comp : `em-${comp}`;
521
- lines.push(` <${compTag} />`);
980
+ if (comp === 'em-uploader') {
981
+ lines.push(` <em-uploader v-model="fileList" :after-read="afterRead" />`);
982
+ }
983
+ else {
984
+ lines.push(` <${compTag} />`);
985
+ }
522
986
  }
523
987
  if (components.length === 0) {
524
988
  lines.push(` <!-- TODO: 添加页面内容 -->`);