m8-mcp-server 1.0.6 → 1.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.
Files changed (67) hide show
  1. package/dist/cli.d.ts +1 -1
  2. package/dist/cli.js +1 -13
  3. package/dist/docs/apis.d.ts +1 -1
  4. package/dist/docs/apis.js +9 -326
  5. package/dist/docs/components.d.ts +1 -1
  6. package/dist/docs/components.js +2 -186
  7. package/dist/docs/index.d.ts +1 -1
  8. package/dist/docs/index.js +18 -177
  9. package/dist/docs/loader.d.ts +1 -1
  10. package/dist/docs/loader.js +1 -165
  11. package/dist/docs/search.d.ts +1 -1
  12. package/dist/docs/search.js +2 -196
  13. package/dist/docs/standards.d.ts +1 -1
  14. package/dist/docs/standards.js +3 -134
  15. package/dist/docs/utils.d.ts +1 -1
  16. package/dist/docs/utils.js +3 -129
  17. package/dist/generator/header.d.ts +1 -1
  18. package/dist/generator/header.js +3 -83
  19. package/dist/generator/index.d.ts +1 -1
  20. package/dist/generator/index.js +283 -648
  21. package/dist/generator/vue-template.d.ts +1 -1
  22. package/dist/generator/vue-template.js +336 -1016
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +1 -73
  25. package/dist/recommend/index.d.ts +1 -1
  26. package/dist/recommend/index.js +2 -412
  27. package/dist/tools/generate-code.d.ts +1 -1
  28. package/dist/tools/generate-code.js +39 -211
  29. package/dist/tools/get-api-info.d.ts +1 -1
  30. package/dist/tools/get-api-info.js +1 -65
  31. package/dist/tools/get-coding-standard.d.ts +1 -1
  32. package/dist/tools/get-coding-standard.js +1 -66
  33. package/dist/tools/get-component-info.d.ts +1 -1
  34. package/dist/tools/get-component-info.js +1 -60
  35. package/dist/tools/get-util-info.d.ts +1 -1
  36. package/dist/tools/get-util-info.js +1 -65
  37. package/dist/tools/index.d.ts +1 -1
  38. package/dist/tools/index.js +3 -101
  39. package/dist/tools/recommend-solution.d.ts +1 -1
  40. package/dist/tools/recommend-solution.js +1 -67
  41. package/dist/tools/search-docs.d.ts +1 -1
  42. package/dist/tools/search-docs.js +1 -71
  43. package/dist/types/index.d.ts +1 -1
  44. package/dist/types/index.js +0 -8
  45. package/package.json +4 -2
  46. package/dist/cli.js.map +0 -1
  47. package/dist/docs/apis.js.map +0 -1
  48. package/dist/docs/components.js.map +0 -1
  49. package/dist/docs/index.js.map +0 -1
  50. package/dist/docs/loader.js.map +0 -1
  51. package/dist/docs/search.js.map +0 -1
  52. package/dist/docs/standards.js.map +0 -1
  53. package/dist/docs/utils.js.map +0 -1
  54. package/dist/generator/header.js.map +0 -1
  55. package/dist/generator/index.js.map +0 -1
  56. package/dist/generator/vue-template.js.map +0 -1
  57. package/dist/index.js.map +0 -1
  58. package/dist/recommend/index.js.map +0 -1
  59. package/dist/tools/generate-code.js.map +0 -1
  60. package/dist/tools/get-api-info.js.map +0 -1
  61. package/dist/tools/get-coding-standard.js.map +0 -1
  62. package/dist/tools/get-component-info.js.map +0 -1
  63. package/dist/tools/get-util-info.js.map +0 -1
  64. package/dist/tools/index.js.map +0 -1
  65. package/dist/tools/recommend-solution.js.map +0 -1
  66. package/dist/tools/search-docs.js.map +0 -1
  67. package/dist/types/index.js.map +0 -1
@@ -1,1034 +1,354 @@
1
- /**
2
- * Vue 模板生成器
3
- * @作者 li peng
4
- * @创建时间 2024-12-29
5
- * @描述 生成 Vue2 和 Vue3 组件模板,严格遵循 M8 规范
6
- */
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
- }
60
- /**
61
- * 可用的 M8 UI 组件列表
62
- * 从 resources/components 目录动态加载,确保与组件文档同步
63
- */
64
- const VALID_COMPONENTS = loadValidComponents();
65
- /**
66
- * 组件名称映射:常见错误名称 -> 正确名称
67
- * 确保使用者输入的无效组件名称能被正确映射到 M8 组件库中的有效组件
68
- */
69
- const COMPONENT_NAME_MAP = {
70
- // 输入框类组件 - 统一使用 em-field
71
- 'em-input': 'em-field',
72
- 'em-text-field': 'em-field',
73
- 'em-textfield': 'em-field',
74
- 'em-textarea': 'em-field',
75
- 'em-text': 'em-field',
76
- 'input': 'em-field',
77
- 'text-field': 'em-field',
78
- // 选择器类组件 - 统一使用 em-picker
79
- 'em-select': 'em-picker',
80
- 'em-dropdown': 'em-picker',
81
- 'select': 'em-picker',
82
- // 日期时间类组件 - 统一使用 em-datepicker
83
- 'em-date': 'em-datepicker',
84
- 'em-time': 'em-datepicker',
85
- 'em-datetime': 'em-datepicker',
86
- 'date-picker': 'em-datepicker',
87
- // 导航类组件 - 统一使用 em-header
88
- 'em-nav': 'em-header',
89
- 'em-navbar': 'em-header',
90
- 'em-navigation': 'em-header',
91
- 'navbar': 'em-header',
92
- // 列表类组件 - 统一使用 em-cell
93
- 'em-list': 'em-cell',
94
- 'em-item': 'em-cell',
95
- 'em-list-item': 'em-cell',
96
- 'list-item': 'em-cell',
97
- // 按钮类组件
98
- 'btn': 'em-button',
99
- // Toast/提示类 - 这些应该用 API 而非组件,添加警告
100
- 'em-message': '', // 应使用 ejs.ui.toast
101
- 'em-alert': '', // 应使用 ejs.ui.alert
102
- };
103
- /**
104
- * 验证并修正组件名称
105
- * @param componentName 组件名称
106
- * @returns 修正后的组件名称,如果无效则返回 null;如果应该使用 API 则返回 { useApi: true }
107
- */
108
- export function validateComponentName(componentName) {
109
- // 标准化组件名称
110
- let normalized = componentName.toLowerCase().trim();
111
- // 首先检查是否在映射表中(不管有没有 em- 前缀)
112
- if (COMPONENT_NAME_MAP[normalized] !== undefined) {
113
- const mapped = COMPONENT_NAME_MAP[normalized];
114
- // 如果映射到空字符串,表示应该使用 API 而非组件
115
- if (mapped === '') {
116
- return null;
117
- }
118
- normalized = mapped;
119
- }
120
- else {
121
- // 如果没有 em- 前缀,添加它再检查
122
- if (!normalized.startsWith('em-')) {
123
- const withPrefix = `em-${normalized}`;
124
- if (COMPONENT_NAME_MAP[withPrefix] !== undefined) {
125
- const mapped = COMPONENT_NAME_MAP[withPrefix];
126
- if (mapped === '') {
127
- return null;
128
- }
129
- normalized = mapped;
130
- }
131
- else {
132
- normalized = withPrefix;
133
- }
134
- }
135
- }
136
- // 验证组件是否存在
137
- if (VALID_COMPONENTS.has(normalized)) {
138
- return normalized;
139
- }
140
- return null;
141
- }
142
- /**
143
- * 过滤并验证组件列表
144
- * @param components 组件列表
145
- * @returns 验证后的组件列表和警告信息
146
- */
147
- export function filterValidComponents(components) {
148
- const valid = [];
149
- const warnings = [];
150
- // 应该使用 API 而非组件的映射
151
- const API_SUGGESTIONS = {
152
- 'em-message': 'ejs.ui.toast({ message: "提示内容" })',
153
- 'em-alert': 'ejs.ui.alert({ title: "标题", message: "内容" })',
154
- 'em-toast': 'ejs.ui.toast({ message: "提示内容" })',
155
- 'em-loading': 'ejs.ui.showWaiting({ message: "加载中..." })',
156
- 'em-confirm': 'ejs.ui.confirm({ title: "确认", message: "确认操作吗?" })',
157
- 'message': 'ejs.ui.toast({ message: "提示内容" })',
158
- 'alert': 'ejs.ui.alert({ title: "标题", message: "内容" })',
159
- 'toast': 'ejs.ui.toast({ message: "提示内容" })',
160
- 'loading': 'ejs.ui.showWaiting({ message: "加载中..." })',
1
+ import{generateVueFileHeader as j,generateFileHeader as k}from"./header.js";import{readdirSync as T,existsSync as D}from"fs";import{join as F,dirname as M}from"path";import{fileURLToPath as U}from"url";const _=U(import.meta.url),S=M(_);function P(){const i=F(S,"../resources/components"),s=new Set,e=["em-actionsheet","em-amap","em-button","em-cell","em-checkbox","em-circle","em-datepicker","em-field","em-form","em-header","em-icon","em-loading","em-noticebar","em-numberkeyboard","em-pagination","em-panel","em-passwordinput","em-picker","em-popup","em-progress","em-radio","em-rate","em-search","em-slider","em-stepper","em-swipecell","em-switch","em-switchcell","em-tag","em-treeselect","em-uploader","em-verifycode","em-minirefresh","em-layout","em-image","em-toast","em-calendar","em-area","em-tab","em-dialog","em-dropdownmenu","em-notify","em-overlay","em-collapse","em-grid","em-countdown","em-divider","em-empty","em-imagepreview","em-lazyload","em-skeleton","em-steps","em-sticky","em-indexbar","em-sidebar","em-tabbar","em-badge","em-popover","em-cascader","em-selectperson","em-swipe","em-easycalendar","em-qrcode","em-imagescale","em-dragsort","em-chart","em-rtc","em-table"];try{if(D(i)){const t=T(i).filter(a=>a.endsWith(".json")&&a!=="index.json");for(const a of t){const r=a.replace(".json","");s.add(`em-${r}`)}}}catch{}if(s.size===0)for(const t of e)s.add(t);return s}const V=P(),f={"em-input":"em-field","em-text-field":"em-field","em-textfield":"em-field","em-textarea":"em-field","em-text":"em-field",input:"em-field","text-field":"em-field","em-select":"em-picker","em-dropdown":"em-picker",select:"em-picker","em-date":"em-datepicker","em-time":"em-datepicker","em-datetime":"em-datepicker","date-picker":"em-datepicker","em-nav":"em-header","em-navbar":"em-header","em-navigation":"em-header",navbar:"em-header","em-list":"em-cell","em-item":"em-cell","em-list-item":"em-cell","list-item":"em-cell",btn:"em-button","em-message":"","em-alert":""};function I(i){let s=i.toLowerCase().trim();if(f[s]!==void 0){const e=f[s];if(e==="")return null;s=e}else if(!s.startsWith("em-")){const e=`em-${s}`;if(f[e]!==void 0){const t=f[e];if(t==="")return null;s=t}else s=e}return V.has(s)?s:null}function x(i){const s=[],e=[],t={"em-message":'ejs.ui.toast({ message: "\u63D0\u793A\u5185\u5BB9" })',"em-alert":'ejs.ui.alert({ title: "\u6807\u9898", message: "\u5185\u5BB9" })',"em-toast":'ejs.ui.toast({ message: "\u63D0\u793A\u5185\u5BB9" })',"em-loading":'ejs.ui.showWaiting({ message: "\u52A0\u8F7D\u4E2D..." })',"em-confirm":'ejs.ui.confirm({ title: "\u786E\u8BA4", message: "\u786E\u8BA4\u64CD\u4F5C\u5417?" })',message:'ejs.ui.toast({ message: "\u63D0\u793A\u5185\u5BB9" })',alert:'ejs.ui.alert({ title: "\u6807\u9898", message: "\u5185\u5BB9" })',toast:'ejs.ui.toast({ message: "\u63D0\u793A\u5185\u5BB9" })',loading:'ejs.ui.showWaiting({ message: "\u52A0\u8F7D\u4E2D..." })'};for(const a of i){const r=a.toLowerCase().trim();if(t[r]){e.push(`"${a}" \u5EFA\u8BAE\u4F7F\u7528 API \u65B9\u5F0F\u8C03\u7528\uFF1A${t[r]}`);continue}const l=I(a);l?(s.includes(l)||s.push(l),(a.toLowerCase().startsWith("em-")?a:`em-${a}`).toLowerCase()!==l&&e.push(`\u7EC4\u4EF6 "${a}" \u5DF2\u81EA\u52A8\u4FEE\u6B63\u4E3A "${l}"`)):e.push(`\u7EC4\u4EF6 "${a}" \u4E0D\u5B58\u5728\u4E8E M8 \u7EC4\u4EF6\u5E93\u4E2D\uFF0C\u5DF2\u5FFD\u7565`)}return{valid:s,warnings:e}}function h(i){const s=[],e=i.toLowerCase();return(e.includes("\u624B\u673A\u53F7")||e.includes("\u624B\u673A")||e.includes("mobile"))&&s.push({name:"\u624B\u673A\u53F7",propertyName:"mobile",type:"tel",validation:"mobile",validationMethod:"Util.string.isMobile"}),(e.includes("\u8EAB\u4EFD\u8BC1")||e.includes("idcard")||e.includes("\u8BC1\u4EF6\u53F7"))&&s.push({name:"\u8EAB\u4EFD\u8BC1\u53F7",propertyName:"idCard",type:"text",validation:"idcard",validationMethod:"Util.string.isIdCard"}),(e.includes("\u90AE\u7BB1")||e.includes("email"))&&s.push({name:"\u90AE\u7BB1",propertyName:"email",type:"text",validation:"email",validationMethod:"Util.string.isEmail"}),(e.includes("\u56FA\u5B9A\u7535\u8BDD")||e.includes("\u56FA\u8BDD")||e.includes("\u5EA7\u673A"))&&s.push({name:"\u56FA\u5B9A\u7535\u8BDD",propertyName:"tel",type:"tel",validation:"tel",validationMethod:"Util.string.isTel"}),(e.includes("\u59D3\u540D")||e.includes("\u540D\u5B57"))&&s.push({name:"\u59D3\u540D",propertyName:"name",type:"text",validation:"required"}),(e.includes("\u7528\u6237\u540D")||e.includes("username"))&&s.push({name:"\u7528\u6237\u540D",propertyName:"username",type:"text",validation:"required"}),(e.includes("\u5BC6\u7801")||e.includes("password"))&&s.push({name:"\u5BC6\u7801",propertyName:"password",type:"password",validation:"required"}),(e.includes("\u9644\u4EF6")||e.includes("\u4E0A\u4F20")||e.includes("\u6587\u4EF6\u4E0A\u4F20")||e.includes("upload"))&&s.push({name:"\u9644\u4EF6",propertyName:"attachments",type:"uploader"}),s}function w(i){const s=[],e=i.toLowerCase(),t=h(i);(e.includes("\u767B\u5F55")||e.includes("login"))&&s.push("em-field","em-button"),(e.includes("\u8868\u5355")||e.includes("form")||e.includes("\u63D0\u4EA4"))&&s.push("em-form","em-field","em-button"),(e.includes("\u8F93\u5165")||e.includes("input"))&&s.push("em-field"),(e.includes("\u6309\u94AE")||e.includes("button"))&&s.push("em-button"),(e.includes("\u5217\u8868")||e.includes("list"))&&s.push("em-cell","em-minirefresh"),(e.includes("\u9009\u62E9")||e.includes("picker")||e.includes("\u4E0B\u62C9"))&&s.push("em-picker"),(e.includes("\u65E5\u671F")||e.includes("date"))&&s.push("em-datepicker"),(e.includes("\u641C\u7D22")||e.includes("search"))&&s.push("em-search"),(e.includes("\u4E0A\u4F20")||e.includes("upload")||e.includes("\u9644\u4EF6")||e.includes("\u6587\u4EF6"))&&s.push("em-uploader");for(const a of t)a.type==="uploader"?s.push("em-uploader"):s.push("em-field");return t.length>0&&s.push("em-form","em-button"),[...new Set(s)]}function q(i){const{name:s,description:e,components:t=[],author:a,useMock:r=!0,pageTitle:l="\u9875\u9762\u6807\u9898"}=i,u={author:a,description:e},d=j(u),m=k(u),{valid:p,warnings:c}=x(t),n=p.length>0?p:w(e),o=h(e),g=O(n,s,e),v=s.replace(/-/g,"_"),b=W(o),y=L(o),$=n.includes("em-uploader")||o.some(N=>N.type==="uploader")?R():"";return`${d}
2
+ <template>
3
+ <div class="${v}">
4
+ ${g}
5
+ </div>
6
+ </template>
7
+
8
+ <script>
9
+ ${m}
10
+
11
+ export default {
12
+ name: '${J(s)}',
13
+
14
+ data() {
15
+ return {
16
+ // \u8868\u5355\u6570\u636E
17
+ formData: ${b},
18
+ // \u52A0\u8F7D\u72B6\u6001
19
+ loading: false
161
20
  };
162
- for (const comp of components) {
163
- const normalized = comp.toLowerCase().trim();
164
- // 检查是否应该使用 API
165
- if (API_SUGGESTIONS[normalized]) {
166
- warnings.push(`"${comp}" 建议使用 API 方式调用:${API_SUGGESTIONS[normalized]}`);
167
- continue;
168
- }
169
- const validated = validateComponentName(comp);
170
- if (validated) {
171
- if (!valid.includes(validated)) {
172
- valid.push(validated);
173
- }
174
- // 如果名称被修正了,添加警告
175
- const original = comp.toLowerCase().startsWith('em-') ? comp : `em-${comp}`;
176
- if (original.toLowerCase() !== validated) {
177
- warnings.push(`组件 "${comp}" 已自动修正为 "${validated}"`);
178
- }
179
- }
180
- else {
181
- warnings.push(`组件 "${comp}" 不存在于 M8 组件库中,已忽略`);
182
- }
183
- }
184
- return { valid, warnings };
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'
21
+ },
22
+
23
+ created() {
24
+ this.initPage();
25
+ },
26
+
27
+ methods: {
28
+ /**
29
+ * \u521D\u59CB\u5316\u9875\u9762
30
+ */
31
+ initPage() {
32
+ this.loadData();
33
+ },
34
+
35
+ /**
36
+ * \u52A0\u8F7D\u6570\u636E
37
+ */
38
+ async loadData() {
39
+ try {
40
+ this.loading = true;
41
+ ejs.ui.showWaiting({ message: '\u52A0\u8F7D\u4E2D...' });
42
+
43
+ const result = await Util.ajax({
44
+ url: Config.serverUrl + '${r?"/rest/mock/"+s:"/api/"+s}',
45
+ type: 'POST',
46
+ data: {
47
+ params: JSON.stringify({})
48
+ }
250
49
  });
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
- }
271
- /**
272
- * 根据需求智能推荐组件
273
- * @param requirement 需求描述
274
- * @returns 推荐的组件列表
275
- */
276
- export function suggestComponents(requirement) {
277
- const components = [];
278
- const req = requirement.toLowerCase();
279
- const fields = extractFormFields(requirement);
280
- // 登录/表单相关
281
- if (req.includes('登录') || req.includes('login')) {
282
- components.push('em-field', 'em-button');
283
- }
284
- if (req.includes('表单') || req.includes('form') || req.includes('提交')) {
285
- components.push('em-form', 'em-field', 'em-button');
286
- }
287
- if (req.includes('输入') || req.includes('input')) {
288
- components.push('em-field');
289
- }
290
- if (req.includes('按钮') || req.includes('button')) {
291
- components.push('em-button');
292
- }
293
- // 列表相关
294
- if (req.includes('列表') || req.includes('list')) {
295
- components.push('em-cell', 'em-minirefresh');
296
- }
297
- // 选择器相关
298
- if (req.includes('选择') || req.includes('picker') || req.includes('下拉')) {
299
- components.push('em-picker');
300
- }
301
- if (req.includes('日期') || req.includes('date')) {
302
- components.push('em-datepicker');
303
- }
304
- // 搜索相关
305
- if (req.includes('搜索') || req.includes('search')) {
306
- components.push('em-search');
307
- }
308
- // 上传相关 - 包含"附件"关键词
309
- if (req.includes('上传') || req.includes('upload') || req.includes('附件') || req.includes('文件')) {
310
- components.push('em-uploader');
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
- }
325
- // 去重
326
- return [...new Set(components)];
327
- }
328
- /**
329
- * 生成 Vue2 组件模板 (Options API)
330
- * 严格遵循 M8 规范:样式外部引入、使用 ejs.ui API
331
- * @param options 模板选项
332
- * @returns Vue2 组件代码
333
- */
334
- export function generateVue2Template(options) {
335
- const { name, description, components = [], author, useMock = true, pageTitle = '页面标题' } = options;
336
- const headerOptions = {
337
- author,
338
- description
339
- };
340
- const header = generateVueFileHeader(headerOptions);
341
- const scriptHeader = generateFileHeader(headerOptions);
342
- // 验证组件
343
- const { valid: validComponents, warnings } = filterValidComponents(components);
344
- // 如果没有指定组件,根据需求智能推荐
345
- const finalComponents = validComponents.length > 0
346
- ? validComponents
347
- : suggestComponents(description);
348
- // 提取表单字段信息
349
- const formFields = extractFormFields(description);
350
- // 生成模板内容
351
- const templateContent = generateTemplateContent(finalComponents, name, description);
352
- // 生成 CSS 类名 (使用下划线风格,符合 M8 规范)
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() : '';
361
- const template = `${header}
362
- <template>
363
- <div class="${cssClassName}">
364
- ${templateContent}
365
- </div>
366
- </template>
367
-
368
- <script>
369
- ${scriptHeader}
370
-
371
- export default {
372
- name: '${toPascalCase(name)}',
373
-
374
- data() {
375
- return {
376
- // 表单数据
377
- formData: ${formDataInit},
378
- // 加载状态
379
- loading: false
380
- };
381
- },
382
-
383
- created() {
384
- this.initPage();
385
- },
386
-
387
- methods: {
388
- /**
389
- * 初始化页面
390
- */
391
- initPage() {
392
- this.loadData();
393
- },
394
-
395
- /**
396
- * 加载数据
397
- */
398
- async loadData() {
399
- try {
400
- this.loading = true;
401
- ejs.ui.showWaiting({ message: '加载中...' });
402
-
403
- const result = await Util.ajax({
404
- url: Config.serverUrl + '${useMock ? '/rest/mock/' + name : '/api/' + name}',
405
- type: 'POST',
406
- data: {
407
- params: JSON.stringify({})
408
- }
409
- });
410
-
411
- if (result.status?.code === 1) {
412
- // TODO: 处理数据
413
- console.log('数据加载成功:', result.data);
414
- } else {
415
- ejs.ui.toast({ message: result.status?.text || '加载失败' });
416
- }
417
- } catch (error) {
418
- console.error('数据加载失败:', error);
419
- ejs.ui.toast({ message: '网络错误,请重试' });
420
- } finally {
421
- this.loading = false;
422
- ejs.ui.closeWaiting();
423
- }
424
- },
425
- ${validateMethod}
426
- /**
427
- * 提交表单
428
- */
429
- async handleSubmit() {
430
- // 表单校验
431
- if (!this.validateForm()) {
432
- return;
433
- }
434
-
435
- try {
436
- ejs.ui.showWaiting({ message: '提交中...' });
437
-
438
- const result = await Util.ajax({
439
- url: Config.serverUrl + '${useMock ? '/rest/mock/' + name + '/submit' : '/api/' + name + '/submit'}',
440
- type: 'POST',
441
- data: {
442
- params: JSON.stringify(this.formData)
443
- }
444
- });
445
-
446
- if (result.status?.code === 1) {
447
- ejs.ui.toast({ message: '提交成功' });
448
- // TODO: 处理成功逻辑
449
- } else {
450
- ejs.ui.toast({ message: result.status?.text || '提交失败' });
451
- }
452
- } catch (error) {
453
- console.error('提交失败:', error);
454
- ejs.ui.toast({ message: '网络错误,请重试' });
455
- } finally {
456
- ejs.ui.closeWaiting();
457
- }
458
- }${uploaderMethod}
459
- }
460
- };
461
- </script>
462
-
463
- <style lang="scss" scoped>
464
- @import './css/${name}.scss';
465
- </style>
466
- `;
467
- return template;
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
- }
586
- /**
587
- * 生成 Vue3 组件模板 (Composition API with script setup)
588
- * 严格遵循 M8 规范:样式外部引入、使用 ejs.ui API
589
- * @param options 模板选项
590
- * @returns Vue3 组件代码
591
- */
592
- export function generateVue3Template(options) {
593
- const { name, description, components = [], author, useMock = true, pageTitle = '页面标题' } = options;
594
- const headerOptions = {
595
- author,
596
- description
597
- };
598
- const header = generateVueFileHeader(headerOptions);
599
- // 验证组件
600
- const { valid: validComponents } = filterValidComponents(components);
601
- // 如果没有指定组件,根据需求智能推荐
602
- const finalComponents = validComponents.length > 0
603
- ? validComponents
604
- : suggestComponents(description);
605
- // 提取表单字段信息
606
- const formFields = extractFormFields(description);
607
- // 生成模板内容
608
- const templateContent = generateTemplateContent(finalComponents, name, description);
609
- // 生成 CSS 类名
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() : '';
618
- const template = `${header}
619
- <template>
620
- <div class="${cssClassName}">
621
- ${templateContent}
622
- </div>
623
- </template>
624
-
625
- <script setup>
626
- /**
627
- * @描述 ${description}
628
- */
629
-
630
- import { ref, reactive, onMounted } from 'vue';
631
-
632
- // ==================== 响应式数据 ====================
633
- const formData = reactive(${formDataInitVue3});
634
- const loading = ref(false);
635
-
636
- // ==================== 生命周期 ====================
637
- onMounted(() => {
638
- initPage();
639
- });
640
-
641
- // ==================== 方法 ====================
642
-
643
- /**
644
- * 初始化页面
645
- */
646
- const initPage = () => {
647
- loadData();
648
- };
649
-
650
- /**
651
- * 加载数据
652
- */
653
- const loadData = async () => {
654
- try {
655
- loading.value = true;
656
- ejs.ui.showWaiting({ message: '加载中...' });
657
-
658
- const result = await Util.ajax({
659
- url: Config.serverUrl + '${useMock ? '/rest/mock/' + name : '/api/' + name}',
660
- type: 'POST',
661
- data: {
662
- params: JSON.stringify({})
663
- }
664
- });
665
-
666
- if (result.status?.code === 1) {
667
- // TODO: 处理数据
668
- console.log('数据加载成功:', result.data);
669
- } else {
670
- ejs.ui.toast({ message: result.status?.text || '加载失败' });
671
- }
672
- } catch (error) {
673
- console.error('数据加载失败:', error);
674
- ejs.ui.toast({ message: '网络错误,请重试' });
675
- } finally {
676
- loading.value = false;
677
- ejs.ui.closeWaiting();
678
- }
679
- };
680
- ${validateMethodVue3}
681
- /**
682
- * 提交表单
683
- */
684
- const handleSubmit = async () => {
685
- // 表单校验
686
- if (!validateForm()) {
687
- return;
688
- }
689
-
690
- try {
691
- ejs.ui.showWaiting({ message: '提交中...' });
692
-
693
- const result = await Util.ajax({
694
- url: Config.serverUrl + '${useMock ? '/rest/mock/' + name + '/submit' : '/api/' + name + '/submit'}',
695
- type: 'POST',
696
- data: {
697
- params: JSON.stringify(formData)
698
- }
699
- });
700
-
701
- if (result.status?.code === 1) {
702
- ejs.ui.toast({ message: '提交成功' });
703
- // TODO: 处理成功逻辑
704
- } else {
705
- ejs.ui.toast({ message: result.status?.text || '提交失败' });
706
- }
707
- } catch (error) {
708
- console.error('提交失败:', error);
709
- ejs.ui.toast({ message: '网络错误,请重试' });
710
- } finally {
711
- ejs.ui.closeWaiting();
712
- }
713
- };
714
- ${uploaderMethodVue3}
715
- </script>
716
-
717
- <style lang="scss" scoped>
718
- @import './css/${name}.scss';
719
- </style>
720
- `;
721
- return template;
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}: []`);
50
+
51
+ if (result.status?.code === 1) {
52
+ // TODO: \u5904\u7406\u6570\u636E
53
+ console.log('\u6570\u636E\u52A0\u8F7D\u6210\u529F:', result.data);
54
+ } else {
55
+ ejs.ui.toast({ message: result.status?.text || '\u52A0\u8F7D\u5931\u8D25' });
736
56
  }
737
- else {
738
- props.push(`${field.propertyName}: ''`);
57
+ } catch (error) {
58
+ console.error('\u6570\u636E\u52A0\u8F7D\u5931\u8D25:', error);
59
+ ejs.ui.toast({ message: '\u7F51\u7EDC\u9519\u8BEF\uFF0C\u8BF7\u91CD\u8BD5' });
60
+ } finally {
61
+ this.loading = false;
62
+ ejs.ui.closeWaiting();
63
+ }
64
+ },
65
+ ${y}
66
+ /**
67
+ * \u63D0\u4EA4\u8868\u5355
68
+ */
69
+ async handleSubmit() {
70
+ // \u8868\u5355\u6821\u9A8C
71
+ if (!this.validateForm()) {
72
+ return;
73
+ }
74
+
75
+ try {
76
+ ejs.ui.showWaiting({ message: '\u63D0\u4EA4\u4E2D...' });
77
+
78
+ const result = await Util.ajax({
79
+ url: Config.serverUrl + '${r?"/rest/mock/"+s+"/submit":"/api/"+s+"/submit"}',
80
+ type: 'POST',
81
+ data: {
82
+ params: JSON.stringify(this.formData)
83
+ }
84
+ });
85
+
86
+ if (result.status?.code === 1) {
87
+ ejs.ui.toast({ message: '\u63D0\u4EA4\u6210\u529F' });
88
+ // TODO: \u5904\u7406\u6210\u529F\u903B\u8F91
89
+ } else {
90
+ ejs.ui.toast({ message: result.status?.text || '\u63D0\u4EA4\u5931\u8D25' });
739
91
  }
740
- }
741
- return `{\n ${props.join(',\n ')}\n}`;
742
- }
92
+ } catch (error) {
93
+ console.error('\u63D0\u4EA4\u5931\u8D25:', error);
94
+ ejs.ui.toast({ message: '\u7F51\u7EDC\u9519\u8BEF\uFF0C\u8BF7\u91CD\u8BD5' });
95
+ } finally {
96
+ ejs.ui.closeWaiting();
97
+ }
98
+ }${$}
99
+ }
100
+ };
101
+ </script>
102
+
103
+ <style lang="scss" scoped>
104
+ @import './css/${s}.scss';
105
+ </style>
106
+ `}function W(i){if(i.length===0)return"{}";const s=[];for(const e of i)e.type==="uploader"?s.push(`${e.propertyName}: []`):s.push(`${e.propertyName}: ''`);return`{
107
+ ${s.join(`,
108
+ `)}
109
+ }`}function L(i){if(i.length===0)return`
110
+ /**
111
+ * \u8868\u5355\u6821\u9A8C
112
+ */
113
+ validateForm() {
114
+ return true;
115
+ },
116
+ `;const s=[];for(const e of i)e.validationMethod?s.push(` // \u6821\u9A8C${e.name}
117
+ if (!this.formData.${e.propertyName}) {
118
+ ejs.ui.toast({ message: '\u8BF7\u8F93\u5165${e.name}' });
119
+ return false;
120
+ }
121
+ if (!${e.validationMethod}(this.formData.${e.propertyName})) {
122
+ ejs.ui.toast({ message: '${e.name}\u683C\u5F0F\u4E0D\u6B63\u786E' });
123
+ return false;
124
+ }`):e.validation==="required"&&e.type!=="uploader"&&s.push(` // \u6821\u9A8C${e.name}
125
+ if (!this.formData.${e.propertyName}) {
126
+ ejs.ui.toast({ message: '\u8BF7\u8F93\u5165${e.name}' });
127
+ return false;
128
+ }`);return`
129
+ /**
130
+ * \u8868\u5355\u6821\u9A8C
131
+ * \u4F7F\u7528 Util.string \u63D0\u4F9B\u7684\u6821\u9A8C\u65B9\u6CD5\uFF0C\u7B26\u5408 M8 \u89C4\u8303
132
+ */
133
+ validateForm() {
134
+ ${s.join(`
135
+
136
+ `)}
137
+
138
+ return true;
139
+ },
140
+ `}function R(){return`,
141
+
142
+ /**
143
+ * \u6587\u4EF6\u4E0A\u4F20\u540E\u7684\u56DE\u8C03
144
+ * @param file \u4E0A\u4F20\u7684\u6587\u4EF6\u4FE1\u606F
145
+ * @param detail \u989D\u5916\u4FE1\u606F
146
+ */
147
+ afterRead(file, detail) {
148
+ // \u8BBE\u7F6E\u4E0A\u4F20\u4E2D\u72B6\u6001
149
+ file.status = 'uploading';
150
+ file.message = '\u4E0A\u4F20\u4E2D...';
151
+
152
+ // TODO: \u8C03\u7528\u4E0A\u4F20\u63A5\u53E3
153
+ // \u793A\u4F8B\uFF1A\u4F7F\u7528 Util.ajax \u4E0A\u4F20\u6587\u4EF6
154
+ // Util.ajax({
155
+ // url: Config.serverUrl + '/rest/api/upload',
156
+ // type: 'POST',
157
+ // data: {
158
+ // file: file.file
159
+ // }
160
+ // }).then(res => {
161
+ // if (res.status?.code === 1) {
162
+ // file.status = 'done';
163
+ // file.url = res.data.url;
164
+ // } else {
165
+ // file.status = 'failed';
166
+ // file.message = '\u4E0A\u4F20\u5931\u8D25';
167
+ // }
168
+ // }).catch(() => {
169
+ // file.status = 'failed';
170
+ // file.message = '\u4E0A\u4F20\u5931\u8D25';
171
+ // });
172
+
173
+ // \u6A21\u62DF\u4E0A\u4F20\u6210\u529F
174
+ setTimeout(() => {
175
+ file.status = 'done';
176
+ file.message = '';
177
+ console.log('\u6587\u4EF6\u4E0A\u4F20\u5B8C\u6210:', file);
178
+ }, 1000);
179
+ }`}function A(i){const{name:s,description:e,components:t=[],author:a,useMock:r=!0,pageTitle:l="\u9875\u9762\u6807\u9898"}=i,d=j({author:a,description:e}),{valid:m}=x(t),p=m.length>0?m:w(e),c=h(e),n=O(p,s,e),o=s.replace(/-/g,"_"),g=H(c),v=E(c),y=p.includes("em-uploader")||c.some($=>$.type==="uploader")?z():"";return`${d}
180
+ <template>
181
+ <div class="${o}">
182
+ ${n}
183
+ </div>
184
+ </template>
185
+
186
+ <script setup>
743
187
  /**
744
- * 生成 Vue3 表单校验方法
745
- * @param fields 表单字段列表
746
- * @returns 校验方法代码
188
+ * @\u63CF\u8FF0 ${e}
747
189
  */
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
- }
190
+
191
+ import { ref, reactive, onMounted } from 'vue';
192
+
193
+ // ==================== \u54CD\u5E94\u5F0F\u6570\u636E ====================
194
+ const formData = reactive(${g});
195
+ const loading = ref(false);
196
+
197
+ // ==================== \u751F\u547D\u5468\u671F ====================
198
+ onMounted(() => {
199
+ initPage();
200
+ });
201
+
202
+ // ==================== \u65B9\u6CD5 ====================
203
+
794
204
  /**
795
- * 生成 Vue3 uploader 处理方法
796
- * @returns uploader 相关方法代码
205
+ * \u521D\u59CB\u5316\u9875\u9762
797
206
  */
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
- }
207
+ const initPage = () => {
208
+ loadData();
209
+ };
210
+
839
211
  /**
840
- * 根据组件列表生成模板内容
841
- * @param components 组件列表
842
- * @param name 页面名称
843
- * @param description 需求描述
844
- * @returns 模板内容
212
+ * \u52A0\u8F7D\u6570\u636E
845
213
  */
846
- function generateTemplateContent(components, name, description) {
847
- const lines = [];
848
- const cssClassName = name.replace(/-/g, '_');
849
- const req = description.toLowerCase();
850
- // 提取表单字段信息
851
- const formFields = extractFormFields(description);
852
- // 判断页面类型
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;
857
- const isListPage = req.includes('列表') || req.includes('list');
858
- const hasUploader = components.includes('em-uploader') || formFields.some(f => f.type === 'uploader');
859
- if (isLoginPage && !hasCustomFields) {
860
- // 纯登录页面模板(只有用户名和密码)
861
- lines.push(` <!-- 登录表单 -->`);
862
- lines.push(` <div class="${cssClassName}__form">`);
863
- lines.push(` <em-field`);
864
- lines.push(` v-model="formData.username"`);
865
- lines.push(` label="用户名"`);
866
- lines.push(` placeholder="请输入用户名"`);
867
- lines.push(` clearable`);
868
- lines.push(` required`);
869
- lines.push(` />`);
870
- lines.push(` <em-field`);
871
- lines.push(` v-model="formData.password"`);
872
- lines.push(` type="password"`);
873
- lines.push(` label="密码"`);
874
- lines.push(` placeholder="请输入密码"`);
875
- lines.push(` clearable`);
876
- lines.push(` required`);
877
- lines.push(` />`);
878
- lines.push(` <div class="${cssClassName}__actions">`);
879
- lines.push(` <em-button type="primary" block @click="handleSubmit">登录</em-button>`);
880
- lines.push(` </div>`);
881
- lines.push(` </div>`);
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
- }
927
- else if (isFormPage) {
928
- // 通用表单页面模板(没有明确字段)
929
- lines.push(` <!-- 表单内容 -->`);
930
- lines.push(` <em-form ref="formRef">`);
931
- for (const comp of components) {
932
- if (comp === 'em-field') {
933
- lines.push(` <em-field`);
934
- lines.push(` v-model="formData.field1"`);
935
- lines.push(` label="字段名"`);
936
- lines.push(` placeholder="请输入"`);
937
- lines.push(` required`);
938
- lines.push(` />`);
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
- }
952
- }
953
- lines.push(` <div class="${cssClassName}__actions">`);
954
- lines.push(` <em-button type="primary" block @click="handleSubmit">提交</em-button>`);
955
- lines.push(` </div>`);
956
- lines.push(` </em-form>`);
957
- }
958
- else if (isListPage) {
959
- // 列表页面模板
960
- lines.push(` <!-- 列表内容 -->`);
961
- lines.push(` <em-minirefresh`);
962
- lines.push(` @refresh="onRefresh"`);
963
- lines.push(` @loadmore="onLoadMore"`);
964
- lines.push(` >`);
965
- lines.push(` <em-cell`);
966
- lines.push(` v-for="item in list"`);
967
- lines.push(` :key="item.id"`);
968
- lines.push(` :title="item.title"`);
969
- lines.push(` is-link`);
970
- lines.push(` @click="handleItemClick(item)"`);
971
- lines.push(` />`);
972
- lines.push(` </em-minirefresh>`);
973
- }
974
- else {
975
- // 默认页面模板
976
- lines.push(` <!-- 页面内容 -->`);
977
- lines.push(` <div class="${cssClassName}__content">`);
978
- for (const comp of components) {
979
- const compTag = comp.startsWith('em-') ? comp : `em-${comp}`;
980
- if (comp === 'em-uploader') {
981
- lines.push(` <em-uploader v-model="fileList" :after-read="afterRead" />`);
982
- }
983
- else {
984
- lines.push(` <${compTag} />`);
985
- }
986
- }
987
- if (components.length === 0) {
988
- lines.push(` <!-- TODO: 添加页面内容 -->`);
989
- }
990
- lines.push(` </div>`);
991
- }
992
- return lines.join('\n');
993
- }
214
+ const loadData = async () => {
215
+ try {
216
+ loading.value = true;
217
+ ejs.ui.showWaiting({ message: '\u52A0\u8F7D\u4E2D...' });
218
+
219
+ const result = await Util.ajax({
220
+ url: Config.serverUrl + '${r?"/rest/mock/"+s:"/api/"+s}',
221
+ type: 'POST',
222
+ data: {
223
+ params: JSON.stringify({})
224
+ }
225
+ });
226
+
227
+ if (result.status?.code === 1) {
228
+ // TODO: \u5904\u7406\u6570\u636E
229
+ console.log('\u6570\u636E\u52A0\u8F7D\u6210\u529F:', result.data);
230
+ } else {
231
+ ejs.ui.toast({ message: result.status?.text || '\u52A0\u8F7D\u5931\u8D25' });
232
+ }
233
+ } catch (error) {
234
+ console.error('\u6570\u636E\u52A0\u8F7D\u5931\u8D25:', error);
235
+ ejs.ui.toast({ message: '\u7F51\u7EDC\u9519\u8BEF\uFF0C\u8BF7\u91CD\u8BD5' });
236
+ } finally {
237
+ loading.value = false;
238
+ ejs.ui.closeWaiting();
239
+ }
240
+ };
241
+ ${v}
994
242
  /**
995
- * 转换为 PascalCase
996
- * @param str 输入字符串
997
- * @returns PascalCase 字符串
243
+ * \u63D0\u4EA4\u8868\u5355
998
244
  */
999
- function toPascalCase(str) {
1000
- return str
1001
- .split('-')
1002
- .map(part => part.charAt(0).toUpperCase() + part.slice(1))
1003
- .join('');
1004
- }
245
+ const handleSubmit = async () => {
246
+ // \u8868\u5355\u6821\u9A8C
247
+ if (!validateForm()) {
248
+ return;
249
+ }
250
+
251
+ try {
252
+ ejs.ui.showWaiting({ message: '\u63D0\u4EA4\u4E2D...' });
253
+
254
+ const result = await Util.ajax({
255
+ url: Config.serverUrl + '${r?"/rest/mock/"+s+"/submit":"/api/"+s+"/submit"}',
256
+ type: 'POST',
257
+ data: {
258
+ params: JSON.stringify(formData)
259
+ }
260
+ });
261
+
262
+ if (result.status?.code === 1) {
263
+ ejs.ui.toast({ message: '\u63D0\u4EA4\u6210\u529F' });
264
+ // TODO: \u5904\u7406\u6210\u529F\u903B\u8F91
265
+ } else {
266
+ ejs.ui.toast({ message: result.status?.text || '\u63D0\u4EA4\u5931\u8D25' });
267
+ }
268
+ } catch (error) {
269
+ console.error('\u63D0\u4EA4\u5931\u8D25:', error);
270
+ ejs.ui.toast({ message: '\u7F51\u7EDC\u9519\u8BEF\uFF0C\u8BF7\u91CD\u8BD5' });
271
+ } finally {
272
+ ejs.ui.closeWaiting();
273
+ }
274
+ };
275
+ ${y}
276
+ </script>
277
+
278
+ <style lang="scss" scoped>
279
+ @import './css/${s}.scss';
280
+ </style>
281
+ `}function H(i){if(i.length===0)return"{}";const s=[];for(const e of i)e.type==="uploader"?s.push(`${e.propertyName}: []`):s.push(`${e.propertyName}: ''`);return`{
282
+ ${s.join(`,
283
+ `)}
284
+ }`}function E(i){if(i.length===0)return`
1005
285
  /**
1006
- * 根据版本生成 Vue 模板
1007
- * @param options 模板选项
1008
- * @returns Vue 组件代码
286
+ * \u8868\u5355\u6821\u9A8C
1009
287
  */
1010
- export function generateVueTemplate(options) {
1011
- if (options.vueVersion === 3) {
1012
- return generateVue3Template(options);
1013
- }
1014
- return generateVue2Template(options);
1015
- }
288
+ const validateForm = () => {
289
+ return true;
290
+ };
291
+ `;const s=[];for(const e of i)e.validationMethod?s.push(` // \u6821\u9A8C${e.name}
292
+ if (!formData.${e.propertyName}) {
293
+ ejs.ui.toast({ message: '\u8BF7\u8F93\u5165${e.name}' });
294
+ return false;
295
+ }
296
+ if (!${e.validationMethod}(formData.${e.propertyName})) {
297
+ ejs.ui.toast({ message: '${e.name}\u683C\u5F0F\u4E0D\u6B63\u786E' });
298
+ return false;
299
+ }`):e.validation==="required"&&e.type!=="uploader"&&s.push(` // \u6821\u9A8C${e.name}
300
+ if (!formData.${e.propertyName}) {
301
+ ejs.ui.toast({ message: '\u8BF7\u8F93\u5165${e.name}' });
302
+ return false;
303
+ }`);return`
1016
304
  /**
1017
- * 检查代码是否使用 Options API (Vue2 风格)
1018
- * @param code 代码字符串
1019
- * @returns 是否使用 Options API
305
+ * \u8868\u5355\u6821\u9A8C
306
+ * \u4F7F\u7528 Util.string \u63D0\u4F9B\u7684\u6821\u9A8C\u65B9\u6CD5\uFF0C\u7B26\u5408 M8 \u89C4\u8303
1020
307
  */
1021
- export function isOptionsApi(code) {
1022
- return code.includes('export default {') &&
1023
- (code.includes('data()') || code.includes('methods:') || code.includes('computed:'));
1024
- }
308
+ const validateForm = () => {
309
+ ${s.join(`
310
+
311
+ `)}
312
+
313
+ return true;
314
+ };
315
+ `}function z(){return`
1025
316
  /**
1026
- * 检查代码是否使用 Composition API (Vue3 风格)
1027
- * @param code 代码字符串
1028
- * @returns 是否使用 Composition API
317
+ * \u6587\u4EF6\u4E0A\u4F20\u540E\u7684\u56DE\u8C03
318
+ * @param file \u4E0A\u4F20\u7684\u6587\u4EF6\u4FE1\u606F
319
+ * @param detail \u989D\u5916\u4FE1\u606F
1029
320
  */
1030
- export function isCompositionApi(code) {
1031
- return code.includes('<script setup>') ||
1032
- (code.includes('setup()') && code.includes('return {'));
1033
- }
1034
- //# sourceMappingURL=vue-template.js.map
321
+ const afterRead = (file, detail) => {
322
+ // \u8BBE\u7F6E\u4E0A\u4F20\u4E2D\u72B6\u6001
323
+ file.status = 'uploading';
324
+ file.message = '\u4E0A\u4F20\u4E2D...';
325
+
326
+ // TODO: \u8C03\u7528\u4E0A\u4F20\u63A5\u53E3
327
+ // \u793A\u4F8B\uFF1A\u4F7F\u7528 Util.ajax \u4E0A\u4F20\u6587\u4EF6
328
+ // Util.ajax({
329
+ // url: Config.serverUrl + '/rest/api/upload',
330
+ // type: 'POST',
331
+ // data: {
332
+ // file: file.file
333
+ // }
334
+ // }).then(res => {
335
+ // if (res.status?.code === 1) {
336
+ // file.status = 'done';
337
+ // file.url = res.data.url;
338
+ // } else {
339
+ // file.status = 'failed';
340
+ // file.message = '\u4E0A\u4F20\u5931\u8D25';
341
+ // }
342
+ // }).catch(() => {
343
+ // file.status = 'failed';
344
+ // file.message = '\u4E0A\u4F20\u5931\u8D25';
345
+ // });
346
+
347
+ // \u6A21\u62DF\u4E0A\u4F20\u6210\u529F
348
+ setTimeout(() => {
349
+ file.status = 'done';
350
+ file.message = '';
351
+ console.log('\u6587\u4EF6\u4E0A\u4F20\u5B8C\u6210:', file);
352
+ }, 1000);
353
+ };`}function O(i,s,e){const t=[],a=s.replace(/-/g,"_"),r=e.toLowerCase(),l=h(e),u=l.some(n=>!["username","password"].includes(n.propertyName)),d=(r.includes("\u767B\u5F55")||r.includes("login"))&&!u,m=r.includes("\u8868\u5355")||r.includes("form")||r.includes("\u63D0\u4EA4")||l.length>0,p=r.includes("\u5217\u8868")||r.includes("list"),c=i.includes("em-uploader")||l.some(n=>n.type==="uploader");if(d&&!u)t.push(" <!-- \u767B\u5F55\u8868\u5355 -->"),t.push(` <div class="${a}__form">`),t.push(" <em-field"),t.push(' v-model="formData.username"'),t.push(' label="\u7528\u6237\u540D"'),t.push(' placeholder="\u8BF7\u8F93\u5165\u7528\u6237\u540D"'),t.push(" clearable"),t.push(" required"),t.push(" />"),t.push(" <em-field"),t.push(' v-model="formData.password"'),t.push(' type="password"'),t.push(' label="\u5BC6\u7801"'),t.push(' placeholder="\u8BF7\u8F93\u5165\u5BC6\u7801"'),t.push(" clearable"),t.push(" required"),t.push(" />"),t.push(` <div class="${a}__actions">`),t.push(' <em-button type="primary" block @click="handleSubmit">\u767B\u5F55</em-button>'),t.push(" </div>"),t.push(" </div>");else if(m&&l.length>0){t.push(" <!-- \u8868\u5355\u5185\u5BB9 -->"),t.push(' <em-form ref="formRef">');for(const o of l)o.type==="uploader"?(t.push(` <!-- ${o.name}\u4E0A\u4F20 -->`),t.push(` <div class="${a}__uploader">`),t.push(` <div class="${a}__uploader-label">${o.name}</div>`),t.push(" <em-uploader"),t.push(` v-model="formData.${o.propertyName}"`),t.push(' :after-read="afterRead"'),t.push(' :max-count="9"'),t.push(" multiple"),t.push(" />"),t.push(" </div>")):(t.push(" <em-field"),t.push(` v-model="formData.${o.propertyName}"`),o.type==="tel"?t.push(' type="tel"'):o.type==="password"&&t.push(' type="password"'),t.push(` label="${o.name}"`),t.push(` placeholder="\u8BF7\u8F93\u5165${o.name}"`),t.push(" clearable"),(o.validation==="required"||o.validationMethod)&&t.push(" required"),t.push(" />"));t.push(` <div class="${a}__actions">`);const n=r.includes("\u767B\u5F55")?"\u767B\u5F55":"\u63D0\u4EA4";t.push(` <em-button type="primary" block @click="handleSubmit">${n}</em-button>`),t.push(" </div>"),t.push(" </em-form>")}else if(m){t.push(" <!-- \u8868\u5355\u5185\u5BB9 -->"),t.push(' <em-form ref="formRef">');for(const n of i)n==="em-field"?(t.push(" <em-field"),t.push(' v-model="formData.field1"'),t.push(' label="\u5B57\u6BB5\u540D"'),t.push(' placeholder="\u8BF7\u8F93\u5165"'),t.push(" required"),t.push(" />")):n==="em-uploader"&&(t.push(" <!-- \u9644\u4EF6\u4E0A\u4F20 -->"),t.push(` <div class="${a}__uploader">`),t.push(` <div class="${a}__uploader-label">\u9644\u4EF6</div>`),t.push(" <em-uploader"),t.push(' v-model="formData.attachments"'),t.push(' :after-read="afterRead"'),t.push(' :max-count="9"'),t.push(" multiple"),t.push(" />"),t.push(" </div>"));t.push(` <div class="${a}__actions">`),t.push(' <em-button type="primary" block @click="handleSubmit">\u63D0\u4EA4</em-button>'),t.push(" </div>"),t.push(" </em-form>")}else if(p)t.push(" <!-- \u5217\u8868\u5185\u5BB9 -->"),t.push(" <em-minirefresh"),t.push(' @refresh="onRefresh"'),t.push(' @loadmore="onLoadMore"'),t.push(" >"),t.push(" <em-cell"),t.push(' v-for="item in list"'),t.push(' :key="item.id"'),t.push(' :title="item.title"'),t.push(" is-link"),t.push(' @click="handleItemClick(item)"'),t.push(" />"),t.push(" </em-minirefresh>");else{t.push(" <!-- \u9875\u9762\u5185\u5BB9 -->"),t.push(` <div class="${a}__content">`);for(const n of i){const o=n.startsWith("em-")?n:`em-${n}`;n==="em-uploader"?t.push(' <em-uploader v-model="fileList" :after-read="afterRead" />'):t.push(` <${o} />`)}i.length===0&&t.push(" <!-- TODO: \u6DFB\u52A0\u9875\u9762\u5185\u5BB9 -->"),t.push(" </div>")}return t.join(`
354
+ `)}function J(i){return i.split("-").map(s=>s.charAt(0).toUpperCase()+s.slice(1)).join("")}function Y(i){return i.vueVersion===3?A(i):q(i)}function Z(i){return i.includes("export default {")&&(i.includes("data()")||i.includes("methods:")||i.includes("computed:"))}function ee(i){return i.includes("<script setup>")||i.includes("setup()")&&i.includes("return {")}export{h as extractFormFields,x as filterValidComponents,q as generateVue2Template,A as generateVue3Template,Y as generateVueTemplate,ee as isCompositionApi,Z as isOptionsApi,w as suggestComponents,I as validateComponentName};