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-left: $transparent-border-style;
border-right: $transparent-border-style;
}
@else if $direction==right {
} @else if $direction==right {
border-left: $color-border-style;
border-top: $transparent-border-style;
border-bottom: $transparent-border-style;
}
@else if $direction==down {
} @else if $direction==down {
border-top: $color-border-style;
border-left: $transparent-border-style;
border-right: $transparent-border-style;
}
@else if $direction==left {
} @else if $direction==left {
border-right: $color-border-style;
border-top: $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: "选项一",
value: 1,
disabled: false,
},
{
label: "选项二",
value: 2,
disabled: false,
},
],
},
@ -554,7 +556,7 @@ export const layoutComponents = [
default: "主要按钮",
},
type: "primary",
icon: "el-icon-search",
icon: "search",
round: false,
size: "default",
plain: false,

View File

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

View File

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

View File

@ -1,33 +1,39 @@
import { deepClone } from "@/utils/index";
import {
ElButton,
ElColorPicker,
ElDatePicker,
ElInput,
ElInputNumber,
ElRate,
ElRow,
ElSelect,
ElRate,
ElInput,
ElSlider,
ElSelect,
ElButton,
ElSwitch,
ElTimePicker,
ElUpload,
ElCascader,
ElDatePicker,
ElTimePicker,
ElRadioGroup,
ElColorPicker,
ElInputNumber,
ElCheckboxGroup,
} from "element-plus";
import { h, defineComponent } from "vue";
// import "element-plus/es/components/input";
const formComponentsMap = {
"el-row": ElRow,
"el-rate": ElRate,
"el-input": ElInput,
"el-input-number": ElInputNumber,
"el-switch": ElSwitch,
"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-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 = {};
@ -46,11 +52,7 @@ keys.forEach((key) => {
function vModel(dataObject, defaultValue) {
dataObject.props.value = defaultValue;
// dataObject.onInput = (val) => {
// this.$emit("input", val);
// };
dataObject.on.input = (val) => {
console.log(val, "input");
this.$emit("input", 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];
if (childObjs) {
Object.keys(childObjs).forEach((key) => {
const childFunc = childObjs[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,
},
},
data() {
return {
arr: [],
tempTime: "",
};
},
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() {
const dataObject = makeDataObject();
const confClone = deepClone(this.conf);
// const children = [this.$slots.default] || [];
const confClone = deepClone(this.$props.conf);
const children = [];
// 如果slots文件夹存在与当前tag同名的文件则执行文件中的代码
mountSlotFiles(h, confClone, children);
mountSlotFiles(confClone, children);
// 将字符串类型的事件,发送为消息
emitEvents.call(this, confClone);
// 将json表单配置转化为vue render可以识别的 “数据对象dataObject
buildDataObject.call(this, confClone, dataObject);
console.log(dataObject);
// return () => h("el-input", dataObject.attrs);
const Tag = formComponentsMap[this.conf.__config__.tag];
const Tag = formComponentsMap[this.$props.conf.__config__.tag];
return (
<Tag
{...dataObject.attrs}
on={dataObject.on}
modelValue={confClone.__config__.defaultValue}
v-model={this.$props.conf.__config__.defaultValue}
>
{children}
</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 {
default(h, conf, key) {
return conf.__slot__[key]
}
}
default(conf, key) {
return conf.__slot__[key];
},
};

View File

@ -1,13 +1,21 @@
export default {
options(h, conf, key) {
const list = []
conf.__slot__.options.forEach(item => {
if (conf.__config__.optionType === 'button') {
list.push(<el-checkbox-button label={item.value}>{item.label}</el-checkbox-button>)
options(conf, key) {
const list = [];
conf.__slot__.options.forEach((item) => {
if (conf.__config__.optionType === "button") {
list.push(
<el-checkbox-button label={item.value}>
{item.label}
</el-checkbox-button>
);
} 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 {
prepend(h, conf, key) {
return <template slot="prepend">{conf.__slot__[key]}</template>
prepend(conf, key) {
return <template slot="prepend">{conf.__slot__[key]}</template>;
},
append(h, conf, key) {
return <template slot="append">{conf.__slot__[key]}</template>
}
}
append(conf, key) {
return <template slot="append">{conf.__slot__[key]}</template>;
},
};

View File

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

View File

@ -1,17 +1,24 @@
export default {
'list-type': (h, conf, key) => {
const list = []
const config = conf.__config__
if (conf['list-type'] === 'picture-card') {
list.push(<i class="el-icon-plus"></i>)
"list-type": (conf, key) => {
const list = [];
const config = conf.__config__;
if (conf["list-type"] === "picture-card") {
list.push(<i class="el-icon-plus"></i>);
} 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) {
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, () => {
loading.close();
// eslint-disable-next-line no-undef
console.log(beautifier);
beautifierObj = beautifier;
cb(beautifierObj);
});

View File

@ -5,16 +5,15 @@
width="500px"
:close-on-click-modal="false"
:modal-append-to-body="false"
v-on="$listeners"
@open="onOpen"
@close="onClose"
>
<el-row :gutter="15">
<el-form
ref="elForm"
:model="formData"
:rules="rules"
size="medium"
ref="elFormRef"
size="default"
label-width="100px"
>
<el-col :span="24">
@ -31,76 +30,116 @@
</el-radio-group>
</el-form-item>
<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-col>
</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>
<div slot="footer">
<el-button @click="close">
取消
</el-button>
<el-button type="primary" @click="handleConfirm">
确定
</el-button>
<el-button @click="close"> 取消 </el-button>
<el-button type="primary" @click="handleConfirm"> 确定 </el-button>
</div>
</el-dialog>
</div>
</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>
export default {
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>

View File

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

View File

@ -1,11 +1,6 @@
<template>
<div>
<el-drawer
v-bind="$attrs"
v-on="$listeners"
@opened="onOpen"
@close="onClose"
>
<el-drawer v-bind="$attrs" @opened="onOpen" @close="onClose">
<div style="height: 100%">
<el-row style="height: 100%; overflow: auto">
<el-col :md="24" :lg="12" class="left-editor">
@ -17,22 +12,34 @@
<el-tabs v-model="activeTab" type="card" class="editor-tabs">
<el-tab-pane name="html">
<span slot="label">
<i v-if="activeTab === 'html'" class="el-icon-edit" />
<i v-else class="el-icon-document" />
<el-icon>
<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
</span>
</el-tab-pane>
<el-tab-pane name="js">
<span slot="label">
<i v-if="activeTab === 'js'" class="el-icon-edit" />
<i v-else class="el-icon-document" />
<el-icon>
<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
</span>
</el-tab-pane>
<el-tab-pane name="css">
<span slot="label">
<i v-if="activeTab === 'css'" class="el-icon-edit" />
<i v-else class="el-icon-document" />
<el-icon>
<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
</span>
</el-tab-pane>
@ -65,7 +72,7 @@
</span>
<span
class="bar-btn delete-btn"
@click="$emit('update:visible', false)"
@click="$emit('update:modelValue', false)"
>
<i class="el-icon-circle-close" />
关闭
@ -95,7 +102,8 @@
/>
</div>
</template>
<script>
<script setup>
import { parse } from "@babel/parser";
import ClipboardJS from "clipboard";
import { saveAs } from "file-saver";
@ -111,7 +119,9 @@ import { exportDefault, beautifierConf, titleCase } from "@/utils/index";
import ResourceDialog from "./ResourceDialog";
import loadMonaco from "@/utils/loadMonaco";
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 = {
html: null,
js: null,
@ -123,193 +133,177 @@ const mode = {
css: "css",
};
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 {
components: { ResourceDialog },
props: ["formData", "generateConf"],
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 preventDefaultSave = (e) => {
if (e.key === "s" && (e.metaKey || e.ctrlKey)) {
e.preventDefault();
}
};
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>
<style lang="scss" scoped>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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