Pre Merge pull request !21 from 弋戈辉/feature-ygh

This commit is contained in:
弋戈辉 2020-05-17 20:48:04 +08:00 committed by Gitee
commit 173f47811a
59 changed files with 4498 additions and 955 deletions

View File

@ -44,12 +44,14 @@
"axios": "0.18.1", "axios": "0.18.1",
"clipboard": "2.0.4", "clipboard": "2.0.4",
"echarts": "4.2.1", "echarts": "4.2.1",
"element-ui": "2.13.0", "element-ui": "2.12.0",
"file-saver": "2.0.1", "file-saver": "2.0.1",
"js-beautify": "^1.10.2",
"fuse.js": "3.4.4", "fuse.js": "3.4.4",
"js-beautify": "^1.10.2",
"js-cookie": "2.2.0", "js-cookie": "2.2.0",
"jsencrypt": "3.0.0-rc.1", "jsencrypt": "3.0.0-rc.1",
"monaco-editor": "^0.20.0",
"monaco-editor-webpack-plugin": "^1.9.0",
"normalize.css": "7.0.0", "normalize.css": "7.0.0",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"path-to-regexp": "2.4.0", "path-to-regexp": "2.4.0",
@ -57,8 +59,8 @@
"sortablejs": "1.8.4", "sortablejs": "1.8.4",
"vue": "2.6.10", "vue": "2.6.10",
"vue-count-to": "1.0.13", "vue-count-to": "1.0.13",
"vue-quill-editor": "3.0.6",
"vue-cropper": "0.4.9", "vue-cropper": "0.4.9",
"vue-quill-editor": "3.0.6",
"vue-router": "3.0.2", "vue-router": "3.0.2",
"vue-splitpane": "1.0.4", "vue-splitpane": "1.0.4",
"vuedraggable": "2.20.0", "vuedraggable": "2.20.0",
@ -66,8 +68,8 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "7.0.0", "@babel/core": "7.0.0",
"@babel/register": "7.0.0",
"@babel/parser": "^7.7.4", "@babel/parser": "^7.7.4",
"@babel/register": "7.0.0",
"@vue/cli-plugin-babel": "3.5.3", "@vue/cli-plugin-babel": "3.5.3",
"@vue/cli-plugin-eslint": "^3.9.1", "@vue/cli-plugin-eslint": "^3.9.1",
"@vue/cli-plugin-unit-jest": "3.5.3", "@vue/cli-plugin-unit-jest": "3.5.3",
@ -81,12 +83,15 @@
"chokidar": "2.1.5", "chokidar": "2.1.5",
"connect": "3.6.6", "connect": "3.6.6",
"eslint": "5.15.3", "eslint": "5.15.3",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-plugin-import": "^2.20.0",
"eslint-plugin-vue": "5.2.2", "eslint-plugin-vue": "5.2.2",
"html-webpack-plugin": "3.2.0", "html-webpack-plugin": "3.2.0",
"http-proxy-middleware": "^0.19.1", "http-proxy-middleware": "^0.19.1",
"husky": "1.3.1", "husky": "1.3.1",
"lint-staged": "8.1.5", "lint-staged": "8.1.5",
"mockjs": "1.0.1-beta3", "mockjs": "1.0.1-beta3",
"monaco-editor-webpack-plugin": "^1.9.0",
"node-sass": "^4.9.0", "node-sass": "^4.9.0",
"plop": "2.3.0", "plop": "2.3.0",
"runjs": "^4.3.2", "runjs": "^4.3.2",

View File

@ -0,0 +1,38 @@
<!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://cdn.bootcss.com/element-ui/2.12.0/theme-chalk/index.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script> -->
<!-- <script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script> -->
<!-- <script src="https://cdn.bootcss.com/axios/0.19.0/axios.min.js"></script> -->
<!-- <script src="https://cdn.bootcss.com/element-ui/2.12.0/index.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>
</body>
</html>

View File

@ -0,0 +1,53 @@
import request from '@/utils/request'
// 查询数据模型列表
export function listForm(query) {
return request({
url: '/system/form/list',
method: 'get',
params: query
})
}
// 查询数据模型详细
export function getForm(formId) {
return request({
url: '/system/form/' + formId,
method: 'get'
})
}
// 新增数据模型
export function addForm(data) {
return request({
url: '/system/form',
method: 'post',
data: data
})
}
// 修改数据模型
export function updateForm(data) {
return request({
url: '/system/form',
method: 'put',
data: data
})
}
// 删除数据模型
export function delForm(formId) {
return request({
url: '/system/form/' + formId,
method: 'delete'
})
}
// 导出数据模型
export function exportForm(query) {
return request({
url: '/system/form/export',
method: 'get',
params: query
})
}

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1588670460195" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1314" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M230.4 307.712c13.824 0 25.088-11.264 25.088-25.088 0-100.352 81.92-182.272 182.272-182.272s182.272 81.408 182.272 182.272c0 13.824 11.264 25.088 25.088 25.088s25.088-11.264 24.576-25.088c0-127.488-103.936-231.936-231.936-231.936S205.824 154.624 205.824 282.624c-0.512 14.336 10.752 25.088 24.576 25.088z m564.736 234.496c-11.264 0-21.504 2.048-31.232 6.144 0-44.544-40.448-81.92-88.064-81.92-14.848 0-28.16 3.584-39.936 10.24-13.824-28.16-44.544-48.128-78.848-48.128-12.288 0-24.576 2.56-35.328 7.68V284.16c0-45.568-37.888-81.92-84.48-81.92s-84.48 36.864-84.48 81.92v348.672l-69.12-112.64c-18.432-28.16-58.368-36.864-91.136-19.968-26.624 14.336-46.592 47.104-30.208 88.064 3.072 8.192 76.8 205.312 171.52 311.296 0 0 28.16 24.576 43.008 58.88 4.096 9.728 13.312 15.36 22.528 15.36 3.072 0 6.656-0.512 9.728-2.048 12.288-5.12 18.432-19.968 12.8-32.256-19.456-44.544-53.76-74.752-53.76-74.752C281.6 768 209.408 573.44 208.384 570.88c-5.12-12.8-2.56-20.992 7.168-26.112 9.216-4.608 21.504-4.608 26.112 2.56l113.152 184.32c4.096 8.704 12.8 14.336 22.528 14.336 13.824 0 25.088-10.752 25.088-25.088V284.16c0-17.92 15.36-32.256 34.816-32.256s34.816 14.336 34.816 32.256v284.16c0 13.824 10.24 25.088 24.576 25.088 13.824 0 25.088-11.264 25.088-25.088v-57.344c0-17.92 15.36-32.768 34.816-32.768 19.968 0 37.376 15.36 37.376 32.768v95.232c0 7.168 3.072 13.312 7.68 17.92 4.608 4.608 10.752 7.168 17.92 7.168 13.824 0 24.576-11.264 24.576-25.088V547.84c0-18.432 13.824-32.256 32.256-32.256 20.48 0 38.912 15.36 38.912 32.256v95.232c0 13.824 11.264 25.088 25.088 25.088s24.576-11.264 25.088-25.088v-18.944c0-18.944 12.8-32.256 30.72-32.256 18.432 0 22.528 18.944 22.528 31.744 0 1.024-11.776 99.84-50.688 173.056-30.72 58.368-45.056 112.128-51.2 146.944-2.56 13.312 6.656 26.112 19.968 28.672 1.536 0 3.072 0.512 4.608 0.512 11.776 0 22.016-8.192 24.064-20.48 5.632-31.232 18.432-79.36 46.08-132.608 43.52-81.92 55.808-186.88 56.32-193.536-0.512-50.688-29.696-83.968-72.704-83.968z"></path></path></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1588552949749" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1802" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M834.2654461 933.87476599H189.7345539A99.37494442 99.37494442 0 0 1 90.12523401 834.2654461V189.7345539A99.37494442 99.37494442 0 0 1 189.7345539 90.12523401h644.53089221A99.37494442 99.37494442 0 0 1 933.87476599 189.7345539v644.53089221A99.37494442 99.37494442 0 0 1 834.2654461 933.87476599zM189.7345539 140.04708127a49.68747262 49.68747262 0 0 0-49.68747263 49.68747263v644.53089221a49.68747262 49.68747262 0 0 0 49.68747262 49.68747262h644.53089221a49.68747262 49.68747262 0 0 0 49.68747263-49.68747262V189.7345539a49.68747262 49.68747262 0 0 0-49.68747263-49.68747263z" p-id="1803"></path><path d="M561.68747262 239.18765188h247.73423676a23.43748728 23.43748728 0 0 1 24.84373673 24.84373591 23.43748728 23.43748728 0 0 1-24.84373673 24.84373589H561.68747262a23.43748728 23.43748728 0 0 1-24.84373672-24.84373589 23.43748728 23.43748728 0 0 1 24.84373672-24.84373591z m0 123.9843057h247.73423676a24.84373591 24.84373591 0 0 1 0 49.68747262H561.68747262a24.84373591 24.84373591 0 1 1 0-49.68747262z m0 123.98430652h247.73423676a24.84373591 24.84373591 0 0 1 0 49.68747181H561.68747262a24.84373591 24.84373591 0 0 1 0-49.68747181zM214.57829062 611.1405698h594.84341876a24.84373591 24.84373591 0 0 1 0 49.68747263H214.57829062a24.84373591 24.84373591 0 0 1 0-49.68747263z m0 123.98430652h594.84341876a24.84373591 24.84373591 0 0 1 0 49.6874718H214.57829062a24.84373591 24.84373591 0 1 1 0-49.6874718z m52.03122061-280.07797001h133.82805103l32.10935696 81.79682959h46.87497372l-123.51555642-297.65608402H311.14073697l-121.40618308 297.65608403h46.87497373z m61.87496594-156.32803812a171.56240497 171.56240497 0 0 0 4.92187226-19.68748901 72.18745972 72.18745972 0 0 1 5.15624688 19.68748901l49.45309717 123.98430652H279.03137918z" p-id="1804"></path></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -189,3 +189,130 @@ aside {
.multiselect--active { .multiselect--active {
z-index: 1000 !important; z-index: 1000 !important;
} }
//form desigen 全局样式
$editorTabsborderColor: #121315;
.editor-tabs{
background: $editorTabsborderColor;
.el-tabs__header{
margin: 0;
border-bottom-color: $editorTabsborderColor;
.el-tabs__nav{
border-color: $editorTabsborderColor;
}
}
.el-tabs__item{
height: 32px;
line-height: 32px;
color: #888a8e;
border-left: 1px solid $editorTabsborderColor!important;
background: #363636;
margin-right: 5px;
user-select: none;
}
.el-tabs__item.is-active{
background: #1e1e1e;
border-bottom-color: #1e1e1e!important;
color: #fff;
}
.el-icon-edit{
color: #f1fa8c;
}
.el-icon-document{
color: #a95812;
}
}
// home
.right-scrollbar {
.el-scrollbar__view {
padding: 12px 18px 15px 15px;
}
}
.el-scrollbar__wrap {
box-sizing: border-box;
overflow-x: hidden !important;
margin-bottom: 0 !important;
}
.center-tabs{
.el-tabs__header{
margin-bottom: 0!important;
}
.el-tabs__item{
width: 50%;
text-align: center;
}
.el-tabs__nav{
width: 100%;
}
}
.reg-item{
padding: 12px 6px;
background: #f8f8f8;
position: relative;
border-radius: 4px;
.close-btn{
position: absolute;
right: -6px;
top: -6px;
display: block;
width: 16px;
height: 16px;
line-height: 16px;
background: rgba(0, 0, 0, 0.2);
border-radius: 50%;
color: #fff;
text-align: center;
z-index: 1;
cursor: pointer;
font-size: 12px;
&:hover{
background: rgba(210, 23, 23, 0.5)
}
}
& + .reg-item{
margin-top: 18px;
}
}
.action-bar{
& .el-button+.el-button {
margin-left: 15px;
}
& i {
font-size: 20px;
vertical-align: middle;
position: relative;
top: -1px;
}
}
.custom-tree-node{
width: 100%;
font-size: 14px;
.node-operation{
float: right;
}
i[class*="el-icon"] + i[class*="el-icon"]{
margin-left: 6px;
}
.el-icon-plus{
color: #409EFF;
}
.el-icon-delete{
color: #157a0c;
}
}
.el-scrollbar__view{
overflow-x: hidden;
}
.el-rate{
display: inline-block;
vertical-align: text-top;
}
.el-upload__tip{
line-height: 1.2;
}

View File

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

View File

@ -190,8 +190,8 @@
} }
/* 拖拽列样式 */ /* 拖拽列样式 */
.sortable-ghost{ // .sortable-ghost{
opacity: .8; // opacity: .8;
color: #fff!important; // color: #fff!important;
background: #42b983!important; // background: #42b983!important;
} // }

View File

@ -47,7 +47,6 @@ Vue.prototype.msgInfo = function (msg) {
Vue.component('Pagination', Pagination) Vue.component('Pagination', Pagination)
Vue.use(permission) Vue.use(permission)
/** /**
* If you don't want to use mock-server * If you don't want to use mock-server
* you want to use MockJs for mock api * you want to use MockJs for mock api

View File

@ -7,12 +7,17 @@ import { getToken } from '@/utils/auth'
NProgress.configure({ showSpinner: false }) NProgress.configure({ showSpinner: false })
const whiteList = ['/login', '/auth-redirect', '/bind', '/register'] const whiteList = ['/login', '/auth-redirect', '/bind', '/register','/preview.html']
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
NProgress.start() NProgress.start()
if (getToken()) { if (getToken()) {
/* has token*/ /* has token*/
if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入
next()
}else{
if (to.path === '/login') { if (to.path === '/login') {
next({ path: '/' }) next({ path: '/' })
NProgress.done() NProgress.done()
@ -47,6 +52,8 @@ router.beforeEach((to, from, next) => {
// 可删 ↑ // 可删 ↑
} }
} }
}
} else { } else {
// 没有token // 没有token
if (whiteList.indexOf(to.path) !== -1) { if (whiteList.indexOf(to.path) !== -1) {

View File

@ -8,7 +8,7 @@ import settings from './modules/settings'
import getters from './getters' import getters from './getters'
Vue.use(Vuex) Vue.use(Vuex)
Vue.config.devtools = process.env.NODE_ENV === 'development'
const store = new Vuex.Store({ const store = new Vuex.Store({
modules: { modules: {
app, app,

View File

@ -1,3 +1,4 @@
// 表单属性【右面板】
export const formConf = { export const formConf = {
formRef: 'elForm', formRef: 'elForm',
formModel: 'formData', formModel: 'formData',
@ -11,39 +12,58 @@ export const formConf = {
formBtns: true formBtns: true
} }
// 输入型组件 【左面板】
export const inputComponents = [ export const inputComponents = [
{ {
// 组件的自定义配置
__config__: {
label: '单行文本', label: '单行文本',
labelWidth: null,
showLabel: true,
changeTag: true,
tag: 'el-input', tag: 'el-input',
tagIcon: 'input', tagIcon: 'input',
placeholder: '请输入',
defaultValue: undefined, defaultValue: undefined,
required: true,
layout: 'colFormItem',
span: 24, span: 24,
labelWidth: null, document: 'https://element.eleme.cn/#/zh-CN/component/input',
// 正则校验规则
regList: []
},
// 组件的插槽属性
__slot__: {
prepend: '',
append: ''
},
// 其余的为可直接写在组件标签上的属性
placeholder: '请输入',
style: { width: '100%' }, style: { width: '100%' },
clearable: true, clearable: true,
prepend: '',
append: '',
'prefix-icon': '', 'prefix-icon': '',
'suffix-icon': '', 'suffix-icon': '',
maxlength: null, maxlength: null,
'show-word-limit': false, 'show-word-limit': false,
readonly: false, readonly: false,
disabled: false, disabled: false
},
{
__config__: {
label: '多行文本',
labelWidth: null,
showLabel: true,
tag: 'el-input',
tagIcon: 'textarea',
defaultValue: undefined,
required: true, required: true,
layout: 'colFormItem',
span: 24,
regList: [], regList: [],
changeTag: true, changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/input' document: 'https://element.eleme.cn/#/zh-CN/component/input'
}, },
{
label: '多行文本',
tag: 'el-input',
tagIcon: 'textarea',
type: 'textarea', type: 'textarea',
placeholder: '请输入', placeholder: '请输入',
defaultValue: undefined,
span: 24,
labelWidth: null,
autosize: { autosize: {
minRows: 4, minRows: 4,
maxRows: 4 maxRows: 4
@ -52,103 +72,131 @@ export const inputComponents = [
maxlength: null, maxlength: null,
'show-word-limit': false, 'show-word-limit': false,
readonly: false, readonly: false,
disabled: false, disabled: false
required: true,
regList: [],
changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/input'
}, },
{ {
__config__: {
label: '密码', label: '密码',
showLabel: true,
labelWidth: null,
changeTag: true,
tag: 'el-input', tag: 'el-input',
tagIcon: 'password', tagIcon: 'password',
placeholder: '请输入',
defaultValue: undefined, defaultValue: undefined,
layout: 'colFormItem',
span: 24, span: 24,
required: true,
regList: [],
document: 'https://element.eleme.cn/#/zh-CN/component/input'
},
__slot__: {
prepend: '',
append: ''
},
placeholder: '请输入',
'show-password': true, 'show-password': true,
labelWidth: null,
style: { width: '100%' }, style: { width: '100%' },
clearable: true, clearable: true,
prepend: '',
append: '',
'prefix-icon': '', 'prefix-icon': '',
'suffix-icon': '', 'suffix-icon': '',
maxlength: null, maxlength: null,
'show-word-limit': false, 'show-word-limit': false,
readonly: false, readonly: false,
disabled: false, disabled: false
required: true,
regList: [],
changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/input'
}, },
{ {
__config__: {
label: '计数器', label: '计数器',
showLabel: true,
changeTag: true,
labelWidth: null,
tag: 'el-input-number', tag: 'el-input-number',
tagIcon: 'number', tagIcon: 'number',
placeholder: '',
defaultValue: undefined, defaultValue: undefined,
span: 24, span: 24,
labelWidth: null, layout: 'colFormItem',
required: true,
regList: [],
document: 'https://element.eleme.cn/#/zh-CN/component/input-number'
},
placeholder: '',
min: undefined, min: undefined,
max: undefined, max: undefined,
step: undefined, step: 1,
'step-strictly': false, 'step-strictly': false,
precision: undefined, precision: undefined,
'controls-position': '', 'controls-position': '',
disabled: false, disabled: false
},
{
__config__: {
label: '编辑器',
showLabel: true,
changeTag: true,
labelWidth: null,
tag: 'tinymce',
tagIcon: 'rich-text',
defaultValue: null,
span: 24,
layout: 'colFormItem',
required: true, required: true,
regList: [], regList: [],
changeTag: true, document: 'http://tinymce.ax-z.cn'
document: 'https://element.eleme.cn/#/zh-CN/component/input-number' },
height: 300, // 编辑器高度
branding: false // 隐藏右下角品牌烙印
} }
] ]
// 选择型组件 【左面板】
export const selectComponents = [ export const selectComponents = [
{ {
__config__: {
label: '下拉选择', label: '下拉选择',
showLabel: true,
labelWidth: null,
tag: 'el-select', tag: 'el-select',
tagIcon: 'select', tagIcon: 'select',
placeholder: '请选择',
defaultValue: undefined, defaultValue: undefined,
layout: 'colFormItem',
span: 24, span: 24,
labelWidth: null,
style: { width: '100%' },
clearable: true,
disabled: false,
required: true, required: true,
filterable: false, regList: [],
multiple: false, changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/select'
},
__slot__: {
options: [{ options: [{
label: '选项一', label: '选项一',
value: 1 value: 1
}, { }, {
label: '选项二', label: '选项二',
value: 2 value: 2
}], }]
regList: [], },
changeTag: true, placeholder: '请选择',
document: 'https://element.eleme.cn/#/zh-CN/component/select' style: { width: '100%' },
clearable: true,
disabled: false,
filterable: false,
multiple: false
}, },
{ {
__config__: {
label: '级联选择', label: '级联选择',
showLabel: true,
labelWidth: null,
tag: 'el-cascader', tag: 'el-cascader',
tagIcon: 'cascader', tagIcon: 'cascader',
placeholder: '请选择', layout: 'colFormItem',
defaultValue: [], defaultValue: [],
dataType: 'dynamic',
span: 24, span: 24,
labelWidth: null,
style: { width: '100%' },
props: {
props: {
multiple: false
}
},
'show-all-levels': true,
disabled: false,
clearable: true,
filterable: false,
required: true, required: true,
regList: [],
changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/cascader'
},
options: [{ options: [{
id: 1, id: 1,
value: 1, value: 1,
@ -159,170 +207,219 @@ export const selectComponents = [
label: '选项1-1' label: '选项1-1'
}] }]
}], }],
dataType: 'dynamic', placeholder: '请选择',
labelKey: 'label', style: { width: '100%' },
valueKey: 'value', props: {
childrenKey: 'children', props: {
separator: '/', multiple: false,
regList: [], label: 'label',
changeTag: true, value: 'value',
document: 'https://element.eleme.cn/#/zh-CN/component/cascader' children: 'children'
}
},
'show-all-levels': true,
disabled: false,
clearable: true,
filterable: false,
separator: '/'
}, },
{ {
__config__: {
label: '单选框组', label: '单选框组',
labelWidth: null,
showLabel: true,
tag: 'el-radio-group', tag: 'el-radio-group',
tagIcon: 'radio', tagIcon: 'radio',
changeTag: true,
defaultValue: undefined, defaultValue: undefined,
layout: 'colFormItem',
span: 24, span: 24,
labelWidth: null,
style: {},
optionType: 'default', optionType: 'default',
border: false, regList: [],
size: 'medium',
disabled: false,
required: true, required: true,
border: false,
document: 'https://element.eleme.cn/#/zh-CN/component/radio'
},
__slot__: {
options: [{ options: [{
label: '选项一', label: '选项一',
value: 1 value: 1
}, { }, {
label: '选项二', label: '选项二',
value: 2 value: 2
}], }]
regList: [], },
changeTag: true, style: {},
document: 'https://element.eleme.cn/#/zh-CN/component/radio' size: 'medium',
disabled: false
}, },
{ {
__config__: {
label: '多选框组', label: '多选框组',
tag: 'el-checkbox-group', tag: 'el-checkbox-group',
tagIcon: 'checkbox', tagIcon: 'checkbox',
defaultValue: [], defaultValue: [],
span: 24, span: 24,
showLabel: true,
labelWidth: null, labelWidth: null,
style: {}, layout: 'colFormItem',
optionType: 'default', optionType: 'default',
border: false,
size: 'medium',
disabled: false,
required: true, required: true,
regList: [],
changeTag: true,
border: false,
document: 'https://element.eleme.cn/#/zh-CN/component/checkbox'
},
__slot__: {
options: [{ options: [{
label: '选项一', label: '选项一',
value: 1 value: 1
}, { }, {
label: '选项二', label: '选项二',
value: 2 value: 2
}], }]
regList: [], },
changeTag: true, style: {},
document: 'https://element.eleme.cn/#/zh-CN/component/checkbox' size: 'medium',
min: null,
max: null,
disabled: false
}, },
{ {
__config__: {
label: '开关', label: '开关',
tag: 'el-switch', tag: 'el-switch',
tagIcon: 'switch', tagIcon: 'switch',
defaultValue: false, defaultValue: false,
span: 24, span: 24,
showLabel: true,
labelWidth: null, labelWidth: null,
layout: 'colFormItem',
required: true,
regList: [],
changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/switch'
},
style: {}, style: {},
disabled: false, disabled: false,
required: true,
'active-text': '', 'active-text': '',
'inactive-text': '', 'inactive-text': '',
'active-color': null, 'active-color': null,
'inactive-color': null, 'inactive-color': null,
'active-value': true, 'active-value': true,
'inactive-value': false, 'inactive-value': false
regList: [],
changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/switch'
}, },
{ {
__config__: {
label: '滑块', label: '滑块',
tag: 'el-slider', tag: 'el-slider',
tagIcon: 'slider', tagIcon: 'slider',
defaultValue: null, defaultValue: null,
span: 24, span: 24,
showLabel: true,
layout: 'colFormItem',
labelWidth: null, labelWidth: null,
disabled: false,
required: true, required: true,
min: 0,
max: 100,
step: 1,
'show-stops': false,
range: false,
regList: [], regList: [],
changeTag: true, changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/slider' document: 'https://element.eleme.cn/#/zh-CN/component/slider'
}, },
disabled: false,
min: 0,
max: 100,
step: 1,
'show-stops': false,
range: false
},
{ {
__config__: {
label: '时间选择', label: '时间选择',
tag: 'el-time-picker', tag: 'el-time-picker',
tagIcon: 'time', tagIcon: 'time',
placeholder: '请选择',
defaultValue: null, defaultValue: null,
span: 24, span: 24,
showLabel: true,
layout: 'colFormItem',
labelWidth: null, labelWidth: null,
style: { width: '100%' },
disabled: false,
clearable: true,
required: true, required: true,
'picker-options': {
selectableRange: '00:00:00-23:59:59'
},
format: 'HH:mm:ss',
'value-format': 'HH:mm:ss',
regList: [], regList: [],
changeTag: true, changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/time-picker' document: 'https://element.eleme.cn/#/zh-CN/component/time-picker'
}, },
{ placeholder: '请选择',
label: '时间范围',
tag: 'el-time-picker',
tagIcon: 'time-range',
defaultValue: null,
span: 24,
labelWidth: null,
style: { width: '100%' }, style: { width: '100%' },
disabled: false, disabled: false,
clearable: true, clearable: true,
'picker-options': {
selectableRange: '00:00:00-23:59:59'
},
format: 'HH:mm:ss',
'value-format': 'HH:mm:ss'
},
{
__config__: {
label: '时间范围',
tag: 'el-time-picker',
tagIcon: 'time-range',
span: 24,
showLabel: true,
labelWidth: null,
layout: 'colFormItem',
defaultValue: null,
required: true, required: true,
regList: [],
changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/time-picker'
},
style: { width: '100%' },
disabled: false,
clearable: true,
'is-range': true, 'is-range': true,
'range-separator': '至', 'range-separator': '至',
'start-placeholder': '开始时间', 'start-placeholder': '开始时间',
'end-placeholder': '结束时间', 'end-placeholder': '结束时间',
format: 'HH:mm:ss', format: 'HH:mm:ss',
'value-format': 'HH:mm:ss', 'value-format': 'HH:mm:ss'
regList: [],
changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/time-picker'
}, },
{ {
__config__: {
label: '日期选择', label: '日期选择',
tag: 'el-date-picker', tag: 'el-date-picker',
tagIcon: 'date', tagIcon: 'date',
placeholder: '请选择',
defaultValue: null, defaultValue: null,
type: 'date', showLabel: true,
span: 24,
labelWidth: null, labelWidth: null,
style: { width: '100%' }, span: 24,
disabled: false, layout: 'colFormItem',
clearable: true,
required: true, required: true,
format: 'yyyy-MM-dd',
'value-format': 'yyyy-MM-dd',
readonly: false,
regList: [], regList: [],
changeTag: true, changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/date-picker' document: 'https://element.eleme.cn/#/zh-CN/component/date-picker'
}, },
placeholder: '请选择',
type: 'date',
style: { width: '100%' },
disabled: false,
clearable: true,
format: 'yyyy-MM-dd',
'value-format': 'yyyy-MM-dd',
readonly: false
},
{ {
__config__: {
label: '日期范围', label: '日期范围',
tag: 'el-date-picker', tag: 'el-date-picker',
tagIcon: 'date-range', tagIcon: 'date-range',
defaultValue: null, defaultValue: null,
span: 24, span: 24,
showLabel: true,
labelWidth: null, labelWidth: null,
required: true,
layout: 'colFormItem',
regList: [],
changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/date-picker'
},
style: { width: '100%' }, style: { width: '100%' },
type: 'daterange', type: 'daterange',
'range-separator': '至', 'range-separator': '至',
@ -330,94 +427,121 @@ export const selectComponents = [
'end-placeholder': '结束日期', 'end-placeholder': '结束日期',
disabled: false, disabled: false,
clearable: true, clearable: true,
required: true,
format: 'yyyy-MM-dd', format: 'yyyy-MM-dd',
'value-format': 'yyyy-MM-dd', 'value-format': 'yyyy-MM-dd',
readonly: false, readonly: false
regList: [],
changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/date-picker'
}, },
{ {
__config__: {
label: '评分', label: '评分',
tag: 'el-rate', tag: 'el-rate',
tagIcon: 'rate', tagIcon: 'rate',
defaultValue: 0, defaultValue: 0,
span: 24, span: 24,
showLabel: true,
labelWidth: null, labelWidth: null,
style: {}, layout: 'colFormItem',
max: 5,
'allow-half': false,
'show-text': false,
'show-score': false,
disabled: false,
required: true, required: true,
regList: [], regList: [],
changeTag: true, changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/rate' document: 'https://element.eleme.cn/#/zh-CN/component/rate'
}, },
style: {},
max: 5,
'allow-half': false,
'show-text': false,
'show-score': false,
disabled: false
},
{ {
__config__: {
label: '颜色选择', label: '颜色选择',
tag: 'el-color-picker', tag: 'el-color-picker',
tagIcon: 'color', tagIcon: 'color',
span: 24,
defaultValue: null, defaultValue: null,
showLabel: true,
labelWidth: null, labelWidth: null,
'show-alpha': false, layout: 'colFormItem',
'color-format': '',
disabled: false,
required: true, required: true,
size: 'medium',
regList: [], regList: [],
changeTag: true, changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/color-picker' document: 'https://element.eleme.cn/#/zh-CN/component/color-picker'
}, },
'show-alpha': false,
'color-format': '',
disabled: false,
size: 'medium'
},
{ {
__config__: {
label: '上传', label: '上传',
tag: 'el-upload', tag: 'el-upload',
tagIcon: 'upload', tagIcon: 'upload',
action: 'https://jsonplaceholder.typicode.com/posts/', layout: 'colFormItem',
defaultValue: null, defaultValue: null,
showLabel: true,
labelWidth: null, labelWidth: null,
disabled: false,
required: true, required: true,
span: 24,
showTip: false,
buttonText: '点击上传',
regList: [],
changeTag: true,
fileSize: 2,
sizeUnit: 'MB',
document: 'https://element.eleme.cn/#/zh-CN/component/upload'
},
__slot__: {
'list-type': true
},
action: 'https://jsonplaceholder.typicode.com/posts/',
disabled: false,
accept: '', accept: '',
name: 'file', name: 'file',
'auto-upload': true, 'auto-upload': true,
showTip: false,
buttonText: '点击上传',
fileSize: 2,
sizeUnit: 'MB',
'list-type': 'text', 'list-type': 'text',
multiple: false, multiple: false
regList: [],
changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/upload'
} }
] ]
// 布局型组件 【左面板】
export const layoutComponents = [ export const layoutComponents = [
{ {
__config__: {
layout: 'rowFormItem', layout: 'rowFormItem',
tagIcon: 'row', tagIcon: 'row',
type: 'default',
justify: 'start',
align: 'top',
label: '行容器', label: '行容器',
layoutTree: true, layoutTree: true,
children: [], children: [],
document: 'https://element.eleme.cn/#/zh-CN/component/layout' document: 'https://element.eleme.cn/#/zh-CN/component/layout'
},
type: 'default',
justify: 'start',
align: 'top'
},
{
__config__: {
label: '按钮',
showLabel: true,
changeTag: true,
labelWidth: null,
tag: 'el-button',
tagIcon: 'button',
defaultValue: undefined,
span: 24,
layout: 'colFormItem',
document: 'https://element.eleme.cn/#/zh-CN/component/button'
},
__slot__: {
default: '主要按钮'
},
type: 'primary',
icon: 'el-icon-search',
round: false,
size: 'medium',
plain: false,
circle: false,
disabled: false
} }
] ]
// 组件rule的触发方式无触发方式的组件不生成rule
export const trigger = {
'el-input': 'blur',
'el-input-number': 'blur',
'el-select': 'change',
'el-radio-group': 'change',
'el-checkbox-group': 'change',
'el-cascader': 'change',
'el-time-picker': 'change',
'el-date-picker': 'change',
'el-rate': 'change'
}

View File

@ -4,10 +4,10 @@ const styles = {
} }
function addCss(cssList, el) { function addCss(cssList, el) {
const css = styles[el.tag] const css = styles[el.__config__.tag]
css && cssList.indexOf(css) === -1 && cssList.push(css) css && cssList.indexOf(css) === -1 && cssList.push(css)
if (el.children) { if (el.__config__.children) {
el.children.forEach(el2 => addCss(cssList, el2)) el.__config__.children.forEach(el2 => addCss(cssList, el2))
} }
} }

View File

@ -0,0 +1,64 @@
const DRAWING_ITEMS = 'drawingItems'
const DRAWING_ITEMS_VERSION = '1.1'
const DRAWING_ITEMS_VERSION_KEY = 'DRAWING_ITEMS_VERSION'
const DRAWING_ID = 'idGlobal'
const TREE_NODE_ID = 'treeNodeId'
const FORM_CONF = 'formConf'
export function getDrawingList() {
// 加入缓存版本的概念,保证缓存数据与程序匹配
const version = localStorage.getItem(DRAWING_ITEMS_VERSION_KEY)
if (version !== DRAWING_ITEMS_VERSION) {
localStorage.setItem(DRAWING_ITEMS_VERSION_KEY, DRAWING_ITEMS_VERSION)
saveDrawingList([])
return null
}
const str = localStorage.getItem(DRAWING_ITEMS)
if (str) {
let result=null;
try {
result=JSON.parse(str)
} catch(err) {
console.log(err)
}
return result
}
return null
}
export function saveDrawingList(list) {
if(Array.isArray(list) && list.length > 0){
localStorage.setItem(DRAWING_ITEMS, JSON.stringify(list))
}
}
export function getIdGlobal() {
const str = localStorage.getItem(DRAWING_ID)
if (str) return parseInt(str, 10)
return 100
}
export function saveIdGlobal(id) {
localStorage.setItem(DRAWING_ID, `${id}`)
}
export function getTreeNodeId() {
const str = localStorage.getItem(TREE_NODE_ID)
if (str) return parseInt(str, 10)
return 100
}
export function saveTreeNodeId(id) {
localStorage.setItem(TREE_NODE_ID, `${id}`)
}
export function getFormConf() {
const str = localStorage.getItem(FORM_CONF)
if (str) return JSON.parse(str)
return null
}
export function saveFormConf(obj) {
localStorage.setItem(FORM_CONF, JSON.stringify(obj))
}

View File

@ -1,29 +1,37 @@
export default [ export default [
{ {
layout: 'colFormItem', __config__: {
tagIcon: 'input', label: '单行文本',
label: '手机号', labelWidth: null,
vModel: 'mobile', showLabel: true,
formId: 6, changeTag: true,
tag: 'el-input', tag: 'el-input',
placeholder: '请输入手机号', tagIcon: 'input',
defaultValue: '', defaultValue: undefined,
required: true,
layout: 'colFormItem',
span: 24, span: 24,
document: 'https://element.eleme.cn/#/zh-CN/component/input',
// 正则校验规则
regList: [{
pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
message: '手机号格式错误'
}]
},
// 组件的插槽属性
__slot__: {
prepend: '',
append: ''
},
__vModel__: 'mobile',
placeholder: '请输入手机号',
style: { width: '100%' }, style: { width: '100%' },
clearable: true, clearable: true,
prepend: '',
append: '',
'prefix-icon': 'el-icon-mobile', 'prefix-icon': 'el-icon-mobile',
'suffix-icon': '', 'suffix-icon': '',
maxlength: 11, maxlength: 11,
'show-word-limit': true, 'show-word-limit': true,
readonly: false, readonly: false,
disabled: false, disabled: false
required: true,
changeTag: true,
regList: [{
pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
message: '手机号格式错误'
}]
} }
] ]

View File

@ -1,5 +1,5 @@
/* eslint-disable max-len */ /* eslint-disable max-len */
import { trigger } from './config' import ruleTrigger from './ruleTrigger'
let confGlobal let confGlobal
let someSpanIsNot24 let someSpanIsNot24
@ -34,27 +34,27 @@ export function cssStyle(cssStr) {
</style>` </style>`
} }
function buildFormTemplate(conf, child, type) { function buildFormTemplate(scheme, child, type) {
let labelPosition = '' let labelPosition = ''
if (conf.labelPosition !== 'right') { if (scheme.labelPosition !== 'right') {
labelPosition = `label-position="${conf.labelPosition}"` labelPosition = `label-position="${scheme.labelPosition}"`
} }
const disabled = conf.disabled ? `:disabled="${conf.disabled}"` : '' const disabled = scheme.disabled ? `:disabled="${scheme.disabled}"` : ''
let str = `<el-form ref="${conf.formRef}" :model="${conf.formModel}" :rules="${conf.formRules}" size="${conf.size}" ${disabled} label-width="${conf.labelWidth}px" ${labelPosition}> let str = `<el-form ref="${scheme.formRef}" :model="${scheme.formModel}" :rules="${scheme.formRules}" size="${scheme.size}" ${disabled} label-width="${scheme.labelWidth}px" ${labelPosition}>
${child} ${child}
${buildFromBtns(conf, type)} ${buildFromBtns(scheme, type)}
</el-form>` </el-form>`
if (someSpanIsNot24) { if (someSpanIsNot24) {
str = `<el-row :gutter="${conf.gutter}"> str = `<el-row :gutter="${scheme.gutter}">
${str} ${str}
</el-row>` </el-row>`
} }
return str return str
} }
function buildFromBtns(conf, type) { function buildFromBtns(scheme, type) {
let str = '' let str = ''
if (conf.formBtns && type === 'file') { if (scheme.formBtns && type === 'file') {
str = `<el-form-item size="large"> str = `<el-form-item size="large">
<el-button type="primary" @click="submitForm">提交</el-button> <el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="resetForm">重置</el-button> <el-button @click="resetForm">重置</el-button>
@ -69,9 +69,9 @@ function buildFromBtns(conf, type) {
} }
// span不为24的用el-col包裹 // span不为24的用el-col包裹
function colWrapper(element, str) { function colWrapper(scheme, str) {
if (someSpanIsNot24 || element.span !== 24) { if (someSpanIsNot24 || scheme.__config__.span !== 24) {
return `<el-col :span="${element.span}"> return `<el-col :span="${scheme.__config__.span}">
${str} ${str}
</el-col>` </el-col>`
} }
@ -79,37 +79,59 @@ function colWrapper(element, str) {
} }
const layouts = { const layouts = {
colFormItem(element) { colFormItem(scheme) {
const config = scheme.__config__
let labelWidth = '' let labelWidth = ''
if (element.labelWidth && element.labelWidth !== confGlobal.labelWidth) { let label = `label="${config.label}"`
labelWidth = `label-width="${element.labelWidth}px"` if (config.labelWidth && config.labelWidth !== confGlobal.labelWidth) {
labelWidth = `label-width="${config.labelWidth}px"`
} }
const required = !trigger[element.tag] && element.required ? 'required' : '' if (config.showLabel === false) {
const tagDom = tags[element.tag] ? tags[element.tag](element) : null labelWidth = 'label-width="0"'
let str = `<el-form-item ${labelWidth} label="${element.label}" prop="${element.vModel}" ${required}> label = ''
}
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} ${tagDom}
</el-form-item>` </el-form-item>`
str = colWrapper(element, str) str = colWrapper(scheme, str)
return str return str
}, },
rowFormItem(element) { rowFormItem(scheme) {
const type = element.type === 'default' ? '' : `type="${element.type}"` const config = scheme.__config__
const justify = element.type === 'default' ? '' : `justify="${element.justify}"` const type = scheme.type === 'default' ? '' : `type="${scheme.type}"`
const align = element.type === 'default' ? '' : `align="${element.align}"` const justify = scheme.type === 'default' ? '' : `justify="${scheme.justify}"`
const gutter = element.gutter ? `gutter="${element.gutter}"` : '' const align = scheme.type === 'default' ? '' : `align="${scheme.align}"`
const children = element.children.map(el => layouts[el.layout](el)) const gutter = scheme.gutter ? `:gutter="${scheme.gutter}"` : ''
const children = config.children.map(el => layouts[el.__config__.layout](el))
let str = `<el-row ${type} ${justify} ${align} ${gutter}> let str = `<el-row ${type} ${justify} ${align} ${gutter}>
${children.join('\n')} ${children.join('\n')}
</el-row>` </el-row>`
str = colWrapper(element, str) str = colWrapper(scheme, str)
return str return str
} }
} }
const tags = { 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)
if (child) child = `\n${child}\n` // 换行
return `<${tag} ${type} ${icon} ${round} ${size} ${plain} ${disabled} ${circle}>${child}</${tag}>`
},
'el-input': el => { 'el-input': el => {
const { const {
disabled, vModel, clearable, placeholder, width tag, disabled, vModel, clearable, placeholder, width
} = attrBuilder(el) } = attrBuilder(el)
const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : '' const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : ''
const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : '' const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : ''
@ -124,10 +146,12 @@ const tags = {
let child = buildElInputChild(el) let child = buildElInputChild(el)
if (child) child = `\n${child}\n` // 换行 if (child) child = `\n${child}\n` // 换行
return `<${el.tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}</${el.tag}>` return `<${tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}</${tag}>`
}, },
'el-input-number': el => { 'el-input-number': el => {
const { disabled, vModel, placeholder } = attrBuilder(el) const {
tag, disabled, vModel, placeholder
} = attrBuilder(el)
const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : '' const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : ''
const min = el.min ? `:min='${el.min}'` : '' const min = el.min ? `:min='${el.min}'` : ''
const max = el.max ? `:max='${el.max}'` : '' const max = el.max ? `:max='${el.max}'` : ''
@ -135,39 +159,39 @@ const tags = {
const stepStrictly = el['step-strictly'] ? 'step-strictly' : '' const stepStrictly = el['step-strictly'] ? 'step-strictly' : ''
const precision = el.precision ? `:precision='${el.precision}'` : '' const precision = el.precision ? `:precision='${el.precision}'` : ''
return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></${el.tag}>` return `<${tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></${tag}>`
}, },
'el-select': el => { 'el-select': el => {
const { const {
disabled, vModel, clearable, placeholder, width tag, disabled, vModel, clearable, placeholder, width
} = attrBuilder(el) } = attrBuilder(el)
const filterable = el.filterable ? 'filterable' : '' const filterable = el.filterable ? 'filterable' : ''
const multiple = el.multiple ? 'multiple' : '' const multiple = el.multiple ? 'multiple' : ''
let child = buildElSelectChild(el) let child = buildElSelectChild(el)
if (child) child = `\n${child}\n` // 换行 if (child) child = `\n${child}\n` // 换行
return `<${el.tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}</${el.tag}>` return `<${tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}</${tag}>`
}, },
'el-radio-group': el => { 'el-radio-group': el => {
const { disabled, vModel } = attrBuilder(el) const { tag, disabled, vModel } = attrBuilder(el)
const size = `size="${el.size}"` const size = `size="${el.size}"`
let child = buildElRadioGroupChild(el) let child = buildElRadioGroupChild(el)
if (child) child = `\n${child}\n` // 换行 if (child) child = `\n${child}\n` // 换行
return `<${el.tag} ${vModel} ${size} ${disabled}>${child}</${el.tag}>` return `<${tag} ${vModel} ${size} ${disabled}>${child}</${tag}>`
}, },
'el-checkbox-group': el => { 'el-checkbox-group': el => {
const { disabled, vModel } = attrBuilder(el) const { tag, disabled, vModel } = attrBuilder(el)
const size = `size="${el.size}"` const size = `size="${el.size}"`
const min = el.min ? `:min="${el.min}"` : '' const min = el.min ? `:min="${el.min}"` : ''
const max = el.max ? `:max="${el.max}"` : '' const max = el.max ? `:max="${el.max}"` : ''
let child = buildElCheckboxGroupChild(el) let child = buildElCheckboxGroupChild(el)
if (child) child = `\n${child}\n` // 换行 if (child) child = `\n${child}\n` // 换行
return `<${el.tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}</${el.tag}>` return `<${tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}</${tag}>`
}, },
'el-switch': el => { 'el-switch': el => {
const { disabled, vModel } = attrBuilder(el) const { tag, disabled, vModel } = attrBuilder(el)
const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : '' const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : ''
const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : '' const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : ''
const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : '' const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : ''
@ -175,33 +199,33 @@ const tags = {
const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : '' 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'])}'` : '' const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : ''
return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}></${el.tag}>` return `<${tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}></${tag}>`
}, },
'el-cascader': el => { 'el-cascader': el => {
const { const {
disabled, vModel, clearable, placeholder, width tag, disabled, vModel, clearable, placeholder, width
} = attrBuilder(el) } = attrBuilder(el)
const options = el.options ? `:options="${el.vModel}Options"` : '' const options = el.options ? `:options="${el.__vModel__}Options"` : ''
const props = el.props ? `:props="${el.vModel}Props"` : '' const props = el.props ? `:props="${el.__vModel__}Props"` : ''
const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"' const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"'
const filterable = el.filterable ? 'filterable' : '' const filterable = el.filterable ? 'filterable' : ''
const separator = el.separator === '/' ? '' : `separator="${el.separator}"` const separator = el.separator === '/' ? '' : `separator="${el.separator}"`
return `<${el.tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}></${el.tag}>` return `<${tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}></${tag}>`
}, },
'el-slider': el => { 'el-slider': el => {
const { disabled, vModel } = attrBuilder(el) const { tag, disabled, vModel } = attrBuilder(el)
const min = el.min ? `:min='${el.min}'` : '' const min = el.min ? `:min='${el.min}'` : ''
const max = el.max ? `:max='${el.max}'` : '' const max = el.max ? `:max='${el.max}'` : ''
const step = el.step ? `:step='${el.step}'` : '' const step = el.step ? `:step='${el.step}'` : ''
const range = el.range ? 'range' : '' const range = el.range ? 'range' : ''
const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : '' const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : ''
return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}></${el.tag}>` return `<${tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}></${tag}>`
}, },
'el-time-picker': el => { 'el-time-picker': el => {
const { const {
disabled, vModel, clearable, placeholder, width tag, disabled, vModel, clearable, placeholder, width
} = attrBuilder(el) } = attrBuilder(el)
const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
@ -211,11 +235,11 @@ const tags = {
const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : '' const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : '' const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : ''
return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}></${el.tag}>` return `<${tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}></${tag}>`
}, },
'el-date-picker': el => { 'el-date-picker': el => {
const { const {
disabled, vModel, clearable, placeholder, width tag, disabled, vModel, clearable, placeholder, width
} = attrBuilder(el) } = attrBuilder(el)
const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
@ -225,46 +249,54 @@ const tags = {
const type = el.type === 'date' ? '' : `type="${el.type}"` const type = el.type === 'date' ? '' : `type="${el.type}"`
const readonly = el.readonly ? 'readonly' : '' const readonly = el.readonly ? 'readonly' : ''
return `<${el.tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}></${el.tag}>` return `<${tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}></${tag}>`
}, },
'el-rate': el => { 'el-rate': el => {
const { disabled, vModel } = attrBuilder(el) const { tag, disabled, vModel } = attrBuilder(el)
const max = el.max ? `:max='${el.max}'` : '' const max = el.max ? `:max='${el.max}'` : ''
const allowHalf = el['allow-half'] ? 'allow-half' : '' const allowHalf = el['allow-half'] ? 'allow-half' : ''
const showText = el['show-text'] ? 'show-text' : '' const showText = el['show-text'] ? 'show-text' : ''
const showScore = el['show-score'] ? 'show-score' : '' const showScore = el['show-score'] ? 'show-score' : ''
return `<${el.tag} ${vModel} ${allowHalf} ${showText} ${showScore} ${disabled}></${el.tag}>` return `<${tag} ${vModel} ${max} ${allowHalf} ${showText} ${showScore} ${disabled}></${tag}>`
}, },
'el-color-picker': el => { 'el-color-picker': el => {
const { disabled, vModel } = attrBuilder(el) const { tag, disabled, vModel } = attrBuilder(el)
const size = `size="${el.size}"` const size = `size="${el.size}"`
const showAlpha = el['show-alpha'] ? 'show-alpha' : '' const showAlpha = el['show-alpha'] ? 'show-alpha' : ''
const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : '' const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : ''
return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}></${el.tag}>` return `<${tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}></${tag}>`
}, },
'el-upload': el => { 'el-upload': el => {
const { tag } = el.__config__
const disabled = el.disabled ? ':disabled=\'true\'' : '' const disabled = el.disabled ? ':disabled=\'true\'' : ''
const action = el.action ? `:action="${el.vModel}Action"` : '' const action = el.action ? `:action="${el.__vModel__}Action"` : ''
const multiple = el.multiple ? 'multiple' : '' const multiple = el.multiple ? 'multiple' : ''
const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : '' const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : ''
const accept = el.accept ? `accept="${el.accept}"` : '' const accept = el.accept ? `accept="${el.accept}"` : ''
const name = el.name !== 'file' ? `name="${el.name}"` : '' const name = el.name !== 'file' ? `name="${el.name}"` : ''
const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : '' const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : ''
const beforeUpload = `:before-upload="${el.vModel}BeforeUpload"` const beforeUpload = `:before-upload="${el.__vModel__}BeforeUpload"`
const fileList = `:file-list="${el.vModel}fileList"` const fileList = `:file-list="${el.__vModel__}fileList"`
const ref = `ref="${el.vModel}"` const ref = `ref="${el.__vModel__}"`
let child = buildElUploadChild(el) let child = buildElUploadChild(el)
if (child) child = `\n${child}\n` // 换行 if (child) child = `\n${child}\n` // 换行
return `<${el.tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}</${el.tag}>` return `<${tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}</${tag}>`
},
tinymce: el => {
const { tag, vModel } = attrBuilder(el)
const height = el.height ? `:height="${el.height}"` : ''
const branding = el.branding ? `:branding="${el.branding}"` : ''
return `<${tag} ${vModel} ${height} ${branding}></${tag}>`
} }
} }
function attrBuilder(el) { function attrBuilder(el) {
return { return {
vModel: `v-model="${confGlobal.formModel}.${el.vModel}"`, tag: el.__config__.tag,
vModel: `v-model="${confGlobal.formModel}.${el.__vModel__}"`,
clearable: el.clearable ? 'clearable' : '', clearable: el.clearable ? 'clearable' : '',
placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '', placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '',
width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '', width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '',
@ -272,64 +304,93 @@ function attrBuilder(el) {
} }
} }
// el-input innerHTML // el-buttin 子级
function buildElInputChild(conf) { function buildElButtonChild(scheme) {
const children = [] const children = []
if (conf.prepend) { const slot = scheme.__slot__ || {}
children.push(`<template slot="prepend">${conf.prepend}</template>`) if (slot.default) {
} children.push(slot.default)
if (conf.append) {
children.push(`<template slot="append">${conf.append}</template>`)
} }
return children.join('\n') return children.join('\n')
} }
function buildElSelectChild(conf) { // el-input 子级
function buildElInputChild(scheme) {
const children = [] const children = []
if (conf.options && conf.options.length) { const slot = scheme.__slot__
children.push(`<el-option v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option>`) if (slot && slot.prepend) {
children.push(`<template slot="prepend">${slot.prepend}</template>`)
}
if (slot && slot.append) {
children.push(`<template slot="append">${slot.append}</template>`)
} }
return children.join('\n') return children.join('\n')
} }
function buildElRadioGroupChild(conf) { // el-select 子级
function buildElSelectChild(scheme) {
const children = [] const children = []
if (conf.options && conf.options.length) { const slot = scheme.__slot__
const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio' if (slot && slot.options && slot.options.length) {
const border = conf.border ? 'border' : '' 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(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`)
} }
return children.join('\n') return children.join('\n')
} }
function buildElCheckboxGroupChild(conf) { // el-radio-group 子级
function buildElRadioGroupChild(scheme) {
const children = [] const children = []
if (conf.options && conf.options.length) { const slot = scheme.__slot__
const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox' const config = scheme.__config__
const border = conf.border ? 'border' : '' if (slot && slot.options && slot.options.length) {
children.push(`<${tag} v-for="(item, index) in ${conf.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')
} }
function buildElUploadChild(conf) { // el-checkbox-group 子级
function buildElCheckboxGroupChild(scheme) {
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}>`)
}
return children.join('\n')
}
// el-upload 子级
function buildElUploadChild(scheme) {
const list = [] const list = []
if (conf['list-type'] === 'picture-card') list.push('<i class="el-icon-plus"></i>') const config = scheme.__config__
else list.push(`<el-button size="small" type="primary" icon="el-icon-upload">${conf.buttonText}</el-button>`) if (scheme['list-type'] === 'picture-card') list.push('<i class="el-icon-plus"></i>')
if (conf.showTip) list.push(`<div slot="tip" class="el-upload__tip">只能上传不超过 ${conf.fileSize}${conf.sizeUnit}${conf.accept}文件</div>`) 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') return list.join('\n')
} }
export function makeUpHtml(conf, type) { /**
* 组装html代码入口函数
* @param {Object} formConfig 整个表单配置
* @param {String} type 生成类型文件或弹窗等
*/
export function makeUpHtml(formConfig, type) {
const htmlList = [] const htmlList = []
confGlobal = conf confGlobal = formConfig
someSpanIsNot24 = conf.fields.some(item => item.span !== 24) // 判断布局是否都沾满了24个栅格以备后续简化代码结构
conf.fields.forEach(el => { someSpanIsNot24 = formConfig.fields.some(item => item.__config__.span !== 24)
htmlList.push(layouts[el.layout](el)) // 遍历渲染每个组件成html
formConfig.fields.forEach(el => {
htmlList.push(layouts[el.__config__.layout](el))
}) })
const htmlStr = htmlList.join('\n') const htmlStr = htmlList.join('\n')
// 将组件代码放进form标签
let temp = buildFormTemplate(conf, htmlStr, type) let temp = buildFormTemplate(formConfig, htmlStr, type)
// dialog标签包裹代码
if (type === 'dialog') { if (type === 'dialog') {
temp = dialogWrapper(temp) temp = dialogWrapper(temp)
} }

View File

@ -0,0 +1,102 @@
/**
* num 小于0左缩进num*2个空格 大于0右缩进num*2个空格
* @param {string} str 代码
* @param {number} num 缩进次数
* @param {number} len 可选缩进单位空格数
*/
export function indent(str, num, len = 2) {
if (num === 0) return str
const isLeft = num < 0; const result = []; let reg; let
spaces = ''
if (isLeft) {
num *= -1
reg = new RegExp(`(^\\s{0,${num * len}})`, 'g')
} else {
for (let i = 0; i < num * len; i++) spaces += ' '
}
str.split('\n').forEach(line => {
line = isLeft ? line.replace(reg, '') : spaces + line
result.push(line)
})
return result.join('\n')
}
// 首字母大小
export function titleCase(str) {
return str.replace(/( |^)[a-z]/g, L => L.toUpperCase())
}
// 下划转驼峰
export function camelCase(str) {
return str.replace(/-[a-z]/g, str1 => str1.substr(-1).toUpperCase())
}
export function isNumberStr(str) {
return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
}
export const exportDefault = 'export default '
export const beautifierConf = {
html: {
indent_size: '2',
indent_char: ' ',
max_preserve_newlines: '-1',
preserve_newlines: false,
keep_array_indentation: false,
break_chained_methods: false,
indent_scripts: 'separate',
brace_style: 'end-expand',
space_before_conditional: true,
unescape_strings: false,
jslint_happy: false,
end_with_newline: true,
wrap_line_length: '110',
indent_inner_html: true,
comma_first: false,
e4x: true,
indent_empty_lines: true
},
js: {
indent_size: '2',
indent_char: ' ',
max_preserve_newlines: '-1',
preserve_newlines: false,
keep_array_indentation: false,
break_chained_methods: false,
indent_scripts: 'normal',
brace_style: 'end-expand',
space_before_conditional: true,
unescape_strings: false,
jslint_happy: true,
end_with_newline: true,
wrap_line_length: '110',
indent_inner_html: true,
comma_first: false,
e4x: true,
indent_empty_lines: true
}
}
function stringify(obj) {
return JSON.stringify(obj, (key, val) => {
if (typeof val === 'function') {
return `${val}`
}
return val
})
}
function parse(str) {
JSON.parse(str, (k, v) => {
if (v.indexOf && v.indexOf('function') > -1) {
return eval(`(${v})`)
}
return v
})
}
export function jsonClone(obj) {
return parse(stringify(obj))
}

View File

@ -1,6 +1,6 @@
import { isArray } from 'util' import { isArray } from 'util'
import { exportDefault, titleCase } from '@/utils/index' import { exportDefault, titleCase } from '@/utils/generator/index'
import { trigger } from './config' import ruleTrigger from './ruleTrigger'
const units = { const units = {
KB: '1024', KB: '1024',
@ -13,9 +13,13 @@ const inheritAttrs = {
dialog: 'inheritAttrs: false,' dialog: 'inheritAttrs: false,'
} }
/**
export function makeUpJs(conf, type) { * 组装js 入口函数
confGlobal = conf = JSON.parse(JSON.stringify(conf)) * @param {Object} formConfig 整个表单配置
* @param {String} type 生成类型文件或弹窗等
*/
export function makeUpJs(formConfig, type) {
confGlobal = formConfig = JSON.parse(JSON.stringify(formConfig))
const dataList = [] const dataList = []
const ruleList = [] const ruleList = []
const optionsList = [] const optionsList = []
@ -23,12 +27,12 @@ export function makeUpJs(conf, type) {
const methodList = mixinMethod(type) const methodList = mixinMethod(type)
const uploadVarList = [] const uploadVarList = []
conf.fields.forEach(el => { formConfig.fields.forEach(el => {
buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList)
}) })
const script = buildexport( const script = buildexport(
conf, formConfig,
type, type,
dataList.join('\n'), dataList.join('\n'),
ruleList.join('\n'), ruleList.join('\n'),
@ -41,41 +45,50 @@ export function makeUpJs(conf, type) {
return script return script
} }
function buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) { // 构建组件属性
buildData(el, dataList) function buildAttributes(scheme, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) {
buildRules(el, ruleList) const config = scheme.__config__
const slot = scheme.__slot__
buildData(scheme, dataList)
buildRules(scheme, ruleList)
if (el.options && el.options.length) { // 特殊处理options属性
buildOptions(el, optionsList) if (scheme.options || (slot && slot.options && slot.options.length)) {
if (el.dataType === 'dynamic') { buildOptions(scheme, optionsList)
const model = `${el.vModel}Options` if (config.dataType === 'dynamic') {
const model = `${scheme.__vModel__}Options`
const options = titleCase(model) const options = titleCase(model)
buildOptionMethod(`get${options}`, model, methodList) buildOptionMethod(`get${options}`, model, methodList)
} }
} }
if (el.props && el.props.props) { // 处理props
buildProps(el, propsList) if (scheme.props && scheme.props.props) {
buildProps(scheme, propsList)
} }
if (el.action && el.tag === 'el-upload') { // 处理el-upload的action
if (scheme.action && config.tag === 'el-upload') {
uploadVarList.push( uploadVarList.push(
`${el.vModel}Action: '${el.action}', `${scheme.__vModel__}Action: '${scheme.action}',
${el.vModel}fileList: [],` ${scheme.__vModel__}fileList: [],`
) )
methodList.push(buildBeforeUpload(el)) methodList.push(buildBeforeUpload(scheme))
if (!el['auto-upload']) { // 非自动上传时,生成手动上传的函数
methodList.push(buildSubmitUpload(el)) if (!scheme['auto-upload']) {
methodList.push(buildSubmitUpload(scheme))
} }
} }
if (el.children) { // 构建子级组件属性
el.children.forEach(el2 => { if (config.children) {
buildAttributes(el2, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) config.children.forEach(item => {
buildAttributes(item, dataList, ruleList, optionsList, methodList, propsList, uploadVarList)
}) })
} }
} }
// 混入处理函数
function mixinMethod(type) { function mixinMethod(type) {
const list = []; const const list = []; const
minxins = { minxins = {
@ -117,73 +130,75 @@ function mixinMethod(type) {
return list return list
} }
function buildData(conf, dataList) { // 构建data
if (conf.vModel === undefined) return function buildData(scheme, dataList) {
let defaultValue const config = scheme.__config__
if (typeof (conf.defaultValue) === 'string' && !conf.multiple) { if (scheme.__vModel__ === undefined) return
defaultValue = `'${conf.defaultValue}'` const defaultValue = JSON.stringify(config.defaultValue)
} else { dataList.push(`${scheme.__vModel__}: ${defaultValue},`)
defaultValue = `${JSON.stringify(conf.defaultValue)}`
}
dataList.push(`${conf.vModel}: ${defaultValue},`)
} }
function buildRules(conf, ruleList) { // 构建校验规则
if (conf.vModel === undefined) return function buildRules(scheme, ruleList) {
const config = scheme.__config__
if (scheme.__vModel__ === undefined) return
const rules = [] const rules = []
if (trigger[conf.tag]) { if (ruleTrigger[config.tag]) {
if (conf.required) { if (config.required) {
const type = isArray(conf.defaultValue) ? 'type: \'array\',' : '' const type = isArray(config.defaultValue) ? 'type: \'array\',' : ''
let message = isArray(conf.defaultValue) ? `请至少选择一个${conf.vModel}` : conf.placeholder let message = isArray(config.defaultValue) ? `请至少选择一个${config.label}` : scheme.placeholder
if (message === undefined) message = `${conf.label}不能为空` if (message === undefined) message = `${config.label}不能为空`
rules.push(`{ required: true, ${type} message: '${message}', trigger: '${trigger[conf.tag]}' }`) rules.push(`{ required: true, ${type} message: '${message}', trigger: '${ruleTrigger[config.tag]}' }`)
} }
if (conf.regList && isArray(conf.regList)) { if (config.regList && isArray(config.regList)) {
conf.regList.forEach(item => { config.regList.forEach(item => {
if (item.pattern) { if (item.pattern) {
rules.push(`{ pattern: ${eval(item.pattern)}, message: '${item.message}', trigger: '${trigger[conf.tag]}' }`) rules.push(
`{ pattern: ${eval(item.pattern)}, message: '${item.message}', trigger: '${ruleTrigger[config.tag]}' }`
)
} }
}) })
} }
ruleList.push(`${conf.vModel}: [${rules.join(',')}],`) ruleList.push(`${scheme.__vModel__}: [${rules.join(',')}],`)
} }
} }
function buildOptions(conf, optionsList) { // 构建options
if (conf.vModel === undefined) return function buildOptions(scheme, optionsList) {
if (conf.dataType === 'dynamic') { conf.options = [] } if (scheme.__vModel__ === undefined) return
const str = `${conf.vModel}Options: ${JSON.stringify(conf.options)},` // 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) optionsList.push(str)
} }
function buildProps(conf, propsList) { function buildProps(scheme, propsList) {
if (conf.dataType === 'dynamic') { const str = `${scheme.__vModel__}Props: ${JSON.stringify(scheme.props.props)},`
conf.valueKey !== 'value' && (conf.props.props.value = conf.valueKey)
conf.labelKey !== 'label' && (conf.props.props.label = conf.labelKey)
conf.childrenKey !== 'children' && (conf.props.props.children = conf.childrenKey)
}
const str = `${conf.vModel}Props: ${JSON.stringify(conf.props.props)},`
propsList.push(str) propsList.push(str)
} }
function buildBeforeUpload(conf) { // el-upload的BeforeUpload
const unitNum = units[conf.sizeUnit]; let rightSizeCode = ''; let acceptCode = ''; const function buildBeforeUpload(scheme) {
const config = scheme.__config__
const unitNum = units[config.sizeUnit]; let rightSizeCode = ''; let acceptCode = ''; const
returnList = [] returnList = []
if (conf.fileSize) { if (config.fileSize) {
rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${conf.fileSize} rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${config.fileSize}
if(!isRightSize){ if(!isRightSize){
this.$message.error('文件大小超过 ${conf.fileSize}${conf.sizeUnit}') this.$message.error('文件大小超过 ${config.fileSize}${config.sizeUnit}')
}` }`
returnList.push('isRightSize') returnList.push('isRightSize')
} }
if (conf.accept) { if (scheme.accept) {
acceptCode = `let isAccept = new RegExp('${conf.accept}').test(file.type) acceptCode = `let isAccept = new RegExp('${scheme.accept}').test(file.type)
if(!isAccept){ if(!isAccept){
this.$message.error('应该选择${conf.accept}类型的文件') this.$message.error('应该选择${scheme.accept}类型的文件')
}` }`
returnList.push('isAccept') returnList.push('isAccept')
} }
const str = `${conf.vModel}BeforeUpload(file) { const str = `${scheme.__vModel__}BeforeUpload(file) {
${rightSizeCode} ${rightSizeCode}
${acceptCode} ${acceptCode}
return ${returnList.join('&&')} return ${returnList.join('&&')}
@ -191,9 +206,10 @@ function buildBeforeUpload(conf) {
return returnList.length ? str : '' return returnList.length ? str : ''
} }
function buildSubmitUpload(conf) { // el-upload的submit
function buildSubmitUpload(scheme) {
const str = `submitUpload() { const str = `submitUpload() {
this.$refs['${conf.vModel}'].submit() this.$refs['${scheme.__vModel__}'].submit()
},` },`
return str return str
} }
@ -206,6 +222,7 @@ function buildOptionMethod(methodName, model, methodList) {
methodList.push(str) methodList.push(str)
} }
// js整体拼接
function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, methods) { function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, methods) {
const str = `${exportDefault}{ const str = `${exportDefault}{
${inheritAttrs[type]} ${inheritAttrs[type]}

View File

@ -0,0 +1,26 @@
import loadScript from './loadScript'
import ELEMENT from 'element-ui'
let beautifierObj
export default function loadBeautifier(cb) {
if (beautifierObj) {
cb(beautifierObj)
return
}
const loading = ELEMENT.Loading.service({
fullscreen: true,
lock: true,
text: '格式化资源加载中...',
spinner: 'el-icon-loading',
background: 'rgba(255, 255, 255, 0.5)'
})
loadScript('https://cdn.bootcss.com/js-beautify/1.10.2/beautifier.min.js', () => {
loading.close()
// eslint-disable-next-line no-undef
beautifierObj = beautifier
cb(beautifierObj)
})
}

View File

@ -0,0 +1,42 @@
import { loadScriptQueue } from './loadScript'
import ELEMENT from 'element-ui'
// monaco-editor单例
let monacoEidtor
/**
* 动态加载monaco-editor cdn资源
* @param {Function} cb 回调必填
*/
export default function loadMonaco(cb) {
if (monacoEidtor) {
cb(monacoEidtor)
return
}
const vs = 'https://cdn.bootcss.com/monaco-editor/0.18.0/min/vs'
// 使用element ui实现加载提示
const loading = ELEMENT.Loading.service({
fullscreen: true,
lock: true,
text: '编辑器资源初始化中...',
spinner: 'el-icon-loading',
background: 'rgba(255, 255, 255, 0.5)'
})
!window.require && (window.require = {})
!window.require.paths && (window.require.paths = {})
window.require.paths.vs = vs
loadScriptQueue([
`${vs}/loader.js`,
`${vs}/editor/editor.main.nls.js`,
`${vs}/editor/editor.main.js`
], () => {
loading.close()
// eslint-disable-next-line no-undef
monacoEidtor = monaco
cb(monacoEidtor)
})
}

View File

@ -0,0 +1,60 @@
const callbacks = {}
/**
* 加载一个远程脚本
* @param {String} src 一个远程脚本
* @param {Function} callback 回调
*/
function loadScript(src, callback) {
const existingScript = document.getElementById(src)
const cb = callback || (() => {})
if (!existingScript) {
callbacks[src] = []
const $script = document.createElement('script')
$script.src = src
$script.id = src
$script.async = 1
document.body.appendChild($script)
const onEnd = 'onload' in $script ? stdOnEnd.bind($script) : ieOnEnd.bind($script)
onEnd($script)
}
callbacks[src].push(cb)
function stdOnEnd(script) {
script.onload = () => {
this.onerror = this.onload = null
callbacks[src].forEach(item => {
item(null, script)
})
delete callbacks[src]
}
script.onerror = () => {
this.onerror = this.onload = null
cb(new Error(`Failed to load ${src}`), script)
}
}
function ieOnEnd(script) {
script.onreadystatechange = () => {
if (this.readyState !== 'complete' && this.readyState !== 'loaded') return
this.onreadystatechange = null
callbacks[src].forEach(item => {
item(null, script)
})
delete callbacks[src]
}
}
}
/**
* 顺序加载一组远程脚本
* @param {Array} list 一组远程脚本
* @param {Function} cb 回调
*/
export function loadScriptQueue(list, cb) {
const first = list.shift()
list.length ? loadScript(first, () => loadScriptQueue(list, cb)) : loadScript(first, cb)
}
export default loadScript

View File

@ -0,0 +1,26 @@
import loadScript from './loadScript'
import ELEMENT from 'element-ui'
let tinymceObj
export default function loadTinymce(cb) {
if (tinymceObj) {
cb(tinymceObj)
return
}
const loading = ELEMENT.Loading.service({
fullscreen: true,
lock: true,
text: '富文本资源加载中...',
spinner: 'el-icon-loading',
background: 'rgba(255, 255, 255, 0.5)'
})
loadScript('https://cdn.bootcdn.net/ajax/libs/tinymce/5.2.2/tinymce.min.js', () => {
loading.close()
// eslint-disable-next-line no-undef
tinymceObj = tinymce
cb(tinymceObj)
})
}

View File

@ -0,0 +1,166 @@
<script>
import render from '@/components/render/render.js'
const ruleTrigger = {
'el-input': 'blur',
'el-input-number': 'blur',
'el-select': 'change',
'el-radio-group': 'change',
'el-checkbox-group': 'change',
'el-cascader': 'change',
'el-time-picker': 'change',
'el-date-picker': 'change',
'el-rate': 'change'
}
function renderFrom(h) {
const { formConfCopy } = this
return (
<el-row gutter={formConfCopy.gutter}>
<el-form
size={formConfCopy.size}
label-position={formConfCopy.labelPosition}
disabled={formConfCopy.disabled}
label-width={`${formConfCopy.labelWidth}px`}
ref={formConfCopy.formRef}
// model https://github.com/vuejs/jsx/issues/49#issuecomment-472013664
props={{ model: this[formConfCopy.formModel] }}
rules={this[formConfCopy.formRules]}
>
{renderFormItem.call(this, h, formConfCopy.fields)}
{formConfCopy.formBtns && formBtns.call(this, h)}
</el-form>
</el-row>
)
}
function formBtns(h) {
return <el-col>
<el-form-item size="large">
<el-button type="primary" onClick={this.submitForm}>提交</el-button>
<el-button onClick={this.resetForm}>重置</el-button>
</el-form-item>
</el-col>
}
function renderFormItem(h, elementList) {
return elementList.map(scheme => {
const config = scheme.__config__
const layout = layouts[config.layout]
if (layout) {
return layout.call(this, h, scheme)
}
throw new Error(`没有与${config.layout}匹配的layout`)
})
}
function renderChildren(h, scheme) {
const config = scheme.__config__
if (!Array.isArray(config.children)) return null
return renderFormItem.call(this, h, config.children)
}
const layouts = {
colFormItem(h, scheme) {
const config = scheme.__config__
let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null
if (config.showLabel === false) labelWidth = '0'
return (
<el-col span={config.span}>
<el-form-item label-width={labelWidth} prop={scheme.__vModel__}
label={config.showLabel ? config.label : ''}>
<render conf={scheme} onInput={ event => {
this.$set(config, 'defaultValue', event)
this.$set(this[this.formConf.formModel], scheme.__vModel__, event)
}} />
</el-form-item>
</el-col>
)
},
rowFormItem(h, scheme) {
let child = renderChildren.apply(this, arguments)
if (scheme.type === 'flex') {
child = <el-row type={scheme.type} justify={scheme.justify} align={scheme.align}>
{child}
</el-row>
}
return (
<el-col span={scheme.span}>
<el-row gutter={scheme.gutter}>
{child}
</el-row>
</el-col>
)
}
}
export default {
components: {
render
},
props: {
formConf: {
type: Object,
required: true
}
},
data() {
const data = {
formConfCopy: JSON.parse(JSON.stringify(this.formConf)),
[this.formConf.formModel]: {},
[this.formConf.formRules]: {}
}
this.initFormData(data.formConfCopy.fields, data[this.formConf.formModel])
this.buildRules(data.formConfCopy.fields, data[this.formConf.formRules])
return data
},
methods: {
initFormData(componentList, formData) {
componentList.forEach(cur => {
const config = cur.__config__
if (cur.__vModel__) formData[cur.__vModel__] = config.defaultValue
if (config.children) this.initFormData(config.children, formData)
})
},
buildRules(componentList, rules) {
componentList.forEach(cur => {
const config = cur.__config__
if (Array.isArray(config.regList)) {
if (config.required) {
const required = { required: config.required, message: cur.placeholder }
if (Array.isArray(config.defaultValue)) {
required.type = 'array'
required.message = `请至少选择一个${config.label}`
}
required.message === undefined && (required.message = `${config.label}不能为空`)
config.regList.push(required)
}
rules[cur.__vModel__] = config.regList.map(item => {
item.pattern && (item.pattern = eval(item.pattern))
item.trigger = ruleTrigger && ruleTrigger[config.tag]
return item
})
}
if (config.children) this.buildRules(config.children, rules)
})
},
resetForm() {
this.formConfCopy = JSON.parse(JSON.stringify(this.formConf))
this.$refs[this.formConf.formRef].resetFields()
},
submitForm() {
this.$refs[this.formConf.formRef].validate(valid => {
if (!valid) return false
// sumit
this.$emit('submit', this[this.formConf.formModel])
return true
})
}
},
render(h) {
return renderFrom.call(this, h)
}
}
</script>

View File

@ -0,0 +1,17 @@
## form-generator JSON 解析器
>用于将form-generator导出的JSON解析成一个表单。
### 安装组件
```
npm i form-gen-parser
```
或者
```
yarn add form-gen-parser
```
### 使用示例
> [查看在线示例](https://mrhj.gitee.io/form-generator/#/parser)
示例代码:
> [src\components\parser\example\Index.vue](https://github.com/JakHuang/form-generator/blob/dev/src/components/parser/example/Index.vue)

View File

@ -0,0 +1,264 @@
<template>
<div class="test-form">
<parser :form-conf="formConf" @submit="sumbitForm1" />
<parser :key="key2" :form-conf="formConf" @submit="sumbitForm2" />
<el-button @click="change">
change
</el-button>
</div>
</template>
<script>
import Parser from '../Parser'
// parsernpm使
// import Parser from 'form-gen-parser'
export default {
components: {
Parser
},
props: {},
data() {
return {
key2: +new Date(),
formConf: {
fields: [
{
__config__: {
label: '单行文本',
labelWidth: null,
showLabel: true,
changeTag: true,
tag: 'el-input',
tagIcon: 'input',
required: true,
layout: 'colFormItem',
span: 24,
document: 'https://element.eleme.cn/#/zh-CN/component/input',
regList: [
{
pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
message: '手机号格式错误'
}
]
},
__slot__: {
prepend: '',
append: ''
},
__vModel__: 'mobile',
placeholder: '请输入手机号',
style: {
width: '100%'
},
clearable: true,
'prefix-icon': 'el-icon-mobile',
'suffix-icon': '',
maxlength: 11,
'show-word-limit': true,
readonly: false,
disabled: false
},
{
__config__: {
label: '日期范围',
tag: 'el-date-picker',
tagIcon: 'date-range',
defaultValue: null,
span: 24,
showLabel: true,
labelWidth: null,
required: true,
layout: 'colFormItem',
regList: [],
changeTag: true,
document:
'https://element.eleme.cn/#/zh-CN/component/date-picker',
formId: 101,
renderKey: 1585980082729
},
style: {
width: '100%'
},
type: 'daterange',
'range-separator': '至',
'start-placeholder': '开始日期',
'end-placeholder': '结束日期',
disabled: false,
clearable: true,
format: 'yyyy-MM-dd',
'value-format': 'yyyy-MM-dd',
readonly: false,
__vModel__: 'field101'
},
{
__config__: {
layout: 'rowFormItem',
tagIcon: 'row',
label: '行容器',
layoutTree: true,
children: [
{
__config__: {
label: '评分',
tag: 'el-rate',
tagIcon: 'rate',
defaultValue: 0,
span: 24,
showLabel: true,
labelWidth: null,
layout: 'colFormItem',
required: true,
regList: [],
changeTag: true,
document: 'https://element.eleme.cn/#/zh-CN/component/rate',
formId: 102,
renderKey: 1586839671259
},
style: {},
max: 5,
'allow-half': false,
'show-text': false,
'show-score': false,
disabled: false,
__vModel__: 'field102'
}
],
document: 'https://element.eleme.cn/#/zh-CN/component/layout',
formId: 101,
span: 24,
renderKey: 1586839668999,
componentName: 'row101',
gutter: 15
},
type: 'default',
justify: 'start',
align: 'top'
}
],
formRef: 'elForm',
formModel: 'formData',
size: 'small',
labelPosition: 'right',
labelWidth: 100,
formRules: 'rules',
gutter: 15,
disabled: false,
span: 24,
formBtns: true,
unFocusedComponentBorder: false
},
formConf2: {
fields: [
{
__config__: {
label: '单行文本',
labelWidth: null,
showLabel: true,
changeTag: true,
tag: 'el-input',
tagIcon: 'input',
required: true,
layout: 'colFormItem',
span: 24,
document: 'https://element.eleme.cn/#/zh-CN/component/input',
regList: [
{
pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
message: '手机号格式错误'
}
]
},
__slot__: {
prepend: '',
append: ''
},
__vModel__: 'mobile',
placeholder: '请输入手机号',
style: {
width: '100%'
},
clearable: true,
'prefix-icon': 'el-icon-mobile',
'suffix-icon': '',
maxlength: 11,
'show-word-limit': true,
readonly: false,
disabled: false
},
{
__config__: {
label: '日期范围',
tag: 'el-date-picker',
tagIcon: 'date-range',
defaultValue: null,
span: 24,
showLabel: true,
labelWidth: null,
required: true,
layout: 'colFormItem',
regList: [],
changeTag: true,
document:
'https://element.eleme.cn/#/zh-CN/component/date-picker',
formId: 101,
renderKey: 1585980082729
},
style: {
width: '100%'
},
type: 'daterange',
'range-separator': '至',
'start-placeholder': '开始日期',
'end-placeholder': '结束日期',
disabled: false,
clearable: true,
format: 'yyyy-MM-dd',
'value-format': 'yyyy-MM-dd',
readonly: false,
__vModel__: 'field101'
}
],
formRef: 'elForm',
formModel: 'formData',
size: 'small',
labelPosition: 'right',
labelWidth: 100,
formRules: 'rules',
gutter: 15,
disabled: false,
span: 24,
formBtns: true,
unFocusedComponentBorder: false
}
}
},
computed: {},
watch: {},
created() {},
mounted() {},
methods: {
change() {
this.key2 = +new Date()
const t = this.formConf
this.formConf = this.formConf2
this.formConf2 = t
},
sumbitForm1(data) {
console.log('sumbitForm1提交数据', data)
},
sumbitForm2(data) {
console.log('sumbitForm2提交数据', data)
}
}
}
</script>
<style lang="scss" scoped>
.test-form {
margin: 15px auto;
width: 800px;
padding: 15px;
}
</style>

View File

@ -0,0 +1,3 @@
import Parser from './Parser'
export default Parser

View File

@ -0,0 +1,25 @@
{
"name": "form-gen-parser",
"version": "1.0.3",
"description": "表单json解析器",
"main": "lib/form-gen-parser.umd.js",
"directories": {
"example": "example"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/JakHuang/form-generator.git"
},
"dependencies": {
"form-gen-render": "^1.0.0"
},
"author": "jakHuang",
"license": "MIT",
"bugs": {
"url": "https://github.com/JakHuang/form-generator/issues"
},
"homepage": "https://github.com/JakHuang/form-generator#readme"
}

View File

@ -1,4 +1,4 @@
import { makeMap } from '@/utils/index' import { makeMap } from '@/utils/generator/index'
// 参考https://github.com/vuejs/vue/blob/v2.6.10/src/platforms/web/server/util.js // 参考https://github.com/vuejs/vue/blob/v2.6.10/src/platforms/web/server/util.js
const isAttr = makeMap( const isAttr = makeMap(

View File

@ -0,0 +1,19 @@
{
"name": "form-gen-render",
"version": "1.0.4",
"description": "表单核心render",
"main": "lib/form-gen-render.umd.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/JakHuang/form-generator.git"
},
"author": "jakhuang",
"license": "MIT",
"bugs": {
"url": "https://github.com/JakHuang/form-generator/issues"
},
"homepage": "https://github.com/JakHuang/form-generator#readme"
}

View File

@ -0,0 +1,59 @@
function vModel(self, dataObject, defaultValue) {
dataObject.props.value = defaultValue
dataObject.on.input = val => {
self.$emit('input', val)
}
}
const componentChild = {}
/**
* ./slots中的文件挂载到对象componentChild上
* 文件名为key对应JSON配置中的__config__.tag
* 文件内容为value解析JSON配置中的__slot__
*/
const slotsFiles = require.context('./slots', true, /\.js$/)
const keys = slotsFiles.keys() || []
keys.forEach(key => {
const tag = key.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = slotsFiles(key).default
componentChild[tag] = value
})
export default {
render(h) {
const dataObject = {
attrs: {},
props: {},
on: {},
style: {}
}
const confClone = JSON.parse(JSON.stringify(this.conf))
const 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))
}
})
}
Object.keys(confClone).forEach(key => {
const val = confClone[key]
if (key === '__vModel__') {
vModel(this, dataObject, confClone.__config__.defaultValue)
} else if (dataObject[key]) {
dataObject[key] = { ...dataObject[key], ...val }
} else {
dataObject.attrs[key] = val
}
})
delete dataObject.attrs.__config__
delete dataObject.attrs.__slot__
return h(this.conf.__config__.tag, dataObject, children)
},
props: ['conf']
}

View File

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

View File

@ -0,0 +1,13 @@
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>)
} else {
list.push(<el-checkbox label={item.value} border={conf.border}>{item.label}</el-checkbox>)
}
})
return list
}
}

View File

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

View File

@ -0,0 +1,13 @@
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>)
} else {
list.push(<el-radio label={item.value} border={conf.border}>{item.label}</el-radio>)
}
})
return list
}
}

View File

@ -0,0 +1,9 @@
export default {
options(h, conf, key) {
const list = []
conf.__slot__.options.forEach(item => {
list.push(<el-option label={item.label} value={item.value} disabled={item.disabled}></el-option>)
})
return list
}
}

View File

@ -0,0 +1,17 @@
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>)
} 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} {conf.accept}文件</div>
)
}
return list
}
}

View File

@ -0,0 +1,17 @@
/**
* 用于生成表单校验指定正则规则的触发方式
* 未在此处声明无触发方式的组件将不生成rule
*/
export default {
'el-input': 'blur',
'el-input-number': 'blur',
'el-select': 'change',
'el-radio-group': 'change',
'el-checkbox-group': 'change',
'el-cascader': 'change',
'el-time-picker': 'change',
'el-date-picker': 'change',
'el-rate': 'change',
tinymce: 'blur'
}

View File

@ -0,0 +1,8 @@
/* eslint-disable max-len */
export const plugins = [
'advlist anchor autolink autosave code codesample directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textpattern visualblocks visualchars wordcount'
]
export const toolbar = [
'code searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote removeformat subscript superscript codesample hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen'
]

View File

@ -0,0 +1,93 @@
<template>
<textarea :id="tinymceId" class="textarea" />
</template>
<script>
import loadTinymce from '@/utils/generator/loadTinymce'
import { plugins, toolbar } from './config'
import { debounce } from 'throttle-debounce'
let num = 1
export default {
props: {
id: {
type: String,
default: () => {
num === 10000 && (num = 1)
return `tinymce${+new Date()}${num++}`
}
},
value: {
type: [String, Number, Boolean],
default: ''
}
},
data() {
return {
tinymceId: this.id
}
},
mounted() {
loadTinymce(tinymce => {
import('./zh_CN').then(() => {
tinymce.init({
selector: `#${this.tinymceId}`,
language: 'zh_CN',
menubar: 'file edit insert view format table',
plugins,
toolbar,
height: this.$attrs.height || 300,
branding: this.$attrs.branding || false,
object_resizing: false,
end_container_on_empty_block: true,
powerpaste_word_import: 'clean',
code_dialog_height: 450,
code_dialog_width: 1000,
advlist_bullet_styles: 'square',
advlist_number_styles: 'default',
default_link_target: '_blank',
link_title: false,
nonbreaking_force_tab: true,
init_instance_callback: editor => {
if (this.value) editor.setContent(this.value)
this.vModel(editor)
}
})
})
})
},
destroyed() {
this.destroyTinymce()
},
methods: {
vModel(editor) {
// setContent
const debounceSetContent = debounce(250, editor.setContent)
this.$watch('value', (val, prevVal) => {
if (editor && val !== prevVal && val !== editor.getContent()) {
if (typeof val !== 'string') val = val.toString()
debounceSetContent.call(editor, val)
}
})
editor.on('change keyup undo redo', () => {
this.$emit('input', editor.getContent())
})
},
destroyTinymce() {
if (!window.tinymce) return
const tinymce = window.tinymce.get(this.tinymceId)
if (tinymce) {
tinymce.destroy()
}
}
}
}
</script>
<style lang="scss" scoped>
.textarea{
visibility: hidden;
}
</style>

View File

@ -0,0 +1,420 @@
/* eslint-disable */
tinymce.addI18n('zh_CN',{
"Redo": "\u91cd\u505a",
"Undo": "\u64a4\u9500",
"Cut": "\u526a\u5207",
"Copy": "\u590d\u5236",
"Paste": "\u7c98\u8d34",
"Select all": "\u5168\u9009",
"New document": "\u65b0\u6587\u4ef6",
"Ok": "\u786e\u5b9a",
"Cancel": "\u53d6\u6d88",
"Visual aids": "\u7f51\u683c\u7ebf",
"Bold": "\u7c97\u4f53",
"Italic": "\u659c\u4f53",
"Underline": "\u4e0b\u5212\u7ebf",
"Strikethrough": "\u5220\u9664\u7ebf",
"Superscript": "\u4e0a\u6807",
"Subscript": "\u4e0b\u6807",
"Clear formatting": "\u6e05\u9664\u683c\u5f0f",
"Align left": "\u5de6\u8fb9\u5bf9\u9f50",
"Align center": "\u4e2d\u95f4\u5bf9\u9f50",
"Align right": "\u53f3\u8fb9\u5bf9\u9f50",
"Justify": "\u4e24\u7aef\u5bf9\u9f50",
"Bullet list": "\u9879\u76ee\u7b26\u53f7",
"Numbered list": "\u7f16\u53f7\u5217\u8868",
"Decrease indent": "\u51cf\u5c11\u7f29\u8fdb",
"Increase indent": "\u589e\u52a0\u7f29\u8fdb",
"Close": "\u5173\u95ed",
"Formats": "\u683c\u5f0f",
"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u6253\u5f00\u526a\u8d34\u677f\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u7b49\u5feb\u6377\u952e\u3002",
"Headers": "\u6807\u9898",
"Header 1": "\u6807\u98981",
"Header 2": "\u6807\u98982",
"Header 3": "\u6807\u98983",
"Header 4": "\u6807\u98984",
"Header 5": "\u6807\u98985",
"Header 6": "\u6807\u98986",
"Headings": "\u6807\u9898",
"Heading 1": "\u6807\u98981",
"Heading 2": "\u6807\u98982",
"Heading 3": "\u6807\u98983",
"Heading 4": "\u6807\u98984",
"Heading 5": "\u6807\u98985",
"Heading 6": "\u6807\u98986",
"Preformatted": "\u9884\u5148\u683c\u5f0f\u5316\u7684",
"Div": "Div",
"Pre": "Pre",
"Code": "\u4ee3\u7801",
"Paragraph": "\u6bb5\u843d",
"Blockquote": "\u5f15\u6587\u533a\u5757",
"Inline": "\u6587\u672c",
"Blocks": "\u57fa\u5757",
"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002",
"Fonts": "\u5b57\u4f53",
"Font Sizes": "\u5b57\u53f7",
"Class": "\u7c7b\u578b",
"Browse for an image": "\u6d4f\u89c8\u56fe\u50cf",
"OR": "\u6216",
"Drop an image here": "\u62d6\u653e\u4e00\u5f20\u56fe\u50cf\u81f3\u6b64",
"Upload": "\u4e0a\u4f20",
"Block": "\u5757",
"Align": "\u5bf9\u9f50",
"Default": "\u9ed8\u8ba4",
"Circle": "\u7a7a\u5fc3\u5706",
"Disc": "\u5b9e\u5fc3\u5706",
"Square": "\u65b9\u5757",
"Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd",
"Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd",
"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd",
"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd",
"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd",
"Anchor...": "\u951a\u70b9...",
"Name": "\u540d\u79f0",
"Id": "\u6807\u8bc6\u7b26",
"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002",
"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f",
"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f",
"Special character...": "\u7279\u6b8a\u5b57\u7b26...",
"Source code": "\u6e90\u4ee3\u7801",
"Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b",
"Language": "\u8bed\u8a00",
"Code sample...": "\u793a\u4f8b\u4ee3\u7801...",
"Color Picker": "\u9009\u8272\u5668",
"R": "R",
"G": "G",
"B": "B",
"Left to right": "\u4ece\u5de6\u5230\u53f3",
"Right to left": "\u4ece\u53f3\u5230\u5de6",
"Emoticons...": "\u8868\u60c5\u7b26\u53f7...",
"Metadata and Document Properties": "\u5143\u6570\u636e\u548c\u6587\u6863\u5c5e\u6027",
"Title": "\u6807\u9898",
"Keywords": "\u5173\u952e\u8bcd",
"Description": "\u63cf\u8ff0",
"Robots": "\u673a\u5668\u4eba",
"Author": "\u4f5c\u8005",
"Encoding": "\u7f16\u7801",
"Fullscreen": "\u5168\u5c4f",
"Action": "\u64cd\u4f5c",
"Shortcut": "\u5feb\u6377\u952e",
"Help": "\u5e2e\u52a9",
"Address": "\u5730\u5740",
"Focus to menubar": "\u79fb\u52a8\u7126\u70b9\u5230\u83dc\u5355\u680f",
"Focus to toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u5de5\u5177\u680f",
"Focus to element path": "\u79fb\u52a8\u7126\u70b9\u5230\u5143\u7d20\u8def\u5f84",
"Focus to contextual toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u4e0a\u4e0b\u6587\u83dc\u5355",
"Insert link (if link plugin activated)": "\u63d2\u5165\u94fe\u63a5 (\u5982\u679c\u94fe\u63a5\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
"Save (if save plugin activated)": "\u4fdd\u5b58(\u5982\u679c\u4fdd\u5b58\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
"Find (if searchreplace plugin activated)": "\u67e5\u627e(\u5982\u679c\u67e5\u627e\u66ff\u6362\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
"Plugins installed ({0}):": "\u5df2\u5b89\u88c5\u63d2\u4ef6 ({0}):",
"Premium plugins:": "\u4f18\u79c0\u63d2\u4ef6\uff1a",
"Learn more...": "\u4e86\u89e3\u66f4\u591a...",
"You are using {0}": "\u4f60\u6b63\u5728\u4f7f\u7528 {0}",
"Plugins": "\u63d2\u4ef6",
"Handy Shortcuts": "\u5feb\u6377\u952e",
"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
"Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247",
"Image description": "\u56fe\u7247\u63cf\u8ff0",
"Source": "\u5730\u5740",
"Dimensions": "\u5927\u5c0f",
"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
"General": "\u666e\u901a",
"Advanced": "\u9ad8\u7ea7",
"Style": "\u6837\u5f0f",
"Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
"Border": "\u8fb9\u6846",
"Insert image": "\u63d2\u5165\u56fe\u7247",
"Image...": "\u56fe\u7247...",
"Image list": "\u56fe\u7247\u5217\u8868",
"Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c",
"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c",
"Flip vertically": "\u5782\u76f4\u7ffb\u8f6c",
"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
"Edit image": "\u7f16\u8f91\u56fe\u7247",
"Image options": "\u56fe\u7247\u9009\u9879",
"Zoom in": "\u653e\u5927",
"Zoom out": "\u7f29\u5c0f",
"Crop": "\u88c1\u526a",
"Resize": "\u8c03\u6574\u5927\u5c0f",
"Orientation": "\u65b9\u5411",
"Brightness": "\u4eae\u5ea6",
"Sharpen": "\u9510\u5316",
"Contrast": "\u5bf9\u6bd4\u5ea6",
"Color levels": "\u989c\u8272\u5c42\u6b21",
"Gamma": "\u4f3d\u9a6c\u503c",
"Invert": "\u53cd\u8f6c",
"Apply": "\u5e94\u7528",
"Back": "\u540e\u9000",
"Insert date\/time": "\u63d2\u5165\u65e5\u671f\/\u65f6\u95f4",
"Date\/time": "\u65e5\u671f\/\u65f6\u95f4",
"Insert\/Edit Link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
"Insert\/edit link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
"Text to display": "\u663e\u793a\u6587\u5b57",
"Url": "\u5730\u5740",
"Open link in...": "\u94fe\u63a5\u6253\u5f00\u4f4d\u7f6e...",
"Current window": "\u5f53\u524d\u7a97\u53e3",
"None": "\u65e0",
"New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00",
"Remove link": "\u5220\u9664\u94fe\u63a5",
"Anchors": "\u951a\u70b9",
"Link...": "\u94fe\u63a5...",
"Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5",
"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f",
"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7f00\u5417\uff1f",
"Link list": "\u94fe\u63a5\u5217\u8868",
"Insert video": "\u63d2\u5165\u89c6\u9891",
"Insert\/edit video": "\u63d2\u5165\/\u7f16\u8f91\u89c6\u9891",
"Insert\/edit media": "\u63d2\u5165\/\u7f16\u8f91\u5a92\u4f53",
"Alternative source": "\u955c\u50cf",
"Alternative source URL": "\u66ff\u4ee3\u6765\u6e90\u7f51\u5740",
"Media poster (Image URL)": "\u5c01\u9762(\u56fe\u7247\u5730\u5740)",
"Paste your embed code below:": "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:",
"Embed": "\u5185\u5d4c",
"Media...": "\u591a\u5a92\u4f53...",
"Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c",
"Page break": "\u5206\u9875\u7b26",
"Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c",
"Preview": "\u9884\u89c8",
"Print...": "\u6253\u5370...",
"Save": "\u4fdd\u5b58",
"Find": "\u67e5\u627e",
"Replace with": "\u66ff\u6362\u4e3a",
"Replace": "\u66ff\u6362",
"Replace all": "\u5168\u90e8\u66ff\u6362",
"Previous": "\u4e0a\u4e00\u4e2a",
"Next": "\u4e0b\u4e00\u4e2a",
"Find and replace...": "\u67e5\u627e\u5e76\u66ff\u6362...",
"Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.",
"Match case": "\u533a\u5206\u5927\u5c0f\u5199",
"Find whole words only": "\u5168\u5b57\u5339\u914d",
"Spell check": "\u62fc\u5199\u68c0\u67e5",
"Ignore": "\u5ffd\u7565",
"Ignore all": "\u5168\u90e8\u5ffd\u7565",
"Finish": "\u5b8c\u6210",
"Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178",
"Insert table": "\u63d2\u5165\u8868\u683c",
"Table properties": "\u8868\u683c\u5c5e\u6027",
"Delete table": "\u5220\u9664\u8868\u683c",
"Cell": "\u5355\u5143\u683c",
"Row": "\u884c",
"Column": "\u5217",
"Cell properties": "\u5355\u5143\u683c\u5c5e\u6027",
"Merge cells": "\u5408\u5e76\u5355\u5143\u683c",
"Split cell": "\u62c6\u5206\u5355\u5143\u683c",
"Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165",
"Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165",
"Delete row": "\u5220\u9664\u884c",
"Row properties": "\u884c\u5c5e\u6027",
"Cut row": "\u526a\u5207\u884c",
"Copy row": "\u590d\u5236\u884c",
"Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9",
"Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9",
"Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165",
"Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165",
"Delete column": "\u5220\u9664\u5217",
"Cols": "\u5217",
"Rows": "\u884c",
"Width": "\u5bbd",
"Height": "\u9ad8",
"Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd",
"Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd",
"Show caption": "\u663e\u793a\u6807\u9898",
"Left": "\u5de6\u5bf9\u9f50",
"Center": "\u5c45\u4e2d",
"Right": "\u53f3\u5bf9\u9f50",
"Cell type": "\u5355\u5143\u683c\u7c7b\u578b",
"Scope": "\u8303\u56f4",
"Alignment": "\u5bf9\u9f50\u65b9\u5f0f",
"H Align": "\u6c34\u5e73\u5bf9\u9f50",
"V Align": "\u5782\u76f4\u5bf9\u9f50",
"Top": "\u9876\u90e8\u5bf9\u9f50",
"Middle": "\u5782\u76f4\u5c45\u4e2d",
"Bottom": "\u5e95\u90e8\u5bf9\u9f50",
"Header cell": "\u8868\u5934\u5355\u5143\u683c",
"Row group": "\u884c\u7ec4",
"Column group": "\u5217\u7ec4",
"Row type": "\u884c\u7c7b\u578b",
"Header": "\u8868\u5934",
"Body": "\u8868\u4f53",
"Footer": "\u8868\u5c3e",
"Border color": "\u8fb9\u6846\u989c\u8272",
"Insert template...": "\u63d2\u5165\u6a21\u677f...",
"Templates": "\u6a21\u677f",
"Template": "\u6a21\u677f",
"Text color": "\u6587\u5b57\u989c\u8272",
"Background color": "\u80cc\u666f\u8272",
"Custom...": "\u81ea\u5b9a\u4e49...",
"Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272",
"No color": "\u65e0",
"Remove color": "\u79fb\u9664\u989c\u8272",
"Table of Contents": "\u5185\u5bb9\u5217\u8868",
"Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846",
"Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26",
"Word count": "\u5b57\u6570",
"Count": "\u8ba1\u6570",
"Document": "\u6587\u6863",
"Selection": "\u9009\u62e9",
"Words": "\u5355\u8bcd",
"Words: {0}": "\u5b57\u6570\uff1a{0}",
"{0} words": "{0} \u5b57",
"File": "\u6587\u4ef6",
"Edit": "\u7f16\u8f91",
"Insert": "\u63d2\u5165",
"View": "\u89c6\u56fe",
"Format": "\u683c\u5f0f",
"Table": "\u8868\u683c",
"Tools": "\u5de5\u5177",
"Powered by {0}": "\u7531{0}\u9a71\u52a8",
"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9",
"Image title": "\u56fe\u7247\u6807\u9898",
"Border width": "\u8fb9\u6846\u5bbd\u5ea6",
"Border style": "\u8fb9\u6846\u6837\u5f0f",
"Error": "\u9519\u8bef",
"Warn": "\u8b66\u544a",
"Valid": "\u6709\u6548",
"To open the popup, press Shift+Enter": "\u6309Shitf+Enter\u952e\u6253\u5f00\u5bf9\u8bdd\u6846",
"Rich Text Area. Press ALT-0 for help.": "\u7f16\u8f91\u533a\u3002\u6309Alt+0\u952e\u6253\u5f00\u5e2e\u52a9\u3002",
"System Font": "\u7cfb\u7edf\u5b57\u4f53",
"Failed to upload image: {0}": "\u56fe\u7247\u4e0a\u4f20\u5931\u8d25: {0}",
"Failed to load plugin: {0} from url {1}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25: {0} \u6765\u81ea\u94fe\u63a5 {1}",
"Failed to load plugin url: {0}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25 \u94fe\u63a5: {0}",
"Failed to initialize plugin: {0}": "\u63d2\u4ef6\u521d\u59cb\u5316\u5931\u8d25: {0}",
"example": "\u793a\u4f8b",
"Search": "\u641c\u7d22",
"All": "\u5168\u90e8",
"Currency": "\u8d27\u5e01",
"Text": "\u6587\u5b57",
"Quotations": "\u5f15\u7528",
"Mathematical": "\u6570\u5b66",
"Extended Latin": "\u62c9\u4e01\u8bed\u6269\u5145",
"Symbols": "\u7b26\u53f7",
"Arrows": "\u7bad\u5934",
"User Defined": "\u81ea\u5b9a\u4e49",
"dollar sign": "\u7f8e\u5143\u7b26\u53f7",
"currency sign": "\u8d27\u5e01\u7b26\u53f7",
"euro-currency sign": "\u6b27\u5143\u7b26\u53f7",
"colon sign": "\u5192\u53f7",
"cruzeiro sign": "\u514b\u9c81\u8d5b\u7f57\u5e01\u7b26\u53f7",
"french franc sign": "\u6cd5\u90ce\u7b26\u53f7",
"lira sign": "\u91cc\u62c9\u7b26\u53f7",
"mill sign": "\u5bc6\u5c14\u7b26\u53f7",
"naira sign": "\u5948\u62c9\u7b26\u53f7",
"peseta sign": "\u6bd4\u585e\u5854\u7b26\u53f7",
"rupee sign": "\u5362\u6bd4\u7b26\u53f7",
"won sign": "\u97e9\u5143\u7b26\u53f7",
"new sheqel sign": "\u65b0\u8c22\u514b\u5c14\u7b26\u53f7",
"dong sign": "\u8d8a\u5357\u76fe\u7b26\u53f7",
"kip sign": "\u8001\u631d\u57fa\u666e\u7b26\u53f7",
"tugrik sign": "\u56fe\u683c\u91cc\u514b\u7b26\u53f7",
"drachma sign": "\u5fb7\u62c9\u514b\u9a6c\u7b26\u53f7",
"german penny symbol": "\u5fb7\u56fd\u4fbf\u58eb\u7b26\u53f7",
"peso sign": "\u6bd4\u7d22\u7b26\u53f7",
"guarani sign": "\u74dc\u62c9\u5c3c\u7b26\u53f7",
"austral sign": "\u6fb3\u5143\u7b26\u53f7",
"hryvnia sign": "\u683c\u91cc\u592b\u5c3c\u4e9a\u7b26\u53f7",
"cedi sign": "\u585e\u5730\u7b26\u53f7",
"livre tournois sign": "\u91cc\u5f17\u5f17\u5c14\u7b26\u53f7",
"spesmilo sign": "spesmilo\u7b26\u53f7",
"tenge sign": "\u575a\u6208\u7b26\u53f7",
"indian rupee sign": "\u5370\u5ea6\u5362\u6bd4",
"turkish lira sign": "\u571f\u8033\u5176\u91cc\u62c9",
"nordic mark sign": "\u5317\u6b27\u9a6c\u514b",
"manat sign": "\u9a6c\u7eb3\u7279\u7b26\u53f7",
"ruble sign": "\u5362\u5e03\u7b26\u53f7",
"yen character": "\u65e5\u5143\u5b57\u6837",
"yuan character": "\u4eba\u6c11\u5e01\u5143\u5b57\u6837",
"yuan character, in hong kong and taiwan": "\u5143\u5b57\u6837\uff08\u6e2f\u53f0\u5730\u533a\uff09",
"yen\/yuan character variant one": "\u5143\u5b57\u6837\uff08\u5927\u5199\uff09",
"Loading emoticons...": "\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7...",
"Could not load emoticons": "\u4e0d\u80fd\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7",
"People": "\u4eba\u7c7b",
"Animals and Nature": "\u52a8\u7269\u548c\u81ea\u7136",
"Food and Drink": "\u98df\u7269\u548c\u996e\u54c1",
"Activity": "\u6d3b\u52a8",
"Travel and Places": "\u65c5\u6e38\u548c\u5730\u70b9",
"Objects": "\u7269\u4ef6",
"Flags": "\u65d7\u5e1c",
"Characters": "\u5b57\u7b26",
"Characters (no spaces)": "\u5b57\u7b26(\u65e0\u7a7a\u683c)",
"{0} characters": "{0} \u4e2a\u5b57\u7b26",
"Error: Form submit field collision.": "\u9519\u8bef: \u8868\u5355\u63d0\u4ea4\u5b57\u6bb5\u51b2\u7a81\u3002",
"Error: No form element found.": "\u9519\u8bef: \u6ca1\u6709\u8868\u5355\u63a7\u4ef6\u3002",
"Update": "\u66f4\u65b0",
"Color swatch": "\u989c\u8272\u6837\u672c",
"Turquoise": "\u9752\u7eff\u8272",
"Green": "\u7eff\u8272",
"Blue": "\u84dd\u8272",
"Purple": "\u7d2b\u8272",
"Navy Blue": "\u6d77\u519b\u84dd",
"Dark Turquoise": "\u6df1\u84dd\u7eff\u8272",
"Dark Green": "\u6df1\u7eff\u8272",
"Medium Blue": "\u4e2d\u84dd\u8272",
"Medium Purple": "\u4e2d\u7d2b\u8272",
"Midnight Blue": "\u6df1\u84dd\u8272",
"Yellow": "\u9ec4\u8272",
"Orange": "\u6a59\u8272",
"Red": "\u7ea2\u8272",
"Light Gray": "\u6d45\u7070\u8272",
"Gray": "\u7070\u8272",
"Dark Yellow": "\u6697\u9ec4\u8272",
"Dark Orange": "\u6df1\u6a59\u8272",
"Dark Red": "\u6df1\u7ea2\u8272",
"Medium Gray": "\u4e2d\u7070\u8272",
"Dark Gray": "\u6df1\u7070\u8272",
"Light Green": "\u6d45\u7eff\u8272",
"Light Yellow": "\u6d45\u9ec4\u8272",
"Light Red": "\u6d45\u7ea2\u8272",
"Light Purple": "\u6d45\u7d2b\u8272",
"Light Blue": "\u6d45\u84dd\u8272",
"Dark Purple": "\u6df1\u7d2b\u8272",
"Dark Blue": "\u6df1\u84dd\u8272",
"Black": "\u9ed1\u8272",
"White": "\u767d\u8272",
"Switch to or from fullscreen mode": "\u5207\u6362\u5168\u5c4f\u6a21\u5f0f",
"Open help dialog": "\u6253\u5f00\u5e2e\u52a9\u5bf9\u8bdd\u6846",
"history": "\u5386\u53f2",
"styles": "\u6837\u5f0f",
"formatting": "\u683c\u5f0f\u5316",
"alignment": "\u5bf9\u9f50",
"indentation": "\u7f29\u8fdb",
"permanent pen": "\u8bb0\u53f7\u7b14",
"comments": "\u5907\u6ce8",
"Format Painter": "\u683c\u5f0f\u5237",
"Insert\/edit iframe": "\u63d2\u5165\/\u7f16\u8f91\u6846\u67b6",
"Capitalization": "\u5927\u5199",
"lowercase": "\u5c0f\u5199",
"UPPERCASE": "\u5927\u5199",
"Title Case": "\u9996\u5b57\u6bcd\u5927\u5199",
"Permanent Pen Properties": "\u6c38\u4e45\u7b14\u5c5e\u6027",
"Permanent pen properties...": "\u6c38\u4e45\u7b14\u5c5e\u6027...",
"Font": "\u5b57\u4f53",
"Size": "\u5b57\u53f7",
"More...": "\u66f4\u591a...",
"Spellcheck Language": "\u62fc\u5199\u68c0\u67e5\u8bed\u8a00",
"Select...": "\u9009\u62e9...",
"Preferences": "\u9996\u9009\u9879",
"Yes": "\u662f",
"No": "\u5426",
"Keyboard Navigation": "\u952e\u76d8\u6307\u5f15",
"Version": "\u7248\u672c",
"Anchor": "\u951a\u70b9",
"Special character": "\u7279\u6b8a\u7b26\u53f7",
"Code sample": "\u4ee3\u7801\u793a\u4f8b",
"Color": "\u989c\u8272",
"Emoticons": "\u8868\u60c5",
"Document properties": "\u6587\u6863\u5c5e\u6027",
"Image": "\u56fe\u7247",
"Insert link": "\u63d2\u5165\u94fe\u63a5",
"Target": "\u6253\u5f00\u65b9\u5f0f",
"Link": "\u94fe\u63a5",
"Poster": "\u5c01\u9762",
"Media": "\u5a92\u4f53",
"Print": "\u6253\u5370",
"Prev": "\u4e0a\u4e00\u4e2a",
"Find and replace": "\u67e5\u627e\u548c\u66ff\u6362",
"Whole words": "\u5168\u5b57\u5339\u914d",
"Spellcheck": "\u62fc\u5199\u68c0\u67e5",
"Caption": "\u6807\u9898",
"Insert template": "\u63d2\u5165\u6a21\u677f"
});

View File

@ -0,0 +1,325 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
<el-form-item label="表单名称" prop="formName">
<el-input
v-model="queryParams.formName"
placeholder="请输入表单名称"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['system:form:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:form:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:form:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['system:form:export']"
>导出</el-button>
</el-col>
</el-row>
<el-table v-loading="loading" :data="formList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="编号" align="center" prop="formId" />
<el-table-column label="表单名称" align="center" prop="formName">
<template slot-scope="scope">
<el-link type="primary" @click="handleDesign(scope.row)">{{scope.row.formName}}</el-link>
</template>
</el-table-column>
<el-table-column label="表单描述" align="center" prop="formComment" />
<!-- <el-table-column label="实体类名称" align="center" prop="tableName" />
<el-table-column label="表单配置json" align="center" prop="options" />
<el-table-column label="版本号" align="center" prop="versionName" />
<el-table-column label="备注" align="center" prop="remark" /> -->
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<!-- 获取当前单元格的值 slot-scope="scope"-->
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleDesign(scope.row)"
v-hasPermi="['system:form:edit']"
>设计表单</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:form:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['system:form:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改数据模型对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="表单名称" prop="formName">
<el-input v-model="form.formName" placeholder="请输入表单名称" />
</el-form-item>
<el-form-item label="表单描述" prop="formComment">
<el-input v-model="form.formComment" type="textarea" placeholder="请输入内容" />
</el-form-item>
<!-- <el-form-item label="实体类名称" prop="tableName">
<el-input v-model="form.tableName" placeholder="请输入实体类名称" />
</el-form-item>
<el-form-item label="表单配置json" prop="options">
<el-input v-model="form.options" placeholder="请输入表单配置json" />
</el-form-item>
<el-form-item label="版本号" prop="versionName">
<el-input v-model="form.versionName" placeholder="请输入版本号" />
</el-form-item> -->
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<!-- 表单设计器 -->
<el-dialog :title="title" :visible.sync="designOpen" :fullscreen="true" append-to-body>
<!-- 动态销毁 -->
<form-design :form="currentRow" v-if="designOpen"></form-design>
</el-dialog>
</div>
</template>
<script>
import { listForm, getForm, delForm, addForm, updateForm, exportForm } from "@/api/system/form";
export default {
name: "Form",
components:{
//
'FormDesign': () => import('@/views/tool/build/index')
},
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
total: 0,
//
formList: [],
//
title: "",
//
open: false,
designOpen:false,
currentRow:{},
//
queryParams: {
pageNum: 1,
pageSize: 10,
formName: undefined,
},
//
form: {},
//
rules: {
formName: [
{ required: true, message: "表单名称不能为空", trigger: "blur" }
],
tableName: [
{ required: true, message: "实体类名称不能为空", trigger: "blur" }
],
versionName: [
{ required: true, message: "版本号不能为空", trigger: "blur" }
],
}
};
},
created() {
this.getList();
},
methods: {
/** 查询数据模型列表 */
getList() {
this.loading = true;
listForm(this.queryParams).then(response => {
this.formList = response.rows;
this.total = response.total;
this.loading = false;
});
},
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.form = {
formId: undefined,
formName: undefined,
formComment: undefined,
tableName: undefined,
options: undefined,
versionName: undefined,
createBy: undefined,
createTime: undefined,
updateBy: undefined,
updateTime: undefined,
remark: undefined
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.formId)
this.single = selection.length!=1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加数据模型";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const formId = row.formId || this.ids
getForm(formId).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改数据模型";
});
},
/** 设计表单操作 */
handleDesign(row){
console.log(row)
this.designOpen=true
this.title = "设计表单模型";
this.currentRow = row
},
/** 提交按钮 */
submitForm: function() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.formId != undefined) {
updateForm(this.form).then(response => {
if (response.code === 200) {
this.msgSuccess("修改成功");
this.open = false;
this.getList();
} else {
this.msgError(response.msg);
}
});
} else {
addForm(this.form).then(response => {
if (response.code === 200) {
this.msgSuccess("新增成功");
this.open = false;
this.getList();
} else {
this.msgError(response.msg);
}
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const formIds = row.formId || this.ids;
this.$confirm('是否确认删除数据模型编号为"' + formIds + '"的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return delForm(formIds);
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
}).catch(function() {});
},
/** 导出按钮操作 */
handleExport() {
const queryParams = this.queryParams;
this.$confirm('是否确认导出所有数据模型数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return exportForm(queryParams);
}).then(response => {
this.download(response.msg);
}).catch(function() {});
}
}
};
</script>

View File

@ -104,3 +104,7 @@ export default {
} }
} }
</script> </script>
<style lang="scss" scoped>
</style>

View File

@ -1,6 +1,6 @@
<script> <script>
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
import render from '@/utils/generator/render' import render from '@/utils/generator/render/render'
const components = { const components = {
itemBtns(h, element, index, parent) { itemBtns(h, element, index, parent) {
@ -22,15 +22,18 @@ const components = {
const layouts = { const layouts = {
colFormItem(h, element, index, parent) { colFormItem(h, element, index, parent) {
const { activeItem } = this.$listeners const { activeItem } = this.$listeners
let className = this.activeId === element.formId ? 'drawing-item active-from-item' : 'drawing-item' const config = element.__config__
let className = this.activeId === config.formId ? 'drawing-item active-from-item' : 'drawing-item'
if (this.formConf.unFocusedComponentBorder) className += ' unfocus-bordered' if (this.formConf.unFocusedComponentBorder) className += ' unfocus-bordered'
let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null
if (config.showLabel === false) labelWidth = '0'
return ( return (
<el-col span={element.span} class={className} <el-col span={config.span} class={className}
nativeOnClick={event => { activeItem(element); event.stopPropagation() }}> nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
<el-form-item label-width={element.labelWidth ? `${element.labelWidth}px` : null} <el-form-item label-width={labelWidth}
label={element.label} required={element.required}> label={config.showLabel ? config.label : ''} required={config.required}>
<render key={element.renderKey} conf={element} onInput={ event => { <render key={config.renderKey} conf={element} onInput={ event => {
this.$set(element, 'defaultValue', event) this.$set(config, 'defaultValue', event)
}} /> }} />
</el-form-item> </el-form-item>
{components.itemBtns.apply(this, arguments)} {components.itemBtns.apply(this, arguments)}
@ -39,7 +42,9 @@ const layouts = {
}, },
rowFormItem(h, element, index, parent) { rowFormItem(h, element, index, parent) {
const { activeItem } = this.$listeners const { activeItem } = this.$listeners
const className = this.activeId === element.formId ? 'drawing-row-item active-from-item' : 'drawing-row-item' const className = this.activeId === element.__config__.formId
? 'drawing-row-item active-from-item'
: 'drawing-row-item'
let child = renderChildren.apply(this, arguments) let child = renderChildren.apply(this, arguments)
if (element.type === 'flex') { if (element.type === 'flex') {
child = <el-row type={element.type} justify={element.justify} align={element.align}> child = <el-row type={element.type} justify={element.justify} align={element.align}>
@ -47,11 +52,11 @@ const layouts = {
</el-row> </el-row>
} }
return ( return (
<el-col span={element.span}> <el-col span={element.__config__.span}>
<el-row gutter={element.gutter} class={className} <el-row gutter={element.__config__.gutter} class={className}
nativeOnClick={event => { activeItem(element); event.stopPropagation() }}> nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
<span class="component-name">{element.componentName}</span> <span class="component-name">{element.__config__.componentName}</span>
<draggable list={element.children} animation={340} group="componentsGroup" class="drag-wrapper"> <draggable list={element.__config__.children} animation={340} group="componentsGroup" class="drag-wrapper">
{child} {child}
</draggable> </draggable>
{components.itemBtns.apply(this, arguments)} {components.itemBtns.apply(this, arguments)}
@ -62,18 +67,19 @@ const layouts = {
} }
function renderChildren(h, element, index, parent) { function renderChildren(h, element, index, parent) {
if (!Array.isArray(element.children)) return null const config = element.__config__
return element.children.map((el, i) => { if (!Array.isArray(config.children)) return null
const layout = layouts[el.layout] return config.children.map((el, i) => {
const layout = layouts[el.__config__.layout]
if (layout) { if (layout) {
return layout.call(this, h, el, i, element.children) return layout.call(this, h, el, i, config.children)
} }
return layoutIsNotFound() return layoutIsNotFound.call(this)
}) })
} }
function layoutIsNotFound() { function layoutIsNotFound() {
throw new Error(`没有与${this.element.layout}匹配的layout`) throw new Error(`没有与${this.element.__config__.layout}匹配的layout`)
} }
export default { export default {
@ -89,12 +95,12 @@ export default {
'formConf' 'formConf'
], ],
render(h) { render(h) {
const layout = layouts[this.element.layout] const layout = layouts[this.element.__config__.layout]
if (layout) { if (layout) {
return layout.call(this, h, this.element, this.index, this.drawingList) return layout.call(this, h, this.element, this.index, this.drawingList)
} }
return layoutIsNotFound() return layoutIsNotFound.call(this)
} }
} }
</script> </script>

View File

@ -0,0 +1,341 @@
<template>
<div>
<el-drawer v-bind="$attrs" v-on="$listeners" @opened="onOpen" @close="onClose">
<div style="height:100%">
<el-row style="height:100%;overflow:auto">
<el-col :md="24" :lg="12" class="left-editor">
<div class="setting" title="资源引用" @click="showResource">
<el-badge :is-dot="!!resources.length" class="item">
<i class="el-icon-setting" />
</el-badge>
</div>
<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" />
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" />
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" />
css
</span>
</el-tab-pane>
</el-tabs>
<div v-show="activeTab==='html'" id="editorHtml" class="tab-editor" />
<div v-show="activeTab==='js'" id="editorJs" class="tab-editor" />
<div v-show="activeTab==='css'" id="editorCss" class="tab-editor" />
</el-col>
<el-col :md="24" :lg="12" class="right-preview">
<div class="action-bar" :style="{'text-align': 'left'}">
<span class="bar-btn" @click="runCode">
<i class="el-icon-refresh" />
刷新
</span>
<span class="bar-btn" @click="exportFile">
<i class="el-icon-download" />
导出vue文件
</span>
<span ref="copyBtn" class="bar-btn copy-btn">
<i class="el-icon-document-copy" />
复制代码
</span>
<span class="bar-btn delete-btn" @click="$emit('update:visible', false)">
<i class="el-icon-circle-close" />
关闭
</span>
</div>
<iframe
v-show="isIframeLoaded"
ref="previewPage"
class="result-wrapper"
frameborder="0"
src="/preview.html"
@load="iframeLoad"
/>
<div v-show="!isIframeLoaded" v-loading="true" class="result-wrapper" />
</el-col>
</el-row>
</div>
</el-drawer>
<resource-dialog
:visible.sync="resourceVisible"
:origin-resource="resources"
@save="setResource"
/>
</div>
</template>
<script>
import { parse } from '@babel/parser'
import ClipboardJS from 'clipboard'
import { saveAs } from 'file-saver'
import {
makeUpHtml, vueTemplate, vueScript, cssStyle
} from '@/utils/generator/html'
import { makeUpJs } from '@/utils/generator/js'
import { makeUpCss } from '@/utils/generator/css'
import { exportDefault, beautifierConf, titleCase } from '@/utils/generator/index'
import ResourceDialog from './ResourceDialog'
import loadBeautifier from '@/utils/generator/loadBeautifier'
import * as monaco from 'monaco-editor';
const editorObj = {
html: null,
js: null,
css: null
}
const mode = {
html: 'html',
js: 'javascript',
css: 'css'
}
let beautifier
//let monaco
export default {
components: { ResourceDialog },
props: ['formData', 'generateConf'],
data() {
return {
activeTab: 'html',
htmlCode: '',
jsCode: '',
cssCode: '',
codeFrame: '',
isIframeLoaded: false,
isInitcode: false, // openruncode
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()
// }
// })
//
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
this.isIframeLoaded = 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 = []
}
}
}
}
</script>
<style lang="scss" scoped>
@import '@/assets/styles/mixin.scss';
.tab-editor {
position: absolute;
top: 33px;
bottom: 0;
left: 0;
right: 0;
font-size: 14px;
}
.left-editor {
position: relative;
height: 100%;
background: #1e1e1e;
overflow: hidden;
}
.setting{
position: absolute;
right: 15px;
top: 3px;
color: #a9f122;
font-size: 18px;
cursor: pointer;
z-index: 1;
}
.right-preview {
height: 100%;
.result-wrapper {
height: calc(100vh - 33px);
width: 100%;
overflow: auto;
padding: 12px;
box-sizing: border-box;
}
}
@include action-bar;
::v-deep .el-drawer__header {
display: none;
}
</style>

View File

@ -0,0 +1,147 @@
<template>
<div>
<el-drawer v-bind="$attrs" v-on="$listeners" @opened="onOpen" @close="onClose">
<div class="action-bar" :style="{'text-align': 'left'}">
<span class="bar-btn" @click="refresh">
<i class="el-icon-refresh" />
刷新
</span>
<span ref="copyBtn" class="bar-btn copy-json-btn">
<i class="el-icon-document-copy" />
复制JSON
</span>
<span class="bar-btn" @click="exportJsonFile">
<i class="el-icon-download" />
导出JSON文件
</span>
<span class="bar-btn delete-btn" @click="$emit('update:visible', false)">
<i class="el-icon-circle-close" />
关闭
</span>
</div>
<div id="editorJson" class="json-editor" />
</el-drawer>
</div>
</template>
<script>
import { beautifierConf } from '@/utils/generator/index'
import ClipboardJS from 'clipboard'
import { saveAs } from 'file-saver'
//import loadMonaco from '@/utils/generator/loadMonaco'
import * as monaco from 'monaco-editor';
import loadBeautifier from '@/utils/generator/loadBeautifier'
let beautifier
//let monaco
export default {
components: {},
props: {
jsonStr: {
type: String,
required: true,
beautifier: null,
jsonEditor: null
}
},
data() {
return {}
},
computed: {},
watch: {},
created() {},
mounted() {
window.addEventListener('keydown', this.preventDefaultSave)
const clipboard = new ClipboardJS('.copy-json-btn', {
text: trigger => {
this.$notify({
title: '成功',
message: '代码已复制到剪切板,可粘贴。',
type: 'success'
})
return this.beautifierJson
}
})
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() {
loadBeautifier(btf => {
beautifier = btf
this.beautifierJson = beautifier.js(this.jsonStr, beautifierConf.js)
// loadMonaco(val => {
// monaco = val
// })
this.setEditorValue('editorJson', this.beautifierJson)
})
},
onClose() {},
setEditorValue(id, codeStr) {
if (this.jsonEditor) {
this.jsonEditor.setValue(codeStr)
} else {
this.jsonEditor = monaco.editor.create(document.getElementById(id), {
value: codeStr,
theme: 'vs-dark',
language: 'json',
automaticLayout: true
})
// ctrl + s
this.jsonEditor.onKeyDown(e => {
if (e.keyCode === 49 && (e.metaKey || e.ctrlKey)) {
this.refresh()
}
})
}
},
exportJsonFile() {
this.$prompt('文件名:', '导出文件', {
inputValue: `${+new Date()}.json`,
closeOnClickModal: false,
inputPlaceholder: '请输入文件名'
}).then(({ value }) => {
if (!value) value = `${+new Date()}.json`
const codeStr = this.jsonEditor.getValue()
const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' })
saveAs(blob, value)
})
},
refresh() {
try {
this.$emit('refresh', JSON.parse(this.jsonEditor.getValue()))
} catch (error) {
this.$notify({
title: '错误',
message: 'JSON格式错误请检查',
type: 'error'
})
}
}
}
}
</script>
<style lang="scss" scoped>
@import '@/assets/styles/mixin.scss';
::v-deep .el-drawer__header {
display: none;
}
@include action-bar;
.json-editor{
height: calc(100vh - 33px);
}
</style>

View File

@ -0,0 +1,114 @@
<template>
<div>
<el-dialog
v-bind="$attrs"
title="外部资源引用"
width="600px"
:close-on-click-modal="false"
v-on="$listeners"
@open="onOpen"
@close="onClose"
>
<el-input
v-for="(item, index) in resources"
:key="index"
v-model="resources[index]"
class="url-item"
placeholder="请输入 css 或 js 资源路径"
prefix-icon="el-icon-link"
clearable
>
<el-button
slot="append"
icon="el-icon-delete"
@click="deleteOne(index)"
/>
</el-input>
<el-button-group class="add-item">
<el-button
plain
@click="addOne('https://cdn.bootcss.com/jquery/1.8.3/jquery.min.js')"
>
jQuery1.8.3
</el-button>
<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>
</el-button-group>
<div slot="footer">
<el-button @click="close">
取消
</el-button>
<el-button
type="primary"
@click="handelConfirm"
>
确定
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
components: {},
inheritAttrs: false,
props: ['originResource'],
data() {
return {
resources: null
}
},
computed: {},
watch: {},
created() {},
mounted() {},
methods: {
onOpen() {
this.resources = this.originResource.length ? JSON.parse(JSON.stringify(this.originResource)) : ['']
},
onClose() {
},
close() {
this.$emit('update:visible', false)
},
handelConfirm() {
const results = this.resources.filter(item => !!item) || []
this.$emit('save', results)
this.close()
if (results.length) {
this.resources = results
}
},
deleteOne(index) {
this.resources.splice(index, 1)
},
addOne(url) {
if (this.resources.indexOf(url) > -1) {
this.$message('资源已存在')
} else {
this.resources.push(url)
}
}
}
}
</script>
<style lang="scss" scoped>
.add-item{
margin-top: 8px;
}
.url-item{
margin-bottom: 12px;
}
</style>

View File

@ -11,9 +11,9 @@
<el-scrollbar class="right-scrollbar"> <el-scrollbar class="right-scrollbar">
<!-- 组件属性 --> <!-- 组件属性 -->
<el-form v-show="currentTab==='field' && showField" size="small" label-width="90px"> <el-form v-show="currentTab==='field' && showField" size="small" label-width="90px">
<el-form-item v-if="activeData.changeTag" label="组件类型"> <el-form-item v-if="activeData.__config__.changeTag" label="组件类型">
<el-select <el-select
v-model="activeData.tagIcon" v-model="activeData.__config__.tagIcon"
placeholder="请选择组件类型" placeholder="请选择组件类型"
:style="{width: '100%'}" :style="{width: '100%'}"
@change="tagChange" @change="tagChange"
@ -21,24 +21,24 @@
<el-option-group v-for="group in tagList" :key="group.label" :label="group.label"> <el-option-group v-for="group in tagList" :key="group.label" :label="group.label">
<el-option <el-option
v-for="item in group.options" v-for="item in group.options"
:key="item.label" :key="item.__config__.label"
:label="item.label" :label="item.__config__.label"
:value="item.tagIcon" :value="item.__config__.tagIcon"
> >
<svg-icon class="node-icon" :icon-class="item.tagIcon" /> <svg-icon class="node-icon" :icon-class="item.__config__.tagIcon" />
<span> {{ item.label }}</span> <span> {{ item.__config__.label }}</span>
</el-option> </el-option>
</el-option-group> </el-option-group>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.vModel!==undefined" label="字段名"> <el-form-item v-if="activeData.__vModel__!==undefined" label="字段名">
<el-input v-model="activeData.vModel" placeholder="请输入字段名v-model" /> <el-input v-model="activeData.__vModel__" placeholder="请输入字段名v-model" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.componentName!==undefined" label="组件名"> <el-form-item v-if="activeData.__config__.componentName!==undefined" label="组件名">
{{ activeData.componentName }} {{ activeData.__config__.componentName }}
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.label!==undefined" label="标题"> <el-form-item v-if="activeData.__config__.label!==undefined" label="标题">
<el-input v-model="activeData.label" placeholder="请输入标题" /> <el-input v-model="activeData.__config__.label" placeholder="请输入标题" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.placeholder!==undefined" label="占位提示"> <el-form-item v-if="activeData.placeholder!==undefined" label="占位提示">
<el-input v-model="activeData.placeholder" placeholder="请输入占位提示" /> <el-input v-model="activeData.placeholder" placeholder="请输入占位提示" />
@ -49,13 +49,13 @@
<el-form-item v-if="activeData['end-placeholder']!==undefined" label="结束占位"> <el-form-item v-if="activeData['end-placeholder']!==undefined" label="结束占位">
<el-input v-model="activeData['end-placeholder']" placeholder="请输入占位提示" /> <el-input v-model="activeData['end-placeholder']" placeholder="请输入占位提示" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.span!==undefined" label="表单栅格"> <el-form-item v-if="activeData.__config__.span!==undefined" label="表单栅格">
<el-slider v-model="activeData.span" :max="24" :min="1" :marks="{12:''}" @change="spanChange" /> <el-slider v-model="activeData.__config__.span" :max="24" :min="1" :marks="{12:''}" @change="spanChange" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.layout==='rowFormItem'" label="栅格间隔"> <el-form-item v-if="activeData.__config__.layout==='rowFormItem'" label="栅格间隔">
<el-input-number v-model="activeData.gutter" :min="0" placeholder="栅格间隔" /> <el-input-number v-model="activeData.gutter" :min="0" placeholder="栅格间隔" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.layout==='rowFormItem'" label="布局模式"> <el-form-item v-if="activeData.__config__.layout==='rowFormItem'" label="布局模式">
<el-radio-group v-model="activeData.type"> <el-radio-group v-model="activeData.type">
<el-radio-button label="default" /> <el-radio-button label="default" />
<el-radio-button label="flex" /> <el-radio-button label="flex" />
@ -78,20 +78,20 @@
<el-radio-button label="bottom" /> <el-radio-button label="bottom" />
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.labelWidth!==undefined" label="标签宽度"> <el-form-item v-if="activeData.__config__.labelWidth!==undefined" label="标签宽度">
<el-input v-model.number="activeData.labelWidth" type="number" placeholder="请输入标签宽度" /> <el-input v-model.number="activeData.__config__.labelWidth" type="number" placeholder="请输入标签宽度" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.style&&activeData.style.width!==undefined" label="组件宽度"> <el-form-item v-if="activeData.style&&activeData.style.width!==undefined" label="组件宽度">
<el-input v-model="activeData.style.width" placeholder="请输入组件宽度" clearable /> <el-input v-model="activeData.style.width" placeholder="请输入组件宽度" clearable />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.vModel!==undefined" label="默认值"> <el-form-item v-if="activeData.__vModel__!==undefined" label="默认值">
<el-input <el-input
:value="setDefaultValue(activeData.defaultValue)" :value="setDefaultValue(activeData.__config__.defaultValue)"
placeholder="请输入默认值" placeholder="请输入默认值"
@input="onDefaultValueInput" @input="onDefaultValueInput"
/> />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.tag==='el-checkbox-group'" label="至少应选"> <el-form-item v-if="activeData.__config__.tag==='el-checkbox-group'" label="至少应选">
<el-input-number <el-input-number
:value="activeData.min" :value="activeData.min"
:min="0" :min="0"
@ -99,7 +99,7 @@
@input="$set(activeData, 'min', $event?$event:undefined)" @input="$set(activeData, 'min', $event?$event:undefined)"
/> />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.tag==='el-checkbox-group'" label="最多可选"> <el-form-item v-if="activeData.__config__.tag==='el-checkbox-group'" label="最多可选">
<el-input-number <el-input-number
:value="activeData.max" :value="activeData.max"
:min="0" :min="0"
@ -107,11 +107,11 @@
@input="$set(activeData, 'max', $event?$event:undefined)" @input="$set(activeData, 'max', $event?$event:undefined)"
/> />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.prepend!==undefined" label="前缀"> <el-form-item v-if="activeData.__slot__&&activeData.__slot__.prepend!==undefined" label="前缀">
<el-input v-model="activeData.prepend" placeholder="请输入前缀" /> <el-input v-model="activeData.__slot__.prepend" placeholder="请输入前缀" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.append!==undefined" label="后缀"> <el-form-item v-if="activeData.__slot__&&activeData.__slot__.append!==undefined" label="后缀">
<el-input v-model="activeData.append" placeholder="请输入后缀" /> <el-input v-model="activeData.__slot__.append" placeholder="请输入后缀" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData['prefix-icon']!==undefined" label="前图标"> <el-form-item v-if="activeData['prefix-icon']!==undefined" label="前图标">
<el-input v-model="activeData['prefix-icon']" placeholder="请输入前图标名称"> <el-input v-model="activeData['prefix-icon']" placeholder="请输入前图标名称">
@ -127,7 +127,17 @@
</el-button> </el-button>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.tag === 'el-cascader'" label="选项分隔符"> <el-form-item
v-if="activeData['icon']!==undefined && activeData.__config__.tag === 'el-button'"
label="按钮图标"
>
<el-input v-model="activeData['icon']" placeholder="请输入按钮图标名称">
<el-button slot="append" icon="el-icon-thumb" @click="openIconsDialog('icon')">
选择
</el-button>
</el-input>
</el-form-item>
<el-form-item v-if="activeData.__config__.tag === 'el-cascader'" label="选项分隔符">
<el-input v-model="activeData.separator" placeholder="请输入选项分隔符" /> <el-input v-model="activeData.separator" placeholder="请输入选项分隔符" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.autosize !== undefined" label="最小行数"> <el-form-item v-if="activeData.autosize !== undefined" label="最小行数">
@ -136,19 +146,22 @@
<el-form-item v-if="activeData.autosize !== undefined" label="最大行数"> <el-form-item v-if="activeData.autosize !== undefined" label="最大行数">
<el-input-number v-model="activeData.autosize.maxRows" :min="1" placeholder="最大行数" /> <el-input-number v-model="activeData.autosize.maxRows" :min="1" placeholder="最大行数" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.min !== undefined" label="最小值"> <el-form-item v-if="isShowMin" label="最小值">
<el-input-number v-model="activeData.min" placeholder="最小值" /> <el-input-number v-model="activeData.min" placeholder="最小值" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.max !== undefined" label="最大值"> <el-form-item v-if="isShowMax" label="最大值">
<el-input-number v-model="activeData.max" placeholder="最大值" /> <el-input-number v-model="activeData.max" placeholder="最大值" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.step !== undefined" label="步长"> <el-form-item v-if="activeData.height!==undefined" label="组件高度">
<el-input-number v-model="activeData.height" placeholder="高度" @input="changeRenderKey" />
</el-form-item>
<el-form-item v-if="isShowStep" label="步长">
<el-input-number v-model="activeData.step" placeholder="步数" /> <el-input-number v-model="activeData.step" placeholder="步数" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.tag === 'el-input-number'" label="精度"> <el-form-item v-if="activeData.__config__.tag === 'el-input-number'" label="精度">
<el-input-number v-model="activeData.precision" :min="0" placeholder="精度" /> <el-input-number v-model="activeData.precision" :min="0" placeholder="精度" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.tag === 'el-input-number'" label="按钮位置"> <el-form-item v-if="activeData.__config__.tag === 'el-input-number'" label="按钮位置">
<el-radio-group v-model="activeData['controls-position']"> <el-radio-group v-model="activeData['controls-position']">
<el-radio-button label=""> <el-radio-button label="">
默认 默认
@ -186,7 +199,7 @@
/> />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="activeData.type !== undefined && 'el-date-picker' === activeData.tag" v-if="activeData.type !== undefined && 'el-date-picker' === activeData.__config__.tag"
label="时间类型" label="时间类型"
> >
<el-select <el-select
@ -222,9 +235,9 @@
<el-option label="txt" value=".txt" /> <el-option label="txt" value=".txt" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.fileSize !== undefined" label="文件大小"> <el-form-item v-if="activeData.__config__.fileSize !== undefined" label="文件大小">
<el-input v-model.number="activeData.fileSize" placeholder="请输入文件大小"> <el-input v-model.number="activeData.__config__.fileSize" placeholder="请输入文件大小">
<el-select slot="append" v-model="activeData.sizeUnit" :style="{ width: '66px' }"> <el-select slot="append" v-model="activeData.__config__.sizeUnit" :style="{ width: '66px' }">
<el-option label="KB" value="KB" /> <el-option label="KB" value="KB" />
<el-option label="MB" value="MB" /> <el-option label="MB" value="MB" />
<el-option label="GB" value="GB" /> <el-option label="GB" value="GB" />
@ -248,11 +261,24 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="activeData.buttonText !== undefined" v-if="activeData.type !== undefined && activeData.__config__.tag === 'el-button'"
label="按钮类型"
>
<el-select v-model="activeData.type" :style="{ width: '100%' }">
<el-option label="primary" value="primary" />
<el-option label="success" value="success" />
<el-option label="warning" value="warning" />
<el-option label="danger" value="danger" />
<el-option label="info" value="info" />
<el-option label="text" value="text" />
</el-select>
</el-form-item>
<el-form-item
v-if="activeData.__config__.buttonText !== undefined"
v-show="'picture-card' !== activeData['list-type']" v-show="'picture-card' !== activeData['list-type']"
label="按钮文字" label="按钮文字"
> >
<el-input v-model="activeData.buttonText" placeholder="请输入按钮文字" /> <el-input v-model="activeData.__config__.buttonText" placeholder="请输入按钮文字" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData['range-separator'] !== undefined" label="分隔符"> <el-form-item v-if="activeData['range-separator'] !== undefined" label="分隔符">
<el-input v-model="activeData['range-separator']" placeholder="请输入分隔符" /> <el-input v-model="activeData['range-separator']" placeholder="请输入分隔符" />
@ -270,15 +296,15 @@
@input="setTimeValue($event)" @input="setTimeValue($event)"
/> />
</el-form-item> </el-form-item>
<template v-if="['el-checkbox-group', 'el-radio-group', 'el-select'].indexOf(activeData.tag) > -1"> <template v-if="['el-checkbox-group', 'el-radio-group', 'el-select'].indexOf(activeData.__config__.tag) > -1">
<el-divider>选项</el-divider> <el-divider>选项</el-divider>
<draggable <draggable
:list="activeData.options" :list="activeData.__slot__.options"
:animation="340" :animation="340"
group="selectItem" group="selectItem"
handle=".option-drag" handle=".option-drag"
> >
<div v-for="(item, index) in activeData.options" :key="index" class="select-item"> <div v-for="(item, index) in activeData.__slot__.options" :key="index" class="select-item">
<div class="select-line-icon option-drag"> <div class="select-line-icon option-drag">
<i class="el-icon-s-operation" /> <i class="el-icon-s-operation" />
</div> </div>
@ -289,7 +315,7 @@
:value="item.value" :value="item.value"
@input="setOptionValue(item, $event)" @input="setOptionValue(item, $event)"
/> />
<div class="close-btn select-line-icon" @click="activeData.options.splice(index, 1)"> <div class="close-btn select-line-icon" @click="activeData.__slot__.options.splice(index, 1)">
<i class="el-icon-remove-outline" /> <i class="el-icon-remove-outline" />
</div> </div>
</div> </div>
@ -307,10 +333,10 @@
<el-divider /> <el-divider />
</template> </template>
<template v-if="['el-cascader'].indexOf(activeData.tag) > -1"> <template v-if="['el-cascader'].indexOf(activeData.__config__.tag) > -1">
<el-divider>选项</el-divider> <el-divider>选项</el-divider>
<el-form-item label="数据类型"> <el-form-item label="数据类型">
<el-radio-group v-model="activeData.dataType" size="small"> <el-radio-group v-model="activeData.__config__.dataType" size="small">
<el-radio-button label="dynamic"> <el-radio-button label="dynamic">
动态数据 动态数据
</el-radio-button> </el-radio-button>
@ -320,27 +346,28 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<template v-if="activeData.dataType === 'dynamic'"> <template v-if="activeData.__config__.dataType === 'dynamic'">
<el-form-item label="标签键名"> <el-form-item label="标签键名">
<el-input v-model="activeData.labelKey" placeholder="请输入标签键名" /> <el-input v-model="activeData.props.props.label" placeholder="请输入标签键名" />
</el-form-item> </el-form-item>
<el-form-item label="值键名"> <el-form-item label="值键名">
<el-input v-model="activeData.valueKey" placeholder="请输入值键名" /> <el-input v-model="activeData.props.props.value" placeholder="请输入值键名" />
</el-form-item> </el-form-item>
<el-form-item label="子级键名"> <el-form-item label="子级键名">
<el-input v-model="activeData.childrenKey" placeholder="请输入子级键名" /> <el-input v-model="activeData.props.props.children" placeholder="请输入子级键名" />
</el-form-item> </el-form-item>
</template> </template>
<!-- 级联选择静态树 -->
<el-tree <el-tree
v-if="activeData.dataType === 'static'" v-if="activeData.__config__.dataType === 'static'"
draggable draggable
:data="activeData.options" :data="activeData.options"
node-key="id" node-key="id"
:expand-on-click-node="false" :expand-on-click-node="false"
:render-content="renderContent" :render-content="renderContent"
/> />
<div v-if="activeData.dataType === 'static'" style="margin-left: 20px"> <div v-if="activeData.__config__.dataType === 'static'" style="margin-left: 20px">
<el-button <el-button
style="padding-bottom: 0" style="padding-bottom: 0"
icon="el-icon-circle-plus-outline" icon="el-icon-circle-plus-outline"
@ -353,8 +380,8 @@
<el-divider /> <el-divider />
</template> </template>
<el-form-item v-if="activeData.optionType !== undefined" label="选项样式"> <el-form-item v-if="activeData.__config__.optionType !== undefined" label="选项样式">
<el-radio-group v-model="activeData.optionType"> <el-radio-group v-model="activeData.__config__.optionType">
<el-radio-button label="default"> <el-radio-button label="default">
默认 默认
</el-radio-button> </el-radio-button>
@ -370,6 +397,14 @@
<el-color-picker v-model="activeData['inactive-color']" /> <el-color-picker v-model="activeData['inactive-color']" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.__config__.showLabel !== undefined
&& activeData.__config__.labelWidth !== undefined" label="显示标签"
>
<el-switch v-model="activeData.__config__.showLabel" />
</el-form-item>
<el-form-item v-if="activeData.branding !== undefined" label="品牌烙印">
<el-switch v-model="activeData.branding" @input="changeRenderKey" />
</el-form-item>
<el-form-item v-if="activeData['allow-half'] !== undefined" label="允许半选"> <el-form-item v-if="activeData['allow-half'] !== undefined" label="允许半选">
<el-switch v-model="activeData['allow-half']" /> <el-switch v-model="activeData['allow-half']" />
</el-form-item> </el-form-item>
@ -386,16 +421,17 @@
<el-switch v-model="activeData.range" @change="rangeChange" /> <el-switch v-model="activeData.range" @change="rangeChange" />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="activeData.border !== undefined && activeData.optionType === 'default'" v-if="activeData.__config__.border !== undefined && activeData.__config__.optionType === 'default'"
label="是否带边框" label="是否带边框"
> >
<el-switch v-model="activeData.border" /> <el-switch v-model="activeData.__config__.border" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.tag === 'el-color-picker'" label="颜色格式"> <el-form-item v-if="activeData.__config__.tag === 'el-color-picker'" label="颜色格式">
<el-select <el-select
v-model="activeData['color-format']" v-model="activeData['color-format']"
placeholder="请选择颜色格式" placeholder="请选择颜色格式"
:style="{ width: '100%' }" :style="{ width: '100%' }"
clearable
@change="colorFormatChange" @change="colorFormatChange"
> >
<el-option <el-option
@ -408,10 +444,11 @@
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="activeData.size !== undefined && v-if="activeData.size !== undefined &&
(activeData.optionType === 'button' || (activeData.__config__.optionType === 'button' ||
activeData.border || activeData.__config__.border ||
activeData.tag === 'el-color-picker')" activeData.__config__.tag === 'el-color-picker' ||
label="选项尺寸" activeData.__config__.tag === 'el-button')"
label="组件尺寸"
> >
<el-radio-group v-model="activeData.size"> <el-radio-group v-model="activeData.size">
<el-radio-button label="medium"> <el-radio-button label="medium">
@ -428,25 +465,25 @@
<el-form-item v-if="activeData['show-word-limit'] !== undefined" label="输入统计"> <el-form-item v-if="activeData['show-word-limit'] !== undefined" label="输入统计">
<el-switch v-model="activeData['show-word-limit']" /> <el-switch v-model="activeData['show-word-limit']" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.tag === 'el-input-number'" label="严格步数"> <el-form-item v-if="activeData.__config__.tag === 'el-input-number'" label="严格步数">
<el-switch v-model="activeData['step-strictly']" /> <el-switch v-model="activeData['step-strictly']" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.tag === 'el-cascader'" label="是否多选"> <el-form-item v-if="activeData.__config__.tag === 'el-cascader'" label="是否多选">
<el-switch v-model="activeData.props.props.multiple" /> <el-switch v-model="activeData.props.props.multiple" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.tag === 'el-cascader'" label="展示全路径"> <el-form-item v-if="activeData.__config__.tag === 'el-cascader'" label="展示全路径">
<el-switch v-model="activeData['show-all-levels']" /> <el-switch v-model="activeData['show-all-levels']" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.tag === 'el-cascader'" label="可否筛选"> <el-form-item v-if="activeData.__config__.tag === 'el-cascader'" label="可否筛选">
<el-switch v-model="activeData.filterable" /> <el-switch v-model="activeData.filterable" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.clearable !== undefined" label="能否清空"> <el-form-item v-if="activeData.clearable !== undefined" label="能否清空">
<el-switch v-model="activeData.clearable" /> <el-switch v-model="activeData.clearable" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.showTip !== undefined" label="显示提示"> <el-form-item v-if="activeData.__config__.showTip !== undefined" label="显示提示">
<el-switch v-model="activeData.showTip" /> <el-switch v-model="activeData.__config__.showTip" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.multiple !== undefined" label="多选文件"> <el-form-item v-if="activeData.__config__.tag === 'el-upload'" label="多选文件">
<el-switch v-model="activeData.multiple" /> <el-switch v-model="activeData.multiple" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData['auto-upload'] !== undefined" label="自动上传"> <el-form-item v-if="activeData['auto-upload'] !== undefined" label="自动上传">
@ -458,20 +495,20 @@
<el-form-item v-if="activeData.disabled !== undefined" label="是否禁用"> <el-form-item v-if="activeData.disabled !== undefined" label="是否禁用">
<el-switch v-model="activeData.disabled" /> <el-switch v-model="activeData.disabled" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.tag === 'el-select'" label="是否可搜索"> <el-form-item v-if="activeData.__config__.tag === 'el-select'" label="能否搜索">
<el-switch v-model="activeData.filterable" /> <el-switch v-model="activeData.filterable" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.tag === 'el-select'" label="是否多选"> <el-form-item v-if="activeData.__config__.tag === 'el-select'" label="是否多选">
<el-switch v-model="activeData.multiple" @change="multipleChange" /> <el-switch v-model="activeData.multiple" @change="multipleChange" />
</el-form-item> </el-form-item>
<el-form-item v-if="activeData.required !== undefined" label="是否必填"> <el-form-item v-if="activeData.__config__.required !== undefined" label="是否必填">
<el-switch v-model="activeData.required" /> <el-switch v-model="activeData.__config__.required" />
</el-form-item> </el-form-item>
<template v-if="activeData.layoutTree"> <template v-if="activeData.__config__.layoutTree">
<el-divider>布局结构树</el-divider> <el-divider>布局结构树</el-divider>
<el-tree <el-tree
:data="[activeData]" :data="[activeData.__config__]"
:props="layoutTreeProps" :props="layoutTreeProps"
node-key="renderKey" node-key="renderKey"
default-expand-all default-expand-all
@ -479,21 +516,21 @@
> >
<span slot-scope="{ node, data }"> <span slot-scope="{ node, data }">
<span class="node-label"> <span class="node-label">
<svg-icon class="node-icon" :icon-class="data.tagIcon" /> <svg-icon class="node-icon" :icon-class="data.__config__?data.__config__.tagIcon:data.tagIcon" />
{{ node.label }} {{ node.label }}
</span> </span>
</span> </span>
</el-tree> </el-tree>
</template> </template>
<template v-if="activeData.layout === 'colFormItem'"> <template v-if="activeData.__config__.layout === 'colFormItem'">
<el-divider>正则校验</el-divider> <el-divider>正则校验</el-divider>
<div <div
v-for="(item, index) in activeData.regList" v-for="(item, index) in activeData.__config__.regList"
:key="index" :key="index"
class="reg-item" class="reg-item"
> >
<span class="close-btn" @click="activeData.regList.splice(index, 1)"> <span class="close-btn" @click="activeData.__config__.regList.splice(index, 1)">
<i class="el-icon-close" /> <i class="el-icon-close" />
</span> </span>
<el-form-item label="表达式"> <el-form-item label="表达式">
@ -548,7 +585,7 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="标签宽度"> <el-form-item label="标签宽度">
<el-input-number v-model="formConf.labelWidth" placeholder="标签宽度" /> <el-input v-model.number="formConf.labelWidth" type="number" placeholder="请输入标签宽度" />
</el-form-item> </el-form-item>
<el-form-item label="栅格间隔"> <el-form-item label="栅格间隔">
<el-input-number v-model="formConf.gutter" :min="0" placeholder="栅格间隔" /> <el-input-number v-model="formConf.gutter" :min="0" placeholder="栅格间隔" />
@ -574,13 +611,12 @@
<script> <script>
import { isArray } from 'util' import { isArray } from 'util'
import TreeNodeDialog from './TreeNodeDialog' import TreeNodeDialog from './TreeNodeDialog'
import { isNumberStr } from '@/utils/index' import { isNumberStr } from '@/utils/generator/index'
import IconsDialog from './IconsDialog' import IconsDialog from './IconsDialog'
import { import {
inputComponents, inputComponents, selectComponents, layoutComponents
selectComponents,
layoutComponents
} from '@/utils/generator/config' } from '@/utils/generator/config'
import { saveFormConf } from '@/utils/generator/db'
const dateTimeFormat = { const dateTimeFormat = {
date: 'yyyy-MM-dd', date: 'yyyy-MM-dd',
@ -688,7 +724,8 @@ export default {
], ],
layoutTreeProps: { layoutTreeProps: {
label(data, node) { label(data, node) {
return data.componentName || `${data.label}: ${data.vModel}` const config = data.__config__
return data.componentName || `${config.label}: ${data.__vModel__}`
} }
} }
} }
@ -696,14 +733,14 @@ export default {
computed: { computed: {
documentLink() { documentLink() {
return ( return (
this.activeData.document this.activeData.__config__.document
|| 'https://element.eleme.cn/#/zh-CN/component/installation' || 'https://element.eleme.cn/#/zh-CN/component/installation'
) )
}, },
dateOptions() { dateOptions() {
if ( if (
this.activeData.type !== undefined this.activeData.type !== undefined
&& this.activeData.tag === 'el-date-picker' && this.activeData.__config__.tag === 'el-date-picker'
) { ) {
if (this.activeData['start-placeholder'] === undefined) { if (this.activeData['start-placeholder'] === undefined) {
return this.dateTypeOptions return this.dateTypeOptions
@ -723,17 +760,37 @@ export default {
options: selectComponents options: selectComponents
} }
] ]
},
activeTag() {
return this.activeData.__config__.tag
},
isShowMin() {
return ['el-input-number', 'el-slider'].indexOf(this.activeTag) > -1
},
isShowMax() {
return ['el-input-number', 'el-slider', 'el-rate'].indexOf(this.activeTag) > -1
},
isShowStep() {
return ['el-input-number', 'el-slider'].indexOf(this.activeTag) > -1
}
},
watch: {
formConf: {
handler(val) {
saveFormConf(val)
},
deep: true
} }
}, },
methods: { methods: {
addReg() { addReg() {
this.activeData.regList.push({ this.activeData.__config__.regList.push({
pattern: '', pattern: '',
message: '' message: ''
}) })
}, },
addSelectItem() { addSelectItem() {
this.activeData.options.push({ this.activeData.__slot__.options.push({
label: '', label: '',
value: '' value: ''
}) })
@ -768,6 +825,7 @@ export default {
this.currentNode = data.children this.currentNode = data.children
}, },
remove(node, data) { remove(node, data) {
this.activeData.__config__.defaultValue = [] //
const { parent } = node const { parent } = node
const children = parent.data.children || parent.data const children = parent.data.children || parent.data
const index = children.findIndex(d => d.id === data.id) const index = children.findIndex(d => d.id === data.id)
@ -783,29 +841,29 @@ export default {
if (Array.isArray(val)) { if (Array.isArray(val)) {
return val.join(',') return val.join(',')
} }
if (['string', 'number'].indexOf(val) > -1) { // if (['string', 'number'].indexOf(typeof val) > -1) {
return val // return val
} // }
if (typeof val === 'boolean') { if (typeof val === 'boolean') {
return `${val}` return `${val}`
} }
return val return val
}, },
onDefaultValueInput(str) { onDefaultValueInput(str) {
if (isArray(this.activeData.defaultValue)) { if (isArray(this.activeData.__config__.defaultValue)) {
// //
this.$set( this.$set(
this.activeData, this.activeData.__config__,
'defaultValue', 'defaultValue',
str.split(',').map(val => (isNumberStr(val) ? +val : val)) str.split(',').map(val => (isNumberStr(val) ? +val : val))
) )
} else if (['true', 'false'].indexOf(str) > -1) { } else if (['true', 'false'].indexOf(str) > -1) {
// //
this.$set(this.activeData, 'defaultValue', JSON.parse(str)) this.$set(this.activeData.__config__, 'defaultValue', JSON.parse(str))
} else { } else {
// //
this.$set( this.$set(
this.activeData, this.activeData.__config__,
'defaultValue', 'defaultValue',
isNumberStr(str) ? +str : str isNumberStr(str) ? +str : str
) )
@ -820,7 +878,7 @@ export default {
}, },
setTimeValue(val, type) { setTimeValue(val, type) {
const valueFormat = type === 'week' ? dateTimeFormat.date : val const valueFormat = type === 'week' ? dateTimeFormat.date : val
this.$set(this.activeData, 'defaultValue', null) this.$set(this.activeData.__config__, 'defaultValue', null)
this.$set(this.activeData, 'value-format', valueFormat) this.$set(this.activeData, 'value-format', valueFormat)
this.$set(this.activeData, 'format', val) this.$set(this.activeData, 'format', val)
}, },
@ -828,14 +886,14 @@ export default {
this.formConf.span = val this.formConf.span = val
}, },
multipleChange(val) { multipleChange(val) {
this.$set(this.activeData, 'defaultValue', val ? [] : '') this.$set(this.activeData.__config__, 'defaultValue', val ? [] : '')
}, },
dateTypeChange(val) { dateTypeChange(val) {
this.setTimeValue(dateTimeFormat[val], val) this.setTimeValue(dateTimeFormat[val], val)
}, },
rangeChange(val) { rangeChange(val) {
this.$set( this.$set(
this.activeData, this.activeData.__config__,
'defaultValue', 'defaultValue',
val ? [this.activeData.min, this.activeData.max] : this.activeData.min val ? [this.activeData.min, this.activeData.max] : this.activeData.min
) )
@ -847,9 +905,9 @@ export default {
if (val) this.activeData['show-text'] = false if (val) this.activeData['show-text'] = false
}, },
colorFormatChange(val) { colorFormatChange(val) {
this.activeData.defaultValue = null this.activeData.__config__.defaultValue = null
this.activeData['show-alpha'] = val.indexOf('a') > -1 this.activeData['show-alpha'] = val.indexOf('a') > -1
this.activeData.renderKey = +new Date() // renderKey, this.activeData.__config__.renderKey = +new Date() // renderKey,
}, },
openIconsDialog(model) { openIconsDialog(model) {
this.iconsVisible = true this.iconsVisible = true
@ -859,9 +917,12 @@ export default {
this.activeData[this.currentIconModel] = val this.activeData[this.currentIconModel] = val
}, },
tagChange(tagIcon) { tagChange(tagIcon) {
let target = inputComponents.find(item => item.tagIcon === tagIcon) let target = inputComponents.find(item => item.__config__.tagIcon === tagIcon)
if (!target) target = selectComponents.find(item => item.tagIcon === tagIcon) if (!target) target = selectComponents.find(item => item.__config__.tagIcon === tagIcon)
this.$emit('tag-change', target) this.$emit('tag-change', target)
},
changeRenderKey() {
this.activeData.__config__.renderKey = +new Date()
} }
} }
} }

View File

@ -71,7 +71,10 @@
</div> </div>
</template> </template>
<script> <script>
import { isNumberStr } from '@/utils/index' import { isNumberStr } from '@/utils/generator/index'
import { getTreeNodeId, saveTreeNodeId } from '@/utils/generator/db'
const id = getTreeNodeId()
export default { export default {
components: {}, components: {},
@ -79,7 +82,7 @@ export default {
props: [], props: [],
data() { data() {
return { return {
id: 100, id,
formData: { formData: {
label: undefined, label: undefined,
value: undefined value: undefined
@ -118,6 +121,9 @@ export default {
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
'formData.value': function (val) { 'formData.value': function (val) {
this.dataType = isNumberStr(val) ? 'number' : 'string' this.dataType = isNumberStr(val) ? 'number' : 'string'
},
id(val) {
saveTreeNodeId(val)
} }
}, },
created() {}, created() {},
@ -147,3 +153,6 @@ export default {
} }
} }
</script> </script>
<style lang="scss" scoped>
</style>

View File

@ -1,19 +1,25 @@
<template> <template>
<div class="container"> <div class="container">
<div class="left-board"> <div class="left-board">
<div class="logo-wrapper"> <div class="logo-wrapper">
<div class="logo"> <div class="logo">
<img :src="logo" alt="logo"> Form Generator <img :src="logo" alt="logo"> Form Generator
<a class="github" href="https://github.com/JakHuang/form-generator" target="_blank">
<img src="https://github.githubassets.com/pinned-octocat.svg" alt>
</a>
</div> </div>
</div> </div>
<el-scrollbar class="left-scrollbar"> <el-scrollbar class="left-scrollbar">
<div class="components-list"> <div class="components-list">
<div v-for="(item, listIndex) in leftComponents" :key="listIndex">
<div class="components-title"> <div class="components-title">
<svg-icon icon-class="component" />输入型组件 <svg-icon icon-class="component" />
{{ item.title }}
</div> </div>
<draggable <draggable
class="components-draggable" class="components-draggable"
:list="inputComponents" :list="item.list"
:group="{ name: 'componentsGroup', pull: 'clone', put: false }" :group="{ name: 'componentsGroup', pull: 'clone', put: false }"
:clone="cloneComponent" :clone="cloneComponent"
draggable=".components-item" draggable=".components-item"
@ -21,63 +27,33 @@
@end="onEnd" @end="onEnd"
> >
<div <div
v-for="(element, index) in inputComponents" :key="index" class="components-item" v-for="(element, index) in item.list"
@click="addComponent(element)"
>
<div class="components-body">
<svg-icon :icon-class="element.tagIcon" />
{{ element.label }}
</div>
</div>
</draggable>
<div class="components-title">
<svg-icon icon-class="component" />选择型组件
</div>
<draggable
class="components-draggable"
:list="selectComponents"
:group="{ name: 'componentsGroup', pull: 'clone', put: false }"
:clone="cloneComponent"
draggable=".components-item"
:sort="false"
@end="onEnd"
>
<div
v-for="(element, index) in selectComponents"
:key="index" :key="index"
class="components-item" class="components-item"
@click="addComponent(element)" @click="addComponent(element)"
> >
<div class="components-body"> <div class="components-body">
<svg-icon :icon-class="element.tagIcon" /> <svg-icon :icon-class="element.__config__.tagIcon" />
{{ element.label }} {{ element.__config__.label }}
</div> </div>
</div> </div>
</draggable> </draggable>
<div class="components-title">
<svg-icon icon-class="component" /> 布局型组件
</div> </div>
<draggable
class="components-draggable" :list="layoutComponents"
:group="{ name: 'componentsGroup', pull: 'clone', put: false }" :clone="cloneComponent"
draggable=".components-item" :sort="false" @end="onEnd"
>
<div
v-for="(element, index) in layoutComponents" :key="index" class="components-item"
@click="addComponent(element)"
>
<div class="components-body">
<svg-icon :icon-class="element.tagIcon" />
{{ element.label }}
</div>
</div>
</draggable>
</div> </div>
</el-scrollbar> </el-scrollbar>
</div> </div>
<div class="center-board"> <div class="center-board">
<div class="action-bar"> <div class="action-bar">
<el-button icon="el-icon-circle-check" v-if="isForm" type="text" @click="save">
保存
</el-button>
<el-button icon="el-icon-video-play" type="text" @click="run">
运行
</el-button>
<el-button icon="el-icon-view" type="text" @click="showJson">
查看json
</el-button>
<el-button icon="el-icon-download" type="text" @click="download"> <el-button icon="el-icon-download" type="text" @click="download">
导出vue文件 导出vue文件
</el-button> </el-button>
@ -125,6 +101,18 @@
@tag-change="tagChange" @tag-change="tagChange"
/> />
<form-drawer
:visible.sync="drawerVisible"
:form-data="formData"
size="100%"
:generate-conf="generateConf"
/>
<json-drawer
size="60%"
:visible.sync="jsonDrawerVisible"
:json-str="JSON.stringify(formData)"
@refresh="refreshJson"
/>
<code-type-dialog <code-type-dialog
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
title="选择生成类型" title="选择生成类型"
@ -137,20 +125,19 @@
<script> <script>
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
import { debounce } from 'throttle-debounce'
import { saveAs } from 'file-saver' import { saveAs } from 'file-saver'
import beautifier from 'js-beautify'
import ClipboardJS from 'clipboard' import ClipboardJS from 'clipboard'
import render from '@/utils/generator/render' import render from '@/utils/generator/render/render'
import FormDrawer from './FormDrawer'
import JsonDrawer from './JsonDrawer'
import RightPanel from './RightPanel' import RightPanel from './RightPanel'
import { import {
inputComponents, inputComponents, selectComponents, layoutComponents, formConf
selectComponents,
layoutComponents,
formConf
} from '@/utils/generator/config' } from '@/utils/generator/config'
import { import {
exportDefault, beautifierConf, isNumberStr, titleCase exportDefault, beautifierConf, isNumberStr, titleCase
} from '@/utils/index' } from '@/utils/generator/index'
import { import {
makeUpHtml, vueTemplate, vueScript, cssStyle makeUpHtml, vueTemplate, vueScript, cssStyle
} from '@/utils/generator/html' } from '@/utils/generator/html'
@ -160,23 +147,46 @@ import drawingDefalut from '@/utils/generator/drawingDefalut'
import logo from '@/assets/logo/logo.png' import logo from '@/assets/logo/logo.png'
import CodeTypeDialog from './CodeTypeDialog' import CodeTypeDialog from './CodeTypeDialog'
import DraggableItem from './DraggableItem' import DraggableItem from './DraggableItem'
import {
getDrawingList, saveDrawingList, getIdGlobal, saveIdGlobal, getFormConf
} from '@/utils/generator/db'
import loadBeautifier from '@/utils/generator/loadBeautifier'
//api
import { listForm, getForm, delForm, addForm, updateForm, exportForm } from "@/api/system/form";
let beautifier
const emptyActiveData = { style: {}, autosize: {} } const emptyActiveData = { style: {}, autosize: {} }
let oldActiveId let oldActiveId
let tempActiveData let tempActiveData
const drawingListInDB = getDrawingList()
const formConfInDB = getFormConf()
const idGlobal = getIdGlobal()
export default { export default {
props:{
//form
form:{
'type':Object,
//
default: function () {
return {}
}
}
},
components: { components: {
draggable, draggable,
render, render,
FormDrawer,
JsonDrawer,
RightPanel, RightPanel,
CodeTypeDialog, CodeTypeDialog,
DraggableItem DraggableItem
}, },
data() { data() {
return { return {
isForm:false,
logo, logo,
idGlobal: 100, idGlobal,
formConf, formConf,
inputComponents, inputComponents,
selectComponents, selectComponents,
@ -188,19 +198,36 @@ export default {
drawerVisible: false, drawerVisible: false,
formData: {}, formData: {},
dialogVisible: false, dialogVisible: false,
jsonDrawerVisible: false,
generateConf: null, generateConf: null,
showFileName: false, showFileName: false,
activeData: drawingDefalut[0] activeData: drawingDefalut[0],
saveDrawingListDebounce: debounce(340, saveDrawingList),
saveIdGlobalDebounce: debounce(340, saveIdGlobal),
leftComponents: [
{
title: '输入型组件',
list: inputComponents
},
{
title: '选择型组件',
list: selectComponents
},
{
title: '布局型组件',
list: layoutComponents
}
]
} }
}, },
computed: { computed: {
}, },
watch: { watch: {
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
'activeData.label': function (val, oldVal) { 'activeData.__config__.label': function (val, oldVal) {
if ( if (
this.activeData.placeholder === undefined this.activeData.placeholder === undefined
|| !this.activeData.tag || !this.activeData.__config__.tag
|| oldActiveId !== this.activeId || oldActiveId !== this.activeId
) { ) {
return return
@ -212,9 +239,51 @@ export default {
oldActiveId = val oldActiveId = val
}, },
immediate: true immediate: true
},
drawingList: {
handler(val) {
this.saveDrawingListDebounce(val)
if (val.length === 0) this.idGlobal = 100
},
deep: true
},
idGlobal: {
handler(val) {
this.saveIdGlobalDebounce(val)
},
immediate: true
} }
}, },
//
mounted() { mounted() {
//
if(Object.keys(this.form).length > 0){
this.drawingList = [];
this.isForm = true
console.log(this.form)
const formOptions = JSON.parse(this.form.options);
if(formOptions){
this.drawingList = formOptions.fields
//
this.activeFormItem(this.drawingList[0])
this.formConf = formOptions
}
}else{
if (Array.isArray(drawingListInDB) && drawingListInDB.length > 0) {
this.drawingList = drawingListInDB
} else {
this.drawingList = drawingDefalut
}
//
this.activeFormItem(this.drawingList[0])
if (formConfInDB) {
this.formConf = formConfInDB
}
loadBeautifier(btf => {
beautifier = btf
})
}
//
const clipboard = new ClipboardJS('#copyNode', { const clipboard = new ClipboardJS('#copyNode', {
text: trigger => { text: trigger => {
const codeStr = this.generateCode() const codeStr = this.generateCode()
@ -233,9 +302,9 @@ export default {
methods: { methods: {
activeFormItem(element) { activeFormItem(element) {
this.activeData = element this.activeData = element
this.activeId = element.formId this.activeId = element.__config__.formId
}, },
onEnd(obj, a) { onEnd(obj) {
if (obj.from !== obj.to) { if (obj.from !== obj.to) {
this.activeData = tempActiveData this.activeData = tempActiveData
this.activeId = this.idGlobal this.activeId = this.idGlobal
@ -248,20 +317,18 @@ export default {
}, },
cloneComponent(origin) { cloneComponent(origin) {
const clone = JSON.parse(JSON.stringify(origin)) const clone = JSON.parse(JSON.stringify(origin))
clone.formId = ++this.idGlobal const config = clone.__config__
clone.span = formConf.span config.formId = ++this.idGlobal
clone.renderKey = +new Date() // renderKey config.span = this.formConf.span
if (!clone.layout) clone.layout = 'colFormItem' config.renderKey = +new Date() // renderKey
if (clone.layout === 'colFormItem') { if (config.layout === 'colFormItem') {
clone.vModel = `field${this.idGlobal}` clone.__vModel__ = `field${this.idGlobal}`
clone.placeholder !== undefined && (clone.placeholder += clone.label) clone.placeholder !== undefined && (clone.placeholder += config.label)
tempActiveData = clone } else if (config.layout === 'rowFormItem') {
} else if (clone.layout === 'rowFormItem') { config.componentName = `row${this.idGlobal}`
delete clone.label config.gutter = this.formConf.gutter
clone.componentName = `row${this.idGlobal}`
clone.gutter = this.formConf.gutter
tempActiveData = clone
} }
tempActiveData = clone
return tempActiveData return tempActiveData
}, },
AssembleFormData() { AssembleFormData() {
@ -291,6 +358,7 @@ export default {
this.$confirm('确定要清空所有组件吗?', '提示', { type: 'warning' }).then( this.$confirm('确定要清空所有组件吗?', '提示', { type: 'warning' }).then(
() => { () => {
this.drawingList = [] this.drawingList = []
this.idGlobal = 100
} }
) )
}, },
@ -301,15 +369,16 @@ export default {
this.activeFormItem(clone) this.activeFormItem(clone)
}, },
createIdAndKey(item) { createIdAndKey(item) {
item.formId = ++this.idGlobal const config = item.__config__
item.renderKey = +new Date() config.formId = ++this.idGlobal
if (item.layout === 'colFormItem') { config.renderKey = +new Date()
item.vModel = `field${this.idGlobal}` if (config.layout === 'colFormItem') {
} else if (item.layout === 'rowFormItem') { item.__vModel__ = `field${this.idGlobal}`
item.componentName = `row${this.idGlobal}` } else if (config.layout === 'rowFormItem') {
config.componentName = `row${this.idGlobal}`
} }
if (Array.isArray(item.children)) { if (Array.isArray(config.children)) {
item.children = item.children.map(childItem => this.createIdAndKey(childItem)) config.children = config.children.map(childItem => this.createIdAndKey(childItem))
} }
return item return item
}, },
@ -330,6 +399,10 @@ export default {
const css = cssStyle(makeUpCss(this.formData)) const css = cssStyle(makeUpCss(this.formData))
return beautifier.html(html + script + css, beautifierConf.html) return beautifier.html(html + script + css, beautifierConf.html)
}, },
showJson() {
this.AssembleFormData()
this.jsonDrawerVisible = true
},
download() { download() {
this.dialogVisible = true this.dialogVisible = true
this.showFileName = true this.showFileName = true
@ -340,6 +413,23 @@ export default {
this.showFileName = false this.showFileName = false
this.operationType = 'run' this.operationType = 'run'
}, },
save(){
let options = {
fields: this.drawingList,
//
...this.formConf
}
this.form.options = JSON.stringify(options)
updateForm(this.form).then(response => {
if (response.code === 200) {
this.msgSuccess("保存成功");
// $parent
//this.$parent.designOpen = false;
} else {
this.msgError(response.msg);
}
});
},
copy() { copy() {
this.dialogVisible = true this.dialogVisible = true
this.showFileName = false this.showFileName = false
@ -347,15 +437,18 @@ export default {
}, },
tagChange(newTag) { tagChange(newTag) {
newTag = this.cloneComponent(newTag) newTag = this.cloneComponent(newTag)
newTag.vModel = this.activeData.vModel const config = newTag.__config__
newTag.formId = this.activeId newTag.__vModel__ = this.activeData.__vModel__
newTag.span = this.activeData.span config.formId = this.activeId
delete this.activeData.tag config.span = this.activeData.__config__.span
delete this.activeData.tagIcon this.activeData.__config__.tag = config.tag
delete this.activeData.document this.activeData.__config__.tagIcon = config.tagIcon
this.activeData.__config__.document = config.document
if (typeof this.activeData.__config__.defaultValue === typeof config.defaultValue) {
config.defaultValue = this.activeData.__config__.defaultValue
}
Object.keys(newTag).forEach(key => { Object.keys(newTag).forEach(key => {
if (this.activeData[key] !== undefined if (this.activeData[key] !== undefined) {
&& typeof this.activeData[key] === typeof newTag[key]) {
newTag[key] = this.activeData[key] newTag[key] = this.activeData[key]
} }
}) })
@ -363,157 +456,25 @@ export default {
this.updateDrawingList(newTag, this.drawingList) this.updateDrawingList(newTag, this.drawingList)
}, },
updateDrawingList(newTag, list) { updateDrawingList(newTag, list) {
const index = list.findIndex(item => item.formId === this.activeId) const index = list.findIndex(item => item.__config__.formId === this.activeId)
if (index > -1) { if (index > -1) {
list.splice(index, 1, newTag) list.splice(index, 1, newTag)
} else { } else {
list.forEach(item => { list.forEach(item => {
if (Array.isArray(item.children)) this.updateDrawingList(newTag, item.children) if (Array.isArray(item.__config__.children)) this.updateDrawingList(newTag, item.__config__.children)
}) })
} }
},
refreshJson(data) {
this.drawingList = JSON.parse(JSON.stringify(data.fields))
delete data.fields
this.formConf = data
} }
} }
} }
</script> </script>
<style lang='scss'> <style lang='scss'>
body, html{
margin: 0;
padding: 0;
background: #fff;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
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;
}
.editor-tabs{
background: #121315;
.el-tabs__header{
margin: 0;
border-bottom-color: #121315;
.el-tabs__nav{
border-color: #121315;
}
}
.el-tabs__item{
height: 32px;
line-height: 32px;
color: #888a8e;
border-left: 1px solid #121315 !important;
background: #363636;
margin-right: 5px;
user-select: none;
}
.el-tabs__item.is-active{
background: #1e1e1e;
border-bottom-color: #1e1e1e!important;
color: #fff;
}
.el-icon-edit{
color: #f1fa8c;
}
.el-icon-document{
color: #a95812;
}
}
// home
.right-scrollbar {
.el-scrollbar__view {
padding: 12px 18px 15px 15px;
}
}
.left-scrollbar .el-scrollbar__wrap {
box-sizing: border-box;
overflow-x: hidden !important;
margin-bottom: 0 !important;
}
.center-tabs{
.el-tabs__header{
margin-bottom: 0!important;
}
.el-tabs__item{
width: 50%;
text-align: center;
}
.el-tabs__nav{
width: 100%;
}
}
.reg-item{
padding: 12px 6px;
background: #f8f8f8;
position: relative;
border-radius: 4px;
.close-btn{
position: absolute;
right: -6px;
top: -6px;
display: block;
width: 16px;
height: 16px;
line-height: 16px;
background: rgba(0, 0, 0, 0.2);
border-radius: 50%;
color: #fff;
text-align: center;
z-index: 1;
cursor: pointer;
font-size: 12px;
&:hover{
background: rgba(210, 23, 23, 0.5)
}
}
& + .reg-item{
margin-top: 18px;
}
}
.action-bar{
& .el-button+.el-button {
margin-left: 15px;
}
& i {
font-size: 20px;
vertical-align: middle;
position: relative;
top: -1px;
}
}
.custom-tree-node{
width: 100%;
font-size: 14px;
.node-operation{
float: right;
}
i[class*="el-icon"] + i[class*="el-icon"]{
margin-left: 6px;
}
.el-icon-plus{
color: #409EFF;
}
.el-icon-delete{
color: #157a0c;
}
}
.left-scrollbar .el-scrollbar__view{
overflow-x: hidden;
}
.el-rate{
display: inline-block;
vertical-align: text-top;
}
.el-upload__tip{
line-height: 1.2;
}
$selectedColor: #f6f7ff; $selectedColor: #f6f7ff;
$lighterBlue: #409EFF; $lighterBlue: #409EFF;
@ -700,7 +661,7 @@ $lighterBlue: #409EFF;
.drawing-item{ .drawing-item{
position: relative; position: relative;
cursor: move; cursor: move;
&.unfocus-bordered:not(.activeFromItem) > div:first-child { &.unfocus-bordered:not(.active-from-item) > div:first-child {
border: 1px dashed #ccc; border: 1px dashed #ccc;
} }
.el-form-item{ .el-form-item{

View File

@ -0,0 +1,64 @@
import Vue from 'vue'
// import Cookies from 'js-cookie'
import Element from 'element-ui'
import '@/assets/styles/element-variables.scss'
import { loadScriptQueue } from '@/utils/generator/loadScript'
import Tinymce from '@/utils/generator/tinymce'
Vue.component('tinymce', Tinymce)
Vue.use(Element)
const $previewApp = document.getElementById('previewApp')
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 buildLinks(links) {
let strs = ''
links.forEach(url => {
strs += `<link href="${url}" rel="stylesheet">`
})
return strs
}
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)
}
$previewApp.innerHTML = `${links}<style>${code.css}</style><div id="app"></div>`
if (Array.isArray(code.scripts) && code.scripts.length > 0) {
loadScriptQueue(code.scripts, () => {
newVue(attrs, code.js, code.html)
})
} else {
newVue(attrs, code.js, code.html)
}
}
}
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')
}

View File

@ -9,6 +9,19 @@ function resolve(dir) {
const name = defaultSettings.title || '若依管理系统' // 标题 const name = defaultSettings.title || '若依管理系统' // 标题
const port = process.env.port || process.env.npm_config_port || 80 // 端口 const port = process.env.port || process.env.npm_config_port || 80 // 端口
//monaco webpack 插件
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
const minify = process.env.NODE_ENV === 'development' ? false : {
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true,
minifyCSS: true,
minifyJS: true
}
// vue.config.js 配置说明 // vue.config.js 配置说明
//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions //官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions
@ -26,6 +39,25 @@ module.exports = {
lintOnSave: process.env.NODE_ENV === 'development', lintOnSave: process.env.NODE_ENV === 'development',
// 如果你不需要生产环境的 source map可以将其设置为 false 以加速生产环境构建。 // 如果你不需要生产环境的 source map可以将其设置为 false 以加速生产环境构建。
productionSourceMap: false, productionSourceMap: false,
//运行时编译
runtimeCompiler: true,
//构建多页面配置
pages: {
index: {
entry: 'src/main.js',
template: 'public/index.html',
filename: 'index.html',
chunks: ['chunk-vendors', 'chunk-common', 'index'],
minify
},
preview: {
entry: 'src/views/tool/build/preview/main.js',
template: 'public/preview.html',
filename: 'preview.html',
chunks: ['chunk-vendors', 'chunk-common', 'preview'],
minify
}
},
// webpack-dev-server 相关配置 // webpack-dev-server 相关配置
devServer: { devServer: {
host: '0.0.0.0', host: '0.0.0.0',
@ -33,7 +65,7 @@ module.exports = {
proxy: { proxy: {
// detail: https://cli.vuejs.org/config/#devserver-proxy // detail: https://cli.vuejs.org/config/#devserver-proxy
[process.env.VUE_APP_BASE_API]: { [process.env.VUE_APP_BASE_API]: {
target: `http://localhost:8080`, target: `http://localhost:8888`,
changeOrigin: true, changeOrigin: true,
pathRewrite: { pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: '' ['^' + process.env.VUE_APP_BASE_API]: ''
@ -48,7 +80,10 @@ module.exports = {
alias: { alias: {
'@': resolve('src') '@': resolve('src')
} }
} },
plugins: [
new MonacoWebpackPlugin()
]
}, },
chainWebpack(config) { chainWebpack(config) {
config.plugins.delete('preload') // TODO: need test config.plugins.delete('preload') // TODO: need test
@ -81,7 +116,6 @@ module.exports = {
return options return options
}) })
.end() .end()
config config
// https://webpack.js.org/configuration/devtool/#development // https://webpack.js.org/configuration/devtool/#development
.when(process.env.NODE_ENV === 'development', .when(process.env.NODE_ENV === 'development',

View File

@ -0,0 +1,103 @@
package com.ruoyi.project.system.controller;
import java.util.List;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.project.system.domain.PCommForm;
import com.ruoyi.project.system.service.IPCommFormService;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.page.TableDataInfo;
/**
* 数据模型Controller
*
* @author ruoyi
* @date 2020-05-14
*/
@RestController
@RequestMapping("/system/form")
public class PCommFormController extends BaseController
{
@Autowired
private IPCommFormService pCommFormService;
/**
* 查询数据模型列表
*/
@PreAuthorize("@ss.hasPermi('system:form:list')")
@GetMapping("/list")
public TableDataInfo list(PCommForm pCommForm)
{
startPage();
List<PCommForm> list = pCommFormService.selectPCommFormList(pCommForm);
return getDataTable(list);
}
/**
* 导出数据模型列表
*/
@PreAuthorize("@ss.hasPermi('system:form:export')")
@Log(title = "数据模型", businessType = BusinessType.EXPORT)
@GetMapping("/export")
public AjaxResult export(PCommForm pCommForm)
{
List<PCommForm> list = pCommFormService.selectPCommFormList(pCommForm);
ExcelUtil<PCommForm> util = new ExcelUtil<PCommForm>(PCommForm.class);
return util.exportExcel(list, "form");
}
/**
* 获取数据模型详细信息
*/
@PreAuthorize("@ss.hasPermi('system:form:query')")
@GetMapping(value = "/{formId}")
public AjaxResult getInfo(@PathVariable("formId") Long formId)
{
return AjaxResult.success(pCommFormService.selectPCommFormById(formId));
}
/**
* 新增数据模型
*/
@PreAuthorize("@ss.hasPermi('system:form:add')")
@Log(title = "数据模型", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody PCommForm pCommForm)
{
return toAjax(pCommFormService.insertPCommForm(pCommForm));
}
/**
* 修改数据模型
*/
@PreAuthorize("@ss.hasPermi('system:form:edit')")
@Log(title = "数据模型", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody PCommForm pCommForm)
{
return toAjax(pCommFormService.updatePCommForm(pCommForm));
}
/**
* 删除数据模型
*/
@PreAuthorize("@ss.hasPermi('system:form:remove')")
@Log(title = "数据模型", businessType = BusinessType.DELETE)
@DeleteMapping("/{formIds}")
public AjaxResult remove(@PathVariable Long[] formIds)
{
return toAjax(pCommFormService.deletePCommFormByIds(formIds));
}
}

View File

@ -0,0 +1,112 @@
package com.ruoyi.project.system.domain;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import com.ruoyi.framework.web.domain.BaseEntity;
/**
* 数据模型对象 p_comm_form
*
* @author ruoyi
* @date 2020-05-14
*/
public class PCommForm extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 编号 */
private Long formId;
/** 表单名称 */
@Excel(name = "表单名称")
private String formName;
/** 表单描述 */
@Excel(name = "表单描述")
private String formComment;
/** 实体类名称 */
@Excel(name = "实体类名称")
private String tableName;
/** 表单配置json */
@Excel(name = "表单配置json")
private String options;
/** 版本号 */
@Excel(name = "版本号")
private String versionName;
public void setFormId(Long formId)
{
this.formId = formId;
}
public Long getFormId()
{
return formId;
}
public void setFormName(String formName)
{
this.formName = formName;
}
public String getFormName()
{
return formName;
}
public void setFormComment(String formComment)
{
this.formComment = formComment;
}
public String getFormComment()
{
return formComment;
}
public void setTableName(String tableName)
{
this.tableName = tableName;
}
public String getTableName()
{
return tableName;
}
public void setOptions(String options)
{
this.options = options;
}
public String getOptions()
{
return options;
}
public void setVersionName(String versionName)
{
this.versionName = versionName;
}
public String getVersionName()
{
return versionName;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("formId", getFormId())
.append("formName", getFormName())
.append("formComment", getFormComment())
.append("tableName", getTableName())
.append("options", getOptions())
.append("versionName", getVersionName())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.toString();
}
}

View File

@ -0,0 +1,61 @@
package com.ruoyi.project.system.mapper;
import java.util.List;
import com.ruoyi.project.system.domain.PCommForm;
/**
* 数据模型Mapper接口
*
* @author ruoyi
* @date 2020-05-14
*/
public interface PCommFormMapper
{
/**
* 查询数据模型
*
* @param formId 数据模型ID
* @return 数据模型
*/
public PCommForm selectPCommFormById(Long formId);
/**
* 查询数据模型列表
*
* @param pCommForm 数据模型
* @return 数据模型集合
*/
public List<PCommForm> selectPCommFormList(PCommForm pCommForm);
/**
* 新增数据模型
*
* @param pCommForm 数据模型
* @return 结果
*/
public int insertPCommForm(PCommForm pCommForm);
/**
* 修改数据模型
*
* @param pCommForm 数据模型
* @return 结果
*/
public int updatePCommForm(PCommForm pCommForm);
/**
* 删除数据模型
*
* @param formId 数据模型ID
* @return 结果
*/
public int deletePCommFormById(Long formId);
/**
* 批量删除数据模型
*
* @param formIds 需要删除的数据ID
* @return 结果
*/
public int deletePCommFormByIds(Long[] formIds);
}

View File

@ -0,0 +1,61 @@
package com.ruoyi.project.system.service;
import java.util.List;
import com.ruoyi.project.system.domain.PCommForm;
/**
* 数据模型Service接口
*
* @author ruoyi
* @date 2020-05-14
*/
public interface IPCommFormService
{
/**
* 查询数据模型
*
* @param formId 数据模型ID
* @return 数据模型
*/
public PCommForm selectPCommFormById(Long formId);
/**
* 查询数据模型列表
*
* @param pCommForm 数据模型
* @return 数据模型集合
*/
public List<PCommForm> selectPCommFormList(PCommForm pCommForm);
/**
* 新增数据模型
*
* @param pCommForm 数据模型
* @return 结果
*/
public int insertPCommForm(PCommForm pCommForm);
/**
* 修改数据模型
*
* @param pCommForm 数据模型
* @return 结果
*/
public int updatePCommForm(PCommForm pCommForm);
/**
* 批量删除数据模型
*
* @param formIds 需要删除的数据模型ID
* @return 结果
*/
public int deletePCommFormByIds(Long[] formIds);
/**
* 删除数据模型信息
*
* @param formId 数据模型ID
* @return 结果
*/
public int deletePCommFormById(Long formId);
}

View File

@ -0,0 +1,96 @@
package com.ruoyi.project.system.service.impl;
import java.util.List;
import com.ruoyi.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.project.system.mapper.PCommFormMapper;
import com.ruoyi.project.system.domain.PCommForm;
import com.ruoyi.project.system.service.IPCommFormService;
/**
* 数据模型Service业务层处理
*
* @author ruoyi
* @date 2020-05-14
*/
@Service
public class PCommFormServiceImpl implements IPCommFormService
{
@Autowired
private PCommFormMapper pCommFormMapper;
/**
* 查询数据模型
*
* @param formId 数据模型ID
* @return 数据模型
*/
@Override
public PCommForm selectPCommFormById(Long formId)
{
return pCommFormMapper.selectPCommFormById(formId);
}
/**
* 查询数据模型列表
*
* @param pCommForm 数据模型
* @return 数据模型
*/
@Override
public List<PCommForm> selectPCommFormList(PCommForm pCommForm)
{
return pCommFormMapper.selectPCommFormList(pCommForm);
}
/**
* 新增数据模型
*
* @param pCommForm 数据模型
* @return 结果
*/
@Override
public int insertPCommForm(PCommForm pCommForm)
{
pCommForm.setCreateTime(DateUtils.getNowDate());
return pCommFormMapper.insertPCommForm(pCommForm);
}
/**
* 修改数据模型
*
* @param pCommForm 数据模型
* @return 结果
*/
@Override
public int updatePCommForm(PCommForm pCommForm)
{
pCommForm.setUpdateTime(DateUtils.getNowDate());
return pCommFormMapper.updatePCommForm(pCommForm);
}
/**
* 批量删除数据模型
*
* @param formIds 需要删除的数据模型ID
* @return 结果
*/
@Override
public int deletePCommFormByIds(Long[] formIds)
{
return pCommFormMapper.deletePCommFormByIds(formIds);
}
/**
* 删除数据模型信息
*
* @param formId 数据模型ID
* @return 结果
*/
@Override
public int deletePCommFormById(Long formId)
{
return pCommFormMapper.deletePCommFormById(formId);
}
}

View File

@ -6,9 +6,9 @@ spring:
druid: druid:
# 主库数据源 # 主库数据源
master: master:
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 url: jdbc:mysql://49.233.38.252:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root username: ckma
password: password password: ckma
# 从库数据源 # 从库数据源
slave: slave:
# 从数据源开关/默认关闭 # 从数据源开关/默认关闭

View File

@ -16,7 +16,7 @@ ruoyi:
# 开发环境配置 # 开发环境配置
server: server:
# 服务器的HTTP端口默认为8080 # 服务器的HTTP端口默认为8080
port: 8080 port: 8888
servlet: servlet:
# 应用的访问路径 # 应用的访问路径
context-path: / context-path: /
@ -57,14 +57,14 @@ spring:
# redis 配置 # redis 配置
redis: redis:
# 地址 # 地址
host: localhost host: 49.233.38.252
# 端口默认为6379 # 端口默认为6379
port: 6379 port: 16379
# 密码 # 密码
password: password: ckma
# 连接超时时间 # 连接超时时间
timeout: 10s timeout: 10s
lettuce: jedis:
pool: pool:
# 连接池中的最小空闲连接 # 连接池中的最小空闲连接
min-idle: 0 min-idle: 0

View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.project.system.mapper.PCommFormMapper">
<resultMap type="PCommForm" id="PCommFormResult">
<result property="formId" column="form_id" />
<result property="formName" column="form_name" />
<result property="formComment" column="form_comment" />
<result property="tableName" column="table_name" />
<result property="options" column="options" />
<result property="versionName" column="version_name" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
</resultMap>
<sql id="selectPCommFormVo">
select form_id, form_name, form_comment, table_name, options, version_name, create_by, create_time, update_by, update_time, remark from p_comm_form
</sql>
<select id="selectPCommFormList" parameterType="PCommForm" resultMap="PCommFormResult">
<include refid="selectPCommFormVo"/>
<where>
<if test="formName != null and formName != ''"> and form_name like concat('%', #{formName}, '%')</if>
</where>
</select>
<select id="selectPCommFormById" parameterType="Long" resultMap="PCommFormResult">
<include refid="selectPCommFormVo"/>
where form_id = #{formId}
</select>
<insert id="insertPCommForm" parameterType="PCommForm" useGeneratedKeys="true" keyProperty="formId">
insert into p_comm_form
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="formName != null and formName != ''">form_name,</if>
<if test="formComment != null and formComment != ''">form_comment,</if>
<if test="tableName != null and tableName != ''">table_name,</if>
<if test="options != null and options != ''">options,</if>
<if test="versionName != null and versionName != ''">version_name,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
<if test="createTime != null ">create_time,</if>
<if test="updateBy != null and updateBy != ''">update_by,</if>
<if test="updateTime != null ">update_time,</if>
<if test="remark != null and remark != ''">remark,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="formName != null and formName != ''">#{formName},</if>
<if test="formComment != null and formComment != ''">#{formComment},</if>
<if test="tableName != null and tableName != ''">#{tableName},</if>
<if test="options != null and options != ''">#{options},</if>
<if test="versionName != null and versionName != ''">#{versionName},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
<if test="createTime != null ">#{createTime},</if>
<if test="updateBy != null and updateBy != ''">#{updateBy},</if>
<if test="updateTime != null ">#{updateTime},</if>
<if test="remark != null and remark != ''">#{remark},</if>
</trim>
</insert>
<update id="updatePCommForm" parameterType="PCommForm">
update p_comm_form
<trim prefix="SET" suffixOverrides=",">
<if test="formName != null and formName != ''">form_name = #{formName},</if>
<if test="formComment != null and formComment != ''">form_comment = #{formComment},</if>
<if test="tableName != null and tableName != ''">table_name = #{tableName},</if>
<if test="options != null and options != ''">options = #{options},</if>
<if test="versionName != null and versionName != ''">version_name = #{versionName},</if>
<if test="createBy != null and createBy != ''">create_by = #{createBy},</if>
<if test="createTime != null ">create_time = #{createTime},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
<if test="updateTime != null ">update_time = #{updateTime},</if>
<if test="remark != null and remark != ''">remark = #{remark},</if>
</trim>
where form_id = #{formId}
</update>
<delete id="deletePCommFormById" parameterType="Long">
delete from p_comm_form where form_id = #{formId}
</delete>
<delete id="deletePCommFormByIds" parameterType="String">
delete from p_comm_form where form_id in
<foreach item="formId" collection="array" open="(" separator="," close=")">
#{formId}
</foreach>
</delete>
</mapper>