right panel

This commit is contained in:
cxc
2022-12-15 17:30:55 +08:00
parent b9fcfbfb6b
commit 57bcaecc90
23 changed files with 1425 additions and 1130 deletions

10
jsconfig.json Normal file
View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
// ...
"types": ["element-plus/global"],
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}

95
public/preview.html Normal file
View File

@ -0,0 +1,95 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>form-generator-preview</title>
<link href="https://unpkg.com/browse/element-plus@2.2.26/dist/index.css" rel="stylesheet">
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://unpkg.com/browse/vue-router@4.1.6/dist/vue-router.global.prod.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://unpkg.com/browse/element-plus@2.2.26/dist/index.full.min.js"></script>
<style>
body {
margin: 0;
padding: 0;
overflow-x: hidden;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
height: calc(100vh - 33px);
padding: 12px;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
}
input,
textarea {
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
}
</style>
</head>
<body>
<noscript>
<strong>抱歉javascript被禁用请开启后重试。</strong>
</noscript>
<div id="previewApp"></div>
<script type="text/javascript">
Vue.prototype.$axios = axios
const childAttrs = {
file: '',
dialog: ' width="600px" class="dialog-width" v-if="visible" :visible.sync="visible" :modal-append-to-body="false" '
}
window.addEventListener('message', init, false)
function init(event) {
if (event.data.type === 'refreshFrame') {
const code = event.data.data
const attrs = childAttrs[code.generateConf.type]
let links = ''
if (Array.isArray(code.links) && code.links.length > 0) {
links = buildLinks(code.links)
}
document.getElementById('previewApp').innerHTML = `${links}<style>${code.css}</style><div id="app"></div>`
if (Array.isArray(code.scripts) && code.scripts.length > 0) {
this.loadScriptQueue(code.scripts, () => {
newVue(attrs, code.js, code.html)
})
} else {
newVue(attrs, code.js, code.html)
}
}
}
function buildLinks(links) {
let strs = ''
links.forEach(url => {
strs += `<link href="${url}" rel="stylesheet">`
})
return strs
}
function newVue(attrs, main, html) {
main = eval(`(${main})`)
main.template = `<div>${html}</div>`
new Vue({
components: {
child: main
},
data() {
return {
visible: true
}
},
template: `<div><child ${attrs}/></div>`
}).$mount('#app')
}
</script>
</body>
</html>

View File

@ -44,23 +44,51 @@
border-bottom: $color-border-style; border-bottom: $color-border-style;
border-left: $transparent-border-style; border-left: $transparent-border-style;
border-right: $transparent-border-style; border-right: $transparent-border-style;
} } @else if $direction==right {
@else if $direction==right {
border-left: $color-border-style; border-left: $color-border-style;
border-top: $transparent-border-style; border-top: $transparent-border-style;
border-bottom: $transparent-border-style; border-bottom: $transparent-border-style;
} } @else if $direction==down {
@else if $direction==down {
border-top: $color-border-style; border-top: $color-border-style;
border-left: $transparent-border-style; border-left: $transparent-border-style;
border-right: $transparent-border-style; border-right: $transparent-border-style;
} } @else if $direction==left {
@else if $direction==left {
border-right: $color-border-style; border-right: $color-border-style;
border-top: $transparent-border-style; border-top: $transparent-border-style;
border-bottom: $transparent-border-style; border-bottom: $transparent-border-style;
} }
} }
@mixin action-bar {
.action-bar {
height: 33px;
background: #f2fafb;
padding: 0 15px;
box-sizing: border-box;
.bar-btn {
display: inline-block;
padding: 0 6px;
line-height: 32px;
color: #8285f5;
cursor: pointer;
font-size: 14px;
user-select: none;
& i {
font-size: 20px;
}
&:hover {
color: #4348d4;
}
}
.bar-btn + .bar-btn {
margin-left: 8px;
}
.delete-btn {
color: #f56c6c;
&:hover {
color: #ea0b30;
}
}
}
}

View File

@ -290,10 +290,12 @@ export const selectComponents = [
{ {
label: "选项一", label: "选项一",
value: 1, value: 1,
disabled: false,
}, },
{ {
label: "选项二", label: "选项二",
value: 2, value: 2,
disabled: false,
}, },
], ],
}, },
@ -554,7 +556,7 @@ export const layoutComponents = [
default: "主要按钮", default: "主要按钮",
}, },
type: "primary", type: "primary",
icon: "el-icon-search", icon: "search",
round: false, round: false,
size: "default", size: "default",
plain: false, plain: false,

View File

@ -1,8 +1,8 @@
/* eslint-disable max-len */ /* eslint-disable max-len */
import ruleTrigger from './ruleTrigger' import ruleTrigger from "./ruleTrigger";
let confGlobal let confGlobal;
let someSpanIsNot24 let someSpanIsNot24;
export function dialogWrapper(str) { export function dialogWrapper(str) {
return `<el-dialog v-bind="$attrs" v-on="$listeners" @open="onOpen" @close="onClose" title="Dialog Title"> return `<el-dialog v-bind="$attrs" v-on="$listeners" @open="onOpen" @close="onClose" title="Dialog Title">
@ -11,7 +11,7 @@ export function dialogWrapper(str) {
<el-button @click="close">取消</el-button> <el-button @click="close">取消</el-button>
<el-button type="primary" @click="handleConfirm">确定</el-button> <el-button type="primary" @click="handleConfirm">确定</el-button>
</div> </div>
</el-dialog>` </el-dialog>`;
} }
export function vueTemplate(str) { export function vueTemplate(str) {
@ -19,53 +19,57 @@ export function vueTemplate(str) {
<div> <div>
${str} ${str}
</div> </div>
</template>` </template>`;
} }
export function vueScript(str) { export function vueScript(str, isSetup) {
return `<script> return `<script ${isSetup ? "setup" : ""}>
${str} ${str}
</script>` </script>`;
} }
export function cssStyle(cssStr) { export function cssStyle(cssStr) {
return `<style> return `<style>
${cssStr} ${cssStr}
</style>` </style>`;
} }
function buildFormTemplate(scheme, child, type) { function buildFormTemplate(scheme, child, type) {
let labelPosition = '' let labelPosition = "";
if (scheme.labelPosition !== 'right') { if (scheme.labelPosition !== "right") {
labelPosition = `label-position="${scheme.labelPosition}"` labelPosition = `label-position="${scheme.labelPosition}"`;
} }
const disabled = scheme.disabled ? `:disabled="${scheme.disabled}"` : '' const disabled = scheme.disabled ? `:disabled="${scheme.disabled}"` : "";
let str = `<el-form ref="${scheme.formRef}" :model="${scheme.formModel}" :rules="${scheme.formRules}" size="${scheme.size}" ${disabled} label-width="${scheme.labelWidth}px" ${labelPosition}> let str = `<el-form ref="${scheme.formRef}" :model="${
scheme.formModel
}" :rules="${scheme.formRules}" size="${
scheme.size
}" ${disabled} label-width="${scheme.labelWidth}px" ${labelPosition}>
${child} ${child}
${buildFromBtns(scheme, type)} ${buildFromBtns(scheme, type)}
</el-form>` </el-form>`;
if (someSpanIsNot24) { if (someSpanIsNot24) {
str = `<el-row :gutter="${scheme.gutter}"> str = `<el-row :gutter="${scheme.gutter}">
${str} ${str}
</el-row>` </el-row>`;
} }
return str return str;
} }
function buildFromBtns(scheme, type) { function buildFromBtns(scheme, type) {
let str = '' let str = "";
if (scheme.formBtns && type === 'file') { if (scheme.formBtns && type === "file") {
str = `<el-form-item size="large"> str = `<el-form-item size="large">
<el-button type="primary" @click="submitForm">提交</el-button> <el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="resetForm">重置</el-button> <el-button @click="resetForm">重置</el-button>
</el-form-item>` </el-form-item>`;
if (someSpanIsNot24) { if (someSpanIsNot24) {
str = `<el-col :span="24"> str = `<el-col :span="24">
${str} ${str}
</el-col>` </el-col>`;
} }
} }
return str return str;
} }
// span不为24的用el-col包裹 // span不为24的用el-col包裹
@ -73,304 +77,360 @@ function colWrapper(scheme, str) {
if (someSpanIsNot24 || scheme.__config__.span !== 24) { if (someSpanIsNot24 || scheme.__config__.span !== 24) {
return `<el-col :span="${scheme.__config__.span}"> return `<el-col :span="${scheme.__config__.span}">
${str} ${str}
</el-col>` </el-col>`;
} }
return str return str;
} }
const layouts = { const layouts = {
colFormItem(scheme) { colFormItem(scheme) {
const config = scheme.__config__ const config = scheme.__config__;
let labelWidth = '' let labelWidth = "";
let label = `label="${config.label}"` let label = `label="${config.label}"`;
if (config.labelWidth && config.labelWidth !== confGlobal.labelWidth) { if (config.labelWidth && config.labelWidth !== confGlobal.labelWidth) {
labelWidth = `label-width="${config.labelWidth}px"` labelWidth = `label-width="${config.labelWidth}px"`;
} }
if (config.showLabel === false) { if (config.showLabel === false) {
labelWidth = 'label-width="0"' labelWidth = 'label-width="0"';
label = '' label = "";
} }
const required = !ruleTrigger[config.tag] && config.required ? 'required' : '' const required =
const tagDom = tags[config.tag] ? tags[config.tag](scheme) : null !ruleTrigger[config.tag] && config.required ? "required" : "";
const tagDom = tags[config.tag] ? tags[config.tag](scheme) : null;
let str = `<el-form-item ${labelWidth} ${label} prop="${scheme.__vModel__}" ${required}> let str = `<el-form-item ${labelWidth} ${label} prop="${scheme.__vModel__}" ${required}>
${tagDom} ${tagDom}
</el-form-item>` </el-form-item>`;
str = colWrapper(scheme, str) str = colWrapper(scheme, str);
return str return str;
}, },
rowFormItem(scheme) { rowFormItem(scheme) {
const config = scheme.__config__ const config = scheme.__config__;
const type = scheme.type === 'default' ? '' : `type="${scheme.type}"` const type = scheme.type === "default" ? "" : `type="${scheme.type}"`;
const justify = scheme.type === 'default' ? '' : `justify="${scheme.justify}"` const justify =
const align = scheme.type === 'default' ? '' : `align="${scheme.align}"` scheme.type === "default" ? "" : `justify="${scheme.justify}"`;
const gutter = scheme.gutter ? `:gutter="${scheme.gutter}"` : '' const align = scheme.type === "default" ? "" : `align="${scheme.align}"`;
const children = config.children.map(el => layouts[el.__config__.layout](el)) const gutter = scheme.gutter ? `:gutter="${scheme.gutter}"` : "";
const children = config.children.map((el) =>
layouts[el.__config__.layout](el)
);
let str = `<el-row ${type} ${justify} ${align} ${gutter}> let str = `<el-row ${type} ${justify} ${align} ${gutter}>
${children.join('\n')} ${children.join("\n")}
</el-row>` </el-row>`;
str = colWrapper(scheme, str) str = colWrapper(scheme, str);
return str return str;
} },
} };
const tags = { const tags = {
'el-button': el => { "el-button": (el) => {
const { const { tag, disabled } = attrBuilder(el);
tag, disabled const type = el.type ? `type="${el.type}"` : "";
} = attrBuilder(el) const icon = el.icon ? `icon="${el.icon}"` : "";
const type = el.type ? `type="${el.type}"` : '' const round = el.round ? "round" : "";
const icon = el.icon ? `icon="${el.icon}"` : '' const size = el.size ? `size="${el.size}"` : "";
const round = el.round ? 'round' : '' const plain = el.plain ? "plain" : "";
const size = el.size ? `size="${el.size}"` : '' const circle = el.circle ? "circle" : "";
const plain = el.plain ? 'plain' : '' let child = buildElButtonChild(el);
const circle = el.circle ? 'circle' : ''
let child = buildElButtonChild(el)
if (child) child = `\n${child}\n` // 换行 if (child) child = `\n${child}\n`; // 换行
return `<${tag} ${type} ${icon} ${round} ${size} ${plain} ${disabled} ${circle}>${child}</${tag}>` return `<${tag} ${type} ${icon} ${round} ${size} ${plain} ${disabled} ${circle}>${child}</${tag}>`;
}, },
'el-input': el => { "el-input": (el) => {
const { const { tag, disabled, vModel, clearable, placeholder, width } =
tag, disabled, vModel, clearable, placeholder, width attrBuilder(el);
} = attrBuilder(el) const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : "";
const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : '' const showWordLimit = el["show-word-limit"] ? "show-word-limit" : "";
const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : '' const readonly = el.readonly ? "readonly" : "";
const readonly = el.readonly ? 'readonly' : '' const prefixIcon = el["prefix-icon"]
const prefixIcon = el['prefix-icon'] ? `prefix-icon='${el['prefix-icon']}'` : '' ? `prefix-icon='${el["prefix-icon"]}'`
const suffixIcon = el['suffix-icon'] ? `suffix-icon='${el['suffix-icon']}'` : '' : "";
const showPassword = el['show-password'] ? 'show-password' : '' const suffixIcon = el["suffix-icon"]
const type = el.type ? `type="${el.type}"` : '' ? `suffix-icon='${el["suffix-icon"]}'`
const autosize = el.autosize && el.autosize.minRows : "";
? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"` const showPassword = el["show-password"] ? "show-password" : "";
: '' const type = el.type ? `type="${el.type}"` : "";
let child = buildElInputChild(el) const autosize =
el.autosize && el.autosize.minRows
? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"`
: "";
let child = buildElInputChild(el);
if (child) child = `\n${child}\n` // 换行 if (child) child = `\n${child}\n`; // 换行
return `<${tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}</${tag}>` return `<${tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}</${tag}>`;
}, },
'el-input-number': el => { "el-input-number": (el) => {
const { const { tag, disabled, vModel, placeholder } = attrBuilder(el);
tag, disabled, vModel, placeholder const controlsPosition = el["controls-position"]
} = attrBuilder(el) ? `controls-position=${el["controls-position"]}`
const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : '' : "";
const min = el.min ? `:min='${el.min}'` : '' const min = el.min ? `:min='${el.min}'` : "";
const max = el.max ? `:max='${el.max}'` : '' const max = el.max ? `:max='${el.max}'` : "";
const step = el.step ? `:step='${el.step}'` : '' const step = el.step ? `:step='${el.step}'` : "";
const stepStrictly = el['step-strictly'] ? 'step-strictly' : '' const stepStrictly = el["step-strictly"] ? "step-strictly" : "";
const precision = el.precision ? `:precision='${el.precision}'` : '' const precision = el.precision ? `:precision='${el.precision}'` : "";
return `<${tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></${tag}>` return `<${tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></${tag}>`;
}, },
'el-select': el => { "el-select": (el) => {
const { const { tag, disabled, vModel, clearable, placeholder, width } =
tag, disabled, vModel, clearable, placeholder, width attrBuilder(el);
} = attrBuilder(el) const filterable = el.filterable ? "filterable" : "";
const filterable = el.filterable ? 'filterable' : '' const multiple = el.multiple ? "multiple" : "";
const multiple = el.multiple ? 'multiple' : '' let child = buildElSelectChild(el);
let child = buildElSelectChild(el)
if (child) child = `\n${child}\n` // 换行 if (child) child = `\n${child}\n`; // 换行
return `<${tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}</${tag}>` return `<${tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}</${tag}>`;
}, },
'el-radio-group': el => { "el-radio-group": (el) => {
const { tag, disabled, vModel } = attrBuilder(el) const { tag, disabled, vModel } = attrBuilder(el);
const size = `size="${el.size}"` const size = `size="${el.size}"`;
let child = buildElRadioGroupChild(el) let child = buildElRadioGroupChild(el);
if (child) child = `\n${child}\n` // 换行 if (child) child = `\n${child}\n`; // 换行
return `<${tag} ${vModel} ${size} ${disabled}>${child}</${tag}>` return `<${tag} ${vModel} ${size} ${disabled}>${child}</${tag}>`;
}, },
'el-checkbox-group': el => { "el-checkbox-group": (el) => {
const { tag, disabled, vModel } = attrBuilder(el) const { tag, disabled, vModel } = attrBuilder(el);
const size = `size="${el.size}"` const size = `size="${el.size}"`;
const min = el.min ? `:min="${el.min}"` : '' const min = el.min ? `:min="${el.min}"` : "";
const max = el.max ? `:max="${el.max}"` : '' const max = el.max ? `:max="${el.max}"` : "";
let child = buildElCheckboxGroupChild(el) let child = buildElCheckboxGroupChild(el);
if (child) child = `\n${child}\n` // 换行 if (child) child = `\n${child}\n`; // 换行
return `<${tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}</${tag}>` return `<${tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}</${tag}>`;
}, },
'el-switch': el => { "el-switch": (el) => {
const { tag, disabled, vModel } = attrBuilder(el) const { tag, disabled, vModel } = attrBuilder(el);
const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : '' const activeText = el["active-text"]
const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : '' ? `active-text="${el["active-text"]}"`
const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : '' : "";
const inactiveColor = el['inactive-color'] ? `inactive-color="${el['inactive-color']}"` : '' const inactiveText = el["inactive-text"]
const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : '' ? `inactive-text="${el["inactive-text"]}"`
const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : '' : "";
const activeColor = el["active-color"]
? `active-color="${el["active-color"]}"`
: "";
const inactiveColor = el["inactive-color"]
? `inactive-color="${el["inactive-color"]}"`
: "";
const activeValue =
el["active-value"] !== true
? `:active-value='${JSON.stringify(el["active-value"])}'`
: "";
const inactiveValue =
el["inactive-value"] !== false
? `:inactive-value='${JSON.stringify(el["inactive-value"])}'`
: "";
return `<${tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}></${tag}>` return `<${tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}></${tag}>`;
}, },
'el-cascader': el => { "el-cascader": (el) => {
const { const { tag, disabled, vModel, clearable, placeholder, width } =
tag, disabled, vModel, clearable, placeholder, width attrBuilder(el);
} = attrBuilder(el) const options = el.options ? `:options="${el.__vModel__}Options"` : "";
const options = el.options ? `:options="${el.__vModel__}Options"` : '' const props = el.props ? `:props="${el.__vModel__}Props"` : "";
const props = el.props ? `:props="${el.__vModel__}Props"` : '' const showAllLevels = el["show-all-levels"]
const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"' ? ""
const filterable = el.filterable ? 'filterable' : '' : ':show-all-levels="false"';
const separator = el.separator === '/' ? '' : `separator="${el.separator}"` const filterable = el.filterable ? "filterable" : "";
const separator = el.separator === "/" ? "" : `separator="${el.separator}"`;
return `<${tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}></${tag}>` return `<${tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}></${tag}>`;
}, },
'el-slider': el => { "el-slider": (el) => {
const { tag, disabled, vModel } = attrBuilder(el) const { tag, disabled, vModel } = attrBuilder(el);
const min = el.min ? `:min='${el.min}'` : '' const min = el.min ? `:min='${el.min}'` : "";
const max = el.max ? `:max='${el.max}'` : '' const max = el.max ? `:max='${el.max}'` : "";
const step = el.step ? `:step='${el.step}'` : '' const step = el.step ? `:step='${el.step}'` : "";
const range = el.range ? 'range' : '' const range = el.range ? "range" : "";
const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : '' const showStops = el["show-stops"]
? `:show-stops="${el["show-stops"]}"`
: "";
return `<${tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}></${tag}>` return `<${tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}></${tag}>`;
}, },
'el-time-picker': el => { "el-time-picker": (el) => {
const { const { tag, disabled, vModel, clearable, placeholder, width } =
tag, disabled, vModel, clearable, placeholder, width attrBuilder(el);
} = attrBuilder(el) const startPlaceholder = el["start-placeholder"]
const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' ? `start-placeholder="${el["start-placeholder"]}"`
const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' : "";
const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : '' const endPlaceholder = el["end-placeholder"]
const isRange = el['is-range'] ? 'is-range' : '' ? `end-placeholder="${el["end-placeholder"]}"`
const format = el.format ? `format="${el.format}"` : '' : "";
const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : '' const rangeSeparator = el["range-separator"]
const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : '' ? `range-separator="${el["range-separator"]}"`
: "";
const isRange = el["is-range"] ? "is-range" : "";
const format = el.format ? `format="${el.format}"` : "";
const valueFormat = el["value-format"]
? `value-format="${el["value-format"]}"`
: "";
const pickerOptions = el["picker-options"]
? `:picker-options='${JSON.stringify(el["picker-options"])}'`
: "";
return `<${tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}></${tag}>` return `<${tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}></${tag}>`;
}, },
'el-date-picker': el => { "el-date-picker": (el) => {
const { const { tag, disabled, vModel, clearable, placeholder, width } =
tag, disabled, vModel, clearable, placeholder, width attrBuilder(el);
} = attrBuilder(el) const startPlaceholder = el["start-placeholder"]
const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' ? `start-placeholder="${el["start-placeholder"]}"`
const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' : "";
const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : '' const endPlaceholder = el["end-placeholder"]
const format = el.format ? `format="${el.format}"` : '' ? `end-placeholder="${el["end-placeholder"]}"`
const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : '' : "";
const type = el.type === 'date' ? '' : `type="${el.type}"` const rangeSeparator = el["range-separator"]
const readonly = el.readonly ? 'readonly' : '' ? `range-separator="${el["range-separator"]}"`
: "";
const format = el.format ? `format="${el.format}"` : "";
const valueFormat = el["value-format"]
? `value-format="${el["value-format"]}"`
: "";
const type = el.type === "date" ? "" : `type="${el.type}"`;
const readonly = el.readonly ? "readonly" : "";
return `<${tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}></${tag}>` return `<${tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}></${tag}>`;
}, },
'el-rate': el => { "el-rate": (el) => {
const { tag, disabled, vModel } = attrBuilder(el) const { tag, disabled, vModel } = attrBuilder(el);
const max = el.max ? `:max='${el.max}'` : '' const max = el.max ? `:max='${el.max}'` : "";
const allowHalf = el['allow-half'] ? 'allow-half' : '' const allowHalf = el["allow-half"] ? "allow-half" : "";
const showText = el['show-text'] ? 'show-text' : '' const showText = el["show-text"] ? "show-text" : "";
const showScore = el['show-score'] ? 'show-score' : '' const showScore = el["show-score"] ? "show-score" : "";
return `<${tag} ${vModel} ${max} ${allowHalf} ${showText} ${showScore} ${disabled}></${tag}>` return `<${tag} ${vModel} ${max} ${allowHalf} ${showText} ${showScore} ${disabled}></${tag}>`;
}, },
'el-color-picker': el => { "el-color-picker": (el) => {
const { tag, disabled, vModel } = attrBuilder(el) const { tag, disabled, vModel } = attrBuilder(el);
const size = `size="${el.size}"` const size = `size="${el.size}"`;
const showAlpha = el['show-alpha'] ? 'show-alpha' : '' const showAlpha = el["show-alpha"] ? "show-alpha" : "";
const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : '' const colorFormat = el["color-format"]
? `color-format="${el["color-format"]}"`
: "";
return `<${tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}></${tag}>` return `<${tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}></${tag}>`;
}, },
'el-upload': el => { "el-upload": (el) => {
const { tag } = el.__config__ const { tag } = el.__config__;
const disabled = el.disabled ? ':disabled=\'true\'' : '' const disabled = el.disabled ? ":disabled='true'" : "";
const action = el.action ? `:action="${el.__vModel__}Action"` : '' const action = el.action ? `:action="${el.__vModel__}Action"` : "";
const multiple = el.multiple ? 'multiple' : '' const multiple = el.multiple ? "multiple" : "";
const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : '' const listType =
const accept = el.accept ? `accept="${el.accept}"` : '' el["list-type"] !== "text" ? `list-type="${el["list-type"]}"` : "";
const name = el.name !== 'file' ? `name="${el.name}"` : '' const accept = el.accept ? `accept="${el.accept}"` : "";
const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : '' const name = el.name !== "file" ? `name="${el.name}"` : "";
const beforeUpload = `:before-upload="${el.__vModel__}BeforeUpload"` const autoUpload =
const fileList = `:file-list="${el.__vModel__}fileList"` el["auto-upload"] === false ? ':auto-upload="false"' : "";
const ref = `ref="${el.__vModel__}"` const beforeUpload = `:before-upload="${el.__vModel__}BeforeUpload"`;
let child = buildElUploadChild(el) const fileList = `:file-list="${el.__vModel__}fileList"`;
const ref = `ref="${el.__vModel__}"`;
let child = buildElUploadChild(el);
if (child) child = `\n${child}\n` // 换行 if (child) child = `\n${child}\n`; // 换行
return `<${tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}</${tag}>` return `<${tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}</${tag}>`;
}, },
tinymce: el => { tinymce: (el) => {
const { tag, vModel, placeholder } = attrBuilder(el) const { tag, vModel, placeholder } = attrBuilder(el);
const height = el.height ? `:height="${el.height}"` : '' const height = el.height ? `:height="${el.height}"` : "";
const branding = el.branding ? `:branding="${el.branding}"` : '' const branding = el.branding ? `:branding="${el.branding}"` : "";
return `<${tag} ${vModel} ${placeholder} ${height} ${branding}></${tag}>` return `<${tag} ${vModel} ${placeholder} ${height} ${branding}></${tag}>`;
} },
} };
function attrBuilder(el) { function attrBuilder(el) {
return { return {
tag: el.__config__.tag, tag: el.__config__.tag,
vModel: `v-model="${confGlobal.formModel}.${el.__vModel__}"`, vModel: `v-model="${confGlobal.formModel}.${el.__vModel__}"`,
clearable: el.clearable ? 'clearable' : '', clearable: el.clearable ? "clearable" : "",
placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '', placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : "",
width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '', width: el.style && el.style.width ? ":style=\"{width: '100%'}\"" : "",
disabled: el.disabled ? ':disabled=\'true\'' : '' disabled: el.disabled ? ":disabled='true'" : "",
} };
} }
// el-buttin 子级 // el-buttin 子级
function buildElButtonChild(scheme) { function buildElButtonChild(scheme) {
const children = [] const children = [];
const slot = scheme.__slot__ || {} const slot = scheme.__slot__ || {};
if (slot.default) { if (slot.default) {
children.push(slot.default) children.push(slot.default);
} }
return children.join('\n') return children.join("\n");
} }
// el-input 子级 // el-input 子级
function buildElInputChild(scheme) { function buildElInputChild(scheme) {
const children = [] const children = [];
const slot = scheme.__slot__ const slot = scheme.__slot__;
if (slot && slot.prepend) { if (slot && slot.prepend) {
children.push(`<template slot="prepend">${slot.prepend}</template>`) children.push(`<template slot="prepend">${slot.prepend}</template>`);
} }
if (slot && slot.append) { if (slot && slot.append) {
children.push(`<template slot="append">${slot.append}</template>`) children.push(`<template slot="append">${slot.append}</template>`);
} }
return children.join('\n') return children.join("\n");
} }
// el-select 子级 // el-select 子级
function buildElSelectChild(scheme) { function buildElSelectChild(scheme) {
const children = [] const children = [];
const slot = scheme.__slot__ const slot = scheme.__slot__;
if (slot && slot.options && slot.options.length) { if (slot && slot.options && slot.options.length) {
children.push(`<el-option v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option>`) children.push(
`<el-option v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option>`
);
} }
return children.join('\n') return children.join("\n");
} }
// el-radio-group 子级 // el-radio-group 子级
function buildElRadioGroupChild(scheme) { function buildElRadioGroupChild(scheme) {
const children = [] const children = [];
const slot = scheme.__slot__ const slot = scheme.__slot__;
const config = scheme.__config__ const config = scheme.__config__;
if (slot && slot.options && slot.options.length) { if (slot && slot.options && slot.options.length) {
const tag = config.optionType === 'button' ? 'el-radio-button' : 'el-radio' const tag = config.optionType === "button" ? "el-radio-button" : "el-radio";
const border = config.border ? 'border' : '' const border = config.border ? "border" : "";
children.push(`<${tag} v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`) children.push(
`<${tag} v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`
);
} }
return children.join('\n') return children.join("\n");
} }
// el-checkbox-group 子级 // el-checkbox-group 子级
function buildElCheckboxGroupChild(scheme) { function buildElCheckboxGroupChild(scheme) {
const children = [] const children = [];
const slot = scheme.__slot__ const slot = scheme.__slot__;
const config = scheme.__config__ const config = scheme.__config__;
if (slot && slot.options && slot.options.length) { if (slot && slot.options && slot.options.length) {
const tag = config.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox' const tag =
const border = config.border ? 'border' : '' config.optionType === "button" ? "el-checkbox-button" : "el-checkbox";
children.push(`<${tag} v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`) const border = config.border ? "border" : "";
children.push(
`<${tag} v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`
);
} }
return children.join('\n') return children.join("\n");
} }
// el-upload 子级 // el-upload 子级
function buildElUploadChild(scheme) { function buildElUploadChild(scheme) {
const list = [] const list = [];
const config = scheme.__config__ const config = scheme.__config__;
if (scheme['list-type'] === 'picture-card') list.push('<i class="el-icon-plus"></i>') if (scheme["list-type"] === "picture-card")
else list.push(`<el-button size="small" type="primary" icon="el-icon-upload">${config.buttonText}</el-button>`) list.push('<i class="el-icon-plus"></i>');
if (config.showTip) list.push(`<div slot="tip" class="el-upload__tip">只能上传不超过 ${config.fileSize}${config.sizeUnit}${scheme.accept}文件</div>`) else
return list.join('\n') list.push(
`<el-button size="small" type="primary" icon="el-icon-upload">${config.buttonText}</el-button>`
);
if (config.showTip)
list.push(
`<div slot="tip" class="el-upload__tip">只能上传不超过 ${config.fileSize}${config.sizeUnit}${scheme.accept}文件</div>`
);
return list.join("\n");
} }
/** /**
@ -379,21 +439,23 @@ function buildElUploadChild(scheme) {
* @param {String} type 生成类型,文件或弹窗等 * @param {String} type 生成类型,文件或弹窗等
*/ */
export function makeUpHtml(formConfig, type) { export function makeUpHtml(formConfig, type) {
const htmlList = [] const htmlList = [];
confGlobal = formConfig confGlobal = formConfig;
// 判断布局是否都沾满了24个栅格以备后续简化代码结构 // 判断布局是否都沾满了24个栅格以备后续简化代码结构
someSpanIsNot24 = formConfig.fields.some(item => item.__config__.span !== 24) someSpanIsNot24 = formConfig.fields.some(
(item) => item.__config__.span !== 24
);
// 遍历渲染每个组件成html // 遍历渲染每个组件成html
formConfig.fields.forEach(el => { formConfig.fields.forEach((el) => {
htmlList.push(layouts[el.__config__.layout](el)) htmlList.push(layouts[el.__config__.layout](el));
}) });
const htmlStr = htmlList.join('\n') const htmlStr = htmlList.join("\n");
// 将组件代码放进form标签 // 将组件代码放进form标签
let temp = buildFormTemplate(formConfig, htmlStr, type) let temp = buildFormTemplate(formConfig, htmlStr, type);
// dialog标签包裹代码 // dialog标签包裹代码
if (type === 'dialog') { if (type === "dialog") {
temp = dialogWrapper(temp) temp = dialogWrapper(temp);
} }
confGlobal = null confGlobal = null;
return temp return temp;
} }

View File

@ -1,17 +1,17 @@
import { isArray } from 'util' // import { Array.isArray } from 'util'
import { exportDefault, titleCase, deepClone } from '@/utils/index' import { exportDefault, titleCase, deepClone } from "@/utils/index";
import ruleTrigger from './ruleTrigger' import ruleTrigger from "./ruleTrigger";
const units = { const units = {
KB: '1024', KB: "1024",
MB: '1024 / 1024', MB: "1024 / 1024",
GB: '1024 / 1024 / 1024' GB: "1024 / 1024 / 1024",
} };
let confGlobal let confGlobal;
const inheritAttrs = { const inheritAttrs = {
file: '', file: "",
dialog: 'inheritAttrs: false,' dialog: "inheritAttrs: false,",
} };
/** /**
* 组装js 【入口函数】 * 组装js 【入口函数】
@ -19,212 +19,263 @@ const inheritAttrs = {
* @param {String} type 生成类型,文件或弹窗等 * @param {String} type 生成类型,文件或弹窗等
*/ */
export function makeUpJs(formConfig, type) { export function makeUpJs(formConfig, type) {
confGlobal = formConfig = deepClone(formConfig) confGlobal = formConfig = deepClone(formConfig);
const dataList = [] const dataList = [];
const ruleList = [] const ruleList = [];
const optionsList = [] const optionsList = [];
const propsList = [] const propsList = [];
const methodList = mixinMethod(type) const methodList = mixinMethod(type);
const uploadVarList = [] const uploadVarList = [];
const created = [] const created = [];
formConfig.fields.forEach(el => {
buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList, created)
})
formConfig.fields.forEach((el) => {
buildAttributes(
el,
dataList,
ruleList,
optionsList,
methodList,
propsList,
uploadVarList,
created
);
});
console.log(
dataList,
ruleList,
optionsList,
methodList,
propsList,
uploadVarList,
created
);
const script = buildexport( const script = buildexport(
formConfig, formConfig,
type, type,
dataList.join('\n'), dataList.join("\n"),
ruleList.join('\n'), ruleList.join("\n"),
optionsList.join('\n'), optionsList.join("\n"),
uploadVarList.join('\n'), uploadVarList.join("\n"),
propsList.join('\n'), propsList.join("\n"),
methodList.join('\n'), methodList.join("\n"),
created.join('\n') created.join("\n")
) );
confGlobal = null confGlobal = null;
return script return script;
} }
// 构建组件属性 // 构建组件属性
function buildAttributes(scheme, dataList, ruleList, optionsList, methodList, propsList, uploadVarList, created) { function buildAttributes(
const config = scheme.__config__ scheme,
const slot = scheme.__slot__ dataList,
buildData(scheme, dataList) ruleList,
buildRules(scheme, ruleList) optionsList,
methodList,
propsList,
uploadVarList,
created
) {
const config = scheme.__config__;
const slot = scheme.__slot__;
buildData(scheme, dataList);
buildRules(scheme, ruleList);
// 特殊处理options属性 // 特殊处理options属性
if (scheme.options || (slot && slot.options && slot.options.length)) { if (scheme.options || (slot && slot.options && slot.options.length)) {
buildOptions(scheme, optionsList) buildOptions(scheme, optionsList);
if (config.dataType === 'dynamic') { if (config.dataType === "dynamic") {
const model = `${scheme.__vModel__}Options` const model = `${scheme.__vModel__}Options`;
const options = titleCase(model) const options = titleCase(model);
const methodName = `get${options}` const methodName = `get${options}`;
buildOptionMethod(methodName, model, methodList, scheme) buildOptionMethod(methodName, model, methodList, scheme);
callInCreated(methodName, created) callInCreated(methodName, created);
} }
} }
// 处理props // 处理props
if (scheme.props && scheme.props.props) { if (scheme.props && scheme.props.props) {
buildProps(scheme, propsList) buildProps(scheme, propsList);
} }
// 处理el-upload的action // 处理el-upload的action
if (scheme.action && config.tag === 'el-upload') { if (scheme.action && config.tag === "el-upload") {
uploadVarList.push( uploadVarList.push(
`${scheme.__vModel__}Action: '${scheme.action}', `${scheme.__vModel__}Action: '${scheme.action}',
${scheme.__vModel__}fileList: [],` ${scheme.__vModel__}fileList: [],`
) );
methodList.push(buildBeforeUpload(scheme)) methodList.push(buildBeforeUpload(scheme));
// 非自动上传时,生成手动上传的函数 // 非自动上传时,生成手动上传的函数
if (!scheme['auto-upload']) { if (!scheme["auto-upload"]) {
methodList.push(buildSubmitUpload(scheme)) methodList.push(buildSubmitUpload(scheme));
} }
} }
// 构建子级组件属性 // 构建子级组件属性
if (config.children) { if (config.children) {
config.children.forEach(item => { config.children.forEach((item) => {
buildAttributes(item, dataList, ruleList, optionsList, methodList, propsList, uploadVarList, created) buildAttributes(
}) item,
dataList,
ruleList,
optionsList,
methodList,
propsList,
uploadVarList,
created
);
});
} }
} }
// 在Created调用函数 // 在Created调用函数
function callInCreated(methodName, created) { function callInCreated(methodName, created) {
created.push(`this.${methodName}()`) created.push(`this.${methodName}()`);
} }
// 混入处理函数 // 混入处理函数
function mixinMethod(type) { function mixinMethod(type) {
const list = []; const const list = [];
minxins = { const minxins = {
file: confGlobal.formBtns ? { file: confGlobal.formBtns
submitForm: `submitForm() { ? {
this.$refs['${confGlobal.formRef}'].validate(valid => { submitForm: `const submitForm = () => {
${confGlobal.formRef}.value.validate(valid => {
if(!valid) return if(!valid) return
// TODO 提交表单 // TODO 提交表单
}) })
}`,
resetForm: `const resetForm = () => {
${confGlobal.formRef}.value.resetFields()
}`,
}
: null,
dialog: {
onOpen: "const onOpen = () => {},",
onClose: `const onClose = () => {
${confGlobal.formRef}.value.resetFields()
},`, },`,
resetForm: `resetForm() { close: `const close = () => {
this.$refs['${confGlobal.formRef}'].resetFields()
},`
} : null,
dialog: {
onOpen: 'onOpen() {},',
onClose: `onClose() {
this.$refs['${confGlobal.formRef}'].resetFields()
},`,
close: `close() {
this.$emit('update:visible', false) this.$emit('update:visible', false)
},`, },`,
handelConfirm: `handelConfirm() { handelConfirm: `const handelConfirm = () => {
this.$refs['${confGlobal.formRef}'].validate(valid => { ${confGlobal.formRef}.value.validate(valid => {
if(!valid) return if(!valid) return
this.close() this.close()
}) })
},` },`,
} },
} };
const methods = minxins[type] const methods = minxins[type];
if (methods) { if (methods) {
Object.keys(methods).forEach(key => { Object.keys(methods).forEach((key) => {
list.push(methods[key]) list.push(methods[key]);
}) });
} }
return list return list;
} }
// 构建data // 构建data
function buildData(scheme, dataList) { function buildData(scheme, dataList) {
const config = scheme.__config__ const config = scheme.__config__;
if (scheme.__vModel__ === undefined) return if (scheme.__vModel__ === undefined) return;
const defaultValue = JSON.stringify(config.defaultValue) const defaultValue = JSON.stringify(config.defaultValue);
dataList.push(`${scheme.__vModel__}: ${defaultValue},`) dataList.push(`${scheme.__vModel__}: ${defaultValue},`);
} }
// 构建校验规则 // 构建校验规则
function buildRules(scheme, ruleList) { function buildRules(scheme, ruleList) {
const config = scheme.__config__ const config = scheme.__config__;
if (scheme.__vModel__ === undefined) return if (scheme.__vModel__ === undefined) return;
const rules = [] const rules = [];
if (ruleTrigger[config.tag]) { if (ruleTrigger[config.tag]) {
if (config.required) { if (config.required) {
const type = isArray(config.defaultValue) ? 'type: \'array\',' : '' const type = Array.isArray(config.defaultValue) ? "type: 'array'," : "";
let message = isArray(config.defaultValue) ? `请至少选择一个${config.label}` : scheme.placeholder let message = Array.isArray(config.defaultValue)
if (message === undefined) message = `${config.label}不能为空` ? `请至少选择一个${config.label}`
rules.push(`{ required: true, ${type} message: '${message}', trigger: '${ruleTrigger[config.tag]}' }`) : scheme.placeholder;
if (message === undefined) message = `${config.label}不能为空`;
rules.push(
`{ required: true, ${type} message: '${message}', trigger: '${
ruleTrigger[config.tag]
}' }`
);
} }
if (config.regList && isArray(config.regList)) { if (config.regList && Array.isArray(config.regList)) {
config.regList.forEach(item => { config.regList.forEach((item) => {
if (item.pattern) { if (item.pattern) {
rules.push( rules.push(
`{ pattern: ${eval(item.pattern)}, message: '${item.message}', trigger: '${ruleTrigger[config.tag]}' }` `{ pattern: ${eval(item.pattern)}, message: '${
) item.message
}', trigger: '${ruleTrigger[config.tag]}' }`
);
} }
}) });
} }
ruleList.push(`${scheme.__vModel__}: [${rules.join(',')}],`) ruleList.push(`${scheme.__vModel__}: [${rules.join(",")}],`);
} }
} }
// 构建options // 构建options
function buildOptions(scheme, optionsList) { function buildOptions(scheme, optionsList) {
if (scheme.__vModel__ === undefined) return if (scheme.__vModel__ === undefined) return;
// el-cascader直接有options属性其他组件都是定义在slot中所以有两处判断 // el-cascader直接有options属性其他组件都是定义在slot中所以有两处判断
let { options } = scheme let { options } = scheme;
if (!options) options = scheme.__slot__.options if (!options) options = scheme.__slot__.options;
if (scheme.__config__.dataType === 'dynamic') { options = [] } if (scheme.__config__.dataType === "dynamic") {
const str = `${scheme.__vModel__}Options: ${JSON.stringify(options)},` options = [];
optionsList.push(str) }
const str = `${scheme.__vModel__}Options: ${JSON.stringify(options)},`;
optionsList.push(str);
} }
function buildProps(scheme, propsList) { function buildProps(scheme, propsList) {
const str = `${scheme.__vModel__}Props: ${JSON.stringify(scheme.props.props)},` const str = `${scheme.__vModel__}Props: ${JSON.stringify(
propsList.push(str) scheme.props.props
)},`;
propsList.push(str);
} }
// el-upload的BeforeUpload // el-upload的BeforeUpload
function buildBeforeUpload(scheme) { function buildBeforeUpload(scheme) {
const config = scheme.__config__ const config = scheme.__config__;
const unitNum = units[config.sizeUnit]; let rightSizeCode = ''; let acceptCode = ''; const const unitNum = units[config.sizeUnit];
returnList = [] let rightSizeCode = "";
let acceptCode = "";
const returnList = [];
if (config.fileSize) { if (config.fileSize) {
rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${config.fileSize} rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${config.fileSize}
if(!isRightSize){ if(!isRightSize){
this.$message.error('文件大小超过 ${config.fileSize}${config.sizeUnit}') this.$message.error('文件大小超过 ${config.fileSize}${config.sizeUnit}')
}` }`;
returnList.push('isRightSize') returnList.push("isRightSize");
} }
if (scheme.accept) { if (scheme.accept) {
acceptCode = `let isAccept = new RegExp('${scheme.accept}').test(file.type) acceptCode = `let isAccept = new RegExp('${scheme.accept}').test(file.type)
if(!isAccept){ if(!isAccept){
this.$message.error('应该选择${scheme.accept}类型的文件') this.$message.error('应该选择${scheme.accept}类型的文件')
}` }`;
returnList.push('isAccept') returnList.push("isAccept");
} }
const str = `${scheme.__vModel__}BeforeUpload(file) { const str = `${scheme.__vModel__}BeforeUpload(file) {
${rightSizeCode} ${rightSizeCode}
${acceptCode} ${acceptCode}
return ${returnList.join('&&')} return ${returnList.join("&&")}
},` },`;
return returnList.length ? str : '' return returnList.length ? str : "";
} }
// el-upload的submit // el-upload的submit
function buildSubmitUpload(scheme) { function buildSubmitUpload(scheme) {
const str = `submitUpload() { const str = `submitUpload() {
this.$refs['${scheme.__vModel__}'].submit() this.$refs['${scheme.__vModel__}'].submit()
},` },`;
return str return str;
} }
function buildOptionMethod(methodName, model, methodList, scheme) { function buildOptionMethod(methodName, model, methodList, scheme) {
const config = scheme.__config__ const config = scheme.__config__;
const str = `${methodName}() { const str = `${methodName}() {
// 注意this.$axios是通过Vue.prototype.$axios = axios挂载产生的 // 注意this.$axios是通过Vue.prototype.$axios = axios挂载产生的
this.$axios({ this.$axios({
@ -234,12 +285,42 @@ function buildOptionMethod(methodName, model, methodList, scheme) {
var { data } = resp var { data } = resp
this.${model} = data.${config.dataKey} this.${model} = data.${config.dataKey}
}) })
},` },`;
methodList.push(str) methodList.push(str);
} }
// js整体拼接 // js整体拼接
function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, methods, created) { function buildexport(
conf,
type,
data,
rules,
selectOptions,
uploadVar,
props,
methods,
created
) {
const newStr = `
import { ref, reactive, toRefs } from "vue";
const data = reactive({
${conf.formModel}: {
${data}
},
${conf.formRules}: {
${rules}
},
})
const { ${conf.formModel}, ${conf.formRules} } = toRefs(data);
const ${confGlobal.formRef} = ref();
${methods}
${created}
`;
return newStr;
const str = `${exportDefault}{ const str = `${exportDefault}{
${inheritAttrs[type]} ${inheritAttrs[type]}
components: {}, components: {},
@ -266,6 +347,6 @@ function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, m
methods: { methods: {
${methods} ${methods}
} }
}` }`;
return str return str;
} }

View File

@ -1,33 +1,39 @@
import { deepClone } from "@/utils/index"; import { deepClone } from "@/utils/index";
import { import {
ElButton,
ElColorPicker,
ElDatePicker,
ElInput,
ElInputNumber,
ElRate,
ElRow, ElRow,
ElSelect, ElRate,
ElInput,
ElSlider, ElSlider,
ElSelect,
ElButton,
ElSwitch, ElSwitch,
ElTimePicker,
ElUpload, ElUpload,
ElCascader,
ElDatePicker,
ElTimePicker,
ElRadioGroup,
ElColorPicker,
ElInputNumber,
ElCheckboxGroup,
} from "element-plus"; } from "element-plus";
import { h, defineComponent } from "vue"; import { h, defineComponent } from "vue";
// import "element-plus/es/components/input";
const formComponentsMap = { const formComponentsMap = {
"el-row": ElRow,
"el-rate": ElRate,
"el-input": ElInput, "el-input": ElInput,
"el-input-number": ElInputNumber,
"el-switch": ElSwitch, "el-switch": ElSwitch,
"el-select": ElSelect, "el-select": ElSelect,
"el-time-picker": ElTimePicker,
"el-color-picker": ElColorPicker,
"el-date-picker": ElDatePicker,
"el-button": ElButton,
"el-row": ElRow,
"el-slider": ElSlider,
"el-rate": ElRate,
"el-upload": ElUpload, "el-upload": ElUpload,
"el-button": ElButton,
"el-slider": ElSlider,
"el-cascader": ElCascader,
"el-time-picker": ElTimePicker,
"el-radio-group": ElRadioGroup,
"el-date-picker": ElDatePicker,
"el-input-number": ElInputNumber,
"el-color-picker": ElColorPicker,
"el-checkbox-group": ElCheckboxGroup,
}; };
const componentChild = {}; const componentChild = {};
@ -46,11 +52,7 @@ keys.forEach((key) => {
function vModel(dataObject, defaultValue) { function vModel(dataObject, defaultValue) {
dataObject.props.value = defaultValue; dataObject.props.value = defaultValue;
// dataObject.onInput = (val) => {
// this.$emit("input", val);
// };
dataObject.on.input = (val) => { dataObject.on.input = (val) => {
console.log(val, "input");
this.$emit("input", val); this.$emit("input", val);
}; };
dataObject.on.change = (val) => { dataObject.on.change = (val) => {
@ -59,13 +61,13 @@ function vModel(dataObject, defaultValue) {
}; };
} }
function mountSlotFiles(h, confClone, children) { function mountSlotFiles(confClone, children) {
const childObjs = componentChild[confClone.__config__.tag]; const childObjs = componentChild[confClone.__config__.tag];
if (childObjs) { if (childObjs) {
Object.keys(childObjs).forEach((key) => { Object.keys(childObjs).forEach((key) => {
const childFunc = childObjs[key]; const childFunc = childObjs[key];
if (confClone.__slot__ && confClone.__slot__[key]) { if (confClone.__slot__ && confClone.__slot__[key]) {
children.push(childFunc(h, confClone, key)); children.push(childFunc(confClone, key));
} }
}); });
} }
@ -172,105 +174,141 @@ export default defineComponent({
required: true, required: true,
}, },
}, },
data() {
return {
arr: [],
tempTime: "",
};
},
emits: ["input"], emits: ["input"],
// setup(props, { slots, emit }) {
// const dataObject = makeDataObject();
// const confClone = deepClone(props.conf);
// // const children = [this.$slots.default] || [];
// console.log(slots, "slots");
// const children = [];
// // 如果slots文件夹存在与当前tag同名的文件则执行文件中的代码
// mountSlotFiles(h, confClone, children);
// // 将字符串类型的事件,发送为消息
// emitEvents(emit, confClone);
// setTimeout(() => {
// emit("input", "4ww3243");
// }, 3000);
// // 将json表单配置转化为vue render可以识别的 “数据对象dataObject
// const update = (val) => {
// console.log(val, "update");
// emit("input", val);
// };
// buildDataObject(update, confClone, dataObject);
// const Tag = formComponentsMap[props.conf.__config__.tag];
// return () => (
// <Tag
// {...dataObject.attrs}
// on={dataObject.on}
// modelValue={confClone.__config__.defaultValue}
// >
// {children}
// </Tag>
// );
// },
render() { render() {
const dataObject = makeDataObject(); const dataObject = makeDataObject();
const confClone = deepClone(this.conf); const confClone = deepClone(this.$props.conf);
// const children = [this.$slots.default] || [];
const children = []; const children = [];
// 如果slots文件夹存在与当前tag同名的文件则执行文件中的代码 // 如果slots文件夹存在与当前tag同名的文件则执行文件中的代码
mountSlotFiles(h, confClone, children); mountSlotFiles(confClone, children);
// 将字符串类型的事件,发送为消息 // 将字符串类型的事件,发送为消息
emitEvents.call(this, confClone); emitEvents.call(this, confClone);
// 将json表单配置转化为vue render可以识别的 “数据对象dataObject // 将json表单配置转化为vue render可以识别的 “数据对象dataObject
buildDataObject.call(this, confClone, dataObject); buildDataObject.call(this, confClone, dataObject);
console.log(dataObject); console.log(dataObject);
// return () => h("el-input", dataObject.attrs); const Tag = formComponentsMap[this.$props.conf.__config__.tag];
const Tag = formComponentsMap[this.conf.__config__.tag];
return ( return (
<Tag <Tag
{...dataObject.attrs} {...dataObject.attrs}
on={dataObject.on} on={dataObject.on}
modelValue={confClone.__config__.defaultValue} v-model={this.$props.conf.__config__.defaultValue}
> >
{children} {children}
</Tag> </Tag>
); );
return (
<>
{tag == "el-input" ? (
<el-input
{...dataObject.attrs}
on={dataObject.on}
modelValue={confClone.__config__.defaultValue}
>
{children}
</el-input>
) : tag == "el-input-number" ? (
<el-input-number
{...dataObject.attrs}
on={dataObject.on}
modelValue={confClone.__config__.defaultValue}
>
{children}
</el-input-number>
) : tag == "el-slider" ? (
<el-slider
{...dataObject.attrs}
on={dataObject.on}
modelValue={confClone.__config__.defaultValue}
>
{children}
</el-slider>
) : tag == "el-time-picker" ? (
<el-date-picker
type="datetimerange"
on={dataObject.on}
modelValue={confClone.__config__.defaultValue}
/>
) : tag === "el-switch" ? (
<el-switch
{...dataObject.attrs}
on={dataObject.on}
modelValue={confClone.__config__.defaultValue}
></el-switch>
) : tag === "el-color-picker" ? (
<el-color-picker
{...dataObject.attrs}
on={dataObject.on}
modelValue={confClone.__config__.defaultValue}
></el-color-picker>
) : tag === "el-button" ? (
<el-button
{...dataObject.attrs}
on={dataObject.on}
modelValue={confClone.__config__.defaultValue}
>
{children[1]}
</el-button>
) : tag === "el-select" ? (
<el-select
{...dataObject.attrs}
on={dataObject.on}
modelValue={confClone.__config__.defaultValue}
>
{children}
</el-select>
) : tag === "el-date-picker" ? (
<el-date-picker
{...dataObject.attrs}
on={dataObject.on}
modelValue={confClone.__config__.defaultValue}
>
{children}
</el-date-picker>
) : null}
</>
);
}, },
// render() {
// return (
// <>
// {tag == "el-input" ? (
// <el-input
// {...dataObject.attrs}
// on={dataObject.on}
// modelValue={confClone.__config__.defaultValue}
// >
// {children}
// </el-input>
// ) : tag == "el-input-number" ? (
// <el-input-number
// {...dataObject.attrs}
// on={dataObject.on}
// modelValue={confClone.__config__.defaultValue}
// >
// {children}
// </el-input-number>
// ) : tag == "el-slider" ? (
// <el-slider
// {...dataObject.attrs}
// on={dataObject.on}
// modelValue={confClone.__config__.defaultValue}
// >
// {children}
// </el-slider>
// ) : tag == "el-time-picker" ? (
// <el-date-picker
// type="datetimerange"
// on={dataObject.on}
// modelValue={confClone.__config__.defaultValue}
// />
// ) : tag === "el-switch" ? (
// <el-switch
// {...dataObject.attrs}
// on={dataObject.on}
// modelValue={confClone.__config__.defaultValue}
// ></el-switch>
// ) : tag === "el-color-picker" ? (
// <el-color-picker
// {...dataObject.attrs}
// on={dataObject.on}
// modelValue={confClone.__config__.defaultValue}
// ></el-color-picker>
// ) : tag === "el-button" ? (
// <el-button
// {...dataObject.attrs}
// on={dataObject.on}
// modelValue={confClone.__config__.defaultValue}
// >
// {children[1]}
// </el-button>
// ) : tag === "el-select" ? (
// <el-select
// {...dataObject.attrs}
// on={dataObject.on}
// modelValue={confClone.__config__.defaultValue}
// >
// {children}
// </el-select>
// ) : tag === "el-date-picker" ? (
// <el-date-picker
// {...dataObject.attrs}
// on={dataObject.on}
// modelValue={confClone.__config__.defaultValue}
// >
// {children}
// </el-date-picker>
// ) : null}
// </>
// );
// },
}); });

View File

@ -1,5 +1,5 @@
export default { export default {
default(h, conf, key) { default(conf, key) {
return conf.__slot__[key] return conf.__slot__[key];
} },
} };

View File

@ -1,13 +1,21 @@
export default { export default {
options(h, conf, key) { options(conf, key) {
const list = [] const list = [];
conf.__slot__.options.forEach(item => { conf.__slot__.options.forEach((item) => {
if (conf.__config__.optionType === 'button') { if (conf.__config__.optionType === "button") {
list.push(<el-checkbox-button label={item.value}>{item.label}</el-checkbox-button>) list.push(
<el-checkbox-button label={item.value}>
{item.label}
</el-checkbox-button>
);
} else { } else {
list.push(<el-checkbox label={item.value} border={conf.border}>{item.label}</el-checkbox>) list.push(
<el-checkbox label={item.value} border={conf.border}>
{item.label}
</el-checkbox>
);
} }
}) });
return list return list;
} },
} };

View File

@ -1,8 +1,8 @@
export default { export default {
prepend(h, conf, key) { prepend(conf, key) {
return <template slot="prepend">{conf.__slot__[key]}</template> return <template slot="prepend">{conf.__slot__[key]}</template>;
}, },
append(h, conf, key) { append(conf, key) {
return <template slot="append">{conf.__slot__[key]}</template> return <template slot="append">{conf.__slot__[key]}</template>;
} },
} };

View File

@ -1,13 +1,19 @@
export default { export default {
options(h, conf, key) { options(conf, key) {
const list = [] const list = [];
conf.__slot__.options.forEach(item => { conf.__slot__.options.forEach((item) => {
if (conf.__config__.optionType === 'button') { if (conf.__config__.optionType === "button") {
list.push(<el-radio-button label={item.value}>{item.label}</el-radio-button>) list.push(
<el-radio-button label={item.value}>{item.label}</el-radio-button>
);
} else { } else {
list.push(<el-radio label={item.value} border={conf.border}>{item.label}</el-radio>) list.push(
<el-radio label={item.value} border={conf.border}>
{item.label}
</el-radio>
);
} }
}) });
return list return list;
} },
} };

View File

@ -1,5 +1,5 @@
export default { export default {
options(h, conf, key) { options(conf, key) {
const list = []; const list = [];
conf.__slot__.options.forEach((item) => { conf.__slot__.options.forEach((item) => {
list.push( list.push(

View File

@ -1,17 +1,24 @@
export default { export default {
'list-type': (h, conf, key) => { "list-type": (conf, key) => {
const list = [] const list = [];
const config = conf.__config__ const config = conf.__config__;
if (conf['list-type'] === 'picture-card') { if (conf["list-type"] === "picture-card") {
list.push(<i class="el-icon-plus"></i>) list.push(<i class="el-icon-plus"></i>);
} else { } else {
list.push(<el-button size="small" type="primary" icon="el-icon-upload">{config.buttonText}</el-button>) list.push(
<el-button size="small" type="primary" icon="el-icon-upload">
{config.buttonText}
</el-button>
);
} }
if (config.showTip) { if (config.showTip) {
list.push( list.push(
<div slot="tip" class="el-upload__tip">只能上传不超过 {config.fileSize}{config.sizeUnit} {conf.accept}文件</div> <div slot="tip" class="el-upload__tip">
) 只能上传不超过 {config.fileSize}
{config.sizeUnit} {conf.accept}文件
</div>
);
} }
return list return list;
} },
} };

View File

@ -22,7 +22,6 @@ export default function loadBeautifier(cb) {
loadScript(beautifierUrl, () => { loadScript(beautifierUrl, () => {
loading.close(); loading.close();
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
console.log(beautifier);
beautifierObj = beautifier; beautifierObj = beautifier;
cb(beautifierObj); cb(beautifierObj);
}); });

View File

@ -5,16 +5,15 @@
width="500px" width="500px"
:close-on-click-modal="false" :close-on-click-modal="false"
:modal-append-to-body="false" :modal-append-to-body="false"
v-on="$listeners"
@open="onOpen" @open="onOpen"
@close="onClose" @close="onClose"
> >
<el-row :gutter="15"> <el-row :gutter="15">
<el-form <el-form
ref="elForm"
:model="formData" :model="formData"
:rules="rules" :rules="rules"
size="medium" ref="elFormRef"
size="default"
label-width="100px" label-width="100px"
> >
<el-col :span="24"> <el-col :span="24">
@ -31,76 +30,116 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item v-if="showFileName" label="文件名" prop="fileName"> <el-form-item v-if="showFileName" label="文件名" prop="fileName">
<el-input v-model="formData.fileName" placeholder="请输入文件名" clearable /> <el-input
v-model="formData.fileName"
placeholder="请输入文件名"
clearable
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-form> </el-form>
<!-- <el-form
ref="elForm"
:model="formData"
:rules="rules"
size="default"
label-width="100px"
>
<el-col :span="24">
<el-form-item label="生成类型" prop="type">
<el-radio-group v-model="formData.type">
<el-radio-button
v-for="(item, index) in typeOptions"
:key="index"
:label="item.value"
:disabled="item.disabled"
>
{{ item.label }}
</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="showFileName" label="文件名" prop="fileName">
<el-input
v-model="formData.fileName"
placeholder="请输入文件名"
clearable
/>
</el-form-item>
</el-col>
</el-form> -->
</el-row> </el-row>
<div slot="footer"> <div slot="footer">
<el-button @click="close"> <el-button @click="close"> 取消 </el-button>
取消 <el-button type="primary" @click="handleConfirm"> 确定 </el-button>
</el-button>
<el-button type="primary" @click="handleConfirm">
确定
</el-button>
</div> </div>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup>
import { reactive, ref, toRefs } from "vue";
const emit = defineEmits(["update:modelValue", "confirm"]);
const props = defineProps(["showFileName"]);
const { showFileName } = toRefs(props);
const data = reactive({
formData: {
fileName: undefined,
type: "file",
},
rules: {
fileName: [
{
required: true,
message: "请输入文件名",
trigger: "blur",
},
],
type: [
{
required: true,
message: "生成类型不能为空",
trigger: "change",
},
],
},
});
const { formData, rules } = toRefs(data);
const elFormRef = ref();
const typeOptions = ref([
{
label: "页面",
value: "file",
},
{
label: "弹窗",
value: "dialog",
},
]);
const onOpen = () => {
if (showFileName.value) {
formData.value.fileName = `${+new Date()}.vue`;
}
};
const onClose = () => {};
const close = (e) => {
emit("update:modelValue", false);
};
const handleConfirm = () => {
elFormRef.value.validate((valid) => {
if (!valid) return;
emit("confirm", { ...formData.value });
close();
});
};
</script>
<script> <script>
export default { export default {
inheritAttrs: false, inheritAttrs: false,
props: ['showFileName'], };
data() {
return {
formData: {
fileName: undefined,
type: 'file'
},
rules: {
fileName: [{
required: true,
message: '请输入文件名',
trigger: 'blur'
}],
type: [{
required: true,
message: '生成类型不能为空',
trigger: 'change'
}]
},
typeOptions: [{
label: '页面',
value: 'file'
}, {
label: '弹窗',
value: 'dialog'
}]
}
},
computed: {
},
watch: {},
mounted() {},
methods: {
onOpen() {
if (this.showFileName) {
this.formData.fileName = `${+new Date()}.vue`
}
},
onClose() {
},
close(e) {
this.$emit('update:visible', false)
},
handleConfirm() {
this.$refs.elForm.validate(valid => {
if (!valid) return
this.$emit('confirm', { ...this.formData })
this.close()
})
}
}
}
</script> </script>

View File

@ -37,13 +37,13 @@ const components = {
const layouts = { const layouts = {
colFormItem( colFormItem(
// h, // h,
currentItem, currentItem
index, // index,
list // list
) { ) {
const { onActiveItem } = this.$attrs; const { onActiveItem } = this.$attrs;
const config = currentItem.__config__; const config = currentItem.__config__;
console.log(arguments, "argu"); // console.log(arguments, "argu");
const child = renderChildren.apply(this, arguments); const child = renderChildren.apply(this, arguments);
let className = let className =
this.activeId === config.formId this.activeId === config.formId
@ -53,7 +53,7 @@ const layouts = {
className += " unfocus-bordered"; className += " unfocus-bordered";
let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null; let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null;
if (config.showLabel === false) labelWidth = "0"; if (config.showLabel === false) labelWidth = "0";
console.log(child); // console.log(child);
return ( return (
<el-col <el-col
span={config.span} span={config.span}
@ -72,10 +72,10 @@ const layouts = {
key={config.renderKey} key={config.renderKey}
conf={currentItem} conf={currentItem}
onInput={(event) => { onInput={(event) => {
console.log(event); // console.log(event);
// console.log(this.currentItem.__config__.defaultValue); // // console.log(this.currentItem.__config__.defaultValue);
// this.$set(config, "defaultValue", event); // this.$set(config, "defaultValue", event);
// console.log(this.currentItem.__config__.defaultValue); // // console.log(this.currentItem.__config__.defaultValue);
this.currentItem.__config__.defaultValue = event; this.currentItem.__config__.defaultValue = event;
// config.defaultValue = event; // config.defaultValue = event;
// config.defaultValue = event; // config.defaultValue = event;
@ -90,12 +90,13 @@ const layouts = {
}, },
rowFormItem( rowFormItem(
// h, // h,
currentItem, currentItem
index, // index,
list // list
) { ) {
const { onActiveItem } = this.$attrs; const { onActiveItem } = this.$attrs;
const config = currentItem.__config__; const config = currentItem.__config__;
// console.log(config);
const className = const className =
this.activeId === config.formId this.activeId === config.formId
? "drawing-row-item active-from-item" ? "drawing-row-item active-from-item"
@ -113,7 +114,6 @@ const layouts = {
</el-row> </el-row>
); );
} }
console.log(child, "row");
return ( return (
<el-col span={config.span}> <el-col span={config.span}>
<el-row <el-row
@ -127,6 +127,11 @@ const layouts = {
<span class="component-name">{config.componentName}</span> <span class="component-name">{config.componentName}</span>
<draggable <draggable
list={config.children || []} list={config.children || []}
style={{
width: "100%",
height: "100%",
display: "flex",
}}
animation={340} animation={340}
group="componentsGroup" group="componentsGroup"
class="drag-wrapper" class="drag-wrapper"
@ -134,7 +139,18 @@ const layouts = {
draggable draggable
> >
{{ {{
item: () => child, item: ({ element, index }) => {
const layout = layouts[element.__config__.layout];
if (layout) {
return layout.call(
this,
element,
index,
element.__config__.children
);
}
return layoutIsNotFound.call(this);
},
}} }}
</draggable> </draggable>
{components.itemBtns.apply(this, arguments)} {components.itemBtns.apply(this, arguments)}
@ -144,9 +160,9 @@ const layouts = {
}, },
raw( raw(
// h, // h,
currentItem, currentItem
index, // index,
list // list
) { ) {
const config = currentItem.__config__; const config = currentItem.__config__;
const child = renderChildren.apply(this, arguments); const child = renderChildren.apply(this, arguments);
@ -155,8 +171,9 @@ const layouts = {
key={config.renderKey} key={config.renderKey}
conf={currentItem} conf={currentItem}
onInput={(event) => { onInput={(event) => {
// this.$set(config, "defaultValue", event); // console.log(event, "oninput");
// console.log(this.currentItem.__config__.defaultValue); // // this.$set(config, "defaultValue", event);
// // console.log(this.currentItem.__config__.defaultValue);
this.currentItem.__config__.defaultValue = event; this.currentItem.__config__.defaultValue = event;
}} }}
> >
@ -167,20 +184,21 @@ const layouts = {
}; };
function renderChildren( function renderChildren(
// h, // // h,
currentItem, currentItem
index, // // index,
list // // list
) { ) {
const config = currentItem.__config__; const config = currentItem.__config__;
console.log(config, "config"); // // console.log(config, "config");
if (!Array.isArray(config.children)) return null; if (!Array.isArray(config.children)) return null;
return config.children.map((el, i) => { return config.children.map((el, i) => {
const layout = layouts[el.__config__.layout]; const layout = layouts[el.__config__.layout];
// console.log(el.__config__.layout, `row's child`);
if (layout) { if (layout) {
return layout.call( return layout.call(
this, this,
// h, // // h,
el, el,
i, i,
config.children config.children
@ -207,14 +225,14 @@ export default defineComponent({
}, },
}, },
render() { render() {
// console.log(this.currentItem.__config__.layout); // // console.log(this.currentItem.__config__.layout);
const layout = layouts[this.currentItem.__config__.layout]; const layout = layouts[this.currentItem.__config__.layout];
// console.log(this.currentItem); // // console.log(this.currentItem);
console.log(layout, "layout"); // // console.log(layout, "layout");
if (layout) { if (layout) {
return layout.call( return layout.call(
this, this,
// h, // // h,
this.currentItem, this.currentItem,
this.index, this.index,
this.drawingList this.drawingList

View File

@ -1,11 +1,6 @@
<template> <template>
<div> <div>
<el-drawer <el-drawer v-bind="$attrs" @opened="onOpen" @close="onClose">
v-bind="$attrs"
v-on="$listeners"
@opened="onOpen"
@close="onClose"
>
<div style="height: 100%"> <div style="height: 100%">
<el-row style="height: 100%; overflow: auto"> <el-row style="height: 100%; overflow: auto">
<el-col :md="24" :lg="12" class="left-editor"> <el-col :md="24" :lg="12" class="left-editor">
@ -17,22 +12,34 @@
<el-tabs v-model="activeTab" type="card" class="editor-tabs"> <el-tabs v-model="activeTab" type="card" class="editor-tabs">
<el-tab-pane name="html"> <el-tab-pane name="html">
<span slot="label"> <span slot="label">
<i v-if="activeTab === 'html'" class="el-icon-edit" /> <el-icon>
<i v-else class="el-icon-document" /> <Edit v-if="activeTab === 'html'" />
<Document v-else />
</el-icon>
<!-- <i v-if="activeTab === 'html'" class="el-icon-edit" />
<i v-else class="el-icon-document" /> -->
template template
</span> </span>
</el-tab-pane> </el-tab-pane>
<el-tab-pane name="js"> <el-tab-pane name="js">
<span slot="label"> <span slot="label">
<i v-if="activeTab === 'js'" class="el-icon-edit" /> <el-icon>
<i v-else class="el-icon-document" /> <Edit v-if="activeTab === 'js'" />
<Document v-else />
</el-icon>
<!-- <i v-if="activeTab === 'js'" class="el-icon-edit" />
<i v-else class="el-icon-document" /> -->
script script
</span> </span>
</el-tab-pane> </el-tab-pane>
<el-tab-pane name="css"> <el-tab-pane name="css">
<span slot="label"> <span slot="label">
<i v-if="activeTab === 'css'" class="el-icon-edit" /> <el-icon>
<i v-else class="el-icon-document" /> <Edit v-if="activeTab === 'css'" />
<Document v-else />
</el-icon>
<!-- <i v-if="activeTab === 'css'" class="el-icon-edit" />
<i v-else class="el-icon-document" /> -->
css css
</span> </span>
</el-tab-pane> </el-tab-pane>
@ -65,7 +72,7 @@
</span> </span>
<span <span
class="bar-btn delete-btn" class="bar-btn delete-btn"
@click="$emit('update:visible', false)" @click="$emit('update:modelValue', false)"
> >
<i class="el-icon-circle-close" /> <i class="el-icon-circle-close" />
关闭 关闭
@ -95,7 +102,8 @@
/> />
</div> </div>
</template> </template>
<script>
<script setup>
import { parse } from "@babel/parser"; import { parse } from "@babel/parser";
import ClipboardJS from "clipboard"; import ClipboardJS from "clipboard";
import { saveAs } from "file-saver"; import { saveAs } from "file-saver";
@ -111,7 +119,9 @@ import { exportDefault, beautifierConf, titleCase } from "@/utils/index";
import ResourceDialog from "./ResourceDialog"; import ResourceDialog from "./ResourceDialog";
import loadMonaco from "@/utils/loadMonaco"; import loadMonaco from "@/utils/loadMonaco";
import loadBeautifier from "@/utils/loadBeautifier"; import loadBeautifier from "@/utils/loadBeautifier";
import { computed, onBeforeUnmount, onMounted } from "vue";
import { ElMessage, ElMessageBox, ElNotification } from "element-plus";
import { Document, Edit } from "@element-plus/icons-vue";
const editorObj = { const editorObj = {
html: null, html: null,
js: null, js: null,
@ -123,193 +133,177 @@ const mode = {
css: "css", css: "css",
}; };
let beautifier; let beautifier;
let monaco; // let monaco;
const props = defineProps(["formData", "generateConf"]);
const { formData, generateConf } = toRefs(props);
const activeTab = ref("html");
const htmlCode = ref("");
const jsCode = ref("");
const cssCode = ref("");
const codeFrame = ref("");
const isIframeLoaded = ref(false);
const isInitcode = ref(false); // 保证open后两个异步只执行一次runcode
const isRefreshCode = ref(false); // 每次打开都需要重新刷新代码
const resourceVisible = ref(false);
const scripts = ref([]);
const links = ref([]);
const monaco = ref(null);
export default { const preventDefaultSave = (e) => {
components: { ResourceDialog }, if (e.key === "s" && (e.metaKey || e.ctrlKey)) {
props: ["formData", "generateConf"], e.preventDefault();
data() { }
return {
activeTab: "html",
htmlCode: "",
jsCode: "",
cssCode: "",
codeFrame: "",
isIframeLoaded: false,
isInitcode: false, // 保证open后两个异步只执行一次runcode
isRefreshCode: false, // 每次打开都需要重新刷新代码
resourceVisible: false,
scripts: [],
links: [],
monaco: null,
};
},
computed: {
resources() {
return this.scripts.concat(this.links);
},
},
watch: {},
created() {},
mounted() {
window.addEventListener("keydown", this.preventDefaultSave);
const clipboard = new ClipboardJS(".copy-btn", {
text: (trigger) => {
const codeStr = this.generateCode();
this.$notify({
title: "成功",
message: "代码已复制到剪切板,可粘贴。",
type: "success",
});
return codeStr;
},
});
clipboard.on("error", (e) => {
this.$message.error("代码复制失败");
});
},
beforeDestroy() {
window.removeEventListener("keydown", this.preventDefaultSave);
},
methods: {
preventDefaultSave(e) {
if (e.key === "s" && (e.metaKey || e.ctrlKey)) {
e.preventDefault();
}
},
onOpen() {
const { type } = this.generateConf;
this.htmlCode = makeUpHtml(this.formData, type);
this.jsCode = makeUpJs(this.formData, type);
this.cssCode = makeUpCss(this.formData);
loadBeautifier((btf) => {
beautifier = btf;
this.htmlCode = beautifier.html(this.htmlCode, beautifierConf.html);
this.jsCode = beautifier.js(this.jsCode, beautifierConf.js);
this.cssCode = beautifier.css(this.cssCode, beautifierConf.html);
loadMonaco((val) => {
monaco = val;
this.setEditorValue("editorHtml", "html", this.htmlCode);
this.setEditorValue("editorJs", "js", this.jsCode);
this.setEditorValue("editorCss", "css", this.cssCode);
if (!this.isInitcode) {
this.isRefreshCode = true;
this.isIframeLoaded && (this.isInitcode = true) && this.runCode();
}
});
});
},
onClose() {
this.isInitcode = false;
this.isRefreshCode = false;
},
iframeLoad() {
if (!this.isInitcode) {
this.isIframeLoaded = true;
this.isRefreshCode && (this.isInitcode = true) && this.runCode();
}
},
setEditorValue(id, type, codeStr) {
if (editorObj[type]) {
editorObj[type].setValue(codeStr);
} else {
editorObj[type] = monaco.editor.create(document.getElementById(id), {
value: codeStr,
theme: "vs-dark",
language: mode[type],
automaticLayout: true,
});
}
// ctrl + s 刷新
editorObj[type].onKeyDown((e) => {
if (e.keyCode === 49 && (e.metaKey || e.ctrlKey)) {
this.runCode();
}
});
},
runCode() {
const jsCodeStr = editorObj.js.getValue();
try {
const ast = parse(jsCodeStr, { sourceType: "module" });
const astBody = ast.program.body;
if (astBody.length > 1) {
this.$confirm(
"js格式不能识别仅支持修改export default的对象内容",
"提示",
{
type: "warning",
}
);
return;
}
if (astBody[0].type === "ExportDefaultDeclaration") {
const postData = {
type: "refreshFrame",
data: {
generateConf: this.generateConf,
html: editorObj.html.getValue(),
js: jsCodeStr.replace(exportDefault, ""),
css: editorObj.css.getValue(),
scripts: this.scripts,
links: this.links,
},
};
this.$refs.previewPage.contentWindow.postMessage(
postData,
location.origin
);
} else {
this.$message.error("请使用export default");
}
} catch (err) {
this.$message.error(`js错误${err}`);
console.error(err);
}
},
generateCode() {
const html = vueTemplate(editorObj.html.getValue());
const script = vueScript(editorObj.js.getValue());
const css = cssStyle(editorObj.css.getValue());
return beautifier.html(html + script + css, beautifierConf.html);
},
exportFile() {
this.$prompt("文件名:", "导出文件", {
inputValue: `${+new Date()}.vue`,
closeOnClickModal: false,
inputPlaceholder: "请输入文件名",
}).then(({ value }) => {
if (!value) value = `${+new Date()}.vue`;
const codeStr = this.generateCode();
const blob = new Blob([codeStr], { type: "text/plain;charset=utf-8" });
saveAs(blob, value);
});
},
showResource() {
this.resourceVisible = true;
},
setResource(arr) {
const scripts = [];
const links = [];
if (Array.isArray(arr)) {
arr.forEach((item) => {
if (item.endsWith(".css")) {
links.push(item);
} else {
scripts.push(item);
}
});
this.scripts = scripts;
this.links = links;
} else {
this.scripts = [];
this.links = [];
}
},
},
}; };
const onOpen = () => {
const { type } = generateConf.value;
htmlCode.value = makeUpHtml(formData.value, type);
jsCode.value = makeUpJs(formData.value, type);
cssCode.value = makeUpCss(formData.value);
loadBeautifier((btf) => {
beautifier = btf;
htmlCode.value = beautifier.html(htmlCode.value, beautifierConf.html);
jsCode.value = beautifier.js(jsCode.value, beautifierConf.js);
cssCode.value = beautifier.css(cssCode.value, beautifierConf.html);
loadMonaco((val) => {
monaco.value = val;
setEditorValue("editorHtml", "html", htmlCode.value);
setEditorValue("editorJs", "js", jsCode.value);
setEditorValue("editorCss", "css", cssCode.value);
if (!isInitcode.value) {
isRefreshCode.value = true;
isIframeLoaded.value && (isInitcode.value = true) && runCode();
}
});
});
};
const onClose = () => {
isInitcode.value = false;
isRefreshCode.value = false;
};
const iframeLoad = () => {
if (!isInitcode.value) {
isIframeLoaded.value = true;
isRefreshCode.value && (isInitcode.value = true) && runCode();
}
};
const setEditorValue = (id, type, codeStr) => {
if (editorObj[type]) {
editorObj[type].setValue(codeStr);
} else {
editorObj[type] = monaco.value.editor.create(document.getElementById(id), {
value: codeStr,
theme: "vs-dark",
language: mode[type],
automaticLayout: true,
});
}
// ctrl + s 刷新
editorObj[type].onKeyDown((e) => {
if (e.keyCode === 49 && (e.metaKey || e.ctrlKey)) {
runCode();
}
});
};
const previewPage = ref();
const runCode = () => {
const jsCodeStr = editorObj.js.getValue();
try {
const ast = parse(jsCodeStr, { sourceType: "module" });
const astBody = ast.program.body;
if (astBody.length > 1) {
ElMessageBox.confirm(
"js格式不能识别仅支持修改export default的对象内容",
"提示",
{
type: "warning",
}
);
return;
}
if (astBody[0].type === "ExportDefaultDeclaration") {
const postData = {
type: "refreshFrame",
data: {
generateConf: generateConf.value,
html: editorObj.html.getValue(),
js: jsCodeStr.replace(exportDefault, ""),
css: editorObj.css.getValue(),
scripts: scripts.value,
links: links.value,
},
};
previewPage.value.contentWindow.postMessage(postData, location.origin);
} else {
ElMessage.error("请使用export default");
}
} catch (err) {
ElMessage.error(`js错误${err}`);
console.error(err);
}
};
const generateCode = () => {
const html = vueTemplate(editorObj.html.getValue());
const script = vueScript(editorObj.js.getValue());
const css = cssStyle(editorObj.css.getValue());
return beautifier.html(html + script + css, beautifierConf.html);
};
const exportFile = () => {
ElMessageBox.prompt("文件名:", "导出文件", {
inputValue: `${+new Date()}.vue`,
closeOnClickModal: false,
inputPlaceholder: "请输入文件名",
}).then(({ value }) => {
if (!value) value = `${+new Date()}.vue`;
const codeStr = generateCode();
const blob = new Blob([codeStr], { type: "text/plain;charset=utf-8" });
saveAs(blob, value);
});
};
const showResource = () => {
resourceVisible.value = true;
};
const setResource = (arr) => {
const _scripts = [];
const _links = [];
if (Array.isArray(arr)) {
arr.forEach((item) => {
if (item.endsWith(".css")) {
links.push(item);
} else {
scripts.push(item);
}
});
scripts.value = _scripts;
links.value = _links;
} else {
scripts.value = [];
links.value = [];
}
};
const resources = computed(() => scripts.value.concat(links.value));
onMounted(() => {
window.addEventListener("keydown", preventDefaultSave);
const clipboard = new ClipboardJS(".copy-btn", {
text: (trigger) => {
const codeStr = generateCode();
ElNotification({
title: "成功",
message: "代码已复制到剪切板,可粘贴。",
type: "success",
});
return codeStr;
},
});
clipboard.on("error", (e) => {
ElMessage.error("代码复制失败");
});
});
onBeforeUnmount(() => {
window.removeEventListener("keydown", preventDefaultSave);
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -59,7 +59,6 @@ const preventDefaultSave = (e) => {
const onOpen = () => { const onOpen = () => {
loadBeautifier((btf) => { loadBeautifier((btf) => {
beautifier = btf; beautifier = btf;
console.log(btf.js);
beautifierJson.value = beautifier.js(jsonStr.value, beautifierConf.js); beautifierJson.value = beautifier.js(jsonStr.value, beautifierConf.js);
loadMonaco((val) => { loadMonaco((val) => {
@ -154,7 +153,7 @@ export default {
::v-deep .el-drawer__header { ::v-deep .el-drawer__header {
display: none; display: none;
} }
// @include action-bar; @include action-bar;
.json-editor { .json-editor {
height: calc(100vh - 33px); height: calc(100vh - 33px);

View File

@ -5,7 +5,6 @@
title="外部资源引用" title="外部资源引用"
width="600px" width="600px"
:close-on-click-modal="false" :close-on-click-modal="false"
v-on="$listeners"
@open="onOpen" @open="onOpen"
@close="onClose" @close="onClose"
> >
@ -31,45 +30,31 @@
> >
jQuery1.8.3 jQuery1.8.3
</el-button> </el-button>
<el-button <el-button plain @click="addOne('https://unpkg.com/http-vue-loader')">
plain
@click="addOne('https://unpkg.com/http-vue-loader')"
>
http-vue-loader http-vue-loader
</el-button> </el-button>
<el-button <el-button icon="el-icon-circle-plus-outline" plain @click="addOne('')">
icon="el-icon-circle-plus-outline"
plain
@click="addOne('')"
>
添加其他 添加其他
</el-button> </el-button>
</el-button-group> </el-button-group>
<div slot="footer"> <div slot="footer">
<el-button @click="close"> <el-button @click="close"> 取消 </el-button>
取消 <el-button type="primary" @click="handelConfirm"> 确定 </el-button>
</el-button>
<el-button
type="primary"
@click="handelConfirm"
>
确定
</el-button>
</div> </div>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script> <script>
import { deepClone } from '@/utils/index' import { deepClone } from "@/utils/index";
export default { export default {
components: {}, components: {},
inheritAttrs: false, inheritAttrs: false,
props: ['originResource'], props: ["originResource"],
data() { data() {
return { return {
resources: null resources: null,
} };
}, },
computed: {}, computed: {},
watch: {}, watch: {},
@ -77,40 +62,40 @@ export default {
mounted() {}, mounted() {},
methods: { methods: {
onOpen() { onOpen() {
this.resources = this.originResource.length ? deepClone(this.originResource) : [''] this.resources = this.originResource.length
}, ? deepClone(this.originResource)
onClose() { : [""];
}, },
onClose() {},
close() { close() {
this.$emit('update:visible', false) this.$emit("update:visible", false);
}, },
handelConfirm() { handelConfirm() {
const results = this.resources.filter(item => !!item) || [] const results = this.resources.filter((item) => !!item) || [];
this.$emit('save', results) this.$emit("save", results);
this.close() this.close();
if (results.length) { if (results.length) {
this.resources = results this.resources = results;
} }
}, },
deleteOne(index) { deleteOne(index) {
this.resources.splice(index, 1) this.resources.splice(index, 1);
}, },
addOne(url) { addOne(url) {
if (this.resources.indexOf(url) > -1) { if (this.resources.indexOf(url) > -1) {
this.$message('资源已存在') this.$message("资源已存在");
} else { } else {
this.resources.push(url) this.resources.push(url);
} }
} },
} },
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.add-item{ .add-item {
margin-top: 8px; margin-top: 8px;
} }
.url-item{ .url-item {
margin-bottom: 12px; margin-bottom: 12px;
} }
</style> </style>

View File

@ -193,7 +193,7 @@
label="默认值" label="默认值"
> >
<el-input <el-input
:value="setDefaultValue(activeData.__config__.defaultValue)" :modelValue="setDefaultValue(activeData.__config__.defaultValue)"
placeholder="请输入默认值" placeholder="请输入默认值"
@input="onDefaultValueInput" @input="onDefaultValueInput"
/> />
@ -203,7 +203,7 @@
label="至少应选" label="至少应选"
> >
<el-input-number <el-input-number
:value="activeData.min" :modelValue="activeData.min"
:min="0" :min="0"
placeholder="至少应选" placeholder="至少应选"
@input="$set(activeData, 'min', $event ? $event : undefined)" @input="$set(activeData, 'min', $event ? $event : undefined)"
@ -214,7 +214,7 @@
label="最多可选" label="最多可选"
> >
<el-input-number <el-input-number
:value="activeData.max" :modelValue="activeData.max"
:min="0" :min="0"
placeholder="最多可选" placeholder="最多可选"
@input="$set(activeData, 'max', $event ? $event : undefined)" @input="$set(activeData, 'max', $event ? $event : undefined)"
@ -394,7 +394,7 @@
label="开启值" label="开启值"
> >
<el-input <el-input
:value="setDefaultValue(activeData['active-value'])" :modelValue="setDefaultValue(activeData['active-value'])"
placeholder="请输入开启值" placeholder="请输入开启值"
@input="onSwitchValueInput($event, 'active-value')" @input="onSwitchValueInput($event, 'active-value')"
/> />
@ -404,7 +404,7 @@
label="关闭值" label="关闭值"
> >
<el-input <el-input
:value="setDefaultValue(activeData['inactive-value'])" :modelValue="setDefaultValue(activeData['inactive-value'])"
placeholder="请输入关闭值" placeholder="请输入关闭值"
@input="onSwitchValueInput($event, 'inactive-value')" @input="onSwitchValueInput($event, 'inactive-value')"
/> />
@ -545,7 +545,7 @@
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.format !== undefined" label="时间格式"> <el-form-item v-if="activeData.format !== undefined" label="时间格式">
<el-input <el-input
:value="activeData.format" :modelValue="activeData.format"
placeholder="请输入时间格式" placeholder="请输入时间格式"
@input="setTimeValue($event)" @input="setTimeValue($event)"
/> />
@ -671,15 +671,30 @@
:data="activeData.options" :data="activeData.options"
node-key="id" node-key="id"
:expand-on-click-node="false" :expand-on-click-node="false"
:render-content="renderContent" >
/> <!-- :render-content="renderContent" -->
<template #default="{ node, data }">
<div class="custom-tree-node">
<span>{{ node.label }}</span>
<span class="node-operation">
<ElIcon @click="append(data)" title="添加">
<Plus />
</ElIcon>
<ElIcon @click="remove(node, data)" title="删除">
<Delete />
</ElIcon>
</span>
</div>
</template>
</el-tree>
<div <div
v-if="activeData.__config__.dataType === 'static'" v-if="activeData.__config__.dataType === 'static'"
style="margin-left: 20px" style="margin-left: 20px"
> >
<el-button <el-button
style="padding-bottom: 0" style="padding-bottom: 0"
icon="el-icon-circle-plus-outline" icon="circle-plus"
link link
@click="addTreeItem" @click="addTreeItem"
> >
@ -1010,22 +1025,24 @@
</div> </div>
<treeNode-dialog <treeNode-dialog
:visible.sync="dialogVisible" v-model="dialogVisible"
title="添加选项" title="添加选项"
@commit="addNode" @commit="addNode"
/> />
<icons-dialog <icons-dialog
:visible.sync="iconsVisible" v-model="iconsVisible"
:current="activeData[currentIconModel]" :current="activeData[currentIconModel]"
@select="setIcon" @select="setIcon"
/> />
</div> </div>
</template> </template>
<script setup> <script setup>
import { Link } from "@element-plus/icons-vue"; import { Link, Plus } from "@element-plus/icons-vue";
import TreeNodeDialog from "./TreeNodeDialog.vue"; import TreeNodeDialog from "./TreeNodeDialog.vue";
import { isNumberStr } from "@/utils/index"; import { isNumberStr } from "@/utils/index";
import IconsDialog from "./IconsDialog.vue"; import IconsDialog from "./IconsDialog.vue";
import { getIdGlobal } from "@/utils/db";
import { import {
inputComponents, inputComponents,
selectComponents, selectComponents,
@ -1047,6 +1064,7 @@ const dateTimeFormat = {
// const size = ref("small"); // const size = ref("small");
// 使changeRenderKey在目标组件改变时可用 // 使changeRenderKey在目标组件改变时可用
const needRerenderList = ["tinymce"]; const needRerenderList = ["tinymce"];
const idGlobal = ref(getIdGlobal());
const props = defineProps({ const props = defineProps({
showField: { showField: {
type: Boolean, type: Boolean,
@ -1087,39 +1105,38 @@ const addTreeItem = () => {
dialogVisible.value = true; dialogVisible.value = true;
currentNode.value = activeData.value.options; currentNode.value = activeData.value.options;
}; };
const renderContent = (h, { node, data, store }) => {
return `<div class="custom-tree-node"> // const renderContent = (h, { node, data, store }) => {
<span>{node.label}</span> // return (
<span class="node-operation"> // <div class="custom-tree-node">
<i // <span>{node.label}</span>
onClick={() => this.append(data)} // <span class="node-operation">
class="el-icon-plus" // <i onClick={() => append(data)} class="el-icon-plus" title="添加"></i>
title="添加" // <i
></i> // onClick={() => remove(node, data)}
<i // class="el-icon-delete"
onClick={() => this.remove(node, data)} // title="删除"
class="el-icon-delete" // ></i>
title="删除" // </span>
></i> // </div>
</span> // );
</div>`; // };
};
const append = (data) => { const append = (data) => {
if (!data.children) { if (!data.children) {
this.$set(data, "children", []); data.children = [];
} }
this.dialogVisible = true; dialogVisible.value = true;
this.currentNode = data.children; currentNode.value = data.children;
}; };
const remove = (node, data) => { const remove = (node, data) => {
this.activeData.__config__.defaultValue = []; // 避免删除时报错 activeData.value.__config__.defaultValue = []; // 避免删除时报错
const { parent } = node; const { parent } = node;
const children = parent.data.children || parent.data; const children = parent.data.children || parent.data;
const index = children.findIndex((d) => d.id === data.id); const index = children.findIndex((d) => d.id === data.id);
children.splice(index, 1); children.splice(index, 1);
}; };
const addNode = (data) => { const addNode = (data) => {
this.currentNode.push(data); currentNode.value.push(data);
}; };
const setOptionValue = (item, val) => { const setOptionValue = (item, val) => {
item.value = isNumberStr(val) ? +val : val; item.value = isNumberStr(val) ? +val : val;
@ -1128,80 +1145,69 @@ const setDefaultValue = (val) => {
if (Array.isArray(val)) { if (Array.isArray(val)) {
return val.join(","); return val.join(",");
} }
// if (['string', 'number'].indexOf(typeof val) > -1) {
// return val
// }
if (typeof val === "boolean") { if (typeof val === "boolean") {
return `${val}`; return `${val}`;
} }
return val; return val;
}; };
const onDefaultValueInput = (str) => { const onDefaultValueInput = (str) => {
if (Array.isArray(this.activeData.__config__.defaultValue)) { if (Array.isArray(activeData.value.__config__.defaultValue)) {
// 数组 // 数组
this.$set( activeData.value.__config__.defaultValue = str
this.activeData.__config__, .split(",")
"defaultValue", .map((val) => (isNumberStr(val) ? +val : val));
str.split(",").map((val) => (isNumberStr(val) ? +val : val))
);
} else if (["true", "false"].indexOf(str) > -1) { } else if (["true", "false"].indexOf(str) > -1) {
// 布尔 // 布尔
this.$set(this.activeData.__config__, "defaultValue", JSON.parse(str)); activeData.value.__config__.defaultValue = JSON.parse(str);
} else { } else {
// 字符串和数字 // 字符串和数字
this.$set( activeData.value.__config__.defaultValue = isNumberStr(str) ? +str : str;
this.activeData.__config__,
"defaultValue",
isNumberStr(str) ? +str : str
);
} }
}; };
const onSwitchValueInput = (val, name) => { const onSwitchValueInput = (val, name) => {
if (["true", "false"].indexOf(val) > -1) { if (["true", "false"].indexOf(val) > -1) {
this.$set(this.activeData, name, JSON.parse(val)); activeData.value.name = JSON.parse(val);
} else { } else {
this.$set(this.activeData, name, isNumberStr(val) ? +val : val); activeData.value.name = isNumberStr(val) ? +val : val;
} }
}; };
const setTimeValue = (val, type) => { const setTimeValue = (val, type) => {
const valueFormat = type === "week" ? dateTimeFormat.date : val; const valueFormat = type === "week" ? dateTimeFormat.date : val;
this.$set(this.activeData.__config__, "defaultValue", null); activeData.value.__config__.defaultValue = null;
this.$set(this.activeData, "value-format", valueFormat); activeData.value.__config__["value-format"] = valueFormat;
this.$set(this.activeData, "format", val); activeData.value.format = val;
}; };
const spanChange = (val) => { const spanChange = (val) => {
formConf.value.span = val; formConf.value.span = val;
}; };
const multipleChange = (val) => { const multipleChange = (val) => {
this.$set(this.activeData.__config__, "defaultValue", val ? [] : ""); activeData.value.__config__.defaultValue = val ? [] : "";
}; };
const dateTypeChange = (val) => { const dateTypeChange = (val) => {
this.setTimeValue(dateTimeFormat[val], val); setTimeValue(dateTimeFormat[val], val);
}; };
const rangeChange = (val) => { const rangeChange = (val) => {
this.$set( activeData.value.__config__.defaultValue = val
this.activeData.__config__, ? [activeData.value.min, activeData.value.max]
"defaultValue", : activeData.value.min;
val ? [this.activeData.min, this.activeData.max] : this.activeData.min
);
}; };
const rateTextChange = (val) => { const rateTextChange = (val) => {
if (val) this.activeData["show-score"] = false; if (val) activeData.value["show-score"] = false;
}; };
const rateScoreChange = (val) => { const rateScoreChange = (val) => {
if (val) this.activeData["show-text"] = false; if (val) activeData.value["show-text"] = false;
}; };
const colorFormatChange = (val) => { const colorFormatChange = (val) => {
this.activeData.__config__.defaultValue = null; activeData.value.__config__.defaultValue = null;
this.activeData["show-alpha"] = val.indexOf("a") > -1; activeData.value["show-alpha"] = val.indexOf("a") > -1;
this.activeData.__config__.renderKey = +new Date(); // 更新renderKey,重新渲染该组件 activeData.value.__config__.renderKey = +new Date(); // 更新renderKey,重新渲染该组件
}; };
const openIconsDialog = (model) => { const openIconsDialog = (model) => {
this.iconsVisible = true; iconsVisible.value = true;
this.currentIconModel = model; currentIconModel.value = model;
}; };
const setIcon = (val) => { const setIcon = (val) => {
this.activeData[this.currentIconModel] = val; activeData.value[currentIconModel.value] = val;
}; };
const tagChange = (tagIcon) => { const tagChange = (tagIcon) => {
let target = inputComponents.find( let target = inputComponents.find(
@ -1214,8 +1220,8 @@ const tagChange = (tagIcon) => {
emit("tag-change", target); emit("tag-change", target);
}; };
const changeRenderKey = () => { const changeRenderKey = () => {
if (needRerenderList.includes(this.activeData.__config__.tag)) { if (needRerenderList.includes(activeData.value.__config__.tag)) {
this.activeData.__config__.renderKey = +new Date(); activeData.value.__config__.renderKey = +new Date();
} }
}; };
@ -1302,7 +1308,6 @@ const justifyOptions = ref([
const data = reactive({ const data = reactive({
layoutTreeProps: { layoutTreeProps: {
label(data, node) { label(data, node) {
console.log(data);
const config = data.__config__; const config = data.__config__;
return data.componentName || `${config.label}: ${data.__vModel__}`; return data.componentName || `${config.label}: ${data.__vModel__}`;
}, },

View File

@ -7,13 +7,12 @@
@open="onOpen" @open="onOpen"
@close="onClose" @close="onClose"
> >
<!-- v-on="$listeners" -->
<el-row :gutter="0"> <el-row :gutter="0">
<el-form <el-form
ref="elForm" ref="elFormRef"
:model="formData" :model="formData"
:rules="rules" :rules="rules"
size="small" size="default"
label-width="100px" label-width="100px"
> >
<el-col :span="24"> <el-col :span="24">
@ -61,7 +60,8 @@
<script setup> <script setup>
import { isNumberStr } from "@/utils/index"; import { isNumberStr } from "@/utils/index";
import { reactive, ref, toRefs, watch } from "vue"; import { reactive, ref, toRefs, watch } from "vue";
const emit = defineEmits(["update:visible", "commit"]); const emit = defineEmits(["update:modelValue", "commit"]);
// const props = defineProps(["modelValue"]);
const id = ref(100); const id = ref(100);
const dataType = ref("string"); const dataType = ref("string");
let inheritAttrs = false; let inheritAttrs = false;
@ -106,11 +106,14 @@ const onOpen = () => {
}; };
}; };
const onClose = () => {}; const onClose = () => {};
const close = () => { const close = () => {
emit("update:visible", false); emit("update:modelValue", false);
}; };
const elFormRef = ref();
const handleConfirm = () => { const handleConfirm = () => {
this.$refs.elForm.validate((valid) => { elFormRef.value.validate((valid) => {
if (!valid) return; if (!valid) return;
if (dataType.value === "number") { if (dataType.value === "number") {
formData.value.value = parseFloat(formData.value.value); formData.value.value = parseFloat(formData.value.value);

View File

@ -19,6 +19,7 @@
:clone="cloneComponent" :clone="cloneComponent"
item-key="id" item-key="id"
:sort="false" :sort="false"
@end="onEnd"
> >
<template #item="{ element }"> <template #item="{ element }">
<div class="components-item" @click="addComponent(element)"> <div class="components-item" @click="addComponent(element)">
@ -119,30 +120,27 @@
@tag-change="tagChange" @tag-change="tagChange"
@fetch-data="fetchData" @fetch-data="fetchData"
/> />
<!-- <el-input v-model="formConf.size"></el-input> -->
<!--
<form-drawer <form-drawer
:visible.sync="drawerVisible" v-model="drawerVisible"
:form-data="formData" :form-data="formData"
size="100%" size="100%"
:generate-conf="generateConf" :generate-conf="generateConf"
/> />
-->
<json-drawer <json-drawer
size="60%" size="60%"
v-model="jsonDrawerVisible" v-model="jsonDrawerVisible"
:json-str="JSON.stringify(formData)" :json-str="JSON.stringify(formData)"
@refresh="refreshJson" @refresh="refreshJson"
/> />
<!--
<code-type-dialog <code-type-dialog
:visible.sync="dialogVisible" v-model="dialogVisible"
title="选择生成类型" title="选择生成类型"
:show-file-name="showFileName" :show-file-name="showFileName"
@confirm="generate" @confirm="generate"
/> />
<input id="copyNode" type="hidden" /> <input id="copyNode" type="hidden" />
-->
<!-- 表单配置详情 --> <!-- 表单配置详情 -->
<el-dialog <el-dialog
:title="formTitle" :title="formTitle"
@ -168,21 +166,33 @@
<script setup> <script setup>
import { debounce } from "throttle-debounce"; import { debounce } from "throttle-debounce";
// import ClipboardJS from "clipboard"; import ClipboardJS from "clipboard";
import draggable from "vuedraggable"; import draggable from "vuedraggable";
import CodeTypeDialog from "./CodeTypeDialog.vue";
import logo from "@/assets/logo/logo.png"; import logo from "@/assets/logo/logo.png";
import { beautifierConf, titleCase, deepClone } from "@/utils/index"; import { beautifierConf, titleCase, deepClone } from "@/utils/index";
import drawingDefault from "@/utils/generator/drawingDefault"; import drawingDefault from "@/utils/generator/drawingDefault";
import draggableItem from "./DraggableItem"; import draggableItem from "./DraggableItem";
import RightPanel from "./RightPanel.vue"; import RightPanel from "./RightPanel.vue";
import loadBeautifier from "@/utils/loadBeautifier";
import JsonDrawer from "./JsonDrawer"; import JsonDrawer from "./JsonDrawer";
import { ElMessage, ElMessageBox } from "element-plus"; import FormDrawer from "./FormDrawer.vue";
import { ElMessage, ElMessageBox, ElNotification } from "element-plus";
import $download from "@/plugins/download.js";
import { import {
inputComponents, inputComponents,
selectComponents, selectComponents,
layoutComponents, layoutComponents,
formConf as formConfig, formConf as formConfig,
} from "@/utils/generator/config"; } from "@/utils/generator/config";
import {
makeUpHtml,
vueTemplate,
vueScript,
cssStyle,
} from "@/utils/generator/html";
import { makeUpJs } from "@/utils/generator/js";
import { makeUpCss } from "@/utils/generator/css";
import { import {
getDrawingList, getDrawingList,
saveDrawingList, saveDrawingList,
@ -192,8 +202,11 @@ import {
} from "@/utils/db"; } from "@/utils/db";
import { nextTick, onMounted, reactive, ref, toRefs, watch } from "vue"; import { nextTick, onMounted, reactive, ref, toRefs, watch } from "vue";
import axios from "axios"; import axios from "axios";
import { useRoute } from "vue-router";
let tempActiveData; let tempActiveData;
let oldActiveId; let oldActiveId;
const route = useRoute();
const drawingListInDB = getDrawingList(); const drawingListInDB = getDrawingList();
const formConfInDB = getFormConf(); const formConfInDB = getFormConf();
const idGlobal = ref(getIdGlobal()); const idGlobal = ref(getIdGlobal());
@ -218,6 +231,7 @@ const jsonDrawerVisible = ref(false);
const generateConf = ref(null); const generateConf = ref(null);
const showFileName = ref(false); const showFileName = ref(false);
const drawingList = ref([]); const drawingList = ref([]);
const operationType = ref(null);
const activeData = ref(drawingDefault[0]); const activeData = ref(drawingDefault[0]);
const activeId = ref(drawingDefault[0].formId); const activeId = ref(drawingDefault[0].formId);
const formOpen = ref(false); const formOpen = ref(false);
@ -228,7 +242,7 @@ const data = reactive({
// 表单参数 // 表单参数
formObj: { formObj: {
formId: undefined, formId: undefined,
formName: "332", formName: undefined,
content: undefined, content: undefined,
remark: undefined, remark: undefined,
}, },
@ -274,10 +288,10 @@ const setRespData = (component, resp) => {
// 此时赋值代码可写成 component[dataConsumer] = respData // 此时赋值代码可写成 component[dataConsumer] = respData
// 但为支持更深层级的赋值dataConsumer的值为'options.data',使用setObjectValueReduce // 但为支持更深层级的赋值dataConsumer的值为'options.data',使用setObjectValueReduce
setObjectValueReduce(component, dataConsumer, respData); setObjectValueReduce(component, dataConsumer, respData);
const i = drawingList.findIndex( const i = drawingList.value.findIndex(
(item) => item.__config__.renderKey === renderKey (item) => item.__config__.renderKey === renderKey
); );
if (i > -1) this.$set(drawingList, i, component); if (i > -1) drawingList.value[i] = component;
}; };
const fetchData = (component) => { const fetchData = (component) => {
@ -308,7 +322,6 @@ const cloneComponent = (origin) => {
const clone = deepClone(origin); const clone = deepClone(origin);
const config = clone.__config__; const config = clone.__config__;
config.span = formConf.value.span; // 生成代码时会根据span做精简判断 config.span = formConf.value.span; // 生成代码时会根据span做精简判断
console.log(formConf.value);
createIdAndKey(clone); createIdAndKey(clone);
clone.placeholder !== undefined && (clone.placeholder += config.label); clone.placeholder !== undefined && (clone.placeholder += config.label);
tempActiveData = clone; tempActiveData = clone;
@ -339,20 +352,20 @@ const showJson = () => {
jsonDrawerVisible.value = true; jsonDrawerVisible.value = true;
}; };
const download = () => { const download = () => {
this.dialogVisible = true; dialogVisible.value = true;
this.showFileName = true; showFileName.value = true;
this.operationType = "download"; operationType.value = "download";
}; };
const run = () => { const run = () => {
// TODO 弹窗类型异常 // TODO 弹窗类型异常
// this.dialogVisible = true // this.dialogVisible = true
// this.showFileName = false // this.showFileName = false
this.operationType = "run"; operationType.value = "run";
let data = { let data = {
fileName: undefined, fileName: undefined,
type: "file", type: "file",
}; };
this.generate(data); generate(data);
}; };
const saveIdGlobalDebounce = debounce(340, saveIdGlobal); const saveIdGlobalDebounce = debounce(340, saveIdGlobal);
const saveDrawingListDebounce = debounce(340, saveDrawingList); const saveDrawingListDebounce = debounce(340, saveDrawingList);
@ -389,18 +402,18 @@ const AssembleFormData = () => {
}; };
}; };
const generate = (data) => { const generate = (data) => {
const func = this[`exec${titleCase(this.operationType)}`]; const func = `exec${titleCase(operationType.value)}`;
this.generateConf = data; generateConf.value = data;
func && func(data); func && eval(`${func}(data)`);
}; };
const execRun = (data) => { const execRun = (data) => {
this.AssembleFormData(); AssembleFormData();
this.drawerVisible = true; drawerVisible.value = true;
}; };
const execDownload = (data) => { const execDownload = (data) => {
const codeStr = this.generateCode(); const codeStr = generateCode();
const blob = new Blob([codeStr], { type: "text/plain;charset=utf-8" }); const blob = new Blob([codeStr], { type: "text/plain;charset=utf-8" });
this.$download.saveAs(blob, data.fileName); $download.saveAs(blob, data.fileName);
}; };
const execCopy = (data) => { const execCopy = (data) => {
document.getElementById("copyNode").click(); document.getElementById("copyNode").click();
@ -424,17 +437,25 @@ const drawingItemDelete = (index, list) => {
nextTick(() => { nextTick(() => {
const len = drawingList.value.length; const len = drawingList.value.length;
if (len) { if (len) {
activeFormItem(this.drawingList[len - 1]); activeFormItem(drawingList.value[len - 1]);
} }
}); });
}; };
const generateCode = () => { const generateCode = () => {
const { type } = this.generateConf; const { type } = generateConf.value;
AssembleFormData(); AssembleFormData();
const script = vueScript(makeUpJs(this.formData, type)); const extraScript = vueScript(`
const html = vueTemplate(makeUpHtml(this.formData, type)); export default {
const css = cssStyle(makeUpCss(this.formData)); inheritAttrs: false
return beautifier.html(html + script + css, beautifierConf.html); }
`);
const script = vueScript(makeUpJs(formData.value, type), true);
const html = vueTemplate(makeUpHtml(formData.value, type));
const css = cssStyle(makeUpCss(formData.value));
return beautifier.html(
html + extraScript + script + css,
beautifierConf.html
);
}; };
const copy = () => { const copy = () => {
@ -443,57 +464,59 @@ const copy = () => {
operationType.value = "copy"; operationType.value = "copy";
}; };
const tagChange = (newTag) => { const tagChange = (newTag) => {
newTag = this.cloneComponent(newTag); newTag = cloneComponent(newTag);
const config = newTag.__config__; const config = newTag.__config__;
newTag.__vModel__ = this.activeData.__vModel__; newTag.__vModel__ = activeData.value.__vModel__;
config.formId = this.activeId; config.formId = activeId.value;
config.span = this.activeData.__config__.span; config.span = activeData.value.__config__.span;
this.activeData.__config__.tag = config.tag; activeData.value.__config__.tag = config.tag;
this.activeData.__config__.tagIcon = config.tagIcon; activeData.value.__config__.tagIcon = config.tagIcon;
this.activeData.__config__.document = config.document; activeData.value.__config__.document = config.document;
if ( if (
typeof this.activeData.__config__.defaultValue === typeof activeData.value.__config__.defaultValue ===
typeof config.defaultValue typeof config.defaultValue
) { ) {
config.defaultValue = this.activeData.__config__.defaultValue; config.defaultValue = activeData.value.__config__.defaultValue;
} }
Object.keys(newTag).forEach((key) => { Object.keys(newTag).forEach((key) => {
if (this.activeData[key] !== undefined) { if (activeData.value[key] !== undefined) {
newTag[key] = this.activeData[key]; newTag[key] = activeData.value[key];
} }
}); });
this.activeData = newTag; activeData.value = newTag;
this.updateDrawingList(newTag, this.drawingList); updateDrawingList(newTag, drawingList.value);
}; };
const updateDrawingList = (newTag, list) => { const updateDrawingList = (newTag, list) => {
const index = list.findIndex( const index = list.findIndex(
(item) => item.__config__.formId === this.activeId (item) => item.__config__.formId === activeId.value
); );
if (index > -1) { if (index > -1) {
list.splice(index, 1, newTag); list.splice(index, 1, newTag);
} else { } else {
list.forEach((item) => { list.forEach((item) => {
if (Array.isArray(item.__config__.children)) if (Array.isArray(item.__config__.children))
this.updateDrawingList(newTag, item.__config__.children); updateDrawingList(newTag, item.__config__.children);
}); });
} }
}; };
const refreshJson = (data) => { const refreshJson = (data) => {
drawingList.value = deepClone(data.fields); drawingList.value = deepClone(data.fields);
delete data.fields; delete data.fields;
this.formConf = data; formConf.value = data;
}; };
/** 保存表单信息 */ /** 保存表单信息 */
const submitForm = () => { const submitForm = () => {
formRef.value.validate((valid) => { formRef.value.validate((valid) => {
console.log(formObj.value);
return;
if (valid) { if (valid) {
if (this.form.formId != null) { if (formObj.value.formId != null) {
updateForm(this.form).then((response) => { updateForm(formObj.value).then((response) => {
ElMessage.success("修改成功"); ElMessage.success("修改成功");
}); });
} else { } else {
addForm(this.form).then((response) => { addForm(formObj.value).then((response) => {
ElMessage.success("新增成功"); ElMessage.success("新增成功");
}); });
} }
@ -517,36 +540,36 @@ onMounted(() => {
activeFormItem(drawingList.value[0]); activeFormItem(drawingList.value[0]);
drawingList.value = []; drawingList.value = [];
// const formId = that.$route.query && that.$route.query.formId; const formId = route.query && route.query.formId;
// if (formId) { if (formId) {
// getForm(formId).then((res) => { getForm(formId).then((res) => {
// that.formConf = JSON.parse(res.data.content); formConf.value = JSON.parse(res.data.content);
// that.drawingList = that.formConf.fields; drawingList.value = formConf.value.fields;
// that.form = res.data; formObj.value = res.data;
// }); });
// } else { } else {
// if (formConfInDB) { if (formConfInDB) {
// that.formConf = formConfInDB; formConf.value = formConfInDB;
// that.formConf.fields = null; formConf.value.fields = null;
// } }
// } }
// loadBeautifier((btf) => { loadBeautifier((btf) => {
// beautifier = btf; beautifier = btf;
// }); });
// const clipboard = new ClipboardJS("#copyNode", { const clipboard = new ClipboardJS("#copyNode", {
// text: (trigger) => { text: (trigger) => {
// const codeStr = this.generateCode(); const codeStr = generateCode();
// this.$notify({ ElNotification({
// title: "成功", title: "成功",
// message: "代码已复制到剪切板,可粘贴。", message: "代码已复制到剪切板,可粘贴。",
// type: "success", type: "success",
// }); });
// return codeStr; return codeStr;
// }, },
// }); });
// clipboard.on("error", (e) => { clipboard.on("error", (e) => {
// this.$message.error("代码复制失败"); ElMessage.error("代码复制失败");
// }); });
}); });
watch( watch(
@ -574,7 +597,6 @@ watch(
watch( watch(
drawingList, drawingList,
(val) => { (val) => {
// console.log(val);
saveDrawingListDebounce(val); saveDrawingListDebounce(val);
if (val.length === 0) idGlobal.value = 100; if (val.length === 0) idGlobal.value = 100;
}, },
@ -587,120 +609,13 @@ watch(
}, },
{ immediate: true } { immediate: true }
); );
</script>
<!-- <script> // 防止 firefox 下 拖拽 会新打卡一个选项卡
import { debounce } from "throttle-debounce"; document.body.ondrop = (event) => {
import ClipboardJS from "clipboard"; event.preventDefault();
import render from "@/utils/generator/render"; event.stopPropagation();
import FormDrawer from "./FormDrawer";
import {
inputComponents,
selectComponents,
layoutComponents,
formConf,
} from "@/utils/generator/config";
import { beautifierConf, titleCase, deepClone } from "@/utils/index";
import {
makeUpHtml,
vueTemplate,
vueScript,
cssStyle,
} from "@/utils/generator/html";
import { makeUpJs } from "@/utils/generator/js";
import { makeUpCss } from "@/utils/generator/css";
import CodeTypeDialog from "./CodeTypeDialog";
import DraggableItem from "./DraggableItem";
import loadBeautifier from "@/utils/loadBeautifier";
import { getForm, addForm, updateForm } from "@/api/workflow/form";
import axios from "axios";
import Vue from "vue";
let beautifier;
const emptyActiveData = { style: {}, autosize: {} };
let oldActiveId;
const drawingListInDB = getDrawingList();
const formConfInDB = getFormConf();
Vue.prototype.$axios = axios;
export default {
components: {
draggable,
render,
FormDrawer,
JsonDrawer,
RightPanel,
CodeTypeDialog,
DraggableItem,
},
data() {
return {
logo,
idGlobal,
formConf,
inputComponents,
selectComponents,
layoutComponents,
labelWidth: 100,
drawingData: {},
saveDrawingListDebounce: debounce(340, saveDrawingList),
saveIdGlobalDebounce: debounce(340, saveIdGlobal),
leftComponents: [
{
title: "输入型组件",
list: inputComponents,
},
{
title: "选择型组件",
list: selectComponents,
},
{
title: "布局型组件",
list: layoutComponents,
},
],
};
},
created() {
// 防止 firefox 下 拖拽 会新打卡一个选项卡
document.body.ondrop = (event) => {
event.preventDefault();
event.stopPropagation();
};
},
watch: {
},
mounted() {
},
methods: {
},
}; };
</script> --> </script>
<style lang="scss"> <style lang="scss">
body, body,

View File

@ -11,6 +11,7 @@ export default function createVitePlugins(viteEnv, isBuild = false) {
vue(), vue(),
vueJsx({ vueJsx({
transformOn: true, transformOn: true,
// include: ["src/**/*.vue", "src/**/*.jsx"],
}), }),
]; ];
vitePlugins.push(createAutoImport()); vitePlugins.push(createAutoImport());