worsoft-frontend-codegen-local-mcp 0.1.79 → 0.1.81

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.
@@ -0,0 +1,5 @@
1
+ import { createCrudApi } from '/@/api/common/crudFactory';
2
+ {{API_REQUEST_IMPORT}}
3
+
4
+ export const { fetchList, getObj } = createCrudApi('/{{API_PATH}}');
5
+ {{EXTRA_API_FUNCTIONS}}
@@ -0,0 +1,103 @@
1
+ <template>
2
+ <div class="layout-padding-auto layout-padding-view">
3
+ <el-card shadow="never" class="w100">
4
+ <template #header>
5
+ <div style="display: flex; justify-content: space-between; align-items: center;">
6
+ <span style="font-size: 16px; font-weight: bold;">{{ t('common.viewBtn') }}</span>
7
+ <el-button @click="handleBack" icon="back">{{ commonActionLabel('back') }}</el-button>
8
+ </div>
9
+ </template>
10
+ <el-form ref="dataFormRef" :model="form" disabled v-loading="loading">
11
+ <el-row :gutter="24">
12
+ {{FORM_FIELDS}}
13
+ </el-row>
14
+ <el-row :gutter="24">
15
+ {{LEDGER_CHILD_SECTIONS}}
16
+ </el-row>
17
+ </el-form>
18
+ </el-card>
19
+ </div>
20
+ </template>
21
+
22
+ <script setup lang="ts" name="{{CLASS_NAME}}Form">
23
+ import { Session } from '/@/utils/storage';
24
+ import { useMessage } from '/@/hooks/message';
25
+ import { useCloseCurrentPage } from '/@/hooks/useCloseCurrentPage';
26
+ import { getObj } from '/@/api/{{API_MODULE_PATH}}';
27
+ import { useDict } from '/@/hooks/dict';
28
+ import { useCrudPageMeta } from '/@/hooks/useCrudPageMeta';
29
+ import { useI18n } from 'vue-i18n';
30
+ import { allDictTypes, childFieldGroups, dataMasterEntity } from './options';
31
+
32
+ const dictRefs = useDict(...allDictTypes);
33
+ const { t } = useI18n();
34
+ const scFormTable = defineAsyncComponent(() => import('/@/components/FormTable/index.vue'));
35
+ const route = useRoute();
36
+ const { closeCurrentPage } = useCloseCurrentPage();
37
+ const pageI18nKey = '{{I18N_NAMESPACE}}';
38
+
39
+ const dataFormRef = ref();
40
+ const loading = ref(false);
41
+ const detail = ref(true);
42
+
43
+ const {
44
+ getFieldMeta: getMasterFieldMeta,
45
+ getFieldLabel: getMasterFieldLabel,
46
+ getChildFieldLabel,
47
+ getDictOptions,
48
+ inputPlaceholder,
49
+ selectPlaceholder,
50
+ fieldRequiredMessage,
51
+ commonActionLabel,
52
+ } = useCrudPageMeta(dataMasterEntity, dictRefs, childFieldGroups);
53
+
54
+ const formInputPlaceholder = () => '';
55
+ const formSelectPlaceholder = () => '';
56
+ const childSectionTitle = (groupName: string) => t(`${pageI18nKey}.children.${groupName}.title`);
57
+
58
+ const createDefaultFormState = () => ({
59
+ {{FORM_DEFAULTS}}
60
+ {{CHILD_FORM_LIST_DEFAULTS}}
61
+ });
62
+
63
+ const form = reactive(createDefaultFormState());
64
+
65
+ const get{{CLASS_NAME}}Data = async (id: string) => {
66
+ try {
67
+ loading.value = true;
68
+ const { data } = await getObj({ {{PK_ATTR}}: id });
69
+ Object.assign(form, Array.isArray(data) ? data[0] || {} : data || {});
70
+ } catch (error) {
71
+ useMessage().error(t('common.messages.fetchError'));
72
+ } finally {
73
+ loading.value = false;
74
+ }
75
+ };
76
+
77
+ const resetFormState = () => {
78
+ Object.assign(form, createDefaultFormState());
79
+ nextTick(() => {
80
+ dataFormRef.value?.resetFields();
81
+ {{CHILD_RESET_LISTS}}
82
+ });
83
+ };
84
+
85
+ const initPage = async () => {
86
+ const id = route.query.id as string;
87
+ detail.value = true;
88
+ resetFormState();
89
+
90
+ if (id) {
91
+ form.{{PK_ATTR}} = id;
92
+ await get{{CLASS_NAME}}Data(id);
93
+ }
94
+ };
95
+
96
+ const handleBack = () => {
97
+ closeCurrentPage();
98
+ };
99
+
100
+ onMounted(() => {
101
+ initPage();
102
+ });
103
+ </script>
@@ -0,0 +1,140 @@
1
+ <template>
2
+ <div class="layout-padding">
3
+ <div class="layout-padding-auto layout-padding-view flex h-full flex-col">
4
+ <SchemaListToolbar
5
+ v-bind="toolbarProps"
6
+ @update:keyword="state.queryForm.smartVal = $event"
7
+ @query="handleToolbarQuery"
8
+ @reset="handleToolbarReset"
9
+ @custom-query-confirm="handleToolbarCustomQueryConfirm"
10
+ @refresh="handleToolbarRefresh"
11
+ />
12
+
13
+ <SchemaListTable
14
+ v-bind="tableProps"
15
+ row-id-key="{{PK_ATTR}}"
16
+ :action-column-width="100"
17
+ @sort-change="handleTableSortChange"
18
+ @size-change="handleTableSizeChange"
19
+ @current-change="handleTableCurrentChange"
20
+ >
21
+ <template #actions="{ row }">
22
+ <el-button text type="primary" icon="view" v-auth="'{{PERMISSION_PREFIX}}_view'" @click="handleDetail(row.{{PK_ATTR}})">{{ t('common.viewBtn') }}</el-button>
23
+ </template>
24
+ </SchemaListTable>
25
+ </div>
26
+ </div>
27
+ </template>
28
+
29
+ <script setup lang="ts" name="system{{CLASS_NAME}}">
30
+ import { fetchList } from '/@/api/{{API_MODULE_PATH}}';
31
+ import { useDict } from '/@/hooks/dict';
32
+ import { useCrudPageMeta } from '/@/hooks/useCrudPageMeta';
33
+ import { useSchemaListQuery } from '/@/hooks/useSchemaListQuery';
34
+ import SchemaListToolbar from '/@/components/schema-list/SchemaListToolbar.vue';
35
+ import SchemaListTable from '/@/components/schema-list/SchemaListTable.vue';
36
+ import { useI18n } from 'vue-i18n';
37
+ import { allDictTypes, crudSchema, dataMasterEntity } from './options';
38
+
39
+ const dictRefs = useDict(...allDictTypes);
40
+ const { t } = useI18n();
41
+ const router = useRouter();
42
+
43
+ const { state, getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, tableStyle, resetQueryForm } = useSchemaListQuery({
44
+ schema: crudSchema,
45
+ pageList: fetchList,
46
+ exportUrl: '/{{API_PATH}}/export',
47
+ exportFileName: '{{FUNCTION_NAME}}.xlsx',
48
+ });
49
+
50
+ const { resolveLabel, getDictOptions, visibleTableColumns, searchKeywordTooltip, queryableDictFields } = useCrudPageMeta(dataMasterEntity, dictRefs);
51
+
52
+ const queryableDictOptions = computed(() =>
53
+ queryableDictFields.value.map((field) => ({
54
+ ...field,
55
+ options: getDictOptions(field.dictType),
56
+ }))
57
+ );
58
+
59
+ const tableColumns = computed(() =>
60
+ visibleTableColumns.value.map((column) => ({
61
+ prop: column.prop,
62
+ label: resolveLabel(column.labelKey, column.fallbackLabel),
63
+ width: column.width,
64
+ dictType: column.dictType,
65
+ options: column.dictType ? getDictOptions(column.dictType) : [],
66
+ }))
67
+ );
68
+
69
+ const toolbarProps = computed(() => ({
70
+ keyword: state.queryForm.smartVal,
71
+ searchPlaceholder: searchKeywordTooltip.value,
72
+ queryModel: state.queryForm,
73
+ customQueryFields: queryableDictOptions.value,
74
+ selectedIds: [],
75
+ showAdd: false,
76
+ showImport: false,
77
+ showDelete: false,
78
+ exportPermission: false,
79
+ }));
80
+
81
+ const tableProps = computed(() => ({
82
+ data: state.dataList,
83
+ loading: !!state.loading,
84
+ columns: tableColumns.value,
85
+ pagination: state.pagination,
86
+ tableStyle,
87
+ showSelection: false,
88
+ }));
89
+
90
+ const getFormPath = () => '/{{VIEW_MODULE_PATH}}/form';
91
+
92
+ const handleDetail = (id: string) => {
93
+ router.push({ path: getFormPath(), query: { id, detail: '1', tagsViewName: t('common.viewBtn') } });
94
+ };
95
+
96
+ const handleQueryFilterConfirm = (values: Record<string, any>) => {
97
+ queryableDictFields.value.forEach((field) => {
98
+ const nextValue = values[field.prop];
99
+ if (Array.isArray(nextValue) && nextValue.length) {
100
+ state.queryForm[field.prop] = nextValue;
101
+ return;
102
+ }
103
+ delete state.queryForm[field.prop];
104
+ });
105
+ getDataList();
106
+ };
107
+
108
+ const resetQuery = () => {
109
+ resetQueryForm();
110
+ getDataList();
111
+ };
112
+
113
+ const handleToolbarQuery = () => {
114
+ getDataList();
115
+ };
116
+
117
+ const handleToolbarReset = () => {
118
+ resetQuery();
119
+ };
120
+
121
+ const handleToolbarCustomQueryConfirm = (payload: { values: Record<string, any> }) => {
122
+ handleQueryFilterConfirm(payload.values);
123
+ };
124
+
125
+ const handleToolbarRefresh = () => {
126
+ getDataList();
127
+ };
128
+
129
+ const handleTableSortChange = (payload: { raw: any }) => {
130
+ sortChangeHandle(payload.raw);
131
+ };
132
+
133
+ const handleTableSizeChange = (payload: { size: number }) => {
134
+ sizeChangeHandle(payload.size);
135
+ };
136
+
137
+ const handleTableCurrentChange = (payload: { current: number }) => {
138
+ currentChangeHandle(payload.current);
139
+ };
140
+ </script>
@@ -0,0 +1,12 @@
1
+ -- Do not execute directly. Review parent menu id and tenant settings before import.
2
+ -- Suggested default parent_id: -1
3
+ -- Generated by worsoft-codegen-local on {{GENERATED_AT}}
4
+
5
+ insert into sys_menu (menu_id, parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
6
+ values ({{MENU_BASE_ID}}, '-1', '/{{MENU_ROUTE_PATH}}/index', '', '0', 'icon-bangzhushouji', '0', null, '8', null, '{{TABLE_COMMENT}}管理', 1);
7
+
8
+ insert into sys_menu (menu_id, parent_id, path, permission, menu_type, icon, visible, del_flag, create_time, sort_order, update_time, name, tenant_id)
9
+ values ({{MENU_BASE_ID_PLUS_6}}, '-1', '/{{MENU_ROUTE_PATH}}/form', '', '0', 'icon-biaodan', '0', '0', null, '9', null, '{{TABLE_COMMENT}}详情', 1);
10
+
11
+ insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
12
+ values ({{MENU_BASE_ID_PLUS_1}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_view', '1', null, '1', '0', null, '0', null, '{{TABLE_COMMENT}}查看', 1);
@@ -0,0 +1,21 @@
1
+ {{DICT_REGISTRY_IMPORT_BLOCK}}
2
+ import { createCrudSchema } from '/@/utils/crudSchema';
3
+ import type { CrudSchemaDefinition } from '/@/utils/crudSchema';
4
+
5
+ const definition: CrudSchemaDefinition = {
6
+ master: [
7
+ {{MASTER_OPTION_FIELDS}}
8
+ ],
9
+ children: {
10
+ {{CHILD_OPTION_GROUPS}}
11
+ },
12
+ };
13
+
14
+ const schema = createCrudSchema(definition);
15
+
16
+ export const crudSchema = schema;
17
+ export const dataMasterEntity = schema.master;
18
+ export const childFieldGroups = schema.children;
19
+ export const filterTypes = schema.filterTypes;
20
+ export const childFilterTypes = schema.childFilterTypes;
21
+ export const allDictTypes = schema.allDictTypes;
@@ -0,0 +1,5 @@
1
+ import { createCrudApi } from '/@/api/common/crudFactory';
2
+ {{API_REQUEST_IMPORT}}
3
+
4
+ export const { fetchList, getObj } = createCrudApi('/{{API_PATH}}');
5
+ {{EXTRA_API_FUNCTIONS}}
@@ -0,0 +1,94 @@
1
+ <template>
2
+ <div class="layout-padding-auto layout-padding-view">
3
+ <el-card shadow="never" class="w100">
4
+ <template #header>
5
+ <div style="display: flex; justify-content: space-between; align-items: center;">
6
+ <span style="font-size: 16px; font-weight: bold;">{{ t('common.viewBtn') }}</span>
7
+ <el-button @click="handleBack" icon="back">{{ commonActionLabel('back') }}</el-button>
8
+ </div>
9
+ </template>
10
+ <el-form ref="dataFormRef" :model="form" disabled v-loading="loading">
11
+ <el-row :gutter="24">
12
+ {{FORM_FIELDS}}
13
+ </el-row>
14
+ </el-form>
15
+ </el-card>
16
+ </div>
17
+ </template>
18
+
19
+ <script setup lang="ts" name="{{CLASS_NAME}}Form">
20
+ import { Session } from '/@/utils/storage';
21
+ import { useMessage } from '/@/hooks/message';
22
+ import { useCloseCurrentPage } from '/@/hooks/useCloseCurrentPage';
23
+ import { getObj } from '/@/api/{{API_MODULE_PATH}}';
24
+ import { useDict } from '/@/hooks/dict';
25
+ import { useCrudPageMeta } from '/@/hooks/useCrudPageMeta';
26
+ import { useI18n } from 'vue-i18n';
27
+ import { allDictTypes, dataMasterEntity } from './options';
28
+
29
+ const dictRefs = useDict(...allDictTypes);
30
+ const { t } = useI18n();
31
+ const route = useRoute();
32
+ const { closeCurrentPage } = useCloseCurrentPage();
33
+
34
+ const dataFormRef = ref();
35
+ const loading = ref(false);
36
+ const detail = ref(true);
37
+
38
+ const {
39
+ getFieldMeta: getMasterFieldMeta,
40
+ getFieldLabel: getMasterFieldLabel,
41
+ getDictOptions,
42
+ inputPlaceholder,
43
+ selectPlaceholder,
44
+ fieldRequiredMessage,
45
+ commonActionLabel,
46
+ } = useCrudPageMeta(dataMasterEntity, dictRefs);
47
+
48
+ const formInputPlaceholder = () => '';
49
+ const formSelectPlaceholder = () => '';
50
+
51
+ const createDefaultFormState = () => ({
52
+ {{FORM_DEFAULTS}}
53
+ });
54
+
55
+ const form = reactive(createDefaultFormState());
56
+
57
+ const get{{CLASS_NAME}}Data = async (id: string) => {
58
+ try {
59
+ loading.value = true;
60
+ const { data } = await getObj({ {{PK_ATTR}}: id });
61
+ Object.assign(form, Array.isArray(data) ? data[0] || {} : data || {});
62
+ } catch (error) {
63
+ useMessage().error(t('common.messages.fetchError'));
64
+ } finally {
65
+ loading.value = false;
66
+ }
67
+ };
68
+
69
+ const resetFormState = () => {
70
+ Object.assign(form, createDefaultFormState());
71
+ nextTick(() => {
72
+ dataFormRef.value?.resetFields();
73
+ });
74
+ };
75
+
76
+ const initPage = async () => {
77
+ const id = route.query.id as string;
78
+ detail.value = true;
79
+ resetFormState();
80
+
81
+ if (id) {
82
+ form.{{PK_ATTR}} = id;
83
+ await get{{CLASS_NAME}}Data(id);
84
+ }
85
+ };
86
+
87
+ const handleBack = () => {
88
+ closeCurrentPage();
89
+ };
90
+
91
+ onMounted(() => {
92
+ initPage();
93
+ });
94
+ </script>
@@ -0,0 +1,140 @@
1
+ <template>
2
+ <div class="layout-padding">
3
+ <div class="layout-padding-auto layout-padding-view flex h-full flex-col">
4
+ <SchemaListToolbar
5
+ v-bind="toolbarProps"
6
+ @update:keyword="state.queryForm.smartVal = $event"
7
+ @query="handleToolbarQuery"
8
+ @reset="handleToolbarReset"
9
+ @custom-query-confirm="handleToolbarCustomQueryConfirm"
10
+ @refresh="handleToolbarRefresh"
11
+ />
12
+
13
+ <SchemaListTable
14
+ v-bind="tableProps"
15
+ row-id-key="{{PK_ATTR}}"
16
+ :action-column-width="100"
17
+ @sort-change="handleTableSortChange"
18
+ @size-change="handleTableSizeChange"
19
+ @current-change="handleTableCurrentChange"
20
+ >
21
+ <template #actions="{ row }">
22
+ <el-button text type="primary" icon="view" v-auth="'{{PERMISSION_PREFIX}}_view'" @click="handleDetail(row.{{PK_ATTR}})">{{ t('common.viewBtn') }}</el-button>
23
+ </template>
24
+ </SchemaListTable>
25
+ </div>
26
+ </div>
27
+ </template>
28
+
29
+ <script setup lang="ts" name="system{{CLASS_NAME}}">
30
+ import { fetchList } from '/@/api/{{API_MODULE_PATH}}';
31
+ import { useDict } from '/@/hooks/dict';
32
+ import { useCrudPageMeta } from '/@/hooks/useCrudPageMeta';
33
+ import { useSchemaListQuery } from '/@/hooks/useSchemaListQuery';
34
+ import SchemaListToolbar from '/@/components/schema-list/SchemaListToolbar.vue';
35
+ import SchemaListTable from '/@/components/schema-list/SchemaListTable.vue';
36
+ import { useI18n } from 'vue-i18n';
37
+ import { allDictTypes, crudSchema, dataMasterEntity } from './options';
38
+
39
+ const dictRefs = useDict(...allDictTypes);
40
+ const { t } = useI18n();
41
+ const router = useRouter();
42
+
43
+ const { state, getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, tableStyle, resetQueryForm } = useSchemaListQuery({
44
+ schema: crudSchema,
45
+ pageList: fetchList,
46
+ exportUrl: '/{{API_PATH}}/export',
47
+ exportFileName: '{{FUNCTION_NAME}}.xlsx',
48
+ });
49
+
50
+ const { resolveLabel, getDictOptions, visibleTableColumns, searchKeywordTooltip, queryableDictFields } = useCrudPageMeta(dataMasterEntity, dictRefs);
51
+
52
+ const queryableDictOptions = computed(() =>
53
+ queryableDictFields.value.map((field) => ({
54
+ ...field,
55
+ options: getDictOptions(field.dictType),
56
+ }))
57
+ );
58
+
59
+ const tableColumns = computed(() =>
60
+ visibleTableColumns.value.map((column) => ({
61
+ prop: column.prop,
62
+ label: resolveLabel(column.labelKey, column.fallbackLabel),
63
+ width: column.width,
64
+ dictType: column.dictType,
65
+ options: column.dictType ? getDictOptions(column.dictType) : [],
66
+ }))
67
+ );
68
+
69
+ const toolbarProps = computed(() => ({
70
+ keyword: state.queryForm.smartVal,
71
+ searchPlaceholder: searchKeywordTooltip.value,
72
+ queryModel: state.queryForm,
73
+ customQueryFields: queryableDictOptions.value,
74
+ selectedIds: [],
75
+ showAdd: false,
76
+ showImport: false,
77
+ showDelete: false,
78
+ exportPermission: false,
79
+ }));
80
+
81
+ const tableProps = computed(() => ({
82
+ data: state.dataList,
83
+ loading: !!state.loading,
84
+ columns: tableColumns.value,
85
+ pagination: state.pagination,
86
+ tableStyle,
87
+ showSelection: false,
88
+ }));
89
+
90
+ const getFormPath = () => '/{{VIEW_MODULE_PATH}}/form';
91
+
92
+ const handleDetail = (id: string) => {
93
+ router.push({ path: getFormPath(), query: { id, detail: '1', tagsViewName: t('common.viewBtn') } });
94
+ };
95
+
96
+ const handleQueryFilterConfirm = (values: Record<string, any>) => {
97
+ queryableDictFields.value.forEach((field) => {
98
+ const nextValue = values[field.prop];
99
+ if (Array.isArray(nextValue) && nextValue.length) {
100
+ state.queryForm[field.prop] = nextValue;
101
+ return;
102
+ }
103
+ delete state.queryForm[field.prop];
104
+ });
105
+ getDataList();
106
+ };
107
+
108
+ const resetQuery = () => {
109
+ resetQueryForm();
110
+ getDataList();
111
+ };
112
+
113
+ const handleToolbarQuery = () => {
114
+ getDataList();
115
+ };
116
+
117
+ const handleToolbarReset = () => {
118
+ resetQuery();
119
+ };
120
+
121
+ const handleToolbarCustomQueryConfirm = (payload: { values: Record<string, any> }) => {
122
+ handleQueryFilterConfirm(payload.values);
123
+ };
124
+
125
+ const handleToolbarRefresh = () => {
126
+ getDataList();
127
+ };
128
+
129
+ const handleTableSortChange = (payload: { raw: any }) => {
130
+ sortChangeHandle(payload.raw);
131
+ };
132
+
133
+ const handleTableSizeChange = (payload: { size: number }) => {
134
+ sizeChangeHandle(payload.size);
135
+ };
136
+
137
+ const handleTableCurrentChange = (payload: { current: number }) => {
138
+ currentChangeHandle(payload.current);
139
+ };
140
+ </script>
@@ -0,0 +1,12 @@
1
+ -- Do not execute directly. Review parent menu id and tenant settings before import.
2
+ -- Suggested default parent_id: -1
3
+ -- Generated by worsoft-codegen-local on {{GENERATED_AT}}
4
+
5
+ insert into sys_menu (menu_id, parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
6
+ values ({{MENU_BASE_ID}}, '-1', '/{{MENU_ROUTE_PATH}}/index', '', '0', 'icon-bangzhushouji', '0', null, '8', null, '{{TABLE_COMMENT}}管理', 1);
7
+
8
+ insert into sys_menu (menu_id, parent_id, path, permission, menu_type, icon, visible, del_flag, create_time, sort_order, update_time, name, tenant_id)
9
+ values ({{MENU_BASE_ID_PLUS_6}}, '-1', '/{{MENU_ROUTE_PATH}}/form', '', '0', 'icon-biaodan', '0', '0', null, '9', null, '{{TABLE_COMMENT}}详情', 1);
10
+
11
+ insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
12
+ values ({{MENU_BASE_ID_PLUS_1}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_view', '1', null, '1', '0', null, '0', null, '{{TABLE_COMMENT}}查看', 1);
@@ -0,0 +1,21 @@
1
+ {{DICT_REGISTRY_IMPORT_BLOCK}}
2
+ import { createCrudSchema } from '/@/utils/crudSchema';
3
+ import type { CrudSchemaDefinition } from '/@/utils/crudSchema';
4
+
5
+ const definition: CrudSchemaDefinition = {
6
+ master: [
7
+ {{MASTER_OPTION_FIELDS}}
8
+ ],
9
+ children: {
10
+ {{CHILD_OPTION_GROUPS}}
11
+ },
12
+ };
13
+
14
+ const schema = createCrudSchema(definition);
15
+
16
+ export const crudSchema = schema;
17
+ export const dataMasterEntity = schema.master;
18
+ export const childFieldGroups = schema.children;
19
+ export const filterTypes = schema.filterTypes;
20
+ export const childFilterTypes = schema.childFilterTypes;
21
+ export const allDictTypes = schema.allDictTypes;
package/mcp_server.js CHANGED
@@ -5,7 +5,7 @@ const fs = require('fs');
5
5
  const path = require('path');
6
6
 
7
7
  const SERVER_NAME = 'worsoft-codegen-local';
8
- const SERVER_VERSION = '0.1.79';
8
+ const SERVER_VERSION = '0.1.80';
9
9
  const PROTOCOL_VERSION = '2024-11-05';
10
10
  const TOOL_NAME = 'worsoft_codegen_local_generate_frontend';
11
11
  const STYLE_CATALOG_PATH = path.join(__dirname, 'assets', 'style-catalog.json');
@@ -267,10 +267,10 @@ const TOOL_SCHEMA = {
267
267
  tableName: { type: 'string', description: 'Canonical main table name from PRD-aligned structured metadata.' },
268
268
  tableComment: { type: 'string', description: 'Canonical main table comment or feature label from PRD-aligned structured metadata.' },
269
269
  apiPath: { type: 'string', description: 'Backend API base path from pre-parsed structured metadata, for example iwmEmpOutsourcePerson.' },
270
- pageType: {
271
- type: 'string',
272
- enum: ['business', 'dict', 'non_standard'],
273
- description: 'Structured page type from parseResult. MCP consumes this value but does not derive it. Dict pages are restricted to dialog-based templates.',
270
+ pageType: {
271
+ type: 'string',
272
+ enum: ['business', 'dict', 'ledger', 'non_standard'],
273
+ description: 'Structured page type from parseResult. MCP consumes this value but does not derive it. Dict pages are restricted to dialog-based templates. Ledger pages are read-only jump-based templates.',
274
274
  },
275
275
  style: { type: 'string', enum: Object.keys(STYLE_CATALOG), description: 'Final style id from parseResult or translated mcpPayload. MCP validates it but does not infer it.' },
276
276
  fields: {
@@ -1641,12 +1641,12 @@ function getStylePreset(styleId) {
1641
1641
  return preset;
1642
1642
  }
1643
1643
 
1644
- function normalizePageTypeInput(pageType) {
1645
- if (pageType === undefined || pageType === null || pageType === '') return '';
1646
- const normalized = String(pageType).trim();
1647
- if (['business', 'dict', 'non_standard'].includes(normalized)) return normalized;
1648
- throw new Error(`Unsupported pageType: ${normalized}. Allowed values are dict, business, non_standard.`);
1649
- }
1644
+ function normalizePageTypeInput(pageType) {
1645
+ if (pageType === undefined || pageType === null || pageType === '') return '';
1646
+ const normalized = String(pageType).trim();
1647
+ if (['business', 'dict', 'ledger', 'non_standard'].includes(normalized)) return normalized;
1648
+ throw new Error(`Unsupported pageType: ${normalized}. Allowed values are dict, business, ledger, non_standard.`);
1649
+ }
1650
1650
 
1651
1651
  function rejectSemanticStageInputs(input) {
1652
1652
  const forbiddenKeys = [
@@ -1669,17 +1669,23 @@ function validatePageTypeAndStyle(pageType, style) {
1669
1669
  if (pageType === 'non_standard') {
1670
1670
  throw new Error('non_standard pages are not supported by worsoft_codegen_local_generate_frontend');
1671
1671
  }
1672
- if (pageType === 'business') {
1673
- if (style === 'single_table_dialog') {
1674
- throw new Error('Business pages must use jump-based styles. pageType=business does not support style=single_table_dialog; use single_table_jump for single-table business pages or master_child_jump for master-child business pages.');
1675
- }
1676
- return;
1677
- }
1678
- if (pageType !== 'dict') return;
1679
- if (style === 'single_table_jump' || style === 'master_child_jump') {
1680
- throw new Error(`Dict pages must use dialog-based styles. pageType=dict does not support style=${style}`);
1681
- }
1682
- }
1672
+ if (pageType === 'business') {
1673
+ if (style === 'single_table_dialog') {
1674
+ throw new Error('Business pages must use jump-based styles. pageType=business does not support style=single_table_dialog; use single_table_jump for single-table business pages or master_child_jump for master-child business pages.');
1675
+ }
1676
+ return;
1677
+ }
1678
+ if (pageType === 'ledger') {
1679
+ if (style !== 'single_table_jump' && style !== 'master_child_jump') {
1680
+ throw new Error('Ledger pages must use jump-based styles. pageType=ledger supports single_table_jump or master_child_jump only.');
1681
+ }
1682
+ return;
1683
+ }
1684
+ if (pageType !== 'dict') return;
1685
+ if (style === 'single_table_jump' || style === 'master_child_jump') {
1686
+ throw new Error(`Dict pages must use dialog-based styles. pageType=dict does not support style=${style}`);
1687
+ }
1688
+ }
1683
1689
 
1684
1690
  function hasRuntimeSupport(stylePreset) {
1685
1691
  return Boolean(stylePreset.runtime && stylePreset.runtime.supported && stylePreset.runtime.templateDir);
@@ -2181,8 +2187,8 @@ function renderChildTempDeclaration(childModel) {
2181
2187
  ].join('\n');
2182
2188
  }
2183
2189
 
2184
- function renderChildSection(childModel, childCount) {
2185
- const deleteExpression = `deleteChild(obj, '${childModel.pk.attrName}')`;
2190
+ function renderChildSection(childModel, childCount) {
2191
+ const deleteExpression = `deleteChild(obj, '${childModel.pk.attrName}')`;
2186
2192
 
2187
2193
  return [
2188
2194
  ' <el-col :span="24" class="mb20">',
@@ -3413,6 +3419,21 @@ function renderBusinessDeleteGuard(model) {
3413
3419
  ].join('\n');
3414
3420
  }
3415
3421
 
3422
+ function asReadonlyField(field) {
3423
+ return { ...field, readonly: true };
3424
+ }
3425
+
3426
+ function renderLedgerChildSection(childModel) {
3427
+ return [
3428
+ ' <el-col :span="24" class="mb20">',
3429
+ ` <div class="mb10" style="font-weight: 600;">{{ childSectionTitle('${childModel.listName}') }}</div>`,
3430
+ ` <sc-form-table v-model="form.${childModel.listName}" hide-add hide-delete :placeholder="t('common.noData')">`,
3431
+ childModel.visibleFields.map((field) => renderChildTableColumn(asReadonlyField(field), childModel.listName)).join('\n'),
3432
+ ' </sc-form-table>',
3433
+ ' </el-col>',
3434
+ ].join('\n');
3435
+ }
3436
+
3416
3437
  function renderBusinessSubmitStatusAssignment(model) {
3417
3438
  if (!hasBusinessBillStateEditControl(model)) return '';
3418
3439
  const statusAttr = toCamelCase(model.statusField);
@@ -3541,7 +3562,7 @@ function buildReplacements(model, sharedSupport) {
3541
3562
  MENU_BASE_ID_PLUS_5: menuBaseId + 5,
3542
3563
  MENU_BASE_ID_PLUS_6: menuBaseId + 6,
3543
3564
  GENERATED_AT: new Date().toISOString(),
3544
- FORM_FIELDS: model.visibleFields.map(renderFormFieldV2).join('\n'),
3565
+ FORM_FIELDS: (model.pageType === 'ledger' ? model.visibleFields.map(asReadonlyField) : model.visibleFields).map(renderFormFieldV2).join('\n'),
3545
3566
  TABLE_COLUMNS: model.gridFields.map((field) => renderTableColumn(field, dictRegistryRefs)).join('\n'),
3546
3567
  FORM_DEFAULTS: renderFormDefaults(model),
3547
3568
  DICT_REGISTRY_IMPORT_BLOCK: model.dictTypes.length ? "import { DictRegistry } from '/@/enums/dict-registry';" : '',
@@ -3549,21 +3570,25 @@ function buildReplacements(model, sharedSupport) {
3549
3570
  CHILD_OPTION_GROUPS: model.children.map((childModel) => renderChildOptionGroupV2(model, childModel, dictRegistryRefs)).join('\n'),
3550
3571
  FORM_RULES: renderFormRulesV2(model.visibleFields),
3551
3572
  CHILD_FORM_LIST_DEFAULTS: renderChildFormListDefaults(model.children),
3552
- CHILD_TEMP_DECLARATIONS: renderChildTempDeclarations(model.children),
3553
- CHILD_RESET_LISTS: renderChildResetListLines(model.children),
3554
- CHILD_SECTIONS: model.children.map((childModel) => renderChildSection(childModel, model.children.length)).join('\n'),
3555
- };
3556
- }
3557
-
3558
- function renderFiles(model, stylePreset, sharedSupport, localeZhSupport) {
3559
- if (!hasRuntimeSupport(stylePreset)) throw new Error('Runtime templates are not implemented for style: ' + model.style);
3560
- if (model.style === 'multi_level_dict') {
3561
- return renderMultiLevelFiles(model, sharedSupport, localeZhSupport);
3562
- }
3573
+ CHILD_TEMP_DECLARATIONS: renderChildTempDeclarations(model.children),
3574
+ CHILD_RESET_LISTS: renderChildResetListLines(model.children),
3575
+ CHILD_SECTIONS: model.children.map((childModel) => renderChildSection(childModel, model.children.length)).join('\n'),
3576
+ LEDGER_CHILD_SECTIONS: model.children.map((childModel) => renderLedgerChildSection(childModel)).join('\n'),
3577
+ };
3578
+ }
3563
3579
 
3564
- const runtime = stylePreset.runtime;
3565
- const templateDir = path.resolve(__dirname, runtime.templateDir);
3566
- const replacements = buildReplacements(model, sharedSupport);
3580
+ function renderFiles(model, stylePreset, sharedSupport, localeZhSupport) {
3581
+ if (!hasRuntimeSupport(stylePreset)) throw new Error('Runtime templates are not implemented for style: ' + model.style);
3582
+ if (model.style === 'multi_level_dict') {
3583
+ return renderMultiLevelFiles(model, sharedSupport, localeZhSupport);
3584
+ }
3585
+
3586
+ const runtime = stylePreset.runtime;
3587
+ const templateDir =
3588
+ model.pageType === 'ledger'
3589
+ ? path.resolve(__dirname, 'assets/templates/ledger_' + model.style)
3590
+ : path.resolve(__dirname, runtime.templateDir);
3591
+ const replacements = buildReplacements(model, sharedSupport);
3567
3592
  const formTemplate = fs.readFileSync(path.join(templateDir, runtime.files.form), 'utf8');
3568
3593
  const listTemplate = fs.readFileSync(path.join(templateDir, runtime.files.list), 'utf8');
3569
3594
  const optionsTemplate = fs.readFileSync(path.join(templateDir, runtime.files.options), 'utf8');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "worsoft-frontend-codegen-local-mcp",
3
- "version": "0.1.79",
3
+ "version": "0.1.81",
4
4
  "description": "Worsoft frontend local-template code generation MCP server.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "worsoft <sw@worsoft.vip>",