---初始化后台管理web页面项目

This commit is contained in:
2025-08-20 14:39:30 +08:00
parent ad49711a7e
commit 87545a8baf
2057 changed files with 282864 additions and 213 deletions

View File

@ -0,0 +1,270 @@
const fs = require('fs');
const path = require('path');
const filePath = path.join(process.cwd(), 'config.ts');
const argv = process.argv;
const fixResponsive = argv.indexOf('fix-responsive') > 0;
const templates = require('./templates');
if (fs.existsSync(filePath)) {
console.log('Reading ' + filePath);
const file = fs.readFileSync(filePath, 'utf-8');
const fileArr = file.split('\n');
const confArr = [];
let parseStart = false;
fileArr.forEach((row) => {
if (!parseStart && row.indexOf('export const formProps: FormProps =') >= 0) {
parseStart = true;
confArr.push('{');
return;
}
if (parseStart) {
if (row.indexOf('};') === 0) {
confArr.push('}');
parseStart = false;
return;
}
confArr.push(row);
}
});
console.log('total rows: ' + confArr.length);
const funStr = `
const uploadApi = ''; // 修复文件上传引用的API
return ${confArr.join('\n')}
`;
const fun = new Function(funStr);
formProps = fun();
} else {
process.exit(0);
}
let tmpl = '';
let tabCount = 0;
let tableCount = 0;
const refList = [];
const refExportList = [];
const importList = [];
const components = [];
// 用于将config的表单格式展开成字段以便二开
formProps.schemas.forEach((prop) => {
appendTmpl(prop);
});
function appendTmpl(prop) {
let schema = `schemaMap['${prop.key}']`;
// 栅格布局
tmpl += `<!-- ${prop.label || prop.field || prop.component} -->\n`;
if (prop.component == 'Grid') {
prop.children.forEach((child) => {
child.list.forEach((listChild) => {
appendTmpl(listChild);
});
});
return;
} else if (prop.component == 'Tab') {
return appendTabTmpl(prop);
} else if (prop.component == 'TableLayout') {
return appendTableLayoutTmpl(prop);
} else if (prop.component == 'Card') {
return appendCardTmpl(prop);
} else if (prop.component == 'SubForm') {
return appendSubFormTmpl(prop);
}
tmpl += `
<Col v-if="getIfShow2('${prop.key}')" v-show="getIsShow2('${prop.key}')" :span="getColWidth(${schema})">
<template v-if="showComponent(${schema})">
<SimpleFormItem v-model:value="formModel[${schema}.field]" :form-api="formApi" :isWorkFlow="isWorkFlow" :refreshFieldObj="refreshFieldObj" :schema="${schema}" />
</template>
</Col>
`;
}
function appendSubFormTmpl(prop) {
let schema = `schemaMap['${prop.key}']`;
tableCount += 1;
createSubFormFile();
tmpl += `
<Col :span="getColWidth(${schema})">
<template v-if="showComponent(${schema})">
<CustomDevTableItem${tableCount} v-model:value="formModel[${schema}.field]" :form-api="formApi" :isWorkFlow="isWorkFlow" :refreshFieldObj="refreshFieldObj" :schema="${schema}" />
</template>
</Col>
`;
importList.push(`import CustomDevTableItem${tableCount} from './CustomDevTableItem${tableCount}.vue';`);
components.push(`CustomDevTableItem${tableCount}`);
}
function createSubFormFile() {
const subFormStr = templates.subFormTmpl;
fs.writeFile(`./CustomDevSubForm${tableCount}.vue`, subFormStr, (err) => {
if (err) throw err;
console.log(`Write SubForm CustomDevSubForm${tabCount}.vue success`);
});
const formItemStr = templates.subFormItem.replace('[count]', tableCount);
fs.writeFile(`./CustomDevTableItem${tableCount}.vue`, formItemStr, (err) => {
if (err) throw err;
console.log(`Write FormItem CustomDevTableItem${tabCount}.vue success`);
});
}
function appendTabTmpl(prop) {
let schema = `schemaMap['${prop.key}']`;
tabCount += 1;
refList.push(`const activeKey${tabCount} = ref(0);`);
refExportList.push(`activeKey${tabCount}`);
tmpl += `<Tabs v-model:activeKey="activeKey${tabCount}" v-bind="getTabProps('${prop.key}')">\n`;
prop.children.forEach((tabChild, index) => {
tmpl += `<TabPane :forceRender="true" :key="${index}" tab="${tabChild.name}">\n`;
tabChild.list.forEach((listChild) => {
appendTmpl(listChild);
});
tmpl += `</TabPane>\n`;
});
tmpl += `</Tabs>\n`;
if(components.indexOf('TabPane') < 0){
components.push('TabPane');
components.push('Tabs');
importList.push(`import { TabPane, Tabs } from 'ant-design-vue';`);
}
}
function appendTableLayoutTmpl(prop) {
if (components.indexOf('TableLayoutPreview') < 0) {
components.push('TableLayoutPreview');
importList.push(`import TableLayoutPreview from '/@/components/Form/src/components/TableLayoutPreview.vue';`);
}
let schema = `schemaMap['${prop.key}']`;
tmpl += `<TableLayoutPreview :element="${schema}">\n`;
tmpl += ` <template #tdElement="{ tdElement }">\n`;
tmpl += ` <div class="h-full">\n`;
tmpl += `<div :style="getTdStyle(tdElement)">\n`;
tmpl += `
<template v-for="childSchema in tdElement.children" :key="childSchema.field">
<SimpleFormItem v-model:value="formModel[childSchema.field]" :form-api="formApi" :isWorkFlow="isWorkFlow" :refreshFieldObj="refreshFieldObj" :schema="childSchema" />
</template>
`;
tmpl += `</div>\n`;
tmpl += ` </div>\n`;
tmpl += ` </template>\n`;
tmpl += `</TableLayoutPreview>\n`;
}
function appendCardTmpl(prop) {
let schema = `schemaMap['${prop.key}']`;
tmpl += `<CollapseContainer :bordered="false" :hasLeftBorder="true" title="${prop.componentProps.title}">\n`;
prop.children[0].list.forEach((lChild) => {
appendTmpl(lChild);
});
tmpl += `</CollapseContainer>\n`;
if (components.indexOf('CollapseContainer') < 0) {
components.push('CollapseContainer');
importList.push(`import { CollapseContainer } from '/@/components/Container';`);
}
}
const fullVue = `
<template>
<div ref="formWrap">
<Form ref="formRef" :label-col="getProps?.labelCol" :labelAlign="getProps?.labelAlign" :layout="getProps?.layout" :model="formModel" :wrapper-col="getProps?.wrapperCol" @keypress.enter="handleEnterPress">
${tmpl}
<div :style="{ textAlign: getProps.buttonLocation }">
<slot name="buttonBefore"></slot>
<a-button v-if="getProps.showSubmitButton" type="primary" @click="handleSubmit">
{{ t('提交') }}
</a-button>
<a-button v-if="getProps.showResetButton" style="margin-left: 10px" @click="handleReset">
{{ t('重置') }}
</a-button>
<slot name="buttonAfter"></slot>
</div>
</Form>
</div>
</template>
<script>
// 注意这里继承的是SimpleFormSetup使用script setup写法的组件无法继承必须使用特别的版本
import SimpleFormSetup from '/@/components/SimpleForm/src/SimpleFormSetup.vue';
import { Col, Form, Row } from 'ant-design-vue';
import SimpleFormItem from '/@/components/SimpleForm/src/components/SimpleFormItem.vue';
import { ref } from 'vue';
import { CheckCircleOutlined } from '@ant-design/icons-vue';
${importList.join('\n')}
const FormItem = Form.Item;
export default {
components: {
CheckCircleOutlined,
Form,
Col,
SimpleFormItem,
Row,
FormItem
${components.length ? (',' + components.join(',\n')) : ''}
},
mixins: [SimpleFormSetup],
setup(props, ctx) {
const ret = SimpleFormSetup.setup(props, ctx);
const expose = ctx.expose;
${refList.join('\n')}
return {
${refExportList.length ? refExportList.join(',\n') + ',' : ''}
...ret
};
},
computed: {
// 这里需要增加一个计算属性 否则流程关联时字段读写状态会失效
schemaMap() {
const schemaMap = {};
this.getSchemas.forEach((schema) => {
schemaMap[schema.key] = schema;
if(schema.children) {
schema.children.forEach(sChild=>{
if(sChild.list){
sChild.list.forEach(lChild=>{
schemaMap[lChild.key] = lChild;
});
}
});
}
});
return schemaMap;
}
},
methods: {
getIfShow2: function (key) {
return this.getIfShow(this.schemaMap[key], this.formModel[this.schemaMap[key].field]);
},
getIsShow2: function (key) {
return this.getIsShow(this.schemaMap[key], this.formModel[this.schemaMap[key].field]);
},
getTabProps(key) {
const schema = this.schemaMap[key];
return {
size: schema.componentProps.tabSize,
tabPosition: schema.componentProps.tabPosition,
type: schema.componentProps.type
}
},
getTdStyle(tdElement) {
return {
height: tdElement.height ? tdElement.height + 'px' : '',
minHeight: (tdElement.height || '42') + 'px',
overflow: 'hidden',
padding: '10px'
}
}
}
};
</script>
`;
fs.writeFile('./CustomDevForm.vue', fullVue, (err) => {
if (err) throw err;
console.log('The file has been saved!');
});

167
dev_tools/templates.js Normal file
View File

@ -0,0 +1,167 @@
const subFormTmpl = `
<template>
<div class="form-detail-table">
<a-table :bordered="showFormBorder" :columns="headColums.length > 0 ? headColums : columns" :data-source="addDataKey(data)" :pagination="showPagination ? { defaultPageSize: 10 } : false" :scroll="{ x: 'max-content' }">
<template #summary>
<a-table-summary-row v-if="columns.some((x) => x.componentProps?.subTotal)">
<a-table-summary-cell v-for="(column, idx) in columns" :key="idx">
<a-typography-text v-if="column.componentProps?.subTotal" keyboard> {{ t('合计:') }} {{ sum(data.map((x) => x[column.dataIndex])) }}</a-typography-text>
</a-table-summary-cell>
</a-table-summary-row>
</template>
<template #bodyCell="{ column, record, index }">
<template v-if="column.key !== 'action'">
<template v-if="column.key === 'index'">
{{ index + 1 }}
</template>
<FormItem v-else :name="[mainKey, index, column.dataIndex]" :rules="rules(column, record, index)" :validateTrigger="['blur', 'change']">
<!---如果是checked一类的组件-->
<template v-if="checkedValueComponents.includes(column.componentType)">
<component :is="componentMap.get(column.componentType)" v-model:checked="record[column.dataIndex]" :bordered="showComponentBorder" v-bind="getComponentsProps(column.componentProps, column.dataIndex, record, index)" />
</template>
<!---如果是RangePicker组件-->
<template v-else-if="column.componentType === 'RangePicker' || column.componentType === 'TimeRangePicker'">
<component
:is="componentMap.get(column.componentType)"
v-model:endField="column.dataIndex.split(',')[1]"
v-model:startField="column.dataIndex.split(',')[0]"
v-model:value="record[column.dataIndex]"
:bordered="showComponentBorder"
:mainKey="mainKey"
:tableIndex="index"
v-bind="getComponentsProps(column.componentProps, column.dataIndex, record, index)"
@change="handleRangePickerChange(record, column.dataIndex)"
/>
</template>
<!---如果是渲染函数组件-->
<template v-else-if="column.componentType === 'Render'">
<component
:is="
column.render({
model: record,
field: column.dataIndex,
rules: column.rules,
componentProps: getComponentsProps(column.componentProps, column.dataIndex, record, index)
})
"
:bordered="showComponentBorder"
/>
</template>
<template v-else-if="column.key !== 'index'">
<component
:is="componentMap.get(column.componentType)"
v-model:value="record[column.dataIndex]"
:bordered="showComponentBorder"
:index="index"
:mainKey="mainKey"
:key="column.dataIndex + record['_key_']"
:row="record"
v-bind="getComponentsProps(column.componentProps, column.dataIndex, record, index)"
@blur="onFieldBlur(column, record, index)"
@change="onFieldChange(column, record, index)"
/>
</template>
</FormItem>
</template>
<template v-if="column.key === 'action' && !disabled">
<MinusCircleOutlined style="padding-bottom: 20px" @click="remove(index)" />
</template>
</template>
</a-table>
<div class="tbl-toolbar">
<a-button v-if="useSelectButton && !disableAddRow" :disabled="disabled" class="select-btn" type="primary" @click="multipleDialog = true">
{{ buttonName }}
</a-button>
<a-button v-if="!disabled && !disableAddRow && useAddButton" type="primary" @click="add">
<PlusOutlined />
{{ t('新增') }}
</a-button>
</div>
<FormItemRest>
<MultipleSelect
ref="MultipleSelectRef"
v-model:multipleDialog="multipleDialog"
:apiConfig="apiConfig"
:datasourceType="preloadType"
:dicOptions="dicOptions"
:isSubFormUse="true"
:params="{ itemId }"
popupType="preload"
@submit="renderSubFormList"
/>
</FormItemRest>
</div>
</template>
<script>
import SubFormV2Setup from '/@/components/Form/src/components/SubFormV2Setup.vue';
import { Form } from 'ant-design-vue';
const FormItem = Form.Item;
const FormItemRest = Form.ItemRest;
export default {
extends: SubFormV2Setup,
components: {
FormItem,
Form,
FormItemRest
},
setup(props, ctx) {
const ret = SubFormV2Setup.setup(props, ctx);
return {
...ret
};
}
};
</script>
`;
const subFormItem = `
<template>
<!-- formitem的标题一般不用如果不需要控制明细表的权限显隐这个formitem可以去掉或者用div代替 -->
<FormItem
v-if="getShow(schema)"
v-show="getIsShow(schema)"
:key="schema.key"
:label="getComponentsProps.showLabel ? schema.label : ''"
:label-col="labelCol"
:labelAlign="formProps?.labelAlign"
:name="schema.field"
:wrapperCol="itemLabelWidthProp.wrapperCol"
>
<!-- 这里原先是component is写法现在需要换成你自己的明细表 -->
<!-- 也可以根据v-if渲染不同表格这样就不需要定义很多次FormItem了 -->
<custom-dev-sub-form v-model:value="formModel[schema.field]" :disabled="getDisable" :size="formProps?.size" v-bind="schema.componentProps" />
</FormItem>
</template>
<script>
import SimpleFormItemSetup from '/@/components/SimpleForm/src/components/SimpleFormItemSetup.vue';
import CustomDevSubForm from './CustomDevSubForm[count].vue';
import { Form } from 'ant-design-vue';
const FormItem = Form.Item;
export default {
extends: SimpleFormItemSetup,
components: {
CustomDevSubForm,
FormItem
},
setup(props, ctx) {
const ret = SimpleFormItemSetup.setup(props, ctx);
return {
...ret
};
}
};
</script>
`;
module.exports = {
subFormTmpl,
subFormItem
}