diff --git a/apps/jingrow/frontend/src/locales/zh-CN.json b/apps/jingrow/frontend/src/locales/zh-CN.json
index e879e63..a79eeb3 100644
--- a/apps/jingrow/frontend/src/locales/zh-CN.json
+++ b/apps/jingrow/frontend/src/locales/zh-CN.json
@@ -703,6 +703,7 @@
"Toolbar": "工具栏",
"Sidebar": "侧边栏",
"Page": "页面",
+ "Select Field Types": "选择字段类型",
"Media Resources": "媒体资源",
"Attachments": "附件",
diff --git a/apps/jingrow/frontend/src/views/dev/CreatePagetypeTemplate.vue b/apps/jingrow/frontend/src/views/dev/CreatePagetypeTemplate.vue
index 15745e4..e0be963 100644
--- a/apps/jingrow/frontend/src/views/dev/CreatePagetypeTemplate.vue
+++ b/apps/jingrow/frontend/src/views/dev/CreatePagetypeTemplate.vue
@@ -9,11 +9,23 @@
-
+
-
+
+
+
+
@@ -61,11 +73,43 @@ const loading = ref(false)
const submitting = ref(false)
const result = ref(null)
const formRef = ref(null)
-const form = ref({ pagetype: '', createFrontend: false, createBackend: true, frontendOptions: ['toolbar'] })
+const form = ref({ pagetype: '', createFrontend: false, createBackend: true, frontendOptions: ['toolbar'], fieldTypeNames: [] })
const pagetypeOptions = ref<{ label: string; value: string }[]>([])
+
+// 可用的字段类型列表(从 /core/components/form/controls 目录自动生成)
+const fieldTypeOptions = ref<{ label: string; value: string }[]>([])
const appName = ref('')
const moduleName = ref('')
+// 动态加载字段类型选项(从 /core/components/form/controls 目录)
+const loadFieldTypeOptions = () => {
+ try {
+ const modules = import.meta.glob('@/core/components/form/controls/*.vue', { eager: true })
+ const fileNames = Object.keys(modules)
+ .map(path => path.split('/').pop()?.replace('.vue', '') || '')
+ .filter(name => name && !name.startsWith('_')) // 过滤私有文件
+
+ fieldTypeOptions.value = fileNames
+ .map(name => ({ label: name, value: name }))
+ .sort((a, b) => a.label.localeCompare(b.label))
+ } catch (e) {
+ console.warn('Failed to load field type options from filesystem:', e)
+ // 如果加载失败,使用基础选项列表
+ fieldTypeOptions.value = [
+ 'Attach', 'AttachImage', 'Autocomplete', 'Barcode', 'Button',
+ 'Check', 'Code', 'Color', 'Comment', 'CronEditor', 'Currency',
+ 'Data', 'Date', 'DateRange', 'Datetime', 'Duration', 'DynamicLink',
+ 'Float', 'Geolocation', 'Heading', 'HTML', 'HTMLEditor', 'Icon',
+ 'Image', 'Int', 'Jeditor', 'JSON', 'Link', 'LongText', 'MarkdownEditor',
+ 'MultiCheck', 'MultiSelect', 'MultiSelectList', 'MultiSelectPills',
+ 'Password', 'Percent', 'Phone', 'Rating', 'Select', 'Signature',
+ 'SmallText', 'Table', 'TableMultiSelect', 'Text', 'TextEditor', 'Time'
+ ].map(name => ({ label: name, value: name }))
+ }
+}
+
+loadFieldTypeOptions()
+
const slug = computed(() => toSnake(form.value.pagetype || ''))
function dotToSlash(s: string): string { return s ? s.split('.').join('/').toLowerCase() : '' }
const frontendPath = computed(() => {
@@ -81,7 +125,13 @@ const frontendPath = computed(() => {
files.push(`apps/${appName.value}/frontend/src/views/pagetype/${slug.value}/${slug.value}.vue`)
}
if (form.value.frontendOptions?.includes('field_types')) {
- files.push(`apps/${appName.value}/frontend/src/views/pagetype/${slug.value}/form/controls/`)
+ if (form.value.fieldTypeNames && form.value.fieldTypeNames.length > 0) {
+ form.value.fieldTypeNames.forEach((name: string) => {
+ files.push(`apps/${appName.value}/frontend/src/views/pagetype/${slug.value}/form/controls/${name}.vue`)
+ })
+ } else {
+ files.push(`apps/${appName.value}/frontend/src/views/pagetype/${slug.value}/form/controls/`)
+ }
}
return files.join('\n')
})
@@ -120,6 +170,18 @@ function onFrontendToggle(value: boolean) {
form.value.frontendOptions = ['toolbar']
} else if (!value) {
form.value.frontendOptions = []
+ form.value.fieldTypeNames = []
+ }
+}
+
+function updateFieldTypesPreview() {
+ // 触发预览更新
+}
+
+function onFrontendOptionsChange(value: (string | number)[]) {
+ // 当取消选择字段类型时,清空字段类型名称
+ if (!value.includes('field_types')) {
+ form.value.fieldTypeNames = []
}
}
@@ -156,7 +218,8 @@ async function handleSubmit() {
module: moduleName.value,
create_frontend: form.value.createFrontend,
create_backend: form.value.createBackend,
- frontend_options: form.value.frontendOptions || []
+ frontend_options: form.value.frontendOptions || [],
+ field_type_names: form.value.fieldTypeNames || []
}, { headers: get_session_api_headers(), withCredentials: true })
result.value = res.data
if (res.data?.success) {
@@ -192,6 +255,11 @@ async function handleSubmit() {
white-space: pre-wrap;
word-break: break-all;
}
+.field-types-row {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
diff --git a/apps/jingrow/jingrow/api/dev.py b/apps/jingrow/jingrow/api/dev.py
index 66a8b5c..6ea97c9 100644
--- a/apps/jingrow/jingrow/api/dev.py
+++ b/apps/jingrow/jingrow/api/dev.py
@@ -17,6 +17,7 @@ async def create_pagetypes(payload: Dict[str, Any]):
create_frontend = bool(payload.get("create_frontend", True))
create_backend = bool(payload.get("create_backend", True))
frontend_options = payload.get("frontend_options", ["toolbar"])
+ field_type_names = payload.get("field_type_names", [])
if not name:
raise ValueError("pagetype is required")
slug = to_snake(name)
@@ -63,11 +64,48 @@ async def create_pagetypes(payload: Dict[str, Any]):
fp.write_text(page_content, encoding="utf-8")
frontend_paths.append(str(fp))
- # 字段类型控件目录
+ # 字段类型控件
if "field_types" in frontend_options:
fp = root / "apps" / app / "frontend" / "src" / "views" / "pagetype" / slug / "form" / "controls"
fp.mkdir(parents=True, exist_ok=True)
- frontend_paths.append(f"{str(fp)} (创建自定义字段控件)")
+
+ if field_type_names:
+ # 为每个选中的字段类型创建文件
+ for field_type_name in field_type_names:
+ field_file = fp / f"{field_type_name}.vue"
+ if field_file.exists():
+ frontend_exists = True
+ else:
+ # 创建字段类型组件的模板
+ field_content = f'''
+
+
+
+
+
+
+
+
+'''
+ field_file.write_text(field_content, encoding="utf-8")
+ frontend_paths.append(str(field_file))
+ else:
+ frontend_paths.append(f"{str(fp)}/ (创建自定义字段控件)")
frontend_path = "\n".join(frontend_paths) if frontend_paths else None
else: